OmniAICreator/Qiita-1.07M huggingface dataset:
DatadogのDBMにおけるQuery InsightをMonitorで通知する

### Monitorで通知したい対象:

DatadogでDBMを有効化すると自動で検知される"Query Insights"
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2727432/72d72102-afe5-3de7-663f-222f1cd30213.png)

### 設定方法：
Explain Planに"@db.insights.type"というAttributeが付与されており、"Index Recommendation"など、一部の"Query Insight"はこちらのAttributteで引っ掛けることで、Monitorで通知可能

### Monitorの設定例：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2727432/40a6faf3-4804-2870-bdfb-f458edd04d42.png)

### 実際の通知：
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2727432/c86dad4e-76ae-ca03-ebde-8e5c452fa406.png)
↓
通知に自動付与されるURLからInsightとして検知された内容を確認可能
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2727432/71ef50aa-bd95-3742-cbe6-1d11e9d61831.png)


### 設定をExportしたJSON Config

Monitor設定右上の"New from JSON"からImportして設定可
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2727432/56067e15-9573-95c9-2ed6-940b5e88ebe5.png)


```
{
	"id": 153136904,
	"name": "DBM Query Insight Monitor",
	"type": "database-monitoring alert",
	"query": "database-monitoring(\"dbm_type:plan @db.insights.type:*\").rollup(\"count\").by(\"@db.plan.signature,@db.statement\").last(\"5m\") > 0",
	"message": "There is a Query Insight.\n\nthe query signature:  \"{{[@db.plan.signature].name}}\"\nquery statment:\n{{[@db.statement].name}} \n\n\n{{databasemonitoring.link}}\n@slack-Datadog_Incidents-team-taka2",
	"options": {
		"thresholds": {
			"critical": 0
		},
		"enable_logs_sample": true,
		"notify_audit": false,
		"on_missing_data": "default",
		"include_tags": true,
		"new_group_delay": 60,
		"groupby_simple_monitor": false,
		"silenced": {
			"*": null
		}
	},
	"priority": 5,
	"restriction_policy": {
		"bindings": []
	}
}
```
IntellijでGaugeのプラグインをいれるときにハマったこと

# はじめに

IntellijにGaugeのプラグインを入れるときに毎回躓くのでやり方を流れでまとめていきます。

# 手順

まずはGaugeのインストールをします。
ここでasdfを使っていれるとIntellijでプラグインが動かなくなるので、gaugeはバージョン管理せずにいれたほうがいいです。

https://docs.gauge.org/getting_started/installing-gauge.html?os=linux&language=javascript&ide=vscode

そのあとJavaをローカルに入れます

```
$ asdf plugin add java
$ asdf install java corretto-17.0.4.9.1
$ asdf global java corretto-17.0.4.9.1
$ echo $JAVA_HOME
$ java -version
```

次にgauge-javaをインストールします

```
$ gauge install java --version 0.10.3
```

ここでまずローカルでGuageのテストが動くかチェックする

```
$ mvn test
```

これでうごけばGaugeは正しく設定できているので、Intelljのプラグインを追加して
Gauge binary pathに`/usr/local/bin/gauge`をいれる

Gaugeは破壊的変更があったので、動かない場合は、`0.7.2`をいれると動くかもしれない

# それまでに起きた問題

```
Error : fork/exec gauge-java: permission denied
```

Gaugeのプラグイン(~/.gauge)を他のPCからコピーしてきたのが原因でだめだった。
gauge installしないとちゃんと権限がつかないみたい

```
Failed to start java. exec: "java": executable file not found in $PATH
````

javaのパスが正しく通っていなかった
Javaをインストールして以下を`~/.bashrc`に追加して通した

```.bashrc
export JAVA_HOME=/home/jinwatanabe/.asdf/installs/java/corretto-17.0.4.9.1
export PATH=$JAVA_HOME/bin:$PATH
```

# おわりに

毎回スムーズに入れられないのでまとめてみました
AWS CloudFormation で環境構築を自動化しよう！～Part1～

## はじめに

「AWSってよく聞くけど、何から始めればいいの？」

そんな悩みをお持ちのあなたへ。 AWS CloudFormation の基本的な機能と使い方を解説します。

###  AWS CloudFormation とは？

 AWS CloudFormation は、EC2 や ELB などの AWS リソースの環境構築を自動化できるサービスです。設定ファイル（テンプレート）を作成すれば、クラウドフォーメーションが自動的にリソースを作成・設定してくれるので、手作業によるミスを防ぎ、構築時間を大幅に短縮できます。

###  AWS CloudFormation のメリット

* **構築作業の自動化:** 手作業での設定ミスを防ぎ、作業時間を短縮できる
* **環境の複製・再構築が容易:**  同じテンプレートを使用することで、開発環境、テスト環境、本番環境など、複数の環境を簡単に作成できる
* **インフラストラクチャのコード化:** インフラストラクチャをコードで管理することで、変更履歴の追跡やバージョン管理が容易になり、開発プロセスを効率化できる

###  AWS CloudFormation の基本機能

* **リソースの作成:** テンプレートに記述されたリソースを自動的に作成します。依存関係がある場合も自動的に解決
* **リソースの変更:** 同じテンプレートを使用しても、最新の定義に基づいてインフラの状態を更新
* **リソースの削除:**  スタックに含まれるすべてのリソースを一括で削除できます。依存関係も自動的に解決

###  AWS CloudFormation の用語

* **テンプレート:** スタックの設計図。どのリソースをどのような設定で起動するかを記述
* **スタック:** 単一のユニットとして管理できる AWS リソースの集合体
* **変更セット:** スタックの更新によってリソースにどのような影響が出るのかを事前に確認するためのもの

###  AWS CloudFormation を使った環境構築の流れ

1. **テンプレートの作成:**  テンプレートを作成
2. **テンプレートのアップロード:** 作成したテンプレートをアップロード
3. **スタックの作成:**  テンプレートに基づいてスタックを作成
4. **スタックの更新:**  必要に応じてスタックを更新

###  テンプレートに記述する内容

テンプレートには、リソースの種類、プロパティ、パラメータ、出力などを記述します。

### まとめ

 AWS CloudFormation は、AWS リソースの構築・管理を自動化することで、開発プロセスを効率化し、運用を安定させる強力なツールです。ぜひ活用して、AWS 環境構築をスムーズに進めていきましょう！ 

### 関係資料
[AWS CloudFormation#1 基礎編](https://youtu.be/4dyiPsYXG8I?si=EqvzF7c_glTJXzUo)
[AWS CloudFormation#1 基礎編/PDF](https://www.youtube.com/redirect?event=video_description&redir_token=QUFFLUhqbHVwaE1QSkFZbWNHTGNMQlRXWngtajhYUHlTUXxBQ3Jtc0ttUm9oSGMwZmo1NnhveGFDM2FrZDExc0tQcFB2cGw5RWpzT0tmUlpZZklWamVlZjUwYzBEVWxTQ1AzTjZzelJ1eG96b0VWNHNMdlR1UHFXVUVBRzFhVUM1RmV5cnBlRG1lbXVwTXlHT0hSenRjYTBaYw&q=https%3A%2F%2Fpages.awscloud.com%2Frs%2F112-TZM-766%2Fimages%2FAWS-Black-Belt_2023_CloudFormation-1_0731_v1.pdf&v=4dyiPsYXG8I)
[AWS CloudFormation のドキュメント](https://docs.aws.amazon.com/ja_jp/cloudformation/)
【Flutter】知ってたら便利！パッケージをコマンドで追加する方法

## はじめに
みなさんこんにちは！
未経験エンジニア歴ほぼ半年です。
今回は、Flutterでよく使われるパッケージを簡単に入れる方法を書いてみました。
FlutterにはFlutter向けの特定のパッケージが豊富に用意されており、UIウィジェットやデバイス機能にアクセスするためのパッケージが多く存在しています。
パッケージを使用することにより、アプリの開発を効率的に進めることができます。

※Flutterのパッケージは基本的に**pub.dev**という公式リポジトリから管理され、簡単にプロジェクトに導入可能です。

一瞬で覚えられるので、皆さんのちょっとした知識としてぜひ読んでいただければ嬉しいです！

## 1. **パッケージの追加**
1. `flutter pub add`コマンドを使用して、追加したいパッケージを指定します
    
    例えば、`http`パッケージを追加したい場合は以下のコマンドを実行します：
    
   ```bash
    flutter pub add http 
   ```
    
    このコマンドを実行すると、以下のことが自動的に行われます：
    
    - `pubspec.yaml`ファイルの`dependencies`セクションに`http`パッケージが追加されます。
    - 最新バージョンの`http`パッケージがインストールされます。
## 2. **pubspec.yamlファイルの確認**
コマンドが成功した後、`pubspec.yaml`ファイルを開いて、指定したパッケージが`dependencies`セクションに追加されていることを確認できます。
    
例：
    
   ```yaml
    dependencies:
      flutter:
        sdk: flutter
      http: ^0.15.0
   ```
  

## **複数のパッケージを追加する**

複数のパッケージを一度に追加するには、それぞれのパッケージ名をスペースで区切って指定します。

   ```
    flutter pub add http provider
   ```

## **特定バージョンのパッケージを追加する**
特定のバージョンを指定してパッケージを追加する場合は、パッケージ名の後にバージョン番号を指定します。
   ```
     flutter pub add http@^0.15.0
   ```
## それより簡単な方法が、、 ！
pub.devで導入したいパッケージを探し、そのパッケージのInstallingタブを見ると、なんとなんとご親切にインストールのためのコードが書かれています！
コピーしてそのままターミナルにペーストするだけの簡単操作でパッケージを入れることが出来ます笑

## まとめ

- `flutter pub add パッケージ名` を使うと、`pubspec.yaml`ファイルに自動的にパッケージが追加され、インストールされます
- `flutter pub add パッケージ名@バージョン` を使うと、特定のバージョンを追加できます
【Dockerfile】Next.js のコンテナを作成する

# はじめに
この記事では、Dockerfile からビルドしたイメージを元に Next.js のコンテナを作成する手順について記載します。

# 開発環境
開発環境は以下の通りです。

- Windows11
- Docker Engine 26.1.1
- Node.js 20.17.0
- npm 10.8.2
- Next.js 14.2.8
- React 18

# Next.js アプリケーションの作成
まずは Next.js アプリケーションを作成します。

```powershell
npx create-next-app@latest
```

インストール中の質問は、以下のように回答します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/0a1c7702-8830-b202-ff61-30ac308577be.png)

ローカルサーバーを起動します。

```powershell
cd next
npm run dev
```

初期画面が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/96fad339-33e6-e6ee-3d4d-339aee02d9ce.png)


# Dockerfile の作成
Dockerfile を作成します。

```Dockerfile
FROM node:20-alpine

