OmniAICreator/Qiita-1.07M huggingface dataset:
Pythonを用いて、Cloud Storageにアップロードされた画像データをCloud Functionsで読み込む

# はじめに
本記事ではGoogle Cloud(GCP)のサービスであるCloud Storageにアップロードされた画像データを、Cloud Run Functions(旧Cloud Functions、以下CF)で読み込む方法について解説します。

# 前提
Cloud StorageをトリガーとしたCFが作成済みであること。

# 画像データの読み込み
Pythonを用いて、Cloud Storageで操作(アップロードなど)された画像データを、CF上で読み込みます。
- 画像データはバイト列として読み込んでいます
- client_blob.download_as_bytes()：コンテンツをバイト列で読み込む。download_as_string()も同じメソッドだがこちらは廃止済み

~~~python
from google.cloud import storage

@functions_framework.cloud_event
def hello_gcs(cloud_event):
    data = cloud_event.data

    # 操作されたCloud Storageのバケット名とファイル名の取得
    # この箇所を指定したいバケット名・ファイル名に変更することで、指定したファイルを読み取ることも可能です
    input_bucket_name = data["bucket"]
    input_file_name = data["name"]

    # Cloud Storageクライアントのインスタンス化
    client = storage.Client()

    # バケットとBlobの取得
    client_bucket = client.bucket(input_bucket_name)
    client_blob = client_bucket.blob(input_blob_name)

    # Blobからバイト列として画像データをダウンロード
    img_bytes = client_blob.download_as_bytes()

    # 以下でデータ加工
~~~


## データ加工
上記のコードの最後に、下記のコードを追加してください。
### 他のサーバーに送る場合
requestsなどを用いて他のサーバーに画像データを送信する場合
- 画像データをバイト列で読み込み→base64でデコード→文字列の順で変換しています。

~~~ python
import base64
    
# 他のサーバーに送る場合
img_base64 = base64.b64encode(img_bytes).decode("utf-8")
~~~

### Pillow(PIL)による画像処理
例：画像のリサイズ
~~~ python
from PIL import Image
import io

# PILで処理する場合
img = Image.open(io.BytesIO(img_bytes))

# 画像データの処理例
# 例: 画像のリサイズ
img_resized = img.resize((300, 300))
~~~

# 画像データの書き込み
今後記事を作成予定

# 参考
- Cloud Storageからの読み取り: https://cloud.google.com/appengine/docs/legacy/standard/python/googlecloudstorageclient/read-write-to-cloud-storage?hl=ja#reading_from
- download_as_bytesに関して: https://cloud.google.com/python/docs/reference/storage/latest/google.cloud.storage.blob.Blob#google_cloud_storage_blob_Blob_download_as_bytes
- サーバー間での画像データの扱い方: https://qiita.com/betio/items/4ba6424628304277c9e9
ナレッジ：業務で使ったコマンド

# Linuxコマンド
### SCPコマンド
SSHプロトコルを使用したコマンドらしい
```:ローカルサーバ→リモートサーバ
# scp [オプション] [EC2インスタンス接続の場合SSHキーのPEMファイルを参照するファイルパス] [コピー元のディレクトリ] [リモートサーバのユーザ]@[リモートサーバのIPorホスト名]:[コピー先のディレクトリ]
scp -r -i /hoge.pem ./local user-name@server-name:/hoge/piyo/
```

```:リモートサーバ→ローカルサーバ
# scp [オプション] [EC2インスタンス接続の場合SSHキーのPEMファイルを参照するファイルパス] [リモートサーバのユーザ]@[リモートサーバのIPorホスト名]:[コピー元のディレクトリ] [コピー先のディレクトリ]
scp -r -i /hoge.pem userid@server-name:/hoge/piyo/ ./local
```
-r：ディレクトリ内を再帰的にコピー
-i：ssh接続に使用する秘密鍵ファイルを指定
-p：ファイルのタイムスタンプ、パーミッションを保持

### 基本的なコマンドとそのオプション
```:catのオプション　wc -l：ファイルの行数を表示
cat [ファイル名] | wc -l
```
```:catのオプション　head -n5：最初の5行を表示
cat [ファイル名] | head -n5
```
```:ファイル名の一部からファイルを探す
locate [キーワード]
```

```:設定されているaliasを確認
alias
```
```:出力例
hoge='echo hoge'
```



```:おまけ：作業ログを保存してたスクリプト
local_log_dir="/hoge/log"
mkdir -p "${local_log_dir}"; \
file_name="$(date +%Y%m%d_%H%M).log"; \
echo "file_path: ${local_log_dir}/${file_name}"; \
script -fa >(awk '{print strftime("%F %T ") $0}{fflush() }'>> ${local_log_dir}/${file_name})
```



### SourceTreeターミナルコマンド
```:異なるブランチの任意のファイルの差分を表示
# git diff --exit-code [ブランチ1] [ブランチ2] -- [パス\対象ファイル名]
# echo $?

git diff --exit-code test release -- hoge\piyo.txt
echo $?
```
↑の`echo $?`は特殊変数`$?`にコマンドの実行結果の戻り値が自動的に格納されるためechoで表示している。diffの場合、「0」は差分なし
疑問：SourceTreeのターミナルでは-sのオプション使えるのかな？

### Cmdコマンド
```:検索パターンに一致するファイルの場所をファイルに出力
# WHERE [オプション] [ファイルパス] [検索する条件] [オプション] > [パス\出力ファイル名]
WHERE /R C:\hoge *.* /T > C:\hoge\piyo.txt
```
/R:指定したパターンに一致するファイルを、指定したディレクトリから開始して再帰的に検索し表示
/T:一致したすべてのファイルのサイズ、最終変更日および時刻を表示
※↑ファイルサイズがでかすぎる場合、容量の表示がバグるので注意！　詳細は後で調べよう…
初心者によるメモ（アルゴ式、「カードの並び替え（２）」）

## 問題
アルゴ式　ロジック実装初級

https://algo-method.com/tasks/59b21a013d03f7f6

## 考察
空のリストBを用意して、それを山Bとしてコードを書く。山Bを作る操作は`front`と`back`の変数を用意してそれぞれ`front=0`、`back=2*n-1`からスタートしてシュミレーションする。自分は`back=2*n-1`がわからなかった。
## ACコード
```py
n=int(input())

b = []

front = 0
back = 2*n -1
for i in range(n):
    b.append(front)
    b.append(back)
    front += 1
    back -= 1

for i in range(len(b)):
    print(b[i])
```
rex0220 計算式プラグイン スペース項目の概要印刷

[計算式プラグイン](https://qiita.com/rex0220/items/ae83c6d5ce50ff7a9a73)と[項目検証プラグイン](https://qiita.com/rex0220/items/be20fd879f5e1bc9e035)でスペース項目にレコードの概要を表示・印刷してみます。

# 概要

[rex0220 計算式プラグイン スペース項目にレコードの概要表示](https://qiita.com/rex0220/items/895e9bc6ac837b6cf7d7)で、表示した内容を印刷します。
項目検証プラグインで、スペース以外の項目を非表示にします。

- 画面表示と印刷表示

![2025-02-11_17h18_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/93136273-eda1-3714-7ec2-2f952adf4f5b.png)

- 印刷イメージ

![2025-02-11_17h20_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/baf868a2-0aff-4f7c-e5bb-8fd936c84808.png)

# 計算式プラグイン設定

印刷のため、横幅の設定を変更しています。
.custom-table { width: 100%;...

![2025-02-11_17h24_15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/3234d8d8-5a7d-bc17-4777-db9c45346293.png)

```.js
OPTION: 

// sp1:(sp1) SPACER
LET(
  label, TAGS_HTML(
    TAG("style", JOIN(ARRAY(
        ".custom-table { width: 100%; table-layout: auto; border-collapse: collapse; }",
        ".custom-table td { border: 1px solid darkgray; padding: 4px 6px; }",
        ".custom-table td:first-child { white-space: nowrap; color: blue; }",
        ".custom-table td:last-child { word-break: break-word; }",
    ), NEWLINE(),1)),
    TAG("div", "概要")
  ),
  table, TAGS_HTML(
  TAG("table", 
    ATTR("class", "custom-table "),
    STYLE("border-collapse", "collapse"),
    TAG("tr", TAG("td", "顧客名"), TAG("td", 顧客名)),
    TAG("tr", TAG("td", "部署名"), TAG("td", 部署名)),
    TAG("tr", TAG("td", "担当者名"), TAG("td", 担当者名)),
    TAG("tr", TAG("td", "案件名"), TAG("td", 案件名)),
    TAG("tr", TAG("td", "確度"), TAG("td", 確度)),
    TAG("tr", TAG("td", "初回商談日"), TAG("td", DATE_FORMAT(初回商談日),
      IF(NOT(初回商談日),STYLE("background-color:#f9bba5;")))),
    TAG("tr", TAG("td", "提案プラン"), TAG("td", 提案プラン)),
    TAG("tr", TAG("td", "オプション"), TAG("td", オプション)),
    TAG("tr", TAG("td", "商談担当者"), TAG("td", 商談担当者)),
    TAG("tr", TAG("td", "プラン費用"), TAG("td", FIXED(プラン費用),
      STYLE("text-align:right;"), IF(プラン費用>1000000,STYLE("color:red;")))),
    TAG("tr", TAG("td", "オプション費用"), TAG("td", FIXED(オプション費用),
      STYLE("text-align:right;"), IF(オプション費用>1000000,STYLE("color:red;")))),
    TAG("tr", TAG("td", "合計費用"), TAG("td", FIXED(合計費用),
      STYLE("text-align:right;"), IF(合計費用>1000000,STYLE("color:red;")))),
    TAG("tr", TAG("td", "受注予定日"), TAG("td", DATE_FORMAT(受注予定日))),
    TAG("tr", TAG("td", "詳細"), TAG("td", 詳細)),
  )),
  ARRAY(label,"",table,"")
)
```

# 項目検証プラグイン設定

スペース項目以外の項目を印刷時は非表示にします。
EV_INFO("cate")="print"

※非表示にする項目欄をまとめて選択して、「EV_INFO("cate")="print"」をコピペできます。


![2025-02-11_17h26_54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/876cb203-9767-364d-c443-9c40379335b5.png)
【統計検定２級】ラスパイレス指数とパーシェ指数の式の覚え方

## はじめに
統計検定２級に出てくるラスパイレス指数とパーシェ指数の式がややこしく、どうやって覚えれば良いか、まとめてみました。

## 指数(index)とは
一般に、多数の同種のデータを比較するために、ある値を基準にして他の値を基準値に対する比で表したもの。

## ラスパイレス指数(Laspeyres index)とは
基準年の購入数量を固定した場合の基準年と比較年の購入金額の比。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387980/f35a0174-0134-05a8-3706-3ba77c4448ba.png)

式は下記の通り。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387980/a4d50599-9972-793f-bf06-25b83cd592f0.png)

## ラスパイレス(Laspeyres)とは
ドイツの経済学者エティエンヌ・ラスパイレス(Etienne Laspeyres)(1834～1913)氏の名前。

## パーシェ指数(Paasche index)とは
比較年の購入数量を固定した場合の基準年と比較年の購入金額の比。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387980/6dc3464f-d1e6-25f9-1100-7029f5528765.png)

