OmniAICreator/Qiita-1.07M huggingface dataset:
デスクトップの背景を動画にしたかった．．その1 for Windows【コードつき!!】

# はじめに
毎度ッ! うるとらまたろうと申す者です．
皆さまはデスクトップ背景，つまり壁紙を動画にしてみたいと思ったことはありませんか？
多分，文明の曙光が射してこのかた，全人類が抱えてきた悩みあろうと思います．
しかし，先人は偉大で既に解決がなされています．
インターネットで「動く壁紙 windows」等と検索すれば簡単に壁紙を動画にするソフトの情報が出てきます．
もし，壁紙を動画にしたいだけなら，そちらを見ていただく方が宜しいかと思われます．
今回の記事はどうにかして先人に漸近しようと悪戦苦闘する筈の漢の序章です．続編が出るかどうかもわかりません．
なにしろ前回の投稿は2年前の2月と云う，オリンピックペースでありますから．

# 基本構想
今回の作戦はパラパラ漫画です．
パラパラ漫画はノートの端なんかに連続した場面の画を少しずつ差をつけて描きパラパラと捲ることによって，場面間の画を脳が補間し，画があたかも連続して動いているように見せるものです．
これは現在のアニメでも変わりません．アニメが滑らかに動いて見えるのは1/24秒おきという極く短い時間に画が切り替わっているからです．
ところで，Windows11には壁紙におけるスライドショーというものがあります．この機能を使えば，壁紙画像を一定間隔で切り替えることができるのです．
例えば，この間隔を短くしてはいかがでしょう．つまり，そういうことです．

# 実践編
まずは動画をVLC media playerの機能を使って静止画に分解しました．(なんと無駄な作業!!)
とりあえず分解できればなんでも良かったのですが，以前同じようなことをExcelでもしたので，それの余りを使いました．

https://www.mizutan.com/wordpress/?p=11781

### 案1 Windowsの設定を弄ってみる
Windowsの設定から壁紙のスライドショーの間隔は変更できますが，最低でも1分間隔です．以下の記事を参考にスライドショーの間隔を1/24秒に変えてスライドショーしてみました．

https://blacknd.com/os-tools/windows10-wallpaper-slideshow-change-interval/

### 結果1
動作が遅くアニメーションと云える状況ではありませんでした．
次の一枚に行くまでに10秒くらいかかります．

### 案2 Pythonでやってみる
Pythonでも壁紙を変えられるという情報を見たのでやってみました．
私はデスクトップ上に"anime"というフォルダを作ってそこに動かしたい画像を入れました．
上記の方法で動画から画像を切り出したならば番号順に並んでいるはずなので無問題です．

```python:wallpaper.py
import ctypes
import os
import time
import glob

folderName = r"C:\Users\**********\Desktop\anime" 
#画像を入れたフォルダを絶対パスで指定してください．
wallpapers = glob.glob(folderName + r"\*.jpg")
#指定したフォルダ中にあるjpgファイルを読み込みます．jpg以外の場合は書き換えてください．


# 壁紙を変更する関数
def change_wallpaper(image_path):
    if os.path.isfile(image_path):
        ctypes.windll.user32.SystemParametersInfoW(20, 0, image_path, 3)
    else:
        print("指定されたパスに画像が存在しません。")

# 壁紙を変える
while True:
    for i in range(len(wallpapers)):
        change_wallpaper(wallpapers[i])
        time.sleep(1/24)
        #1/24は画像の切り替え間隔です．ご自由に変更ください．
```

以下を参考に~~ほぼ丸パクリ~~しました．元のコードはChatGPTで生成されたもののようですね．
余談ですが，プログラミングを初めて二日ぐらいの友達が生成AIを使ってPyhtonで簡単なゲームを作っていました．技術の進歩を感じますね．

https://qiita.com/shizusouth_com/items/cd0c295e203cda806416

### 結果2
案1よりも高速な動作が得られました．しかし，私は3000枚の画像を入れて24枚/秒で動かそうとしましたから元動画の0.25倍速くらいでした．30枚程度だと0.5倍速くらいで動きます．もっと間のコマを減らしても速くなりそうです．

# おわりに
とりあえず今回はここでおしまいです．長い映像を動かすにはまだまだ工夫が足りません．
Windows Vista UltimateのDreamSceneが存命ならこんな工夫もいらないのですが．
最終目標は動画をヌルヌルと動かすことです．いずれは私の好きなこのゲームの映像を動かしてみたいですね．

https://www.youtube.com/watch?v=6p3UYUP4IYI

https://www.minori.ph/lineup/eden_/

YouTubeで映像が公式に公開されていますが，公式サイトでは高画質な映像をダウンロードすることが出来ます．
実は，他のゲームでは『君の名は』や『すずめの戸締まり』の新海誠がオープニングアニメーション監督をしています．そちらもオススメなのですが18禁ですのでここでは控えさせていただきます．
後書きと云う名の布教でした．
Wear OS 搭載スマートウォッチで曲がった(curved)スクロールバーを表示したい

## 概要
円形のスマートウォッチで縦棒のスクロールバーではなく、画面の淵に沿って曲がった(curved)スクロールバーを表示したかったがうまくいかなかったので試行錯誤した

## 実装
何も考えず `ScrollBar` or `RecyclerView` を配置すると縦棒のスクロールバーが表示された

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141937/24f66ef2-15bb-8690-99fc-de809579bf47.png" width="200">

<details>
<summary>コードを見る</summary>

    <androidx.wear.widget.BoxInsetLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/black">

        <ScrollView
            android:id="@+id/scrollView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_gravity="center_horizontal"
            android:fadeScrollbars="false"
            android:layout_marginHorizontal="16dp">

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_gravity="center_horizontal"
                android:gravity="center_horizontal"
                android:orientation="vertical"
                android:paddingHorizontal="4dp" >

                <TextView
                    android:id="@+id/textView"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:fontFamily="@font/noto_sans_jp"
                    android:gravity="center"
                    android:text="あ\nい\nう\nえ\nお\nか\nき\nく\nけ\nこ\nさ\nし\nす\nせ\nそ\nた\nち\nつ\nて\nと"
                    android:textColor="@color/white"
                    android:textSize="12sp" />

            </LinearLayout>

        </ScrollView>

    </androidx.wear.widget.BoxInsetLayout>
    
</details>

## 解決方法
`ScrollBar` or `RecyclerView` を画面全体に配置した上で、余白を `padding` で指定する。 `margin` で設定してしまうと縦棒のスクロールバーになってしまう


修正前
```kotlin
        <ScrollView
            ...
            android:layout_marginHorizontal="16dp">
```

修正後
```kotlin
        <ScrollView
            ...
            android:paddingHorizontal="16dp">
```

## 結果
`ScrollView` を画面全体にレイアウトし余白を `padding` で設定したら、画面に沿って曲がったスクロールバーが表示された :ok_woman:

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/141937/3ffa2171-d38a-8c0d-0d80-cf7f77c69d77.png" width="200">

:tada:

## 参考
https://stackoverflow.com/questions/43447254/how-to-create-a-curved-scrollbar-in-android-wear-2-0

> This is f**king ridiculous. Adding margin on only the LEFT or RIGHT (not even margin top) prevents a curved scrollbar. Zero documentation on this. As of Nov 2023 they are also requiring scrollbars in your WearOS app, otherwise they won't allow you to update your app.

外人さんもお怒りのもよう...
SAP S/4HANA Trial(無料評価版)をAzureで構築してみよう（前編）

SAP S/4HANA Trial(無料評価版)をAzureで構築してみようと思います。
S/4HANA Trialの手順もAzureの手順も、新規にアカウントを作成した前提で進めます。
前編はS/4HANA TrialとAzureの前準備段階までとします。

## S/4HANA Trial(無料評価版)の登録
まず最初に、以下のURLからS/4HANA Trial(無料評価版)の"今すぐ試す"ボタンを押して登録を行います。

https://www.sap.com/japan/products/erp/s4hana-private-edition/trial.html

![スクリーンショット-2024-02-10-19.26.24-1024x606.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1181562/4d517b1e-c379-362d-667f-ba25798f0105.png)

次に勤務先のメールアドレスを入力し、"次へ"を押します。
![スクリーンショット-2024-02-10-19.36.36-1024x532.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1181562/bbc69332-cfc4-a753-9ce9-e4aed7988031.png)

次の画面で必要項目を入力し、"送信"ボタンを押します。
![スクリーンショット-2024-02-10-19.37.36-1024x858.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1181562/4062581c-ddaf-e799-859d-4dd258113848.png)

送信してから、しばらくすると？後日？登録完了のメールが届きます。私は数年前に登録をしていたらしく、以下画像のメールが届いていました。現在は内容が異なる可能性があります。
![スクリーンショット-2024-02-10-19.41.43-1024x798.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1181562/b54c6593-5b35-d3a9-5df0-8cb432b3780b.png)

## Azureアカウントの作成
S/4HANA Trial環境を構築するには、AWS/Azure/Google Cloudのいずれかのアカウントが必要になります。今回はAzureで構築するのでAzureのアカウントが必要になります。