WORKDIR /app

COPY . .

RUN npm install
RUN npm run build

CMD ["npm", "start"]
```

# イメージのビルド
作成した Dockerfile からイメージをビルドします。

```powershell
docker image build --tag next-image:0.0.1 .
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/0f82852e-251b-826e-9c07-3b45e78ed44a.png)


イメージがビルドできたか確認します。

```powershell
docker image ls next-image
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/253306d8-cbf6-5d3f-d1bb-7c65829cb3b9.png)


# コンテナの起動
ビルドしたイメージからコンテナを起動します。

```powershell
docker container run `
--name app           `
--rm                 `
--publish 3000:3000  `
next-image:0.0.1
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/cca16c02-f5ce-8258-7e45-23954495ff05.png)

http://localhost:3000 にアクセスすると、先ほどと同じ初期画面が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/df5fbec6-d634-2475-4c8a-3b17f726f3c7.png)


# 関連
https://qiita.com/Yasushi-Mo/items/789871a7f3eb86b01a62

https://qiita.com/Yasushi-Mo/items/0c6ed9be966f764ff7ca
Scratch ライブラリ（縦スクロール）

# Scratch ライブラリ（縦スクロール）

縦スクロールもサラッと改造して用意しました。

これも、良くあるスクロール系は単に縦方向にスクロールするだけの感じですが、ある程度左右にも動かすことによって**より広がりのある表現**になっています。

* 同じ背景でずっと縦スクロールを表現
* 上下だけでなく左右に追加の背景画像を用意することにより、左右への部分的なスクロール拡張も可能
* スクロールスピードの動的な変更も可能

# 縦スクロール

## プロジェクト

ここにあります。

https://scratch.mit.edu/projects/1060885134/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579271/6479e7ac-e2d3-3d22-efec-75935f25b5cb.png)

## ライブラリ概要

横スクロール機能の機能のライブラリ化です。

## 使い方

### 基本

* 'Background Speed' 変数でスクロール速度を変更します
* `Background X' 変数をコントロールし、プレイヤーの位置を調整します
    * 動きがこのままで良ければ、プレイヤーのコードはサンプルをそのまま使えます
* 背景画像を差し替えて任意の背景でスクロールさせます
    * 背景画像はそれぞれ上下 20px が重なるようにコントロールされています
    * それぞれの上下 20px が同じ画像となるようにしてください

## 各種情報

### 変数・リスト
> In: 他スプライト管理の値を参照／Out: 本スプライトで設定

| 変数名 | In/Out |        概要        |
| ------ | :----: | ------------------ |
| `Background Speed (Initial)` | Out | スクロールスピードの初期値|
| `Background Speed` |   In   | スクロール速度 |
| `Background X` |   In   | 上下移動用 Y 座標補正値 |
| `Background Left/Right Available Area Size` | Out | 上下移動幅の限界サイズ |

### イベント

特になし。

### コスチューム

スクロール用に使う背景画像です。

| 背景画像名 |             概要             |
| :--------------: | ---------------------------- |
| Background1  | 背景画像1 |
| Background2  | 背景画像2 |

## 拡張ポイント

### 背景画像

* 背景画像 1, 2 の上下 20px が重なるように画像を作成してください
* 左右移動用に必要な幅の画像を付けておいてください

### 初期設定値の変更

以下の値を Initizalize ブロック定義の中でしているので、必要に応じて書き換えてください。

|変数名|概要|備考|
|:---|:---|---|
|`Background Speed (Initial)`|スクロールスピードの初期値|マイナスで上スクロール|
| `Background Left/Right Available Area Size` | 左右移動幅の限界サイズ | 正の整数で最大 480 までです |

---

色々ネタはあるのだが、記事を書く時間が作れていない。頑張ろう。

---

> [Scratch ライブラリと Scratch Tips 目次へ戻る](https://qiita.com/Kray-G/items/e1900d5b6c6dcfbe5321/)
【python入門】関数定義と位置専用引数・キーワード引数について

# はじめに

関数定義と位置専用引数・キーワード引数について解説したいと思います。
この記事を読んで、他の言語にはないと思われる、デフォルト引数・位置専用引数・キーワード引数について理解してもらえればと思います。

もし、参考になった場合はいいねをお願いします。

Python基本構文については[【Python入門】基礎構文のまとめ](https://qiita.com/atsuuun/items/60e2cec16add7091b109)でまとめてますので、参考にしてください。

## デフォルト値引数
デフォルト値引数とは、引数にデフォルト値を指定することができます。
例えば、```def f(i=1)```とすることで```f()```として呼び出すと、```i=1```を省略することが可能です。
デフォルト値引数の一度しか評価されないため、ミュータブルオブジェクトを利用する場合について注意が必要となります。

値を引き継ぐ・引き継ぎたくない場合については、以下のように定義できます。

### 値を引き継ぎたい場合
以下のようにイミュータブルオブジェクトを渡した場合は引き継がれます。
```python
    def f(a, L=[]):
        L.append(a)
        return L
    
    print(f(1))
    print(f(2))
    print(f(3))
```

### 値を引き継ぎたくない場合
以下のようにイミュータブルオブジェクトのデフォルト値をNoneにすることでオブジェクトは引き継がれません。
```python
   def f( a, L=None):
        if L is None:
            L = []
        L.append(a)
        return L
    
    print(f(1))
    print(f(2))
    print(f(3))
```

用途によって使い分けてください。

## 関数を呼び出す際の引数について
引数とは関数に渡す値となりますが、引数には２種類（位置専用引数、キーワード引数）あります

* 位置専用引数
引数の順番によって値を指定する引数となります。値を渡す順番を間違えると意図しない結果となります。
```python
complex(3,5)
complex(*(3,5))
```
引数の順番、または可変長引数```*```を使用した場合は位置引数となります。

* キーワード引数、可変長キーワード引数
引数の前に識別子付き,dictionaryとして渡された引数のことを言います。
例えば[complex()](https://docs.python.org/ja/3/library/functions.html#complex)の呼び出しでは以下は同等になります。
```python
complex(real=3,image=5)
complex(**{'real':3,'image':5}})
```
引数に識別子(例：real=)を付与、または```**```を付与することでキーワード引数となります。

### 引数の```*```と```**```の意味について

#### ```*```について
```*```については利用箇所により意味が変わるため、どのような利用方法についてなるか説明したいと思います。
* 位置引数```*args```　関数の引数として```*```を利用する場合
関数の引数として任意の数の引数を受け取るために使用します。
```python
def func(*args):
    for arg in args:
        print(arg)

func(1,2,3)
func(1,2,3,4)
```
* アンパッキング
アンパッキングとはリスト、辞書、タプル等から要素を取り出す操作のことを言います。

```python
# numbersというリストを作成し、printでアンパッキング
numbers = [1,2,3]
print(*numbers)
```
```python
# numbersリストをアンパッキング
numbers = [1,2,3]
numbers_ex = [*numbers,4,5]
```

#### ```**```について
* キーワード専用引数```**kwargs``` 関数の引数で```**```を使用する場合、辞書引数として受け取れます。
```python
def func(**kwargs) :
    print(f"{kwargs=}")