式は下記の通り。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387980/ee214f8c-d471-d12d-7bc3-fa072990b76d.png)

## パーシェ(Paasche)とは
ドイツの経済学者ハーマン・パーシェ(Hermann Paasche)(1851―1925)氏の名前。

## 覚え方
両方の式をそのまま覚えようとすると頭が混乱してしまうので、覚え方として、まずラスパイレス指数の式だけ覚えておいて、パーシェ指数の式はラスパイレス指数の分母と分子を入れ替えて、あとは全部基準と比較を入れ替える、とすれば覚えやすいのでは、と思っています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387980/37e176ee-f1e2-4f8f-8385-98047c0f0d04.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387980/93e3f893-0635-9a6c-2a61-5f521d61c179.png)

## おわりに
ここまで読んでいただきありがとうございます！
GitHub ActionsでFirebaseにデプロイしたら画面が真っ白になった

# はじめに
GitHub Actionsを利用してFirebase Hostingに学習記録アプリのCI/CDを行いました。
デプロイ自体は完了したのですが、画面が真っ白の状態になり、解決に手間取ったため、備忘録を兼ねてまとめます。

# 事象
## 発端
- GitHub Actionsでのデプロイ時、`Directory 'dist' for Hosting does not exist.`というエラーが出て失敗する
- このエラーは下記の記事で解消

https://qiita.com/AK_React/items/05957ddc311d3bc14c28

## エラーは解消したが……
- GitHub Actionsでのdeploy自体は成功
- しかし画面は真っ白で何も表示されず
- 開発者ツールを開くと以下のエラーが表示される

```
MIME タイプ (“text/jsx”) が許可されていないため、“https://example.web.app/src/main.jsx” からのモジュールの読み込みがブロックされました。
```

これに対して、下記の記事を参考に解消を試みましたが、うまくいきませんでした。

https://qiita.com/bignyokki/items/95093e042a09ba68cf56

とりあえず、main.jsxがそのままデプロイされていることが原因のエラーであることは共通しているはずですが、記事に従ってjob間でdistディレクトリを共有してもエラーが解消せず、ハマりました。

# 解決方法
私が取った解決法は以下の通りです。

1. `firebase.json`の記述を修正する
1. buildとdeployを同じjobにする

## 1. `firebase.json`の記述を修正する
`firebase.json`の記述を見直したところ、修正が誤っていたことが判明しました。

```json:firebase.json
{
  "hosting": {
    "public":".",  //正しくは"source":"."
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  }
}
```

この記述ではプロジェクトルートが公開ディレクトリになってしまうため、余計なファイルまでアップロードされてしまい、結果としていつまでもエラーが解消しなかったのでした。

ところで、この部分を変更したのは[発端](#発端)で発生したエラー解消のためでしたが、そもそもリポジトリにdistディレクトリがなかったとしても、build時に生成されているはずです。
しかし、job間での成果物の共有がなされていなかったため、deploy時にFirebase Hostingにアップロードがされず、大元のエラーが出たと考えられます。

なので、buildとdeployを同じjobにするか、成果物の共有がなされていれば、本来この部分の変更は必要ないはずです。

そのため、`firebase.json`の記述をデフォルトに戻します。

```diff_json:firebase.json
{
  "hosting": {
-    "public":".",
+    "public": "dist",   
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ]
  }
}
```

## 2. buildとdeployを同じjobにする
前項の修正でエラー自体は解決するのですが、ワークフローを見直しているとbuidとdeployで重複している部分があるのがどうしても気になりました。
そのため、buildとdeployで別れていたjobをこの機会に統合することにしました。
これにより`artifact`を使用する意味もなくなったため、その部分も除去します。

GitHub Actionsでは1つのJobごとに1つのRunner(=コンテナ実行環境)が用意される、ということなので、buidとdeployでjobを分ける必要性が薄いのでは？という気がしたのも一因です。

ただ、失敗したときのre-runを考慮するとbuildとdeployは分けたほうがいいようなので、この判断が正しいのかどうかは自信ないです。どなたか詳しい方教えてください。

https://zenn.dev/hsaki/articles/github-actions-component#job%E3%81%AE%E5%A0%B4%E5%90%88

# おわりに
直接の原因は非常にしょうもない記述ミスなのですが、そこにたどり着くまでの過程の調べ物でGitHub ActionsやFirebase Hostingの仕組みについて少し理解が進んだので、元は取れたかな、という気はしています。
統計検定1級　2024　統計応用(理工学)　問5　解説

![img20250210_09200848.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576811/cfb98995-064d-3050-c9a0-80348daf7a17.jpeg)
![img20250210_09203661.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3576811/57db2971-6ad9-480d-5896-df5b78fc1d85.jpeg)
VirtulBOXのNAT仮想サーバへSSHする備忘録

## 前置き

VirtulBOXを用いて、仮想サーバを立てて検証および作業する方は多いと思います。
ネットワーク設定によって、NAT状態にしている仮想サーバへVSCODEからSSH接続する際の手順の備忘録です。

## 手順１：VirtualBox でポートフォワーディングを設定

下記は、VirtulBox側で設定する一連の設定の流れです。
```
1.VirtualBox を開く

2.左のリストから "接続したいサーバを選択" の仮想マシンを選択

3.[設定] → [ネットワーク] を開く

4.[アダプター1] タブを確認し、"割り当て" が NAT になっていることを確認

5.右下の [詳細] をクリック または[高度(D)]をクリック

6.[ポートフォワーディング] ボタンをクリック

7.新しいルールを追加

名前: SSH
プロトコル: TCP
ホストIP: 127.0.0.1 (省略可)
ホストポート: 2222（例: 任意の未使用ポート）
ゲストIP: 空欄 (デフォルトでOK)
ゲストポート: 22（SSHのデフォルトポート）

8.[OK] をクリックして保存
```
<参考画像>

工程2:起動したいサーバ選択

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596363/bcfdd87b-200e-2e78-622c-876134d34c8d.png)

工程3,4:NW設定の画面を出す

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596363/a2c25da0-70bc-b799-35ca-6d7fcbc70c19.png)

工程5：ポートフォワーディング画面を出す

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596363/cef3cc33-d066-f0ee-9b2e-ca0a1b123ce8.png)

工程6,7:ポートフォワーディングを押し、[+]を押して設定画面を開く

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596363/3a49de78-31ee-a03b-08c2-3bc3801f4ab4.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596363/6d20ebc2-9ae7-3fce-cce2-fec12e514448.png)

:::note info
**ポイント**
Host に設定する名称は 自由に決められる。
HostName には 実際の接続先（今回の場合は 127.0.0.1）。
Port は VirtualBox のポートフォワーディングで設定したポート（2222 など）。
User には 仮想マシンの Linux ユーザー名（your-username 部分）。

:::

## 接続先サーバ側で設定

1.仮想マシンを起動し、ターミナルで以下を実行して SSH サーバが動作しているか確認してください。
```
sudo systemctl status sshd
```
2.もし "active (running)" でなければ、以下を実行

```
sudo systemctl start sshd
sudo systemctl enable sshd
```
3.ファイアウォールを確認
```
sudo firewall-cmd --permanent --add-service=ssh
sudo firewall-cmd --reload
```

## VS CODEでSSH接続
1.VS Code に SSH 拡張機能をインストール
```
拡張機能 (Remote - SSH) をインストール
```
2.VS Code の SSH 設定ファイルを編集
```
~/.ssh/config に以下を追加~/.ssh/config に以下を追加~/.ssh/config に以下を追加~/.ssh/config に以下を追加
```

