OmniAICreator/Qiita-1.07M huggingface dataset:
REST APIでDynamoDBからデータを取得してみる

# 概要

本記事は [AWSでWebアプリを構築してみる](https://qiita.com/ti_and6id/items/9670ee60965a4d10bec7) シリーズの7回目の記事です。
前回の記事は [こちら](https://qiita.com/ti_and6id/items/3cc12183659493d735d7)。

# 本記事で作成する構成

[REST APIを作成してみる](https://qiita.com/ti_and6id/items/b5fd3eec7bf0a68ecc8c) では Lambda に直接レスポンスするデータ（JSON）を記載していましたが、今回はデータをDynamoDBから取得したものをレスポンスするようにしてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/74ae02a7-b9f2-5bd9-f921-892b23dc2991.png)

# REST APIでDynamoDBからデータを取得してみる

## 1. DynamoDBにテーブルを作成する

1. テーブルの作成では、テーブル名を任意の名前をつけ、今回は、パーティションキーに「id:文字列」、ソートキーに「name:文字列」としてみます（それ以外はデフォルト）

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/9ec000f2-19dd-c4e6-175c-686313e05e74.png)

## 2. テーブルにデータを入れる

1. DynamoDBのサイドメニューのPartiQLエディタから、Queryに以下を1行毎に実行して、テーブルにデータを入れる

    ```
    INSERT INTO "user" VALUE { 'id' : '1', 'name': '森 聡',     'age': 45 }
    INSERT INTO "user" VALUE { 'id' : '2', 'name': '井上 祐輔', 'age': 20 }
    INSERT INTO "user" VALUE { 'id' : '3', 'name': '高橋 智明', 'age': 35 }
    INSERT INTO "user" VALUE { 'id' : '4', 'name': '村田 正人', 'age': 53 }
    INSERT INTO "user" VALUE { 'id' : '5', 'name': '石井 俊輔', 'age': 37 }
    ```

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/a1701ef6-f9d7-d8c4-7782-c0ce4056b8cd.png)

## 2. LambdaからDynamoDBのテーブルを読み込む

1. マネジメントコンソールから Lambda を開き、[REST APIを作成してみる](https://qiita.com/ti_and6id/items/b5fd3eec7bf0a68ecc8c)で作成したLambda関数を開き、設定タブのアクセス権限に設定されているロールに対して、許可ポリシーからDynamoDBに対する読み取り権限のポリシーを追加する

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/20af4be8-b306-5c79-9c84-767192c7d96f.png)
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/4a1ab3e7-50a5-8c08-3574-fc607d32f5a6.png)

1. [REST APIを作成してみる](https://qiita.com/ti_and6id/items/b5fd3eec7bf0a68ecc8c)で作成したLambda関数を開き、コードタブで以下のように修正する

    ```
    import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
    import { DynamoDBDocumentClient, ScanCommand } from "@aws-sdk/lib-dynamodb";

    const client = new DynamoDBClient({});
    const docClient = DynamoDBDocumentClient.from(client);

    export const handler = async (event) => {
      const command = new ScanCommand({
        TableName: "user",
      });
      const result = await docClient.send(command);
      const response = {
        statusCode: 200,
        body: JSON.stringify(result.Items)
      };
      return response;
    };
    ```

## 3. CloudFront経由でのAPI呼び出しでテストしてみる

1. マネジメントコンソールから CloudFront を開き、[静的ウェブサイトをCloudFront経由のアクセスしか許可しないようにする](https://qiita.com/ti_and6id/items/e9ef9da23b3e33116f6b) で作成したディストリビューションを開き、ディストリビューションドメイン名をコピーし、末尾に「/api/」を付加してブラウザで開く

    ↓以下のようなレスポンスがあればDynamoDBからデータを取得したCloudFront経由でのAPI呼び出しに成功している
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3630916/f5255895-af65-2332-e0b6-3cf008b7e7d3.png)
adb devices -l で出た番号を変数に出力したい

Androidデバイスが複数あるとadb devices -lの後に番号を指定しなければならないため作成した。

下記は:の後を取得するようにした。

```
@echo off
setlocal enabledelayedexpansion

REM adb devicesコマンドを実行してデバイス番号を取得
for /f "tokens=1,2" %%i in ('adb devices -l') do (
    REM デバイスのシリアル番号は2列目（statusが"device"の行）にある
    if "%%j"=="device" (
        set DEVICE_ID=%%i
        REM コロンの位置を探して、それ以降の文字を抽出する
        for /l %%x in (0,1,255) do (
            set CHAR=!DEVICE_ID:~%%x,1!
            if "!CHAR!"=="^:" (
                set AFTER_COLON=!DEVICE_ID:~%%x+1!
                goto :FOUND
            )
        )
    )
)

:FOUND
REM コロン以降の文字列を表示（デバッグ用）
echo String after colon: %AFTER_COLON%

REM 続けて処理を行う
pause
```

adb devices -l でデバイス番号だけ取得したい時はこちら

```
@echo off
setlocal enabledelayedexpansion

:: adb devices -l の出力を変数に読み込む
for /f "tokens=1,2 delims=:" %%a in ('adb devices -l') do (
    if "%%a"=="usb" (
        set device_number=%%b
        echo デバイス番号: !device_number!
    )
)

:: 最後に変数 device_number を出力
echo 取得されたデバイス番号は: !device_number!

pause

```

ループさせる

```
@echo off
setlocal enabledelayedexpansion

rem aDBコマンドの結果を取得
for /f "tokens=* delims=" %%a in ('aDB') do (
    set "output=%%a"
)

rem カウント用の変数
set count=0

rem forループで区切り文字を ":" に設定し、4回目以降のトークンを取得
for /f "tokens=1,2,3,4,5 delims=:" %%a in ("%output%") do (
    set /a count+=1
    if !count! equ 5 (
        set result=%%a
    )
)

rem 結果を表示
echo Result: %result%
```
```
@echo off
setlocal enabledelayedexpansion

REM コマンドの結果を一時ファイルに出力
your_command > temp.txt

REM 結果を読み込んで最後の「:」以降の数字を抽出
set lastNumber=
for /f "tokens=* delims=" %%a in (temp.txt) do (
    set line=%%a
    for /f "tokens=1,* delims=:" %%b in ("!line!") do (
        set lastNumber=%%c
    )
)

REM 抽出した数字を表示
echo 最後の:以降の数字は: %lastNumber%

REM 一時ファイルの削除
del temp.txt

```
GMOのコンビニ決済の追加で躓いた時の話

## GMOのコンビニ決済を実装することになった理由
2024年8月21日にリリースされた中古/新品トレーディングカードを扱う通販サイト「トレオタ」の開発を進めている中で、既存の決済方法にコンビニ決済が含まれていないことが課題となりました。ユーザーにとって利便性の高いコンビニ決済を提供するため、急遽GMO PAYMENT GATEWAYのコンビニ決済APIを利用して、機能を追加することになりました。

https://www.toreota.com/

## GMOの決済APIについて
GMO PAYMENT GATEWAYが提供する決済APIには、以下の4つの主要な機能があります。

1. 取引登録: 決済の基本情報を登録する
1. 決済実行: 実際に決済を行う
1. 支払停止: 決済をキャンセルする
1. 取引状態参照: 決済のステータスを確認する

これらのAPIを組み合わせて、コンビニ決済の処理を実現しています。APIドキュメントには詳細な実装手順が記載されており、それに基づいてDjangoプロジェクトに導入しました。

 ## コンビニ決済の処理実装で躓いた箇所
 基本的な処理は、以前に実装したクレジットカード決済を参考にして比較的スムーズに進めることができました。しかし、APIに送信するリクエストデータの入力パラメータでエラーが頻発し、その対応に時間がかかりました。

### 最も多かったエラー: M01036001
特に厄介だったのが、M01036001エラー（「お問い合わせ先が指定されていません。」）です。このエラーが発生した原因は、リクエストデータに含まれるReceiptsDisp11、ReceiptsDisp12、ReceiptsDisp13というフィールドに正しい値を設定しなかったためです。

ReceiptsDispは1から10までのフィールドも存在し、これらはレシートに表示される値を指定します。しかし、1から10までのフィールドと11から13のフィールドは異なる目的で使用されますが、名前が似ているため混同しやすく、番号の違いを見落としてしまうことがありました。その結果、正しくない番号に値を設定してしまい、エラーが頻発しました。

さらに、POSレジに表示される文字列が英数字に対応していないこともエラーの原因の一つでした。このため、フィールドに設定する値を調整し、エラーが出ないように工夫しました。

 他のエラーについてはこちらから確認できます。
 
 https://mp-faq.gmo-pg.com/s/article/D00875

## コンビニ決済のコード作成で参考にしたサイト

基本的には GMO PAYMENT GATEWAYのドキュメントですが下記githubのページも参考にしていました。
 
 https://github.com/everright/gmo-pg-php/blob/master/src/GMO/Payment/ShopApi.php

djangoで開発していますが、参考にしやすかったです。
【Tablacus Explorer】フォルダを常に新しいタブで開く方法

こんにちは！

自分用の備忘録で「Tablacus Explorer でフォルダを常に新しいタブで開く方法」をメモしておきます。

上記についてググってもうまく探せず...

簡単な設定方法でしたがなぜか見つかりにくく手間取った

# Tablacus Explorer とは？
こちら
https://tablacus.github.io/explorer.html

↓こんな感じでファイルエクスプローラーにおいてタブが使える
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188038/a4212fc5-6644-fa4b-0b8d-6a706d17fd3f.png)

