OmniAICreator/Qiita-1.07M huggingface dataset:
E2Eテスト自動化：自動化の向き不向きとは？

## E2Eテストとは？
E2Eテストとは、**ユーザー視点でシステムを操作し、システム全体が正しく機能しているか？想定通りの動作となっているのか？をテストすること**です。
E2Eテストを実施することで、ユーザーが利用する際に実際に発生する可能性のある問題やバグを検出することができます。

## E2Eテスト自動化への期待
E2Eテスト自動化に取り組む場合、テスト対象アプリの画面操作を自動化すること（UIテスト自動化）で、テストの効率や品質を向上できます。
最近は、E2Eテスト自動化へ取り組もうという方も多くなってきていますが、E2Eテスト自動化に取り組んだ結果、思ったよりも効果が得られなかったという話はよく聞きます。

「テスト自動化」という言葉を聞くと、ロボットやAIが、これまでやっていた人間の作業を完全に代替えして自動化してくれる、というイメージを持ちがちですが、テスト自動化ツールはあくまでツールであり、決まった操作を実施することを得意としています。
そのため、テスト自動化ツールですべての作業を自動化しようとすると、テストの作成に膨大な時間がかかってしまい、思っていたよりも自動化の効果が得られないといった結果になります。

E2Eテスト自動化を実施するには、自動化の効果が出やすい作業を自動化することが重要となります。
そこで、E2Eテスト自動化に向いているケースと向いていないケースについて、簡単にまとめてみました。

## E2Eテスト自動化に向いているケース
**ユーザーの利用頻度が高い画面や機能**
すべての機能のテストを自動化するのではなく、主機能（王道シナリオ）から自動化をおこないます。
理由として、最初からすべての操作を自動化しようとすると、テストの作成に多くの時間を要してしまい、なかなかテスト実行までに至らないという結果となります。
まずは主機能から自動化をおこない、徐々にテスト範囲を広げていくことで、自動化の効果が出やすくなります。
![main_feature.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3826211/443d20ac-eca4-265f-8009-d65483633446.png)

**複数のデータパターンを入力する画面**
アプリケーションへのログイン操作など、複数のデータパターンをテストする必要がある画面は、自動化に向いています。
理由として、テストツールを利用することで、手動テストでは網羅しきれない、膨大なデータパターンでのテストを短時間でおこなえます。
テスト自動化をおこなうことで、これまで手動テストでは実施しきれなかったテストまで実施することができます。

**複数ブラウザーで動作するアプリケーション**
同じテストを複数の環境で実行する必要がある場合には、既存のテストケースを流用することで、新しくテストケースを作る手間が省けます。
Webアプリケーションの場合、Chrome、Firefox、Edgeブラウザーなどのクロスブラウザーのテストは自動化に向いています。デスクトップアプリケーションの場合、パッチ適用後のOS上でのテストなどで使用できます。

## E2Eテスト自動化に向いていないケース
**ユーザーの主観的な判断が必要な操作**
例として、視覚的な要素や感触に基づく判断が必要な場合、テスト自動化は難しいです。
理由として、自動化ツールはあくまでツールとなり、決まった操作を得意としています。そのため、人の感覚で判断するようなテストをツールでおこなうことは難しく、自動化に向いていません。

**テスト対象が頻繁に変更される場合**
テスト対象アプリの画面が頻繁に変更される場合、アプリの更新内容によっては、作成したテストシナリオの修正作業（メンテナンス）が発生します。
テストシナリオのメンテナンスが多く発生してしまうと、テストのメンテナンスに多くの時間を費やすことになり、本来目的としていたテスト工数の削減がおこなえません。
そのため、テスト対象アプリが頻繁に変更されるケースは、テスト自動化に向いていません。

**実行回数が少ない場合**
テスト自動化は、作成したテストの実行回数が多いほど自動化の効果が見込めます。
テストの作成には多少なりとも時間を要します。作成したテストを1-2回しか実行しない場合、本来手動テストでかかっていた時間よりもテストの作成時間の方が多くなってしまい、自動化の効果が得られません。
そのため、作成したテストは実行回数が多ければ多いほど、費用対効果が高くなります。

## まとめ
今回、E2Eテスト自動化に向いているテスト・向いていないテストについて簡単にまとめてみました。
E2Eテスト自動化に取り組む場合には、向いているテストを自動化することが成功の近道となります。

また、今後自動化ツールを選定する場合、大前提としてテスト自動化ツールがテスト対象アプリに対応している必要があります。

UIテスト自動化ツールの [Ranorex](https://ranorex.techmatrix.jp/) は、デスクトップアプリ・Webアプリ・モバイルアプリに対応しているテスト自動化ツールとなります。
Ranorex はテスト対応アプリの幅が広いため、テスト対象アプリが複数ある場合には、Ranorex １つですべてのアプリに対して自動化できます。

E2Eテストを自動化するメリット、自動化のポイントについては、以下の記事で紹介されています。
合わせてご覧ください。
・E2Eテストとは？
[https://ranorex.techmatrix.jp/what_is_e2e_test/](https://ranorex.techmatrix.jp/what_is_e2e_test/)
IBM i でFTPコマンドを使わずに区画間コピーする手順

# はじめに
「ちゃちゃっと本番区画のデータを開発区画に移したい！けどFTPコマンドってあまり馴染みがないしよく分からない…」という方のためのFTP以外の方法でファイル(オブジェクト)を区画間コピーする手順です。

# こんな人のための記事
- IBM i (AS/400)で区画をまたいでオブジェクトorライブラリーをコピーしたい人。
- SAVFはなんとなく分かる人。
- FTPはよく分からない人。

# 実際の手順
## SAVFの作成
これについては先人の方々がたくさん記事を作成されているのでそちらを参考にしましょう。ざっくりしたイメージは以下の通り。

1. コピー元の区画で`CRTSAVF`でウツワ(SAVFオブジェクト)を作る。
1. `SAVOBJ`もしくは`SAVLIB`でコピーしたいオブジェクトorライブラリーをウツワに入れる。

## SAVFの移動
さてこのSAVFをどうやって隣の(もしくは離れた)区画に移動させるか、なのですが…FTP…よく分からない…と困っていたところ、[ACS](https://www.ibm.com/docs/ja/i/7.5?topic=access-i-client-solutions)の[IFS](https://www.ibm.com/docs/ja/i/7.5?topic=system-integrated-file-ifs)で扱えるということを教えていただきました！

https://www.ibm.com/docs/ja/i/7.5?topic=access-i-client-solutions

https://www.ibm.com/docs/ja/i/7.5?topic=system-integrated-file-ifs

手順は以下の通り。
1. ACSを起動し`システム`でSAVFを作成した区画をプルダウンから選択し、一般の中にある`統合ファイル・システム`を開く。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3112348/64ef47bf-8382-c356-951a-dbc18826e0dd.png)
1. `QSYS.LIB`というフォルダアイコンを探して開く。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3112348/80e02b0d-0f2a-ebe9-5fe9-ffa0be21e463.png)
1. ずらっと有象無象が並ぶので、SAVFを作成したライブラリーを探して開く(今回はNAKANOというライブラリー)。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3112348/c7bda2a8-39fa-e1ec-2307-20beded09b2c.png)
1. その中にSAVFがあるはずなので右クリックからダウンロードする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3112348/23aea0f6-bf23-d5eb-fddd-9063a23341c4.png)

次にダウンロードしたファイルをコピー先へアップロードします。アップロード手順も同様に統合ファイル・システムでOK！
1. `システム`でSAVFをコピーしたい区画をプルダウンから選択し、一般の中にある`統合ファイル・システム`を開く。
1. `QSYS.LIB`というフォルダアイコンを探して開く。
1. ずらっと有象無象が並ぶので、SAVFを一時保管するライブラリーを探して開く。
 (ここで開くのは復元先ではなく、復元するための一時置き場)
1. メニューバーの`アクション`>`アップロード`で、先ほどダウンロードしたSAVFをアップロードする。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3112348/c9345b63-1282-caad-8c03-9323e7a66041.png)

## SAVFの復元
例によって先人の方々がたくさん記事を作成されているのでそちらを参考にしましょう。

1. コピー先の区画で`RSTOBJ`もしくは`RSTLIB`でコピーしたオブジェクトorライブラリーを復元する。


# さいごに
FTPコマンドも使いこなせた方が良いのは間違いないのですが、便利なACSでちゃちゃっと済ませましょう！

# 所感
今回の手順ひとつひとつは大したことないと思うのですが、逆に言えば大したことない手順を知らないがためにやりたいことがスマートにできない状況でした。
特にやりたいことと手順がシンプルに一対一でない場合、調べるのは結構ハードルが高いです。
(今回でいえば`SAVF`と`統合ファイル・システム`は知っていましたが、`QSYS.LIB`に独力ではたどり着けませんでした)
たまたま話しかけやすいスペシャリストが横にいる環境だったので私は解決できましたが、そうでない環境の情シスが解決できるようになるには…AIが解決してくれるんだろか？
Teams Workflows & Pythonでエラー通知

# 背景
内製ツールで発生したエラーをTeamsのチャネルに通知したかったので、Teams Workflowsを使ってみました。
TeamsのIncoming Webhookを使う方法がありますが、Incoming Webhookは2025年末に廃止され、後継としてMS公式からも推奨されているため、今回Workflowsを選択しました。
https://devblogs.microsoft.com/microsoft365dev/retirement-of-office-365-connectors-within-microsoft-teams/

# WorkFlowsの作成
Teamsのアプリ画面からWorkflowsを開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935381/6f8a0ab6-4abd-bce2-6065-b1d06b022bf1.png)


今回はテンプレート「Webhookを受信するとチャネルに投稿する」をそのまま使用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935381/cc88dc51-3ec9-989e-ddf9-b22226a2ed8d.png)


フローを確認します。
「When a Teams webhook request is received」を展開すると、HTTP POSTのURLが表示されます。これは後ほど使用します。