```
Host centos8-vm # 接続時に識別するための任意の名前 接続先の実際のホスト名でなくてOK
    HostName 127.0.0.1
    User your-username # 接続先のユーザ名
    Port 2222　# ポートフォワーディングで設定したポート番号
    IdentityFile ~/.ssh/id_rsa  # 鍵認証を使う場合 <-無くてもOK
```
3.VS Code で SSH 接続
```
Ctrl + Shift + P を押して Remote-SSH: Connect to Host... を選択
centos8-vm を選択
```
:::note info
**ポイント**
your-username は、CentOS のログインユーザー名に置き換えてください。
id_rsa の鍵がない場合、ssh-keygen -t rsa で作成し、仮想マシンに ssh-copy-id でコピー。
Port 2222 はポートフォワーディングの設定に合わせる。
::::::::::::

:::note info
**ポイント**
これは、**ssh -p 2222 your-username@127.0.0.1** と同じ意味になりますが、エイリアスを使うことで 覚えやすく簡単に接続できる ようになります。


Host に設定する名称は 自由に決められる。
HostName には 実際の接続先（今回の場合は 127.0.0.1）。
Port は VirtualBox のポートフォワーディングで設定したポート（2222 など）。
User には 仮想マシンの Linux ユーザー名（your-username 部分）。
:::

<参考画像>
.sshファイル書き込み
configファイルを右クリックして**プログラムから開く**を押して手動で書き込みが可能
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/596363/574a2225-b721-581c-5d4b-7d2a9fbed9b8.png)

以上で作業は終了です。
お疲れ様でした！
SQLite(sqlite3コマンド)の実行結果をJSONにしてPowerShellで扱う

この記事は、先日書いた以下の記事のSQLite(sqlite3コマンド)版です。

https://qiita.com/noobow/items/25efab18a2836abb668d

# まずは結果から
サンプルスクリプトは以下のGistに格納しました。
https://gist.github.com/noobow34/e3e98b60e33336dd7b62765ab06e4bbc

https://gist.github.com/noobow34/e3e98b60e33336dd7b62765ab06e4bbc

```powershell
PS C:\> .\Get-SQLiteJsonData.ps1
-----------行を指定して使用する-----------

SEQ SEQ2
--- ----
  2    3
-----------行と列を指定して使用する-----------
3
-----------ループで処理する場合-----------
1:2
2:3
3:4
4:5
5:6
6:7
7:8
8:9
9:10
10:11
```

# やりかた
## 扱うデータ
Oracle版で使ったのと同じ結果になるSQLをChatGPTに作ってもらいました。
```sql
WITH RECURSIVE Numbers AS (
    SELECT 1 AS SEQ
    UNION ALL
    SELECT SEQ + 1 FROM Numbers WHERE SEQ < 10
)
SELECT SEQ, SEQ + 1 AS SEQ2 FROM Numbers;
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149348/f533228b-5b6a-7dc0-fd7f-3b1238172342.png)
```powershell
PS C:\>  @"
>> WITH RECURSIVE Numbers AS (
>>     SELECT 1 AS SEQ
>>     UNION ALL
>>     SELECT SEQ + 1 FROM Numbers WHERE SEQ < 10
>> )
>> SELECT SEQ, SEQ + 1 AS SEQ2 FROM Numbers;
>> "@ | sqlite3
1|2
2|3
3|4
4|5
5|6
6|7
7|8
8|9
9|10
10|11
```


## sqlite3の出力をJSONにする
Oracle版では出力をXMLとしていましたが、SQLiteでは普段使っているSQLをそのまま使いつつ（XMLを出力するように書き換えをせずに）XMLを出力することはできないようだったので、代わりにJSONを出力することにしました。JSONであれば簡単に出力することができ、かつPowerShellでオブジェクト化することもできます。
sqlite3コマンドで出力をJSONにするのは`.monde json`とするだけです。また、sqlite3コマンドではSQL*Plusの様に余計なメッセージは出ないので、特にオプションを加える必要はありません。
```powershell
PS C:\> @"
>> .mode json
>> WITH RECURSIVE Numbers AS (
>>      SELECT 1 AS SEQ
>>      UNION ALL
>>      SELECT SEQ + 1 FROM Numbers WHERE SEQ < 10
>>  )
>>  SELECT SEQ, SEQ + 1 AS SEQ2 FROM Numbers;
>> "@ | sqlite3
[{"SEQ":1,"SEQ2":2},
{"SEQ":2,"SEQ2":3},
{"SEQ":3,"SEQ2":4},
{"SEQ":4,"SEQ2":5},
{"SEQ":5,"SEQ2":6},
{"SEQ":6,"SEQ2":7},
{"SEQ":7,"SEQ2":8},
{"SEQ":8,"SEQ2":9},
{"SEQ":9,"SEQ2":10},
{"SEQ":10,"SEQ2":11}]
```

## JSONをPowerShellの変数にする
PowerShellでは`ConvertFrom-Json`コマンドでJSONをオブジェクト化することができますが、今回のsqlite3コマンドの結果の改行が含まれるJSONではエラーになってしまうようだったので`Out-String -NoNewline`で改行なしにしてから`ConvertFrom-Json`コマンドに渡しました。
結果、以下の様にすれば`$json`変数にjsonがオブジェクト化されて格納され、属性名でアクセスできるようになります。
```powershell
$json = $execSql | sqlite3 databasefile.db | Out-String -NoNewline | ConvertFrom-Json
```

## JSON変数の扱い方
`$json`自体が配列になっているので`$json[0]`の様にインデックスを指定して扱います。あとは、`$json[0].SEQ`の様に属性名をそのままプロパティとして扱えます。
個人的備忘録：シンプルなコマンドでAPIを試せるcurlコマンドを紹介し、実際に使ってみた

## はじめに

今回は`curl`コマンドについて紹介します。***`curl`はWeb上のデータを取得したり、APIを利用する際に便利なコマンドラインツールです。***

普段の開発やシステム運用で活用できるため、その基本的な使い方を学ぶことは非常に有益です。

## 書こうと思ったきっかけ

最近、APIのテストをする機会が増え、手軽にリクエストを送る方法を探していました。

その中で`curl`コマンドの便利さを再認識したため、基本的な使い方や実際の使用例をまとめておこうと思いました。

## curlコマンドについて

`curl`は「Client URL」の略で、データの転送を行うためのコマンドラインツールです。

:::note info
**HTTP/HTTPSをはじめ、FTP、SFTPなどのプロトコルにも対応しています。**
:::

特にAPIのリクエストを簡単に送れることが特徴です。

### 基本的な使い方
1. **Webページの取得**
   ```sh
   curl https://www.example.com
   ```
   指定したURLのページを取得し、ターミナルに表示します。

   **実際の画面**

   ![Screenshot 2025-02-11 at 16.21.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/048986c2-61bd-658b-5d26-c5b0ba8da057.png)

2. **ファイルのダウンロード**
   ```sh
   curl -O https://www.example.com/sample.txt
   ```
   `-O`オプションを使うと、ファイル名を保持したままダウンロードできます。

   **実際の画面**

   ![Screenshot 2025-02-11 at 16.29.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/98abde0d-108f-6e7c-1b33-83d02662cb06.png)

3. **HTTPヘッダーの確認**
   ```sh
   curl -I https://www.example.com
   ```
   `-I`オプションを指定すると、レスポンスのヘッダー情報のみを表示します。

   **実際の画面**

   ![Screenshot 2025-02-11 at 16.31.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/744d829d-2092-9cbe-fb17-1630451b70f0.png)

4. **APIリクエストの送信**
   ```sh
   curl -X GET "https://api.example.com/users" -H "Authorization: Bearer YOUR_TOKEN"
   ```
   `-X`でリクエストメソッドを指定し、`-H`でヘッダー情報を追加できます。

## 実際に使ってみた

実際にAPIをリクエストして、レスポンスを取得してみました。

```sh
curl -X POST "https://reqres.in/api/login" \
     -H "Content-Type: application/json" \
     -d '{"email":"eve.holt@reqres.in", "password":"cityslicka"}'
```

このコマンドを実行すると、サーバーからJSON形式のレスポンスが返ってきました。

```json
{"token":"xxx"}%
```

**実際の画面**

![Screenshot 2025-02-11 at 16.49.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3662571/de1c22a4-2a8d-b783-992d-3ced19d70885.png)

このように、簡単なコマンドでAPIとやり取りできるため、非常に便利です。

## まとめ

`curl`コマンドは、Webページの取得、ファイルのダウンロード、APIリクエストなど、さまざまな用途で利用できます。

:::note warn
**特にAPIのテストやデバッグには欠かせないツールです。**
:::

今後も実践を重ねて、より便利な使い方を探していこうと思います。
【Git/GitHub】リポジトリ名変更後に remote の URL を(半)自動更新したい

# はじめに

Github では組織名やリポジトリ名を変更すると自動的に URL のリダイレクトが設定されますが，(特に組織名の変更では) 元の URL が空くためローカルブランチの接続先 URL は早いうち更新したほうが安全です．

これが案外手間だったので，submodule に渡って再帰的に自動更新してくれるシェルスクリプト (Bash) を用意してみました．

:::note
HTTPS 接続のリポジトリが前提です．
:::

# 方針
`curl` を使うと任意の URL のリダイレクト先を取得できるので，`xargs` 等ですべての submodule について適当に更新してやればよいです．

# 完成品
https://github.com/uni-kakurenbo/git-redirect-resolver

## インストール
```bash
# required only if shc is not installed
sudo apt-get install shc

