OmniAICreator/Qiita-1.07M huggingface dataset:
KFold最適化テクニック～StratifiedKFoldを超えろ

# TL;DR

 * KFoldのseedをたくさん試して手元のCVスコアを上げるのは有効なテクニックである
 * それが有効であることの数学的証明はないが、Stratified KFoldという機能の存在が傍証になっている
 * これが効いてSignateCup2024で銀メダル取れた。しかも自分の時間は４時間ほどしか使わなかった。この省エネ入賞は全参加者中でもトップレベルのはず。
 * ただし、データ量が少なめのコンペ向け。実務でどれだけ役立つかは不明、というか役に立たないことのほうが多そう

 # はじめに

機械学習の分野では、KFoldというテクニックがあります。KFoldの基本については他に優れた記事が多数あるので解説はそちらを参照してください。要は、訓練データをうまく分割して独立した複数のモデルを作り、そのアンサンブルを最終的なモデルとすることで成績向上を目指す、というものです。

その派生形で、StratifiedKFoldというのがあります。公式マニュアルによると、

This cross-validation object is a variation of KFold that returns stratified folds. The folds are made by preserving the percentage of samples for each class.

とあります。単に訓練データを分割するのではなく、分割された各Foldにおいて各カラムの分布ができるだけ等しくなるようにする、という制約を課すのですね。

# どんなときに役に立つのか

主に、訓練データの件数が少なくて、適当な乱数でFoldに分割すると特徴量や目的変数の分布に偏りが出やすい場合です。
考えてみれば当然の話で、あるFoldには5%の陽性率、別のFoldでは20%の陽性率、とかではモデルのでき方が全然異なるので、それのアンサンブルを取っても効果は薄いというわけです。これは納得いきます。

訓練データに充分な量があれば適当にFoldに分けても均等になりますが、今回のコンペのように訓練データ件数が数千の量しかないと乱数によって不均衡になることはよくあります。

# 均等分散だけでいいのか？

僕が疑問を持ったのはここです。適当な乱数でFoldを作り、特徴量は均等に分布していてもFold間の「件数だけを見ていてはわからない偏り」によっては、偏ったモデルができて成績は不振になるのではないか？という疑問です。実際、各Foldの挙動を見ると、同じ入力でもけっこう違う値を出すことが見られます。

逆に、すべての特徴量がFold間で**完全に均一で均衡**していると、どのFoldでも同じモデルになってしまってアンサンブルの効果が出ません。アンサンブルの効果が出るのは**均衡と不均衡の中間のどこかの「ちょうどいい」地点**にあるはずなのです。単に陽性率が均等、とかだけではなく、分布の仕方が手元のCVに絶妙に効く「**秘孔**」があるのではないか？　これが発想です。このへんの意味合いは言葉では伝えづらいところがあるのでもどかしいですが。

# Super Seedを探せ！

僕はこの戦術をとりました。Stratified KFoldに任せきりでなく、究極のKFoldが出てくるようなseedを自力で探索するのです。

 * ある範囲(例えば1～100万)の乱数シードについて、それぞれでKFoldを作成する(この個数が適切かはよくわかりません。自分の時間的都合で適当に決めました。）
 * 各Foldの陽性率が均衡していないシードをはじく
   - これは計算量削減のためです。StratifiedKFoldに採用されないようなseedは無条件で弾いていきます。僕は事前のちょっとしたリサーチで、陽性率の均衡率でトップ1%に入る乱数シードだけを次のステップに進ませました。
 * この一次選考を通過した乱数シード(約１万個)によるKFoldで実際にモデルを作り、訓練データに対するスコアを計算していく。
 * 訓練データでベストスコアを出した乱数シード(これをSuper Seedと名付けました)を使ってできたモデルをsubmit候補とする。

　これが意味のある行為なのか、はかなり悩みました。特に、この試行錯誤の序盤で見つかった乱数シードがずっと自分の中ではベストスコアを叩きだしてたので、単なる偶然にしてはできすぎてるのではないかという疑問が生じ判断に乱れが出ました。ですが考察した結果、これはハイパーパラメータの最適値の探索の一種に過ぎない、というのが結論です。ハイパーパラメータの最適値探索でいろいろ試し、ハイスコアのものを採用するのは基本的な手順です。

　また、KFoldで分割した評価用データは評価値の計算にしか使わない、というルールを愚直に守っていればリークにはなりようがないですから、これも自信になりました。リークしてないことが確信できるならセコい手段でもいいんです。

　繰り返しますが、これは全体の訓練データが静的かつ量が少なく、一方でモデルの精度を追求する場合にしか使えないテクニックです。コンペ専用といってもいいでしょう。
ユニークな値を作成する方法

### 前提
typeScrypt,Vue.jsを扱っていた時の話。
ほぼメモに近いです
こんな感じで生成できるんかって言う忘備録

### 経緯
for文のなかで一意になる値を何かしら指定しなければいけない状況だったが、保持しているオブジェクトでは一意な値は作成できず外部で作成したものを無理矢理注入するしかないと言う意見に至った。
👆できるならこの考え方はやりたくなかったがクリティカルなバグだったため、暫定的に対応しようとしていた。


## 方法
### Math.random(）
以下のような感じにすると１００までの乱数が作成できる。
（好きな範囲を設定してください）
```.js
Math.floor(Math.random() * 100) + 1;
```

### Crypto: randomUUID()
これは便利だなって思いましたね。
ライブラリ必要とせずにできるし、nodeの環境であれば使えるのは助かりますね。
ただ、これに関してはクライアント側で使えないと言うことがわかったので、今回の場合だと採用できなかった感じ
また「https」のような安全な環境でしか使えない。
https://developer.mozilla.org/en-US/docs/Web/API/Crypto/randomUUID
```.js
randomUUID()
```

### Web Crypto API
これは一つ前に紹介したやつCrypto: randomUUID()が使えなかったときはこっちを使ってみて、みたいなやつをみて知った書き方は以下
```.js
const createUUID = (length: number) => {
  const array = new Uint8Array(length);
  return window.crypto.getRandomValues(array).join(“”);
};
```


## 結果
要件的に不要になったのでそもそも使わなかった
もっと色々な方法があると思うな
どれがベストかは全くわからないが、node環境であるならCryptoを使うのがいいのかなと個人的には思った
Docker で使えるコマンドをまとめてみた。

Dockerは、開発環境の構築やデプロイメントを効率化するための強力なツールですね。この記事では、Dockerを使った基本的なコマンド操作を中心に、実践で使えると思ったコマンドをまとめます。

### Dockerイメージの操作
Dockerイメージは、アプリケーションやサービスを動かすためのテンプレートです。公式のイメージはDocker Hubから取得できますが、利用が少ないイメージには注意が必要です。以下のコマンドを使用して、イメージの管理を行うことができます。

**イメージのダウンロード**  
任意のイメージをダウンロードするには、以下のコマンドを使います。

```bash
docker image pull ubuntu
```
**イメージの削除**
使用しなくなったイメージは、ディスクスペースを節約するために削除をしましょう。
```bash
docker image rm ubuntu
```

**イメージの一覧表示**
ローカルに保存されているイメージを確認するには、以下のコマンドを実行します。
```bash
docker image ls
```

### Dockerコンテナの操作
Dockerコンテナは、イメージから作成される実行環境です。コンテナは、アプリケーションが実際に動作するプロセスであり、その起動や停止、削除などを管理することができます。以下に、基本的なコンテナ操作コマンドを紹介します。

**コンテナの起動**
ダウンロードしたイメージを基に、新しいコンテナを作成し起動するには、以下のコマンドを使用します。例えば、hello-worldというシンプルなイメージを使用してコンテナを起動する場合:
```bash
docker container run  my_image
```

**起動中のコンテナ一覧表示**
現在起動中のコンテナを確認するには、以下のコマンドを使用します。従来のdocker psも使用可能ですが、最新の形式ではdocker container lsを推奨します。
```bash
docker container ls
```

**コンテナの停止**
実行中のコンテナを停止するには、以下のコマンドを使用します。例えば、my_containerという名前のコンテナを停止する場合:
```bash
docker container stop my_container
```

**デタッチモードでのコンテナ起動**
コンテナをバックグラウンドで実行するには、デタッチモードで起動します。以下のコマンドでは、-dオプションを使用しています。
```bash
docker container run -d my_image
```
デタッチモードを利用することで、コンテナがバックグラウンドで実行され、シェルを占有しないため、他の作業を行いながらコンテナの実行状況を管理できます。

**コンテナ内に入る**
実行中のコンテナ内に入り、直接コマンドを実行するには、以下のコマンドを使用します。例えば、my_containerという名前のコンテナ内に入りたい場合:
```bash
docker container exec -it my_container bash
```

新しいコンテナをBashで起動
イメージを使用して、新しいコンテナをBashシェルで起動するには、以下のコマンドを使用します。
```bash
docker container run -it my_image
```


### 終わり
この記事では、Dockerの基本的なコマンド操作について解説しました。これらのコマンドを習得することで、Dockerを使った開発環境の構築や管理が効率的に行えるようになります！他にも必要なコマンドはあると思うので、習得していきたいですね。

最後まで読んでいただき、ありがとうございました。
AWS Certified AI Practitioner Beta（AIF-C01） 受験記

## はじめに

AWS Certified AI Practitioner(AIF)のBeta試験を受験してきたので、勉強内容と試験の感想を書いていきます。

## 認定試験について
**◆概要**

「AWS Certified AI Practitioner」とは、2024年8月27日からBeta試験が公開された、AI関連の新しいAWS認定試験です。

https://aws.amazon.com/jp/certification/certified-ai-practitioner/

**◆試験情報**

Beta試験のため同カテゴリの「AWS Certified Cloud Practitioner（CLF）」とは問題数や試験時間が異なる点に注意が必要です。

問題数：85問
試験時間：120分
料金：75 USD/10,000 円
言語：英語/日本語

## 試験対策
**①試験ガイド**

まずは試験ガイドに一通り目を通します。

https://d1.awsstatic.com/ja_JP/training-and-certification/docs-ai-practitioner/AWS-Certified-AI-Practitioner_Exam-Guide.pdf

試験ガイドに出てくる用語やAWSサービスについて説明することができるようであれば、追加で勉強を行う必要はないかと思います。

**②AWS Skill Builder**

試験ガイド内で不明な用語があった方や取り敢えず網羅的に学習を進めたい方向け。

https://explore.skillbuilder.aws/learn/public/learning_plan/view/2193/plan

受講時間は18時間で基本的なAI関連の用語やAWSサービスについて学ぶことができます。
受講にあたってSkill BuilderのIDが必要になりますが、無料での受講が可能です。

**③模擬試験**

現時点での理解度の確認に役立ちます。

https://explore.skillbuilder.aws/learn/course/external/view/elearning/19790/exam-prep-official-practice-question-set-aws-certified-ai-practitioner-aif-c01-english

②と同様にSkill BuilderのIDが必要になります。
言語は英語のみのため、苦手な方は翻訳を活用してみてください。

## 感想

普段の業務の中でAI関連の用語やAWSサービスに触れることがないため、個人的にはCLFよりも難易度が高かったように感じました。
ただ、上述のskillbuilderのコンテンツでその辺りの内容については一通り網羅されていて、わかりやすかったためスムーズに学習を進めることができました。

## 受験結果

ぎりぎりでしたが、合格することができました。

![スクリーンショット 2024-09-02 103925.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3637963/809ad71a-49a3-1ecd-fddc-4ced700e847d.png)

## さいごに

あまり勉強時間がとれなかったものの合格出来てよかったです。（AWS Skill Builderのおかげです。）
もう一つの新試験である「AWS Certified Machine Learning Engineer - Associate」も後日受験してみようかと思っているので、引き続き勉強していきたいと思います。
(QA) IBM i Db2 for i のRCACの数値カラムをヌル値でマスクしたい

[こちらの記事](https://qiita.com/gomAnomalocaris/items/2fe6c4a8333d7511b536)の続編です。

## お題：Db2 for i のRCACの数値カラムをヌル値でマスクしたい

回答サンプルは下記のようになります。

ユーザー GOMA2 は売掛残TKUZANカラムをヌル値で返す、それ以外のユーザーはTKUZANカラムの値を返す。

```
CREATE PERMISSION
GOMALIB.P8 ON GOMALIB.TOKMSPRCA4
FOR ROWS WHERE CURRENT_USER = 'GOMA2 '
ENFORCED FOR ALL ACCESS ENABLE ;

CREATE MASK GOMALIB.M4 ON GOMALIB.TOKMSPRCA4 AS TOKMSPRCA4
FOR COLUMN TKUZAN
RETURN
CASE WHEN ( CURRENT_USER = 'GOMA2 ' )
THEN
  NULL
ELSE
  TOKMSPRCA4.TKUZAN
END
ENABLE;

ALTER TABLE GOMALIB.TOKMSPRCA4 ACTIVATE COLUMN ACCESS CONTROL ;
```

ポイントは下記黄色部のCASE式の書き方です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/bb882d30-4dd8-6404-13f2-33c7dea676a1.png)


## Db2 for i CASE式のマニュアル

IBM i infoセンター7.5　：　https://www.ibm.com/docs/ja/i/7.5?topic=expressions-case-expression

上記ページの下記の記述がサンプルSQL文の黄色個所に該当するようです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/c8ef67ba-b99e-521c-a0f3-413de26b3f71.png)

