OmniAICreator/Qiita-1.07M huggingface dataset:
【個人開発】学習記録アプリ作成で得た気付き

# はじめに
学習記録アプリを作成したので、気づきを記載します。

# 作成したもの
<img width="60%" alt="image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3881019/48146576-00ba-9ed5-452f-c3229d0dc020.png">


### 機能
- 入力に応じて「入力されている学習内容」、「入力されている学習時間」に即時反映
- 登録ボタンを押すことでデータ登録
- 登録されているデータに応じて合計時間を表示

<img width="60%" alt="image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3881019/cbdb2c42-7529-b664-89d9-b5b29af9f605.png">

# 感想
- じゃけぇさんの「【最新ver対応済】モダンJavaScriptの基礎から始める挫折しないためのReact入門」を復習する良い機会になった
- 実際に手を動かしてアプリを作ると、理解が深まる
- 要件定義や実際の開発へのヒントを得た
    - NVPに落とし込んだ課題の指示が明確
    - 何を作りたいのかを明確にして、要件定義する
    - 詰まりやすい箇所に、対処法を明示する(数値の計算の際にはparseIntを使って、文字列型で保存されているデータを数値型に直すなど)

# 今後
実際の開発も、何がしたいかを明確にして、仮説を立ててプログラムを書き、エラーが出たらどう対処すればいいかを調べる、ということの繰り返しだと思います。
勉強になりました。
引き続きPDCAを回しながら取り組んでいきます。
ROS2 set up on Raspberry Pi5

# 対象となる読者

:::note info
- ROS2のインストールを初めておこなう人
- Raspberry Pi5でROS2を使う人
- 過去インストールをしたが流れを忘れてしまった人
:::

# installするROS2のバージョン

https://docs.ros.org/en/humble/index.html

