OmniAICreator/Qiita-1.07M huggingface dataset:
Pythonを基礎から学ぶその5　デスクトップのアプリを作るその2

この記事は、[JavaとPythonを基礎から学びたい私のための Advent Calendar 2024](https://qiita.com/advent-calendar/2024/aruhitonocalendar)の17日目の記事です。
昨日の続きです。遅くなりました
tkinterを使ってアプリを制作する続きです。

# ラベルの位置を変更する
ラベルを出すだけなら
```py
label=tk.Label(text='文字が出ました')
label.pack()
```
で文字を出せました。このラベルの位置は、さまざまなやり方で変更できるみたいです。
### 上下左右につける
```
label.pack(side=tk.TOP)
```
こんな感じで、side=tk.○○のような書き方をするとラベルがその位置で表示されます。それぞれこんな感じでした。
| 場所 | 書くもの |
|:-:|:-:|
| 上 | TOP |
| 下 | BOTTOM |
| 左 | LEFT |
| 右 | RIGHT |

この四つで場所を指定できるみたいです。すべて端によります。真ん中はありませんでした。
### placeを使う
```
label.place(x=100,y=200)
```
こんな感じでxとyを使ってラベルの位置を変えることができます。先ほどのものと違って、細かい場所の指定ができるため、使い勝手がいいです。
### gridを使う
gridを使って文字の列、行を指定して出すことができるようです。
```
A=tk.Tk()
A.geometry("200x300")
A.title("アプリもどき")
label=tk.Label(text='場所が変わりました',foreground='#00fa9a')
label.grid(row=1,column=1)
label2=tk.Label(text='文字が出ました',foreground='#00fa9a')
label2.grid(row=0,column=0)
A.mainloop()
```
「場所が変わりました」の文字がちょっと移動します。rowは行、columnは列です。上のものでは端から1ずつ動いています。
### anchorを使う
調べた結果このやり方もあるそうなのです。いろいろ種類があるようですが、右下とか中央とかを選んでもどうしても一番上に寄ってしまいました。ただ、左右には動いてくれます。参考程度に見てください。
```
label.pack(anchor=tk.W)
```
label.packのほうの()内に書くことで、動くようです。
| 向き | 必要な文字 |
|:-:|:-:|
| 上 | N |
| 下 | S |
| 左 | W |
| 右 | E |
| 右上 | NE |
| 右下 | SE |
| 左上 | NW |
| 左下 | SW |
| 中央 | CENTER |

## 端から少し移動させたいとき
padyとかいうものを使うことで、端からちょっとだけ動かすことができるみたいです。padxとpadyなるものがあるようです。
例えば先ほどのsideで位置を指定した後にpadyを使って
```
label.pack(side=tk.TOP,pady=200)
```
とすると、sideで本来一番上段真ん中に表示されるものがpadyで指定した分だけ下に移動しました。
padxは指定した分だけ横に移動します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2961125/6ae710d2-7e1e-fed4-bfb6-20507571da29.png)

ほかにもやり方があるみたいですが、上のやり方だけ覚えておけばとりあえずラベルの位置を設定できるでしょう。
# ラベルの色や大きさなどを変える
ラベルの文字の色、大きさを自由に変えることができるみたいです。
### 色を変える
foregroundを使うことで色の変更が可能なようです。
textの横に書くみたいです。ライトな緑色にしてみます。
```
label=tk.Label(text='文字が出ました',foreground='#00fa9a')
```
上の画像のように、緑色の文字に代わります。
ところで、=の右側の数字とアルファベット小文字の羅列は色コードです。
下のサイトなどを参考にするのがいいと思います

https://www.colordic.org/

### フォントと大きさを変える
文字のフォントと大きさは一緒に指定するようです。
```
label=tk.Label(text='文字が出ました',foreground='#00fa9a',font=('Roman',18))
```
font=(フォント名,フォントの大きさの数値)で記載するようです。フォントは大量にありますが日本語に対応していないものもあるので注意してください。ちなみに上のコードをそのまま使うと3文字くらい出ませんでした。

# ボタンを付ける
文字を出すだけでなく、いろいろ追加できるみたいです。とりあえず今日はボタンだけつけておきます。
```
button=tk.Button(text='何も起こらない')
button.pack(padx=50,pady=30)
```
ボタンの文字を選択して、位置を決めるだけです。文字と同じく、位置を指定しないと上段中央になります。
ちょっと昔のwebサイトとかでよく見かけるボタンになりました
# まとめ
文字は位置、フォントや文字サイズなどを細かく指定することができる。
ボタンも文字と同様にいろいろ指定することができる。
# 参考文献

https://nnahito.gitbooks.io/tkinter/content/helloworld/30e9-30d9-30eb-3092-shi-3063-3066-307f-308b/30e9-30d9-30eb-306e-wei-zhi-3092-ren-yi-306b-bian-geng-3057-3066-307f-308b.html

https://youta-blog.com/tkinter-pack/

https://ossa2019.stars.ne.jp/Remember/tkinter/tk3.html

https://lambdalisue.hatenablog.com/entry/2013/07/18/134507

https://youta-blog.com/tkinter-grid/

https://nnahito.gitbooks.io/tkinter/content/helloworld/30e9-30d9-30eb-3092-shi-3063-3066-307f-308b/wen-zi-306e-se-3092-bian-3048-3066-307f-308b.html

https://previous-se.com/python-tkinter-label-left/
windowsでiverilog その164

# 概要　
windowsでiverilogやってみた。
自作cpu、見つけたので、調査してみた。
アセンブラやってみた。

# 参考にしたページ

https://github.com/kkinos/zktc-asm

# 手順

```
$ cd rust
$ ls
asm0     asm1     asm2     dentaku  hello.rs  mandel    qiita  server0  wget    zundoko
asm0.rs  asm1.rs  asm2.rs  hello    hello0    openmind  scrap  server1  zipapi

$ cargo install --git https://github.com/kkinos/zktc-asm.git
    Updating git repository `https://github.com/kkinos/zktc-asm.git`
  Installing zktc-asm v0.1.0 (https://github.com/kkinos/zktc-asm.git#f59c77e6)
    Updating crates.io index
  Downloaded anstyle-parse v0.2.6
  Downloaded clap_lex v0.7.4
  Downloaded anstream v0.6.18
  Downloaded clap-num v1.1.1
  Downloaded strsim v0.11.1
  Downloaded heck v0.5.0
  Downloaded colorchoice v1.0.3
  Downloaded anstyle-query v1.1.2
  Downloaded is_terminal_polyfill v1.70.1
  Downloaded anstyle v1.0.10
  Downloaded clap_derive v4.5.18
  Downloaded unicode-ident v1.0.14
  Downloaded anyhow v1.0.94
  Downloaded proc-macro2 v1.0.92
  Downloaded clap v4.5.23
  Downloaded clap_builder v4.5.23
  Downloaded syn v2.0.90
  Downloaded 17 crates (834.6 KB) in 0.87s
   Compiling proc-macro2 v1.0.92
   Compiling unicode-ident v1.0.14
   Compiling utf8parse v0.2.2
   Compiling autocfg v1.4.0
   Compiling anstyle-parse v0.2.6
   Compiling anstyle-query v1.1.2
   Compiling anstyle v1.0.10
   Compiling colorchoice v1.0.3
   Compiling is_terminal_polyfill v1.70.1
   Compiling anstream v0.6.18
   Compiling strsim v0.11.1
   Compiling num-traits v0.2.19
   Compiling heck v0.5.0
   Compiling clap_lex v0.7.4
   Compiling anyhow v1.0.94
   Compiling clap_builder v4.5.23
   Compiling memchr v2.7.4
   Compiling quote v1.0.37
   Compiling syn v2.0.90
   Compiling minimal-lexical v0.2.1
   Compiling nom v7.1.3
   Compiling clap-num v1.1.1
   Compiling clap_derive v4.5.18
   Compiling clap v4.5.23
   Compiling zktc-asm v0.1.0 (/home/ore/.cargo/git/checkouts/zktc-asm-86776037d8a0ab1f/f59c77e)
    Finished release [optimized] target(s) in 1m 28s
  Installing /home/ore/.cargo/bin/zktc-asm
   Installed package `zktc-asm v0.1.0 (https://github.com/kkinos/zktc-asm.git#f59c77e6)` (executable `zktc-asm`)
warning: be sure to add `/home/ore/.cargo/bin` to your PATH to be able to run the installed binaries

$ explorer.exe .

$ zktc-asm sample.asm -o sample.mem
zktc-asm: command not found

$ /home/ore/.cargo/bin/zktc-asm sample.asm -o sample.mem

$ ls
asm0     asm1     asm2     dentaku  hello.rs  mandel    qiita       sample.mem  server0  wget    zundoko
asm0.rs  asm1.rs  asm2.rs  hello    hello0    openmind  sample.asm  scrap       server1  zipapi

$ cat sample.mem
31
0a
52
00
20
2a
4c
01
6c
11
6c
6c
48
65
```

# 投入したソース

```
start:
	lil x1, msg@l
	lih x2, msg@h
	or x1, x2
	lw x2, x1, 0
	lw x3, x1, 2
msg:
	.word 0x6c6c
	.word 0x6548
```

# 実行結果

```
31
0a
52
00
20
2a
4c
01
6c
11
6c
6c
48
65
```

以上。
フリーコメントから頻出ワードを機械的にフラグ化する(SPSS Modeler データ加工逆引き13-1)

# フリーコメントから頻出ワードを機械的にフラグ化する

![スクリーンショット 2024-12-13 10.29.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/a0089124-98ce-27f0-4c68-2781951edcfe.png)

この記事はSPSS Modeler Premium Ver18.6から追加された日本語テキスト機能を利用して書いています。SPSS Modeler Professionalをお使いの方はアップグレードするかPython連携を用いて実現します。

Python連携を紹介する記事はこちら

https://qiita.com/harrowharrow16/items/248ff8fe3879b63e7ae3

## 1.想定される利用目的

・コンタクトセンターの応答ログから解約に影響する単語の探索
・営業日報のコメントから成約予測モデルの説明変数を作成
・アンケート自由回答データからブランドと評価のポジショニングマップを作成
   
## 2.ストリームとデータのダウンロード

ストリーム

https://github.com/yoichiro0903n/blue/raw/b02b11130c8c35ff26b8f2336d1aa8001374316a/frequent_words.str

データ

https://github.com/yoichiro0903n/blue/blob/b02b11130c8c35ff26b8f2336d1aa8001374316a/comments2024_mac.csv

## 3.サンプルストリームの説明

![スクリーンショット 2024-12-13 10.30.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/66cf9a16-f676-aa1c-3261-64d665f75e91.png)

a.入力データは以下の通りです。[comment]列の文章から頻出ワードを列に展開します。

![スクリーンショット 2024-12-13 10.41.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/5e740adc-dfde-7dfd-f7b6-6a51676d053c.png)

b.[テキストマイニング]ノードを編集します。[ビルドモード]を[直接生成]に変更します。[モデルが含む最大コンセプト数]を[50]に変更します。

![スクリーンショット 2024-12-13 10.41.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/eb7a2ae2-2b4b-7482-00bc-3358a2c36e0c.png)

[テキストマイニング]ノードを実行します。cが生成されます。

c.[テキストマイニング]ナゲットを編集します。[真/偽の値]を[1/0]に変更します。

![スクリーンショット 2024-12-13 10.42.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/94d45736-f94b-a0ae-3812-3ade08c3a90d.png)

[テーブル]を実行します。

![スクリーンショット 2024-12-13 10.43.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/e67464f4-8ab3-dc25-c84f-c99c99e9c2ea.png)

#### 注意事項

今回は頻出ワード上位50に接頭辞コンセプトをつけて列に展開しましたが、後続処理でフィールド名（変数名）が不要になる場合に以下のように省略します。

![スクリーンショット 2024-12-13 10.45.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/f11580f2-5ddb-c631-4c21-9252be6d937f.png)

抽出されたワード（コンセプト）がそのままフィールド名になります。

![スクリーンショット 2024-12-13 10.46.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/83978f43-e144-1c56-9c13-8bd5ec6f4aa5.png)


## 4.参考情報

#### Python連携によるテキストマイニング

https://qiita.com/harrowharrow16/items/248ff8fe3879b63e7ae3

#### SPSS Modeler ノードリファレンス目次

https://qiita.com/416nishimaki/private/503037195081d3323eee

#### SPSS Modeler 逆引きストリーム集（データ加工）

https://qiita.com/416nishimaki/items/4bf813e6cba84b2a1008
アナログデジタルNAND回路

アナログデジタルNAND回路
```
function [analog_out, digital_bit1, digital_bit2] = nand_analog_digital(V1, V2, VDD)
% 入力:
% V1: 入力電圧1
% V2: 入力電圧2
% VDD: 電源電圧
%
% 出力:
% analog_out: アナログ出力
% digital_bit1: デジタル出力1 (中間NANDの状態)
% digital_bit2: デジタル出力2 (反転状態)

% 閾値電圧
Vth = VDD / 2;

% 論理NAND動作（デジタルビット）
if (V1 > Vth) && (V2 > Vth)
    digital_bit1 = 0; % 両方が高い場合 -> 0
else
    digital_bit1 = 1; % それ以外 -> 1
end

% デジタル出力2: 反転 (NOT)
digital_bit2 = ~digital_bit1;

% アナログ出力: NAND結果に応じて電源電圧または0V
if digital_bit1 == 1
    analog_out = VDD; % 高い場合 -> VDD
else
    analog_out = 0;   % 低い場合 -> 0V
end
end
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3759109/f5a426f3-a3ff-641e-a044-955e61bf8374.png)