git clone https://github.com/uni-kakurenbo/git-redirect-resolver.git
sudo shc -v -r -f ./git-redirect-resolver/git-resolve.sh -o /bin/git-resolve
```

## 実行

```bash
git-resolve [--submodule] [--recursive]
```

- `--submodule`: sumodule についても更新します (`--recursive` が指定されている場合，無視されます)
- `--recursive`: すべての sumodule について，再帰的に更新します

## ソースコード
https://github.com/uni-kakurenbo/git-redirect-resolver/blob/main/git-resolve.sh

# おわりに
submodule の数次第では結構時間がかかります．
まあ，計画的に命名しましょうということです．(はい...)

なお `git-resolve` という名前は何かしら衝突しそうなので，被ったら適当に変更してください．

# 参考文献

https://git-scm.com/docs/git-submodule

https://qiita.com/niwasawa/items/bb42a35b541c650dd345
snap版Thunderbirdの代わりにdeb版Thunderbirdをインストール

## `~/Download`に`thunderbird.tmp`フォルダがある？

Ubuntu 24にしてから、`~/Download`に`thunderbird.tmp`を見つけるようになりました。調べてみたところ、Ubuntu 22だと、deb版だったThunderbirdが、24からはsnap版に統一され、snap版Thunderbirdがこのフォルダを作るのだということがわかりました。

### なんで？

snap版のアプリケーションは、ライブラリのバージョン衝突や依存パッケージの不足を解消する目的で推し進められているらしく、そのなかにはセキュリティの強化も含まれているようです。
そのセキュリティの強化の一環として、アプリケーションは**ホームディレクトリの外にアクセスさせない**という措置が含まれているのだそうで、つまりsnap版のアプリケーションは`/tmp`にアクセスできず、かわりに作業ディレクトリとして`~/Downloads`にファイルを置いているらしいです。

#### 非難轟々っぽい

`ubuntu snap tmp`や`Thunderbird snap tmp`とかで検索したらわかりますが、`~/Downloads`に`thunderbird.tmp`を置く措置については、不満な人が多いようです。セキュリティ上の有意性は認めつつも「なんで`~/Downloads`やねん」と。せめてどっか別のところにできるようにしてという意見を複数読みましたが、どうもうまく行く方法はないみたい。

ChatGPTは「環境変数（TMPDIR）を、`~/tmp`あたりに変えたらうまく行くかも」と提案してくれましたが、ぼくにおいてはうまくいきませんでした。

snapも特にsnap全体で作業ディレクトリをどうにかすることはできないみたいで、またThunderbirdとしても、作業ディレクトリをあとから変更する方法は提供されていないようでした。

とはいっても、`~/Downloads`は、ぼくの運用としては、「いま作業している途中のもの以外は削除する。すなわち『空っぽ』が常態」なので、ここにフォルダが作られると認知負荷が高くなって困ります。

## 対処法

- 「OSの環境変数を変更」は、うまく行かない
- snap及びsnap版Thunderbirdには環境変数を外からいじる手段は用意されていないっぽい

ということで、調べ続けてみると、どうも2択のようでした。

- `thunderbird.tmp` を非表示にする
- deb版Thunderbirdをインストールする

これらの情報は、[Get Rid of thunderbird.tmp in Ubuntu 24.04 Downloads Folder](https://ubuntuhandbook.org/index.php/2024/03/thunderbird-tmp-ubuntu/)と、[How to Install Thunderbird as DEB (Not Snap) in Ubuntu 24.04](https://ubuntuhandbook.org/index.php/2024/03/install-thunderbird-deb-ubuntu-2404/)にあったものです。

## `thunderbird.tmp` を非表示に

いちばんシンプルな手法です。

```bash
echo "thunderbird.tmp" > ~/Downloads/.hidden
```
`.hidden`ファイルに、`thunderbird.tmp`と書くだけです。

でも、ぼくは不可視ファイルを表示して作業しているので、無意味っちゅうか、`.hidden`ファイルが増える、というなんとも言い難い事態になります。

## deb版Thunderbirdのインストール（これで解決）

Mozillaのひとたちが、提供してくれているPPAのリポジトリからdeb版がインストールできるのだそうです（ありがとう、Mozilla！ 寄付していてよかった）。

すでにThunderbirdを運用している場合は、`snap/thunderbird/common/`あたりにいまのメールのデータがあるので、これを退避させます（このフォルダの名前は人によって違うので、Thunderbirdのデータの引っ越し等を検索して間違いのないようにします）。

### snap版の削除
```bash
sudo snap remove --purge thunderbird
```

### ラッパも削除
ふつうに`sudo apt install thunderbird`すると、snapでインストールしようとするので、削除する。

```bash
sudo apt remove thunderbird
```

### リポジトリの追加
```bash
sudo add-apt-repository ppa:mozillateam/ppa
```

#### リポジトリの優先措置
以下のファイルを作成する。

```bash
sudo vi /etc/apt/preferences.d/mozillateamppa
```
内容は次の通り。

```ini:/etc/apt/preferences.d/mozillateamppa
Package: thunderbird*
Pin: release o=LP-PPA-mozillateam
Pin-Priority: 1001
Package: thunderbird*
Pin: release o=Ubuntu
Pin-Priority: -1
```

### インストール
```
sudo apt update
sudo apt install thunderbird
```

これで、非snap版のThunderbirdをインストールでき、`~/Downloads`もきれいにになりました。

snapの理想については、賛成はしたいんですが、まあ、もうちょっと柔軟な設定の余地を残してほしいものです。
VTKに対する包括的な理解


#### この記事の目的
VTKの環境構築が非常に大変だった事から，なんでこんなもの使わなくてはいけないのかと思い，VTKについて調べた事を纏めた．

#### VTKの概要
3Dグラフィックスの可視化や画像処理などを行うためのオープンソースのソフトウェア．
Kitware社が中心となって開発を進めており，C++で書かれたライブラリとして提供されている．  
研究活動から産業応用まで幅広い分野で利用されている．

#### VTKにできること，利用シーン
 科学可視化: 流体解析や分子構造などのデータセットを3Dで可視化することができる  
 画像処理: 画像のフィルタリングやセグメンテーションなど，複雑な画像処理パイプラインを構築できる  
 インタラクティブな表示: ユーザーがマウスやキーボードを使って対話的にモデルを回転，拡大縮小できる  
 マルチプラットフォーム対応: Windows，macOS，Linuxなど主要なOSで同等の機能が利用できる  
 プログラミング言語バインディング: PythonやJavaなど複数の言語から呼び出すことが可能である


#### VTKにしかできないこと
VTK独自のパイプライン設計が強力で，巨大データセットでも分割して効率よくレンダリングできる．
並列分散処理を行うためのMPI連携が標準サポートされており，数千万〜数億単位の要素を持つデータでも高速に可視化できる．  
最新のGPU機能を活用したボリュームレンダリングや，豊富な汎用フィルタ群が標準で揃っているため，追加のプラグインなしで多種多様な可視化手法を試すことができる．

#### VTKの強み
豊富な機能と拡張性: ゼロからグラフィックスAPIを扱うより手軽に高品質な可視化が実現できる  
コミュニティの活発さ: 世界中の研究者や開発者が参加するMLやGitLabリポジトリがあり，新機能やバグ修正のペースが速い  
Pythonとの親和性: Pythonバインディングにより，科学技術計算のエコシステム(PyTorch，NumPyなど)との連携が容易  
オープンソース: 学習コストはかかるがソースコードが公開されているため，カスタマイズやデバッグが柔軟

#### VTKの弱み
習熟難易度が高い: マニュアルやチュートリアルは充実しているが，自由度の高さゆえ学習コスト高い  
コンパイル時間が長い: 多機能なライブラリであり，ビルドに時間がかかる場合がある  
依存関係が複雑: 画像処理や並列処理など追加モジュールを有効にすると，多数の外部ライブラリが必要
GUI構築のサポートは限定的: 画面上のウィジェット類など，UI部分はほかのフレームワークとの併用が前提になりがち

#### なぜVTKのビルドはうまく行かないのか
VTKは機能が多いぶん，オプションや依存ライブラリも多岐にわたる．
CMakeでプロジェクトを構成する際に，環境によって設定が食い違ったりバージョンの不一致が起こることがある．  
また，GPUアクセラレーションやPythonバインディングなどビルド時に有効にするオプションが多く，設定を誤るとリンクエラーやコンパイルエラーが発生しやすい．

#### VTKの互換製品
ParaView: VTKをベースにKitwareが開発している可視化ソフトウェア．大規模データを対話的に扱う場合に有用  
ITK: 画像処理に特化したライブラリで，VTKとの連携も想定されている  
OpenGL/DirectX: グラフィックスAPIとして直接使う方法．フレームワークを自作したい場合は選択肢になるが，VTKほど高レベルな機能は提供されない  
VTK-m: GPUなどの並列環境に特化したVTKの派生プロジェクト．大規模データをより高速に処理するためのアプローチを提供している


#### 参考文献
VTK Official Website (https://vtk.org/) (最終閲覧日：2025年2月15日)
Will Schroeder, Ken Martin, Bill Lorensen: “The Visualization Toolkit” (Kitware) (最終閲覧日：2025年2月15日)
VTK Documentation (https://docs.vtk.org/) (最終閲覧日：2025年2月15日)
Kitware GitLab VTK Repository (https://gitlab.kitware.com/vtk/vtk) (最終閲覧日：2025年2月15日)
The ITK Community (https://itk.org/) (最終閲覧日：2025年2月15日)
ロジスティクス回帰で分類する

# 内容

ロジスティクス回帰を用いて分類を行うモデルを構築します。
回帰とついてますが、分類モデルとしても使用できるので、
分類モデルの内容をまとめます。

## 実行内容
まずはライブラリをインポートします。
```python
# ライブラリインポート
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.model_selection import train_test_split
from sklearn.datasets import make_classification
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import accuracy_score, confusion_matrix, classification_report
```

次に学習に使用するデータを読み込みます。
今回もirisデータを読み込んで分類を行います。

```py
# データ読み込み
from sklearn.datasets import load_iris
iris = load_iris()
# データフレームを作成
data = pd.DataFrame(iris.data, columns=iris.feature_names)
# ターゲットデータを追加
data['target'] = iris.target
# （本来、irisデータはデータフレーム化まで不要ですが備忘録のために作成）
```

作成したデータセットをtrainデータとtestデータに分割、
標準化まで実施します。

```py
# データセットを作成
x = data.drop('target' , axis = 1)
y = data['target']  