func(a=1,b=2)
func(a=1,b=2,c=3)
```

## 関数の引数について
python関数定義についての位置専用引数・キーワード引数については記載方法が少し特殊となります。パターンがあるので覚えておくとコードが読み易くなります。

1. 位置専用引数と、キーワード引数
以下のような形式が多い
``` def func(args, key=None)```
1. 位置専用引数またはキーワード引数
```/```以降は位置専用引数またはキーワード引数どちらも利用可能
```def func(args1, args2, /, keyword):```
呼び出し方は以下のどちらでも可能
```func(1,2,keyword=3)```
```func(1,2,3)```
1. キーワード引数のみ
```*```以降の引数についてはdictonary型のみ利用可能
```def func(args1, args2, *, keyword_only):```
呼び出し方は以下のようになる
```func(1,2,keyword=3)```
```func(1,2,3)```　# NG
1. 可変長引数と可変長キーワード
```def func( *args, **kwargs):```
```*args```は可変長引数をうけとり、```**kwargs```はdictionaryを受け取ります。

# まとめ
関数の引数について解説しました。
引数の意味を理解することでpythonドキュメントを正しく読めるようになり、関数の利用方法についてもサンプルコードを探さず開発ができると良いかともいます。
売上予測



+ [売上予測の設定したが、私の商談しか表示されず、他ユーザの商談を選択・表示することができない状況です。](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007ifkm8)

:::note
個々のユーザへ割り当てされていないのでしょうか。
＜確認方法＞
ユーザ>ユーザの情報セクション”売上予測を許可” 
がㇾになっているか

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/b15826d2-52fc-c4f2-1c28-9eaff2d16dba.png)
:::
WinDbg で実行結果をログファイルへ出力する方法

コマンドの実行結果が長いとき、スクロールしても最初のほうがきれてしまい実行結果を確認できないことがあると思います。また、実行結果を他の人に渡す場合も文字を選択後にコピー＆ペーストするのは、なんだかなぁーって感じがします。

WinDbg には、実行したコマンドとその結果を自動的にログに出力してくれる機能があるので、その方法を紹介します。特に難しくなく、ログファイルを指定後、コマンドを実行、ログファイルを閉じるといった流れです。

WinDbg って何？という人は、こちらの投稿をチェックしてください。

https://qiita.com/ausamayuni/items/968bb331a333c63481f3

## コマンド
```powershell
# ログファイル指定
# /t オプションでタイムスタンプをファイルの末尾につけてくれます。
.logopen /t c:\path\to\windbg.log

# いろいろコマンドを実行
!analyze -show

!mex.crash

!mex.tasklist

!vm 1

# ログファイルを閉じて、ログ終了
.logclose
```

参考URL：
.logopen (Open Log File) - Windows drivers | Microsoft Learn
https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/-logopen--open-log-file-

.logclose (Close Log File) - Windows drivers | Microsoft Learn
https://learn.microsoft.com/en-us/windows-hardware/drivers/debuggercmds/-logclose--close-log-file-
Mulesoft IDP

[MuleSoft Composerまとめに戻る](https://qiita.com/Keiji_otsubo/items/ef2fdd213551168c360b)

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

[Use AI to effortlessly extract data from documents in any format](https://www.mulesoft.com/platform/intelligent-document-processing)


+ [Is there a way Mulesoft IDP can identify the language of the document to be scanned?](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007erMGh)

:::note
1. MuleSoft IDP は現在、英語、スペイン語、ドイツ語のみをサポートしています。
2. IDP は言語を識別できるはずですが、この設定を変更することはできず、製品の設定に依存するため、主要言語の選択は疑わしいものになります。
3. 他の言語を使用したい場合は、製品チームが利用可能になるまで待つ必要があるかもしれません。
:::

+ [ドキュメントが IDP にインポートされ、レビューされた後、統合がどのように機能するかがよくわかりません。たとえば、ドキュメントがレビューされると、Mulesoft Anypoint Platform はメッセージを受信し、JSON 出力を読み取って外部システム (Salesforce など) にインポートできるようになりますか?](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000J5lTE0AZ)

:::note
IDP でドキュメント アクションを作成して公開したら、ドキュメント アクションを Anypoint フローに統合できます。Exchange のドキュメント アクションのエンドポイントを確認すると、POST および GET エンドポイント、または公開されたすべてのドキュメント アクションが表示されます。Anypoint フローは、S3 バケット、Salesforce などのドキュメント ストレージからドキュメントを取得し、IDP にフィードして処理します。IDP はドキュメントを受信すると、後で結果をポーリングするために使用する実行 ID を返します。

IDP で送信されたドキュメントのステータスに基づいて、Anypoint フローを介して結果を取得できます。

ここで、ドキュメントに手動検証が必要であるとします (ステータス: MANUAL_VALIDATION_REQUIRED)。その場合、レビュー担当者は IDP UI を介してドキュメントを検証する必要があり、検証が完了すると、ドキュメントのステータスは SUCCEEDED になります。

ドキュメントを IDP に送信し、10 ～ 15 秒待ってから結果をポーリングし、ステータスが「MANUAL_VALIDATION_REQUIRED」の場合は、電子メールまたは Slack でレビュー担当者に通知し、ドキュメント ID とともにメタデータをメッセージ キューに書き込むように Anypoint フローを設計できます。

別のフローをスケジュールして、メッセージング キューからのドキュメント ID でステータスを確認し、ステータスが「SUCCEEDED」のときに別のフローを呼び出すことができます。

IDP のコールバック機能を使用することもできます。この機能では、ドキュメントのステータスが SUCCEEDED、FAILED、または MANUAL_VALIDATION_REQUIRED の場合に、定義した URL を IDP がコールバックします。

https://docs.mulesoft.com/idp/automate-document-processing-with-the-idp-api
:::

+ [MuleSoft IDP によるラジオ ボタンの選択やチェックボックスの読み取りで不正確な結果が発生する](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000J6QNN)

:::note
IDP によるラジオ ボックスとチェック ボックスの抽出には、Einstein ではなく NLP テクノロジーを使用することをお勧めします。また、プロンプトを作成するときは、ドキュメント自体の特定のテキスト (「性別は男性が選択されていますか?」や「性別は女性が選択されていますか?」など) を使用するようにしてください。
:::
SQLで複合キーの複数データ取得するための条件を作成【Python】

## 説明

ループで条件を作成
- `enumerate()` を使って、`data` の各要素をループしています。
- `start=1` でカウンタを`1`から始めます。

各ループのステップで以下の処理を行っています
- `(id = :id{cnt} AND code = :code{cnt})` という文字列を `cond` リストに追加。
- `param` 辞書にパラメータとして `id` と `code` の値をキー付きで追加 (`id{cnt}` や `code{cnt}` 形式)。

例えば、最初のループでは次のように処理されます
- `cond.append('(id = :id1 AND code = :code1)')`
- `param['id1'] = 10`
- `param['code1'] = 'A10'`

条件の結合
- `cond` リストに保存された各条件を `" OR "` で結合し、最終的なSQL条件を作成します。
```python
data = [
    { 'id': 10, 'code': 'A10' },
    { 'id': 20, 'code': 'A20' },
    { 'id': 30, 'code': 'A30' },
]

cond = []
param = {}
for cnt, item in enumerate(data, start=1):
    cond.append(f'(id = :id{cnt} AND code = :code{cnt})')
    param[f'id{cnt}'] = item['id']
    param[f'code{cnt}'] = item['code']

print(" OR ".join(cond))
# (id = :id1 AND code = :code1) OR (id = :id2 AND code = :code2) OR (id = :id3 AND code = :code3)