Python

```
import numpy as np
import matplotlib.pyplot as plt

# パラメータ設定
VDD = 5  # 電源電圧 (V)
Vth = 2.5  # 閾値電圧 (V)
k = 10  # ゲイン

# 入力電圧1と入力電圧2
V_in1 = np.linspace(0, 5, 100)
V_in2 = np.linspace(0, 5, 100)

# シグモイド関数を使用した出力計算
def sigmoid(V_in, VDD, Vth, k):
    return VDD / (1 + np.exp(-k * (V_in - Vth)))

# 出力電圧1と出力電圧2
V_out1 = sigmoid(V_in1, VDD, Vth, k)
V_out2 = sigmoid(V_in2, VDD, Vth, k)

# 閾値に基づいて最終出力電圧を決定
def apply_threshold(V_out, Vth, VDD):
    return np.where(V_out > Vth, VDD, 0)

V_final1 = apply_threshold(V_out1, Vth, VDD)
V_final2 = apply_threshold(V_out2, Vth, VDD)

# NAND回路: 出力を決定
V_out = np.logical_not(np.logical_and(V_final1, V_final2))  # NAND回路

# プロット
plt.figure(figsize=(10, 6))

# 入力電圧1と出力電圧1
plt.subplot(2, 1, 1)
plt.plot(V_in1, V_out1, label="V_out1 (Sigmoid)")
plt.plot(V_in1, V_final1, label="V_final1 (Threshold)", linestyle="--")
plt.title("Input Voltage 1 and Output Voltage 1")
plt.xlabel("Input Voltage (V)")
plt.ylabel("Output Voltage (V)")
plt.legend()

# 入力電圧2と出力電圧2
plt.subplot(2, 1, 2)
plt.plot(V_in2, V_out2, label="V_out2 (Sigmoid)")
plt.plot(V_in2, V_final2, label="V_final2 (Threshold)", linestyle="--")
plt.title("Input Voltage 2 and Output Voltage 2")
plt.xlabel("Input Voltage (V)")
plt.ylabel("Output Voltage (V)")
plt.legend()

# 最終出力のプロット
plt.figure(figsize=(10, 6))
plt.plot(V_in1, V_out, label="V_out (NAND output)", color='r')
plt.title("Final NAND Output")
plt.xlabel("Input Voltage (V)")
plt.ylabel("Output Voltage (V)")
plt.legend()

plt.tight_layout()
plt.show()
```
LeetCodeを1日1題解いてみる (day44)

44日目に取り組んだ問題はこちら！

# 590. N-ary Tree Postorder Traversal 

https://leetcode.com/problems/n-ary-tree-postorder-traversal/description/?lang=pythondata

## 問題文
Given the root of an n-ary tree, return the postorder traversal of its nodes' values.

Nary-Tree input serialization is represented in their level order traversal. Each group of children is separated by the null value 

## 僕の回答 (GPTの助けあり)
```py
"""
# Definition for a Node.
class Node:
    def __init__(self, val: Optional[int] = None, children: Optional[List['Node']] = None):
        self.val = val
        self.children = children
"""

class Solution:
    def postorder(self, root: 'Node') -> List[int]:
        stack = [root]
        result = []
        
        while stack:
            node = stack.pop()
            if not node:
                if not stack:
                    break
                else:
                    continue
            
            for child in node.children:
                stack.append(child)

            result.append(node.val)
        return result[::-1]
```

## 他の回答例

```py
"""
# Definition for a Node.
class Node:
    def __init__(self, val: Optional[int] = None, children: Optional[List['Node']] = None):
        self.val = val
        self.children = children
"""

class Solution:
    def postorder(self, root: 'Node') -> List[int]:
        result = []

        def dfs(node):
            if not node:
                return
            for child in node.children:
                dfs(child)

            result.append(node.val)

        dfs(root)
        return result
```

## 学んだこと
Preorderとの違いは、
```diff
- for child in node.children[::-1]:
+ for child in node.children:

- return result
+ return result[::-1]
```
だけだということを学んだ。

## コメント
以前にも似たような問題を解いたけど、忘れてしまっていたからしっかり理解して定着させたい。

## 次の問題はこちら!
https://qiita.com/h_y_s_d/items/cb07e0ae525e927f4379
Android Gradle Pluginを更新したらBuildConfig.VERSION_NAMEが参照出来なくなったお話

