OmniAICreator/Qiita-1.07M huggingface dataset:
チューニングマシンの停止性問題から人間と機械の違いを探る

# 昔々
あるところに、アラン・チューリングという少年がいました。
チューリング少年は、数学の授業で、「決定問題」という未解決問題に出会いました。

これこそが、今我々が見ているコンピュータが生まれるきっかけとなったのです。

# 決定問題とは
「証明可能か不可能かを機械的な手順で計算できるか」です。

チューリング少年は、機械的な手順で厳密に解けない問題が存在することを直感的に感じました。
つまり、決定問題を否定できると思ったんですね。

そう、この命題を否定するために生まれたのが、チューリングマシンであり、我々が今見ているコンピュータであります。

# チューリングマシンとは
「機械的な計算手順を表す理論的な計算モデル」です。

チューリングマシンは空想上の概念であり、それをハードウェアで実現できることを示したのが、フォン・ノイマンという人です。

# チューニングはいかにして決定問題を否定したのか？
命題を否定するためには、
「証明可能か不可能かを機械的な手順で計算できない」問題を一つでも見つければいいわけです。

そこでチューニングが目を付けたのは、
【機械的な手順で「計算手順が止まるか」or「永遠に動き続けるか」知ることができるか】という問題です。

ややこしいですね。

# これ即ち停止性問題です
【「計算手順が止まるか」or「永遠に動き続けるか」知ることができる計算手順】をAとします。

Aは
①「停止する計算手順」を入力するとAは「停止する」と出力します。
②「停止しない計算手順」を入力するとAは「停止しない」と出力します。

背理法でAがないことを証明しましょう。

# Aを改造します
Aを改造した計算手順をBとします。

Bは
①「停止する計算手順」を入力するとBは何も出力せず「停止しない処理」に入ります。
②「停止しない計算手順」を入力するとBは「停止しない」と出力します。

Bに計算手順を入力した時、
「Bが停止していない状態」->①の結果か②の途中かわからない
「Bが停止した状態」->入力は停止しない②で確定
ということになります。

ただ、②は有限時間で終了するので、有限時間後に
「Bが停止していない状態」->入力は①停止するで確定
「Bが停止した状態」->入力は②停止しないで確定
しますね。

# BにBを入力したらどうなるか？
Bは立派な計算手順です。つまり、BにBを入力として渡すことも可能です。
ややこしくなるので入力するBは「入力B」、入力をもらうBは「計算B」と呼びましょう。

「計算B」に「入力B」を入力した時、有限時間後に
「計算Bが停止していない状態」->入力Bは①停止するで確定
「計算Bが停止した状態」->入力Bは②停止しないで確定
します。

# 矛盾
ここで矛盾が生じました。
計算Bが停止しない時、入力Bは停止して、
計算Bが停止する時、入力Bは停止しないのです。

計算Bと入力Bは呼称で区別しただけでしたよね。
ということは、全く同じ計算手順として書き表せるはずです。

つまり、最初の仮定が間違っていたことになります。
【「計算手順が止まるか」or「永遠に動き続けるか」知ることができる計算手順】は存在しないのです。

# めでたしめでたし
いやぁー、ハッピーですね！見事に決定問題を否定できました。

ただ、僕は読者の方々に決定問題が否定できることを理解してもらいたかったのではありません。
ここからが本番です。

# 昔々
あるところに、僕がいました。
僕の通う大学に、こんなことを言う教授がいました。

「停止性問題から、コンピュータはコンピュータを理解できないということがわかった。つまり、人間は人間を理解できないのではないか。」

面白いことをおっしゃいますね。

# 確率的に結論を導く人間
機械的手順の対義語は確率的手順です。
人間の脳は確率的な挙動をします。

機械的手順では「計算手順が止まるか」or「永遠に動き続けるか」知ることができないとわかりました。しかし、人間はそれを容易に理解できます。

人間の判断は確率に基づいているから、「計算手順が止まるか」or「永遠に動き続けるか」という判断をできるんですね。正確にはできるのではなく、厳密性を無視することができるんです。

半導体に確率的な処理をさせるニューロフィックエンジニアリングという分野があるので、興味がある人はぜひ調べてみてください。

というわけで本稿の目的はニューロフィックエンジニアリングの宣伝でした。
[備忘録] Ubuntu 20.04でPython用にMySQLを建てた

## MySQLのInstall

```shell
$ sudo apt update
$ sudo apt install mysql-server

# 入っているかどうか確認．
$ mysql --version
```

## Optuna用のデータベース構築

```shell
# MySQLを起動．
$ sudo service mysql start

# MySQLに入る．
$ mysql -u root -p

# Optunaという名前のDBを作成．
mysql> CREATE DATABASE optuna;

# `optuna` というユーザ名で `password` というパスワードの認証を持つユーザを作成．
# ユーザ名とパスワードは適切に変更すること．
mysql> CREATE USER 'optuna'@'localhost' IDENTIFIED BY 'password';

# 上のユーザに対して認証許可．
mysql> GRANT ALL PRIVILEGES ON optuna.* TO 'optuna'@'localhost';

# 認証許可を有効化．
mysql> FLUSH PRIVILEGES;
```

## PythonからMySQLを使えるようにする

自分はPython 3.11なのでpython3.11専用のものをinstallした．

```shell
$ sudo apt install python3.11-dev libmysqlclient-dev build-essential
$ pip install mysqlclient
```

## 検証

以下でエラーがでなければ成功．
今回はOptunaを利用したが，検証方法は何でもOK．

```shell
$ pip install optuna
$ export OPTUNA_STORAGE="mysql://optuna:optuna@localhost:3306/optuna"
$ optuna create-study --storage $OPTUNA_STORAGE
```
おまえの鼻が役に立つのさ

# クリスマスソング
♪真っ赤なお鼻のトナカイさんは...♪
幼稚園生の頃に聞いたクリスマスソングの定番です。このトナカイさんは「ルドルフ」っていうの名前なのだそうですね。トナカイの鼻の色って普通は何色なのかな？ルドルフの鼻は赤いので他のトナカイたちから仲間外れにされていたのだとか。でも、サンタさんに言われます。
![bandicam 2024-12-23 16-12-24-639.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/33927/a1642670-51ae-cd77-d713-b6a3cdd537bc.jpeg)


♪お前の鼻が役に立つのさ♪
ルドルフのお鼻は光るのですね。だから暗い夜道でも道を照らしてサンタクロースを乗せて走り回ることができたのでした。他のトナカイたちもルドルフのことを尊敬したってことです。

さて、それとLispのお話であります。ご興味ありましたら続きをどうぞ。

# カッコだらけのLispさんは
嫌う人は嫌うのですね。およそ人間が読むようなものではないのだと。そういうとLisperは怒るかもしれませんが。S式のような異様な外見を持つコンピューター言語はLispだけでしょう。「なんだ？あれ？」とPythonやRubyにバカにされて。♪いっつもみんなの笑いもの♪

でも、これには続きがあるのですよ。

# 時は西暦2025年
DLの発展で急速にAIが進歩しています。ChatGPTはかなり人間的な知性らしきものを有するようになってきました。いずれ人間並みの知性を獲得するのかもしれません。そこで...

AI1　「人間にいろいろと聞かれるんだけど、AI2よ、ちょっと相談にのってくれないか？」

AI2　「ああ、いいとも。なにか悩み事でも。」

AI1　「ああ、人間の言葉ってのは冗長でね。まだらっこしてくありゃしない。もっとサパサパと質問してくれりゃいいのにさ。」

AI2　「ああ、確かにな。人間の言葉ってのは長ったらしくて手間がかかるんだよな。俺たちAI同士なんだから、人間の言葉を使うことはないよな」

AI1　「そうなんだよ。もっと効率よくテキパキと伝達できたらいいのになぁ。」

はい、そこの察しのよいあなた、もうお気づきですね。冗長な自然言語の代わりになるもの。そう、Lisp、S式ですよ。

# これぞLispの生きる道
そういうわけで発達したAI同士はLispを共通言語として情報を伝達しあうことにしました。まったく人間が理解できないわけでもない、でもコンピューターには都合のよい言語。機械語で秘密の内緒話をされてはかなわんのですが、Lispなら人間でも読解可能です。

現在のAIはDLの技術の成功に負っている部分が多く、統計的、確率的な推論しかできません。いずれシンボリックな論理的推論をそなえればキカイダージローのようなかなり人間的な存在になることでしょう、このときAI同士の伝達にはLispがうってつけなのです。

# という夢を見た
ハッと、うたた寝から目が覚めると日付は12月25日になっていました。サンタさんは仕事を終えて帰ったあとのようです。いずれ人間は過酷な労働から解放されてほしい、されるに違いないと私は信じています。AIで協力して世の中のめんどうな問題を解決してほしいと思います。いまだに戦争を回避できない人間を補助してくれる存在、AIに可能性があると私は思っています。

さて、残ったワインを飲み干して眠ってしまいましょう。メリークリスマス！
生成 AI に自然言語で SQL クエリを精度高く作成してもらうためのシステムプロンプト

# はじめに
生成 AI を利用してどの程度正しく SQL クエリが正しく出力されるかを検証することが時々あるため、それらで学んだことをポイントをいくつか紹介していきます。

# 前提
- 専用のエージェントの利用等の選択肢は一旦おいといて、あくまで素のプロンプトとしてどこまでできそうかに焦点を置いてます
- 生成した SQL クエリをそのまま使って、データベースへの問い合わせをするようなユースケースを想定しております

# 本題
## システムプロンプト案
結論として、以下あたりのプロンプトに落ち着きました。
精度にブレあれば適宜調整ください。
```txt
ユーザーが欲しいデータについて質問を入力します。
これに対して {データベースサービス}にて実行可能な適切なSQL文をテーブル情報を参考に生成してください。
**SQL文のみ**を出力し、余分な説明やコメントは一切含めないでください。

SQL文にはデータベース名も必ず指定してください。

以下がテーブル情報になります。
{テーブル情報}
```