一方「Post card in chat or channel」を展開すると、メッセージを送信したい投稿先、チーム、チャネルを設定できるので、エラー通知を送信したい宛先を設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935381/a75eb28e-8b7a-dd38-4c0e-45712c37d95c.png)


# ツール側でエラー送信
Pythonを例に説明します。
メインの処理でエラーが発生した場合、例外処理として下記関数を呼び出すようにします。

```error_notification.py
import os
import requests
import logging

# ロガーの設定
logger = logging.getLogger(__name__)

def send_failure_notifications(error_message):
    """エラー発生時の通知を送信する"""
    try:
        workflows_url = "{HTTP POSTのURL}"
        notification_message = create_teams_notification_message(error_message)
        response = requests.post(workflows_url, json=notification_message)
        if response.status_code in [200, 202]:
            logger.info("Notification sent successfully.")
        else:
            logger.error(f"Failed to send notification. Status code: {response.status_code}")
    except Exception as e:
        logger.error(f"Error sending notification: {e}")
```

Workflowsのフローで確認したHTTP POSTのURLに向かってPOSTを投げることで、発生したエラーをTeams通知できます。

※create_teams_notification_messageは通知用アダプティブカードを構築する関数です。この部分はご自身の都合に合わせ改変してください。

# 実行結果
狙ったチャネルにアダプティブカードを送信できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3935381/f41c84bb-aeed-2a82-95dd-fbd64b0e5a54.png)

# 最後に
当初予想していたよりも楽に実装できました。
内製ツールのエラー通知をTeamsに投げる需要は地味ながらそこそこあると思いますので、よろしければお試しください。
エッジAIのメリット・デメリットについて調査したこと

# はじめに
ご覧いただきまして、ありがとうございます。
ニッセイ情報テクノロジー株式会社 プロダクト・サービス事業推進室の青木と申します。

本稿では、不正検知AIのエッジ化を検討した際の気づきについて記述いたします。エッジ化を検討し始めた当初は、エッジAIにはメリットしかないものと考えておりましたが、実際に作ってみると、これまで認識していなかったデメリットも存在し、使いどころの難しさがあることを知りました。具体的な内容についてご紹介いたします。

# 不正検知AIのエッジ化を検討した背景
弊社は保険業界向けにサービスを提供していますが、この保険業界において不正請求はつきものであり、古くから問題とされてきました。この対策の１つとして、AIによる不正の自動検知を検討しています。

例えば、医療機関が発行する退院証明書の入院期間を改竄して日数を伸ばし、不正に給付金額を増やそうとされる場合がありますが、この改竄した証明書の撮影画像をAIに入力すると、改ざん箇所を特定し、不正の有無を判断するようなAIです。

この不正検知AIを別のWEBアプリケーションサービスに組み込み、顧客企業様へ提供することを検討いたしました。この時障壁となったのは、AIの処理のために、サーバーサイドへ医療機関が発行する証明書のような機微情報を渡すことを嫌う顧客企業様がいらっしゃったことです。

そこで、エッジサイドで処理させることで機微情報の漏洩リスクを軽減することを検討しました。

# 不正検知AIをJavaScriptで実装してみた
元々はPythonで実装した画像処理による改竄検知AIをJavaScriptで実装し直しました。
- OpenCV等のライブラリについてはPython版からJavaScript版を利用
- numpy (数値計算ライブラリ) はJavaScript版が存在しないため代替になる関数を実装

# JavaScript版（エッジAI）に対する期待
私のエッジAIに対するイメージは、ドライブレコーダーに搭載されたリアルタイム物体検出のような**高速処理**を実現できるものでした。

また、データをサーバーサイドに送信する必要がないため、**ネットワークの負荷軽減**や、機微情報がエッジサイドで処理されることによる**情報漏洩リスクの軽減**を期待していました。

# Python版とJavaScript版の実行結果を比較してみた
私の期待はあっけなく裏切られました。

最も期待していた高速処理は実現できず、速度が1秒以上遅くなった上に、検知精度も低下するという結果になりました。

この原因は３つあると考えています。
1\. 利用したライブラリのマイナーバージョンがJavaScript版の方が低い
2\. エッジサイド（例：スマートフォン）の性能に依存するため、複雑な処理には限界がある
3\. JavaScriptの処理の省力化が不十分であった

サーバーサイドで処理する場合は、高性能な計算リソースを利用できることや、常に最新のライブラリを利用できることから、ネットワーク負荷はある程度発生するものの、全体的に処理速度は速く、精度の高い結果が得られるケースが多いように感じます。

# さいごに
今回の気づきとして、**エッジ化を検討する際には、エッジサイドの性能に依存しないようにすること**と、**エッジ化する機能が複雑にならないように機能を限定すること**が大切だと学びました。

ドライブレコーダーには必要なリソースが内蔵されているのかもしれません。私が検討したようなスマートフォンの性能に依存するような形は、適切ではない可能性があります。不正検知AIのエッジ化について、引き続き研究していきたいと思います。

本稿で取り上げたWEBアプリケーションの詳細はこちらからご覧いただけます。
気になった方はぜひ、こちらもご覧ください。

https://www.nissay-it.co.jp/solution/read-it.html
CSSのボックスモデルについて

テキストボックスはCSSでコンテンツの表示領域を調整する基本的なボックスモデルを扱います。本記事では、初心者が理解しやすいように基本プロパティと実例を交えて解説します。

# 1. width（幅の設定）

::: note info
width はコンテンツの幅を設定するプロパティです。
デフォルトでは、親要素やブラウザの幅に基づいてサイズが決まります。
:::

~~~md:基本的な書き方

h1 {
    width: 200px; /* 幅を200pxに設定 */
}
~~~

# 2. Height（高さの設定）

::: note info
height はコンテンツの高さを指定するプロパティです。
初期値はコンテンツのサイズに合わせて自動調整されます。
:::

~~~md:基本的な書き方
h1 {
    height: 100px; /* 高さを100pxに設定 */
}
~~~

# 3. margin（外側の余白）

::: note info
margin は要素の外側に余白をつけるプロパティです。
また負の要素、値にautoを扱えます
	•	負の値: 要素を引き寄せる。
	•	auto: 要素を親要素の中央に配置する（特に横方向）。
:::

~~~md:基本的な書き方
h1 {
    margin: 20px auto; /* 上下20px、左右中央寄せ */
}
~~~

# マージンの相殺

::: note warn
上下のマージンは相殺されます。
（例: 親の margin-bottom と子の margin-top が重なる場合。隣り合う要素同士のmarginがかぶる場合）
左右のマージンやパディング、ボーダーがある場合 は相殺されません。
:::

# 4.text-align（テキストの中央揃え）

::: note info
text-align は、ボックス内のテキストやインライン要素を水平中央に配置するプロパティです。
:::

~~~md:基本的な書き方
h1 {
    text-align: center; /* テキストを中央揃え */
}
~~~

# 5. box-sizing（ボックスモデルの制御）

::: note info
box-sizing は、要素のサイズにパディングやボーダーを含めるかどうかを制御します。
	•	content-box（デフォルト）: パディング・ボーダーは含まない。
	•	border-box: パディング・ボーダーを含める。
:::

~~~md:基本的な書き方
/* パディングとボーダーを含める設定 */
h1 {
    box-sizing: border-box;
}
~~~

~~~md:例
div {
    width: 200px;
    padding: 20px;
    border: 5px solid black;
    box-sizing: border-box;
}
~~~
結果の違い
| 設定 | 2 |
|:-:|:-:|
|  content-box | width + padding + border |
| border-box  |  width |   


# 6.マージンとパディングの違い
| 項目 | マージン | パディング |
|:-:|:-:|:-:|
| 位置  |  要素の外側にある余白 | 要素の内側にある余白  |
|  影響 | 隣接要素との間隔を調整  |  ボックス内のコンテンツとの間隔を調整 |

# 7.よくある間違いと解決策

1.	テキストがはみ出してしまう
→ box-sizing: border-box; を設定。
2.	中央揃えがうまくいかない
→ テキストの場合は text-align: center、ボックス全体なら margin: auto。
3.	マージンの相殺に困る
→ 親要素に padding や border を設定して解消。

# まとめ
::: note info
width と height: コンテンツの幅や高さを制御。
margin と padding: 要素の外側・内側の余白を調整。
box-sizing: ボックスの計算方法を制御。
中央揃え: テキストは text-align、ボックス全体は margin: auto。
:::
ReactのJSXとJavaScriptのmap()を混同して意図しないfalseが出力された話

## はじめに
JavaScriptのmap()メソッドを使って空文字の要素をスキップする処理が書きたかったのですが、map()メソッドの使い方に誤解があり、躓いてしまいました。
この経験を記録として残しておきます。

## やろうとしていたこと
```js
const FRUITS_LIST = ["りんご", "みかん", "ばなな", "", "ブドウ", ""];
```
このような配列があったとします。
配列に空文字が含まれてしまっているので、空文字をスキップして、全てカタカナの果物のみの配列にしたいと思っていました。
つまり、以下のような結果を期待していました。
```js
['リンゴ', 'ミカン', 'バナナ', 'ブドウ']
```

## 最初に書いていたコード
最初、このようにコーディングしておりました。
```js
const FRUITS_LIST = ["りんご", "みかん", "ばなな", "", "ブドウ", ""];

// カタカナに変換する関数
const formatFruit = (fruit) => {
  return fruit.replace(/[\u3041-\u3096]/g, function(match) {
		var chr = match.charCodeAt(0) + 0x60;
		return String.fromCharCode(chr);
	});
};

const FORMAT_FRUITS = FRUITS_LIST.map((fruit) => fruit !== '' && formatFruit(fruit));
```

### 出力された結果
するとこのように意図しないfalseがデータに入ってしまいました。

```js
['リンゴ', 'ミカン', 'バナナ', false, 'ブドウ', false]
```

## 問題の原因
以下のコードが原因でした。
```js
const FORMAT_FRUITS = FRUITS_LIST.map((fruit) => fruit !== '' && formatFruit(fruit));
```
次の2点によりミスが起きたと思うので、1つずつ深掘りします。
- map()メソッドの使い方を理解していなかった
- Reactでの&&による条件付きレンダリングと混同してしまった