他にも、
・4分割～6分割～9分割など、分割数も増やせる
・アドオンが充実しており好みの設定が自由にできる
・軽い。ポータブルアプリでインストール不要

ブラウザはこだわってるけどファイルエクスプローラーはデフォルトを使っているという人は使ってみると作業効率UPするはず

# 困ったこと
自宅PCで活用していた
会社のPCでも利用したいとなった

フォルダを掘るとき、常に新しいタブで開くようにしたいが
その設定についてアドオンやアドオンの設定、そしてTablacus Explorer自体の設定を探したが見当たらず...

ググってみてもそれらしい記事がヒットせず
うーん...

# 解決方法
以下の手順で設定できた
ツール → オプション → タブ → 「新しいタブで開く」にチェック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/188038/e9fe2937-ca6e-33ca-b2e8-337708af1e11.png)

### なんだ簡単じゃないか😂
　
　
いや、簡単に見つかるだろうと思うが、
大量にアドオンがあり、タブプラスの設定を探してしまった、「常に新しいタブで開く」的なアドオンとして存在していたかも、そしてオプション内でツリーのカテゴリをクリックした中にも設定があった
という様々な要因から半日探してその翌日も探してもなかなかたどり着けなかった...😇

次回Tablacus Explorerの環境構築する際に思い出せるように、同様の困りごとに遭遇した人のためにメモしておいた

# 使っているアドオン
誰得かもしれないが、活用させていただいているアドオンについて列挙しておく
どれがデフォルトで入ってたか覚えてないが...↓
・タブプラス
・ダークモード
・重複タブ禁止
・ストライプ
・ロックされたタブを先頭に
・同一のロックされたタブがあれば開く
・タイトルバーを消す
・拡張子でアイコン変更
・拡張子で色分け
・高速一覧表示
・迅速なメニュー表示


以上です！

参考になったという方は👍をポチッとお願いします🙏
うっかりミスを修正! git commit --amend でスマートに履歴修正!

## はじめに
みなさん、コミットをした後に、「このファイルの変更をコミットし忘れた、、、」や「コミットメッセージを誤字ってしまった、、、」などあると思います。
そんな時にむちゃくちゃ使えるのがgit commit --amendコマンドです。では紹介していきます。

## git commit --amend

### 直前のコミットメッセージを変更する場合
```
git commit --amend -m <commit message>
```
ステージ領域に何もない状態でこのコマンドを入力すると、直前のコミットメッセージの内容を書き換えることができます。

### コミット済みのファイルの内容を変更する場合

```
git commit 
git add hoge.py
git commit --amend --no-edit
```
上記の例では、コミットした後にhoge.pyをコミットするのを思い出した場合を想定しています。
この時、たとえコミットした後でも、コミットしたかったファイルをステージング領域に置き、git commit --amend --no-editを入力することで、コミットメッセージを変更することなく、直前のコミットの内容にhoge.pyの変更を加えることができます。

## 最後に
git commit -amendってむちゃくちゃ便利なコマンドですよね。初めてこのコマンドを知ったときはとても感動しました。ぜひ使ってみてください！
【UiPath】【OCR】印鑑被りの文字を読み取るための前処理（画像の白黒2階調化）

# はじめに

- 本記事は、抽出対象文字列と印鑑が被るためにOCRで正確に読み取れないケースへの対処法を書きます。
- 記事の内容は、個人の見解または確認結果であり、UiPath の公式見解ではありません。
- 製品仕様や参考画像は 24.10 バージョンのもので構成しています。

# 説明用サンプル画像

![白黒2階調化1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/f17c946e-a621-7ecd-ab48-5d8ad508cbc3.jpeg)

上の画像を「ドキュメントをデジタル化」アクティビティ × Extended Language OCR で読み取ると次の様な結果が返ってきます↓↓

::: note warn
上段の会社名は「C」を「Q」に読み違えただけですが、下段の住所はまともに読めていません。
```
ABQ 株 式 会 社 〒 1 TA BC
```
:::

ワークフローで白黒2階調化した画像で試した結果↓↓

（白黒2階調化後の画像）
![白黒2階調化2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/0b02198a-3a09-fef9-25ef-e13a0cc600bd.jpeg)
（OCRの抽出結果）
```
ABC 株 式 会 社

〒 100-0004 東 京 都 千 代 田 区 大 手 町 1-1-1
```

# 実装例

![白黒2階調化3.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/ca1f3c5b-27ef-66f2-c9fc-efc1c5f2c3d4.jpeg)

1. 「コードを呼び出し」アクティビティを配置し、上図の様に引数を設定します。
1. 「コードを編集」で次のコードを貼り付け＞保存する

```
' 画像ファイルのパス
Dim inputImagePath As String = inputFilePath
Dim outputImagePath As String = outputFilePath
 ' 画像を読み込む
Using originalBitmap As New Bitmap(inputImagePath)
    ' 処理後の画像を作成する
    Using newBitmap As New Bitmap(originalBitmap.Width, originalBitmap.Height)
        ' 画像の各ピクセルを処理する
        For y As Integer = 0 To originalBitmap.Height - 1
            For x As Integer = 0 To originalBitmap.Width - 1
                Dim pixelColor As Color = originalBitmap.GetPixel(x, y)
                 ' 黒色を抽出し、それ以外は白色にする
                If pixelColor.R < Red AndAlso pixelColor.G < Green AndAlso pixelColor.B < Blue Then
                    newBitmap.SetPixel(x, y, Color.Black)
                Else
                    newBitmap.SetPixel(x, y, Color.White)
                End If
            Next
        Next
         ' 新しい画像を保存する
        newBitmap.Save(outputImagePath)
    End Using
End Using
```
::: note
本例では、RGBの引数の値はいずれも100を指定しています。
※一般的に黒色というとRGBは0,0,0をイメージされるかとおもいますが、スキャン画像の黒は色の幅があるので広めにとるために100を指定します。

RGBの赤の値（Int32）：100
RGBの緑の値（Int32）：100
RGBの青の値（Int32）：100
:::

（参考）RGB：50,50,50
![白黒2階調化4_50_50_50.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/6c0a5862-8603-9760-f683-c0f5772fe599.jpeg)

（参考）RGB：200,200,200
![白黒2階調化5_200_200_200.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301444/1fd12001-7eeb-831f-9eac-dc6b8fbba169.jpeg)

# さいごに
いかがでしたでしょうか。
デジカメの画像なんかで試すととんちんかんな結果となりますが、白黒＋αで構成されている請求書の様なドキュメントであれば印鑑だけ除くことはできるかとおもいます。
印鑑被りでお困りの方は是非一度試してみてください☆
最後までお読みいただきありがとうございます(・ω・)ノ
サイバーエージェント　プロトスプリントリーグで学んだことまとめてみた　第3弾

# はじめに
kigです．プロトスプリントリーグで学んだことを自分のメモと共にまとめます．
今回が最終回です！

