OmniAICreator/Qiita-1.07M huggingface dataset:
AndroidでOpenAI APIを叩きたい

# 結果

叩けました
サンプルアプリとしてこんなものを作りました
　※ 結果が5つ表示されているのはパラメータn=5にしているからです

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/441643/053419f5-3e63-ea35-0969-4d227c5d33f6.gif" width="300" />

このアプリのリポジトリはこちら

https://github.com/a7ther/android-open-ai-sample

# 経緯

iOSでOpenAI APIを呼び出しているのを見て、
Androidだったらどうなのかな？と思いやってみました

取り合えず、公式リファレンスを見る
イントロダクションにライブラリを使えとある

https://platform.openai.com/docs/api-reference/introduction

Kotlinはコミュニティライブラリでした
Ktorに依存しており、コードを見るとServer Side Kotlin向けっぽい

https://github.com/Aallam/openai-kotlin

公式リファレンスを読み進めると、OpenAI APIはただのREST APIのようなので、
ライブラリ使わないで実装してみました

https://platform.openai.com/docs/api-reference/chat/create

# コードの説明

API KEYはこちらに書いてください
コメントにも書きましたが、キーの取り扱いは注意してね！
```kotlin:app/src/main/java/com/example/open_ai_sample/network/RetrofitOpenAiNetwork.kt#L25
//XXX: サンプルのため、ここにおいてますが、API_KEYは外部から取得するようにしてください
private const val OPENAI_API_KEY = "{YOUR_API_KEY}"
```

通信ライブラリはRetrofitを使っていて、今回はエンドポイント`v1/chat/completions`のみです
```kotlin:app/src/main/java/com/example/open_ai_sample/network/RetrofitOpenAiNetwork.kt#L17
//api reference : https://platform.openai.com/docs/api-reference/chat/create
@Headers("Authorization: Bearer $OPENAI_API_KEY")
@POST("v1/chat/completions")
suspend fun chatCompletions(@Body request: ChatCompletions.Request): ChatCompletions.Response.Success
```

ここでプロンプトを設定します
引数のpromptは画面で入力したテキストです
```kotlin:app/src/main/java/com/example/open_ai_sample/MainViewModel.kt#L60
/**
 * ここでプロンプトをカスタムしてください
 * api reference : https://platform.openai.com/docs/api-reference/chat/create
 */
private fun createRequest(prompt: String) = ChatCompletions.Request(
    model = "gpt-3.5-turbo",
    messages = listOf(
        ChatMessage(
            role = ChatRole.SYSTEM.role,
            content = "You are a helpful assistant.",
        ),
        ChatMessage(
            role = ChatRole.USER.role,
            content = prompt,
        )
    ),
    temperature = 0.0,
    n = 5,
)
```

後はリクエストを投げて、レスポンスからchoices.message.contentを取り出して、
```kotlin:app/src/main/java/com/example/open_ai_sample/MainViewModel.kt#L41
uiState = when (result) {
    is ChatCompletions.Response.Success -> {
        uiState.copy(
            sendResultState = MainUiState.SendResultState.Success(
                result.choices.map { it.message?.content ?: "" }
            )
        )
    }

    is ChatCompletions.Response.Failure -> {/** 省略 */}
}
```
整形して、おわりです
```kotlin:app/src/main/java/com/example/open_ai_sample/ui/MainScreen.kt#L99
items(sendResultState.results.size) { index ->
    Text(
        modifier = Modifier.padding(8.dp),
        text = "${index + 1}. ${sendResultState.results[index]}",
    )
    Divider()
}
```

# まとめ
- 簡単に叩けた
  - ライブラリを使わなくても、ただのREST APIなので超簡単
問題としては、modelを作るのが面倒
- 今回はお遊びということで、普通はバックエンドで叩くよね！
  - API KEYの管理問題（外部からとってきて、保存するなら暗号化しないと。。）
  - 変更にアプリリースが必要（プロンプトを簡単に変更できない。。）
【JavaScript】配列・オブジェクトをコピーする時に、イコール(=)で代入しちゃダメなの、なぁぜなぁぜ？

# はじめに
* この記事ではJavaScriptの配列・オブジェクトを正しくコピー(複製)する方法を紹介します
* 初歩的な内容ですが、筆者が初見殺しに会った為、備忘録として共有します

# この記事の対象者
* 現在JavaScriptを学ばれている方
* これからJavaScriptを学ばれる方

# NGパターン
以下は配列「arr1」のコピー(複製)を作るソースコードです。

```js
const arr1 = [10, 20];
const arr2 = arr1; // 代入によるコピー

console.log(arr1);
console.log(arr2);
```
以下、実行結果です。
```
> [10, 20]
> [10, 20]
```
コピーは出来てそうですね。

ただ、これは危険な書き方です。
何が危険なのでしょうか？それは...

# 配列のイコール(=)による代入は、コピー元を参照してしまう
上述NGパターンについて、arr2[0]の値を変えてみます。
```js
const arr1 = [10, 20];
const arr2 = arr1;

arr2[0] = 30;

console.log(arr1);
console.log(arr2);
```
以下、実行結果です。
```
> [30, 20]
> [30, 20]
```
コピー先(arr2)の値を変えるとコピー元(arr1)の値も変わってしまう、
つまり**コピー元を参照**していますね！！

これは予期せぬ挙動と成り得るので修正しましょう。

# じゃあどうすれば？
いくつか方法はありますが、この記事ではその中で最も直感的に分かり易かった
**スプレッド構文**(後ほど補足します)を使用します。

