OmniAICreator/Qiita-1.07M huggingface dataset:
古いCDKでLambdaがデプロイできない原因（の一つ）

## 背景
以下のようなことが起きました

- 古いバージョンのCDKでリソースをデプロイしようとした（新規スタック作成・既存スタック更新）
    - CDKの中でLambdaリソースを定義している
    - そのLambdaのランタイムはまだサポートされている
- にもかかわらず、「Lambdaのランタイムがサポート外」というエラーでデプロイできなかった

### 環境

- Node.js: `12.22.2`
- CDK: `1.119.0`

### 実際のログ

```io
5:41:20 AM | CREATE_FAILED        | AWS::Lambda::Function       | LogRetentionaae0aa...b201efdd8aFD4BFC8A
Resource handler returned message: "The runtime parameter of nodejs14.x is no longer supported for creating or updating AWS Lambda functions. We recommend you use the new runtime (no
dejs20.x) while creating or updating functions. (Service: Lambda, Status Code: 400, Request ID: （略）)" (RequestToken: （略）, HandlerErrorCode: InvalidRequest)

        new LogRetentionFunction (/home/ec2-user/environment/proj/node_modules/@aws-cdk/aws-logs/lib/log-retention.ts:122:22)
        \_ LogRetention.ensureSingletonLogRetentionFunction (/home/ec2-user/environment/proj/node_modules/@aws-cdk/aws-logs/lib/log-retention.ts:89:12)
        \_ new LogRetention (/home/ec2-user/environment/proj/node_modules/@aws-cdk/aws-logs/lib/log-retention.ts:48:27)
        \_ new Function 

（以下略）
```

## 原因（結論）

実際のログをよく読めばわかるのですが、「Lambdaのランタイムがサポート外」というエラーは、LogRetention作成時に起きています。 **自分で定義しているLambdaのランタイムバージョンは関係ないということです。**
詳しくは以下の通りです。

- CDKでのLambda定義にて `logRetention` 引数でログの保持期間を設定していた
- ログの保持期間を設定すると、ログローテートLambdaがCDK内部で自動作成される
- 上記バージョンのCDKでは、ログローテートLambdaのRuntimeバージョン指定をベタがきで `nodejs14.x` 指定していた

### CDKパッケージ内の原因箇所のリンク

https://github.com/aws/aws-cdk/blob/v1.119.0/packages/%40aws-cdk/aws-logs/lib/log-retention.ts#L161

## 解決方法

### 元のソースコードを全く変えずにデプロイする方法？

**ない**と思われます。StackOverflowやGitHub Issuesをある程度漁りましたが、大体結論は以下の解決法１：CDKバージョンを上げる　でした。

試験的に、ログローテートLambdaのRuntime指定部分を変えてみましたが、デプロイ処理が止まってしまいました。（「原因」を力づくで解決しようとすると、パッケージに改変を行うことになりますので、この方向での解決はやめましょう。）

### 解決法１：CDKバージョンを上げる

記法が変わるなど影響はあるかもしれませんが、これしかないかと思います。
また、CDKバージョンを上げる際、ほぼ確実に環境のNode.jsのバージョンも上げることになると思います。
今回私はこちらの方法を選択することになりました。

### 解決法２ (新規スタック作成時のみ可)

ログローテートLambdaを自動作成しない方法があるようです。この解決法は既存スタック更新には利用できないようです。CDK内部でバージョン固定のログローテートLambdaが作られないのは良いですね。

簡単な手順は以下の通りです。

1. 事前にロググループリソースを自前で定義する
2. そのロググループにログ保持期間を設定
3. そのロググループに、対象Lambdaを紐づける

（"logRetention lambda runtime" などで調べていたら出てきた記事で、私が実際には試したわけではありません）。

## 参考

- [Lambda ランタイム - AWS Lambda](https://docs.aws.amazon.com/ja_jp/lambda/latest/dg/lambda-runtimes.html)
- [How to Define a Log Retention in Lambda Using CDK Without Creating a Custom Resource — It can help you save 💲Money💲 | by Sahil Gupta | Medium](https://sahilg98.medium.com/how-to-define-a-log-retention-in-lambda-using-cdk-without-creating-a-custom-resource-it-can-help-3d83e7b7cf0a)
マンデルブロの指数変化に伴う面積の変化

過去に投稿した記事から時を経て、Processingの複素数ライブラリを得たので、指数部を小数にした場合の面積検証。

https://qiita.com/hextomino/items/6929f021db492c4cfeeb

https://qiita.com/hextomino/items/47a5a4207df0522df770

マンデルブロの指数変化に伴う面積
```math
z_{n+1}=z_n^2+c
```
上記式について、指数部2を変化させたときの、発散する面積について、その変化を追ってみた。
なお、解像度と繰り返し回数は高くないため、精度は高くありません。
2から増やしていった場合の極限は3.14...多分。
2から減らしていった場合も掲載。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510257/38cbefe4-871d-e935-a3ad-40bdc11c3f74.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510257/38b9b9d7-cd46-0cc9-5bf0-058e572b79fc.png)

```java:processing
import complexnumbers.*;

double d=2.0d;
int count=0;

void setup() {
  size(256, 256);
}

void draw() {
  loadPixels();
  int py=0;
  for (double y = -1.75d; y < 1.75d; y += 3.5d/256.0d)
  {
    int px=0;
    for (double x = -2.0d; x < 1.5d; x += 3.5d/256.0d)
    {
      Complex z = new Complex(0, 0);
      Complex c = new Complex(x, y);
      for (int n = 0; n < 256; n++)
      {
        //z = z.pow(f);
        z = myPow(z, d);
        z = z.add(c);

        if (z.abs() > 2)
        {
          pixels[(int)(py*width+px)] = color(n*10%256);
          n=0;
          break;
        }
        if (n == 255)
        {
          pixels[(int)(py*width+px)] = color(0, 0, 255);
          count++;
        }
      }
      px++;
    }
    py++;
  }
  updatePixels();
  text(str((float)d), 5, 10);
  text(str(count),5,25);
  double area=3.5*3.5*(count/65536d);
  text(str((float)area),5,40); 
  println(d+","+count+","+area);
  //saveFrame("mandelFP#####.png");
  d+=0.1000000000000000000000000d;
  count=0;
}

Complex myPow(Complex c, double n) {
  double r = c.abs();
  double th = c.arg();
  double prn = Math.pow(r, n);
  double re = prn*(Math.cos(n*th));
  double im = prn*(Math.sin(n*th));
  return new Complex(re, im);
}

```
指数部1000000で3.1417542
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510257/bb6e3a8f-8667-a29b-0485-cd21cf80cac7.png)
ChatGPT4が調子悪い時の解決方法 - クッキーとストレージのクリアで復旧



こんにちは、あきらパパです。

最近、ChatGPT4を使っていて「メッセージが送れない」といった不具合に遭遇する方が多いようです。私自身も同じ問題が起きていたので、今回はその解決方法を紹介したいと思います。

動画で撮影もしてみました。画面見ながらの方が分かりやすい方はこちらどうぞ。

https://www.youtube.com/watch?v=euoMRecGKp4

## 解決手順

問題の原因は、ChatGPTのバックエンドのバージョンアップによって、ブラウザ上に保存されているユーザー情報やステータスにズレが生じているためです。そこで以下の3つのデータを削除することで、不具合を解消できます。

1. クッキー（Cookies）の削除
2. セッションストレージ（Session Storage）の削除 
3. ローカルストレージ（Local Storage）の削除

### 削除方法

1. ChatGPTのページで右クリック→「検証ツール」を開く
2. 「Application」タブに移動
3. 「Local Storage」「Session Storage」「Cookies」の各項目で、`https://chat.openai.com`を右クリック→「クリア」で中のデータを削除
4. ページをリロードし、再度ログイン

これで、新しいストレージのデータが作成され、メッセージが正常に送信できるようになります。

### 日本語設定

ログイン後、言語が英語になっている場合は以下の手順で日本語に変更しましょう。

1. 設定（Settings）を開く
2. LANGUAGE から日本語を選択
3. 変更を保存

再びチャットを開始すると、日本語での会話が可能になります。

## まとめ

ChatGPT4の調子が悪く、メッセージ送信できない不具合が起きた際は、クッキーとストレージのクリアを試してみてください。私自身もこの方法で問題を解決できました。 

同じような問題で困っている方の参考になれば幸いです。

以上、『ChatGPT4が調子悪い時の解決方法』でした。
Pardot のメールで動画を使用する

