OmniAICreator/Qiita-1.07M huggingface dataset:
SJISからUTF8に変換(GAS)

前職にて、「csvが文字化けしてスプレッドシートで見れない！」と問い合わせが来ました。よくよく話を聞いてみたら、「古いシステムから吐き出されたcsvをGoogle Driveにアップロードし、中身を見たら文字化けしている。」とのことでした。ということで今回はSJISのファイルをUTF8に変換する関数のご紹介です。そもそも古いシステムを新しくした方が良いかもしれませんが、それまでの応急処置にどうぞ！
# コード
こちらの関数に引数としてSJISの文字列を渡せば、UTF-8の文字列が返ってきます。
```javascript
/**
* 受け取ったSJISの文字列をUTF-8の文字列に変換します。
* @param    {String} shiftJISString    SJISの文字列
* @return   {String}                   UTF-8の文字列
*/
function convertBlobsTextEncodingFromSjisToUtf8(shiftJISString) {
  return Utilities.newBlob(shiftJISString).getDataAsString();
}
```
# 使い方
```javascript
function main() {
  const fileId = createFileWithShiftJISEncoding();
  const shiftJISFile = DriveApp.getFileById(fileId);
  const shiftJISBlob = shiftJISFile.getBlob();
  console.log(shiftJISBlob.getDataAsString());
  // ����ɂ���, ���E, ���悤�Ȃ�, ���E

  const shiftJISString = shiftJISBlob.getDataAsString('Shift-JIS');
  console.log(shiftJISString);
  // こんにちは, 世界, さようなら, 世界

  const utf8String = convertBlobsTextEncodingFromSjisToUtf8(shiftJISString);
  console.log(utf8String);
  // こんにちは, 世界, さようなら, 世界
}

/**
* rootフォルダにSJISのcsvファイルを作成します。
*/
function createFileWithShiftJISEncoding() {
  const blob = Utilities
    .newBlob('', MimeType.CSV, 'CSV(SJIS)')
    .setDataFromString('こんにちは, 世界, さようなら, 世界', 'Shift-JIS');
  const newFile = DriveApp.createFile(blob);

  const csvBlob = newFile.getBlob());
  const stringData = csvBlob.getDataAsString();
  console.log(stringData);
    // ����ɂ���, ���E, ���悤�Ȃ�, ���E
  console.log(newFile.getId());
    // fileのidが表示されます。

  return newFile.getId();
}

/**
* 受け取ったSJISの文字列をUTF-8の文字列に変換します。
* @param    {String} shiftJISString    SJISの文字列
* @return   {String}                   UTF-8の文字列
*/
function convertBlobsTextEncodingFromSjisToUtf8(shiftJISString) {
  return Utilities.newBlob(shiftJISString).getDataAsString();
}
```
# 簡単解説
使われている主な関数はUtilitiesクラスのnewBlob(data)とBlobクラスのgetDataAsString()です。
<table>
  <thead>
    <tr>
      <th>関数名</th> <th>詳細</th>
    </tr>
  </thead>
  <tr>
    <td>newBlob(data)</td>
    <td>
        文字列から新たにBlobを作成
        <br>
        data: blobにするUTF-8の文字列
    </td>
  </tr>
  <tr>
    <td>getDataAsString()</td>
    <td>blobデータをUTF-8の文字列として取得</td>
  </tr>
</table>
<br>
SJISの文字列をこの関数に渡すと、newBlob(data)でその文字列を基にデータの処理に使われるBlobを作成します。それをgetDataAsString()でUTF-8の文字列として取り出すことで、最終的にSJISからUTF-8に変換しています。

# おわりに
何かありましたらお気軽にご連絡ください！
「いいね」もお願いします！！

個人ブログではQiitaに載せきれていない内容もあるので、ご興味ありましたら是非みてください！！

https://vegcale.com/
(mac) Homebrewによるmysql8.0インストールからログインまで

### Homebrewがインストールされていることを確認
```
brew -v
```

### mysql8.0がインストール可能か確認
```
brew search mysql
```

### mysql8.0をインストール
```
brew install mysql  // こちらもインストールする必要がある？
brew install mysql@8.0
```

### mysql8.0がインストールできたか確認
```
brew list | grep mysql
```

### mysql8.0サーバーを起動
```
brew services start mysql@8.0
```

### mysql8.0サーバーが起動されていることを確認
```
brew services list
```

### (mysql8.0サーバーをストップする場合は下記)
```
brew services stop mysql@8.0
```

### Homebrewによるインストールだとrootユーザーにパスワードが設定されていないので、パスワードを設定する。
```
mysql_secure_installation
```

### 設定したら下記実行後、パスワード入力してログイン
```
mysql -u root -p
```

### (ログアウトする場合は下記)
```
exit
```
【Linux】CentOS9にteratermSSH接続で入る方法

■環境
　・VirtualBox-7.0.20
　・CentOS 9

■前提

　初期インストール画面で、「ユーザの設定」欄の「rootパスワード」をクリックする。

 ![スクリーンショット 2024-09-14 082747.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761139/400235bc-2fd7-eb6e-ae70-497b945bd099.png)

　「rootパスワード」画面で[パスワードによるrootSSHログインを許可]にチェックを入れる。

 ![スクリーンショット 2024-09-14 082714.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761139/b0df46e6-be2a-d740-62ae-08f86660f999.png)

 　※ここにチェックを入れずに、インストールを完了して、teratermでパスワードログインすると何度やっても認証失敗します。
　自分はここを見落としていて、永遠と/etc/ssh/ssh_configをいじってなんで入れない！！！って頭抱えていました（笑）

 ■インストール後

◆ネットワークアダプタの割り当て変更

 　コンソールが立ち上がって、rootログインできたらとりあえず、仮想マシンをオフにします。

  「Oracle VM VirtualBox」画面で作成した仮想マシンを右クリックし、「設定」をクリックする。

　「設定」画面が開いたら、左ペインの[ネットワーク]をクリックする。

　そうすると、以下の画面が立ち上がる。
 
![スクリーンショット 2024-09-14 083720.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761139/0c333644-f19a-94ba-65ca-7447a280fd64.png)

　以下のように、割り当て[NAT]から[ブリッジアダプター]を選択する。
  　※NIC名はお使いのPCによって異なる。

  ![スクリーンショット 2024-09-14 083738.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761139/e92380f5-9e1d-00c8-e655-02ccf2ea0100.png)

　[OK]をクリックし、「設定」画面を閉じる。

　再びcentosを起動する。

◆teratermに打ち込むIPを確認する

　以下のコマンドを実行する

    ip a

![スクリーンショット 2024-09-14 083946.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761139/4b478481-18ab-0f41-c490-bc58cce9831d.png)

　ハイライト箇所のIPをteratermに打ち込む。
　※IPは自身のコンソール画面に表示されているIPです。このIPじゃないですよ（笑）
Aurora MySQLの不要データを削除してストレージを最適化する

Aurora MySQLの使用容量の8割(約400 GB)を占めるテーブルがあったのですが、過去データについてはCSV化してアーカイブできていたので、過去レコードを削除してDB容量を解放することにしました。

レコード削除だけで容量が解放されると思っていたら予想外に手間がかかったので、備忘録として詳細を残します。

## テーブル使用容量の確認
以下のクエリで各`.ibd`ファイルの容量を確認できます。

```sql
SELECT file_name, ROUND(SUM(total_extents * extent_size)/1024/1024/1024,2) AS "TableSizeinGB" from information_schema.files GROUP BY file_name ORDER BY total_extents DESC;
```

![スクリーンショット 2024-09-14 8.21.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569054/90fad41d-7172-e2af-3a92-430b1de6d9bc.png)

https://dev.mysql.com/doc/refman/8.0/ja/glossary.html

### 注意点
AWS re:Postの解決策では以下のクエリが推奨されていたのですが、該当テーブルについてはなぜか前述のクエリよりも150 GBほど小さい値が表示されました。

```sql
SELECT table_schema "DB Name", table_name,(data_length + index_length)/1024/1024/1024 AS "TableSizeinGB" from information_schema.tables where table_schema='database_name';
```

AWSサポートに確認したところ、`information_schema.tables`は統計値のため不正確であることがわかりました。正確な情報を取得するには、`information_schema.files`を参照することを推奨されました。

> テーブルサイズは 390 GB" という情報がinformation_schema."tables"から取得した情報となっており、統計値のため不正確であった可能性が高いと判断しております。
つきましては、今後テーブルサイズ等をご確認いただく際はinformation_schema."files"から情報を取得いただければと存じます。

https://repost.aws/ja/knowledge-center/view-storage-rds-mysql-mariadb

## DB使用容量の確認
以下のクエリでDB全体の使用容量を確認できます。
こちらについても`information_schema.files`を参照しています。

```sql
SELECT file_name, ROUND(SUM(total_extents * extent_size)/1024/1024/1024,2) AS "TableSizeinGB" from information_schema.files where file_name like '%/database_name/%';
```

## DB容量の解放手順
手順は以下の通りです。
1. レコードを削除する
2. 必要に応じてインスタンスクラスを変更する
3. `OPTIMIZE TABLE <table_name>;`を実施する

レコードを削除するだけでは容量は解放されず、`OPTIMIZE TABLE`まで実施する必要があります。