まだAzureアカウントをお持ちでない方、課金したく無いので無料アカウントを利用したい方は、以下のリンクから新規にAzureアカウントを作成しましょう。簡単なので手順はここでは取り上げません。なお、サブスクリプションを従量課金にアップグレードしておく必要があります。

https://azure.microsoft.com/

## サブスクリプションIDのコピー

Azureアカウントの作成後、以下のリンクからAzureにログインし、ホーム画面からサブスクリプションをクリックします。

https://portal.azure.com/#home

![スクリーンショット-2024-02-10-12.07.12-1024x638.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1181562/398130d5-2156-9891-1195-d9f3dcb55f95.png)

サブスクリプションの画面が開くので、"Azure subscription 1"をクリックします。

![スクリーンショット-2024-02-10-12.14.47-1024x238.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1181562/ab805fdf-f15e-aa91-15da-51cdc23e584b.png)

"Azure subscription 1"の画面が開くので、サブスクリプションIDの右側のコピーアイコンをクリックして、サブスクリプションIDをコピーしておきます。後の手順で利用します。
![スクリーンショット 2024-02-10 12.21.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1181562/99892792-0ae8-7d41-f0d1-7b1decfb6006.png)


前編は以上です。
後編でS/4HANA Trial(無料評価版)の作成と接続を行います。
Kotlin Flowを使ったポーリングの実装

# はじめに

APIから一定間隔で自動的にデータを取得（ポーリング）して、UIを最新の状態に保ちたいことがあります。タイマーを使う方法もありますが、Flowを使うと安全で効率的な処理が綺麗に書けます。


https://developer.android.com/kotlin/flow?hl=ja

# ポーリング

今回、必要な条件は以下のように考えました。

- 一定間隔でAPIに問い合わせて画面を更新する
- UIコンポーネントが画面に表示されていない時は、更新処理を行わない
- ユーザーが任意のタイミングで更新することもできる

## Flow

Flowは非同期なデータの流れです。このケースでは、定期的にAPIに問い合わせた結果が流れていく場所がFlowになります。

Flowは「コールド」です。コールドとは、値を収集することではじめて動作するという意味です。つまり、UIが表示を行うためにFlowの値を収集することによって、Flowの処理が実行されます。

## Flowの作成

`flow`ビルダーを使ってFlowを作成します。
リポジトリからデータを取得して、`emit`でFlowにデータを出力。これを一定間隔で繰り返します。
`while(true)`は無限ループのように見えますが、Flowはコルーチンの一種であり、必要なくなればキャンセルされるものなので問題ありません。

```kotlin
    fun usersFlow(): Flow<List<UserData>> = flow {
        while (true) {
            val users = usersRepository.fetchUsers()
            emit(users) // 新しい値をFlowに流す
            delay(5.seconds) // 5秒間待機
        }
    }
```

## StateFlow

UIが最新のデータを監視して更新できるようにするためにはStateFlowを使います。
`stateIn`を使ってFlowをStateFlowに変換します。ここでのポイントは`WhileSubscribed`を指定して必要な時だけ上流のFlowをアクティブにすることです。言い換えると、このStateFlowの値を収集していない時は元のFlowは停止します。

```kotlin:ViewModel
    val users: StateFlow<List<UserData>> = usersFlow()
        .stateIn(
            viewModelScope,
            SharingStarted.WhileSubscribed(), // 必要な時だけアクティブにする
            emptyList() // 初期値は空のリスト
        )

```

## Lifecycleを考慮したStateFlowの収集

StateFlowをComposableの中で使うには、Stateに変換します。普通は`collectAsState()`を使いますが、ここでは`collectAsStateWithLifecycle()`を使います。こうすることで、アプリがバックグラウンドになったとき、値の収集が止まり、上流のFlowの動作が停止します。

```kotlin:Composable
    val users by viewModel.users.collectAsStateWithLifecycle()
    LazyColumn {
        items(users) { user ->
            Text(user.name)
        }
    }
```

## Flowの再起動

ユーザーが明示的に更新処理を行ったとき、実行中の定期処理のタイミングに関わらず更新を行い、その後、新しい定期処理を行うにはどうしたらいいでしょうか。

`flatMapLatest()`を使ってFlowを再起動する方法を考えてみました。

```kotlin:ViewModel
    // 更新処理の制御用
    val refreshCount = MutableStateFlow<Int>(0)

    val users: StateFlow<List<UserData>> = refreshCount
        .flatMapLatest { usersFlow() }
        .stateIn(viewModelScope, SharingStarted.WhileSubscribed(), emptyList())

    fun refresh() {
        // refreshCountを変更して、新しいFlowを開始させる
        refreshCount.update { it + 1 }
    }
```

更新処理を制御するためのStateFlowを用意します。
flatMapXXXはFlowで得られた値を使って別のFlowを作成するとき、それらをひとつのFlowとして扱うためのものです。３種類あり、次のような違いがあります。
- `flatMapConcat` 前のFlowが終わってから次のFlowを収集する
- `flatMapMerge` すべてのFlowを並列に収集してマージする
- `flatMapLatest` 新しい値が流れてきたら、前のFlowをキャンセルして、新しいFlowを収集する

ここでは、refreshCountの値そのものに意味はなく、`flatMapLatest`の「前のFlowをキャンセルして新しいFlowを開始するという」性質を利用して、Flowの再起動を行なっています。

# まとめ

Flowを使ったポーリングの実装を紹介しました。タイマーを使う方法だとタイマーの開始・終了処理が複雑になり、タイマーを止め忘れると無駄な処理が動き続けることになりかねません。

「値を収集している時だけ動作する」というFlowの性質を使うと、定期実行処理の開始・終了が簡単になります。

ポイントをまとめると以下のようになります。
- `flow`で定期更新処理を作成
- `WhileSubscribed`なStateFlowに変換
- `collectAsStateWithLifecycle`で収集
- `flatMapLatest`でFlowを再起動

# 参考

https://qiita.com/dosukoi_android/items/e8bbaa662c52b8e1cc20

https://qiita.com/CuriosityKilledTheCat/items/23c12cb0f92002cf2756
【SwiftUI】SectionにisExpandedが追加され、折りたためるようになった（iOS17）

# はじめに
iOS17からSectionにisExpandedという引数が追加されました。
これを使用することで、Sectionを開閉することができるようになります。

# サンプルアプリ
![Simulator Screen Recording - iPhone 15 Pro - 2024-02-10 at 22.07.05.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1745371/7d092ec5-55ba-1706-2d92-e412f1e8e76c.gif)

# 実装
ヘッダーの横に矢印ボタンが出るのはListStyleのsidebarだけでした。
その他のスタイルでは`isExpanded.toggle()`のように値を直接変更しないと開閉できません。
```swift
import SwiftUI

struct ContentView: View {
    @State var isExpanded = false
    
    var body: some View {
        List {
            Section(isExpanded: $isExpanded) {
                Text("コンテンツ")
            } header: {
                Text("ヘッダー")
            }
            
            Button {
                withAnimation {
                    isExpanded.toggle()
                }
            } label: {
                Text("開閉")
            }
        }
        .listStyle(.sidebar)
    }
}
```

# おわり
自前で実装するのも簡単そうですが、公式で提供してくれるのは嬉しいですね

# 公式ドキュメント
https://developer.apple.com/documentation/swiftui/section/init(isexpanded:content:header:)-561d7
shell script備忘録

忘れては調べてを繰り返しているので、
shell scriptの構文を備忘録としてまとめる。
bashを想定。


## コマンドを複数行に分けて記載する方法
行末に\をつける
```shell
curl -X GET "test.com" \
-H 'Content-Type: application/json' \
-d '{"id": 1, text: "test"}'
```

## 変数の扱い
```shell
#初期化
##=の前後にスベースを入れてはいけない
test_var="qiita"

#数値計算
##shell scrptでは通常変数はすべて文字列として扱われるため、
##数値として扱う場合は以下のように記載する必要がある
cnt=0
# cnt=$(($cnt + 1)) ※非推奨
cnt=$((cnt + 1))

#配列
##一括初期化
array_str=("sh" "bash")

##一要素ずつ初期化
array_str2[0]="sh"
array_str2[1]="bash"

#コマンド出力を変数に格納(pwdの場合、$PWD)
cmd_result=$(pwd)
```

## if
```shell
#基本
if [ <条件> ] ;then
    <処理>
elif [ <条件> ] ;then
    <処理>
else
    <処理>
fi
```

## for

continue, breakが使える

```shell
# 基本
for <変数> in <値リスト>
do
    <処理>
done

# 10回繰り返し
for i in {1..10}
do
    echo $i
done

# seqを利用
N=10
for i in $(seq 1 $N)
do
    echo $i
done

# ""あるなしで挙動が異なるので注意
# (変数の展開の挙動なのでforに限った話ではない)
# ※下の配列を使う方法を推奨
chars="a b"
for c in $chars
do
    echo c
done
#> a
#> b

for c in "$chars"
do
    echo c
done
#> a b


# ※bashの場合配列が使うことができ、
# スペースの有無に関係なく動くため推奨とのコメントを頂いたため追記
chars=("a" "b")
for c in "${chars[@]}"
do
    echo c
done
#> a b
```