[Pardotの質問と回答をまとめに戻る](https://qiita.com/Keiji_otsubo/items/a3ccd1370697dd914723)


+ [【ヘルプ】Pardot のメールで動画を使用する](https://help.salesforce.com/s/articleView?id=000383670&type=1)

動画とメールは相性がいいわけではありません。どのメールクライアントにも、再生可能なフォーマットに制限があります。Campaign Monitor による広範なテストでは、Apple Mail 以外では、動画を確実に埋め込むことはできないという結果が示されています。壊れたメールを送信したり、スパムとして報告されたりせずにプロスペクトに動画を届けるには、代わりの方法がいくつかあります。

注意: メールに動画を追加してすべてのメールクライアントで動作することの確認は、Pardot サポートの範囲に含まれていません。

+ [How To Utilize Videos In Your Account Engagement (Pardot) Emails](https://saleslabx.com/resources/how-to-utilize-videos-in-your-pardot-emails/)

# 質問

+ [Embedding videos in Pardot Lightning Email Builder](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007BHjaqSAD)

:::note
You may give a try to this [Custom Component](https://appexchange.salesforce.com/appxListingDetail?listingId=a0N3u00000PH4IoEAL) by Salesforce Labs, which does the job of embedding video. That being said, as it pertains to Videos in Email, they always redirect to Youtube or Vimeo and do not play directly in the email since most email clients would not support it.
:::

Video for Emailというアプリを使えとあるなぁ。

+ [Embedding videos in Account Engagement Lightning Email Builder](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007agbb7SAA)

It seems like the custom component "Video for Emails" By Salesforce Labs for embedding videos on the Account Engagement Lightning Email Builder isn't functioning properly anymore.


Account Engagement Lightning Email Builder にビデオを埋め込むための Salesforce Labs のカスタム コンポーネント「Video for Emails」が正しく機能しなくなったようです。


+ [Embed Video Content into Email](https://trailhead.salesforce.com/es/trailblazer-community/feed/0D54S00000PeT9xSAF)
SpringBoot ビルド時に必要なこと

# @SpringBootApplicationのところを変更する
これを…
```DemoApplication.java
@SpringBootApplication
public class DemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
}
```

こうする
```DemoApplication.java
@SpringBootApplication
public class DemoApplication extends SpringBootServletInitializer {

	public static void main(String[] args) {
		SpringApplication.run(DemoApplication.class, args);
	}
	
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
        return application.sources(DemoApplication.class);
    }

}
```

# warファイルを作成する
pom.xmlの場合は、以下を追加する
```pom.xml
<packaging>war</packaging>
```


# Marvenビルドのゴールには何を入れる？
以下を入れる
```
package
```
targetフォルダをキレイしたいとき
```
clean package
```
テストコードでJSONデータを比較する際の注意点 (XCTest)

## 概要
XCTestでJSONを生成する関数のテストコードを書いていました。
生成したJSONと期待されるJSONを`XCTAssertEqual()`で比較する際は、JSONのkey値の順番を揃える必要があることを学びました。


## 詳細
### つまずいたところ
最初に、生成したJSONと期待されるJSONの**Data型**を`XCTAssertEqual()`で比較した際、テストに通りませんでした。
調べてみたところ、JSONのkey順が等しくない場合`XCTAssertEqual()`に通らないということがわかりました[[1](#参考)]。

### 解決方法
しかし、Dictionary型からJSONに変換する場合や、構造体からJSONに変換する場合、生成されたJSONのkey順は保証されません。
そのため、JSONに変換する際に`.sortedKeys`を利用し、keyの順番を辞書式順序にソートすることで解決できることを学びました[[1, 2](#参考)]。

### Dictionary型からJSONに変換する関数
具体例として、以下の関数でテストコードを書きました。
`options: [.sortedKeys]`でkeyがソートされたJSONに変換されます。

```JsonDataUtility.swift
struct JsonDataUtility {
    func dictionaryToJson(data: [String: Any]) -> Data? {
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: data, options: [.sortedKeys])
            return jsonData
        } catch {
            print("エラー", error)
            return nil
        }
    }
}
```

### テストコード
生成結果と期待される結果はstring型で比較しています。
期待されるJSON文字列`expectedJsonString`は、key値が辞書式順序（アルファベット順）になっています。

```JsonDataUtilityTest.swift
import XCTest
@testable import TestApp

final class JsonDataUtilityTest: XCTestCase {
    
    var util: JsonDataUtility!

    override func setUp() {
        super.setUp()
        util = JsonDataUtility()
    }
    
    func testDictionaryToJson() {
        let testData: [String: Any] = ["name": "John", "age": 30, "isStudent": true]
        
        // 期待されるJSON文字列
        let expectedJsonString = "{\"age\":30,\"isStudent\":true,\"name\":\"John\"}"
        
        // 関数の実行
        guard let jsonData = util.dictionaryToJson(data: testData) else {
            XCTFail("dictionary型からjson型へのconvert失敗")
            return
        }
        
        // JSONデータを文字列に変換
        guard let jsonString = String(data: jsonData, encoding: .utf8) else {
            XCTFail("json型からString型へのconvert失敗")
            return
        }
        
        // Assert: 結果と想定される結果の評価
        XCTAssertEqual(jsonString, expectedJsonString)
    } 
}
```

## 参考
0. [Developer Forums](https://forums.developer.apple.com/forums/thread/735895)
0. [sortedKeys](https://developer.apple.com/documentation/foundation/jsonserialization/writingoptions/2888322-sortedkeys)
Git Github コマンドメモ

# Git学習メモ
GitやGithubを利用する際のコマンドメモ

## Git設定
- Gitの設定をする
  - git config --global user.name "Takaaki Tsunoda"
  - git config --global core.editor "code --wait"
  - git config --global init.defaultBranch main
- 設定の一覧を確認
  - git config --list
- 設定を削除
  - git config --global --unset user.name
- 設定例
  ~/Documents/Work ❯ git config --list
credential.helper=osxkeychain
user.email=takaaki.tsunoda@
user.name=Takaaki Tsunoda
core.hookspath=/usr/local/dd/global_hooks
core.editor=code --wait
init.defaultbranch=main
  ~/Documents/Work ❯

## Gitコマンド
- ローカルリポジトリを作成 *該当のディレクトリ上で作成
  - git init
- ステータス確認git 
  - git status
- ステージング環境に登録
  - git add .
- 差分確認 ワークツリーとステージングエリア
  - git diff
- 差分確認 ステージングエリアとGitディレクトリ
  - git diff --cached
- 認証付きでコミットを実行(GitHub)
  - git commit -S -m "update" 
- コミットを実行
  - git commit
- コミットを実行(1行)
  - git commit -m "コマンドを追記"
- ワークツリーの変更を取り消し、ステージングエリアの状態に戻す
  - git checkout -- README.md
- ステージングエリアへの変更を元に戻す
  - git reset HEAD Git_MEMO.md
- ファイルを削除　*要削除後のcommit
  - git rm remove_me.md
  - git commit -m "remove_me.mdファイルを削除"
- ディレクトリを削除
  - git rm -r subDirectory
- git管理外のファイルを指定
  - .gitignoreファイルを作成し、除外したいファイルやディレクトリを記載
- コミット履歴の確認
  - git log
- コミット履歴の確認 差分付き
  - git log -p

# GitHub学習メモ
- クローン *httpでクローンするとpushできないので注意
  - git clone git@github.com:taka2noda/memo_taka2.git
- リモートリポジトリの設定確認
  - git remote -v
- VSCodeのコマンドライン起動(カレントディレクトリがVS Codeで起動)
  - code .
- ブランチを作成
  - git branch update-taka2noda-cli
- 使用中ブランチ確認
  - git branch
- ブランチに移動
  - git chechout update-taka2noda-cli
- mainブランチとの差分確認
  - git diff main
- main branchのlog確認
  - git log --oneline main
- プルリク作成
  - git push origin main
- GitHub上の変更をローカルリポジトリに取り込む
  - git pull origin main
- ローカルに存在しないブランチを取り込む
  - git fetch origin
  - git checkout fix-branch
- ローカルブランチの削除
  - git branch -d branch-name
- Commit履歴の確認
  - git log
- Commitの修正
  - git rebase -i <コミットハッシュ>
  - git commit --amend -S
- ssh
  - git config --global user.signingkey ~/.ssh/id_rsa.pub
  - git config --global --list
Mac で Google スプレッドシートのシート移動するショートカットは Option + 上下キー

以上。


# チャットメンバー募集


何か質問、悩み事、相談などあればLINEオープンチャットもご利用ください。

https://line.me/ti/g2/eEPltQ6Tzh3pYAZV8JXKZqc7PJ6L0rpm573dcQ


# プロフィール・経歴

https://github.com/YumaInaura/YumaInaura
AWS S3バケット作成

## 前提
AWSのアカウントを作成しておきます
この記事執筆時点での画面案内になります、AWSはよくデザインが変わるのでご注意ください
※AWSのサービス利用は料金が発生しますのでご注意ください

## S3とは？
AWSのS3（Simple Storage Service）は、オンラインストレージを提供するサービスでありデータの保存・配信・保護に使用されます

## バケット作成
・AWSのコンソールにログインします

https://aws.amazon.com/jp/console/

![スクリーンショット 2024-03-12 7.35.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535004/dc3afe68-4512-0829-b153-6dd7a3fafbb4.png)

・`s3`で検索し、サービスから`S3`を選択します
![スクリーンショット 2024-03-12 7.36.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535004/dfb50404-8592-494e-637b-28ac0d7edd88.png)

・S3のページから`バケット作成`を選択します
![スクリーンショット 2024-03-12 7.37.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535004/b45d20be-d9ac-01ca-6176-5d1fd08af478.png)

・バケット作成
![FireShot Capture 006 - S3 バケットを作成 - S3 - Global - s3.console.aws.amazon.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535004/1313e53d-3ef3-353d-9082-6b48cf303081.png)
バケット名を入力します
設定はデフォルトのまま進めます（利用用途に合わせて設定は変更してください）

・バケット画面から`tas9-my-bucket`(作成したバケット)を選択します
![スクリーンショット 2024-03-12 7.50.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535004/cd9b59e9-62bf-59c5-3c5b-623306afb0c7.png)


・`フォルダの作成`を選択します
![スクリーンショット 2024-03-12 7.50.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535004/4dcca5a2-2da7-7646-f4ec-3d37803c61ec.png)

・フォルダの作成
![FireShot Capture 007 - フォルダを作成 - S3 バケット tas9-my-bucket - S3 - Global - s3.console.aws.amazon.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535004/e9eb1224-d21e-78b8-1764-90de1173773f.png)
フォルダ名指定します

・フォルダが追加されました作成したフォルダを選択します
![スクリーンショット 2024-03-12 7.51.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535004/3b1ef5e9-4b3d-cc51-ab45-2267ed5f0cf7.png)

・`アップロード`を選択します
![スクリーンショット 2024-03-12 7.52.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535004/642633cd-1e6e-1be2-fb1f-f417cf958ebd.png)

・アップロードしたいファイルなどをドラックアンドドロップします
![FireShot Capture 008 - オブジェクトをアップロード - S3 バケット tas9-my-bucket - S3 - Global_ - s3.console.aws.amazon.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535004/ad2b3238-ef11-72e1-bd02-9301fbf56776.png)

・アップロードで完了
![スクリーンショット 2024-03-12 7.59.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/535004/29f8449a-651b-5725-1e30-55b5c3368596.png)


## おわり
S3のバケット作成しました、バケットを作成することでファイルをUPできるのでログファイルをS3に置いたりなど色々できます
また、アップロードしたファイルを外部公開することもできますがその場合は公開設定などが必要です
次回PythonでS3のバケットからファイルをDLする処理をやってみます
pythonで複数の.pyファイルからのメイン実行ファイルを探したい

## 前提
ゴールとしてアップロードされたファイルが複数あった場合にメインで実行するファイルを探したい
複数のpythonのファイルから実行対象のファイルを探すスクリプトです
人間が確認する場合は各ファイルを確認したり、ファイル名から辺りをつけたりするのが普通ですが
今回は機会的に各ファイルを確認してメイン実行ファイルを探していく処理を試していきます

## ファイルサンプル
アップロードファイル
```
・main.py
・test1.py
・test2.py
```
今回の例だと「main.py」が実行ファイルとなっており
main.pyで「test1.py, test2.py」をimportしています

例としてこのようなファイルがあったとして
パッとみた時人間ならばとりあえず「main.py」が命名的に実行対象かと思う人が多いと思います
今回はサンプルですがファイル数が増えたり名称が分かりずらい場合は1つ1つのファイルを確認するのも大変になってきます


## 条件
今回はシンプルに大きく2つの条件でメインの実行ファイルを探してみます

①「__main__」の記載があればメインファイルとして扱う
②各ファイルを検索して他のファイル全てがimportされていればメインファイルとして扱う

今回の例ではmain.pyで「test1.py, test2.py」をimportしているので②の条件で検索します

## 実戦

```search.py
import os
import ast
import copy

def find_main_file(directory):
  # ディレクトリ内に存在する「.py」ファイルを一覧で取得
  files = [f for f in os.listdir(directory) if f.endswith('.py')]
  # 検索用の「search.py」は不要なので除外
  files.remove('search.py')
  print("ディレクトリ内の「.py」一覧")
  print(files)
  
  for file_name in files:
    print(file_name + "のファイル中を検索")
    file_path = os.path.join(directory, file_name)
    
    # ファイルの中身から「__main__ 」の記述があるファイルの場合終了
    if has_main_func(file_path):
      return file_name

    # 検索中のファイルに対して 他のファイルがimportされているか
    check_files = copy.deepcopy(files)
    # 検索中のファイルは除外
    check_files.remove(file_name)

    # 各要素から.pyを取り除いた新しいリストを作成
    check_files_extension = [item[:-3] if item.endswith('.py') else item for item in check_files]

    # 検索中のファイルからform importの内容をリストで取得
    # 例:{'re', 'json', 'lxml', 'random', 'csv'}
    dependencies = get_dependencies(file_path)
    print(file_name + "のform importの内容をリストで取得")
    print(dependencies)

    if all(item in dependencies for item in check_files_extension):
      # 全てのファイルがインポートされているのでこれがメインファイル
      return file_name
          
  return None

def get_dependencies(file_path):
  with open(file_path, 'r') as file:
    tree = ast.parse(file.read(), filename=file_path)

  dependencies = set()
  for node in ast.walk(tree):
    if isinstance(node, ast.Import):
      for alias in node.names:
        dependencies.add(alias.name)
    elif isinstance(node, ast.ImportFrom):
      dependencies.add(node.module)
  return dependencies

def has_main_func(file_path):
  with open(file_path, 'r') as file:¥
    content = file.read()
    return 'if __name__ == "__main__":' in content

if __name__ == "__main__":
  # 現在のスクリプトが実行されているディレクトリを取得
  directory_path = os.path.dirname(os.path.abspath(__file__))
  main_file = find_main_file(directory_path)

  if main_file:
    print(f"メインファイルはこちらです is: {main_file}")
  else:
    print("メインファイルが見つかりません")

```

実行した時のログはこちらになります
```
ディレクトリ内の「.py」一覧
['main.py', 'test1.py', 'test2.py']
main.pyのファイル中を検索
main.pyのform importの内容をリストで取得
{'lxml', 'json', 're', 'random', 'test1', 'test2', 'csv'}
メインファイルはこちらです is: main.py
```

処理の内容としては検索対象のディレクトリから「.py」の拡張子のファイルを一覧で取得します(search.pyは検索用のファイルなので一覧からは除外)
ファイル一覧をループして各ファイルの中身を確認します
「__main__」の記載がある場合はそのファイルをメインファイルとして返却
他のファイルがimportされている場合もメインファイルをして返却
とファイル一覧を全て調べて最終的にメインファイルを特定するといった流れになります

## おわり
今回はシンプルな条件での検索にしましたがこれだけではフォローしきれない場合もあるかと思います
フォローしきれない場合は条件付けをもっと細かくするとか、検索でメインファイルが特定できない場合はデフォルトの固定のファイル名称をメインファイルとして扱うなど必要かもしれません
PythonでMySQL接続してデータ取得(mysql-connector-python)

## 前提
ローカル環境でMySQLのusersテーブルからpythonでmysql-connectorを使いDBに接続し
usersテーブルのデータを取得して表示する簡単な処理を試してみます


## 事前準備
今回のテスト用にDB「python_test」を用意しておきます
「users」テーブルを用意しておき「id, name, email」のカラムを用意しておき
サンプル用に適当にレコード入れておきます
```sql
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| python_test        |
| sys                |
+--------------------+

mysql> use python_test;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A

Database changed
mysql> show tables;
+-----------------------+
| Tables_in_python_test |
+-----------------------+
| schema_migrations     |
| users                 |
+-----------------------+

mysql> SELECT * FROM users;
+----+-------+----------------+----------+---------------------+---------------------+
| id | name  | email          | password | inserted_at         | updated_at          |
+----+-------+----------------+----------+---------------------+---------------------+
|  1 | user1 | test1@mail.com | 1234pass | 2024-03-09 23:00:52 | 2024-03-09 23:00:52 |
|  2 | user2 | test2@mail.com | 1234pass | 2024-03-09 23:03:00 | 2024-03-09 23:03:00 |
+----+-------+----------------+----------+---------------------+---------------------+
```

こちらの記事を参考にusersテーブル(「id, name, email」のカラム)を用意しておいてください

https://qiita.com/Tasuku-Q/items/635e13c3b66c828807d5



## 実戦
```main.py
import os
import mysql.connector

# DBへ接続
conn = mysql.connector.connect(
  user='root',
  password='',
  host='localhost',
  database='python_test'
)

# DBの接続確認
if not conn.is_connected():
  raise Exception("MySQLサーバへの接続に失敗しました")

cur = conn.cursor(dictionary=True)

# クエリ作成
query__for_fetching = """
SELECT id, name, email
FROM users
ORDER BY users.id
;
"""

cur.execute(query__for_fetching)

for fetched_line in cur.fetchall():
  id = fetched_line['id']
  name = fetched_line['name']
  print(f'{id}: {name}')
```

実行結果サンプル
```
1: user1
2: user2
```

## おわり
思っていたより簡単に接続できました
今回のサンプルでは取得したデータの表示だけですが、mysql-connector使えばDBの操作ができるので用途によってはテーブル作成/削除・レコード追加など色々と活用できそうです
クエリが実行できるのでDB扱う上で困ることはなさそうです
今回はローカル環境でのテストでしたが、RDSでDBを用意してLambdaでDBからデータ取得して処理するみたいことができそうなので色々と便利に使えそうです
MySQL 基本入門

## 前提

MySQLのインストールが必要(まだの人はこちらから)

https://qiita.com/Tasuku-Q/items/16a0b0d0107c77ab4a90


## MySQLとは？
MySQLは多くのウェブアプリケーションやウェブサイトで使用されるデータベース管理システムです、データを保存/操作/クエリするためのソフトウェアです
MySQLはオープンソースであり無料で利用できるため多くの開発者や企業によって広く採用されています

## MySQLの特徴
・オープンソース
MySQLはオープンソースであり、誰でも無料で利用できます

・高速かつ効率的
MySQLは高速で効率的なデータベース管理システムであり、大規模なデータベースでも高パフォーマンスを提供します

・柔軟性
MySQLはさまざまなプラットフォームで動作し、多くのプログラミング言語との統合が容易です

・セキュリティ
MySQLは強力なセキュリティ機能を備えており、データの保護とアクセス制御を確保します


## MySQLの基本操作

### MySQLに接続する
ターミナルから下記コマンドでMySQLに接続します
```zsh
% mysql -u root -p
```
-u の後ろは`ユーザー名`を指定します、今回は`root`ユーザーを指定します
※初期設定ではrootのパスワードはありません


### データベースの一覧表示
全てのデータベースを表示します
```sql
mysql> SHOW DATABASES;
```

### データベースの作成
データベースを作成します
```sql
mysql> CREATE DATABASE databasename;
```
`databasename`は作成するデータベースの名前を指定します

### データベースの選択
アクセスしたいデータベースを指定します
```sql
mysql> USE databasename;
```
※このデータベースの選択をしていないと次以降の記事のコマンド系は動きません

### テーブルの一覧表示
選択したデータベース内に存在する全てのテーブルを表示します
```sql
mysql> SHOW TABLES;
```

### テーブルの作成
データを格納するためにはテーブルを作成します、CREATE文を使用します
```sql
mysql> CREATE TABLE users (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50),
    email VARCHAR(100)
);
```

### データの挿入
データをテーブルに挿入します、INSERT文を使用します
```sql
mysql> INSERT INTO users (username, email) VALUES ('takasi', 'takasi@example.com');
```

### データの検索
データを検索します、SELECT文を使用します
```sql
mysql> SELECT * FROM users;
```

### データの更新
データを更新します、UPDATE文を使用します
```sql
mysql> UPDATE users SET email='new_email@example.com' WHERE username='takasi';
```

### データの削除
データを更新します、DELETE文を使用します
```sql
mysql> DELETE FROM users WHERE username='takasi';
```

### テーブルの削除 
テーブルを削除します、DROP文を使用します
```sql
mysql> DROP TABLE users;
```

### データベースの削除
データベースを削除します、DROP文を使用します
```sql
mysql> DROP DATABASE databasename;
```

## おわり
MySQLの基本的な概念と操作方法について説明しました
今回のようにMySQLを直接操作することもあればアプリケーション経由でMySQLを扱うことがあるとおもいます、基本的な操作は同じなのでしっかり基礎を押さえておけば大丈夫です
2024/3/12主にITとかセキュリティの記事

Pythonベースの情報窃取マルウェア、Facebookメッセージで拡散(3/10)
https://news.mynavi.jp/techplus/article/20240310-2901813/

セキュリティ業界に物申す！”武闘派 セキュリティYouTuber”アクトの山本による最新動画【NTT西日本の「3000万件」個人情報漏洩を斬る！！】を公開(3/10)
https://prtimes.jp/main/html/rd/p/000000103.000076503.html

「世界は中国の偽情報戦に備えていない」…米大統領選の勝敗も左右＝アナリスト(3/10)
https://www.epochtimes.jp/2024/03/209640.html

高齢者狙うオンライン詐欺トップ10、高齢化はサイバー攻撃にも好都合(3/10)
https://news.mynavi.jp/techplus/article/20240310-2901827/

偽のSkype・Google Meet・Zoomサイトからトロイの木馬が配布中、注意を(3/10)
https://news.mynavi.jp/techplus/article/20240310-2901818/

GoLangとMailgunで実装する自動メール送信機能(3/10)
https://qiita.com/rakibw/items/3d641b04adbd2d64a4b6

「エロ」ミームが大流行、ポルノ系ボットでXが埋め尽くされる理由　米(3/9)
https://news.mynavi.jp/article/20240309-2902524/

DX進める中外製薬、デジタル起点に「石橋をたたいて渡らない」組織から脱却(3/11)
https://xtech.nikkei.com/atcl/nxt/column/18/02772/030700002/
Phoenix LiveViewの挙動がおかしくなった話

# 概要
Phoenixでアンケートフォーム作成アプリを作ろうとした際、自動生成したLiveViewのページで要素の作成が出来なくなることがあったので、そのことについて書きます。

# 環境
OS:Ubuntu22.04LTS(WSL)
Elixir version:1.16.1-otp-26
Phoenix Framework version:1.7.11

# エラー内容
新しく作成したPhoenixプロジェクトで、最初にフォーム作成のためのページを作ろうと以下のようなコマンドでページを生成しました。
```bash
$ mix phx.gen.live Forms Form forms title:string type:integer deadline:naive_datetime
```
生成したページで試しにフォームを作成しようとすると、何らかの項目を入力したとたんに画面がリロードされてしまう事態が発生しました。

# 解決
プロジェクトの再構築やフォームの内容の変更を何度か試しましたがフォームの作成がうまくいかず、原因を探っているとPhoenixの関数名などでformという単語が多く使われていることに気づき、試しにテーブル名やリソース名などを変更して以下のような生成コマンドで新しくLiveViewページを作成しました。
```bash
$ mix phx.gen.live Questions Question questions title:string type:integer deadline:naive_datetime
```
生成したページでフォーム作成を試してみると、今度はすんなりと成功しました。


# 結論
Phoenixフレームワークでリソース名やテーブル名にFormという名称を使うと、挙動がおかしくなることが分かりました。
Rails 7 と Vite: Uncaught TypeError 解決策

最近、Rails 7 アプリケーションで Vite を使用して開発を進めている中で、特定のエラーに直面しました。そして、その解決策を共有したいと思います。

## 問題

開発中、以下のようなエラーがブラウザのコンソールに表示されました。

```
An import map is added after module script load was triggered.
localhost/:1 Uncaught TypeError: Failed to resolve module specifier "application". Relative references must start with either "/", "./", or "../".
```

このエラーは、JavaScript モジュールのインポートが適切に解決されないことに関連しています。

## 暫定的な解決策

エラーの原因を特定し解決する過程で、`app/views/layouts/application.html.erb` ファイル内の以下の行をコメントアウトしたところ、エラーメッセージが消えました。

```erb
<%#= javascript_importmap_tags %>
```

この変更により、Vite が正しく機能し、エラーが解消されました。しかし、この方法は Importmap を使用している場合には適用できません。

## Importmap を使用する場合の注意点

Vite と Importmap の併用については、今回のエラーを解決する明確な方法を見つけることができませんでした。Importmap を使用する場合、同様のエラーに直面する可能性があり、この記事の執筆時点ではまだ解決策を見つけていません。

## まとめ

Rails 7 で Vite を使用して開発を行う場合、特定の設定が原因でエラーが発生することがあります。今回は、`javascript_importmap_tags` のコメントアウトにより一時的な解決策を見つけましたが、Importmap を使用する場合の解決策については、さらなる調査が必要です。

開発中に同じエラーに直面した方がいれば、この情報が何らかの参考になれば幸いです。また、Importmap を使用する場合の解決策をご存じの方がいれば、情報の共有をお願いします。

## 補記
以下にコードを記載します。
```app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>VueRails</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%#= javascript_importmap_tags %>
    <%= vite_client_tag %>
    <%#= vite_javascript_tag 'application' %>
    <%= vite_javascript_tag 'entrypoints/application' %>

  </head>

  <body>
    <%= yield %>
  </body>
</html>
```

```app/javascript/application.js
https://github.com/rails/importmap-rails
// viteつかうならこのファイルは不要な気がする
import "@hotwired/turbo-rails"
import "controllers/application"
```

```app/javascript/entrypoints/application.js
console.log('Vite ⚡️ Rails')
console.log('Visit the guide for more information: ', 'https://vite-ruby.netlify.app/guide/rails')
import 'vue2-animate/dist/vue2-animate.min.css';
import { createApp } from 'vue';
import ExampleComponent from '../components/ExampleComponent.vue';

const app = createApp(ExampleComponent);
app.mount('#app');
```

```app/javascript/controllers/application.js
import { Application } from "@hotwired/stimulus"

const application = Application.start()

application.debug = false
window.Stimulus   = application

export { application }
```

```vite.config.js
import { defineConfig } from 'vite';
import RubyPlugin from 'vite-plugin-ruby';
import vue from '@vitejs/plugin-vue';

export default defineConfig({
  plugins: [
    RubyPlugin(),
    vue(),
  ],
});

```
コード開発ができるにシステムエンジニアに

# 自己紹介
ITスタートアップでシステムエンジニアとして働いている30歳。
普段は福岡の自宅からフルリモートで働いてます。
タイトルに書いている通り「コード開発ができるシステムエンジニア」になるべく、まずはReactの学習を始めます。
# 現状
Salesforce・YextといったSaaSシステムをクライアントの業務に合わせてカスタイズするのが主な仕事で、システムエンジニアという肩書きだがコードでの開発はほとんど発生しない（＝正確にはコーディングスキルがほとんどないためアサインされないだけ笑）ため、いわゆるプログラミング言語でコーディングスキルがほとんどない。
# 背景
社内でコード開発が伴うプロジェクトは発生するものの、社外のSEに外注することも多く、会社の利益減＋自分自身の経験が積めない状態になっている。
会社の利益を増やす＋自分のスキルをアップさせたい→Reactの学習を決意。
# 現在のスキル
- HTML,CSSはある程度読めるし書ける
- jQueryが使える程度のJavscriptは何となく分かる
- だが、いわゆるプログラミング言語は書けない
# 今後の活動
今日からQiitaで発信しながら、Reactについて学習し、コード開発ができるエンジニアを目指す
# 目標
- 短期：Reactの基礎・基本を身につける
- 中期：Reactを使ったサービスを作成できるようになる
- 長期：React以外の言語も使いこなせるようになる
flutter 非同期処理をほぼ同期的に画面に反映する方法

#### 筆者はまだまだflutter学習中のため間違った情報が記載されている可能性もあります。予めご了承ください。間違い、改善点等あればお手柔らかに教えていただけるとありがたいです。

### この記事で得られる知識
Futureなどの非同期処理後に、リビルドされる前にその結果を取得し画面に反映する方法。なお、この記事のコードはサンプルのためそのままでは動きません。

### 対策1 Flutter Hooksを使用して変更の完了を通知して、データを再取得させるようにする
Flutter HooksのuseEffectの第２引数に変更を知らせる変数を渡しておき、再取得させたいタイミングで通知させます。通知を受け取ると再度データを取得してくれます。

```dart
final sampleChange = ref.watch(sampleChangeProvider)
useEffect(
    () {
        newData = repository.getData();
        return null;
    },
      [sampleChange],
    );
```

```dart
class SampleChangeNotifier extends StateNotifier<bool> {
  SampleChangeNotifier() : super(false);
  void notifyChange() {
    state = !state;
  }
}
final sampleChangeProvider = StateNotifierProvider<SampleChangeNotifier, bool>(
  (ref) => SampleChangeNotifier(),
);
```

### 対策2 RiverpodのonDispose関数を利用する
データの変更が終了すれば、dbProviderを一旦破棄します。すると、dbProviderを監視していたproviderも更新され、データを再度取得できるという方法です。

```dart
final sampleProvider =          //データ取得のためのprovider
    FutureProvider.family.autoDispose<bool, DateTime>((ref, date) async {
  final datebase = ref.watch(dbProvider);  //ここでdatabaseの変更を監視している
  final response = await datebase.getData();
  return response;
});
```

```dart
ref.invalidate(dbProvider); //データを変更した処理のあとにdbProviderを一旦破棄
```

```dart
final dbProvider = Provider<SampleDatabase>(
  (ref) {
    final database = SampleDatabase();
    ref.onDispose(() async {  //onDispose関数を使ってref.invalidateされた際の処理を記載
      await database.close(); //ここでdatabaseのインスタンスをcloseしておかないとメモリーリークにとなることがある
    });
    return database;
  },
);
```

### 最後に
いかがだったでしょうか。この記事が誰かの役に立てば嬉しいです。ちなみに筆者は、databaseのインスタンスをcloseしてなくて「LateInitializationError」となるエラーに苦しめられました(笑)
そのため、機能を実現する別の方法を探して(対策１)、ヒントをもらって再度取り組んでみた（対策２）というのが今回の記事の背景です。読んでいただきありがとうございました!
###
Docker+jupyter+vscode(.devcontainer) 環境構築

# 背景
.devcontainerを使えば、vscodeでcontainerに入って作業がしやすい話を聞き、環境構築をした。
vscodeでcontainerに入って作業する方法は大きく2つ。
１．wslのシェル上で以下を実行
```
docker run -it -d <image ID>
```

vscodeで「F1→Dev Containers:Attach to running containers」を実行。

２．.devcontainersでいい感じにローカルにmountしてくれたり、containerつくってくれる。

# 内容　.devcontainerの構成
今回はこれがメイン。私が現在使ってる環境は
```
p_env--.devcontainer--devcontainers.json
                     |-Dockerfile
                     |-requirement.txt
```

具体的にファイルの中身を見ていく。
```:devcontainer.json
{
    "name": "pyenv",
    "image": "mcr.microsoft.com/devcontainers/python:1-3.11-bullseye",
    "build": {
      "dockerfile": "Dockerfile"
    },
    "customizations": {
      "vscode": {
        "extensions": [
          "ms-python.python",
          "njpwerner.autodocstring",
          "ms-toolsai.jupyter",
          "ms-azuretools.vscode-docker"
        ]
      }
    }
  }
```
上記ファイルの書く際に少し止まったところが２点。
・image
containerを立ち上げる際にpip installerがなかったので色々調べたところ"mcr.microsoft.com/devcontainers/python:1-3.11-bullseye"のようにmicrosoftが出してるイメージファイルはpipが入ってるらしく書き換えた。

・customizations
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3661566/2dbda5fd-03b8-60c6-3c42-c248fdca3c10.png)
extensionIDでcopyして、customizationsで貼り付け。
これでdocker run したときにいちいちinstallしなくて済む。

```:Dockerfile
FROM python:3.11
USER root

RUN apt-get update
RUN apt-get -y install locales && \
    localedef -f UTF-8 -i ja_JP ja_JP.UTF-8
ENV LANG ja_JP.UTF-8
ENV LANGUAGE ja_JP:ja
ENV LC_ALL ja_JP.UTF-8
ENV TZ JST-9
ENV TERM xterm

COPY requirements.txt /root/

RUN apt-get install -y vim less
RUN pip install --upgrade pip
RUN pip install --upgrade setuptools
RUN pip install -r /root/requirements.txt
```
これに関して特に気を付けたことはなく、ネット上におちてるものをコピーしてきた。最後の行でrequirement.txtをインストールしてくるところが機能としてほしかったところ。
以下がその中身。
```:requirement.txt
jupyter
psycopg2
numpy
pandas
plotly
```
これでdocker runの際にinstallされて立ち上がる。
【JTC向け?】Teams/Zoomで社内Vtuberになる方法

# なんの記事？
タイトルの通り。
遊びでやってみたが、ゴールにたどり着くまでに以外とまとまった情報がなかった。
&(社内LTで類似の内容のものがあった)ので、以外とネタになりそうなので書いてみる。

# 構成
#### 1. WebcamMotionCapture
通常のWebカメラから骨格検出＆モデルのモーションを生成
#### 2. OBS Studio
他のWeb会議ツールから読ませるための仮想カメラを生成。WebCamMotionCaptureのウィンドウをカメラにする
#### 3. VroidStudio
WebCamMotionCaptureに読み込ませるVRMファイル(Vtuberのアバター)を作成
あくまで、オプション的な立ち位置。[Booth](https://booth.pm/ja/browse/3D%E3%83%A2%E3%83%87%E3%83%AB?q=VRM) で購入とかでいいと思う。WebCamMotionCaptureのデフォルトの女の子もかわいい

## 動作環境
- ハードウェア: Surface Pro 8
- OS: Windows 11
今回は上記構成だが、MacOSでも問題なく動作する。
LinuxはWebCamMotionCaptureが非対応。
OBSはUbuntuサポートしているので、アバターの動作画面を別デバイスから転送できれば行けると思う。

# 手順
## 1. WebcamMotionCapture
https://youtu.be/M3HujhnF76M
- 下記公式リンクからソフトウェアのzipファイルをダウンロードして、展開、exeを実行する
https://webcammotioncapture.info/ja/index.php#download
― exe起動後、WebCam Motion ReceiverというウィンドウとWebCam Motion Captureというウィンドウが開く
- WebCam MotionRecieverはモデル設定とアバターの実動作を行う
- WebCam Motion Captureは外部カメラから動画の取り込み、骨格検出を行う
- WebCam Motion Captureでカメラを設定すれば、秒でおじさんが3Dモデル美少女になる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573632/c43cef87-25b3-543a-bab3-3c5d82dc2fdc.png)

## OBS
- いわずと知れた配信用OSS。本家のVtuberさんとかも使ってると思う。  
- 下記よりインストール用のexeをダウンロードする  
https://obsproject.com/ja
- インストール用exeを叩いて、インストールを行う。  
- 画面下部の「ソース」を右クリックして、「追加」→ 「ウィンドウキャプチャ」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573632/cfef0aa6-93a3-4562-9400-11ff61b7701d.png)
- 新規作成を選択して、OKを押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573632/e4309de6-15e5-7391-56b3-31885a2f7e75.png)
- 「ウィンドウ」タブででWebCam Motion Receiver を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573632/50633503-c3c2-dabc-07d4-d924a7839260.png)
- 「コントロール」タブで「仮想カメラ開始」を押下すると仮想カメラが生成される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573632/711b321c-d68d-dd24-3896-1ffe6819a4ad.png)
- うまく起動できれば、Teams/Zoom/GoogleMeet/Chimeにカメラとしてアバターが表示される
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573632/3cf69c4b-16a0-ec17-7d8f-a396e40826d7.png)

## VroidStudio作成モデルのWebCamMotionCaptureへの連携(オプション)
- 操作方法、モデルの作成方法は下記の動画やQiita記事にたくさんあると思うので、本稿では記載しない
https://www.youtube.com/watch?v=H0o5vOabAyQ
https://www.udemy.com/course/vroidstudio/?couponCode=ST15MT31224
- モデル作成後、VroidStudioからVRMでの出力を行う。  
- WebCamMotionCaptureの右上の「VRMを開く」を押下して、VRMファイルを選択する。  
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573632/e736a6a8-cbed-a5f9-8edb-7be7f997c6bc.png)
- この時、読み込んだVRMファイルをはじめとした、モデルの動作設定は保存されないので、次回WebCamMotionCaptureをひらいた際には、初期設定に戻ってしまう
- このため、画面下部の「保存」ボタンを押下が必要
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573632/46addff6-51d1-b6d6-2371-7d4350c67dff.png)
 - 次回WebCamMotionCaptureを開いた際は、「読み込む」ボタンを押下すると前回設定を開ける

## WebCamMotionCaptureの課金について(オプション)
- WebCamMotionCaptureはフルで利用するためには月額199円の課金が必要。メールアドレス登録+Paypalなどで課金可能。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/573632/7819b7dc-6e2e-5f92-e6c2-2de08812f44b.png)
Python3エンジニア認定基礎試験合格体験記

# 初めに
まず、Python3エンジニア認定基礎試験とはこのような試験です。

https://www.pythonic-exam.com/exam/basic

40問中70%の **28問正解** で合格となります。

私は、2022年8月に初受験し、57.5%の23問正解で不合格となってしまいました。
そして、2023年1月に再受験し、75%の30問正解で合格することができました。

そんな私が、不合格の理由と、合格するために役立ったことを自分なりに考えてみたので、これから受験を考えている方のお役に立てれば幸いです。

# 初受験までにやったこと
私は当時プログラミング教室に通い始めて2年程で、以下の本を使ってPythonの勉強をしていました。

https://gihyo.jp/book/2018/978-4-7741-9763-0

https://www.shoeisha.co.jp/book/detail/9784798163642

プログラミング、そしてPythonを学ぶのに非常に役立ちました。

しかし、インターネット上の模試を解きましたが、正答率はいまいちでした。
もっと「試験」に特化した勉強を集中して行った方がいいのかなと感じました。

# 再受験までにやったこと
### その１
この試験には **主教材** という、「試験問題はこの本から出題されますよ」という本があります。以下の本です。

https://www.oreilly.co.jp/books/9784873119359/

この本を読み、少しでもわからない所があれば本やインターネットで調べて理解を深めました。また、数日おきに読み返して覚えていなかったところは手書きでメモを取る→そのメモは毎日見て覚える を繰り返しました。

### その２
初受験時にも解いていた模試ですが、種類と解く回数を大幅に増やしました。週に何回ということではなく、合格点が取れるようになるまでやりました。
利用していたサイトを以下に載せておきます。
* https://study.prime-strategy.co.jp/
* https://python-basic.com

## 他の本でPython勉強したけど、主教材って読んだ方がいいの？
結論から申しあげると、試験に合格することを最優先にお考えの方はこの本を読むことをお勧めします。

試験合格のためには**試験に出るポイント**を掴むということが大事だからです。

私は一回目の試験が不合格になり、この本を読んでから模試の点数が伸び始めました。

# 最後に
私は当時プログラミング自体初めてだったので、まずはプログラミングを知ることから始め、よりPythonに特化した知識を深めていくやり方が私には合っていたと思います。

しかし、既に他言語を学ばれていてプログラミングに慣れている方であれば主教材を読んで模試を解くのが近道かなと思います。

最後までお読みいただきありがとうございました。
個人的.zshrcメモ

個人的.zshrcファイル

```zsh
PS1="%n:%d %# "
PS1="%F{magenta}%n%f:%F{yellow}%d%f %# "
# lsをls -Gへ

export LSCOLORS=cxfxcxdxbxegedabagacad

alias ll='ls -lGF'
alias ls='ls -GF'
export PATH="$PATH":"$HOME/.pub-cache/bin"

export VOLTA_HOME="$HOME/.volta"
export PATH="$VOLTA_HOME/bin:$PATH"


# git ブランチ名を色付きで表示させるメソッド
function rprompt-git-current-branch {
  local branch_name st branch_status

  if [ ! -e  ".git" ]; then
    # git 管理されていないディレクトリは何も返さない
    return
  fi
  branch_name=`git rev-parse --abbrev-ref HEAD 2> /dev/null`
  st=`git status 2> /dev/null`
  if [[ -n `echo "$st" | grep "^nothing to"` ]]; then
    # 全て commit されてクリーンな状態
    branch_status="%F{green}"
  elif [[ -n `echo "$st" | grep "^Untracked files"` ]]; then
    # git 管理されていないファイルがある状態
    branch_status="%F{red}?"
  elif [[ -n `echo "$st" | grep "^Changes not staged for commit"` ]]; then
    # git add されていないファイルがある状態
    branch_status="%F{red}+"
  elif [[ -n `echo "$st" | grep "^Changes to be committed"` ]]; then
    # git commit されていないファイルがある状態
    branch_status="%F{yellow}!"
  elif [[ -n `echo "$st" | grep "^rebase in progress"` ]]; then
    # コンフリクトが起こった状態
    echo "%F{red}!(no branch)"
    return
  else
    # 上記以外の状態の場合
    branch_status="%F{blue}"
  fi
  # ブランチ名を色付きで表示する
  echo "${branch_status}[$branch_name]"
}
 
# プロンプトが表示されるたびにプロンプト文字列を評価、置換する
setopt prompt_subst
 
# プロンプトの右側にメソッドの結果を表示させる
RPROMPT='`rprompt-git-current-branch`'
export PATH=$PATH:/Users/itsuki/Library/Android/sdk/platform-tools


# Aliases
# git
alias g='git'
alias gst='git status'
alias gd='git diff'
alias gl='git pull'
alias gp='git push'
alias gd='git diff'
alias ga='git add .'
alias gc='git commit'
alias gch='git checkout'
alias gb='git branch'
alias gf='git fetch'

# zsh
alias czsh="code ~/.zshrc"
alias szsh="source ~/.zshrc"
alias zvi='vim ~/.zshrc'

# npm
alias ndev='npm run dev'
alias nstart='npm start'
```
私がQiitaで記事を書き始めた理由と展望をまとめてみる

100%ポエムです。

# 何これ？

おかげ様で、投稿記事が100本を越えました。
100本超えるとポエムを書いていいという自分ルールに基づき、一つの節目として自分が記事を書き始めた理由や今後やりたいことを書き記しておきます。初心忘るべからず。

何となく一問一答形式(?)で書きます。
技術要素は欠片もありません。

# いつから投稿始めたの？

最初にQiita投稿したのは以下の記事でした。

https://qiita.com/isanakamishiro2/items/477fa0df6c5b69c77a1b

[FDUAの第一回 金融データ活用チャレンジ](https://signate.jp/competitions/841)の挑戦記事ですね。
※　[第二回](https://www.fdua.org/blog/996fy6fi23m)は当時仕事が忙しすぎて実質参加を見送りました。。。残念。。。
これが2023年の2月初頭のこと。
ただ、本格的に投稿を開始したのは同年の8月ごろからですね。

# どうして記事を投稿し始めたの？


きっかけは、[Databricksユーザ研究会(JEDAI)](https://jedai.connpass.com/)での@taka_yayoiさんとの会話。
Databricks界隈では皆知ってる「ほぼ毎日Databricksの記事をQiitaで発信する」超人の方なのですが、お話したときに **「仕事ではなく既にライフワーク」** という旨のことをおっしゃっていたのを強く覚えています。

<br>

**これはカッコイイな**、と。

<br>

私もそういうことを言える景観が見てみたいな、と思い投稿を始めました。

# 何をコンセプトに書いてるの？

Databricksのスゴさと生成AI(LLM)の面白さを伝えたいなと思い、その内容を中心に組み立てています。

・・・
いや、嘘ですね。単純に**DatabricksもLLMもやってて楽しー！**という**IQ3**の発想で書いています。おそらくこれからも。

Databricksのプラットフォーム、凄い&面白いんですよ。（小学生並みの感想）
何が凄いかを書き出すと大変なことになりますが、１点あげるとすると、ここまでデータエンジニアリングからAI含めた活用を一元的に利用できるプラットフォームってなかなか無くて、これが仕事でも利用を始めた理由だったりします。（早口）

そしてLLM。特にローカル（オープン）LLMの分野は日々新たなテクノロジが生まれ、そして消えていく黎明期です。
ローカルLLMの事業活用は日本ではまだあまり事例を聞かないのですが、企業内のユースケースにおいて活用領域は多くあると考え、これは自分が楽しいのに加えてより多くの人に知って欲しいと思って書いています。（早口）

# そもそもどういう立場の人なの？

Databricksの記事を書いてはいますが、特にDatabricksの社員だったりパートナー企業の社員というわけではありません。ただの事業会社でデータエンジニアリングをしているだけのユーザです。
だから忖度は特にしてないと思うよ。たぶん。

# 今後は何を書いていくつもり？

最近はDatabricksをただのPython実行環境として書く内容が増えているので、今後はDatabricksの良さをもっと記事にできればと思っています。
あと本業がなんちゃってデータエンジニアなので、そのあたりを書ければとは思うのですが何を書いたらいいのか迷ってます。

# 生成AI関連の記事は減らしていくの？

いやまあ、書くんですけどね。楽しいので。

# 最近の記事、長くね？

もうちょっとシンプルにまとめたいのですが、能力が足りない。。。
あと、なるべくEnd2Endで動くコード載せたいという思惑があって、なおさら長くかつ複雑になりがちです。

もうちょっと軽めな内容を増やしたい（願望）。

# ファインチューニングとか、継続事前学習モデルは作らないの？

ファインチューニングは、個人的に記事を書きづらいのでここではやらないかな。。。

# てか、ローカルLLMよりプロプライエタリのサービスでしょ

LLMにおいては、性能面や何ならコスト面含めてプロプライエタリのサービスを使うのが現状優位だと思います。

しかし、ローカルLLMの発展も早いですし、ユースケースによってはローカルLLMの方が向いていると思うんですよね。データのセキュリティが厳しい環境下での利用とか。
適材適所で使い分けしていくためにもどちらもキャッチアップする必要があり、今のところ記事が少な目（たぶん）な後者を中心に今後も記事を書きたいと思っています。

# Databricksタグ付けてるけど、内容的に詐欺じゃね？

実行環境としてDatabricksを利用しているので詐欺というほどでは・・・。
ただ、Databricksのマネージドサービスと関係ない内容が多いのは事実。。。

# Databricksで好きな機能・サービスは？

Delta Live Tables(DLT)とUnity Catalog。
dbtもいいけど、DLTで宣言的に書くのが好きです。

# Databricksで不満のあるサービスは？

Delta Live Tables + Unity Catalog。
制限の緩和を早く。

# 何かアピールしとくことある？

[Databricksユーザ研究会](https://jedai.connpass.com/)にみんなも入ろう！


あと、発信用にXのアカウントを作りました。https://twitter.com/isanakamishiro2
JetpackComposeで指定した文字列の色を変更する

## はじめに
どうもこんにちは、最近Kotlin始めました。普段はUnityを触っています。
今回はテキストフィールドの一部文字列の色を変更するという処理を書いてみたのでまとめました。

## JetpackComposeで文字列の色を変更
テキストフィールドに入力する文字列が一定文字数を超えた場合、超えている分だけ赤くするコードを書きました。

↓こんな感じ↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2882297/2eb72dde-68fc-bac1-976c-a0e980dc9e40.png)

## VisualTransformationを使用
VisualTransformationとは簡潔に言うと、Input TextFieldの見た目を変更するときに使用するインターフェイスです。[^1]
このインターフェイスを継承したクラスをTextField関数の引数に持たせ、InputFieldの見た目を変更することができます。


## 手順① テキストフィールドの一部配色を変更するクラスを用意
それではVisualTransformationを継承したクラスを作成します。
<b>ドン！</b>
```kotlin
class ColorsTransformation() : VisualTransformation {
    override fun filter(text: AnnotatedString): TransformedText {
        return TransformedText(
            SetOverLimitTextColor(text.toString()),
            OffsetMapping.Identity
        )
    }

    // 文字数制限をオーバーしている文字だけ色を変更する
    fun SetOverLimitTextColor(text: String): AnnotatedString {
        // 文字が指定の文字数より少なければ処理は行わない
        if (text.length < 150) {
            return AnnotatedString(text);
        }

        var first = text.substring(0, 150) // 0 ~ 150のindexまでの文字を抽出
        var last = text.substring(150, text.length) // 150 ~ 最後の文字列を抽出

        var result = buildAnnotatedString {
            append(first)
            withStyle(SpanStyle(color = Red)) {
                append(last)
            }
        }
        return result;
    }
}
```
ここでは、filter関数[^2]をオーバーライドしています。filterは任意のtextの見た目を変更できます。
戻り値のTransformedTextには以下の情報を保持しています。
- Annotating …変換後のテキストのスタイルの情報[^3]
- OffsetMapping…変換後の文字に対するオフセットの設定[^4]

## 手順②TextFieldに実際に適用する
手順①で作ったクラスを呼び出して、テキストフィールドの見た目に適用させるだけ
```kotlin
TextField(
            visualTransformation = ColorsTransformation(),
            
            modifier = Modifier
                    .padding(vertical = 4.dp, horizontal = 4.dp),
            .
            .
            .

        )
```

## おわりに
おお！ここC#と同じ！違う！とかとても新鮮でした

[^1]: [VisualTransformation](https://developer.android.com/reference/kotlin/androidx/compose/ui/text/input/VisualTransformation)
[^2]: [Change the visual output of given text.](https://developer.android.com/reference/kotlin/androidx/compose/ui/text/input/VisualTransformation#filter(androidx.compose.ui.text.AnnotatedString))
[^3]: [The basic data structure of text with multiple styles. ](https://developer.android.com/reference/kotlin/androidx/compose/ui/text/AnnotatedString)
[^4]:[Provides bidirectional offset mapping between original and transformed text.](https://developer.android.com/reference/kotlin/androidx/compose/ui/text/input/OffsetMapping#Identity())

### 参考
- https://developer.android.com/reference/kotlin/androidx/compose/runtime/package-summary
- https://stackoverflow.com/questions/67517762/multiple-colors-in-textfield-of-jetpack-compose
[MV2期限間近] ブラウザ拡張の Manifest V2/V3 をチェックできるツールを作った


## TL;DR
ここからツールをダウンロードしてチェック!
https://github.com/owayo/extensions_manifest_checker

## はじめに
2024年6月、Google Chromeは開発者版（Dev、Canary、Beta）でManifest V2の拡張機能のサポートを終了し、ChromeウェブストアからのManifest V2拡張機能のダウンロードができなくなり、インストールされているManifest V2 拡張機能が自動的に無効化されます。  
安定版(Stableチャンネル)での変更は、最低でも1か月間の様子見の期間を経て少しずつ導入されるとのことです。  
Microsoft Edgeも、Google Chromeのこの方針に従って同じタイミングでManifest V2の拡張機能を使えなくする方向で動いているようです。  

もしManifest V2の拡張機能を使っているなら、Manifest V3に更新した類似の拡張機能への切り替えが必要です。

このツールを使えば、使っている拡張機能がどれだけ影響を受けるかチェックできます。  

**延期が繰り返されていましたが、今回はマジっぽいので注意!!**

参考: [Manifest V2 のサポート タイムライン](https://developer.chrome.com/docs/extensions/develop/migrate/mv2-deprecation-timeline?hl=ja)

## ダウンロード
[GitHub](https://github.com/owayo/extensions_manifest_checker)からダウンロードして下さい。  
macOSの場合は検証エラー出るので、セキュリティ設定から許可してください。(詳細は GitHub の README 参照)

~~Windows版ですがWindows Defender で Trojan:Script/Phonzy.B!ml が誤検出されておりMSに申請中です~~  
~~MSで対応していただき、定義ファイル 1.407.345.0 以降で誤検出されなくなります~~
**定義ファイル配信され、誤判定なくなったことを確認しました**
**VirusTotalではClean判定です**  

## 使い方

起動すると、Chromeのデフォルトディレクトリを読み取り、拡張機能を一覧表示します。  
本アプリでは、リミットを2024年6月+1か月としています。

![スクリーンショット-2024-03-11-23-43-05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3741957/b4268ac1-a3b1-fc48-036b-3f15e5ee53d7.png)

デフォルトのディレクトリ以外を選択する場合は、**EXTENSIONS ディレクトリ選択**ボタンをクリックし、プロファイル配下の**Extensions**ディレクトリを選択してください。

拡張のインストール/アンインストールを行い、**更新**ボタンをクリックすると、変更が反映されます。  

要望等あればコメント下さい。
「DX推進パスポート」の申請ができなかった

2024年1月31日にプレスリリースされた「DX推進パスポート」がどんなものかを知りたくて
ざっくり調べてまとめました。

https://www.ipa.go.jp/pressrelease/2023/press20240131.html

## DX推進パスポートとは
DXを推進するのに必要な基本スキルを持ってますよ、を証明してくれるデジタルバッチみたいです。
その基本スキルと言っているのが、
* ITパスポート試験
* DS検定 リテラシーレベル
* G検定

の3つの試験のこと。

### ITパスポート試験
* ITに関する共通的な基礎知識の証明
* IPA（独立行政法人情報処理推進機構）が運営している

必要勉強時間：150時間前後（基礎知識があれば100時間前後）
合格率　　　：50％前後
受験料　　　：7,500円（税込）

### DS検定 リテラシーレベル
* データサイエンス力・データエンジニアリング力・ビジネス力についてリテラシーレベルの総合的な実務能力と知識の証明
* データサイエンティスト協会が運営している

データサイエンス力・データエンジニアリング力・ビジネス力ってなんなの、と言うと
**データサイエンス力**
基礎数学、基礎統計学、機械学習、データ可視化　など
**データエンジニアリング力**
データ処理、SQL、基礎プログラミング、ITセキュリティ　など
**ビジネス力**
AI・データ人材としての行動規範、論理的思考、AI・データを取り巻く法律・倫理　など

のことみたい。ちょっと面白そう。難易度的にG検定の後に受けた方がいいらしい。

必要勉強時間：50時間前後
合格率　　　：50％前後
受験料　　　：10,000円（税抜）※学生は5,000円

### G検定
* AIの技術的手法や事業活用に必要な知識・能力を体系的に学び、AI・データを活用したビジネスを推進する総合的知識の証明
* 日本ディープラーニング協会が運営している

必要勉強時間：40時間前後
合格率　　　：60％前後
受験料　　　：13,200円（税込）※学生は5,500円

## バッジの発行条件は？
試験3つに合格しなくても、申請すれば発行してもらえるみたいです。
3試験のうち、いずれか1種類の合格者であれば「DX推進パスポート1」
2種類合格で「DX推進パスポート2」、全部合格で「DX推進パスポート3」

https://www.dilite.jp/passport

試験合格1種類からバッジ貰える！申請費用かからないみたいだから申請してみよ～
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3726019/e93fce56-d3e0-6afe-0da4-fd673bc41db0.png)


ダメもとで確認したら2018年12月の試験を受けていたので申請できませんでした。

## さいごに
ITパスポート試験の受け直しはたぶん無いので、「DX推進パスポート3」のバッジは取得できないことが判明しました。
諸々の入門としては、いい勉強になりそうですね。
【Next.js】全てのログをターミナルに出力する方法

## Pinoをインストール

ロガーとしてPinoをインストールします。

https://github.com/pinojs/pino

```terminal
yarn add pino
```

## Pinoの設定ファイルを作成

Pinoの設定をするために`logger.js`を作成します。

```js:logger.js
import pino from 'pino'

const logLevel = process.env.NODE_ENV === 'production' ? 'info' : 'trace'
const frontendOrigin = process.env.NEXT_PUBLIC_FRONTEND_ORIGIN
const logUrl = `${frontendOrigin}/api/log`

export const logger = pino({
  level: logLevel,  // 出力するログレベルを設定。指定したレベル以上のログが出力される
  timestamp: pino.stdTimeFunctions.isoTime,  // タイムスタンプの形式の設定
  browser: {
    // see https://github.com/pinojs/pino/issues/1795
    write: () => {},  // ブラウザのコンソールにログを出力しないようにする
    // ブラウザやミドルウェアのログをサーバーに送信するための設定
    transmit: {
      send: (level, logEvent) => {
        // childを使用する場合にはlogEvent.messagesだけでなく、bindingsもサーバーに送信する必要がある
        const messages = logEvent.messages
        // ミドルウェアではnavigator.sendBeaconは使用できないため、keepalive:true の fetch を使用
        // /api/logにリクエスト
        void fetch(logUrl, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
          },
          body: JSON.stringify({ level, messages }),
          keepalive: true,
        })
      },
    },
  },
})
```

`middleware.js`はEdgeランタイムのみに対応しており、Node.jsランタイムに対応していないため、`middleware.js`でログを記録した場合にも`logger.js`の`browser`の設定が必要です。

https://nextjs.org/docs/app/building-your-application/routing/middleware#runtime

https://nextjs.org/docs/app/building-your-application/rendering/edge-and-nodejs-runtimes

https://nextjs.org/docs/app/api-reference/edge

Pinoの設定については以下をご参照ください。

https://getpino.io/#/docs/api

- browser
 
https://getpino.io/#/docs/browser

[child](https://getpino.io/#/docs/api?id=loggerchildbindings-options-gt-logger)を使用する場合の参考

https://github.com/pinojs/pino/issues/591#issuecomment-480239446

## ログのためのRoute Handlerを実装する

サーバー側でログを記録するためにRoute Handerを以下のように実装します。
`/api/log`にリクエストするとサーバー側でログが記録されます。

```js:app/api/log/route.js
import { logger } from '@/lib/pino/logger'

export async function POST(req) {
  const data = await req.json()

  logger[data.level](...data.messages)

  return new Response('Message has been successfully logged on the server', {
      status: 200,
  })
}

```

## 使用方法

クライアントコンポーネントに以下のように記述します。

```js
'use client'

import { useEffect } from 'react'

export default function Sample() {
  useEffect(() => {
    logger.info('コンポーネントが表示されました。')
  }, [])
  return null
}
```

`Sample`コンポーネントが表示されるとターミナルに以下のようなログが出力されます。

```terminal
{"level":30,"time":"2024-03-11T15:51:47.156Z","pid":13565,"hostname":"39f2edc6b1ea","msg":"コンポーネントが表示されました。"}
```

[pino-pretty](https://github.com/pinojs/pino-pretty)を使用すると以下のように出力されます。

```terminal
[00:54:06.999] INFO (14420): コンポーネントが表示されました。
```

`middleware.js`で`logger`を使用した場合も上記と同じようにターミナルに出力されます。

Pinoの使用方法については以下をご参照ください。

https://getpino.io/#/?id=usage


## 参考

https://github.com/pinojs/pino-nextjs-example