また、`OPTIMIZE TABLE`(`ALTER TABLE ... FORCE`)の操作では、一時的に中間テーブルファイルが作成され、Auroraではこの一時ファイルの格納にローカルストレージが使用されます。
Auroraのローカルストレージはインスタンスクラスに依存するのですが、私の使用する`db.r6g.xlarge`ではローカルストレージが80 GBしかなく、削除レコードの量の方が大きかったので、`db.r6g.8xlarge`(640 GB)に一時的にスケールアップしました。

https://dev.mysql.com/doc/refman/8.0/ja/optimize-table.html

https://dev.mysql.com/doc/refman/8.0/ja/alter-table.html

https://dev.mysql.com/doc/refman/8.0/ja/innodb-online-ddl-space-requirements.html

https://docs.aws.amazon.com/ja_jp/AmazonRDS/latest/AuroraUserGuide/AuroraMySQL.Managing.Performance.html#AuroraMySQL.Managing.TempStorage

### `OPTIMIZE TABLE`の注意点
250 GBほどのレコード削除後に`OPTIMIZE TABLE`を行ったところ、約130分(約2時間)かかりました。`OPTIMIZE TABLE`中はテーブルロックがかかるので、サービスの夜間停止などが必要になる場合があります。ちなみにレコード削除は数日にわけて行ったのですが、合計で約15時間かかりました。
AWSを不正利用された際の反省と教訓

# 反省
### 被害
AWSのSESを利用され、約5万通のメールが不正に送信され、約10ドルの使用料が発生しました。また、焦ってルート権限のMFA設定時にアプリ（別のアプリ用の番号を選択し続ける）の選択を誤り、余計に焦ることになりました。

# 頭によぎった過去の悪夢
https://x.com/8xNeXY/status/1766066506620924010