## マスク値としてヌル値をしていたカラムへのアクセス結果例

**Excelからマスク指定されたユーザーIDでODBC接続**

空欄のカラムとして表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/a44fc0f3-a3d9-2cf8-8e42-58b7aec2a41e.png)

**QUERY for i**

'-' と表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/6dbf60da-eb40-c7dd-76c6-30a52358e9d6.png)


CASE式、色々な指定ができるようです。
プリザンターMySQL対応＋日本MySQLユーザ会と交流

株式会社インプリムのhmineです。

最近は、**OSSのノーコード・ローコード開発ツール【プリザンター】のMySQL対応**を手掛けています。この記事では、プリザンターのMySQL対応について情報共有と、このプロジェクトを通して私が感じたことをお話します。

# 🎊プリザンターのMySQL対応について🎊

プリザンターは、OSSとして公開しているソースコードを、利用者自身が用意したサーバにセットアップして使うことができる、Webアプリケーションです。

https://github.com/Implem/Implem.Pleasanter

:::note info
👉GitHubのスターもいただけるとよろこびます。
:::

今までプリザンターでは、以下の2種類からDBMSを選択することができました。
- SQLServer
- PostgreSQL

**このたび、DBMSのラインナップにMySQLを追加します**！🎉🎉🎉🎉🎉

本記事の公開から約1か月半後の、2024年10月8日（火）にリリースします！📅
対応するMySQLのバージョンは8.4以降です。