```shell
# 基本2
for ((<初期化>; <条件>; <式>))
do
    <処理>
done

# 例
for ((i=1; i<10; i++))
do
    echo $i
done
```

## 関数
```shell
# 定義
function func_test() {
    # 引数は$xで受け取り
    # 返り値を標準出力として渡す
    echo "hello, $1"
}

# 呼び出し
func_test "qiita"

# 呼び出し(返り値受け取り)
result=$(func_test "qiita")

```

## `` と $()
コマンド結果の展開をしたい時、``より$()を使用することを推奨とのこと。
※理由は調べ中
Mac(M2)でmysql2のinstallで泣きそうになった話し

Mac(M2)で、RoRの開発環境をローカルにとりあえず準備しようとしたらハマった。
mysql2がinstallできなくて泣きそうだったという話し。

PATHの問題なんだろなと思って、下記を参考に試したらできた。

https://github.com/brianmario/mysql2/issues/1175#issuecomment-846496862

下記が実行と結果。

```terminal
❯ gem install mysql2 -- \
--with-mysql-lib=$(brew --prefix mysql)/lib \
--with-mysql-dir=$(brew --prefix mysql) \
--with-mysql-config=$(brew --prefix mysql)/bin/mysql_config \
--with-mysql-include=$(brew --prefix mysql)/include

Building native extensions with: '--with-mysql-lib=/opt/homebrew/opt/mysql/lib --with-mysql-dir=/opt/homebrew/opt/mysql --with-mysql-config=/opt/homebrew/opt/mysql/bin/mysql_config --with-mysql-include=/opt/homebrew/opt/mysql/include'
This could take a while...
Successfully installed mysql2-0.5.6
Parsing documentation for mysql2-0.5.6
Done installing documentation for mysql2 after 0 seconds
1 gem installed
```
シェルスクリプトで空の変数と比較したかった

地味に間違えるやつ。
※以下の処理内容は適当です。

# 変数が空じゃないとき

```Shell
$ a="a"
$ if [ $a != "hoge" ]; then echo "not hoge"; fi
not hoge
```

# 変数が空のとき

```Shell
$ a=""
$ if [ $a != "hoge" ]; then echo "not hoge"; fi
bash: [: !=: 単項演算子が予期されます
```

```!=``` が怒られてて、単行演算子を予期していたとのことなので、おそらく ```if [  != "hoge" ]``` と解釈されていると思われます。

# これで解決

```Shell
$ a=""
$ if [ "$a" != "hoge" ]; then echo "not hoge"; fi
not hoge
```

変数って""で挟んでもいいんですね。

# 環境
Linux Mint 21.3
bash 5.1.16(1)-release (x86_64-pc-linux-gnu)
[Zeitwerk] クラスを定義しようとすると "XXX is not a class" と怒られるパターン

## 概要

```
foo.rb
foo/
  bar/
    baz.rb
```

という構造があったときに、"foo.rb内で"クラス Bar を定義しようとした瞬間、`Bar is not a class` と怒られるという話です。

以下、具体的な再現コードです。

```ruby
require 'bundler/inline'
require 'tmpdir'

gemfile(true) do
  source 'https://rubygems.org'
  gem 'zeitwerk', '= 2.6.13'
end

Dir.mktmpdir { |dir|
  loadpath_1 = "#{dir}/loadpath_1"
  FileUtils.mkpath "#{loadpath_1}"

  IO.write("#{loadpath_1}/foo.rb", <<~RUBY)
    module Foo
      # 次の行に到達した瞬間エラーが発生
      class Bar
      end
    end
  RUBY


  FileUtils.mkpath "#{loadpath_1}/foo/bar"

  IO.write("#{loadpath_1}/foo/bar/baz.rb", <<~RUBY)
    module Foo
      class Bar
        class Baz
        end
      end
    end
  RUBY

  Zeitwerk::Loader.new.then { |loader|
    loader.push_dir loadpath_1
    loader.setup
  }

  # causes
  # `loadpath_1/foo.rb:2:in `<module:Foo>': Bar is not a class (TypeError)`
  p(Foo::Bar)
}

```

`# 次の行に到達した瞬間エラーが発生` の箇所でエラーとなります。

## 関連情報

https://qiita.com/fursich/items/717a720d9f4465e4cbbb#5-%E5%90%8D%E5%89%8D%E7%A9%BA%E9%96%93%E3%81%A8%E3%81%97%E3%81%A6%E3%82%AF%E3%83%A9%E3%82%B9%E3%82%92%E4%BD%BF%E3%81%86

にも関連するセクションがあります。

> Zeitwerkは事前定義されてない名前空間は全てmoduleであるとして決め打ちします。
> 
> つまり、こういうものを書いていると・・・
> 
> ```ruby:app/controllers/admins/items_controller.rb
> class Admins # 外側をクラスにする
>   class ItemsController # ここはclass・moduleどちらでも構わない
>   end
> end
> ```
> 
> ```ruby
> > Admins::ItemsController
> => TypeError: Admins is not a class
> ```
> 怒られてしまいます。

これと多少似ている状況です。本記事では、このAdminsに相当する名前空間である `Foo::Bar` を、きちんと foo.rb の中でクラスとして定義しようとしています。