### 1.map()メソッドについて
> 与えられた関数を配列のすべての要素に対して呼び出し、その結果からなる新しい配列を生成します。

MDNからの引用です。
ここから、map() メソッドは元の配列の長さと同じ長さの新しい配列を返すということがわかります。

> 論理積 (&&) はオペランドを左から右に向けて評価し、遭遇した最初の偽値のオペランドを直ちに返します。

こちらもMDNからの引用です。今回のコードで
```js
fruit !== '' && formatFruit(fruit)
```
fruitが空文字の場合、falseが返されます。
そのため、map()によって空文字がfalseに変換されてしまいました。

### 2.Reactでの&&による条件付きレンダリングと混同してしまった
Reactでは条件付きレンダリングによく&&をよく使用します。
例えば以下のようなコードです。
```jsx
{ isAuth && <Profile />}
```
この場合isAuthがtrueなら、Profileコンポーネントがレンダリングされますが、isAuthがfalseの場合何も表示されません。
ReactのJSXで使われるこの書き方と混同してしまったことが今回の原因だと思います。

## 修正
filterを使って、条件に合わないものはスキップするように修正しました。
メソッドチェーンを使うと、コードが綺麗に書けます。
```js
const FORMAT_FRUITS = FRUITS_LIST
  .filter(fruit => fruit !== '')
  .map(fruit => formatFruit(fruit));
```

## 参考
ありがとうございました！

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/map

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Logical_AND

https://qiita.com/mimoe/items/855c112625d39b066c9a
Tips #024 DataSpiderのスクリプトを別の環境で使いたいゾ

https://qiita.com/Novskiiiiiiiiii/items/d3c4a919add7898777ae

今回は私が7年以上愛してやまない最強のデータ連携ツール DataSpider の話です。

DX推進するのにデータ連携ツールを導入していない会社は100000年遅れてるので、
いますぐ導入しましょう。

https://www.hulft.com/software/dataspider

弊社でもバリバリ活用しております。

今回、サーバーαからサーバーβへスクリプトを引越することになったので
その方法を残しておきます。

マイプロジェクトからコピーしたいプロジェクトを右クリックして、
ローカルへダウンロードを選択します。
（実務のものなので真っ黒ですみません）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3891071/e4206ac1-cb59-576c-7c61-6ad8cda01c57.png)

最新のバージョンをダウンロードします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3891071/47485d69-96f0-b82f-e99c-4af7afda8ed9.png)

ダウンロード先フォルダを選択してOKをクリックします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3891071/d1e559eb-d283-51d2-6738-06f91f094c61.png)

ダウンロードが完了しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3891071/6207ba9e-5fb6-f315-e3c1-db0b47ca3f2c.png)

真っ黒で恐縮ですがフォルダが出力されています。
zipなどの圧縮ファイルではなくフォルダなのは意外でした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3891071/2bcf7dde-5f23-3558-2e2c-cd42c0c56850.png)

それでは次にコピー先のサーバーで作業しましょう。
ダウンロードしたフォルダは事前にコピー先のサーバーへ移しておきましょう。

コピー先でアップロードをする際、デフォルトでドキュメントフォルダがひらくので、
ドキュメントフォルダに置いておくとスムーズです。

※ここから先はコピー先のサーバーでの作業です。

マイプロジェクトのファイルから、ローカルからアップロードを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3891071/7cecceab-9a58-381e-8eab-102c3c7656e8.png)

さきほど格納したフォルダがポツンと置かれていますので、
選択して開く、を押下します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3891071/3565eb97-8293-a4e2-2e34-1ec5a94be3fa.png)

今回は新規プロジェクトとしてアップロードします。
既存のものに上書きする場合は既存プロジェクトにコミットしてください。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3891071/62e3dcb1-19d3-788d-e451-f1f375ede3af.png)

プロジェクトのアップロードが完了しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3891071/a091e74b-2926-4ca0-fddc-fc8e8b520194.png)

ちゃんとスクリプトも反映されていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3891071/7ba2ec41-469c-6075-4a88-98f674f002a1.png)

今回コピーしたかったのは１つのスクリプトでしたが、
コピーはプロジェクト単位で行うので、不要なスクリプトは削除しておきます。

非常に便利なツールなので、これを読んだ方はいますぐDataSpiderを導入しましょう。

それではまた。

https://qiita.com/Novskiiiiiiiiii/items/f8c66d18e741998468c2
業務内で困ったこと(その1)

私は今、開発したWMSシステムの保守運用をしています。(社会人3年目 客先常駐)
行う業務は主に5つです
1．エラー発生時の調査・報告・対応指示
2．ユーザー様からのシステム問い合わせ対応
3．ユーザー様のオペレーションミスやシステム不具合で発生したデータに対するリカバリ作業
4．各種マスタデータの修正作業
5．各月の棚卸対応

今回は1で遭遇した事象について記載します。

エラー調査時、SQLを使ってデータ調査しますが、クエリのパフォーマンス等気にせず作成し実行しました。
最終的には実行結果が返ってきましたが、レスポンスが遅いクエリは、サーバに負荷をかけるため避けたいです。

【原因】
・インデックス設定しているのにそれを使わなかったため、フルスキャンしたためパフォーマンスが落ちた。

【対策】
・検索条件でインデックスカラムを適切に使用

インデックスカラムを適切に使うことで、フルスキャンを避け、パフォーマンス向上につながりました。
今後はインデックス設定しているカラムを意識してクエリを作成しようと思います。

もしクエリ結果が返ってこない場合、影響が出そうな場合はプロセスIDを確認してkillすることもできるようです。
killするにはIDが必要です。show processlistで確認できます。
show processlist について、使ったことがなかったためついでに学習しました。
私が理解できた部分を記載します。※浅い知識ですのであしからず。

抽出される項目は以下8つ
・ID・・・接続ID
・User・・・実行したMySQLユーザ
・HOST・・・接続しているホスト名
・DB・・・DB名。選択していなければNULL
・COMMAND・・・実行しているコマンドの種類
・TIME・・・コマンドの状態になってからの経過時間(秒)
・STATE・・・現在の状態
・INFO・・・SQLなどの実行している内容。実行していない場合はNULL

Commandステータス(ほかにもいろいろありますが2つ記載します。)
Sleep・・・SQLクライアントが新しいSQLステートメント(クエリ)の実行を待機している状態(セッションアイドル状態)。 
Query・・・クエリを実行している状態