print(param)
# {'id1': 10, 'code1': 'A10', 'id2': 20, 'code2': 'A20', 'id3': 30, 'code3': 'A30'}
```
【RubyKaigi2024】チャットを活用した参加方法はおススメ！

# はじめに
RubyKaigi2024（2024年5月15-17日）に参加しました。RubyKaigiへの参加は今回で2回目でした。[前回](https://qiita.com/Takaharu_01/items/fac8829bebd59c441308)はオンライン参加、今回はリアル参加（先輩と二人）でした。今回はチャットを繋げた状態で講演を聞くスタイルを取り、そのスタイルがあまりにもよかったので紹介します。

https://rubykaigi.org/2024/

# チャットを活用した方法
**①チャットはラインを利用し、常に連絡できる状態にする。**
**②講演が始まったら発表内容に関するメッセを送りあったり、終わったら感想を送る。**
たったこれだけですが、効果としては、
**①構えることなく楽しく講演を聞くことができた**
**②他の講演がどんな感じだったのか迅速に共有できた**
です。

# おわりに
今回は二人でリアル参加し、チャットを活用した方法で講演を聞きました。その結果、楽しく講演を聞くことができました。また、公演は各会場で開催されるので、「あっちの講演どうたったかな」と気になっている講演の情報を互いに共有することができました。次参加する際もチャットを活用していきたいと思います。
無料で使える生成AIおすすめ3選！用途別の使い方と特徴を徹底比較

生成AIって、どれを選べばいいか迷いますよね。種類が多すぎて、どれが自分の仕事に役立つのか分からない…そんな悩み、あなただけじゃありません。

本記事では、無料で使える高性能なAIを3つ厳選してご紹介します。あなたの仕事や学習を効率化し、新たなアイデアを生み出すためのヒントがきっと見つかります。今すぐこの記事を読み進めて、あなたにぴったりのAIを見つけてみませんか？

[>> 生成AIによってITエンジニアの仕事が奪われる！？](https://taishi-official.com/2024/04/16/2149/)

## 生成AIとは？知っておくべき革新的技術

生成AIとは、新しいコンテンツを創造する能力を持つAIシステムのことを指します。テキスト、画像、音声、動画など、さまざまな形式のデータを生成することができます。

### 生成AIの仕組みと特徴

- 機械学習
生成AIは大量のデータを学習し、そのパターンを理解します。
- パターン認識
学習したパターンを基に、新しいコンテンツを生成します。
- 創造性
既存のデータを組み合わせて、全く新しいアイデアや表現を生み出すことができます。

### 生成AIの主な応用分野

- テキスト生成
記事、詩、スクリプトなどの文章を自動で作成。
- 画像生成
写真やイラストなどのビジュアルコンテンツを作成。
- 音声合成
人間の声を模倣した音声を生成。
- 動画制作
アニメーションや実写風の動画を自動生成。

[>> 生成AIがもたらす影響について](https://taishi-official.com/2024/09/05/2874/#toc4)

### 無料で使えるおすすめの生成AI3選：生産性を高める最強ツール

生成AIの発展により、私たちの日常生活やビジネスにおける作業効率が飛躍的に向上しています。しかし、どの生成AIツールを選べばよいのか悩む方も多いのではないでしょうか？今回は、無料で利用できる特におすすめの生成AIツール3つをご紹介します。おすすめの生成AIは、以下の3つです。

- <b>Gemini</b>：多用途で気軽に使える万能ツール
- <b>Claude</b>：高品質な日本語文章生成に特化
- <b>Perplexity</b>：正確な情報収集と検索機能を備えたAI

[>> 各生成AIの詳細説明はコチラ！](https://taishi-official.com/2024/09/05/2874/#toc5)

## 生成AIってどうやって勉強すればいい？→スクールの活用も検討

生成AIは、私たちの仕事や日常生活を大きく変革する可能性を秘めた革新的な技術です。これからの時代を生き抜くために、<b>生成AIに関する知識やスキルを身につけることは、もはや選択肢ではなく必須</b>となっています。

しかし生成AIはまだ比較的新しい技術分野であり、関連情報が分散していることもあって、個人で体系的に学習するのは容易ではありません。そこで、効率的かつ効果的に生成AIスキルを習得したい方には、<b>「DMM 生成AI CAMP」</b>の活用をおすすめします。

[>> 「DMM 生成AI CAMP」について詳しく紹介！](https://taishi-official.com/2024/09/05/2874/#toc10)

## まとめ

今回はおすすめの無料で使える生成AIなどについて紹介いたしました。紹介した3つの生成AIツールは、それぞれに特徴があり、用途によって使い分けることで最大限の効果を得ることができます。

今回紹介した生成AIを適切に活用することで、あなたの日常生活や仕事の生産性を大きく向上させることができるでしょう。ぜひ、自分に合ったツールを見つけ、生成AIの力を最大限に活用してください。

### おまけ

https://qiita.com/taishi_will_be/items/70a8ce8938944286b487

<strong>＼Follow me／</strong>

- X（旧Twitter）：https://x.com/taishi_it_blog
- Instagram：https://www.instagram.com/taishi_will_be/
- YouTube：https://www.youtube.com/@Engineer_ILLBE
- TikTok：https://www.tiktok.com/@taishi_will_be
- note：https://note.com/taishi_wii_be/

<br>

最後にいいねお願いします:thumbsup:
OMC206C

https://onlinemathcontest.com/contests/omc206/tasks/6042

それぞれの貨幣を何枚ずつあげるかは他の貨幣のあげ方には関係ないので貨幣ごとに考えることができる。

5枚の貨幣を5人の子供にあげる分け方は、
5枚の貨幣を1列に並べて、4本の棒で仕切ることと同じなので、5枚の貨幣と4本の棒を1列に並べることと同じ。
したがって$_9C_5=\frac{9×8×7×6×5}{5×4×3×2×1}=126$

①すべての貨幣の分け方は、
$_9C_5×_8C_4×_7C_3×_6C_2=126×70×35×15$。
②それぞれの子が 紙幣の枚数>貨幣の枚数 なので、19枚のあげ方は固定される。
残り1枚を誰にあげるかは5通り。

①×②=23152500
M5Stamp S3でアナログ入力して遊ぶ(電圧表示)




x 過去ログをみよ


結果


![o_coq370.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/71bcfc74-bfec-de21-d739-2df383f844f7.jpeg)

![o_coq371.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/52a6f4de-3a1a-ccef-d8a0-ec61ad3009ee.jpeg)


プログラム


```c++



//dennatu_hennkan1_S3_1


//定義
#define ADC1 15


//初期化
void setup() {

  //シリアルポートの初期化
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);

  //GPIOの初期化
  //アナログ入力の時は、特になし

}//setup


//メインループ
void loop() {

  int sensorValue; //センサーの読み取り値
  int Voltage;     //電圧

  //センサーから値を読み込む
  sensorValue = analogRead(ADC1); //S3

  //センサーの値から電圧に変換
  Voltage = (sensorValue * 3300) >> 12; //4096step-adc  (>>12)=(/4096) [S3]

  //電圧を表示
  Serial.print(Voltage);
  Serial.print("mV");
  Serial.println();

  delay(500);//0.5秒待つ

}//loop



```
Gumroad 不正働く？



なかなかPayPalに入金されないなと思ったら、
ずっと繰越処理になっているし(問い合わせ中)、

そもそも売上金と入金予定額が半分ほど減って数値があっていない。



問い合わせを押してもこんな画面。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17342/ee4062b2-3888-112f-bd82-a2feb573edcd.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/17342/f193716e-6bb9-8f43-405f-0affab91f97a.png)

https://x.com/gumroad
も無反応だ。
Dateによるソート

```ts
interface DateItem {
  id: number;
  date: string;
}

const dateItems: DateItem[] = [
  { id: 12, date: '2021-10-12' },
  { id: 1, date: '2021-10-01' },
  { id: 2, date: '2021-10-02' },
  { id: 11, date: '2021-10-11' },
  { id: 3, date: '2021-10-03' },
  { id: 4, date: '2021-10-04' },
  { id: 5, date: '2021-10-05' },
  { id: 10, date: '2021-10-10' },
  { id: 7, date: '2021-10-07' },
  { id: 6, date: '2021-10-06' },
  { id: 9, date: '2021-10-09' },
  { id: 8, date: '2021-10-08' },
];

const sortedDateItems = dateItems.sort((firstItem, secondItem) => {
  return new Date(firstItem.date).getTime() - new Date(secondItem.date).getTime();
});

console.log(sortedDateItems); 

```
Why Salesforce CPQ Might Fail Your Business and How to Avoid It?

Salesforce CPQ (Configure, Price, Quote) is a powerful tool designed to streamline the sales process by automating quoting, pricing, and proposal generation. However, despite its potential, businesses may encounter challenges that lead to CPQ failure. Here’s why Salesforce CPQ might fail your business and how working with experienced <b><a href="https://revsolutions.co/salesforce-cpq/">Salesforce CPQ consultants</a></b>
 can help you avoid these pitfalls.

<b>1. Lack of Customization</b>
One of the main reasons businesses struggle with Salesforce CPQ is the lack of proper customization. Every business has unique pricing models, product configurations, and sales workflows. Without tailoring CPQ to fit these needs, the system can become cumbersome, leading to inefficiencies and inaccurate quotes.

<b>2. Poor Integration with Existing Systems</b>
For Salesforce CPQ to work efficiently, it must integrate seamlessly with other business systems like CRM, ERP, and billing platforms. When integration is poorly managed, data silos can occur, leading to inconsistent information across platforms and delayed sales processes.

<b>3. Inadequate Training and Adoption</b>
Even the most advanced tools can fail if your team doesn’t know how to use them effectively. Without proper training, sales teams may struggle to adopt Salesforce CPQ, leading to misquotes, missed opportunities, and frustration.

<b>4. Overcomplicating the Setup</b>
A common mistake is overcomplicating the Salesforce CPQ configuration with too many custom rules, approval processes, and pricing tiers. This can slow down the quoting process and frustrate users.

By partnering with expert Salesforce CPQ consultants, you can avoid these challenges and ensure that Salesforce CPQ becomes a valuable asset to your business, driving efficiency and growth.
分割キーボードのテンティングとパームレストについて

## テンティング(tenting)で手首が浮き疲れる
テンティング(tenting)とは、キーボードを立てることにより手首の回転を抑え自然な姿勢でタイピングを行う方法のことです。

テンティングはキーボードが打ちやすくなるだけでなく、左右のスペースが空くため間に本などを置きやすくなるのもメリットです。

![S__21340162.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2864472/688a3df2-96cf-2709-1cf6-6b77ff158f4d.jpeg)

しかし、このテンティングは

1. ホームポジションの位置が高くなる
2. 手と机の接する部分が減る

ことで腕が疲れやすくなります。下の図は右がテンティングしてます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2864472/196b6cda-eace-4fc9-b625-a1a02c16ca08.png)

## パームレストで解決
この「手が疲れる問題」パームレストで解決できました。普通の解決法ですが、テンティングでこうなることはあまり言及されてない気がしたため記事を書きました。

ちなみに僕はこのパームレストを使っています。背が高く反発力もあるところがお気に入りです。少し大きめです。

https://www.sanwa.co.jp/product/syohin?code=TOK-ERG5DGY-S

## 具体的なテンティング方法
僕はこちらの記事を参考にしてテンティングしました。

https://bou7254.com/posts/keyball39-tenting-device-ball-head-magnet-vs-smartphone-stand

ここまで読んでいただきありがとうございました。最後にこちらも読んでいただけると幸いです。

https://qiita.com/uenotakato/items/51b734428a769604cc1d
同一のショートカットを指定して順番に選択できるようにする

## はじめに

[Godot](https://godotengine.org/)ではボタンにショートカットを設定できますが、複数のボタンで同一のショートカットを設定すると、次のように最後に設定されたボタンが反応します。

![before.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/953829/e37e1038-781c-72f7-91f1-7e017c4578ea.gif)

ここでは、ペイントツールでSキーを押すと選択ツールが順番に切り替わるように、次のように同一のショートカットで複数のボタンが順番に反応することを目指します。

![after.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/953829/286b3bd1-565c-41b8-962b-857beef1d718.gif)

## 環境

v4.2.2.stable.official [15073afe3]

## ShortcutManager

ショートカットを独自に処理するために`ShortcutManager`というクラスを用意します。

```gd:shortcut_manager.gd
class_name ShortcutManager
extends Node