#  trainデータとtestデータに分割
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size=0.2, random_state=42)

# 標準化
from sklearn.preprocessing import StandardScaler
scaler = StandardScaler()
X_train = scaler.fit_transform(x_train)
X_test = scaler.transform(x_test)
```

これで標準化まで完了しました。
これで学習するデータが整いましたので、学習を行います。
```py
# インスタンス化
model = LogisticRegression()
# 学習
model.fit(x_train, y_train)

# 予測と評価
y_pred = model.predict(x_test)
accuracy = accuracy_score(y_test, y_pred)
conf_matrix = confusion_matrix(y_test, y_pred)
report = classification_report(y_test, y_pred)

print(f'Accuracy: {accuracy:.2f}')
print('Confusion Matrix:\n', conf_matrix)
print('Classification Report:\n', report)
```

結果は下記の結果が得られました。
精度が1.00なので分類できていることがわかります。

```py
Accuracy: 1.00
Confusion Matrix:
 [[10  0  0]
 [ 0  9  0]
 [ 0  0 11]]
Classification Report:
               precision    recall  f1-score   support

           0       1.00      1.00      1.00        10
           1       1.00      1.00      1.00         9
           2       1.00      1.00      1.00        11

    accuracy                           1.00        30
   macro avg       1.00      1.00      1.00        30
weighted avg       1.00      1.00      1.00        30
```

ロジスティクス回帰では重みを確認することで、
それぞれのクラスにどの要素がどのくらい影響するか可視化することが可能です。
この要素の可視化が一番の目的かと思うので、
可視化のコードをまとめておきます。

```py
# プロット
plt.figure(figsize=(10, 6))  
for i in range(len(model.coef_)):
    plt.subplot(2, 3, i + 1)  
    plt.bar(x=iris.feature_names, height=model.coef_[i])
    plt.title(iris.target_names[i])
    plt.xticks(rotation=45) 

plt.tight_layout() 
plt.show()
```

出力結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3569853/3bf40940-ea68-0e23-9ea7-1cdb23cecf2e.png)


実際のデータに合わせてグラフのXやタイトルを適宜合わせれば仕様できると思います。

## 回帰なのに分類できる理由
備忘録もかねて、回帰ですが分類できる理由も合わせて記載します。
まず最初に線形回帰を行います。

```math
z = w_1x_1 + w_2x_2 + ... + w_nx_n + b
```
この値をシグモイド関数に使うことで **0~1** の値に変換します。
その際に閾値（通常：0.5）を定めることで閾値に対する判定により分類を行います。
また各係数は上記の $z$ を求める際に使用した係数が重みとなって出力されています。

```math

sigmoid(z)=\frac{1}{1+{e}^{-az}}
```
最高のオンラインM3U8プレイヤー4選

## M3U8フォーマットとは何ですか？

M3U8は、HTTP Live Streaming（HLS）プロトコルに基づくマルチメディアプレイリストを定義するために使用されるテキストベースのプレイリストファイルフォーマットです。主な特徴は以下の通りです：

- .m3u8ファイルは、UTF-8文字エンコーディングを使用する拡張された.m3uファイルです。
- ファイルの内容は、マルチメディアセグメントを指す一連のURL、再生時間、およびその他の説明情報を含むことが一般的です。

M3U8は、ストリーミングメディアプレイリストに使用されるフォーマットとして、以下のような利点があります：

- **リアルタイムストリーミングのサポート**
- **アダプティブビットレート**
- **クロスプラットフォームおよびデバイスサポート**
- **セキュリティと著作権保護**
- **シンプルで柔軟**
- **ネットワークジッタへの耐性**
- **高い信頼性**
- **広範なコンテンツ配信ネットワーク（CDN）サポート**

これらの利点により、M3U8は現代のストリーミングサービスで広く採用されているソリューションになり、効率的で信頼性が高く柔軟なストリーミング再生のニーズを満たしています。
M3U8フォーマットとタグに関する詳しい情報が必要な場合は、[HLS仕様](https://tools.ietf.org/html/draft-pantos-http-live-streaming-07)ドキュメントを参照してください。

## オンラインM3U8プレイヤーのおすすめ

現時点でシステム内蔵のプレイヤーは.mp4形式のビデオファイルを直接再生することができますが、拡張子が.m3u8、.mpd、.flvのビデオファイルを直接再生することはできません。

以下は、ブラウザで直接M3U8ストリーミングコンテンツを再生するためのオンラインM3U8プレイヤーツールとウェブサイトのおすすめです。再生URLを入力するだけでオンライン再生ができます。

![Clipboard_Screenshot_1739259573.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3895563/0b3ddb1f-40d5-befc-9174-1916cca954bd.png)


### 1. **[EdgeOne HLS Player](https://edgeone.ai/ja/tools/hls-player)**

EdgeOneは、WebRTC、FLV、HLSライブストリーミング、およびHLS、FLV、MP4形式のオンデマンド再生をサポートしています。

EdgeOneでオンライン再生する利点は、登録やログインが不要で、リンクをコピーするだけですぐに再生できることです。

### 2. **[Castr HLS Player](https://castr.io/hlsplayer)**

Castrが提供するオンラインHLSプレイヤーで、機能が豊富でアダプティブビットレートストリーミングをサポートしています。M3U8 URLを入力するだけで再生でき、多くのデバイスに対応しています。

### 3. **[Openload Stream Player](https://www.openload.co/streamplayer)**

Openloadが提供するオンラインM3U8プレイヤーで、シンプルなインターフェースを持ち、使いやすく、M3U8アドレスを入力するだけで再生できます。

### 4. **[HLSPlayer.net](http://www.hlsplayer.net/)**

HLSPlayer.netは、よく知られたオンラインM3U8プレイヤーです。HLS .m3u8ファイルやM3U8プレイリストURLを入力ボックスに貼り付け、「再生」ボタンをクリックするだけでストリーミングを開始できます。プラグインのインストールは不要で、ほとんどの主流ブラウザに対応しています。

### 使用方法

これらのオンラインプレイヤーの使用方法は一般的に非常に簡単です。例えば、EdgeOneの使用方法は以下の通りです：

1. [EdgeOne](https://edgeone.ai/ja/tools/hls-player)ウェブサイトを開きます。
2. M3U8ファイルのURLを入力します。
3. 再生ボタンをクリックして再生を開始します。

ニーズと使用シーンに基づいて最も適したオンラインM3U8プレイヤーツールを選んでください。
Rust学習

# 環境構築
## 1. rustupのインストール
1. https://www.rust-lang.org/ja/tools/install を開く
1. 上記ページのシェルそのまま実行してrustupを入れる `curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh` 
1. 選択肢は `Proceed with standard installation` で進む
1. ターミナル再起動
1. `cargo` コマンドが動けばok
## 2. エディタ設定
1. vscode開く
1. 拡張機能 `rust-analyzer` を入れる
1. clippyいれる
    1. 歯車でvscodeの設定開く
    1. 赤枠のボタン押して設定をjson形式で開く
         ![スクリーンショット 2025-02-11 16.31.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/225823/1349ec5a-3db8-44a5-dc4b-d9a51d53e2bb.png)
    1.  `"rust-analyzer.check.command": "clippy"` と追記して保存
## 3. リポジトリ作成
1. `cargo init rust_practice` （これで `.git` 込のpjが生成）
1. `cd rust_practice`
1. `cargo run` すると `Hello, world!` が表示される

# hello world
1. `cargo build` すると `target/debug/rust_practice` が生成される（pj名と同じものが出来るので `rust_practice` となっている）
1. `target/debug/rust_practice` と直接バイナリ実行でも `Hello world!` ひょうじされｒ
PodがマウントしているEFSファイルシステムをリストアしたらPVの再作成が必要か？

# 結論

リストア方法による。

EFSのバックアップはAWS Backupでとる。AWS Backupでリストアする際、`ソースファイルシステムのディレクトリに復元する`と`新しいファイルシステムに復元する`が選べる。

## 「ソースファイルシステムのディレクトリに復元する」の場合

バックアップしたファイルシステムに復元するためファイルシステムIDが変わらない。なのでPVの再作成は不要。ただし、リストアしてもファイルシステム内の同じ場所にはリストアされない。`aws-backup-restore_`から始まるディレクトリ内にリストアされる。復元タイプで`完全な復元`と`項目レベルの復元`どちらを選んでも`aws-backup-restore_`にリストアされる。

## 「新しいファイルシステムに復元する」の場合

バックアップしたファイルシステムとは別のファイルシステムになるためファイルシステムIDが変わる。なのでPVの再作成が必要。`aws-backup-restore_`から始まるディレクトリ内にリストアされるのは`ソースファイルシステムのディレクトリに復元する`の場合と一緒。
PodがNFSマウント中に接続を切ったらどうなるか？

PodがNFSの領域をマウントできないとPodは起動できない。ではPodがマウント中にNFSの接続を切ったらPodはどうなるのか？

# 結論

Podはrunningし続ける。Pod内に入りマウントポイントをlsしても応答が返って来ない。

# 事前準備

- EFSのファイルシステムを作成
- EKSにEFS CSI ドライバーをインストール
- SC、PVC、PVも作成
- PodでPVをマウント

``` yaml
---
apiVersion: storage.k8s.io/v1
kind: StorageClass
metadata:
  name: efs-sc