Stateの内容(ほかにもいろいろありますが3つ記載します。)
Sending to client・・・サーバーがクライアントにパケットを書き込んでいます。
executing・・・クエリの実行を開始
Updating・・・更新処理中(※ロック解除待ち状態のときや、更新中はこのStateになる）

気になる方は公式ページをご参照ください
Commandの値について
MySQL :: MySQL 8.0 リファレンスマニュアル :: 8.14.2 スレッドのコマンド値
https://dev.mysql.com/doc/refman/8.0/ja/thread-commands.html

Stateの値について
MySQL :: MySQL 8.0 リファレンスマニュアル :: 8.14.3 一般的なスレッドの状態
https://dev.mysql.com/doc/refman/8.0/ja/general-thread-states.html
パワポの資料作成を生成AIで効率化する

# はじめに
ご覧いただきまして、ありがとうございます。
ニッセイ情報テクノロジー株式会社 プロダクト・サービス事業推進室の青木と申します。

私、普段はエンジニアとしてではなく、IT企画などの企画担当として業務を行っています。そのため、ソースコードを書くよりも、パワポに文字を書く業務の方が多いことから、最近は生成AIを活用した資料作成の効率化について考える機会が増えています。

この記事では、私なりの資料作成における生成AIの活用術について記述いたします。

# パワポ資料作成における生成AIの使いどころ
私は２つの目的で生成AIを使います。
1\. アウトラインを考えるため
2\. 文章要約のため

もっと使いこなしている方は、図表の作成やスライド自体の作成にも生成AIを活用されているのかもしれませんが、私は見た目やトーン＆マナーに拘りたい派なので使いません。

# 具体的な使い方（１．アウトラインを考える）
資料の作成目的を伝えて、アウトラインを考えてもらいます。

入力メッセージ：
>Qiitaのアドベントカレンダーに記事を投稿します。 今回は、生成AIを活用した資料作成術に関して記事を書きたいと考えています。この記事のアウトラインを考えてください。

出力メッセージ：
>以下は、生成AIを活用した資料作成術に関する記事のアウトラインの提案です。
>1\. はじめに
>- 記事の目的と背景
>- 生成AIとは何か
>- 資料作成におけるAIの重要性
>
>2\. 生成AIの基本的な使い方
>- アカウント作成とログイン方法
>- インターフェースの紹介
>- 質問の仕方と応答の受け取り方
>
>3\. 資料作成における生成AIの活用方法
>- アイデア出し
>…

プロンプトを入力するときには以下のことを心がけています。
- 自分の立場や役割を伝える
- 資料の作成目的を伝える
- 資料の利用シーンを伝える
- （必要に応じて）使用してほしいキーワードを箇条書き(#)で伝える

これだけで、かなり参考になるアウトラインを考えてくれます。
あとは、人が取捨選択すればアウトラインは完成します。

また、目次レベルを読んだだけでは内容が想像できないものについては、生成AIにどのような内容を書けばいいのかを追加で聞きます。

入力メッセージ：
>生成AIの応答の信頼性とは、具体的にどのようなことを書けばいいのでしょうか？

出力メッセージ：
>AIの応答の信頼性について書く際には、以下のポイントを考慮すると良いでしょう。
>1. 情報の正確性
>- 生成AIが生成する情報の正確性について説明します。特に、特定のデータや事実に基づく応答がどの程度信頼できるかを示すことが重要です

参考程度に使うのがいいと考えます。
Qiitaのガイドラインにも注意書きがある通り、生成AIが生成したものは内容が正しいかどうかを検証していないため、ご自身で参照元となる情報かを確認したり、情報の出所を明確にしたりする必要があります。

それでも、ゼロから考えるよりも早くアウトラインを作ることができます。

# 具体的な使い方（２．文章要約）
私は各スライドのサブタイトルに書く文章を悩んだ際に、生成AIを活用します。
スライドで伝えたい内容は頭に浮かんでいるのですが、短い１行の文章にまとめようとすると、どうしてもきれいな文章を書こうとしてしまい、手が止まってしまうことがあります。

そのような時には、生成AIに文章を考えてもらうのが良いです。
スライドで伝えたい内容を箇条書き(#)で伝えて、この内容に合う短い文章を考えてもらいます。

すると、いくつかの候補文章を提案してくれるので、その中から良いと思うものを選ぶだけで、きれいな文章を書こうと悩んでいた時間を短縮することができるので便利です。

# さいごに
エンジニアの方は資料作成が苦手な方が多いように感じます。専門技術の追求に偏りがちで、ご自身の考えが中心になり、聞き手がどう感じるかは考慮されていないことが多いです。

伝わる資料の基本はストーリーがあること、アウトラインが整っていることです。このアウトライン作成を生成AIに頼るだけでも、資料の質は格段に上昇すると思います。

一方で、資料作成が苦手な方は、生成AIを使用しない方が良いとも考えます。
どのような事柄においても、苦労したからこそ得られる経験こそが重要であり、資料作成の基本的な知識も身に着けていないのに、最初からAIに頼っていては、人間として成長することは難しいと考えます。

知識はあるが面倒に感じるときに、生成AIで資料作成を効率化するのが有効ですが、本来集中したいこと（技術研究）に時間を使うために、面倒な資料作成は何も考えずにAIに頼るのも良いと思います！効率的に社会人生活を送りましょう！
AstroでNano Storesを使ってみた

# はじめに
HRBrainでオウンドメディアやランディングページの開発を担当している中川です。
AstroでNano Storesを使う機会があったので、備忘録的にまとめてみます。
本記事は HRBrain Advent Calendar 2024 の10日目の記事です。

https://qiita.com/advent-calendar/2024/hrbrain

# Nano Storesとは

Nano Storesとは、小さくて、高速で、使いやすいJavaScriptの状態管理ライブラリです。複雑な設定やボイラープレートコードなしで、コンポーネント間でデータを簡単に共有し、更新を追跡できます。React、Vue、Svelteなど、様々なフレームワークで使用でき、シンプルなAPIで直感的に操作できます。状態管理に初めて挑戦する方にもおすすめです。軽量なので、バンドルサイズを小さく抑えたい場合にも最適です。
詳しくは公式ドキュメントを参照してください。

https://docs.astro.build/ja/recipes/sharing-state-islands/

https://github.com/nanostores/nanostores


# Astroとは

Astroは、Webサイトを構築するための最新の静的サイトジェネレーターです。コンテンツ中心のウェブサイト構築に重点を置いており、高速なパフォーマンスと優れた開発体験を提供します。
詳しくは以下で解説しているので、興味がある方はよかったら見てください。

https://qiita.com/N4K4J/items/b4f141526d142708b9dd

# 実際にNano StoresをAstroに導入してみた

今回はAstro + Vue.jsのプロジェクトなので、Vue用のものをインストールします。

```bash
npm install nanostores @nanostores/vue
```

以下は、シンプルなカウンターの例です。


```store.js
import { atom } from 'nanostores'

export const count = atom(0)
```

```Component.vue
<template>
  <div>
    <p>StoreCount: {{ storeCount }}</p>
    <p>count.get(): {{ countGet }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script>
import { useStore } from "@nanostores/vue";
import { count } from "./store.js";

export default {
  setup() {
    const storeCount = useStore(count);
    const countGet = count.get();
    const increment = () => {
      count.set(count.get() + 1);
    };
    return {
      storeCount,
      countGet,
      increment,
    };
  },
};
</script>


```

```index.astro
---
import Component from './Component.vue'
---
<Component client:visible />
```
初期状態では、storeCount と countGet はどちらも 0 です。ボタンをクリックすると、storeCountは1に更新されます。このとき、template内のcountGetは初期値の0のままです。

useStore(count) を使うことで、ストアの値の変化に反応してコンポーネントを再レンダリングすることができます。一方、count.get() は、特定の時点でのストアの値を取得したい場合に便利です。リアクティブな更新が必要ない場合は、count.get() を使用することでパフォーマンスのオーバーヘッドを避けることができます。

以下で動作確認できます

https://codesandbox.io/p/devbox/vldchq

# おわりに
このように、Nano StoresはAstroで簡単に利用でき、軽量でシンプルな状態管理を実現できます。 Astroを使用する際の状態管理ライブラリの選択肢としてはとても有力だと思うので、試してみてください。

# PR
HRBrainではフロントエンドエンジニア（コミュニケーションデザイン）の採用も行なっているので、Astro触ってみたいよ〜って方はぜひご応募ください！

https://hrmos.co/pages/hrbrain/jobs/2110310

https://www.hrbrain.co.jp/recruit
Djangoに入門して知ったWSGIについて理解する

# はじめに

Django に入門していて、WSGI という単語が出てきて困惑したので、自分なりに調べてわかったことを簡単にまとめました。Django や Flask などの python のフレームワークを使った開発をこれから始める方やおさらいしたい方の参考になれば幸いです。


# 結論

- 通常の Web サーバ（例: Nginx や Apache）は、直接 Python コードを実行することはできないため、WSGI を介してアプリケーションサーバと連携する
- WSGI の非同期対応版が ASGI サーバ


# WSGI とは？

WSGI は Web Server Gateway Interface の略で、**WSGI は、Web サーバと Python アプリケーション間でのリクエストとレスポンスのやりとりを規定する仕様**です。

これだけだと、？？って感じだと思うので、もう少しだけ説明します。

### そもそも Web サーバとアプリケーションサーバとは

Web アプリケーションを作成する時には、通常、Web サーバとアプリケーションサーバが必要です。

AWS にいい記事がありました。

https://aws.amazon.com/jp/compare/the-difference-between-Web-server-and-application-server/

これは Django 以外の何らかのフレームワーク(Rails や Laravel など)でアプリケーションを作成したことがある方には馴染みがあると思います。nginx や apache のことです。ローカルで開発している場合は、`php artisan serve`などで簡易的なサーバを立てているのであまり Web サーバを意識しないかもしれませんが、本番環境ではこの Web サーバが必要になります。

Web アプリケーションの場合、クライアントは、ブラウザから Web サーバにリクエストを送ります。Web サーバはリクエストを受け取ったらさらにアプリケーションサーバに対してリクエストを送ります。アプリケーションサーバはいわゆるバックエンド部分のビジネスロジックだったり DB へのアクセスなどを行い、その結果を Web サーバに返します。Web サーバはその結果をクライアントに返します。

クライアント（ブラウザ）→Web サーバ → アプリケーションサーバ →Web サーバ → クライアント（ブラウザ）って感じです。

クライアントとアプリケーションサーバの間に Web サーバが挟まっているのを見ると、プロキシの構造と似ていると思いますが、実際 Web サーバはプロキシサーバ（リバースプロキシ）としての機能もあります。

<br>

### WSGI サーバと実行環境

ここまでが、Web サーバとアプリケーションサーバの説明で、ここで WSGI が登場します。
上述したように WSGI は、Web サーバとアプリケーションサーバの間にあり、Web サーバとアプリケーションサーバの間のやりとりを管理するのですが、なぜ WSGI が必要なるのかというと、

**Web サーバは直接 Python コードを実行することができない**からです。実行環境がないという感じです。

ここで自分は「じゃあ Laravel は？」と思ったのですが、Laravel の場合は、Web サーバに PHP-FPM といったものがあり。それを介して PHP を実行することができます。なので、Web サーバとアプリケーションサーバの間には何もなくてもうまくやり取りができるのです。（単語は知っていたがあまり理解していなかった...）

https://www.php.net/manual/ja/install.fpm.php

python の場合は、そのような機能はないので、WSGI を介してアプリケーションサーバと連携することで Python コードを実行することができるのです。

![](https://storage.googleapis.com/zenn-user-upload/8dd086c4de30-20241116.png)

<br>

![](https://storage.googleapis.com/zenn-user-upload/b10cb7795485-20241116.png)

https://wsgi.readthedocs.io/en/latest/

とはいっても、WSGI は Django のデフォルトのサーバである `runserver` では使われていない(runserver は開発用サーバ) & 本番環境でもそこまで意識しなくてもアプリケーションを作成できるので、あまり深く考えずに、python を実行するためのものと理解しておけばいいと思います。

ASGI も考え方は同じで、WSGI の非同期対応版です。
ここでは詳しく説明しませんが、気になる方は以下の記事をご覧ください。

https://asgi.readthedocs.io/en/latest/

# まとめ

フレームワークが便利すぎて、WSGI や ASGI を意識しなくてもアプリケーションを作成できてしまうので、ここら辺はおろそかになりがちですが、Django を勉強しているときにやたら出てくるので、おおまかな理解はしておくことは大事かなと思いました。(PHP-FPM も知るいい機会になった)

<br>

# 参考

https://aws.amazon.com/jp/compare/the-difference-between-Web-server-and-application-server/

https://www.php.net/manual/ja/install.fpm.php

https://wsgi.readthedocs.io/en/latest/

https://asgi.readthedocs.io/en/latest/
ML-AgentsでAIにゲームをさせてみた


## はじめに
この記事では、文化祭に展示していた、AIがプレイしているゲームのAIをどう作ったかを説明していきます


## 作ったもの

りんごと爆弾が上から落ちてくるシンプルなゲームを作成し、AIに「りんごを取る」「爆弾を避ける」行動を学習させました。

![スクリーンショット 2024-11-29 12.35.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2780676/6307ae65-45cf-117f-bf8b-76d2ab689e58.png)





## 作品作成の流れ

以下の4つの手順でゲームを作成しました：

1. Unityで基本的なゲームを作成する
2. ML-Agentsをインストールしてセットアップする
3. AIが学習できる環境と設定を作る
4. 深層強化学習を使ってAIをトレーニングする

各ステップの詳細を順に説明していきます。



## 1. Unityでゲームをつくる

本当に少しUnity(およびC#)の知識があれば簡単なゲームは作れます。

プログラム面では今回は、りんご・爆弾を管理するプログラム(FallController.cs)、プレイヤーを操作するプログラム(AgentController.cs)を作成しました。

FallController.csでは、落とすオブジェクトの種類(りんごか爆弾か)や、オブジェクトの位置(プレイヤーとの当たり判定で使う)などを管理し、プレイヤーと当たったかどうかを確認して、当たったら上から落とす関数などを作り、

AgentController.csでは、パソコンの矢印キーで動くようにし、りんごに触れたらポイントを増やし、爆弾に触れたら減らす関数などを作っています。



## 2. ML-AGentsをインストール

ML-Agentsは、UnityでAIをトレーニングしたり、ゲーム内で動作させたりするためのフレームワークです。これを使用することで、深層強化学習を手軽に導入できます。


現在公開されているのは、「ML-Agents-Release-22」というバージョンなのですが、僕は「ML-Agents-Release-20」を使いました(特に理由はありません)

## 3.0. 深層強化学習とは

深層強化学習では、AIが動いた動きがどれくらい良いか、どれくらい悪いかを評価していくことでAIの行動をどんどん改善させていく手法です。

まず最初に、AIに情報を与える設定をします。

次に、どんな時に報酬を与えるかの設定を考えます。

これで学習すると、この条件で高得点を取るようにAIが行動を改善していってくれます。



## 3. AIの設定と学習の準備

AIが学習できるように、ゲームのプログラムを少し変更する必要があります。

全部説明すると長いので、簡単に説明します。

------

### 2. AIに与える情報を設定 

AIはゲーム環境の情報を元に行動を決定します。そのため、`CollectObservations`関数というものをつかってAIに与える情報の設定をしました。

与えた情報としては、今回の場合は、落ちてくるものをとった時のポイント、落ちてくるものの速さ、場所を設定しました。



```c#
public override void CollectObservations(VectorSensor sensor)
{
    sensor.AddObservation(Apple.transform.position.x); // りんごのx座標
    sensor.AddObservation(Bomb.transform.position.x);  // 爆弾のx座標
    sensor.AddObservation(transform.position.x);       // プレイヤーのx座標
}
```

↑プログラムイメージ

------

### 3. AIの行動を受け取る

AIが選んだ行動に応じて、プレイヤーを操作します。

AIは-1~1の値で行動を表してくるので、適切に動くようなプログラムをかきます。

今回はAIは右・左にだけ動くので値が大きいほど右に、小さいほど左に動かすようにしました。


```c#
public override void OnActionReceived(ActionBuffers actions)
{
    float moveX = actions.ContinuousActions[0]; // AIが選択した行動
    transform.Translate(moveX * Time.deltaTime * speed, 0, 0); // プレイヤーを移動
}
```

↑プログラムイメージ

------

### 4. 報酬の設定 

AIに行動の良し悪しを教えるために、「報酬」の設定をします。

例えば、今回は、りんごを取ったら+1, 爆弾を取ったら-5, りんごを無視したら-0.1, 爆弾を取ったら+0.5という得点をAIに与えるようにしています。

```c#
if (hitApple)
{
    AddReward(1.0f); // りんごを取ったら +1
}
if (hitBomb)
{
    AddReward(-5.0f); // 爆弾を取ったら -5
}
if (missedApple)
{
    AddReward(-0.1f); // りんごを無視したら -0.1
}
if (avoidedBomb)
{
    AddReward(0.5f); // 爆弾を回避したら +0.5
}
```

↑プログラムイメージ

------

### 5. エピソードを終了する

ゲームが終了したとき、AIのプレイを強制的に終わらせてあげます

AIは、何千万回もプレイを繰り返す中で少しずつ上達していきます。この1プレイ（エピソード）の間に、どんな行動が良かったか、悪かったかを評価し、それを基に次のプレイで行動を改善していきます。

鬼ごっこなどは鬼に捕まったら終わりなのに対し、今回は明確な終わりが存在しません。なので、AIが~回行動を選択したら終わらせる、のような設定にしました。

```c#
if (gameOver)
{
    EndEpisode(); // ゲーム終了
}
```

↑プログラムイメージ

## 4. AIの学習

あとは先ほどのプログラムと、AIの学習方法(どれくらい複雑な学習をするのか、深層強化学習の中の種類、AIを何回に一回保存するか、AIに何回プレイさせるか)などの設定を作り、それをもとにコマンドで学習したら完成です！



## おわりに
どうでしたか？ML-Agentsでは簡単にAIを使ったゲームが作れるような色々なものが用意されているため、相当簡単にゲームにAIを実装することができます！
TanStack React TableでsetFilterValueに数値の範囲を指定する方法

TanStack React Tableでは、setFilterValueを使って列のフィルター値を設定できます。数値の範囲でフィルタリングしたい場合は、以下のように実装できます。

## 1. カスタムフィルター関数の作成

まず、数値の範囲でフィルタリングするカスタム関数を作成します:

```typescript
const numberRangeFilter: FilterFn<any> = (row, columnId, filterValue) => {
  const rowValue = row.getValue(columnId) as number;
  const [min, max] = filterValue as [number, number];
  
  if (min !== undefined && max !== undefined) {
    return rowValue >= min && rowValue <= max;
  } else if (min !== undefined) {
    return rowValue >= min;
  } else if (max !== undefined) {
    return rowValue <= max;
  }
  
  return true;
};
```

## 2. フィルター関数の登録

作成したフィルター関数をuseReactTableのオプションに登録します:

```typescript
const table = useReactTable({
  data,
  columns,
  filterFns: {
    numberRange: numberRangeFilter,
  },
  // その他のオプション...
});
```

## 3. 列の定義でフィルター関数を指定

フィルタリングしたい列の定義で、作成したフィルター関数を指定します:

```typescript
const columns = [
  {
    accessorKey: 'age',
    header: '年齢',
    filterFn: 'numberRange',
  },
  // その他の列...
];
```

## 4. フィルター値の設定

最後に、setFilterValueを使ってフィルター値を設定します:

```typescript
const handleFilterChange = (min: number, max: number) => {
  table.getColumn('age')?.setFilterValue([min, max]);
};
```

このようにして、TanStack React Tableで数値の範囲を指定したフィルタリングが実現できます。

## まとめ

TanStack React Tableを使用して数値の範囲でフィルタリングする方法を紹介しました。
カスタムフィルター関数を作成し、適切に設定することで、柔軟なフィルタリング機能を実装できます。

この方法を応用することで、日付範囲や他の複雑なフィルタリングにも対応できるでしょう。
【ESlint,prettier】Vue+javascript

vueとjavascript環境でESlintをかける機会があったので記録として残しておきます。

今回はフォーマッタとしてprettierを使用します。

### prettierのインストール＆設定
```bash
npm install --save-dev prettier eslint-config-prettier
```
```prettier.config.js
module.exports = {
    printWidth: 120, // 折り返す行の長さ
    tabWidth: 4, // タブサイズ
    singleQuote: true, // シングルクォートに置き換え
    jsxSingleQuote: true, // シングルクォートに置き換え(JSX)
    singleAttributePerLine: true, // JSXの属性ごとに改行する
    semi: false, // 行末のセミコロンを削除
    endOfLine: 'lf', // 改行コードを指定
}

```

### ESlintのインストール＆設定
```bash
npm install --save-dev eslint
```

```bash
npm install --save vue-eslint-parser eslint-config-prettier eslint-plugin-prettier
```

今回の環境はeslint v8.49.0で行っています。
v9以降ではeslintrcファイルは非推奨となるようです。
実際、v9をインストールして以下のファイルは使用できませんでした。


設定の内容はpretteirを使う設定と、vueのparserを入れています。
```.eslintrc.cjs
module.exports = {
    extends: ['eslint:recommended', 'plugin:prettier/recommended'],
    plugins: ['prettier'],
    parserOptions: {
        ecmaFeatures: {
            jsx: true,
        },
        ecmaVersion: 13,
        sourceType: 'module',
    },
    parser: 'vue-eslint-parser',
    env: {
        browser: true,
        node: true,
        es2022: true,
    },
    globals: { process: true },
    rules: {
        'no-debugger': 'warn',
        'no-unused-vars': 'warn',
    },
}

```

### package.jsonの設定
ルートディレクトリで実施するようにしています。
scriptのlintで`backend`と`frontend/src`に対して実施しています。
```package.json
{
    "name": "example",
    "version": "1.0.0",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1",
        "lint": "eslint --fix backend/**/*.{js,cjs} && eslint --fix frontend/src/**/*.{js,vue}"
    },
    "author": "",
    "license": "ISC",
    "devDependencies": {
        "eslint": "^8.49.0",
        "eslint-config-prettier": "^9.1.0",
        "eslint-plugin-prettier": "^5.2.1",
        "prettier": "^3.4.1",
        "vue-eslint-parser": "^9.4.3"
    }
}

```
【Java】index++ と ++index の違い

# はじめに
LeetCodeの問題「4Sum」の解説を読んで疑問を覚えたので調べた内容をメモ。

# 問題
整数の配列から２つの要素をピックアップして、２つの数字の合計値がターゲットとなる数字と一致した場合、２つの数字をリストに追加するという処理の部分。
（解説のコードとは少し変えてあります。）

```java
int nums[] = [-2, -1, 0, 0, 1, 2];
int target = 3;

List<List<Integer>> res = new ArrayList<>();
int left = 0;
int right = nums.length - 1;

while (left < hi) {
	int currSum = nums[left] + nums[right];
	if (currSum < target || (left > start && nums[left] == nums[left - 1])) {
		++left;
	} else if (
		currSum > target ||
		(right < nums.length - 1 && nums[right] == nums[right + 1])
	) {
		--right;
	} else {
		res.add(Arrays.asList(nums[left++], nums[right--]));
	}
}
```

以下の部分であれ？？となった。
addするときにインクリメント（とデクリメント）しちゃったらダメじゃん？と。
```java
res.add(Arrays.asList(nums[left++], nums[right--]));
```

# なぜこうなるか
```java
System.out.println(array[++index]);
```
上記のように `++` を先に書いた場合は、まずindexに１を加えてから値を与える。

一方で
```java
System.out.println(array[index++]);
```
のように `++` を後に書いた場合は、まずindexの値を与えてからインクリメントする。
  
  
例）
```java
int nums[] = [-2, -1, 0, 0, 1, 2];
int i = 0;
System.out.println(nums[++i]); // -> -1
// -> indexをまずインクリメントするのでnums[1] が出力される。

System.out.println(nums[i++]); // -> -2
// -> まずiの値を与えるので nums[0] が出力され、それからiをインクリメントする。
```

# おわりに
5年近くやってて、ほんっとに知らなかった。ここで知れて良かった・・・
LLMの評価モデルの作成のために参考にした記事など

## はじめに

LLMを組み込んだアプリケーションの評価手法について、色々と調査しています。
私が以前、取り組んでいた機械学習の評価を含む運用などはMLOpsとも呼ばれ、ある程度、手法が確立されてきていたりしますが、LLMに関連する手法はLLMOpsとも言われており、まだ発展途上の状況であるのが現状のようです。自然言語の評価は単一の正解を定めにくく、「流暢さ」や「一貫性」といった複雑な基準の評価をするには、どうしても人間の判断が必要となる点で、MLOpsと大きく異なります。そして、そのことは、スケールした場合に手間が増加してしまうなどスケーラビリティに関連する課題を生み出します。
それを解決するために計算ベース(Computation-based)に加え、モデルベース（Model-based)と呼ばれる手法を用いたLLM-as-a-Judgeという手法があります。モデルベースとは、LLM自体を判定モデルとして用いて、人間による評価に近い形で評価を行う手法のことで、プロンプトを作成して行います。プロンプトによる評価は0か1かというはっきりとしたものに加えて、グラデーションのあるものにも対応することができます。このような、計算ベース（BLEU、ROUGEなど）と、モデルベースを組み合わせた手法は、Gen AI evaluation serviceなどに評価手法として取り入れられており、非常に参考になります。

# データセットの作成から評価までのPDCAを回す

LLMOpsにおいて重要な点は、評価のみを単独で作るのではなく、評価するためのデータセットの作り方などから一貫した設計が必要となる点です。学校のテストなどにおいても出題範囲が設定され、その出題範囲のなかでの評価を行うことで公平な比較ができるように、LLMの評価においてもこうしたデータセットから一貫性をもった設計を行うことが必要となります。また、データセットの作成および評価についての良し悪しは、最終的には人間の判断が必要で「ドメインエキスパート」と呼ばれるその分野に精通した人の存在も必要となります。こういった実際の使われ方などを見て漸進的に改善していくプロセスとして、EvalGenというフレームワークが提唱されていて、非常に参考になります。

# 参考にした記事

データセット
1.人間が作る
2.LLMなどに作ってもらう(Synthetic Dataset)
3.実際の実行ログから集める
https://zenn.dev/gaudiy_blog/articles/ba06e37d226182#評価基準をどう作るか

LLMを活用したLLMの評価アプローチ
https://arxiv.org/abs/2304.10149

LLM-as-a-Judge
https://techblog.gaudiy.com/entry/2024/10/22/105609

https://blog.g-gen.co.jp/entry/gen-ai-evaluation-service-in-vertex-ai

RagasとPromptFlowでのメトリックの違い
https://zenn.dev/headwaters/articles/c9c3f19d5edc62

https://speakerdeck.com/nohanaga/azure-machine-learning-prompt-flow-ping-jia-metorikusujie-shuo?slide=21

https://medium.com/microsoftazure/evaluate-rag-qna-apps-with-azure-ml-promptflow-9931356f13ea

LLM as a Judege
https://techblog.gaudiy.com/entry/2024/10/22/105609

Ambiguous Question
https://techblog.asia-quest.jp/202409/specific-evaluation-methods-actually-used-to-improve-the-accuracy-of-llm-responses

https://blog.shuntaito.net/posts/begin-thinking-llmops

https://qiita.com/nohanaga/items/b68bf5a65142c5af7969#-qna-groundedness-evaluation


https://nttdocomo-developers.jp/entry/20231211_1


https://qiita.com/ksonoda/items/28586434904c26ec465b


https://engineers.ntt.com/entry/2023/09/25/091245
OMC235B(637)

https://onlinemathcontest.com/contests/omc235/tasks/3387

金額$X$をお釣りなく支払う硬貨の最小枚数が$f(X)$です。
$f(X)=N$なので、$N$は枚数です。
$f(X)=N$を満たす$X$が複数存在しますが、そのうち最も小さい$X$を$g(N)$とします。
例えば金額$1$と金額$5$の場合、$f(1)=f(5)=1$枚ですが、$g(N=1)=1$です。
言い換えると、硬貨$N$枚で支払える最少額が$g(N)$ということです。

$N=1～4$の場合、それぞれ金額$1$の硬貨$1～4$枚が最少額です。
したがって、$g(1)=1,g(2)=2,g(3)=3,g(4)=4$です。

硬貨$5$枚の場合、金額$1$の硬貨$4$枚と金額$5$の硬貨$1$枚がもっとも小さくなり、$g(5)=9$です。

(金額$1$の硬貨$5$枚は金額$5$の硬貨$1$枚と同額であり、少ない枚数は後者であるので不適です。また、金額$5$の硬貨を$2$枚以上含んでしまうと金額$9$より大きくなるので、これも不適です。)

同様に$g(6)～g(8)$も$g(5)$に金額$5$の硬貨を$1$枚ずつ足すことが最少額となるので、$g(6)=14,g(7)=19,g(8)=24$となります。

ここで、硬貨$8$枚(金額$1$と金額$5$の硬貨各$4$枚)で表せる額は、金額$5^2$の硬貨$1$枚で表せる額から$1$を引いた額である$g(8)=5^2-1$になりました。
$g(9)$は$g(8)$に金額$5^2$の硬貨$1$枚を加えるので、$g(9)=2×5^2-1$です。
同様に$g(10)=3×5^2-1,g(11)=4×5^2-1,g(12)=5×5^2-1$です。
$g(12)$の$5×5^2-1$は$5^3-1$です。

したがって、$\Sigma_{N=8}^{11}g(N)=(1+2+3+4)×5^2-4$が言えるので、$g(0)=5^0-1=0$も許容して、$g(4×a)～g(4×(a+1)-1)$の和を計算すると、
$\Sigma_{N=0}^{3}g(N)=(1+2+3+4)×5^0-4$
$\Sigma_{N=4}^{7}g(N)=(1+2+3+4)×5^1-4$
$\Sigma_{N=8}^{11}g(N)=(1+2+3+4)×5^2-4$
$\Sigma_{N=12}^{15}g(N)=(1+2+3+4)×5^3-4$
$\Sigma_{N=16}^{19}g(N)=(1+2+3+4)×5^4-4$
$\Sigma_{N=20}^{23}g(N)=(1+2+3+4)×5^5-4$
$\Sigma_{N=24}^{27}g(N)=(1+2+3+4)×5^6-4$
$\Sigma_{N=28}^{31}g(N)=(1+2+3+4)×5^7-4$
です。

上記の各式から$g(1)～g(30)$の和は、
$\Sigma_{N=1}^{30}g(N)=(1+2+3+4)×(5^0+5^1+5^2+5^3+5^4+5^5+5^6+5^7)-32-g(31)$
$=10×\frac{5^8-1}{5-1}-32-(4×5^7-1)$
$=\frac{10×(390625-1)}{4}-32-(312500-1)$
$=664029$
となり、解を求めることができました。
OnVUE（ピアソンVUEオンライン試験）の動作テストで「chrome [g fxngbv」が起動しており試験に進めれなかった件

# OnVUEの事前動作テスト
オンライン試験開始前に、起動しているアプリの検出テストが行われ、
無関係のアプリ（やプロセス）が起動している場合は停止するように求められます。

ChromeなどのブラウザやMicrosoftTeamsなどの常駐アプリも当然その対象です。
これらのアプリを停止しないと、試験を開始することができません。

# 謎のChromeプロセスが起動したままに…
すべてのアプリを停止し、検出テストボタンを押したところ、
謎のchrome関連プロセスが起動したままと判定されてしまい
試験にすすむことができませんでした。

![ss.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2723699/f7b29f34-d6fd-8975-df33-534b3a1757a9.png)

タスクマネージャーから起動中のプロセスを確認しても、
Chrome関連のプロセスは何も起動していないように見えます。
原因がわからず試験開始時間になってしまい、再予約をすることに…。

# （原因）chrome remote desktop が有効だった
色々切り分けた結果、どうやらChrome remote desktopを有効にしていると
この文字化けしているプロセス名のエラーが表示されて先に進めないようでした。

検出テストに合格するために、Chrome remote desktop を無効化します。

以下のリンクから chrome remote desktop のページへ飛べます。
https://remotedesktop.google.com/access

画像の通り、ゴミ箱マークから、Chrome remote desktop を無効化します。

![ss2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2723699/ca080d47-e70f-845c-1faf-ba848249e73b.png)


これで検出アプリの検出テストも通過し、無事オンライン試験を開始することができます。
試験、頑張ってくださいね。
atcoder練習(2024.11.29)

# キーワード
-1, 
# 問題
https://atcoder.jp/contests/abc376/tasks/abc376_a

## 問題文
不思議なボタンがあります。
このボタンを押すと飴を1つもらえますが、前回飴をもらってからの経過時間がC秒未満である場合はもらえません。
高橋君はこのボタンをN回押してみることにしました。
i回目にボタンを押すのは今からTi​秒後です。
高橋君は何個の飴をもらうことができますか？

## 制約
1≤N≤100
1≤C≤1000
0≤T1​<T2​<⋯<TN​≤1000
入力は全て整数

## 入力
入力は以下の形式で標準入力から与えられる。
N C
T1​ T2​ … TN​

## 出力
高橋君がもらうことのできる飴の個数を出力せよ。

## 入力例 1
6 5
1 3 7 8 10 12

## 出力例 1
3

高橋君はボタンを 6 回押します。
1 回目（今から 1 秒後）：初めてボタンを押すときは必ず飴を 1 つもらえます。
2 回目（今から 3 秒後）：前回飴をもらってからの経過時間が 3−1=2<C 秒なので、飴はもらえません。
3 回目（今から 7 秒後）：前回飴をもらってからの経過時間が 7−1=6≥C 秒なので、飴を 1 つもらえます。
4 回目（今から 8 秒後）：前回飴をもらってからの経過時間が 8−7=1<C 秒なので、飴はもらえません。
5 回目（今から 10 秒後）：前回飴をもらってからの経過時間が 10−7=3<C 秒なので、飴はもらえません。
6 回目（今から 12 秒後）：前回飴をもらってからの経過時間が 12−7=5≥C 秒なので、飴を 1 つもらえます。
よって、高橋君は飴を 3 個もらうことができます。

## 入力例 2
3 2
0 2 4

## 出力例 2
3

## 入力例 3
10 3
0 3 4 6 9 12 15 17 19 20

## 出力例 3
7


# 回答
```Python
n, c = map(int, input().split())
t = list(map(int, input().split()))
key = []
key.append(t[0])

for i in range(1,len(t)):
  if t[i] - key[-1] >= c:
    key.append(t[i])

print(len(key))
```

# 参考
https://atcoder.jp/contests/abc376/submissions/60267199
# 備考
- 分かりませんでした。人のを参考にしました。himan様ありがとうございます。
- 単純に引いた値をcと比較すればいいのだが、飴がもらえない時にはそれ以前の値を引かないといけないのがポイント、そこの実装ができなかった。
- ifなどの条件分岐で書こうと思ってたけど、それだと機械的になって、さまざまなパターンに対応できなくなるのが問題。
- 他の人のコードを見る限り、ほとんど同じような仕様になっている。
- どうやら、for文を使うときはlist[-1]が動的に動いて便利らしい。確かにこれだと値の変化に対応することができる。
Qiitaに記事を書いてみる【投稿テスト】

# はじめに

趣味でMindustryのMod開発を行っていたが、あまり記事がないため、備忘録も兼ねてQiitaに記事を投稿しようと思ったのがきっかけで記事を書いてみました。文章を書く機会もあまりないので、これを機に文章を書くことに慣れようと思います。

# 自己紹介

私は情報系の大学に通っている学生です。大学では、Python、Java、Cなどの言語を学びました。趣味ではPython、Java、C#を使うことが多いです。趣味はMindustry、MinecraftのMod開発です。Unityを学習中でC#を学んでいます。最近は研究室の関係でPythonを使用する機会が増えるのでpaizaラーニングでPythonを学んでます。

# 適当なサンプルコード

```python
import sys

# paizaで驚いた入力受付
# stdin.readline()の方が高速らしい
element_num: int = int(sys.stdin.readline())

# リスト内法表記による入力の取得と格納
input_list: list = [int(sys.stdin.readline()) for _ in range(element_num)]

# 一行ずつ配列要素を表示
for num in input_list:
    print(num)
```

# リスト表現

- Python
    - 組み込み
        - `all()`
        - `any()`
        - `input()`
    - `sys`
        - `stdin`
            - `readline()`

# まとめ

以上が記事投稿テストでした。
javaでgradle project

メモです。`gradle`はRustの`cargo`、Goの`go`みたいな感じです。プロジェクトにbundleされている`./gradlew`のshellファイルを呼び出しても同じことができます。

また、`build.gradle`は`cargo.yaml`や`go.mod`のように、依存関係を管理するファイルです。

```bash
mkdir hello-gradle-java
```

```bash
gradle init --type java-application
```

```
Select build script DSL:
  1: Groovy
  2: Kotlin
Enter selection (default: Groovy) [1..2]
```

1を選択。

```
Select test framework:
  1: JUnit 4
  2: TestNG
  3: Spock
  4: JUnit Jupiter
```

1を選択。

あとは、Enterを押してdefault指定にする。


## 実行

```bash
./gradlew run
```
or
```bash
gradle run
```

```
Reusing configuration cache.

> Task :app:run
Hello World!

BUILD SUCCESSFUL in 479ms
2 actionable tasks: 1 executed, 1 up-to-date
Configuration cache entry reused.
```


## テスト実行

```bash
./gradlew test
```
or
```bash
gradle test
```

```bash
Reusing configuration cache.

BUILD SUCCESSFUL in 456ms
3 actionable tasks: 3 up-to-date
Configuration cache entry reused.
```
背景(画像)の追加方法

## 背景を追加しようと考えた理由
現在は入道雲が出現したらそれをお知らせしてくれるアプリケーションの制作を進めています。
その中で東西南北のどこに入道雲が出現したかを表示する際に、文字で記入すると分かりづらいと思い、背景に地図を設置することで分かりやすいUIになるのではないかと考えました。

## 背景の追加方法
https://zenn.dev/captain_blue/articles/flutter-background-image

基本的にこの資料に沿って進めていきました。

プロジェクト直下にassetsフォルダーとimageフォルダーを作成し、
imageフォルダーの中に「入道雲写真.png」という画像を置きました。
## コード
```
      body: Container(
          decoration: const BoxDecoration(
          image: DecorationImage(
          image: AssetImage('image/入道雲写真.png'),
          fit: BoxFit.cover,// 画像を全体に表示
          )
          ),
      )
```

## エラーの出現
実行をすると以下のようなエラーが出ました
```
Unable to load asset: "image/入道雲写真.png".
Exception: Asset not found
```
このエラーが出た時に、依存関係のクリアなどを行っても意味がありませんでしたが、プロジェクトを開き直したら動くようになりました。

## 実装結果
※青のところは大学の位置です
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3669980/c99cd3a7-3107-cb4d-37e5-ea725d430726.png" width="50%">
【UE5】保存日時、スクリーンショットを表示したセーブデータのリストを作成する方法

## この記事を読むと
以下の画像のようにセーブデータをリスト表示できる。
![savedatalist.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3927318/8f6e1809-7441-6063-8154-03420d4a2a2b.png)

## はじめに
通称「極め本」完了後に躓いたので記録。
ウィジェット上のボタンを押してセーブデータを読み書きするよう実装したが、そのままではロードするまで中身の情報がわかりづらかった。そこで、セーブされた日時とその時のゲーム内情報、スクリーンショットを表示するようにした。
UEのバージョンは5.5.0

## 前提知識・技能
セーブデータの読み書き
List Viewの使用

## 大まかな手順
1. 画像と文字を配置したボタンのリストをListViewで作成し、ボタンを押すとセーブできるようにする
1. SaveGameクラスを継承するオブジェクトを作成し、ボタン上に表示する情報を変数に追加する
1. ゲームデータ保存時にスクリーンショットを撮影し、古いものと差し替える
1. ゲームデータ保存時に手順2で作成したオブジェクトについてもセーブする
1. ListViewを開くときに手順2で作成したオブジェクトをロードし、情報を読み出し表示する

## 詳細
- ### 画像と文字を配置したボタンのリストをListViewで作成し、ボタンを押すとセーブできるようにする
今回はListViewに子ウィジェットを追加し、子ウィジェットのボタンを押した際にセーブするイベントを実装した。
子ウィジェットに表示する情報は3種で、ボタン左部にスクリーンショット、ボタン上部にセーブ時の現実の日時、その下にゲーム内の日時を表示する。
![savedataitem.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3927318/4ec0bc07-e609-6419-30c5-f42a3109d197.png)
- ### SaveGameクラスを継承するオブジェクトを作成し、ボタン上に表示する情報を変数に追加する
セーブデータを保存するクラスとは別にSaveGameクラスを継承するクラス`SaveDataPreview`を作成し、保存時のスクリーンショット、日時、ゲーム内での日時を格納する変数を作成した。

- ### ゲームデータ保存時にスクリーンショットを撮影し、古いものと差し替える
コンソールコマンド`Shot`にオプション変数でファイル名を指定してスクリーンショットを保存したところ、ファイル名には拡張子の前に5桁の通し番号が追加されており、上書き保存はできなかった。
ブループリントでファイル操作はできないため、ファイルを削除するC++の関数`Delete File`を作成し、スクリーンショット保存前に古いファイルを削除するようにした。
詳しくは[別記事](https://qiita.com/Unreal_Engine_memo/items/92f28af48ea95d461b5f)を参照。

- ### ゲームデータ保存時に手順2で作成したオブジェクトについてもセーブする
以下のようなイベントをセーブデータ保存時に実行するようにした。
![saveprev.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3927318/c2c1aae0-340c-55f9-4486-89c01175ae7c.png)
以下グラフ各箇所の解説。
見切れている`Slot to File Name`及びAddノードの引数は両方ともセーブデータのスロット(1~3)である。
今回は3スロットのセーブデータを保持するためスロット名「1」「2」「3」を使用済みなので、スロット名にはそれぞれに10を加算した「11」「12」「13」を使用した。
`Get Date And Time in Game`ノードはウィジェット上に表示したいゲーム内情報を取得するためのもの。
`Now`関数で現在の日時（日本標準時）を取得できる。
`Slot to File Name`関数はセーブデータのスロット別にスクリーンショット実行用のコマンドとファイル名を出力する。
`Delete Fie`関数はC++で作成したもので、差し替えたいファイルを削除する。
`Execute Console Command`関数はスクリーンショット用のコンソールコマンド`shot`を実行する。オプション引数としてファイル名を指定している。

- ### ListViewを開くときに手順2で作成したオブジェクトをロードし、情報を読み出し表示する
リストへのアイテムの追加と同時にロードして情報を表示するよう以下のようにノードを組んだ。
それぞれListViewを配置したウィジェット、その子ウィジェットにイベントを作成した。
![createitem.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3927318/8771e2c2-f8b8-985f-c600-136d02fa98d1.png)
![savelist.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3927318/8bc22cae-7e89-9708-f32d-0c5b04ed1953.png)
`Import File as Texture 2D`関数は引数にファイル名を指定すると画像を読み込んでTexture2Dクラスで出力してくれる。それを`Set Brush from Texture`関数で表示している。

## 結果
セーブデータの中身がわかりやすくなる！！！
【UE5】動的にスクリーンショットを撮影し、指定したファイルを差し替え保存する方法

## はじめに
通称「極め本」完了後に躓いたので記録。
イベント内でスクリーンショットを撮影し既存のファイルに上書き保存しようとしたところ、ブループリントでは無理そうだったのでC++でファイルを削除して差し替えるよう実装した。
UEのバージョンは5.5.0

## 前提知識・技能
C++クラスの作成

## 手順

### 差し替え対象ファイルの削除
C++でブループリント関数ライブラリを作成し、指定したファイルを削除する以下のような関数を作成した。
```cpp:MyBlueprintFunctionLibrary.h
#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "MyBlueprintFunctionLibrary.generated.h"

/**
 * 
 */
UCLASS()
class MYCITYBUILDER_API UMyBlueprintFunctionLibrary : public UBlueprintFunctionLibrary
{
	GENERATED_BODY()
	
	//新たに追加した部分
	UFUNCTION(BlueprintCallable, Category = "MyBlueprintFunctionLibrary")
	static void DeleteFile(FString A);
};
```

```cpp:MyBlueprintFunctionLibrary.cpp
#include "MyBlueprintFunctionLibrary.h"
#include "stdio.h"

//新たに追加した部分
using namespace std;

void UMyBlueprintFunctionLibrary::DeleteFile(FString ImageFileName)
{
    string strImageFileName = TCHAR_TO_ANSI(*ImageFileName);
    remove(strImageFileName.c_str());
}
```

### スクリーンショットの撮影
スクリーンショットは`Execute Console Command`関数でコンソールコマンドShotをオプション引数でファイル名を指定して実行し保存する。
古いファイル削除後にスクリーンショットを保存することでファイルを差し替えるよう実装した。
カスタムイベントの引数`Slot`を変更することで保存するファイル名を変えられる。
![func.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3927318/7c1fb924-2cc1-9ad9-f55f-bd9011645c2a.png)

![func.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3927318/2883f983-172d-bae3-94c1-956c798b7cf5.png)

## 結果
[セーブデータのプレビュー画像を表示する](https://qiita.com/Unreal_Engine_memo/items/3a0d94cb3b6afcb256fd)など、スクリーンショットをゲーム内で活用できる！！！
GitHub Actionsをセキュアに運用する

## 概要

本記事は[QualiArts Advent Calendar 2024](https://qiita.com/advent-calendar/2024/qualiarts)の6日目の記事です。

株式会社QualiArtsでバックエンドエンジニアをやっている末吉と申します。今回はGitHub Actionsの運用方法ついて書いていこうと思います。

GitHub Actionsを使用するとCI/CDが簡単に実装できる反面、攻撃されてしまうと自社の資産に対して重要な被害をもたらす可能性があります。
（ここでいう資産とはコードや、Secretsで登録しているクレデンシャル、Dockerイメージなどの成果物を指しています)

そこで今回はGitHub Actionsでセキュリティに役立つテクニックをいくつか紹介させていただきます。

尚、本記事ではGitHub Actionsの基本は割愛させていただきます。

## ①Permissionsをワークフローの中で明示的に指定する

突然ですが、ここで質問です。

下記のコードにおけるtestジョブはコードの読み取りまで可能なのか、書き込みまで可能なのかだとどちらでしょうか？
尚、privateなリポジトリに置かれているものとします。

```yaml
name: Test

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Test
        run: echo "hoge"
```

正解は「**リポジトリのデフォルト権限による**」です。

実は、GITHUB_TOKENのデフォルトの権限はリポジトリの出来上がった時期によってバラバラです。

Settings > Actions > Generalにて、Workflow Permissionsが

- Read and write permissionsなら全てのパーミッションで読み書きOK
- Read repository contents and packages permissionsならコードとパッケージ読みOK

となっています。セキュアなのは後者のRead repository contents and packages permissionsです。

当然ここのデフォルトの設定を変えた方が良いのは間違いないのですが、そもそも明示的にワークフローの中でPermissionsを書いておくことでデフォルトの挙動に左右されず、チーム内で権限を意識した運用をすることができます。

先ほどのワークフローだと、checkoutが出来れば良いのでコードの読み取りだけ許可すればOKです。

```yaml
name: Test

jobs:
  test:
    runs-on: ubuntu-latest
    permissions:
      contents: read ← これを追加
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Test
        run: echo "hoge"
```

また、**最小権限の法則からjobごとに書くのをオススメします。**

下記のようにworkflowレベルで書くと、本来コードの読み取りが必要ないJobに対しても権限を付与してしまうからです。

```yaml
name: Test

permission:
  contents: read ← workflowレベルで書くのは良くない

jobs:
```


## ②サードパーティーアクションをコミットハッシュで固定する

```yaml
name: Test

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Test
        run: echo "hoge"
```

上記のactions/checkout@v4はGitタグを参照しています。

これ自体はよくあるのですが、忘れてはいけないのがGitタグは可変であるということです。

対象のリポジトリに悪意あるコードを忍び込ませた上でv4.0.0などをpushすることができます。
そのため、同じコードを使用できるという保証がありません。

そこで有効なのが、コミットハッシュで固定してしまうことです。
GitHubのハッシュアルゴリズムにはSHA-1を使用されているので、値がぶつかる可能性は限りなく低いからです。

```yaml
name: Test

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2

      - name: Test
        run: echo "hoge"
```



## ③こまめにアップデートを行う

最後はありきたりなことを書いてしまいましたが、こまめにアップデートを行うことが大切です。

ソフトウェアでは脆弱性が見つかると対応のパッチバージョンがリリースされると思うのですが、あまりにも古いマイナー、メジャーバージョンは対象外になっていることがあります。

その時にverがかけ離れているのを取り戻すのは工数も大きく、こまめにアップデートしている状態より格段に時間がかかります。作業に時間がかかってしまうと、その間に脆弱性を突かれて攻撃されてしまう可能性があります。

定期的にアップデートを行うことで、脆弱性が見つかった時にすぐに対応できるようになるでしょう。

とはいえ開発の合間に並行してアップデート作業を行うのも大変だとは思うので、Dependabotのauto mergeなどを活用するのをオススメします。


## 終わりに

本記事では3つほどテクニックを紹介しました。

運用のお役に立てたら幸いです。
AndroidStudioでテストカバレッジを確認する

# はじめに

この記事は[and factory.inc Advent Calendar 2024](https://qiita.com/advent-calendar/2024/andfactory) 1日目の記事です。

and factoryでandroidエンジニアをやっています。
日々の開発で意識していることを書こうと思います。

# AndroidStudioでテストカバレッジを確認する方法

業務でなにかの実装をしたときテストコードも書くと思います。
書いたテストコードが実際に網羅されているのか確認したいですよね。
AndroidStudioでポチポチするだけでレポートが確認できます。

## Cmd+Shift+Tで新しいテストを作るか、テストコードに移動できます

![スクリーンショット 2024-11-29 13.28.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193770/2630bdb9-82e3-50e9-492a-3dc4b47c4283.png)


## Run “XxxxTest” with Coverageを実行
![スクリーンショット 2024-11-29 13.29.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193770/2248dde6-1989-c0f3-af61-e248595cfefb.png)


## エクスポートしてOpen generated HTML in browserにチェックを入れてSave

with Coverageを実行すると右側に結果が出てきます。

![スクリーンショット 2024-11-29 13.30.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193770/cf065d9d-f2a6-9c30-38c6-48d8f81b2076.png)


## テストしたコードをモジュール→クラスで探す

![スクリーンショット 2024-11-29 13.36.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193770/af2d44b9-b252-5da1-155e-8dad9a70f741.png)


それぞれの色はこの様になっています。
カバレッジレポートを出すことで「テストし忘れてた！」というのを視覚的に気がつくこともできて良いですね。

- 緑
  - 通りました
- 黄色
  - 条件分岐で通ってないものがあります
- 赤
  - 通ってません

レポートと作るとAndroidStudio側でも確認可能です
![スクリーンショット 2024-11-29 13.37.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193770/9274b0d5-54bc-3ce3-5c79-7673aaa0ce70.png)

# おわりに

いかがでしたでしょうか？
初歩的なTipsではありますが
カバレッジレポートを見ることで自身が書いたテストコードで本当にケースが網羅できているのか確認したり、
PRにテストコードのカバレッジレポートスクショを乗せてPRの品質向上を目指すとよいのかなと思います。

明日の[Advent Calendar](https://qiita.com/advent-calendar/2024/andfactory)の記事もお楽しみに:santa:
ファイルの末尾に複数行を追記

##### 環境
VirtualBox 7.0.20
Rocky Linux release 9.5 (Blue Onyx)
webmin 2.202

ファイルの末尾に複数行を追記したい時、わざわざファイル開くのめんどくさいですよね💦
ということでこのコマンド。

```
cat <<EOL >> /tmp/test.txt
あいうえお
かきくけお
さしすせそ
たちつてと
なにぬねの
EOL
```

最初と最後の「EOL」の文字は同じであれば何でもOK。
僕の名前である「SONKUN」でもいけます🐵

```
cat <<SONKUN >> /tmp/test.txt
あいうえお
かきくけお
さしすせそ
たちつてと
なにぬねの
SONKUN
```

「EOL」は「End Of Line（最終行）」という意味のようです😊