OmniAICreator/Qiita-1.07M huggingface dataset:
🎨 Gemini × LINE Bot × クラウドスケジューラ：展示情報を毎朝お届けする通知Botを作ってみた話


## ✨ はじめに

「今日はどんな展示がやってるんだろう？」  
「障がい者向けに無料で入れる美術館ってあるのかな？」  

そんな疑問に応えるために、**毎朝 LINE に展示情報を自動で通知する Bot** を作ってみました。  
生成AI（Gemini）と通知の仕組み（LINE API）を組み合わせ、**コードを書くことで文化を届ける**という試みにチャレンジした記録です。

---

## 🧩 構成概要

```
[定時実行型クラウドサービス（例：Google Cloud Scheduler）]
         ↓
[Gemini APIで展示情報を取得・要約]
         ↓
[LINE Messaging APIを使ってグループに通知]
```

- データ収集：Gemini API（Google Generative AI）
- 配信手段：LINE Messaging API
- 自動化：定時実行スケジューラ（Google Cloud Scheduler など）
- フォーマット整形：Pythonスクリプト（Colabで開発）

---

## 📦 機能概要

- 東京の美術館の**障がい者向け割引/無料の特別展示情報**を取得
- Geminiで自然文から必要情報だけを要約
- LINEグループに **絵文字付きで見やすくフォーマットされた通知**をPush送信

通知例：

```
🗓️2025年4月5日時点、東京で開催中の障がい者向け無料鑑賞ができる美術館情報です。

🌟特別展示🌟  
🎨ミロ展：東京都美術館  
🖼️TDC2025：ギンザ・グラフィック・ギャラリー  
🎭青池保子展：弥生美術館  
🏛️皇后のまなざし：明治神宮ミュージアム

✨常設展示✨  
🎨東京国立博物館  
🖼️東京都現代美術館
```

---

## 🔐 アクセストークンとグループIDの取得

- **LINE チャネルアクセストークン**は LINE Developers から発行。
- **グループIDの取得**には、Botをグループに招待しWebhook経由で取得します。

```python
@handler.add(MessageEvent, message=TextMessage)
def handle_message(event):
    print("✅ 受信した groupId:", event.source.group_id)
```

---

## 🧠 Gemini APIプロンプト設計

生成AIは便利ですが、意図通りの出力にするには工夫が必要。  
LINEに最適な形で情報を届けるため、プロンプトは以下のように設計しました。

```text
🗓️{日付}時点、東京で開催中の障がい者向け無料鑑賞ができる美術館情報です。

🎨展示名：館名  
🏛️展示名：館名

特別展示と常設展示に分けて簡潔に、詳細説明やリンクは不要
```

この結果、毎朝LINEで届く情報が **わかりやすく・コンパクト・視覚的**になりました。

---

## 🤖 自動化における工夫

- スケジューラ（例：Google Cloud Scheduler）を使って定期的に実行
- トークンや機密情報はクラウドサービス側のシークレット管理機能で安全に管理
- Pythonでロジックを組み、**生成・整形・送信まで一気通貫で実行**

💡 **本取り組みは、個人開発者が文化情報を自動配信することを目的とした、非営利で軽量な構成として構築しています。**

---

## 💡 学びと気づき

| 学び | ポイント |
|------|----------|
| Geminiは要約と分類が得意 | 絵文字の使い方もプロンプトで調整可能 |
| LINE Botは送信先制限に注意 | グループID取得など事前準備が必要 |
| 自動通知はシンプルに実装可能 | 開発や学習を目的にすれば実現ハードルは低い |
| 小さく作って確実に動かす | 毎日続けるなら軽量・簡素な設計が重要 |

---

## 🚀 おわりに

今回の仕組みは、小さなアイデアから生まれた個人開発の実践例です。  
誰かにとって「今日、行ってみよう」と思えるきっかけになれば本望です。

また、このBotの仕組みは以下のようにも展開可能です：

- 子ども向けのイベント通知  
- 地域ごとのバリアフリー施設情報  
- 季節に応じたおすすめスポット  

生成AIとAPIの力で、社会参加や文化アクセスのハードルを一歩下げられたら──  
そんな思いで開発しました。

---

📬 ご感想・フィードバック・質問など大歓迎です！  
ぜひ、みなさんのアイデアや応用事例も教えてください 🙌
【SwiftUI】initとdeinitについて

## はじめに
Swiftの実装をしていてinitとdeinitについてなんとなくで使っていたのでこの機会に勉強がてらまとめてみました。

### initについて
初期化
オブジェクトが生成された時、最初に実行される処理
プロパティに値を設定したり、オブジェクト生成時に必ず実施する処理を記載する。

``` Swift
class Person {
    var name: String

    init(name: String) {
        self.name = name
        print("\(name) が生成されました")
    }
}

let p = Person(name: "TAKASHI")
// → "TAKASHI が生成されました"
```

### deinitについて
終了処理
インスタンスがメモリから解放される時に呼び出される処理

ファイルを開いたり、変更監視設定を行った場合はメモリが確保された状態になる。
その際に明示的にリソース解放を行わないとメモリ確保された状態が続くためアプリクラッシュやメモリリークにつながる。

例. Realmの変更監視を行う場合
◾️処理概要
Realm（TodoEntity）の変更を監視し、変更されるたびTodoViewに最新情報を表示する。

ここでdeinit内で`notificationToken?.invalidate()`の設定を入れておかないと、、、
TodoViewが破棄された後も変更通知を実施しようとする。
↓
すでに破棄されたTodoViewのメモリ領域にアクセスを試みる。
↓
Viewは破棄済みのためアクセス不可となりアプリクラッシュ or メモリリークが起こる。

``` Swift
// Realmの変更通知設定を行う
class TodoManager: ObservableObject {
    private var notificationToken: NotificationToken?

    init() {
        let realm = try! Realm()
        let todos = realm.objects(TodoEntity.self)

        // Realmに「監視させて！」ってお願いしてる
        notificationToken = todos.observe { changes in
            print("データ変わった！")
        }
    }

    deinit {
        // Viewが破棄されるとき、監視も止める
        notificationToken?.invalidate()
    }
}

// Realmの変更検知し最新のリストを表示するView
struct TodoView: View {
    var category: TodoEntity.Category
    @StateObject private var todoManager: TodoManager
    @State var newTask: String = ""
    
    init(category: TodoEntity.Category) {
        self.category = category
        _todoManager = StateObject(wrappedValue: TodoManager(category: category))
    }
    
    var body: some View {
        VStack {
            List {
                ForEach(0..<todoManager.count, id: \.self) { i in
                    Text(todoManager.todos[i].task!)
                }
            }
        }
    }
}
```
【Rails】カラムのデフォルト値変更時にロールバックが失敗する原因と対策

Railsでは change_column_default を使うことで、カラムのデフォルト値を変更することができます。
ただし、マイグレーションを記述する際に少し注意が必要な点があります。それはロールバック時の挙動です。

今回は、実際の書き方とあわせて、書き方次第では rollback できなくなる可能性があるという点について解説します。

 # 基本の書き方
まず、Railsのマイグレーションでカラムのデフォルト値を変更する基本的な書き方はこちらです。

```ruby
class ChangeDefaultToPublishedOnArticles < ActiveRecord::Migration[7.0]
  def change
    change_column_default :articles, :published, from: false, to: true
  end
end
```
このように `from:` と `to:` を明示的に指定することで、マイグレーションの内容が明確になります。

※ `:articles`：これはテーブル名で、ブログ記事などを管理するためのテーブルを想定しています。

※ `:published`：これはカラム名で、記事が公開されているかどうかを示すブール値（true または false）のカラムを想定しています。

# rollbackができなくなる例
一方、`from:` と `to:` を指定せずに書いてしまうと、以下のようになります。

```ruby
class ChangeDefaultToPublishedOnArticles < ActiveRecord::Migration[7.0]
  def change
    change_column_default :articles, :published, true
  end
end
```
この書き方でも db:migrate は正常に通りますが、db:rollback ができません。

なぜなら、change メソッドは reversible（可逆的）なマイグレーションを自動的に生成しますが、from / to を指定しない場合、元のデフォルト値が何だったかRails側で把握できないため、元に戻す処理が自動生成できないのです。

# まとめ
- change_column_default を使ってカラムのデフォルト値を変更できる

- from: と to: を省略すると rollback できない可能性がある

これらのポイントを意識することで、マイグレーションの可逆性が保たれ、より安全な開発が可能となります。
【概念理解】プレゼンター

# 【概念理解】プレゼンターとは何か？

クリーンアーキテクチャを学ぶ際に、最初に戸惑う概念の一つが「プレゼンター（Presenter）」である。  
この記事では、プレゼンターの役割や必要性について、常体でわかりやすく説明する。


## 🔷 プレゼンターとは何者か？

プレゼンターとは、ユースケース（UseCase）の実行結果を**ユーザーや外部向けに整形して渡す責任を持つ層**である。  
つまり、**ビジネスロジックの結果を「見せる形」に変換する担当**だ。

## 🎯 プレゼンターがいない場合

ユースケースが `domain.Node` のような構造体をそのまま返してしまうと、次のような問題が発生する：