ところが、定義しようとするとその瞬間、 `Foo::Bar` は module として [autovivify](https://github.com/fxn/zeitwerk?tab=readme-ov-file#implicit-namespaces) により定義されてしまうのです。何のこっちゃと驚いてしまう現象です。

## 解決方法

とはいえ、そもそもZeitwerkで名前空間として扱われるようなクラスなら、ファイルとして独立させ、 `foo/bar.rb` で定義するべきといえます。 
`Foo::Bar` は `Foo` 下のクラスなんだからと、 `foo.rb` の中へ押し込んでしまうのはそもそも分かりづらいです。

`Foo::Bar` の定義を `foo/bar.rb` に分離してやれば解決します：

```rb
  IO.write("#{loadpath_1}/foo.rb", <<~RUBY)
    module Foo
      # Bar の定義を除去
    end
  RUBY

  # Bar の定義は foo/bar.rb に分離
  FileUtils.mkpath "#{loadpath_1}/foo"
  IO.write("#{loadpath_1}/foo/bar.rb", <<~RUBY)
    module Foo
      class Bar
      end
    end
  RUBY
```
【ST言語ことはじめ】インデックス装飾とループで効率化

### はじめに

この記事では、インデックス装飾を使用したプログラムの一例を紹介します。インデックス装飾は、制御システムにおいて位置情報を管理するための重要な要素であり、適切な活用が求められます。

## 使用機材

|   |機種など   |
|:---:|------|
|PLC  |Q02CPU, QX41, QY41P, Q62P, Q38B    |
|アプリ  |GX Works 2    |

I/O割り付けはこちらを[参照](https://qiita.com/so2/items/0f0cab2e324179718b89 "I/O割り付け表")

## インデックス装飾の利用例

以下のサンプルコードは、インデックス装飾を使用して加算処理を行うプログラムの一例です。

```st
(* 加算する数値の初期化 *)
D0 := 1;      (* 初期値ではD0に1を設定する *)
D1 := 1;
D3 := 5;
D8 := 3;
D10 := 10;
D15 := 8;

D20 := K1X2; (* ループ階数をボタンの組み合わせで決める。最低値は0 *)
             (* ボタン２とボタン3がONの場合は2#11となり3となる *)

IF X1 THEN
    D0 := 0;              (* ループ前に合計値保持に使うD0を初期化 *)
    FOR Z1 := 1 TO D20 BY 1 DO
        D0 := D0 + D0Z1;  (* D0からZ1値分のデータを加算 *)
    END_FOR;
END_IF;

K4Y40 := D0;
```

このサンプルコードでは、インデックス装飾を使用してループ処理を行い、加算を行っています。

## インデックスの重要性

インデックス装飾は、制御システムにおいて位置情報を管理するための重要な要素です。適切なインデックスの設定と利用により、制御精度や生産性の向上が期待できます。

## まとめ

本記事では、インデックス装飾の基本的な概念と利用例について解説しました。インデックス装飾は、制御システムにおいて位置情報を管理するための重要な機能であり、適切な設定と利用が求められます。制御システムの開発において、本記事を参考にしてインデックス装飾の活用方法を検討してみてください。
ゆるゆる受験感想 Certified Ethical Hacker（CEH）

こんにちは。ヤドンが好きな人です。
先日、CEHを受験して合格しましたので記事を書かせていただきます。

実は2022年12月に研修を受けてはいたのですがバウチャーの期限切れギリギリまで受験を後回しにしていたものになります。(おかげでver11の研修を受けたのにver12の試験を受験することになりました)

## 目次
- [CEHとは](#CEHとは)
- [公式トレーニングサイト](#公式トレーニングサイト)
- [試験概要](#試験概要)
- [学習方法](#学習方法)
- [受験後の所感](#受験後の所感)
- [今後の学習について](#今後の学習について)


## CEHとは
CEHとはCertified Ethical Hackerの略になります。日本語に直すと認定ホワイトハッカーです。EC-Councilという組織の認定資格になります。
CEH試験（知識）とCEH Practical試験（実技）の2つがあるそうで、両方に合格すればCEH MASTERとして認定されるようです。
自分が受験したのは前者で、言語は日本語版です。


## 公式トレーニングサイト

自分が研修を受けた会社のサイトです。概要などはこちらを見れば大体理解できるかと思います。

https://www.gsx.co.jp/services/securitylearning/eccouncil/ceh.html

EC-Councilの公式サイトは宣伝などが多く受験者目線では情報が分かりにくいのであまり見る価値はないかもです。（Qiitaや個人ブログなどのほうが試験内容に関する情報が出てきます。）


## 試験概要
```
問題数：125問
合格基準：70%以上
試験時間：制限時間4時間
言語：日本語のみ（英語への切り替え不可）
```

完全なる知識問題です。
4問の選択肢から1つを選ぶといった問題が125問続きます。
カンペなどの持ち込みは一切不可能になっています。
言語の切り替えが出来ないため苦労した箇所がありました。

## 学習方法
受験してから一年が経ち、あまり記憶もないので最初はUdemyを見ていましたが億劫になって段々と見なくなり、教科書を寝る前に一気見することを一か月ほど続けていた感じです。結構「○○を行うツールはどれか？」みたいに聞かれたりするので、研修中の講師の方のコメントはあまり聞き逃さない方がいいかもです。

※CEHはトレーニング映像や音声をくれないので、一生懸命内容を教科書にメモした方がいいかと思われます。また大量のコンテンツのあるラボ環境をくれるのですが、CEH試験は知識ゲーなので合格を目指すだけならラボはしなくてもいいのかなといった印象を受けました。

## 受験日を可能な限り後回しにする方法ついて
あまり好ましいことではないかもですが、諸事情で受験をぎりぎりまで後回しにしたいという方がいるかもしれません。そんな方向けの情報です。


バウチャー付きのトレーニングを受けている方は、EC-CouncilかどこかのページからピアソンVUEのバウチャーを受け取るようにと指示があるかと思います。しかしすぐにバウチャーを受け取らないようにしましょう。バウチャーの受験期限は受け取ってから一年という風にカウントされるので、余命を大きく延ばすことができます。


## 受験後の所感
選択式の試験ということもあり、他のセキュリティ資格試験を持っているような方は一周するだけで合格点とまでは行かずとも割と良い線までは行けるかと思います。
ですが「偵察」に使うツールなどあまり意識しない箇所から攻撃者視点で広く学ぶことができたことは非常に良い点だと思います。

## 今後の学習について
今後はOSCP取得に向けて頑張ります。ただ自費で購入するには高すぎるのでまずは基礎固めに中断していたTryHackMeを再開したいと思います。
中断と言いつつも一日一問やっており、120日の連続記録を達成できました。
来週の東北旅行中も途切れないように気を付けて365日連続のバッジを取得したいです。


<font color="Lavender">本試験に関してひとつ文句があります。それは問題文についてチェックしていないのでは？という疑問です。文字化けだったり、選択肢に問題文と一字一句同じものが紛れ込んでいたり、選択肢の中にそもそも正解が存在しておらず、確率1/4の運ゲーに挑まされたり、なんとも理不尽な気分を味わいました。自分は125問のうち3問でそのようなものを引いたので、今後受験するみなさまも悩まされたりするかもですね。
</font>
比較してみた（PostgreSQLのunnest関数）

# はじめに
今回は、データの件数分、INSERT文を実行して登録するのと
PostgreSQLのunnest関数を使って、1度のINSERT文で登録することの比較をしました。
データ件数は1万件です。

# 結論
unnest関数を使ったほうが早かった(10秒くらい違う)
タイパというものが囁かれる今日この頃、先に結果だけ書かせて頂きました:bow:
具体的にどうやったの？など気になる方は読み進めて頂ければと:grinning:

# きっかけ
業務での実装時に、登録処理の部分は
PostgreSQLのunnest関数を使った実装にしてねと指示頂く。
理由：性能が良いからとのこと。
それは本当なのか自分でも確認したくて比較を実施。

# 環境
PostgreSQL 13.2
A5:SQL Mk-2(SQLの実行時に使用)

# サンプルコード
テーブルはINSERT文を実行するだけなので、制約などは何も設定せずです。
※unnest関数を使って登録するのがcolumn_1～column_3。他は固定値を登録。

```table.sql
create table public.bulkinserttest (
  column_1 character varying(255)
  , column_2 character varying(255)
  , column_3 character varying(255)
  , userid character varying(20)
  , regdm character varying(20)
);
```

INSERT文
①unnest関数を使わない

```insert_1.sql
-- 下記の感じで1万行用意
insert into bulkinserttest values ('test1','test2','test3','batch','20240128214000');
insert into bulkinserttest values ('test2','test3','test4','batch','20240128214000');
insert into bulkinserttest values ('test3','test4','test5','batch','20240128214000');
```

②unnest関数を使う
```insert_2.sql
-- 「・・・」の部分に、他と同様にカンマ区切りで値入れ込む
insert into bulkinserttest values (
unnest(ARRAY['test1','test2','test3',・・・,'test10000'])
,unnest(ARRAY['test1','test2','test3',・・・,'test10000'])
,unnest(ARRAY['test1','test2','test3',・・・,'test10000'])
, 'batch'
, '20240128214000'
);

```

# 検証
まずはunnest関数を使わない場合

実行前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437054/cdd12830-8051-e200-eb2c-7bd50e9cc3c7.png)


実行→結果は約12秒でした
![insert_1.sqlの実行結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437054/42a4914d-bae4-3878-5f8c-016107e9b290.png)


次はunnest関数を使った場合

実行前
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437054/6d19f8e2-37f9-38fa-7ce3-5e4fa3f4004b.png)

ちゃんと1万個くらい用意:sweat_smile:
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437054/a54b1f84-921c-d461-cba3-157053d5ec92.png)

実行→結果は約0.1秒でした
![insert_2.sqlの実行結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/437054/abd4194e-6abb-7d10-e8ca-d46c1116a1bf.png)

# おわりに
検証の結果、unnest関数を使うと約10秒くらい早くなりました。
(unnest関数の処理で1秒に満たないくらいの待ちは発生しますが)

今回のデータ件数は1万件でしたが
これが2万、3万と増えるにつれ
性能の差はもっと如実に出るのかもしれません。

ということで、今回はPostgreSQLのunnest関数を比較してみました。
次回以降のどこかで、実際にJavaとかで実装した時に
どうやってSQLの発行まで持って行くのかなども共有出来たらと思います。

最後までお読みいただきありがとうございました！:bow:
LINE Messaging APIのイメージマップメッセージの画像について

# 画像のURLの指定について
イメージマップについて公式が記載しているので、リンク貼っておきます。
[Messaging API イメージマップメッセージ](https://developers.line.biz/ja/reference/messaging-api/#imagemap-message)
>・画像フォーマット： JPEGまたはPNG
・画像の幅：240px、300px、460px、700px、および1040px
・最大ファイルサイズ：10MB

まず、このような指定があります。
この通りに準備すると、
```
Image
  |- 240.jpeg
  |- 300.jpeg
  |- 460.jpeg
  |- 700.jpeg
  ┗- 1040.jpeg
```
のようになっていると思います。
そして、このままサーバーにアップロードして、
いざイメージマップを読み込もうとすると、エラーとなりました。

よくよくリファレンスを読んでみると、

>注意
画像のURLには拡張子を含めないでください。「https://example.com/bot/images/rm001/700.png」のように、URL?に拡張子が含まれている場合、イメージマップメッセージでは画像が表示されません

とあります。
よって「〜〜〜.jpeg」ではうまく読み込んでくれないのです。

# 解決法

拡張子を含めないでと言われているので、拡張子を消します。
ここではMacで説明します。
1. はじめに拡張子を表示してください。Finderの設定を開いて、詳細->すべてのファイル名拡張子を表示をオンにすると表示されます。
1. 拡張子を消したいファイルを選択して、Enterを押します。(ファイル名編集モードになります。)拡張子を削除して、Enterを押します。警告が出てきますが、進みます。以上で拡張子を消すことができます。
<img width="100" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1938842/b218e1b6-dc13-ca81-cbf0-2a6f0fda1bec.png">
->
<img width="100" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1938842/9c87d938-a51a-4c98-ab59-13e24222745d.png">
->
<img width="100" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1938842/551b56d6-ffd6-ee9b-5fd6-92d5cbef5ad3.png">
->
<img width="110" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1938842/21457a5e-cb89-9713-e2dc-863f4f2dd13f.png">
->
<img width="100" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1938842/8a82119b-cd26-bf15-077d-ad72cad6e6e1.png">
Swift基本構文


変数と定数
---

var-定数（一度値を設定したら以降変更できない）
let-変数（値を変更可能）

基本的な型
---

|型名|概要|
|:--|:--:|	
|string|文字列型|
|int|整数型|
|double|浮動小数点型|
|Bool|真偽型|

型推論
---
変数や定数の宣言時に型を明示しなくても、自動的に型を推論する機能です。

```
例1　let number = 420
```
例えば例１のように宣言した場合、SwiftコンパイラはInt型を推論する

```
例2　let name = "Aさん"
```
同様に例2ではString型であることを推論している。

真偽値(bool型)
---
#### 論理演算-否定,論理積,論理和
Bool型は真理値を表す型です。
ある命題が真であれば、true、偽であれば、falseの値を返します。
```
let a = true //真
let b = false //偽
```
##### 否定
```
let a = true //true
let b = !a //false
```
##### 論理積(与えられたbool型の値が全てtrueだった時に結果がtrueになる演算）
```
let a = true && true    // 全てtrueなので、true
let b = true && false   // 一つはfalseなので、false
let c = false && true   // 一つはfalseなので、false
let d = false && false  // 二つともfalseなので、false
```
##### 論理和(与えられた値のいずれか一つがtrueだった時に結果がtrueになる演算)
```
let a = true || true    // 全てtrueなので、true
let b = true || false   // 一つでもtrueなので、true
let c = false || true   // 一つでもtrueなので、true
let d = false || false  // 二つともfalseなので、false
```
数値計算
---
#### 四則演算と剰余演算(余りの値)
```
let plus = 1 + 1
let minus = 1 - 1
let multiply = 1 * 1
let divide = 1 / 1
let surplus = 10 % 3 
print(surplus)
1
```
#### Int型とDouble型の計算
```
let int = 1
let doubel = 0.5
let result = Double(int) + doubel //Dobleをつけることで1.0 + 0.5になる
print(result)
15
```
配列(Array<Element>型)
---
#### 配列というのは、データの集まりをまとめて格納するデータ構造であるコレクションの1つ
```
let name: Array<String> = ["Takashi", "Akemi", "Yukihiro"]
```
```
let name = ["Takashi", "Akemi", "Yukihiro"] //型推論
```
Optional型
---
```
var a:Int
a = nil //エラー
```
普通のIntにnilが入るとエラーになります。
ここでnilを使いたい場合、またはnilが入る可能性がある場合にoptional型を使います。
```
var a:Int?
a = nil 
```
使い方は変数型の後にoptional型と宣言するための?を付けるだけ!!

if文
---
```
テストの点数
var score: Int = 80

if score == 100 {
    print("100点満点！")
} else if score >= 80 {
    print("高得点!!")
} else if score >= 60 {
    print("いんじゃ〜ん！")
} else if score >= 40 {
    print("それじゃあかんよ！")
} else {
    print("赤点です。再試おっつ！")
}
```
上記だと高得点！！が表示される
|もし〜ならば|それ以外|
|:--|:--:|
|if文の場合： 「else if」を用いる|if文の場合： 「else」|

switch文
---
#### switch文-式の値に応じて分岐処理を行うための文
Switch文はif文と異なりcaseで条件となる値や変数をいくつか指定して、一致するcase文の処理を実行する
if文とswitch文の使い分けだが、正直どっちでもいいと思うのでそんな気にしなくていいのかなと。
```
let day = "土曜日"

switch day {
case "日曜日":
    print("燃えるゴミの日")
case "水曜日":
    print("不燃物の日")
case "土曜日":
    print("資源のゴミの日") 
default:
    print("ゴミの日ではありません")
}
```
上記の場合は、let day = "土曜日"と宣言しているので 資源のゴミの日 が表示される
|もし〜ならば|それ以外|
|:--|:--:|
|switch文の場合： 「case」を用いる|switch文の場合： 「defult」|


今回は以上！！
これからも勉強したことのアウトプットとしてあげていきます！
【Unity×VRM1.0】読み込んだVRMオブジェクトをAnimatorで制御する

# 概要
前回の続きです。

https://qiita.com/Gamo_2683/items/8ef88dbef59e975e5fae

前回、フォルダから任意のVRMファイルを読み込めるようにしましたが、その読み込んだVRMのオブジェクトのAnimatorコンポーネントにはRuntimeAnimatorControllerがセットされていないため、アニメーションの制御ができません。
そこで、今回はアニメーションの制御を試しました。

結果は、以下の通り。Tポーズで読み込まれていたAvatarにIdleアニメーションをセットできました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3604990/e712cc04-8735-69a4-9dd1-01f1be48ea07.png)

なお、Unityの使い方（AnimatorとかAnimatorControllerとか）についてはここでは触れませんので、必要ならキーワードから調べてみてください。

# 開発環境・使用したパッケージ

前回と同じです。

# 実装
Playモードに入りVRMファイルを読み込むと「VRM1」というゲームオブジェクトが生成されます。このゲームオブジェクトには、はじめからAnimatorコンポーネントがセットされているので、Controllerパラメータに事前に準備しておいたAnimatorControllerをセットすれば良いです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3604990/0f8d67fe-673f-324a-bae2-47f672355a48.png)

上記を実現しているのが、SetAnimatorController()関数です。
なお、今回はセットするAnimatorControllerをSerializeFieldに予めセットしておく実装にしています。
セットし忘れると機能しないのでご注意ください。


```csharp:UniVrm10.cs
using UnityEngine;
using System.Threading.Tasks;
using UniVRM10;
using SFB;

public class UniVrm10 : MonoBehaviour
{
    // デフォルトで読み込みたいモデルがある場合はpathをセットする。
    string path = "";

    [SerializeField] private RuntimeAnimatorController vrmAnimatorController;

    // Open file with filter
    // 拡張子がvrmまたはVRMのファイルのみ選択させる。
    ExtensionFilter[] extensions = new[] {
        new ExtensionFilter("VRM Files", "vrm", "VRM"),
    };

    // Start is called before the first frame update
    async void Start()
    {
        if (path != "" && path != null)
        {
            // VRMファイルのロード
            Vrm10Instance vrm10Instance = await Vrm10.LoadPathAsync(path);

            // セットされたVRMのAnimatorコンポーネントに事前に準備したRuntimeAnimationControllerをセット
            SetAnimatorController();
        }

    }

    // Update is called once per frame
    void Update() { }

    // ButtonコンポーネントとOn Click()にセットして利用することを想定
    public void LaodVRM()
    {
        string[] paths = StandaloneFileBrowser.OpenFilePanel("Load VRM File", "", extensions, true);
        // Vrm10Instance vrm10Instance = await Vrm10.LoadPathAsync(paths[0]);

        // すでに開かれているVRMファイルがある場合は削除する。
        GameObject currentVrm = GameObject.Find("VRM1");
        if (currentVrm != null)
        {
            Destroy(currentVrm);
        }
        OpenVRM(paths);
    }

    private async Task OpenVRM(string[] paths)
    {
        // VRMファイルのロード
        Vrm10Instance vrm10Instance = await Vrm10.LoadPathAsync(paths[0]);
        
        // セットされたVRMのAnimatorコンポーネントに事前に準備したRuntimeAnimationControllerをセット
        SetAnimatorController();
    }

    private void SetAnimatorController()
    {
        GameObject vrm1 = GameObject.Find("VRM1");
        Animator vrm1Animator = vrm1.GetComponent<Animator>();
        vrm1Animator.runtimeAnimatorController = vrmAnimatorController;
    }
}
```

# 利用方法
前回と同じです。
ただし、SerializeFieldにAnimatorControllerを予めセットするのを忘れないでください。

# デモ画像で使用したアバター
New NecoMaid（製作：QuQu）

https://sonovr.booth.pm/items/4252664

# 使用したIdleアニメーション
ただのIdleポーズではなく、ちゃんと呼吸のアニメーションが入っていて芸が細かいです。

https://valice.booth.pm/items/4308504

# 参考資料

https://discussions.unity.com/t/how-to-set-animators-controller-in-script/63474
ReactにおけるAPI通信

## はじめに
前回の記事で外部APIを通信するJavaScriptのfetch関数の使い方について書きました。
本記事ではReactでAPI通信を使いこなせるようになる方法を、初心者でも理解できるように解説します。


## ReactにおけるAPI通信

Webアプリケーションは、ユーザーが必要とする最新情報を提供するために、サーバーからデータを取得する必要があります。
 
Reactは、このデータをすばやくフロントエンドに反映させるJavaScriptのライブラリです。
  
API（Application Programming Interface）を通じて、Reactはサーバーからデータを取得し、画面をリアルタイムで更新します。

ReactとAPIの連携により、ユーザーは常に最新の情報を見ることができるのです。




## Reactで外部APIからデータを取得して表示する方法
ここでは、公開APIから実際のデータを取得し、それを表示する方法を学びます。

### 使用するAPIのJSONデータ例
以下のJSONデータは、公開API「JSONPlaceholder」からのタスク情報のサンプルです。これは、実際に試すことができる無料のテストAPIです。

＊JSONPlaceholderを試してみた方はこちら
https://jsonplaceholder.typicode.com/

 ```json
{
  "userId": 1,
  "id": 1,
  "title": "Sample Task",
  "completed": false
}

```
このデータは、特定のタスクに関する情報を表しています。ここでは、タスクのID、ユーザーID、タイトル、および完了状態が含まれています。

### Reactコンポーネントでのデータ取得
以下は、ReactでAPIからデータを取得し、表示する基本的な例です。

```jsx
import React, { useState, useEffect } from 'react';

function App() {
  const [task, setTask] = useState(null);

  useEffect(() => {
    //fetch('APIのエンドポイントを記述')
    fetch('https://jsonplaceholder.typicode.com/todos/1')
      .then(response => response.json())
      .then(data => setTask(data))
      .catch(error => console.error("Fetching data failed", error));
  }, []);// 空の依存配列を渡すことで、コンポーネントのマウント時に一度だけ実行される

  if (!task) return <div>Loading...</div>;

  return (
    <div>
      <h1>Task Information</h1>
      <p>Title: {task.title}</p>
      <p>Completed: {task.completed ? 'Yes' : 'No'}</p>
    </div>
  );
}

}
```
この例では、useEffectフックを使用してコンポーネントのマウント時にAPIからデータを取得(fetch)しています。取得したデータは、useStateフックによってコンポーネントの状態として保持され、UIがそれに応じて更新されます。


## まとめ
Reactを使用して外部APIからデータを取得し表示する方法は、API通信の基本から始め、Webアプリケーションを構築するための重要なスキルです。この記事で紹介した手法を活用し、実際に様々なAPIとの連携を試してみてください
Goを利用し、API経由で自分の草の情報を取得したい

# はじめに

GitHubのコントリビューション、俗にいう「草」の情報をGitHub APIを利用して取得する方法を紹介します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/105859/486da189-dab6-89be-4607-9d848451852f.png)

なお取得するのは自分のコントリビューション情報です。また言語としてはGoを利用します。

```
$ go version
go version go1.20.3 linux/amd64
```

# 事前準備

事前準備として、アクセストークンの作成を行います。

① アクセストークンの作成ページへ移動します。

https://github.com/settings/tokens

② Generate new token を選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/105859/5b43cdc7-ade0-4ccd-5aed-b0e9eedf72cb.png)

③ Token name に任意の名前を入力。Repository acess は Public Repository (read-only)を選択し、Generate token ボタンを押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/105859/685751e6-3a9e-aab0-50d7-dbe60c1a68fa.png)

④ 作成されたアクセストークンを控えておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/105859/970113f2-f75f-80cd-9bcf-f8b1ef6c2ad1.png)