signal handled(targets: Array[Variant])

var _shortcut_map = {}


func _shortcut_input(event):
	var event_name = ShortcutManager._get_event_name(event)
	var targets = _shortcut_map.get(event_name)
	if targets == null:
		return

	handled.emit(targets)
	get_viewport().set_input_as_handled()


func add_shortcut(shortcut: Shortcut, target: Variant):
	for event_name in ShortcutManager._get_shortcut_event_names(shortcut):
		if _shortcut_map.has(event_name):
			_shortcut_map[event_name].append(target)
		else:
			_shortcut_map[event_name] = [target]


static func _get_shortcut_event_names(shortcut: Shortcut):
	return shortcut.events.map(_get_event_name)


static func _get_event_name(event: InputEvent):
	return StringName(str(event))
```

`add_shortcut`メソッドでショートカットと、それに反応する対象を登録します。
クラスの使用側がショートカットに対するアクションを判別できれば、`target`はなんでも構いません。

`_shortcut_input`メソッドでイベントが登録されていれば、`handled`シグナルを発信します。
`handled`シグナルで送られるのは、トリガーとなったショートカットが紐づけられたすべての`target`です。

## ボタンのリスト

次のようにノードを組み合わせ、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/953829/56e9c6c2-d28d-5653-c62f-6f73dbb4241a.png)

`ButtonList`ノードに次のようなスクリプトをアタッチすれば、上記のGIFような動作になります。

```gd:button_list.gd
extends VBoxContainer

@onready var buttons = [
	$Button1,
	$Button2,
	$Button3,
	$Button4,
	$Button5,
	$Button6,
]

var selected_button: Button = null


func _ready():
	for button in buttons:
		button.toggled.connect(_on_button_toggled.bind(button))
		$ShortcutManager.add_shortcut(button.shortcut, button)
		button.shortcut = null

	$ShortcutManager.handled.connect(_on_shortcut_manager_handled)


func _on_button_toggled(toggled_on: bool, button: Button):
	if toggled_on:
		selected_button = button


func _on_shortcut_manager_handled(targets):
	var index = targets.find(selected_button) + 1
	var button = targets[index % len(targets)]
	button.button_pressed = true
```

ここで各ボタンにはボタングループとショートカットが設定されており、Toggle Modeがオンになっています。

`ShortcutManager`ノードには上記の`ShortcutManager`クラスのスクリプトをアタッチします。ベースノードは`Node`です。

## おわりに

以上でペイントツールのツールボタンのショートカットのようなものがGodotで実装できます。

GDScriptにも早くジェネリクスが導入されてほしいですね。
【Power Apps】無料の個人アカウントでも使えるって

Power Appsをご存じだろうか。名前くらいは聞いたことあるが実際に何をするものなのかはわかってないという人も少なくないのではないか。私もその一人だ。

windows更新プログラムのインストール完了を待ちながら暇つぶしに記事を漁っていたらPower Appsでアコーディオンを使ってみた！などPower Appsやってみたものを読んでいるうちに触ってみたいという欲求にかられ、手を出してしまった。

調べてみると、無料の開発者プランを使用することができるらしい。
https://www.microsoft.com/ja-jp/power-platform/products/power-apps

無料。無料とか割引とか、そういうのがあれはほいほい手を出してしまうのは生まれ持っての嵯峨である。ではでは。

microsoftの個人アカウントはメールアドレスさえ持っていれば誰でも作成することができ、個人アカウントで開発者プログラムに参加すればmicrosoftサービスを無料で触ることができるらしい。暇な日の需要が満たされる。
https://learn.microsoft.com/en-us/office/developer-program/microsoft-365-developer-program-faq

早速、開発者プログラムに参加してみた。
https://developer.microsoft.com/en-us/microsoft-365/profile

ちなみに参加方法はmicrosoftアカウントでログインした後、
* 国
* 会社名
* 電話番号（SMSで届くコードの認証が必要）

を入力し、どんなサービス使いたいの？なにが君を突き動かしたの？私と何がしたいの？など、おとゲーのメン〇ラ気質な彼女ばりの質問に答えていくと登録・セットアップが完了してダッシュボードに入ることができた。

参加といっても要は登録。言い回しをそれっぽくきれいにする意味はないと思う。なんかこれ言ったらビ〇ガン辺りに怒られそうだ。（こじれた男の偏見）

デベロッパープログラムってなんだ？と思った時のＦＡＱ
https://learn.microsoft.com/en-us/office/developer-program/microsoft-365-developer-program-faq

「コンセプト」の一覧に「Power Apps でアプリを作成する」があったので誘われるがままタップしてみた。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/608177/4cd25af7-e84f-22f6-feaf-051fb9cdc8ec.png)

「あなたの好みに合うコンテンツが見つかりませんでした。」
こじれすぎていたせいか。そうか、見つかりませんでしたか。じゃあ他をあたります。その前にPower Appsさわります。

リンクの先はスターターガイド
https://learn.microsoft.com/en-us/power-apps/maker/

ちょっとやってみるか。（続ける意思があればつづく）
Docker(Docker-Compose)でGitlabを構築

# 背景
サクッと構築しちゃおうと着手したら、意図せずはまってしまったので、忘備のために、手順を記録する。

# 構築環境
ローカル環境に構築するだけだったので、ネットワーク内にDNSサーバは存在せず、ローカルIPでサーバに接続している。
また、1つのサーバで複数のアプリケーションを動作させていたのでGitlabへのアクセスは80ポートではなく、ほかのポートを指定する必要がある。

# ハマった事象

以下のように、サーバのローカルIP（'192.168.1.2'）を指定した。
また、ポートは8080を利用することにした。
external_urlを指定しないと、gitlab上で、clone用のURLが、ホスト名称になってしまうため、「external_url 'http\://192.168.1.2:8080'」として、指定した。

```docker-compose.yaml
version: '3.6'
services:
  gitlab:
    image: 'gitlab/gitlab-ce:17.2.4-ce.0'
    container_name: gitlab
    hostname: '192.168.1.2'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://192.168.1.2:8080'
    ports:
      - '8080:80'
      - '4433:443'
      - '2222:22'
    volumes:
      - '/home/com/gitlab/config:/etc/gitlab'
      - '/home/com/gitlab/logs:/var/log/gitlab'
      - '/home/com/gitlab/data:/var/opt/gitlab'
    shm_size: '256m'
```

上記のdocker-composeを用いて、コンテナを起動し、ブラウザからアクセスしたが、gitlabに接続できなかった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/404547/ee47a3f9-69a3-042e-9b54-b19300a9e487.png)

# 原因と解決方法
下記の記事を見て解決することができた。。。

https://qiita.com/tomoyukikobori/items/b61ab74d827ac580931f

### 原因
下記のようにexternal_urlをポート付きで指定すると、gitlabのNGINXのポートも自動的に同じポート(本ケースでは8080)に切り替わるようになっているらしい。
```docker-compose.yaml
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://192.168.1.2:8080'
```

### 解決方法
下記のようにNGINXのポートを指定してあげることで解決できた。
```docker-compose.yaml（NGINX待ち受けポートの指定）
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://192.168.1.2:8080'
        nginx['listen_port'] = 80
```

# まとめ
上記を踏まえて、正常にアクセスすることができた際のdocker-compose.yamlを記載する。
```docker-compose.yaml
version: '3.6'
services:
  gitlab:
    image: 'gitlab/gitlab-ce:17.2.4-ce.0'
    container_name: gitlab
    hostname: '192.168.2.10'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://192.168.2.10:8080'
        nginx['listen_port'] = 80
    ports:
      - '8080:80'
      - '4433:443'
      - '2222:22'
    volumes:
      - '/home/com/gitlab/config:/etc/gitlab'
      - '/home/com/gitlab/logs:/var/log/gitlab'
      - '/home/com/gitlab/data:/var/opt/gitlab'
    shm_size: '256m'
```

# P.S
gitlabの公式ページをよく見たら、確かにコンテナ側の指定ポートが、external_urlで指定したURLのポート番号(8929)と一致していた。。。
```docker-compose.yaml
version: '3.6'
services:
  gitlab:
    image: gitlab/gitlab-ee:<version>-ee.0
    container_name: gitlab
    restart: always
    hostname: 'gitlab.example.com'
    environment:
      GITLAB_OMNIBUS_CONFIG: |
        external_url 'http://gitlab.example.com:8929'
        gitlab_rails['gitlab_shell_ssh_port'] = 2424
    ports:
      - '8929:8929'
      - '443:443'
      - '2424:22'
    volumes:
      - '$GITLAB_HOME/config:/etc/gitlab'
      - '$GITLAB_HOME/logs:/var/log/gitlab'
      - '$GITLAB_HOME/data:/var/opt/gitlab'
    shm_size: '256m'