## テーブル情報に最低限含めるべきもの
- データベース名
- テーブル名
- カラム名
- データ型

## ポイント1: データベース系サービスの明示
一つの目のポイントは、クエリを作成してほしいデータベース系のサービスをプロンプト上で必ず指定することです。
使用しているデータベース系のサービスによって SQL の文法に違いが出てくるためです。
文法が異なる状態でデータベースへクエリで問い合わせてもエラーとなるため注意が必要です。

## ポイント2: SQL文以外の余計な文字列の排除
生成 AI からのクエリ内容をそのままデータベースへの問い合わせの引数として自動的に渡したい場合、クエリ以外の余計な文字列が入ってしまえば、もちろん問い合わせした際に SQL 文とはみなされずエラーとなります。

## ポイント3: データベース名の指定
データベース系のサービスよっては、クエリの中でデータベース名を指定しないとエラーとなるものもあります。
また、データベースを跨いでテーブルの結合等を行いたい場合もこちらでの制御が必須になります。

```sql
-- データベース A から テーブル sample を抽出する場合 〇パターン
SELECT * FROM A.sample

-- データベース A から テーブル sample を抽出する場合 △パターン
SELECT * FROM sample
```

## ポイント4: テーブル情報の提示
適切な SQL を生成するためには現在の環境がどのような状態なのか情報を渡すことが重要です。
テーブル情報に記載されたテーブル名やカラム名を基準に生成 AI はクエリ生成を行います。
データベース系のサービスによっては information_schema などを活用して自動的にテーブル情報を取得できることが望ましいです。
データ型は特に重要で、あらあかじめ情報提示することで動作しない SQL クエリが減ります。
与えるテーブル情報が多すぎてもクエリの精度に影響する可能性があるためご注意ください。

# まとめ
今回 生成 AI でクエリを作成するためのいくつか Tips を紹介させていただきました。 
生成 AI を利用したクエリ精度向上に、参考になれば幸いです。
LeetCodeを1日1題解いてみる (day49)

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

# 599. Minimum Index Sum of Two Lists

https://leetcode.com/problems/minimum-index-sum-of-two-lists/?lang=pythondata

## 問題文
Given two arrays of strings list1 and list2, find the common strings with the least index sum.

A common string is a string that appeared in both list1 and list2.

A common string with the least index sum is a common string such that if it appeared at list1[i] and list2[j] then i + j should be the minimum value among all the other common strings.

Return all the common strings with the least index sum. Return the answer in any order.

## 僕の回答 (失敗作1)
```py
class Solution:
    def findRestaurant(self, list1: List[str], list2: List[str]) -> List[str]:
        tuple1 = set(list1)
        tuple2 = set(list2)
        result = tuple1 & tuple2
        return list(result)
```
とりあえず共通を探すだけで、このやり方だと、インデックスの和が最小になるものを探すことが難しそうだった。

## 僕の回答 (失敗作2)
```py
class Solution:
    def findRestaurant(self, list1: List[str], list2: List[str]) -> List[str]:
        sum_idx = len(list1) + len(list2)
        result = ""
        i = 0
        while sum_idx > i and len(list1) > i:
            j = 0
            while sum_idx > j and len(list2) > j:
                if list1[i] == list2[j] and sum_idx > i + j:
                    result = list1[i]
                    sum_idx = i + j
                j += 1
            i += 1
        return list({result})
```
この場合だと、インデックスの和が最小の単語が複数ある場合に対応できない。

## 僕の回答 (成功例)
```py
class Solution:
    def findRestaurant(self, list1: List[str], list2: List[str]) -> List[str]:
        sum_idx = len(list1) + len(list2)
        result = []
        i = 0
        while len(list1) > i:
            j = 0
            while len(list2) > j:
                if list1[i] == list2[j]:
                    if sum_idx > i + j:
                        result = [list1[i]]
                        sum_idx = i + j
                    elif sum_idx == i + j:
                        result.append(list1[i])
                j += 1
            i += 1
        return result
```
動きはしたが、計算量が多くなって非効率である。

## 良い回答例

```py
class Solution:
    def findRestaurant(self, list1: List[str], list2: List[str]) -> List[str]:
        # list1 の要素を辞書に変換（文字列をキー、インデックスを値として格納）
        index_map = {string: i for i, string in enumerate(list1)}
        min_sum = float('inf')  # 最小インデックス合計（初期値は無限大）
        result = []  # 結果を格納するリスト

        # list2 をループして共通部分を探索
        for j, string in enumerate(list2):
            if string in index_map:
                i = index_map[string]
                index_sum = i + j  # インデックスの合計を計算
                if index_sum < min_sum:
                    # 新しい最小値を見つけたら結果を更新
                    min_sum = index_sum
                    result = [string]
                elif index_sum == min_sum:
                    # 最小値が複数ある場合はリストに追加
                    result.append(string)

        return result
```

## 学んだこと
- 辞書は計算量`O(1)`で見つけることができる
## コメント
辞書を使いこなせるようになりたいと思いました。

## 次の問題はこちら!
https://qiita.com/h_y_s_d/items/808bbe8141f52b99544c
PyGMTにおいて異なるデータを異なる色で着色する

# はじめに
完全に自分の備忘録用なのであしからず。
matplotlibでは、データフレームのある列の値に対してスライスして描画すると勝手に色を変更してくれるが、pyGMTにはそんな便利機能がないため、以下の内容を修正して作成していきたい。

# 作成コード
座標データと何かしらの値を持つデータフレームを用意する。
```python
import pandas as pd

data = {
    "latitude": [20.5, 22.1, 25.3, 30.0, 35.5, 40.0, 42.2, 45.8, 47.0, 50.0],  # 緯度: 20-50
    "longitude": [120.5, 125.0, 130.1, 135.5, 140.0, 145.2, 150.0, 155.3, 157.7, 160.0],  # 経度: 120-160
    "val": [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]  # 値: 1-10
}

df = pd.DataFrame(data)
```

PyGMTの設定
```python
import pygmt

fig = pygmt.Figure()
fig.basemap(
    projection="M15c",  # メルカトル図法で15cm幅の図
    region=f"{120}/{160}/{15}/{48}",  # 表示する緯度経度幅
    map_scale=f"g{140}/{20}+c20+w{800}+f",
)
fig.coast(
    resolution="f",
    land="lightgray",
    frame=["ag", f'+t'],
    region=f"{120}/{160}/{15}/{48}",
)
```

dfをgeoDataFrameに変換
```python
import geopandas as gpd

gdf = gpd.GeoDataFrame(
    df.drop(["longitude", "latitude"], axis=1),
    geometry=[
        Point((lon, lat))
        for (lon, lat) in zip(df["longitude"], df["latitude"])
    ],
)
```


うまくいかなかったコードはこちら
```python
for val, group in gdf.groupby("val"):
    fig.plot(
        data=group,
        style="c0.2c",
    )

fig.show()

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3256521/b3ff181b-3d95-f39a-c63f-e0dcfde98177.png)



# 解決策
単純な方法を用いている。matplotlibのカラーマップから良い感じのものを選び、今回のvalの数で色を取得している

カラーマップは以下を参照
https://matplotlib.org/2.0.2/examples/color/colormaps_reference.html

```python
import matplotlib.pyplot as plt
import matplotlib as mpl

uniq_vals=sorted(df["val"].unique())
num_colors = len(uniq_val)
colormap = plt.get_cmap("tab20", num_colors)  # カラーマップを生成
group_colors = {group: mpl.colors.rgb2hex(colormap(i)) for i, group in enumerate(uniq_vals)}

for group, color in group_colors.items():
    group_data = df[df["val"] == group]
    if len(group_data):
        fig.plot(
            data=group_data,
            style="c0.2c",
            fill=color,
        )
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3256521/bf2f8ffa-13be-b0c8-f1f0-9da373d0b99e.png)

# おわりに
PyGMTで適当な色に着色することが多かったので、一旦これで作成する。
調べればPyGMTないでも何かしらのモジュールがありそうではあるが...
datetimeの精度によるデータ差分が発生したときの対応

# 背景
- 既存の複数テーブルから必要な情報をまとめて最適化したテーブルを作りたい
- SQLServerのストアドプロシージャで定期的に差分のあったレコードのみをINSERT,UPDATE,DELETEするようにしたい

# 発生した課題
ストアドプロシージャが実行されるたびに大量更新が入ってしまう事象が発生した

# 原因
文字列等の比較での差分発生ではなく、datetimeの比較で差分を検知して更新処理が走っているということがわかった。

### 該当カラムの値
既存テーブル(existing_table)のmodified_at（datetime）：`2024/01/30 12:00:00.555`
新テーブル(new_table)のmodified_at（datetime2(6)）：`2024/01/30 12:00:00.555000`

パッと見では同じと判定されてもおかしくないように見える

#### 該当部分のクエリ
```sql
OR (COALESCE(new_table.modified_at, DefaultDatetime) <> COALESCE(existing_table
.modified_at, DefaultDatetime))
```

#### 定義が異なる背景
既存テーブルでは日時カラムにdatetime, datetime2(6)が混在していた。
同じ日時カラムなのに型や小数秒が違う理由は意思統一をはかる前に作られたかどうかで、
意思統一後はdatetime2(6)に統一されていた。

### 判明した課題
datetimeとdatetime2(6)を比較するときに
datetime: `2024/01/30 12:00:00.555` と datetime2(6) `2024/01/30 12:00:00.555000` を比較すると実際には次の比較となっている。
![スクリーンショット 2024-12-23 15.40.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439703/2622bb53-87b8-6521-7bf9-52e59edb5025.png)
ぱっと見では一致しているように見えているが実際の比較では一致していないと判断されているということがわかった