provisioner: efs.csi.aws.com
---
apiVersion: v1
kind: PersistentVolume
metadata:
  name: efs-pv
spec:
  capacity:
    storage: 5Gi
  volumeMode: Filesystem
  accessModes:
    - ReadWriteOnce
  storageClassName: efs-sc
  persistentVolumeReclaimPolicy: Retain
  csi:
    driver: efs.csi.aws.com
    volumeHandle: fs-045001d08d85dd703
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: efs-claim
spec:
  accessModes:
    - ReadWriteOnce
  storageClassName: efs-sc
  resources:
    requests:
      storage: 5Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
        volumeMounts:
        - name: persistent-storage
          mountPath: /data
      volumes:
      - name: persistent-storage
        persistentVolumeClaim:
          claimName: efs-claim
```

# 検証

## マウント中にEFSのSGからインバウンド許可を消したら？

PodはRunningし続ける。Pod内に入りマウントポイントをlsしても応答が返って来ない。

インバウンドを再度許可すると繋がる。

## マウント中にEFSのマウントターゲットを削除？

PodはRunningし続ける。Pod内に入りマウントポイントをlsしても応答が返って来ない。

再度マウントターゲットを作成。しばらく(数分)するとまた繋がる。

## マウント中にEFSファイルシステムを削除したら？

PodはRunningし続ける。Pod内に入りマウントポイントをlsしても応答が返って来ない。
AWS CodeDeployのライフサイクルフック

AWS CodeDeployのライフサイクルフックを各デプロイメントタイプ別に主要な機能と用途をまとめました。

### 1. EC2/オンプレミスデプロイメント

| フック名           | タイミング               | 主な用途                                         |
|-------------------|------------------------|------------------------------------------------|
| ApplicationStop   | 古いアプリケーション停止時 | 現在稼働中の古いバージョンを停止する               |
| DownloadBundle    | コード取得時             | 新しいアプリケーションコードをダウンロードする       |
| BeforeInstall     | インストール前           | バックアップ作成や事前準備（例：ファイルの暗号解除）を実施 |
| Install           | インストール時           | 新しいコードやファイルを実際にインストールする       |
| AfterInstall      | インストール後           | インストール後の設定変更や権限付与などの調整を行う      |
| ApplicationStart  | 起動時                  | 新しいアプリケーションを起動する                  |
| ValidateService   | 検証時                  | デプロイが正しく完了したか、動作確認を行う           |

### 2. ECSデプロイメント（ブルー/グリーンデプロイ）

| フック名               | タイミング                      | 主な用途                                       |
|-----------------------|-------------------------------|----------------------------------------------|
| BeforeInstall         | 新タスクセット作成前             | 新しいタスクセット作成前の準備を行う             |
| AfterInstall          | 新タスクセット作成後             | タスクセット作成後に初期設定や確認処理を実施       |
| AfterAllowTestTraffic | テストトラフィック開始後         | テスト環境で新タスクセットの動作確認を行う         |
| BeforeAllowTraffic    | 本番トラフィック移行前           | 本番環境へ移行する前の最終確認を実施             |

### 3. Lambdaデプロイメント

| フック名            | タイミング         | 主な用途                                       |
|--------------------|------------------|----------------------------------------------|
| BeforeAllowTraffic | トラフィック移行前   | 新しいLambda関数の動作確認や準備を実施           |
| AfterAllowTraffic  | トラフィック移行後   | デプロイ後、実際のトラフィック下での動作確認を行う   |

### 4. Auto Scalingとの連携（Auto Scalingライフサイクルフック）

| フック名   | 用途              | 説明                                                     |
|-----------|-----------------|--------------------------------------------------------|
| 起動フック   | インスタンス起動時   | 新しいインスタンスが起動した際に、初期設定やカスタム処理を実行する     |
| 終了フック   | インスタンス終了時   | インスタンス終了前に、クリーンアップなどの後処理を実施する             |

### キーポイントまとめ

- **EC2/オンプレミス:**  
  古いアプリケーションを停止（ApplicationStop）→ 新コードの取得（DownloadBundle）→ インストール前準備（BeforeInstall）→ インストール（Install）→ インストール後調整（AfterInstall）→ アプリケーション起動（ApplicationStart）→ 動作検証（ValidateService）。

- **ECS:**  
  新しいタスクセットの作成前後やトラフィック移行時に適切な処理を挟むことで、ブルー/グリーンデプロイを安全に実施。

- **Lambda:**  
  トラフィックの移行前後で検証を行い、デプロイの成功を確認。

- **Auto Scaling:**  
  インスタンスの起動・終了時にカスタム処理を実施するための仕組みで、CodeDeployのイベントとは異なる。
Pythonの多重継承を学んだ

# 継承


定義済みのClassのプロパティやメソッドを引き継いでClassを作る仕組みです


## 1. メソッドについて


以下のようなclassを定義して継承してみます


```python:継承元クラス
class A:
    def __init__(self, value):
        self.set(value)

    def get(self):
        return self.value

    def set(self, value):
        self.value = value

a = A(1)
a.get()  # 1が得られます
```


### 1-1. そのまま継承する


上で定義したclassをそのまま継承してclassを作ると以下のようになります
意味がないので実際に使うことはないと思いますがこれが最小構成です


```python:特に何も加えずに継承する
class B(A):
    pass

b = B(1)
b.get()  # 1が得られます
```


### 1-2. メソッドをoverrideする


通常は以下のように既にあるメソッドを書き換えて作成すると思います


```python:setメソッドをoverrideする
class C1(A):
    def set(self, value):
        self.value = value * 2

c1 = C1(1)
c1.get()  # 2が得られます
```


### 1-3. メソッドを追加する


存在しないメソッドを新たに追加して継承することもできます。これをやるとポリモーフィズムが崩れるのでオブジェクト指向だと非推奨な書き方だと思いますが、書けばその通り動きます


```python:setメソッドをoverrideする
class C2(C1):
    def get_double(self):
        return self.value * 3

c2 = C2(1)
c2.get()  # 6が得られます
```


## 2. __init__とプロパティ


### 2-1. 親classの__init__メソッドを引き継ぐ


__init__メソッド内でsuper().__init__()を呼ぶと、親classの__init__()を呼び出すことができます。親classの__init__()内でもsuper()を読んでいれば親classの親classと順に辿っていけますので継承する場合はこちらで書くのが推奨されています


```python:
class D2(A):
    def __init__(self, value):
        super().__init__(value * 5)

d2 = D2(3)
d2.get()  # 15が得られます
```


### 2-2. __init__メソッドを新たに書く


単に書くこともできますが、継承元に既に書かれているものをこちらにも書いてしまうと修正時に複数個所の書き換えが必要になってしまいます。DRY原則から考えても完全に書き換える場合を除いてこの書き方はすべきでないと思います


```python:
class D1(A):
    def __init__(self, value):
        self.set(value * 5)

d1 = D1(2)
d1.get()  # 10が得られます
```


# 多重継承


複数のclassから継承することができます
同名のメソッドが存在する場合は先に指定された方が優先になります


## 1. メソッド継承の優先順位


```python:継承元class
class E:
    def __init__(self, value):
        self.value = value
        
    def hoge(self):
        print('I am E: value is {}'.format(self.value))

e = E(3)
e.hoge()  # I am E: value is 3 が出力されます
```


```python:継承元class
class F:
    def __init__(self, value):
        self.value = value * 2
        
    def hoge(self):
        print('I am F: value is {}'.format(self.value))

f = F(3)
f.hoge()  # I am F: value is 6 が出力されます
```


上記で定義した2つのclassを多重継承すると以下のようになります


```python:Eを優先して継承する
class G1(E, F):
    pass

g1 = G1(3)
g1.hoge()  # I am E: value is 3
```


```python:Fを優先して継承する
class G2(F, E):
    pass