```


https://docs.gitlab.com/ee/install/docker/installation.html
SES やめます

新卒から 3 年ほど自社開発の企業でお世話になったのち、SES 企業に転職し約 1 年半ほど経ちましたが
この度、SES を辞めてまた別の自社開発の企業に転職することとなりました。
せっかくなので、SES として働いて得た知見を共有します。

※この記事は SES がダメとか、自社開発が良いとかそういった内容ではありません。

## 自己紹介
色々あってまさかの 27 歳で新卒の私を拾ってくれた会社で Web アプリの開発に 3 年従事、
現在は関西の SES の企業でフロントエンドプログラマをしております。
入社してからの 1 年半で関わった案件は 2 件です。
9 月末に退職します。

## やめるに至った理由
### モダンな開発にアサインされなかった
1 回目の転職理由として、Qiita や Zenn などで見かけるような方々がたくさんいるモダンな開発現場で働きたいという願望がありました。

当時は何を持って「モダン」なのか良く分かっておらず、とりあえず React とかなんか良い感じのライブラリとかフレームワークを扱っている案件なら、アジャイルで開発していて、なんかよくわからないけど CI/CD 環境が整っているだとか、ドキュメントも Excel 方眼紙じゃなくてクラウドサービス使って管理しているだとか、色々と工夫して開発やってるんだろうな〜くらいに思っておりました。

ですが、結局のところ運良く React を扱っているプロジェクトにアサインはしてもらえたものの、開発環境は前職とさほど変わりませんでした。

まぁ 2 案件しか関わっていないので、運が悪かっただけかもしれません。

## SES で嫌だったこと
### スキルシートでしか判断されないこと
SES 企業に入るとまずどの言語を何年経験してきたか、というスキルシートを記入させられます。
お客様はこのスキルシートを見てエンジニアがどの程度できるエンジニアかを判断します。
なので、これは 27 歳で新卒というメチャクチャな学生時代を送ってきた自分が悪いのですが、たとえ年数以上に経験値が高くても評価されませんし、仕事も任せてもらえません。
(React 未経験で React の案件にアサインしていただけたのは本当に運が良かったと思います)

### 技術的な議論が発生しないこと
これは案件によりけりなのかもしれませんが、自分が関わった案件は両方ともこの傾向がありました。
仕様に懸念があって改善案を挙げたり、仕事が進めにくいので改善を要求したりとしてきましたが、ほぼその場で却下されます。

もちろん私の意見が悪かったり、ウォーターフォールという開発の特性上仕方のないところもあるのですが、一度却下された意見と同じことを上の方が言うとすんなり通ったりするところを見ると結構嫌な気持ちになりましたね……。

また周囲のメンバーは、あまり私のように意見を言わず言われたことを淡々とこなすタイプが多く、そういった人たちを見ているうちに、自分はこの仕事向いていないのかなぁと思うようになりました。

## 逆に SES で良かったこと
### 人間が出来た人が多い
もちろんこれも案件によるのでしょうが、どちらの案件も大手の SIer さんと一緒に働かせていただいたのですが、
やはり大手に勤められるだけあって、良い人が多かったです。というか、良い人しかいませんでした。
仕様について何度も質問しても嫌な顔せず回答してくださりますし、なんと言うか……人間としての余裕があるんですよね。
そう言う人たちに囲まれて仕事をしていく中で、自分はなんて小さいヤツなんだろうと気づくことが出来たことは SES として働いたこの 1 年半での一番大きな収穫だったと思います。

## SES はこんな人におすすめ

### 関東在住の方
いきなり何？　と思われるかもしれませんが、地方の SES と東京の SES では結構差があると思います。
今関わっている案件では大阪と東京で分かれて仕事しており、一時期ヘルプとして東京のメンバーに関わる機会があったのですが、明らかにレベルが違いました。

なので、転職活動中に東京の SES 企業のオファー面談のときに「地方でモダンな開発案件はない。地方で 4 年半働いたキミよりこっち (東京) で 1 年働いたエンジニアの方が経験値高いよ」と言われても不思議とあまり怒りが湧いて来なかったんですよね……。まぁそうだろうな〜と思いました。

### 25 歳くらいまでの方
この業界は幅広い知識が求められますので、まずは広く浅くいろんな案件に関わって、そこからキャリアプランを描いていけば良いのかな〜と思いました。

## SES 企業を検討する際注意すべきこと
### 商流の深さ
求人に書かれている「プライム案件⚪︎% !!」みたいな、アレのことです。

商流の深さは給料に関わってきますが、それだけではなく深ければ深いほど裁量も与えてもらえずやりがいが無くなる傾向にあると思います。
今の案件で言えば、タスクをジョインしている企業ごとに割り振られているため、より適正のあるコーディングのタスクを回してもらえないということがありました。

### 自分の求めている案件を本当に持っているか
ここはマジで確認すべきです。
なので、自分の希望をうまく言語化できるようにできるようにしておく必要があります。
私は希望をうまく伝えられず失敗しました。

## 最後に
これから SES を目指す方の何かヒントになれば幸いです。
【AWS】クラウドコンサルティング経験で学んだこと

※不備があったため、以前投稿した記事の再投稿になります。
SIer→コンサル業界に転身し、AWSのクラウドコンサルティングを1年経験してきて、個人的に重要だと感じたポイントをまとめました。

# クラウドコンサルティングで重要なポイント一覧
- システムへの理解を深めること
- ドキュメントスキル
- ベストプラクティスに囚われないこと
- もちろん技術力！

# 各ポイントごとの説明
各項目の詳細を以下に記載していきます。

## システムへの理解を深めること
クラウド移行を提案する立場では、クライアントのシステムを利用する担当部署の方とコミュニケーションをとることが必須となります。ここで認識しておくべきポイントは、**必ずしも担当部署の方がシステムの現状を把握していない**ということです。私の経験では、情報システム部門や担当のベンダにシステム運用は任せきりで必要な手続きだけ関与しているといったケースがありました。
クラウド移行の提案で忘れてはいけないものとして、**システムのAsISにXXXの課題があるため、解決するためにToBeのクラウド構成を提案すること**があります。そのため、最適なクラウド移行を実行するためには提案者側がシステムの現状を理解することが重要になります。ToBeだけ鮮明に描いて、AsISが不透明にならないよう気を付けしましょう！
現行システムのドキュメントを読むことも重要ですが、規模によってはかなりのドキュメント量になります。個人的には、情報システム部門や担当ベンダ等システム理解がある方にヒアリングをすることも効率的なのでおすすめです。

## ドキュメントスキル
コンサルティング会社に従事して感じたこととして、ドキュメントに関する指摘がSIerと比較するとかなり厳しいことがありました。文字のフォントサイズやオブジェクトの少しのずれ等かなり詳細な部分まで指摘があります。最初、詳細な部分はどうでもいいと考えていました。
ドキュメントにこだわる理由は、**コンサルティング業界はSIerと異なり、ソフトウェアやソリューション等の製品で商売はしていないため**です。コンサルティング業界で提供しているサービスは形のないものであるため、クライアントから信頼を得るためには、ドキュメントの品質やレスポンスのスピードなど印象的な部分がポイントになってきます。
そのため、クラウド以外の提案にも当てはまることですが、ドキュメントスキルは重要になります。

## ベストプラクティスに囚われないこと
AWSでは、クラウドでシステムを構成するうえでのベストプラクティスを提供しています。RDS等のマネージドサービスの採用やAPI Gateway、Lambdaを使ったサーバレス構成等、あまり深く考えずに検討してしまうことがありがちですが、なんでもベストプラクティスに準拠していればいいとは限りません。前項でも記載した通り、**システムをクラウドに移行する背景には、何かしら達成したいミッションがあります**。そのミッションを達成するために、必要な構成をとることが重要になります。
例えば、コスト削減がミッションであるのに、高可用性のためにサーバ冗長化を提案してしまうと、かえってコスト増加してしまう可能性があります。
YYYのミッションを達成するために、ベストプラクティスのZZZ構成を提案するというように、必ず理由をつけてベストプラクティスを採用するようにしましょう。

## もちろん技術力！
最後は技術力です。クラウドコンサルティングの課題として、提案したものがシステムの実現が極めて難しい「絵に描いた餅」状態になっていることがあります。
絵に描いた餅になる要因として大きく2つあると考えます。
1つは前項で記載した「システムへの理解が浅い」ことです。システムの現状をあまり理解しない状態で提案をすると、クライアント特有の制約に引っ掛かることや、改修に大きな時間がかかってしまい想定しているスケジュール内で移行できない、といった懸念があります。
2つ目はクラウドに関する知識が足りないことです。AWSのサービスには便利なサービスがたくさんあります。その知識が少ないことで、ある程度マネージドサービスに置き換えて効率化できるものをすべてIaaSサーバで実装したり、サービスのユースケースや制約を調査せずに提案して後々利用できないことが判明したり、といった懸念があります。
**クラウド構成を提案するうえで、「提案する選択肢を増やす」こと、「実現可能な構成を提案する」ことが重要になります。そのために、「選択肢を増やすための幅広い知識を持つ」こと、「実現可能な提案をするためのサービスの詳細を調査し理解する」ことが必要と考えます。**
ただし、自分自身でも最低限知識を持っておくことは重要ですが、AWSのサポートやソリューションアーキテクト、社内の有識者に頼ることもおすすめです。

# 最後に
自身の備忘も兼ねて、クラウドコンサルティングで重要と感じたポイントをまとめました。あくまで個人的な意見ですので、参考程度に確認いただければと思います。
追加で何かあれば本ブログで更新していこうと思います。間違い等あればご指摘いただけると幸いです。
OCIイベント通知の内容を見やすくする

# 概要
[Oracle Cloud Infrastructure（OCI）](https://www.oracle.com/jp/cloud/)の監視機能としてモニタリングやイベント等があり、あらかじめ設定したメールIDやSlack等の通知先に通知することができます。
モニタリングについては通知内容は情報を見やすくフォーマットする機能がありますが、イベントについてはJSON形式の内容をそのまま送信します。
今回はSlackに送付するイベント通知内容をFunctionsで処理して見やすくしました。

# Functions作成
[OCIのAlarmをLINE Notifyを利用して通知](https://qiita.com/tktk2712/items/37f2c82b6f458f87f733)を参考にFunctionsを作成します。
コードは以下のように作成しました。
- OCIイベントが作成するJSONを読み込み、必要な要素を抽出して通知テキストを作成しました。今回はブロックボリュームバックアップ作成イベント情報から日付やボリューム名等の情報を抽出しています
- 送信元をわかりやすくするためユーザー名や絵文字を追加しています
- これらの情報を事前に取得したSlackのWebhook URLにPostします

```func.py
import io
import json
import logging
import requests