# ソースコード

変数TOKENには事前に作成したアクセストークンを、変数USERにはGitHubのユーザ名をそれぞれ設定します。

```main.go
package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"strings"
)

type GithubResponse struct {
	Data struct {
		User struct {
			ContributionsCollection struct {
				ContributionCalendar struct {
					Weeks []struct {
						ContributionDays []struct {
							Color             string `json:"color"`
							ContributionCount int    `json:"contributionCount"`
							Date              string `json:"date"`
							Weekday           int    `json:"weekday"`
						} `json:"contributionDays"`
					} `json:"weeks"`
				} `json:"contributionCalendar"`
			} `json:"contributionsCollection"`
		} `json:"user"`
	} `json:"data"`
}

var TOKEN = "..."
var USER = "nekoTheShadow"
var URL = "https://api.github.com/graphql"
var QUERY = fmt.Sprintf(`
{
  user(login: "%s") {
    contributionsCollection {
      contributionCalendar {
        weeks {
          contributionDays {
            color
            contributionCount
            date
            weekday
          }
        }
      }
    }
  }
}
`, USER)

func main() {
	// リクエストボディを作成する
	requestBody, err := json.Marshal(map[string]string{"query": QUERY})
	if err != nil {
		log.Fatal(err)
	}

	// リクエストを作成する
	request, err := http.NewRequest("POST", URL, strings.NewReader(string(requestBody)))
	if err != nil {
		log.Fatal(err)
	}
	request.Header.Set("Authorization", fmt.Sprintf("bearer %s", TOKEN))

	// POSTを実行する
	client := &http.Client{}
	response, err := client.Do(request)
	if err != nil {
		log.Fatal(err)
	}
	defer response.Body.Close()

	// レスポンスを構造体にマッピングする。
	var githubReponse GithubResponse
	if err := json.NewDecoder(response.Body).Decode(&githubReponse); err != nil {
		log.Fatal(err)
	}

	// 構造体をPretty Printする
	b, err := json.MarshalIndent(githubReponse, "", " ")
	if err != nil {
		log.Fatal(err)
	}
	fmt.Println(string(b))
}
```

