OmniAICreator/Qiita-1.07M huggingface dataset:
Googleスプレッドシートの編集権限を与えずにGASから書き込みをさせたい！

こんにちは、サーバエンジニアの池脇と申します。
みなさんは **「Google Apps Script(以下GAS)からスプレッドシートへの書き込みはさせたいけど、そのユーザに編集権限を与えたくない！」** と思ったことはありませんか？
今回はそんな時にどのような手法を取ればよいかについて、私がとった解決策を共有します。

## 今回解決した問題について
今回解決したのは2つのスプレッドシート( **編集権限のあるスプレッドシートA** と **閲覧権限のみのスプレッドシートB** )間の連携です。

具体的には
1. **スプレッドシートA** に書き込み
1. GASのスクリプトを実行し、書き込んだ内容を **スプレッドシートB** に転記
1. 閲覧権限しか持っていないため書き込むことができずエラー発生

上記のエラーの解消を行いました。

## GASをウェブアプリとして公開する
解決策は **スプレッドシートBの関数をライブラリとしてスプレッドシートAに取り込むのをやめ、ウェブアプリとして公開してアクセスする** ことです。

なぜウェブアプリとして公開を行うのか？
これはウェブアプリのデプロイ時に
* どのユーザーとして実行するのか
* アクセスできるユーザーは誰か

の指定を行うことができ、閲覧権限のみのユーザーでも別ユーザーとして実行することで書き込みを行うことができるからです。

## 手順
#### 1. ウェブアプリとして公開したいGASにアクセスし、右上の「デプロイ」→「新しいデプロイ」を選択
![スクリーンショット 2024-08-30 18.56.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3780649/6bfe4907-b496-84b8-f9d9-f2eb2667eaa3.png)

#### 2. 種類を選択の歯車から「ウェブアプリ」を選択
![スクリーンショット 2024-08-30 18.57.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3780649/6ad068a1-4b8b-1b11-ac87-0c5597bdd6c9.png)

#### 3. 次のユーザーとして実行を「自分」、アクセスできるユーザーを「全員[^1]」を選択
![スクリーンショット 2024-08-30 18.57.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3780649/bfe5641b-081b-2cde-1420-fd5864ea6fb7.png)

#### 4. デプロイを行いURLを取得
![スクリーンショット 2024-08-30 19.09.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3780649/2372e1db-b12d-9a33-fcd7-3caaef6e8dad.png)

#### 5. 以下のようにウェブアプリを叩く
```javascript
function myFunction() {
  const payload = {
    // 書き込み処理であれば書き込む内容等
  };
  const options = {
    method: "GET",
    contentType: "application/json",
    payload: JSON.stringify(payload)
  };
  const url = "【4.で取得したURL】";
  const response = UrlFetchApp.fetch(url, options);
}
```

これで閲覧権限のみを持つユーザーが 5.のGASのスクリプトを実行したとしても、ウェブアプリを通して書き込む際には自分の権限で(すなわち編集権限を持つユーザーとして)書き込むことになり、編集権限を与えることなく書き込みをさせることが可能になります。

### 注意点
1. ウェブアプリはGETであればdoGet、POSTであればdoPostの関数を探します。ウェブアプリ側の実装では関数名に注意してください
1. アクセスできるユーザーに「全員」を指定する場合は誰でもアクセスすることができます。URLの公開には注意し、必要であれば認証機能をつけることを検討しましょう[^2]


## まとめ
今回はGoogleスプレッドシートの編集権限を与えずにGASから書き込みを行う手法について解説しました。
権限周りの問題は安全に仕事を進める上で避けては通れない問題です。この記事がその解決の一助になれば嬉しく思います。