# 症状
- **DKIM設定成功通知:** 不審なDKIM設定完了の通知を受け取りました（9月12日７時ごろ）
[![Image from Gyazo](https://i.gyazo.com/a209714f5516c67e3f2dfd0d336ea047.png)](https://gyazo.com/a209714f5516c67e3f2dfd0d336ea047)
  <br>
  
- **料金アラート:** AWSからSESの使用料金が閾値に達したアラートが送信されました（9月13日9時ごろ）
[![Image from Gyazo](https://i.gyazo.com/fec0494595623946ebdb3f6166fba7ef.png)](https://gyazo.com/fec0494595623946ebdb3f6166fba7ef)  
  <br>
- **不正なメール送信:** 数万通の不正メールが送信され、料金が急増しました
[![Image from Gyazo](https://i.gyazo.com/70640a84f1917244a19cf0b9f63103fd.png)](https://gyazo.com/70640a84f1917244a19cf0b9f63103fd)
  <br>
- **ドメインの登録と削除:** 知らないドメインがAWSに登録されていました。至急登録されていたドメインを削除しました(9月13日11時ごろ）
[![Image from Gyazo](https://i.gyazo.com/e367e0b022b5c89ffa493100e761ead9.png)](https://gyazo.com/e367e0b022b5c89ffa493100e761ead9) 
  <br>
- **IAMユーザーでのログイン失敗:** 一旦削除したのでホッとした１時間後、IAMユーザー権限でのログインができなくなり、焦りが増しました（9月13日12時ごろ）
- **ルートユーザーのMFAエラー:** ルートユーザーでログインしようとしたところ、MFA認証時アプリで間違った番号を選択し、ログインに失敗し続けさらに焦りが増しました(9月13日12時ごろ）

# 対応
- **AWSへの連絡:** AWSのサポート窓口に連絡を試みましたが、回答が来るを待つ必要があり、不安が募りました(9月13日13時ごろ）


- **別アカウントで問い合わせ:** この間にも不正利用されるリスクがあるため家族名義で新たなAWSアカウントを作成し問い合わせを行いましたが、情報提供のみで有効な対応は得られませんでした(9月13日14時ごろ）

[![Image from Gyazo](https://i.gyazo.com/b5e243c206ae5f6214ae0aa493f33886.png)](https://gyazo.com/b5e243c206ae5f6214ae0aa493f33886)  
  <br>
- **冷静になり対応:** 冷静さを取り戻し、ルートユーザーでのログインを成功させ、IAMユーザーのパスワードとMFAを変更しました。これにより、再度IAMユーザーでのログインが成功しました(9月13日18時ごろ）


# 教訓
- **ログインできないと何もできない:** AWSへの問い合わせや操作は、まずログインできなければ始まりません
- **非常時こそ冷静に:** 焦りから誤操作を連発してしまいました。非常時こそ冷静に対応することが最も重要で、次回のために手順をメモに残しておくべきだと学びました

# 追記（９月１５日未明）
先日、完全にセキュリティの問題が解決したと思っていましたが、9月15日の深夜に再び問題が発生しました。驚くべきことに、IPアドレスを追跡すると、攻撃者は海外から不正アクセスを試みていることがわかりました。

## 相手の行動
- アクセスキーを勝手に作成: 攻撃者は私のAWSアカウントで新しいアクセスキーを作成し、悪用を開始しました。
- IAMユーザーの作成: 新しいIAMユーザーを勝手に作成し、さらに悪用できるように準備。
- SESを利用して数万通のメールを送信: 攻撃者はSESを利用して不正メールを大量に送信しようと試みました。

## 自分の対応
- アクセスキーの削除: 不正に作成されたアクセスキーを即座に削除
- IAMユーザーの削除: 不正に作成されたIAMユーザーも削除。
- SESの設定削除: SESの設定を削除し、不正利用を阻止。

この攻防を数回繰り返すことになりましたが、完全にイタチごっこの状態でした。根本的な解決が必要だと感じ、以下の対策を実施しました。
## 最終的な対策
- IAMユーザーのアクセスキーを削除: 自分自身のIAMユーザーのアクセスキーも削除し、これ以上の悪用を防ぎました
- IAMユーザーのポリシーを最小限に制限: 自分のIAMユーザーに付与していた過剰なポリシーを見直ししました
  <br>
[![Image from Gyazo](https://i.gyazo.com/e7f1ddd33113b5efd68c4d7c95973716.png)](https://gyazo.com/e7f1ddd33113b5efd68c4d7c95973716)

  <br>

## 原因の推測
ルートユーザーの権限は奪われていないため、おそらく攻撃者が利用したのは、IAMユーザーのアクセスキーの流出です。私自身、環境変数の取り扱いには十分注意していたつもりでしたが、どこかで流出してしまった可能性が高いです。

また、IAMユーザーに過剰なポリシー（権限）を付与していたことも、悪用されやすい要因となりました。今回のケースでは、SESのみが悪用されたため、さらに大きな被害には至りませんでしたが、非常に危険な状況でした。

## 教訓
- 最小権限の原則の徹底
- アクセスキーの定期的なローテーションが重要
- IAMユーザーの過剰な権限付与は非常に危険

## まとめ
今回のインシデントを通して、セキュリティ対策の重要性を改めて痛感しました。冷静に対応できたことで被害は最小限に抑えることができましたが、今後はさらに強化されたセキュリティ対策を講じていきます。
オリジナルアプリ「wants」のビュー作成の過程

こんにちは！今回は、オリジナルアプリ「wants」を作っている中で、AIを使ってビューに使う画像を作った体験をシェアします。最近は、AIがどんどん進化していて、プロのデザイナーじゃなくても、手軽にクオリティの高い画像が作れるようになってきました。そこで、実際にどんな風にAIを使って画像を生成したのか、詳しくお話ししていきます！

### AI画像生成のメリット

AIを使って画像を作ると、アプリ開発にどんなメリットがあるのか、少しだけ振り返ってみましょう。

1. **すばやく画像が作れる**: AIは指示した内容をもとに、数秒～数分で画像を生成してくれます。デザインに時間がかかりません！
2. **いろんなスタイルが試せる**: ファンタジー風、写実的な感じ、カートゥーンっぽいイメージなど、様々なスタイルが選べます。
3. **調整が簡単**: 「ちょっと違うな」と思ったら、プロンプトをちょっと変えるだけで新しいバリエーションがすぐに作れます。

「wants」アプリでは、ユーザーが使いやすいインターフェースにするため、こうしたAIで生成した画像を使って、見た目も楽しめるアプリ作りを目指しました。

---

### プロンプトってなに？

AIで画像を作るとき、一番大事なのが「プロンプト」です。簡単に言うと、AIに「こんな画像を作ってね！」と指示する言葉のことです。この指示が詳しければ詳しいほど、AIは私たちが思い描くような画像を作ってくれます。

じゃあ、どんな風にプロンプトを作ればいいのか？ 以下のテンプレートを使うと、より狙った画像を作りやすくなります。今回は直接chatGPT様に聞きました。
![スクリーンショット 2024-09-14 8.25.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763893/6328c3d0-7f86-e1f3-610d-5ac303dec7dc.png)

出力された結果が以下になります。

#### プロンプトのテンプレート

1. **テーマ/シーン**: 画像の全体的なテーマや場所。  
   （例: 森の中に立つファンタジーキャラクター）
2. **主な要素**: 画像の中心となる特徴や雰囲気。  
   （例: 明るい色彩、幻想的な雰囲気、夕暮れ時）
3. **キャラクター/オブジェクトの詳細**: 描かれているキャラクターや物体の具体的な特徴。  
   - 外見の特徴: （例: 長い金髪、青い目、白いローブ）
   - 表情やポーズ: （例: 笑顔で空を見上げている）
4. **背景の特徴**: 画像の背景に欲しい要素。  
   （例: 背後に大きな滝と茂った木々）
5. **スタイル/雰囲気**: 画像全体の描き方や雰囲気。  
   （例: 写実的、コミック調、ファンタジー風）

#### オプションで追加できる要素

6. **光や影の演出**: 光の加減や影の使い方。  
   （例: 柔らかな日差し、木々の影）
7. **カラーパレット**: 使いたい色の配色。  
   （例: パステルカラー、モノクローム）
8. **カメラアングル/視点**: 画像をどの角度から見るか。  
   （例: 上から見下ろす、近くでアップ）

---

### 実際に使ったプロンプトの例

実際に私が「wants」アプリのためテンプレートをどのように編集したかお伝えします。今回は、アプリの使いやすさや見た目の良さを引き立てるため、シンプルかつ現代的なイメージで作成しました。

#### 実際のプロンプト

1. **テーマ/シーン**: 明るい都市公園で携帯を操作している人物。
2. **主な要素**: 未来的でおしゃれな感じ、自然光が差し込んでいる雰囲気。
3. **キャラクター/オブジェクトの詳細**:
    - 外見の特徴: 若い男性、カジュアルな服装（白いシャツとジーンズ）
    - 表情やポーズ: スマホを真剣に見つめている姿。
4. **背景の特徴**: 緑の多い公園、遠くにビルが見える。
5. **スタイル/雰囲気**: 写実的だけど、少しデザインが入った感じ。
6. **光や影の演出**: 柔らかい自然光、背後に木々の影。
7. **カメラアングル/視点**: 人物をクローズアップした視点で、半身がメイン。

このプロンプトをAIに入力したら、まあままなビジュアルができあがりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763893/63ebaee1-0ebd-d52e-7fc9-82965efce681.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3763893/4344fc73-f9d2-354f-3e98-cb81fc773a9b.png)



今後は違うAIツールに同じプロンプトを入力して比較してみようと思います。

---

### プロンプト作成のコツ

プロンプトを作るときに押さえておくべきコツも紹介します！

1. **具体的に指示する**: AIは具体的な情報を元に動くので、「カジュアルな服」ではなく「白いシャツとジーンズ」というように、なるべく詳しく伝えるのがポイントです。
2. **スタイルや雰囲気を明確に**: 画像全体の雰囲気やスタイルもはっきりさせましょう。「ファンタジー風」や「都会的な感じ」など、どんなムードを出したいかを伝えます。
3. **調整は何度でもOK**: 一回で完璧な画像ができるわけではないので、何度も少しずつ調整していきましょう。いろいろ試してみることが大切です！

---

### まとめ

AIを使った画像生成は、アプリやウェブサイトを作る際にとても役立ちます。特に、プロンプトを工夫すれば、自分のイメージにぴったりな画像を短時間で作成できます。これからもAIをうまく活用しながら、アプリ開発の効率を高めていきたいと思います。

読んでくれてありがとう！ぜひみなさんもAI画像生成を試してみてくださいね！

---
Angular でrouter-outlet を複数使うまで

## やったこと
Angular でRoute の設定をいじりrouter-outlet を複数使うことで深いネスト構造にした。  
イメージとしては↓みたいな感じ。  
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/822099/cefdc1b3-83fe-9ee6-4507-1cf8ebfb72d8.png)

## なんでやったの
ページごとに全く雰囲気が違うサイトをつくりたかったから。  
というのが半分。  
もう半分はやりたかったからです！  

## 具体的にどうするの？
言葉だけだと難しかったので、実際にやった作業ベースで説明します。  

まずコンポを3つ用意します。  
- StudyBaseComponent  
  - `<router-outlet />`付きのヘッダーとか書くとこ  
- StudyListComponent  
- StudyDetailComponent  
  - コンテンツ部分  

続いて、study-config.ts とstudy-routes.ts を用意します。
study-config.ts はprovider を記載する箇所です。  
空なので割愛。  

study-routes.ts は以下の通りです。  
```
import { Route } from '@angular/router';

import { StudyDetailComponent } from './children/study-detail.component';
import { StudyListComponent } from './children/study-list.component';
import { StudyBaseComponent } from './study-base.component';
import { StudyConfig } from './study-config';

export const studyRoutes: Route = {
  path: 'study',
  component: StudyBaseComponent,        ←ポイント！
  ...StudyConfig,
  children: [
    {
      path: '',
      component: StudyListComponent,
    },
    {
      path: ':studyId',
      component: StudyDetailComponent,
    },
  ],
};
```

ポイントは`<router-outlet />`を付けたコンポを登録したとこに、children も一緒に付けることです。  
こうすることで、`<router-outlet />`が動作します。  

最後にapp.routes.ts に登録します。
```
import { Routes } from '@angular/router';

import { articleRoutes } from './article/article-routes';
import { HomeComponent } from './home/home.component';
import { studyRoutes } from './study/study-routes';

export const routes: Routes = [
  {
    path: '',
    component: HomeComponent,
  },
  articleRoutes,
  studyRoutes,          ←ここ！
];
```

実行した結果がこちらです。
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/822099/19e6790d-3717-1035-319c-359acd9dc9d8.png)


ちゃんと`<router-outlet />`が2つ動いていることが確認できました。  

手探りでやっていたので、うまくいった時は本当に嬉しかったです。  
あと、やっているうちに気付いたのですが、これってmain.ts と似ていることしていますね。  
いままでおまじないくらいの認識だったのが、少し意味が分かった気がします。  


今回の記事は以上です。  

前回記事はこちら  

https://qiita.com/magurouhiru/items/a713445660969c2365a8

詳細見たい人はこちら  

https://github.com/magurouhiru/mysite/pull/28

https://magurouhiru.github.io/mysite/
はじめてのPytest

### テストコードをさくっと学んだので自分用のメモを残す

---
### Documents
[Pytest]("https://docs.pytest.org/en/stable/getting-started.html#get-started")

[GitHub]("https://github.com/pytest-dev/pytest/")

---
### Installation
```
pip install pytest
```
---

### まずは関数をテストする。
---
src >> testするファイルを格納

```
# src/code.py
def sum_numbers(a, b):
    return a + b
```
---
tests >> testcodeを格納するフォルダ
from folder_name import fileName
```
# tests/test_1.py
from src import code

def test_1():
    result = code.sum_numbers(1, 2)
    assert result == 3
    #assert result == 2 #error 
```
---
テストコードを実行する

```
pytest testS/test_1.py
```
成功例
```
tests\test_1.py .                                                                   [100%] 
================================== 1 passed in 0.02s ====================================== 
```
失敗例
```
tests\test_1.py .                                                                   [100%] 
================================== 1 failed in 0.06s ======================================
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866385/16d7fe21-f369-a962-6a78-457b43fa890c.png)

引数あり

```
# src/code.py
def sum_numbers(a, b):
    return (a + b), a
```

```
# tests/test_1.py
from src import code

def test_1():
    result1, result2 = code.sum_numbers(1, 2)
    assert result1 == 3
    assert result2 == 2
```

```
def test_1():
        result1, result2 = code.sum_numbers(1, 2)
        assert result1 == 3
>       assert result2 == 2
E       assert 1 == 2

tests\test_1.py:6: AssertionError
================================= short test summary info ================================== 
FAILED tests/test_1.py::test_1 - assert 1 == 2
================================== 1 failed in 0.07s ======================================= 
```

```
# src/code.py
def sum_numbers(a, b):
    return (a + b), a
```
assert result2 == 1に変更
```
# tests/test_1.py
from src import code

def test_1():
    result1, result2 = code.sum_numbers(1, 2)
    assert result1 == 3
    assert result2 == 1
```

```
tests\test_1.py                                                                      [100%] 
================================== 1 passed in 0.02s =======================================
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866385/cdeaaf7f-7e76-b33f-c465-fb8bd2b09b91.png)

```
# from src import code

# def test_1():
#     result1, result2 = code.sum_numbers(1, 2)
#     assert result1 == 3
#     assert result2 == 1

# content of test_class.py

def test_equal():
    result = 1 +2
    assert result == 3

def teest_notequal():
    result = 1 + 2
    assert result != 10
def test_string():
    result = 'hello'
    assert result == 'hello'
def test_string():
    string = 'hello'
    assert string.startswith('H')
def test_string():
    string = 'hello'
    assert 'he' in string
def test_boolean():
    result = True
    assert result == True
```
めも（WIN10）

### SCOOP を使ってPython をInstallする

```
#Power Shell
scoop install python
```

```
python3 -V
#Python 3.12.2
```

```
#Power Shell
mkdir Pyenv
```

```
# python -m venv <Dir name>
 python -m venv C:\Users\UserName\Pyenv
 ```

 #### Script直下にActivate.ps1がある事を確認してActivate.ps1を実行する（仮想環境が有効になる）
 ```
 Scripts\activate.ps1
 ```

 ```
CREATE TABLE shuzen_list (
    id TEXT, 
    工事No TEXT, 
    工事内容 TEXT, 
    建物名 TEXT, 
    場所 TEXT, 
    受付日 TEXT, 
    工事完了日 TEXT, 
    売価予定金額 INTEGER, 
    売価合計 INTEGER, 
    "値引後売価（本体）" INTEGER,
    "原価(本体)" INTEGER, 
    "値引後原価(本体)" INTEGER, 
    利益 INTEGER
);
 ```

 #### Sqlite
 ```
 SELECT * FROM 修繕一覧;
 ```
 ##### TABLE ROWS COUNT
 ```
 SELECT COUNT(*) FROM 修繕一覧;
 ```
 
 ```
 SELECT COUNT(*) FROM 修繕詳細;
 ```

 ```
 SELECT 工事番号,
 建物名,
 工事内容,
 受付日,
 売価合計
 FROM 修繕一覧;
 ```
##### 重複する値を除外して抽出
```
 SELECT DISTINCT "建物名" FROM "修繕一覧";
 ```

 ```
 SELECT
DISTINCT "建物名",
 "工事番号",
 "工事名",
 "工事内容",
 "場所",
 "受付日",
 "売価合計",
 "原価",
 "利益"
FROM "修繕一覧"
GROUP BY "建物名";
```
ガントチャートとプレシデンスダイアグラム（PDM）の違いについて見ていきましょう

# はじめに
　ガントチャートとプレシデンスダイアグラム（PDM）は、どちらもプロジェクトマネジメントにおけるスケジュール管理の手法ですが、それぞれ異なる特徴と目的があります。以下では、それぞれの違いを詳しく説明します。

## ガントチャート
### 特徴
視覚的なタイムライン: ガントチャートは、プロジェクトの各タスクを横棒で表し、時間軸に沿って配置します。タスクの開始日と終了日、期間が一目でわかります。
使いやすさ: 視覚的に直感的で、プロジェクトの進行状況を把握するのに適しています。多くのプロジェクト管理ツールで簡単に作成できます。
進捗管理: タスクの進行状況をリアルタイムで追跡でき、どのタスクが遅れているかなどをすぐに確認できます。
リソース管理: リソースの割り当てやタスクの担当者を表示することもできるため、リソース管理にも使えます。
### 利点
視覚的にプロジェクト全体の進捗がわかりやすい
タスクの開始日・終了日、期間を簡単に管理できる
チームメンバーとのコミュニケーションが容易
### 欠点
タスク間の詳細な依存関係を示すことが難しい
複雑なプロジェクトの場合、全体のフローを把握しにくいことがある
### 使いどころ
プロジェクトのスケジュールを大まかに管理したいとき
タスクのタイムラインや進捗状況を視覚的に把握したいとき
小規模から中規模のプロジェクトでのスケジュール管理

##プレシデンスダイアグラム（PDM）
### 特徴
タスクの依存関係: PDMは、タスク間の依存関係を視覚的に示すダイアグラムです。矢印でタスクを結び、開始・終了の関係（先行・後続関係）を表します。これにより、タスクのフローと順序を明確にします。
4種類の依存関係: PDMでは、以下の4つの依存関係を示すことができます。
終了-開始（FS）: 先行タスクが終了してから後続タスクが開始
開始-開始（SS）: 先行タスクが開始したら後続タスクも開始可能
終了-終了（FF）: 先行タスクが終了したら後続タスクも終了
開始-終了（SF）: 先行タスクが開始したら後続タスクが終了
クリティカルパス: PDMは、プロジェクトのクリティカルパス（最も時間がかかるタスクの連鎖）を特定するのに役立ちます。これにより、プロジェクト全体のスケジュールを最適化できます。
### 利点
タスク間の依存関係を詳細に示すことができる
クリティカルパスを特定し、プロジェクトの遅延リスクを把握しやすい
複雑なプロジェクトでもタスクの順序と関係を明確にできる
### 欠点
作成がガントチャートよりも複雑で時間がかかる
視覚的に理解しにくいことがあり、特に非専門家には説明が必要
### 使いどころ
複雑なプロジェクトでタスク間の依存関係を詳細に管理する必要があるとき
プロジェクトのクリティカルパスを特定し、最適なスケジュールを組みたいとき
大規模プロジェクトや多くの依存関係があるプロジェクトの管理

## ガントチャートとプレシデンスダイアグラムの違いのまとめ

|主な目的|タスクのタイムラインの視覚化|タスク間の依存関係の明確化|
|---|---|---|
|視覚的な特徴|横棒グラフで期間を示す|矢印でタスク間の関係を示す|
|タスク間の依存関係|表示が難しい|詳細に示すことができる|
|クリティカルパス分析|難しい|簡単|
|作成の難易度|低い|高い|
|適用するプロジェクトの規模|小規模～中規模|中規模～大規模|

　ガントチャートは、プロジェクトの全体的な進行状況やスケジュールを視覚的に把握したい場合に最適です。一方、プレシデンスダイアグラムは、タスク間の依存関係を詳細に管理し、プロジェクトのクリティカルパスを特定して最適なスケジュールを作成する際に有用です。プロジェクトの特性やニーズに応じて、これらの手法を使い分けると良いでしょう。
過去7日間のAPI使用状況レポート : API Usage Last 7 Days Report



+ [API Usage Last 7 Days Report](https://trailhead.salesforce.com/ja/trailblazer-community/feed/0D54S00000Bqt6S)

:::note
Note: This is available in Salesforce Classic Only. 

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/6d93b1e6-ac87-f9a9-91ac-5909bd819ab9.png)
:::


+ [My organization has started getting the API Usage Exceeding Limit.](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007erbHx)

こういうのもあるらしい

:::note
In Lightning Experience you can see the limits using Salesforce Optimizer App -
https://help.salesforce.com/s/articleView?id=sf.optimizer_kick_off.htm&type=5
:::

# 他のレポート

+ [ユーザのMFA設定状況をレポートで確認したい](https://trailhead.salesforce.com/trailblazer-community/feed/0D5KX00000KfOCe)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/34b298ce-8a33-3fe6-4158-4be6faec9646.png)

:::note
こちらの件、Salesforceのサポートに問い合わせて解決しました。

権限・エディションの問題ではなく、「ID 検証方法」レポートタイプがなぜか「レポートタイプが非表示」の状態になっており、「管理レポート」グループに表示されなかったのが原因でした。

レポートタイプの選択画面で「レポートタイプを非表示 」を押下したところ、「ID 検証方法」レポートタイプが存在したため、「レポートタイプを表示 」を実施し、レポートを作成できるようになりました。
:::
ダッシュボードのURLハック



+ [Dashboards with URL Parameters in Lightning Experience](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007erbYo)

:::note

+ [Filter Dashboards with URL Parameters in Lightning Experience](https://help.salesforce.com/s/articleView?id=sf.dashboards_filter_url.htm&type=5)

+ [Save Dashboard Results with Filter URL Parameters](https://help.salesforce.com/s/articleView?id=release-notes.rn_rd_dashboards_filter_url.htm&release=230&type=5)
:::


+ [Lightning Experience での URL パラメーターによるダッシュボードの絞り込み](https://help.salesforce.com/s/articleView?id=sf.dashboards_filter_url.htm&type=5)

URL の末尾に検索条件を追加します。たとえば、次の URL はフェーズ (最初の検索条件、つまり fv0) を「Closed Won」に設定し、取引先名 (2 番目の検索条件、つまり fv1) を「Acme」に設定します。

```
https://MyDomainName.my.salesforce.com/lightning/r/Dashboard/01ZRM0000005jrv2AA/view?queryScope=userFolders&fv0=Closed%20Won&fv1=Acme
```


+ & は、URL 内の新しいパラメーターを示します。ベース URL に他のパラメーターを示す ? が含まれていない場合は、& の代わりに ? を使用します。
+ fv0 — fv は「検索条件値」を表します。番号は、ダッシュボードに検索条件が表示される順序を表します。最初の条件は 0、2 番目の条件は 1、3 番目の条件は 2 です。
+ =Closed%20Won — 検索条件値を指定します。パラメーター値は URI エンコードする必要があります。つまり、特定の文字 (スペースなど) を URL が理解できる形式で記述する必要があります。検索条件値「Closed Won」の「Closed」と「Won」の間のスペース (「 」) は、URI エンコードすると %20 になります。


困ったことに、検索条件に予め値をセットしていない場合は使えないみたい


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/6a0e1655-bc5c-b48b-885f-34a14fa3daef.png)

何故か勝手に検索条件が増えていく不思議。使えませんね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/f15c0ba1-9af9-a651-3e9c-e82253725777.png)
modelにフィールドを追加してリモートでmigrateしようとしても全然言うこと聞いてくれないんですけど！

### いや、こないだまで普通にできててんけどなんで？

modelにフィールドを追加して開発環境でマイグレート→push→本番環境ログインしてpull→本番環境でマイグレート。
これで出来るんちゃうん？と何度も試すが、no changeやらなんやらで全然いうこと聞いてきれなくなった。にっちもさっちもいかなくなり一旦データベースのバックアップをとって、```python manage.py migrate your_app zero```で一旦リセットして再度マイグレート。それでもエラーは直らないし、no such columと<めっちゃ前に作ったテーブル名> already existsの一点張り。

### 拉致が開かないので一旦原理を勉強してみる。
makemigrationとかmigrateとか、必要なコマンドとして実行してたけどこれってそれぞれなんなんだろう。

https://docs.djangoproject.com/ja/2.2/topics/migrations/

なるほど、追加や変更をマイグレーションファイルとしてコードベースで書いてから、マイグレートで適応させることによって、本番環境でとか、他の人がそのファイルを使ってマイグレートしても同じ環境を作れるようにするためって事か。だからローカルでmakemigrationsをするたびにファイルが作成させていたのね。

### さあここからが問題
makemigrationsとmigrateの違いはわかったものの依然エラーは直らない。
```python manage.py showmigrations```実行で
```
[ ] 0001_initial
[ ] 0002_remove_*******
[ ] 0003_*********and_more
 ```
 と一つもマイグレーションが適用されていない。
 この状態でmigrateすると先ほどと同様```<めっちゃ前に作ったテーブル名> already exists```のエラー。もはやどうしようもないのでSQLコマンドから手動で追加できないかと言うアプローチに変更。

 ```
 INSERT INTO django_migrations (app, name, applied) VALUES ('my_app', '0001_initial', datetime('now'));
 ```
その後```python manage.py showmigrations```を確認すると
```
[X] 0001_initial
[ ] 0002_remove_*******
[ ] 0003_*********and_more
 ```
 え！いけてる！
 しかし```.schema 'my_app'_'table_name'```を見てみるここには追加フィールドが存在しない。ここも手動で
 ```ALTER TABLE 'my_app'_'table_name' ADD COLUMN '<追加のフィールド名>' varchar(100) NULL;```
 を実行しマイグレートを行うとゴリ押しで新しいフィールドが追加できた！
 しかも``python manage.py showmigrations```確認で
```
[X] 0001_initial
[X] 0002_remove_*******
[X] 0003_*********and_more
 ```
 と適応されている！


 ### 難しい...
 正直なところなぜ通常通りマイグレーションができなかったのか分からない😭
 しかし最終手段としてSQLコマンドから手動で操作するという方法を一つ引き出しには増やすことができた。今すぐには分からないけど徐々に「あの時のあのエラーの原因はこれかも？」とわかる日が来ることを願う🙏
Object でも Hash みたいに分割代入したい

# やりたいこと

Object でも Hash みたいに分割代入 (destructuring assignment) したい。

```rb
# Hash オブジェクトは右代入を使って分割代入ができる。
heterogeneity = { brand_and_name: 'mowl - ヘトロジェニティ', color: '紫' }
heterogeneity => { brand_and_name:, color: }
[brand_and_name, color]
#=> ["mowl - ヘトロジェニティ", "紫"]

# ちなみに Data オブジェクトでもできる。
Yoyo = Data.define(:brand_and_name, :color)
heterogeneity = Yoyo.new(brand_and_name: 'mowl - ヘトロジェニティ', color: '紫')
heterogeneity => { brand_and_name:, color: }
[brand_and_name, color]
#=> ["mowl - ヘトロジェニティ", "紫"]

# Yoyo という PORO (Plain Old Ruby Object) でも同じようなことがしたい。
class Yoyo
  # 略
end

heterogeneity = Yoyo.new(some_arguments)
heterogeneity => { brand_and_name:, color: }
[brand_and_name, color]
#=> ["mowl - ヘトロジェニティ", "紫"]
```

# 方法

class に [deconstruct_keys](https://docs.ruby-lang.org/ja/latest/method/Data/i/deconstruct_keys.html) メソッドを実装する。

```rb
class Yoyo
  attr_reader :name, :brand, :color

  def initialize(name:, brand:, color:)
    @name = name
    @brand = brand
    @color = color
  end

  def brand_and_name
    "#{@brand} - #{@name}"
  end

  def deconstruct_keys(keys)
    {
      name: @name,
      brand: @brand,
      brand_and_name: brand_and_name,
      color: @color
    }
      .slice(*keys.map(&:to_sym))
  end
end

heterogeneity = Yoyo.new(name: 'ヘトロジェニティ', brand: 'mowl', color: '紫')
heterogeneity => { brand_and_name:, color: }
[brand_and_name, color]
#=> ["mowl - ヘトロジェニティ", "紫"]
```

# バージョン情報

```shell-session
$ ruby -v
ruby 3.3.4 (2024-07-09 revision be1089c8ec) [arm64-darwin23]
```

# 参考

- Ruby 3.3 リファレンスマニュアル
  - [非プリミティブなオブジェクトのマッチ: deconstruct メソッドと deconstruct_keys メソッド](https://docs.ruby-lang.org/ja/latest/doc/spec=2fpattern_matching.html#matching_non_primitive_objects)

<img alt="ヘトロジェニティ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6697/e4851d87-2c4d-3e79-e6b1-0614a176b496.jpeg" width=400>

↑ コード中に出てきたヘトロジェニティはこちら 👼
Sカーブプロジェクト管理

Sカーブプロジェクト管理
プロジェクト管理にはいろいろな方法があります。でも、「Sカーブプロジェクト管理」は特に効果的です。Sカーブを使うと、プロジェクトの進捗を簡単に見ることができます。

![S Curve Project Management.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3885642/01ff51b0-2ab7-8a45-1f03-dd4684196409.jpeg)

これは、予算、時間、リソースの最適化に役立ちます。プロジェクトを効率的に運営するための実践的なヒントを紹介します。

Sカーブプロジェクト管理とは何か
Sカーブプロジェクト管理は、プロジェクトを進めるための方法です。Sカーブを使って、時間、予算、リソースを管理します。プロジェクトの進捗可視化とパフォーマンスカーブを実現します。

Sカーブは、プロジェクトの3つの大切な要素を一目で見ることができます。出来高、予算、作業時間の変化を把握できます。これにより、プロジェクトの成果を早く見つけ、問題を解決できます。

Sカーブはプロジェクト管理の強力なツールです。プロジェクトマネージャーは、Sカーブで進捗を正確に把握できます。課題を早く発見し、迅速に対応することができます。

S Curve Project Managementの4つの主要フェーズ
S Curve Project Managementには、プロジェクトを成功させるための4つのフェーズがあります。プロジェクト着手フェーズ、計画立案フェーズ、実行フェーズ、そして完了フェーズです。

プロジェクト着手フェーズでは、目標と要件を決め、スコープをはっきりさせます。計画立案フェーズでは、詳細なスケジュールや予算、リソースの計画を立てます。実行フェーズでは、Sカーブを使って進捗を追跡し、工程管理と監視を行います。最後に、完了フェーズでは、プロジェクトを終了し、振り返りや改善点を考察します。

これらの4つのフェーズをうまく使うことで、プロジェクトを成功させることができます。各フェーズをしっかり理解し、次々と進めていくことが大切です。

Sカーブを活用したプロジェクト計画と実行のヒント
Sカーブプロジェクト管理で成功するヒントを紹介します。まず、プロジェクトの3つの要素である出来高、予算、作業時間を正確に計測します。これにより、現実的なSカーブを作成できます。

次に、定期的にSカーブを更新し、進捗をチェックしましょう。そうすると、いつでも対策を立てることができます。

Sカーブがずれていれば、原因をすぐに調べます。適切な対策を立てることが大切です。リスク要因を事前に分析し、Sカーブに反映することも大切です。

プロジェクトチームとのコミュニケーションも重要です。情報共有とフィードバックを重ねることで、成功率が上がります。

これらの方法で、Sカーブの活用方法、プロジェクト計画、工程管理、リスク管理を上手に行えます。プロジェクトの成功に繋がります。
Arduino UNO 3でサイコロの期待値(平均)を求めて遊ぶ


目的
合計から割り算して平均(期待値)求める
サイコロの期待値は、3.5が正解
小数点がないので、切り捨てで3
いちお、高校レベルらしい


いろいろ
アルゴリズムの教科書では、
ドラクエ1的に言ったらレベル55+で真ん中ぐらい
いろいろ違うが、乗り物に乗れるレベル


●なんじゃー

![o_coq394.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/a507ba66-d363-7b9a-8ffc-986a9eb4b5a3.jpeg)



結果

![o_coq395.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/1b66721f-03d4-73bd-3e65-e4758db33822.jpeg)


プログラム


```c++


//heikin_UNO_1

//初期化
// the setup routine runs once when you press reset:
void setup() {

  //シリアルポートの初期化
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);

}//setup



#define n 6

//メインループ
// the loop routine runs over and over again forever:
void loop() {

  int x[n + 1];

  x[1] = 1; x[2] = 2; x[3] = 3; x[4] = 4; x[5] = 5; x[6] = 6;

  //合計を求める
  int z = 0; int a;
  for (int k = 1; k <= n; k++) {

    z = z + x[k];

  }//for

  //合計を総数で割って平均を求める
  a = z / n;

  //結果の表示
  // print out the value you a:
  Serial.println(a);
  delay(1000);        // delay in between reads for stability

}//loop


```
Elixirで作ったリポジトリをアップデート管理〜Dependabot〜

# 前提条件
- githubにリポジトリを作ってある
- Elixir CIが設定済みであること
- mix ファイルのdepsに古いバージョンを指定する

# github設定

Settings→Code security and analysis

下記を有効にする

・Dependabot alerts
・Dependabot security updates
・Dependabot version updates

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/d641e2ce-ee5b-4113-ef70-9d3d3b3299db.png)



dependabot.ymlの設定（Dependabot version updates）


```yaml:.github/dependabot.yml
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file

version: 2
updates:
  - package-ecosystem: "github-actions" 
    directory: "/" # Location of package manifests
    schedule:
      interval: "daily"
  - package-ecosystem: "mix" 
    directory: "/" # Location of package manifests
    schedule:
      interval: "daily"
```



# 通知結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/d4c4680e-54f1-4944-ae39-9b879dd779fc.png)

# ソース(github)　
https://github.com/ymn-t-yamanashi/dependabot_experiment

# 参考
https://docs.github.com/ja/code-security/getting-started/dependabot-quickstart-guide

https://qiita.com/zacky1972/items/993f50a2add27763edf3
djangoで画像の容量を圧縮してパフォーマンスの向上を図ってみた話し

### サーバー上でたくさんの写真を使用すると読み込みに時間がかかって仕方がない...
ということで画像の容量を圧縮すればいいのではと、色々調べてみた。画像の拡張子を調べてみるとJPEGの圧縮率が高く写真の記録やインターネット上で大きな画像を表示したい時に多く使用されるとのこと。ではどうやってこの圧縮を行うのか。一枚一枚圧縮してからアップロードなどというしちめんどくさいことはできない。と思い調べてみるとDjangoのカスタム管理コマンドで画像を圧縮するスクリプトを書けばいいということに行き着く。

### Djangoのカスタム管理コマンド
*chat GPTとの対話にて作成。。。
アプリ/management/commands/compress_images.pyとディレクトリを作って以下のスクリプトを作成。
```
class Command(BaseCommand):
    help = 'メディアディレクトリ内の画像を全て85%に圧縮し、photo1_compressed、photo2_compressed、photo3_compressed フィールドに上書き保存'

    def handle(self, *args, **kwargs):
        media_root = settings.MEDIA_ROOT
        for root, dirs, files in os.walk(media_root):
            for file in files:
                if file.lower().endswith(('.png', '.jpg', '.jpeg')):
                    file_path = os.path.join(root, file)
                    self.compress_images(file_path)

    def compress_images(self, file_path):
        if '_compressed' in os.path.basename(file_path):
            self.stdout.write(self.style.SUCCESS(f'Skipping already compressed file {file_path}'))
            return

        compressed_file_path = f"{os.path.splitext(file_path)[0]}_compressed{os.path.splitext(file_path)[1]}"
        
        try:
            with Image.open(file_path) as img:
                if img.mode in ("RGBA", "P"):
                    img = img.convert("RGB")
                img = ImageOps.exif_transpose(img)  # Exifデータに基づいて画像を回転
                img.save(compressed_file_path, optimize=True, quality=85)
            
            with open(compressed_file_path, 'rb') as f:
                django_file = File(f, name=os.path.basename(compressed_file_path))
                
                for <index> in <モデル名>.objects.all():
                    if <index>.photo1.name == os.path.basename(file_path):
                        <index>.photo1_compressed.delete(save=False)
                        <index>.photo1_compressed.save(django_file.name, django_file, save=True)
            
            self.stdout.write(self.style.SUCCESS(f'Compressed and saved as {compressed_file_path}'))
        
        except Exception as e:
            self.stdout.write(self.style.ERROR(f'Error processing {file_path}: {str(e)}'))
```
簡単に要約すると、メディアディレクトリ内の画像を見回ってrowデータを見つけたら圧縮をかけてファイル名に_compressedというプレフィックスを追加する。これによって圧縮済みのファイルを区別して、_compressedがついているファイルは圧縮をスキップする、という仕組み。そうじゃないとコマンドを実行するたびに85%圧縮、そのまた85%圧縮を繰り返すのでは？という危惧があり...

### そのほかの整合性を揃える。
今回は
modelにphoto1,photo2,photo3といったフィールドを用意していたが、これとは別にphoto1_compressed,photo2_compressed,photo3_compressedといった別のフィールドを追加。(上記スクリプトにおいても圧縮したファイルはこちらのフィールドに追加するようにしている。)

ここでカスタムコマンド```python manage.py compress_images ```を実行して、

テンプレートも書き直すと
```
<img src="{{ object.photo1_compressed.url }}" alt="">
```
画像が表示され、デベロッパーツール上で確認しても_compressedのファイル名に変更されている。


念の為filezillaで写真の容量を確認すると多少の誤差はあるが85%カットになっている！
確かに心なしか表示が速くなった！

### 余談
今回は85%カットにしたがさらに数字を小さくすると高速化を図れるが画質が荒くなる。どこまでカットできるのだろうか。なんて悠長なことを書いているが実際にこの高速化ができるまで一週間以上かけて作った血と汗の結晶である。(エラーざんまい、デバックしまくり、途中で変なことしてデータベース消えかける、permisshion errorなど様々なことに悩まされた。)さらには上記のスクリプトを完全には理解できてい節も...これから徐々にできるようになろう😂これは自分のための備忘録になってくれるだろう。
Ectoで「エラーメッセージカスタマイズ」と「独自バリデーション関数作成」

# 今回の目標
- エラーメッセージカスタマイズ
  - validate_requiredエラーメッセージを変更
    - 「can't be blank」→ 「空でござる」
- 独自バリデーション関数作成
  - 最小文字数が2文字
  - a〜zを含む

# プロジェクト作成

```
$ mix phx.new ecto_experiment --database sqlite3
$ cd ecto_experiment
$ mix ecto.create
```

# phx.gen.liveでecto実験用ソース作成

```
$ mix phx.gen.live Accounts User users name:string
$ mix ecto.migrate
```

ルーター設定
```diff_elixir:lib/ecto_experiment_web/router.ex
defmodule EctoExperimentWeb.Router do
# 〜省略〜

  scope "/", EctoExperimentWeb do
    pipe_through :browser

+   live "/users", UserLive.Index, :index
+   live "/users/new", UserLive.Index, :new
+   live "/users/:id/edit", UserLive.Index, :edit

+   live "/users/:id", UserLive.Show, :show
+   live "/users/:id/show/edit", UserLive.Show, :edit

    get "/", PageController, :home
  end

# 〜省略〜 
end
```

# 「エラーメッセージカスタマイズ」と「独自バリデーション関数作成」

```diff_elixir:lib/ecto_experiment/accounts/user.ex
defmodule EctoExperiment.Accounts.User do
  use Ecto.Schema
  import Ecto.Changeset

  schema "users" do
    field :name, :string

    timestamps()
  end

  @doc false
  def changeset(user, attrs) do
    IO.inspect(attrs)
    user
    |> cast(attrs, [:name])
-   |> validate_required([:name])
+   |> validate_required([:name], message: "空でござる") # エラーメッセージカスタマイズ
+   |> validate_name()

  end

  # 独自独自バリデーション関数作成
+ defp validate_name(changeset) do
+   changeset
+   |> validate_length(:name, min: 2, message: "最小は２文字でござる")
+   |> validate_format(:name, ~r/[a-z]/, message: "a〜zを含むでござる")
+ end

end

```

# 実行
```
$ mix phx.server
```

`http://localhost:4000/users/new` にアクセス

エラーメッセージカスタマイズ検証
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/b8264c3c-765c-12df-63c7-6df563e695b9.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/7dd7f208-726c-2f20-b35e-e772666505d5.png)

独自バリデーション関数検証
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/66801379-5290-921e-6274-40dda29d08f2.png)

User.changesetが通過するとエラーが消える
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/295814/0127f6ee-df62-4ebd-3dfc-61972150aadc.png)

# ソース(github)
https://github.com/ymn-t-yamanashi/ecto_experiment
Jetpack Composeでバケツリレーをしない方法

# はじめに
今回はJetpackCompose実装時に必要な型やクラスをComponentにバケツリレーをしない方法を紹介していきます
### 本文
その方法とはCompositionLocalです
まずは準備からです
```kotlin
private val MyLocalColor: ProvidableCompositionLocal<Color> = compositionLocalOf<Color> {  
    error("No current color")  
} 
```
ラムダ内はデフォルト値で値が与えられる前に取得しようとすると呼ばれます。
値を適用するには下記のようにします
```kotlin
CompositionLocalProvider(MyLocalColor provides Color.Black) {  
    MyApp()  
} 

```
ラムダ内は使用したいComposable関数を定義します。
これは、子要素に伝播していくので基本的には該当Moduleや該当パッケージの上部要素に対して設定しておけば問題ないです。
滅多に変わらない値はstaticCompositionLocalOf()で生成するのをおすすめします。
### 最後に
普段何気なく使ってるLocalContext.currentなどはまさに今回紹介したCompositionLocalを使って実装されています
どなたかのお役に立てれば幸いです
コミュニケーション型Ｗｅｂ地図（Ｌｉｍｉｔ１２、地図の伝言板）

コミュニケーション型のＷｅｂ地図を２つ作ってみました。

１つは、自由投稿型の「Ｌｉｍｉｔ１２」（クチコミＷｅｂ地図）。
もう一つは、グループ内情報共有型の「地図の伝言板」です。

【Ｌｉｍｉｔ１２】
https://chosashi-data.org/shirado/limit12/

【地図の伝言板】
https://office-shirado.com/dengon/
![イメージ_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67836/f2d7870b-a7c0-9b01-7f24-fe3a5622dd98.png)
![イメージ_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/67836/c54364a9-9c3a-cf3d-7ca7-2edb51b94eca.png)

どちらとも、機能はシンプルで、Ｗｅｂ地図上に、ダブルクリック（ダブルタップ）でメモを投稿（プロット）するだけです。

「リミット１２」は、クチコミＷｅｂ地図という位置づけで、不特定多数の人が地域情報や楽しかった情報を投稿し、共有するものです。災害時には、危険個所や支援情報（避難所・給水所など）を投稿し、助け合うということも可能です。
「地図の伝言板」は、グループ内という閉じた関係の中での情報共有を目的とするものです。

両方とも、コミュニケーションをＷｅｂ地図上で行うことは共通しています。

クチコミ・メモともに動的データであるため、データベースのＭｙＳＱＬで運用しています。

今まで、静的データを扱うことが多く、ベクトルタイルデータ（ＰＭＴｉｌｅｓ）で配信することが多かったのですが、今回はベクトルタイルを都度作ることはあきらめ、ＰＨＰでデータベースに接続し、ローカルでＧｅｏＪＳＯＮを組み立て、Ｓｏｕｃｅを入れ替えしたり、削除・追加して更新しています。

また、「リミット１２」・「地図の伝言板」ともに気象庁のデータ（雨雲・台風・地震）を表示するようにしており、動的ジオデータの処理は静的ジオデータとは違った感覚であることが分かりました。

細かいところを言えば、
・雨雲の動き：常にデータはある（しかし、時系列でＵＲＬが変わる）【結果と予報】
・台風：データがある時とない時がある
・地震：データがある時とない時がある
となります。

気象庁のＨＰでは、ＡＰＩで、その情報を取得することができ、取得したデータを解析してデータの有無やデータのＵＲＬなどを判別し、必要な処理をとるという静的データでは考えられない機能が必要であることが分かりました。

災害はない方が良いのですが、万が一あった際に「行政機関からどのようにして配信データを取得し、表示するか？」などは今回が良い機会となったかと思います。

また、行政側に期待するのは、「データを配信すれば、民間が使うということに気づいて欲しい」ということです。行政側でＷｅｂ地図サービスを展開しているところもありますが、そのサイトの情報にほかの情報も加えたいという時もありますので、「Ｗｅｂ地図サービス」＋「データ配信」＋「オープンデータ」の３つセットが今後のポイントになるかと思います。


技術的なことは書ききれないのですが、気象庁の雨雲・台風・地震のデータをＷｅｂ地図に載せたり、ＱＧＩＳ等のローカルＧＩＳで表示することが柔軟にできると、ＧＩＳ自体の有用性はますます上がるものと期待しております。

地図の伝言板は、月額サービスを検討しておりますが、体験版（２週間ほど）も準備する予定ですので、ぜひ一度グループ・ユーザー登録頂ければと思います。


自身でＬｉｍｉｔ１２及び地図の伝言板を使ってみた感想は、「Ｗｅｂ地図って、自由な使い方ができる」、「地図型ＳＮＳもありじゃないか？」というもので、ＧＩＳ＝業務用というイメージを塗り替える良いきっかけだと思っております。


最後に、オープンソースのMapLibreのたくさんの機能を使っております。クラスタリングなども今回使用し、ここまでのものをオープンソースとして出して頂いていることに感謝いたします。

今後も、個人として様々なＷｅｂ地図サービスを考え、開発していきたいと思います。


土地家屋調査士　白土洋介
MERNスタックアプリをVercelにデプロイしてみた

こんにちは
エンジニア転職のために、MERNスタックアプリを作りデプロイしました。
いろいろ考えた結果Vercelにデプロイしました。失敗したことも含めて皆様に共有させえていただきます。
今ポートフォリオを作って、無料でデプロイしようと考えていらっしゃる方には特に参考になるかと思います。

## Vercelにデプロイしようと思った理由
結論、無料だったからです。
昔は、AWSのFC2やHerokuという選択があり、それらにデプロイをしていました。
しかし、AWSの無料枠は使い切っているようで、、、さらにいつのまにかHerokuは有料化になってしまい、、、、

Next.jsをお使いの方はよくご存じのVercelにデプロイしようということになりました。
※Vercelの前にRailwayというサービスも使いましたがデプロイがうまく行かず、、、Vercelにしました。

Vercelは↓こちら

https://vercel.com/

## Vercelへのデプロイのやり方

MERNスタックのブルスタックアプリのフォルダ構成は下記です。
　frontend
 　　└フロントエンドのフォルダ構成
　Backend
  　　└バックエンドのフォルダ構成
    
Vercelでfrontendをデプロイして、そのあとに、backendを別プロジェクトをデプロイします。

１、Vercelで新しいプロジェクトを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426016/eb90e1a4-e20f-0d0a-97c4-828de240466c.png)

２、GitHubのプロジェクトを選んだあと、frontendとbackendに分けてビルトしていきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426016/9e8198cb-f30e-a9b5-1a3d-36d27a464885.png)

３、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426016/6edb2970-60ba-8e4b-f2a6-f2e1218041ce.png)
↑
ここで、最初にfrontendを選択しデプロイします。

frontendと同じように、次に、Backendの新しいプロジェクトを選んでビルドします。

※backendのみ必要なvercel.jsonについて
backendのルートフォルダに「vercel.json」というファイルを作成し下記の通り記述してデプロイする必要があります。
````json
{
  "version": 2,
  "builds": [
    {
      "src": "server.js",
      "use": "@vercel/node"
    }
  ],
  "routes": [
    {
      "src": "/api/(.*)",
      "dest": "/server.js"
    }
  ]
}
````

以上でデプロイができました。
※frontendにはvercel.jsonは必要ありません、

このような形で、frontendとbackendのデプロイができます。
mongoDBは「MongoDB Atlas」を使用しました。

https://www.mongodb.com/ja-jp/products/platform/atlas-database

全体の構成はこんな感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/426016/6cedf6c2-0dca-08b3-d00c-b71d7d2e28f3.png)


以上、Vercelへのデプロイ方法でした。
Linux DE クリップボードの小技 'clip'

なかなか役に立つクリップボードの小技をご紹介します。

```
sudo pacman -S xclip
```
で、クリップボードにアクセスするプログラムをインストールします。

$PATHの通ったディレクトリに、次のスクリプトを書きます。

```clip.sh
#!/bin/bash
    cat $1 | xclip -selection clipboard
```

`chmod +x clip`で実行権を付けます。

これで、コマンドラインから`clip file`とすると、クリップボードにファイルを丸ごとコピーすることができます。

そして、ファイルマネージャのテキストを開くプログラムの中に入れておけば、すぐにファイルをクリップボードにコピーできます。

終わり。
【AtCoder】尺取り法

## ポイント
- 2つの変数を用意して、一方の変数を追いかけるようにもう一方の変数を動かしていく
- 単調性を利用するので、配列要素の順番を動かしていく場合には配列を予めソートしておく

## 例題
[ABC212C - Min Difference](https://atcoder.jp/contests/abc212/tasks/abc212_c)

```C++
#include <bits/stdc++.h>
using namespace std;
#define all(a) (a).begin(), (a).end()
#define rep(i, n) for (int i = 0; i < (n); ++i)

const int INF = 1001001001;
void chmin(int &a, int b) { if (a > b) a = b; }

int main() {
  int N, M; cin >> N >> M;
  vector<int> A(N), B(M);
  rep(i, N) cin >> A[i];
  rep(i, M) cin >> B[i];
  
  sort(all(A));
  sort(all(B));
  
  int j = 0, ans = INF;
  rep(i, N) {
    // 末端まで行くか「B[j] >= A[i]」を満たすようになるまでjを増やしていく
    while (j + 1 < M && B[j] < A[i]) j++;
    chmin(ans, abs(A[i] - B[j]));
    if (j - 1 >= 0) chmin(ans, abs(A[i] - B[j - 1]));
  }
  
  cout << ans << endl;
}
```

<br>

[ABC330C - Minimize Abs 2](https://atcoder.jp/contests/abc330/tasks/abc330_c)

```C++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;

const int INF = 1001001001;
void chmin(ll &a, ll b) { if (a > b) a = b; }

int main() {
  ll D; cin >> D;
  
  ll y = 2e6, ans = D;
  for (ll x = 0; x * x <= D; x++) {
    while (y > 0 && x*x + y*y >= D) y--;
    ll v1 = abs(x*x + y*y - D);
    ll v2 = abs(x*x + (y+1)*(y+1) - D);
    chmin(ans, v1); chmin(ans, v2);
  }
  
  cout << ans << endl;
}
```
【Azure】LogicAppsに最適なマスタテーブル（CosmosDB vs TableStorage vs Azure Files）

#### はじめに
LogicAppsでの開発が最近多くて、大変です。今回のタイトル「LogicAppsで作るアプリのマスタテーブルは何に保管するのが良いのか」について、簡単に比較してみました
もし、ご意見やご質問などあれば気軽にください。内容は、この先いろいろ検証して更新していくかもです。

## 候補
主にマスタの格納先として考えられうのは以下候補かと思います。
+ AzureTableStorage
+ CosmosDB
+ AzureFiles + CSV

それでは、ひとつづつ見ていきましょう

## AzureTableStorage
柔軟性：★★　（最大★5です）
手軽さ：★★★★★
安さ　：★★★★★

私の一番のおすすめはこのTableStorageです。LogicAppsはTableStorageに対して、さまざまなコネクタを持っており、行やテーブルの追加・削除・更新などの基本的な操作が一通り可能です。
+ [MS Learn | Azure テーブル ストレージ](https://learn.microsoft.com/ja-jp/connectors/azuretables/)


柔軟性の低さとしては、クエリのバラエティがやや欠けている点や、Functionsなどと連携する場合は他(特にCosmosDB)と比較して見劣りしてしまいます。
ただし、安くて手軽なので、まずはAzureTableStorageで検討してみるのはいかがでしょうか。

## CosmosDB
柔軟性：★★★★★　（最大★5です）
手軽さ：★★★★
安さ　：★★★

次点でのおすすめはCosmosDBです。TableStorageで検討してみて、どうしても実現できそうにないときは、CosmosDBを検討してみましょう。ComosDBは豊富なクエリを持っていますので、より柔軟なデータ操作を可能にします。スループットや整合性レベルも自由自在に操作できます。CosmosDBの構築についても、それほど難しくはありません。
コネクタの設定などに関しては以下Docsをご確認ください。
+ [MS Learn | Azure Logic Apps を使用した Azure Cosmos DB ドキュメントの処理および作成](https://learn.microsoft.com/ja-jp/azure/connectors/connectors-create-api-cosmos-db?tabs=consumption)
+ [MS Learn  | Azure Cosmos DB](https://learn.microsoft.com/ja-jp/connectors/documentdb/)

値段ではやはりTableStorage先行かと個人的には思っています。しかし、TableStorageでは要件に合わないときの選択肢としては申し分ないと私は思います。

## AzureFiles + CSV
柔軟性：★★　（最大★5です）
手軽さ：★★
安さ　：★★

私個人の意見としてはこの構成はできるだけ避けたいところです。柔軟性、構築難易度、価格すべてにおいてセキュリティ上もあまりよろしくはないかと思います。可能な限りCosmosDBかTableStorageで検討をしましょう。それでも、CSVファイルをテーブルとして使いたいケースもあると思います。

例えば、外部のシステムとCSVでマスタを共有しているとか・・・(滅多に見たことないです)
できれば、このケースは一度CosmosDBかTableStorageにデータ取り込みたいです。

参考までに以下記事がとても詳しく手順を載せてくれています。ぜひご参考になさってください
+ [ナストンのまとめ(Blog) | LogicAppsでCSVファイルを読み込む](https://nasuton.net/blog/logicapps_csv_read/)
オーダーメイドの鉄鋼加工が建築デザインを変える方法

建築デザインにおいて、素材の選定はプロジェクトの成功に大きく影響します。その中でも、オーダーメイドの鋼材加工は、単に構造の強度を提供するだけでなく、デザインの柔軟性や美観を大きく向上させる重要な要素です。本記事では、オーダーメイドの鋼材加工がどのように建築デザインを革新するかについて詳しく解説します。

## オーダーメイド鋼材加工とは
オーダーメイド鋼材加工とは、特定の建築プロジェクトに合わせて鋼材を切断、成形、組み立てるプロセスを指します。この加工は、標準的な部品を使用するのではなく、プロジェクトごとに異なる要件に応じて材料をカスタマイズすることが特徴です。これにより、建物の設計や機能に最適な部品が提供され、設計の自由度が高まります。

## デザインの柔軟性を向上
オーダーメイドの鋼材加工を利用することで、建築家や設計者は、標準的な鋼材では実現できない独自のデザインを可能にします。例えば、曲線的な形状や複雑な構造を必要とするプロジェクトにおいて、カスタム鋼材はその要求に応じた正確な寸法と形状を提供します。この柔軟性により、建物の外観や内部構造において、創造性を最大限に発揮することができ、より魅力的で機能的な空間を作り上げることが可能となります。

## 建築プロジェクトの効率化
オーダーメイドの鋼材加工は、建築プロジェクト全体の効率を高めるための鍵となります。事前に設計された部品が現場で正確にフィットするため、施工のスピードが向上し、作業の遅延を最小限に抑えることができます。また、カスタム鋼材は、設計段階での精度が高いため、現場での調整が不要となり、結果的にコスト削減につながります。

さらに、オーダーメイド加工により、資材の無駄を減らすことができます。必要な寸法に正確に加工されるため、余分な材料が生じにくく、環境負荷を軽減することにも寄与します。メルボルンでの優れた鉄骨製作サービスは、[構造用スチールファブリケーターズ（Structural Steel Fabricators）でご覧ください](https://www.structuralsteelfabricators.com.au/)。

## 構造の強度と耐久性
オーダーメイドの鋼材加工は、建物の構造的な強度と耐久性を向上させる点でも非常に有効です。カスタム鋼材は、特定の設計要件や環境条件に合わせて最適化されるため、標準的な鋼材と比べて、より高い性能を発揮します。例えば、地震や強風に対する耐性を強化するための特別な加工が施されることがあります。

また、鋼材そのものの品質も重要です。高品質の鋼材を使用することで、長期的な耐久性が確保され、メンテナンスコストの削減にもつながります。これにより、建物の寿命が延び、投資価値が向上します。

## 見た目の美しさとデザイン性
オーダーメイドの鋼材加工は、建築デザインの美しさと独自性を高めるための強力なツールです。カスタム加工により、通常の鋼材では難しい繊細なデザインや、特定の意匠を反映した部品を作り出すことが可能です。これにより、建物全体のデザイン性が向上し、他にはない個性的な外観を実現することができます。[StructuralSteelFabricatorsで住宅建設におけるスチール](https://www.structuralsteelfabricators.com.au/reasons-to-use-steel-in-residential-construction/)の利点をご覧ください。

特に、商業施設や公共建築物では、デザインの独自性が注目を集める要因となるため、オーダーメイド鋼材の使用は、ブランドイメージの向上や利用者の満足度向上にも貢献します。

オーダーメイドの鋼材加工は、建築デザインにおいて多くの利点を提供します。デザインの柔軟性、プロジェクトの効率化、構造の強度、そして美観の向上といった点で、建築プロジェクトにおける成功の鍵となります。プロフェッショナルな鋼材加工業者と協力することで、より高品質で耐久性のある建物を実現し、デザインの可能性を最大限に引き出すことができるでしょう。
【Linux】readコマンドの使い方

## 基本的な使い方
`read`コマンドを使用するとユーザーからの入力を受け取ることができます。
基本的な構文は以下の通りです。

```bash
read [オプション] [変数名...]
```

ユーザーからの入力を受け取り、その入力を変数に格納します。

```bash:read.sh
#!/bin/bash

read -p "名前を入力してください:" name
echo "こんにちは、${name}さん！"
```

このスクリプトを実行すると、ユーザーに名前の入力を促し、`こんにちは、sampleさん！`のように出力します。

```terminal:ターミナル
$ bash read.sh
名前を入力してください:sample
こんにちは、sampleさん！

$ bash read.sh
名前を入力してください:test
こんにちは、testさん！
```

## 注意事項

Zshで`read -p "名前を入力してください:" name`を実行すると以下のようなエラーが発生します。
Bashではなく、Zshで実行する場合には`read.sh`を以下のように書き換えます。

```zsh:read.sh
#!/bin/zsh

read name"?名前を入力してください："
echo "こんにちは、${name}さん！"
```

```terminal:ターミナル
$ zsh read.sh
名前を入力してください:sample
こんにちは、sampleさん！
```

または、`-p`を使用せず`echo`でプロンプトを出力します。

```zsh:read.sh
#!/bin/zsh

echo -n "名前を入力してください:"
read name
echo "こんにちは、${name}さん！"
```
JavaScript　初心者落書き(全く正しくありません)

あくまでイメージです

# コールスタック
同期処理タスクの待ち行列
# スタックキュー
非同期処理の待ち行列。ここにいるタスクはコールスタックが空になればタスクキューからコールスタックに戻ってくる。

# setTimeout
setTimeout(cb, 時間）で表す。中の処理は実行時にスタックキューに送られ、時間後にコールスタックに戻ってくる。

# promise()
- new promise((resolve,reject) =>{});
- 中の処理が完了しているならresolve,完了しないならreject状態になる。
また、resolve(),reject()の関数で状態を変更できる。
- resolve状態ならthenメソッドに、reject状態ならcatchメソッドに進む。
- catchが実行されたらpromiseはresolve状態となる
- thenメソッドcatchメソッドにはresolve(),reject()の関数の引数が渡される。
- thenをチェーンしたい場合。(promise.then().then())⇒thenの中でpromiseをリターンする必要がある。



# promise.all
- いくつかのpromiseからなる配列を引数としたメソッド。引数となるpromiseたちがいずれもresolve状態ならresolve状態になる

# promise.race
- いくつかのpromiseからなる配列を引数としたメソッド。引数となるpromiseたちのいずれかがresolve状態ならresolve状態になる

# async
async 関数
- promiseを生成する関数となる。
- またその関数のreturnの値を引数としてresolveする。
- rejectにしたいときはthrow new Errorの値を引数としてrejectとする。

# await 
- その関数内のあるプロミスの処理が終わるまでその後の処理を始めない。
- awitはasinc関数の中でしか使えない。
- X = awaitプロミス名　ならxへの代入、それ以降の処理はプロミスの結果が返ってくるまで行われない。


## await時のエラー処理
awaitでエラーが出た場合はそれ以降の処理は実行できないので.catch()は使えない。
try{}catch(e){}を使う。


# ここまでの勉強の意味
## 非同期処理
- 遅い処理でユーザーを待たせたくなかった
- ただし順序制御が難しかった
## コールバック関数
- 非同期タスクの順序を制御することができる
- 関数A(タスクA；関数B(タスクB))となっているとき、
タスクAが実行されてから関数B(タスクB)に移るという順序が制御できた。
### コールバック関数の注意
メソッドや関数の引数として渡されるコールバック関数には();をつけない。関数の定義を入れる。
# api
 公開されている便利サービス

# fetch
- サーバーとの非同期通信を行うためのメソッド。apiを使えるようになる。
- プロミス形式,戻り値はレスポンス

`` fetch("https://example.com/api/endpoint")
  .then(response => {
    // レスポンスを処理するコード
  })
  .catch(error => {
    // エラー処理
  }); ``

  という形式で記述。
  レスポンスはjson形式で帰ってくることが多い。

# async/awaitを使ったfetch()
async 関数{
 try{response = await fetch()
    //何らかの処理}
 catch(error){} }
fetchData(); 

async:プロミスを関数で作成できる.※プロミス自体は非同期処理ではない。
await:直後のプロミスがresolveされるまでasync関数内の処理が止まる。なおreject時はcatchへ
- 非同期処理の戻り値を変数に代入したいとき便利。
- 通常は非同期処理の終了を代入が待ってくれない。
- await抜きで変数＝promise(非同期処理)としてもpromiseが処理中であるという情報が代入されるだけ