プログラムを実行します。

```
go run main.go
```

実行結果は以下のようになります。

https://gist.github.com/nekoTheShadow/eb6f4e7b2f7ae9fee5542e6ec2c20870
ALBの新機能発表：ワンクリックでWAFを導入！AWSの推奨ルールで簡単セキュリティ強化

# ■概要

ALBに関する地味？アプデが来たようです。
ALBへのWAF導入がワンクリックで可能になったとの事。

AWS Application Load Balancer announces one-click WAF integrations
（和訳：AWS Application Load Balancer がワンクリック WAF 統合を発表）
https://aws.amazon.com/about-aws/whats-new/2024/02/aws-application-load-balancer-one-click-waf-integrations/

# ■アップデート内容

以下の内容になっています。

・ALBの「統合」タブからWAFを導入できるようになった
・導入には「Web ACL」の事前準備が必要だったが、ワンクリック導入の場合はAWS推奨の内容で自動作成される
・自分で作成しておいた既存のACLを選択する事も可能
・もちろん、料金は通常のWAFと変わりはない

# ■コンソールから見てみる

ALB画面下部のタブ「統合」から確認できる「AWS Web Application Firewall (WAF)」が今回のアップデートと思われます！（恐らく…）

![waf1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954355/28f00dd7-8966-fa1c-e828-8a4875558096.png)


前からあったと言われても不思議に思わない地味アプデです。（地味だけど便利）

関連付けに進んでみます。
「事前定義済みのウェブ ACL を適用するか、または既存の WAF ウェブ ACL のいずれかを関連付けます。」とありますね。

![waf2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954355/20310685-0085-9bab-636c-492ff5ffb05b.png)

今回は定義済みのものを自動作成してみようと思います。

![waf3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954355/9c5e16bd-21a9-5ab4-8bbf-6921afece556.png)

本当にワンクリックで出来てしまいました…、すごい

![waf4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954355/cefab19d-7ccb-7491-5972-e4c62cad0119.png)

↓ （この間30秒くらい）

![waf5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954355/247fc8dc-9192-e422-dc5d-f9a1a1012e75.png)

作成が完了して、WAFの統合が検出されたようです。

![waf6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954355/88a87f68-8b4b-7412-ef5f-21b9eb5bdfec.png)

# ■自動作成されたACLについて

肝心の中身ですが、AWS推奨ルールという事でしたが、
自動作成されたACLには、以下ルール３つが登録されていました。

### ・AWS-AWSManagedRulesAmazonIpReputationList 
Amazonの内部脅威情報に基づくルールを含んでいます。
通常、ボットやその他の脅威と関連付けられているIPアドレスをブロックするために使用されます。
これにより、ボットの軽減と、悪意のあるアクターが脆弱なアプリケーションを発見するリスクを減らすことができます。

### ・AWS-AWSManagedRulesCommonRuleSet 
ウェブアプリケーションに一般的に適用可能なルールが含まれています。
これにより、OWASP Top 10などのOWASPの出版物に記載されている、リスクが高く一般的に発生するいくつかの脆弱性を含む、さまざまな脆弱性の悪用に対する保護が提供されます。

### ・AWS-AWSManagedRulesKnownBadInputsRuleSet 
脆弱性や悪用のあるパターンに報告されているインプットをブロックするルールが含まれています。
これにより、悪意のあるアクターが脆弱なアプリケーションを発見するリスクを減らすことができます。

# ■最後に

これだけ簡単だとALB作成したついでにWAF導入までしたくなりますね。
WAFですが、導入するのに不安な方はブロックせずカウントだけするという事もできます。
是非この機会に試してみましょう！
Drizzleを使ったときに躓いたことの対処まとめ

# これはなに？

DrizzleというORMを使ってみたときに躓いたことの対処をまとめました。

https://orm.drizzle.team/

# リレーションテーブルを定義するとdrizzle studioが起動しない

例えば以下のようなスキーマを定義したとします。

```ts
export const master = createTable("master", {
  id: varchar("id", { length: 128 }).primaryKey(),
  createdAt: timestamp("created_at")
    .default(sql`CURRENT_TIMESTAMP`)
    .notNull(),
  updatedAt: timestamp("updatedAt"),
});

export const sub = createTable("sub", {
  id: varchar("id", { length: 128 }).primaryKey(),
  masterId: varchar("id", { length: 128 }),
  createdAt: timestamp("created_at")
    .default(sql`CURRENT_TIMESTAMP`)
    .notNull(),
  updatedAt: timestamp("updatedAt"),
});
```