def handler(ctx, data: io.BytesIO = None):
    try:
        body = json.loads(data.getvalue())
        text =  "eventType: " + body["eventType"] + "\n" \
                + "eventTime: " + body["eventTime"] + "\n" \
                + "volumeName: " + body["data"]["additionalDetails"]["volumeName"] + "\n" \
                + "status: " + body["data"]["additionalDetails"]["status"]
       
        data = json.dumps({
           "text": text,
           "icon_emoji" : ":cloud:",
           "username" : "OCI Events"
        })

        requests.post('https://hooks.slack.com/services/.....', data=data)

    except Exception as e:
        print('ERROR: bad Event!', flush=True)
        raise
```


```func.yaml 
schema_version: 20180708
name: event2slack
version: 0.0.3
runtime: python
build_image: fnproject/python:3.11-dev
run_image: fnproject/python:3.11
entrypoint: /python/bin/fdk /function/func.py handler
memory: 256
```

```requirements.txt
fdk>=0.1.75
requests
```
# イベントのアクションに追加
通知したいイベントのアクションに以下のいずれかを登録します
- アクションタイプ：ファンクションで作成したFunctionsを登録
- アクションタイプ：通知でトピックにFunctionsを登録

# 結果
イベント結果のJSONをそのままNotoficationsでSlackに送信した場合とFunctionsで見やすくしてSlackに送信した場合の比較です。
## NotoficationsでSlackに送信した場合
<img width="800" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730992/212108ff-d0a4-de3c-6c0a-87a21a837773.png">

## Functionsで見やすくしてSlackに送信した場合
<img width="800" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2730992/81b1c531-6c80-8798-c703-3227332cd320.png">
PostgreSQLで取得結果の文字列に含まれるカンマ区切りの要素の数をカウントする方法【PostgreSQL】

## 説明

### 方法 1: `string_to_array` と `array_length` を使用
`string_to_array` 関数を使って文字列をカンマで分割し、`array_length` 関数で配列の要素数をカウントします。
- `'10,20,30,40,50'` はカンマ区切りの文字列です。
- `string_to_array('10,20,30,40,50', ',')` は、カンマで区切られた文字列を配列に変換します。
- `array_length(..., 1)` は、その配列の要素数を返します。
```sql
SELECT array_length(string_to_array('10,20,30,40,50', ','), 1) AS element_count;

 element_count 
---------------
             5
```
### 方法 2: `regexp_split_to_array` と `array_length` を使用
もし文字列の中に余分なスペースや特殊なパターンが含まれている場合、`regexp_split_to_array` 関数を使って正規表現で文字列を分割できます。
- `regexp_split_to_array('10,20,30, 40, 50', '\s*,\s*')` では、カンマの前後にあるスペースを無視して要素を分割します。
```sql
SELECT array_length(string_to_array('10,20,30,40,50', ','), 1) AS element_count;

 element_count 
---------------
             5
```
### 方法 3: 単純なカンマの数を数える方法
要素数がカンマの数に1を加えた値である場合（要素がカンマで分割されている前提）、`length` 関数と `replace` 関数を使ってカウントできます。
- `length('10,20,30,40,50')` は元の文字列の長さを取得します。
- `replace('10,20,30,40,50', ',', '')` はカンマをすべて削除し、その後の長さを取得します。
- 元の長さからカンマを削除した後の長さを引くと、カンマの数が分かります。そこに `+1` することで、要素の数を取得します。
```sql
SELECT array_length(string_to_array('10,20,30,40,50', ','), 1) AS element_count;

 element_count 
---------------
             5
```
（完全自分用メモ）モジュール設計・モジュール結合度

# モジュール設計
（応用情報技術者合格教本のp498あたり）

モジュール分割技法には「データの流れ」に注目した技法と「データの構造」に注目した技法に分けられる。

### データの流れに注目した技法
* STS分割
    * プログラムは入力→処理→出力の構造でできているという前提に立った分割技法
    * 入力処理機能（Source）、変換処理機能（Transform）、出力処理機能（Sink）の3つにモジュールを分割する
    * 最大抽象入力点と最大抽象出力点でモジュールを分割する
* TR分割
    * トランザクション分割の意
    * 下記の3つのモジュールに分割する
        * トランザクションを入力するモジュール
        * トランザクションを属性ごとに各モジュールに振り分けるモジュール
        * トランザクションごとの処理モジュール
* 共通機能分割
    * 上記のモジュールの中で共通する機能を持ったモジュールがあった時、そのモジュールを共通モジュールとして独立させる方法


### データの構造に注目した技法
* ジャクソン法
    * JSP木（基本・連続・繰り返し・選択の要素を組み合わせた木構造）に則ってプログラムを構造化する
* ワーニエ法
    * 集合論に基づいてプログラムを構造化する
    * ジャクソン法は出力データに注目してデータを構造化するが、ワーニエ法は入力データに注目し、「いつどこで何回」という考え方で展開する


# モジュール結合度

モジュール結合度の種類は6種類。
下記の表では下に行くほど結合度が低いため望ましい。
覚え方は結合度が**弱い順**（＝良い順）に「デスセガキナ」（Death SEGA 来な）

| 種類名 | 説明| 具体例|
|--|--|--|
| データ結合 | 必要なデータだけを引数として渡す | 得点と順位を表示するクラスがあった場合、得点と順位のデータのみを渡す| 
| スタンプ結合 | 構造体データを引数として渡す | structやdata classで作成したデータの塊を渡す |
| 制御結合 | モジュール内の処理を制御するデータを引数として渡す | 得点を表示するかどうかを引数の内容によって決める |
| 外部結合 | 必要なデータだけを外部宣言し、他のモジュールからの参照を許可する | データをpublicで宣言し、他のクラスから参照する | 
| 共通結合 | 共通領域に参照したデータを参照する | データ格納庫的なクラスを作り、そのクラスのデータを参照する|
| 内容結合 | 他のモジュールを直接参照している| goto文で他のモジュールのデータを参照しにいく |

※具体例は人によって捉え方がまちまちらしいのであくまで一例
Git revertは「コミット修正内容を元に戻す」のであってコミットそのものは無かったことにならない

## 概要

Gitのrevertは過去の修正内容を無かったことにするときによく使われる。
しかし、コミットそのものを打ち消すのではなく「コミット修正内容」のみを無かったことにする。
「マージコミットをrevertしたんだけどやっぱりもう一度マージしたい」みたいなときにGithubでPRが作れないみたいなことが時々起きる。そういうときにどうしたらいいのかを考えて見る。

## 例

下記のような状態を例に上げる。

![CleanShot 2024-09-06 at 00.12.20@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/600366bd-bbb5-a9ec-7180-737155abc44a.png)

今の困りごとは、やっぱりfeat/firstブランチをマージしたくなり、リバート後にGithubでまたPRを作ろうとした。
でも下記のような画面になってPRが作れない。

![CleanShot 2024-09-06 at 00.10.58@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/0797e813-db8b-628f-de64-b044a69c1b80.png)

「There isn’t anything to compare.」を和訳すると「比較するものはなにもない。」

要はbaseブランチとfeat/firstブランチに差分が無いと言われている。
このときに「???」となるはずだ。だってPRのマージコミットXは直前でリバートしているのだから、feat/firstブランチのコミットAはbaseブランチに入っていないはずなので差分があるはずだからだ。

## なんで差分が出ないのか

これはbaseブランチに「Aコミットの修正内容」はリバートされているので入っていないが「Aコミットそのもの」は入ってしまっているからだ。
コミットベースでPR作成可否は比較しているため（たぶん）Aコミットはすでにbaseブランチに入っているため差分は無いものとして扱われている模様

## 解決策

今回の問題の解決策は複数存在する。
一つ上げると「マージコミットXのリバートコミット」を「リバート」してしまうことが挙げられる。（裏の裏が表になるように「修正内容の打ち消し」を「打ち消す」事により何も無かった状態になる。）
他にもfeat/firstブランチに空コミットを行って差分を作りPRを作るという方法なんかもある。
aws-cli バージョン違いによるcloudformation deploy 実行ステータスコード255

bashのスクリプト内でaws-cliを使ってCloudFormation deployを行っている部分があり、新たにCloudFormation deployのコードを追加したところ、aws-cliのバージョンによって、挙動が異なったので、ソレについて話す。

## きっかけ
会社のサービスで使用している年期の入ったCircleCIのビルドスクリプトに、新しくCloudFormation Deployを追加したところ、スタックに変更がなくメッセージ出力されてスクリプトの後続処理が停止した。
```shell
No changes to deploy. Stack ExampleStack is up to date
```

## 原因
ローカル環境で動作を確認したときのaws-cliバージョンとCircleCIで動いているaws-cli
のバージョンが異なることで、CloudFormation deployの返すスクリプトの終了ステータスコードが異なっていたことで、ローカル環境では発生しなかった問題がCircleCIで発生した。

### ローカル環境
- python: v3.9.13
- aws-cli: v2.17.40
### CircleCI
- python: v3.6.5
- aws-cli: v1.15.7

### aws-cli: v2.17.40
```shell
$ aws cloudformation deploy \
--template-file ./deploy \
--stack-name ExampleStack \
--capabilities CAPABILITY_NAMED_IAM