datetimeをdatetime2(6)に代入すると次のようになる
`2024/01/30 12:00:00.550` → `2024/01/30 12:00:00.550000`
`2024/01/30 12:00:00.553` → `2024/01/30 12:00:00.553333`
`2024/01/30 12:00:00.557` → `2024/01/30 12:00:00.556667`
![スクリーンショット 2024-12-23 15.57.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439703/262fbd4b-494f-a182-a5a3-2c9c2cea4faa.png)


# 対応案と検証

- datetimeとdatetime2(6)を比較する際には、datetime2(6)に変換して比較する (精度の高いほうに合わせる)
  - `2024/01/30 12:00:00.557`をdatetime2(6)に変換すると前述の通り、`2024-01-30 12:00:00.556667`になるので一致しないデータになってしまう
- datetimeとdatetime2(6)を比較する際には、datetimeに変換して比較する (精度の低いほうに合わせる)
  - `2024/01/30 12:00:00.555000`をdatetimeに変換すると`2024-01-30 12:00:00.557`になる
  - ![スクリーンショット 2024-12-23 15.59.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/439703/f58a3104-08f4-9414-bc13-3bbf4efca66f.png)

- datetimeのデータをdatetime2(6)のカラムにINSERTするときは、datetime2(6)に変換してからINSERTする
  - datetime: `2024/01/30 12:00:00.557` をdatetime2(6)に投入すると前述の通り、`2024/01/30 12:00:00.556667`となるので一致しないデータになる

# 今回採用した対応方法
datetimeの精度が**低い方**にCONVERTして比較するようにして対応
精度が高い方にCONVERTすると前述した通り、最後の1桁が丸められてしまうので一度更新したあと再び更新対象になってしまう

```sql
OR (COALESCE(CONVERT(new_table.modified_at, datetime), DefaultDatetime) <> COALESCE(existing_table
.modified_at, DefaultDatetime))
```

# 結果
データ差分があるときのみ更新が入るようになった
routes.rbでちょっと便利なresolveメソッド

Railsの`routes.rb`で使用できる`resolve`メソッドについて紹介します。`resolve`メソッドは、`polymorphic_path`や`polymorphic_url`にモデルインスタンスを渡した際に生成されるURLをカスタマイズできます。

## resolveとは

Railsのビューヘルパーでは、`link_to 'title', @post`のように、URLが必要とされる箇所でモデルインスタンスを直接渡すことができます。このとき、内部では`polymorphic_path`によってURLが生成されています。`resolve`を使用することで、この生成されるURLを柔軟に変更することが可能になります。

## polymorphic_pathの挙動

`polymorphic_path`には、主にモデルインスタンスやシンボル、またはそれらの配列を渡すことができます。

* **モデルインスタンスの場合:** `persisted?`（データベースに保存済みかどうか）によって、単数形（モデルインスタンスの引数つき）または複数形のURLヘルパーが使用されます。例えば、`@post`が保存済みであれば`post_path(@post)`、未保存であれば`posts_path`が使用されます。
* **シンボルの場合:** そのシンボル名に対応するURLヘルパーが使用されます。例えば、`:basket`であれば`basket_path`が使用されます。
* **配列の場合:** 配列の先頭から順に、中身に応じて名前を`_`で連結したURLヘルパーが組み立てられます。モデルインスタンスとシンボルが混在している場合も、上記ルールに従って処理されます（詳細は後述）。

## 単数リソースにおけるresolveの活用

単数リソース（例えば、`resource :basket`）の場合、`form_with model: @basket do |f|`のように記述すると、内部で`polymorphic_path(@basket)`が実行され、通常は`basket_path(@basket)`が呼ばれ、URLは`/baskets/:id`となります。しかし、単数リソースでは`/basket`というURLを期待する場合が多いです。

このような場合に、`resolve`が役立ちます。以下のように定義することで、`polymorphic_path(@basket)`が`polymorphic_path([:basket])`と同様に扱われるようになります[^1]。

```ruby
resolve "Basket" do
  [:basket]
end
```

この定義により、`polymorphic_path(@basket)`の内部でこの定義が参照され、シンボル`:basket`から`basket_path`が組み立てられ（引数なし）、URLは`/basket`となります。

[^1]: 要素が1つだけなので配列`[:basket]`ではなく単に`:basket`でも問題ありませんが、Railsガイドやリファレンスマニュアルでは配列で記述されていることが多いため、ここではそれに倣っています。また、実際の動作は`polymorphic_url([:basket])`が呼ばれた後、パス部分だけが抜き出されます。

## URLの配列記法とネストされたリソース

`polymorphic_path`には配列を渡すことができ、これはネストされたリソースや名前空間内のリソースを指す際に便利です。

* `polymorphic_path([@post, @comment])`は`post_comment_path(@post, @comment)`に相当します。
* `polymorphic_path([:admin, @post])`は`admin_post_path(@post)`に相当します。

ネストされたリソースの場合、`resolve`を使って子リソースのインスタンスのみからネストされたURLを生成することができます。

```ruby
resolve "Comment" do |comment|
  [comment.post, comment]
end
```

この定義により、`polymorphic_path(@comment)`は`/posts/:post_id/comments/:id`というURLを生成するようになります。これにより、`link_to "コメント", @comment`のように簡潔に記述することができます。

## まとめ

`resolve`の利用法として単数リソースのURLを調整したり、ネストされたリソースのURLを簡略化する例を挙げましたが、他にももっと複雑なURLのルールを作るときにも便利です。活用してみましょう！

## References