* スプレッド構文以外の手法について
    * 参考 [【JavaScript】配列を複製する](https://zenn.dev/kou_pg_0131/articles/js-clone-array)

まずは修正後のソースコードを見てみましょう。
```js
const arr1 = [10, 20];
const arr2 = [...arr1];  // スプレッド構文

arr2[0] = 30;

console.log(arr1);
console.log(arr2);
```
以下、実行結果です。
```
> [10, 20]
> [30, 20]
```
コピー先(arr2)のみが変更されました。めでたしめでたし...

# ...と思うじゃん？　※2023/06/26追記
有識者の方々にご指摘頂きましたので追記です。
実は、スプレッド構文は1次元までのコピー(シャローコピー)でした。

その為、それより深い階層では以下のようにコピー元を参照してしまいます。
```js
const arr1 = [10, [20, 30]];
const arr2 = [...arr1];

arr2[1][0] = 100;

console.log(arr1); // [ 10, [ 100, 30 ] ]
console.log(arr2); // [ 10, [ 100, 30 ] ]
```
これを回避する為に、全ての階層のコピー(ディープコピー)を行う必要があります。

それを実現するのが...

# structuredClone() ※2023/06/26追記
以下、structuredClone()を使用したソースコードです。
シンプル且つ直感的で分かりやすいですね！！
```js
const arr1 = [10, [20, 30]];
const arr2 = structuredClone(arr1);

arr2[1][0] = 100;

console.log(arr1); // [ 10, [ 20, 30 ] ]
console.log(arr2); // [ 10, [ 100, 30 ] ]
```
コピー先(arr2)の2階層目のみが変更されました。めでたしめでたし。




# 補足
## スプレッド構文とは？
* スプレッド構文は本来、配列・オブジェクトの前に「...」を記述することで、展開した要素内の値を出力するものです
* ES2015(ES6) で追加された機能で、現在では全てのブラウザで対応しています
```js
const arr1 = [10, 20];

console.log(arr1);     // [10, 20]
console.log(...arr1);  // 10 20
```
今回紹介したスプレッド構文を使って配列をコピーする方法は、展開した値から新しい配列が生成されたということですね。

## オブジェクトも代入を行うと、コピー元を参照してしまう
配列同様にスプレッド構文を使用することで、1階層であればコピー元が参照されることを回避できます。
※ディープコピーを行う場合はstructuredClone()を使いましょう

* 代入によるコピー(オブジェクト)
```js
const obj1 = {val1: 10, val2: 20};
const obj2 = obj1; // 代入によるコピー

obj2.val1 = 30;

console.log(obj1);  // {val1: 30, val2: 20}
console.log(obj2);  // {val1: 30, val2: 20}
```
* スプレッド構文を使用したコピー(オブジェクト)
```js
const obj1 = {val1: 10, val2: 20};
const obj2 = {...obj1};

obj2.val1 = 30;

console.log(obj1);  // {val1: 10, val2: 20}
console.log(obj2);  // {val1: 30, val2: 20}
```

# まとめ
* 配列・オブジェクトはイコール(=)で代入すると、コピー元を参照してしまう
* 代入ではなく、~~スプレッド構文~~ structuredClone()を使おう！
AZ-104 合格まで (2023.06)

前回の受験記録。
[AZ-900 と MS-900 合格まで (2023.01) - Qiita](https://qiita.com/e99h2121/items/4ee69f047e555990065d)

2023.6.23 AZ-104 を受けて合格したメモです。

## 感想
- 難易度的に、AWS のナントカアソシエイトより、むずかった。。。
- 何が難しいかと言われると AWS と Azure の一連の名付けの違いだったり、Azure の分かりづらさ起因の気もする。
- ケース スタディとか、画面を見て答える問題とか、タブを行き来する UI が答えづらい。
- 単に問題集や情報量の差かもしれない。
- こういうやつ
    - [Azure の Load Balancer - Qiita](https://qiita.com/e99h2121/items/61121923a500391c76bc)
    - [ブロック BLOB、追加 BLOB、ページ BLOB - Qiita](https://qiita.com/e99h2121/items/30fccc49b38001a3d368)


## 勉強の友

[Microsoft Certified: Azure Developer Associate - Certifications | Microsoft Learn](https://learn.microsoft.com/ja-jp/certifications/azure-developer/)
[AZ-104:Microsoft Azure Administrator模擬試験問題集 | Udemy](https://www.udemy.com/course/az-104azure-administrator-associate/)
[合格対策Microsoft認定試験AZ-104:Microsoft Azure Administratorテキスト&演習問題 | 吉田 薫 |本 | 通販 | Amazon](https://www.amazon.co.jp/%E5%90%88%E6%A0%BC%E5%AF%BE%E7%AD%96Microsoft%E8%AA%8D%E5%AE%9A%E8%A9%A6%E9%A8%93AZ-104-Microsoft-Azure-Administrator%E3%83%86%E3%82%AD%E3%82%B9%E3%83%88-%E6%BC%94%E7%BF%92%E5%95%8F%E9%A1%8C/dp/4865943080/)
あとは O'Reilly のサブスクにあったこれをずっとやっていた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/93824/97b394f1-0ed9-17d8-9e49-bc1447c3ef95.png)



## 次ステップ

AZ-305、AZ-400 あたりを取るのが妥当そう

[試験 AZ-305: Microsoft Azure インフラストラクチャ ソリューションの設計 - Certifications | Microsoft Learn](https://learn.microsoft.com/ja-jp/certifications/exams/az-305/)
[試験 AZ-400: Microsoft DevOps ソリューションの設計と実装 - Certifications | Microsoft Learn](https://learn.microsoft.com/ja-jp/certifications/exams/az-400/)
[【合格体験記】Azure DevOps Engineer Expert【AZ-400】 | SIOS Tech. Lab](https://tech-lab.sios.jp/archives/33168)
[Azure認定資格 AZ-400を取得しました - Qiita](https://qiita.com/shingo_kawahara/items/1e0d371276d264fe65fc)

[Azure関連の資格の情報（2023年1月版）](https://zenn.dev/tomot/articles/80cbfb818e7c27)
[Azure関連資格、体系とか受ける順番とか。](https://zenn.dev/tomot/articles/c60883beaf3c5e)
[認定資格および試験を見る | Microsoft Learn](https://learn.microsoft.com/ja-jp/certifications/browse/?expanded=azure&products=azure)


以上です～
TelnetコマンドでHTTP接続をするときにRequest Timeoutになってしまう

### はじめに

はじめまして、初のQiita投稿となります。
1番初めに、大学の課題で自分が躓いた点を、備忘録的な意味で投稿したいと思います。
私自身、IT関連の知識に疎く、正確ではない情報が含まれている場合もあるかと思います。その点にご注意いただいたうえでお読みください。

また、この症状が起きたのは特定のWebサイトです。googleのサイトでは同様のことは起こらなかったので、一部のサイトに限られる話かもしれません。

### なにが起こったか

###### 私がやりたかったこと
→telnetコマンドを用いて、Webサイトのindex.htmlファイルをGETする

###### つまづいたポイント
→GETやHostを打ち込んでいる間に接続が切れてしまう

### 解決策
この不具合は、GETやHostを高速で打ち切ることで解決できました。
高速タイピングでごり押しでも可能ですが、打ち込みたい内容をコピーしておいて、telnetコマンドを打った後にペーストでも可能です。Linuxの場合、コピーはCtrl + Shift + C、ペーストはコピーはCtrl + Shift + Vで可能です。
2023/6/26主にITとかセキュリティの記事

Amazon S3 オブジェクトロックと Veeam Backup を使用した SIOS LifeKeerper で構成されるクラスターシステムのランサムウェア対策(6/23)
https://aws.amazon.com/jp/blogs/psa/s3-object-lock-veeam-backup-sios-lifekeeper-for-ransomware/

チャットGPTがサイバーセキュリティに突きつける新たなリスク(6/24)
https://news.yahoo.co.jp/articles/a736400745519868bb88869294c3f3276cd29de7?source=rss

セキュリティを高めるIDaaSとは何か。金融業界での活用方法もチェック(6/24)
https://the-owner.jp/archives/14949

「通信の秘密の保護」に制限検討　サイバー攻撃への対処、政府が強化(6/23)
https://news.yahoo.co.jp/articles/5406ed0bbc54e5a5308daacf314bce1a80495d0b

政府「能動的サイバー防御」導入へ 有識者会議で法整備を検討(6/24)
https://www3.nhk.or.jp/news/html/20230624/k10014108251000.html

ロシア軍通報機能やW杯ストリーミング機能もあるウクライナのゼレンスキー大統領肝いりのアプリ「Diia」とは？(6/23)
https://gunosy.com/articles/euozX

クラウドアプリのセキュリティ対策とは。知っておきたいポイント
https://www.ntt.com/business/services/network/internet-connect/ocn-business/bocn/knowledge/archive_39.html

大至急アップデートが必要！　Apple製品に重大なセキュリティ脆弱性が発見(6/24)
https://www.epochtimes.jp/2023/06/158401.html

うちのPCが遅過ぎなのは「マルウェア」のせい？　Windows 11のパフォーマンス問題解消に役立つ「無料のMicrosoft純正セキュリティツール」(6/24)
https://news.yahoo.co.jp/articles/bcb05928edb15d7aff35b2bd478fdbfa0e804da5

米当局がアマゾンを提訴、「顧客だましプライム会員に登録」　解約も煩雑と(6/22)
https://www.bbc.com/japanese/65982205?at_medium=RSS&at_campaign=KARANGA

ユニコードとあまり使われない拡張子を悪用したWordPressのバックドアに注意(6/24)
https://news.mynavi.jp/techplus/article/20230624-2711194/

GitHubの数百万のリポジトリに乗っ取りのリスク、確認を(6/24)
https://news.mynavi.jp/techplus/article/20230624-2711168/

パスワードレス認証「パスキー」対応を強化した「Windows 11」Build 23486がDevチャネルに(6/23)
https://forest.watch.impress.co.jp/docs/news/1510946.html

イギリスの名門大学、ランサムウェア攻撃でデータ窃取の被害か(6/24)
https://news.mynavi.jp/techplus/article/20230624-2710728/

インターネットによる人権侵害(6/23)
https://www.pref.nagano.lg.jp/jinken-danjo/kurashi/jinkendanjo/jinken/main/jinken/internet/index.html

税務行政のデジタル・トランスフォーメーション－税務行政の将来像　２０２３－(6/23)
https://www.nta.go.jp/about/introduction/torikumi/digitaltransformation2023/index.htm

iOS/iPadOS16.5.1や、watchOSとmacOSの最新版がリリース(6/22)
https://iphone-mania.jp/news-542582/

児童の個人情報を含む動画の流出について(6/23)
https://www.pref.saitama.lg.jp/f2207/news/page/news2023062301.html

＜Q＆A＞マイナンバー「総点検本部」の役割、方法は？(6/24)
https://www.tokyo-np.co.jp/article/258566

情報漏えい、他人の情報ひも付け…「マイナンバーカード」でトラブル続出　被害に遭ったら賠償請求は可能？　弁護士に聞く(6/24)
https://news.yahoo.co.jp/articles/d6784ac911c42981332b48cd31f12610bd21f04d

交際相手の女性のインターネットバンキングに不正にアクセス　約840万円送金　30代の男を逮捕(6/24)
https://news.yahoo.co.jp/articles/35f76be1f070840e75f565b4b3e0ed48305884fa

マイナカード「便利さ」の代償は？堤未果さんが斬り込む〝煽り〟の罪(6/23)
https://withnews.jp/article/f0230623000qq000000000000000W08u10101qq000025850A
DBテーブルがない場合のテストをJUnitで実施する方法

# 目的
JDBCを用いてMySQLサーバにアクセスするJavaプログラムの単体テストにおいて、
対象のテーブルが存在しないテストケースをJUnitを用いて自動化したい。

# 方法
1. 初期化用SQLファイルを用意しておく。
1. Junitのテストから初期化用SQLファイルの内、DROP TABLEのみを実行する。
1. テーブルが存在しない状態でテスト対象メソッドを実行、結果を観測する。
1. 初期化用SQLファイルをすべて実行する。
1. テーブルが初期状態に戻る。

```SQL:初期化用SQLファイルサンプル
DROP TABLE IF EXISTS `sample`;

CREATE TABLE `sample` (
  `ID` int NOT NULL AUTO_INCREMENT,
  `Name` char(10) NOT NULL,
  `Address` char(20) NOT NULL,
  PRIMARY KEY (`ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

INSERT INTO `sample` VALUES (1,'abc','hoge');
INSERT INTO `sample` VALUES (2,'def','fuga');
INSERT INTO `sample` VALUES (3,'ghi','piyo');
```
```Java:テストケースサンプル
@Test
public void testCase() {
  // テーブル管理クラス
  TableManager tm = new TableManager(con);

  // テーブルを削除する
  tm.dropTable(SQL_FILE_PATH);

  assertThrows(SQLException.class, () -> {
    dao.getSampleById(0); // 引数のIDでテーブルを検索するテスト対象メソッド
  });

  // テーブルを初期化する
  tm.initTable(SQL_FILE_PATH);
}
```
```Java:TableManager.java
public class TableManager {

  private Connection con;

  public TableManager(Connection con) {
    this.con = con;

    try {
      con.setAutoCommit(false);
    } catch (SQLException e) {
      e.printStackTrace();
    }
  }

  /**
   * SQLファイルを実行しテーブルのリセットを行う。
   * SQLファイルは上から順に以下の形式である必要がある。
   * <ol><li>既存のテーブルをドロップ</li>
   * <li>テーブルをCREATEで作成</li>
   * <li>INSERT文</li></ol>
   * @param path SQLファイルがある相対パス
   */
  public void initTable(String path) {
    <略>
  }

  /**
   * DROP TABLEが記述されているSQLファイルを読み取り、実行する。
   * ファイルに記述されているDROP TABLE以外のSQLは実行されない。
   * @param path SQLファイルがある相対パス
   */
  public void dropTable(String path) {
    <略>
  }

  /**
   * 引数の更新系SQLを実行する。
   * 更新系以外のSQLである場合はSQLExceptionをスローする。
   * @param sql SQL文
   * @throws SQLException
   */
  private void doSql(String sql) throws SQLException {
    try {
      Statement stmt = con.createStatement();
      stmt.executeUpdate(sql);
    } catch(SQLException e) {
      throw e;
    }
  }
}
```
# ポイント
- 初期化用SQLファイルはBufferReader等を利用して行単位で読み込む。<br>その後、複数行にわたるSQL（サンプルのCREATE TABLE）は1行にフォーマットする。
- AutoCommitは切っておいてすべてのSQLが実行できてからCommitする。
Azure AD Identity Protection のレポートを読み解く！

# きっかけ
ある日「Azure AD Identity Protection Weekly Digest」 というメールを発見！
どうやら何かしらのリスクがあるらしいがよくわからない。。。

ということで、今回は **Azure AD Identity Protection** について調べたことと、通知されてきたレポートの内容についてまとめていこうと思います。

# Identity Protection の概要
Identity Protection を使用することで、主に以下の3点ができるようになります。

1. リスクの検出
1. リスクの調査
1. リスク情報の活用

### 1. リスクの検出
Identity Protection によって次のようなさまざまな種類のリスクが検出されます。

- 匿名 IP アドレスの使用
- 特殊な移動
- マルウェアにリンクした IP アドレス
- 通常とは異なるサインイン プロパティ
- 漏洩した資格情報
- パスワード スプレー
- その他...

### 2.  リスクの調査
次の3つの主要レポートを参考にして管理者が調査できるようになっています。

- 危険なユーザー
- リスクの高いサインイン
- リスク検出

### 3. リスク情報の活用
データを他のツールと連携したり色々できるよってことなのですが、奥が深いので今回は省略します:stuck_out_tongue:

## 必要なライセンス
**Azure AD Identity Protection** の機能をフルで使いたい場合は、Azure AD Premium P2 ライセンスが必要になります。
詳しくは[ライセンス要件](https://learn.microsoft.com/ja-jp/azure/active-directory/identity-protection/overview-identity-protection#license-requirements)を参照してください。

![スクリーンショット 2023-06-23 1.46.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982074/90c10c13-dd65-712f-745e-ffb6f98dd558.png)

# 届いたレポート

届いたメールには対象の Identity Protection へのリンクが張られていました。
![スクリーンショット 2023-06-23 2.02.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982074/47943007-876c-3ae7-14e5-4b45337fea28.png)

リンクをたどって「危険なサインイン」 や 「リスク検出」を見ると、しっかり検知されています。
![スクリーンショット 2023-06-23 2.10.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982074/135ed41b-c1ec-590b-2152-d06bb129b1fc.png)
![スクリーンショット 2023-06-23 2.15.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982074/5f5e4c0d-6925-0845-d5bb-c9da3114f902.png)

# 検出の種類について
リスク検出の種類は多肢に渡ります。
参考：[リスクの種類と検出](https://learn.microsoft.com/ja-jp/azure/active-directory/identity-protection/concept-identity-protection-risks#risk-types-and-detection)

全部取り上げるとキリがないので、今回検出された「通常とは異なる移動」にスポットを当ててみます。

## 通常とは異なる移動？
Microsoftのドキュメントには、次のように記載されています。
>このリスク検出の種類では、地理的に離れた場所で行われた 2 つのサインインを識別します。
少なくとも 1 つの場所は、ユーザーの過去の行動から考えて、ユーザーにそぐわない可能性がある場所でもあります。 このアルゴリズムでは、2 回のサインインの間隔や、最初の場所から 2 回目の場所にユーザーが移動するのに要する時間など、複数の要因が考慮されます。 
このリスクは、別のユーザーが同じ資格情報を使用していることを示している場合があります。

ざっくり言えば、ずっと同じ場所からサインインしてたのに、突然違う場所からサインインされたのを検知したってことみたいです。

# 対応方法
今回と同じようなレポートを検知した場合は、以下の観点で確認してみましょう。
- 対象ユーザーが普段と異なる場所からログインしたかを確認してみる。
- 対象ユーザーがそもそも存在してる(退職済みとかじゃない？)かを確認する。
- 対象ユーザーのパスワード変更をする。
- アプリ経由でAD認証した際にブラウザバージョン等が古くないか確認する。

上記のような状況だと検知されることがあるそうです。
さらに怪しい場合は不正アクセスがないかなど、ログ等から深掘りをする必要があります。

## まとめ
今回のメールをきっかけにリスク検知した場合の対応方法をルール化しておこうと思います。
いい教訓になったといえばそれまでですが、スルーしてきたものもあると思うと怖いですね。。。
今後はAWSだけじゃなくて、Azureさんにも気を配ろうと思います(笑)

最後まで読んでいただきありがとうございました！！！:wave:
Amazon FSx for NetApp ONTAP のマウントで Failed to resolve server 〜 になった時の対処

# 概要
Amazon FSx for NetApp ONTAP の NFSマウントでエラーになったので、同じ過ちを繰り返さないためメモしておきます。
今回は Amazon Linux2 のEC2環境から、mountコマンドを実行しています。

# エラー内容
`Failed to resolve server　〜 Name or service not known` となっていて、どうやら名前解決ができていないっぽい。
```
$ sudo mount -t nfs svm-***.fs-***.fsx.ap-northeast-1.amazonaws.com:/vol1 /fsx
mount.nfs: Failed to resolve server svm-***.fs-***.fsx.ap-northeast-1.amazonaws.com: Name or service not known
mount.nfs: Operation already in progress
```

# 原因
VPCの設定が以下になっていたことが原因でした。
- DNS名前解決：有効
- DNSホスト名：<font color="red">**無効**</font>　　<- これがダメ

![スクリーンショット 2023-06-21 19.22.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982074/cdcc427c-fac2-eea5-f571-553278016932.png)
この状態だと、同一VPC内のサーバーに対してDNS名の名前解決ができないため、今回のようなエラーとなってしまいます。

:::note warn
ファイルシステムを作成すると、SVMとボリュームの作成処理等で20分〜45分程度時間がかかります。この間にマウントしようとしてもエラーになるので、作成されるまで待ちましょう。
:::

# 対処法
VPCの設定で **DNSホスト名を有効化** にチェックします。
![スクリーンショット 2023-06-18 11.55.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982074/bb90fb4a-877b-7937-a82f-4c78f142cb84.png)

設定編集後、**DNSホスト名** と **DNS解決** の両方が有効化されていればOKです。
![スクリーンショット 2023-06-18 11.55.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2982074/8493dc59-aa32-4371-7125-581202bb7920.png)

数分後に再度 mountコマンドを実行した結果、正常にマウントできるようになりました。
```
$ sudo mount -t nfs svm-***.fs-***.fsx.ap-northeast-1.amazonaws.com:/vol1 /fsx
$
```
```
$ df /fsx
ファイルシス                                               1K-ブロック      使用    使用可 使用% マウント位置
svm-***.fs-***.fsx.ap-northeast-1.amazonaws.com:/vol1  1020054784 117761152 902293632   12% /fsx
```

# まとめ
- FSxの設定ミスだと思っていたが、VPCの設定問題だったので基本を忘れずに(灯台下暗し)
- AWSでONTAPさわれるの最高:relaxed:

# 参考文献
https://dev.classmethod.jp/articles/amazon-fsx-for-netapp-ontap/

https://blog.serverworks.co.jp/efs/name
【PHP初級】三項演算子を使ってみた

## 初めに

- PHP/Mysqlを使用して１年ほど
- SymfonyやLaravelを半年ずつほど
- PHP初級を取るためにPHP初級問題集、初めてのPHPを読んでいます

## 概要

いつもif elseを使用してコードを書いているのですが三項演算子というものを見たので自分でも使ってみたくまとめてみました。

## 1. 三項演算子

そもそも三項演算子って何？と思うので簡単に調べました。

:::note info
三項演算子は演算子のひとつで3つの項目を使用する唯一の演算子です。
条件式を評価し、TRUEであれば式1、FALSEであれば式2を返します。
:::

```php
条件式 ? 式1 : 式2;
```

ざっくり、条件が２パターンでのIF文を一文で書けるって認識です。
コードにするとこんな感じ。

```php
$setdate = '1';

//IF文の場合
if($setdate == 1){
	$msg1 = 'OK';
}else{
	$msg1 = 'NG';
}
print $msg;

//三項演算子の場合
$msg2 = $setdate == '1'  ? 'OK' : 'NG';
print $msg2;
```

```php
OK
OK
```

便利、、コードの行がすごく減ったので見やすい!!
でも普段IFでネストすることがあるので三項演算子でネストは可能なのか？？？

調べたらいけるっぽいですね、IFと比べて行は減ったが複雑になってきた気がする。。

```php
(条件式1 ? 真の式1 : (条件式2 ? 真の式2 : 偽の式));
```

とりあえず使ってみよう！！

```php
$Country = 'JP';
$CountryTop = ($Country == 'USA' ? '米国' : ($Country == 'JP' ? '日本' : '概要なし'));
 
echo 'あなたの国は、'.$CountryTop;
```

簡単に米国か日本かを分ける処理を作成してみました。

Countryに省略コードが入るのでUSAであればTRUE「米国」となります、
FALSEの場合は$Country == 'JP'に移り、TRUEであれば「日本」それ以外であれば「該当なし」ななります。

```php
あなたの国は日本
```

## まとめ

- IF文が一行でまとまるので可読性が高い
- ネストも使えるのでより文をまとめたい場合も使用可能
- 使ってみて、要所で使用なら可読性は高まるが三項演算子ばかりでネスト多様をすると逆に可読性は下がると感じた
表示中の検索結果ページでタイトル一覧を取得する

前稿「[[Bardに聞く]WEBスクレイピングで子ページにある情報を検索](https://qiita.com/ginger_ale/items/2ae4cbc1679ad2e87c69)」で子ページにある記載からの検索を実装しました。

前稿は簡単な構造であるため、ログインが必要なページや前操作があるなど複雑な内容には対応できません。
複雑でも実装すればいい、という手法もありますが、
日常的な業務を気軽に効率化するならば、そのあたりは除外したいもの。

今回は、今表示されているページでJavascriptが実行できる
Chrome拡張機能「[User JavaScript and CSS](https://chrome.google.com/webstore/detail/user-javascript-and-css/nbhcbdghjpllgmfilhnhkllmkecfmpld)」を使用し
検索結果ページでタイトル一覧を取得します。

# 目的
・よくある検索ページ構成
![03_draw_io_target_search_result.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/972909/d1bef865-a59a-3dca-050c-67d379d700c4.png)

このような構成のページ内で、
検索結果のタイトルである会社名の一覧を取得します。
Excelに張り付けるべく、会社名ごとに改行した文字列を作成します。

# 対応
Chrome拡張機能「[User JavaScript and CSS](https://chrome.google.com/webstore/detail/user-javascript-and-css/nbhcbdghjpllgmfilhnhkllmkecfmpld)」をChromeに追加します。
拡張機能を有効にすると下記の画面が表示されますので、右上の設定ボタンを押します。
![03_User_Javascript_click.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/972909/24ac50b4-ffa4-e68f-c36c-6f554f324f9a.png)
すると保存されたJavascript and CSSの一覧ページが表示されます。
Add new siteを押すと名前、URL、Javascript、CSSを入力する欄が表示される、シンプルな機能です。
jsエディタとして文字色がついている親切さです。
![03_User_Javascript_edit.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/972909/dc44999a-22ba-7f9c-9ada-9311b7e8023f.png)

ソースは今回はgitに置かせていただきました。
https://github.com/gingerale333/qiita/blob/main/UserJavascript/getCompanyList.js
・取得対象項目（例では会社名）
・次ページへの遷移ボタン
のクラス名は対象サイトに合わせて書き換える必要があります。

# 動作
User JavaScript and CSSの設定ページでURLを指定した検索ページで検索を実行すると、
今表示されている検索結果のタイトル一覧を作成し、小窓で表示します。
OKを押すと次のページに進みます。キャンセルすると表示中画面に戻ります。
表示中の結果しか一覧化しないので、テキストをコピーして控えたらOKを押す動作の繰り返しを手動で行いました。
![03_exec_getCompanyList.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/972909/6d6f7c5c-a63c-8aab-3597-1a33eaa02f83.png)

デベロッパーツールのコンソールを表示すると、
``User Javascript is running.``
が出力されていますので、そこからソースへ飛んでデバッグ実行することも可能です。

# 残課題：全結果の一覧化はできない
![03_exec_getCompanyList_next.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/972909/e7bc8cee-c912-6ee3-954e-d4962c5a3abd.png)
URLパラメータによって前ページの表示分を次ページへ引き継ぐ動作を記載したのですが
3ページ目までは成功して4ページ目はエラーになってしまい、この動作を削除しました。
原因はURLが長すぎることです。
前ページからの遷移時に引継ぎを行うソースにご興味のある方は
git履歴から最初のバージョンをご取得ください。

# 最後に
以上、本日はChrome拡張機能「User JavaScript and CSS」の使い方のご紹介でした。
業務自動化とはいかず一部効率化となりましたが、
詳細表示有無の選択が無い検索ページではどこでも使うことができそうです。
数式を英語で読む

# きっかけ
確率・統計、特にベイズを勉強しているときのことです．
「Bという条件におけるAの確率は、、、」と読むのが、ふと面倒になりました．
また、「この読み方、あってるんだっけ？」と考えながら読むので、数式の内容をあまり理解できていないことに気づきました．

「ちゃんと数式を英語で読みたい！」と思い、自分用にまとめてみました．

[面白くて眠れなく数学](https://www.php.co.jp/books/detail.php?isbn=978-4-569-79117-3)も参考にしました．

今回、記事執筆にあたってTeXを使ってみましたが、**TeXの意味を理解すれば、数式の読み方の理解が速くなりそう**な感覚を持ちました．

shellで「cdは"change directory"の略」と理解すれば、ちょっと覚えやすくなるのと近いですね．




# 条件付き確率は「given」
```math
 P(A∣B)
```
```markdown
 probability of A given B
 probability of A under the condition B
```

# 関数は「of」で表現する<br>
```math
f(x)
```
```markdown
a function f of x
f of x
```



# 「イコール」ではなく「equals」<br>
```math
x+y=z
```
```markdown
x plus y equals = z.
```
日本語読みだと「イコール」読みですが、英語では3人称単数のsがつくそうです．


# 英語の方が左辺・右辺の違いに敏感<br>
```math
a=b
```
```markdown
a equals b.
a is equal to b.
```


# `'`はダッシュではなくプライム<br>
```math
y' = \frac{dy}{dx}
```
```markdown
y prime equals dy dx.
```
ダッシュは「—」のことです．
[ダッシュ (記号)](https://ja.wikipedia.org/wiki/%E3%83%80%E3%83%83%E3%82%B7%E3%83%A5_(%E8%A8%98%E5%8F%B7))


# 組み合わせのCは省略せずに combinationsと読む<br>
```math 
_n C_r
```
```markdown
the combinations of n taken r.
the combinations n r.
```


# 大文字はCapital、小文字はsubと読んで区別する<br>
```math
A_k​
```
```markdown
Capital A sub k.
```




# 不等号はless than, greater than、 or equal<br>
```math
a>b
```
```markdown
a is greater than b.
```
```math
a \leqq b
```
```markdown
a is less than or equal to b.
```


# 集合はin<br>
```math
a \in A
```
```markdown
The element a is a member of the set A.
a is an element of the set A.
a is a member of A.
a is in A.
```



# 終わりに
他の数式も随時更新予定です．
Docker composeでMySQLのコンテナを複数同時に起動する場合のポート設定

## はじめに
Docker ComposeでMySQLのコンテナを複数同時に起動して作業したいことがあったのですが、その際にうまくいかなかったので備忘録として残しておきます。

## MySQLのコンテナ

```yml:docker-compose.yml
version: "3.9"
volumes:
  db-store:
services:
  db:
    build:
      context: .
      dockerfile: ./infra/docker/mysql/Dockerfile
    ports:
      - 3306:3306
    volumes:
      - type: volume
        source: db-store
        target: /var/lib/mysql
        volume:
          nocopy: true
    environment:
      - MYSQL_DATABASE=${DB_DATABASE}
      - MYSQL_USER=${DB_USERNAME}
      - MYSQL_PASSWORD=${DB_PASSWORD}
      - MYSQL_ROOT_PASSWORD=${DB_PASSWORD}
```

## 事象
2つ目のコンテナを起動する際、MySQLのポート番号がそれぞれ通常の3306のままだった場合、以下のようなエラーが表示されます。

```
$ docker compose up -d
 ⠿ Container container-db-1   Starting
Error response from daemon: driver failed programming external connectivity on endpoint container-db-1 (...): Bind for 0.0.0.0:3306 failed: port is already allocated
make: *** [up] Error 1
```
ポート番号が被っているのでそれは確かに接続できないですよね。

## 対処：ホスト側のポート番号を変更
そこでホスト側のポート番号を3307へ変更します。

```yml:docker-compose.yml
    ports:
      - 3307:3306
```

コンテナが起動できました。
```
$ docker compose up -d
 ⠿ Container container-db-1   Started
```

## 試しにSequel Aceで接続
しかし、データベース管理アプリのSequel Aceで接続しようとすると、エラーが出てしまいました。

```
Connection failed!

Unable to connect to host 127.0.0.1, or the request timed out.

Be sure that the address is correct and that you have the necessary privileges, or try increasing the connection timeout (currently 10 seconds).

MySQL said: Lost connection to MySQL server at 'reading initial communication packet', system error: 0
Lost connection to MySQL server at 'reading initial communication packet', system error: 0
```

Sequel Aceの設定値は以下の通りです。

<table>
  <thead>
  <tr>
    <th> 項目名 </th> <th>設定値</th>
  </tr>
</thead>
  <tbody>
  <tr>
    <td> Host </td> <td>127.0.0.1</td>
  </tr>
  <tr>
    <td> Username </td> <td>MYSQL_USERの値を入力</td>
  </tr>
  <tr>
    <td> Password </td> <td>MYSQL_PASSWORDの値を入力</td>
  </tr>
  <tr>
    <td> Database </td> <td>MYSQL_DATABASEの値を入力</td>
  </tr>
  <tr>
    <td> Port </td> <td>3307</td>
  </tr>
  </tbody>
</table>

## 対処：コンテナ側のポート番号を変更
結局、コンテナ側のポート番号も3307に変更してホスト側と合わせました。
```yml:docker-compose.yml
    ports:
      - 3307:3307
```

Sequel Aceでも接続できました。
ひとまずこれで複数同時に起動して利用することができました。
証明書の無いAppxファイルを自分で署名してインストールする

## 経緯
1. ffmpegだとキーフレームで動画の切り出しするの難しいなあ…
1. せや、フリーソフトで良さそうなの探そう（GitHubで検索）
1. [LosslessCut](https://github.com/mifi/lossless-cut) 良さそうやん
1. Microsoft Store からは購入しかできんからAppxからインストールするか～
1. 0x800B010A エラー吐いた。まじか、署名されてないんかこれ。。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1461400/06409d60-2bdf-625c-7513-c9817688daca.png)

## 手順
:::note warn
本来であれば開発者の方で署名をしておくべきものだと思うので、何が起こっても自己責任の上で実行していただくようお願いします。
:::
### 証明書の発行
署名をするには証明書（.pfx）が必要となります。
もし、開発者が配布しているのであれば以下の手順は必要なくなります。

2. [MSIX Packaging Tool](https://www.microsoft.com/store/productId/9N5LW3JBCXKF) を入手して起動する
2. パッケージエディタを選択し、署名したい.appxファイルを選択する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1461400/243a6d75-e5ae-2ffd-81a0-698e04a8e0e3.png)
2. 発行元名を確認する
2. 管理者権限の Windows Powershell にて以下のコマンドを実行する
    ```powershell
    New-SelfSignedCertificate -Type Custom -Subject "<コピーした発行元名をここに貼り付ける>" -KeyUsage DigitalSignature -CertStoreLocation "Cert:\CurrentUser\My" -TextExtension @("2.5.29.37={text}1.3.6.1.5.5.7.3.3", "2.5.29.19={text}")
    ```
2. 以下のコマンドを実行して、署名リストを確認する
    ```powershell
    Set-Location Cert:\CurrentUser\My
    Get-ChildItem | Format-Table Subject, Thumbprint
    ```
2. Subjectの箇所に発行元名が書かれていると思うので、該当する行のThumbprintをコピーする
2. 以下のコマンドを実行して、証明書 (.pfx) をデスクトップに作成する
    ```powershell
    $password = ConvertTo-SecureString -String <任意のパスワードをここに入力する> -Force -AsPlainText
    Export-PfxCertificate -cert Cert:\CurrentUser\My\<コピーしたThumbprintをここに貼り付ける> -FilePath "%USERPROFILE%\Desktop\cert.pfx" -Password $password
    ```

### Appxの署名をする
3. [MSIX Packaging Tool](https://www.microsoft.com/store/productId/9N5LW3JBCXKF) を入手して起動する
3. パッケージエディタを選択し、署名したい.appxファイルを選択する
3. **署名の基本設定** を `証明書 (.pfx) を使用して署名する` に変更する
3. **証明書を参照** の `参照` ボタンから、先ほど作成した、証明書 (.pfx) を選択する
3. **パスワード** に 前項の手順7. で入力した任意のパスワードを記入する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1461400/59d46207-023c-c212-3ed3-63c224c50fe1.png)
3. `保存` → `いいえ、上げません` → `保存`（拡張子を.appxに変更すること）

### Appxをインストールする
4. 署名済み.appxファイルのプロパティを開く
4. `デジタル署名` タブを開き、`一覧にある署名` をクリックして `詳細` をクリック
4. `証明書の表示` をクリック
4. `証明書のインストール` をクリック
4. 保存場所を `ローカル コンピューター` に変更して次へ
4. `証明書をすべて次のストアに配置する` を選択
4. `証明書ストアの参照` をクリック
4. `信頼されたユーザー` を選択してOK
4. `次へ` → `完了`
4. 署名済み.appxファイルを開きインストール！！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1461400/022c78f8-9178-1d56-5a2a-015d0d60a6a5.png)

## まとめ
お疲れさまでした。

今回署名した LosslessCut は普通のインストーラーでも配布されてるので、こんな面倒なことやる必要なかったんですが、今回の手順が必要なソフトが他にもあるだろうと考えて、備忘録として書き上げました。
Microsoft Store で配布されているものと同じ形でインストールされるため、アップデートはここからできるという親切設計。普通のインストーラーより管理しやすく、利便性も爆上げです！


LosslessCut はUIがわかりやすく動作も快適なので、録画した番組などから必要な部分だけ動画にしたい場合などで、非常に強力なツールです。皆さんも利用してみては。

**この記事は Notion で作成・公開していたページからコピペして適宜再編集したものです。
記事の一部で見られる表記乱れなどはご了承ください。**

## 参考サイト
- [Install Unsigned APPX / UWP](https://forum.xda-developers.com/t/install-unsigned-appx-uwp.4178179/)
- [パッケージ署名用証明書を作成する - MSIX](https://learn.microsoft.com/ja-jp/windows/msix/package/create-certificate-package-signing)
smb.conf 3つのセクション[global]、[homes]、[printers]

●smb.confには3つの特別なセクションである[global]、[homes]、[printers]セクションと、個々の共有の設定を行う任意の名前のセクションがあります。

・[global]セクション
Samba全般に関わる設定を行うセクション。

・[homes]セクション
Linuxユーザのホームディレクトリを共有するためのセクション。たとえばユーザtestがログインすると、/home/testディレクトリを共有名testとして利用することが出来ます。[homes]セクションを利用することで、ユーザごとにホームディレクトリの共有の設定を行う必要がありません。

・[printers]セクション
共有プリンタに関わる設定を行うセクション。複数のプリンタがある場合でも、プリンタごとに共有の設定(任意の名前のセクションを作成)を行う必要がなく、一括で設定を行うことが出来る。

・任意の名前のセクション
任意の名前の共有を作成するためのセクション。セクション名が共有名となる。
AWS EC2インスタンスにApacheとMySQLをインストールする

はじめに


この記事では、AWSのEC2を設定する際にターミナルからApacheとMySQLをインストールするためのコマンドをよく忘れるので書き留めておきます。

この記事の対象者
- AWS EC2を設定できている方
- RDSでDBサーバーを構築できている方


開発環境
```
mac Ventura 13.4
Visual Studio Code
```


## AWS EC2インスタンスにApacheをインストールする

AWSのEC2インスタンスにApacheをインストールする手順を以下にまとめます。この手順は、すでにEC2インスタンスが起動しており、SSH接続が可能な状態から始めます。

1: パッケージの更新
まずは、インスタンス上のパッケージを最新の状態に更新します。これにより、最新のセキュリティパッチやバグ修正が適用されます。
```
sudo yum update -y
```

2: Apacheのインストール
次に、Apacheをインストールします。Apacheは、世界中で最も広く使用されているWebサーバーソフトウェアの一つです。

```
sudo yum -y install httpd
```

3: Apacheサービスの起動
Apacheをインストールしたら、Apacheサービスを起動します。
```
sudo systemctl start httpd.service
```

4: Apacheサービスのステータス確認
Apacheサービスが正常に起動したか確認します。

```
sudo systemctl status httpd.service
```

5: プロセスの確認
全てのプロセスを表示します。これにより、Apacheが正常に動作しているかを確認できます。

```
ps -axu
```

6: Apacheプロセスの確認
Apacheのプロセスだけを表示します。これにより、Apacheが正常に動作しているかをより詳細に確認できます。

```
ps -axu | grep httpd
```

7: Apacheサービスの自動起動設定
Apacheサービスをシステム起動時に自動的に起動するように設定します。これにより、インスタンスが再起動した場合でもApacheサービスが自動的に起動します。

```
sudo systemctl enable httpd.service
```

8: Apacheサービスの自動起動設定確認
Apacheサービスがシステム起動時に自動的に起動するように設定されているか確認します。

```
sudo systemctl is-enabled httpd.service
```

以上が、AWSのEC2インスタンスにApacheをインストールする手順となります。これにより、EC2インスタンスをWebサーバーとして使用することができます。


## AWS EC2インスタンスにMySQLをインストールする
AWSのEC2インスタンスにMySQLをインストールする手順を以下にまとめます。この手順は、すでにEC2インスタンスが起動しており、SSH接続が可能な状態から始めます。

1: SSH接続
まずは、SSHを使用してEC2インスタンスに接続します。以下のコマンドを使用します。

```
ssh -i <sshまでのパス> ec2-user@<IP4>
```

ここで、<sshまでのパス>にはSSH接続に使用する秘密鍵のパスを、<IP4>には接続先のEC2インスタンスのIPv4アドレスを指定します。

2: MySQLのインストール
次に、MySQLをインストールします。MySQLは、世界中で最も広く使用されているオープンソースのリレーショナルデータベース管理システム（RDBMS）の一つです。

```
sudo yum -y install mysql
```

3: MySQLへの接続
MySQLをインストールしたら、次にMySQLに接続します。以下のコマンドを使用します。

```
mysql -h <RDSエンドポイント> -u <指定したユーザー名> -p
```

ここで、<RDSエンドポイント>には接続先のRDSインスタンスのエンドポイントを、<指定したユーザー名>にはMySQLに接続するためのユーザー名を指定します。-pオプションを指定すると、コマンド実行後にパスワードの入力が求められます。

以上が、AWSのEC2インスタンスにMySQLをインストールし、MySQLに接続する手順となります。これにより、EC2インスタンスをデータベースサーバーとして使用することができます。
S3へのアップロードを多重度変えてやってみた

【結論】
　・オブジェクト数が1,000, 2,000ぐらいだとデフォルト値から変更する意味があまりない。
　・5,000オブジェクトだとある程度効果がみられた。

【検証結果】
　・1オブジェクトあたりのファイルサイズ：200KB
　・オブジェクト数：5,000
　・インスタンスタイプ：t2.micro
|max_concurrent_requests(デフォルト10)  |アップロード時間  |平均CPU使用率  |
|---|---|---|
|10  |51.108s  |60%  |
|20  |38.208s  |90%  |
|30  |33.077s  |94%  |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472386/4ebfe966-9c84-af57-80f7-7235a0d4f7b3.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472386/f0fda810-96e4-497e-c9e1-ff97a20e702c.png)

　・1オブジェクトあたりのファイルサイズ：500KB
　・オブジェクト数：2,000
　・インスタンスタイプ：t2.micro
|max_concurrent_requests(デフォルト10)  |アップロード時間  |平均CPU使用率  |
|---|---|---|
|10  |22.635s  |70%  |
|20  |20.073s  |79%  |
|30  |18.985s  |94%  |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472386/ab3f852f-2969-b1d8-bf05-af27ce891177.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472386/d2b45478-0cac-30f0-d0bc-7e14650017a1.png)

　・1オブジェクトあたりのファイルサイズ：1MB
　・オブジェクト数：1,000
　・インスタンスタイプ：t2.micro
|max_concurrent_requests(デフォルト10)  |アップロード時間  |平均CPU使用率  |
|---|---|---|
|10  |16.196s  |74%  |
|20  |18.817s  |93%  |
|30  |19.447s  |93%  |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472386/d6e4fda4-50d5-85f4-0f4d-b4b4fe52415b.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3472386/e6f3b01c-b23e-6168-d64c-7bf73d799b56.png)

【参考】
https://docs.aws.amazon.com/cli/latest/topic/s3-config.html
サクラエディタでフォント設定ができない

# 本記事の目的
仕事でサクラエディタ「sakura 2.2.0.1」を利用している中で、
メイリオフォントが使用できないことについて、簡単に調べたので以下にまとめております。


# 結論
下記サイトを発見し、内容を確認したところどうやら「sakura 2.2.0.1」では、プロポーショナルフォントは表示されない仕様であるとのことでした。
まじか。。。と思いました。

サイトはサクラエディタの作成者が起こしている掲示板で、過去の変更履歴に関するスレッドが並べられています。

<br>参考：https://sakura-editor.github.io/bbslog/sf/general/8108.html

# まとめ
仕事上仕方のないことですが、メイリオフォントやその他プロモーションフォントが使用できないのは少し残念でした。
リリース時期は2015年と、かなり古いバージョンですので使用しておられる方も少ないと思いますが同じような疑問を持った人に伝わればいいなと思います。

閲覧していただきありがとうございます。
【Java初心者】JRubyをJavaから呼び出す

# なぜこれが必要?

RubyをインストールできないPCでJavaはOKとなると、JRubyを使おうという選択肢が出てきます。ただ、起動するのに時間がかかる。

このため、以下のようなGUIを作ってJVM環境を作っておいて、Rubyスクリプトや、処理するためのデータを書き換えればいいのかなと思った。

今回は、このGUIの作り方は省略しますが、このJavaアプリからJRubyを呼び出し、スクリプトを実行させる方法だけやってみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/660337/101dc42f-19e1-7866-2a40-b06a69681db3.png)

# 必要なもの

* JRuby本体 jruby-complete-9.4.3.0.jar を使いました
* このサンプルでは log4j2を使用しています

GEM_HOMEは $HOME/JGEM にあることにしています。
このため、以下のような形で、事前に必要なRubyライブラリーを入れておきます
(ドキュメント生成を省略するため -N としています)

```bash
GEM_HOME=/home/XXXXX/JGEM java -jar jruby-complete-9.3.6.0.jar -S gem install ライブラリー名 -N
```

# 書いてみる

```java
     /**
     * JRubyスクリプトをJavaから呼び出す
     *
     * @param inputData 処理するデータ(文字)
     * @param script Rubyスクリプト本体
     * @return 処理結果
     */
    String execRubyScript(String inputData, String script) {
        // 処理結果を格納
        String out;
        // JRuby
        ScriptingContainer jruby = new ScriptingContainer();
        // 処理したいデータを標準入力として入れる
        logger.debug("スクリプトに渡す文字列:" + inputData);
        if (!inputData.isEmpty()) {
            String[] stdin = new String[1];
            stdin[0] = inputData;
            jruby.setArgv(stdin);
        }
        // この一行がないと二度目の実行に影響が出た
        jruby.terminate();

        // GEM_HOMEの場所を指定
        String gemHome = System.getProperty("user.home") + File.separator + "JGEM";
        jruby.callMethod(jruby.runScriptlet("Gem"), "use_paths", gemHome, gemHome);

        // スクリプトの実行
        try {
            Object runScriptlet = jruby.runScriptlet(script.strip());
            out = runScriptlet.toString();
            logger.debug("スクリプト実行結果:" + out);
        } catch (Exception e) {
            logger.debug("スクリプト実行失敗:" + e.getMessage());
            out = e.getMessage();
        }
        return out;
    }
```

# 追記

Rubyスクリプトに変数を渡す方法は、標準入力を使う方法以外もあると思います。

https://tyfkda.github.io/blog/2015/03/08/jruby-from-java.html
こちらのサイトを参考にさせていただきました。ありがとうございました。
プログラミング入門コース　3週目

<h2>1週間の振り返り</h2>
3週目の途中で講義動画などの差し替えがあったため1週間間が空いたので空いている時間で1週目～2週目までの復習に時間を充てることができたと思う。今回の3週目からより難易度がぐっと上がった感じがした。

<h2>1週間で学んだこと</h2>

- Python
　プロジェクトごとに仮想環境を構築できる(ほかのプロジェクト上には影響がない)
　必要なライブラリーはパッケージを追加インストールし使用する(使用する際の宣言は必要)

`実際の記載したコードの実行結果を確認する際にはprint()を使用`

```Python:hello.py
test = 'テスト'

print(test)

```

`リスト型とDictionary型はjavascriptと考え方は同じ`

```Python:hello.py
#リスト型
a=['りんご','なし','かき']
print(a[0]) # りんご

#Dictionary型

a= {}
a= {'name':'太郎','age':24}

# aの値は？ {'name': '太郎'、 'age': 24}
# a['name']の値は？ '太郎'

```

`関数の定義`

```Python:hello.py
f(x) = 2*x+3
y = f(2)

# JavaScriptの場合
function f(x) {
	return 2*x+3
}

# Pythonの場合
def f(x):
	return 2*x+3

```

`条件分岐と繰り返し`

```Python:hello.py
#条件分岐はif/elseで構成する
age = 25

if age > 20:
	print("成人です")
else:
	print("未成年です")

#繰り返しはfor/inで指定
#Pythonで繰り返しはリストの要素を1つずつ取り出す際に使用

fruits = ['りんご','なし','かき','みかん']

for fruit in fruits:
	print(fruit)

```

- ウェブスクレイピング(クローリング)
　<b>スクレイビングとクローリングの違い</b>
  - スクレイビング
  　特定のデータ構造から不要なデータを除去し、重要な情報を抽出することを指す。
　　Web上の情報をスクレイピングすることを、Webスクレイピングと呼ぶこともある。
    
  - クローリング
  　Web上で様々なサイトを巡回し情報の保存や複製など様々なことを行うことを指す。情報を集める目的として使われる。

`基本クローリング設定用`

```Python:dbprac.py
import requests
from bs4 import BeautifulSoup

URL = "https://movies.yahoo.co.jp/"
headers = {'User-Agent' : 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get(URL, headers=headers)
soup = BeautifulSoup(data.text, 'html.parser')
```

`よく使う基本コード`

```Python:dbprac.py
# 保存 - 例
doc = {'name':'太郎','age':21}
db.users.insert_one(doc)

# 一つ探す - 例
user = db.users.find_one({'name':'太郎'})

# 複数検索 - 例 (_id値を除いて出力)
all_users = list(db.users.find({},{'_id':False}))

# 変更 - 例
db.users.update_one({'name':'太郎'},{'$set':{'age':19}})

# 消去 - 例
db.users.delete_one({'name':'太郎'})
```

<h2>今週の課題提出</h2>

`課題`

```Python:billboard.py
import requests
from bs4 import BeautifulSoup

from pymongo import MongoClient

client = MongoClient('mongodb+srv://sparta:test@cluster0.pmuxf9z.mongodb.net/Cluster0?retryWrites=true&w=majority')
db = client.dbsparta

URL = "https://www.billboard-japan.com/charts/detail?a=hot100"

headers = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64)AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}
data = requests.get(URL, headers=headers)

soup = BeautifulSoup(data.text, 'html.parser')

musics = soup.select('#content2 > div > div.leftBox > table > tbody > tr')

for music in musics:
    rank = music.select_one('td > span')

    if rank is not None:
        rank = rank.text
        title = music.select_one('p.musuc_title').text.strip()
        artist = music.select_one('p.artist_name').text.strip()
        print(rank, title, artist)

```
<h2>来週の目標</h2>
3週目の復習を再度行い、5週目までに実際に作成したいWebサイトの企画書の記事を作成できるようにすることを目標に頑張っていきます。
【iTerm2】任意のキーで単語単位で移動できるようにする方法

## 単語単位の移動
単語単位の移動は`Esc + b`や`Esc + y`で可能ですが、連続で移動しようとすると`b`や`y`が入力されます。  
任意のキーかつ連続で移動できる設定方法を以下に記載します。

### 設定画面を開く
iTerm2を開いて`cmd + ,`で`Preferences`を開きます。  
そして`Preferences`の`Keys`を開きます。
![image220427_003532.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/db56ab7a-24ef-e29b-1a26-2f40049ca8f5.png)


### 設定する
`Keys`の中の`Key Bindings`を開き、右下の`+`をクリックします。
![image220427_003907.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/f0a5a062-5a8a-9228-0bc0-20da16120075.png)


`Keyboard Shortcut`に任意のキー、`Action`に`Send Escape Sequence`を選択、`Esc +`に`b`を入力し、`OK`をクリックします。
![image220427_004414.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/3cf3b380-5cf1-ecfb-5cfc-a3fb2754c529.png)


すると、任意のキー（上記の例では`cmd + ←`）で単語単位の移動ができるようになります。  
`Esc + f`は上記の`Esc +`の箇所に`y`を入力することで設定可能です。

### コマンドの先頭、末尾に移動する
コマンドの先頭、末尾に移動するにはデフォルトでは`ctl + a`、`ctl + e`です。
これも単語移動と同様に設定することで任意のキーで実行できます。

`Keyboard Shortcut`に任意のキー、`Action`に`Send Hex Code`を選択、入力欄に`0x01`を入力し、`OK`をクリックします。
![image220427_010727.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/7b9e33cf-bdce-47bd-0008-3eb4af9fba5f.png)


これで任意のキーでコマンドの先頭に移動できます。  
`ctl + e`は上記の入力欄に`0x05`を入力することで設定可能です。

## 単語単位の削除
デフォルトでは`Ctr + w`で単語削除ができますが、以下のように設定することで任意のキーで単語単位の削除ができます。  

### 設定方法
「設定画面を開く」までは移動と同様です。  
`Keyboard Shortcut`に任意のキー、`Action`に`Send Hex Code`を選択、入力欄に`0x1b 0x08`を入力し、`OK`をクリックします。
![image220427_012849.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/85e0c419-06e5-f81a-0e40-e38ed7d9f7a0.png)


上記の設定をすると任意のキーで単語単位の削除ができます。
JavaScriptで使用する便利な構文

## スプレッド構文
スプレッド構文は、for…ofブロックで処理できるオブジェクト（配列など）を個々の値に展開するための構文。
メソッドに引数を渡すとき（実引数で利用するとき）に便利。

例えば、Math.maxメソッドに配列をそのまま引き渡しても、結果はNaNになる

```
console.log(Math.max(15, -3, 78, 1));     // 結果 : 78 (値を個々に渡した場合)
console.log(Math.max([15, -3, 78, 1]));   // 結果 : NaN　（配列を渡した場合）
```
しかし、...を利用してスプレッド構文を使用すると、配列の中身が個々の値に分解され、正しい結果が返ってくるようになる

```
console.log(Math.max(...[15, -3, 78, 1])); 　　// 結果 : 78 (スプレッド構文を使用した場合)
```
もちろん変数を渡すことも可能
```
array = [15, -3, 78, 1];
console.log(Math.max(...array));　　　　　// 結果 : 78
```
日本国内の大学一覧 データセット

# 1. 動機
+ 就活サイト等で大学名の頭文字を入力すると候補が出てきてオートフィルできるのが便利だと思った
+ 自分でそういった機能を作ってみようとしたが成形されたデータセットがパッと見当たらなかった
+ Wikipediaを参考に大学名・読み仮名 (ひらがな) のみのリストを作った
    + [日本の国立大学一覧](https://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC%E3%81%AE%E5%9B%BD%E7%AB%8B%E5%A4%A7%E5%AD%A6%E4%B8%80%E8%A6%A7)
    + [日本の公立大学一覧](https://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC%E3%81%AE%E5%85%AC%E7%AB%8B%E5%A4%A7%E5%AD%A6%E4%B8%80%E8%A6%A7)
    + [日本の私立大学一覧](https://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC%E3%81%AE%E7%A7%81%E7%AB%8B%E5%A4%A7%E5%AD%A6%E4%B8%80%E8%A6%A7)

# 2. データセット (.csv)
+ [日本の国立大学一覧](https://drive.google.com/file/d/1WaCr10F7LHag_kU9wQo-Rc1FHU4CUNfU/view?usp=sharing)
+ [日本の公立大学一覧](https://drive.google.com/file/d/1qVcm9bagnZj63KxAoJ6-5KQDRC_OB_AF/view?usp=sharing)
+ [日本の私立大学一覧](https://drive.google.com/file/d/1G0P0P8zXd703MrcgKRwWVZreXSHL2O4n/view?usp=sharing)

# 3. 留意事項
+ 国立・公立に関しては読み仮名をチェックしましたが、万が一間違っていればご指摘いただきたいです。
+ 私立に関しては量も多いのでチェックできておりません。現在進行形で修正しておりますが、皆様からもご指摘いただきたく存じます。
+ 申し訳ございませんが、短期大学に関しては含めておりません。
大学院へ進学一直線だったワイがエンジニアになった理由

# 背景
この記事は。
> ワイ「念願の大学生になったで！」
> ワイ「大学に入ったからには、たくさん勉強して大学院に進学するんや!!」
> ワイ「会社なんて金の亡者！会社員なんて、金のために働く資本主義の犬でしかないわ！」
>
> 〜3年後〜
>
> ワイ「わんわん🐶」（就活中）

となったキッカケについて紹介します。

## 就職活動する前のわい

本題に入る前へ簡単に就職活動前の自分がどういった人物かというと。

> ワイ「理学こそ至高！人間が作った工学を学んで何が面白いわけがない」

という人物で、会社や技術に対しては。

> ワイ「企業は営利だけ求めてる！」

というイメージを持っていました。

そんな自分が大学でプログラミングと出会い、どのようなキッカケで就職に舵を切ったのか説明します。

### キッカケ1：技術の進歩の中身

> ワイ「理学こそ至高！人間が作った工学を学んで何が面白いわけがない」

と思っていた自分はプログラミングと出会い、ソフトウェアの歴史について調べてみることにしました。

> ワイ「はえ〜、RubyってPythonの影響を受けているのか...」

> ワイ「はえ〜、UnixってOSからMacとLinuxが生まれたんか...」

> ワイ「はえ〜、UnixってMulticsってOSの影響でできたんや...」

こんな感じで調べていくと、だんだんと、**技術の進歩は多くの人の積み重ねでできている**

ことを知りました。

### キッカケ2：人の集団の強さ

> ワイ「企業は営利だけ求めてる！」

と思っていた自分は在学中に放送していた大河ドラマ「青天を衝け」でスエズ運河がイギリスなどによる国営事業ではなく、[国際スエズ運河株式会社](https://ja.wikipedia.org/wiki/%E3%82%B9%E3%82%A8%E3%82%BA%E9%81%8B%E6%B2%B3%E4%BC%9A%E7%A4%BE)による民間企業であると知ります。

>　ワイ「はえ〜、スエズ運河って株式会社が作ったものなんやなぁ」
> ワイ「エジプトのピラミッドも東京タワーも人間が作ったもんなぁ」
> ワイ「最近だと、宇宙も株式会社の事業になりつつあるなぁ」
> ワイ「株式会社ってすごいんやなぁ」

そこで、**人は集団によって想像を超える力を発揮する**ことを知ります。

この辺りはもっと掘り下げると面白いのでいつか note とかで紹介します。

### キッカケ3：ソフトウェア開発の多様性

プログラミングが面白いと感じ、独学で勉強していくとコード管理ツールである GitHub と出会います。

この GitHub では OSS というのが存在することを知ります。

> ワイ「はえ〜、OSSってのは誰でも開発に参加することができるんか」
> ワイ「このOSSを自社風にアレンジしてサービス展開している企業もあるんか」
> ワイ「逆に自社サービスをOSSとして展開している企業もあるんか」
> ワイ「いろんな国や地域の人と人種、信条、性別、社会的身分などによって制限なく活動できるんか！」
> ワイ「はえ〜、素敵やん」

そこで、**古今東西、様々な人間によって技術は作られている**ことを知ります。

### 結果として

これらの要因で自分は、**IT技術の力で社会問題を解決し、より良い世界を未来に残す**という理念のもとエンジニアになろうと思いました。

現在は 23 卒のモバイルアプリエンジニアとして、日々精進しています。

また、なぜモバイルアプリを志望したのかについてはまたいつかどこかで書ければ良いなと考えています。

## 今後のキャリアプラン

まだ入社して 3 ヶ月しか経っていません。

<!-- textlint-disable -->
現状は、**iOSやAndroidなどのモバイルアプリで日本や世界の社会問題を解決する**を軸に仕事をして、趣味などプライベートで**技術者を助ける技術者**を軸に Qiita をはじめとして登壇やキャッチアップに参加していけたらなと考えています。
<!-- textlint-enable -->

## まとめ

長々と話してきましたが、自分が大学院を進学せずに就活へ舵を切ったキッカケは以下の 3 つに気づいたからでした。

- 技術の進歩の中身
- 人の集団の強さ
- ソフトウェア開発の多様性

**プログラミングが楽しい**

というのがあったからと考えています。

なので、皆さんもプログラミングを楽しみましょう！
DockerでPostgreSQLを立ててCSV取り込みするまで

Dockerを使って、PostgreSQLのサーバーを立て、テーブル作成とCSVでデータを取り込むまでをやってみました。
docker-composeの記事はそこそこあったけど、なぜかDockerfile単独のはあんまりなかったのと、DockerでCSVデータを取り込むまでの話はほとんどなかったので、そこまで含めたという点で、参考になる方がいましたら、という記事です。

# 概要
1. [postgres - Official Image | Docker Hub](https://hub.docker.com/_/postgres) を使って、自分用のサーバーを立てる
1. あらかじめ定義したDDLを使ってCREATE TABLE
1. あらかじめ用意した、テーブルごとのCSVを使ってインポート（COPYというPostgreSQL独自のコマンド）

ソースは下記にあります。

https://github.com/yo16/docker_postgresql

# 1. Dockerfileでサーバーを立てる
Dockerfileの全容は下記です。
```dockerfile:Dockerfile
FROM postgres:15.3

# 作成用の環境変数
ENV POSTGRES_DB mydatabase
ENV POSTGRES_USER myuser
ENV POSTGRES_PASSWORD mypassword

# クライアントで接続するための環境変数
ENV PGDATABASE mydatabase
ENV PGUSER myuser
ENV PGPASSWORD mypassword

EXPOSE 5432

COPY ./init/init.sh /docker-entrypoint-initdb.d/init.sh

COPY ./init/ /tmp/init_data/
```
使っているベースのイメージは [postgres:15.3](https://hub.docker.com/_/postgres) 。URLに使い方とか丁寧に書いてあります。

すごく気持ち悪いけど、同じような環境変数を立ててます。PostgreSQLサーバーを作るときの環境変数と、クライアントとしてそこへ接続するための環境変数。微妙に名前が違います。
クライアントとしての環境変数の説明は、[PostgreSQL 8.2.6文書 環境変数](https://www.postgresql.jp/document/8.2/html/libpq-envars.html) を参考に。

あと、`/docker-entrypoint-initdb.d/` へ初期実行してほしいshファイルをコピーするのと、それに使用するファイルを`/tmp/init_data/`にコピーしてます。
このフォルダに、`*.sql`か`*.sql.gz`か`*.sh`を置いておくと、実行される仕組みです。今回は、`init.sh` を置きました。
```sh:init.sh
psql -f /tmp/init_data/ddl.sql
psql -f /tmp/init_data/init_data_import.sql
```
SQLファイルを指定して、PostgreSQL(psql)を実行してるだけです。
環境変数で`PGDATABASE`とかを指定しているので、接続先やユーザー名は指定しなくてもつながります。もし環境変数で指定していないとすると、ここで接続先やらを書く必要があり、避けたかったのです。


# 2. あらかじめ定義したDDLを使ってCREATE TABLE
`init.sh`の１行目で実行する処理。
SQLの中身は`CREATE TABLE`してるだけです。
```sql:ddl.sql
CREATE TABLE IF NOT EXISTS books (
    book_id integer,
    book_name varchar(100),
    author_id integer
);

CREATE TABLE IF NOT EXISTS authors (
    author_id integer,
    author_name varchar(100)
);
```

# 3. あらかじめ用意した、テーブルごとのCSVを使ってインポート（COPYというPostgreSQL独自のコマンド）
`COPY`というクエリで、CSVをインポートします。
```sql:init_data_import.sql
COPY books FROM '/tmp/init_data/init_data_books.csv'
WITH (FORMAT csv, HEADER true);

COPY authors FROM '/tmp/init_data/init_data_authors.csv'
WITH (FORMAT csv, HEADER true);
```
独特だけど、複雑なことをやってるわけではないので、そういうモンなんだと理解します。
CSVファイルの置き場所は、`Dockerfile`で指定した場所です。

# 実行
準備は以上で、あとは実行。ポートを開けるのとVOLUMEでデータを永続化することを忘れずに。
```sh
# ビルド
docker build -t mypostgre . --no-cache
# イメージから実行
docker run -p 5432:5432 -v ./mypostgresql_data:/var/lib/postgresql/data mypostgre
```

無事実行出来たら、ホスト（Windows端末・DockerコマンドをたたいているPC）で、任意のDBクライアントソフトを使って、localhost:5432へ、指定したDB、ユーザー、パスワードで接続すれば確認OKです。プログラムからも接続できます。

# まとめ
テーブルを作成するだけでなく、初期データまでインポートする例でした。
Dockerfileでできればdocker-composeもできます。
VirtualBoxのディレクトリマウント

1. VB の画面で、「設定 ＞ 共有フォルダ」から設定を編集
    * フォルダのパスは Windows でマウントしたいディレクトリを選択
    * マウントポイントは「/share」を入力
    * 自動マウントにチェック
2. マシンの起動
3. VirtualBox Guest Additionsのインストール
メニューバーの「デバイス -> Guest Additions CD イメージの挿入...」を選択
4. 以下のコマンドを実行してGuest Additionsをインストール
    ```
    sudo apt-get update
    sudo apt-get upgrade
    cd /media/$USER/VBox_GAs_*
    sudo ./VBoxLinuxAdditions.run
    ```
    ※ インストールが途中で停止したらパッケージが足りていないので、以下のコマンドを実行
    ```
    sudo apt-get install dkms build-essential linux-headers-generic gcc make perl
    ```
5. 再起動
6. 以下のコマンドを実行して共有フォルダにアクセスできるようにする
    ```
    sudo adduser $USER vboxsf
    ```
7. 再起動
Error:SQL access control error: Insufficient privileges to operate on integration XXXX

SnowflakeでCOPY INTOコマンドを実行したときに、以下のエラーが出たときの原因と解決方法を書く。

```
Error:SQL access control error: Insufficient privileges to operate on integration XXXX
```

## 原因と解決方法

COPY INTOコマンドを実行するときに、以下のどちらかの権限付与が必要。

1. ステージのOWNERSHIP権限を持つロールにストレージ統合のUSAGE権限を付与。

https://docs.snowflake.com/ja/user-guide/data-load-s3-config-storage-integration#step-6-create-an-external-stage

2. COPY INTOコマンドを実行するロールにストレージ統合のUSAGE権限を付与。
   公式ドキュメントに書いていない？

ALTER STAGEでステージを操作する場合は1.の設定が必要なため、1.の設定をおすすめする。


## エラーが出る状況

ステージ作成時にはストレージ統合のUSAGE権限が必須なため、あとからステージのOWNERSHIP権限かストレージ統合のUSAGE権限をはく奪するとエラーが起きる。

具体的にエラーが起きるパターン

1. ステージのOWNERSHIP権限をFUTURE GRANTSで制御している
    FUTURE GRANTSでステージのOWNERSHIP権限を制御している場合、ステージ作成後にステージのOWNERSHIP権限が指定のロールに移動する。ステージのOWNERSHIP権限が付与されたロールがストレージ統合のUSAGE権限を持っていない場合、エラーが起きる。
Ubuntuでパッケージを手動で別のシステムに移行する

# モチベーション

プロキシ環境下で特定のパッケージが apt によりインストールできないという事象が発生したため、プロキシ環境外でインストールした同様のパッケージのデータを使用して、別のシステムでも使用できるようにできないかとかんがえた

# 手順

* PC-A: パッケージがインストールされているマシン
* PC-B: パッケージがまだインストールされていないマシン

1. まず PC-A で、必要なパッケージとその依存関係を全て .deb 形式でダウンロード
パッケージの依存関係を調べる
   ```
   apt-cache depends [パッケージ名]
   ```

   上記コマンドで表示されたパッケージリストのうち、PC-B でインストールできないものは、同様に .deb 形式でダウンロードする必要がある

   パッケージをダウンロード
   ```
   apt download [パッケージ名]
   ```

2. ダウンロードした .deb ファイルをストレージデバイスに保存

3. PC-B にストレージデバイスを接続し、.deb ファイルを移動

4. PC-B で、.deb ファイルを手動でインストール
依存関係のあるパッケージから順にインストールする

   ```
   sudo dpkg -i /path/to/package.deb
   ```
プロキシ環境で sudo apt update

1. 環境変数にプロキシが設定されているか確認
    ```
    $ echo $http_proxy
    $ echo $https_proxy
    ```
2. また、aptがプロキシを使用するように設定されているか確認
    ```
    /etc/apt/apt.conf.d/proxy.conf
    ```
    このファイルには以下のような内容が含まれているべきです。
    ```
    Acquire::http::Proxy "http://[username]:[password]@[proxy_url]:[proxy_port]";
    Acquire::https::Proxy "http://[username]:[password]@[proxy_url]:[proxy_port]";
    ```
3. 確認
    ```
    $ cat /etc/apt/apt.conf
    Acquire::http::Proxy "http://www***.jp:8080";
    Acquire::https::Proxy "http://www***.jp:8080";
    ```