[^1]:アクセスするユーザーがGoogleアカウントを持つユーザに限られる場合は「Googleアカウントを持つユーザー全員」でも可
[^2]:[getOAuthToken()](https://developers.google.com/apps-script/reference/script/script-app?hl=ja#getOAuthToken())を使ってトークンを取得し、認証する等の手法があります
複数リポジトリにPR

複数のGitHubリポジトリを同時に更新できるツール「MPR（Multiple Pull Request）」を紹介します。

## MPRとは？

MPRは、特定のファイルやディレクトリを複数のGitHubリポジトリで一度に更新するためのツールです。例えば、CI/CDの設定ファイルや共通のドキュメントなどを、組織内の全リポジトリで同時に更新したい場合に役立ちます。

## 主な特徴

1. **複数リポジトリ同時更新**: 一度のコマンドで複数のリポジトリにPull Requestを作成できます。
2. **簡単な操作**: コマンドラインから簡単に使用できます。
3. **GitHub CLI統合**: GitHub CLIを利用しているため、認証や操作が簡単です。

## インストール方法

mprはHomebrewを使ってインストールできます。以下のコマンドを実行してください：

```bash
brew tap koriym/mpr
brew install mpr
```

インストール後、GitHub CLIの認証を行う必要があります：

```bash
gh auth login
```

認証時には、`repo`、`workflow`、`read:org`のスコープが必要です。`gh auth status`で確認してください。

```
% gh auth status
```
```
  - Token scopes: 'read:org', 'repo', 'workflow'
```

## 使い方

基本的な使い方は以下の通りです：

```bash
mpr <ファイルまたはディレクトリのパス> <コミットメッセージ> <リポジトリ1> <リポジトリ2> ...
```

例えば、CI設定ファイルを更新する場合：

```bash
mpr .github/workflows/ci.yml "CIワークフローの更新" owner1/repo1 owner2/repo2
```

.githubフォルダを追加/更新する場合：

```bash
mpr .github "GHアクションの追加" owner1/repo1 owner2/repo2
```

## 活用シーン

1. **CI/CD設定の一括更新**: 組織内の全リポジトリのCI/CD設定を一度に更新できます。
2. **ライセンスファイルの更新**: オープンソースプロジェクトで、ライセンスファイルを一括で更新できます。
3. **セキュリティポリシーの適用**: セキュリティ関連のファイルを複数のリポジトリに同時に追加または更新できます。
4. **共通ドキュメントの管理**: READMEやCONTRIBUTINGなどの共通ドキュメントを一括で管理できます。


## メリット

1. **時間の節約**: 手動で各リポジトリを更新する手間が省けます。
2. **一貫性の確保**: 全リポジトリで同じ変更を適用できるため、一貫性が保てます。
3. **ミスの防止**: 手動操作によるミスを減らせます。
4. **効率的なレビュープロセス**: 複数のPull Requestを一度に作成し、レビューできます。

## 注意点

- GitHub CLIの認証が必要です。
- 対象リポジトリへの適切な権限が必要です。

## 同様のツール

他にも複数リポジトリを管理するためのツールがあります。以下にいくつか紹介します：

2. **Multi-gitter**
   - 特徴：複数のGitリポジトリに対して一括で変更を加え、プルリクエストを作成できます。
   - 使い方：`multi-gitter run "sed -i 's/old/new/g' README.md" --branch-name "update-readme"`
   - リンク：[Multi-gitter GitHub](https://github.com/lindell/multi-gitter)

3. **Gita**
   - 特徴：複数のGitリポジトリのステータスを一覧表示し、一括で操作できます。
   - 使い方：`gita fetch`で全リポジトリのfetchを実行
   - リンク：[Gita GitHub](https://github.com/nosarthur/gita)

5. **Gitbatch**
   - 特徴：複数のGitリポジトリを並行して管理するためのCLIツール。
   - 使い方：`gitbatch status`で全リポジトリのステータスを確認
   - リンク：[Gitbatch GitHub](https://github.com/isacikgoz/gitbatch)

これらのツールはそれぞれ特徴が異なり、用途によって使い分けることができます。他のツールはより広範な Git 操作や管理機能を提供していますが、MPRは特定のファイルやディレクトリを複数リポジトリで更新することに特化しています。

## まとめ

MPRは特定の用途に特化した効率的なツールですが、他のツールも含めて検討することで、より適切なソリューションを見つけることができるでしょう。プロジェクトの規模や要件に応じて、これらのツールを使い分けたり、組み合わせたりすることで、より効率的な開発ワークフローを構築できます。

複数リポジトリの管理は開発者にとって重要なタスクの一つです。これらのツールを活用して、作業の効率化とコード管理の一貫性向上を図りましょう。使い方や詳細については、[MPRのGitHubリポジトリ](https://github.com/koriym/mpr)を参照してください。
【ChatGPT＋Code Interpreter】でPDFをページごとに分割します。


　どうも、カーブミラーです。

　今回は、PDFをページごとに分割します。

:::note info
本記事は、ChatGPT（有料版）
で行なっております。画像は
ありません。　あしからず。
:::
　まずは、ニュース。

　GoogleのImageFXについてですが、
　　生成すると、その時刻が
　　　ファイルのタイムスタンプに
　　　　なります。

　しかし、
　　この時刻は
　　　サーバーに
　　　　設定されているようです。

　まぁ、
　　ふつうは
　　　気にすることでも
　　　　ありませんけれど、
　　　　　人によっては、大事なこと。

　注意しておきましょう。
***
　では、
　　本題です。

　今回は、
　　PDFをページごとに分割します。

　残念ながら
　　私では、用途を思い付きません😅
　　　普段から使っていませんので。

　それはともかく（逃げるw）

　手順は
　❶GPT-4oであることを確認
　❷もとのPDFをアップロード
　❸Prompt入力
```Text:
CodeInterpreterで、このPDFをページごとに分割して、PDFにしてください。
コード不要。
必ず実行してください。
```
　❹送信
　❺ダウンロードリンクが表示

　これで、各ページの
　　ダウンロードリンクが
　　　表示されます。

　みなさんの
　　作品制作の糧に
　　　なれば、幸いです。

　今回は、ここまで。
【JavaScript】コールバック関数を引数に取る配列のメソッドまとめ

## 学習に使った教材
以下の動画で、コールバック関数を引数に取る配列のメソッドについて勉強したので、自分の備忘録として残しておきます

https://www.udemy.com/course/the-web-developer-bootcamp-2021-japan/

## コールバック関数を引数に取る配列のメソッド
### forEach
- コールバック関数を、配列の各要素に対して一度ずつ実行するメソッド
- `配列名.forEach(コールバック関数);`
```javascript:app.js
const players = ["堂安", "久保", "三苫", "遠藤", "冨安"];

players.forEach(function (player) {
    console.log(`頑張れ、${player}!`);
});

// 上記コードの簡略型
players.forEach(player => console.log(`頑張れ、${player}!`));

// 出力結果
// 頑張れ、堂安!
// 頑張れ、久保!
// 頑張れ、三苫!
// 頑張れ、遠藤!
// 頑張れ、冨安!
```
### map
- コールバック関数を配列の各要素で実行し、その実行結果から新たな配列を生成するメソッド
- `配列名.map(コールバック関数);`
```javascript:app.js
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

const squares = numbers.map(number => number ** 2);

console.log(squares);

// 出力結果
// [1, 4, 9, 16, 25, 36, 49, 64, 81, 100]
```
### filter
- 真偽値を返すコールバック関数を配列の各要素で実行し、その実行結果を元に新たな配列を生成するメソッド
- `配列.filter(コールバック関数);`
```javascript:app.js
const scores = [77, 48, 81, 56, 61, 93, 32, 72];

const successfulApplicants = scores.filter(score => score >= 70);

console.log(successfulApplicants);

// 出力結果
// [77, 81, 93, 72]
```
### some, every
- `some`
    - 真偽値を返すコールバック関数を配列の各要素で実行し、1つでも`true`になる要素があれば`true`を返すメソッド
    - `配列名.some(コールバック関数);`
- `every`
    - 真偽値を返すコールバック関数を配列の各要素で実行し、全ての要素で`true`になれば`true`を返すメソッド
    - `配列名.every(コールバック関数);`
```javascript:app.js
const scores = [81, 68, 76, 84, 93, 85, 77, 91];

const boolSome = scores.some(score => score >= 70);
const boolEvery = scores.every(score => score >= 70);

console.log(`someメソッド: ${boolSome}`); // someメソッド: true
console.log(`everyメソッド: ${boolEvery}`); // everyメソッド: false
```
### reduce
- コールバック関数を配列の各要素で実行し、その実行結果を1つの値にまとめるメソッド
- `配列名.reduce(コールバック関数);`
```javascript:app.js
const scores = [81, 68, 76, 84, 93, 85, 77, 91];

const maxScore = scores.reduce((accumulator, current) => {
    if (current > accumulator) {
        return current;
    }
    return accumulator;
});

console.log(`最高点は${maxScore}点`);

// 出力結果
// 最高点は93点
```
- `accumulator` ... 1つ前の要素までの実行結果
- `current` ... 現在、コールバック関数を実行している要素の値
【Python入門】list,tuple,dict,rangeからのデータ抽出方法について

# はじめに
Pythonではシーケンス型というものが存在します。他プログラミング言語でいうところの配列と同等となります。
配列系であれば[共通のシーケンス演算](https://docs.python.org/ja/3/library/stdtypes.html#common-sequence-operations)がサポートされています。

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


# データ検索のサンプル
例えばJava等のプログラミング言語であればリストの要素数ループすることで要素を取得し比較する必要がありますが、pythonでは```x in s```演算を用いて簡潔に記載できます。
javaとPythonのコードについては同じ意味になります。とても簡単ですよね。

```java:java
for ( Integer number : numbers ) {
    if ( number == 2 )
        return true;
    }
}
return false;
```

```python:python
2 in numbers
```

もちろんディクショナリ型でも使用可能です。
```python:python
dict_a = {1: "one", 2: "two", 3: "three", 4: "four", 5: "five"}
2 in dict_a.keys()
```
key=1,2のオブジェクトを取得することもできます。
```python:python
dict_a1 = { key:dict_a[key] for key in [1,2] }
```
本当に便利ですよね。

# まとめ
データ抽出方法について書いてみました。
dictでのサンプルコードを記載しましたが、list,tuple,rangeでも考え方は同じです。

Pythonではコードを簡潔に記載できる便利な演算が用意されているため、どんどん利用していきたいですね。慣れすぎると他の言語を使用した場合に違和感を感じてしまうかもしれませんね。

参考になれば幸いです。
[GitHubActions] workflowをGithub上のAll workflowsに表示する方法

## 今回の記事を書こうと考えた背景について
* GitHubActionsを使用してHelloWorldを出力するのにworkflowを設定しようとした。
その際にGitHubの「Actions」タブのworkflow一覧に表示されなかったため

# GitHub上で表示されなかった理由について
* workflow用に作成したymlファイル（ワークフロー）がデフォルトブランチにある場合のみ表示されるから
* デフォルトブランチとしてymlファイルがあるブランチを設定していなかったため

# GitHub上で表示されなかったときの状態
* 左側にAll workflowsとworkflowの一覧が表示されない
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789688/f1900f31-21ff-7def-1746-9687b7246787.png)
* ymlファイルがあるブランチはmasterだがmainがデフォルトブランチとして設定されている
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789688/fd05f69b-9840-73ba-97e5-37563559cc8e.png)

# 対処方法
* ymlファイルがあるブランチをデフォルトブランチとして設定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789688/2d2c77fd-c9bf-8e2d-1c63-187a414ca1fd.png)
* Updateをクリックする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789688/1f8e2ac6-c2b9-c924-4945-8e2304afdd1b.png)
* I understand〜をクリックする
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789688/aee1a555-ee88-08af-c24d-fd87cbb0418a.png)
* 表示されるようになる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3789688/ad7ea147-0ad7-0a03-aa99-d8150004c578.png)

補足：<font color="red">設定からデフォルトブランチを切り替える場合は運用上問題がないか確認をしてください</font>

# さいごに
今回はデフォルトブランチを切り替えてworkflowを表示させましたが、デフォルトブランチとしてmainが設定されている場合は、mainブランチにpushすることでも解決ができるようです。
Snowflakeのディレクトリテーブルについてメモ

Udemyの講座でSnowProの模擬試験をやってるとちょいちょい出てくるディレクトリテーブル。
よくわからないので調べて触ってみました。
完全にSnowPro Core試験のための自分用まとめです。