- [ドメインロジックと表示形式が密結合になる](https://qiita.com/koichi2426/items/c2b3461bf4f18848bc60)
- 複数の出力形式（HTML / JSON / CLIなど）に対応しづらくなる
- 出力形式が変わるたびにユースケース側の修正が必要になる


## ✅ プレゼンターの利点

プレゼンターを導入することで、以下のような利点が得られる：

| 項目 | 内容 |
|------|------|
| 分離 | ロジックと表示の責務を明確に分けられる |
| 柔軟性 | 出力形式（JSON、HTMLなど）を柔軟に変更できる |
| テスト性 | 出力部分だけに注目したテストが可能になる |
| 再利用性 | 同じユースケースを、複数の出力方法で再利用できる |


## 🧱 実装イメージ

### プレゼンターのインターフェース（usecase 層）

```go
type CreateNodePresenter interface {
	Output(domain.Node) CreateNodeOutput
}
```

### プレゼンターの実装（adapter 層など）

```go
type jsonCreateNodePresenter struct{}

func (p jsonCreateNodePresenter) Output(n domain.Node) CreateNodeOutput {
	return CreateNodeOutput{
		ID:       string(n.ID()),
		Question: n.Question(),
		Answer:   n.Answer(),
		URLs:     n.URLs(),
		Next:     toStringList(n.Next()),
	}
}
```

### ユースケースでの利用

```go
createdNode, err := repo.Create(ctx, node)
return presenter.Output(createdNode), nil
```


## 🌐 アーキテクチャ内での立ち位置

```txt
[Controller] → [UseCase] → [Presenter] → [View or API]
                         ↑
                    ドメインモデル
```

- UseCase：ビジネスロジックを実行する層
- Presenter：結果を整えて外部に渡す層
- View / API：ユーザーが見る最終出力


## 🧭 まとめ

| 項目 | 内容 |
|------|------|
| プレゼンターの役割 | ユースケースの出力を「見せる形式」に整える |
| メリット | 表示の柔軟性、責務の分離、テスト性の向上など |
| 実装のポイント | ユースケースの出力をラップするだけの軽い責務に留める |


## 💬 補足

「ユースケースは“何をしたか”を返す。プレゼンターは“どう見せるか”を考える」  
この考え方を頭に入れておくと、責務の切り分けがうまくいく。
code コマンドで vs code insiders を起動させる

# VS Code Insidersとは？

Visual Studio Code（VSCode）の開発版。新機能を早く試すことができる。

現在(2025/04/05)のVS Code InsidersはcopilotをMCPとして利用できるcopilot agentを利用できるため、これを使うようにしている。

これを利用しているとき、普段の癖で`code`コマンドでウィンドウを開こうとしてしまった。この際にそのままの設定だともちろん通常のVS Code が起動してしまう。これをaliasコマンドを用いてVS Code Insidersを起動できるようにした。

## 環境

- WSL2(Ubuntu)

## VS Code Insidersの導入

以下からダウンロードしてください。(詳細は本記事では省略します)

https://code.visualstudio.com/insiders/#linux

## 一時的なエイリアス
ubuntuにおいてエイリアスコマンドは以下のように書きます。

```bash
alias code=code-insiders
```

これでcodeと入力した際にcode-insidersが実行されます。

ただし、これだと一時的な設定にしかならず、マシンを落とした時に設定が消えてしまいます。

## 永続的なエイリアス設定

`.bashrc` (bash run commandsの略)というファイルに実行したいコマンドを書き込むことで、bashの起動時に実行されます。

```bash
echo 'alias code=code-insiders' >> ~/.bashrc
source ~/.bashrc
```

なお、bashではなくZsh を利用している場合は以下で`.zshrc`に書き込みます。

```bash
echo 'alias code=code-insiders' >> ~/.zshrc
source ~/.zshrc

```


新しいターミナルを開いたり、現在のシェルを再読み込みしなおして確認します。これで`code` コマンドを実行すると自動的に `code-insiders` が起動するようになりました。
ドメインロジックと表示形式が密結合になることの問題点とは？

ソフトウェア開発において「関心の分離（Separation of Concerns）」は基本原則の一つである。  
しかし、よくあるアンチパターンとして、**ドメインロジックと表示形式が密結合**してしまうケースがある。  
この状態が何を引き起こすのかを具体的に解説する。


## 💡 密結合とは？

ここで言う「密結合」とは、**ある層の変更が別の層にも影響を与える状態**を指す。

例えば、ユースケースが以下のように「JSONの構造そのもの」を返していたとしよう。

```go
func (u UseCase) Execute(...) map[string]interface{} {
	return map[string]interface{}{
		"id": "123",
		"question": "...",
		...
	}
}
```

このようにユースケースが表示形式（ここではJSON）に直接関与してしまうと、変更に弱くなる。


## ⚠ 密結合が引き起こす問題

### ① 表示形式の変更がユースケースに影響する

APIとしてJSONを返す設計にしていたが、ある日「CLI出力も対応してほしい」となったとする。

このとき、ユースケースが JSON 固定で出力していた場合、次のような事態になる：

- JSON部分のロジックを書き換える必要がある  
- 本来変えるべきでないユースケースの中身が変更されてしまう  
- テストの再設計・再実行が必要になる


### ② テストしづらくなる

ユースケースのテストは「ロジックが正しいか」に集中したい。  
しかし表示形式も組み込まれていると、「表示フォーマットも合わせて正しいか」という**複数の観点**を同時に検証することになる。

→ **テストコードが複雑になる**  
→ **バグの原因が特定しにくくなる**


### ③ 再利用しづらくなる

ユースケースが特定の表示形式に依存していると、以下のような再利用の場面で問題が起きる：

- Webアプリ用の表示
- CLIツールでの出力
- バッチ処理での内部利用

出力フォーマットが埋め込まれているため、**ロジックを使い回すためには、余計な表示ロジックも引きずる**ことになる。


## ✅ 解決策：表示はプレゼンターに任せる

ドメインロジックと表示形式は、責任が全く異なる。

| 層 | 役割 |
|----|------|
| ユースケース | 何をするか、どう処理するか |
| プレゼンター | どう見せるか、どんな形式で出力するか |

このように責務を切り分けておくことで：

- 表示の変更がプレゼンター内で完結する  
- ロジックが軽量かつ再利用しやすくなる  
- テストも明確な単位で書けるようになる



## 🧭 まとめ

| 問題点 | 内容 |
|--------|------|
| 表示形式の変更が本来のロジックに波及する |
| テストの粒度が不明確になり、バグの原因が追いにくくなる |
| 再利用が困難になり、機能追加時にコストがかかる |

密結合は一見「楽」に見えるが、後から必ずツケが回ってくる。  
プレゼンターを用いた責務分離を意識することで、柔軟で保守性の高いコード設計が実現できる。
AtCoder Beginner Contest 399 E - Replace

# 導入
atcoderで精進してますが、  
どこかにアウトプットしたかったので、qiitaに書くことにしました。  
ACしてから載せるようにしてますが、考え方が至っていない点があればご指摘頂けると幸いです。  
また、誰かの参考になると嬉しいです。

# 使用しているアルゴリズム
- Union-Find

# 解法のポイント

この問題では、`S` から `T` への変換に必要な「最小の置換回数」を求めます。  
制約の中で重要なのは、置換の過程において循環があった場合に余計な1手が必要になる点です。

解法の流れは以下の通りです：

1. **変換関数 A を構築**  
   - `S[i] -> T[i]` に従って `A[ord(S[i]) - ord('a')] = ord(T[i]) - ord('a')` を構築。
   - 一貫しない変換（すでに登録されてるのに違う変換先が出てくる）は `-1` を出力。

2. **トリビアルな条件処理**  
   - `S == T` の場合は置換不要なので `0`。
   - `A` が `[0, 1, ..., 25]` の場合置換不可能なため `-1`。

3. **置換回数のカウント**  
   - `A[i] != -1` で `i != A[i]` の場合に置換が必要なのでカウント。
   - 同時に UnionFind によって変換グループを構築（無向グラフで連結成分を作る）。
   - ある連結成分において、全ノードが変換先として使われている場合（in_digが全て1）、
     変換のサイクルがあると判断して、追加で1手必要。

4. **UnionFind のグルーピング**  
   - 変換関係が閉じたサイクルになっている場合は、変換に余分な一手が必要（例：a→b→c→a など）。

全体的に、UnionFind で変換の「連結成分」を検出しつつ、変換に必要なステップ数を数えていくアプローチです。

# ACサンプル
```python
class UnionFind:
  def __init__(self,n):
    self.parent = [i for i in range(n)]
  
  def find(self,x):
    if self.parent[x] != x:
      self.parent[x] = self.find(self.parent[x])
    return self.parent[x]
  
  def merge(self,x,y):
    x_root = self.find(x)
    y_root = self.find(y)
    if x_root != y_root:
      self.parent[x_root] = y_root
  
  def groups(self):
    from collections import defaultdict
    r = defaultdict(list)
    for i,p in enumerate(self.parent):
      r[self.find(p)].append(i)
    
    return r.values()

C = 26
N = int(input())
S = input()
T = input()
A = [-1] * C

if S == T:
  print(0)
  exit()

for i in range(N):
  a = ord(S[i]) - ord("a")
  b = ord(T[i]) - ord("a")
  if A[a] != -1 and A[a] != b:
    print(-1)
    exit()
  A[a] = b

if sorted(A) == [i for i in range(26)]:
  print(-1)
  exit()

in_dig = [0] * C
uf = UnionFind(C)
ct = 0

for i,x in enumerate(A):
  if x != -1:
    in_dig[x] = 1
    uf.merge(i,x)
    if x != i:
      ct += 1

for vs in uf.groups():
  if len(vs) > 1:
    if all(in_dig[v] == 1 for v in vs):
      ct += 1

print(ct)
```

# リンク
[ACサンプル集(アルゴリズム逆引き)](https://www.notion.so/motonao/1cc8aca4381b80f39713ddb52fa85e8f?v=1cc8aca4381b80519654000c236bb8d6&pvs=4)

[問題へのリンク](https://atcoder.jp/contests/abc399/tasks/abc399_e)
Record-Triggered Flow Superbadge Unit


https://trailhead.salesforce.com/content/learn/superbadges/superbadge_record_triggered_flows_sbu


# エンゲージメントフローから商談を作成する

エンゲージメント レコードから商談レコードの作成を自動化する新しいフローを作成します。

何もしないで検証ボタンをクリック

:::note alert
We can't find an active flow named 'Opportunity from Engagement'.
:::

Use a Get Records element to query the Donation record type based on its DeveloperName and store all fields.


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/0dab02c0-419a-44a5-bb1b-6028b72f2753.png)


:::note alert
The 'Opportunity from Engagement' flow isn't configured to run only when the expected fields are changed.
:::

:::note alert
The 'Opportunity from Engagement' flow doesn't set the record type of the opportunity dynamically.
:::


# 主要寄付マネージャーの割り当てフローを作成する


商談レコードの所有者の再割り当てを自動化する効率的なフローを作成します。

何もしないで検証ボタンをクリック

:::note alert
We can't find an active flow named 'Assign Major Gift Manager'.
:::

:::note alert
The flow 'Assign Major Gift Manager' isn't configured to run when the expected fields are changed.
:::



# プライバシーデータの削除と寄付金額の変更フローを構成する

自動起動フローを作成して、寄付者のリクエストごとにデータ更新を効率化し、寄付金額の変化率を計算するフローを構成します。

何もしないで検証ボタンをクリック

:::note alert
We can't find an active flow named 'Privacy Data Removal'.
:::

もう１つある？

:::note alert
We can't find the formula resource 'Change_Percentage' in the 'Donation Amount Changes' flow.
:::

同じエラーだなぁ。もしかして有効化してないから？


# The Start element in the 'Assign Major Gift Manager' flow isn't configured with the specified flow type.

+ [Record-Triggered Flow Superbadge Unit - Challenge 2 failing](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000TjMWK0A3)
競プロ典型 90 問 009 - Three Point Angle（★6）



# 導入
atcoderで精進してますが、  
どこかにアウトプットしたかったので、qiitaに書くことにしました。  
ACしてから載せるようにしてますが、考え方が至っていない点があればご指摘頂けると幸いです。  
また、誰かの参考になると嬉しいです。

# 使用しているアルゴリズム
- 全探索
- 偏角ソート（atan2 → degree → %360）
- 二分探索（`bisect_left`）
- 幾何（最大角度差の探索）

# 解法のポイント
この問題では、任意の2点を結んだ直線のなす角度のうち、最大の角度差を求めるという、**幾何的性質を用いた探索問題**です。

方針としては、「ある点を中心にして、他の点との偏角（角度）を求め、それらの中から最も鋭角でないもの＝最も広い角度」を探します。

1. 各点を中心 `(x, y)` として全探索します。
2. 他の全ての点との偏角（ベクトル `(x-dx, y-dy)` の角度）を `atan2` を使って求め、度数に直してリスト `tmp` に格納します。
3. `tmp` をソートして、各角度に対して180°反対側に最も近い角度を探します。  
   - ここで二分探索 `bisect_left` を使うことで効率よく反対側の角度候補を得られます。
4. 180°との差が小さい方が最大角度になる可能性があるので、`(tmp[i] - tmp[idx])%360` と `(tmp[i] - tmp[idx-1])%360` の2通りを比較して最大値を更新していきます。
5. 全体の最大値が答えになります。

最終的に `r` に最も大きな角度（180度以下の最大角度）が格納され、それを出力します。

# ACサンプル
```python
import bisect
import math

def to180(degree):
  degree %= 360
  return degree if degree <= 180 else 360 - degree

N = int(input())
nodes = [(x,y) for x,y in (map(int,input().split()) for _ in range(N))]
r = 0

for x,y in nodes:
  tmp = []
  for dx, dy in nodes:
    if (x,y) == (dx, dy):
      continue
    
    tmp.append(math.degrees(math.atan2(y - dy, x - dx)) % 360)
  
  tmp.sort()
  for i in range(N - 1):
    target = (tmp[i] + 180) % 360
    idx = bisect.bisect_left(tmp, target)
    a = to180(tmp[i] - tmp[idx % (N - 1)])
    b = to180(tmp[i] - tmp[idx - 1])
    r = max(r,a,b)

print(r)
```

# リンク
[ACサンプル集(アルゴリズム逆引き)](https://www.notion.so/motonao/1cc8aca4381b80f39713ddb52fa85e8f?v=1cc8aca4381b80519654000c236bb8d6&pvs=4)

[問題へのリンク](https://atcoder.jp/contests/typical90/tasks/typical90_i)
CLIPを意味で操作する：Monosemantic Sparse AutoencoderによるVLMの解釈と制御

# CLIPを意味で操作する：Monosemantic Sparse AutoencoderによるVLMの解釈と制御
今回は、視覚と言語を統合するAIモデルにおいて、解釈可能性と制御可能性を同時に実現する最先端研究、「Sparse Autoencoders Learn Monosemantic Features in Vision-Language Models（2025）」をご紹介します。

この論文は、Vision-Language Model（CLIPなど）の内部表現を **スパースな意味単位（monosemantic features）へ変換し、さらにはLarge Language Model（LLaVAなど）の出力に影響を与える概念操作を可能にする**、マルチモーダルAIにおける“意味”のエンジニアリングという新たなフロンティアを切り開いた重要研究です。

---

## 論文情報

- **タイトル**: Sparse Autoencoders Learn Monosemantic Features in Vision-Language Models  
- **リンク**: [https://arxiv.org/abs/2504.02821](https://arxiv.org/abs/2504.02821)  
- **発表日**: 2025年4月3日  
- **著者**: Mateusz Pach, Shyamgopal Karthik, Quentin Bouniot, Serge Belongie, Zeynep Akata  

---

## 1. 研究の位置づけと社会的背景

### 1.1 なぜ“monosemantic”が重要か？

- 現代のAIは、文や画像の意味を高次元ベクトルとして扱いますが、その**構成要素が人間にとって解釈不可能**であることが大半です。
- この“ブラックボックス性”は、AIの出力に信頼・制御・責任を求める産業や政策にとって致命的です。
- 本研究は、「**1つのニューロン＝1つの意味概念**」という構造（monosemanticity）を人工的に構築し、AIを**“意味的に分解可能な装置”**へと変貌させる道を示しました。

---

## 2. 技術の中核：Sparse Autoencoderによる意味単位の構築

### 2.1 通常のCLIPの問題点

- 埋め込み空間が**多義的（polysemantic）**であり、例えば「猫」と「椅子」と「絵画」の特徴が混在することもある。

### 2.2 Sparse Autoencoder（SAE）の設計思想

| コンポーネント | 機能 |
|----------------|------|
| エンコーダ | CLIP出力を高次元スパース辞書へ射影 |
| Top-Kフィルタ | **最も反応の強いK個だけを活性化**＝スパース性誘導 |
| デコーダ | 元のベクトルへ再構成（意味損失の検出も可能） |

→ 結果として、「赤い鳥に反応するニューロン」「青い海に反応するニューロン」など、**意味単位のスロットが自動的に形成**

---

## 3. モデルによって明らかになった“意味の地図”

### 3.1 Monosemanticity Score（MS）の意義

- **ニューロンが反応する画像ペア間のコサイン類似度**に基づいて計測
- 高MSスコア：**一貫した意味を持つニューロン**
- 低MSスコア：**複数概念にまたがる曖昧なニューロン**

---

## 4. LLMへの概念ステアリング：AI制御技術の未来形

### 実験：CLIP + SAE + LLaVA構成
白紙画像＋Neuron#39（鉛筆）を強制活性化 → 詩生成

| α（操作強度） | 出力の変化（抜粋） |
|---------------|--------------------|
| 0 | Roses are red, love is in the air... |
| 25 | I hold a pencil, lost in thought... |
| 50 | My pencil writes what words cannot say... |

→ **画像情報がゼロでも、概念を注入できる**＝制御可能性の実証

---

## 5. 社会実装と制度設計の視点

### 5.1 国家レベルのAI政策と整合

| 視点 | 適合性 |
|------|--------|
| AIガバナンス | 出力の理由を人間が検証可能に（説明責任） |
| 産業応用 | 医療・広告・教育などでの“意図された意味制御”が可能 |
| 倫理 | ステルスバイアス注入のリスクを監視・抑制できる構造が構築可能 |

---

## 6. 他技術・学問領域との接続

### 6.1 Multimodal Agentとの統合

- AutoGPTやMulti-Agent Systemsにおいて、**視覚的意図のコントロール（Visual Prompt Steering）**が可能に

### 6.2 Neurosymbolic AIとの架橋

- monosemanticニューロンを「記号単位」として扱えば、**ベクトル表現とシンボリック操作の融合**が現実的に

### 6.3 認知科学・哲学的含意

- この研究は、「**理解可能性の物理実装**」を目指した試みとも捉えられる
- ウィトゲンシュタインの“意味は使用である”という命題が、ニューロン活性の形で表現される構造

---

## 7. 最終的な問い：意味を持つAIは可能か？

- 本研究は、「意味を埋め込む」だけでなく、「意味単位でAIを操作する」道を開きました。
- これは単なる機械学習の話ではなく、**人間とAIの意味的共通基盤をいかに作るか**という、次世代AI設計哲学の問いへつながっています。

---

## 本研究が残したもの

| 視点 | インパクト |
|------|------------|
| 学術 | マルチモーダル解釈性における初の定量・実装成果 |
| 応用 | 教育・医療・創作・倫理AIに向けた基盤技術 |
| 理論 | 意味論とニューラル表現の橋渡し |
| 社会 | “透明で制御可能なAI”という次世代要件への一歩 |

---

この記事が、単なる論文紹介を超え、「意味とAI」「人と機械の理解の橋渡し」に興味を持つすべての読者にとって、新しい視座を提供することを願っています。
【勉強記録】英語の技術書を読むメリット

# はじめに

こんにちは。アメリカに住みながら独学でエンジニアを目指しているTairaです。
本日より、英語で『リーダブルコード』を読み始めました。今までのほとんどは日本語で勉強するようにしていたのですが、英語で学習することによるメリットはかなりあるなと感じました。
Qiitaを見ているような方は、やる気のあるエンジニアばかりだと思うので、ぜひ参考にしてもらえれば幸いです。

## 英語で読み始める前に
最近は英語で情報収集や勉強をするようになりましたが、体感としては最低限大学受験レベルの単語力がないと、理解できない可能性があります。
例えば、英単語ターゲット1900の最後の400単語以外をしっかり理解できていれば、『リーダブルコード』の話に限れば、1ページに1・2単語程度の知らない単語が出てくる程度なので、すらすら読めると思います。

## 1. 英語を学べる
これは自明すぎますね。次に行きましょう。

## 2. 技術的な英語を学べる
これは、英語を学ぶことで、将来的に海外でソフトウェアエンジニアになるぞ、と考えている方向けです。
どの企業に入るにも、コーディング面接というものがあり、アルゴリズムの問題を英語で説明しながら解いたり、DB設計について聞かれたりなど、技術的な英語を知っておく必要があります。
そういった意味で、日ごろから技術書や情報を英語で取得することで、そういった単語(ex: 二分探索はbinary search)を事前に知ることができ、面接の一助となる可能性があります。

## 3. 一次情報を得ることができる
Railsチュートリアルでよく紹介されるサイトやReactチュートリアルなどは英語で書いてあります。
もちろん和訳しても理解できますが、翻訳サイトと自分が訳した内容はややニュアンスが異なることがあるので、英語で読めるのが良いと思います。

## 4. 脑のトレーニングになる
これはChatGPTから得た情報です。
本日英語版の『リーダブルコード』を読んだのですが、頭の疲労がすごく、痛くなるくらいだったのでその胸について相談してみました。
どうやら、脑の使っている領域が複合的に働いているからだそうです。

| 領域           | 主な役割             | 関連                               |
| -------------- | -------------------- | ---------------------------------- |
| 前頂前野       | 思考、論理、問題解決 | 新しい技術の理解・設計思考など     |
| 側頂葉         | 言語理解             | 英語の読解（意味理解）             |
| 前頂葉         | 言語生成             | 頭の中で英語を組み立てる／発言準備 |
| 海馬           | 記憶の定着           | 新しい概念や単語を覚える           |
| 帯状回や扁案体 | 感情・モチベーション | ストレスややる気の変動もここに影響 |

つまり、英語・プログラミングを同時にやると、

「言語脑」・「論理脑」・「記憶」・「感情」 すべてがフル動いしている状態です。
この組み合わせ、まるで「筋トレ・ランニング・読書」を同時にやってるようなものなので、普段よりもずっと疲れるのは自然なことのようです。

ソフトウェアエンジニアは運動習慣が少なく、脑に対していい影響を与えることが少ないと思います。
そういった意味で、脑の筋トレをするいい機会だと思います。


## まとめ
英語で技術書を読むことには、単に「英語力を伸ばす」だけでなく、「技術的な英語に慣れる」「一次情報に触れられる」「脳の多方面を刺激できる」といった多くのメリットがあります。

最初は難しく感じるかもしれませんが、読み進めるうちに徐々に慣れていきますし、慣れた先には大きな成長が待っていると思います。私自身もまだ始めたばかりですが、この挑戦が自分のエンジニアとしての成長につながると信じて、これからも続けていこうと思います。
フランス語の特殊文字が入ったファイルの編集

特殊文字が入ったファイルをviで開くとこんなエラーが出て編集できません

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/9bc87fbc-c3fe-4448-a40a-9490e6e3fba9.png)

以下を設定すると編集できるようになりました。

```
set fileencoding=ISO-8859-1
```

これでこつこつ特殊文字を置換します。

もっと効率の良い方法ないかな。。。
1Passwordのssh鍵をローカルマシンでのssh実行時のみに制限する

sshの鍵をVaultに保管する機能が1Passwordにあります。下記の設定を```~/.ssh/config```に加えるよう[ドキュメント](https://developer.1password.com/docs/ssh/get-started/)に記載されています。

```
Host *
  IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock"
```

Macのローカルマシン上でsshを使う限りは問題ないのですが、例えばiPadからMacにsshでリモートログインしたあと、```git push```コマンドを実行するなどMacから別のサーバに接続すると失敗します。

```mermaid
graph LR
    A("クライアント(iPad)") --> |ssh| B("中継ホスト(Mac)")
    B --> |git push| C("サーバ(github)")
```

これは1Passwordが認証を実行しようと、ローカルデスクトップ上で1Password GUIを起動するからです。リモートログインしているため当然デスクトップは見えず、ユーザーの反応がないのでタイムアウトしてエラーになります。

iPad側のSSHクライアントアプリ(例えば、[Shelly](https://apps.apple.com/jp/app/shelly-ssh-client/id989642999))にSSHエージェントの転送機能がある場合、Mac側のssh鍵を使わずに、iPad側の鍵情報を利用することでこの問題を回避できます。具体的には、サンプルの```~/.ssh/config```を下記のように書き直します。

```
Match host * exec "[[ ! -n $SSH_CLIENT ]]"
  IdentityAgent "~/Library/Group Containers/2BUA8C4S2C.com.1password/t/agent.sock"
```

Matchを使って分岐ロジックを書くのがポイントです。環境変数```SSH_CLIENT```の有無で現在リモートからログインしている状態がどうかを判定し、```IdentityAgent```オプションを有効にしています。

#### FAQ

Q. ```~/.zshrc```にfunctionとして分岐ロジックを記述する方法は駄目なの？
A. 駄目です。中継ホスト上のシェルからさらに```ssh```する場合は動作しますが、```git push```の場合にはzsh経由でsshが実行されないので無視されてしまいます。sshクライアントの設定レベルで分岐を記述する必要があります。
新人向けITエンジニア面談🔰ノーコードで実現するAIを活用したスキルシート修正と面談トークスクリプト作成

![mendan01b.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4006596/401cc1f0-abab-4b00-8ddd-a34b2bb03f46.png)



IT業界でのキャリアアップを目指す際、スキルシートの内容を磨いたり面談でのトークスクリプトを用意したりすることは非常に重要です。
プログラミング不要でチャット機能や各種オンラインツールを活用し、ノーコードでこれらの資料を作成・改善する方法について解説します。

## 1. スキルシートの書き方修正

### 1.1 スキルシートの役割
- **自己アピール:** 
自分の技術や経験を明確に伝え、第一印象を良くするための重要な資料です。  
- **キャリアの棚卸し:** 
これまでのスキルや実績を整理し、今後のキャリアプランの基盤となります。

### 1.2 AI活用のメリット
- **表現のブラッシュアップ:** 
AIは文章のニュアンスや表現を洗練させ、よりプロフェッショナルな内容に改善してくれます。  
- **効率的な編集:** 
複数の修正案や表現方法をすぐに提示してくれるため、修正作業を大幅に短縮できます。  
- **一貫性の向上:** 
一定のフォーマットやテンプレートに沿った内容を維持でき、全体の統一感が得られます。

### 1.3 ノーコードでのAI利用方法
- **チャットボットの活用:**  
  ChatGPTなどのチャットツールを利用して、既存のスキルシートを入力し「もっとプロフェッショナルな表現にしてほしい」などと指示するだけで、即座に改善案が得られます。

- **オンラインプラットフォーム:**  
  一部のWebサービスでは、文章の修正や要約、表現の改善をノーコードで行える機能が提供されています。
  これらのツールに原稿をコピー＆ペーストするだけで、AIが自動的に編集を行ってくれます。

## 2. 面談時のトークスクリプト作成

### 2.1 トークスクリプトの重要性
- **事前準備:** 
面談前にトークスクリプトを用意することで、緊張を和らげスムーズなコミュニケーションが可能になります。  
- **多様なシナリオ:** 
面談中に起こりうるさまざまな質問や状況に備え、複数のパターンの回答例を事前に準備できます。

### 2.2 AI活用のメリット
- **シナリオ生成:** 
AIは自己紹介、これまでの経験、将来のビジョンなど、面談に必要なスクリプトを多様なシナリオで生成してくれます。  
- **客観的な改善:** 
出力された文章を元に、自分の強みや改善点を客観的に見直すことが可能です。

### 2.3 ノーコードでのAI利用方法
- **チャット機能の利用:**  
  ChatGPTのような対話型AIに「IT企業の面談用自己紹介トークスクリプトを作成して」と依頼するだけで、自然な会話形式のスクリプトが提案されます。  
- **インタラクティブな編集:**  
  出力結果に対して「もう少しカジュアルな表現にしてほしい」や「専門用語を増やしてほしい」といった追加の指示をチャット上で繰り返すことで、最適なスクリプトに仕上げることができます。

## 3. AI活用時のポイントと注意事項

- **具体的な指示を与える:**  
  修正や生成したい文章の内容を具体的に伝えることで、AIはより適切なアウトプットを提供します。  
- **出力内容の確認と調整:**  
  AIが生成した文章はあくまで提案です。自分の言葉や経験に合わせて、内容を精査・修正することが大切です。  
- **プライバシーの管理:**  
  個人情報や機密情報を扱う場合、使用するツールのセキュリティポリシーを確認し、安全に利用しましょう。

## 4. AI活用例

- **実際に生成したサンプル:**  
  ChatGPTでファイルをアップロードし、「自己紹介文を作成してください」とプロンプトで指示を出します。  

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4006596/7fe22768-4db2-432a-942c-f64e4eff714b.png)

　書き出された内容を自分の言葉として表現できるように編集します。

```
はじめまして、〇〇と申します。現在25歳で、エンジニアとしてはまだ経験が浅いのですが、
日々勉強しながら業務に取り組んでいます。

これまでMicrosoft 365やSaaSのアカウント管理、簡単な運用作業などに携わってきました。
最近はAWSやAzureなどのクラウドについても興味があり、業務で触れたり、個人で学習を
進めています。

G検定（AIジェネラリスト）を取得しており、将来的にはAIやDX関連の分野でも知識を活かして
いきたいと考えています。まだまだ知識・経験ともに足りない部分も多いですが、吸収力と
フットワークの軽さを強みに、一つずつできることを増やしていきたいと思っています。

本日はよろしくお願いいたします。
```
注意点としては、AIっぽさが表面化しないよう、ご自身が知らない単語や使ったことのない情報は削って、質問されても答えられるように準備しておく必要があります。

## まとめ

ノーコードで利用できるチャット機能や各種オンラインツールを活用することで、スキルシートの修正や面談用トークスクリプトの作成が驚くほど効率化されます。
簡単な入力と指示だけで、プロフェッショナルな文章の改善や多様なシナリオの生成が可能となるため、面接準備やキャリアアップに大いに役立つでしょう。
ぜひ、これらのノーコードツールを活用して、あなたの魅力を最大限に引き出す資料作成に挑戦してみてください。

---

**参考リンク**  
- [ChatGPT公式サイト](https://openai.com/chatgpt)  
- [Qiita](https://qiita.com/)
個人的備忘録：サーバーエラーが出たときに読みたい！5xxコードの原因とチェックリストをまとめてみた

## はじめに
WebアプリケーションやAPIを扱っていると、クライアント側のエラー（4xx）に加えて、サーバー側のエラー（5xx）もよく遭遇します。

:::note info
**個人の備忘録程度の走り書きとなっておりますが、温かい目で見守っていただければ幸いです。**
:::

この記事では、サーバーエラーに特化して、その意味や原因、対処方法を整理しておきます。

## サーバーエラー（5xx）とは？
HTTPステータスコードの「5xx」番台は、リクエストは正しいが、サーバー内部で問題が発生した場合に返されます。原因はサーバー側にあるため、クライアント側では直接解決できないことが多いです。

**参考文献**

https://baremetal.jp/blog/2022/06/06/1210/

## 代表的な5xxステータスコード一覧

### 500 Internal Server Error
- サーバー内部で予期しないエラーが発生
- 最も一般的な5xxエラー
- 原因例：アプリケーションのバグ、スクリプトの例外、権限不足

### 501 Not Implemented
- サーバーがリクエストされたメソッドをサポートしていない
- 例：PUTやPATCHがサポートされていないWebサーバーに対するリクエスト

### 502 Bad Gateway
- ゲートウェイやプロキシサーバーが無効なレスポンスを受け取った場合に発生
- 例：リバースプロキシ（例：NGINX）がバックエンドから不正な応答を受信

### 503 Service Unavailable
- サーバーが一時的に過負荷、またはメンテナンス中で利用不可
- 再試行すれば回復する可能性がある

### 504 Gateway Timeout
- ゲートウェイやプロキシサーバーが応答を待ってタイムアウト
- 例：API GatewayがLambda関数のタイムアウトを迎えた場合

### 505 HTTP Version Not Supported
- サーバーがリクエストで使用されたHTTPバージョンをサポートしていない

## よくある原因と対策
| ステータスコード | 原因例 | 対処方法 |
|------------------|---------------------------|-------------------------|
| 500 | アプリの例外発生、設定ミス | ログ確認、コード修正 |
| 502 | バックエンドがダウン | バックエンドの状態確認 |
| 503 | 過負荷、メンテナンス中 | リソース増強、Auto Scaling |
| 504 | 外部APIの応答遅延 | タイムアウト設定見直し |

## まとめ
5xxエラーはサーバー側の問題であり、発生時にはログ解析やインフラ構成の見直しが必要です。

特に、500番系エラーは開発中・本番環境問わず頻出するため、原因特定のためにエラーログを活用し、根本原因を突き止める力を身につけておくことが重要です！
GoのMapで未割り当てのキーの値はゼロ値

ドキュメントに書かれているけど、今まで知らなかった...という話です。

## Mapから値を取り出す前に`v, ok := m[key]; ok`でチェックする

書いてるとおりですが、いつも、次のようなコードを書いていました。

```go
type Hoge {
  HasX bool
}
```

という型があったとして、m `map[string]Hoge`に値が割り当てられており、かつ、`hasX`が`true`かどうかをチェックする関数があったとすると、以下のように書いていました。

```go
func checkHasX(m map[string]Hoge, k string) bool {
	if v, ok := m[k]; ok {
		return false
	}
	return v.HasX
}
```

別にこのように書いても間違いではないとは思いますが、[Goのドキュメント](https://go.dev/ref/spec#Index_expressions
)には以下のように書いています。

> For a of map type M:
>   x's type must be assignable to the key type of M
>  if the map contains an entry with key x, a[x] is the map element with key x and the type of a[x] is the element type of M
>  if the map is nil or does not contain such an entry, a[x] is the zero value for the element type of M

最後の文ですね。`if the map is nil or does not contain such an entry, a[x] is the zero value for the element type of M`とあるので、Mのゼロ値となるとあります。

## Mapがnilまたは、キーに値が未割り当てだった場合はゼロ値が返る

つまり、以下のように書いても同じということです。

```go
func checkKey(m map[string]Hoge, k string) bool {
	return m[k].hasX
}
```

ですので、以下のようなコードも、コメントに書いている通りの結果になります。

```go
	m := map[string]bool{}
	fmt.Printf("%v", m["hoge"]) // false
```

```go
	m := map[string]string{}
	fmt.Printf("%v", m["hoge"]) // 空文字
```

```go
	var m map[string]int // Map自体がnilの場合でも同じ
	fmt.Printf("%v", m["hoge"]) // 0
```

なお、当然ですが、ゼロ値が帰ってくるからと言って、アサインされているわけではないです。

```go
	var m map[string]int
	fmt.Printf("%v", m["hoge"]) // 0
    _, ok := m["hoge"]
	fmt.Printf("%v", ok) // false
```

## つまり、`v, ok := m[key]; ok` は場合によっては不要

もちろん、書いているコードによっては、未割り当てかどうかを確認する必要がある場合もあるとは思いますが、値が入ってなくても、入っていても、ゼロ値がfalse扱いというようなケースに関しては、特に割当てられているかをチェックする必要はないです。

## 終わり

ドキュメントに書いてあるのに、今まで気づいてなかったという話でした。ちなみに、Effective Goにもちゃんと[書いてあります](https://go.dev/doc/effective_go#maps)。

>An attempt to fetch a map value with a key that is not present in the map will return the zero value for the type of the entries in the map. For instance, if the map contains integers, looking up a non-existent key will return 0. A set can be implemented as a map with value type bool. Set the map entry to true to put the value in the set, and then test it by simple indexing.

むしろ、存在チェックがその後に書かれているくらいなので、至って常識的な話だったのかもしれません。

ゼロ値がfalseって、なんとなくPerlっぽいなぁ、と思ったりしました。

存在しないキーにアクセスしただけでエラーになる言語もあるので、意外に思ったのですが、こうしとかないと、ちゃんとチェックしていないコードの場合、実行時にpanicしまくるからかなぁ？などと思ったりしました。

というわけｄドキュメントはちゃんと読みましょう、と改めて思いました。反省。
ケーキで例えるIaaS・PaaS・SaaS

**むずい単語をわかりやすく言語化しようの回！**

**IaaS・PaaS・SaaSをケーキで例えると**

|分類|名前|意味|ケーキ:cake:で例えると…|
|:---:|:---|:---|:---|
|IaaS|Infrastructure as a Service|インフラ（サーバーとか）を借りる|材料とキッチンだけ借りて、自分でケーキを作る|
|PaaS|Platform as a Service|開発用の環境を借りる|ケーキの土台（スポンジ）だけ用意してくれて、デコレーションは自分でやる|
|SaaS|Software as a Service|ソフトそのものを使う|できあがったケーキを食べるだけ


**ケーキ以外で簡単な言語化をすると・・・**
# IaaS(Infrastructure as a Service)
**サーバーやネットワークなどの「インフラだけをクラウドで提供」**
* 自分でOSやアプリをインストールする必要がある
* 自分でOSやアプリをインストールする必要がある
* 例：AWS EC2, Microsoft Azure VM, Google Compute Engine

例えば、OSやアプリケーションは自分で準備したものを使いたいけれど、機材の保有・管理はしたくないという場合にIaaSは最適

# PaaS(Platform as a Service)
**アプリ開発に必要な「〇〇土台（プラットフォーム）を提供」**
* サーバーやOSはもう用意されてる
* 自分はアプリだけ作ればいい！
* 例：Heroku, Google App Engine, AWS Elastic Beanstalk

アプリケーションを開発したり稼働させたりするためのプラットフォーム機能を、ネットワーク経由で利用することが可能

# SaaS(Software as a Service)
**完成済みの「ソフトウェアをそのまま使う」サービス**
* インストール不要で、すぐ使える
* 保守もアップデートも提供側が全部やってくれる
* 例：Gmail, Microsoft 365, Google Drive, Zoom, Salesforce

メールや文書作成などソフトウェア・アプリケーションの機能を利用できる

# まとめ
|項目|自由度|管理の手間|使いやすさ|
|:---:|:---|:---|:---|
|IaaS|高い|多い|△|
|PaaS|中|少なめ|〇|
|SaaS|低い(決まってる)|ほぼゼロ|◎|
#0093（2025/04/05） 単一責任の原則 (Single Responsibility Principle: SRP)

# 単一責任の原則 (Single Responsibility Principle: SRP) 

単一責任の原則（Single Responsibility Principle: SRP）とは、ロバート・C・マーチンによって提唱されたSOLID原則の一つであり、「あるクラスはたった一つの変更理由のみを持つべき」という原則です。本記事では、SRPを実践する上級プログラマー向けに、PythonとTypeScriptによる具体例を用いてSRPが守られているケースとそうでないケースを解説します。

## 単一責任の原則（SRP）の本質

単一責任の原則を正しく理解するには、「責任」を「変更理由」と考えると良いでしょう。一つのクラスに複数の変更理由が存在すると、変更が加えられるたびに他の無関係な機能に予期せぬ影響を与えるリスクが増大します。

---

## Pythonによる例

### ❌ SRPが守られていない例

```python
class UserManager:
    def create_user(self, user_data):
        self.save_to_database(user_data)

    def send_welcome_email(self, email):
        print(f"Sending welcome email to {email}")

    def save_to_database(self, data):
        print(f"Saving {data} to database")
```

上記のクラスは、ユーザーの作成、データベースへの保存、メール送信と、複数の責任を持っているため変更が多方面に影響します。

### ✅ SRPが守られている例

```python
class UserRepository:
    def save(self, user_data):
        print(f"Saving {user_data} to database")

class EmailService:
    def send_welcome_email(self, email):
        print(f"Sending welcome email to {email}")

class UserManager:
    def __init__(self, repository: UserRepository, email_service: EmailService):
        self.repository = repository
        self.email_service = email_service

    def register_user(self, user_data):
        self.repository.save(user_data)
        self.email_service.send_welcome_email(user_data['email'])
```

この例では、データベース操作、メール送信、それぞれの責務が明確にクラスごとに分離されています。

---

## TypeScriptによる例

### ❌ SRPが守られていない例

```typescript
class OrderProcessor {
    processOrder(order: any) {
        this.validateOrder(order);
        this.saveOrder(order);
        this.notifyCustomer(order.customerEmail);
    }

    validateOrder(order: any) {
        console.log('Validating order');
    }

    saveOrder(order: any) {
        console.log('Saving order to DB');
    }

    notifyCustomer(email: string) {
        console.log(`Sending notification to ${email}`);
    }
}
```

この例では、注文の検証、データベース保存、顧客通知が1つのクラス内に含まれており、変更に弱い構造です。

### ✅ SRPが守られている例

```typescript
class OrderValidator {
    validate(order: any) {
        console.log('Validating order');
    }
}

class OrderRepository {
    save(order: any) {
        console.log('Saving order to DB');
    }
}

class NotificationService {
    notify(email: string) {
        console.log(`Sending notification to ${email}`);
    }
}

class OrderProcessor {
    constructor(
        private validator: OrderValidator,
        private repository: OrderRepository,
        private notifier: NotificationService
    ) {}

    process(order: any) {
        this.validator.validate(order);
        this.repository.save(order);
        this.notifier.notify(order.customerEmail);
    }
}
```

それぞれの責務を別クラスに分割し、変更があった際の影響範囲を限定しています。

---

## まとめ

単一責任の原則は「変更理由を1つに絞ること」であり、変更の影響範囲を制御し、テストや保守のしやすさを高めます。上級プログラマーとしては、SRPを厳格に守るだけでなく、実際のプロジェクトの状況に応じて柔軟に適用する力も求められます。
YouTube Data API V3を使って 登録チャンネルを抽出する。

# やりたいこと

YouTubeを視聴していたら、把握しきれないほどに登録チャンネル数が増えてしまっていて、管理出来なくなってしまいました。
なので、まずはその第一歩として、最近活動していないチャンネルを登録チャンネルから抽出し、しばらく活動していないチャンネルを登録削除させたい。

## 今回の目標
今回はYouTube Data API V3を使えばプログラマブルにYouTubeのデータにアクセス出来るらしいので、YouTube Data API V3の使い方を学びつつ、登録チャンネルを抽出するところまでやっていきます。

## 開発＆実行環境
OS:Windows 11 の WSL2
言語:Python3

## 事前準備
### 1.Google Cloud Consoleにアクセスし、YouTubeにアクセスするための情報を取得する。

こちらの記事を参考にさせて頂きました。
https://qiita.com/nbayashi/items/bde26cd04f08de21d552
https://qiita.com/mnoguchi/items/7d7795444afb9d9dafa8

最終的には、
![スクリーンショット 2025-04-05 095208.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624329/2d127f99-1a0a-4ef6-87f2-272392b5b459.png)
このJSONをダウンロードする。

以下の項目は以下を設定する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624329/fa590bb5-7997-4b5f-b5d3-709c66fe0dee.png)

対象→テストユーザーにYouTubeで使用しているアカウントを設定する。
![スクリーンショット 2025-04-13 085118.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624329/2d902f7e-3f25-4ac0-9dd3-36864b96ef5a.png)

### 2.Pythonコード
```python
from google.oauth2.credentials import Credentials
from google_auth_oauthlib.flow import InstalledAppFlow
from apiclient.discovery import build
from apiclient.errors import HttpError
import pandas as pd

# API情報
YOUTUBE_API_SERVICE_NAME = 'youtube'
YOUTUBE_API_VERSION = 'v3'
SCOPES = ['https://www.googleapis.com/auth/youtube.readonly']

def get_authenticated_service():
  flow = InstalledAppFlow.from_client_secrets_file(
          'client_secrets.json', SCOPES)
  creds = flow.run_local_server(port=8080, open_browser=True)
  return build(
    YOUTUBE_API_SERVICE_NAME,
    YOUTUBE_API_VERSION,
    credentials=creds
    )

def get_subscribed_channels(youtube):
  channels = []
  request = youtube.subscriptions().list(
    part='snippet',
    mine='true',
    maxResults=50,
  )

  while request:
    response = request.execute()
    for item in response['items']:
      title = item['snippet']['title']
      channel_id = item['snippet']['resourceId']['channelId']
      print(f'{title}（Channel ID: {channel_id}）')
      channels.append((title, channel_id))
    request = youtube.subscriptions().list_next(request, response)
  return channels

youtube = get_authenticated_service()
subscribed_channels = get_subscribed_channels(youtube)
print(f'\n📺 登録チャンネル数：{len(subscribed_channels)}')
```
ダウンロードしたJSONはclient_secrets.jsonに名前を変更し、同じフォルダに設置する。

実行すると、コンソールにURIが出力されるので、コンソールのリンクからGoogleログイン画面にアクセスし、YouTubeアカウントでログインする。

## 実行結果
登録チャンネル名とChannel ID一覧が出力される。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/624329/546df82a-0e09-4c9d-b4a2-f3b88295d092.png)

## プログラムでやっていること解説

* Client_secrets.jsonの情報からGoogleアカウントのログイン画面に遷移し、ログインしたユーザーのアクセストークンを取得
* YouTubeにアクセスするためのクライアントを作成
* subscriptions().list()で登録したチャンネルのリストを取得するリクエストを作成
* リクエストを実行し、結果を取得
* リクエスト実行結果は一回につき最大50件まで取得できるので、list_next()で取得できていない残りを繰り返し取得する

ブログやってます。
https://taki-lab.site/bocci/
StringBuilderで文字列連結しよう

## はじめに
文字列を連結しているときに+演算子を使って文字列連結しているが、必ずしも最適な方法ではない。
大量の文字列操作や繰り返し処理内で行う文字列連結では、StringBuilderクラスを使うことで大幅にパフォーマンスを向上させることができる

## 文字列連結の問題点

```csharp:sample.cs
string result = "Hello";
result += " World";
```
このコードではメモリは
①Helloのメモリ領域
②文字列連結の Wroldのメモリ領域
③結果の文字列として"Hello World"
上記3つのメモリ領域が必要になる

最終的には①と②については、ガベージコレクション対象となる

操作が少ない場合は問題ありませんが、ループ内で頻繁に行われると次のような問題が発生
```csharp:sample.cs
string result = "";
for (int i = 0; i < 10000; i++)
{
    result += i.ToString();  // 毎回新しい文字列が作成される
}
```
このコードでは、ループの各反復処理で新しい文字列が作成され、前の文字列はガベージコレクションの対象となる
そのため以下のような問題が起きる
①メモリ使用量の増加
②ガベージコレクションの負荷が増加
③アプリケーションのパフォーマンス低下

## 解決策
StringuBuilderk流明日には、内部的に可変なバッファを持ち、効率的に文字列操作を行う

```csharp:sample.cs
using System.Text;

StringBuilder sb = new StringBuilder();
sb.Append("Hello");
sb.Append(" World");
string result = sb.ToString();  // 最後に一度だけ文字列を生成
```
①可変バッファ: 内部的に可変長の文字配列を持ち、必要に応じて自動的にサイズを拡張
②効率的なメモリ管理: 連結操作ごとに新しいメモリを割り当てる必要なし
③最終変換: 全ての操作が完了した後、ToString() メソッドを呼び出すことで、最終的な文字列を一度だけ生成

## まとめ
単純な文字列連結では+演算子で問題ないが、ループ内や多数の文字列連結する場合はStringBuilderを使う方が効果的
Windows11のパスキーで不要なデバイスを削除する

Windows 11でPasskeysを使うと、古いスマホ（機種変更後など）がデバイス選択画面に残ってしまうことがあります。これを手動で削除する手順です。

# 前提
- Windows11前提です。
- Windows10ば未対応です。
  - レジストリエディタでデバイス名を検索することで該当するレジストリを見つけることができるかもしれません。

# 手順
1. Passkeysを利用する時のデバイスを表示するダイアログは下記イメージの通りです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1117709/070e8b65-f93c-4b42-9b54-0d922ed671fc.png)

2. `regeidt`を実行し、次のレジストリに移動します。
  ```
  HKEY_USERS\S-1-5-20\Software\Microsoft\Cryptography\FIDO\{Account SID}\LinkedDevices\{Device ID}
  ```
3. デバイス名を確認して、該当するレジストリを削除します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1117709/dba8238f-d017-44a4-9ba7-46f9cb0b6589.png)

4. Passkeysのデバイス選択ダイアログを表示すると、削除されています。（再起動などは不要です）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1117709/085cdabc-123a-40cd-bef4-e5a1dbb0ba96.png)


# 参考
https://c7solutions.com/2024/05/deleting-a-rogue-passkey-device
PostfixとMailCatcher

**Postfixをインストールして、メールの配送先をMailCatcherサーバーに設定していくよ！**
# 1.Postfixとは
Postfixは「メールサーバー（MTA：Mail Transfer Agent）」と呼ばれるソフトで、
メールの「送信」を担当するシステムのひとつ。

Postfixは、Linuxサーバーでよく使われてて、たとえば以下のようなことができる
* サーバーからの通知メールを送る（例：Zabbixやcronの通知）
* フォームの送信内容をメールで届ける
* MailCatcherやGmailなど、別のSMTPサーバーにメールをリレー（中継）する

# 2.MailCatcherとは
ざっくりいうと **「実際には送らないテスト用のメール受信箱」**
MailCatcherは、開発者やサーバー管理者がメールのテストをしたいときに使う便利ツールで
* 実際のGmailや会社のメールには送信しない
* 代わりに、ローカル環境（自分のPCや開発サーバー）で受け取る
* Web画面でそのメールの内容がすぐに見られる

使用例
* サーバーから「アラート通知メール」がちゃんと送れるか試したい
* フォーム送信時に「確認メール」が正しく作られるか見たい
* 本番環境に送って事故らないように、テスト環境で安全に確認したい

# 3.さっそく作業
①まずは必要なコマンドのインストール（mailコマンド）
```
　sudo dnf install net-snmp-utils
 ```

②Postfixのインストール
```
　sudo dnf install postfix
 ```

 ③Postfixの設定ファイル編集
 　Postfixの設定ファイルを編集して、メールをMailCatcherサーバーにリレーするよう設定
  ```
　sudo vi /etc/postfix/main.cf
  ```
  　以下の設定を追加または変更
   ```
　relayhost = [192.168.xxx.xxxx]:25

  #Postfixがすべてのネットワークインターフェースでリッスンするように設定
　inet_interfaces = all
  #Postfixが自分自身のメールだけを処理するようにする
  mydestination = localhost
 ```
 ④Postfixの再起動
 ```
 sudo systemctl restart postfix
 ```
 ⑤ファイアーウォールの設定
 　もしファイアーウォールが有効になっている場合、PostfixがMailCatcherにアクセスできるようにポートを開放する必要がある。
  以下のコマンドでSMTPポート25を開放
  ```
 sudo firewall-cmd --add-port=25/tcp --permanent
 sudo firewall-cmd --reload
 ```

 ⑥動作確認
 　Postfixが正しくMailCatcherにメールを送信しているか確認するためにメールを送信
  ```
  echo "Test email" | mail -s "Test Postfix to MailCatcher" user@example.com
  ```
  MailCatcherのUIで、上記によって送信されたメールを確認（Webブラウザでhttp://192.168.xxx.xxxにアクセス）
個人的備忘録：「200 OK」だけじゃない！使い分けたいHTTPステータスコードをまとめてみた

## はじめに
Webアプリケーションを開発したりAPIを扱う中で、HTTPステータスコードは「**サーバーからの返事**」を理解するために不可欠です。

:::note info
**個人の備忘録程度の走り書きとなっておりますが、温かい目で見守っていただければ幸いです。**
:::

今回は、自分の開発や学習中によく出会うステータスコードについて、簡潔にまとめておきます。

## HTTPステータスコードとは？
HTTPステータスコードは、クライアントのリクエストに対してサーバーが返す3桁の番号です。
- **1xx（情報）**：処理中
- **2xx（成功）**：成功（OK、作成されたなど）
- **3xx（リダイレクト）**：別のURLに移動
- **4xx（クライアントエラー）**：リクエストが不正
- **5xx（サーバーエラー）**：サーバー側の問題

**参考文献**

https://developer.mozilla.org/ja/docs/Web/HTTP/Reference/Status

## よく使うステータスコード一覧
### 成功系（2xx）
- `200 OK`：正常に処理された
- `201 Created`：リソースの作成成功（例：POST）
- `204 No Content`：成功したが返すデータがない

### リダイレクト系（3xx）
- `301 Moved Permanently`：恒久的に移動
- `302 Found`：一時的に移動
- `304 Not Modified`：キャッシュが有効、再取得不要

### クライアントエラー系（4xx）
- `400 Bad Request`：リクエストが不正（構文ミスなど）
- `401 Unauthorized`：認証が必要
- `403 Forbidden`：アクセス権なし
- `404 Not Found`：リソースが見つからない

### サーバーエラー系（5xx）
- `500 Internal Server Error`：サーバー内部の一般的なエラー
- `502 Bad Gateway`：ゲートウェイ・プロキシのエラー
- `503 Service Unavailable`：一時的にサービス利用不可

## 活用シーンと注意点
- API開発では、ステータスコードで処理結果を伝えることが非常に重要
- `200` だけで済ませず、意図に応じたコードを返すようにすると保守性が高まる
- ログ分析でもステータスコードを見れば、ユーザーの失敗やサーバーの異常が分かる

## まとめ
HTTPステータスコードは、Web開発の基本ながら非常に重要な知識です。

:::note warn
***自分のアプリで「何を返すか」を明確にすることが、ユーザーにとっても開発者にとっても優しい設計になります。***
:::

今後は、カスタムエラーハンドリングやAPIのベストプラクティスにも踏み込んでいきたいです！
個人開発　Habitica-外部連携　基本設計


# 基本設計を書く目的
```
システムの目的を満たすための動作や振る舞い、構造を決定記述することです。
基本設計では実装方法についてはあまり触れません。
プログラム（実現手段ではない）については配慮せずデータの入出力/格納を記述する。
外部から見える「動作、振る舞い」を記述する。
ブラックボックス的とも、ソフトウェア、フレームワークに依存しない。

```

# インプットとなる資料
概念設計

https://qiita.com/RYA234/items/675442e8a2acc17c0f3e


# 成果物


## 外部連携一覧
#|連携元システム名|連携先システム名|連携対象データ
-|-------------|--------------|------------
1|タスク管理ツール|BIツール|タスクトランザクション

#|接続方式|連携タイミング|連携データ想定量|備考
-|-------|------------|--------------|----
1|Webサービス|日時処理|50件



## 入出力
### API側

#### Habitica API  リクエスト

URI:https://habitica.com/api/v3/tasks/user


#### リクエストパラメーター

パラメーター名|種類|備考
------------|----|---
x-api-user|ヘッダーパラメーター|Habiticaの設定→サイトのデータ→ユーザーで値を確認
x-api-key|ヘッダーパラメーター|Habiticaの設定→サイトのデータ→ユーザーで値を確認
x-client|ヘッダーパラメーター|x-api-user + "-Testing"

#### Habitica API 　レスポンス

項目名|型|例
------|--|--
_id|String |'f8d9468a-991c-42fe-bd9b-5ddc9ec1eaaa',
completed|bool|false,
collapseChecklist|bool|false,
checklist|Array| [ [Object], [Object] ],
type|String|'todo',
text|String|'Habiticaの基本設計',
notes|String|'',
tags: |Array<String>|[ 'bd52d6af-661b-478f-9dc0-be48f2d20aaa' ],
value|Number| -1,
priority|Number| 1.5,
attribute|String|'str',
challenge|any| {},
group|any| { completedBy: {}, assignedUsers: [] },
reminders|Array<String>|{"time":"2017-01-13T16:21:00.074Z",</br>"startDate":"2017-01-13T16:20:00.074Z"</br>,
"id":"b8b549c4-8d56-4e49-9b38-b4dcde976aaa"}
byHabitica|bool| false,
createdAt|String|'2025-03-27T11:47:52.832Z'
updatedAt|String|'2025-03-28T12:55:17.307Z'
userId|String|'1045522e-dbb4-4541-9cc7-1b65fb5acaaa'
id|String|'f8d9468a-991c-42fe-bd9b-5ddc9ec1eaaa'


### DB

####  テーブル定義書　出力
A5SQLから拝借
IDとバッチ処理実行日時を主キーにしています。
正規化とか全く考慮してません！

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/34f1a8d6-5bf9-47c8-b9eb-c38dbe590b65.png)








# 参考


## APIパラメータ種類
https://apidog.com/jp/blog/introduction-to-api-parameters/

## Habitica API
https://habitica.com/apidoc/#api-Task-GetUserTasks


## 外部連携設計
書籍：システム設計の謎を解く p163-164

## DB設計
書籍:書籍：システム設計の謎を解く p156


## 個人開発目次
https://qiita.com/RYA234/items/c7918ebadb035c12aca2
EC2で1つのinstance idを指定してインスタンスを取得する

EC2で1つのinstance idを指定してインスタンスを取得する方法。

`describe instances`で複数のインスタンスを取得しそこから絞り込む方法がよく解説されているが、RubyまたはPythonであれば1つのinstance idから1つのインスタンスを取得するAPIが用意されている。


## Rubyの場合

```ruby
require 'aws-sdk-ec2'

ec2 = Aws::EC2::Resource.new(region: 'ap-northeast-1')
instance = ec2.instance(id)
name = instance.tags.find { |t| t.key == 'Name' }&.value

puts "id=#{id} name=#{name}"
```

## Pythonの場合

```python
import boto3

ec2 = boto3.resource('ec2', region_name='ap-northeast-1')
instance = ec2.Instance(id)
tag = next((t for t in instance.tags if t['Key'] == 'Name'), None)
name = tag['Value'] if tag is not None else None

print(f'id={id} name={name}')
```

## JavaScriptの場合

JavaScriptの場合は、RubyやPythonとは異なり1つだけインスタンスを取得するAPIが無い。

```javascript
import {EC2Client, DescribeInstancesCommand} from "@aws-sdk/client-ec2";

const client = new EC2Client({region: "ap-northeast-1"});
const command = new DescribeInstancesCommand({InstanceIds: [id]});
const response = await client.send(command);
const instance = response["Reservations"][0]["Instances"][0];
const name = instance["Tags"].find(t => t["Key"] === "Name")?.["Value"];

console.log(`id=${id} name=${name}`);
```
関数電卓で動く公開鍵暗号を作る：パート１

# 耐量子計算機暗号HQCを参考に多項式暗号を作ってみた
HQCでは複数のベクトルを使って公開鍵を構成する。
これと同じようにして、公開鍵暗号を作ってみる。
大きな素体上の多項式$f_0,f_1$を秘密とし、$h$をランダム多項式とする。

## 鍵生成：
多項式$f_0,f_1$を大きな素体上のそれぞれn次多項式、n/2次の多項式であり、それぞれ$n,n/2$個の1次多項式の積に分解するものとし、これを秘密鍵とする。
またhを次数n/2次のランダム多項式として公開鍵にする。
ここで公開鍵は$pk=h,s=f_0+hf_1$とし、秘密鍵は$sk=(f_0,f_1)$である。

## 暗号化；
ランダム多項式を$r=(r_1,r_2)$とし、$deg(m)=n$であるとする。ここで、
$$u=r_1+hr_2, v=sr_2+m=f_0r_2+hf_1r_2+m $$とすると暗号文$c$は$c=(u,v)$となる。

## 復号：
復号は以下のようにする。
$$f_m(\alpha_i)=v-uf_1(\alpha_i)$$
$$=f_0r_2+hf_1r_2+m-(r_1+hr_2)f_1$$
$$=f_0r_2-r_1f_1+m$$
$$u=r_1+hr_2, v=sr_2+m=f_0r_2+hf_1r_2+m $$とすると暗号文$c$は$c=(u,v)$となる。

ここで、$deg(m+r_1f_1) = deg(f_0),　deg(f_1)=deg(m)$であるとする。
$$m+r_1f_1(\alpha)-r_0f_0(\alpha)=m+r_1f_1+0=y$$（$\alpha$は多項式$f_0$の解集合）
なら以下のようにして$f_1+m$が計算できる。（普通の多項式の剰余でいい）

更に多項式$y=m+r_{1}f_{1}$から$f_1$を使って平文多項式$m$を計算できる。

高次の多項式を消すことで、秘密の多項式を知っている受信者だけが暗号文多項式から平文多項式を復元できる。例えば、鍵となる秘密多項式で暗号多項式の剰余を取ることで簡単に復号できる。
これは秘密多項式のyを知っているものだけが、条件
$$deg(r_0f_0)=deg(r_1f_1),deg(r_0)=deg(r_1),deg(f_0)=deg(f_1)=deg(m)+1$$を満たすときに受信多項式の次数を減らすことができるからである。

後日、続きを書きます。

# 攻撃法：
## 鍵に対する攻撃

## 暗号文に対する攻撃
個人的備忘録：IPアドレスとMACアドレスをつなぐ「ARP（Address Resolution Protocol）」の基本を整理してみた

## はじめに
ネットワークを学ぶ上で避けて通れないのが「ARP（Address Resolution Protocol）」です。EC2やクラウド環境でも、内部でIPアドレスとMACアドレスを解決するこの仕組みは密かに動いています。

:::note info
**個人の備忘録程度の走り書きとなっておりますが、温かい目で見守っていただければ幸いです。**
:::

ここでは、ARPの基本動作を自分の言葉で整理します。

## ARPとは？
ARP（Address Resolution Protocol）は、IPアドレスから対応するMACアドレスを取得するためのプロトコルです。
- 層：OSI参照モデルの第3層（ネットワーク層）と第2層（データリンク層）を橋渡しする役割
- 主にLAN内の通信に使われる

**参考文献**

https://business.ntt-east.co.jp/bizdrive/word/arp.html#:~:text=ARP%EF%BC%88Address%20Resolution%20Protocol%EF%BC%89%E3%81%AF,%E3%80%8C%E3%82%A2%E3%83%BC%E3%83%97%E3%80%8D%E3%81%A8%E5%91%BC%E3%81%B0%E3%82%8C%E3%82%8B%E3%80%82&text=%E3%82%A4%E3%83%BC%E3%82%B5%E3%83%8D%E3%83%83%E3%83%88%E4%B8%8A%E3%81%AE%E6%A9%9F%E5%99%A8%E9%96%93,%E7%89%A9%E7%90%86%E3%82%A2%E3%83%89%E3%83%AC%E3%82%B9%E3%81%8C%E4%BD%BF%E3%82%8F%E3%82%8C%E3%82%8B%E3%80%82

## 仕組みの流れ
### 1. ARPリクエスト
- 自分が持っているのは「通信相手のIPアドレス」のみ
- そのIPに対応するMACアドレスが不明なので、LAN内にブロードキャスト

例：
```
Who has 192.168.0.10? Tell 192.168.0.1
```

### 2. ARPリプライ
- 該当するIPアドレスを持っている機器が、MACアドレスを返答する

例：
```
192.168.0.10 is at aa:bb:cc:dd:ee:ff
```

### 3. ARPキャッシュ
- 一度解決したIPとMACの対応は一時的にキャッシュされる（高速化のため）

コマンド例（Linux）：
```
$ arp -a
```

## トラブルシューティングでの活用
- ネットワーク疎通ができないとき、ARPテーブルにエントリがあるか確認することで原因の切り分けが可能
- IP競合などもARPで見抜ける場合がある

## まとめ
ARPは、普段は意識しなくてもネットワーク内部で常に動いている大事なプロトコルです。

:::note warn
***クラウド環境でも抽象化の下で活躍しているので、基本を押さえておくとトラブルシュートの際に役立ちます。***
:::

今後は、ARP spoofing 対策などセキュリティ面にも触れていきたいと思います！
JavaとSpring BootでAPIを構築してみた【REST APIs】

## 1.APIの概要

#### Create
PUT /books/<isbn>

#### Read
GET /books/<isbn>
GET /books

#### Update
PUT /books/<isbn>

#### Delete
DELET /books/<isbn>

## 2.spring.initializr でプロジェクトを作成する
(1) ブラウザを開き、(https://start.spring.io/) を入力し、開く
(2) 下記を入力し、Dependenciesで下記を選択し、『GENERATE』ボタンを押す

Project: Maven
Language: Java
SpringBoot: 3.4.4

Group: com.devtiro.books
Artifact: books
Name : books
Description: REST API for Books
Package name:com.devtiro.books

Packaging: Jar
Java: 17

Dependencies

Spring Web
Lombok
Spring Data JPA
H2 Database

## 3.Domain Modelling

(1)domain/BookEntitiy.javaを作成する

```main/java/com/devtiro/books/domain/BookEntitiy.java

package com.devtiro.books.domain;

import jakarta.persistence.Entity;
import jakarta.persistence.Table;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@Entity
@Table(name = "books")
public class BookEntity {

    @Id
    private String isbn;
    private String author;
    private String title;
}
```

(2)repositories/BookRepository.javaを作成する

```main/java/com/devtiro/books/repositories/BookRepository.java
package com.devtiro.books.repositories;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.devtiro.books.domain.BookEntity;

@Repository
public interface BookRepository extends JpaRepository<BookEntity, String> {
}
```

(3)services/BookServices.javaを作成する

```main/java/com/devtiro/books/services/BookServices.java

package com.devtiro.books.services;

public interface BookServices {

}
```

(4)services/BookServicesImpl.javaを作成する

```main/java/com/devtiro/books/impl/BookServiceImpl.java
package com.devtiro.books.impl;

import org.springframework.stereotype.Service;

import com.devtiro.books.services.BookServices;

@Service
public class BookServiceImpl implements BookServices {

}
```
(5)domain/Book.javaを作成する

```main/java/com/devtiro/books/domain/Book.java

```




## 参考サイト
[The Web is Built on REST APIs: Let’s Build One with Java & Spring Boot](https://www.youtube.com/watch?v=HmRVrAT4uA0&t=4693s)
windows11で、sketchup6 その49

# 概要
windows11に、sketchup6を入れてみた。
rubyで、3Dを書く。
練習問題やってみた。

# 練習問題

sketchupで、メビウスの輪を描け。

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/753c7cd6-134c-4c79-8295-01689cad2334.png)

# サンプルコード

```

def wa
	ent = Sketchup.active_model.entities
	n = 30
	wx = 10
	wy = 50
	wz = 200
	r_mobius = 300
	twist_region = 20
	pi = Math::PI
	(-n..n).each { |i|
		group = ent.add_group
		face = group.entities.add_face [0, 0, 0], [0, wy, 0], [wx, wy, 0], [wx, 0, 0]
		face.pushpull -wz
		twist_angle = pi / (twist_region * 2) * i - pi / 2
		transY = [0, r_mobius, 0]
		phi = i * pi / n
		trs1 = Geom::Transformation.rotation [0, wy / 2, wz / 2], [1, 0, 0], twist_angle
		trs2 = Geom::Transformation.translation transY
		trs3 = Geom::Transformation.rotation [0, 0, 0], [0, 0, 1], phi
		if (i).abs <= twist_region then
			group.transform! trs1
		end
		group.transform! trs2
		group.transform! trs3
	}
end



```

以上。