g2 = G2(3)
g2.hoge()  # I am F: value is 6
```


メソッドである__init__とhogeが両方重複しているので、先に継承指定されている側だけが反映されています


## 2. 確認方法


上記ではprintで確認していましたが、classの__mro__で確認することができます


```python:python
>>> print(G1.__mro__)  
(<class '__main__.G1'>, <class '__main__.E'>, <class '__main__.F'>, <class 'object'>)
```


## 3. 継承元classの数


継承は3つ以上のclassから実施することも出来ます
その場合も先に指定したものが優先されて継承となります


## まとめ


多重継承なんていうとなんだか難しそうですが使い方自体は簡単ですｎ
レッツトライ
量子ビットをイメージでつかむ（量子もつれ編）

　単量子ビットのイメージは[こちら](https://qiita.com/sin-gee/items/7a55a1a859e6c72821f0)に示しましたが、本記事では[こちら](https://www.amazon.co.jp/IBM-Quantum%E3%81%A7%E5%AD%A6%E3%81%B6%E9%87%8F%E5%AD%90%E3%82%B3%E3%83%B3%E3%83%94%E3%83%A5%E3%83%BC%E3%82%BF-%E6%B9%8A%E9%9B%84%E4%B8%80%E9%83%8E/dp/4798062804/ref=sr_1_1?__mk_ja_JP=%E3%82%AB%E3%82%BF%E3%82%AB%E3%83%8A&crid=18WGGEKDLVU7H&dib=eyJ2IjoiMSJ9.iITWJCAUFuLJ2zm4sJLfluEJV0CGOUvheeEi49_6h8V0B9sGPyn7H83TfHypuoo1OeYluf5QPD88bklVWUq_ScqIi96Fk3kR-3wOXO6IV9YL5ANw2PdkojPBscoBUwcCzq3Ic2CXDOWzqVN4F4Oew6cjdA6wuqlvqTglKNsp5uM.lSuRRqUx1vsjrN1wVeTvbdZ3mMnqGMib4s1ImML_znk&dib_tag=se&keywords=IBM+Q&qid=1739238434&sprefix=ibm+q%2Caps%2C179&sr=8-1)を教科書にして２量子ビットの量子もつれ（Bell状態）について考えていこうと思います。使ったJupyter notebookはこちらです。https://github.com/sin-gee/ImageOfQuantumBit/blob/main/ImageOfBellState.ipynb
 
　複数の量子ビットを扱う場合、量子ビット間で相関関係が発生するため、（量子ビットそれぞれで扱うのではなく）まとまった形（$\lvert\hspace{4px}q_{high}\hspace{4px}q_{low}\hspace{4px}\rangle$）で扱う必要があります。つまり、（単ビットでは$\lvert0\rangle$と$\lvert1\rangle$がそうであったように）$\lvert00\rangle$,$\lvert01\rangle$,$\lvert10\rangle$,$\lvert01\rangle$,$\lvert11\rangle$をそれぞれ相互に直交している波として扱います。そして、その操作は、（単ビットの場合2×2の行列で表現されたように）4×4の行列で表現されます。

　目指す量子回路は次のとおりです。

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3921213/9f7e8822-575f-4309-1b43-51f5f9c61f81.png)

### 初期状態の設定
初期で上位ビットを混合状態にしてもよかったのですが、量子回路に沿って$\lvert00\rangle$を作ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3921213/d3e99c7e-cd49-06f8-be85-adcc9a99b0d2.png)

```python:
#量子bit×2の生成

#量子bit1
phi_high  = math.pi*0 # ブロッホ球での|0>からの角度
shift_high= math.pi*0 # 位相差
#量子bit2
phi_low   = math.pi*0 # ブロッホ球での|0>からの角度
shift_low = math.pi*0 # 位相差
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3921213/f5c5ca30-ba02-527e-4c7a-ed0b37ff04a1.png)


### 上位ビットへのアダマール操作

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3921213/7a6dd3ab-4509-85a3-2ad1-a4b3cea4e33b.png)

```math
H
\otimes
I =
\frac{1}{\sqrt{2}}
\begin{pmatrix}
1
\otimes
I
&
1
\otimes
I
\\
1
\otimes
I
&
-1
\otimes
I
\end{pmatrix}
=
\frac{1}{\sqrt{2}}
\begin{pmatrix}
1 & 0 & 1 & 0 \\
0 & 1 & 0 & 1 \\
1 & 0 & -1 & 0 \\
0 & 1 & 0 & -1
\end{pmatrix}
```
```python:
#to high bit
q_00,q_10 = h_gate(q_00,q_10)
q_01,q_11 = h_gate(q_01,q_11)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3921213/3e8734f3-54ba-4151-4c0e-1a7c8f5e8d74.png)


### cx(cnot)操作

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3921213/0112d852-4fc6-0140-bffc-8c5b6f43038d.png)

```math
\begin{pmatrix}
1 & 0 & 0 & 0 \\
0 & 1 & 0 & 0 \\
0 & 0 & 0 & 1 \\
0 & 0 & 1 & 0
\end{pmatrix}
```
```python:
#cx(highをコントロールビット、lowを標的ビットとした場合)
q_10,q_11 = x_gate(q_10,q_11)
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3921213/034d507e-a15a-12fa-fe8b-558c73b159ed.png)

無事、$\lvert00\rangle$と$\lvert11\rangle$の混合状態ができました。
Kotlin Koans やってみた 8

# Classes
## Data classes
### 問題
クラス、プロパティ、データクラスについて学び、その後、次の Java コードを Kotlin に書き換えてみましょう。
```java
public class Person {
    private final String name;
    private final int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}
```
その後、変換した Kotlin のクラスに data 修飾子を追加してください。
この修飾子を付けることで、コンパイラが equals / hashCode、toString などの便利なメソッドを自動生成してくれます。

### 解答
```kotlin
data class Person( val name: String, val age: Number)

fun getPeople(): List<Person> {
    return listOf(Person("Alice", 29), Person("Bob", 31))
}

fun comparePeople(): Boolean {
    val p1 = Person("Alice", 29)
    val p2 = Person("Alice", 29)
    return p1 == p2  // should be true
}
```

