OmniAICreator/Qiita-1.07M huggingface dataset:
PHP number_formatの挙動について

# PHPのnumber_formatを用いた計算にて
皆様、こんにちは。

先日、初歩的なのに気付けなかったミスがあり、自戒の念を込めて記事にさせて頂きます。

「こんなの当たり前！」って人もいるかと思いますが、記憶の定着にはアウトプットが良いということもあるので、しばしお付き合いくださいませ。

## 前提
- PHPバージョン
  - PHP7.1

## 何が起こった？
表題、PHPの`number_format()`を用いた計算でうっかりがありました。

現プロジェクトで料金をフロントで表示する時に`,`区切りを付ける必要がありました。

int型の数値をそのまま表示ではマズイ訳です。
```php
$a = 2000;
$b = 100;
echo number_format($a - $b); //1,900(string)
```
このような感じですね。

さて上記の既存式に追加修正で`割引額を追加してくれ`となりました。

本来なら…
``` php
# 省略
$discount = 200;
echo number_format($a - $b - $discount); //1,700(string)
```
とすべきですね。

ところが半年前(!?)の私は…
``` php
# 省略
echo number_format($a - $b) - $discount; //?(?)
```
としていたようです。

## どうなってしまったのか？
すでに上記結果で書いているのですが、[number_format()](https://www.php.net/manual/ja/function.number-format.php)の戻り値は`string`です。

ということは
``` php
# これは…
number_format(1900) - 200;
#こうなります
'1,900' - 200;
```
上記のような感じですね…よろしくないのが一目瞭然です…
### 結果
「でもPHPには _暗黙的型変換_ があるんじゃ？」となりそうです。

これがうまく機能してしまえば(そもそも良くないのですが…)、`1900 - 200`となるはず…

ところがどっこい！結果は…`-199(int)`となります！

どうやら`string`から`int`への変換時に`,`を変換できずに`1`となってしまうようです。

暗黙的型変換時での`,`の扱いについては出典が見つけられず…公式ドキュメントのここだよというのを知っている方がいたら教えて頂きたいです…

ちなみに[intval()](https://www.php.net/manual/ja/function.intval.php)でも同様の挙動なので、`,`が含まれている文字列数値はそこまでの数値になってしまうようですね。

# 所感
元々を考えれば`)`の位置を間違えなければ良かったように思えますがそこは人間、間違いもやっぱりあるはずです。

テストコードがあれば防げるかと思いきや、`number_format()`の引数が1000未満のテストケースだと引っ掛からないという…

ただ、`カンマを付けた表示にしたい`という仕様であるなら、`カンマを付けた`結果がどうなるかに考慮できればテストケースに落とし込めたようにも思えます。

やっぱりテストケース考慮は大事！という安直ではありますが、確実に必要な考えを学べただけでも良かったかなと思います。

# おまけ
こういうイージーミスをコンパイル時に弾いてくれる`静的言語`はやっぱり魅力ですね…(PHPも良い言語ですけどね！)
自作UIライブラリを作ったときに、CSSのPathで苦労した話

## 概要
- タイトル通り

## 内容
https://qiita.com/keitaMax/items/8657d469e4d2dc094d75

https://zenn.dev/tatsssssssssuya/articles/2d539b00715d61

だったりを参考に作っていたところ、何をやってもCSSのインポートエラーが発生し続けた。

実際のPathには存在するのに、感知できないようだった。

いろいろと調べたが、直すのはシンプルでインポートのパスが違うということだった。


https://stackoverflow.com/questions/76406764/shareable-vite-vue-component-why-am-i-getting-error-about-missing-css-export

答えは↑にありました。

ずっと、`import '@tetex-public/ui/dist/style.css'`としていたが、`import '@tetex-public/ui/style.css'`が正解だった。

直感的ではないので、嫌いです、、、
とはいえ/distがあるのも冗長であんまり好きではないので、見つけた設定場所を共有しておきます。

```json
{
  "name": "@tetex-public/ui",
  "version": "0.0.26",
  "author": "koinunopochi",
  "license": "MIT",
  "description": "",
  "private": false,
  "type": "module",
  "main": "./dist/index.umd.js",
  "module": "./dist/index.es.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.es.js",
      "require": "./dist/index.umd.js",
      "types": "./dist/index.d.ts"
    },
    "./dist/style.css": "./dist/style.css" <==== ここ `左が名前の解決で、右が実際の場所`
                                                 左を./style.cssにしていたのが今回の原因である。
  },
```

モノレポだからかなあとかいろいろと考えたのが馬鹿らしいくらい簡単でした、、、
KVMPods: 専用KVM仮想マシンでDocker Composeアプリを実行

# Docker Composeアプリを専用VMで実行する新しい方法

従来のコンテナプラットフォームでは、リソースの共有や機能の制限など、様々な課題がありました。KVMPodsは、これらの問題を解決する新しいアプローチを提供します。

## 従来の課題

- 他のユーザーとリソースを共有
- カーネル機能の制限
- パフォーマンスの不安定さ
- Docker Compose機能の制限

## KVMPodsの解決策

KVMPodsでは、専用のKVM仮想マシンを提供し、その中で複数のDocker Composeアプリを実行できます。

### 主な特徴

- 専用KVM仮想マシン
- 複数のDocker Composeアプリを実行可能
- NVMeストレージ
- x86とARMのサポート
- Docker Compose機能の完全サポート

## 使用例

以下のような`docker-compose.yml`をそのまま実行できます：

```yaml
services:
  app:
    image: wordpress:latest
    ports:
      - "8080:80"
    volumes:
      - wp_data:/var/www/html
  db:
    image: mariadb:latest
    volumes:
      - db_data:/var/lib/mysql

volumes:
  wp_data:
  db_data:
```

## 使い方

1. [KVMPods](https://kvmpods.com)でアカウントを作成
2. KVM仮想マシンを設定
3. Docker Composeファイルを貼り付け
4. デプロイ

CLIツールや複雑な設定は不要です。シンプルなWebインターフェースで全ての操作が完結します。

## 料金

専用KVM仮想マシンが月額€10から利用可能です。

## まとめ

KVMPodsは、Docker Composeアプリケーションの実行環境として、以下の利点を提供します：

- 他ユーザーとのリソース共有なし
- Docker Compose機能の完全サポート
- シンプルな操作性
- 安定したパフォーマンス

実際のプロダクション環境での運用に最適な選択肢となるでしょう。

---
ご質問やフィードバックがありましたら、コメントをお願いします！
実行したGitHub Actionsのワークフローログを取得したい

## はじめに
GitHub Actionsのワークフローが実行されると自動でGitHub内にログが保存されます。ただ、保持できる期間が決まっているためログを別のストレージに保存するための設定をします。

なお今回はストレージにS3を採用しています。

## 検証
全体像はこちらです。
![githubactions-log.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/606568/619a2c74-4072-9ca4-377c-4b847a56ddea.png)


まずはログを取得する対象のワークフローを作成します。何でもよいので以下のようにしました。

```
name: Target Log Workflow

on:
  workflow_dispatch:

jobs:
  first:
    runs-on: ubuntu-latest
    permissions:
      actions: write
    steps:
      - run: echo "test workflow successfully finished!"
```

次に本題のログを取得するためのワークフローを作成します。
以下に注意して実装します。
* ワークフローが完了すると`workflow_run`が発行される
* ワークフローのログは決まったURLから取得できる
  * https://api.github.com/repos/<リポジトリ名>/actions/runs/<ワークフローID>/logs
* <ワークフローID>はGitHub Actionsの変数から取得できる
  * ${{ github.event.workflow_run.id }}

最終的に実装したワークフローは以下のようになっています。
```
name: Upload Logs

on:
  workflow_dispatch:
  workflow_run:
    workflows: 
      - "Target Log Workflow"
    types: 
      - "completed"

jobs:
  second:
    runs-on: ubuntu-latest
    permissions:
      actions: read
    steps:

      # S3にアップロードするために対象のロールを設定（事前にassume roleの設定が必要)
      # Access keyの設定でも可能
      - name: Setup AWS Credentials
        uses: aws-actions/configure-aws-credentials@v4
        with:
          role-to-assume: ${{ vars.AWS_ROLE_ARN }}
          aws-region: ${{ vars.TARGET_REGION }}

      # ワークフローのログを$log_file_nameで保存
      - name: Download the Workflow Log
        run: |
          workflow_id=${{ github.event.workflow_run.id }}
          log_prefix=$(date +"%Y%m%d%H%M%S")
          log_file_name=${log_prefix}_logs.zip

          # Download logs using GitHub API
          curl -H "Authorization: token $GITHUB_TOKEN" \
               -L \
               "https://api.github.com/repos/${{ github.repository }}/actions/runs/$workflow_id/logs" \
               --output $log_file_name
          
          # pass the log file name to the next step
          echo "LOG_FILE_NAME=$log_file_name" >> $GITHUB_ENV

      # 取得したログをS3にアップロード
      - name: Upload the Log to S3
        run: |
          echo "Uploading $LOG_FILE_NAME to S3 bucket: ${{ vars.LOG_TARGET_S3 }}"
          aws s3 cp $LOG_FILE_NAME "s3://${{ vars.LOG_TARGET_S3 }}/github_actions/$LOG_FILE_NAME"
```

それぞれを実装することで対象のワークフローのログを取得できるようになっているはずです!

## おわりに
今回の記事では1つのワークフローに対するログ取得を実装しました。その場合、対象を複数にする場合には `workflows` で指定するワークフローを変数化してあげるとよさそうです。ちなみにログの取得できる単位はWorkflowごとになっており、同じワークフロー内からログを取得しようとするとファイルの中身がおかしくなります。

最後までお読みいただきありがとうございました。

## 参考
* 順番にWorkflowを実行する方法
  * https://victorlillo.dev/blog/run-github-action-after-another-action-finished
* GitHub ActionsのログをCLIで取得する方法
  * https://dev.classmethod.jp/articles/check-the-github-actions-workflow-execution-log-with-github-cli/
* ログの保持期間
  * https://docs.github.com/ja/organizations/managing-organization-settings/configuring-the-retention-period-for-github-actions-artifacts-and-logs-in-your-organization
dockerで動かそうとしています。

Space ROS (Robot Operating System) Calendar 2024
https://qiita.com/advent-calendar/2024/space-ros
Day 16 投稿予定記事です。

# 自己紹介
なぜ経済学徒を辞め、計算機屋になったか（経済学部入学前・入学後・卒業後対応） 転職(1)
https://qiita.com/kaizen_nagoya/items/06335a1d24c099733f64

JAXA/IPA クリティカルソフトウェアワークショップ WOCS言語関連発表(改定版)
https://qiita.com/kaizen_nagoya/items/4789832baf494cb74626

NASAを超えるつもりがあれば読んでください。OSEK(85) 安全(97)
https://qiita.com/kaizen_nagoya/items/e81669f9cb53109157f6

お盆には「箱庭」記事を書きましょう「もくもく会」の題材になる（2）
https://qiita.com/kaizen_nagoya/items/e3c0ceb2dcc9cf3f3fac

# Space ROS
Space ROS
https://space.ros.org
https://github.com/space-ros

##  moveit2
moveit2
https://github.com/space-ros/docker/tree/main/moveit2

```macOS:dbash
$ docker run -it osrf/space-ros:latest /bin/bash
Unable to find image 'osrf/space-ros:latest' locally
latest: Pulling from osrf/space-ros
6414378b6477: Pull complete 
b65f1d11f71a: Pull complete 
a7f2481d1ef1: Pull complete 
aae34eb940be: Pull complete 
5bde5e197698: Pull complete 
bd159e9d0602: Pull complete 
1cf6b57f97b1: Pull complete 
d000a0878bbb: Pull complete 
728adcc50de8: Pull complete 
3e33010ee772: Pull complete 
672874448f4d: Pull complete 
4f4fb700ef54: Pull complete 
50eff225ed02: Pull complete 
bc32f75d7300: Pull complete 
e224a1ab9392: Pull complete 
fc5149418b6e: Pull complete 
3dbc27c2fa07: Pull complete 
807cd5639c70: Pull complete 
1cf6f1986f08: Pull complete 
ae04c7577d13: Pull complete 
44c16f2c1c8f: Pull complete 
e917675ef0f6: Pull complete 
b8922e005450: Pull complete 
d2df3315c806: Pull complete 
750d0d951a2b: Pull complete 
654fdb6b836a: Pull complete 
11d2e0bac51a: Pull complete 
e5cd08ed36dd: Pull complete 
1a4e61f2b77d: Pull complete 
e6c82d7f4fa1: Pull complete 
1900a2cab7f4: Pull complete 
df932ca63906: Pull complete 
bdda9898bf4f: Pull complete 
Digest: sha256:c02efac0b9e59328d3930c6963f648340dd0244bcbf37dcffab1d77f5d925ebc
Status: Downloaded newer image for osrf/space-ros:latest
WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
```
API Best Practices ~不透明データをWebセーフエンコーディングとバイナリシリアライズでエンコードする

## 不透明データをWebセーフエンコーディングとバイナリシリアライズでエンコードする

https://protobuf.dev/programming-guides/api/#encode-opaque-data-in-strings

サーバーとクライアント間でデータを送受信する際に、一部のデータはクライアントには直接理解できない形式で送られることがあります。このようなデータを「不透明なデータ」と呼びます。  
例えば、サーバー側で生成された内部的な識別子やバージョン情報などがこれに該当します。

### 手法:  
 1. プロトコルバッファの定義:  
不透明なデータを格納するための専用のプロトコルバッファを定義します。  
このプロトバッファには、不透明なデータの各要素に対応するフィールドが含まれます。

2. シリアライゼーション:  
プロトコルバッファをバイナリ形式にシリアライズします。  
バイナリ形式は、コンピュータが直接理解できる形式で、効率的なデータ転送が可能です。  

3. WebセーフBase64エンコーディング:  
シリアライズされたバイナリデータを、URLやファイル名などに安全に使用できるWebセーフBase64形式に変換します。  

4. クライアントへの送信:  
WebセーフBase64エンコードされたデータを、クライアントに送信します。

5. クライアント側での処理:  
クライアントは、受信したWebセーフBase64エンコードされたデータをデコードします。  
デコードされたバイナリデータを、事前に定義されたプロトコルバッファの構造に従ってデシリアライズします。  
デシリアライズされたデータから、必要な情報を抽出します。

### なぜプロトコルバッファを使うのか?  
- 効率性: バイナリ形式でコンパクトにデータを表現できるため、ネットワーク通信やディスクへの保存において高速な処理が可能です。
- 拡張性: 新しいフィールドを追加したり、既存のフィールドの型を変更したりしても、古いデータとの互換性を保つことができます。
- 言語のサポート: 様々なプログラミング言語に対応しており、一度定義したスキーマを様々な言語で利用できます。

### 例外
非常に特殊なケースにおいて、厳密に設計された独自のフォーマットが、プロトコルバッファよりもコンパクトな表現を実現できる場合があります。  
ただし、このようなケースは非常にまれであり、一般的にはプロトコルバッファを使用することを推奨します。
API Best Practices ~IDにはstring型をつかうこと , クライアントが構築/解析することを期待する文字列にデータをエンコードしてはならない

## IDにはstring型をつかうこと

[https://protobuf.dev/programming-guides/api/#integer-field-for-id]

オブジェクトの識別子としてint64を使いたくなるが代わりに文字列を使おう。  

DB で AUTO_INCREMENT ID などを利用していると特に int64 にしたくなりますが、  
とりあえず string 型で定義しておくと、リンク先にあるようなシリアライズする拡張性も生まれてきます。  

## クライアントが構築/解析することを期待する文字列にデータをエンコードしてはならない

[https://protobuf.dev/programming-guides/api/#dont-encode-data-in-a-string]

利用者にとっては手間がかかり、開発者にも混乱を招く。  

string型にメッセージ定義で表現しきれない独自の仕様を内包するために、膨大なコメントを書いたり別個にドキュメントを書いたりしたりコスト増加を招きます。

身近(?)な例でいうと json 文字列そのものを保存したい場合に、string型として定義してはいけないということ。  
パースした構造をメッセージ定義して、それぞれをプリミティブな型まで分解したメッセージ定義にしておくこと。

### 対フロントエンドでHTMLを返す（NG)

[https://protobuf.dev/programming-guides/api/#returning-html]

フロントエンド用の定義でHTMLを返したいケースなど、ついやってしまいがちなパターンの危険性が記載してあります。  
通常はデータを返し、HTML構築はフロントエンドのテンプレートを利用するなどしたほうがいいでしょう。
Advent of code 2015 Day 8 Part 2 を Livebook で楽しむ

## はじめに

Advent of code 2024 の準備として、過去回の Advent of code 2015 を Livebook で楽しみます

本記事では Day 8 の Part 2 を解きます

問題文はこちら

https://adventofcode.com/2015/day/8

実装したノートブックはこちら

https://github.com/RyoWakabayashi/elixir-learning/blob/main/livebooks/aoc/2015/2015_day8_part2.livemd

Part 1 はこちら

https://qiita.com/RyoWakabayashi/private/6a4edaee445b29a8a298

## セットアップ

Kino AOC をインストールします

```elixir
Mix.install([
  {:kino_aoc, "~> 0.1"}
])
```

Kino AOC の使い方はこちらを参照

https://qiita.com/RyoWakabayashi/items/3d3abcf9134cee4dff05

## 入力の取得

"Advent of Code Helper" スマートセルを追加し、 Day 8 の入力を取得します

![スクリーンショット 2024-11-22 20.55.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/9f4b9e2a-0098-3089-d6cc-cd60dbc15a3e.png)

## 私の答え

私の答えです。
折りたたんでおきます。
▶を押して開いてください。

<details><summary>回答</summary>

Part 1 の真逆、 Elixir コードを文字列として評価することになります

この操作は `Macro.to_string` で実行できます

```elixir
puzzle_input
|> String.split("\n")
|> Enum.map(fn code ->
  string_length =
    code
    |> Macro.to_string()
    |> String.length()

  string_length - String.length(code)
end)
|> Enum.sum()
```

</details>

## まとめ

メタプログラミングの考え方を知っていれば簡単でした
学生研究とシステム会社でのプログラミングの違いで感じたこと

## はじめに
学生研究でのプログラミングと、システム会社に入社してからのプログラミングについて、感じたことを書いていきます。まったくもって一般的な話しではなく、私自身(n=1)の話しでしかないですが、誰かの参考になれば幸いです。

## まず結論
通用はするけど別物、難しさの方向性が違う。というのが私の結論です。

以下はこの結論に至った私の略歴と、それぞれのときの難しかったところと楽だったところです。

## 学生時代
### 略歴
学生時代は情報系ではなく数学科でした[^1]。プログラミングは必要に応じて独学です[^3]。勉強は『Pythonで学ぶネットワーク分析』のような、やりたいことに特化した本を読みつつ、わからないことは都度ネット検索[^5]です。扱っていた言語はRとPython[^4]でした。

[^1]: 数学科といっても私の専門は純粋数学ではなく応用でした。「数学科です」と名乗ることになんとなく引け目を感じます。

[^3]: プログラミングについてはとくに教授から指導は受けていません。これは一般的なのかしら？。ゼミではもっぱら、「こんな計算やってみましょうか」と、その評価でした。

[^4]: 主に学部生時代にR、院生のときにPythonを使いました。統計家にとってはRの人気が根強いですが、Pythonのほうが私は扱いやすかったです。（こんなこというと怒られそうですが）

[^5]: 当時はまだChatGPTなどつかえなかったですが、今ならネット検索だけでなくChatGPTなども使えるので勉強がはかどりそうです。

### 難しかったところ
- 自分の研究でやりたいプログラミングとドンピシャなことをやっている人がいない
    - ネット検索しても限界があり、自力でコードを書ききる必要がある
    - アルゴリズムを気にする（自分で考える）必要がある[^6]

[^6]: 複雑ネットワークが専門でした。行列の計算をけっこうしていたので油断すると計算量が多くなりすぎて終わりません。ちょっとの工夫で計算量が劇的に短くなったりすると感動しますよね。

### 楽だったところ[^20]
- 自分しか、短期的にしか使わない
    - わかりやすさやルールを気にしなくていい
    - 気楽、何かあっても自分にしか影響がない[^7]

[^7]: もちろん、だからこそ辛いみたいな面もあります。しかも、プログラミング自体がメインではないので、苦労してプログラミングしたところで大した結果が得られないこともあります。もちろん逆もありますが。

[^20]: 当時は「楽なところ」という認識はしていなく、今思えば、という感じです。昔のコードを今見返すとなかなかにひどいです。がんばったなあ。

 
## システム会社に入ってから
### 略歴
新卒ですぐにシステム会社に入社したわけではなく[^8]、転職してシステム会社に入社しました。.NETでWindowsアプリを作ったりWebサイトを作ったりしています[^31]。前職では「パソコンの人」のような扱いになっていましたが、プログラミングはほとんど[^2]せず、やっていたのはワードやエクセルをさわる程度です。

[^8]: 新卒でまず高校数学教師を数年やりました。実は、大学院も学部後にすぐではなく、数年後に教師をしたまま入学・修了しています。

[^2]: プログラミングをしたのは、wordを一括でpdfにしたとき、と、生徒からきたカードゲームの確率の質問を解いたとき、くらい。今後は数学教師もプログラミングをする機会がでてくるかもしれないですね。

[^31]:すでに出来上がっているアプリやwebサイトに機能追加したり修正したり、直接お客さんと話して設計から最後まで通してやったり、 色々やらせてもらっています。



### 難しいところ
- いろんな人が、長期的に使う
    - 読みやすさやルールを気にする必要がある
    - 変なことをすると自分以外の人に影響がでる
　　
- 同時に扱う言語が多い
    - C#,SQL,HTML,CSS,JavaSciptなど、同時にたくさんさわるので混乱する[^9]
　　
- 自分がやりたいことではなく他人がやりたいことを作る
    - 何ができるようになりたいか、しっかり確認しなければいけない[^10]

### 楽なところ
- 資料が豊富
    - 調べたら使えそうなものがだいたいでてくる[^30]

[^9]: 転職前は、使ったこのない言語にさわること、に少し身構えていました。しかし、それ自体は案外苦じゃないな、という印象です。言語そのものより、サーバー側かクライアント側か、C#かSQLか、のような判断が難しいです。こういうことは学生のときはなかったので。

[^10]: この仕事において、これが一番重要、かつ、難しいこと、のような気がします。せっかく作っても間違っていたら無駄になってしまいますから。（だから上流の仕事の方が給料高いんでしょうね）

[^30]: これは会社によると思います。私の会社は、新しい技術をつくりあげる！みたいなことではないので、だいたいは調べたりChatGPTで解決できます。


## おわりに
大変なのはこれからかもしれないですが、思っていたよりもやれているなという感じです。ただ、毎日勉強は必要で、そしてそれはこれからもずっとそうなんだろうな、という風に感じます。

これからも頑張っていこうと思います。エンジニア仲間を作りたいと思って投稿を始めました。そのうち何かしらのイベントにも顔をだしてみたいです[^12]。

ここまで読んでいただきありがとうございました[^11]。

[^11]: ものすごい量の脚注を書いてしまいました。ここも全部読んだ人はいるのでしょうか。読んでくれた方は本当にありがとうございました。

[^12]: [こちら](https://x.com/ken1math) 私のX(新:twitter)です。なにとぞ！！！
API Best Practices ~プリミティブ型をトップレベルのメッセージ定義に含めない , 後でもっとパターンが増える可能性があるものにはboolを使わないこと

## プリミティブ型をトップレベルのメッセージ定義に含めない

https://protobuf.dev/programming-guides/api/#dont-include-primitive-types

トップレベルのメッセージ定義は殆どの場合、独立して拡張できるよう他のメッセージの集合体であるべきです。
単一のプリミティブ型だけが必要な場合でも、それをラップすることでその型を拡張し同じような値を返す他のメソッド間で型を共有する汎用性も生まれます。

例
```proto
message MultiplicationResponse {
  // Bad: NumericResult のように後で拡張したくても、型変更が破壊的変更になる
  optional double result;

  // Good: 拡張性がある
  optional NumericResult result;
}

message NumericResult {
  optional double real_value;
  optional double complex_value;
  optional UnitType units;
}
```

例外が1つ:  
文字列が実際に構造化されたプロトのエンコードである場合、継続トークン、バージョン情報トークン、IDはすべて文字列として返すことができます。


## 後でもっとパターンが増える可能性があるものにはboolを使わないこと

https://protobuf.dev/programming-guides/api/#never-use-booleans-for-two-states

フィールドにboolを使用する場合は、そのフィールドが本当に2つの状態を記述していることを確認してください。   
（現在と近い将来だけでなく利用停止する最後の瞬間まで）

```proto
message GooglePlusPost {
  // Bad: この投稿を2つのカラムにまたがって表示するかどうかを想定したbool
  optional bool big_post;

  // Good: 将来的にカラムは増える可能性もありそうなので、拡張性を考慮し他のメッセージ型とする
  optional LayoutConfig layout_config;
}

message Photo {
  // Bad: gifかどうかだけを表すbool
  optional bool gif;

  // Good: 参照する写真のファイル形式（例：GIF、WebP、PNG）
  optional ExtensionType type;
}

enum ExtensionType {
  EXTENSION_TYPE_UNSPECIFIED = 0;
  EXTENSION_TYPE_GIF = 1;
  // EXTENSION_TYPE_WEBP = 2;
  // EXTENSION_TYPE_PNG = 3;
}
```
Advent of code 2015 Day 8 Part 1 を Livebook で楽しむ

## はじめに

Advent of code 2024 の準備として、過去回の Advent of code 2015 を Livebook で楽しみます

本記事では Day 8 の Part 1 を解きます

問題文はこちら

https://adventofcode.com/2015/day/8

実装したノートブックはこちら

https://github.com/RyoWakabayashi/elixir-learning/blob/main/livebooks/aoc/2015/2015_day8_part1.livemd

## セットアップ

Kino AOC をインストールします

```elixir
Mix.install([
  {:kino_aoc, "~> 0.1"}
])
```

Kino AOC の使い方はこちらを参照

https://qiita.com/RyoWakabayashi/items/3d3abcf9134cee4dff05

## 入力の取得

"Advent of Code Helper" スマートセルを追加し、 Day 8 の入力を取得します

![スクリーンショット 2024-11-22 20.55.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1485835/9f4b9e2a-0098-3089-d6cc-cd60dbc15a3e.png)

## 私の答え

私の答えです。
折りたたんでおきます。
▶を押して開いてください。

<details><summary>回答</summary>

問題文で求められている変換は以下の通り

- `\"` -> `"`
- `\\` -> `\`
- `\x27` -> `'`

つまり、文字列を Elixir のコードとして評価すれば良いことになります

`Code.eval_string` でその通りのことが可能です

`Code.eval_string("\"\"")` を実行すると、実行結果は `{"", []}` となります

同様に以下のように変換されます

- `Code.eval_string("\"abc\"")`: `"abc"`
- `Code.eval_string("\"aaa\\\"aaa\"")`: `"aaa\"aaa"`
- `Code.eval_string("\"\\x27\"")`: `"'"`

これをそのまま使えば良いだけです

```elixir
puzzle_input
|> String.split("\n")
|> Enum.map(fn code ->
  code_length =
    code
    |> Code.eval_string()
    |> elem(0)
    |> String.length()

  String.length(code) - code_length
end)
|> Enum.sum()
```

</details>

## まとめ

Day 8 は「何の関数を使えば良いか」というだけの問題ですね

Part 2 はこちら

https://qiita.com/RyoWakabayashi/private/df930f4d909167100d7a
API Best Practices ~外部と内部では異なる定義ファイルを使用すること , 更新は完全な置換ではなく、部分的な更新または追加のみの更新をサポートすること

## 外部と内部では異なる定義ファイルを使用すること

https://protobuf.dev/programming-guides/api/#use-different-messages

例えば BFF(backend for frontend) と InternalAPI のような構成の場合に、同じ定義ファイルを参照していると時間の経過とともに問題が出てきます。  
BFF に影響の出ないよう InternalAPI の変更をしたい場合など、同一のメッセージを参照していると困難です。  
こうならないよう、レイヤー構造のように別々のメッセージ定義をしておくことを推奨します。

例外は次のとおりです。

- proto フィールドが google.type や google.protobuf のような一般的な型である
- サービスのパフォーマンスが極めて重要な場合
    - 柔軟性を犠牲にして実行速度を上げる価値があるかもしれません
    - サービスが数百万 QPS でミリ秒単位のレイテンシを求めるような場合においては


## 更新は完全な置換ではなく、部分的な更新または追加のみの更新をサポートすること

https://protobuf.dev/programming-guides/api/#support-partial-updates

例えば以下のようなメッセージにフィールド追加したい場合に、 
クライアントが追加フィールドを保持しない旧versionのメッセージ定義を利用する場合、追加のフィールドを取得できずラウンドトリップでデータが失われます。
```proto
message User {
  string id = 1;
  string name = 2;
  // string address = 3; <- 追加したい項目
}
```

### Fix #1: 更新用のフィールドマスクを使用する

https://protobuf.dev/programming-guides/api/#use-update-field-mask

クライアントが変更したいフィールドを渡し、そのフィールドだけを 更新リクエストに含めるようにする。 
サーバーは他のフィールドはそのままにしておき、マスクで指定されたフィールドだけを更新します。 
一般的に、マスクの構造はレスポンスのメッセージ定義の構造を反映したものであるべきです。  
つまり、FooがBarを含んでいれば、FooMaskはBarMaskを含んでいます。

https://github.com/protocolbuffers/protobuf/blob/main/src/google/protobuf/field_mask.proto

フィールドマスクの利用については、ただ有ればよいというものではなく注意点なども多いのでよく理解してから定義したほうがよい。  

### Fix #2: 個々のフィールドに限定した、より狭い範囲の API を定義する

https://protobuf.dev/programming-guides/api/#expose-more-narrow-mutations

例でいうと UpdateUser() にフィールド追加するのではなく、新たに UpdateUserAddress() を定義しようということ。  
あまりに数が増えるとメンテナンスコストや認知負荷も高まるので程々に。
Apache HTTPサーバーとLaravelのビルトインサーバー(php artisan serve)

<p>初めてLaravelでチーム開発を行なっていた時、php artisan serveとターミナルに入力して、開発中のアプリケーションにアクセスしていました。しかし、その際XAMPPを使っていたため、XAMPPのApache Http サーバーが起動してアプリケーションにアクセスしているのだと勘違いしていました。</p>

<h1>php artisan serveコマンドとは一体なんぞや？</h1>
<p>php artisan serveとターミナルにコマンドを入力すると、Laravelに内蔵されている（PHP本体にある）ビルトインサーバー機能が利用され、アプリケーションにアクセスすることとなります。そのため、XAMPPのApache Http サーバー（Webサーバー）は経由していません。この場合、ブラウザであなたが見ている画面は、Webサーバーを経由することなくApplicationサーバーにダイレクトでアクセスし、画面が表示されているということになります。</p>

<h1>Apache HTTP サーバーを経由してアプリにアクセスしてみる</h1>
<p>ブラウザのアドレスバーに"http://localhost/プロジェクト名/public"と入力すると、Apache HTTP サーバーを経由してアプリにアクセスすることができます。この場合、Webサーバー(Apache HTTP サーバー）を経由した後、Applicationサーバー(php artisan serveコマンドを実行）を経由してアプリにアクセスしているという流れとなっております。</p>

<h1>どのように使い分けたらいいの？</h1>
<p>では一体、どのように使い分けたらいいのでしょうか？　開発段階では、php artisan serveコマンドを使ってアプリケーションにアクセスします。そして、アプリケーションを公開する場合は、Apache HTTP サーバーを使用します。ただしここで注意！XAMPPに搭載されているApache HTTPサーバーは、ローカルIPアドレスで動作します。そのため、XAMPPをインストールしたコンピュータ以外からは直接アクセスすることができません。それだと、Apache HTTPサーバーはアプリケーションを公開するために使えないじゃないかと思うかと思います。実は、Apache HTTPサーバーはXAMPPに搭載されている以外に、レンタルサーバー（ホスティングサービス）でよく利用されているのです。そのため、アプリケーションを公開するために、よくApache HTTPサーバーが利用されるわけです。アプリケーションを公開したい場合は、レンタルサーバーを利用することが一般的であるようです。つまり、アプリケーションを公開しようと思うとお金がかかるわけですな...</p>
API Best Practices ~殆どのフィールドとメッセージを正確に、簡潔にコメントしてドキュメント化しておくこと

## なんの記事？
protobuf 公式の API Best Practices ドキュメントの自分なりの要約です。  

https://protobuf.dev/programming-guides/api/

あくまで翻訳ではなく、自分にとって理解し易いよう噛み砕いた要約を記載していきます。

### なぜ読んだか・書いたか
仕事で Protocol Buffers を利用することが多く、  
このレビューにおいてレビュアーとレビューイが共通の判断基準を持つことは、  
コードの品質向上、チームの効率化、そしてプロジェクトの成功に大きく貢献します。

この判断基準として当該記事がよさそうだということで職場で話題となり、  
読んだか？読んだよ！では味気ない。  
よい機会 & とても長いので、全部俺カレンダーにてアウトプットしようという試みです。

以下から本文開始です。

---

# API ベストプラクティス
将来性のあるAPIを正しく作るのは驚くほど難しい。  
このドキュメントの提案は、長期的でバグのない進化を優先するためにトレードオフを行うものです。

この文書はプロトのベストプラクティスを補完するもので、Java/C++/Go やその他の API に対する処方箋ではありません。 
コードレビューでこれらのガイドラインから外れているものを見かけたら、その作者にこのトピックを示し情報を広める手助けをしてください。

注意:  
これらのガイドラインはあくまでガイドラインであり、その多くには文書化された例外があります。  
例えば、パフォーマンスを重視するバックエンドを書く場合、柔軟性や安全性を犠牲にしてでもスピードを追求したくなるかもしれません。  
このトピックは、トレードオフをよりよく理解し、あなたの状況に合った決断を下すのに役立つだろう。

## 殆どのフィールドとメッセージを正確に、簡潔にコメントしてドキュメント化しておくこと

https://protobuf.dev/programming-guides/api/#precisely-concisely

あなたが書いた（あるいは修正したもの）は、その際に何を考えていたかを知らない人々によって継承され、使われる可能性が高い。  
新しいチームメンバーや、システムについての知識がほとんどない人にとって役に立つ言葉で、各フィールドを文書化してください。

具体的な例をいくつか：
```proto
// 悪いコメント： Fooを有効にする設定
// 良いコメント： Foo機能の動作を制御する設定
message FeatureFooConfig {
// 悪いコメント： 機能が有効かどうか
// 良いコメント： hoge で Foo 機能が有効かどうかを示すフィールド。 hoge の FOO_OPTIN が設定されていない場合はfalseでなければなりません。
  bool enabled;
}

// 悪いコメント： Fooオブジェクト
// 良いコメント： APIで公開されるFooのクライアント向け表現
message Foo {
  // 悪いコメント： Title of the foo.
  // 良いコメント： ユーザーが入力した Foo のタイトルを、正規化もエスケープもせずに示します。
  optional string title [(max_length) = 512];
}

// 悪いコメント： Foo config.
//   最も有用なコメントが名前の再記述である場合、そのコメントは省略した方がよい。
FooConfig foo_config = 3;
```

このように各フィールドの制約、期待、解釈をできるだけ少ない単語でコメントします。

上記の例には`max_length`が記述されていますが、このようにカスタムアノテーションを使用できます。  
※ [bufbuild/protovalidate](https://github.com/bufbuild/protovalidate) を採用した例では、以下のような書き方をします。  
```proto
  optional string title = 1 [(buf.validate.field).string.max_len = 512];
```

時間が経つにつれてドキュメントはどんどん長くなります。長さは明確さを損ないます。  
簡潔さを心がけましょう。
C_TS452_2021 - SAP Certified Application Associate - SAP S/4HANA Sourcing and Procurement対策（20/100）

【20日目】
トピック3：調達プロセス（1/13）

≪問題20≫
梱包に関する金額契約について仕入先と合意しました。正確な品目と購買価格はリリース発注まで確定されません。この契約の登録時に使用する必要がある明細カテゴリはどれですか。
▢T（テキスト）
▢B（制限）
▢W（品目グループ）
▢M（品目未選定）

≪解答≫
▢T（テキスト）
▢B（制限）
▢W（品目グループ）
█M（品目未選定）

≪解説≫  
◆品目未選定（M）および品目グループ（W）という明細カテゴリは、契約においてのみ使用可能な特別な明細カテゴリであり、品目コードを指定せずに契約明細を入力する際に用いられます。  
1. **明細カテゴリM** は、同一価格の類似した品目に使用することが推奨されます。ただし、品目コードが異なる場合や品目マスタレコードが存在しない品目にも使用可能です。このカテゴリは、数量契約と金額契約のどちらにも対応しています。  
2. **明細カテゴリW** は、同一の品目グループに属しているものの、価格が異なる品目に使用することが適しています。この明細カテゴリは、金額契約にのみ使用可能です。
Go言語でGeoIP2/GeoLite2(IP位置情報）データベースを使う方法

# はじめに

IPアドレスから位置情報を検索するためのデータベースGeoIP2/GeoLite2をGo言語から使う方法の説明です。


# パッケージ

Go言語のパッケージは、

https://github.com/oschwald/geoip2-golang

です。


# GeoLite2データベースの入手方法

無償版のGeoLite2はパッケージのREADME.mdにダウンロードのリンクが記載されていますが、
現在はページがありません。404のページが表示されます。
以前はユーザー登録をしないでダウンロード可能でしたが現在は登録してからダウンロードするようになりました。


https://dev.maxmind.com/geoip/geolite2-free-geolocation-data/

のページ

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3874862/b4eb6f39-a87c-4765-c2cb-241c56d4a90a.png)


にあるリンクから登録してログインすると


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3874862/d223233e-d3e3-1cba-74f5-7d047bd99522.png)

のような画面からダウンロードできます。Download Filesメニューです。

GeoLite2 CityのGZIPをダウンロードすればよいです。
解凍すると
```
GeoLite2-City.mmdb
```
というファイルがあります。これがGeoLite2のデータベースファイルです。



# サンプルプログラム

このデータベースを利用してIPアドレスから位置情報を取得するサンプルプログラムは

```go
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"os"

	"github.com/oschwald/geoip2-golang"
)

func main() {
	if len(os.Args) < 2 {
		log.Fatal("No IP")
	}
	db, err := geoip2.Open("GeoLite2-City.mmdb")
	if err != nil {
		log.Fatal(err)
	}
	defer db.Close()
	ip := net.ParseIP(os.Args[1])
	record, err := db.City(ip)
	if err != nil {
		log.Fatal(err)
	}
	js, err := json.MarshalIndent(record, "", " ")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(js))
}

```

です。データを見やすくするためJSON形式にフォーマットしています。

```terminal
$ go run main.go 219.118.71.18
```
のように実行すると


```json
{
 "City": {
  "Names": null,
  "GeoNameID": 0
 },
 "Postal": {
  "Code": ""
 },
 "Continent": {
  "Names": {
   "de": "Asien",
   "en": "Asia",
   "es": "Asia",
   "fr": "Asie",
   "ja": "アジア",
   "pt-BR": "Ásia",
   "ru": "Азия",
   "zh-CN": "亚洲"
  },
  "Code": "AS",
  "GeoNameID": 6255147
 },
 "Subdivisions": null,
 "RepresentedCountry": {
  "Names": null,
  "IsoCode": "",
  "Type": "",
  "GeoNameID": 0,
  "IsInEuropeanUnion": false
 },
 "Country": {
  "Names": {
   "de": "Japan",
   "en": "Japan",
   "es": "Japón",
   "fr": "Japon",
   "ja": "日本",
   "pt-BR": "Japão",
   "ru": "Япония",
   "zh-CN": "日本"
  },
  "IsoCode": "JP",
  "GeoNameID": 1861060,
  "IsInEuropeanUnion": false
 },
 "RegisteredCountry": {
  "Names": {
   "de": "Japan",
   "en": "Japan",
   "es": "Japón",
   "fr": "Japon",
   "ja": "日本",
   "pt-BR": "Japão",
   "ru": "Япония",
   "zh-CN": "日本"
  },
  "IsoCode": "JP",
  "GeoNameID": 1861060,
  "IsInEuropeanUnion": false
 },
 "Location": {
  "TimeZone": "Asia/Tokyo",
  "Latitude": 35.6897,
  "Longitude": 139.6895,
  "MetroCode": 0,
  "AccuracyRadius": 500
 },
 "Traits": {
  "IsAnonymousProxy": false,
  "IsAnycast": false,
  "IsSatelliteProvider": false
 }
}

```

のような結果が表示されます。

国や緯度経度などの情報が取得できます。
City（都市名）も取得できる場合があります。

:::note alert
有償のGeoIP2のデータベースでは試したことがりません。
もっと、正確な情報が表示されるかもしれません。
:::


# 余談

このGeoIPのパッケージは、TWSNMPシリーズで使っています。

https://note.com/twsnmp/n/na5c62f59833d

TWSNMP FCのアドレス分析

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3874862/11adb114-f182-d5bd-2efd-c2d3654649e5.png)

の画面でIPアドレスから位置を調べられます。
NetFlowの情報からも自動で位置情報を取得してレポートを作成しています。

:::note warn
GeoLite2のデータベースは、組み込んでいないので、この記事で紹介した方法に従ってダウンロードしてください。
:::
異なるネットワークを跨ぐ時の問題

# インターネットルーティングにおけるそれぞれのネットワークの問題
>基本的に単一のインターネットワーク・ルーティングと同様の問題が生じるが、もう少し複雑になる。まず、それぞれのネットワークは内部的に異なるルーティング・アルゴリズうを用いている可能性がある。

>インターネットワーク・コネクションにまたがる最短経路の見つけ方が不明瞭になっているといえる

# 各社それぞれの方針のルーティングを行う
>異なるネット運営会社が運用するネットワーク間ではさらに大きな問題が発生する。またどんな経路を良い経路を考えるかがネットワーク運営会社によって異なることによって異なる可能性がある。遅延が最小となる経路が良いと考えるネットワーク運営会社もいれば、最も安価な経路が良いと考えるネットワーク運営会社もいる。このため、経路のコストとして設定する量がネットワーク運営会社によって異なる

### アルゴリズムの重みや経路で機密情報が反映される
>さらに悪いことに、重みや経路には、各社の商業的な強みを表す機密情報が反映されることがあり、ネットワーク内の詳細な経路を他のネットワーク運営会社に知られたくない場合すらある。

# イントラドメイン、インタードメイン
>接続後のネットワークはそこに含まれるいかなるネットワークよりも大きくなる。したがって、階層構造を利用することによってうまくスケーラビリティを実現できるルーティング・アルゴリズムが必要となる。

>ある２階層のルーティング・アルゴリズムが導かれる。各ネットワークないのルーティングでは、`イントラドメイン`プロトコルが利用される。これはすでに紹介したリンク状態プロトコルでも良い。ネットワーク間をまたがる場合には、インタードメインプロトコルが利用される。ここのネットワークがすべて異なるイントラドメイン・プロトコルを使用しても良いが、インタードメイン・プロトコルは同じものを使用しなければならない。

# 自律システム
>それぞれのネットワークは、他のすべてのネットワークから独立して動作するため、AS（Autonomous System:自律システム）と呼ばれる。ASを理解するための良い例の一つが、ISPネットワークである。実際には、一つのISPネットワークが二つ以上のASから構成されていることもあるが、たいていの場合、それらに大きな違いはない

# ISPのビジネス
>相互接続ネットワークにまたがるルートがどのようにして決定されるかについてほとんど言及していない。インターネットでは、ISP間のビジネス的な取り決めが大きな決定要因となる。ISPが他のISPのトラフィックを運ぶ際には、それらのISP間で金銭の授受が発生する場合がある。

# 異なるインターネットの接続の非技術的な問題をルーティング・ポリシー
>インターネットワーク・ルーティングが国境を越える必要がある場合、種々の法律が関わってくることである。例えば、スウェーデンには、自国民の個人情報の国外流出に関して厳しい法律ある。これらの非技術的な要因全ては、ASが用いるルートの選び方を管理するルーティング・ポリシーという概念が含まれている。

https://bookplus.nikkei.com/atcl/catalog/23/03/16/00728/
Angular without ZoneJS (Zoneless) を少し触った。(その１ー２)

**注意**  
**Zoneless はexperimental(実験的機能)です。**  
**Angular は@18.2.12です。**  

## 前置き
Angular の記事を読むとZoneless という単語がよく出てくる。  
気になったので、公式サイトを見ながら少し触って通常(withZone)とZonelessとを比較したメモがこちら。  
なお、今回比較したのは次の3点。  
- AsyncPipe の挙動
- 制御フロー構文の挙動
- Signal input の挙動

↓公式サイト  

https://angular.jp/guide/experimental/zoneless

↓成果物  

https://magurouhiru.github.io/test-zoneless/

## 本編
### 結果
AsyncPipe の挙動の変化も制御フロー構文の挙動の変化も、今回のやり方では分からなかった。  
Signal input の挙動の変化は以下の通りであった。  
withZone の場合は、都度`ngDoCheck()`が発火し、更新された値が画面に表示された。  
Zoneless の場合は、Signal を使用した子コンポーネントのみ`ngDoCheck()`が発火し、それ以外は発火しなかったた。  
また、Signal を使用していなければ更新された値が画面に表示されなかった。  

## 感想
楽しかった。  
あと、今後どうなるか分からないけど、Signal って重要なんだなって思った。  


今回の記事は以上です。  

前回記事はこちら  

https://qiita.com/magurouhiru/items/d2ca76724f0eac37a0b0

詳細見たい人はこちら  

https://github.com/magurouhiru/test-zoneless

https://magurouhiru.github.io/test-zoneless/
【手順書】NEXT.JS の静的サイトのGitHub Pages へのデプロイ

### ステップ 1: GitHub Actions ワークフローの作成
1. リポジトリのルートに `.github/workflows` フォルダーを作成します。
2. `deploy-gh-pages.yml` という名前のファイルを作成し、以下の内容をコピー＆ペーストします。

   ```yaml
   name: Deploy static content to Pages

   on:
     push:
       branches: ["main"]
     workflow_dispatch:

   permissions:
     contents: read
     pages: write
     id-token: write

   concurrency:
     group: "pages"
     cancel-in-progress: true

   jobs:
     deploy:
       environment:
         name: github-pages
         url: ${{ steps.deployment.outputs.page_url }}
       runs-on: ubuntu-latest
       env:
         NEXT_PUBLIC_SERVICE_ID: ${{ secrets.NEXT_PUBLIC_SERVICE_ID }}
         NEXT_PUBLIC_BASE_PATH: /リポジトリ名を記載
       steps:
         - name: Checkout
           uses: actions/checkout@v3
         - name: Set up Node
           uses: actions/setup-node@v4
           with:
             node-version: 20
             cache: "npm"
         - name: Install dependencies
           run: npm ci
         - name: Build
           run: npm run build
           env:
             GITHUB_PAGES: true
             NEXT_PUBLIC_BASE_PATH: /リポジトリ名を記載
         - name: Setup Pages
           uses: actions/configure-pages@v4
         - name: Upload artifact
           uses: actions/upload-pages-artifact@v3
           with:
             path: "./out"
         - name: Deploy to GitHub Pages
           id: deployment
           uses: actions/deploy-pages@v4
   ```

### ステップ 2: 環境変数の設定
1. GitHub リポジトリの「Settings」タブに移動します。
2. 左側のメニューから「Secrets and variables」を選択し、「Actions」をクリックします。
3. `NEXT_PUBLIC_SERVICE_ID` を追加し、必要な値を設定します。

### ステップ 3: リポジトリ名の設定
- `deploy-gh-pages.yml` 内の `NEXT_PUBLIC_BASE_PATH` にリポジトリ名を記載します。

### ステップ 4: gitのコミットとプッシュ
1. 変更をコミットし、`main` ブランチにプッシュします。
   ```bash
   git add .
   git commit -m "Setup GitHub Pages deployment"
   git push origin main
   ```

### ステップ 5: デプロイの確認
1. GitHub リポジトリの「Actions」タブを開き、ワークフローが実行されていることを確認します。
2. デプロイが成功すると、GitHub Pages の URL が表示されます。
スイッチロールの要点だけざっくり

## スイッチロールとは
AWS環境で権限を切り替えたいときに使う。
検証用→本番用、AWSアカウントA→AWSアカウントB　など。
手順は他のサイトにたくさんあるので、本投稿では要点だけを書きます。

## ざっくり図
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628630/33745263-3ea6-ef17-0b03-9d2b194362d4.png)

## 切り替え操作
AWSマネジメントコンソールにログイン後、右上のほうにロールの切り替えボタンがある。
スイッチ先のAWSアカウントID、スイッチ先のIAMロール名を指定してロールを切り替える。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628630/183af644-ddb8-9ca9-0739-6a683bcec240.png)

## stsを許可するjson
スイッチ元のIAMユーザがスイッチロールするための許可を与える。
```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": "sts:AssumeRole",
            "Resource": "*"
        }
    ]
}

```

## 信頼ポリシーはユーザ単位またはロール単位で指定可能
### Principalに書くパターン
```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "{スイッチ元IAMユーザまたはロールARN}"
            },
            "Action": "sts:AssumeRole",
            "Condition": {}
        }
    ]
}

```

### Conditionに書くパターン
```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::{AWSアカウント}:root"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "ArnLike": {
                    "aws:PrincipalArn": [
                        "{スイッチ元IAMユーザまたはロールARN}"
                    ]
                }
            }
        }
    ]
}
```

### （おまけ）グループ単位はNG
```
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Principal": {
                "AWS": "arn:aws:iam::{AWSアカウント}:root"
            },
            "Action": "sts:AssumeRole",
            "Condition": {
                "ArnLike": {
                    "aws:PrincipalArn": [
                        "{スイッチ元ユーザにアタッチされているIAMグループのARN}"
                    ]
                }
            }
        }
    ]
}

```
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/628630/2a6f2302-abdf-bf44-c4ae-28745dc54ae3.png)
IAMロール自体は作成できるものの、スイッチしようとするとエラーになる。

以上
GitHubでたまに見るColabやKaggleのアイコンの出し方



# GitHubでたまに見るColabやKaggleのアイコンって？

例えば、ゼロつく⑤のGitHubレポジトリにあります。押すと該当するNotebookに飛ぶアレのことです。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2627015/4c50c8a6-9d6f-cc9b-0c66-c4087ed2a1f4.png)


https://github.com/oreilly-japan/deep-learning-from-scratch-5

# このアイコンはどこで手に入るのか

公式がSVGとして公開しています。下記から入手可能です。

**Colab**

https://colab.research.google.com/assets/colab-badge.svg

**Kaggle**

https://kaggle.com/static/images/open-in-kaggle.svg

# GitHubでたまに見るColabやKaggleのアイコンの出し方

## **1. 一般的な記述方法**
Markdownを利用してColabやKaggleのバッジを表示し、リンクを付ける方法は下記です。

### **Colabバッジ**
```markdown
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/<ユーザー名>/<リポジトリ名>/<パス>/<ノートブック名>.ipynb)
```

### **Kaggleバッジ**
```markdown
[![Kaggle](https://kaggle.com/static/images/open-in-kaggle.svg)](https://kaggle.com/kernels/welcome?src=https://github.com/<ユーザー名>/<リポジトリ名>/<パス>/<ノートブック名>.ipynb)
```

## **2. 具体例**
以下は、[ゼロから作るDeep Learning⑤](https://github.com/oreilly-japan/deep-learning-from-scratch-5)のサンプルノートブックを用いた具体例です。

### **Colabバッジ**
```markdown
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/oreilly-japan/deep-learning-from-scratch-5/blob/master/notebooks/01_normal.ipynb)
```

### **Kaggleバッジ**
```markdown
[![Kaggle](https://kaggle.com/static/images/open-in-kaggle.svg)](https://kaggle.com/kernels/welcome?src=https://github.com/oreilly-japan/deep-learning-from-scratch-5/blob/master/notebooks/01_normal.ipynb)
```

### **ColabとKaggleバッジを並べる場合**
```markdown
[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/oreilly-japan/deep-learning-from-scratch-5/blob/master/notebooks/01_normal.ipynb) | [![Kaggle](https://kaggle.com/static/images/open-in-kaggle.svg)](https://kaggle.com/kernels/welcome?src=https://github.com/oreilly-japan/deep-learning-from-scratch-5/blob/master/notebooks/01_normal.ipynb)
```


以上！
【忘備録】WPFでMVVMやるときのサンプルテンプレート集

WPFアプリをMVVMで開発する際によく使うコードのテンプレートをここに残しておく。
## コマンドクラス
```cs:RelayCommand.cs
using System;
using System.Windows.Input;

namespace ListViewLib
{
    public class RelayCommand : ICommand
    {
        private readonly Action<object> _execute;
        private readonly Predicate<object> _canExecute;

        public RelayCommand(Action<object> execute, Predicate<object> canExecute = null)
        {
            _execute = execute;
            _canExecute = canExecute;
        }

        public bool CanExecute(object parameter) => _canExecute?.Invoke(parameter) ?? true;
        public void Execute(object parameter) => _execute(parameter);

        public event EventHandler CanExecuteChanged
        {
            add => CommandManager.RequerySuggested += value;
            remove => CommandManager.RequerySuggested -= value;
        }
    }
}
```
## INotifyPropertyChangedの実装
```cs:sample.cs
        public event PropertyChangedEventHandler PropertyChanged;
        protected void OnPropertyChanged(string propertyName) =>
            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
```
## VMのプロパティ
```cs:sampleViewModel.cs
        private string _property;
        public string Property
        {
            get => _property;
            set
            {
                if (_property == value) return;
                _property = value;
                OnPropertyChanged(nameof(Property));
            }
        }
```
## DependancyProperty
```cs:SampleDependancyProperty.cs
        static public DependencyProperty SampleProperty =
            DependencyProperty.Register(
            nameof(Sample),
            typeof(string),
            typeof(SampleDependancyProperty),
            new PropertyMetadata(null, SamplePropertyChanged));

        public string Sample
        {
            get => (string)GetValue(SampleProperty);
            set => SetValue(SampleProperty, value);
        }
        private static void SamplePropertyChanged(DependencyObject obj, DependencyPropertyChangedEventArgs e)
        {
            Trace.WriteLine($"Sampleが変わりました。NewValue={e.NewValue}");
        }
```
TryHackMeのOWASP TOP10でシェルを奪えなかった件

## 問題
タスク26で手順通り実行したのにシェルを奪えない
## 状況
使用パソコン：Macbook Pro（M3チップ）
VPNマシン：ParrotOS（UTMで起動）
ブラウザ：Google Chrome
インターネット回線：自宅のwi-fiを利用
事象：タスク26の手順通りに実施して求めたencodedPayloadをセットしてリロードしてもシェルが奪えない（待ち受けているターミナル画面に接続が表示されない）。ブラウザはずっとグルグル回ってて一定時間経過後504エラーが発生する。
## 解決方法
TryHackMeのAtackBoxを使用する。
## 事象の原因
wi-fiを利用した環境では待受端末のIPアドレスがローカルアドレスとなりTryHackMeのマシンから通信できない（Openvpnでブラウザを見ることができるのはこちらから通信しており、NATによる変換が可能なため。今回はリバースシェルなので相手側から通信してもらうわなければならないが、指定する宛先IPがローカルアドレスだと通信できない）
## 最後に
待受してるのに接続されないことからIPアドレスを疑った。しかし何度見直してもIPアドレスは間違っていない。見本の画像と見比べていると見本はグローバルアドレスなのに自分はローカルアドレスを使っていることに気づいた。考えてみれば当たり前のことだがなかなか解決できなかった。ネットワークの知識はある程度身につけてるつもりだったけどまだまだ足りない…
知っている知識から使える知識に変えていかねば！
Vikeを利用するときはBatiを使って生成する

## batijs とは
https://batijs.dev/

vike を利用するときに React なり Tailwind なりを組み込んで利用することがおおいと思うが、
bitejs を利用すると色々含めてプロジェクト作成をおこなってくれる。便利。

### vike とは
NodeJS系のフレームワーク。vite が載っている。
独自のファイル構造と Config があるので、導入コストはすこし重いかも。

SPA と SSR をページごとに分けて書き出さないといけない案件で利用したが、
ディレクトリごとに設定できてその点は扱いやすかった
Databricksでstable-diffusion-xl-1.0-inpaintingを使って顔ハメ看板を作ってみる

インペインティングのモデルというものがあるんですね。

https://huggingface.co/diffusers/stable-diffusion-xl-1.0-inpainting-0.1

そもそも、インペインティング(inpainting)って何？状態でした。絵画の修復という意味があるんですね。これも、モデルを使ってみて腑に落ちました。

GPUクラスターを使います。

![Screenshot 2024-11-22 at 19.37.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/20336184-8b3d-8cf4-f23f-318a2b61fe45.png)


```py
%pip install mlflow==2.17.2 diffusers==0.31.0 
dbutils.library.restartPython()
```

```py
import torch
print(torch.cuda.is_available())
```
```
True
```

```py
from huggingface_hub import login 
login(token="<Hugging Faceのアクセストークン>")
```

```py
from diffusers import AutoPipelineForInpainting
from diffusers.utils import load_image
import torch

pipe = AutoPipelineForInpainting.from_pretrained("diffusers/stable-diffusion-xl-1.0-inpainting-0.1", torch_dtype=torch.float16, variant="fp16").to("cuda")

img_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo.png"
mask_url = "https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo_mask.png"

image = load_image(img_url).resize((1024, 1024))
mask_image = load_image(mask_url).resize((1024, 1024))

prompt = "a tiger sitting on a park bench"
generator = torch.Generator(device="cuda").manual_seed(0)

image = pipe(
  prompt=prompt,
  image=image,
  mask_image=mask_image,
  guidance_scale=8.0,
  num_inference_steps=20,  # steps between 15 and 30 work well for us
  strength=0.99,  # make sure to use `strength` below 1.0
  generator=generator,
).images[0]
```

**img_urlの画像**

![](https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo.png)

**mask_urlの画像**

![](https://raw.githubusercontent.com/CompVis/latent-diffusion/main/data/inpainting_examples/overture-creations-5sI6fQgYIuo_mask.png)

つまり、**img_urlの画像**で、修復すべき(入れ替えるべき)領域を**mask_urlの画像**で指定するということですね。いわゆる[顔ハメ看板](https://ja.wikipedia.org/wiki/%E9%A1%94%E3%83%8F%E3%83%A1%E7%9C%8B%E6%9D%BF)ですね。面白い。

アウトプット画像を保存します。

```py
image.save("./output_image.png")
```

上で指定した`prompt = "a tiger sitting on a park bench"`の通り、ベンチに虎が座ってます。

```md
![](./output_image.png)
```

![output_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/9541dc16-016d-da30-c9b9-370abef3d577.png)

プロンプトを変えてみます。

```py
prompt = "a cat sitting on a park bench"
```

![output_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1168882/34c5ef5a-5d29-6456-c3f4-d01c79127b1e.png)


画像系生成AIも色々なアプリケーションが出てきてますね。

### はじめてのDatabricks

[はじめてのDatabricks](https://qiita.com/taka_yayoi/items/8dc72d083edb879a5e5d)

### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)
異種ネットワークをまたぐエンドポイント間の接続

# 同じタイプのネットワークだと容易に接続できる
>異なるネットワークをまたいで接続する一般的な方法を考えるのは非常に難しい。しかし、プロトコルが異なるネットワーク間を接続できる特殊な場合も存在する。それは送信元と宛先のホストが同じタイプのネットワーク上にあり、その間が異なるネットワークで接続されている場合である。

# トンネリングで解決する
>このルーターがIPv6パケットとIPv4のインターネットを接続するマルチプロトコル・ルーターに送信する。このルーターがIPv6パケットを受け取ると、まず、そのパケットをIPv4ヘッダーでカプセル化する。

>ルーターが、IPv6パケットをIPv4パケットの中にはめこむ。このパケットがロンドンのルーターへ到着すると、本来のIPv6パケットを取り出し、宛先ホストへ転送する

# トンネリングを使ってネットワークをオーバーレイと呼ばれる
>トンネリングは、遠く離れたホストやネットワーク間を、他のネットワークを介して接続する際に広く利用される。その結果得られるネットワークは、ベースとなるネットワーク上にうまく上張りの層を敷いていることから、オーバーレイと呼ばれる。

# トンネリングの欠点
>トンネリングの欠点はパケットがトンネルを途中で抜け出すことができないため、トンネルが通過するネットワーク上のホストへパケットを送ることができない点である。

https://bookplus.nikkei.com/atcl/catalog/23/03/16/00728/

# 感想
異なるプロトコルのネットワークを跨ぐときは異なるプロトコルに包むことで問題を解消する
Claudeにjson出力ぽいことをやらせる

# 前提
claudeのAPIを関数的に組み込んで使いたいとなるとデータ形式が指定されていれば嬉しいシーンは多いと思われる。そんなとき最初に試すのはjson形式ではないだろうか。
残念ながら基本的にClaudeには公式のjsonモードは存在しない。(2024/11/22現在)
一応tool_useを強制することでjson出力を指定することは出来るが…<br>その場合はこの記事を参考とするとよい。
[Amazon Bedrock Converse API で Claude3 の JSON モードを利用する](https://qiita.com/ren8k/items/3d5f66df251703b8407e)

本記事ではtool_useを使わない方法で各属性ごとに出力する方法を検討する。
こちらの方法での気合パースの公式のcookbookが[こちら](https://github.com/anthropics/anthropic-cookbook/blob/main/misc/how_to_enable_json_mode.ipynb)で公開されている。

cookbookでは以下の関数を例示している(他にもいくつか公開している)
```python
    def extract_json(response):
        json_start = response.index("{")
        json_end = response.rfind("}")
        return json.loads(response[json_start:json_end + 1])
```
しかしそれでも確実な方法ではない。
うまくいくときもあるが複雑なjsonを吐かせると
すぐにjson.loadsが動作しなくなり以下のエラーを吐く。

```
json.decoder.JSONDecodeError: Invalid control character at: line * column ** (char **)
```

# 結論
**json出力はパースの観点から非常に不安定なのでCSV出力を使う**。

# 方法
promptに以下の文章を入れればよい。
```txt
以下の要素を含むCSVフォーマットで回答する事
"hoge": "hoge属性の説明"
"fuga": "fuga属性の説明"
CSVのみを出力し、CSVフォーマット以外の文字は一切応答に含めないでください。
```

出力は以下の方法でパース出来る。

```python
import csv
import io

# CSV形式のレスポンスをパース
csv_reader = csv.DictReader(io.StringIO(explanation_response))
response_data = next(csv_reader)
explanation = response_data.get("hoge")
is_correct = response_data.get("fuga")
```
docker compose で VisualBasic を動かせる環境を作成してみた

docker、VBの素人です、WindowsのPCを持っていないのにVBの勉強の必要が出てきたので、
ググりながら環境を作成しました、ご了承ください。

# docker の設定ファイルなど

```yml:docker-compose.yml
services:
  vb_sandbox:
    build:
      context: .
      dockerfile: Dockerfile
    # ホストとゲストの work フォルダを同期
    volumes:
      - ./work:/work
    # 接続したときのデフォルトフォルダ
    working_dir: /work
    # コンテナを起動したままにする
    tty: true
```

```Dockerfile
FROM ubuntu:22.04

# mono と vbnc コマンドを使うためにインストール
#   参考 https://www.mono-project.com/download/stable/#download-lin
#        https://life-is-command.com/mono-vb-1/
RUN apt update && \
    apt install ca-certificates gnupg -y && \
    gpg --homedir /tmp --no-default-keyring --keyring /usr/share/keyrings/mono-official-archive-keyring.gpg --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF && \
    echo "deb [signed-by=/usr/share/keyrings/mono-official-archive-keyring.gpg] https://download.mono-project.com/repo/ubuntu stable-focal main" | tee /etc/apt/sources.list.d/mono-official-stable.list && \
    apt update && \
    apt install mono-devel -y && \
    apt install mono-vbnc -y
```

```vb:work/hello.vb
Imports System

Module test
    Sub main()
        Console.WriteLine("Hello")
    End Sub
End Module
```

# コンテナ実行

```bash
# ホストで実行すると、コンテナが起動
docker compose up -d
# ゲストに入る
docker compose exec vb_sandbox bash

# コンパイル
vbnc hello.vb

# 実行
mono hello.exe 
Hello
```

![スクリーンショット 2024-11-22 19.27.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142910/cf3be82b-21e4-8532-8ead-96b3e670143a.png)
DNSゾーンフォワーダ、ゾーン委任、ゾーン転送メモ

DNSにおける**ゾーンフォワーダ**、**ゾーン委任**、**ゾーン転送**は、名前解決やDNSゾーンの管理に関する異なる機能や手法を指します。それぞれ以下のように説明できます。

---

### **1. ゾーンフォワーダ (Zone Forwarder)**  
特定のDNSクエリを他のDNSサーバーに転送する設定です。  
- **目的**: 自サーバーで解決できない名前解決リクエストを指定したDNSサーバーに転送する。  
- **利用シーン**:  
  - 外部ドメインへの名前解決を特定のサーバーに依存させたい場合。  
  - ファイアウォール内外で分離されたDNSを使用する場合。  
- **動作**:  
  クエリ → ローカルサーバーが解決不可 → 設定されたフォワーダにクエリを転送。  

---

### **2. ゾーン委任 (Zone Delegation)**  
親DNSゾーンが子ゾーンに名前解決の責任を委任する仕組みです。  
- **目的**: 大規模なDNS管理を分散させるため、特定のサブドメインを別のDNSサーバーで管理させる。  
- **利用シーン**:  
  - ドメイン「example.com」の一部である「sub.example.com」を別のDNSサーバーで管理する場合。  
- **動作**:  
  親ゾーンが子ゾーンのネームサーバー(NSレコード)情報を登録 → 子ゾーンが名前解決を担当。  

---

### **3. ゾーン転送 (Zone Transfer)**  
DNSゾーンデータをプライマリDNSサーバーからセカンダリDNSサーバーにコピーする機能。  
- **目的**: 冗長性確保や負荷分散のため、複数のサーバーでゾーン情報を同期。  
- **利用シーン**:  
  - プライマリDNSサーバーが障害発生時に備え、セカンダリサーバーにデータを同期させる。  
- **動作**:  
  - **フルゾーン転送 (AXFR)**: ゾーン全体を転送。  
  - **インクリメンタルゾーン転送 (IXFR)**: 差分のみ転送。  

---

### **まとめ**  
- **ゾーンフォワーダ**: クエリを別のサーバーに転送。  
- **ゾーン委任**: 子ゾーンに管理を委任。  
- **ゾーン転送**: ゾーンデータを他のサーバーと同期。  

適切な設定を行うことで、DNSの効率性、冗長性、分散管理を実現できます。
【Testing Library】userEvent v14

# userEvent v14 にアップデート🎉 v13 との違い
#### 1. 非同期処理と setup() の導入
`userEvent v13`： `userEvent` は直接メソッドを呼び出す形で使用できましたが、非同期の動作や状態の管理に関する細かい部分を管理するのは手動で行う必要がありました。例えば、キーボード入力やクリック操作が複数連続で行われる場合、適切な順序でイベントが発火することを保証するためには、テストの中で追加のコードが必要でした。

`userEvent v14`: 新たに `userEvent.setup()` メソッドが導入されました。これにより、ユーザーインタラクションをシミュレートするためのインスタンスを設定できるようになり、連続する操作の管理が容易になりました。`setup()` を使うことで、キーボードの状態やその他の入力デバイスの状態が維持され、実際のユーザーインタラクションに近い挙動を再現できるようになりました。

#### 2. クリップボードのモック
`userEvent v13`： クリップボード API をテストする方法が提供されていませんでした。

`userEvent v14`： クリップボード API がセキュアなコンテキスト外で利用できない場合でも、`userEvent.setup()` が `window.navigator.clipboard` をスタブに置き換え、クリップボードを操作するテストが可能になりました。

#### 3. 非同期操作の管理
`userEvent v13`： `userEvent.type()` などのメソッドはデフォルトで同期的に動作しますが、`delay` オプションを指定することで遅延を設定することができました。このため、複数のインタラクションが非同期的に動作する場合、手動で非同期操作を管理する必要がありました。

`userEvent v14`： 非同期操作がより一貫性を持って管理されるようになり、`setup()` を使用することで、非同期のタイミングや状態の管理が簡単になりました。これにより、テストがより安定し、予測可能な動作を示すようになりました。

#### 4. テストの推奨方法
`userEvent v13`： `userEvent` のメソッドを直接呼び出すことができますが、テストの中で `before` や `after` フック内で使用することは避けるべきだという注意がありました。

`userEvent v14`： より厳格にテスト内で `userEvent.setup()` を使用することが推奨されるようになり、テストコードの可読性と安定性が向上しました。`setup()` を使用することで、入力デバイスの状態が適切に管理され、複数の連続した操作が正しく再現されます。

#### 5. 移行と互換性
`userEvent v13`： バージョンアップに伴う移行作業がやや手間がかかりました。特に、イベントのシミュレーションに関する挙動が不安定な場合があり、テストコードが壊れることもありました。

`userEvent v14`： `setup()` メソッドを使用することで、バージョン14への移行が容易になり、以前のバージョンとの互換性も高くなっています。`setup()` を使うことで、今後のバージョン変更にも柔軟に対応できるようになりました。

#### 6. 推奨されるテスト方法の変更
`userEvent v13`： より自由にメソッドを使うことができましたが、テストの中でイベントを発火させる順序や状態管理には注意が必要でした。

`userEvent v14`： テスト内で `setup()` を使うことが推奨され、テストの実行順序や状態の一貫性が保たれるようになりました。

# 使い方
① `userEvent.setup()` で初期化する
② ①で初期化した userEvent を使う
![userEvent.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3695365/a978b404-de70-072a-e40d-92d2afb7625d.png)

# 初期化する理由
- ユーザーインタラクションをシミュレートするための初期設定を行う
- 実際のブラウザでのユーザーインタラクションに近い動作を再現する
- インタラクション可能な要素をチェックする
- 非同期タスクを適切に管理し、テストが正確に実行されるようにする

そのため、テスト内で `userEvent.setup()` を呼び出すことは、テストを信頼性高く、かつ実際のユーザーの動作に近い形でシミュレートするために必要である。

# おわりに
下記のページを読んでいて気づきました。

https://testing-library.com/docs/user-event/intro

公式のドキュメントって読まないと知らないことが多いな〜という学びです。
だってだって・・・初期化しなくても普通に使えてるし・・・。
もしかしたらどこかで悪さしてたのかもしれないけど、絶対気付けないや〜つ😇