# はじめに
[CYBIRD Advent Calendar 2024](https://qiita.com/advent-calendar/2024/cybird) 21日目を担当します、@R_araiと申します。
20日目は@yoooosuさんの[「【Unity】UnityでHD-2Dの表現をしてみた」](https://qiita.com/yoooosu/items/d322676bc8d626cee742)でした！
# 自己紹介
株式会社サイバードにてAndroidアプリのネイティブエンジニアをしております。
普段は担当アプリの運用や開発を行なっております。

# 概要
この記事ではTargetSDK33から34へ更新する際に発生した、BuildConfig.VERSION_NAMEとBuildConfig.VERSION_CODEが参照出来なくなった問題の解決策について記載します。

# 目次

# 発生した問題
TargetSDK33から34へ更新する事によりサポートする最小のAndroid Gradle Plugin(AGP)バージョンが8.1.1に上げられました。
その為今回対応するアプリをAGP8.1.1以上に上げたところ、このAGPの更新が原因でBuildConfig.VERSION_NAMEを参照する事が出来なくなりました。

# そもそもBuildConfig.VERSION_NAMEって何ぞや？
BuildConfig.VERSION_NAMEはbuild.gradleのdefaultConfigブロック内で以下のように定義することでBuildConfig.javaに定数が自動生成されBuildConfig.VERSION_NAMEを呼ぶだけでどこでもバージョン名を参照する事が出来ます。

```
//build.gradle
android {
    defaultConfig {
        versionName "1.0.0" 
        versionCode 100000
    }
}
```

バージョン名やバージョンコードはアプリケーションのバージョン比較やバージョンに基づいて機能を有効化/無効化するなど使用用途が多いので便利な機能でした。

PackageInfoから取得する方法もありますが、PackageInfoだとcontextが必要になる為、BuildConfigの方が使い勝手の良い機能だと思います。

:::note info
defaultConfigにversionCodeを定義するとversionName同様にBuildConfig.VERSION_CODEでint型のバージョンコードを取得できます。
:::


# 解決策
自分が調べたところ以下3点の解決策がありました。
1,gradle.propertiesにBuildConfigの生成をtrueに設定する方法
2,PackageInfoからバージョン名を取得する方法。
3,生成されなくなったVERSION_NAMEを自前で定義する方法。

## 1,gradle.propertiesにBuildConfigの生成をtrueに設定する方法
ルートのgradle.properties以下の設定を追加するだけで今まで通りBuildConfig.VERSION_NAMEでバージョン名を取得出来る様になります。
```
android.defaults.buildfeatures.buildconfig=true
```
じゃあこれでいいじゃんってなると思うのですが、この設定は既に非推奨になっていてAGP8.4から完全に削除されると明記されています。
その場凌ぎで使うのであればこれで問題ないですが、出来れば使いたくない方法です。

## 2,PackageInfoからバージョン名を取得する方法。
先程も述べたPackageInfoから取得する方法です。
呼び出し箇所がcontextを持っているのであれば、一番分かりやすい修正方法だと思います。
ただし、呼び出しの全ての箇所でcontextを持っておく必要があり、もしcontextを受け取れない設計になっていると改修規模が大きくなってしまいます。


## 3,生成されなくなったVERSION_NAMEを自前で定義する方法。
BuildConfigに定義が無くなったのなら自分で定義すればいいじゃない。
生成されなくなったBuildConfig.VERSION_NAMEとBuildConfig.VERSION_CODEを自分でBuildConfigに定義する方法です。
出来るだけ呼び出し方を変えずかつ非推奨の機能を使わずに改修したいので今回はこちらの方法で解決しました。



ルートのgradleに以下を記述
```
buildscript {
    project.ext.set("android_version_code", 100000)
    project.ext.set("android_version_name", "1.0.0")
}
```
上記はビルドスクリプト全体で共有出来るカスタムプロパティを設定しています。

次に上記で作ったカスタムプロパティを使って各モジュールのbuild.gradleに以下を記載します。

```
android {
    defaultConfig {
        buildConfigField 'int', 'VERSION_CODE', "${project.android_version_code}"
        buildConfigField 'String', 'VERSION_NAME', "\"${project.android_version_name}\""
    }
    
    buildFeatures {
        buildConfig true
    }
}
```

defaultConfig内にbuildConfigFieldを記述する事によってBuildConfigクラスにint型のVERSION_CODEという定数がBuildConfigクラスに定義されます。
buildFeaturesのbuildConfig trueはBuildConfigを生成する様に指示する設定です。
この設定をしておかないとBuildConfigクラスが生成されないので注意しましょう。

これでcleanしてからビルド出来れば今まで通りBuildConfig.VERSION_NAMEを呼ぶ事が出来る様になっているはずです。

# 最後に
今回はAGP8.3に更新した時に発生したBuildConfig.VERSION_NAMEが参照出来なくなった問題の解決策についてまとめてみました。
利用頻度も高い機能かと思うので同じ現象で悩んでいる方がおられましたら、参考にしていただけたら幸いです。

明日の[CYBIRD Advent Calendar 2024](https://qiita.com/advent-calendar/2024/cybird) 22日目は@cy-tsuzawaさんの[「Sign in with Apple のユーザーを別のチームに移行してみる」](https://qiita.com/cy-tsuzawa/items/b169cbb7ae7c4a118934)です！
お楽しみに！
[Flutter] Android AppLinks と iOS UniversalLinks への対応

DymnamicLinks が来年廃止されるのに備えて、Flutterアプリで、Android AppLinks と iOS UniversalLinks に対応したので、その一連の手順についてまとめたいと思います。

# 開発環境
- Flutter 3.19.6
    - [こちら](https://zenn.dev/altiveinc/articles/separating-environments-in-flutter) のように、Dart-define-from-fileを使用して開発環境と本番環境を分けています。
- Xcode 16.0

## 1. Flutter で使用するライブラリの選定

今回は、[app_links](https://pub.dev/packages/app_links) を使用しました。

以下のコマンドでインストールします。
```
flutter pub add app_links
```

## 2. 使用するURLの決定
サーバーサイドのエンジニアとアプリ起動に使用するURLを決めます。


## 3. アプリ側の改修

app_links のドキュメントに従って、設定を行います。
scheme、host、pathPrefix については手順２で決めたものを設定します。
- Android は [こちら](https://github.com/llfbandit/app_links/blob/master/doc/README_android.md)
- iOS は [こちら](https://github.com/llfbandit/app_links/blob/master/doc/README_ios.md)
    - iOS側は、Xcode での設定も必要です。Apple公式サイトでも良いですが、[こちら](https://qiita.com/bull012/items/5e6570bf878677b11981#ios%E3%81%AE%E8%A8%AD%E5%AE%9A) が大変参考になりました。

※ Dart-define-from-file を使用している場合、環境毎の変数を定義したファイル内でアプリを起動させるURLのドメインを指定している場合は、そのドメイン名を修正します。

次に、[公式](https://pub.dev/packages/app_links#applinks-usage) の通り、/lib/main.dart 内に以下のコードを追加します。

```dart
final appLinks = AppLinks(); // AppLinks is singleton

// Subscribe to all events (initial link and further)
final sub = appLinks.uriLinkStream.listen((uri) {
    // Do something (navigation, ...)
});
```

※ DynamicLinks を使用していた場合は、以下の部分が上記に置き換わるイメージです。
```dart
final sub = FirebaseDynamicLinks.instance.onLink.listen((uri) {
    // Do something
});
```

## 4. サーバーサイドの対応
アプリ起動に使用するドメインの /.well-known/ 配下に、apple-app-site-association（iOS用）と assetlinks.json（Android用）を設置します。

詳細についてはここでは記載しませんが、以下をご覧ください。
- [Apple公式サイト](https://developer.apple.com/documentation/xcode/supporting-associated-domains#Add-the-associated-domain-file-to-your-website)
- [Android Developers ドキュメント](https://developer.android.com/training/app-links/verify-android-applinks?hl=ja#web-assoc)

## 5. テストする

指定のURLを押下した時にアプリが起動するかテストします。
問題なければ対応完了です！

## トラブルシューティング
### Android
- [app_links のドキュメント](https://github.com/llfbandit/app_links/blob/master/doc/README_android.md) の下の方にも書いてありますが、本番環境以外では、Android端末側で「リンクの追加」の設定を行う必要があります。


### iOS
- apple-app-site-association を設置してから反映されるまで、最大2時間程度かかるようなので、気長に待つ必要があります。
    - すぐに動作確認したい場合は、Xcode（Dart-define-from-file を使用している場合、環境毎の変数を定義したファイル内）のURLのドメイン名の後ろに `?mode=developer` を付与すると反映を待たなくても良くなります。
ただしこの場合は、iOS端末の設定アプリから デベロッパ > 「ユニバーサルリンク」セクションの「関連ドメインの開発」をONにする必要があります。
参考：[Associated Domains Entitlement (Apple公式)](https://developer.apple.com/documentation/bundleresources/entitlements/com.apple.developer.associated-domains)
おいでよ顧客の森〜迷子のCRE、3つのミッションに挑む〜


## プロローグ：森への招待

7月、私はエンジニアからCREに転向しました。それは、新しい挑戦の始まりであると同時に、未知の「顧客の森」に足を踏み入れることを意味していました。
この森では、CS（カスタマーサポート）という案内人が日々顧客とのやり取りを続け、顧客は個性豊かな住人としてその奥深くに暮らしています。私は、この森で彼らとどう関わればいいのか、全く分からない状態で迷子になってしまいました。

最初は混乱ばかり――。
「CSの主語がわからない！」
「顧客が何をしたのか全然わからない！」
「開発チームにどんな要望を伝えればいいんだろう！」

こうして私は、顧客の森で生き抜くための3つのミッションに挑むことになったのです。


## ミッション1：CSを解読せよ！

「会話の主語が顧客名すぎてワカラナイ」

顧客の森を歩き始めた私は、CSチームという頼れる案内人と共に行動を始めました。彼らは顧客を熟知し、日々その声を聞き届けているスペシャリストです。とはいえ、案内人の話を理解するのは簡単ではありませんでした。

CS：「XX商事さんで、設定の解除が必要みたい！」
私：「（ステータスなんだろう…）（システムのバグとかじゃないよな…？）」
　　「えっと、そもそもなぜ解除が必要なんですか？」
CS：「運用が複数走っていて、対象者の入れ替えとか変更が発生しやすいの！」
私：「なるほど…運用上の問題なんですね」


#### 解決方法1：伝えてもらう情報をロックする

プロダクト対応をするためには、調査の対象が誰なのか、理想状態はなんなのかを知る必要がありました。

そこで、以下の情報をこまめに確認するようにしました。
1. 対象
どの企業、あるいはどの対象者が問題に直面しているのかを具体の情報で提示してもらう
例）企業ID、対象者のメールアドレス

2. 現状
問題の内容や背景を明確にする。
例） 「設定解除が必要」「データが見られない」など。

3. 原因・背景（なぜ問題が発生する／したのか）
問題のきっかけや、顧客特有の運用状況を確認。
例） 「複数運用が並行しており、対象者の変更が多発している」。


#### 解決方法2：顧客を知りに行く

また、CSの話を解読するためには、単に質問を投げるだけでは不十分でした。
そこで、自分自身で顧客の運用や納品状況を理解するために、顧客運用のマニュアルや情報を確認するようにしました。

CSの話の主語は常に「顧客」であり、その背景にある運用や個別事情を前提に会話を進められていました。
一方で、私の主語は「プロダクト」や「ログ」であり、システム的に想定された挙動を基準に物事を考えています。
この違いが、情報の齟齬（そご）や疑問を生む原因になっていると気づきました。

この違いを意識するようになってから、CSとのやり取りは徐々にスムーズになりました。
顧客の森では、まず案内人であるCSの言葉を解読すること――これが旅を進める最初のカギだったのです。


## ミッション2：顧客を解読せよ！

「何をしたのかワカラナイ」

次に私は、森の奥深くに住む顧客という未知の存在と向き合うことになりました。
顧客「ログインしたらエラーが出た！」

「（アクセスログにエラー通知は来てないぞ…？一体何をしたんだ？）」と内心思いながら調査を始めます。
顧客の言葉を解読するカギは、「事実情報を元に、何をやりたかったのかを想像すること」でした。


#### 解決方法：顧客のやりたかったことを想像する

兎にも角にも、再現しないと何もできません。

- とにかく事実情報を集める
    - データベース、ログなどから何がいつ変更されているのか、何をしたのかを調べる
- CSを通じて具体的な情報を聞き出す
    - 事実情報と並行して、顧客からも追加情報を聞き出す
        - 「その画面で入力した内容は何ですか？」
        - 「エラー画面のスクリーンショットを送ってもらえますか？」
- 二つの情報を合わせて考察する
    - CSに運用面の背景も確認しながら、「何をしようとしたのか？」を考察する

その結果、「ログインしたらエラーが出た」という顧客の言葉は、「権限のがないはずの画面にアクセスしようとして、エラーが起きた」ことを意味していると判明しました。


## ミッション3：双方を翻訳せよ！

「CSや顧客の要望や問い合わせを開発組織に伝えよ」

ミッションはこれで終わりかと思いきや、最終試練が待ち受けていました。
顧客の問題を解決するため、CSと開発チームをつなぐ「翻訳者」として動く必要があったのです。

例）改善要望の場合
- CSの視点：「ちょっと文言を追加で出すだけで問い合わせを減らせるんです！」
- 開発の視点：「文言の妥当性を考えなきゃ…この時には出しわけが必要だし、検討すること多いな。これ優先度高いの？」

ここで、CSと開発のどちらの視点も尊重しながら、顧客が求める結果を届ける翻訳スキルが求められました。

#### 解決方法：双方の言葉をすり合わせる！
- CSに開発組織・システムの話を伝え続ける
    - 開発組織は何を気にしているのか？などの思考フローを話す
    - （問い合わせなどがあるたびに）技術的な話を咀嚼して伝える
- 開発に顧客視点を伝える
    - ミッション1で得た情報を使って、顧客特有の情報も含めて問い合わせを伝える
        - 「XX商事さんはこんな運用をしているので、こういう問題が起きています」
    - CSと顧客のアポの録画をみんなで見ながら、何をやっているのかを解説

さらに、顧客の納品状況を直接CSから開発組織に共有してもらうようにしていくことになりました。


## エピローグ：共存の道を選ぶ

3つのミッションを進めるうちに気づきました。
この「顧客の森」は、抜け出すべき場所ではなく、共に生きていく場所なのだと。

森の中でCSは顧客に寄り添い、CREは技術的な知識を持ってその橋渡しをします。
そして、森の住人である顧客たちは、時に戸惑いながらもサービスを育てていく重要な存在です。

この森での日々は、常に「新たな挑戦」の連続です。

もし、あなたが「顧客の森」に迷い込むことがあれば、一緒にこの森を楽しむ方法を探していきましょう。
いつでも、だれでも、どっか（Docker）らでも（笑）


## はじめに

開発者なら一度は環境構築で詰まった経験があるはずです。
「動かない...」「バージョンが違う...」「ローカルでは動いたのに...」

でももう安心してください。**Docker**がその悩みを全て解決してくれます。

しかも、Dockerは大規模開発だけのものではありません！**個人開発**でも使ったほうが絶対にいい、便利すぎる理由を紹介します。

---

## Dockerって何？

Dockerは、アプリケーションを実行するための**コンテナ型仮想環境**を提供するツールです。
簡単に言うと、必要な環境や依存関係を**コンテナ**という箱にまとめて、どこでも同じ状態で動かせるんです。

---

## Docker Desktopで簡単管理！

**Docker Desktop**を使えば、複雑なコマンド操作も必要なく、視覚的にコンテナやイメージを管理できます。

- **GUIで管理**できるので、初心者でも直感的に操作可能！
- どのコンテナが動いているか、何を使っているかが一目でわかる。

インストールして数クリックでセットアップ完了！この簡単さ、驚きです。
![dockerd.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3527006/ba9f745a-5f06-add8-3e1f-ec6c5d06fa0a.png)

---

## 環境構築が一瞬で終わる

Dockerを使えば、面倒な環境構築も一瞬で終わります。
例えば、次のような感じで準備完了！

```bash
docker-compose up
```

これだけで、必要なバージョンや依存関係が整った環境がすぐに作成されます。
「え？これだけ？」って思うかもしれませんが、本当にこれだけ。

---

## 個人開発にもDockerは必須

「Dockerって大規模開発で使うものでしょ？」
と思う方、違います。**個人開発こそDockerを使うべき**です。

### 1. PCが変わってもすぐに環境が復元できる

例えば、開発中のPCが壊れてしまったとします。
でもDockerがあれば、新しいPCにDockerをインストールしてコンテナをビルドし直せば、すぐに同じ環境が復元されます。

もう泣かなくて大丈夫！

```bash
docker-compose build
```

こんな感じでビルドすれば、5分もかからず戻ってきます。

### 2. 環境が壊れても安心

何かよくわからない原因で開発環境が壊れてしまったとき、普通なら再構築に時間がかかりますよね？

でもDockerなら安心！

```bash
docker-compose down && docker-compose up --build
```

この一撃でクリーンな状態に戻せます。
しかも、めちゃくちゃ速い。

### 3. バージョンや依存関係で悩まない

「Python3.8じゃなくて3.7じゃないと動かない...」
「Node.jsのバージョンが...」

こんな悩み、Dockerを使えば無縁になります。
**Dockerfile**にバージョンや環境をまとめておけば、
どのPCでも同じ環境が一瞬で作れます。

---

## まとめ

Dockerを使えば、

- 環境構築は一瞬で終わる。
- Docker Desktopで簡単に管理できる。
- バージョンや依存関係を調べる必要がない。
- 環境が壊れてもすぐに復元できる。
- 別PCからでも瞬時に開発環境が復元できる。

これが大規模開発だけでなく**個人開発にも必須な理由**です。

---

## 最後に

Dockerがあれば、どんな環境でも、どこからでも、簡単に開発が始められます。

**いつでも、だれでも、どっか(Docker)らでも（笑）**

これからはDockerを使って、環境構築の悩みとはサヨナラしましょう！

---

### おすすめ参考リンク
- [Docker公式サイト](https://www.docker.com/)
- [Docker Desktopのインストールガイド](https://docs.docker.com/desktop/)
UEFI_Libc

# UEFI Libc

これは、UEFI BOOTする、64bit整数版 TinyBASICインタープリターに付随する形で作成中のLibc （ NewLib-4.4.0 を移植中 ）です

# 64bit整数版 TinyBASICインタープリター とは？

・Tiny BASIC for the Motorola MC68000 をUEFIブートするように改造したものです。

・予約語を普通の文字列型に変更

・整数のビット数を16bitから64bitに変更

・多少の Refactoring を実施

・追記： オリジナルのGitHubは2024年時点で消滅しておりました。

https：//github.com/LHerrmeyer/UEFI_Basic


## 環境構築メモ

(1)・Lubuntu24.04 を用意（普通のUbuntu24.04でも良い） QEMUを動かすので、GUIが必要。

(2)・gnu-efiを入れる。

 ```
 # apt install gnu-efi
 ```

(3)・QEMUを入れる。・・・UEFIブートを試すエミュレータ。

 ```
 # apt install qemu-system-x86
 ```

(4)・OVMFを入れる。・・・UEFIのBIOS（変な言い方だが、そう）というかファームウェアね。 Open Virtual Machine Firmware とかそんな感じの。

 ```
 # apt install ovmf
 ```

## QEMU上で動かす

 $ make run

・エラー発生時の行番号は、BASICの行ではなく、 basic.c のエラー発生時の行番号であることに注意

・変数は A-Zしか使用できない。

・文字列変数はない。

・整数は64bit

・配列は実装されない。

・Arduino等にあるような　SD Card File I/O や Port Pin Control など、は実装されない。

## QEMUの終了のさせ方

・[CTRL] ＋ [ALT] + [２] を押してQEMUコンソールに戻す
・>quit で終了


## 注意事項

・Libc (NewLib-4.4.0) 移植途上です。

・とりあえず、リンクエラーする関数をダミー関数として undef.c に置いています。

・以前はLinuxコマンドライン上でもbasicが実行可能なMakefileを用意していましたが、
現在はUEFI環境へのLibc移植に主眼を置いているため、サポートしていません。

## どの程度実装されている？

・strcpy()とかstrcmp()とか string系
・printf() sprintf()
・malloc() はたぶんOK. (AllocatePages()でsbrk() )

・timeとかfileとかdirとかprocessとかは全く未実装

・ぶっちゃけいうと、libcの最低限の機能を使いたい（UEFI APIに繋ぎたい）ので、とりあえずNewLibのコンパイル通るようにしてみました。という感じです。libcの全機能が使えると思ったら大間違い。

・テスト方法は、ub_test.c に書いて、 BASICから、 user 命令を実行することで、cmd_User()関数がキックされる。

・（現状では、BASIC起動時に自動実行している）

## 実機で動くのか？

ちなみに、てきとーなUSBメモリーをFAT32フォーマットして、

USBドライブ名: /EFI/BOOT/BOOTX64.EFI

のようにさっきのファイルを配置して、

C:ドライブだったSSDを抜いておいて起動すると、ちゃんとBASICが起動する。


だけど、メーカーロゴが残ったままだし、全画面じゃないし（中央部分にしかコンソールがprintしない）不満が・・・



# GitHub URL: （ソースコード）
https://github.com/iruka-git/UEFI_Libc

- にっき
https://iruka-git.github.io/ATMEL_AVR/html/UEFI_Basic.html
【OpenAI ChatGPTクリスマスイベント第８日目】ChatGPT Searchのバージョンアップです。


　どうも、カーブミラーです。

　今回は、OpenAIのクリスマスイベント。今日は第８日目。ChatGPT Searchのバージョンアップです。

:::note info
本記事は、ChatGPTについての
記事です。　　　　あしからず。
:::
　ChatGPT等の動作状況を知らせる
　　OpenAIのステータスページ。
　　　[OpenAIステータス](https://status.openai.com/)
***
　まずは、ニュースです。

　昨日（2024/12/16）、
　　X風の新しいSNS「mixi2」が
　　　リリースされました。

　アプリは、
　　iPhone用、
　　Android用が
　　　あります。

　招待制です。

　[ITmediaNEWSページ](https://www.itmedia.co.jp/news/spv/2412/16/news143.html)

　mixi、
　　以前やっていました。
　　　入っていたレベルですけど😅
***
　さて、【本題】です。

　OpenAIの
　　クリスマスイベントの第８日。
　　　まずはごあいさつ。
　　　　[OpenAI公式ページ](https://openai.com/12-days/?day=8)
　　　　　中身は、OpenAIの
　　　　　　イベントページ。

　イベントのようす。
　　[OpenAI公式YouTube動画](https://www.youtube.com/live/OzgNJJ2ErEE?si=5XUCXSRk9c-Lxmmy)

　　[OpenAI公式ポスト](https://x.com/OpenAI/status/1868760655878406352?t=7PYcYvgjV4gY6jgELZ5blQ&s=19)


　今回は？

　ChatGPT Search。

　すでにあった機能です。

　今回は、
　　そのバージョンアップです。

　高速・高精度化され、
　　マップ表示やYouTube動画、
　　　画像、映画、ビジネスや
　　　　レストラン情報取得に対応。

　ナビ系では
　　関連サイトを上位表示し、
　　　要約も提供する。

　音声会話中に
　　直接ウェブ検索が可能になります。

　ChatGPTが利用可能な
　　全プラットフォームで、
　　　ログイン済みユーザーに対して
　　　　本日より今週中に提供。
　　　　　私はまだでした😅

　なお、
　　Web版、
　　デスクトップアプリ、
　　スマホアプリ
　　　から検索機能へアクセス可能。

　ただし、Web検索機能は、
　　GPT-4oのみで
　　　動作します。

　それから
　　ヨーロッパ方面は
　　　少し遅れるかもしれません。

　以下は、日本語字幕&日本語音声付き。
　　[エクセル兄さんポスト](https://x.com/excel_niisan/status/1868742786641805572?t=V8JbIEpXs9u9rSPeUGWfsQ&s=19)

　　再生スピードが速いので
　　　0.75くらいに
　　　　調整した方が良いかと。
***
　ブラウザのデフォルトの
　　検索エンジンを
　　　ChatGPTにしておくと、
　　　　アドレスバーに
　　　　　検索したいお店などを
　　　　　　入力すると、
　　　　　　　すぐにアクセスできる
　　　　　　　　そうです。

　　ちなみに、
　　　Chrome以外のブラウザでも
　　　　デフォルトの検索エンジンを
　　　　　ChatGPTにできるそうです。

　ブラウザでの
　　マップ表示ですが、
　　　英語表記だそうです。
　　　　ご注意を。
　　　　　[miraiさんポスト](https://x.com/mirai_spark/status/1868783357964042464?t=uHB_0qYoRaykI5zo0f1NKA&s=19)
***
　PS.
　　明日は、開発者向けの
　　　発表を行なうそうです。

　[ChatGPT開発者ポスト](https://x.com/OpenAIDevs/status/1868830612951622020?t=adbFsv8Kb5dOGWqMlVi8PA&s=19)
>（DeepL翻訳）
>明日のライブストリームは開発者向けです。 PT午前10時です！
>
>そしてその直後、チームは10:30-11:30am PTに開発者フォーラムでAMAを開催します。 

　PT午前10時は、
　　日本時間3時ね。

　みなさんの
　　作品制作の糧に
　　　なれば、幸いです。

　今回は、ここまで。
lambda製のapiにおけるpydanticでのvalidation

この記事はデジタルキューブグループ エンジニアチームアドベントカレンダー2024 12月5日の記事です。

https://qiita.com/advent-calendar/2024/digitalcube-heptagon

# 目的

apigateway + lambdaでバックエンドの開発をする際に、API リクエストのバリデーションにpowertools for aws lambdaを使っている

この構成でバリデーションが正しく動いているか確認する必要が出てきたため、本記事のような検証を行った

# 環境
- python(3.11.x)
- pydantic(v2.x)
    - powertools for aws lambdaの一部機能に pydantic が使われているが、pydanticが部分的にしか使えないので大元のパッケージも利用する
- powertools for aws lambda(v3.x)

https://docs.pydantic.dev/latest/

https://docs.powertools.aws.dev/lambda/python/latest/#extra-dependencies

# バリデーションを行う際のコード

lambda本体側は以下の感じ

```python
from aws_lambda_powertools.utilities.parser import parse, ValidationError
from models import Hoge # 各自pydanticなクラスを定義したものをimportする

def handler(event, context):
    try:
        request_parameter = parse(event["body"], Hoge)
    except ValidationError as e:
        # エラー時にlambdaが返すレスポンスやエラーログ出力などの処理

    # なにか後続の処理
```

# 任意のキーと必須のキー混合

 ```python
from typing import Any
from pydantic import RootModel, model_validator

 class Hoge(RootModel[dict[str, Any]]):
    # 基本的には全てのキー/バリューを許可、一部必須フィールドをチェックするためのvalidatorを定義
    @model_validator(mode="before")
    def required_field_validation(self: dict[str, Any]):
        keys = self.keys()
        
        # 必須キーのキー名と型を定義
        required = {
            "key1": [float],
            "key2": [str, int],
        }
        
        error_messages = []
        
        for key_name, types in required.items():
            if key_name not in keys:
                error_messages.append(f"{key_name} is required")
            elif type(self[key_name]) not in types:
                error_messages.append(f"{key_name} must be {' or '.join([str(t.__name__) for t in types])}")

        if error_messages:
            raise ValueError("\n".join(error_messages))

        return self
 ```

 # 任意のキーすべてを受け入れる

apiのpostで以下のようなbodyを受け入れる場合

```json
{
    "hoge": "hoge",
    "fuga": 1
}
```

以下のように定義

```python
from typing import Any
from pydantic import RootModel

class Hoge(RootModel[dict[str, Any]]):
    pass
```

# 配列を受け入れる

apiのpostで以下のようなbodyを受け入れる場合

```json
[
    {
        "hoge": "hoge",
        "fuga": 1
    },
    {
        "hoge": "hoge",
        "fuga": 2
    },
    {
        "hoge": "hoge",
        "fuga": 3
    },
]
```

以下のように定義

```python
from typing import Any
from pydantic import RootModel

class Hoge(RootModel[list[dict[str, Any]]]):
    pass
```

任意のdictでないならたとえば以下でいいはず

```python
from typing import Any
from pydantic import RootModel, BaseModel

class HogeDict(BaseModel):
    hoge: str
    fuga: int

class Hoge(RootModel[list[HogeDict]]):
    pass
```

# その他

基本的にはよくこういう感じで使うのでメモ的に残しておく

```python
from pydantic import BaseModel

class HogeProps(BaseModel):
    hoge: str
    fuga: int

class Hoge(BaseModel):
    props: HogeProps
    name: str
    age: int
```

想定されるリクエストボディ

```json
{
    "name": "name",
    "age": 111,
    "props": {
        "hoge": "hoge",
        "fuga": 1
    }
}
```
後輩のためのGit講座（応用編）

# Git講座

- ブランチ構造
- Gitの仕組み
    - コミットとは
    - ブランチとは
- たまに使うコマンド一覧

# ブランチ構造

ブランチ構造は様々な種類があり、それぞれに良さがあります。
今回は、先輩方から教えていただき、自分でも使いやすかったブランチ構造を紹介します

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950744/df6e106b-2ba2-1dce-9e1b-64e67d9e51a2.png)

リモートのブランチ構造はmainからdevelop-mainを生やします。
そこから役割別にdevelopp-をつけて分割していきます。

<br/>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950744/9e886823-cc88-eecb-b74c-67b9f1535523.png)

ローカルのブランチは、リモートのブランチからfeature/をつけて分割します

このブランチ構造を使うことで、どの階層のブランチかわかりやすい。
そのため、マージミスなどがなくなる。

# Gitの仕組み

ここでは、Gitの仕組みのイメージを書きます

<br/>

## コミットについて
まず、ステージング・コミット・プッシュについて説明します。

### ステージング

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950744/bfbc1eac-da31-25e1-0e92-fc1541329dee.png)

ステージングとは修正されたデータを箱に入れることです。

### コミット

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950744/c52a6337-eaeb-fc27-1b64-7776736bcb30.png)

そして、コミットとは箱を閉じることです。

### プッシュ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950744/859b2583-6b80-fc5e-10a3-bd10fb909808.png)
そして、プッシュとは、Githubに箱を送ることです。

これらの操作を繰り返すことで、修正データを保存しています。
プルは、他の人の箱を取り込む操作であり、Githubで箱を管理することで、チーム開発が楽になります。

## ブランチについて
次に、ブランチについて説明します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950744/67b34ff8-b98a-0e22-8108-d5c10065f5f2.png)


よく、ブランチは左のように説明されることがあります。しかし、実際は右のようにすべてが同じ階層にあります。
ブランチを作成するときに、もともといたブランチの内容をコピーするだけであり、そのあとは繋がりがありません。
しかし、それでは使いにくいため、左の図のような使い方をする人が多くいます。


そして、混乱しやすいリモートのブランチとローカルのブランチですが、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3950744/45303384-c066-4af9-d71a-c55fd50af2dc.png)
このように、リモートのブランチの最後の節をローカルに持ってきて、そこからブランチを切って作業します。

これを読んで理解度が増してくれたらうれしいです。

## たまに使うコマンド一覧
- 今のファイルの状態を確認する。⇒`git status`
- 一つ前のコミットを取り消す。⇒`git reset --soft HEAD~1`
- 今のファイルに.gitを適用する。⇒`git init`
- git initしたファイルとリポジトリを繋ぐ。⇒`git remote add origin "url"`
- リモートのブランチを確認する。⇒`git branch -r`
- ブランチをプッシュする⇒`git push origin ブランチ名`

## まとめ
今回は、Gitの説明を行いました。
基本編と合わせて一通り書いたと思います。
チーム開発でリーダーをする場合は、チーム開発編をご覧ください。
Laravelのジョブスケジューリング：Railsのwheneverとの比較

### はじめに
プログラミング初学者にとって、ジョブスケジューリングは少し難しく感じるかもしれませんが、実はLaravelやRailsには便利な仕組みが用意されています。本記事では、Laravelのジョブスケジューリング機能とRailsのwhenever gemを比較し、それぞれの特徴や設定方法を解説します。

### ジョブスケジューリングとは？

ジョブスケジューリングとは、定期的にタスク（ジョブ）を実行する仕組みのことです。たとえば、毎日午前1時にデータベースのバックアップを取る、一定間隔で不要なファイルを削除する、といったタスクに活用されます。

LaravelとRailsには、このジョブスケジューリングを簡単に実現できる仕組みが用意されています。

### Laravelのジョブスケジューリング

Laravelには、組み込みのジョブスケジューリング機能があります。これを使うと、コードベースでスケジュールを定義できます。

#### 基本のセットアップ

`Task Scheduling`の設定
Laravelのスケジューリングは`app/Console/Kernel.php`で管理します。
このファイル内の`schedule`メソッドにタスクを追加します。


```php
protected function schedule(Schedule $schedule)
{
    $schedule->command('emails:send')
             ->dailyAt('13:00');
}
```

この例では、毎日13:00に`emails:send`というArtisanコマンドを実行するように設定しています。

#### スケジューラーの起動
`schedule:run`コマンドを`cron`に登録する必要があります。
以下のように設定します：

```
* * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1
```

これにより、Laravelのスケジューリング機能が1分ごとにチェックされ、必要なタスクが実行されます。

#### メリット

コードベースでスケジュール管理ができるため、バージョン管理がしやすい。

柔軟な設定が可能で、Artisanコマンド、クロージャ、外部スクリプトなどを実行可能。

デバッグやロギングがしやすい。

### Railsのwhenever gem

Railsでは、whenever gemを使うことでジョブスケジューリングを簡単に設定できます。

#### 基本のセットアップ

**gemのインストール**
Gemfileに`whenever`を追加し、インストールします：

```
gem 'whenever', require: false
```

その後、bundle installを実行します。

**schedule.rbの作成**
`wheneverize`コマンドを実行して、設定ファイル`schedule.rb`を作成します。

```
bundle exec wheneverize .
```

**スケジュールの設定**
`config/schedule.rb`にスケジュールを定義します。

```ruby
every 1.day, at: '1:00 am' do
  rake "db:backup"
end
```

この例では、毎日午前1時に`db:backup`というRakeタスクを実行します。

**cronへの反映**
以下のコマンドでスケジュールをcronに反映します：

```ruby
bundle exec whenever --update-crontab
```

#### メリット

cron形式に自動変換してくれるため、記述が簡単。

RubyのDSLで直感的に設定可能。

必要に応じて設定をcronから削除できる。

### Laravelとwheneverの比較

| 特徴 | Laravel | Rails（whenever） |
|:-----------|------------|------------|
| 設定ファイル | コードベース（PHP）で設定 | `config/schedule.rb`に設定|
| 実行方法 | `artisan schedule:run` | cron経由で直接実行 |
| 柔軟性 | コードで高度な制御が可能 | cronベースのタスクのみ対応 |
| 簡易さ | 初期設定が少し複雑 | DSLで簡単に設定可能 |

### まとめ

LaravelとRailsのジョブスケジューリングには、それぞれの利点があります。

Laravelはスケジュール管理をアプリケーション内で一元化したい場合に適しています。

Railsのwheneverは、簡単にcronタスクを管理したい場合に便利です。

初心者の方は、どちらのフレームワークでもまずは基本的なタスクを設定し、小さな成功体験を積むことをおすすめします！

### 参考記事
https://laravel.com/docs/11.x/scheduling

https://github.com/javan/whenever

https://readouble.com/laravel/11.x/ja/scheduling.html
LINQってなに？

## LINQってなに？
#### 1. LINQとは？
 簡単に説明すると、こいつが登場したおかげで、ソースコード内で簡単にクエリ操作やグルーピングができるようになった！
 実際に使うときの記述方法は後述しますが、書き方はほぼforeachと同じ書き方で、foreachの強化版みたいなもの

#### 2. LINQの種類
 LINQには、クエリ構文とメソッド構文と2種類あり、クエリ構文はやりたいことをそのまま書くだけで簡単！メソッド構文はラムダ式を使って書く！と覚えちゃいましょう
※メソッド構文では、WhereやSelectのような便利なメソッドも使えるぞ！

 #### 3. LINQの書き方
 ```method_code
 var list = new List<int> { 1, 84, 95, 95, 40, 6 };

// クエリ構文
var query = from x in list
            where x % 2 == 0
            orderby x
            select x * 3;

// メソッド構文（この記事ではこちらを記法を採用します）
var query = list
            .Where(x => x % 2 == 0)
            .OrderBy(x => x)
            .Select(x => x * 3);
ConsoleWriteLine(query);

```
```出力結果
18
80
168
```
###### 3.1. SelectとWhereの使い方
※ SelectとWhereはSQL構文の時と全く違う！
メソッド構文で使用されるSelectはFindAllメソッド、WhereはConvertAllメソッドとほぼ同義。上のソースコードを書き換えると以下のようになる
```
var list = new List<int> { 1, 84, 95, 95, 40, 6 };

// クエリ構文
var query = from x in list
            where x % 2 == 0
            orderby x
            select x * 3;

// メソッド構文（この記事ではこちらを記法を採用します）
var query = list
            .FindAll(x => x % 2 == 0)
            .OrderBy(x => x)
            .ToList()  //OrderByで静的にIEnumerable型に変換されたためList型に変換する
            .ConvertAll(x => x * 3);
ConsoleWriteLine(query);
```
 ```出力結果
18
80
168
```
API Best Practices ~リクエストとレスポンスのサイズを制限する

## リクエストとレスポンスのサイズを制限する

https://protobuf.dev/programming-guides/api/#bound-req-res-sizes

リクエストとレスポンスのサイズは制限すべきです。
8MiB程度の制限を推奨し、多くのプロト実装が壊れる2GiBはハードリミットです。
多くのストレージシステムはメッセージサイズに制限があります。

無制限のメッセージは
- クライアントとサーバーの両方を肥大化させる
- 予測できないレイテンシーを引き起こす
- 単一のクライアント・サーバー間で長時間接続が発生し耐障害性が低下する

API内のすべてのメッセージを制限する方法はいくつかあります。

- 各RPCを論理的に独立し境界付きメッセージを返すRPCを定義する
- 無制限のクライアント指定オブジェクトリストではなく、単一のオブジェクトに対して操作するRPCを定義する
- 無制限のデータを文字列、バイト、または繰り返しフィールドにエンコードしないこと
- 明示的に長時間かかるRPCを定義します
    - 結果をスケーラブルで同時読み取り用に設計されたストレージシステムへ保存します
- ページングAPIを使用する
- ストリーミングRPCを使用する

UIを構成済みの場合、[小さなデータを操作するメソッドを作成しクライアントがそれらの複数RPCをバッチ処理してUIを構成することを期待する](https://qiita.com/bushiyama/items/ccaeb855f8987124dae2)も参照してみてください。

---

方法について、個人的に思う具体的な方法を補足してみたい。

- 各RPCを論理的に独立し境界付きメッセージを返すRPCを定義する
    - `preloadしすぎない`
- 無制限のクライアント指定オブジェクトリストではなく、単一のオブジェクトに対して操作するRPCを定義する
    - `ListではなくGetで賄えないか検討する`
- 無制限のデータを文字列、バイト、または繰り返しフィールドにエンコードしないこと
- 明示的に長時間かかるRPCを定義します
    - 結果をスケーラブルで同時読み取り用に設計されたストレージシステムへ保存します
        - `非同期処理を検討する`
- ページングを使用する
- ストリーミングを使用する
Rubyで、JavaやPythonで言うところのセレクタを実装する

Ruby版BayServerを開発する際、セレクタが必要になったので実装してみました。

:::note info
BayServerは、横浜ベイキット(http://baykit.yokohama) が開発している爆速Webサーバーで、Ruby、Python、PHPなどさまざまな言語で開発されています。
:::

セレクタとは、JavaやPythonで取り入れられているソケットやパイプを扱うためのAPI（クラス）で、select関数などの低レベルのソケットAPIに比べて色々とメリットがあります。

メリットはこんなところでしょうか

- 低レベルなソケットAPIは使い方にクセがあり少し使いづらいのを分かりやすいメソッドにしてくれている
- select, epoll, kqueue など、プラットフォームによるソケットAPIの実装を隠蔽できる

実際、とても扱いやすいクラスだと思います。

で、JavaやPythonでのセレクタの使い方としては

1. セレクタのインスタンスを作成する
1. 監視したいソケットやパイプを登録する。登録する際は何をしたいか（読み込み/書き込み)も指定する
1. selectメソッドを呼び監視を開始する（ここでブロックされる）
1. 読み込みや書き込みが可能になったらselectメソッドから返る
1. 読み込みや書き込みの処理を行う

実際、従来のselectやepollなんかを使って書くのに比べて、メッチャ書きやすいし分かりやすいです。

で、BayServerの話に戻りますが、BayServerでは、実装言語が違っても、クラス構造やメソッド名、その中身のロジックは全く同じになるように記述されています。

ところが、RubyにはセレクタのAPIがないので、JavaやPythonのようには書けないんですね。

なのでSelectorクラスを実装してみました。

コードはGitHub上のを参照してください。

https://github.com/baykit/BayServer_Ruby/blob/main/gems/bayserver-core/lib/baykit/bayserver/util/selector.rb

今は、内部的に従来のselect関数を使っていますが、selectは制限が多いので、今後epollやkqueueなども扱えるようにしたいと思っています。（まあ、今のところこれで十分なので、リクエストがあればですが笑）

と言うわけで、BayServerをよろしくお願いします！
iOSの新たなメジャーバージョンがリリースされた後、どれくらいの期間、古いメジャーバージョンでいられるか調べてみた

企業でiPhoneを管理していると、iOSの新たなメジャーバージョンが公開された後でも、どれくらいの間旧メジャーバージョンのままマイナーバージョンアップし続けられるか、気になります（よね？）。ちょっと調べてみました。

# 最初に結論
- iOS18がリリースされた後、手元のiPhoneでiOSのソフトウェアアップデートをするときに、どれくらいの期間、iOS17台を選択できるか、調べました。
- 結果は、2024/9/16にiOS18が公開された後、2024/12/11にiOS18.2が公開されるまでの間は、バージョンアップ先としてiOS17台が選択できました。

# 詳細
## ここ数年のiOSメジャーバージョンアップ時の仕様について説明します
- iOSを含むApple製品のOSは、ここ数年にわたってずっと、毎年秋に、新メジャーバージョンが公開されます。
- はるか昔は、新メジャーバージョンが公開されると、旧メジャーバージョンが動いているiPhoneでiOSのソフトウェアバージョンアップを試みると、その新たに公開された新メジャーバージョンだけに、バージョンアップ出来ました。
- しかし数年前のメジャーバージョン公開時以降、現在に至るまで、iPhoneでiOSのソフトウェアバージョンアップを試みると、現メジャーバージョンのままマイナーバージョンアップだけするのか、それとも、新メジャーバージョンにバージョンアップするのか、GUI上で選択できるようになりました。
- ただし、この「現メジャーバージョン内でマイナーバージョンアップするのか」「新メジャーバージョンにバージョンアップするのか」の選択は、新メジャーバージョンが公開されて数カ月後には、出来なくなります。新メジャーバージョンが公開されて数カ月後には、「現メジャーバージョン内でマイナーバージョンアップする」選択肢はiPhoneのGUI画面上表示されなくなり、「新メジャーバージョンにバージョンアップする」しか、選択肢はなくなります。
- つまりまとめると、「新メジャーバージョン公開後も、数カ月間は、現メジャーバージョン内でマイナーバージョンアップするという猶予期間が設けられている」ということになります。

## どうしてこの猶予期間の長さを知りたくなったのか
- iOSがメジャーバージョンアップすると、いろいろとGUIや挙動がかわったりしますよね。新機能も実装されたりします。多くの社員が使っている重要なアプリが新メジャーバージョンにすぐに対応するとも限りません。
- つまり、私のような企業でiPhoneを管理している立場からすると、どれだけの期間、現バージョンに留まれるのかという猶予期間を知りたいな、と思うことがかなりあると思います。これが今回の調査を実施した動機です。

# iOS18が公開された時の実績

| 日付 | 公開されたバージョン | 公開後にiPhoneの[設定アプリ]-[一般]-[ソフトウェアアップデート]をタップしたときの挙動 |
|:-:|:-:|:-:|
| 2024/09/16 | 18.0 , 17.7  | 最初に17.7が大々的に表示される。もちろん18.0も表示されますが、画面下に控えめに表示される。下記のパターン1参照。|
| 2024/10/03  | 18.0.1|  見かけ上の変化なし |
| 2024/10/28  | 18.1 , 17.7.1 | 最初にバージョン17台ではなく18台(18.1)が大々的に表示されるように変化した。ただしまだ17.7.1を選択可能。下記のパターン2参照。  |
| 2024/11/19  | 18.1.1 , 17.7.2 | GUIが少し変わった。シンプルになったというか。が、18.1.1が大々的に表示されその一方でまだ17.7.2を選択可能という点は変わらず。下記のパターン3参照。|
| 2024/12/11  | 18.2 | 候補として18.2しか表示されなくなった。17.7.2は候補として表示されなくなった。 |

パターン1

![sono1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3835992/81f7f412-1b2c-9115-48fc-e3b888def788.gif)

パターン2

![Videotogif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3835992/55611394-88a7-e113-ec84-5262232b21a8.gif)

パターン3
![Videotogif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3835992/19be9c7a-eb97-4e2b-8669-7f285ec6b0d0.gif)


以上です
SmartProxyでインターネット接続を実現しているWebSocketについて

この記事は [**セゾンテクノロジー Advent Calendar 2024**](https://qiita.com/advent-calendar/2024/saison-tech) 17日目の記事です。
シリーズ2は [**HULFT10**](https://www.hulft.com/software/hulft-10) のエンジニアによる投稿をお届けします。

# はじめに
12月10日にリリースされたHULFT10のラインナップとして、オンプレ環境に導入してインターネット経由での安全なファイル集配信を実現するSmartProxyが追加されました。
SmartProxyはインターネット経由のファイル転送を実現する仕組みとして、元々のラインナップであるHULFT-WebConnectと同様にWebSocketの仕組みが使用されています。このWebSocketについて触れてみたいと思います。

## SmartProxyについて
基本的な構成としては以下のように配信HULFTと集信HULFTの両側にSmartProxyが配置される構成となります。
(以下は簡易的な図となります)
![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3943848/033ccd80-c47a-346e-5b64-0824857084c4.png)
このSmartProxyの間の通信でWebSocketの仕組みが使用されています。

## WebSocketの特徴
1. 双方向通信
サーバーとクライアント間で双方向のデータ通信が可能です。
2. フルデュプレックス通信
サーバーとクライアントが同時にデータを送受信できます。
3. 低遅延
接続の確立後、データの送受信に必要なオーバーヘッドが非常に少ないため、低遅延で通信が可能になります。
4. 効率的なリソース使用
一度のハンドシェイクで接続が確立され、その接続を維持することで、リソースの使用が効率化されます。


## WebSocketの手順
大まかなWebSocketの手順としては
1. 接続の確立 (openingハンドシェイク)
2. データの送受信
3. 接続のclose (closingハンドシェイク)

です。 
### 1. 接続の確立 (openingハンドシェイク)
まずは接続を確立するために、接続をopenしてハンドシェイクと呼ばれる接続確立のプロセスを行います。
具体的には以下のようなリクエストをクライアント側から送信します。
HULFTの配信要求時の転送ではクライアントは配信HULFTとなるので、配信HULFT側のSmartProxyから送信します。
```
GET /chat HTTP/1.1
Host: server.example.com
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Origin: http://example.com
Sec-WebSocket-Protocol: chat, superchat
Sec-WebSocket-Version: 13
```
このリクエストはHTTPの乗っ取った形式となっています。リクエストの中身にもあるようにHTTPをWebSocketにUpgradeするような要請となります。

サーバ側 (集信HULFT側のSmartProxy)から応答が返ってくることで接続が確立されます。

### 2. データの送受信
接続確立後はクライアント、サーバのどちらからも、接続がcloseされるまでいつでもデータを伝送することができます。
データは一連のフレームを利用して伝送されます。フレームはデータの情報を付与したデータの小さな塊のようなものです。
データの情報としては
- データの種類
- データが最後かどうか
- データを隠すための情報 (WebSocketにはマスクと呼ばれるデータを隠す仕組みがある)

等々の情報があげられます。
HULFTの転送においてはここで転送される実データがHULFT独自のプロトコルで流れることになります。

### 3. 接続のclose
前述のフレームの中でcloseを通知するようなCloseフレームが存在します。これを送信後、相手からの応答のCloseフレームを受信することで接続を安全にcloseします。

## まとめ
以上のような仕組み+TLS暗号化 を用いたWSS(WebSocket Secure)を使用して、安全なインターネット接続を実現しています。
これをまとめるためにRFC6455を読みつつ、他の方の記事を読みつつでしたが、理解がふんわりだった部分があるのを実感しました。仕組みを理解していなくても今はライブラリのおかげで一行に書けてしまう部分もあります。こういった根本の技術の理解もすすめることを心掛けたいです。

## 参考
- RFC6455 The WebSocket Protocol
https://triple-underscore.github.io/RFC6455-ja.html#section-11.1.2
- WebSocketについて調べてみた
https://qiita.com/south37/items/6f92d4268fe676347160
micro:bitを拍手👏でコントロール

micro:bitのV2以降には本体に「マイク」がついています。

micro:bitを音声で操作できたらすごいのですが、それはちょっと難しそうなので、代わりに「拍手の回数」でmicro:bitを操作できるようにしてみました。例えば、拍手１回でメッセージ表示、拍手２回で音を鳴らす、ということができます。

プログラムは以下の通りです。

https://makecode.microbit.org/S25373-77706-89257-99402

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245196/6bb3db7c-5355-8ecb-8830-c4368471222d.png)

ためしに、何度か「拍手」をしてみてください。「拍手したよ」関数が呼び出され、画面に拍手の回数が表示されます。拍手してから2秒間に拍手した回数がカウントされます。もしあなたがこのプログラムを使用したい場合は、「拍手したよ」関数内で、回数に応じて、処理を実行すればOKです。

# プログラムの説明

プログラムが行っている事は難しくありません。
変数「拍手回数」と「拍手中？」を作成してください。

- まわりの音がうるさくなったとき
    - 拍手されたよイベントを発生させる
- 拍手されたよイベントを受け取ったとき
    -  拍手回数をインクリメント
    -  (初回拍手時のみ) 拍手監視イベントを発生させる
-  拍手監視イベントを受け取ったとき
    -  2000ミリ秒待って、拍手おわったよイベントを発生させる
-  拍手おわったよイベントを受け取ったとき
    -  「拍手したよ」関数を呼び出す
    -  拍手回数をゼロに戻す

# イベント

今回、イベントを初めて利用してみました。イベントとは、micro:bitのボタンが押された、本体を揺さぶられた、などを伝える仕組みです。例えば、micro:bitのAボタンが押された時には、以下のイベントが発生します。
発生源：MICROBIT_ID_BUTTON_A
値：MICROBIT_BUTTON_EVT_CLICK

発生したイベントは「イベントが届いたとき」ブロックで受け取ります。例えば以下のようにすると、「ボタンAが押された時」ブロックと同じことが実行できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245196/3732eab6-8dd1-03cb-438e-421ef6061eab.png)

このようにイベントはmicro:bitのシステム内でも多様されていますが、プログラムでも自由に使用することができます。

イベントについての[詳細はこちら](https://lancaster-university.github.io/microbit-docs/data-types/event/)。

※ただし、このページによると、発生源（event source）は、「ランタイムで既に使用されているソースIDは使用しないように」と記載されています。

※また、「予約済みのソースIDの完全な表については、messageBusページを参照してください。」とありますが、そんな表はどこにもありません。[検索しましたが](https://forum.makecode.com/t/table-of-reserved-event-source-ids/768)、完全な表は[こちら](https://github.com/microsoft/pxt-microbit/blob/master/libs/core/dal.d.ts)にあり、ページ内「ID」で検索すればよいそうです。

今回、発生源として「MICROBIT_ID_IO_P0」を使用しました。これは、上記注意事項に反しています。しかし以下のように考え、今回は使用しました。
- micro:bitのP0端子には何も接続していないため
- プログラムのサンプルを見やすくするため（IDを独自に定義すると、変数を増やす必要がある）

# おわりに
作ってみましたが、意外と認識精度が良いので驚きました。必要に応じて「うるさいかどうかのしきい値」を変更して、お使いください。
これで、作ったラジコンの操作できないか、挑戦してみようと思います。
7zipの分割圧縮を活用してgithubにプッシュする

## はじめに
GitHubでは通常、100MB以上の大きなファイルを直接プッシュするのは推奨されていません。しかし、ゲームエンジンでの開発データをGithubで管理していたりするとどうしても大きなファイルをgit管理下に置きたくなる場合があります。そういう場合、**7zip**の分割圧縮機能を活用し、GitHubにプッシュする方法が有効です。

この記事では、7zipを使った分割圧縮の方法とGitHubでの管理手順を紹介します。

## 手順

### 1. 7zipのインストール

まず、7zipがインストールされている必要があります。

### 2. ファイルを分割圧縮する

100mb以上のデータでこのままではリモートへプッシュできません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524352/2869fdce-94e0-9f4e-88be-cc432900feb9.png)

1. フォルダを右クリック
1. 7-Zipをクリック
1. 圧縮をクリック

![スクリーンショット 2024-12-17 110851.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524352/2f11b1cd-2e8b-b586-13aa-e3592527ac93.png)

分割ボリュームサイズを設定する（ここでは10Mに設定した）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524352/53bc4649-a1b2-0458-bdf5-06a15ab8d80d.png)

これで10mb以下のファイル2つに分割＆圧縮がされる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524352/aa77c40d-f963-fae0-d830-69b4d959e5cc.png)

## 復元する

001のファイルを展開すると元に戻せる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2524352/c08a2527-c51c-aae8-43f3-b461043998f3.png)

## おわり
これで大きなファイルを10mb以下のファイルに分割圧縮ができました。
一つあたりのファイルサイズも指定できるのでファイルサイズを大きくして分割数を少なくするということも可能ですね。
TypeScript(Node.js)で標準出力をファイルにリダイレクトしている際、プログラム終了時にバッファに溜まったデータが出力されない問題を解決した話

BayServerの実装時にハマった問題があったので備忘録。

以前から、TypeScript版BayServerに、「エラーが発生してプログラムが終了した場合に、原因となるエラーがログに吐き出されない」という問題があって、どうやったら直せるかずっと悩んでおりました。

これ、直さないと、ユーザーさんはエラーの報告できないですからね笑

で、直したので、BayServer 3.0 for TypeScrpt からは大丈夫ですよという話。

:::note info
ちなみに、BayServerは、横浜ベイキット(https://baykit.yokohama/) で開発している、爆速WebServerです。RubyやらPHPやらいろんな言語で実装されています。
:::

以下、コードは端折っています。

BayServerでは、設計ファイルでredirectFileを指定すると、ログは標準出力ではなくファイルに出力されます。内部的に何をやっているか説明します。

標準出力や標準エラーをリダイレクトする方法として、とりあえずcolsole.logとconsole.errorを書き換えます。オリジナルは別変数に保存しておきます。
```ts
this.originalConsoleLog = console.log
this.originalConsoleError = console.error

this.logConsole = fs.createWriteStream(redirectFile)
this.logConsole.on('open', () => {
    console.log = (...args: any[]) => {
        let message = args.join(' ');
        this.logConsole.write(message + '\n')
    };
    console.error = console.log
})
```
redirectFileをオープンして、console.log関数を、オープンしたファイルに出力するように書き換えます。

console.errorはconsole.logと同じ動きになるように書き換えます。

:::note info
後で戻せるように、元々のconsole.log, console.errorの実装は別の変数に保存しておきます。
:::

さて、これでconsole.logやconsole.errorを呼んだ時のデータはファイルにリダイレクトされるわけですが、実はメソッドを呼んだ時点ではファイルに書き込まれず、バッファに溜まるだけなんですね。で、いつ書かれるかというと、Node.jsのイベントループで順番が回ってきた時に書かれます。

この挙動は通常あまり問題にならないですが、プログラム終了時に問題を起こします。

どんな問題かというと、プログラム終了直前に書こうとしたデータはバッファに残ったまま、ファイルに吐き出されずプログラムが終了してしまうんですね。

では、プログラム終了時に、次のようにendを呼べば良いと思うかも知れないが、残念ながらそれもダメ。

```ts
this.logConsole.end()
```
chatGPT先生に聞いてみたが、何度もうまくいかず、何億回とやり取りして、結局、プログラム終了時に以下の２点を行うとうまくいきました。

- console.log, console.error を元の実装に戻す
- endメソッドを呼び、finishedイベントの発生をプロミス化して待つ

何故これでうまくいくのかは全然分かんないですが笑

```ts
console.log = this.originalConsoleLog
console.error = this.originalConsoleError

this.logConsole.end()

const finishedAsync = promisify(finished)
finishedAsync(this.logConsole).then( () => {
});
```

というわけで、BayServerをよろしくお願いします！
AWS Certified AI Practitioner（AIF）対策：Bedrock モデルのカスタマイズ（ファインチューニング・継続的事前トレーニング）

# Bedrockのモデルのカスタマイズ

モデルは公開されている一般的なデータでトレーニングされているため、もちろん企業や特定プロジェクトに関するデータでのトレーニングはされていません。

そのため、モデルの自社のユースケースに合わせた **<font color="Darkcyan">トレーニングデータのカスタマイズ</font>** することができます。

Bedrockでは、モデルをカスタマイズするために

- **<font color="MediumBlue">ファインチューニング</font>**
- **<font color="MediumBlue">継続的事前トレーニング</font>**

の2つの方法が提供されています。

## ファインチューニング

***<font color="MediumVioletRed">『ラベル付きデータを使用して、基礎モデルを特定のタスクに特化させる方法』</font>***

例えば、要約など特定のタスクにモデルを最適化したい場合に適しています。

### 特徴

- 既にトレーニングされたモデルを、特定のユースケース・タスクや目的に合わせて最適化するために行います。
- 元々汎用的な基盤モデルに対して追加学習を行うことで、例えば、特定の業界や用途に適した結果を提供するために非常に有効です。
- このプロセスにより、モデルは新しいデータパターンを学習し、特定のニーズに応じたカスタマイズが可能になります
- ファインチューニングは、既存のモデルを一からトレーニングするよりも効率的で、短期間で高い成果を得ることができます。

### ファインチューニングにおける重要な要素

Bedrockを使ってトレーニングデータをカスタマイズして基盤モデルを再トレーニングする場合、トレーニングデータの品質が生成結果に直接影響します。

- **<font color="MediumBlue">トレーニングデータの品質</font>**
- **<font color="MediumBlue">関連性</font>**

### モニタリング

モデルが特定のタスクに適切に適応しているかを確認するために、特に優先してモニタリングすべきメトリックは、

- **<font color="MediumBlue">モデルの精度</font>**

です。


## 継続的事前トレーニング

***<font color="MediumVioletRed">『ラベルなしデータを使用して、モデルをドメインに慣れさせるための方法』</font>***

特定のタスクに依存しません。

### 特徴

- AIモデルがトレーニングされたデータセットに最新のデータを定期的に追加し、それを基に再トレーニングすることでモデルの性能を維持し、改善する仕組みです。
- これにより、モデルは新しいトレンドやデータの変化に対応でき、最新の情報に基づいた予測や結果を提供することができます。
- 継続的にモデルをアップデートすることで、データドリフトなどによるパフォーマンス低下を防ぎ、信頼性の高い結果を維持します。

### ラベルがないデータの形式

継続的な事前トレーニングには、ラベル付けされていないデータを利用します。

次のような、ラベルがないデータの形式を利用します。
```J
{"input": "<input text>"}
```

:::note info
Bedrock の公式ドキュメントでは、継続的なトレーニングを通じて、モデルがより適応性の高いものになり、長期的な運用に適したモデルが提供できることが説明されています。
:::