echo $? # 0
```
v2.17.40では、終了ステータスコードを0の値を返しており、正常終了している。
### aws-cli: v1.15.7
```shell
$ aws cloudformation deploy \
--template-file ./deploy \
--stack-name ExampleStack \
--capabilities CAPABILITY_NAMED_IAM

echo $? # 255
```
v1.15.7では、終了ステータスコードを255の値を返しており、これが原因でスクリプトが途中で終了してしまいそれが原因で後続処理が止まっていた。

## 解決方法
古のCircleCIのビルドスクリプトを修正したくなかったので、何か良い方法はないかと
aws-cliのドキュメントを漁っていたら、`--no-fail-on-empty-changeset`をつけることで終了ステータスコードを0にできそうだったのでそれで対応した。
```diff: 変更後
$ aws cloudformation deploy \
--template-file ./deploy \
--stack-name ExampleStack \
--capabilities CAPABILITY_NAMED_IAM \
+ --no-fail-on-empty-changeset
```
deploy
https://docs.aws.amazon.com/cli/latest/reference/cloudformation/deploy/
`--no-fail-on-empty-changeset (boolean) Specify if the CLI should return a non-zero exit code if there are no changes to be made to the stack. The default behavior is to return a non-zero exit code.`

## まとめ
古のビルドスクリプト化しないようにしっかりCIはメンテナンスしたほうがいい
このビルドスクリプトはどうやらgitの更新履歴を見る限り7年前にfirst commitされていて、そこから秘伝のタレを今日まで作っていたようだ

技術負債をしっかり解消できるように努めていきたい
・・・そういうことに前向きな組織じゃないと難しいけどネ
文字列を繰り返してカンマ区切りの文字列を作成　リスト化　指定範囲の要素を取得【Python】

## 説明

1 . 文字列を繰り返してカンマ区切りの文字列を作成
- `["1,2,3"]*3` は、リスト `["1,2,3"]` を3回繰り返します。その結果、リストは `["1,2,3", "1,2,3", "1,2,3"]` になります。
- `",".join(...)` は、このリストの要素をカンマで結合し、1つの文字列にします。
- 結果として、`1,2,3,1,2,3,1,2,3` という文字列が生成されます。
```python
# 文字列を繰り返してカンマ区切りの文字列を作成
print(
    ",".join(["1,2,3"]*3)
)

# 1,2,3,1,2,3,1,2,3
```
2 . カンマ区切りの文字列をリスト化
- 前のコードと同じく、まず `",".join(["1,2,3"]*3)` で `1,2,3,1,2,3,1,2,3` という文字列が生成されます。
- その後、この文字列に対して `split(",")` を呼び出します。これにより、文字列はカンマで区切られ、それぞれの部分がリストの要素として分割されます。
- 結果として、リスト `['1', '2', '3', '1', '2', '3', '1', '2', '3']` が生成されます。
```python
# カンマ区切りの文字列をリスト化
print(
    (",".join(["1,2,3"]*3)).split(",")
)

# ['1', '2', '3', '1', '2', '3', '1', '2', '3']
```
3 . リストから指定の範囲の要素を取得
- `",".join(["1,2,3"]*3)` によって、文字列 `1,2,3,1,2,3,1,2,3` が生成されます。
- `split(",")` によって、この文字列をカンマで分割し、リスト `['1', '2', '3', '1', '2', '3', '1', '2', '3']` が生成されます。
- `start = 2` と `count = 6` なので、リストの `start-1` 番目（インデックス1、つまり2番目の要素）から `start-1+count` 番目（インデックス7まで）の要素を取得します。具体的には、`['2', '3', '1', '2', '3', '1']` です。
```python
# リストから指定の範囲の要素を取得
start=2
count=6
print(
    (",".join(["1,2,3"]*3)).split(",")[start-1:start-1+count]
)

# ['2', '3', '1', '2', '3', '1']
```
ASP .NET Core Tips - Tooltipの表示

# 概要
ASP .NET CoreでWebアプリを作っているときに、
Tooltipがうまく表示されなくて調べた内容の備忘録。

# 環境
Visual Studio 2022（Version 17.11.2）
ASP .NET Core
.NET 8

# やりたかったこと
ラベルタグのマウスホバーで、以下のページにあるようなツールチップを表示したい。
https://getbootstrap.jp/docs/5.3/components/tooltips/


# 調べたこと
上記のWebページに書いてある通りにしてみたつもりなのに、
Titleプロパティを入れた時の普通のTooltipで表示される・・・。

一旦、ASP .NET Coreベースのプロジェクトで使用されているBootstrapのバージョンを調べてみる。
bootstrap.jsの先頭には以下のように書かれていた。

```html
/*!
  * Bootstrap v5.1.0 (https://getbootstrap.com/)
  * Copyright 2011-2021 The Bootstrap Authors (https://github.com/twbs/bootstrap/graphs/contributors)
  * Licensed under MIT (https://github.com/twbs/bootstrap/blob/main/LICENSE)
  */

```

ブラウザの開発者モードで見てみると、
JavaScriptの部分で「bootstrap is not defined」となっていた。

上記のページでは、以下のように書いてと指示されている。

``` js
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
```

WebページのScriptタグ内に上記を書いているけど、単純にそれだと動かない。
Scriptタグにtype属性を使って「module」を指定する必要があるとのこと。
⇒https://stackoverflow.com/questions/75868787/tooltip-bootstrap-5-2-5-3-still-not-working-in-my-end

``` js
<script type="module">
    const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]');
    const tooltipList = [...tooltipTriggerList].map((tooltipTriggerEl) => new bootstrap.Tooltip(tooltipTriggerEl));
</script>
```
ライブラリで優勝、Pythonでリスト内の重複した辞書を削除する

## やりたいこと
リストの中に入ってる複数の辞書が、**キーと値が全て同じもの**であったら重複として削除したい

## 結論
`unique_everseen`を使用する。
```bash
pip install more-itertools
```
<br>

辞書の順番が異なっていたら別物として扱いたい場合
```python:
from more_itertools import unique_everseen

# [0]と[3]は区別する
target_list = [
    {"item1": "value1", "item4": "value4"},
    {"item2": "value2", "item5": "value5"},
    {"item3": "value3", "item5": "value5"},
    {"item4": "value4", "item1": "value1"},
]

list(unique_everseen(target_list))
```

辞書の順番が異なっていても、キーと値が同じなら同一として扱いたい場合
```python
from more_itertools import unique_everseen

# [0]と[3]は同じとみなす
target_list = [
    {"item1": "value1", "item4": "value4"},
    {"item2": "value2", "item5": "value5"},
    {"item3": "value3", "item5": "value5"},
    {"item4": "value4", "item1": "value1"},
]

list(unique_everseen(target_list, key=lambda dict: frozenset(dict.items())))
```

## 補足
このケースにおいて考えられるパターンは以下2パターン。
どちらのケースにおいても、上記の実装は対応可能。

### パターン1
単純なdict
```python
target_list = [
    {"item1": "value1"},
    {"item2": "value2"},
    {"item3": "value3"},
    {"item1": "value1"},
]
```

### パターン2
dictの中にさらにdictが含まれてるパターン
```python
target_list = [
    {
        "item1": "value1",
        "item2": {
            "nest1": "nestValue1",
            "nest2": "nestValue2",
        }
    },
    {
        "item3": "value3",
        "item4": {
            "nest1": "nestValue1",
            "nest2": "nestValue2",
        }
    },
    {
        "item1": "value1",
        "item2": {
            "nest1": "nestValue1",
            "nest2": "nestValue2",
        }
    },
]
```

この関数知らなかったので、自作してた。。全然要らなかった。
```python
# 重複する辞書を削除
unique_list = list(
    {convert_to_hashable(d): d for d in target_list}.values()
)

def convert_to_hashable(obj: Any) -> Union[tuple, Any]:
    """再帰的に辞書やリストをハッシュ可能な形式に変換する関数

    Args:
        obj (Any): 辞書やリスト、またはその他の型を受け付けます

    Returns:
        Union[tuple, Any]: タプルに変換されたハッシュ可能なオブジェクト、または元のオブジェクト
    """
    if isinstance(obj, dict):
        # 辞書のキーと値をソートしてタプルに変換し、frozendictに変換
        # ソートするので順番が異なる辞書も比較可能
        return tuple(
            (key, convert_to_hashable(value)) for key, value in sorted(obj.items())
        )

    elif isinstance(obj, list):
        # リスト内の要素もハッシュ可能にするため、リストを再起的にタプルに変換
        return tuple(convert_to_hashable(item) for item in obj)
    return obj
```