:::note warn
ROS2のバージョンにはFoxy, Humble, Iron...といくつかあるが，サポート期限に違いがあるとのこと．
Humbleを入れようとしたがinstallが上手くいかず，今後も使えそうなJazzyをセットアップした．

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2043769/be411404-4ae2-7334-77c6-38996cbe4149.png)
(https://docs.ros.org/en/humble/Releases.html より)
:::

# install

https://docs.ros.org/en/jazzy/Installation/Ubuntu-Install-Debs.html

公式のinstall手順を参考に環境を構築

:::note warn
raspi OSはDevianベースのOSであるため，公式の手順では初期の段階でErrorが出る．
そのためubuntu24.04のOSをSDカードに焼き直した．
:::

<details><summary>raspi osのinstall Error</summary>
手順通り`universe`をinstallしようとすると以下のErrorが出る

```bash
aptsources.distro.NoDistroTemplateException: Error: could not find a distribution template for Raspbian/bookworm
```

installしようとしているものがraspbianにあったものでないため起こるErrorらしい．
回避策は以下の通り
https://qiita.com/hnw/items/734f82bee26a40269c1b
</details>

## 環境構築

環境設定を```UTF-8```に変更，なお既に```locale```が入っている場合は初期のinstallはpassしてOK

```bash
locale  # check for UTF-8

sudo apt update && sudo apt install locales
sudo locale-gen en_US en_US.UTF-8
sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
export LANG=en_US.UTF-8

locale  # verify settings
```

<details><summary>変更前と後</summary>
変更前
```bash
LANG=C.UTF-8
LANGUAGE=
LC_CTYPE="C.UTF-8"
LC_NUMERIC="C.UTF-8"
LC_TIME="C.UTF-8"
LC_COLLATE="C.UTF-8"
LC_MONETARY="C.UTF-8"
LC_MESSAGES="C.UTF-8"
LC_PAPER="C.UTF-8"
LC_NAME="C.UTF-8"
LC_ADDRESS="C.UTF-8"
LC_TELEPHONE="C.UTF-8"
LC_MEASUREMENT="C.UTF-8"
LC_IDENTIFICATION="C.UTF-8"
LC_ALL=
```
変更後
```bash
LANG=en_US.UTF-8
LANGUAGE=
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=
```
</details>

ros2 repositoryの追加

```bash
sudo apt install software-properties-common
sudo add-apt-repository universe
```

ライブラリ取得先の追加
```bash
sudo apt update && sudo apt install curl -y
sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
```

source.listにrepositoryを追加
```bash
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
```

## ros2コードのinstall

```bash
sudo apt update
sudo apt upgrade
sudo apt install ros-jazzy-desktop
```

# 動作確認

talker側
```bash
source /opt/ros/jazzy/setup.bash
ros2 run demo_nodes_cpp talker
```

listener側

```bash
source /opt/ros/jazzy/setup.bash
ros2 run demo_nodes_py listener
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2043769/0310cb7e-4b13-be3a-672d-a706909655be.png)

:::note info
動いた！！
:::

# 感想

Humbleの環境構築できると思ったらinstall先に居なかったのは罠
困ったら```apt search |grep ***```で検索をして，本当に存在するかどうかを確かめることが時間を溶かさないコツ
ESP32: GATT通信でLチカ (Callback)

スケッチ例を参考にしました。
Central（クライアント）からの指示で、8個のLED を点灯します。

## プログラム

```text:CallbackLED.ino
// ---------------------------------------------------------------
/*
	CallbackLED.ino

					Sep/23/2024

*/
// ---------------------------------------------------------------
#include <ArduinoBLE.h>

#define	PROGRAM	"CallbackLED.ino"
#define	VERSION	"2024-9-23 PM 14:03"

BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214"); // create service

// create switch characteristic and allow remote device to read and write
BLEByteCharacteristic switchCharacteristic("19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite);

#define LED_1 32
#define LED_2 33
#define LED_3 25
#define LED_4 26
#define LED_5 27
#define LED_6 14
#define LED_7 12
#define LED_8 13

int leds[] = {LED_1,LED_2,LED_3,LED_4,LED_5,LED_6,LED_7,LED_8};

int icount = 0;
// ---------------------------------------------------------------
void setup() {
	Serial.begin(115200);
	while (!Serial);

	for (int it=0; it<8; it++)
		{	
		pinMode(leds[it], OUTPUT);
		}

	if (!BLE.begin()) {
		Serial.println("starting Bluetooth® Low Energy module failed!");

		while (1);
	}

	BLE.setLocalName("LEDCallback");
	BLE.setAdvertisedService(ledService);

	ledService.addCharacteristic(switchCharacteristic);

	BLE.addService(ledService);

	BLE.setEventHandler(BLEConnected, blePeripheralConnectHandler);
	BLE.setEventHandler(BLEDisconnected, blePeripheralDisconnectHandler);

	switchCharacteristic.setEventHandler(BLEWritten, switchCharacteristicWritten);
	switchCharacteristic.setValue(0);

	BLE.advertise();

	Serial.println(("Bluetooth® device active, waiting for connections..."));

	delay(2000);
	Serial.println(PROGRAM);
	Serial.println(VERSION);
	Serial.println("*** setup end ***");
}

// ---------------------------------------------------------------
void loop() {
	if ((icount % 100) == 0)
		{
		Serial.print("*** loop *** ");
		Serial.println(icount);
		}
	icount++;
	delay(50);

	BLE.poll();
}

// ---------------------------------------------------------------
void blePeripheralConnectHandler(BLEDevice central) {
	// central connected event handler
	Serial.print("Connected event, central: ");
	Serial.println(central.address());
}

// ---------------------------------------------------------------
void blePeripheralDisconnectHandler(BLEDevice central) {
	// central disconnected event handler
	Serial.print("Disconnected event, central: ");
	Serial.println(central.address());
}

// ---------------------------------------------------------------
void switchCharacteristicWritten(BLEDevice central, BLECharacteristic characteristic) {
	// central wrote new value to characteristic, update LED
	Serial.print("Characteristic event, written: ");

	int ss = switchCharacteristic.value();
	Serial.println(ss);

	switch(ss)
		{
		case 0:
			Serial.println("LED off");
			for (int it=0; it<8; it++)
				{	
				digitalWrite(leds[it], LOW);
				}
			break;

		default:
			if (ss < 9)
				{
				int jt = ss - 1;
				Serial.println("LED on");
				digitalWrite(leds[jt], HIGH);
				}
			break;
		}
}

// ---------------------------------------------------------------
```
格安 16ch ロジアナ LA1010 を pulseview で使うには

Kingst の LA1010 という、激安ロジアナよりはマトモそうな 16ch なロジアナが日本円で ￥6,500.- くらいでありまして数年前には出てたらしいんですが終ぞ知りませんでした。

注文して届いて取り急ぎ確認してまあ行けそうだ、との結論でした電気的には。

というのは付いてきた soft は色味がケバくて線が硬いのでとても見づらい。 表示回りの設定の選択肢がないので如何とも。 

その前に買った 8ch のが使ってる pulseview は少なくとも色味や表示形状は好みなのでこれが使えると嬉しいんだけどな、と、…


で、あれこれ試していたらば とりあえず何とかなったので書き留めておくことにしました。

[続きを見る（別サイト） …](https://supermodel.blog.jp/archives/93809149.html)

*
AppleScriptで効率的なファイル管理を実現：内容抽出と日付付与によるファイル名自動生成スクリプト

下記のAppleScriptは、指定したフォルダ内に存在するファイル名から内容部分を抽出し、重複を除いたリストを生成します。ユーザーは既存の内容を選択するか、新しい内容を追加して、日付と組み合わせた新しいファイル名をクリップボードにコピーできます。特定のファイル命名規則に基づくファイル管理を効率化したい場合に便利です。

### スクリプトの主な機能
- **保存フォルダの設定**: スクリプトが置かれたフォルダを対象フォルダとします。
- **ファイルリストの取得**: フォルダ内のすべてのファイル名を取得します。
- **内容部分の抽出**: ファイル名から特定の規則（`_`区切り）で内容部分を抽出します。
- **重複を除外したリストの作成**: 抽出された内容から重複を除外し、リストを作成します。
- **内容の選択または新規追加**: 既存の内容を選ぶか、新しい内容を入力するオプションを提示します。
- **ファイル名の生成**: 選択した内容と現在の日付を組み合わせて、拡張子なしのファイル名を生成します。
- **クリップボードへのコピー**: 生成されたファイル名をクリップボードにコピーします。

### サンプルコード
```applescript
-- 保存フォルダを設定（スクリプトのフォルダを初期値に）
set scriptFilePath to (path to me)
set scriptFolderPath to (do shell script "dirname " & quoted form of POSIX path of (scriptFilePath as text))

-- ファイルリストを取得
set fileList to {}
tell application "Finder"
	set fileList to name of every file in (POSIX file scriptFolderPath as alias)
end tell

-- 既存ファイルの内容部分を抽出
set contentList to {}
repeat with aFile in fileList
	if aFile contains "_" then
		set fileParts to my split(aFile, "_")
		-- ファイル名の「内容」部分をリストに追加
		set contentList to contentList & item 1 of fileParts
	end if
end repeat

-- 重複を除いたリストを作成し、新規追加オプションを加える
set contentList to my removeDuplicates(contentList)
set contentList to contentList & {"新しい内容を追加"}

-- ダイアログで既存の値を選択 or 新規追加
set dialogResult to choose from list contentList with prompt "既存の内容を選択するか、新規内容を追加してください。"

if dialogResult is false then
	display dialog "処理がキャンセルされました。" buttons {"OK"}
	return
else if dialogResult as string is "新しい内容を追加" then
	-- 新しい内容を入力するダイアログを表示
	set newContentDialog to display dialog "新しい内容を入力してください:" default answer ""
	set newContent to text returned of newContentDialog
else
	-- 既存の内容を選択
	set newContent to dialogResult as string
end if

-- 日付部分を生成
set currentDate to do shell script "date +'%Y%m%d'"

-- 新しいファイル名（拡張子なし）を生成
set newFileName to newContent & "_" & currentDate

-- クリップボードに拡張子なしのファイル名をコピー
set the clipboard to newFileName

-- 結果を表示
display dialog "新しいファイル名 '" & newFileName & "' がクリップボードに保存されました。" buttons {"OK"}

-- ファイル名の内容部分を抽出する関数
on split(theString, delimiter)
	set oldDelimiters to AppleScript's text item delimiters
	set AppleScript's text item delimiters to delimiter
	set theArray to every text item of theString
	set AppleScript's text item delimiters to oldDelimiters
	return theArray
end split

-- リストから重複を除く関数
on removeDuplicates(theList)
	set newList to {}
	repeat with i from 1 to count of theList
		set anItem to item i of theList
		if anItem is not in newList then
			set end of newList to anItem
		end if
	end repeat
	return newList
end removeDuplicates
```

### スクリプトの使用方法
1. このAppleScriptをスクリプトエディタにコピーし、適切な名前で保存します。
2. 対象となるフォルダにあるファイル名の命名規則に基づき、新しいファイル名を生成します。ファイル名は、`内容部分_日付`という形式で作成され、クリップボードにコピーされます。
3. 生成されたファイル名は、他のアプリケーションで貼り付けて使用できます。

### 例
たとえば、`report_20230923.txt`というファイルがフォルダ内にある場合、このスクリプトは`report`を内容部分として抽出し、ユーザーが新しいファイル名を生成すると、`report_20230924`のような形式のファイル名がクリップボードに保存されます。

### 拡張のアイデア
- **ファイルの自動リネーム**: クリップボードにコピーするだけでなく、実際にファイル名を変更する機能を追加できます。
- **カスタム日付形式の対応**: 日付の形式をカスタマイズするオプションを追加し、`YYYY/MM/DD`などの形式に対応できます。

このスクリプトを利用することで、日常的なファイル管理の効率化が期待できます。
[MacOS 14] JISキーボード上でUSキー配列を実現

# 背景
JIS配列 MacBookの内蔵キーボードでUS配列を実現するってのは案外需要が多いと思います．私も以下の方法を使ってUS配列として認識させ，キートップもUS版に交換していました．

それがMacOS 14 Sonomaにアップデートしたら設定が吹き飛び設定項目自体も消滅していて使えなくなってしまいました...
ネットに情報もなく，Karabinerで全部mappingするしかないか...と途方に暮れていましたが, 設定を延々彷徨いやっと代替の設定を発見したので備忘録としてここに記そうかと思います．

先人：https://qiita.com/yohm/items/75ff8d8b159375c83b1f

# 設定方法
ANSI等を選ぶのではなく，JIS配列上にいい感じにUS配列っぽく配置できる国の言語を入力ソースとして設定します．
今回はオーストラリア(Australian)を使用します．
他の言語でも良いですが，＃やらのシフト記号(?)の配置が異なる場合が多いのでその点に注意が必要です．

まず，システム設定 -> キーボード設定 -> 入力ソース欄(Text Input) -> 編集(Edit)　へ移動．

### 英語入力
英語入力では既存の入力のレイアウト変更がなぜかできないので新しく設定して古いものを後から消します．

1. +を選択
1. 左の選択欄から英語(English)を選択
1. 右の選択欄からオーストラリア(Australian)を選択 (添付の写真の状態．JIS上に上手いことUSキー配列風な配置にボタンがアサインされているのがわかります．)
1. 古い英語入力を選択して-ボタンで削除

![Screenshot 2024-09-23 at 18.07.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1756017/be7e0c7d-6cd0-b88c-72a2-f7526e93dcbf.png)


### かな入力

1. 全入力ソース(All Input Sources)欄からかな入力を選択
1. ローマ字レイアウト(Romaji Layout)にAustralianを選択

![Screenshot 2024-09-23 at 18.08.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1756017/9f2cc337-a71d-e333-30b3-050f463f1141.png)


# あとがき
結論としてANSIとかJISとかの配列を直接指定できる概念が内蔵キーボード向けとしては消えているっぽいです．(外付け向けには残っているかも)
ユーザにファイルを意識させない！とたわごとを吐いていたAppleはさすがです．
ただ最初からJIS配列をにいい感じ使えるマッピングにしてくれているのでその点は良い   ですね．
*以前の方法だとUSには存在しない「ろ」の場所のキーが認識されなくなったりしていました．今回の方法を使うとチルダが割り当てられます．

せっかくなので最後に私の最強の配列を自慢して締めたいと思います．
シンプルなキートップ + 超絶便利な英字/かなキーが共存できる最強配列です．

![Screenshot 2024-09-23 at 18.38.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1756017/1b2d1815-72cc-b05f-b625-ce2157b87506.png)

US配列のShift+Spaceで言語を切替えるやり方って，私の場合だと今どっちかわからなくて「あ，元々かな入力だった..」と何度も切替える羽目になるんですが，英字/かなキーがあれば押したら確実に目的の入力言語にスイッチできるのでストレスフリーなんですよね．

みんなもUS配列キートップをアリエクで買って最強レイアウトを手に入れろ！
コロナに感染したITエンジニアがすべきこと

# はじめに
9月上旬に新型コロナに感染し、1週間出社できなかったのでその間にやったことを記事にします。

## 寝る
現在の新型コロナウイルスは何種類もウィルスがあり、常にウィルス遺伝子の組み換えが行われているので、まずは薬を飲んで寝ましょう

## 本を読む
症状が改善されても発症してから5日間は他の人にウィルスを感染をさせてしまうので、家に本があったら読みましょう。私は日経ソフトウェアの記事を読みました。

# 最後に
手洗い、うがい、マスクの着用を忘れずに
IERAE CTF 2024 Writeup - derangement

2024年9月21日〜22日に開催された[IERAE CTF 2024](https://ierae-ctf.com/)に参加しました。

# Crypto
## derangement
permutationに変な制限つけないで！の問題。

### 1. 問題の理解
配布されたファイルを読み、出題サーバは以下の動きをすると理解した。

1. nc経由でアクセスされたら、15文字のランダムに文字を取ってきて、文字列を作る。それをsecretとする。
2. ヒントを教えてとリクエストされたら、secretをpermutationした結果を教えてあげる。ただし、もしもsecretとpermutation結果を比べて、一文字でも同じ場所に同じ文字が来ていたら、permutationし直す。
3. ヒント（permutation結果）はいくらでも教えてあげる。
4. ncの向こう側の人が、secretをわかって入力したら、FLAGを教えてあげる。  

つまり、解答者は
1. 文字列に含まれる15文字は、最初のヒントでわかる。
2. secretのk文字目は、 ヒント（permutation結果）でk文字目に来ない文字である。
と考えて文字列を推定すればよい。

### 2.解く
1. ヒントを集める。
ncで出題サーバにつなぎ1、Enterの順にキーボードを叩くと、1個ヒントを出力してくれる。ヒントをたくさんほしいので、たくさん1とEnterを叩く必要がある。
どうしても自動化できなかったので、teeでファイルに出力しつつ、1とEnterを40回ほど叩いた。
```
$ nc 104.199.xx.xx 55555 |tee hoge
```

2. 集めてきたヒントを整形する。gauche scriptで処理する予定なので、文字列のリストにする。
```
$ rep hint: hoge |awk 'BEGIN{print "(define input-lst ("}{print "\""$3"\""}END{print "))"}' >input.txt
```
なお、リストにするためのシングルクォートはemacsで手入力した。
その結果、input.txt は次のようになっている。
```scheme:input.txt
(define input-lst '(
"Uh*c}YVeA<ZNXo{"
...
"Y}{VoUeXZ*<cANh"
))
```

3. コードを書きながら走らせる
以下はスクリプトとして走らせるのではなく、emacsで開いて、各行（各S式）でemacs内で走らせた（evalした）。
推定結果（eval結果）は、emacsの\*scheme\* bufferに表示される。今回は、ncに手で打ち込めばよいので、単に各位置の文字の候補のリストを表示させた。
```
(define N 15)
(define cand (string->list (car input-lst)))
(define input
  (map
   string->list input-lst))

(define (iter cand-list it-input)
  (if (null? it-input)
      cand-list
      (iter 
       (map
        (lambda (x y) (remove (lambda (z) (eq? y z)) x))
        cand-list (car it-input))
       (cdr it-input))))
(iter (make-list N cand) input)
```

4. テストデータを走らせたら、一部の場所（k文字目）で候補が2文字以上残った。しかしながら、他の場所で確定した文字は別の場所には入らないため、それによって（人間の暗算で）、その場所（k文字目）の文字も確定できるのでよいことにした。
もう一度ncで問題サーバにつないで1とEnter Keyを40回押してコードを走らせて回答を入力したら、FLAGが表示された！

### 3. 補足
permutationと言ったら、この小説を紹介したい。
グレッグ イーガン著『順列都市』。原題は"Permutation City"。私は大好き。
https://www.amazon.co.jp/%E9%A0%86%E5%88%97%E9%83%BD%E5%B8%82-%E4%B8%8A-%E3%83%8F%E3%83%A4%E3%82%AB%E3%83%AF%E6%96%87%E5%BA%AB-SF-2-1/dp/4150112894
Reactを使った初めてのアプリ作成

## 概要
Reactで始めてアプリを作成したので、その際の学びを記事にしました。
Reactの学習を始めたばかりのため、間違い等あるかもしれませんが、その際は優しくご指摘・アドバイス等頂けると幸いです。
よろしくお願いします。

## 対象読者
- これからReactの学習を始めたい方
- 始めてReactでアプリを作ろうと考えている方

## 本記事の内容
Reactで始めてアプリを作成する際に学んだこと

# 何をやったのか
大まかに以下の要件を持ったアプリを作成しました。
- 学習時間と学習内容を登録できる
- フォームに入力されている内容を動的に表示する（確認用）
- 登録をしたらフォームが初期化される
- フォームを入力せずに登録ボタンを押すとエラーが表示される
- 記録した勉強の時間を合計して表示する

|　　　　　　　　  　　　　　　　　   　　　　　　　　　　　　　　　　　　　　　|
|:-:|
|![movie](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1838981/a72bb39c-be8a-dc4d-d3c2-c3d11fa21fae.gif "movie")|
<font color="gray">完成品</font>

# 難しかった点
今回苦労した点としては、`state` でのデータの管理についてです。
今回作成したアプリでは学習内容と学習時間という二つの入力内容があり、それぞれをセットに一つのレコードとして扱わなくてはなりません。
例えば学習内容という一つの入力内容であれば特に悩むことはなく、以下の記述でデータの持ち方を実現できます。

```javascript
// 学習内容を保持するstate
const [newTitle, setNewTitle] = useState("");
// 入力された値を保存する処理
const handleTitleChange = (e) => {
    setNewTitle(e.target.value);
  };
// 入力フォーム
<input
  type="text"
  id="title"
  name="title"
  placeholder="学習内容を入力"
  value={newTitle}
  onChange={handleTitleChange}
/>
```

ただ、今回に関しては入力内容が二つあり、それを一意のレコードとして扱わなければならないため、少し混乱してしまいました。
この点に関しては以下の方法で実現することができました。

```javascript
// 学習内容と学習時間を保持するstate
const [records, setRecords] = useState([]);
// 学習内容を保持するstate
const [newTitle, setNewTitle] = useState("");
// 学習時間を保持するstate
const [newTime, setNewTime] = useState("");

// 学習内容を保存する処理
const handleTitleChange = (e) => {
  setNewTitle(e.target.value);
};

// 学習時間を保存する処理
const handleTimeChange = (e) => {
  setNewTime(e.target.value);
};

// 学習内容と学習時間を一つのレコードとして保存する処理
setRecords([...records, { title: newTitle, time: Number(newTime) }]);
```

学習内容を保持する`state`、学習時間を保持する`state`に加え、それらを一つの配列として保持する`state`の合計3つ用紙する方法です。
ただ、実装し終わった後にもう少しスマートにできてないかと思い色々と思考錯誤した結果、以下のやりかたでも実現できそうです。

```javascript
const [records, setRecords] = useState([]);
const [newRecord, setNewRecord] = useState({ title: "", time: "" });

const handleInputChange = (e) => {
  const { name, value } = e.target;
  setNewRecord(prev => ({ ...prev, [name]: value }));
};
```
自分としてはまだこの辺が曖昧になってしまっているのでさらに学習を深めていきたいと思います。


# 成長を実感した点
今回のアプリ制作を通してReactの処理の流れをだいぶ追えるようになったと感じています。
具体的には、以下の二点です。
- 定義した関数をどのタイミングで呼び出すのか
- `state`にどのように値が入っていくのかなどです

実際にアプリを0から作ってみないとこの部分の成長は実感できなかったと思うので、成長が感じられてよかったです。

# JISOUのメンバー募集中
プログラミングコーチングJISOUではメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか？

気になる方はぜひHPからお願いします👇

https://projisou.jp
ニューラルネットワークの基礎

# ニューラルネットワークの基礎

## 1. 正規化線形関数 (ReLU)

ReLU関数は、入力が0未満の場合は0を出力し、0以上の場合はそのままの値を出力します。

$$
h = f(u) = \max(0, u)
$$

式としては以下のように表せます：

$$
f(u) =
\begin{cases} 
0 & (u < 0) \\
u & (u \geq 0) 
\end{cases}
$$

グラフで表すと次のようになります：

```
        h
        |
     3  |          /
        |         /
     1  |        /
        |_______/________
               0      u
```

## 2. ソフトマックス関数 (分類の出力層)

ソフトマックス関数は、複数のクラスの確率分布を出力するために使われます。出力の総和は常に1です。

$$
h = f(u) = \frac{ \exp(u_i) }{ \sum_{i=1}^{n} \exp(u_i) }
$$

例として、以下のAとBという2つのニューロンがあった場合、確率は次のようになります：

- A: \( u = 2.0 \)
  - \( \frac{ e^{2.0} }{ e^{2.0} + e^{1.5} } \approx 0.622 \)
- B: \( u = 1.5 \)
  - \( \frac{ e^{1.5} }{ e^{2.0} + e^{1.5} } \approx 0.378 \)

## 3. ニューラルネットワーク内の計算手順

### STEP 1: 線形変換

入力層から中間層へ、そして中間層から出力層への信号を線形変換します。

### STEP 2: 非線形変換

各層における非線形変換を行います（例：ReLU）。

### STEP 3: 目的関数（損失関数）の計算

予測値と目標値との差を計算し、それを最小化するようにモデルを最適化します。以下は回帰問題における平均二乗誤差 (Mean Squared Error: MSE) です。

$$
L = \frac{1}{N} \sum_{n=1}^{N} (t_n - y_n)^2
$$

## 4. 分類問題における交差エントロピー

分類問題では、損失関数として交差エントロピーを使用します。

$$
L = -\frac{1}{N} \sum_{n=1}^{N} \sum_{c=1}^{C} t_{nc} \log y_{nc}
$$

ここで、\( t_{nc} \) は目標値、\( y_{nc} \) は予測値です。具体例として、4クラス分類の場合、目標クラスがDであった場合の損失計算を示します。

## 5. 逆伝播法と最急降下法

### 逆伝播

逆伝播法では、損失関数に基づいてパラメータを更新します。具体的には、最終的な予測誤差を各層に逆伝播させ、各重みを更新します。

### 最急降下法

最急降下法は、パラメータの勾配に基づいてモデルの重みを更新するアルゴリズムです。

1. 初期のパラメータをランダムに設定
2. 勾配を計算
3. パラメータを更新

学習率 \( \rho \) は更新幅を決定します。学習率が大きすぎると発散、小さすぎると収束が遅くなります。

局所解ではなく、最適解を見つけることが重要です。

## 6. 参考文献
- [ニューラルネットワークの解説動画](https://youtu.be/TrRHMjhlq_w?si=XcLQ4PHf11sTbJSX)
初ハッカソンに参加してみた

## はじめに
今回は、初めてハッカソンに参加した経験を共有したいと思います。

## 経緯
ハッカソンに参加しようと思ったきっかけは、エンジニア1年目として自分のスキルがどれほど通用するのか試したかったからです。
唯一心配だったのは、2日間開催されるイベントのうち、2日目にしか参加できなかったことです。それでも、コーチの方の後押しもあり、思い切って参加することにしました！

## テーマ
今回のハッカソンのテーマは「AIで日常を魔法に」でした。
そこで私が、今回作成したのは「自動文章作成アプリ」です。

このアプリを作成しようと思った理由は、
私自身がメールやチャットの文章を打つときに、言葉の言い回しや語尾は何が良いのかなど、<font color='lightgreen'><strong>たった１つのタスクに対して無駄に時間を費やすことが多かったからです。</strong></font>

そして、私のように<font color='lightsalmon'><strong>「文章を考えるのが苦手で、時間を効率的に使いたい」</strong></font>と考える人に役立つものを作りたいという思いもありました。

## 作成したアプリ

以下が今回作成したアプリです。
発表時から少し修正しましたが、基本的な構成は変わっていません。
文章の内容以外はリストボックスで選択できるようにし、入力時間を短縮しています。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3851095/0ce53c7a-90d1-7ae6-2472-832e82e87471.gif" width=70% >

AIには、 `Gemini` を使用しました。
特に、`Gemini API`を利用するのが今回苦労しましたが、他の受講生や参考記事を活用し、発表ギリギリでなんとか形にすることができました。

今後は、データベース連携や条件分岐の追加など、さらに機能を拡張していきたいと考えています。



[生成AI - Gemini](https://gemini.google.com/?utm_source=google&utm_medium=cpc&utm_campaign=2024jaJP_gemfeb)





## 最後に
2日目しか参加できませんでしたが、短期間でアプリを作り上げるという貴重な経験ができました。結果として、思ったよりも形にできたことで自信にも繋がりました。

そして何より、他の参加者の発表を聞けたのが一番の収穫です。参加された方々の発想力やプレゼン力には驚かされることが多く、今後の目標も明確になりました。次回のハッカソンに向けて、ビジネス力や技術力をさらに高めていきたいです！

## 参考サイト

https://qiita.com/o-ga/items/d901351e948bb3d8928f

https://qiita.com/kitao6/items/e7d74ef1b262ee049254

# JISOUのメンバー募集中
プログラミングコーチングJISOUではメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか？

気になる方はぜひHPからお願いします👇

https://projisou.jp
初学者が頑張ってエラーを直したので共有する。（MUI）

## 実装イメージ
![スクリーンショット 2024-09-23 172625.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3809793/f0c42cc3-27fd-5753-7025-c6eadc840a1f.png)

## エラーが出たコード

```ts:Err.tsx
<TextField
        id={"select-type"}//エラーが出て追加してみたが直らなかった
        label="グラフの範囲"
        select
        fullWidth
        value={selectedType}
        onChange={handleChange}
      >
        <MenuItem value="monthExpense">月間</MenuItem>
        <MenuItem value="yearExpense">年間</MenuItem>
      </TextField>
```

### エラーコードの問題点
最初のコードでは、`TextField`コンポーネントに対して`label`が指定されていますが、`label`の`for`属性に相当するもの（つまり`id`属性）は、`"select-type"`になっています。しかし、この設定では、`TextField`内の`label`が正しく`select`フィールド（プルダウンメニュー）と関連付けられていません


## エラー内容
問題なく動作はするがコンソールにエラーが出てしまう

:::note alert
The label's for attribute doesn't match any element id. This might prevent the browser from correctly autofilling the form and accessibility tools from working correctly.
To fix this issue, make sure the label's for attribute references the correct id of a form field.

日本語
この問題は、<label>タグのfor属性がフォームフィールドのidと一致していないことを示しています。このため、ブラウザがフォームを正しく自動入力できなくなったり、アクセシビリティツールが正常に機能しなくなる可能性があります。

この問題を解決するには、<label>タグのfor属性が、フォームフィールドの正しいidを参照していることを確認してください。
:::

## 完成系コード
```ts:Fixed.tsx
<FormControl fullWidth>
        <InputLabel id="type-select-label">グラフの範囲</InputLabel>
        <Select
          labelId="type-select-label"
          id="type-select"
          value={selectedType}
          label="グラフの範囲"
          onChange={handleChange}
        >
          <MenuItem value="monthExpense">月間</MenuItem>
          <MenuItem value="yearExpense">年間</MenuItem>
        </Select>
      </FormControl>
```

### 変更点
1.`InputLabel`に`id`を持たせ、これを`Select`の`labelId`に関連付けた。

2.`Select`の`labelId`プロパティと`InputLabel`の`id`が一致しているため、ラベルとフィールドが正しくリンクされている。

### 修正点
修正後のコードでは、`FormControl`と`InputLabel`、`Select`の組み合わせを使用しています。この構造では、`InputLabel`に`labelId="type-select-label"`を指定し、その`labelId`を`Select`コンポーネントの`labelId`プロパティと一致させることで、ラベルと選択フィールドが正しく関連付けられます。また、`Select`には`id="type-select"`があり、このidを使ってフォームフィールドが識別されるようになっています。

これで、エラーメッセージが出なくなりました！参考になってくれればうれしいです。
【脱ぬるぽ】代数的データ型のすすめ

# はじめに

最近学んだ代数的データ型が有用だったのでまとめます！
参考にした記事は以下の通りです
> [Java21とKotlinの代数的データ型 & パターンマッチの紹介と本当に嬉しい使い方](https://speakerdeck.com/yuitosato/algebraic-data-type-in-java-and-kotlin-happy-use-of-pattern-match)



# 代数的データ型とは
代数的データ型とは、複数の型を組み合わせることで表現されるデータ型のことで、主に直積集合と直和集合によって表されます。

## 直積集合
直積集合は代数学における**AND**（かつ）と同義の集合です。いわば集合Aと集合Bの掛け算です。
```math
A\times B = \lbrace (a,b) | a \in A, b\in B\rbrace
```
要するに構造体のことです。
よく使われる具体的な例として「名前」と「年齢」のデータ型があります。
```Java:cap.java
class Person{
    String name; //名前
    int age; //年齢
}
```
この時、名前の集合をA,年齢の集合をBとして、直積集合について以下で表現できます
```math 
\displaylines{
 \lbrace佐藤さん,鈴木さん、高橋さん \rbrace \in A \\
 \lbrace 21, 23, 26 \rbrace \in B \\

}
```
<img width="auto" alt="直積集合" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2022270/f13360a6-d463-4540-51f9-065ce288851e.png"> 

このように全ての集合の組み合わせを積で表すことができます。

## 直和集合
直和集合は集合理論における**OR**(または)を表したものになります。（ただしAかつBは0です）
```math 
U = A \cup B \quad ( A \cap B = 0)
```
図で表すのこのような集合になります。（集合Aと集合Bで重なり合う部分がない集合）

<img width="400" alt="直和集合" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2022270/1a10b986-c25f-b23e-5f21-bbaa41bdef01.png"> 

例として、Javaではsealed ~ permits ~ が直和を表現きます。
```Java:cup.java
sealed interface S 
    permits A,B,C{}
```
直和表現
```math 
S = A \oplus B \oplus C
```
この場合ならず集合Sの値はA or B or Cの集合に属することになります。

## 代数的データ型の表現
先ほどの直積集合と直和集合の組み合わせによって表現するのが代数的データ型です。
例えば、先ほどの直和表現の例で、集合Cの要素が「int型のnum」と「String型のstr」の値を保持するとすると、
```math
S = A \oplus B \oplus (num \times str)
```
の式によって集合Sを表現することができます。
代数的データ型を使うメリットとして、取りうるデータの範囲を任意のデータ型で表現することができます。
ここまで式だけの説明では微妙なので実際の表現を見て行きましょう（ここからが本題）




# 実際の表現の仕方
実際に代数的データ型を活用する例を見て行きましょう。
タスク管理アプリについて考えます。
* タスクの状態はInProgressとCompleted
* タスクはタスク名「title」(String)を保持し、タスクの終了時刻「completedAt」(LocalDateTime)を保持する

### 代数的データ型を使用しない場合
TaskのクラスにはTitleとLocalDateTimeが存在することになります。
```Java:task1.java
class Task {
    String title;
    LocalDataTIme completedAt;
}
```
このTaskクラスを呼び出すときに、タスクまだ完了していない場合はTaskクラスのcompletedAtの持つ値は**Null**となります。タスクが進行中の時は終了時刻を持っていないため、ぬるぽのリスクがあります。

### 代数的データ型を使用する場合
Task全体を代数的データ型と捉え、「InProgressTask」と「CompletedTask」の積和集合として表現する
```math
Task = InProgressTask \oplus CompletedTask
```
```Java:task2.java
sealed interface Task 
    permits InProgressTask, CompletedTask{
    String title();
}

record InProgressTask(
    String title
) implements Task{}

record CompletedTask(
    String title,
    LocalDateTIme completedAt
) implements Task{}
```
このようにTaskを表現すると,タスクが進行中(InProgressTask)の時には終了時刻(completedAt)をそもそも持たないため、不要なぬるぽのリスクを避けることができます。
また、データ不整合があった場合、コンパイルフェーズで検出できます。

```java
new InprogressTask("買い物");
new CompletedTask(
    "買い物",
    LocalDateTime.now()
);


//コンパイルエラー
new InProgressTask(
    "買い物",
    LocalDateTime.now()
);
```


# おわりに
不要なぬるぽのリスクを代数的データ型を使って排除した方が将来的なリスク低減につながると思います！自身のコードでもしっかり取り入れて行きたいと思います！
拙い記事ですが最後まで読んでいただきありがとうございました！＞＜
（意見・コメント・ご指摘などもよろしくお願いいたします！）

# 参考文献
* [Java21とKotlinの代数的データ型 & パターンマッチの紹介と本当に嬉しい使い方](https://speakerdeck.com/yuitosato/algebraic-data-type-in-java-and-kotlin-happy-use-of-pattern-match)
Go言語のSNMPパッケージgosnmpでTRAPを受信する方法

# はじめに

Go言語のSNMPパッケージgosnmpを紹介するシリーズの４回目です。

１回目は

https://qiita.com/twsnmp/items/45a02243ce3fbca2a55f

です。

今回は、TRAPを受信する方法です。


# TRAPを受信するプログラム

TRAPを受信するプログラムは

```go
package main

import (
	"log"
	"net"

	g "github.com/gosnmp/gosnmp"
)

func main() {
	tl := g.NewTrapListener()
	tl.Params = &g.GoSNMP{}
	tl.OnNewTrap = func(s *g.SnmpPacket, u *net.UDPAddr) {
		log.Printf("snmp trap from=%s trap=%+v", u.IP.String(), s)
	}
	err := tl.Listen("0.0.0.0:162")
	if err != nil {
		log.Fatalln(err)
	}
}

```

これだけで、SNMPv2のTRAPを受信できます。


### リスナーの作成

```go
tl := g.NewTrapListener()
```

でTRAPのリスナーを作成します。

### パラメーター設定

```go
tl.Params = &g.GoSNMP{}
```

パラメータをSNMP v3用に設定すれば、SNMPv3のTRAPも受信できます。

### 受信関数の設定
TRAPを受信した時に呼ばれる関数を設定します。

```go
tl.OnNewTrap = func(s *g.SnmpPacket, u *net.UDPAddr) {
		log.Printf("snmp trap from=%s trap=%+v", u.IP.String(), s)
	}
```

受信したTRAPパケットと、TRAPの送信元の情報が関数のパラメータです。


# 受信結果

```
2024/09/23 17:26:36 snmp trap from=192.168.1.210 trap=&{Version:2c MsgFlags:NoAuthNoPriv SecurityModel:SnmpV3SecurityModel(0) SecurityParameters:<nil> ContextEngineID: ContextName: Community:public PDUType:SNMPv2Trap MsgID:0 RequestID:932629191 MsgMaxSize:0 Error:NoError ErrorIndex:0 NonRepeaters:0 MaxRepetitions:0 Variables:[{Value:45384 Name:.1.3.6.1.2.1.1.3.0 Type:TimeTicks} {Value:.1.3.6.1.4.1.8072.4.0.2 Name:.1.3.6.1.6.3.1.1.4.1.0 Type:ObjectIdentifier} {Value:.1.3.6.1.4.1.8072.4 Name:.1.3.6.1.6.3.1.1.4.3.0 Type:ObjectIdentifier}] Logger:{logger:<nil>} SnmpTrap:{Variables:[] IsInform:false Enterprise: AgentAddress: GenericTrap:0 SpecificTrap:0 Timestamp:0}}
2024/09/23 17:26:36 snmp trap from=192.168.1.210 trap=&{Version:2c MsgFlags:NoAuthNoPriv SecurityModel:SnmpV3SecurityModel(0) SecurityParameters:<nil> ContextEngineID: ContextName: Community:public PDUType:SNMPv2Trap MsgID:0 RequestID:296096074 MsgMaxSize:0 Error:NoError ErrorIndex:0 NonRepeaters:0 MaxRepetitions:0 Variables:[{Value:4 Name:.1.3.6.1.2.1.1.3.0 Type:TimeTicks} {Value:.1.3.6.1.6.3.1.1.5.1 Name:.1.3.6.1.6.3.1.1.4.1.0 Type:ObjectIdentifier} {Value:.1.3.6.1.4.1.8072.3.2.10 Name:.1.3.6.1.6.3.1.1.4.3.0 Type:ObjectIdentifier}] Logger:{logger:<nil>} SnmpTrap:{Variables:[] IsInform:false Enterprise: AgentAddress: GenericTrap:0 SpecificTrap:0 Timestamp:0}}
2024/09/23 17:27:08 snmp trap from=10.30.1.11 trap=&{Version:2c MsgFlags:NoAuthNoPriv SecurityModel:SnmpV3SecurityModel(0) SecurityParameters:<nil> ContextEngineID: ContextName: Community:public PDUType:SNMPv2Trap MsgID:0 RequestID:4400 MsgMaxSize:0 Error:NoError ErrorIndex:0 NonRepeaters:0 MaxRepetitions:0 Variables:[{Value:150700508 Name:.1.3.6.1.2.1.1.3.0 Type:TimeTicks} {Value:.1.3.6.1.4.1.9.9.131.2.0.1 Name:.1.3.6.1.6.3.1.1.4.1.0 Type:ObjectIdentifier} {Value:[7 232 9 23 17 27 12 0 43 9 0] Name:.1.3.6.1.4.1.9.9.131.1.1.1.0 Type:OctetString}] Logger:{logger:<nil>} SnmpTrap:{Variables:[] IsInform:false Enterprise: AgentAddress: GenericTrap:0 SpecificTrap:0 Timestamp:0}}

```

受信したTRAPの数値のOIDは、

https://qiita.com/twsnmp/items/2986b696cced09820291

の記事で紹介した方法を使って名前に変換できます。


# TRAPの受信ができない時

以下の記事が参考になると思います。

https://note.com/twsnmp/n/nf161d8dd36f3
ソフトウェアファースト第2版（第4章）の読書メモ

# はじめに

前回に続き、ソフトウェアファーストの第2版を読んで思ったことを書き連ねていきます。

https://amzn.asia/d/2fxjKjS

第4章は、「手の内化に必要な今どきの開発手法」です。

# アセンブリ言語

ITの進化の歴史について、紹介されていたものの中に、アセンブリ言語がありました。

昔は基本情報技術者試験で、CASL2という言語がありました。
それを学ぶことで、高級言語だと1行で済んでしまう、文字列の検索や置換の処理の内部で、どのような動きをしているのかがわかるため、とてもためになった記憶があります。

シミュレータや過去問があるので、この辺りを学びたい方は、CASL2に取り組むことをおすすめします。

# ソフトウェア技術に対する知識を網羅的に把握する

本書では、技術から開発プロセス、テストに至るまで、歴史やトレンドが幅広く紹介されています。これら全てを把握することは、なかなか難しいと思います。

私のおすすめは、情報処理技術者試験です。これの応用情報技術者くらいまでは抑えておくことが望ましいです。

https://www.ipa.go.jp/shiken/index.html

あとは、参画するプロジェクトの特性や自身の役割に合わせて、都度補強すると良いでしょう。


# 採用する技術

個人的には、新しい技術というより、できるだけメジャーな技術を使って欲しいかなと思います。（それで賄えるのであれば）

メジャーな技術を採用するメリットとしては、技術者にとって嬉しいだけでなく、外部から人員を調達しやすくなります。

あとは、Webアプリを書くなら、サーバサイドは、まずNode.js(Typescript)を検討に入れて欲しいですね。Node.jsのメリット・デメリットは、様々ありますが、使用する言語の数は抑えたいものです。

# グリーンコーディング

地球環境のため、メモリやCPUの使用を最適化するソフトウェア開発を行いましょうという考え方です。
「グリーンIT」は主にサーバ（ハードウェア）に対して適用される言葉ですが、こちらは、ソフトウェアに対して適用する考えのようです。

コードを書くときは、地球環境よりも、人にとっての読みやすさを優先して欲しいかなと思いました。それが保守の時間短縮につながり、結果的にエネルギー効率化をもたらすでしょう。

グリーンコーディングについては、以下の記事が参考になります。

https://takoratta.hatenablog.com/entry/2023/06/20/080240

# まとめ

第4章の後半では、アジャイル、 AI、IoT、DevSecOps、カオスエンジニアリング、各種デプロイメント手法など、様々な技術・開発手法が目白押しです。
改めてソフトウェア開発の難しさ、複雑さを感じました。
GoにおけるGoogle Sheets APIのリトライ処理

## 要約
1. リトライ処理には、`github.com/hashicorp/go-retryablehttp`を使う
1. ただし、直接利用できないので、StandardClientからTransportを取得して、`NewTransport()`を使ってGoogle APIsで利用可能な状態に変換する
1. Sheetsサービスには、`WithHTTPClient()`でリトライ可能なクライアントを設定する

## 本文
Google Sheets APIには、当然ながら呼び出し回数の制限がある。1分毎に呼び出し可能回数が回復するので、適切にリトライを行うことで、処理の失敗を抑えることができる。

リトライ間隔の戦略としては、[Exponential backoffが挙げられていた](https://developers.google.com/sheets/api/limits#quota)。
有名なパッケージでは、`github.com/cenkalti/backoff/v4`を使うことで、Exponential backoffを使ったリトライ処理を書くことができる。
  今回は、`github.com/hashicorp/go-retryablehttp`（以下、retryablehttp）を利用する。理由としては以下。
- `http.Client`を設定するだけで、リトライが行われるので簡単
- HTTPのステータスコードが理解されてリトライが行われる
- `Retry-After`ヘッダーをサポートしている

retryablehttpを使う際の注意点としては、`StandardClient()`で生成した`http.Client`をそのままSheets APIのサービスに利用できない。  
`option.WithHTTPClient(retryClient.StandardClient())`のように渡してしまうと、シークレットが正しく設定されないためAPIを利用することができなくなるためである。  
対策としては、StandardClientからTransportを取り出し、`google.golang.org/api/transport/http`パッケージの`NewTransport()`を使って、Google APIsで利用可能な状態にする。

### サンプルコード
```go
package main

import (
	"context"
	"fmt"
	"net/http"

	"github.com/cockroachdb/errors"
	"github.com/hashicorp/go-retryablehttp"
	"github.com/samber/mo"
	"google.golang.org/api/option"
	"google.golang.org/api/sheets/v4"
	googlehttp "google.golang.org/api/transport/http"
)

const (
	spreadsheetID = ""
	sheetID       = 0
)

type Client struct {
	srv *sheets.Service
}

func main() {
	ctx := context.Background()
	retryClient := retryablehttp.NewClient()
	retryClient.RetryMax = 5
	standardClient := retryClient.StandardClient()
	transport, err := googlehttp.NewTransport(context.Background(), standardClient.Transport)
	if err != nil {
		panic(err)
	}
	service, err := sheets.NewService(ctx, option.WithHTTPClient(&http.Client{
		Transport: transport,
	}))
	if err != nil {
		panic(err)
	}
	c := Client{srv: service}
	res := c.getSheetTitle(ctx, spreadsheetID, sheetID)
	if res.IsError() {
		panic(res.Error())
	}
	fmt.Println(res.Get())
}

// getSheetTitle シート名を取得
func (c Client) getSheetTitle(ctx context.Context, spreadsheetID string, sheetID int64) mo.Result[string] {
	s, err := c.srv.Spreadsheets.Get(spreadsheetID).Context(ctx).Do()
	if err != nil {
		return mo.Err[string](errors.Wrap(err, "get spreadsheet"))
	}
	for _, v := range s.Sheets {
		if v.Properties.SheetId == sheetID {
			return mo.Ok(v.Properties.Title)
		}
	}
	return mo.Err[string](fmt.Errorf("sheet not found"))
}
```
rclone で作ったフォルダーを Windows のファイル履歴で使う

## 前提

以下は実施済みとします。

1. Rclone を入れる
1. WinFsp も入れる
1. (rclone config で) Rclone の設定ファイルを作る


## マウントする

G ドライブにマウントした例です (Google Cloud Storage を使っています)。設定の名前は settei_no_namae としています。

```terminal
C:\Users\hanotch>rclone mount --vfs-cache-mode writes --gcs-bucket-policy-only settei_no_namae: G: --network-mode
The service rclone has been started.
The service rclone has been stopped.
2024/09/16 19:12:00 ERROR : G:: Unmounted rclone mount
```

`--network-mode` を指定しないとファイル履歴はバックアップ先として利用させてくれないようで、ここでハマりました。`--volname` によって暗黙的に `--network-mode` を使用しても大丈夫です。その辺はお好みで。


## アンマウント時の ERROR ログについて

スタンバイに入るたびに ERROR が出ているのがちょっと気になりますが、`-vv` を指定して実行すると以下のように「Unmounted successfully」とログが出ていること、[Non-zero exit status from rclone mount, even with clean unmount](https://forum.rclone.org/t/non-zero-exit-status-from-rclone-mount-even-with-clean-unmount/38884) を見る限り問題なさそうなことから、気にしないようにしています。

```
2024/09/23 16:43:46 DEBUG : Unmounted successfully
2024/09/23 16:43:46 DEBUG : vfs cache: cleaner exiting
2024/09/23 16:43:46 ERROR : G:: Unmounted rclone mount
```


## ファイル履歴を設定する

コントロール パネル の システムとセキュリティ の ファイル履歴でファイルのバックアップ コピーを保存 より、作成したネットワーク ドライブをコピー先として設定してください。
いまさらWebSocketに触ってみる

## Websocketとは

クライアントとサーバー間の間で双方向通信を可能にするプロトコルです。
- 一度接続が確立されると、新しいリクエストを送る必要なく通信できます
- ws://(暗号化なし)またはwss://(暗号化あり)から始まります

活用シーンは、チャットアプリやリアルタイムのゲームなど即時性が求められる場面で活用されます。

## WebsocketとHTTPとの違い

- HTTP：クライアントからのリクエストに対してサーバーが応答する一方向の通信
- WebSocket：クライアントとサーバーが双方からデータを送信可能

## 触ってみた結果

２つのブラウザを立ち上げて、リアルタイムで別ブラウザにデータが送信されるかを検証しました。

1. ２つのブラウザを開く
2. 片方のブラウザのinputボックスにデータを入力する
3. 「送信」ボタンを押す

結果、送信ボタンを押した後、もう片方のinputボックスのデータが即座に更新されました！

![websocket.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/689023/a8cf62e3-f766-a90c-3fa3-b8ea9a5b0b11.gif)

## 前提

下記の技術で構成しています。

- フロント
  - Typescript
  - React

- バックエンド
  - Express
## ソースコード

### パッケージのインストール

Socket.ioライブラリを使用して検証しました。

https://socket.io/ 

バックエンド
```
npm i socket.io
```
フロントエンド
```
npm i socket.io-client
```
### バックエンド

```typescript
import express from 'express';
import 'dotenv/config'
import { Server } from 'socket.io';
import { createServer } from 'node:http';

const PORT = process.env.PORT;

const app = express();
const server = createServer(app);
const io = new Server(server, {
  cors: {
    origin: '*',
    methods: ['GET', 'POST']
  }
});

app.use(express.json());
app.use(express.urlencoded({ extended: true}));

io.on('connection', (socket) => {
  socket.on('chat message', (msg) => {
    io.emit('chat message', msg);
  });
})

server.listen(PORT, () => {
  console.log(`Server is running on port ${PORT}`);
});
```

### フロントエンド
```react
import React, { useEffect, useState } from 'react';
import io from 'socket.io-client';

export const Websocket = () => {
  const [message, setMessage] = useState('');

  const socket = io('ws://localhost:3000');
  useEffect(() => {
    // WebSocket接続を確立
    socket.on('connect', () => {
      console.log('WebSocket connected');
  });
    // コンポーネントがアンマウントされたときにクリーンアップ
    return () => {
      socket.disconnect();
    };
  }, [socket]);

  const handleInputChange = (event: { target: { value: React.SetStateAction<string>; }; }) => {
    setMessage(event.target.value);
  };

  const submitMessage = () => {
    socket.emit('chat message', message);
  }

  socket.on('chat message', (msg) => {
    setMessage(msg)
  })

  return (
    <div>
      <div>下記に入力</div>
      <input type="text" style={{border: '1px solid black'}} value={message} onChange={handleInputChange}/>
      <button onClick={submitMessage}>送信</button>
      <div>下記に出力</div>
      <div>{message}</div>
    </div>
  );
}
```

## 説明
```
const socket = io('ws://localhost:3000');
```
サーバーへの接続を作成し、インスタンスを返却しています。
```
socket.emit('chat message', message);
```
データをサーバーに送信しています。
上記のコードは第一引数で イベントの名前を指定し、第二引数でデータをサーバーに送信しています。
```
socket.on('chat message', (msg) => {
```
第一引数に指定したイベントをリッスン(特定のイベントや状態変化が起きるのを待ち受けている状態)します。

## 参考

https://socket.io/docs/v4/tutorial/api-overview 

https://socket.io/how-to/build-a-basic-client 

https://zenn.dev/nameless_sn/articles/websocket_tutorial
VSCodeでGoのデバッグができない　Failed to launch: could not launch process: unsupported architecture of windows/i386 - only windows/amd64 windows/arm64 are supported

# はじめに
VSCodeでGoのデバッグが出来ないものかと色んな方々の記事を参考に試していました。

↓参考にした記事
https://qiita.com/momotaro98/items/7fbcad57a9d8488fe999

しかし記事のとおり準備をしてもデバッグができない…デバッグボタン押してもなんかへんなエラーポップアップが出る…？🤔

なんてことになりましたので備忘録残しました。

※原因はほんとしょうもなかったです笑

# 環境
Windows10

# 問題
冒頭でも書きましたが、上記記事などを参考にVSCodeでGoのデバッグをするための拡張機能、必要ライブラリ、launch.jsonの設定をしていざデバッグクリック！！！

……と進めたら以下VSCodeのエラーポップアップが出てきました😇

```
Failed to launch: could not launch process: unsupported architecture of windows/i386 - only windows/amd64 windows/arm64 are supported
```

# 解決方法
原因はエラーポップアップに書いてある内容そのままの意味で、GoのバージョンがサポートされていないものをWindowsでインストールして使ってたから、でした！！！~~(Copilot君に頼ってばっかじゃなくてちゃんとエラー内容翻訳して自分で確認しろ)~~

自分がインストールしていたのはこれ
↓
`go1.23.1.windows-386.msi`

最新バージョンの`go1.23.1`の部分だけ見てインストールしていたみたいです。
後ろの`windows-xxx`の部分も適切なものにしないと動いてくれないみたいですね。

なので、正しいGoのバージョンを再インストールします。

1. Windowsのコントロールパネル→プログラムと機能から、現在インストールされているGoをアンインストール

2. Goの公式ページからwindows-386ではない(amd64 or arm64のGo)バージョンのものをインストールしてくる



# おわりに
超初歩的なミスでした。。
エラーが出たらすぐにCopilotに頼る癖を治すべきですな。
銀行への生成AI導入における課題について元銀行員が考えてみる

# 銀行への生成AI導入の現状と課題

銀行にAIを導入する話、よく耳にしますよね。最近では「生成AIで事務効率化」なんて言葉もちらほら。銀行にとっては、業務効率化やミスの削減が重要課題です。しかし、元銀行員の視点から見ると、AI導入にはまだまだ高い壁があります。今回は、特に「銀行内の事務における事務相談チャットボット」の導入に焦点を当てて、その課題と可能性について語りたいと思います。

## 経歴紹介

まず簡単に私の経歴から。私は地方銀行で総合職として10年間勤務していました。現在はナレッジコミュニケーションにてエンジニアとして仕事をさせてもらっています。 
銀行員時代、支店では、国内為替、二線、外国為替、法人融資や個人ローン、そして法人渉外を担当。本部では事務効率化や事務相談対応、外国為替集中事務や企画に携わりました。個人渉外と窓口対応以外は大体経験しています。銀行業務の多方面からお話しできるかと思います。

## 銀行における事務相談チャットボット

生成AIを使った銀行業務の効率化でよく出るのが、「事務マニュアルを読ませて事務相談チャットボットを作る」というアイデア。現場で使う事務マニュアルは非常に膨大かつ複雑です。「マニュアル通りの事務」のはずなのに難易度高すぎで、新人の時は事務をしている時間と同じくらいやり方を調べることに時間を割くことがあります。では、マニュアルをAIに覚えさせ、ユーザーの困ったことに答えてくれるとなれば、業務効率が格段に上がるはず…ですよね？

でも、実際のところはどうでしょうか？銀行特有の背景から見ると、AI導入にはまだ多くの課題が残っています。

---

## 銀行特有の課題

銀行業務に生成AIを導入する際には、以下の特有の課題があります。

### 1. **ミスが絶対に許されない**

銀行はお客様の大切なお金を預かっています。たとえば、振込のミスや預金口座の誤処理は、直接的な損害や大きなトラブルにつながります。修正には多大なコストと時間がかかり、顧客の信頼も失われる危険があります。

また、銀行の評価システムは「**減点方式**」とよく言われます。通常の業務がスムーズに行われるのは当然のことですが、ミスやトラブルが発生した場合、査定や評価に影響が出ることがあります（なので責任のなすりつけ合いはよく起こります）。こうした環境下では、AIが間違った回答を提示するリスクを避けるため、導入には非常に慎重な対応が求められます。

### 2. **責任の所在を明確にする必要がある**

銀行の業務では、**誰がどの手続きに関わったのか**が明確に記録されます。これは、ミスやトラブルが発生した際に責任を特定するために重要です。AIが業務に関与する場合でも、その出した回答や判断に対して、誰がどのように従ったかを追跡できる仕組みが必要です。

AIが回答した内容に従って処理を行った場合でも、その結果が間違っていれば、最終的に人間が責任を問われます。こうした責任の明確化と記録管理は、AI導入において特に慎重に扱うべき課題です。

### 3. **マニュアルの複雑さと断片的な対応**

銀行の事務マニュアルは非常に詳細で、しばしば**一部を読むだけでは対応できない**ことが多いです。複数のページを横断して確認しなければならず、全体を理解して初めて正しい対応ができる場合があります。

たとえば、「この手続きを進めれば大丈夫」と思っていたところ、別のページに重要な追加手続きが記載されていて、それを見落としていたということがよくあります。さらに、法律対応が絡む場合、新たに書類作成が必要になることもあり、こうした手続きをAIにすべて対応させるのは非常に難しい状況です。

### 4. **例外処理が多すぎる**

銀行業務では「原則」と「例外」が多く存在します。例えば、「原則として本人が来店する必要があるが、やむを得ない場合は委任状で家族が代行できる」といった手続きがあります。しかし、「やむを得ない理由」や「家族の範囲」などの具体的な条件は無数に存在します。
* やむをえない理由 : 今入院していて意識がない状態であったり蒸発して連絡がつかなかったり、刑務所に入っていたり　など
* 家族の範囲 ：　本人の子供の配偶者であったり甥・姪の場合は？、近しい親族がいなくて遠い親戚しかいない場合は？など

このような例外処理をすべてAIに理解させることは非常に難しく、**現場の判断力**が依然として求められます。

### 5. **システムのレガシー化**

銀行では、 **古いシステム（レガシーシステム）** が今でも広く使われています。このシステムは新しい技術との統合が難しいことが多く、AIの導入にも支障をきたす場合があります。特に、複雑な業務や多数のシステムが絡む環境では、AIの統合には大きなコストがかかります。

---



## それでもAIには可能性がある

とはいえ、生成AIには大きな可能性も秘めていると感じていますし、自分が銀行員だった時代にあれば良かったなと思っています。ある程度指標になったり、どこを見ればいいか、自分の知識の補完とするのであれば十分いまでも役に立つと思います。
あとは、定型的な質問や簡単な事務手続きに関しては、AIが適切に対応できれば業務の大幅な効率化が期待できます。AIは、日常的な手続きや定型化された業務を迅速かつ正確に処理する能力があるため、その分人間が行うべき業務に集中できる環境を作り出すことができます。

また、技術の進歩によって、将来的には複雑な例外処理にも対応できる可能性があります。現時点では慎重な導入が求められますが、AIの活用範囲を徐々に広げていくことが銀行業務の効率化につながるかもしれません。

---

## 結論

銀行業務における生成AIの導入は、現段階ではまだ多くの課題を抱えていると感じていますが、非常に興味深い分野だとおもます。今後、技術の進化により、複雑な業務にも対応できるようになることが期待されています！銀行業務の特性を理解した上で、慎重にAIを導入し、業務効率化を図ることが必要です。


金融業界におけるAI活用事例について詳しく知りたい方は、こちらのサイトも参考にしてください。

https://www.ai-souken.com/article/banking-industry-ai-application-cases
.cppのコードを.mllにして、Mayaプラグインを読み込む

# コンパイラーってなに？コンパイルって何？どうして.cppで書いて配布したの？
やりたい事は単純なのが分かっているけど、
もう勉強不足が追いつかないTA志望のお時間。

今回はMayaでノードを追加するプラグインに関して。

https://mukai-lab.org/library/maya-cpp-plugin/
「プリフェッチってな～に？」ってきかれたときに困らない様に

# プリフェッチで行こう！！

宴会にて「飲み物が足りなくなりそう」になったとき、いちいち無くなってから頼むのが面倒くさいときに私はいつも「プリフェッチしとこうよ！」といいますが、これがなかなか非エンジニアの人には伝わらなくて毎回説明するハメになりますので、プリフェッチについて簡単に説明します。

## プリフェッチとは？

プリフェッチとは、あらかじめ必要**そうな**データをメモリからキャッシュに持ってきておくことです。
**そうな**ということはデータが今後すぐに必要かもしれないし、必要じゃ無いかもしれないし、ある種のギャンブルに近い事もあります。必要な時はどういう時かというと、処理が分岐無く続いているときじゃないでしょうか、そして必要じゃ無いと時はどういう時かというと分岐でデータを読む必要が無くなったとき等です。

メモリからキャッシュにデータが来ると言うことは「データがまもなく消費される」位置に来るわけです。

## 高等テクニック「たこわさプリフェッチ」

たこわさとは「蛸」と「山葵」が混ざっている宴会では欠かせない比較的低料金(私がお酒を飲んでいた20年前くらいは、の話ですが今は蛸が高いと聞いております)のおつまみの一種で、飲み物がすすみます。
しかし、大衆居酒屋等で提供されるたこわさはその量が少なく、5～6人以上で食べ始めるとあっという間におわってしまいます。
そこで出るのが「たこわさプリフェッチ」です。方法は簡単、「たこわさ」が来たときにすぐにそのまま店員さんに「たこわさ」を頼むのです。そうするとこちらでたこわさ(データ)を消費している間に新たなたこわさ(データ)が準備され食べ終わる頃には次のたこわさが店員さんによって運ばれてくるという好循環が生まれます。
つまり、データ(たこわさ)が消費され尽くされる前に次のデータ(たこわさ)がフェッチされるのです。

こうすれば処理(食べること)がストールする事無く進むので処理(飲み)がスムーズにすすみます。

## 応用テクニック「ビアプリフェッチ」

これに似た方法が「ビアプリフェッチ」です。つまりビールがくると次のビールを頼んでおく。これだけです。
しかしこれには罠があります。それは「グラス交換制」の飲食店の飲み放題などの場合にはこれは適用できません。残念。

## 皆さんは、積極的にIT用語を生活に取り入れていますか？

皆さんはこのように「プリフェッチ」とか単純に「フェッチ」、「投機的実行」など、日常生活で使って親しみを感じたりしていますでしょうか？

また、エンジニアと非エンジニアが混ざる、例えば同窓会などの場所で「プリフェッチ」を使ったときにきちんと説明できる準備はできていますでしょうか？

エンジニアは、生活もエンジニアリングするのです。

･･･というポエムでした。
おしまい。
BlueprintでCSVをランタイムにインポートしてDatatableを更新したい

## はじめに
欲しかったPlugin見つけたので紹介です

## どんなプラグイン？
BlueprintでフォルダPathから指定したCSVをインポートしてDatatableを更新する処理を実装したくて以下のプラグインを買いました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115955/e986267c-0981-fd83-1e2d-37ca0e3943a2.png)

https://www.unrealengine.com/marketplace/en-US/product/data-table-expand#

## 実装内容
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3115955/b1739e4d-b21a-a148-abfe-8c7d41aef87a.png)

自分はこのノードしか使っていません。
Shippingでパッケージ化した後もちゃんと動作しました、わーい。

## おわりに
自分は、制作中のゲームのデバッグを自分以外の人にお願いすべく今回導入しました。

スプレッドシート → CSV → DatatableのフローでインポートしたDatatableを使ってストーリー進行を管理しているのですが、スプシの記入ミスがちょこちょこありそうで...。

パッケージ化したゲームのデータとスプシを渡せば、エンジニアじゃなくてもスプシのミスを確認できるように！
いいね！

以上！

## 追伸
実際にゲームをリリースする際は、この仕組みは使わない予定です。

「コンテンツフォルダに、ゲームのストーリーが全て書かれたCSVファイルが入ってる...」
って事態は避けたいので（）
データベース設計で教えてもらったことの備忘録

# 設計の流れ

1. エンティティの整理
2. 属性の抽出
3. 正規化
4. ER図の作成

# 注意するべきこと

- 属性の抽出は、基本的に画面要件定義書をもとに項目を洗い出す。そこに画面には表示しない項目（削除フラグなど）を追加していく
- （好みではあるが、）管理画面から追加するようなレコードの場合、画面に表示しない場合でも管理者名をカラムとして持っておく
- 監視などで将来使うだろうという項目は入れておく。（YAGNIと反するのでどこまで入れるかは要検討）
- 想定されるフローを実行できるように属性の追加や正規化を行う
    - 特に検索、追加、削除が行えるように、それらの処理が明確となるようにする 
- ビューの実態はクエリ文であるため、多段ビューは作らないようにする（多段だと参照するたび複数のクエリ文が処理されるため、パフォーマンスが悪くなる）
気をつけろ奴らは遅れてやってくる

# はじめに

JISOU の課題で setState を使った勉強時間を登録するアプリを開発しました。
勉強内容と時間を入力して登録すると累計時間を求めることができます。

# 気づき

`setState` は関数（ `handleSubmit` ）が終わってから変数に反映される＝画面で確認できるので関数のなかで何か追加で対応したいことを書いても期待通りにはなりません。

この例では配列に連想配列を追加し、その配列を使って累計時間を求めますがどうしても `records` が空 配列になります。

```react
// NG パターン
function App() {
  const [time, setTime] = useState(0)
  const [records, setRecords] = useState([]);
  const [totalTime, setTotalTime] = useState(0);

  const handleSubmit = (e) => {
    const newRecord = { title, time:  parseInt(time, 10)}
    setRecords((prevRecord) => [...prevRecord, newRecord])

    // ここで累計時間を求めても空配列になる
    setTotalTime(records.reduce((acc, cur) => acc + cur.time, 0))
  }
}
```

# どうしたらいいか

累計時間は JSX の中で足し上げるといいと思いました。

```react
<div>合計時間：{records.reduce((acc, cur) => acc + cur.time, 0)} / 1000 (h)</div>
```

# 終わりに

昔の react は `bind(this)` が必要だったり、コンストラクタに `super` が必要だったり**お作法**が必要でしたが、いまはフックを使うのでなんてシンプルなんでしょう。ただ、便利さにつられてあれこれし始めるとタイトルの通り変数への反映は「遅れてやってくる」ので注意が必要です。

# JISOUのメンバー募集中
プログラミングコーチングJISOUではメンバーを募集しています。
日本一のアウトプットコミュニティでキャリアアップしませんか？

気になる方はぜひHPからお願いします👇

https://projisou.jp
Cookieとは何か？

Cookieほどプログラミング初心者を混乱させる用語はないでしょう。名が体を表していないですし、何か魔術的な雰囲気[^1]を醸し出しています。**ネット上で見られる情報も誤りがあったり本質を掴んでいないものがほとんど** です。そんなCookieをなるべく簡潔に説明してみようと思います。

## Cookieとは

> CookieとはWebサービスが **クライアントを識別するために使用する短い文字列** です。Webサーバからの応答にSet-Cookieヘッダがあると、クライアント(ブラウザ)は以降の要求の際にCookieヘッダに指定された文字列(Set-Cookieヘッダの値)を使用します。

できる限り簡潔に説明するなら **これですべて** です。

## なぜCookieを使うのか
Cookieを使う理由は、Web以前のシステムの知識があると理解しやすいでしょう。Web以前のクライアント/サーバシステムでは、クライアントはまずサーバに接続し、ユーザID/パスワードでログインします。サービスを使い終えるとログアウトし、接続を切断します。つまり、認証済みの単一の接続を通してサービスを受ける形態になります。ですので、サーバ側は認証後はその接続になりすましがないと信用することができます[^2]。

一方Webでは、ページや画像などにアクセスする度に接続/切断を繰り返すので、接続を信用することができません[^3][^4]。そこで発明された仕組みがCookieであり、Web黎明期の1994年に当時もっとも人気のあったブラウザNetscapeに実装されました。先に述べたように、サーバが指定した(ユーザー個別の)文字列を、ブラウザが(自動的に)要求に含めることで「合言葉」の要領で相手を識別しようという仕組みがCookieなのです。

## Cookieの設定手順
Cookieは以下の手順で設定/使用されます。

  1. サーバは20～100バイト程度のランダムな文字列[^5]を生成し、これをSet-Cookieヘッダに設定して応答する
  2. これを受けたクライアントは、以降そのサーバへの要求のCookieヘッダに指定された文字列を設定する

1の際に 2の送信条件などを指定することもできます。例えば、以下のものがあります。

  - サーバの特定のURL配下の場合にだけCookieを送信せよ(**Path**)
  - HTTPS接続のときだけCookieを送信せよ(**Secure**)
  - 指定された有効期限までこのCookieを使い続けよ(**Expires**)
  - JavaScriptからCookieへのアクセスを禁止せよ(**HttpOnly**)

Cookieの有効期限はデフォルトではブラウザを終了するまでですが、上記のようにExpiresで有効期限を指定された場合は、次にブラウザを使用するときに以前のCookieを使用し続けなければなりません。このためにブラウザはCookieをブラウザのストレージに便宜的に保存します。ですがこれは有効期限までCookieを使い続けるための「副作用」であり、巷によくみられる **「Cookieをデータストアとして使う」は誤った用法** です。

ブラウザのJavaScriptから document.cookie を参照してCookieにアクセスすることもできますが、これが脆弱性となることが多かったため上記のようにHttpOnlyで禁止することができます。また、Cookieをデータストアとして使う(という誤った)目的で、document.cookie に値を設定するコーディングを見かけますが、これを行うと以降の要求の際に設定した値も送信されるようになってしまいます。**ローカルに保存したつもりのデータがネットワーク上に流れている** 訳ですので、この用法は大変危険です。

## セッションとの関係
Cookieに関連して **セッション** という言葉を聞いたことがあるかもしれません。セッションの本来の意味はクライアント/サーバ間の一連のデータのやり取りの期間を指します。Web以前のシステムであれば接続がセッションであり、WebシステムではCookieを使用した一連のやり取りがセッションになります。

ですが、セッションと言ったときに「セッション情報」を話者が意図していることが多々あるので注意が必要です。セッション情報という用語自体があいまいなのですが、多くの場合は「Webベースのアプリケーションサーバ(TomcatやExpressなど)上に実装された **セッションの最中に使用するデータの格納とアクセスの方法、およびそこで管理されるデータ** のこと」と思えばよいでしょう。

セッション情報として管理されるデータの典型例は、ユーザID・氏名などの個人情報や「買い物かご」に入れられた商品一覧などでしょう。セッション情報を提供するアプリケーションサーバではCookieを直接扱うことはなく、代わりにCookieと1対1に対応づけられた **セッションID** をキーとしたデータストアとして実装されていることが多いです。つまりセッション情報とはアプリケーションサーバのメモリやストレージに格納されるサーバ側の情報のことです[^6]。「Cookieとはデータストアである」という誤った言説をよく見かけますが、これはCookieとセッション情報を混同しているものと思われます。

## まとめ
  - CookieとはWebサービスが クライアントを識別するために使用する短い文字列 である
  - Cookieはデータストアではない
  - document.cookieを使うな
  - Cookieとセッションを混同するな

[^1]: そもそも語源はX Windowのmagic cookieらしい
[^2]: 画面を開いたまま離席し他者に使われたらこの限りではないが
[^3]: 接続処理のオーバーヘッドを避けるために毎回切断しないことの方が一般的だが、接続しっぱなしにはならないので信用できないという意味では同じこと
[^4]: IPアドレスを利用すればよいという考えもありそうだが、ネカフェのような共同利用の形態もある
[^5]: 総当たり攻撃で成り済まされるのを避けるためにランダムな文字列を使うので、意味のあるデータは含めるべきでない
[^6]: セッション情報が発明される以前は、アプリケーションがCookieの値をキーにDBなどにデータを格納していました
久々にopenaiパッケージを用いたコードをいじったら「but this is no longer supported in openai>=1.0.0」が発生したので備忘録

Pythonのopenaiパッケージの話です。

バージョンは「openai==1.47.0」で指定していた時の話です。

発生したエラー

```
You tried to access openai.Embedding, but this is no longer supported in openai>=1.0.0 - see the README at https://github.com/openai/openai-python for the API.

You can run `openai migrate` to automatically upgrade your codebase to use the 1.0.0 interface. 
 
Alternatively, you can pin your installation to the old version, e.g. `pip install openai==0.28`

A detailed migration guide is available here: https://github.com/openai/openai-python/discussions/742 
```

つまりは「あなたはopenai.Embeddingを呼び出そうとしましたが、openaiの1.0.0以降ではそれはもうサポートしていません。バージョン1.0.0移行のインターフェース（=規格）に合わせるか、もしくは旧版を指定してopenaiパッケージを利用してください」というエラーです。

解決策は二つあります。

1. `openai migrate`を行う
2. `pip install openai==0.28`を実行する

①番の方法は現状のopenAIのバージョンを維持できたり、今後OpenAIのバージョンを新しいもので更新していけるメリットがあります。

一方で、①番では新たにOpenAIの仕様（openaiパッケージの使い方）に現状のコードを合わせていく必要があるのでそのデグレが怖かったり、めんどくさいといった事情がある場合は②番の方法を用いることになるかと思います。

今回は①番を行なったのでその備忘録を記します。

やり方はシンプルです。

エラーに記載のある通り、`openai migrate`を実行しました。

すると現状のコードで「どこがopenaiパッケージの規格と一致していないか」が一覧で以下のように出力されました。

（一部抜粋）

```
    -            response = openai.ChatCompletion.create(
    -                model="gpt-3.5-turbo-0613",
    -                messages=[
    -                    {"role": "system", "content": system_prompt},
    -                    {"role": "user", "content": question},
    -                ],
    -                functions=functions,
    -                function_call="auto",
    -            )
    
    +            response = client.chat.completions.create(model="gpt-3.5-turbo-0613",
    +            messages=[
    +                {"role": "system", "content": system_prompt},
    +                {"role": "user", "content": question},
    +            ],
    +            functions=functions,
    +            function_call="auto")
```

上記の例で行くと旧版では`openai.ChatCompletion.create`としていればよかったところを移行すると`client.chat.completions.create`としてください、と教えてくれているので該当する箇所をそのように修正します。

早い話が「`-`」が出ているところを消して、「`+`」が出ているもので書き換えたということです。

自分はこれで新しい規格に対応させたコードを動かせました。

久々にいじったコードで「流石にopenaiパッケージの仕様も変わっていそうだなぁ、めんどくさいな」と感じていましたが、どこを変更するべきか、どのように変更するべきかを教えてくれたので思ったよりもスムーズに移行できました。

`openai migrate`、便利です。