### 解説
[Kotlin 公式ドキュメント > Data classes](https://kotlinlang.org/docs/data-classes.html)

#### Data classes
Kotlin のデータクラスは、主にデータを保持するために使用されます。データクラスを定義すると、コンパイラが以下のような便利なメンバ関数を自動生成してくれます。例えば、インスタンスを可読性の高い形式で出力したり、比較・コピーしたりする機能が追加されます。データクラスは、data 修飾子をつけて宣言します。

```kotlin
data class User(val name: String, val age: Int)
```

コンパイラは、プライマリコンストラクタで宣言されたすべてのプロパティを元に、以下のメンバを自動生成します。
- `equals()` / `hashCode()` のペア
- `toString()`（例: `User(name=John, age=42)`）
- 宣言順に対応する `componentN()` 関数
- `copy()` 関数


# おわりに
data class って型みたいなものだと思ってたけど、他にもできることがあるっぽくて、イメージしていたものと違ったな〜という学び。

---

[← 前回の記事](https://qiita.com/b-yuko/items/01541f78faf31c1ba311)　|　[次回の記事 →](https://qiita.com/b-yuko/items/de0ea00db977bc9d14a8)
next.jsのImageコンポーネントはメモリを食いすぎる

## ぶち当たった問題
- Next.jsの[Imageコンポーネント](https://nextjs.org/docs/pages/api-reference/components/image)を使った結果、Cloud Runのメモリをバカ喰いしてました
- 今回は、この原因と解決法を備忘録として残す

<details>
<summary>先に結論見たい人用</summary>

## 解決法は？

### 1. 画像最適化を無効化
    ```javascript:next.config.js
    module.exports = {
      images: {
        unoptimized: true
      }
    }
    ```
### 2. imgタグを使う
```javascript
<img 
  src="/images/example.jpg"
  width={640}
  height={480}
  loading="lazy"
  decoding="async"
/>
```

### 3. CDNを使う
```javascript
<img 
  src="https://your-cdn.com/image.jpg?w=800&q=80"
  alt="説明"
  width={800}
  height={600}
  loading="lazy"
  decoding="async"
/>
```
- シンプルな実装で確実な動作
- サーバーリソースの消費が少ない
- CDNの最適化機能をダイレクトに活用可能
</details>

## なんで、 next.jsのImageコンポーネントはメモリをたくさん使うの？

### Imageコンポーネントの画像最適化処理（メモリを消費の原因）
1. **画像デコード**：入力画像をピクセルデータに変換（13MB PNG→約100MB RAM）
1. **変換処理**：リサイズ/フォーマット変換（Sharp/Squoosh使用）
1. **エンコード**：最適化画像を出力（例：194KB WebP）
1. **キャッシュ保存**：`.next/cache/images`に書き込み

:::note alert
**問題は、変換処理中のメモリ解放遅延** 
Node.jsのガベージコレクションが処理完了後もメモリを完全に解放しないことがある。
:::

:::note info
**最適化による効果**
1. **フォーマット最適化**：画質劣化を最小限に抑えつつファイルサイズを大幅削減
1. **リサイズ最適化**：最適な解像度を動的に選択
1. **配信最適化**：遅延読み込みによる初期描画速度向上や、プリロードによるLCP改善
:::

## Imageコンポーネントは、多くのケースでオーバスペック
### **サーバーリソースへの影響**
- 画像最適化処理がサーバー上で実行され、CPUとメモリを大量に消費する
- コンテナ環境での運用時に`_next/cache/image`へのリクエストが集中し、CPUスパイクやコンテナ停止の原因となる
- マルチインスタンス環境での運用が困難

### **パフォーマンスへの影響**
- サーバーサイドでの画像処理により、ページ応答時間が遅延
- 画像のバリアントを複数生成するため、初期ロード時間が増加
- ビルド時間の増加、特に大規模プロジェクトで顕著

## 解決法は？
### 1. 画像最適化を無効化
    ```javascript:next.config.js
    module.exports = {
      images: {
        unoptimized: true
      }
    }
    ```
### 2. imgタグを使う
```javascript
<img 
  src="/images/example.jpg"
  width={640}
  height={480}
  loading="lazy"
  decoding="async"
/>
```

### 3. CDNを使う
```javascript
<img 
  src="https://your-cdn.com/image.jpg?w=800&q=80"
  alt="説明"
  width={800}
  height={600}
  loading="lazy"
  decoding="async"
/>
```
- シンプルな実装で確実な動作
- サーバーリソースの消費が少ない
- CDNの最適化機能をダイレクトに活用可能

### 4. 画像サイズを小さくする・WebPけいしきにすr


## 参考

https://www.reddit.com/r/nextjs/comments/yl5zg4/nextimage_component_is_it_terrible_or_am_i_just/

https://nextjs.org/docs/pages/api-reference/components/image#unoptimized
RAGとファインチューニングの違い

AIモデルを現実のタスクに活用する際、**RAG（Retrieval-Augmented Generation）**と**ファインチューニング**の選択が重要なポイントになります。本記事では、両者の違い、関係性、そして使い分けの基準について詳しく解説します。
※この記事は、ChatGPTの出力を基に作成しています。

---

## 一言で表すと？
- **RAG**：**必要なときに外部から知識を借りるアプローチ**
- **ファインチューニング**：**必要な知識をモデルに染み込ませるアプローチ**

---

## RAG（Retrieval-Augmented Generation）とは？
RAGは、**情報検索（Retrieval）**と**生成（Generation）**を組み合わせたアプローチです。

### 仕組み
1. **情報検索**：ユーザーの質問に応じて、外部データベースやドキュメントから関連情報を検索します。
2. **回答生成**：検索した情報を基に、AI生成モデル（例：GPTなど）が回答を生成します。

### 特徴とメリット
- **外部データを動的に参照**するため、最新情報にも即対応可能。
- **モデルの再トレーニングが不要**で、データベースの更新だけで知識を更新できます。
- スケーラブルなデータ管理が可能で、大規模データにも対応しやすいです。

**例**：FAQ応答システムやニュース生成では、RAGが頻繁に使われます。

---

## ファインチューニングとは？
ファインチューニングは、**事前学習されたモデルを特定のタスクやドメインに最適化するための再トレーニング手法**です。

### 仕組み
1. **ドメイン固有データの準備**：専門分野や特定タスクに関連するデータを用意します。
2. **再学習（ファインチューニング）**：そのデータを使ってモデルに再トレーニングを施し、パラメータを調整します。

### 特徴とメリット
- **モデルの内部に知識を固定的に組み込む**ため、特定のタスクに高い精度で対応可能。
- **オフライン環境でも利用可能**で、外部データベースへのアクセスが不要。
- 特に特化型のタスク（医療、法律、金融など）で高い効果を発揮します。

**例**：法律相談モデルに特化したAIや医療文書解析システム。

---

## RAGとファインチューニングの違い
| 項目                     | RAG（情報検索+生成）                           | ファインチューニング（再トレーニング）                |
|------------------------|----------------------------------------------|--------------------------------------------------|
| **知識の更新方法**        | 外部データベースを更新するだけで反映          | 新しいデータでモデルを再トレーニングする必要がある   |
| **知識の柔軟性**          | 動的でリアルタイムの情報に対応可能            | 固定的な知識をモデルに埋め込む                     |
| **モデルのトレーニング負荷**| 低い（トレーニング不要）                      | 高い（再トレーニングが必要）                       |
| **実行時の依存**          | 外部データベースに依存                       | 独立（オフラインでも利用可能）                      |
| **適用範囲**              | 最新の情報を含む広範囲な質問                  | 特定分野や特化型タスクに適した応答                 |

---

## 例で理解する：製品マニュアルを用いた応答システム

### RAGの場合
- 製品のマニュアルやFAQをデータベースに保存。
- ユーザーからの質問があれば、データベースから関連情報を検索し、回答を生成。
- 新しいマニュアルが追加されても、モデルの再トレーニングなしで即対応可能。

### ファインチューニングの場合
- 製品マニュアルの内容をデータセットとして用意し、AIモデルをファインチューニング。
- モデル自体に製品情報が埋め込まれるため、質問に対して直接回答可能。
- 新しい製品情報が追加される場合、再度トレーニングが必要。

---

## RAGとファインチューニングの関係性
RAGとファインチューニングは対立する概念ではなく、**補完的な関係**にあります。

### 組み合わせたハイブリッドなアプローチの例
- **基本的なドメイン知識**はファインチューニングでモデルに組み込み。
- **頻繁に更新される情報**はRAGを活用して動的に取得。

**例**：カスタマーサポートシステムでは、一般的な製品知識はファインチューニングしたモデルで対応し、新しい製品情報やFAQはRAGを活用してリアルタイムに回答する、といった形が効果的です。

---

## どちらを選ぶべき？
プロジェクトやタスクに応じて適切な方法を選ぶことが重要です。

- **動的な情報が必要な場合**：RAGが適しています（例：ニュースやFAQ）。
- **特定分野に特化した高精度な応答が必要な場合**：ファインチューニングが有効です（例：医療、法律など）。
- **ハイブリッド運用**：広範囲な質問に対応したい場合は、両者を組み合わせた運用が効果的です。

---

## まとめ
RAGとファインチューニングは、AIモデルに知識を与える2つの異なる方法です。それぞれに得意分野がありますが、両者を適切に組み合わせることで、柔軟で高性能なAIシステムを構築することが可能です。
enPiT2024ふりかえり

# はじめに
こんにちは。MM9999と申します。今回は筑波大学で行われたenPiT2024の振り返りを何かしらのブログで書いてね。ということがあったので初qiitaです。おぼつかない所が多いと思いますが暇だったら見てってください。
# enPiTとは？
enPiTを簡単に説明すると、アジャイル開発というものを約半年かけて行う感じの授業です。身近にある困りごとを見つけ、それを解決するようなプロダクトをチームで作成します。
ここで得られる経験として大きいものはやはりチーム開発経験でしょうか。チーム開発経験というのはなかなか経験できないものだと思います。例えばインターンなど外部で活動を行うなどをすればチーム開発経験が得られるかもしれませんが、なかなかハードルが高いと思います。一方enPiTの場合は、授業の1つとして行うため、ハードルが他のものと比べて低いです。
# チームについて
私の所属していたチームは脱・薄情者というチームです。人の名前を覚えられない、という共通の悩みを持った人同士で集まったチームです。メンバー数は7人で、M1が4人、B2が3人でした。本来B3がメイン層のはずのenPiTでB3がおらず、レアなはずのM1とB2が集まってる謎チームです。メンバーの名前についてなのですが、割と本名(苗字or下の名前)でやってる人が多かったので省略します。チームの雰囲気はかなり和気あいあいとした感じで、自分に合っていたように感じます。チーム作業では大なり小なり人間関係で問題がおこってしまいそうなものですが、このチームには少なくとも大きな問題が起こらなくて良かったです。(小さな問題も起こってなかったかも)　正直このチームの雰囲気があって開発に対するやる気があったところがあるので、チームの雰囲気は大事だと思います。
# 開発したプロダクトについて
私たちが開発したプロダクトは「Who?Name!」と言います。これは名前が覚えられない大学生向けのアイスブレイクゲームとなっています。これを使って遊べば、初対面の人の名前を1度で覚えられるようになるというものです。
このゲームを一言で言えば「人間版なんじゃもんじゃゲーム」です。プレイヤーはカードになります。その人の特徴や本名にちなんだ名前を付け、それを繰り返し呼ぶことで名前を覚えることを目的としています。
遊び方について軽く説明します。Who?Name!には簡単に2つのフェイズが存在します。1つは写真登録フェイズ、2つは遊ぶフェイズです。写真登録フェイズについて、ここでは各プレイヤーはお題にちなんだポーズをします。それを何枚か撮ったら遊ぶフェイズに移ります。遊ぶフェイズについて、ここでは写真登録フェイズで撮った写真をカードとしてなんじゃもんじゃゲームを遊びます。多分なんじゃもんじゃゲームについて理解すると早いと思います。
# 秋学期中のプロダクト・チームの軌跡
大きな流れとしては以下の通りです。
スプリント1：現メンバーの一人が加入
スプリント1～2.3：元々しようとしていたプロダクトがそもそも実装出来ないことが発覚
スプリント2.4～ロングレビュー：夏で出てた別案を採用し、プロトタイプ作成→レビューでの反応が良かったため、本格的に実装を進める
スプリント3～：Who?Name!の制作を進める

元々実装しようとしていたプロダクトは「U_Name」というプロダクトで、一度登録した人をパッと見返せる、名刺アプリのようなプロダクトでした。これを実装するためにはbluetoothを利用する必要があったのですが、これが出来なかったために方針転換を行いました。
U_Name作成段階ではbluetoothの実装が必須であったため、その実装だけをしていました。しかし、それを実装できていなかったため成果無しみたいな状況が多々ありました。ここで困ったことがレビューで何も確認できないといったことでした。この問題を解決するために、演劇をする、人の手でU_Nameのプロダクトを体験させるなどをしてレビューを行いました。
Who?Name!開発中はあまりこれ実装出来ないといったことはありませんでしたので、その辺の問題はありませんでした。ただ、プロダクトの性質上レビューの時間が長くないとまともにレビュー出来ないといった問題はありました。これを解決するために、他のレビュー班の人たちに頼み込んで時間を長く取ってもらうといったことをしていました。
# 期間を通した個人的振り返り
個人的にはこのenPiTの経験はとても役に立ったと思います。技術的なことに関しては他のチームメンバーが結構してくれていたので怪しいのですが、チーム開発については学べたと思います。履修前と今とで異なることは、開発は必ずしもプログラミングだけではないということです。勿論プログラミングは必要となりますが、それ以外にクライアントに意見を聞いたり、チーム内でアイデア出しを行ったりなど、プログラミング以外のことも沢山必要でした。実際の経験を通して開発をどうやって進めているか学べたことはどこの業界に行ったとしても役に立つことだと考えています。
後は経験談になることも履修して良かったと思います。今後インターンや就活などで開発経験があるかどうか聞かれた場合、はっきりとあると言えることはかなり大きいです。
# チームAMF最終版
![スクリーンショット 2025-02-11 155031.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4007793/67203a57-905d-8813-5ebf-e94db56432bb.png)