[公式ドキュメント：ディレクトリテーブル](https://docs.snowflake.com/ja/user-guide/data-load-dirtables "ディレクトリテーブル")

:::note
ディレクトリテーブルは、ステージで複数層になった暗黙のオブジェクトで（独立したデータベースオブジェクトではない）、ステージ内のデータファイルに関するファイルレベルのメタデータを格納するため、概念的には外部テーブルに似ています。ディレクトリテーブルには、独自の付与可能な権限はありません。
外部（外部クラウドストレージ）ステージと内部（Snowflake）ステージの両方でディレクトリテーブルをサポートします。ディレクトリテーブルは、ステージの作成時（CREATE STAGE を使用）またはそれ以降（ALTER STAGE を使用）にステージに追加できます。
:::

テーブルやViewのように独立した項目で存在するわけではなくて、内部ステージ・外部ステージに対して設定するもののようです。

# やってみる
内部ステージを作ってみます。
GUIから作成します。
![GUI内部ステージ作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3487310/536cc98d-9d78-7e89-40dd-b68683d3d5a4.png)

SQLも見えますね。
デフォルトでディレクトリテーブルはTRUEになってました。

次に作った内部ステージに、適当な画像ファイルをアップロードします。（snowsql.png）
![スクリーンショット 2024-08-31 103752.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3487310/e4e63781-bfe0-afc5-d6cf-a60908233fcf.png)
※画面内の「TEST_DB.TEST_SCHEMA」 は[一個前の投稿](https://qiita.com/baihebu/items/267a18c13fa7c66cc278 "SnowflakeでのJSON内のテキストデータの扱い方で気づいたこと（AS_VARCHAR）")で作ったDBとスキーマ。

アップロードしたらワークシートで内部ステージの中身を見てみましょう。

まずはディレクトリテーブル関係なしのLISTコマンド。
これは今まで外部ステージのファイル確認とかで使ったことあります。

```
list @TEST_INTERNAL_STAGE;
```

| name | size | md5 | last_modified |
|:-----------|:------------|:-----------|:------------|
| test_internal_stage/snowsql.png | 6000 | 5cf3d36b64e616b0d10a05e07af34b37 | Sat, 31 Aug 2024 01:38:00 GMT |

![スクリーンショット 2024-08-31 104013.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3487310/c4a547eb-5598-338b-2b53-8c4dee5b4a39.png)
4項目が取得できました。

次にディレクトリテーブルを有効化しているからできるselect文。
これは知らんかった。初。
```
select * from DIRECTORY(@TEST_INTERNAL_STAGE);
```

| RELATIVE_PATH | SIZE | LAST_MODIFIED | MD5 | ETAG | FILE_URL |
|:-----------|:------------|:-----------|:------------|:-----------|:------------|
| snowsql.png | 6000 | 2024-08-30 18:38:00.000 -0700 | 5cf3d36b64e616b0d10a05e07af34b37 | 5cf3d36b64e616b0d10a05e07af34b37 |https://xxxxx.snowflakecomputing.com/api/files/TEST_DB/TEST_SCHEMA/TEST_INTERNAL_STAGE/snowsql%2epng |

※FILE_URLの値のドメイン部分だけ伏字に変えています。

![スクリーンショット 2024-08-31 104126.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3487310/c3bc7d1e-29f8-119b-50a6-e43cf3ed239c.png)
6項目が取得できました。

# まとめ
ディレクトリテーブルのselect文の方が取得できる項目が多いです。
FILE_URLが取得できるのが良いところっぽいです。

公式ドキュメントで書いていた下記はそーゆーことか。

:::note
特に、ディレクトリテーブルを使用すると、以下のような非構造化データのタスクを達成できます。

ステージにあるすべての非構造化ファイルのリストをクエリする。ディレクトリテーブルをクエリして、ステージにあるすべてのファイルのリストを取得できます。クエリ出力には、サイズ、最終更新日時のタイムスタンプ、および Snowflakeファイル URL などの各ファイルに関する情報が含まれます。

非構造化データのビューを作成する。非構造化ファイルに関する追加データとメタデータを含んだSnowflakeテーブルとディレクトリテーブルを結合して、非構造化ファイルとその関連データを単一のビューで表示することができます。

ファイル処理パイプラインを構築する。ディレクトリテーブルをSnowpark API または外部関数で使用して、ファイル処理パイプラインを作成できます。
:::

# 疑問点
ただ、このFILE_URLをブラウザのURL欄にコピペしてもアクセスできない。
ワークシートの結果のURLをクリックするとファイルダウンロードできる。
ここがまだよくわかっていないところ。
「Snowflake経由じゃないとアクセスできないよ」ということ？
SwiftUIでプレビュー時にprivateな変数にアクセスする方法

SwiftUIでビューを作成する際、@Stateを使ってビューの状態を管理することが多いです。しかし、@State変数は通常privateとして扱われるため、プレビュー時に直接アクセスして変更することができません。この記事では、privateな@State変数にアクセスして、プレビュー時に表示内容や表示状態を変更する方法を紹介します。

**問題の背景**

通常、@State変数はprivateであり、外部から直接変更することができません。そのため、プレビュー時に特定の状態を再現したい場合でも、その状態にアクセスできず、手動で状態を変更しなければなりません。

例えば、以下のようなコードがあるとします。

```
struct MainView: View {
    @State private var isTextVisible: Bool = false
    @State private var displayText: String = "Default Text"

    var body: some View {
        VStack {
            if isTextVisible {
                Text(displayText)
                    .font(.headline)
                    .padding()
            }
        }
    }
}
```

このコードでは、isTextVisibleがfalseであるため、プレビュー時にはテキストが表示されません。しかし、プレビューでテキストが表示される状態を確認したい場合、この変数にアクセスする方法が必要です。
また、Textの内容もprivateでアクセスできないです。

**解決策: extensionを使ったapplyメソッド**
この問題を解決するために、extensionを使ってビューにapplyメソッドを追加します。このメソッドを使うことで、プレビュー時にprivateな変数を簡単に設定できるようになります。

```
extension MainView {
    func apply(isVisible: Bool, text: String) -> Self {
        var copy = self
        copy._isTextVisible = State(initialValue: isVisible)
        copy._displayText = State(initialValue: text)
        return copy
    }
}
```
本番環境で拡張したコードの情報を含めたくない場合は"#if DEBUG #endif DEBUG"で囲むことも視野。
また、Previewのみ動かす方法として
```
ProcessInfo.processInfo.environment["XCODE_RUNNING_FOR_PREVIEWS"] == "1"
```
という文を条件として使用することができる。

**全体のコードと実行結果**

```
import SwiftUI

struct MainView: View {
    @State private var isTextVisible: Bool = false
    @State private var displayText: String = "Default Text"

    var body: some View {
        VStack {
            if isTextVisible {
                Text(displayText)
                    .font(.headline)
                    .padding()
            }
        }
    }
}

extension MainView {
    func apply(isVisible: Bool, text: String) -> Self {
        var copy = self
        copy._isTextVisible = State(initialValue: isVisible)
        copy._displayText = State(initialValue: text)
        return copy
    }
}

struct MainView_Previews: PreviewProvider {
    static var previews: some View {
        MainView().apply(isVisible: true, text: "This text is visible in Preview!")
    }
}
```

simulatorでの実行結果
![Simulator Screenshot - iPhone 15 Pro Max - 2024-08-31 at 11.22.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619746/720e722e-89bb-6c17-bd67-4ac6ae63110d.png)
これだと分かりにくいので、isTextVisibleをtrueにして実行
![Simulator Screenshot - iPhone 15 Pro Max - 2024-08-31 at 11.25.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619746/9735eb47-ca0a-ff0b-b8dd-811d2dd2fd37.png)
そして、Previewでは
![スクリーンショット 2024-08-31 11.25.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1619746/2df738fc-ab1b-8a71-59c5-44c35bed3708.png)

これを活用してsnapshot testなどを進めていきたい。
クイズをコレクションしながら楽しく学べる！Notion連携の新アプリ『Quizionary』で知識を深めよう！ ~覚えたいことが多すぎて復習する時間がないあなたに~


|![quizView.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260695/5431b470-7068-2c57-c528-bbbe73cecafe.gif)|![addQuizView.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260695/60221792-c9e2-54e8-2dd5-8e90b6e5f481.gif)|![collectionView.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260695/eaf59ba8-252b-c1fb-0079-fea7d82a227d.gif)|
|--|--|--|


大人になるに連れていろんな分野の知識が必要だけれど、覚えている暇がない…

Notionにメモをとっているけど、メモするだけで定期的に復習する機会がない…

そんな問題を解決するために、「Quizionary」というiOSアプリを開発しました！

https://apps.apple.com/app/quizionary/id6630366339

## 主な機能

### iOSの写真アプリライクなクイズビュー

![quizView.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260695/5431b470-7068-2c57-c528-bbbe73cecafe.gif)


- 記憶に残りやすい画像をメインにしたUI[^1]
- 写真アプリのように、横スワイプ、下のバーをスクロールすることが可能で簡単にブラウジング
- タグ、解答数（☆マーク）、正解数（★マーク）を表示することで、俯瞰的な学習が可能

[^1]: 無しでも利用可能ですがあり推奨

### 自動化による簡単なクイズ追加ビュー

![addQuizView.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260695/60221792-c9e2-54e8-2dd5-8e90b6e5f481.gif)

- Wikipedia検索によるクイズの半自動生成
- 誤答選択肢をワンタップで生成（Geminiを使用）
- Notion連携をしているのでNotionデータベースにアクセスすることでPCからもクイズを追加、変更可能

### ソシャゲライクなコレクションビュー

![collectionView.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260695/eaf59ba8-252b-c1fb-0079-fea7d82a227d.gif)

- コレクションすることで思い出す機会を多く！
- 一定数正解すると、エフェクトがつくのでモチベーションに
- タグベースでジャンルごとの表示も可能（クイズ機能でもできます）

### 様々な形で可視化する学習記録ビュー

![achievementView.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260695/7c396eac-c707-c1b7-3519-48dc2a6443b5.gif)

- 全体の学習記録をランクとして表示
- グラフで更なる学習のモチベーションに！
- Githubの草のチャートで毎日学習へのモチベーションをUP！
- タグごとの進捗状況も確認可能

### カスタマイズ

![preferenceView.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/260695/a40f4f0f-afaf-f2ba-f7e9-562fcb47dbad.gif)

- テーマカラー、モードを簡単にカスタマイズ！
- ハプティクス（触覚フィードバック）をON/OFF可能！

## 技術

- TCA (The Composable Architecture) + SwiftUI
    - メインライブラリとして利用しました
    - ビジネスロジックが分離しやすく開発しやすかったです[^2]
 - Swift Package Manager
    - ビルドの高速化など様々なメリットがあるため、Swift Packageによるモジュール化しながら実装しました[^3]
- Gemini
    - 現状は誤答の選択肢を生成するために利用しています
- NotionAPI
    - Notion連携のために利用しました
    - Notionの内部インテグレーションの実装はWebの知見に乏しく大変でしたが、幸いに先行研究があり、そこまで詰まることなく実装できました[^4]

[^2]: ビジネスロジックが小さい子ViewはSwiftUIだけで実装してます
[^3]: ここだけの話、モジュール化したせいでリソース管理などでめちゃめちゃ苦労しましたw
[^4]: 次のサイトが非常に助かりました、ありがとうございます（https://rakuraku-engineer.com/posts/react-native-notionapi-public/）

### なぜNotionを選んだのか？

Notionを選んだ主な理由は、基本的に無料で使用できること、そして一括検索機能が非常に便利だからです。多くのユーザーが知識管理やプロジェクト管理にNotionを活用していることからも、Quizionaryのユーザーが学習データを簡単かつ効率的に管理できるようにするため、Notionとの連携を決めました。

## **これからの展望**

- 画像自動生成機能
    - Apple Intelligence使ってみたい
- 生成AIを利用したランダムなクイズ作成機能
    - iPhoneへのchatGPT組み込みで可能？
- プリセットクイズの拡充

## **まとめ**

Quizionaryは、クイズに挑戦しながら学びを深め、その知識を図鑑のようにコレクションできるアプリです。クイズに正解するたびに、その知識があなたのコレクションに追加され、いつでも振り返ることができます。また、Notionとの連携により、学習進捗やコレクション内容を自由に管理することが可能です。

もし興味があれば、ぜひアプリをダウンロードして試してみてください。また、フィードバックや質問があれば、お気軽にコメントや連絡をください。

https://apps.apple.com/app/quizionary/id6630366339
Windows10からWindows11へアップグレード

はじめに
サポート業務を務めて3年目の小林です。
2025/10/14にサポートが終了予定との事でついにWindows11へアップグレードしてみようと思いました。
せっかくなので作業内容を記入してみようと思います。
Windows11へのアップグレード方法にはいくつかあるようで私は、Windowsupdateから行いました。

### Windows11へアップグレードする為の最小スペック
|アイテム|システム要件|
|-|-|
|プロセッサ|1GHz以上で 2コア以上の64 ビット互換プロセッサまたは System on a Chip (SoC)
|メモリー|4ギガバイト(GB)|
|ストレージ|64GB 以上の記憶装置|
|システム ファームウェア|UEFI、セキュア ブート対応|
|RPM|トラステッド プラットフォーム モジュール (TPM) バージョン 2.0|
|グラフィックスカード|DirectX 12 以上 (WDDM 2.0 ドライバー) に対応|
|ディスプレイ|対角サイズ 9 インチ以上で 8 ビット カラーの高解像度 (720p) ディスプレイ|

### PCの正常性をチェック
設定 > Windows Update を見ると最小システム要件を満たしているか確認が可能です。
正常性のチェック後、もしWindows11へのアップグレードの要件を満たしていない場合の
対処方法として下記の方法があるようです

1．パソコンを買い替える
   もっとも簡単な方法は、パソコンを買い替えることです。
   
2．個別要件ごとに対応する
要件を1〜2つだけ満たしていない場合は、個別に対応した方が安価になる場合があります。 いま使っているパソコンに必要なRAMを追加したり、ストレージを増やすことは可能です

3．最小要件を満たさないままアップデートする
MicrosoftのホームページからWindows11ディスクイメージ (ISO) をダウンロードしてアップデートする方法です。

ダウンロード後はレジストリに設定を追加してWindows11にアップデートします。 ただし、最小要件を満たしていないパソコンでWindows11を使う場合は、サポート・補償は受けられないのでご注意が必要です。

### Windows11へアップグレードする際の注意点
1．Windows 10で使用可能だった一部の機能が使用不可になる事がある。
2．一部のアプリが動作しなくなる可能性がある。
3．未発表の新しい不具合が発生する可能性がある。


### 最後に
Windows 10へのダウングレード期限は、アップグレード後から10日以内のため
注意点などを確認してからアップグレードをおすすめします。
私自身は今のところ問題ないく動いています。

以上、小林でした。
JavaでAWSのS3ストレージからファイルをアップロードする方法

こんにちは、東です。
今回はJavaを利用してファイルをS3ストレージへアップロードする方法を
まとめたいと思います。

あくまでも参考程度なので大まかな外枠の仕組み程度に捉えていただければ幸いです。

# 必要なことは？
まず必要なものは以下のものです。
・**「ユーザーID」** →IAMユーザーのユーザーID
・**「シークレットアクセスキー」** →IAMユーザーのシークレットアクセスキー
・**「S3バケット名」** →S3コンテナ名
・**「ルートディレクトリパス」** →S3コンテナ内のルートパス
・**「リージョン名」** →S3コンテナが配置されている地域のリージョン名
・**「ファイル情報」** →アップロードするFile（JavaのFile型）
となります。

流れとしては
1.認証情報を用意
↓
2.S3へアクセスするクライアントを作成。
↓
3.ファイルをアップロードする。

という流れになります。
では、詳しく見ていきましょう。

# 手順
### 1.認証情報を用意
AWSのS3ストレージへアクセスするためには、
**「アクセスID」** **「シークレットアクセスキー」** というユーザーIDとパスワードにあたるものが必要です。
また、S3ストレージがある **「リージョン」** という情報も必要です。
1例として参考となるサイトがありましたので載せさせていただきます。

https://www.jpcyber.com/support/create-iam-user-and-iam-policy

### 2.S3へアクセスするクライアントを作成。
続いてJava上でS3ストレージにアクセスできるようにアカウント情報を設定します。
AwsBasicCredentialsというClassが用意されているので、**「アクセスID」** **「シークレットアクセスキー」** を渡すことで戻り値でCredential情報を取得できます。
上で取得したCredential情報をAmazonS3ClientBuilderをに渡すだけで
AmazonS3クライアント情報を簡単に組み立てることができるようです。
ありがたいですね。
コード的には以下のようになります。

```ruby:Java
// S3クライアントを作成
public AmazonS3 getAmazonS3Account {
    // アクセスID、シークレットアクセスキーを設定する
    AwsBasicCredentials credentials = AwsBasicCredentials.create("アクセスID",
        "シークレットアクセスキー");

    // AmazonS3ClientBuilderを使用してクライアント情報を組み立てる
    AmazonS3 client = AmazonS3ClientBuilder.standard()
        .withCredentials(new AWSStaticCredentialsProvider(credentials)).withRegion(Regions.fromName("リージョン名"))
        .build();
    return client;
  }
```

3.ファイルをアップロードする。
ここまで出来たらアップロードするのは簡単でした。
AmazonS3インターフェース内に.putObjectというメソッドがあるのでそちらを使います。
S3上の**ルートディレクトリパス** と **リージョン名** 、**バケット名** が必要です。
コードとしては以下のようになります。

```ruby:Java
//ファイルをアップロードする
public void uploadFile (File file, String S3FilePath){
   getAmazonS3Account().putObject("バケット名","ルートディレクトリパス", "ファイル情報");
}
```

ここまですれば、以下の場所にアップロードされたファイルが確認できると思います。
**バケット名** /**ルートディレクトリパス** /**ファイル名** 

# まとめ
今回はJavaを使用してS3ストレージへファイルをアップロードする方法をまとめました。
次回はこのアップロードされたファイルをダウンロードする方法をまとめたいと思います。
ありがとうございました。
Flutterでソースコードそのままにプロジェクトを再作成する

## はじめに
表題通り、ソースコードをそのままでプロジェクトを再作成します。
Simulatorにビルドした時、どうやってもコネクションエラーが発生し解決できなかったので、プロジェクトを再作成しました。やり方をメモ程度に残しておこうと思います

### 現在の依存情報を削除
•ios/ フォルダ
•android/ フォルダ
•.dart_tool/ フォルダ
•build/ フォルダ
•Pods/ フォルダ
•.packages ファイル
•pubspec.lock ファイル

### Flutterプロジェクトの再作成
```shell
flutter create .
flutter pub get
cd ios
pod install
```
javascriptのfetchでサーバの子プロセスのエラーメッセージをブラウザ（bootbox）に表示される方法

## 目的
javascriptのfetchはサーバーのコマンドが失敗しても通信が成功すればerror補足はしないので、自分でなんとかする必要があります。特に子プロセスのエラーメッセージをブラウザに伝える方法についての一考察です。cgiで通信ステータスをなんとかするとかじゃなくて単純にエラーメッセージさえ返せればいいじゃん、ってわけでトライしてみました。ここでのコードは動いたコードを抽象化したので誤記があったらすみません。本格的なエラーハンドリングではないのでしょうが簡単なのでご参考まで。

### fetch apiのエラーハンドリングについては例えば↓
https://qiita.com/IzumiSy/items/031df1df4f4541e536b4

## 処理流れと前提
- 呼び出し経路と処理流れ：ブラウザ(javascript)→通信（apache）→CGI（親プロセス）→子プロセス（エラー）→親プロセス（子プロセスエラー補足とエラーメッセージにhttpのヘッダ付加）→通信→ブラウザ
- モーダル表示はbootbox.jsを使っています（必須ではないです）。見栄えで使っています。 https://bootboxjs.com/
## コードの考え方
### javascript
javascriptではサーバのエラーメッセージをfetchの`.then()`の中でmodalの`bootbox()`で表示させます。サーバーからのメッセージの中の"error"を見つけるだけです。`alert()`でも問題ないでしょう。

```javascript
// fetchの基本形（何かを登録した）
fetch("https://yourdomein/cgi/cmd.cgi")
.then((response)=>{
    if( ! response.ok ){
    	bootbox.alert({title: '登録失敗', message: 'レスポンスエラー'});
    } else {
        response.text().then(text => {
        	// textの中に"error"文字を捕捉
            if (text.includes('error')) {
        		bootbox.alert({title: '登録失敗 (;´д｀)', message: text});
        	} else {
        		bootbox.alert({title: '登録成功', message: "登録成功：(⑉•ᴗ•⑉)Thanks❤︎ "});
        	}
        });
    }
})
.catch(error => {
    bootbox.alert({title: '登録失敗', message: error.message });
});         
```
### サーバコマンド（親プロセス:`CMD.cgi`）
親プロセスで、子プロセスでエラーの補足とエラーメッセージを受け取り通信に返します。

```bash
#!/usr/bin/env bash

set -vxeu

# エラーメッセージを出力して終了 文字列にはerrorを入れること
error500(){
	echo "Content-type: text/html"
	echo ""
	echo "$@"
	exit 1
}
# 子プロセスの起動とエラー補足
if ! errmsg=$( child.bash 2>&1 ); then
    error500 "error in child.bash: $errmsg"
fi
```
### 子プロセス(`child.bash`)
子プロセスでは普通にエラーメッセージで終了します。
```bash
#!/usr/bin/env bash
# エラーのみ出す cgiから呼ばれるbashはこれを使うこと
error(){
	echo "$@"
	exit 1
}
# commandが失敗した場合、errorを実行します。
command || error "command error because ..."
```
### 適用したコードのブラウザ画面例
![スクリーンショット 2024-08-31 10.52.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/178143/efdd8772-9d2d-b7c5-fa78-eb7bd408dd25.png)

## まとめ
サーバーの子プロセスのエラーメッセージをどうしたら`fetch()`に返せるかいろいろ調べたのですが長年わからなかった。今日久しぶりに生成AIと一緒に取り組んだら見つけられたので共有です。誰かのお役に立てれば幸いです。
ros2 jazzy gazebo harmonic インストール手順

お疲れ様です。秋並です。

ROS2 jazzyでgazeboをインストールする際に、少し手間取ったので記録を残しておきます。

## 動作環境
- ubuntu24.04 (on WSL2)

# ROS2 jazzyのインストール
ROSをインストールしていない場合、公式サイトを参考にして、ROS2 jazzyをインストールしてください。

[ROS2 jazzy Documentation: Ubuntu (deb packages)](https://docs.ros.org/en/jazzy/Installation/Ubuntu-Install-Debs.html)

# gazebo harmonicのインストール
gazeboの公式サイトを参考に、gazebo harmonicをインストールしてください。


[Gazebo harmonic: Binary Installation on Ubuntu](https://gazebosim.org/docs/latest/install_ubuntu/)

```bash
sudo apt-get update
sudo apt-get install lsb-release gnupg
```

```bash
sudo curl https://packages.osrfoundation.org/gazebo.gpg --output /usr/share/keyrings/pkgs-osrf-archive-keyring.gpg
echo "deb [arch=$(dpkg --print-architecture) signed-by=/usr/share/keyrings/pkgs-osrf-archive-keyring.gpg] http://packages.osrfoundation.org/gazebo/ubuntu-stable $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/gazebo-stable.list > /dev/null
sudo apt-get update
sudo apt-get install gz-harmonic
```

# ROS2用のgazeboパッケージのインストール

gazeboの公式サイトを参考に、ROS用のgazeboパッケージをインストールしてください。

[Gazebo harmonic: Installing Gazebo with ROS](https://gazebosim.org/docs/latest/ros_installation/)


```bash
sudo apt-get install ros-${ROS_DISTRO}-ros-gz
```

# (WSL2の場合）環境変数の設定

WSL2の場合、デフォルトの設定ではgazeboを起動しても強制終了することがあるので、その場合は `~/.bashrc`に以下を設定してください、

```bash
export LIBGL_ALWAYS_SOFTWARE=1
```

# gazeboの起動
最後に、以下コマンドでgazeboが起動すればOKです。

```bash
ros2 launch ros_gz_sim gz_sim.launch.py
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2030125/3aef45f0-c76b-5082-3cee-cb8e8da49a1e.png)

# 参考サイト
- [ROS2 jazzy Documentation: Ubuntu (deb packages)](https://docs.ros.org/en/jazzy/Installation/Ubuntu-Install-Debs.html)
- [Gazebo harmonic: Binary Installation on Ubuntu](https://gazebosim.org/docs/latest/install_ubuntu/)
- [Gazebo harmonic: Installing Gazebo with ROS](https://gazebosim.org/docs/latest/ros_installation/)
erbからjavascriptに@userのidを渡す

## data属性を使う
erbと別ファイルのjavascriptにuserのidなどを渡したいときの書き方です。

erbファイル
```
<button id="hoge" 
        class="hoge" 
        type="button" 
        data-user-id="<%= @user.id %>" 
        data-post-id="<%= @post.id %>">
  削除
</button>
```

javascriptファイル
```
  const button = document.getElementById('hoge');

  const userId = button.dataset.userId;
  const postId = button.dataset.postId;

  console.log('User ID:', userId);
  console.log('Post ID:', postId);
```

ありがとうございました。

### 参考記事
[データ属性の使用](https://developer.mozilla.org/ja/docs/Learn/HTML/Howto/Use_data_attributes)
オブジェクト指向とは

オブジェクト指向とは、そふとうぇあ開発におけるプログラミングパラダイムの一つで、データとその操作を一つにまとめた「オブジェクト」を基本的な構成要素として扱う考え方です。オブジェクト指向は、複雑なそふとうぇあシステムを分かりやすく設計・実装するために広く採用されています。
```test.ts
class Test {
    ~
}
```
入力処理とセキュリティ

引き続き、[体系的に学ぶ 安全なWebアプリケーションの作り方](https://www.amazon.co.jp/%E4%BD%93%E7%B3%BB%E7%9A%84%E3%81%AB%E5%AD%A6%E3%81%B6-%E5%AE%89%E5%85%A8%E3%81%AAWeb%E3%82%A2%E3%83%97%E3%83%AA%E3%82%B1%E3%83%BC%E3%82%B7%E3%83%A7%E3%83%B3%E3%81%AE%E4%BD%9C%E3%82%8A%E6%96%B9-%E7%AC%AC2%E7%89%88-%E8%84%86%E5%BC%B1%E6%80%A7%E3%81%8C%E7%94%9F%E3%81%BE%E3%82%8C%E3%82%8B%E5%8E%9F%E7%90%86%E3%81%A8%E5%AF%BE%E7%AD%96%E3%81%AE%E5%AE%9F%E8%B7%B5-%E5%BE%B3%E4%B8%B8/dp/4797393165)を読んだので、覚えたい部分を記事にしていく。

## 入力処理

入力処理では、入力値に対して以下の処理を行う。
- 文字エンコーディングの妥当性検証
- 文字エンコーディングの変換
- 入力値の妥当性検証

### 文字エンコーディングって？
文字をコンピュータで使えるように数字に変換する処理のこと。
変換する文字コードは複数の種類があるため、同じ文字でも異なる文字コードでは異なる数字に変換される。

jsでiconv-liteを使用して文字エンコーディングについて確認する。

``` js
var iconv = require("iconv-lite");

let buf = iconv.encode("testです。", "utf-8");
console.log(buf); // <Buffer 74 65 73 74 e3 81 a7 e3 81 99 e3 80 82>

let buf2 = iconv.encode("testです。", "Shift_JIS");
console.log(buf2); // <Buffer 74 65 73 74 82 c5 82 b7 81 42>

let test = iconv.decode(buf, "Shift_JIS");
console.log(test); // test縺ｧ縺吶�

let test2 = iconv.decode(buf, "utf-8");
console.log(test2); // testです。

```

utf-8とshift_jisでエンコードすると異なる数値に変換されていることがわかる。
utf-8でエンコーディングしたものをutf-8とshift_jisでデコード（数値→文字列）すると、utf-8は正常に変換できるが、shift_jisは文字化けする。

### 入力値の検証
本来は数値だけしか入らないのに、文字を入力できてしまうと、
その値はDBに登録されるとき、エラーになる。
といった現象が起こるので、入力値の検証は必要。

メインは上記のようなエラーを防ぐことが目的だけど、セキュリティ向上にもつながる。
- SQLインジェクションの対策をしていなかったが、英数字のみを許可していたため、実害には至らない
などなど。

#### バイナリセーフ
バイナリセーフとは、入力値がどんなバイト列であっても正しく扱えること。
値ゼロのバイト（ヌルバイト）が現れても正しくしょるできることを主に指す。

ヌルバイトは、C言語やUnix、WindowsのAPIでは、ヌルバイトを文字列の終端とみなす取り決めがある。
なので、一部関数では途中でヌルバイトが現れるとそこが文字列の終端だと誤解してしまう。
こういった関数はバイナリセーフでない関数と呼ぶ。

#### ヌルバイト攻撃
ヌルバイトを使った攻撃をヌルバイト攻撃と呼ぶ。
Cloud Traceへコード変更無しでトレースをエクスポートする

## はじめに

[Cloud Trace](https://cloud.google.com/trace)はアプリケーションのトレース情報を収集し、リクエストの遅延やボトルネックを特定を可能にするGoogle Cloudのサービスです。

今回は、[OpenTelemetry Java Agent](https://opentelemetry.io/docs/zero-code/java/agent/)を使用して、JavaアプリケーションのコードおよびGradleの変更無しでトレース情報を送信することを検証します。

## ユースケース

この手法を検証した理由は、マルチクラウド環境でアプリケーションをデプロイした際に、各クラウドの Observability マネージドサービスを効率的に活用したかったためです。

例えば、Azure 側にホストしているアプリケーションのパフォーマンス監視は Application Insights で行い、Google Cloud では Cloud Monitoring/Trace で行いたいという状況です。

もちろん、クロスクラウドな集約型 APM ツールを導入するという手法も一般的ですが、今回はその手法には触れません。

Active/Passive マルチクラウドアーキテクチャにおいては、片方のクラウドを監視する機会は限定的であるため、各マネージドサービスを活用するアプローチは軽量で合理的だと考えています。

また、ソースコードを変更したくない理由は、各クラウド向けに同一アプリケーションのソースコードを二重管理したくないためです。

## Cloud Trace Agent?

多くのMonitoringツールはコードなしで導入可能なJava agentを提供しています。前述の Azure Application Insightsについても同様です。

Cloud Trace Agent の jarは提供されていませんが[OpenTelemetry Java Agent](https://opentelemetry.io/docs/zero-code/java/agent/)を活用することで自動計装(auto-instrumentation)が実現可能となります。

加えて、Google Cloud へエクスポートするためには、OpenTelemetry agentの拡張として提供されるexporter-autoが必要となります。

https://github.com/GoogleCloudPlatform/opentelemetry-operations-java

## 自動計装

以下の検証環境で自動計装を実施します。

デプロイ先：GKE
コンテナ：Spring Boot Application

### 必要なJarのダウンロード

- opentelemetry-javaagent : https://github.com/open-telemetry/opentelemetry-java-instrumentation/releases/download/v1.31.0/opentelemetry-javaagent.jar

- com/google/cloud/opentelemetry/exporter-auto : https://repo1.maven.org/maven2/com/google/cloud/opentelemetry/exporter-auto/0.31.0-alpha/exporter-auto-0.31.0-alpha-shaded.jar

### Dockerfileの作成

上記のJarをビルドするアプリケーションのルートに配置し、以下のDockerfileを作成。


```Dockerfile
FROM amazoncorretto:21-alpine-jdk

COPY opentelemetry-javaagent.jar opentelemetry-javaagent.jar
COPY build/libs/SampleApp-0.0.1-SNAPSHOT.jar /app/sampleapp.jar
COPY exporter-auto-0.31.0-alpha-shaded.jar exporter-auto.jar

WORKDIR /app

ENTRYPOINT ["java", \
            "-javaagent:/opentelemetry-javaagent.jar", \
            "-Dotel.javaagent.extensions=/exporter-auto.jar", \
            "-Dotel.service.name=sampleapp",\
            "-Dotel.metrics.exporter=google_cloud_monitoring", \
            "-Dotel.traces.exporter=google_cloud_trace", \
            "-jar", "/app/sampleapp.jar"]

```

ここでの設定値は以下の通り。（詳しくはhttps://opentelemetry.io/docs/languages/java/configuration/）

| プロパティ  | 環境変数  |  値 |  説明 |
|-----------|---|---|---|
|  otel.traces.exporter | N/A  | google_cloud_trace  |  トレース用のexporter　|
| otel.metrics.exporter  | N/A	  |  google_cloud_monitoring |  メトリクスの用のexporter |
|  GOOGLE_CLOUD_PROJECT |  GOOGLE_CLOUD_PROJECT |  自動検知 |  metrics/traces againtのprojectId |
|  GOOGLE_APPLICATION_CREDENTIALS | GOOGLE_APPLICATION_CREDENTIALS  | 自動検知  |  Google Cloud APIのクレデンシャル | 
	 		

問題なくデプロイできたら、Console の Cloud Trace でトレース情報が確認できます。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3580319/787769ac-7680-314b-72c5-8ed24b69fa8d.png)
文字列中に同じ文字が並んでいたら1文字にまとめたい

文字列中に同じ文字が並んでいたら1文字にまとめたい場合、Rubyでは`String#squeeze`を利用します。


```ruby
p "112233445566778899".squeeze
#=> "123456789"
```

Python3で、同じことを実現するにはどうすればよいでしょうか?

まず`itertools.groupby`を利用する方法があります。

```python
import itertools
s = "112233445566778899"
t = ''.join(c for c, _ in itertools.groupby(s))
print(t)
#=> '123456789'
```

内包表記の代わりに`operator.itemgetter`を利用することもできます。

```python
import itertools, operator
s = "112233445566778899"
t = ''.join(map(operator.itemgetter(0), itertools.groupby(s)))
print(t)
#=> '123456789'
```

正規表現を利用する方法もあります。

```python
import re
s = "112233445566778899"
t = re.sub(r'(.)(\1+)', r'\1', s)
print(t)
#=> 123456789
```

もっとも、この程度であれば、愚直に実装するほうがPythonicかもしれません。

```python
def squeeze(s):
    stack = []
    for c in s:
        if len(stack) == 0 or stack[-1] != c:
            stack.append(c)
    return ''.join(stack)
    
    
s = "112233445566778899"
t = squeeze(s)
print(t)
#=> 123456789
```

**環境情報**

- Python 3.8.10
- ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux]
【2024年08月版】Linux namespace 体験メモ【初学者向け、教育資料、namespace】

# はじめに

Linux の namespace機能を使って仮想的なネットワーク接続を体験するメモ

初学者向け

Linux 1台でできるので、お手軽にネットワークの基礎を学べます

:::note info
その他の namespace を利用した体験メモはこちら
- [【2024年08月版】Linux 1台でブリッジ体験メモ【初学者向け、教育資料、namespace】](https://qiita.com/nouernet/items/3d113360e700d0e9a5f9)
- [【2024年08月版】Linux 1台でルーティング体験メモ【初学者向け、教育資料、namespace】](https://qiita.com/drafts/99ee965efe7df3704f80)
:::

# 環境準備

- Ubuntu22.04などのLinuxを1台準備します

# 手順

- ２つのPCを同一のセグメント(10.11.12.0/24)に配置します
- ２つのPC間でPingのやり取りができることを確認します

```plantuml
@startuml
nwdiag {

    network 01 {
        address = "10.11.12.0/24";
        testns01 [address = ".1"];
        testns02 [address = ".2"];
    }
    
}
@enduml
```

```
sudo ip netns add testns01
sudo ip netns add testns02
sudo ip netns list
sudo ip link add testns02-veth0 type veth peer name testns01-veth0
sudo ip link
sudo ip link set testns01-veth0 netns testns01
sudo ip link set testns02-veth0 netns testns02
sudo ip netns exec testns01 ip link
sudo ip netns exec testns02 ip link
sudo ip netns exec testns01 ip a add 10.11.12.1/24 dev testns01-veth0
sudo ip netns exec testns02 ip a add 10.11.12.2/24 dev testns02-veth0
sudo ip netns exec testns01 ip link set testns01-veth0 up
sudo ip netns exec testns02 ip link set testns02-veth0 up
sudo ip netns exec testns01 ip link set lo up
sudo ip netns exec testns02 ip link set lo up
sudo ip netns exec testns01 ping 10.11.12.2
sudo ip netns exec testns02 ping 10.11.12.1
sudo ip netns exec testns01 ping 10.11.12.1
sudo ip netns exec testns02 ping 10.11.12.2
```

# さいごに

かんたんでしたね

# 関連リンク

- [【2024年08月版】Linux 1台でブリッジ体験メモ【初学者向け、教育資料、namespace】](https://qiita.com/nouernet/items/3d113360e700d0e9a5f9)
- [【2024年08月版】Linux 1台でルーティング体験メモ【初学者向け、教育資料、namespace】](https://qiita.com/drafts/99ee965efe7df3704f80)
Raspberry Pi4で画像認識を試してみた

## はじめに
最近は僕の身の回りやメディアでもAIの話題が増えてきました。AIについて、少しずつでも勉強していかないとエンジニアとして取り残されてしまう懸念を持っています。
そこでまずは使って慣れてみることが重要だと思いますので、Tensorflowとkerasを使って、Raspberry Pi 4上で以下のWebページにある各モデルで画像認識を試してみました。

https://keras.io/api/applications/

## Tensorflow、kerasとは？
Tensorflowは、以下のWebページに詳細が説明されておりますが、Googleが開発したディープラーニングを簡単に使用可能なソフトウェアフレームワークのことです。僕のような初心者でも簡単に使えるようです。

https://www.tensorflow.org/learn?hl=ja

また、kerasはTensorflowのラッパーとして機能し、簡単にディープラーニングを行うことができるpythonのライブラリです。

まずはTensorflowとkerasを使ってディープラーニングを体験してみたいと思います。

## 事前準備
定番ではありますが、今回は「ゴールデンレトリバー」のJpeg画像を2枚用意しました（サイズ違いで同じ内容：224x224と600x600）。
これらのJpeg画像を入力として、各モデルでどのくらいの精度で「ゴールデンレトリバー」と認識できるかを試してみました。
使用したPythonコードは、[Keras Applications](https://keras.io/api/applications/)に記載されている**Usage examples for image classification models**を使用しました。

## 結果
僕の環境では以下のようになりました。
まずは入力画像224x224を使った場合の結果です。
![golden_224x224.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3750854/d3587a5a-1f0b-7512-4b87-e5619f18a2bc.png)

次に入力画像600x600を使った場合の結果です。
![golden_600x600.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3750854/eee447c5-3b58-c0e1-d7db-010968c82bfc.png)

入力画像600x600を使った方が全体的に認識率が上がっているような気がします。入力画像の解像度も認識率には影響しそうです。
僕の環境ではResNetが比較的、認識率が高めでした。処理時間は圧倒的にMobileNetが早かったです。認識率もそこそこ高いので、Raspberry PiでディープラーニングするならMobileNetを使うのが良いのかなという印象でした。
SnowflakeでのJSON内のテキストデータの扱い方で気づいたこと（AS_VARCHAR）

以前、「SnowflakeのFLATTEN、完全に理解した」とか言って以下の投稿をしました。

https://qiita.com/a-kamiya/items/5b307003775797948f63

その際には件数だけを書いていて実データについては書いてはいないのですが、
JSONデータをselectすると、テキストデータはダブルクォーテーションで囲って出力されたんです。

再現してみます。
# 準備
```
--DB作成
create database TEST_DB;
--スキーマ作成
create schema TEST_SCHEMA;
--JSONテスト用テーブル作成
create table TEST_JSON(JSON_DATA VARIANT) COMMENT='JSONテスト用テーブル';
--テストデータを3件INSERT
INSERT INTO TEST_JSON SELECT TO_VARIANT(PARSE_JSON('{"id":"A1","object":"A","amount":100}'));
INSERT INTO TEST_JSON SELECT TO_VARIANT(PARSE_JSON('{"id":"B1","object":"B","amount":200}'));
INSERT INTO TEST_JSON SELECT TO_VARIANT(PARSE_JSON('{"id":"B2","object":"B","amount":10}'));
--INSERT結果確認
select * from TEST_JSON;
```
![スクリーンショット 2024-08-31 094034.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3487310/748a7fe0-5f5f-e215-078d-56ec248bf9b7.png)
データ入ってますね。
ダブルクォーテーション、気になります。

JSONを分解して出力します。
```
--JSONを分解
select
    t1.JSON_DATA:amount as amount,
    t1.JSON_DATA:id as id,
    t1.JSON_DATA:object as object
from TEST_JSON t1;
```
![スクリーンショット 2024-08-31 094421.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3487310/07544419-8630-c121-4bb7-38e7f9765c43.png)
ここで”A1”というようにテキストの値はダブルクォーテーションが付いてきます。
数値項目と解釈されているAmountの値はそのままです。

実際データとして使う時に邪魔だなーと思い、
replace(対象カラム,'"') as なんちゃら
というようにダブルクォーテーションを除去するようにしてました。

# 思い付き実装で対応
```
--replace関数をかます
select
    t1.JSON_DATA:amount as amount,
    replace(t1.JSON_DATA:id,'"') as id,
    replace(t1.JSON_DATA:object,'"') as object
from TEST_JSON t1;
```
![スクリーンショット 2024-08-31 094529.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3487310/1652cd9a-2525-6f26-9da6-1516136f97d5.png)
求めていた結果としては正しい。

:::note info
追記です。
ひょっとしてreplace関数だとダブルクォーテーションは除去できても実は型はVariantのままなのでは？と疑問に思い再度確認したところ、textってなってました。
![スクリーンショット 2024-09-11 101535.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3487310/c5a2ecec-c2f2-aca8-3875-5968ee2cc63e.png)
黄色に塗ったところ、Aにカーソルを乗せると「text」となっていました。
replace関数をかます前の結果の"A1"の値の方は「variant」となっています。
replace関数をかますことで文字列型へのキャストが行われていることが確認できました。
間違ったことを書いてなくてよかったです。
:::

でも、「これってなんかもっとスマートにやる方法があるはず。
こんな泥臭いことをわざわざやるなんて、そんなわけない。」
って思ってたんです。（思ってたけど放置。）

で、UdemyでSnowPro対策の講座見ていて発見しました。↓
![スクリーンショット 2024-08-31 100952.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3487310/9859b33f-7b08-fb67-a39e-05e1f53f6c57.png)

:::note info
AS_VARCHAR(対象カラム) as なんちゃら
:::
うおー、顧客が求めていたもの。
これで行けました。

# 発見した技（これが正解）
```
select
    t1.JSON_DATA:amount as amount,
    AS_VARCHAR(t1.JSON_DATA:id) as id,
    AS_VARCHAR(t1.JSON_DATA:object) as object
from TEST_JSON t1;
```
![スクリーンショット 2024-08-31 094657.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3487310/0b05ccff-b4fc-5b42-7a6a-83a5cc0ae731.png)

おしまい。
【rails】renderデプロイ時の[ActiveSupport::MessageEncryptor::InvalidMessage]


## はじめに
renderでデプロイ時にcredentials.yml.encの設定がうまくかなかったので、備忘録に。

## 仕様
Rails7
dokcer
render

##  デプロイ時のエラー
```
ActiveSupport::MessageEncryptor::InvalidMessage
```
暗号化されたメッセージを復号化できていない。
おそらくconfig/master.key の鍵が config/credentials.yml.enc の鍵穴と一致していない。

##  credentials.yml/master.key の関係について
master.key を鍵、credentials.yml.enc を鍵穴に例えるとわかりやすいです。

credentials.yml.enc は暗号化されたデータ（例えば、アプリケーションのパスワードやAPIキー）で、このデータを読むためには特定の鍵が必要です。その鍵が master.key です。つまり、master.key を使って初めて credentials.yml.enc に格納された情報を解読（鍵を使って鍵穴を開けるイメージ）し、アクセスすることができます。

この鍵（master.key）がない、または間違っていると、鍵穴（credentials.yml.enc）に何が入っているかを知ることはできませんし、鍵が流出すると、誰でも鍵穴を開けて中身を見られる危険性があるということになります。


## 結論

config/credentials/production.yml.enを設定していたので、次の二つをやりました。

* production.yml.enc に secret_key_base を設定。

設定方法は次の通り。


下記のコマンドでsecret_key_baseに入れる値を生成します。
```
$ rails secret
```

上記のコマンドで生成されたキーをconfig/credentials/production.yml.encを開いてsecret_key_baseに設定します。
```
$ EDITOR="vim" rails credentials:edit --environment production
```

```
$ secret_key_base: #rails secretで生成されたキー
```
* RAILS_ENVの設定とRAILS_MASTER_KEYをproduction.keyに設定。

```
RAILS_ENV: production
RAILS_MASTER_KEY: #production.keyの値
```

## それでもダメなら
config/credentials/production.keyの再設定をしてみる。
鍵と鍵穴が一致していないなら、新しく作りなおりましょう、ということですね。

```
$ rm config/credentials/production.yml.enc
$ rm config/credentials/production.key
$ EDITOR=vim rails credentials:edit --environment production
```
上記コマンドを実行し、再設定する。



## まとめ
最初に、config/credentials.yml.encを使用していたことと、secret_key_baseが設定できていなかったことが原因でした。

* Rails ガイドにも、環境ごとの設定ファイルが存在する場合はそれを使用し、存在しない場合はデフォルトの config/credentials.yml.enc を使用すると書かれています。

> デフォルトでは、config/credentials/#{Rails.env}.yml.encがあればそのパスを使い、なければconfig/credentials.yml.encを使います。
[railsガイド](https://railsguides.jp/configuring.html#config-credentials-content-path)から引用

その[railsのgithub](https://github.com/rails/rails/blob/429f400cde3db5e956b9bfa46d4e927999f7b2ff/railties/lib/rails/application/configuration.rb#L631-L639) のコードはここに書いてある。（興味があれば）

* secret_key_baseについてはこんな感じ。

>secret_key_base: このメソッドは、改竄防止のためにアプリケーションのセッションを既知の秘密キーと照合するためのキーを指定するときに使います。test環境とdevelopment環境の場合、secrets.secret_key_baseでランダムに生成されたキーを使います。その他の環境ではキーをconfig/credentials.yml.encに設定すべきです。
[railsガイド](https://railsguides.jp/v5.2/configuring.html#rails%E5%85%A8%E8%88%AC%E3%81%AE%E8%A8%AD%E5%AE%9A)から引用


>秘密鍵は十分に長く、かつランダムでなければなりません。一意な秘密鍵を得るにはbin/rails secretを使います。[Rails セキュリティガイド](https://railsguides.jp/security.html#%E3%82%BB%E3%83%83%E3%82%B7%E3%83%A7%E3%83%B3%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B8)


## 参考記事
[【Rails + Docker】Render.comでMulti Environment Credentialsに対応する](https://qiita.com/futaro0405/items/5e3b648c838681244818)

[Rails の Credentials で使用する yml.enc を変えたい](https://shiro-16.hatenablog.com/entry/2023/04/20/141205)
[credentials.yml master.key関係について](https://qiita.com/jojo232/items/6c3235e7efde85216129)
【2024年08月版】Linux 1台でブリッジ体験メモ【初学者向け、教育資料、namespace】

# はじめに

Linux の namespace機能を使ってブリッジを体験するメモ

初学者向け

Linux 1台でできるので、お手軽にブリッジの基礎を学べます

:::note info
その他の namespace を利用した体験メモはこちら
- [【2024年08月版】Linux namespace 体験メモ【初学者向け、教育資料、namespace】](https://qiita.com/nouernet/items/4c37dbef38c12939b72e)
- [【2024年08月版】Linux 1台でルーティング体験メモ【初学者向け、教育資料、namespace】](https://qiita.com/nouernet/items/99ee965efe7df3704f80)
:::

# 環境準備

- Ubuntu22.04などのLinuxを1台準備します

# 手順

- ３つのPCを同一のセグメント(10.11.12.0/24)に配置します
- ３つのPC間でブリッジを経由してPingのやり取りができることを確認します

```plantuml
@startuml
nwdiag {

    network 01 {
        address = "10.11.12.0/24";
        testns01 [address = ".1"];
        testns02 [address = ".2"];
        testns03 [address = ".2"];
    }
    
}
@enduml
```

```
sudo ip --all netns delete

sudo ip netns add testns01
sudo ip netns add testns02
sudo ip netns add testns03
sudo ip netns add testbr01
sudo ip netns list
sudo ip link add testns01-veth0 type veth peer name testbr01-veth0
sudo ip link add testns02-veth0 type veth peer name testbr01-veth1
sudo ip link add testns03-veth0 type veth peer name testbr01-veth2
sudo ip link
sudo ip link set testns01-veth0 netns testns01
sudo ip link set testns02-veth0 netns testns02
sudo ip link set testns03-veth0 netns testns03
sudo ip link set testbr01-veth0 netns testbr01
sudo ip link set testbr01-veth1 netns testbr01
sudo ip link set testbr01-veth2 netns testbr01
sudo ip netns exec testns01 ip link
sudo ip netns exec testns02 ip link
sudo ip netns exec testns03 ip link
sudo ip netns exec testbr01 ip link
sudo ip netns exec testns01 ip a add 10.11.12.1/24 dev testns01-veth0
sudo ip netns exec testns02 ip a add 10.11.12.2/24 dev testns02-veth0
sudo ip netns exec testns03 ip a add 10.11.12.3/24 dev testns03-veth0
sudo ip netns exec testns01 ip link set testns01-veth0 up
sudo ip netns exec testns02 ip link set testns02-veth0 up
sudo ip netns exec testns03 ip link set testns03-veth0 up
sudo ip netns exec testbr01 ip link set testbr01-veth0 up
sudo ip netns exec testbr01 ip link set testbr01-veth1 up
sudo ip netns exec testbr01 ip link set testbr01-veth2 up
sudo ip netns exec testns01 ip link set lo up
sudo ip netns exec testns02 ip link set lo up
sudo ip netns exec testns03 ip link set lo up
sudo ip netns exec testbr01 ip link add dev br0 type bridge
sudo ip netns exec testbr01 ip link set br0 up
sudo ip netns exec testbr01 ip link set testbr01-veth0 master br0
sudo ip netns exec testbr01 ip link set testbr01-veth1 master br0
sudo ip netns exec testbr01 ip link set testbr01-veth2 master br0
sudo ip netns exec testns01 ping 10.11.12.2
sudo ip netns exec testns01 ping 10.11.12.3
sudo ip netns exec testns02 ping 10.11.12.1
sudo ip netns exec testns02 ping 10.11.12.3
sudo ip netns exec testns03 ping 10.11.12.1
sudo ip netns exec testns03 ping 10.11.12.2
```

# さいごに

かんたんでしたね

# 関連リンク

- [【2024年08月版】Linux namespace 体験メモ【初学者向け、教育資料、namespace】](https://qiita.com/nouernet/items/4c37dbef38c12939b72e)
- [【2024年08月版】Linux 1台でルーティング体験メモ【初学者向け、教育資料、namespace】](https://qiita.com/nouernet/items/99ee965efe7df3704f80)
windowsでiverilog その151

# 概要　
windowsでiverilogやってみた。
SPI HOSTな、verilog見つけたので、テストベンチ書いてみた。

# 参考にしたページ

https://yuji2yuji.hatenablog.com/entry/2019/08/13/190239


# サンプルコード

```
`timescale 1 ns/ 100 ps

module test();
	reg CLK;
	reg RST;
	reg RXSIG;
	reg START;
	reg [7:0] TXDATA;
	wire [7:0] RXDATA;
	wire SPICLK;
	wire TXSIG;
	spi_msb u(.CLK(CLK), .RST(RST), .RXDATA(RXDATA), .RXSIG(RXSIG), .SPICLK(SPICLK), .START(START), .TXDATA(TXDATA), .TXSIG(TXSIG));
	always
	#10
		CLK = ~CLK;
	initial
	begin
		CLK = 1;
		RST = 1;
		START = 0;
		TXDATA = 8'b01010011;
		RXSIG = 0;
		#200
			START = 1;
		#100
			RXSIG = 1;
		 	START = 0;
		#100
			RXSIG = 1;
		#5000
			RXSIG = 0;
		#5000
			RXSIG = 0;
		#5000
			RXSIG = 1;
		#5000
			RXSIG = 1;
		#5000
			RXSIG = 0;
		#5000
			RXSIG = 0;
		#5000
			RXSIG = 1;
		#5000
			RXSIG = 0;
		#5000
			$finish;
	end
	initial
	begin
		$dumpfile("test.vcd");
		$dumpvars(0, u);
	end
endmodule





```

# 写真

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/b261dbaf-39c7-14bf-c550-9d5ee5855c3f.png)

以上。
【AWS】開発知識はないがなんとかAWS DVA合格した話

## はじめに
　AWS-DVAの資格試験を受けてきました。無事合格はしたのですが、正直これまで取得したAWSの資格の中で一番苦戦しました（大した数取得はしてないんですが）。苦戦した理由は、資格の難しさだけでなく自身の勉強方法や気持ち的な部分もあったので、自戒の念を込めて備忘録として残しておくので、これからDVAの勉強を始める方の参考になれば嬉しいです。

伝えたいこと
・継続は力なり
・開発系について問われるので、AWS以外の知識で知らないことは概要だけでも調べる
・SAA、SOAと比較すると難しかった

## 自己紹介
エンジニアとしては３年目、AWSの勉強を始めたのは去年の３月からで、今年の１月からAWSのプリセールスエンジニアとして活動するようになったが、実際にAWSの構築や作業などの経験はないです。

・保持資格
Lpic　lv.1
CCNA
CLF
SAA
SOA

## AWSDVAとは
DVAはAWSクラウドベースのアプリケーションの開発、テスト、デプロイ、デバッグについて技術的に習熟していることを認定する資格です。

受験対象者は、試験ガイドによるとAWSサービスを使用してアプリケーションの開発と保守を行う1年以上の実務経験が望ましいとされています。

## 教材
①参考書
ポケットスタディ AWS認定デベロッパーアソシエイト ［DVA-C02対応］

https://www.amazon.co.jp/%E3%83%9D%E3%82%B1%E3%83%83%E3%83%88%E3%82%B9%E3%82%BF%E3%83%87%E3%82%A3-AWS%E8%AA%8D%E5%AE%9A%E3%83%87%E3%83%99%E3%83%AD%E3%83%83%E3%83%91%E3%83%BC%E3%82%A2%E3%82%BD%E3%82%B7%E3%82%A8%E3%82%A4%E3%83%88-%EF%BC%BBDVA-C02%E5%AF%BE%E5%BF%9C%EF%BC%BD-%E5%B1%B1%E4%B8%8B%E5%85%89%E6%B4%8B/dp/4798070696

②問題集
CloudTechのAWS資格演習対策コース「DVA-C02 Developer – Associate」

https://kws-cloud-tech.com/lessons/dva-c02-developer-associate

## 勉強時間
2024年1月から勉強を開始して試験を受けたのが8月末なので期間としては8ヶ月です。
ただ、現場や他にも忙しかったりで1ヶ月まるまる勉強できなかったのが３回くらいあったので実際は５ヶ月くらいです。

1日の勉強時間は、平日休日どちらも1時間くらいでした。

## 学習スケジュール
・1月
参考書を読んでましたが、内容難しいのと新しい現場に配属されたこともあってあまり読み進められなかったです。

・２月
中旬くらいでようやく参考書を一周しました。そのあと参考書に付属している問題集を解いてました。

・３月
CloudTechの問題集を解き始めましたが内容が難しく、色々あり勉強ストップしました。

・４月
何もせず

・５月
さすがに勉強しようと思い、もう一度問題集を解き始めましたが再度色々あり２週間くらいで勉強ストップ

・６月
月の半分くらいは勉強していた気がする
正直問題の答えを見ても半分くらい理解できてなかった

・７月
さすがにそろそろ本腰を入れて勉強を再開しました。
月初の問題集の正答率は５０％くらい、月末は７０％くらいまで上がった

・８月
試験の予約を済ませて気合を入れて、問題集を解いてました。
この時点で、まだ理解できない問題は、サービスを調べたり開発の用語を調べたりして少しでも正答率をあげるように意識しました。
テスト前には正答率８〜９割程度だったので、たぶんいけるでしょ精神で試験に臨みました。


## 結果
点数は763点でした。
合格最低ラインが720点なので、結構ギリギリ・・・

体感として、3〜4割くらい問題集の問題が出たと思います。
あとの3割も勉強したような内容が出てきたのですが、２割くらいは頭を悩ませる問題でした。

⭐️個人的に出題され理解するのが難しかったリスト
・API　GW、lambda、DynamoDBを使ったサーバーレスモデル
・cognitoのIDプールとユーザープールの使い分け
・API　GWの統合レスポンスや統合プロキシ
・code系のサービスの使い分け
・デプロイ方法（イミュータブルやローリングとか）
・Secrets ManagerとParameter Storeの違い(定期的にローテーションと出てきたらSecrets Manager)
・Lambdaレイヤー

## 反省点
①勉強を継続できなかった
勉強をしなかった期間が何度かあったのですが、やっぱり内容は忘れているので少しでもいいので毎日継続して勉強はしておいた方がいいと思いました。

②わからないサービスや用語を調べるのが遅かった
これまで取得した資格は参考者を読み込んだり問題の解説を読めば理解できたのですが、DVAに限っては理解できない問題が1~3割くらいありした。この範囲を暗記でカバーではなく早めに用語を調べておけばもっと早く楽に取得できたと思ってます。

## 参考
https://aws.amazon.com/jp/certification/certified-developer-associate/