このテーブルの内容から、masterからsubのリストを取得したいと考えてリレーションテーブルを定義します。

```ts
export const masterRelations = relations(master, ({ many }) => ({
  subs: many(sub),
}));
```

これで一見良さそうに見えますが、これで`drizzle-kit studio`を起動しようとするとエラーが発生します。対処方法は簡単で、もう一方のテーブルのリレーションも作れば大丈夫です。

```ts
export const subRelations = relations(sub, ({ one }) => ({
  createdUser: one(master, {
    fields: [sub.master_id],
    references: [master.id],
  }),
}));
```

# 主キーを自動生成したい

Primary keyにCUIDを使って、それをスキーマ定義側で自動生成できるような実装ができないかと思って調べました。結論として、`$defaultFn`というメソッドを使って実現可能です。

```ts
export const table = createTable("table", {
  id: varchar("id", { length: 128 }).primaryKey().$defaultFn(createId),
  createdAt: timestamp("created_at")
    .default(sql`CURRENT_TIMESTAMP`)
    .notNull(),
  updatedAt: timestamp("updatedAt"),
});
```
`createId`という関数を作って、一意なIDを返却するように実装します。
緊急事態に備えて備品保管場所をGlideで制作してみた。

# 今回の制作について

### 制作の背景
 こんにちは、飲食店で働く50歳のおじさんです！
 　今回三回目の投稿をさせていただきます。
 まず、緊急事態というと今年お正月の能登半島地震にて被災された方、心よりお見舞い申し上げます。この時期に、緊急事態というと不謹慎かもしれませんが、人間、色んな意味で、頻繁に緊急事態と感じる場面があるかと思います。

 今回も仕事面での緊急事態ですが、飲食店も多くの機器・備品を扱い調理しています。
機器の故障、備品の破損など突発的に良くないことは発生します。また、発生するときは、世間が休日の土曜・日曜・年末年始などに多く発生します。
　その時**どのように行動するか**で**お客さまの満足**も変わってきます。

 ## 追記。（どのような場面にで便利なのか？）
 　現状、機器・備品が故障や破損した時、まず、上長に連絡し指示を仰ぎ、上長より予備の保管場所を確認してもらいます。指示が下りるまで時間がかかる場合もあり、アプリをスマホなどにインストールしておけば、各店長から保管店舗にすぐ、連絡できます。上長へはその後、報告することで時間の短縮が出来るようになります。

 ## 今回の制作物
### 修理・発注に時間がかかり調理に必要な機器備品管理アプリ

### 使用したツール　
<details><summary>使用したツール</summary>

**Glide**



**Google SpreadSeet**
</details>
 

 https://www.glideapps.com/

**色々検索しましたが、この記事を見ていました。**


https://kotaro-note.com/glide1/


https://plat.io/ja/posts/glide-ap

### 制作方法
　まず、`Google SpreadSeet`にデータを入力しました。
 ![スクリーンショット (48).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3656986/f07832c9-b632-e097-bbbb-3bf0b1dee11d.png)
**都合により、若干、ボカシております**

### 次に`Glide`へ連携します。
**画像はこちら**
![スクリーンショット (50).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3656986/754fae98-6797-7df0-540a-51011bcf857c.png)

**データ・写真などを入れ込み
![スクリーンショット (53).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3656986/1988e15e-71d2-89da-b071-617280689db9.png)

**アプリメイン画面**
![スクリーンショット (52).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3656986/5415f2e8-7cf6-bd63-ccc4-9e7518ee439c.png)

**写真が入り、見栄え・見やすさが増しました！**
スマホアプリの画像です。
![スクリーンショット (55)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3656986/cede16a4-a323-d996-256e-6189736a88b0.jpeg)
![スクリーンショット (57)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3656986/e3b746df-72e4-8dad-b98e-0062c415a933.jpeg)



:::note info
数値確認ができるようにしました。2月14日追加。
:::


https://twitter.com/nakamori315/status/1756247949787701274

### 今回、制作した備品保管場所を仕事仲間に聞いてみた。
 アプリを、仕事仲間の店長に聞いてみた。
店長の声は
・とっさの時に、備品の在庫が分かるので便利。
・身近なものがあると便利。
・発注タイミングなど分かると良い。
・結局、取りに行かないといけないから不便。

などなど、意見をいただきました。

### 制作してのまとめ
　今回、大まかな**機器・備品**に特化しましたが、
 店長の申すように、アルバイトさんが利用できる店舗での**資材**などのアプリを作った方が使用頻度も多く、利用価値が高いのかもしれません。多くの人が便利になることが作業の時短と安心を与えるのかと感じました。
 　また、50歳の今、今後の夫婦、どちらかに何が起きてもよいように、家の管理していることをアプリにまとめておくのも、**もしもの時**、慌てず行動できるかと思います。
  男って、普段、奥さんの話を話半分、「うん、うん。」ってしか聞いてないですもんね！
 では、いろいろ、試してみますね！
 ありがとうございました。
Salesforceレポートの値を色で強調させよう～条件付き書式の設定～

こんにちは
本橋孝昭です

皆さんはエクセルなどでよく「売上〇円以上は緑で表示、売上〇円以下は赤で表示」などエクセルで色付けしたことはないでしょうか？

https://support.microsoft.com/ja-jp/office/%E6%9D%A1%E4%BB%B6%E4%BB%98%E3%81%8D%E6%9B%B8%E5%BC%8F%E3%82%92%E4%BD%BF%E7%94%A8%E3%81%97%E3%81%A6%E6%83%85%E5%A0%B1%E3%82%92%E5%BC%B7%E8%AA%BF%E8%A1%A8%E7%A4%BA%E3%81%99%E3%82%8B-fed60dfa-1d3f-4e13-9ecb-f1951ff89d7f

エクセルでは条件付き書式で色分けして情報を視覚化することができますね

Salesforceでも、このように特定の条件を設定することで「売上〇円以上は緑で表示、売上〇円以下は赤で表示」などの表現が可能です

![レポート１.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/d530a321-7d77-2049-65b4-6ae12d9a7fd1.png)

上記画像のような表示はSalesforceの「条件付き書式」設定により可能です

今回はこのように色で強調表示をさせるレポートの「条件付き書式」について設定方法をご紹介します


今回の内容は動画でも解説しております


https://youtu.be/xDmiCqFKUqI?si=eeJKJGBcYBo9oDBQ


<iframe width="560" height="315" src="https://www.youtube.com/embed/xDmiCqFKUqI?si=d7dgmdcH-QNCLzXt" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>

なお、「そもそもレポートってなんだ？」という疑問がある方はこちらをご確認ください

https://qiita.com/Takaa/items/00e1e10bcd8460af6af6

## レポートの条件付き書式を設定する前の注意点

まずはレポートの条件付き書式を設定するためにはいくつか条件がございます


## 1 レポートの「グループ化」が必要
まずはレポートのグループ化が必要です
下記の画像は「取引先名」をグループ化したレポートです
しかしながら、これだけではまだ「条件付き書式」設定はできません。では、次にやるべきことはなんでしょうか

![グループ化１.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/ee8428e3-f580-5953-4da4-6879d684ccf3.png)


## 2 色付けしたい数値項目の「集計」（合計値、平均値、最大値、最小値）を設定もしくは集計項目の作成をする

次は「どの項目を色で強調するか」を明確にすることです

「条件付き書式の設定」を行うためには以下のどちらかの設定が必要です

### 2-1. オブジェクトの項目を表示させ、「集計」（合計値、平均値、最大値、最小値）　設定をする
参考：
https://help.salesforce.com/s/articleView?id=sf.reports_builder_fields_summaries.htm&type=5

### 2-2　レポートから項目「集計項目」を作成

レポートから集計項目を作成する方法はこちらを参考にしてください
https://successjp.salesforce.com/article/NAI-000122

このうちのどちらかの設定をしないと「条件付き書式」の設定ができないです。
上記の内容はつまり「色付けで強調表示をさせる項目をちゃんと決めましょうね」という内容です

なお、Salesforceレポートの「条件付き書式」の設定は項目型が数値や金額などが対象です。
なので通常のベタ打ちができる項目型「テキスト項目」や項目型「選択リスト」は対象外なので
「住所に「東京」がついているものを赤色にしたい」
「商談の「完了」を赤で表示したい」
という設定はできないです

さあ、オブジェクトの項目を表示させ、１項目でも「集計」（合計値、平均値、最大値、最小値）　設定をする　とこのように右下に状う権付書式の設定が表示されます

![設定１.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/c3600115-adfd-71d4-a52f-0f1c7b0ec211.png)

## 3 どの値をどの色にするか設定
あとはどのような基準でどの項目にどの色を表示させるか設定して「完了」を行います




こちらで設定は完了です
Salesforceレポートですが、色を付ける設定があまりないので
・これっとどんな結果なんだろう
と分かりにくかったりしますね
今回の「条件付き書式」をうまく使い、分かりやすいレポート作成を目指しましょう！