# 再掲
今回のプロトスプリントリーグで開発したゲームは↓になります，ぜひプレイしてね．
[UnityRoom: ChargeCat](https://unityroom.com/games/chargecat)
#### 対象者
Unityの入門書を読み終えた後に，実際に何か開発したことがある方におすすめです．
まだ何も作ってないよという方でも，一度読んでみて，「これ実装したいけどなんかあの記事であったような...」となってもらえれば嬉しいです．

# 目次
[1. Interface](#1-interface)
[2. Abstruct](#2-abstruct)
[3. ScriptableObject](#3-scriptableobject)
[おわり](#おわり)

# 1. Interface
参考サイト；[C# インターフェースを使う実例](https://qiita.com/Nekomasu/items/fe175fbb2cd4282a0e1c)
インターフェースを使用するメリットは，継承さえすれば，最低限実装しなければいけない関数の実装を忘れることがなくなる(実装していなければエラーになるため)ことだと思います．

＋α：[Generic(ジェネリック)まとめ](https://hiyotama.hatenablog.com/entry/2015/06/02/090000)
↑関数定義時に，型を指定せず，呼び出す時にこちらで指定する方法．

# 2. Abstruct
参考サイト[抽象クラス（abstract）の使い方を学ぶ](https://xr-hub.com/archives/19842)

こちらもほぼインターフェースと同じと考えています．interfaceとabstructの違いについては，下記サイトが参考になります．

[abstract(抽象)クラス、Interfaceはどう使い分けるべきか](https://qiita.com/kokada420/items/1e9bc256f872782a3a60)

abstructは，そのabstructを継承するオブジェクトたちが，共通して使いたい関数がある際に使用します．(継承前に関数の内容が実装できるもの)
interfaceは，そのinterfaceを継承するオブジェクト側で，継承した関数の中身を実装します．という認識です！！難しい．

チーム開発で，この種類のオブジェクトには最低限この関数たちを実装してほしい！というのがある時は，実装忘れを防ぐためにも，interfaceまたはabstructを使用すると実装ミスが起こらなそう．また，いちいち同じような関数書くなと感じたらabstructを使うのが良さそうです．

# 3. ScriptableObject
参考動画：[ScriptableObjectの使い方について](https://www.youtube.com/watch?v=NqioWWDBG7I)

アイテムのデータベースとして，事前にアイテムの名前，価格，説明文などを用意しておいて，後から参照するときに使えそうです．動画内でも紹介されているように，配列を使用する際は，要素数ではなく，アイテム名(Type)で配列の中身を参照できるのもgood．

```cs:ItemDatabaseSO.cs
[CreateAssetMenu]
public class ItemDatabaseSO : ScriptableObject
{
    public List<ItemData> itemDatas = new List<ItemData>();
}

[System.Serializable]    // ItemDataの各値をUnityで設定できるようにする
public class ItemData
{
    public string name;
    public int price;
    public Sprite sprite;
    [TextArea]
    public string text;
}
```

```cs:GameManager.cs
public class GameManager : MonoBehaviour
    [SerializeField] ItemDatabaseSO itemDatabaseSO;

    private void Start()
    {
        Debug.Log(itemDatabaseSO.itemDatas[0].name);    // 参照する
    }
```
チーム開発のプロジェクトでよく見た，水色の四角+{}のアイコンの正体はこれでした．
シングルトンたちとの使い分け方も，実際に開発で使ってみてから考察したいところです．

# おわり
なんか第3弾の内容が薄い気がしますが，第1弾からたくさんインプットすることができました！！今開発しているゲームにも使えそうなことがたくさんあるので，実際に使ってみるのが楽しみです．今後学んだことを使用していく中で，新たに気づいたことがあればまた記事にしてみようと思います．
改めて，社員さんや今回お話しできた方，チームメンバーへ，ありがとうございました！
皆様良いゲーム開発Lifeを！！！！！
JDKトラストストアに証明書をインポートしたとき、Gradleの実行に反映されない場合の対処法

# 備忘録
Gradleで、自己署名証明書をサーバ証明書として持つNexus RepositoryにHTTPSでpublishする場合、Gradleを実行する端末のJDKのトラストストアにサーバ証明書をインポートする必要があります。

この際の注意点として、**インポートの前に`gradle`コマンドを実行している場合、インポート後に`gradle publish`を実行しても、サーバ証明書のインポートは反映されません**。

Gradleを一度実行するとバックグラウンドのデーモンが起動し、デーモンの起動中はサーバ証明書のインポートが反映されないためです。

反映させるためには、以下のコマンドでGradleのデーモンを終了させる必要があります。

```bash
./gradlew --stop
```

# 参考
- [Nexus RepositoryをHTTPS化し、Gradleで利用する際の手順](https://qiita.com/hisato_imanishi/items/b6cb3680c6fc08b005a1)
文系未経験エンジニアが「ここで躓いた！」シリーズ～認証局って何？編～

# はじめに
このシリーズでは、文系出身未経験入社の開発エンジニア2年目の自分が、
なんとなく分からずにモヤモヤしていた知識や疑問をざっくりと解説していきます。
ざっくりとなので、多少正確性を欠くことがありますが、もし誤った記載がありましたらご指摘いただけますと幸いです。

# 認証局とは？
### 前提知識：公開鍵暗号方式
認証局は、「ネット上で安全に情報のやり取りを行うため」に必要となる組織です。
認証局の必要性を理解するためには、**公開鍵暗号方式**を理解する必要があります。
知っているよという方は飛ばしてください。

（公開鍵暗号方式の例）
あるWebサイトAがあり、太郎さんがそのサイトでショッピングをするとします。
買った商品を太郎さんの家に届けるためには、WebサイトAに太郎さんの名前や住所などの個人情報を渡す必要があります。

このとき、太郎さんがWebサイトAに送信した個人情報を、第三者に傍受されるリスクがあります。
送信した個人情報はWebサイトAの運営者だけが知ることができるようにしたいので、以下の手順で個人情報を送信することにします。
>①太郎さんが入力した個人情報を暗号化する
>②WebサイトAに送信する（ここで傍受されても暗号化されているため、個人情報は分からない）
>③WebサイトAの運営者が暗号化された太郎さんの個人情報を復号する。

↑のような手順で暗号化して通信を行う方式を**公開鍵暗号方式**といいます。
①の暗号化のときに太郎さんが使う鍵→公開鍵
②の復号化のときにWebサイトAの運営者が使う鍵→秘密鍵
です。

公開鍵はその名の通り、公開して色んな人にばらまきまくってOKです。
秘密鍵はその名の通り、絶対に他の人に知られてはいけません。
他の人に知られてしまうと、通信を暗号化した意味がなくなります。

>補足：「鍵」というとイメージがつきにくいですが、公開鍵や暗号鍵の実体はバイナリ(0と1の羅列）です。ただし、実際に私たちが見るときには、バイナリをBase64エンコードという方法で文字列に変換した後です。
aGVsbG87EjsLske=　←　こんな感じの文字列です。
### 認証局の必要性
認証局は、「**公開鍵-webサイトのドメイン-webサイトの運営者の身元**」
の3つが紐づいていることを確認します。
確認できた場合は、TLS/SSL証明書（デジタル証明書の一種）を発行します。
>※証明書の実体は.pem/.crt/.cerなどの拡張子で保存されるファイルです。

ユーザーのブラウザ（chromeとかsafariとか）はwebサイトと通信を行う際、
webサイトのTLS/SSL証明書をチェックして、信頼できる認証局が発行しているかどうかや、証明書の期限が切れていないかなどを確かめます。
確認できなかった場合は、ユーザーに警告を出すなどして知らせます。

こうして、認証局があることで、（一応は）安全にwebサイトとユーザーが通信を行うことができるというわけです。

以下は補足になるので、読んでも読まなくてもよいです。

# 補足①：「信頼できる認証局が発行している」はどうやって確かめる？
OSやブラウザには「ルート証明書」というものが組み込まれていて、
この中に信頼できる認証局のリストが含まれています。
証明書のチェックを行う際には、このリストに含まれている認証局が発行しているかどうかをチェックしています。

# 補足②：認証局が証明書を出している＝安全なサイトではない！？
認証局はあくまで、
「**公開鍵-webサイトのドメイン-webサイトの運営者の身元**」
が一致しているかどうかが確認できれば、証明書を発行します。
そのため例えば、
>○○銀行のwebサイトのドメイン→www.marumaruGinko.com
>○○銀行の偽のwebサイトのドメイン→www.marumalGinko.com

のように、わざと酷似したドメインを取得した悪質なサイトだったとしても、
証明書の発行自体はできてしまうことがあります。
悪質なサイトであることが認証局に発覚した場合、証明書は取り消されますが、
証明書があるからといって必ずしも安全なサイトではないということは留意しておいた方がよいです。
>ちなみに、malというのはスペイン語で「悪」という意味で、偽サイトのドメイン名はスペイン語ジョークでした。「マルウェア」の語源である英語malicious（悪意のある）と同じ語源です。
英語もスペイン語も同じラテン語由来なので。

# 補足③ 認証局は公的機関？
「局」とついているので、なんとなく公的機関っぽいですが、民間の株式会社が運営していることが大半です。国によっては公的機関が運営していることもあるらしいですが。
どんな会社が運営しているのかというと、アンチウイルスソフトを売っているなど、
他の事業でもサイバーセキュリティに携わっているような会社が多いようです。

# おわりに
IT業界で当たり前のように使われている用語や概念って、
なにを指しているのか曖昧だったり、分かりづらかったりすることが多い気がします。

特に未経験で入社すると、「これってどういう意味なんだろう？」と思って調べてみても、結局曖昧な理解のままになってしまい、
上司の指示がちゃんと理解できずに怒られ、結果としてITエンジニアという仕事に苦手意識を持ってしまう...というようなことも少なくないのではないでしょうか？

この記事が、そんな悩みを抱えている方の一助になることができれば幸いです。
Windows Serverで自動ログオン（PowerShell）

# 経緯
- ParallelsなりHyper-VでWindows Server検証環境を動かすときに毎回Ctrl Alt Delでログインするのがめんどくさい。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/01be5da3-d9ef-6b9c-d578-6835c45fbdf2.png)

## 2025年追記
- Ctrl + Alt + Delキャンセルのコマンド追記しました。
```
# レジストリパスの定義
$WinlogonPath = "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon"
$PoliciesPath = "HKLM:\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System"

# 自動ログイン用の設定値
$DefaultUserName = "Administrator"  # 自動ログインするユーザー名
$DefaultPassword = "YourPasswordHere"  # ユーザーのパスワード
$AutoAdminLogon = "1"  # 自動ログインを有効化

# Ctrl+Alt+Del不要の設定値
$DisableCAD = 1  # 1にするとCtrl+Alt+Delが不要になる

# 自動ログインの設定
Set-ItemProperty -Path $WinlogonPath -Name "DefaultUserName" -Value $DefaultUserName
Set-ItemProperty -Path $WinlogonPath -Name "DefaultPassword" -Value $DefaultPassword
Set-ItemProperty -Path $WinlogonPath -Name "AutoAdminLogon" -Value $AutoAdminLogon

# Ctrl+Alt+Del不要の設定
Set-ItemProperty -Path $PoliciesPath -Name "DisableCAD" -Value $DisableCAD

# 設定完了メッセージ
Write-Host "自動ログインとCtrl+Alt+Del不要の設定が完了しました。再起動してください。"

```


# ポエム
- ParallelsのVMは再起動が3秒と爆速だったり、スナップショットが秀逸なので、自動ログインにより気軽に検証ができます。
- 本番環境ではもちろんパスワードを設定しましょう。
- Parallels Toolsを入れるとMacのTouchIDとの連携もできるようです。親切だなあ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/790864/23517875-8b49-0b68-ba5a-6172db7384b4.png)
爆速でPDFを全世界にS3で公開する方法

# はじめに
#### 爆速でPDFを全世界に公開したい時ありますよね？
そんな迷える初心者/一人情シスのみなさんにこちらの
S3を使った爆速公開術を授けます。

# 必要なもの
・AWSアカウント
・アップロードするPDF
・あきらめない心

## S3の設定
AWSマネジメントコンソールにログインし、S3サービスに移動します。
リージョンを選択したら「バケットを作成」ボタンをクリックします。
バケット名を入力します。（例：tonikakupdf001）

「パブリックアクセスをすべてブロック」のチェックを外します。
警告が表示されますが、今回はパブリックアクセスを許可する必要があるため大丈夫です！
その他の設定はデフォルトのままで、「バケットを作成」をクリックします。

## PDFファイルのアップロード

作成したバケットを開いて「アップロード」ボタンをクリックします。PDFファイルをドラッグ＆ドロップするか、「ファイルを追加」をクリックして選択し、「アップロード」をクリックしてファイルをアップロードします。

## PDFファイルの公開設定

アップロードしたPDFファイルを選択します。
「アクション」ドロップダウンメニューから「パブリック権限の編集」を選択します。
「このオブジェクトのパブリックアクセス権限を付与する」にチェックを入れます。
警告を確認し、「変更を保存」をクリックします。

##  バケットポリシーの設定

バケットの「アクセス許可」タブに移動します。
「バケットポリシー」セクションで「編集」をクリックします。
以下のポリシーをコピーし、エディタに貼り付けます。※YOUR_BUCKET_NAMEを実際のバケット名に、YOUR_PDF_NAME.pdfを実際のPDFファイル名に置き換えてください。
```

jsonCopy{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "PublicReadGetObject",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::YOUR_BUCKET_NAME/YOUR_PDF_NAME.pdf"
        }
    ]
}
```

「変更の保存」をクリックします。

## PDFファイルのURLの取得

バケット内のPDFファイルを選択します。
「オブジェクトの概要」セクションにある「オブジェクトURL」をコピーします。

これで完了です！コピーしたURLをブラウザに貼り付けると、PDFファイルが表示されるはずです。URLが長くなってかっこ悪い場合は外部のURL短縮ツールを使ってみても大丈夫です。

# おわりに
ふとした場面でURLにて共有したい時に知ってるだけで使えるテクニックだなって思えました！
記事執筆中に上司から「最近の保育園はS3で保育だより来るんだよね～。S3だとライフサイクルルールで毎月新しいのしか閲覧できないように管理できるから便利だね」と令和最先端なS3の使用例も教わりました。もうプリントじゃないんですね、、、。
wslでmanim その16

# 概要
wslでmanimやってみた。
練習問題やってみた。

# 練習問題
ソートのアルゴリズムをわかりやすく表示せよ。

# 参考にしたページ

https://zenn.dev/k41531/articles/828e81c86560ae


# 成果物

![SortAnimation_ManimCE_v0.18.1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/3df8ef55-8e94-0a96-90cf-bd7bd6f09202.gif)


# サンプルコード

```

from manim import *
import random

def bubble_sort(seq):
	n = len(seq) - 1
	for i in range(n):
		for j in range(n - i):
			if seq[j] > seq[j + 1]:
				seq[j] , seq[j + 1] = seq[j + 1], seq[j]
				yield (j, j + 1)

class SortAnimation(Scene):
	def construct(self):
		g = Group()
		numbers = list(range(1, 11))
		random.shuffle(numbers)
		for number in numbers:
			g.add(Circle(radius = number / 2 * 0.1))
		self.add(g.arrange_in_grid(rows = 1, col_widths = [1] * 10))
		for i, j in bubble_sort(numbers):
			self.play(Swap(g.submobjects[i], g.submobjects[j]))
			g.submobjects[i], g.submobjects[i + 1] = g.submobjects[i + 1], g.submobjects[i]



```

以上。
Uncategorized (Xcode): Command CodeSign failed with a nonzero exit codeで沼った話

- これは私が以下のエラーで詰まった時の話
```bash
Uncategorized (Xcode): Command CodeSign failed with a nonzero exit code


Could not build the application for the simulator.
Error launching application on iPhone SE (3rd generation).
```

## 何をしようとしていたか
- flutterをインストールしてSimulatorでIOSアプリをPC上で動かしたかった
- flutterのインストールについて[こちら](https://docs.flutter.dev/get-started/install)から
    - 手順はざっくりこんな感じ（上記リンク先のタイトル）
        - Install the Flutter SDK
        - Configure iOS development
            - Install and configure Xcode
            - Configure your target iOS device
            - Install CocoaPods
        - Check your development setup
            - Run Flutter doctor
            - Troubleshoot Flutter doctor issues
- `flutter doctor`は素晴らしい


## 本題
- flutter doctorを実施して全て通っているのに何故か起動するとエラーが起こる
```bash
$ flutter doctor                       
Doctor summary (to see all details, run flutter doctor -v):
[✓] Flutter (Channel stable, 3.24.2, on macOS 14.6.1 23G93 darwin-x64, locale ja-JP)
[✓] Android toolchain - develop for Android devices (Android SDK version 35.0.0)
[✓] Xcode - develop for iOS and macOS (Xcode 15.4)
[✓] Chrome - develop for the web
[✓] Android Studio (version 2024.1)
[✓] VS Code (version 1.89.0)
[✓] Connected device (3 available)
[✓] Network resources

• No issues found!


### flutter起動 ###
Uncategorized (Xcode): Command CodeSign failed with a nonzero exit code


Could not build the application for the simulator.
Error launching application on iPhone SE (3rd generation).
```

### 調査したところ

- バグ原因
    - 調べた原因はいかが考えられた
        1. 証明書の有効期限が切れている可能性
        1. pods(cocoapods)が正常にインストールできていない可能性
        1. flutter側のキャッシュで不要なものが残っている可能性
        1. `flutter`のソースコードの配置場所の問題
- バグ調査
    - 以下あくまで自分の考えですので異なっている可能性もあります
        1. 証明書の有効期限が切れている可能性
            → 同時期に案件参画された方は正常に動いているのを見ると証明書の線はなさそう
        1. pods(cocoapods)が正常にインストールできていない可能性
            → 実際に何度かインストールを繰り返したが効果はなかった
            → 正常に動いていない場合は`flutter doctor`の際に怒られそう
        1. flutter側のキャッシュで不要なものが残っている可能性
            → 以前に適当な手順で`flutter`を起動させたことがあったのでその可能性はありそう
        1. flutterのソースコードの配置場所の問題
            → iCloudと同期しているフォルダの配下がNGっぽいみたいな記事を見た
            → iCloudと同期しているフォルダに入っていたので可能性はありそう

### 結論
- 自分の場合は以下二つが原因だった
    - flutter側のキャッシュで不要なものが残っている可能性
    - flutterのソースコードの配置場所の問題

- 解消
    - `flutter clean`を行ってbuildしていたものを全てクリアにした
    - 配置場所がiCloudと同期しているフォルダに配下になっていたので同期していない場所に移動

### 最後に
- 割とそんなことない内容だったが結構いろんな情報があったのでそれなりの数を試す必要があったのはしんどかった
- 解消方法に関しては配置場所だけの可能性もあったけど、buildしてエラーが起きていたので念の為削除した
Elixirで画像を加工する〜Evision〜

# 今回の目標
- 画像の読み込み
- 線を複数本描く
- /dev/shm/cv.pngに保存
- eogコマンドで表示

完成画像
![cv.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/6a8445f0-53fe-f54b-5e6e-ef876e14c2c2.png)

# 動作環境

OS Ubuntu 22.04
 - RAMドライブ(/dev/shm)を使う為
 - eogコマンドを使う為

 # プロジェクト作成
 
```
$ mix new evision_experiment 
$ cd evision_experiment
```

# Evision組み込み
```diff_elixir:mix.exs
# 〜省略〜
  defp deps do
    [
+     {:evision, "~> 0.2"}
      # {:dep_from_hexpm, "~> 0.3.0"},
      # {:dep_from_git, git: "https://github.com/elixir-lang/my_dep.git", tag: "0.1.0"}
    ]
  end
# 〜省略〜
```

```
$ mix deps.get
```

# 検証ソース

```elixir:lib/evision_experiment.ex
defmodule EvisionExperiment do
  @moduledoc """
  Documentation for `EvisionExperiment`.
  """
  require Logger
  alias Evision
  @tmp_image_path "/dev/shm/cv.png"

  @doc """
  Hello world.

  ## Examples

      iex> EvisionExperiment.hello()
      :world

  """
  def hello do
    File.rm(@tmp_image_path)

    Evision.imread("test.png")
    |> lines(1..1000//5 |> Enum.to_list)
    |> display_image()

    :world
  end

  def lines(mat, []), do: mat
  def lines(mat, [x | range]), do: line(mat, x) |> lines(range)

  def line(mat, x) do
    Evision.line(
      mat,
      {0, 0},
      {x, 450},
      {255, 0, 0},
      thickness: 1
    )
  end

  def display_image(binary) do
    Evision.imwrite(@tmp_image_path, binary)
    System.cmd("eog", [@tmp_image_path])
  end
end
```


# 実行
```
$ mix run -e "EvisionExperiment.hello()"
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/1f39343e-933e-5a3f-5f03-5ba0322466bf.png)

# ソース(github)
https://github.com/ymn-t-yamanashi/evision_experiment
Nuxtの開発サーバーに外部からIPv6でアクセスしたい

## 動機

iOS17.2からテザリングではiPhoneからPCへのアクセスにIPv4が使用できなくなった。
仕方がないのでIPv6でアクセスできるようにする。


## 解決方法

nuxt.config.tsでhostに'::'を指定する。

```ts
// nuxt3
  devServer: {
    host: '::',
    port: 3900,
  },

// nuxt2
  server: {
    host: '::',
    port: 3900,
  },
```

nuxt3は開発サーバー起動時にIPv6のURLが表示される。
nuxt2はifconfigでIPを調べる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498329/142cff9c-b4ff-6096-18e3-60cdab6c2e4c.png)

先頭にinet6、末尾にsecuredが表示されているIPから下記のようにURLを導く。

```
http://[240a:61:5025:2266:be:ba2:961e:84dd]:3900/
```
AWS SDK for JavaでS3メタデータを扱う

## 課題

AWS SDK for Javaで、S3メタデータを扱うとき、日本語でまとまった情報を入手するのに苦労しました。

## 解決方針

どのようにSDKを利用したか、この記事にまとめます。

## 方法

### 前提

- Javaバージョン: 17
- AWS SDKバージョン: v2

### 書き込み

アップロードのリクエストにメタデータを付加します。メタデータは`Map<String, String>`の型で付加します。

例として、指定したバケットにファイルをアップロードする`uploadFileToS3`メソッドを、以下に記載します。

```Java
  // S3クライアントを用意
  private final S3Client s3Client;

  private void uploadFileToS3(
      byte[] file
    , String bucketName
    , String key
    , Map<String, String> metadata
  ) {

    try {
      s3Client.putObject(builder -> builder.bucket(bucketName).key(key).metadata(metadata).build(), RequestBody.fromBytes(file));
    } catch (Exception e) {
      throw new RuntimeException("ファイルのS3へのアップロードに失敗しました。", e);
    }
  }
```

#### 注意点

メタデータのキーには`x-amz-meta-`というプレフィックスを付ける必要があります。例えば、`date`というキーのメタデータを付加したい場合、以下のようにマップを作成する必要があります。

```Java
  Map<String, String> metadata = new HashMap<>();
  metadata.put("x-amz-meta-date", "2024-04-01");
```


### 読み込み

まず、オブジェクトの情報を`HeadObjectResponse`型で取得し、そこからメタデータを取り出します。書き込み時と同じく、メタデータは`Map<String, String>`の型で取得できます。

例として、指定したオブジェクトのメタデータを取得する`getMetadata`メソッドを、以下に記載します。

```Java
  // S3クライアントを用意
  private final S3Client s3Client;

  public Map<String, String> getMetadata(String bucketName, String key) {
    return s3Client.headObject(builder -> builder.bucket(bucketName).key(key).build()).metadata();
  }
```

#### 注意点

メタデータのキーは、書き込み時とは逆に、プレフィックス`x-amz-meta-`が外された状態で返却されます。例えば、`date`というキーのメタデータを取得したい場合、そのまま`date`というキーを指定して、値を取得します。

```Java
  Map<String, String> metadata = getMetadata("sample-backet", "sample-object");
  System.out.println(metadata.get("date"));
  // "2024-04-01"
```

## 参考資料

- [オブジェクトメタデータの使用](https://docs.aws.amazon.com/ja_jp/AmazonS3/latest/userguide/UsingMetadata.html)
- [SDK for Java 2.x を使用した Amazon S3 の例](https://docs.aws.amazon.com/ja_jp/sdk-for-java/latest/developer-guide/java_s3_code_examples.html)
- [AWS Java SDK 2.0 get s3 object meta only](https://stackoverflow.com/questions/61204824/aws-java-sdk-2-0-get-s3-object-meta-only)
- [PutObjectRequest](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model/PutObjectRequest.html)
- [HeadObjectResponse](https://sdk.amazonaws.com/java/api/latest/software/amazon/awssdk/services/s3/model/HeadObjectResponse.html)
(分析2)grep等でRPGソース分析

# ■概略
オープンソースエンジニア歴30年超の筆者が2023年からIBMiを学びだした学習記録です
オープンソースではソース分析によく使うgrepとUNIXコマンドのパイプ連結を、
RPG3のソース分析に使い見通しをよくします

# ■特定の文字列を含むファイルを特定する(grep)
RPGソースをIFSにコピーする方法は後述
IFS上にRPGソースがある状態で、例えばGMBKAFというテーブル(ファイル)を修正する
必要が出た場合に、影響するプログラムの一覧を求めたい
FGMBKAF とファイル指定しているRPGをgrepコマンドで抜き出す

```
$ grep FGMBKAF *|more
```
```
CKB103.rpg:     FGMBKAF  IF  E           K        DISK
CKB103R.rpg:     FGMBKAF  IF  E           K        DISK
…
```
IBMiでは多くの場合ファイルとフィールドはシステムで一意になっているので
grepは有効！
次のUNIXコマンドとパイプの考え方を学んだらいろいろな分析ができるようになる


# ■RPGソースをIFSにコピーする

### ◯CPYTOSTMFはソース毎に指定！
IBMiネイティブファイルにあるRPG3/RPGLEソースをIFSにCPYTOSTMFで
ソースメンバー名を1つづつ指定してコピーする！
多分数千あるであろうソースメンバー名から転送スクリプトを生成する方法を考えた
UNIXコマンドとパイプの使い方サンプルになっている

以下のbashスクリプトを作成する
ls_{ライブラリ名}_QRPGSRC.bash
```
#!/bin/bash
system "DSPFD FILE({ライブラリ名}/QRPGSRC) TYPE(*MBRLIST)"
```

実行すると下記になる
```
5770SS1 V7R4M0  190621                 ファイル記述の表示                                    24/09/xx   8:42:17      sﾟｰﾆﾞ    1
    ファイル. . . . . . . . . : QRPGSRC
      ライブラリー. . . . . . : {ライブラリ名}
    情報のタイプ. . . . . . . : *MBRLIST
    ファイル属性. . . . . . . : *ALL
    システム. . . . . . . . . : *LCL
…
   メンバー       ﾅbﾇﾞ        jbﾎﾟ  日付    日付   時刻    ﾛﾃｰnﾞ数  ﾛﾃｰnﾞ数
   #CDZZAN               16384 RPG    09/04/07 24/07/31 10:15:29         72          0
     mgﾇn:  指定年月日前日残高セット
   #CMMI                  8192 RPG    09/04/07 24/07/31 10:15:29         35          0
     mgﾇn:  指定月セット         (ﾅﾎﾞﾓｰk] aﾒ)   SBMMI
   #CMTZAN               16384 RPG    09/04/07 24/07/31 10:15:29         69          0
…
```
<BR>ここからソースメンバー名だけを抜き出す
grepでRPGとある行を抜き出し、grep -vでQRPGSRCとある行を除外するよう
ls_{ライブラリ名}_QRPGSRC.bashを修正する
| のパイプで処理を連結する

```
#!/bin/bash
system "DSPFD FILE({ライブラリ名}/QRPGSRC) TYPE(*MBRLIST)"|grep RPG|grep -v QRPGSRC
```
```
   #CDZZAN               16384 RPG    09/04/07 24/07/31 10:15:29         72          0
   #CMMI                  8192 RPG    09/04/07 24/07/31 10:15:29         35          0
…
```

<BR>awkでファイル名だけを抜き出し、cpytostmf命令に整形する
```
#!/bin/bash
system "DSPFD FILE(PMLIB/QRPGSRC) TYPE(*MBRLIST)"|grep RPG|grep -v QRPGSRC \
|awk '{print "system \"CPYTOSTMF FROMMBR(\047\${from}"$1".MBR\047) TOSTMF(\047\${to}"$1".rpg\047) \$opt\""}'      
```
```
system "CPYTOSTMF FROMMBR('${from}#CDZZAN.MBR') TOSTMF('${to}#CDZZAN.rpg') $opt"
system "CPYTOSTMF FROMMBR('${from}#CMMI.MBR') TOSTMF('${to}#CMMI.rpg') $opt"
…
```

<BR>リダイレクト(>)でファイルに書出し、先頭に変数などを設定する
```
$ ./ls_{ライブラリ名}_QRPGSRC.bash > cp2IFS_{ライブラリ名}_QRPGSRC.bash
```
cp2IFS_{ライブラリ名}_QRPGSRC.bash
```
#!/bin/bash

opt="STMFOPT(*REPLACE) STMFCCSID(1208)"
from='/QSYS.LIB/PMLIB.LIB/QRPGSRC.FILE/'
to='./'
### ここまでの行を追加する

system "CPYTOSTMF FROMMBR('${from}#CDZZAN.MBR') TOSTMF('${to}#CDZZAN.rpg') $opt"
system "CPYTOSTMF FROMMBR('${from}#CMMI.MBR') TOSTMF('${to}#CMMI.rpg') $opt"
…
```

<BR>cp2IFS_{ライブラリ名}_QRPGSRC.bashを実行しIFSにRPGソースをコピーする
```
$ ./cp2IFS_{ライブラリ名}_QRPGSRC.bash
```

<BR>以下のようにIFSに*.rpgソースができる
```
total 93272
-rwx------ 1 {ユーザ} 0   3356 Jul 31 11:16 #CDZZAN.rpg
-rwx------ 1 {ユーザ} 0   1860 Jul 31 11:16 #CMMI.rpg
…
```
AWS AppSyncとGraphQLの入門ガイド

## はじめに
AWS AppSyncは、GraphQLを使用してアプリケーションのバックエンドを構築するためのマネージドサービスです。
この記事では、AppSyncを使用してGraphQL APIを作成し、DynamoDBと連携させる基本的な手順及び簡単なGraphQLの使用例について紹介します。

## AppSync、GraphQL API、DynamoDBの作成

下記のドキュメントに従ってAppSyncとDynamoDBを作成します。

https://docs.aws.amazon.com/ja_jp/appsync/latest/devguide/schema-launch-start.html

まずAppSyncからAPIを作成を選択します。

ステップ1におけるAPIタイプはGraphQL APIs、データソースはDesign from scratchを選択します。

![スクリーンショット 2024-09-12 17.04.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487820/6bebf810-a826-70cd-0b9f-e6c120f7973c.png)

ステップ2でAPIに適切な名前を入力した後、GraphQLのタイプでは「DynamoDBテーブルを使用するタイプを今すぐ作成」を選択します。

AWS公式に従ってモデルとフィールドを設定します。

![スクリーンショット 2024-09-12 17.06.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487820/ccb9b9b7-897d-7209-59e0-5e039ae5e730.png)

最後に`TodoAPITable`などのテーブル名を入力し、プライマリキーを`id`に設定してAPIを作成します。

上記のように作成するとDynamoDBが自動で作成されます。

![スクリーンショット 2024-09-12 17.12.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487820/c3838ee6-d51f-e805-027b-c3ce36699328.png)


## GraphQLクエリの実行

AppSync→クエリからGraphQLクエリの実行ができます。

### データの作成(Mutation)

まず`TodoAPITable`にデータを追加するために、`createTodo`を実行します。

GraphQLクエリ及び実行結果は以下です。

![スクリーンショット 2024-09-12 17.16.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487820/dba35522-08e8-0fc7-7c76-1a1a734e7492.png)

### データの取得(Query)

次にデータが追加されているかを確認するために`listTodos`を実行します。

GraphQLクエリ及び実行結果は以下です。

![スクリーンショット 2024-09-12 17.18.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487820/a43fcb27-5703-9701-b398-c7eb2347e421.png)

実際にDynamoDBのログを見てみると、読み取りと書き込み処理が実行されていることがわかります。


![スクリーンショット 2024-09-12 17.19.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487820/e889deb0-6f5e-79d8-f3ad-c4df59044411.png)

### GraphQLの柔軟性

GraphQLの特徴を活かし、`id`と`name`のみを指定して`createTodo`を実行できます。
この場合、それ以外のフィールドはnull埋めされます。

![スクリーンショット 2024-09-12 17.24.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487820/d96f0512-1437-1a5d-093c-982950b2a6e1.png)

また`listTodo`で特定のフィールド(ex.`name`)の読み込みも可能です。

![スクリーンショット 2024-09-12 17.26.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487820/8f1bc4a3-5d8e-18f7-5cae-a6735c6d3e43.png)


余談ですが、モニタリングからAPIメトリクス（エラー、リクエスト総数、レイテンシー）の確認が可能です。

![スクリーンショット 2024-09-12 17.30.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/487820/0188fa9c-5760-3f5d-a03e-b9985ab00b00.png)


## おわりに
AWS AppSyncを使用してGraphQL APIを素早く構築し、DynamoDBと連携させる方法を紹介しました。
主なポイントは以下の通りです.


- AppSyncを使用したGraphQL APIの簡単な設定
- DynamoDBとの自動連携
- GraphQLクエリの基本的な使用方法
- GraphQLの柔軟性（部分的なデータ作成と取得）


AppSyncとGraphQLを使用することで、柔軟で拡張性の高いAPIを簡単に構築できます。

参考になれば幸いです。
Azure AzureGuestAgentのメモ

**Azure Guest Agent (Azure VM Agent)** は、Azure上の仮想マシン (VM) にインストールされているエージェントソフトウェアで、VM と Azure 管理サービスの間のインターフェースとして機能します。こちらのエージェントは各種拡張機能のベースとなっていたりしていて、停止したらログやメトリックが取れなくなったりバックアップがコケてしまったします。
主な機能は以下の通りです:

1. **拡張機能のインストールと管理**  
   Azure VM に対する拡張機能（バックアップやセキュリティ、構成管理など）をインストール・管理するために使用されます。

2. **状態監視**  
   VM の状態を Azure 管理サービスに定期的に報告します。これにより、Azure 上で VM の健全性を監視することができます。

3. **Azureバックアップとディスクスナップショットの実行**  
   VMのバックアップやディスクのスナップショットを、エージェントを通じて自動化することが可能です。

4. **カスタムスクリプト実行**  
   Azureポータルを介して VM 上でカスタムスクリプトを実行する際に使用されます。

5. **プロビジョニング**  
   仮想マシンの初期セットアップ時に、ネットワーク設定やディスクのアタッチなどを自動的に実行するためのプロビジョニングタスクを処理します。

このエージェントが正常に動作していることで、Azure上の管理タスクを効率的に実行できるようになります。
今日の関数: VLOOKUP

今日の関数: VLOOKUP
用途: VLOOKUP 関数は、指定した値を検索し、その値が見つかった行の指定列から値を返します。データベースやリストから特定の情報を引き出すのに便利です。

使用例:

=VLOOKUP("検索値", A1:B10, 2, FALSE)

この例では、範囲 A1:B10 から「検索値」を探し、その行の2列目の値を返します。

メリット:

大量のデータから特定の情報を迅速に取得できます。
データベース管理やレポート作成に役立ちます。
Webアプリへの攻撃方法についてメモ

### ディレクトリトラバーサル
アクセスされることを想定していない非公開情報が保存されているディレクトリ（ファイル）に不正な手段でアクセスするサイバー攻撃。

* 正常な動作: ユーザーが指定した画像ファイル名（例：image1.jpg）に基づいて、その画像を表示する機能。
* 攻撃: 攻撃者は、画像ファイル名として「../../etc/passwd」を指定する。
* 結果: システムのパスワードファイルがブラウザに表示されてしまい、パスワード情報が漏洩する。

のような流れらしい。
2020年には大手電機メーカーで、第三者による不正アクセスにより個人情報や機密情報が漏えいした可能性がある事例もある。

### クロスサイトスクリプティング
クロスサイトスクリプティング（Cross-Site Scripting、XSS） とは、Webアプリケーションの脆弱性を悪用し、攻撃者がサイトに悪意のあるスクリプトを注入し、それを閲覧した他のユーザーに実行させる攻撃手法。

* 攻撃者が悪意のあるスクリプトを作成: <script>alert('XSS攻撃！');</script>のようなJavaScriptコードを作成。
* Webサイトの入力フォームに入力: 掲示板やコメント欄など、ユーザーが入力できる箇所を狙って、このスクリプトを入力。
* 被害: スクリプトの内容によっては、Cookie情報などの窃取、ページの改ざん、悪意のあるサイトへのリダイレクトなど、様々な被害が発生する。

被害例としては、Cookie窃取、ページの改ざん、悪意のあるサイトへのリダイレクト、ブラウザの機能を乗っ取りなどがある。

### SQLインジェクション
SQLインジェクションとは、Webアプリケーションの脆弱性を悪用し、不正なSQL文をデータベースに送り込み、データを盗んだり改ざんしたりする攻撃。

* 入力フォームへの不正な入力: ユーザーが入力フォームに、通常の入力値だけでなく、SQL文の一部となるような文字列を入力。
* SQL文の生成: Webアプリケーションは、ユーザーの入力を含んだSQL文を生成。
* データベースへの不正なSQL実行: 生成されたSQL文には、攻撃者が意図したSQL文が含まれているため、データベースは意図しない処理を実行。

ユーザーがusernameの欄に ' OR '1'='1 と入力した場合、生成されるSQL文
```SQL
SELECT * FROM users WHERE username = '' OR '1'='1' AND password = 'password1';
```
【React(TypeScript)】二度押し防止対応

# 概要 
React(Typescript)で二度押し防止対応をしようと考えた時、押されたら困るボタン等のdisabledを変える方法をパッと思い付きますが、それよりも良い方法が見つかったのでマイベストプラクティスとして共有させていただきたいと思います。

## 実装方法
useStateでisLoadingを定義し、これを使ってローディング中は「pointer-events: none;」のクラスを有効します。「pointer-events: none;」を有効にすることで、指定したdivの中にあるボタンやリンクなどが押せなくなります。
paizaラーニング問題集「平方分割」を解いてみた

▼感想：

区間(l,r)が、分割した中に存在する場合と、存在しない場合で、
分けて考えました。

後者の場合は、左端lからl2を計算します。
左端l、l2、右端rの位置関係を場合分けして、
最大の要素の値を求めるやり方にしました。

計算量を考えてプログラムを組む、ということを
ものすごく意識させられた問題でした。

▼コード：

```py
########## 処理0(準備) インプット,リスト定義など ###########

K = int(input())

# tmp: 最大の要素の値を求める際に、利用するリスト
# maxdata: 分割した中の最大の要素の値を、格納する辞書
A = []
tmp = []
maxdata = {}

########## 処理1 配列Aを分割し、分割した中の最大の要素の値を求める ##########

import math
N = int(math.sqrt(10000))

for i in range(10000):

    tmp.append(int(input()))

    if len(tmp) == N:
        maxdata[i+1] = max(tmp)
        A.extend(tmp)
        tmp.clear()

########## 処理2 区間(l,r)に応じて最大の要素の値を求める ##########

for _ in range(K):

    l,r = map(int,input().split())

    l -= 1
    r -= 1

    ########## 処理2.1 区間(l,r)が分割した中に存在する場合の処理 ##########

    if r-l+1 < N:
        for i in range(l,r+1):
            tmp.append(A[i])

    ########## 処理2.2 区間(l,r)が分割した中に存在しない場合の処理 ##########

    else:
        # 左端lから、l2を計算
        l2=((l//100)*100)+99

        # 左端lとl2が、分割した中に存在する
        if l2-l+1 < N:
            for i in range(l,l2+1):
                tmp.append(A[i])

        # 左端lとl2が、分割した中に存在しない
        else:
            tmp.append(maxdata[l2+1])

            # l2を、次の、分割した中に移動させる
            l2 += N

            # l2と右端rの位置関係を場合分け
            while True:

                if l2-r == N:
                    break

                elif l2-r == 0:
                    tmp.append(maxdata[l2+1])
                    break

                elif l2-r > 0 and l2-r < N:
                    l2 -= N
                    for i in range(l2+1,r+1):
                        tmp.append(A[i])
                    break

                elif l2-r < 0:
                    tmp.append(maxdata[l2+1])
                    l2 += N

        print(max(tmp))
        tmp.clear()
```
統計学の基礎、中心傾向の指標をPythonで理解する

## はじめに
データ分析や統計学を学ぶ上で、中央傾向の指標(Measures of central tendency)は基本中の基本です。これらの指標は、データの中心的な傾向を示し、データセットの代表的な値を理解するのに役立ちます。本記事では、平均値、中央値、最頻値の3つの主要な中央傾向の指標について詳しく説明し、Pythonを使って具体的な例とともに理解を深めていきます。文章は全てChatGPT o1-previewで書いています。

## 中央傾向の指標とは
中央傾向の指標は、データがどのような値を中心に分布しているかを示す統計的な指標です。主に以下の3つが広く使われています。

### 1. 平均値（算術平均）
データの総和をデータ数で割った値です。一般的に「平均」と言えばこの算術平均を指します。

計算方法：すべてのデータの値を合計し、データの個数で割ります。
特徴：データ全体のバランス点を示しますが、外れ値（極端に大きい値や小さい値）の影響を受けやすいです。


### 2. 中央値
データを小さい順に並べたときの中央の値です。

計算方法：データを昇順（または降順）に並べ、中央の位置にある値を取ります。データの個数が偶数の場合は、中央の2つの値の平均を取ります。
特徴：データの中心的な位置を示し、外れ値の影響を受けにくいです。

### 3. 最頻値
データの中で最も頻繁に出現する値です。

計算方法：データ内で出現回数が最も多い値を選びます。
特徴：データがどの値に集中しているかを示し、特にカテゴリカルデータで有用です。

## Pythonで中央傾向の指標を計算してみる

```python
import numpy as np
import matplotlib.pyplot as plt

# Data Generation
np.random.seed(0)
data = np.random.normal(loc=50, scale=10, size=1000)  # Normal distribution with mean=50, std=10
outliers = np.random.uniform(low=100, high=120, size=10)  # Outliers
data = np.concatenate((data, outliers))

# Calculation of Measures of Central Tendency
mean = np.mean(data)
median = np.median(data)

# Calculate the mode using histogram bins
counts, bins = np.histogram(data, bins=50)
max_count_index = np.argmax(counts)
mode = (bins[max_count_index] + bins[max_count_index + 1]) / 2

print(f"Mean: {mean}")
print(f"Median: {median}")
print(f"Mode: {mode}")

# Plotting the Histogram
plt.figure(figsize=(10, 6))
plt.hist(data, bins=50, color='skyblue', edgecolor='black')
plt.axvline(mean, color='red', linestyle='dashed', linewidth=2, label=f'Mean: {mean:.2f}')
plt.axvline(median, color='green', linestyle='dashed', linewidth=2, label=f'Median: {median:.2f}')
plt.axvline(mode, color='orange', linestyle='dashed', linewidth=2, label=f'Mode: {mode:.2f}')
plt.legend()
plt.title('Data Distribution and Measures of Central Tendency')
plt.xlabel('Value')
plt.ylabel('Frequency')
plt.show()
```

## 出力結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36629/ab3f1cc5-110b-430d-d8ff-96e47b7abed5.png)

## 中央傾向の指標の使い分け

### 平均値を使うべき場合
データが正規分布に近い対称な分布をしているとき。
外れ値が少なく、データ全体の傾向を把握したいとき。

### 中央値を使うべき場合
データに外れ値が存在し、それらの影響を排除したいとき。
データが非対称な分布をしているとき。

### 最頻値を使うべき場合
データがどの値に最も集中しているか知りたいとき。
カテゴリカルデータや離散データの場合。

## まとめ
中央傾向の指標である平均値、中央値、最頻値は、データの中心的な傾向を理解する上で欠かせない指標です。それぞれの特性と適用場面を理解し、適切に使い分けることで、データ分析の精度を高めることができます。

Pythonを使って実際に計算し、データの分布とともに可視化することで、各指標の意味と違いを直感的に理解できたかと思います。データ分析や統計学の学習の一助となれば幸いです。
【2024年9月版】VyOSで apt によるパッケージ管理を行う方法メモ

# はじめに

VyOSで ssh のセキュリティアップデート適応しなくちゃ。。。

ソースからビルドするのも面倒だ。。。

そうだ、apt で更新しよう！

# 環境

- VyOS13
  インストールは以下の記事を参考に
  [【2023年8月版】VyOSインストールiso作成から、ESXi7/8へのインストール](https://qiita.com/nouernet/items/a41b40dad2efea7b0efc)

# apt のリポジトリ設定

VyOSでaptによるパッケージ管理を行いたいなら、リポジトリを登録すると使える。

## バージョン確認

ここでは VyOS 1.3なので、CODENAMEは、`buster`になっている。

```
$ cat /etc/os-release 

PRETTY_NAME="VyOS 1.3-rolling-202308081250 (equuleus)"
NAME="VyOS"
VERSION_ID="1.3-rolling-202308081250"
VERSION="1.3-rolling-202308081250 (equuleus)"
VERSION_CODENAME=buster
ID=vyos
HOME_URL="https://vyos.io"
SUPPORT_URL="https://support.vyos.io"
BUG_REPORT_URL="https://vyos.dev"
```

## リポジトリを登録

リポジトリを `debian` の `buster` で登録する。

```
$ echo "deb http://deb.debian.org/debian/ buster main" | sudo tee -a /etc/apt/sources.list
$ echo "deb http://security.debian.org/ buster/updates main" | sudo tee -a /etc/apt/sources.list
```

## apt の動作確認

ssh server を更新するなら以下の通り。

```
$ sudo apt update
$ sudo apt install openssh-server
```

# さいごに

かんたんでしたね
Scratch Tips（画面範囲外への描画）

# はじめに（雑談）

Jerrydog ですが、ちょっと思い立って最近自分で Final Take Off を打ち込んでみました。記憶で作ったのでいくらか間違ってるかも...というか曲中通してドラムはほぼ覚えてないのでだいぶ適当。

割と原曲に忠実に、でもメロディはピアノにして、ソロは昔 CD で聞いてたのを思い出しながら記憶をたどってみたソロをピアノで奏でてます（さて、ソロのメロディラインはあってるのかな？＜独り言）。

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

> **"This game is a homage to After Burner from SEGA."** ですよ。
> ※ Final Take Off は上記には入ってません（ご注意）

自作曲ではないため公にするのは許諾とか権利とか、そういった関係であまりよろしくないので、自分一人だけの遊びですが。

> セガに問い合わせてみようかと思ったのですが、問い合わせ窓口が分かりませんでした[^1]。。。 セガの関係者で「いいですよ」っていうコメントがいただけるようなら Scratch 上でも使えるようにしてみたいなぁ、と。Final Take Off かっこいいなあ。

[^1]: 最近のプレイ動画に関するガイドラインはあったんですが、音楽の公開とかアレンジとかはあまり見当たらなかった。。。あと、問い合わせ窓口は（当たり前だが）カスタマーサポートなのでこういう質問の入り口が分からなかった。

んで、ローカルでは 1 面の最初の BGM をそれにしてみたりしたんですが、いやぁ、空母上でのデレレレレレレーからの Get Ready? がカッコ良すぎてシビれました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579271/ed6b7675-de31-6833-bdd7-23752afb6386.png)

> ここね。

# Scratch Tips（画面範囲外への描画）

さて、本題。正直 Scratch 動作のハック的な要素でお勧めしづらいのだが、これなしには生きられないほど多用してしまっているので残します。これ、ちゃんとした仕様で実装されませんかね。というのも、画面の端っこでスプライトがスタックしてしまうのは、かなり動作が不自然なので、自然な動作をさせようとするとどうしても必要になるのです。

# 画面範囲外への描画

## 概要

Scratch では、画面サイズを超えた範囲にスプライトを移動させたり、ペンで描画したりすると位置が勝手に補正されて思った所に描画されません。これを救う手段です。ただし、最初にも言った通り多少ハックの要素が強いため、将来のアップデートで同じように正しく動くかは自己責任になります。

## サンプル

実際の所、これまで作ったすべての作品で多用しています。

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

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579271/6d9666f6-13d7-325e-19ca-0b85da6eb4a7.png)

> 小さなネコは大きなネコのサイズ（画面サイズも含む）縮小版。大きなネコが端っこでスタックすると、小さなネコは黒い枠線のところでスタックします。モードを変えるとスタックしないようになります。

## 方法

### Step 1

大きさ 0 x 0 のコスチュームを作ります。何もしないと 2 x 2 になってるので、一度四角形でも描いて削除すると 0 x 0 になります。名前は **Empty** とでもしておきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579271/55b7b818-a9a9-2205-a87b-5a002e6964e5.png)

### Step 2

ブロック定義を使います。**「画面を再描画せずに実行する」にチェックを付けて** ブロック定義を作ります。名前は **_Infinate Move** とてもしておきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579271/2b19d510-b45f-994f-ebe4-536d457f56f5.png)


### Step 3

先ほどのブロック定義の中で、一旦コスチュームを **Empty** に変更し、大きさを `1 / 0` にします。これは Infinity（無限大）を意味します。Scratch では、実は大きさは元のサイズによって制限がかかるのですが、元のサイズが 0 x 0 なので大きさを無限大にすることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579271/a24ccaca-b04c-5d5f-4bd6-0c917936a1ef.png)

### Step 4

その状態で、座標を変更します。

> （Step 3 の図参照）

### Step 5

その後、通常のコスチュームと大きさに戻します。

一度違うコスチュームに変更するので、不用意にコスチューム変更が画面に反映されないようにしておくために「画面を再描画せずに実行する」にチェックを付けておく、といった形です。

> （Step 3 の図参照）


## 別解

もう少しまともそうな方法として、**透明で大きなボックスをコスチュームにつけておく** という方法があります。こちらのほうがまだ将来的にも使える（梯子を外されない）方法かもしれない。透明で大きなボックスを描いておくことで、上記ほどではないが X 座標と Y 座標をある程度外側まで拡大することができる。これで十分な場合はこちらを採用しても良い。

違いとしては、今回の方法の場合は無限に移動できる、という点。

## これをしないことによる問題の例

Jerrydog では、スクロールするオブジェクトが正しく画面外に流れていかずに、**浮き上がる** ような振る舞いをしてしまいます。X, Y 座標として画面外の座標を指定できるようになると、画面の端で詰まってしまうことなく自然に流れていくようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/579271/398256ec-954b-84d5-107d-74f6df912953.png)

> （左の図）右下の飛び出してるヤツ、なんだ？！
> （右の図）自然にスクロール

---

間違いやご指摘などありましたら、ぜひコメントくださいませ～。

> [Scratch ライブラリと Scratch Tips 目次へ戻る](https://qiita.com/Kray-G/items/e1900d5b6c6dcfbe5321/)
Djangoでログイン時やサインアップ時に特定の処理をする

# 概要
- ログイン時やサインアップ時に、Google Analytics にイベントを送信します
    - Google Analyticsへのイベント送信は、別記事「[DjangoからGoogle Analyticsにコンバージョンを送信する](https://qiita.com/jansnap/items/1215265197ac06b64876)」をご覧ください
        - `config/signals.py` に `send_ga4_event` という関数を作っています

- 認証は、Djangoの標準の機能（[django.contrib.auth](https://docs.djangoproject.com/ja/5.1/ref/contrib/auth/)）を使っています
- シグナルを使うため、アダプタを作成する必要はありません
    - ログインのシグナルは、公式ドキュメントの[django.contrib.authの「ログインとログアウトのシグナル」](https://docs.djangoproject.com/ja/5.1/ref/contrib/auth/#topics-auth-signals)をご覧ください
    - サインアップは、公式ドキュメントの[「既存の User モデルを拡張する」](https://docs.djangoproject.com/ja/5.1/topics/auth/customizing/#extending-the-existing-user-model)をご覧ください



# シグナルハンドラの作成

- シグナルを受け取った時の処理を記述するための accounts/signals.py ファイルを作成します
```python
from django.contrib.auth.signals import user_logged_in
from django.db.models.signals import post_save
from django.dispatch import receiver
from django.contrib.auth import get_user_model
from config.google_analytics import send_ga4_event

User = get_user_model()

@receiver(user_logged_in)
def user_logged_in_handler(sender, request, user, **kwargs):
    # GA4イベントを送信
    client_id = request.session.session_key
    event_name = "user_login"
    params = {
        "user_id": user.id,
        "username": user.username,
    }
    send_ga4_event(client_id, event_name, params)

@receiver(post_save, sender=User)
def user_created_handler(sender, instance, created, **kwargs):
    if created:
        # GA4イベントを送信
        client_id = instance.id  # 新規ユーザーの場合、session_keyがないのでuser.idを使用
        event_name = "user_signup"
        params = {
            "user_id": instance.id,
            "username": instance.username,
        }
        send_ga4_event(client_id, event_name, params)
```
        
# シグナルハンドラの読み込み

- accounts/apps.py を修正して、シグナルハンドラを読み込みます

```python
from django.apps import AppConfig

class AccountsConfig(AppConfig):
    default_auto_field = 'django.db.models.BigAutoField'
    name = 'accounts'

    def ready(self):
        import accounts.signals  # シグナルを読み込む
```

# INSTALLED_APPS の確認
- プロジェクトのsettings.pyファイルで、 INSTALLED_APPSに`accounts`が含まれているのを確認します
    - `accounts`が含まれているのに、追加で「"accounts.apps.AccountsConfig",」のように書くと、ユニークな名前ではないとエラーになります

# Google Analyticsで動作確認

- Google Analyticsの「リアルタイム」などで動作確認します


# 参考リンク

- [Djangoの認証システムを使用する](https://docs.djangoproject.com/ja/5.1/topics/auth/default/)

- [Djangoで、ログイン・ログアウトの履歴を管理する](https://blog.narito.ninja/detail/77#google_vignette)
DjangoからGoogle Analyticsにコンバージョンを送信する

# 概要

- Measurement Protocol を使用して、コンバージョンのイベントをGoogle アナリティクスのサーバーに直接送信します
    - 詳細は、 https://support.google.com/analytics/answer/9900444?hl=ja を参照

# GA4の「コンバージョン」と「キーイベント」
- 「コンバージョン」は「キーイベント」に名前が変わったようです


# 測定IDとAPI Secretの準備

## サイトの登録と、測定IDの確認

- Google Analytics のサイトにログインし、アカウントの登録や、サイトの登録をしておきます
- 測定IDをメモしておきます
    - 左下の歯車のマーク「管理者」をクリック
    - 「Data collection and modification」－「Data streams」
    - 該当アプリのストリームをクリック
    - 表示される「MEASUREMENT ID」が測定IDです。`G-12345ABCDE` のような形式です


- Djangoのテンプレートに、Google Analyticsのトラッキングコードを埋め込んでおきます


## Measurement Protocol API secrets の取得

- 上記のストリームの画面で「Events」ー「Measurement Protocol API secrets」をクリック
    - Google Analytics上のユーザー種別が「管理者」や「編集者」である必要があります。「マーケティング担当者」などの場合は項目が表示されないので注意が必要です
- 初回は規約の確認が必要なので「Review terms」をクリック
- ユーザーデータ収集に関する内容を確認し「I acknowledge」をクリック
- 「Create」ボタンを押し、表示されたポップアップに名前（後からどのキーか分かるようなニックネーム）を入れ、「Create」をクリック
- 一覧に「Secret value」が表示されるので、安全な場所にメモしておく


# コンバージョン送信関数を定義

- 読み込みしやすいファイルに、以下のコードで送信関数を定義します
    - config/google_analytics.py など
    - 設定ファイルや.envファイルからIDを読み込むようにしてあります
    - `requests`など必要パッケージはpipで入れたり、requirements.txtに書くなどしてください
    - debug環境の場合は、イベントに「_debug」を付けるようにしてあります

```python
import requests
from django.conf import settings


def send_ga4_event(client_id, event_name, params):
    measurement_id = settings.GA4_MEASUREMENT_ID
    api_secret = settings.GA4_API_SECRET

    # if debug is true, add event_name with value "_debug"
    if settings.DEBUG:
        event_name = f"{event_name}_debug"

    url = f"https://www.google-analytics.com/mp/collect?measurement_id={measurement_id}&api_secret={api_secret}"
    data = {"client_id": client_id, "events": [{"name": event_name, "params": params}]}

    response = requests.post(url, json=data)
    return response
```


# イベント（コンバージョン）を送信

- DjangoでサーバーサイドからGA4にイベントを送信します
- 問い合わせボタンの押下時の処理など、必要なタイミングで、上記の関数を呼び出して送信します
- 例えば、Djangoのビューでユーザー登録が完了した後に、GA4にイベントを送信します

- あらかじめ`from config.google_analytics import send_ga4_event` のように、送信用の関数をimportしておきます

```python
        # Send GA4 event
        client_id = self.request.session.session_key
        event_name = "ordered"
        params = {"method": "order", "value": 1, "order_id": form.instance.id}
        send_ga4_event(client_id, event_name, params)
```


# Google Analyticsで集計して表示

## リアルタイム集計で、イベントの受信を確認

- 該当ボタン押下など、イベントを発生させます
- Google Analytics画面で、左メニューの「Reports」－「Realtime」で、直近で受信したイベントを表示します
- `Event count by Event name`などで、該当イベントがGA側に届いていることを確認します

## イベントを「キーイベント」に設定

- 左下の歯車のマーク「管理者」をクリック
- イベント一覧の右端にある3点マークをクリックし、「Mark as key event」をクリックします