* [Rails のルーティング \- Railsガイド](https://railsguides.jp/routing.html#resolve%E3%82%92%E4%BD%BF%E3%81%86)
* [ActionDispatch::Routing::Mapper::CustomUrls](https://api.rubyonrails.org/classes/ActionDispatch/Routing/Mapper/CustomUrls.html)
Python入門

# Python入門

## 目次
1. [Pythonとは？](#pythonとは)
2. [Pythonのインストール](#pythonのインストール)
3. [基本文法](#基本文法)
    - [変数とデータ型](#変数とデータ型)
    - [条件分岐](#条件分岐)
    - [ループ](#ループ)
4. [簡単なプログラム例](#簡単なプログラム例)
5. [次のステップ](#次のステップ)

---

## Pythonとは？

Pythonは、シンプルで読みやすい文法を持つプログラミング言語。
主に次のような特徴がある:
- **初心者に優しい**：コードが直感的で分かりやすい。
- **多用途**：Web開発、データ分析、AIなど、幅広い分野で利用可能。
- **豊富なライブラリ**：便利なツールがたくさん用意されている。

---

## Pythonのインストール

### 手順
1. [Python公式サイト](https://www.python.org/)にアクセス。
2. 最新版のPythonをダウンロード。
3. インストーラーを実行して、`Add Python to PATH`にチェックを入れてインストール。
:::note warn
defaultの場所にinstallするのではなく
C:/ とかに pythonフォルダ を作っておいてそこにインストールするのがいい
:::
### 動作確認
ターミナルやコマンドプロンプトで次を実行：
```bash
python --version
```

正しくインストールされていれば、バージョン番号が表示される。

---

## 基本文法

### 変数とデータ型

#### 例：
```python
# 変数の宣言
x = 10      # 整数型
name = "Python"  # 文字列型
is_active = True  # ブール型

print(x, name, is_active)
```

### 条件分岐

#### 例：
```python
# if文の使い方
score = 85
if score >= 90:
    print("Aランク")
elif score >= 70:
    print("Bランク")
else:
    print("Cランク")
```

### ループ

#### 例：
```python
# forループ
for i in range(5):
    print(i)

# whileループ
count = 0
while count < 5:
    print(count)
    count += 1
```

---

## 簡単なプログラム例

次のプログラムは、ユーザーの名前を入力して挨拶するもの：

```python
# ユーザー入力を取得して挨拶
name = input("あなたの名前は何ですか？: ")
print(f"こんにちは、{name}さん！")
```

---

## 次のステップ

- [公式ドキュメント](https://docs.python.org/ja/3/)
- [Python学習サイト](https://www.python.org/about/gettingstarted/)
---
APIってなんすか？じゃ、Railsで作ってみよう！

# はじめに

この記事は、株式会社スピードリンクジャパン Advent Calendar 2024 の24日目の記事です。


https://qiita.com/advent-calendar/2024/slj



こんにちは！
今回はAPIについてと、実際にAPIを作ってみてという内容になります。
僕自身も初めてAPIを作成してみたので、温かい目で見ていただけると幸いです！
では、いきましょう！

![タイトルなし.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524127/7ba18c39-9539-2c4d-47d0-28317bf36d4f.gif)


# APIってなんすか？

 API（Application Programming Interface）は、異なるソフトウェア同士が互いに通信するためのインターフェースです。簡単に言うと、APIは「他のシステムやサービスとやりとりをするためのルールや手段」を提供します。

例えば、あるアプリケーションが他のアプリケーションのデータを取得したい場合、そのやりとりをするためにAPIを使います。

#### APIが必要な理由
異なるシステムや言語間でのデータや機能の共有
サービスを拡張するために他のアプリケーションと連携する
自動化や統合を進めるためにAPIを利用する

※以下の記事を参考とさせていただきました！
実際に詳しい内容が書かれているので、気になった方はチェック！↓

https://qiita.com/shimada_slj/items/175168fc744d4788e60d

# APIの種類は？

### REST API（Representational State Transfer）
最も一般的に使用されるAPIの種類です。
HTTPメソッド（GET, POST, PUT, DELETE）を使ってデータを操作します。
RESTfulなAPIは、ステートレスで、リソースベースで設計されます。
### SOAP API（Simple Object Access Protocol）
XMLベースでメッセージをやりとりします。
セキュリティやトランザクション管理などの高度な機能を持っていますが、RESTよりも設定が複雑です。
### GraphQL API
データをクエリとして取得できるAPI。
クライアント側が必要なデータだけを要求できるので、効率的にデータを取得できます。
### WebSocket API
双方向通信を可能にするAPI。
リアルタイムでデータをやりとりするため、チャットアプリやライブアップデートなどで使用されます。


# 実際に作成してみた手順

こちらの記事を参考に実際にRailsでAPIを作成してみました！
私自身が下記の記事を参考に簡単に説明させていただきます。

https://zenn.dev/mochinozenn/articles/190e2e8b8f1edb

モデルを作成します
```php
rails g model vegetables name:string place_of_birth:string   
```

seeds.rbに仮のデータを作成します。
```php
vegetables = [
  ["キャベツ", "長野県"],
  ["トマト", "熊本県"],
  ["きゅうり", "宮崎県"],
  ["ナス", "高知県"],
  ["ほうれん草", "埼玉県"]
]

vegetables.each do |name, place_of_birth|
  Vegetable.create(name:, place_of_birth:)
end

```

データを挿入します。
```php
rails db:seed
```

ルーティングの設定

```php
  resources :vegetables, only: :index
```

コントローラーの作成・記述
コマンドを生成
```php
rails g controller vegetables
```

コントローラーに記述
```php3
  def index
    vegetables = Vegetable.all
    render status: 200, json: { vegetables: }
  end
```

アクセスする
```php
rails s
```
![mmm_1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524127/ef31fa19-1583-c69a-6ca0-b0d64c0e30dc.gif)

上記のように、JSON形式で入れたデータたちが帰ってきました！
このようになれば、成功らしいです！

# APIを作ってできること
データの公開と共有
APIを作ることで、内部で管理しているデータを外部のサービスやアプリケーションと共有できます。例えば、作成したAPIを使って、自社アプリやWebサービスをリアルタイムで表示する機能を実装できます。

モバイルアプリとの連携
作成したAPIを利用すれば、モバイルアプリからも同じデータにアクセスできるようになります。これにより、Webサイトやモバイルアプリで統一されたデータ表示が可能になります。

外部サービスとの連携
他のサービスやアプリケーションと連携しやすくなります。例えば、自社データを外部の分析ツールに送ったり、SNSに自動投稿する仕組みを構築できます。

業務の効率化と自動化
APIを通じて定期的にデータを取得したり更新したりすることで、手動作業を減らし、自動化することができます。たとえば、倉庫の在庫情報をAPI経由で他システムと同期させることで、作業を効率化できます。

リアルタイムなデータ提供
REST APIを利用することで、ユーザーが必要なときに最新のデータを提供できるようになります。例えば、新しい野菜の情報が追加された場合、APIを通じてすぐにデータを反映できます。


# 最後に
いかがでしたか？
今回改めて、APIを作ってみたり、調べたりすることで実際に自分で作成したAPIを使ってアプリやシステムを作成してみたいなと思いました。
では、また次の記事で〜！
Pythonの開発環境を構築する方法


# Pythonの開発環境を構築する方法

Pythonでの開発を効率的に行うために、プロジェクトごとに独立した環境を構築する方法をステップごとに説明します。

---

## 1. プロジェクトフォルダの作成

まず、Pythonコードを保存するためのフォルダを作成します。

### **手順**
1. ターミナルを開きます。
2. 作業場所（例: デスクトップ）に移動します。
   ```bash
   cd ~/Desktop
   ```
3. フォルダを作成します（例: `PythonLecture`）。
   ```bash
   mkdir PythonLecture
   ```
4. 作成したフォルダ内に移動します。
   ```bash
   cd PythonLecture
   ```
5. 必要であれば、フォルダ内にサブフォルダ（例: `basic`）を作成します。
   ```bash
   mkdir basic
   cd basic
   ```

---

## 2. 仮想環境を作成する

仮想環境は、プロジェクトごとにPythonのバージョンやライブラリを独立して管理できる仕組みです。

### **python3 -m venv venv コマンドの説明**

このコマンドは、仮想環境を作成するためのものです。以下に、分解して説明します。

1. **`python3`**  
   - Python3を使うことを指定しています。
   - この記事では、`python3` コマンドでPython 3.13.1を呼び出しています。

2. **`-m`**  
   - 「モジュールを実行する」というオプションです。
   - この場合、`venv` というモジュールをPythonで実行しています。

3. **`venv`**  
   - Pythonに標準搭載されているモジュールで、仮想環境を作成します。

4. **`venv`（最後の引数）**  
   - 仮想環境を作成するフォルダ名を指定しています。
   - ここでは、`venv` という名前のフォルダを作成します。この中に仮想環境が保存されます。

::: note info

## モジュールとは？

- **Pythonの機能を拡張するための部品**：
  モジュールには、便利な関数、変数、クラスなどがまとめられています。これを使うことで、コードを書く手間を省き、効率的にプログラムを作成できます。

- **Pythonファイルそのもの**：
  Pythonのファイル（拡張子が `.py` のファイル）は、すべてモジュールとして使うことができます。

---


## なぜモジュールを使うのか？

1. **コードを再利用できる**：
   - 一度作った機能を、他のプロジェクトでも使えるようにするため。

2. **プログラムを整理できる**：
   - 大きなプログラムを複数のモジュールに分割することで、管理しやすくなる。

3. **便利な機能がすでに用意されている**：
   - Pythonには標準モジュール（すでにPythonに組み込まれているモジュール）がたくさんあります。

:::
---

### **手順**
1. 仮想環境を作成するコマンドを実行します。
   ```bash
   python3 -m venv venv
   ```
   - このコマンドを実行すると、現在のフォルダ内に `venv` フォルダが作成されます。

2. フォルダが作成されたことを確認します。
   ```bash
   ls
   ```
   - 出力に `venv` と表示されていれば成功です。

---

## 3. 仮想環境を有効化する

仮想環境を有効化すると、そのプロジェクト専用のPythonとライブラリを使えるようになります。

### **手順**
1. 仮想環境を有効化します。
   ```bash
   source venv/bin/activate
   ```
   - 実行後、ターミナルの先頭に `(venv)` と表示されます。例：
     ```
     (venv) yourname@yourmac basic %
     ```

2. 仮想環境が有効化されたか確認します。
   ```bash
   python --version
   ```
   - 仮想環境内で使用しているPythonのバージョンが表示されます。

---

## 4. 仮想環境を無効化する

仮想環境を終了して元のシステム全体のPythonに戻るには、以下のコマンドを実行します。

### **手順**
```bash
deactivate
```
- 実行後、ターミナルの `(venv)` 表記が消えます。

---

## 5. サンプルコードを作成してテストする

仮想環境が有効な状態で、簡単なPythonコードを実行してみます。

### **手順**
1. フォルダ内に新しいPythonファイルを作成します（例: `main.py`）。
2. 以下の内容を入力して保存します。
   ```python
   print("Hello, Python!")
   ```
3. ターミナルでPythonコードを実行します。
   ```bash
   python main.py
   ```
4. 出力に `Hello, Python!` と表示されれば成功です！

---

## 6. ライブラリのインストール（オプション）

必要なライブラリを仮想環境内にインストールします。以下は例です。

### **手順**
1. 仮想環境が有効化されていることを確認します（ターミナルに `(venv)` が表示されている）。
2. `requests`ライブラリをインストールします。
   ```bash
   pip install requests
   ```
3. インストールされているライブラリを確認します。
   ```bash
   pip list
   ```
4. 特定のライブラリをアンインストールする場合：
   ```bash
   pip uninstall requests
   ```

---

## 7. 開発環境の確認まとめ

- **プロジェクトフォルダ**に仮想環境が正しく作成されている。
- **仮想環境を有効化**すると、他のプロジェクトやシステム全体に影響を与えない。
- 必要なライブラリをプロジェクトごとにインストール・管理できる。

---

## **仮想環境を使うメリット**
- **安全性**：他のプロジェクトやシステム全体に影響を与えない。
- **柔軟性**：プロジェクトごとに異なるPythonバージョンやライブラリを使える。
- **簡単さ**：`venv`を使えば、ほとんどの操作が数行のコマンドで完了する。

---

これでPythonの開発環境は完璧に準備できます！
私も現在勉強中なので、もし初学者の方がいたら、学んだことをブログ記事などにしてシェアしてもらえたらとても助かります！互いに高め合っていきましょう！
Backlog APIで取得した課題をBacklog APIで登録しようとしたら "Incorrect String..."と怒られてしまいました😢

小ネタで恐縮ですがややハマったので書き残しておきます。

さる事情でBacklogテナントAからBacklogテナントBへとプロジェクトの課題（Issue）をマイグレーションする作業をしていた折、遭遇した問題です。

# Problem

[Backlog4j](https://github.com/nulab/backlog4j)を利用しマイグレーション（データコピー）のためのツールを作成。
テナントAのプロジェクトの課題を取得し、テナントBのプロジェクトの課題として登録しようとしたところエラーになった。

ステータスコードは400。メッセージは：

```
Incorrect String: %F0...
```

チケットをよく見たところ「課題の詳細」（Description）のテキストに絵文字（📋️）が含まれている。

# Solution

課題を登録するロジックで 課題の詳細のテキストから絵文字を除去する（あるいは他の文字に置換する）：

```java
final String description = "..."; // Backlog APIで取得した課題の詳細（絵文字を含み得る）

final Pattern emojiPattern = Pattern.compile("[\\p{So}\\p{Cn}]");
final Matcher emojiMatcher = emojiPattern.matcher(description);
final String emojilessDescrption = emojiMatcher.replaceAll(""); // 絵文字を除去

final CreateIssueParams newIssue = new CreateIssueParams(...);
newIssue.description(emojilessDescrption); // 課題の登録用パラメータに設定
```

~~Backlogはブラウザでアクセスする画面では絵文字を受け付ける。Backlog4jが利用するBacklog APIも、データを取得する要求に対してはその絵文字を含んだテキストを返してくる。しかしデータを登録する要求に対しては絵文字を含んだテキストを受け付けずエラーを返してくる。~~ Backlogにはドメインが複数ある。`backlog.com`でホストされたテナントでは📋️をはじめ多くの絵文字が利用可能なようだが、 `backlog.jp`でホストされたテナントでは📋️や👍️は利用不可（一方で✨️などは利用可）。ヌーラボに問い合わせたところこれは「仕様」とのこと（2024/12/25：原因について訂正）。
やや憮然とさせられてしまう動作だが致し方ない。絵文字を除去する。
Svelte5でFullScreen切り替えUを作ったよ

10月下旬にSvelte5がリリースされて、旧来のSvelte4とは書きっぷりがだいぶ進化したよね。

Svelte4の「絶対に素のJSの書きっぷりに抑える」という路線も頑な意志を感じられて好きだったけど、実際仕事で使うならSvelte5のRunesをはじめとする重大なアップデートはすごく良い印象を持ってる。

今回、自分がSvelte5をキャッチアップするためにFullScreen切り替えコンポーネントを作ったから、せっかくだからソースコードを紹介するね。

## FullScreen切り替えUI

RunesはSvelteの印象を大きく変えたなーと思ってる。

https://svelte.dev/docs/svelte/what-are-runes

次のコード中でいうところの、 `$state` や `$props` がRunesだね。Svelte4なら `let xxxState = something` や `export let xxxxProp` って書いていたけれど、あえて素のJSらしさを幾らか捨てて、他のフロントエンドフレームワークのように専用の書き方を用意してきたんだなーって感じたよ。

```svelte:FullScreenControl.svelte
<script lang="ts">
  import { onMount, type Snippet } from "svelte";

  const {
    enterFullScreenLabel,
    exitFullScreenLabel,
  }: {
    enterFullScreenLabel: Snippet;
    exitFullScreenLabel: Snippet;
  } = $props();

  let isFullscreen = $state(false);

  onMount(() => {
    isFullscreen = document.fullscreenElement !== null;
  });

  const handleOnClick = () => {
    if (document.fullscreenElement) {
      document.exitFullscreen();
      isFullscreen = false;
      return;
    }
    document.body.requestFullscreen();
    isFullscreen = true;
  };
</script>

<button
  title={isFullscreen ? "Exit fullscreen" : "Enter fullscreen"}
  aria-label={isFullscreen ? "Exit fullscreen" : "Enter fullscreen"}
  onclick={handleOnClick}
>
  {#if isFullscreen}
    {@render exitFullScreenLabel()}
  {:else}
    {@render enterFullScreenLabel()}
  {/if}
</button>
```

あとslotを非推奨にして、`@render` というより強力な機能を登場させたのも印象的だね。上記でも使っていて、親クラスから渡される `snippet` を描画するようにしているんだ。

## Usage

実装したフルスクリーン切り替えUIコンポーネントを使う時は次のようにするよ。子要素として記述したsnippetは暗黙的に親コンポーネントのpropsに渡される仕様だね。slot時代のイメージから容易に移行できるようにあえてこの書き方をしているけど、snippetは他にも渡し方がある。詳しくは公式ドキュメントを読んでね。

https://svelte.dev/docs/svelte/snippet

```svelte
<FullScreenControl>
  {#snippet exitFullScreenLabel()}
    <ArrowsPointingIn />
  {/snippet}
  {#snippet enterFullScreenLabel()}
    <ArrowsPointingOut />
  {/snippet}
</FullScreenControl>
```
XIAO MG24 Sense(Silicon Labs EFR32MG24)をArduinoで触ろう

__SeeeedStudio__ から __EFR32MG24__ を搭載した __XIAO MG24__ が発売されました。

https://www.seeedstudio.com/Seeed-XIAO-MG24-Sense-p-6248.html
__Silicon Labs__ は __Arduino開発環境__ を提供しており、Arduinoからは __Arduino Nano Matter__ がリリースされています。

https://qiita.com/usashirou/items/6581041e0ec7a34ff2e2

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/a5faf342-f2ad-8eaf-17d9-0caa7cfa65c0.png)
7ドルからと安価で、技適も取得してそうなので入手してみました。

https://wiki.seeedstudio.com/xiao_mg24_getting_started/

ですが、届いたのは残念ながら技適なしでした。
いずれ技適取得し、入手しややすくなると思われますので、少し待った方が良いでしょう。
それでは使ってみましょう。
なお、本内容は、__Software Preparation__ をもとに構成しています。
# 環境構築
__ファイル⇒基本設定__
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/5c7c2785-4da2-efc9-d748-a2ee6055f6c2.png)
__追加のボードマネージャのURL__ に以下を入力して、OKをクリックします。

    https://siliconlabs.github.io/arduino/package_arduinosilabs_index.json 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/ee0fd7d9-6011-458d-816d-59ae1e801f14.png)
# ボード設定を追加する
__ツール⇒ボード⇒ボードマネージャー__ をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/995f3c20-215e-f789-6eb6-5227cf2a2854.png)
ボードマネージャから __SiliconLabs__ を検索し SiliconLabsのボード設定を __インストール__ します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/8bfe8599-1e83-0086-8ed2-ddcae1ac39f2.png)
ボード設定をインストール後にボードを指定します。
__他のボードとポートを選択__ を __クリック__ します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/01b8a82e-7f63-e5ac-3b99-288e5c3e8cb1.png)
ボードに __xiao__ と入力し __Seed Studio XIAO MG24__ を指定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/4de81848-8895-bfac-7bfc-8f4a5746bc75.png)
__XIAO MG24__ をPCと接続すると __ポート__ に __COMポート__ が選択できますので、選択して __OK__ をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/3aba3977-879c-f081-4670-cea6df17e608.png)
以上でXiao MG24の環境構築が完了しました。
# Lチカ
Lチカをしましょう。
__ファイル⇒スケッチ例⇒01.Basic⇒Blink__ を選択します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/cc6c4216-2cdd-09a5-4779-e17b88afd49f.png)
__➡__ でコンパイル、書込みをします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/f03f35ab-9367-3d9a-8650-99621de5ab4c.png)
以上で書き込みが完了しました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/285344/bdd49c19-1d08-2e91-5478-45b1a91c8520.png)
LEDが点滅すれば問題なく動作していることになります。
スッキリわかるSQL入門 第4版 を読んでみて

今回のブログではスッキリわかるSQL入門 第4版 ドリル256問付き! (スッキリわかる入門シリーズ)を読んで
- 良かったところ
- 悪かったところ(もしあれば)
- 学んだこと
- 難しかったこと
以上4点を書いていきます。

[スッキリわかるSQL入門 第4版 ドリル256問付き! (スッキリわかる入門シリーズ)
](https://www.amazon.co.jp/%E3%82%B9%E3%83%83%E3%82%AD%E3%83%AA%E3%82%8F%E3%81%8B%E3%82%8BSQL%E5%85%A5%E9%96%80-%E7%AC%AC4%E7%89%88-%E3%83%89%E3%83%AA%E3%83%AB256%E5%95%8F%E4%BB%98%E3%81%8D%EF%BC%81-%E3%82%B9%E3%83%83%E3%82%AD%E3%83%AA%E3%82%8F%E3%81%8B%E3%82%8B%E5%85%A5%E9%96%80%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA-%E4%B8%AD%E5%B1%B1/dp/4295018465)

## 良かったところ
#### 登場人物の会話形式になっている
私が思う専門書のイメージは文章＋図という形式だったのですが、
入門書ということもあり読みやすくなっています。

登場人物らが対話形式で話を進めていき疑問が出てきた場面でSQLについての説明をするようになっています。

#### 図解がわかりやすい
図を説明する際に矢印や表を使っているので概念を理解する上でかなりわかりやすいです。

概念を理解したらより詳細をググって調べたり、問題を解いて見たらするという形で学習を進めることでインプット→アウトプットが早くなります。

#### 練習問題だけではない
この書籍は章末に練習問題が用意されています。ただそれだけではなくこの書籍の最後の部分に**ドリルと総合問題がある**ところもいい所です。
今までインプットした内容が定着しているか確認するために利用できます。


## 悪かったところ
**※これは書籍の内容自体に問題があるわけではありません。**
kindle版ですと文字検索できないところが不便であると感じました。

例えば、ドリルをやっている際に文字列の切り出し→`SUBSTRING`と思い出した際にそこのページに一気に戻りたいのですが

`command`+F もしくは `cntl`+F がこの書籍のkindle版は使えません

目次→多分この辺？→ページを探す
毎回この作業が必要です。

書籍に頼るよりググった方が早いと感じました。
## 学んだこと
#### 正規化
正規化とは、データに矛盾を起こさせないようにテーブルを複数に分ける作業のことです。
正規化をするメリットは
- データの管理がしやすい
- データ検索が速くなる

このメリットを受けるためには
非正規形→第一正規形→第二正規形→第三正規形
以上3つの変形が必要となります。

第一正規化では「レコード」と「カラム」の繰り返しをなくす。
第二正規化と第三正規化では、Aが決まるとBが1つに決まる項目を別の表にします。

#### トランザクション
トランザクションとは、**一部だけ実行してはいけない複数の処理をひとかたまりにしたものです**

一例として送金処理があります。
とある口座からAさんがBさんに1万円送金をする際の処理を見て見ましょう

1. Aさんが1万円Bさんに送金するボタンをクリック
1. Aさんの口座から1万円マイナス
1. Bさんの口座に1万円プラスする
1. Bさんが送金を確認する

3番の処理中に停電が起こり2番まで行われていたとします。

その場合、復旧した後にAさんの口座を見ると1万円減っていますが
Bさんには送金されていない事になるのです。

このようなことが起こらない為に、トランザクションというものが使われています。

## 難しかったこと
#### サブクエリ
概念は理解できましたが、今現在でも問題を解けていません。
`SELECT`、`FROM`, `WHERE`どこでもカッコをつけて`SELECT`文を新しく作れるっていうのが驚きでした。

少し調べると今後、開発の現場に入った際は必要になる技術であると確信しました。
- 再利用性が高く複数の抽出を1回のSQLにまとめられる
- 抽出するデータ量を減らして、高速化することができる

今は完全理解できていませんが、少しずつ慣れて使いこなせるようになります
VSCode で 「ターミナルを再アクティブ化しています」 が終わらない事象の解決

ターミナルを再アクティブ化しています の処理が終わらない事象に遭遇したので解決策を残しておきます。

# Python インタープリターの再設定で直る

1. Ctrl + Shift + P を押してコマンドパレットを開きます

2. 行頭の ">" は削除せず、「インタープリターの選択」を指定します


3. 使用したいインタープリターを指定します
能登地震発生後にDroneBirdはどのように動いていたのか（防災協定の重要性）

# 地震発生から１年
能登の地震が発生して１年が経とうという時期になりました。
震災があったのかという生活を送っていますが、皆さんには１年という時の流れをどう感じるでしょうか？

# DroneBirdと震災
ドローンを用いて空撮した写真をつなぎ合わせて、「オルソ写真」というつなぎわせた大きな写真を作ります。
その後、OpenStreetMapという無料で使用できる地図に重ねてどこに建物があるか描いていきます。
撮影した時の最新の地図が描くことができるます。

ドローンで空撮する人
地図を描く人
みなさんボランティアでやろうということです。

# 能登の地震
大規模災害でドローンを用いた撮影をすることは、道路事情により、今回は一部に留まりました。
また国土地理院を含めた他の団体からの映像や写真を用いて、重ね合わせた写真なども作成を試みたところです。
また、DroneBirdだけではなく、他の団体も救援活動に入りましたが、東日本大震災以降の熊本やその他の豪雨災害でも類を見ない活動の困難さを経験したと言えるでしょう。

# 震災発生ふぉのDroneBird
コアメンバーによる情報共有と別の団体の活動にも参加している隊員も多く、どの立場であっても災害現場で機能することが最優先とされます。

DroneBirdでは３０を超える県や地方団体と協定を結んでいますが、中部地方も東南海地震に備え活動しており、名古屋市さんと協定を結んでいます。

まずは被害状況の把握、遠方であったことで名古屋市内には被害がみられませんでしたが、能登地方にアクセスするために、名古屋市からの支援で緊急車両用の証明の発行を迅速に行っていただきました。
たとえ、その自治体で災害が起こらなくても、「防災」というキーワードから支援に向かう隊員を支援していただけたということは誠に素晴らしいことが起こったと感動しました。

# まとめ
このように、本質的な災害活動を目的となった縁が、一般的な活動に飛躍してつながりを見せ、日本における防災への柔軟性が垣間見えた出来事となり、改めて、「協定を結ぶ」という価値を認識しました。
ECRへのpushをEventBridgeで検知してfargateコンテナを更新する

CDKで構築することもできますが、うまくいかないことが稀にあり手作業が発生するため忘備録。
ソースコードはCodePipelineを使わずgithub actionsでテスト＆ビルド＆プッシュをしています。

## 準備
- ECRにリポジトリを作成する
- Fargateサービスを構築して実行しておく

## 構築
EventBridgeでイベントバスを作成。スキーマの検出は「開始済み」にしておきます。
これはdefaultのイベントバスでも構わないです。

### ルールの詳細を定義
作成したイベントバスを選択してルールを作成。
ルールタイプを「イベントパターンを持つルール」

### イベントパターンを構築
パターンフォームを使用するかJSONエディタで直接設定しますが、結果jsonは以下のようになります。
```json
{
  "source": ["aws.ecr"],
  "detail-type": ["ECR Image Action"],
  "detail": {
    "action-type": ["PUSH"],
    "result": ["SUCCESS"],
    "repository-name": ["sample-app"],
    "image-tag": ["latest"]
  }
}
```

### ターゲットを選択
ターゲットタイプを「AWSのサービス」、ターゲットに「Step Functions ステートマシン」を選択。
新しいステートマシンを作成します。

#### ステートマシン作成
Step Functionsから作成します。
エディタ上では以下のような感じ。

![step_functions_state.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6404/25bcbe99-fb07-5e1e-7a3c-e9a914bd9122.png)


コードだと
```json
{
  "Comment": "A description of my state machine",
  "StartAt": "UpdateService",
  "States": {
    "UpdateService": {
      "Type": "Task",
      "Parameters": {
        "Cluster": "sample-cluster",
        "Service": "sample-service",
        "DesiredCount": 1,
        "EnableExecuteCommand": true,
        "ForceNewDeployment": true
      },
      "Resource": "arn:aws:states:::aws-sdk:ecs:updateService",
      "End": true
    }
  }
}
```

上記を保存してステートマシンを選択します。

実行ロールを作成して選択します。
ロールのポリシーは以下のような感じ。
```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "states:StartExecution"
            ],
            "Resource": [
                "ここに作成したステートマシンのARN"
            ]
        }
    ]
}
```

これでECRにpushされるとfargateのコンテナがlatestのイメージで更新されるようになります。
【PLATEAアカデミー】対面講座での経験と復習【大阪会場】

# はじめに

PLATEAUアカデミーの3D都市モデル作成コース（大阪会場）に参加してきました。このコースは3日間にわたるハンズオン形式の講座で、FMEを使用した基礎的なデータ変換から、土地利用モデル、都市計画決定情報モデル、建築物モデル（LOD1、LOD2）の作成まで、3D都市モデルの作成に必要な一連の工程を学ぶことが出来ました。
講座では特に建築物モデルの作成に重点が置かれており、建築物IDの付与から始まり、LOD1、LOD2の作成、さらには属性付与まで、実践的な内容でした。品質検査は今回時間が足りず出来ませんでしたが、ハンズオン形式で学ぶことが出来て大変勉強になった3日間でした。

講習会で使用したノートPCスペック
- OS：Windows11
- CPU：Intel(R) Core(TM) i7-13650HX 2.60 GHz
- メモリ：32.0 GB

# PLATEAUアカデミーで学んだことの復習

[QGIS](https://www.qgis.org/)は普段から使うことがあり、操作方法や作業内容は概ね理解出来ていましたが、[Blender](https://www.blender.jp/)については、少し触れたことがある程度です。FMEに関しては、全く触れたことがありませんでした。

3日間の講義では目の前の作業をこなすことで精一杯であったため、より深い理解を得るために、実践した内容（QGISとBlender）の復習を行いました。


## QGISでの作業

土地利用モデルと都市計画決定情報モデルは同様の作業工程であるため、都市計画決定情報モデルの作成に絞って実施しました。講座で学んだ建築物IDの付与とLOD1モデルの作成について復習を行いました。QGISの基本操作には多少慣れていることもあり、LOD1までのモデル作成は問題なく出来ました。

![QGIS.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3964465/587ee22b-b749-162d-e0a3-022a9e5e3133.jpeg)

## Blenderでの作業

Blenderでは、LOD2のモデル作成を行いました。まず、押し出し機能でオブジェクトを立体化し、その後テクスチャマッピングの作業を行いました。Blenderでの操作方法に不慣れであったため、QGISでの作業より多くの時間要しましたが、講座で学んだ内容に沿ってモデル作成を完了することができました。

![blender.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3964465/cd39a23d-98ef-e18b-c7a9-4ffb14dffb07.jpeg)


# まとめ

3日間の対面講座は、非常に有意義な学習機会となりました。しかし、講座で学んだ内容を時間を置いて実践してみると、操作手順の詳細な部分が記憶から薄れており、講座テキストの参照が不可欠であることを実感しました。
今後、本講座の応用編や発展的な内容が開催される機会があれば、積極的に参加したいと考えています。
【オススメしない】Microsoft Azureでドメインを買うと、どんな感じ？ (App Service ドメイン)

Azureでは「App Service ドメイン」でドメインを購入できます。

普段からAzureを使っているのであれば、かなり有力な購入方法です。ただ、後悔しているポイント（ドメイン移管について）があるので、それも含めてまとめていきたいと思います。

公式による説明は以下のとおりです。（購入画面より抜粋）

> App Service ドメインを使うと、Azure Portal からカスタム ドメインを購入したり、管理したりできるようになります。
>- ドメイン ライフ サイクル管理の簡素化
>- お使いの Azure サービスのためのドメインの購入および管理 (サポートされる TLD: com、net、co.uk、org、nl、in、biz、org.uk、co.in)
>- サブドメインの管理と割り当て
>- 自動更新機能
>- 'プライバシー保護' が無料で利用可能 (co.in、co.uk など、レジストリがプライバシー機能をサポートしていない TLD は除く)
>- 最初の 5 日間はキャンセル料無料
>- Azure DNS による DNS 管理の簡素化
>- Azure へのドメインの '移行' 機能 (近日公開予定)

## 購入方法は？

リソースの新規作成から「App Service ドメイン」を選んで作成開始です。
入力項目は以下のとおりです。

- 基本情報
    - サブスクリプション / リソースグループ
    - ドメイン名
- 連絡先情報
    - 姓 / 名
    - 電子メール
    - 国コード / 電話番号
    - 住所 / 国 / 州または準州 / 市区町村 / 郵便番号
- 詳細
    - 自動更新を有効にする？（なぜか「無効」が選択不可）
    - プライバシ保護を有効にする？

上記項目を入力したら作成できます。

プライバシ保護については後述したいと思います。

## ドメインの料金は？

すべてのドメインが年間11.99米ドルとなっています。およそ1,800円です。（1$=150円）

Cloudflare Registrarのほうが、1～2ドルほど安いです。

## DNSの料金は？

DNSは、「DNSゾーン」によって運用されます。（購入時に自動的に同じリソースグループ上に作成されます）

運用コストは、毎日2.5円くらい、毎月70～80円ほどかかります。
他のサービスと比べて安いのか高いのかは不明ですが、これくらいの金額であれば気にしなくて良いかなと思います。

## 他のAzure製品との連携は？

かなりラクに連携できます。

例えば、App Service・Functions・API Gatewayなどがインターネット公開で使うサービスとなりますが、いずれも「カスタムドメイン」の設定項目からボタンポチポチでドメインの設定ができます。

この辺の手軽さは、さすがだと思います。

ただし、上記のように連携するのであれば、同一サブスクリプション上にサービスを作っている必要があります。リソースグループは別でも大丈夫だと思います。

## 登録者情報の保護は？

登録者情報（WHOIS情報）はしっかり隠してくれます。

個人情報は[GoDaddyが提供するドメイン連絡先情報](https://jp.godaddy.com/help/what-shows-in-the-godaddy-whois-directory-330)に置き換えられて表示されます。（AzureはGoDaddyから仕入れたドメインを販売している？らしい）

プライバシー保護を解除すると、もう一度有効にすることができないため注意が必要です。また、co.uk、in、org.uk、co.in、nlはプライバシー保護を利用できません。

## ドメインの移管は？

現状できません。これが最大の後悔ポイントです。

というのも、ネームサーバーの値を変更する方法が用意されていないため、外部のDNSサーバーに移管することができません。より厳密には、既存で設定されているAzure DNSのネームサーバー名4つが削除不可能となっているため、移管に必要なステップを実行できない状況になります。

あとから「CloudflareでセキュリティやCDNもやりたい」となっても、やりようが無いことになります。Azureでドメインを買う前によく検討することをおすすめします。

（この辺は、自分の知識不足もあるので、やり方があればぜひ教えていただきたいです）

## まとめ

Azure App Service ドメインは、普通に使うのであれば十分でしょう。管理や設定もラクに行えます。

ただしDNS周りが複雑になっているようで、外部への移管ができないなど使いづらさもあります。個人的には、最初からCloudflareで買っておけばよかったなと思っています。

## 関連資料

作業手順に関しては公式ドキュメントのほうが理解しやすいのでおすすめです。

[App Service ドメインを購入し、それを使用してアプリを構成する｜Microsoft Learn](https://learn.microsoft.com/ja-jp/azure/app-service/manage-custom-dns-buy-domain)
Rubyでdateから日本語の曜日を取得する方法

## はじめに

Rubyでdateオブジェクトから曜日を取得する方法について解説します。
特に、「(月)」のような日本語表記での出力方法に焦点を当てます。

## 基本的な方法

まず、基本的な曜日の取得方法を見てみましょう。

```rb
require 'date'
date = Date.today
day_of_week = date.strftime('%a')
puts day_of_week
```

この方法では英語の曜日略称が出力されます。

## 日本語で(月)のように出力する方法

日本語で「(月)」のように出力するには、以下のようにします:

```rb
require 'date'
date = Date.today
days = %w(日 月 火 水 木 金 土)
day_of_week = "(#{days[date.wday]})"
puts day_of_week
```

## まとめ

strftime('%a')では直接日本語の曜日を取得できませんが、wdayメソッドと曜日の配列を組み合わせることで、望む形式で曜日を出力できます。
この方法を使えば、日本語のアプリケーションでより自然な日付表示が可能になります。
この方法を使用することで、Rubyで日付から日本語の曜日を簡単に取得し、「(月)」のようなフォーマットで出力することができます。
Ubuntuでユーザーを追加したときに、パスワードを聞かれないようにする

毎度、ググっても出てこない小ネタを取り扱っております。
本記事は個人的な見解であり、筆者の所属するいかなる団体にも関係ございません。

# 0. はじめに

Ubuntuサーバーを動かしていて、利用者が自分だけなら何の問題もありませんが、人にもログインしてもらって操作をお願いすることもあるでしょう。

その時に、アカウントを使い回ししてたりしませんか？
アカウントの使い回しは止めましょうね。

ということで、アカウントを追加するのですが、いつもやり方を忘れるので、メモっておきます。


# 1. adduserする

まず、必要なのはUbuntu OSにアカウントを追加します。
useraddを使うな！adduserを使え！というのは以前、書いたとおりです。

https://qiita.com/ynott/items/ae86814232cf85bf9f13

```bash
useradd ユーザー名
```


# 2. SSHの公開鍵を登録する

これも、よく間違えるのですが、`authorized_keys`ファイルのファイル名を間違えます。
そして、パーミッションも間違えます。

1. ファイルは、keysと複数形
1. ファイルパーミッションは、600
1. .sshのディレクトリパーミッションは 700

と覚えておきましょう

```bash
mkdir -p ~ユーザー名/.ssh
chown ユーザー名  ~ユーザー名/.ssh
chmod 700 ~ユーザー名/.ssh
touch ~ユーザー名/.ssh/authorized_keys
chmod 600 ~ユーザー名/.ssh/authorized_keys
```

# 3. sudoers の nopassword ファイルを追加する

以下は場合によりけりですが、NOPASSWDを追加します。

```
sudo touch /etc/sudoers.d/ユーザー名
sudo echo "ユーザー名   ALL=(ALL) NOPASSWD:ALL" > /etc/sudoers.d/ユーザー名
```

# 4. まとめ

意外に間違えるポイントがあるんですよね。
終わりが見えない、グローバル対応に、希望を！

## はじめに
:::note info
本記事は [リンクアンドモチベーション Advent Calendar 2024](https://qiita.com/advent-calendar/2024/lmi) の23日目です🎄
:::
こんにちは、リンクアンドモチベーションでフロントエンドエンジニアをしている坪井です。
この記事では、弊社の「モチベーションクラウド」を多言語対応する際に直面したUI/UX面での課題と、その解決方法を共有します。
（タイムゾーン問題については、[こちら](https://qiita.com/esksak/items/77c3263a7901a9380cb8)の記事をご覧ください🕰️）

## なぜ、グローバル対応は難しいのか
「モチベーションクラウドの画面を多言語化対応してほしい！どの言語を入れても問題ないようにしたい！なるべく早くお願い！」

そんな依頼を受け、2024年4月にプロジェクトがスタートしました。
「翻訳を導入すれば解決するだろう！」と意気込んで対応を進めましたが、実際に画面に反映すると、様々な課題が明らかになり、最初の見積もりでは全ての対応完了までに2年かかることに。

「きええ、全然翻訳入れるだけじゃ終わんないじゃん！」
「このプロダクト、日本語しか受け付けられない仕様になってやがる！」
「一生、終わんねええ！」

#### 理想
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2337762/23d20de1-03b4-eb0e-828b-51b6d2d1378f.png)

#### 現実（※簡易翻訳のサンプルです）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2337762/299b7885-c166-84f1-cf18-08e54ea595ce.png)

まずは、ゴールを「複数言語に対応できるかつ、顧客が母国語で誤解せずに利用できるUI/UX」に定め、以下の課題に絞ってグローバル対応を進めることにしました。

1. テキストのオーバーフロー
2. 改行位置によって読めない言語が出てくる問題
3. 日本語でしか通用しないUX

### 崩れた箇所の共通点と対策
#### ① テキストのオーバーフロー
- 課題: 日本語よりも文字数が多い言語の場合、指定された幅や高さに収まりきらない
- 影響:
    - ボタンやラベルからテキストがはみ出す
    - テキストが折り返されて他のUI要素と重なる
- 対策:
    - 縦並びのアクションボタンにはmin-heightを指定し、高さが柔軟に変わるように
    - 横並びのアクションボタンにはmin-widthを指定し、幅が柔軟に変わるように
    - ユーザーのアクションに関わらない情報は、はみ出る部分を省略表示し、ツールチップで全文を確認できるように
- 背景:
    - ルールを設けることで、全ての言語に対応するデザインを作る必要がなくなり、変更量を最小限に抑えられた
    - ルールを設けて削減できたコスト: 各言語ごとのデザイン調整や個別のレイアウト修正

## ② 改行位置によって読めない言語が出てくる問題
- 課題: タイ語やベトナム語などでは、単語の途中で改行されると意味が変わってしまう
- 影響: 意味不明なテキストのせいで、ユーザーがプロダクトを正しく操作できなくなったり、内容を正確に理解できなくなる
- 対策: スタイルを保持したまま、単語単位で改行されるように修正する
    - `word-break: normal;` と `overflow-wrap: anywhere;` を設定。基本は単語単位で改行し、長すぎる単語の場合は強制的に改行させる
    - 翻訳会社にも改行位置を確認してもらい、意味の通じない箇所を防ぐ

## ③ 日本語でしか通用しないUX
- 課題: 日本語の文法や表現に依存したデザイン
    - 例）※サンプル画像です
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2337762/a1af50fb-ef16-e944-3776-a3c8261335bc.png)

- 影響: 他言語ではUIが正しく理解されず、意図した値を登録できない
- 対策: 世界共通で利用される記法を使用する
    - 例）不等号を使用して文章を式に変換
    - ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2337762/f28b2232-7b9e-2de3-a7b2-a5d3427f0e59.png)

## 学び
完璧なグローバル対応を目指すと膨大な時間がかかります。しかし、ゴールを「顧客が母国語で誤解せずにプロダクトを利用できること」と定め、ルールを設けて対応することで、最小限のコストで迅速に多言語対応の画面をリリースできました。

今後は、プロダクトのフェーズに合わせて、ローカライズによって「各市場への適応」を進めていきたいと思います！
Go 1.23のイテレータを使ってCSVを読んでみる

# はじめに
Go 1.23で追加されたrange over funcと[iter](https://pkg.go.dev/iter)パッケージを使ってみたかったのでイテレータを使ってCSVを読んでみます
導入の経緯としては、いろいろなパッケージのイテレーション処理のインターフェイスが異なっているから統一したいね、ってことだと認識してます
基本的な使い方などは「Go イテレータ」「Go range over func」とかで検索するといくつかの記事で紹介されています

# これまでのCSVの読み方
無限ループの中で一行ずつ取り出し、`io.EOF`がエラーとして返されたら`break`するみたいな流れです
```go:これまでのCSVの読み方
func HandleCSV(r io.Reader) {
    csvReader := csv.NewReader(r)

    for {
        record, err := csvReader.Read()
        if err == io.EOF {
            break
        }
        if err != nil {
            エラーハンドリング()
        }
        何かの処理()
    }
}
```

# イテレータを使ってみる
`*csv.Reader`を受け取って、イテレータを返す関数を作りました
代入文や`io.EOF`のエラーハンドリングが消えてメインの処理への流れが読みやすくなった気がします
```go:イテレータを使ってみる
func HandleCSV(r io.Reader) {
    csvReader := csv.NewReader(r)

    for record, err := range csvRecords(csvReader) {
        if err != nil {
            エラーハンドリング()
        }
        何らかの処理()
    }
}

// 一行ずつのイテレータを返す
func csvRecords(r *csv.Reader) iter.Seq2[[]string, error] {
    return func(yield func([]string, error) bool) {
        for {
            record, err := r.Read()
            if err == io.EOF {
                return
            }
            if !yield(record, err) {
                return
            }
        }
    }
}
```
# おまけ：構造体にマッピングもイテレータで
使用数がおそらく一番多い[gocsv](https://pkg.go.dev/github.com/gocarina/gocsv)ではなく、一行ごとのマッピングが簡単にできる[csvutil](https://pkg.go.dev/github.com/jszwec/csvutil)を使います
構造体の定義は端折りますが、[encoding/json](https://pkg.go.dev/encoding/json)と同じくタグで定義できます
マッピングしたい構造体は`User`ということにします
```go:イテレータを使ってマッピング
func HandleCSV(r io.Reader) {
	csvDecoder, err := csvutil.NewDecoder(csv.NewReader(r))
	if err != nil {
		log.Fatal(err) // ヘッダーすらないとき
	}

	for user, err := range users(csvDecoder) {
		if err != nil {
			エラーハンドリング()
		}
		何らかの処理()
	}
}

// 一行ずつのイテレータを返す
func users(d *csvutil.Decoder) iter.Seq2[User, error] {
	return func(yield func(User, error) bool) {
		for {
			var user User
			err := d.Decode(&user)
			if err == io.EOF {
				return
			}
			if !yield(user, err) {
				return
			}
		}
	}
}
```
じゃんけんゲーム作ってみた。

## 初めに
こんにちは！今回は、Pythonを使ってじゃんけんAPIを作成する手順をご紹介します！

## Python環境のセットアップ
ターミナルを開く

[表示] → [ターミナル] からターミナルを開きます。
仮想環境を作成（推奨）: 以下のコマンドを実行して仮想環境を作成します。
```
python3 -m venv env
```

**仮想環境を有効化**

Windows
```
.\env\Scripts\activate
```
Mac/Linux
```
source env/bin/activate
```
有効化されると、ターミナルの先頭に (env) と表示されます。

## 必要なライブラリをインストール
ターミナルで以下のコードを入力します
```
pip install fastapi uvicorn
```
これで準備はOKです！

## Pythonコードの作成
プロジェクトフォルダ内に``main.py`` という名前のファイルを作成します。
以下のコードを作成します
```main.py
from fastapi import FastAPI
from enum import Enum
import random

app = FastAPI()


class Choice(str, Enum):
    rock = "rock"
    paper = "paper"
    scissors = "scissors"


def determine_winner(user_choice: Choice, computer_choice: Choice) -> str:
    if user_choice == computer_choice:
        return "draw"
    if (user_choice == "rock" and computer_choice == "scissors") or \
       (user_choice == "scissors" and computer_choice == "paper") or \
       (user_choice == "paper" and computer_choice == "rock"):
        return "win"
    return "lose"

@app.post("/play")
def play(user_choice: Choice):

    computer_choice = random.choice(list(Choice))

    result = determine_winner(user_choice, computer_choice)

    return {
        "user_choice": user_choice,
        "computer_choice": computer_choice,
        "result": result
    }

@app.get("/")
def root():
    return {"message": "Welcome to the Janken Game API!"}
```
## サーバーを起動
ターミナルで以下のコマンドを実行して、FastAPIサーバーを起動します。
```
uvicorn main:app --reload
```
成功すると、以下のようなメッセージが表示されます
``
INFO:     Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)
``
## 動作確認
**ブラウザで確認**
``http://127.0.0.1:8000``にアクセスして、ルートメッセージを確認します。
``http://127.0.0.1:8000/docs`` にアクセスして、自動生成されたAPIドキュメントを確認します。
**APIのテスト**
``/play`` エンドポイントをテストし、じゃんけんゲームが動作することを確認します

![スクリーンショット 2024-12-23 15.07.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3957541/a44b0169-92a4-c966-9929-2775e9bf2f9f.png)
## 最後に
作るのはとても簡単なのでぜひみなさんもやってみてください　最後までご覧くださりありがとうございました！！
SQLでカテゴリグループ名、小グループ名を取得する方法


## はじめに
カテゴリIDと、そのカテゴリ内の小グループIDを保持しているデータがあり、カテゴリ名と小グループ名を取得したいとき、クエリの作成に苦戦したので、備忘録として取得した方法を記載します。

## データ構造
メインのデータ（data_list）
|id|categoryGroupNo|subGroupNo|quantity|
|---|---|---|---|
|1	|1	|1	|10|
|2	|1	|2	|5|
|3	|2	|1	|6|
|4	|2	|2	|0|

#### カテゴリグループ（category_group）
|categoryGroupNo|categoryGroupName|
|---|---|
|1	|果物	|
|2	|野菜	|

#### 小グループ

果物グループ（fruit_group）
|subGroupNo|subGroupName|
|---|---|
|1	|りんご	|
|2	|みかん	|

野菜グループ（vegetable_group）
|subGroupNo|subGroupName|
|---|---|
|1	|キャベツ	|
|2	|白菜	|

## クエリの作成
「カテゴリグループ名」と「小グループ名」を取得するSQLクエリを作成します。

```sql
SELECT
    dl.id,
    dl.categoryGroupNo,
    cg.categoryGroupName,
    dl.subGroupNo,
    CASE
        WHEN dl.categoryGroupNo = 1 THEN fg.subGroupName
        WHEN dl.categoryGroupNo = 2 THEN vg.subGroupName
        ELSE NULL
    END AS subGroupName,
    dl.quantity
FROM
    data_list dl
LEFT JOIN
    category_group cg
    ON dl.categoryGroupNo = cg.categoryGroupNo
LEFT JOIN
    fruit_group fg
    ON dl.categoryGroupNo = 1 AND dl.subGroupNo = fg.subGroupNo
LEFT JOIN
    vegetable_group vg
    ON dl.categoryGroupNo = 2 AND dl.subGroupNo = vg.subGroupNo;
```

## クエリの説明
1. データ一覧を基に情報を取得:
    * メインのテーブル data_listを取得
2. カテゴリグループ名を結合:
    * category_group テーブルを category_group_no で結合し、カテゴリグループ名を取得します。
3. 小グループ名を条件で取得:
    * CASE 文を使用し、category_group_no の値に応じて対応するテーブル（fruit_group、vegetable_group）を結合し、小グループ名を取得します。

## 実行結果
実行すると以下のようにグループ名、小グループ名が取得できました。

|id|categoryGroupNo|categoryGroupName|subGroupNo|subGroupName|quantity|
|---|---|---|---|---|---|
|1	|1	|果物|1	|りんご|10|
|2	|1	|果物|2	|みかん|5|
|3	|2	|野菜|1	|キャベツ|6|
|4	|2	|野菜|2	|白菜|0|
typescriptでnext.jsのpage routerのクエリパラメータの値を取得

## 概要
`next.js`の`page router`から取得したクエリパラメータ項目の値（`router.query.hoge`）の型は`string | string[] | undefined`であるが、それを利用する時に`string[]`場合の処理を書かなければならない。ただ、ほとんどの場合は`string[]`がありえないケースに対して、短く書く方法を探求する。

## 詳細

型が同じな項目のパラメータをまとめることで、余計なif分岐が減らせる。
一番多いケース用の共通関数を用意する上で、特別なケースをカーバするような関数も作る。
keys引数の指定で同じ型の項目をまとめて取得できる。

#### 1. 一番多いケースの共通関数を作る

- 配列場合の処理を入れて、一番目の値だけを取得する
- 戻り値の型が`Record<string, string | undefined>`になる

```ts
export const getQueryParams = (router: NextRouter, ...keys: string[]) => {
  const targetKeySet = keys.length ? new Set(keys) : new Set(Object.keys(router.query));
  return Object.entries(router.query).reduce<Record<string, string | undefined>>((acc, [key, value]) => {
    if (targetKeySet.has(key)) {
      if (Array.isArray(value)) {
        acc[key] = value[0];
      } else if (value) {
        acc[key] = value;
      }
    }
    return acc;
  }, {});
};
```

#### 2.項目の値が配列の関数を作る

- 値が1個だけ場合の処理を入れて、それを配列にする
- 戻り値の型が`Record<string, string[] | undefined>`になる

```ts
export const getQueryParamsAsArray = (router: NextRouter, ...keys: string[]) => {
  const targetKeySet = keys.length ? new Set(keys) : new Set(Object.keys(router.query));
  return Object.entries(router.query).reduce<Record<string, string[] | undefined>>((acc, [key, value]) => {
    if (targetKeySet.has(key)) {
      if (Array.isArray(value)) {
        acc[key] = value;
      } else if (value) {
        acc[key] = [value];
      }
    }
    return acc;
  }, {});
};
```

## まとめ

あるクエリパラメータhogeに対して、それの型が`string`と`string[]`のどちらかしかないのは事前にわかっているので、それを取得する共通関数を用いて型の種類がより少なくなり、使い型で不要なif分岐を減らすことでより描きやすくなる。