![レポート１.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531631/d530a321-7d77-2049-65b4-6ae12d9a7fd1.png)
どうする書籍検索 - 2024

# はじめに

皆さん、蔵書や図書館から借りた本や読書記録の管理のために、日々書籍検索WebAPIを使っていると思います。もちろん私もそうなのですが、先日読み終った本のデータをデータベースに取り込もう[^sita]としたら、大変困った状態になりましたので、それについて書いていこうと思います。（まだ解決してませんので変な期待はしないでください。）

[^sita]: 図書館から借りた本だったので、この時点では既に返却してしまっていて、手元にはありませんでした。なので、今回は[Rubyで標準入出力がリダイレクトされてもリダイレクトされていない時と同じように動いてほしい](https://qiita.com/koizumistr/items/8516a6819369d17485eb)に書いたバーコードリーダーの出番はありませんでした。

また、恒例のお断りですが、この文章の内容は、筆者が所属している会社・団体とは一切関わりがありません。いわゆる「自主的な研究の成果の発表」というものです。

# 各WebAPI

## 国立国会図書館サーチ
これが一番良いのだと思います。今までも私は使っていたのですから。

ですが、久々にここにリクエストを投げるプログラムを動作させたところ、"303"と表示されました。HTTPのレスポンスが想定外だった場合には、レスポンスコードを表示させるようにしていましたので、これは"See Other"と言うことです。「ああ、サイトが移動したのね」と言うことで、ここまでならよくあることかもしれません。[国立国会図書館サーチ](https://ndlsearch.ndl.go.jp/)のお知らせにも

> 「国立国会図書館オンライン」と「国立国会図書館サーチ」を統合・リニューアルしました
>
> 2024年1月5日

 (https://ndlsearch.ndl.go.jp/news/2024010400) なんて出ていますし。

そこで、プログラムにあるURLを"iss.ndl.go.jp"から"ndlsearch.ndl.go.jp"に変更して再挑戦です。ええっ！？それでも動きません。レスポンスを見ると「パラメータ不正」とあります。どうやらmediaTypeの値が間違っているようです。[データグループ ID・mediaType 一覧](https://ndlsearch.ndl.go.jp/file/help/api/specifications/ndlsearch_api_ap2_20240105.pdf)を確認して適切な値（例えばbooklet）に修正します。あれっ？でもmediaTypeって数値で指定するのではなかったっけ。ここまで来るともう嫌な予感しかしません、、、

mediaTypeを修正して再リクエストです。予感の通り、動作しませんでした。どうやらデータ形式がプログラムが期待しているものと異なるようです。国立国会図書館サーチのページをあちこち調べると
> 2024年1月、国立国会図書館サーチではDC-NDL（Simple）のデータ提供を終了しました。

とありました。(https://ndlsearch.ndl.go.jp/renkei/dcndl)

そういえば、簡単だからとDC-NDL（Simple）を受信する前提でプログラムを書いていたのでした:scream::sob::scream::sob::scream::sob:

SimpleでないDC-NDLで動作するようプログラムを書き換えようとしましたが、私の能力ではすぐにはできないということで、Qiitaに記事を書いて現実逃避です ← 今ココ

 ## 版元ドットコム
 :::note warn
警告
私はこのWebAPIを利用したことはありません。今回、回避策を調査した時の行き止まりの一つですが、ここに書いておけば誰かの役に立つかな、と。
:::
 https://www.hanmoto.com/about_api を見てください。
 > 版元ドットコムのサイトからご提供しているAPIは、2019年11月末日をもって停止します。
> 下記openBDのAPIをご利用下さい。

だそうです。これでは使いたくても使えませんね。

## openBD
 :::note warn
警告
私はこのWebAPIを利用したことはありません。今回、回避策を調査した時の行き止まりの一つですが、ここに書いておけば誰かの役に立つかな、と。
:::
https://openbd.jp/

版元ドットコムのサイトで代替WebAPIとして勧められているので見てみます。が、[「openBD API（バージョン1）」の提供終了について](https://openbd.jp/news/20230725.html)なんてあります[^alt]。さらに「ガイドライン・利用規約」に
> openBD APIが提供する書誌・書影・内容紹介・書評情報などすべての情報は、本の販促・紹介目的に限り使用できます。

[^alt]: こう書くとopenBD APIの提供が終わってしまったように読めるので補足を。このニュースを出した時に提供していたものを「バージョン1」と呼んでいるようです。現時点でもopenBD APIは動作しており、それがどうやらAPI的には変更はないが中身が異なる「代替API（仮称）」というもののようです。「すくなくとも60か月以上の期間、（中略）代替API（仮称）を提供します。」とのことですから「どうする書籍検索 - 2029」とか書かないといけないかもですね。

とありますので、そもそも私はお呼びでなかった、かも。

## Amazon
確かもの凄く以前に使っていた書籍管理フリーウェアが裏で使っていたはずなのですが、openBDと同じような条件がついて、そのフリーウェアがまともに使えなくなってしまったような記憶が、、、（正確なことは覚えていません。「何だと！それなら自分で書籍管理ソフトを作ってやる」となって国立国会図書館のWebAPIを使うようになったことだけは確実ですが。）

ということで、やはり私はお呼びでないですね。

# で、結局どうする
ここまでに書いたことから、（私が私の目的のために）利用するのは「国立国会図書館サーチ」一択だと思います。（頑張って探せば他にもあるかもしれませんが、ちょっと頑張る気には、、、）

自前主義にこだわるつもりもないのですが、国立国会図書館サーチが変わってしまったのがつい最近ですので、今「国立国会図書館」で検索して見つかるサイトはほとんどが同じ問題を抱えているように思います。やはり、国立国会図書館サーチから（Simpleでない）DC-NDL形式のデータを受信するようプログラムを自分で書き換えるのが筋でしょう。
githubのプッシュでパスワードを求められた。

以下の2記事がとても参考になりました。

・【git エラー解決策】Permission denied (publickey). fatal: Could not read from remote repository. Please make sure you have the correct access rights and the repository exists. が出た時の解決方法
https://tusukuru.hatenablog.com/entry/2018/08/29/021651

・gitでSSH認証済みなのに急にusernameとpasswordを聞かれるようになった話https://zenn.dev/marigotten/articles/e8d675c5117b26

メモ
lessコマンドが使えなかったのでcatで代用。
SSH keyのtitleは無視して良い。
vimは使えなかったらnano
【GAS】Google ClassroomのcourseId等一覧を取得する

## 1　はじめに
最近、Google Apps Script（GAS）を始めました。Googleクラスルームで色々試してみようと思ったのですが、GASの中ではクラスルームはID（courseId）で指定する必要があります。そこで、自分が関係しているクラスルームの名前とcourseIdの一覧を調べるスクリプトを書いてみました。スプレッドシートから作成するコンテナバインドプロジェクトです。  
（続きの投稿がありますのでこちらも読んで下さい↓  
[Google ClassroomのcourseId等一覧を取得する(その2)](https://qiita.com/freshcl/items/2dba10222f2a0487fe23)）
## 2　スクリプト
```JavaScript:listMyCourses.gs
function listMyCourses() {
  const name = 'myclasslist';  //リストを書き出すシート
  const myfile = SpreadsheetApp.getActiveSpreadsheet();
  const newSheet = myfile.insertSheet();  //シートの作成
  const isSheetExist = myfile.getSheetByName(name);
  if (isSheetExist) {   //シート名nameがあれば削除
    myfile.deleteSheet(isSheetExist);
  }
  newSheet.setName(name); //新しいシートの名前をnameにする
  newSheet.clear();
  newSheet.appendRow(["クラス名", "courseId"]); //項目名追加
  const response = Classroom.Courses.list();
  const courses = response.courses;
  if (!courses || courses.length === 0) {
    console.log('No courses found.');
    return;
  } else {
    for (let i = 0; i < courses.length; i++){
      Logger.log('%s(%s)',courses[i].name,courses[i].id);
      newSheet.appendRow([courses[i].name,courses[i].id]);
    }
    newSheet.activate();
    Browser.msgBox(name+"作成");
  }
}
```
結果を書き出すシート名（ここではmyclasslist）を指定しています。同じ名前のシートがあれば、削除して新規にシートを作ります。結果のシートには、一行目が項目名で（クラス名、courseId）、2行目以下に自分がメンバーであるクラスルームの名前とcourseIdが入ります。

## 3　使い方
シート（nameに指定した以外の名前）に図形描画でボタンを作って、このスクリプトを割り当てて使用すると良いと思います。シートmyClassListに書き出されたcourseIdは数値型になっていますが、GASの中でcourseIdを使用する時には文字列である必要があります。

## 4 参考
Google Classroom ガイド Google Apps Script クイックスタート
https://developers.google.com/classroom/quickstart/apps-script?hl=ja

【GAS】Google Classroomで、生徒に個別に連絡をする。（Phys Ken）
https://qiita.com/phys-ken/items/269a118df0bc0c895ad4