プリザンターのツールとしての強みは、カスタマイズ性が高く、外部アプリケーションとの連携機能が充実していることです。Qiita上でも [#プリザンター](https://qiita.com/tags/%e3%83%97%e3%83%aa%e3%82%b6%e3%83%b3%e3%82%bf%e3%83%bc) 、 [#Pleasanter](https://qiita.com/tags/pleasanter) のタグでたくさん活用事例が登録されています。得意分野を生かすアイデアが豊富だと感じます。

私自身が好きなのは、**プログラミング学習者としてプリザンターのGitHubを見てみること**です。業務アプリ規模の巨大なソースコードに個人でも触ることができます。独特なデータベース設計も面白いポイントです。

## 📝関連記事
- [プリザンターをVisual Studioでデバッグする](https://qiita.com/Implem/items/f0704ebaee8f061fc94a)
- [Pleasanterで楽しく学ぼうC# (1)](https://qiita.com/hmine-implem/items/f2e26d58b5a36eb7f5a0)

# 🤝日本MySQLユーザ会のみなさまと交流しました🤝

株式会社インプリムのイベントスペース【Pleasanter Lounge】は、ITのイベントの会場として無料で使っていただくことができます。
2024年8月29日（木）には、**日本MySQLユーザ会のみなさまのオフライン交流会**の会場として、ご利用をいただきました。

https://mysql.connpass.com/event/325749/

私自身は、会場スポンサー枠でLTのお時間をいただき、その場をお借りして**MySQL対応の情報解禁**をさせていただきました🙇‍♀️🙇‍♀️🙇‍♀️
また、技術トークとしてPleasanterのマルチDBMS対応や、MySQL対応に関わるエピソードもお話しました。交流会でフィードバックもいただきまして、ありがとうございます。

交流会では、MySQLのプロの方々の思考の凄さを実感しました。みなさんが日頃、技術の探求をどんな観点で進めているのか、ということを間近で拝見しました！
- 新バージョンが出たらキャッチアップ
- MySQLの機能をすみずみまで使い倒す
- 巨大なシステムの運用に堪えるDB構成の探求
- 生成AIと連携
- GIS（地理情報システム）で技術活用

今までオンラインの技術イベントによく耳で参加をしていましたが、今回はオフライン参加することで会話の機会が増えて、ひと味違った体験になりました。

すごいイベントの場にいたのだなあと、この記事を書きながら改めて実感しています。
ですが、いつも一緒に仕事をしている仲間が見守ってくれたので、あまりプレッシャーは感じませんでした。

いろいろな意味で有難みを感じた時間でした！

## 🍻ラウンジの情報

https://www.pleasanter.org/forms/pleasanter-lounge-reservation

:::note info
👉Pleasanter Loungeの情報はこちらです。
:::

# 🛠️MySQL対応をした感想🛠️

「「「**とにかくMySQL対応版を出すぞ！！！**」」」の精神で開発しました。
**MySQL版を出すことでプリザンターを知ってもらう**、という一つの目的を実現するための開発プロジェクトでした。

株式会社インプリムの製品開発チームは「サポートに寄せられたお声にすばやく対応する」というコミュニケーション重視の開発や、「品質や機能の妥当性を分析し、将来の計画を立案する」というロジカルな開発も進めていますが、今回のMySQL対応のような一つの目的を原動力に開発を進める側面もあり、様々な顔をもつ開発チームだと感じます。

## 📝MySQL対応でやったことを今後公開します！

MySQL対応チーム（開発：hmine／レビュー：[@pierusan2010さん](https://qiita.com/pierusan2010)）で対応した主な作業は、SQL文をMySQLで実行可能な構文に修正したことです。
単一DBMSに向き合うシステムの現場に在籍していたときには気付きづらかったですが、**SQL文にはDBMSごとの色がかなりある**のだと思いました。

また、プリザンターのDB処理の特徴は、ORマッパーを使わずに独自のSQL実行処理を記述していることです。MySQL対応では、プリザンターでSQLServerとPostgreSQLのマルチDBMSを今まで実現していた仕組みや、SQL実行処理のソースコードを調べる機会がありました。

プリザンターはソースコードを公開しています。こちらのQiita上でプリザンターのコードの解説する記事を、開発のかたわら準備中です。
MySQL対応のリリース以降も、MySQLでプリザンターをより使いやすくする探求を日々重ねていきたいと思います。

# 🙌おわりに🙌

プリザンターのMySQL対応は、2024年10月8日リリースです！
リリース準備を進めつつ、技術情報について掘り下げて発信できればと思います。
Jupyter でもコードフォーマットしたい！ on Docker

昨今のプログラミングでは自動でコード整形されるのは当たり前で、人間が手でコードを綺麗にしたりレビューで指摘したりといった作業は過去のものである。Python においても black + isort や Ruff といったツールが盛んに使用されており、IDE と連携することで簡単に自動フォーマットを実現できる。

...そんなプログラミング環境に慣れていると、たまに Jupyter Lab を触ったときに「フォーマットされねぇのかよ〜」となってしまう。自分が Jupyter を使うときはたいてい書き捨てのコードなのでそこまできれいに書く必要ないのだけど、それでも自動フォーマットしてくれたほうが読みやすいし気分もいい。

## JupyterLab Code Formatter
なんと JupyterLab Code Formatter[^jupyterlab-code-formatter] という便利パッケージがあり、これを入れてちょっと設定するだけで簡単に実現できる。

[^jupyterlab-code-formatter]: [JupyterLab Code Formatter](https://jupyterlab-code-formatter.readthedocs.io/)

``` console
pip install jupyterlab-code-formatter black isort
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/16369/1fe9abd6-010b-90c7-5844-98e43a752878.png)

## on Docker
Jupyter Lab を Docker で立ち上げる場合は Settings から変更してもコンテナ再作成時に元に戻ってしまうので、コンテナイメージをビルドするときに設定を埋め込みたい。

``` json:jupyterlab_code_formatter.json
{
  "preferences": {
    "default_formatter": {
      "python": ["isort", "black"]
    }
  },
  "black": {
    "line_length": 120
  },
  "isort": {
    "line_length": 120
  },
  "formatOnSave": true,
  "suppressFormatterErrorsIFFAutoFormatOnSave": true
}
```

例えばこんな設定ファイルを用意しておいて、適切な場所に配置するように Dockerfile を書けばよい。

``` Dockerfile:Dockerfile
RUN pip install jupyterlab-code-formatter black isort
COPY jupyterlab_code_formatter.json /root/.jupyter/lab/user-settings/jupyterlab_code_formatter/settings.jupyterlab-settings
```

この「どこに配置したらいいか」を調べるのがちょっと面倒だった。(どうやって調べたのか忘れた)

また、 `root` ではないユーザで Jupyter Lab を実行する場合は配置するパスが違いそうだけど、おそらく `/root/` の部分を実行するユーザのホームディレクトリに変えてやればいけると思われる。
Google Apps Script (GAS)を使ってLINEから入力できる家計簿を作った話

# はじめに
初めまして！f4samuraiサーバサイドエンジニアの田中です。
今回は、社内の第4回[技術記事コンテスト](https://qiita.com/f4samurai/items/e5c246b522ce3172d343)で優秀賞をもらった記事を紹介します。


技術記事コンテストには毎回テーマがあります。
僕が受賞した第4回のテーマは **「今までで一番の作り物」** でした。

仕事で作ったものはBtoBサービスが多く書きづらかったのでプライベートで作った一番のもの(というよりただ直近作ったものかも)を紹介します。

# 家計簿サービスを作ったきっかけ
家計簿の管理、皆様はどうされていますでしょうか？

もともと我が家では家計簿を共有できる良い感じのアプリがなかったので、Googleフォームから日々の買い物内容を入力しそれをGoogleスプレッドシートで管理していました。

僕はこれで十分だったのですが、同居人から「ブラウザを開いてフォームに入力するのは面倒だからLINEとかでできるようにしてほしい」という要望がありました。

ちょうど「[【LINE】Messaging API＋スプレッドシートで同棲生活の家計計算を自動化](https://qiita.com/kiwi_btmnc/items/02a754e6dc102f1c7da8)」という、ほぼ同じようなことをされている方の記事を見つけたので、今回は+α でCI/CDを含めた仕組みを作ったことを共有します。

# サービスの概要
- 下記のようにLINE上から入力した家計簿情報をLINE Messaging API,Google Apps Script(以下GAS)を経由してスプレッドシートに登録させる
- 加えてCI/CDを行い、ログをGCP Cloud Loggingにためてアラート発報をSlackに連携する

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3190574/c0cb562c-6dbe-42f6-0502-62de25fae79b.png)

入力は、次のルールで行います。
> 1行目:カテゴリ
2行目:金額
3行目:購入日(yyyyMMDDやdd、今日、昨日とかの文字列)
4行目:購入方法
5行目:メモ

下記のようにカテゴリなど一部データはスプレッドシートで入力規則を指定しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3190574/8a0b2cd8-7e08-46a0-0117-8bfde4d395e6.png)

LINEに入力された内容は、スプレッドシートに連携されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3190574/c3c00118-dc09-4e2f-163a-5e71a40fa517.png)

スプレッドシートでは分析など各種管理を実行することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3190574/812b504f-1f3f-fcec-48c4-32fa667b57cb.png)

データの削除もLINEから実行することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3190574/7d2b2280-029d-a22d-cfd1-9461753496d0.png)

# CI/CDの導入
GASは開発・実行環境がいらず簡単なスクリプトだとブラウザ上でコードを書いてそのままデプロイできる気軽さが大きなメリットだと思いますが、

1. GitHubと直接連携してソース管理ができない
2. テストを実行する基盤がない

といったデメリットがあります。

今回のサービスを作っていくと保守性を高める上で無視できないレベルのコード量になりそうだったので下記方法で解消しました。

### 1. GitHubとの連携
GitHubとの連携のためこちらの仕組みを導入しました。

https://github.com/google/clasp
claspとはGoogle製のOSSツールで、GASをローカルで開発・管理できるツールです。

ReadMeにもありますが、clasp pushなどのコマンド操作で簡単にGASにデプロイできる点、個人的に読みやすく保守のしやすいTypeScriptで書いたコードをGASのスクリプトファイル形式gsに変換してくれる点で便利でした。

また、ローカルでエディタを用いた開発や、GitHubでソースコード管理もできるようになります。

### 2. テスト
claspを入れたことでコードをTypescript化できたので、JavaScipt用のテストフレームワーク[Jest](https://d.hatena.ne.jp/keyword/%A5%D5%A5%EC%A1%BC%A5%E0%A5%EF%A1%BC%A5%AF)でテストを作成いたしました。

また、claspもJestも特に複雑な設定をすることなくそのままパイプライン上でも使えるので、mainブランチへのマージなどを契機にテストやデプロイまでできるようにしました。

# 今後とまとめ
このサービスを運用してかれこれ１年近く経っているのですが、最近同居人から

- 金額とメモ以外の入力が面倒くさい（LINEのリッチメニューでボタンぽちぽち形式にできそうだけどメニュー数の上限があって難しい）
- というか入力そのものが面倒くさい（Google Vision APIとかでレシート画像からテキスト変換できそう）

という要望がきており、「面倒くさい」という感情はこういう何かを生み出すアイデアに直結するエンジニアにとって大事な資質でもあるなと改めて思いました。


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



---------------------------------------


**▼ f4samuraiについて**
<script async class="speakerdeck-embed" data-id="6b71c7f8b917480e899f7a1f9daffef8" data-ratio="1.77777777777778" src="//speakerdeck.com/assets/embed.js"></script>
Python Tkinter ツールチップ (マウスホバーでメッセージ表示）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/423803/46cc92fd-102b-57b9-4fc2-d730bf76d747.png)


```
import tkinter as tk

class ToolTip:
    def __init__(self, widget, text):
        self.widget = widget
        self.text = text
        self.tooltip_window = None

        self.widget.bind("<Enter>", self.show_tooltip)
        self.widget.bind("<Leave>", self.hide_tooltip)

    def show_tooltip(self, event):
        if self.tooltip_window or not self.text:
            return

        x, y, _, _ = self.widget.bbox("insert")
        x += self.widget.winfo_rootx() + 25
        y += self.widget.winfo_rooty() + 25

        self.tooltip_window = tw = tk.Toplevel(self.widget)
        tw.wm_overrideredirect(True)
        tw.wm_geometry(f"+{x}+{y}")

        label = tk.Label(tw, text=self.text, justify='left',
                         background='#ffffe0', relief='solid', borderwidth=1,
                         font=("tahoma", "8", "normal"))
        label.pack(ipadx=1)

    def hide_tooltip(self, event):
        if self.tooltip_window:
            self.tooltip_window.destroy()
            self.tooltip_window = None

root = tk.Tk()
root.title("Tkinter Tooltip Example")


#--------------------
# label ウィジェットを作成
label1 = tk.Label(root, text="ツールチップ例　ここにマウスカーソルを合わせるとメッセージが表示されます")
label1.pack(pady=10, padx=10)

# labelにツールチップを設定
ToolTip(label1, "ツールチップの例です")

#--------------------
# Entry ウィジェットを作成
entry1 = tk.Entry(root)
entry1.pack(pady=10, padx=10)

# Entry1にツールチップを設定
ToolTip(entry1, "ここにテキストを入力してください")


#--------------------
root.mainloop()

```
AWS IAMポリシーをわかりやすく解説～Part2～

## はじめに

前回は、AWSのセキュリティの要となるIAMポリシーの基本的な仕組みについて解説しました。今回は、IAMポリシーのさらなる活用を可能にする**条件 (Condition)** について詳しく解説していきます。

### 条件 (Condition) とは？

条件とは、IAMポリシーがいつ有効になるかを指定する追加のルールです。条件を追加することで、ポリシーをより細かく制御し、セキュリティを強化することができます。

例えば、特定のIPアドレスからのアクセスのみ許可する、特定の時間帯のみアクセスを許可する、といった条件を設定できます。条件は、**条件キー**と**条件値**のペアで構成されます。

### 条件キー (Condition Key) とは？

条件キーは、アクセス要求に関する情報 (認可コンテキスト) 内の特定の属性を指し示します。例えば、アクセス要求を行ったユーザーのIPアドレス、リクエストされたリソースのタグ、リクエストされたアクションなどです。

### 条件値 (Condition Value) とは？

条件値は、条件キーの値と比較される値です。例えば、条件キーが `aws:SourceIp` (アクセス要求を行ったユーザーのIPアドレス) であれば、条件値には具体的なIPアドレスを指定します。

### 条件演算子 (Condition Operator) とは？

条件値を比較するためには、**条件演算子**を使用します。主な条件演算子には以下のようなものがあります。

* **Equals:** 完全一致
* **Like:** ワイルドカードによる一致
* **StringEqualsIgnoreCase:** 大文字小文字を区別しない文字列一致
* **NumericEquals:** 数値一致
* **DateEquals:** 日付一致
* **IpAddress:** IPアドレス一致

### 条件の活用例

条件を活用することで、よりきめ細かいアクセス制御を実現することができます。以下は条件の活用例です。

* **特定のIPアドレスからのアクセスのみ許可する:**

```json
{
  "Condition": {
    "IpAddress": {
      "aws:SourceIp": ["192.168.1.10", "10.0.0.1"]
    }
  }
}
```

* **特定の時間帯のみアクセスを許可する:**

```json
{
  "Condition": {
    "DateEquals": {
      "aws:CurrentTime": ["2023-03-15T10:00:00Z", "2023-03-15T18:00:00Z"]
    }
  }
}
```

* **特定のタグを持つリソースへのアクセスのみ許可する:**

```json
{
  "Condition": {
    "StringEqualsIgnoreCase": {
      "aws:ResourceTag/Department": ["Finance", "Marketing"]
    }
  }
}
```

### まとめ

条件は、IAMポリシーの柔軟性を高め、セキュリティを強化するための重要な要素です。条件を活用することで、よりきめ細かいアクセス制御を実現し、AWS環境のセキュリティを強化することができます。

### 関係資料
[IAM Policy Evaluation Series: Conditions](https://youtu.be/4PJienr4gZI?si=FU7MHYLfN1a8f3lX)
AWS Managed Microsoft ADを利用する際の注意点

## はじめに
皆さんはAWSでActive Directoryを利用しようとされたことはありますか？
今回は、AWSでActive Directoryを利用することができるサービスの1つであるAWS Managed Microsoft ADについて、利用する際の注意点を紹介します。

## AWS Managed Microsoft ADの概要
AWS Directory Serviceの1つである、「AWS Managed Microsoft AD」は、実際にWindows Server上に構築したActive Directory環境を提供するマネージドサービスです。

StandardとEnterpriseの2つのエディションに分けられます。

### 基本料金
Standard(オブジェクト数：30,000)：$0.146 /時間当たり
Enterprise(オブジェクト数：500,000)：$0.445 /時間当たり
※2台構成(最小)のドメインコントローラーの料金となります。

### メリット
* 実際のMicrosoft Active DirectoryをAWS環境でそのまま利用できます。
* デフォルトでマルチAZに冗長化されているため、高い可用性を備えています。
* マネージドサービスのため、OSメンテナンス等の運用作業が不要です。


### 注意点
* ドメインにユーザを追加する等の管理作業のために、管理用インスタンス（EC2）が必要となります。
    * 必要時のみ起動することで、コスト削減が可能です。

* オンプレミスのActive DirectoryをAWS Managed Microsoft ADに移行する場合、移行ツールがAWSから提供されていません。
    * Microsoftから提供されているActive Directory移行ツールキッド（ADMT）を利用することは可能です。

* 「administrator」や「Domain Admins」などの従来のActive Directoryにて提供されているユーザやグループが使用できません。
    * administratorに相当するユーザとして「admin」が、AWS Managed Microsoft AD で管理用に最初から用意されています。
    * Domain Adminsに相当するグループとして「AWS Delegated Administrators」が用意されており、同等の権限をADユーザに付与可能です。

* Default Domain Policyの変更ができません。
    * PSO(Password Setting Object)を利用したパスワードポリシー運用を行うことが推奨されています。


## 最後に
AWS Directory Serviceの特徴についてまとめてみました。
ADは重要な要素なため、ユースケースに合っているか確認し、適切なサービスを選択しましょう。
Mecabのインストール手順

## Mecabってなに？
- 形態素分析を行ってくるオープンソースのツールです
- 形態素分析は自然言語処理の手法の１つで、生成AIブームもあり注目されています
- 文章を解析して、名詞や動詞などの要素の単位に分解してくれる
- 京都大学情報学研究科と日本電信電話株式会社コミュニケーション科学基礎研究所が共同で開発した
- perl/ruby/python/java/C#に対応している
- ChaSenやKAKASIといった他ツールを比べて高速

## 環境
Ubuntu20.4

## Mecabのインストール手順
インデックスファイルを最新にします。

```
$sudo apt-get update
```

インストールするのに必要なライブラリは、ググるとサイトによって色々な情報が散見されます。

>mecab libmecab-dev mecab-ipadic-utf8

こちらの３つだけでOKにしているサイトもいくつかありますが、一番多くのライブラリ（６つ）をインストールしている手順を参考にしました。

```
$sudo apt-get install libmecab2 libmecab-dev mecab mecab-ipadic mecab-ipadic-utf8 mecab-utils
```

**mecab-ipadic-NEologd**という新出単語や固有表現に強いという辞書もインストールします。適当なディレクトリを作成して、そこにダウンロードします。

```
$mkdir download
$git clone --depth 1 https://github.com/neologd/mecab-ipadic-neologd.git
```

ダウンロードしたディレクトリに移動して、インストール用のスクリプトファイルを実行します。

```
$cd mecab-ipadic-neologd
$./bin/install-mecab-ipadic-neologd -n -a
```

オプションnを付けると、インストールと同時に、**mecab-ipadic-NEologd**が最新版にアップデートされます。オプションaを付けると、すべてのseedファイルがインストールされるようになります。オプションの詳細は、スクリプトファイルの中に書かれています。

インストールはなぜかわりと時間がかかります。ご丁寧にカウンターが走ります。

途中で、mecab-ipadic-NEologdをインストールするか聞いてくるので、yesを入力します。

インストールされたことを確認します。

```
$which mecab
/usr/bin/mecab
```

git cloneでダウンロードしたファイルは削除しておきます。

```
$rm -rf download
```

動作確認です。

```
$ mecab -d /usr/lib/x86_64-linux-gnu/mecab/dic/mecab-ipadic-neologd
UbuntuにMecabのインストールができました。
Ubuntu  名詞,固有名詞,一般,*,*,*,Ubuntu,ウブントゥ,ウブントゥ
に      助詞,格助詞,一般,*,*,*,に,ニ,ニ
Mecab   名詞,一般,*,*,*,*,*
の      助詞,連体化,*,*,*,*,の,ノ,ノ
インストール    名詞,一般,*,*,*,*,インストール,インストール,インストール
が      助詞,格助詞,一般,*,*,*,が,ガ,ガ
でき    動詞,自立,*,*,一段,連用形,できる,デキ,デキ
まし    助動詞,*,*,*,特殊・マス,連用形,ます,マシ,マシ
た      助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
。      記号,句点,*,*,*,*,。,。,。
EOS
```

## 参考

http://kakedashi-xx.com:25214/index.php/2020/08/25/post-364/

公式サイト
https://taku910.github.io/mecab/#usage-tools

## Rubyでmecabを使う

Rubyでmecabを使うには、nattoというgemを使うようです。
Gemfileにgemを追加して、インストールします。

```Gemfile
gem 'natto'
```

```
$bundle install
```

サンプルコードです。

```hoge.rb
require 'natto'

txt = "本日は晴天なり。"
natto = Natto::MeCab.new
natto.parse(txt) do |n|
    puts "#{n.surface}: #{n.feature}"
end
```

このような結果になります。

```
$ruby hoge.rb
本日: 名詞,副詞可能,*,*,*,*,本日,ホンジツ,ホンジツ
は: 助詞,係助詞,*,*,*,*,は,ハ,ワ
晴天: 名詞,一般,*,*,*,*,晴天,セイテン,セイテン
なり: 助動詞,*,*,*,文語・ナリ,基本形,なり,ナリ,ナリ
。: 記号,句点,*,*,*,*,。,。,。
: BOS/EOS,*,*,*,*,*,*,*,*
```
UbuntuにROS2をインストールしたときのメモ

# 背景・目的
以前、下記の記事でROS2のBasic Conceptを整理しました。
今回は、ROS2の環境を構築してみます。

https://qiita.com/zumax/items/5f638645fda99b75b82a

# 実践
## 前提
- MacOS
- UTM※1

※1 UTMのセットアップは、下記の記事をご確認ください

https://qiita.com/zumax/items/8090f59fc2b779af55be

## System setup
下記を基に整理します。

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

### Set locale

1. localeを確認します
    ```bash    
    sudo apt update && sudo apt install locales
    sudo locale-gen en_US en_US.UTF-8
    sudo update-locale LC_ALL=en_US.UTF-8 LANG=en_US.UTF-8
    
    $ locale
    LANG=C.UTF-8
    LANGUAGE=
    LC_CTYPE="C.UTF-8"
    LC_NUMERIC="C.UTF-8"
    LC_TIME="C.UTF-8"
    LC_COLLATE="C.UTF-8"
    LC_MONETARY="C.UTF-8"
    LC_MESSAGES="C.UTF-8"
    LC_PAPER="C.UTF-8"
    LC_NAME="C.UTF-8"
    LC_ADDRESS="C.UTF-8"
    LC_TELEPHONE="C.UTF-8"
    LC_MEASUREMENT="C.UTF-8"
    LC_IDENTIFICATION="C.UTF-8"
    LC_ALL=
    $ 
    ```

### Enable required repositories
ROS 2 apt リポジトリをシステムに追加する必要がある。

1. Ubuntu Universe リポジトリが有効になっていることを確認します
    ```bash
    sudo apt install software-properties-common
    sudo add-apt-repository universe
    ```
1. apt を使用して ROS 2 GPG キーを追加します
    ```bash
    sudo apt update && sudo apt install curl -y
    sudo curl -sSL https://raw.githubusercontent.com/ros/rosdistro/master/ros.key -o /usr/share/keyrings/ros-archive-keyring.gpg
    ```

1. リポジトリをソース リストに追加します
    ```bash
    echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/ros-archive-keyring.gpg] http://packages.ros.org/ros2/ubuntu $(. /etc/os-release && echo $UBUNTU_CODENAME) main" | sudo tee /etc/apt/sources.list.d/ros2.list > /dev/null
    ```
### Install development tools (optional)
ROS パッケージをビルドする場合、またはその他の開発を行う場合は、開発ツールをインストールする。

1. 下記のコマンドを実行します
    ```bash
    sudo apt update && sudo apt install ros-dev-tools
    ```
## Install ROS 2
1. リポジトリを設定した後、apt リポジトリ キャッシュを更新します
    ```bash
    sudo apt update
    ```

1. apt updateします
    ```bash
    sudo apt upgrade
    ```

1. デスクトップをインストールします。（少し時間がかかります）
    ```bash
    sudo apt install ros-jazzy-desktop
    ```
1. ROS-baseをインストールします
    ```bash
    sudo apt install ros-jazzy-ros-base
    ```

## Setup environment
1. sourceで環境をセットアップします
    ```bash
    source /opt/ros/jazzy/setup.bash
    ```

## Try some examples
ros-jazzy-desktop をインストールした後にいくつかの例を試します。
1 つの端末でセットアップ ファイルをソースし、C++ talkerを実行します。

1. 下記のコマンドを実行します
    ```bash
    source /opt/ros/jazzy/setup.bash
    ros2 run demo_nodes_cpp talker
    ```

1. 下記のように表示されました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/b78107a1-4a0e-05f1-d317-7348e8216505.png)

1. Terminalをもう一つ起動し、下記のコマンドを実行します
    ```bash
    source /opt/ros/jazzy/setup.bash
    ros2 run demo_nodes_py listener
    ```

1. 下記のように表示されました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/58ee2b3d-a4e8-79b1-f7af-87b596eaf22a.png)

Talkerは「Publishing」が表示され、listnerはそれらのメッセージを「heard」と表示されました

# 考察
今回、ROS2の環境を構築しました。次回以降下記のチュートリアルを試してみます。

https://docs.ros.org/en/jazzy/Tutorials.html

# 参考 
https://qiita.com/zumax/items/5f638645fda99b75b82a

https://qiita.com/zumax/items/8090f59fc2b779af55be
Javascript - オブジェクトの配列を同士を比較する ( JSON.stringfy ) ( 3人のアリス )

# 課題

2個の配列同士で、オブジェクトの値が1個でも違うものがれば、その差分を検出したい


# 対策

-  `JSON.stringify` でオブジェクトを丸ごと比較する


## コード例

それぞれ名前・年齢を持つオブジェクトの配列を比較する

```js
// 配列Aと配列B
const arrayA = [
  { first_name: 'Alice', age: 20 }, // 両方の配列に存在する要素
  { first_name: 'Alice', age: 30 }, // 両方の配列に存在する要素
  { first_name: 'Bob', age: 30 }, // 片方の配列にしか存在しない要素
]

const arrayB = [
  { first_name: 'Alice', age: 20 }, // 両方の配列に存在する要素
  { first_name: 'Alice', age: 30 }, // 両方の配列に存在する要素
  { first_name: 'Alice', age: 40 }, // 片方の配列にしか存在しない要素
  { first_name: 'Carol', age: 20 }, // 片方の配列にしか存在しない要素
]

const findDifferences = (array1, array2) => {
  // array1にあってarray2にないものを見つける
  const onlyInArray1 = array1.filter((obj1) => !array2.some((obj2) => JSON.stringify(obj2) === JSON.stringify(obj1)))

  // array2にあってarray1にないものを見つける
  const onlyInArray2 = array2.filter((obj2) => !array1.some((obj1) => JSON.stringify(obj1) === JSON.stringify(obj2)))

  // 差分を結合
  return [...onlyInArray1, ...onlyInArray2]
}

// 差分を見つける
const differences = findDifferences(arrayA, arrayB)

console.log(differences)
```

## 出力結果

- 同じ名前、なおかつ同じ年齢の要素は検出されない
- 名前か年齢か、その両方が違う要素が検出される

```js
[
    { first_name: 'Bob', age: 30 },
    { first_name: 'Alice', age: 40 },
    { first_name: 'Carol', age: 20 }
]
```

# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ


# プロフィール・経歴

https://github.com/YumaInaura/YumaInaura
Django + Fullcalendar

## Django + Fullcalendar

「[中古カメラのB2Bオークションサイト：+Auction](https://plusauction.net/)」で、オークションの日程の表示・提供に、[Fullcalendar](https://fullcalendar.io/)を採用しました

管理側で入力した内容をフロントに表示するという至極単純な方法です
難しいことは何もしておらず、必要最低限デス

### Step.1 スケジュールを格納するModel作成
``` home/models.py
from django.db import models
from config.models import BaseModel

class Schedule(BaseModel):
  title = models.CharField(verbose_name='タイトル', max_length=500, blank=True, null=True)
  started = models.DateTimeField(verbose_name='開始日時', blank=True, null=True)
  ended = models.DateTimeField(verbose_name='終了日時', blank=True, null=True)

  def __str__(self):
    return self.title

  class Meta:
    db_table = 'da_schedule'
    verbose_name = '開催スケジュール'
    verbose_name_plural = '開催スケジュール'
```
+ BaseModelは
``` config/models.py
class BaseModel(models.Model):
  class Meta:
    abstract = True

  created = models.DateTimeField(verbose_name='生成日時', auto_now_add=True)
  modified = models.DateTimeField(verbose_name='更新日時', auto_now=True)
  deleted = models.DateTimeField(verbose_name='削除日時', blank=True, null=True)
```  
各Modelでの共通項ですので、BaseModelでまとめて設定し、各Modelでimportするようにしています

### Step.2：templateファイルにfullcalendar設定
``` templates/home/index.html
{% block content %}
<div id='calendar'></div>
{% endblock content %}

{% block javascripts %}
<script src='https://cdn.jsdelivr.net/npm/fullcalendar@6.1.15/index.global.min.js'></script>
<script>
  document.addEventListener('DOMContentLoaded', function() {
    var calendarEl = document.getElementById('calendar');
    var calendar = new FullCalendar.Calendar(calendarEl, {
      initialView: 'dayGridMonth',
      locale: 'ja',
      height: 650,
      businessHours: true,
      events: [
        {% for schedule in schedules %}
        {
        title: '{{ schedule.title }}',
        start: '{{ schedule.started| date:"Y-m-d H:i" }}',
        end: '{{ schedule.ended| date:"Y-m-d H:i" }}',
        color: '#05ad16',
        },
        {% endfor %}
      ]
    });
    calendar.render();
  });
</script>
{% endblock javascripts %}
```


### Step.3： viewsで、日程検索
``` home/views.py
class HomeIndexView(TemplateView):
  template_name = 'home/index.html'
  def get_context_data(self, **kwargs):
    context = super().get_context_data(**kwargs)
    context['schedules'] = Schedule.objects.filter(deleted__isnull=True)
    return context
```


結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/14219/90f8d217-12bf-412c-a277-18802e109f57.png)
が表示されます
Node-RED MCU Edition版 #スタックチャン (Stack-chan)の作例②

# はじめに

Node-RED MCU Edition版 #スタックチャン (Stack-chan)の動作に必要なモジュールが揃ってきたので、作例を紹介します。

作例①はこちらです。
Moddable SDKとNode-RED MCU Editionの開発環境の構築方法はこちらを参照してください。

https://qiita.com/kitazaki/items/bef12c0958da0466194a

# 作例② (入力した文章をおしゃべり)

## 構成

全体構成は下図のようになっています。
クラウドから入力されたテキストメッセージをおしゃべりしてくれます。

![スクリーンショット 2024-09-02 8.41.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/303c20cc-ca08-1a0d-5869-49e37b0818a3.png)

## 左

Node-RED Dashboardからテキストメッセージを入力します。
インターネット上のMQTTブローカー(中央のNode-RED)へ接続し、入力されたテキストメッセージを送信します。

## 中央

Node-REDでAedesノード(MQTTブローカー)を動かしています。

## 中央(elevenlabs)

REST APIで入力されたテキストメッセージを音声(mp3)に変換します。

## 右

テキストメッセージの情報を受信します。
受信したテキストメッセージをElevenLabsへ送信し、変換された音声データ(mp3)を取得して再生します。

## 右(デバイス側)

![スクリーンショット 2024-09-02 8.26.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/383b003a-93a2-f34b-5cbd-6a5b35abf889.png)

## 中央(MQTTブローカー)

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/57634204-d392-84fd-b503-071a02ea6a8c.png" width="50%">

## 左(クラウド側)

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/1e6add0a-2126-3286-13a7-c7a8bcf6b709.png" width="50%">

![スクリーンショット 2024-09-02 8.12.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/72479/605eec64-0a6b-b970-256f-a445da005cf2.png)
Content Builder

[まとめページに戻る](https://qiita.com/Keiji_otsubo/items/60afc2a8d211b6f7d89f)
[まとめA～M](https://qiita.com/Keiji_otsubo/items/f3ddedee6793bf1576f8)




+ [【ヘルプ】Content Builder](https://help.salesforce.com/s/articleView?id=sf.mc_ceb_content_builder.htm&type=5)

Content Builder は、Marketing Cloud エンゲージメントで使用するために、画像、ドキュメント、コンテンツを 1 つの場所で統合できるクロスチャネルコンテンツ管理ツールです。使いやすい、ドラッグアンドドロップインターフェースの Content Builder は、従来の作成および編集ツールより効率的に使用できます。Content Builder には、コンテンツリポジトリ (CMS) と API、コンテンツ管理アプリケーション (旧称 Content Canvas)、新しい HTML コンテンツエディター (旧称 Content Builder) も含まれています。

+ [【トレイル】コンテンツビルダーで魅力的なコンテンツを作成する](https://trailhead.salesforce.com/content/learn/trails/create-compelling-content-with-content-builder)

コンテンツ ビルダーは、Marketing Cloud Engagement アカウントのコンテンツ ライブラリによく似ています。コンテンツ ビルダーを使用して、アセットを整理し、メールの作成を効率化し、適切なユーザーにアクセス権を付与します。

# コンテンツビルダーを見てみましょう
コンテンツ ビルダーに移動して、作業を開始しましょう。コンテンツ ビルダーには、いくつかの方法でアクセスできます。Marketing Cloud Engagement では、上部のナビゲーション バーで[コンテンツ ビルダー]をクリックして、コンテンツ ビルダーに直接移動します。すでに Email Studio で作業している場合は、Email Studio ナビゲーションで[コンテンツ] をクリックできます。

# コンテンツビルダーのアクセスを構成する

+ Marketing Cloud Engagement ユーザーの Content Builder のロールと権限を決定します。
+ ビジネス ユニット レベルで個々の資産へのアクセス レベルを設定します。
+ 個々のコンテンツの編集権限を設定します。

# コンテンツビルダーのカスタマイズと強化

+ エンタープライズ共有を有効にし、タブを構成してコンテンツ ビルダー インターフェイスをカスタマイズします。
+ コンテンツ ブロック エディターで特定のタブを構成します。
+ カスタム ブロック ウィジェットを使用してコンテンツ ビルダー プラットフォームを拡張します。

# コンテンツビルダーの機能

https://trailhead.salesforce.com/content/learn/modules/content-builder-features/explore-content-types?trail_id=create-compelling-content-with-content-builder
ブラウザ上でzipの中身の閲覧、編集が可能な無料のオンラインサービス

こんにちは、りょうです。
最近あつすぎて死にそうです。ではいきましょー
## Zipadee
紹介するのはこの[Zipadee](https://zipadee.vercel.app/)というサイトです！
登録などは不要で、簡単に対応しているファイルの閲覧、編集ができます。

## 使い方
まずサイトにアクセスします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3818367/b610e258-7c79-ed1e-48d8-d6a845fbe3c5.png)

次に**Edit the Contents of a ZIP file**というところを押してzipファイルを選択orドロップします。
するとこのように中身が表示されます。自由に編集も可能です。
(画面右下に出てるタイマーはapp on flyを使ってるせいなので気にしないでください)

![Screenshot 2024-08-30 17.05.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3818367/a6706784-a961-5d13-6e4a-fa094b991eec.png)
![Screenshot 2024-08-30 17.06.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3818367/0d9c67eb-372b-7c5b-a607-ed46bf3e5722.png)

編集後、**SAVE UPDATED FILE**を押すと確認が表示されるので了承すればこのようにちゃんとzipの中身が編集された状態で保存されます。

![Screenshot 2024-08-30 17.10.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3818367/1b0b1e98-3025-1b17-692e-6c3d1c27ad27.png)
![Screenshot 2024-08-30 17.08.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3818367/30f16247-f080-f22d-9b58-ba689e1133f1.png)

### その他
> zip以外にも**EPUB、DOCX、XLSX、PPTX、ODT、CRX**に対応しています。

> ZipadeeのGithub

https://github.com/hubgit/zipadee

> 画面右上の**INSTALL**を押せばアプリとしてインストール(ショートカット)もできます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3818367/9b66dd7f-2892-7caf-6542-abbbd31c5d0a.png)

## まとめ
今回はZipadeeの紹介でした。
送られてきたファイルをローカル環境で開きたくないときに便利なのでおすすめです。

わからないことあったら書いてくれれば答えていきます。

**Discordにいる荒らしの情報や対策方法の共有をしている自鯖です**
先日500人突破しました。ぜひ参加お願いします↓↓↓↓

https://discord.com/invite/ESRGwGQhxc
lnavで行うログファイルの分析

# はじめに
Linux環境で出力するログファイルの分析を行うにあたってシンプルな方法は、コマンドを使用する方法です。

しかし、大量のログファイルを効率的に分析する場合は、シェルスクリプトやPythonなどプログラミングの手段が必要になってきます。

LinuxではMultiTailやGoAccessなどコマンドラインで使用できる便利なツールが存在します。MultiTailについては、以前書いた[MultiTailで効率的にログを見る](https://qiita.com/Brutus/items/3e6425c84142902da376)をご参照ください。

本記事では効率的にログファイルの分析を可能とするlnavについて記載しています。

## lnav
[**lnav**](https://lnav.org/)は、ターミナル用のログファイルビューアです。

ログファイルを簡単にマージ、追跡、検索、フィルターできます。また、SQLクエリを使用した分析も可能です。セットアップも不要なため、パッケージをインストールするだけですぐに使用できます。

### lnavのインストール
Linux環境（Debian/Ubuntu Linux）の場合は、以下のコマンドでインストールできます。

`$ sudo snap install lnav`

その他のOSについては[Downloads](https://lnav.org/downloads)をご確認ください。また、GitHubでも公開されているため、パッケージが取得できない環境でもダウンロードして使用できます。

https://github.com/tstack/lnav

### 使用方法
`lnav`の引数にログファイルを渡すだけで、簡単に実行できます。

`lnav <ログファイル名>`

ログファイルは複数まとめて処理することができるため、globパターンであるワイルドカードを使用することができます。また、`.gz`など圧縮したファイルも対応しているたため、圧縮ファイルの展開も不要です。

なお、大量のファイルを高速に開くにあたって、次の条件を満たすファイルは自動的に非表示になり、インデックスが作成されません。

- バイナリファイル
- 128KBを超えるプレーンテキストファイル
- 重複したログファイル

例として、syslogを分析する場合は以下の様なコマンドを実行します。

`lnav /var/log/syslog`

![スクリーンショット 2024-08-30 20.44.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/4b2f6e30-e210-fb3c-4628-0034494f29e2.png)

`vi`の様なキーバインド感覚で実行できるため、ログファイルの先頭に移動したい場合は`g`を押します。末尾に行きたい場合は`shift + g`を押します。また、`/`で検索や`e`を押すことでエラーメッセージ（Error/error）に移動できます。

::: note
lnavはテキストファイルだけでなく、journalctlやdockerが出力するログなども処理することができます。

`$ journalctl | lnav`
`$ docker logs -f <container-id> | lnav`
:::

### SQLite
lnavは、SQLiteインターフェースを用いてSQLクエリも使用できます。

以下は、Webサーバで出力された複数のアクセスログを基にSQLクエリを使用して、特定のユーザーエージェントを抽出する例です。

アクセスログのファイル名は、`access.log.1`の様なファイル名になっていますが、アクセスログの中のタイムスタンプを基にソートされるため、ログファイル名は気にすることなく処理できます。

`$ lnav access.log*`

![スクリーンショット 2024-08-30 20.45.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/b2d99c86-ebc1-7689-2b4c-345d5a26ced4.png)

`;`を入力後、SQLが入力できます。カラムは構造化されたログのフィールドから抽出され、テーブル名はログファイル名となります。

![スクリーンショット 2024-08-30 22.01.54.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/95e8ed82-5560-4787-3c51-7a5a8481900b.png)

特定のユーザーエージェントを抽出するために、`where`句を使用しています。

```sql
select log_line, log_time, c_ip, cs_method, cs_uri_stem, cs_user_agent from access_log where cs_user_agent like '%GPT%'
```

ユーザーエージェントがGPTBotであるAIクローラーからのアクセスログを抽出しました。

![スクリーンショット 2024-08-30 22.02.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/221759/7f65a30c-9a9a-f6af-c5b7-57be425d1edd.png)

::: note
AIクローラーのアクセスをブロックしているため、`robots.txtファイル`のみのアクセスが確認できます。AIクローラーのアクセスをブロックする方法については以前書いた[robots.txtファイルを使用してAIクローラーをブロックする方法](https://qiita.com/Brutus/items/440d19f185f37352eb75)をご参考にください。
:::

# おわりに
lnavはトラブルシューティングやアクセスログ解析などさまざまな目的で使用することができます。

セキュリティの観点ではSIEMにも勝るとも劣らない機能によりインシデント対応の調査にも活用できると思います。

## 参考
- [lnav – Awesome terminal log file viewer for Linux and Unix](https://www.cyberciti.biz/open-source/lnav-linux-unix-ncurses-terminal-log-file-viewer/)
【React】100本ノック #02 Counter

## はじめに
フロントエンドスキル向上のため書籍やudemyでインプットを行っていますが、アウトプットも並行して進めるべく[@Sicut_study](https://qiita.com/Sicut_study "Sicut_study")さんのReact100本ノックを始めることにしました。

https://qiita.com/Sicut_study/items/82059f9cbb5b2996e5b3

## ルール
- 主要なライブラリやフレームワークはReactである必要がありますが、その他のツールやライブラリ（例: Redux, Next.js, Styled Componentsなど）を組み合わせて使用することは自由
- TypeScriptを利用する
## 今回作るもの
ボタンを押すと、数値が増減するカウンターコンポーネント

## 条件
1\. 表示領域: 数字を表示するエリアが存在する。
2\. インクリメントボタン: このボタンをクリックすると、表示されている数字が 1 増える。
3\. デクリメントボタン: このボタンをクリックすると、表示されている数字が 1 減る。
4\. 初期表示: アプリを起動した際の数字は 0 である。
5\. ステート管理: React の useState や useReducer を使って、カウントの数字を管理する。

## 成果物
https://statuesque-scone-c8b31e.netlify.app/

https://github.com/okido-yukinari/02-counter.git

### 使用技術

- React
- TypeScript
- MUI

前回はEmotionを使いましたが、今回はMUIを使ってスタイルを実装しました。

### PC
![02_counter_pc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/453680/eff98c51-17a9-446a-cf17-05c86de2b1c5.png)

### SP
![02_counter_sp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/453680/bf09847d-1ca0-b60c-7917-885d096219c3.png)

## 感想
useStateの使い方を理解することができました。
個人的にはデザインをもうちょっとこだわって作りたかったのですが、今回はあくまでuseStateについて理解することが第一目的だったのでこのくらいで。。。
XserverでPHPのバージョンを上げる時に困った話


# はじめに

こんにちは、エンジニアのkeitaMaxです。

今回はXserverでPHPのバージョンが上げられなくて困ったことについて備忘録として書きます。

# 困ったこと

XserverのサーバパネルでPHPのバージョンを上げたが、コマンドでPHPのバージョンを確認したら上がってなくて困りました。

![スクリーンショット 2024-08-29 13.06.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3715123/1c1fb5e8-5c3e-b435-a167-83680c8350e0.png)

```
$ php -v
PHP 8.1.22 (cli) (built: Aug 15 2023 11:02:44) (NTS)
```

# 解決策

`/usr/bin/`にあるphpのシンボリックリンクを修正したら直りました。

```:/usr/bin/
$ unlink php
$ ln -s /usr/bin/php8.2 php
```

```
$ php -v
PHP 8.2.9 (cli) (built: Aug 16 2023 11:23:31) (NTS)
```

# おわりに

この記事での質問や、間違っている、もっといい方法があるといったご意見などありましたらご指摘していただけると幸いです。

最後まで読んでいただきありがとうございました！

# 参考

https://community.xserver.ne.jp/question/tid/107/
Macで`python3.10.x`のvenv環境をつくる際の備忘録

Macで`python3.10.x`のvenv環境をつくる際の備忘録

# 背景

記事作成時点の最新環境だと`python3.12.x`が使われる。
しかし、`python3.10.x`を使いたい場合もある。

# install

`homebrew`により`python3.10`をインストールする

```sh
brew install python@3.10
```

`python3.10`の`venv`を利用

```sh
python3.10 -m venv venv3.10
```

`activate`して、バージョンが`3.10.x`になっていればOK

```sh
$ source venv3.10/bin/activate
(venv3.10)$ python --version
Python 3.10.14
```

ubuntu環境でも同様のことをしたい場合、上記に沿って`pyton3.10.x`をインストールすればOK
https://gist.github.com/rutcreate/c0041e842f858ceb455b748809763ddb
> `sudo apt install python3.10 python3.10-venv python3.10-dev`

# 参考

https://qiita.com/unaginokabayaki/items/81d7b2bf8a6bdaee37a9#mac

https://python.land/virtual-environments/virtualenv
Viteで環境変数が 'Undefined' になった際の対処法 〜 VITE_ プレフィックスの重要性 〜

# はじめに
Viteを使ってWebアプリケーションを開発しているなかで、環境変数を設定した際に、**クライアントソースコードで文字列が`undefined`と**なってしまう事象に遭遇。

▼コード（.env.localファイル）
```
API_KEY=your_api_key
```

▼コード（example.tsx）
```
const API_KEY = import.meta.env.API_KEY;
console.log('API KEY: ', API_KEY);
```

▼コンソール
![Screenshot 2024-09-01 at 2.37.51 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3875835/aa6370e7-339c-64ea-ce1e-eaf0ec0ad0f5.png)

そのときの原因と解消方法について、簡単にまとめた。

## 原因と解消方法

結論から話すと、**環境変数には`VITE_` というプレフィックスが必要**だった。

▼修正前のコード（.env.localファイル）
```
API_KEY=your_api_key
```
↓

▼修正後のコード（.env.localファイル）
```
VITE_API_KEY=your_api_key
```

## なぜ `VITE_` プレフィックスが必要なのか？

なぜViteは環境変数に`VITE_`を付けることを要求するのか？その理由は主に2つある。

### 1. セキュリティ

公式より以下のような説明がある。
> 環境変数が誤ってクライアントに漏れてしまうことを防ぐために、`VITE_` から始まる変数のみが Vite で処理されたコードに公開されます。例えば、以下の環境変数だと:
> ```
> VITE_SOME_KEY=123
> DB_PASSWORD=foobar
> ```
> `VITE_SOME_KEY` だけが `import.meta.env.VITE_SOME_KEY` としてクライアントソースコードに公開され、`DB_PASSWORD` は公開されません。
> 
> ```
> console.log(import.meta.env.VITE_SOME_KEY)// "123"
> console.log(import.meta.env.DB_PASSWORD) // undefined
> ```

（↓公式ドキュメントリンク：envファイルについて）

https://ja.vitejs.dev/guide/env-and-mode#env-files

Viteはデフォルトで、クライアントサイドで利用できる環境変数を厳密に管理している。すべての環境変数がクライアントサイドに露出すると、セキュリティリスクが高まる可能性がある。特に、サーバーサイドの環境変数や機密情報（APIキー、データベースの認証情報など）がクライアントに漏れ出るのは避けたい。

そのため、Viteでは`VITE_`プレフィックスが付いた変数だけをクライアントサイドで利用できるようにし、意図しない情報の露出を防いでいる。

### 2. 明確さ

`VITE_`プレフィックスがあることで、どの環境変数がクライアントサイドで使用されるものかが明確となる。チーム開発時にも、コードの可読性が向上し、意図しない変数の使用を防ぐことができる。

## 補足：

### 1. 環境変数とは？

環境変数とは何かを簡単におさらい。環境変数とは、アプリケーションが動作する際に必要となる設定値を外部から与えるための変数。これにより、アプリケーションを異なる環境（開発、テスト、本番など）で簡単に切り替えることができる。

### 2. Viteにおける環境変数の設定

Viteで環境変数を使うには、プロジェクトのルートディレクトリに`.env`ファイルを作成し、その中に変数を定義する。ここで重要なのが冒頭にも触れたが、**変数名の先頭に `VITE_` を付けること**。

例：外部サービスのAPIキーを設定する場合、以下のように書く。

▼.envファイル

```
VITE_API_KEY=your_api_key
```

↓

▼example.tsx（クライアントソースコード内で呼び出す際）

```tsx
const apiKey = import.meta.env.VITE_API_KEY;
```

### 3. **TypeScript 用の自動補完**

私のように抜けがちな人対策として、`import.meta.env` のための型定義があるので、これを利用するとコードの正確性が高まる。

簡潔に内容を記載すると、`src` ディレクトリーに `vite-env.d.ts` を作成し、`ImportMetaEnv` に記述することで設定が可能。

↓公式ドキュメント：typescript-用の自動補完

https://ja.vitejs.dev/guide/env-and-mode#typescript-用の自動補完

### まとめ

Viteで環境変数を設定する際には、必ず変数名に `VITE_` プレフィックスを付ける必要がある。
これにより、セキュリティが向上し、アプリケーションのパフォーマンスも最適化される。今後、Viteでプロジェクトを開発する際には、このポイントをしっかり押さえておくことが大事。
【要件定義】システム開発を発注する側も何を発注しているのか分かっていない説

# はじめに
システム開発をするときの大半は顧客との対話から始まります。
顧客「こんなものを作って欲しいんだよね」
自分「ふむふむ、なるほど。わかりました。」
こんな感じです。（本当にわかったのかはさておき）

一方でこんな場面もあります。
自分「先程このようなお話がありましたが、そうなるとxx機能も必要だと思いますがどうでしょう？」
顧客「あー確かに。その機能も開発しないといけないですね。」

これって本来ならばおかしいことではないのか？と思います。
なぜなら自分から開発してほしいと依頼しているにもかかわらず、その内容がわかっていないことになるためです。
言われて初めて気付いているような状況です。

要件定義の現場ではこういうことが多々あります。そのためヒアリングが重要です。
このヒアリングが甘いと、あれもこれも追加開発しなきゃと現場にしわ寄せがいって大変なことになります。

それではなぜこのような、顧客自身が作ってほしいものを把握していない状況が生まれるのでしょうか？

# 「もっと速い馬がほしい」──ヘンリー・フォードの名言が示す、第三者の視点の重要性

> 「もし私が顧客に何がほしいかを聞いていたら、彼らは『もっと速い馬がほしい』と答えただろう。」

この言葉は、自動車の父とも呼ばれるヘンリー・フォードの有名な名言です。
この一言に、顧客のニーズを深く理解し、それを超えて革新を生み出すための重要な教訓が詰まっています。
フォードが伝えたかったのは、顧客自身も本当に求めているものが何であるかを正確に把握していない場合が多いということです。
そして、それを見極めるためには第三者の目線が不可欠であるということです。

## 顧客のニーズを超えた真の課題を見つける

多くの場合、顧客は自分の現状を基にした解決策を求めます。
しかし、現状の枠組みの中での解決策が必ずしも最善であるとは限りません。
彼らが「もっと速い馬」を求めたのは、移動手段としての馬に依存していたからです。
当時の移動手段は馬であるため、車というものを想像することは人々にはできないことでした。

しかし、フォードはその背後にある真のニーズ──「より効率的で速い移動手段」を見抜き、それを自動車という形で提供しました。

顧客の要望に単に「YES」と応えることは簡単ですが、それが最善の解決策であるかは疑問です。
顧客の発言をそのまま受け入れるのではなく、その背後にある目的や課題を理解しようとする姿勢が求められます。

## 質問することの価値

顧客からの要望を受けた際には、「なぜこの要望を出しているのか？」という視点を持つことが重要です。

その要望がどのような問題を解決しようとしているのか、またその問題を解決するための他のアプローチはないかを考えることで、より効果的で革新的な解決策を見つけることができるかもしれません。

例えば、顧客が「この機能を追加してほしい」と言ったときに、単にその機能を実装するのではなく、「なぜその機能が必要なのか？」と問いかけることが、より深い理解と優れたソリューションの提供に繋がります。

## 第三者の視点がもたらす価値

第三者の視点は、顧客が見落としている新たな可能性を発見する助けになります。
顧客が抱えている課題に対して新鮮な見方を提供し、革新を促すきっかけを生むのです。

私たちが提供するべきものは、顧客が最初に思い描いたものではなく、その先にある本当に必要とされる解決策です。
そのためには、顧客の言葉にただ従うのではなく、深く考え抜くことが求められます。

# まとめ

ヘンリー・フォードの名言は、顧客のニーズを超えてその本質を見抜くことの重要性を示しています。
私たちが顧客に本当に価値あるものを提供するためには、単に要望に応えるだけでは不足していることが分かります。
その背後にある目的や課題を理解し、第三者の目線で新たな可能性を模索することが不可欠です。

このことを意識すると、顧客がまだ気づいていない本当のニーズを見つけ出し、それを満たすソリューションを提供することで、より良い発注・受注ができるのではと思います。

# 余談
この度ポートフォリオとなるサービスを開発しました🎉
サービス名は「[**PowerPointChecker**](https://lp.powerpoint-checker.com/)」です！
チームでパワーポイント資料を作成するとき
* 資料の内容よりまず見た目が気になる
* 見た目の指摘を受けていつまでも完成しない
* 作成ルールを守れていない資料が提出される
* 作成ルールがわからない

このようなお悩みを解決するサービスが「[**PowerPointChecker**](https://lp.powerpoint-checker.com/)」です！


しかしこの記事公開時点ではα版リリースで、設定できるルールも少なければバグも多々あるかと思います。
すべて無料でご使用いただけますので、是非利用いただきまして様々なご意見をいただきたいです。
ユーザー数の増加が開発の励みになります。
WordPressの有料テーマのランキングは本当かどうか調べてみたった

WordPress歴は長いけど、ずーっと使っていると、まぁ、いろんな情報が入ってくる。

特に自分がモヤモヤとしたのは【WordPressテーマ】だ。
無料が良いとぶっちゃけ最初は思って使っていたけど、段々そのうちいろんなブログを見ているうちに有料テーマってすごいことできるに違いない！と思って購入。

しかし、その時に絶望を味わうことになった。

なんと公式サイトで見たことができないのだ。
問い合わせしてみても「それはデモ用なので改造してあります」と言われた。

正直、公式サイトで見せているような内容がノーコードで作り出せないなんて…って詐欺かと思った。

そこからWordPressの有料テーマをのらりくらりとたくさん買ってきたわけだが、デザインが実現できないだけでなく、「SEOに強い！」もめちゃくちゃ詐欺っぽいことが分かった。

データの構造化や、HTMLが崩れていないか、サイトの速度などなどいろいろと検証していると、まぁ、とにかく「これじゃ上に上がるものもあがらないだろう！」と感じるものも多かったのだ。

さすがに嫌気が指してきて、「これ全部検証したほうがいいんじゃね？」と思った。

そうしてできあがったのが、この記事。
https://greencoatle.soratobunezumi.co.jp/seo/wordpress_thema_paid/

手前味噌だが、めちゃくちゃ一個ずつ丁寧に検証しているため、進みが遅い。だけど、間違いなく価値はあると思う。

なんちゃらアフィリエイトとかももちろんあるんだが、そういう忖度は入れないようにしている。
あの時の「騙された！」「詐欺だ！」と思われたくないからだ。

他のランキングとは全然違うかもしれない。

だが、もし、WordPressを使っているなら良ければ見に来てほしい。初心者の方が騙されないために作った記事だ。

Qiitaにいる人は基本的に知識がある人だろう。そんなベテランはうなづくとか、笑ってくれても構わない。
ここまで読んでくれてありがとう。
https://greencoatle.soratobunezumi.co.jp/seo/wordpress_thema_paid/
パッケージ化用pyproject.tomlボイラープレート

# プロジェクトのパッケージ化
自分のプロジェクトをパッケージ（厳密にはライブラリ）として扱わない場合、プロジェクト内のモジュール間でのimportが通常のようにできなく、マニュアルでsys.pathをいじる必要があります。これはものすごく不便です。ので、poetry・pipを使ったプロジェクトのパッケージ化方法をまとめます。

## 基本方針
基本手順はpoetry・pipに変わらず同じ。(PEP 621のおかげっぽい)
1. パッケージ化する際に、プロジェクトの設定（どのフォルダーをパッケージとしたいのか、パッケージ化際どのツールを使うかなど）を記述する必要がある。これにはpyproject.tomlファイルを使う
2. パッケージ化するコマンドを使う

想定ディレクトリ構造
```
.
└── project/
    ├── src/
    │   └── package_name/
    │       └── # python files
    └── pyproject.toml
```




## pip + setuptools


### pyproject.toml
```toml
[project]
name = "package_name"
version = "0.1.0"

[build-system]
requires = ["setuptools", "wheel"]
build-backend = "setuptools.build_meta"
```

### command
上を記述した後に以下のコマンドを打つことで、自動的にパッケージ化してくれます。
```bash
pip install -e .
```

#### 注意
- `e`はeditableの略です。editableでないと、プロジェクト内のコードを変更をした際に毎回buildしないといけなくなります。ので、デベロップ段階ではeditableでインストールします

### 参考
https://setuptools.pypa.io/en/latest/userguide/pyproject_config.html

## poetry
### pyproject.toml
```toml
[tool.poetry]
name = "package_name"
version = "0.1.0"
description = ""
authors = ["yourname <your_name@gmail.com>"]
packages = [{ include = "package_name", from = "src"}]

[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
```

#### 注意
- name、version、description、authorsは必須です (ないとエラーが出ます)
- poetryはデフォルトでpyproject.tomlと同じdirectory内のtool.poetry.nameがprojectのソースダイレクトり (=パッケージ化の対象)であることを想定している。そのため、src内にプロジェクトがある場合、tool.poetry.packagesにマニュアルでプロジェクトフォルダーを指定する必要があります

### command
上を記述した後に以下のコマンドを打つことで、自動的にパッケージ化してくれます。
```bash
poetry install
```
### 参考
https://python-poetry.org/docs/pyproject/#packages
php-fpmの設定ファイルの場所を探す

`php-fpm -t` コマンドで探す

```
$ docker run php:8.3-fpm php-fpm -t
[01-Sep-2024 19:57:29] NOTICE: configuration file /usr/local/etc/php-fpm.conf test is successful
```

```
$ docker run php:8.3-fpm ls /usr/local/etc/php-fpm.conf
/usr/local/etc/php-fpm.conf
```

見つかりました！

## 補足

```
$ docker run php:8.3-fpm php-fpm -h
Usage: php [-n] [-e] [-h] [-i] [-m] [-v] [-t] [-p <prefix>] [-g <pid>] [-c <file>] [-d foo[=bar]] [-y <file>] [-D] [-F [-O]]
  -c <path>|<file> Look for php.ini file in this directory
  -n               No php.ini file will be used
  -d foo[=bar]     Define INI entry foo with value 'bar'
  -e               Generate extended information for debugger/profiler
  -h               This help
  -i               PHP information
  -m               Show compiled in modules
  -v               Version number
  -p, --prefix <dir>
                   Specify alternative prefix path to FastCGI process manager (default: /usr/local).
  -g, --pid <file>
                   Specify the PID file location.
  -y, --fpm-config <file>
                   Specify alternative path to FastCGI process manager config file.
  -t, --test       Test FPM configuration and exit
  -D, --daemonize  force to run in background, and ignore daemonize option from config file
  -F, --nodaemonize
                   force to stay in foreground, and ignore daemonize option from config file
  -O, --force-stderr
                   force output to stderr in nodaemonize even if stderr is not a TTY
  -R, --allow-to-run-as-root
                   Allow pool to run as root (disabled by default)
```