OmniAICreator/Qiita-1.07M huggingface dataset:
負荷ツール vegeta を使って multipart/form-data で画像を送る方法

## はじめに
負荷テストツールの vegeta　を使った画像を送る API のパフォーマンステストを実施したときに、
multipart/form-data で画像を送る設定に詰まったので、備忘録として記録します。

## vegeta とは
**あのベジータ**から名前を取った、 golang 製の負荷テストツールです。
https://github.com/tsenart/vegeta

## やり方
### 概要

テキストファイルを作ることで、 vegeta で POST API をテストする際の、パラメータを設定することが出来ます。

### body.txt を作成する

画像のバイナリデータをテキストファイルに入れる際に、 Go で以下のようなコードを書いて、 body.txt を作成します。

```go
package main

import (
   "bytes"
   "fmt"
   "io"
   "io/ioutil"
   "mime/multipart"
   "os"
)

func main() {
   // 送信するバイナリデータ
   imageFile, err := os.Open("./img/hoge.jpeg")
   if err != nil {
      fmt.Println("ファイルを開く際にエラーが発生しました:", err)
      return
   }
   defer imageFile.Close()

   // マルチパート形式のリクエストボディを作成
   var requestBody bytes.Buffer
   multipartWriter := multipart.NewWriter(&requestBody)

   // 1つ目のパート (画像ファイル)
   imagePart, err := multipartWriter.CreateFormFile("file", "hoge.jpeg")
   if err != nil {
      fmt.Println("ファイルパートを作成する際にエラーが発生しました:", err)
      return
   }
   _, err = io.Copy(imagePart, imageFile)
   if err != nil {
      fmt.Println("ファイルのコピー中にエラーが発生しました:", err)
      return
   }

   // 2つ目のパート (テキストデータ)
   err = multipartWriter.WriteField("key1", "value1")
   if err != nil {
      fmt.Println("フィールドを書き込む際にエラーが発生しました:", err)
      return
   }

   // 3つ目のパート (テキストデータ)
   err = multipartWriter.WriteField("key2", "value2")
   if err != nil {
      fmt.Println("フィールドを書き込む際にエラーが発生しました:", err)
      return
   }

   // マルチパートの終端を書き込む
   err = multipartWriter.Close()
   if err != nil {
      fmt.Println("マルチパートの終端を書き込む際にエラーが発生しました:", err)
      return
   }

   // リクエストボディをテキストファイルに保存
   err = ioutil.WriteFile("body.txt", requestBody.Bytes(), 0644)
   if err != nil {
      fmt.Println("リクエストボディを保存する際にエラーが発生しました:", err)
      return
   }

   fmt.Println("リクエストボディがテキストファイルに保存されました。")
}
```

#### 完成したファイル

```body.txt
--HogeHogeBoundary
Content-Disposition: form-data; name="file"; filename="hoge.jpeg"
Content-Type: image/jpeg

{画像のバイナリ}
--HogeHogeBoundary
Content-Disposition: form-data; name="key1"

value1
--HogeHogeBoundary
Content-Disposition: form-data; name="key2"

value2
--HogeHogeBoundary--
```

### targets.txt を作成する

targets.txt 以下のように作成します。

```targets.txt
POST https://hogehoge.jp/upload-image
Content-Type: multipart/form-data; boundary=HogeHogeBoundary
@body.txt
```

### コマンド実行

```shell
vegeta attack -targets=targets.txt -rate 5 -duration 60s
```

これで、画像アップロードの API がテスト出来ます！

## おわりに

最初バイナリをシェルで作成して、それをコピペするということをやっていましたが、
上手く行きませんでした。
なので、Go を使って直接作成したバイナリを書き込む方法に変更しました。

## 参考文献
https://qiita.com/nrnrk/items/4e0dab2c8573e3fe7576
squidを使ってみる

## やりたいこと
squidを通ってwebサーバーにアクセスする。

![構成図.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636718/e2152965-f63b-7291-c536-3658e99209d5.jpeg)

## squidのインストール
```
[root@proxy ~]# yum install -y squid
```

## squidの設定ファイルの編集

```/etc/squid/squid.conf
acl client2 src 192.168.56.108　★ACLの設定
http_access allow client2　★クライアント２を許可する
```

squidの再起動をする
```
[root@proxy ~]# systemctl restart squid
```

## webブラウザでプロキシを使うように設定

firefoxで設定→インターネット接続から手動でプロキシーを設定するを選択。
プロキシのIPアドレスとポートを入力

## クライアント２からwebサーバーに接続してみる

![接続確認.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636718/003a935c-9aca-5078-be21-5ad8e66ae704.jpeg)

プロキシをちゃんと通って接続できているかログ確認する。

```/var/log/squid/access.log
1688543426.843     11 192.168.56.108 TCP_MISS/200 405 GET http://web.linux.com/ - HIER_DIRECT/192.168.56.105 text/html
```

ステータスコード200なので無事にGETリクエストが完了していることを確認。
TCP_MISSはプロキシにキャッシュがないということなので通信が失敗しているといことではないです。

WEBサーバー側でもログを確認する

```/etc/httpd/logs/access_log
192.168.56.110 - - [05/Jul/2023:16:50:26 +0900] "GET / HTTP/1.1" 200 15 "-" "Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0"
```

ちゃんとステータスコード200になっていることを確認

## アクセスを拒否してみる

```/etc/squid/squid.conf
- http_access allow client2
+ http_access deny client2　★denyに変更する
```

squidの再起動をする
```
[root@proxy ~]# systemctl restart squid
```

webサーバーに接続してみる

![接続拒否.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636718/1b8e33f8-8ecd-188f-ec26-f2441428386d.jpeg)


squidのログを確認する

```/var/log/squid/access_log
1688544275.006      0 192.168.56.108 TCP_DENIED/403 4297 GET http://web.linux.com/ - HIER_NONE/- text/html
```

squidで通信を拒否しているためWEBサーバーにはログはありません。

![構成図.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/636718/d4dd5659-2e59-1db1-b86f-9987036eedea.jpeg)

## キャッシュを利用してみる

```/etc/squid/squid.conf
cache_dir ufs /var/spool/squid 100 16 256　★#を消す
```

ディレクトリをキャッシュディレクトリとして使用し、そのサイズを100MBに設定し、最大16の第一レベルのサブディレクトリと、それぞれの第一レベルのサブディレクトリあたり最大256の第二レベルのサブディレクトリを作成することを指示しています。

```/var/log/squid/access.log
1688546677.909      0 192.168.56.108 TCP_INM_HIT/304 304 GET http://web.linux.com/ - HIER_NONE/- text/html
```

TCP_INM_HITとなっているのでキャッシュを返されていることが分かります。

2つ目の項目が0となっていてこれはリクエストが完了するまでの時間です。
0となっているので即座にリクエストが完了していることが分かります。
また、HIER_NONE/-となっているためローカルキャッシュから取得されています。
-は、取得元のサーバーが存在しないことを示します。

初めてアクセスしたときのログと比較してみましょう

```
初めてアクセス
1688543426.843     11 192.168.56.108 TCP_MISS/200 405 GET http://web.linux.com/ - HIER_DIRECT/192.168.56.105 text/html

キャッシュを返してる
1688546677.909      0 192.168.56.108 TCP_INM_HIT/304 304 GET http://web.linux.com/ - HIER_NONE/- text/html
```

初めてアクセスしているときは2つ目の項目が11になっています。
 HIER_DIRECT/192.168.56.105となっているので192.168.56.105にデータを取りに行っていることが分かります。
Akamai's cloud computing servicesのアカウント設計を考える

# Akamai's cloud computing servicesのコンソールについて

執筆時時点でAkamai's cloud computing servicesのコンソールであるAkamai Cloud Managerは、CDNなどを管理するコンソールである​Akamai Control Center​と統合されていません。
そのため、現状の段階でAkamai's cloud computing servicesは、どのようなアカウント構成がベストな選択肢であるかを理解して利用する必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49877/3c424938-cd1e-d65a-772c-4a149d02701f.png)

[Akamai Control Centerについて](https://techdocs.akamai.com/control-ctr/docs)

[Akamai Cloud Managerについて](https://www.linode.com/docs/products/tools/cloud-manager/)


## 契約と支払いについて

Akamai's cloud computing servicesを利用開始するには、いくつか方法があります。

1. Akamai's cloud computing servicesのWebからアカウントを開設し、クレジットカードで支払いを行う。（USドルベースで支払い）
https://www.linode.com/
2. Akamaiの担当営業へ連絡のうえ契約を行い、アカウントを開設する。（日本円での支払いに対応）
https://www.akamai.com/ja/why-akamai/contact-us/contact-sales

個人利用等の場合には1の方法でアカウントを開設します。
既存でAkamaiとの契約を有している場合には、2の方法でアカウントを開設することをお勧めします。

## アカウントの設計について

Akamai's cloud computing servicesの1つのアカウントで複数のシステムを構築することもできますが、アカウントを分けたほうが良いケースがあります。

**1. 構築したシステムごとに請求先が異なる場合**
部署やグループ会社ごとに請求を行いたい場合など
**2. 権限を分けたい場合**
構築したシステムの権限を完全に分けたい場合など

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49877/4b45a197-889b-6a08-dddb-d7d6f0d83093.png)


## 複数のアカウントの開設について

アカウント開設の際にはUsernameとMail Addressの登録が必要になりますが、作成時には注意すべき点があります。

- Username : Akamai's cloud computing services全体でユニークである必要があります。
- Mail Address : ほかのアカウントで利用していても問題ありません

Akamaiとすでに契約を有している場合には既存の契約とAkamai's cloud computing servicesへとのアカウントの紐づけが必要となります。
複数のアカウントを作成して請求を分けたい場合には、複数の契約を作成し、アカウントをそれぞれ紐付ける必要があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/49877/c61ec37c-31e5-1bcb-ee03-f41393b26384.png)

上記のようにアカウントを分割することによって、部門ごとの請求金額を明確にしたり、権限を分割することができます。

# まとめ

Akamai's cloud computing servicesを複数の部署で分けて利用する場合、アカウントを分けて請求金額を明確にしておくと、各部署の利用料も把握しやすくなります。
ぜひ、Akamai's cloud computing servicesを活用して、多くのシステムを構築してみてください。


# 関連記事

アカマイ・テクノロジーズ合同会社はQiitaでAkamai's cloud computing services関連など開発者向けの記事を掲載しております。

https://qiita.com/xenbase/items/52e2956a64cedc772bd9
OpsRamp カスタムフィールドを使用したChatシステムのスレッドへのインシデント通知

本記事では、チケットのカスタムフィールドを用いてインシデントの作成、更新時の通知をChatシステムのスレッドに連携する方法について記載します。

# 概要
OpsRampには、チケット情報の項目にユーザが任意の値を設定できるカスタムフィールドを設定することができます。
これを使用することで、インシデント作成の通知連携時にChatシステムが返すレスポンス内に含まれるスレッド情報を保存し、そのスレッド情報をインシデント更新時に付加し、Chatシステムの既存スレッドに通知することが可能です。

# 設定方法
- カスタムフィールドの設定
Setup > Service Desk > Custom Formsの「Incident」タブから「T」マークを選択し、下記項目を入力します。(ここにChatシステムのスレッド情報を格納します。)
Display Label: 任意
Mandatory field?: 無効
Is editable?: 無効
Visible to Business User: 無効
Description (optional): 任意
Default Value: 空
![custom-form_config.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/6518c386-9452-8501-5ea8-647fa0bc0049.png)

- カスタムインテグレーションの設定 (Chatシステム連携)
Setup > Integrations > Integrationsの「Other」カテゴリの「Custom Integration」を追加します。
![custom_integration_1.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/c0bc6b82-c625-dcc8-c1c3-640d20ab688a.png)
「Name」、「Description」に任意の文字列を入力し、「Category」に「Custom」を選択します。
![custom_integration_2.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/2f30173e-1bcc-32f8-6f50-633e506e41f4.png)
「Outbound(OpsRamp to custom)」タブの「3. Integration Events」の「Add」からイベントを追加し、下記項目を入力します。これにより、インシデント発行時にChatシステムへ通知およびChatシステムから返ってきたレスポンス内のスレッド情報をカスタムフィールドに格納することができます。
Name: 任意
On: 「Service Desk」、「Incident」、「Create」
Endpoint URL: 任意、ChatシステムのURL
Notification Type: 任意
Authentication Type: 任意
Web Method: 任意
Headers: 任意
Payload: 任意
Response: 上記で作成したカスタムフィールドを選択し、Chatシステムに連携時に返ってきたレスポンス内のスレッド情報を指定 (指定方法は「\$変数名」)
::: note warn
JSONまたはXMLのみ対応
:::
![custom_integration_4.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/5381dbb1-0f1e-cd5f-3e3c-df2f99c99ed5.png)
「Outbound(OpsRamp to custom)」タブの「3. Integration Events」の「Add」からイベントを追加し、下記項目を入力します。これにより、インシデント更新時にChatシステムへ通知します。また、その際に上記でスレッド情報を格納したカスタムフィールドをリクエスト内に含めることにより、Chatシステム側でスレッドとしてメッセージを投稿することが可能です。

::: note info
「Place Holders」の「Custom Fields」から指定することが可能です。
:::
Name: 任意
On: 「Service Desk」、「Incident」、「Update」
Endpoint URL: 任意、ChatシステムのURL
Notification Type: 任意
Authentication Type: 任意
Web Method: 任意
Headers: 任意
Payload: 任意
![custom_integration_5.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/ab47a532-0f73-7f54-4019-e5bdc761681d.png)


---------------------------------------
[<img width = 250 height = 200 src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/111de30a-4d33-aa33-5c7d-83d9015093e6.png">](https://www.vistanet.jp/knowledge-base/)
【ほぼコピペで完成】スクロールに合わしてヘッダーを表示・非表示する

# 経緯
**ちょっとオシャレ？なヘッダーを作ってみたくなった備忘録です。**

railsで普段開発してるので最後はrailsで導入する方法もサラッと書いてます。

# 完成形
<p class="codepen" data-height="400" data-default-tab="result" data-slug-hash="mdQMRjN" data-user="okok111" style="height: 300px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/okok111/pen/mdQMRjN">
  Untitled</a> by okok111 (<a href="https://codepen.io/okok111">@okok111</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

# 実装
- と言いつつも上のcodePenに書いてあることコピペしてもらえれば何とかなるはず。
- 見栄えのために書いた不必要な部分があるので必要な部分だけ解説付きで書くとこんな感じ。

```html:html部分
~~~~省略~~~~~
<body>
  <header>
headerの内容
  </header>
  <main>
mainの内容
  </main>
</body>
```
:::note info
headerとmainの要素あれば何とかなります。
:::

---

```css:cssの部分
header{
  height: 80px;
  width: 100%;
  position: fixed;
  top: 0;
  z-index: 99;
  transition: transform 0.3s 0.3s;
}
header.hidden {
    transform: translateY(-80px);
}
main{
  padding-top: 80px;
}

```
:::note info

- `header`の`height: 80px;`
- `header.hidden`の`transform: translateY(-80px);`
- `main`の`padding-top: 80px;`

これらの数値 **(80pxの所)** を変更する場合は揃えるようにしましょう。
:::

---

```javascript:javascriptの部分
const header = document.querySelector('header');
let prevY = window.scrollY; 

window.addEventListener('scroll', () => {
  const currentY = window.scrollY;
  if (currentY < prevY) { 
    header.classList.remove('hidden'); 
  } else { 
  if (currentY > 0) {
      header.classList.add('hidden'); 
    }
  }
  prevY = currentY; 
});
```
:::note info

- コピペで大丈夫です
- htmlに直接貼り付ける場合は `<script></script>`で挟むようにしてください。
:::

以上です。

# おまけ

railsで作ってる人に向けて普段発信しているのでrailsで適用する方法も置いときます。

## htmlの記述
```erb:application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>movie Memo</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
    <header>
    </header>
    <%= javascript_pack_tag 'header' %>
    <main>
      <%= yield %>
    </main>
  </body>
</html>

```

:::note info
- 後ほど作成するJSファイルと紐づけるための`<%= javascript_pack_tag 'header' %>`を追記
- `<%= yield %>`を`main`で囲む

この二点だけご注意ください。
:::

## cssの表記
前述と内容は同じなので省略

## JavaScriptの表記

* `app/javascript/packs/`下に`header.js`を作成してください

```javascript:app/javascript/packs/header.js
const header = document.querySelector('header');
let prevY = window.scrollY; 

window.addEventListener('scroll', () => {
  const currentY = window.scrollY;
  if (currentY < prevY) { 
    header.classList.remove('hidden'); 
  } else { 
    if (currentY > 0) {
      header.classList.add('hidden'); 
    }
  }
  prevY = currentY; 
});

```
::: note info
内容は前述のJavaScriptの物と変わりはないです。
:::
型システムのアップデート: 研究から開発への移行 by José Valim

原文: https://elixir-lang.org/blog/2023/06/22/type-system-updates-research-dev/

1年前のElixirConf EU 2022で，私たちはElixirの型システムを研究開発する取り組みを発表しました．[動画](https://www.youtube.com/watch?v=Jf5Hsa1KOc8) [報告書](https://elixir-lang.org/blog/2022/10/05/my-future-with-elixir-set-theoretic-types/) [訳註: 報告書日本語訳](https://qiita.com/zacky1972/items/6c9cc82d9ba5f83e76d2)

この研究は，CNRS 上級研究員の [Giuseppe Castagna](https://www.irif.fr/~gc/) の指導の下で行われており，[Guillaume Duboc](https://www.irif.fr/users/gduboc/index) が博士課程の研究の一環として担当し，さらに私 (José Valim) が指導しています．

この記事は，私たちの取り組みの現状と今後の方向性をまとめたものです．

## 研究完了

研究中の私たちの主な目標は，Elixir の機能のセマンティクス(意味論)のほとんどをモデル化できる型システムを見つけること，さらに互換性がない，または欠けているとわかった領域についてまったく新しい理論を開発することです．私たちは段階的な集合論型システムでこの目標を達成できたと信じており，現在開発に向かう準備ができています． 過去2か月にわたって，私たちは結果に関する多くのリソースを公開してきました．

* [Elixirの型システムの設計原則に関する技術レポート(訳註: 日本語訳計画中)](https://arxiv.org/abs/2306.06391)
* [上記の研究に関する ElixirConf 2023 での Guillaume Duboc による技術プレゼンテーション](https://youtu.be/gJJH7a2J9O8)
* [SmartLogic ポッドキャストでの Giuseppe Castagna，Guillaume Duboc，José Valim の非公式ディスカッション](https://smartlogic.io/podcast/elixir-wizards/s10-e12-jose-guillaume-giuseppe-types-elixir/)
* [Guillaume Duboc，José Valim，Twitch コミュニティでの非公式 Q&A](https://www.twitch.tv/videos/1841707383)

これまでのところ，セマンティクス(意味論)に焦点を当ててきました．新しい集合論的型システムのセマンティクスを表現できる新しい構文を導入しましたが，ユーザー向けのプログラミング言語の変更に関する具体的な計画がまだないため，この構文は最終的なものではありません．これらの変更を行うと確定したときに，型システム・インターフェイスとその構文についてコミュニティと十分な議論を行う予定です．

これまでの取り組みは，[Fresha](https://www.fresha.com)，[Supabase](https://supabase.com)，[Dashbit](https://dashbit.co) からのスポンサーシップによる [CNRS](https://www.cnrs.fr/fr) と [Remote](https://remote.com) のパートナーシップのおかげで可能になりました．

## 開発開始

研究はまだ進行中ですが，2023年後半以降は開発に重点を置きます．

大規模に使用される言語に型システムを組込むことは，困難な作業になる可能性があります．私たちの懸念は，コミュニティが型システムとどのように対話して使用するかから，大規模なコードベースでどのように実行されるかまで，多岐にわたります．したがって，私たちの計画は，(冗談を意図した)漸進的型システムを漸進的に(徐々に)Elixirコンパイラーへ導入することです．

最初のリリースでは，型はコンパイラーによって内部的にのみ使用されます．型システムは，言語にユーザー側の変更を加えることなく，パターンとガードから型情報を抽出し，フィールド名のタイプミスや文字列に整数を追加しようとしたときの型の不一致など，最も明らかな間違いを見つけます．この段階での主な目標は，型システムのパフォーマンスへの影響と，型指定違反の場合に生成できるレポートの品質を評価することです．結果に満足できない場合は，自分の仕事を再評価するか，主導権を完全に放棄する時間がまだあります．

2番目のマイルストーンは，Elixirコードベースで名前が付けられ静的に定義される構造体のみに型アノテーションを導入することです．Elixirプログラムは構造体のパターン・マッチを頻繁に実行し，構造体フィールドに関する情報を明らかにしますが，それぞれの型については何も知りません．プログラム全体にわたって構造体とそのフィールドから型を伝播することで，型システムの実装にさらに負担をかけながら，型システムのエラー検出能力を向上させます．

3番目のマイルストーンは，関数に(おそらく)`$`を接頭辞(prefix)としてつけた型アノテーションを導入することです．型の再構築はまったく行われないか，非常に限定されています．ユーザーはコードに型の注釈を付けることができますが，型のないパラメータは`Dynamic()`型であると想定されます．成功すれば，事実上，言語に型システムが導入されたことになります．

この新しいエキサイティングな開発段階は，[Fresha](https://www.fresha.com)[(採用中!)](https://www.fresha.com/careers/openings?department=engineering)，[Starfish*](https://starfish.team)[(採用中!)](https://starfish.team/jobs/experienced-elixir-developer)，[Dashbit](https://dashbit.co)によって後援されています．
#6 作成したAMIから2つ目のEC2インスタンスを起動する

# はじめに
[#6で作成した](https://qiita.com/kokichi8/items/b6cff42945b902f57bf8)AMIから、以下の構成図の通り2つ目のインスタンスを作成します。
![ami-2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3501605/9931564f-5a68-38a1-7cf9-fdd6075b25e1.png)

# 手順
## 設定値の指定
```
#リージョンの指定
export AWS_DEFAULT_REGION='ap-northeast-1'

#ec2インスタンスのタグ名
EC2_INSTANCE_TAG_NAME='handson-ec2-1st-instance'

#起動時に利用するAMIイメージ名
EC2_INSTANCE_IMAGE_NAME="cli-ami"

#ec2インスタンスタイプ
EC2_INSTANCE_TYPE="t2.micro"

#ユーザデータファイル名
USER_DATA_NAME='handson-ec2-1st-httpd-userdata'

DIR_USER_DATA="${HOME}/environment/conf-handson-ec2-1st"

FILE_USER_DATA="${DIR_USER_DATA}/${USER_DATA_NAME}.bash" \
  && echo ${FILE_USER_DATA}
```
## 設定値の確認
```
cat << END

  # 0. AWS_DEFAULT_REGION:"ap-northeast-1"
       AWS_DEFAULT_REGION="${AWS_DEFAULT_REGION}"

  # 1. EC2_INSTANCE_TAG_NAME:"handson-ec2-1st-instance"
       EC2_INSTANCE_TAG_NAME="${EC2_INSTANCE_TAG_NAME}"
  # 2. EC2_INSTANCE_IMAGE_NAME:"ami-xxxx"
       EC2_INSTANCE_IMAGE_NAME="${EC2_INSTANCE_IMAGE_NAME}"
  # 3. EC2_INSTANCE_TYPE:"t2.micro"
       EC2_INSTANCE_TYPE="${EC2_INSTANCE_TYPE}"
  # 4. FILE_USER_DATA:"${HOME}/environment/conf-handson-ec2-1st/handson-ec2-1st-httpd-userdata.bash"
       FILE_USER_DATA="${FILE_USER_DATA}"

END

```
## 処理の実行
* `run-instances`コマンドを使います。
* `EC2_INSTANCE_IMAGE_ID`で設定したAMIを`--image-id`オプションで指定することで、2つ目のEC2インスタンスを作成します。
```
#イメージID取得
EC2_INSTANCE_IMAGE_ID="ami-xxxx"

#イメージIDの変数確認

cat << END

EC2_INSTANCE_IMAGE_ID="ami-xxxx"
EC2_INSTANCE_IMAGE_ID="${EC2_INSTANCE_IMAGE_ID}"

END


#タグ設定文字列の生成
EC2_TAG_KEY='Name'

STRING_TAG_CONF_INSTANCE="ResourceType=instance,Tags=[{Key=${EC2_TAG_KEY},Value=${EC2_INSTANCE_TAG_NAME}}]" \
  && echo ${STRING_TAG_CONF_INSTANCE}

#2台目のインスタンス作成
aws ec2 run-instances \
--security-group-ids sg-xxxx \
--subnet-id subnet-xxxx \
  --image-id ${EC2_INSTANCE_IMAGE_ID} \
  --instance-type ${EC2_INSTANCE_TYPE} \
  --tag-specifications ${STRING_TAG_CONF_INSTANCE} \
  --user-data file://${FILE_USER_DATA} \
  --associate-public-ip-address
```

# 完了確認
* `describe-instances`コマンドで、作成したインスタンスを確認します。
```
aws ec2 describe-instances \
  --filters Name=tag-key,Values=Name \
            Name=tag-value,Values=${EC2_INSTANCE_TAG_NAME} \
            Name=instance-state-name,Values=running \
  --query 'Reservations[].Instances[].Tags[].Value' \
  --output text
```
# #7 RDSのマルチAZ配置
* [AWS CLIでスケーラブルウェブサイトを構築する](https://qiita.com/kokichi8/items/fedb84896a286b70cc23)
Djangoのマイグレーション実行時に独自のクエリを実行する

マイグレーションファイルに`migrations.RunPython`で処理を書くと実行されます

## 例（テーブルを作成した後にマスタデータを入れる）
```python
# Generated by Django 3.2.18 on 2023-05-22 02:24

from django.db import migrations, models
import django.db.models.deletion

def insert_member_status(apps, schema_editor):
    from api.models.member_status import MemberStatus
    MemberStatus(name='仮会員').save()
    MemberStatus(name='本会員').save()
    MemberStatus(name='退会').save()
    MemberStatus(name='メルマガ会員').save()

class Migration(migrations.Migration):

    dependencies = [
        ('api', '0152_auto_20230517_1058'),
    ]

    operations = [
        migrations.CreateModel(
            name='MemberStatus',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
                ('name', models.CharField(max_length=255)),
                ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='created')),
                ('updated_at', models.DateTimeField(auto_now=True, verbose_name='updated')),
                ('deleted_at', models.DateTimeField(blank=True, null=True, verbose_name='deleted')),
            ],
            options={
                'abstract': False,
            },
        ),
        # マスタデータを入れる
        migrations.RunPython(insert_member_status),
    ]
```

直接クエリで書くこともできます
```python
from django.db import connection

def insert_member_status(apps, schema_editor):
    with connection.cursor() as cursor:
        sql = """
        INSERT INTO `api_member_status` (`name`, `created_at`, `updated_at`, `deleted_at`)
        VALUES
            ('仮会員', '2023-06-02 00:00:00.000000', '2023-06-02 00:00:00.000000', NULL),
            ('本会員', '2023-06-02 00:00:00.000000', '2023-06-02 00:00:00.000000', NULL),
            ('退会', '2023-06-02 00:00:00.000000', '2023-06-02 00:00:00.000000', NULL),
            ('メルマガ会員', '2023-06-02 00:00:00.000000', '2023-06-02 00:00:00.000000', NULL);
        """
        cursor.execute(sql)
        cursor.fetchone()

        sql = """
        UPDATE api_csvtypes SET is_update_only = 1, skip_function = "sales_plan_skip" WHERE id = 14;
        """
        cursor.execute(sql)
        cursor.fetchone()
```

あとDjangoのマイグレーションで作られないALTER文流したりとかもできると思います
lubuntuはオススメ

## lubuntuに感動したので記事を書きます。

WindowsノートにVirtualboxをインストールし、そこにLinuxVMを立てて開発しているエンジニアです。
Windowsも悪くはないですが、どうしても慣れ親しんだLinuxコマンドを使いたくなる。

WSLを利用してUbuntuコンテナを使うのもありなんですが、
なんかOSじゃないと落ち着かない…。

## 最初はUbuntuDesctop22.04の公式ISOで使っていた。
UbuntuってCentOSと比べて、ちょっとしたものが動かしやすいし、GUIも親切。
ただ、遅い…。
使っていくうちになんか起動が遅くなってきた。
GUI使わなければよいというのもあるんだけど、画像とか確認す
るときにServerOSだけだと手間がかかるので。
UbuntuDesktop22.04はなんやかんや1.5GBくらい常時メモリを使うような感じになってしまった。


## ウィンドウマネージャという結論に至る。
不要なものを削除し、使っていないプロセスを停止したけど、
どうにもメモリもやけに使ってる気がするし、昔はもっと軽かったのにな…。と。

そういや以前、awesomeっていうウィンドウマネージャにしたら早くなったのをおもいだした。
awesome自体、日本語が豆腐になってしまう（と記憶してます…）ので、日本語でも大丈夫なやつを調べてみた。

grub-pcというパッケージを使うと、ログイン画面でいくつかのウィンドウマネージャと切り替えられるらしい。

### Debian系LXDEが良かった。
だいぶ古い感じ、Windows95くらいなイメージ。
でも軽い。これで十分だ。

### lubuntu22.04へ乗り換え。
なんか色々なソフトが勝手に入ってて消してた。（LibreOfficeとか要らない。）
/var/spool/crash/にも起動するたびになにか吐いてるし。

いっそのこと最初からLXDEのUbuntuにしてしまおう。
ということで、lubuntuを起動。

### 満足しました。
起動直後でメモリ使用が500MB以下。日本語表示もOK。
MOZCはいれないと不便だ。
壁紙もきれい。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213689/06413c3f-59a3-0efb-5ff5-5eaea8a672ec.png)

DockerやらVScodeやらなにやら色々入れないと駄目ですね。
不要なプロセスcupsは使わないから停止してしまおう。
sshdは入ってないようですね。
SCPで古いUbuntu22.04からデータ持ってこないと行けないので入れました。

またOSが重くなったら考えよう。
#5 AWS CLIでAMIを作成する

# はじめに
AWS CLIでAMIを作成します。構成図は以下の通りです。
![ami.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3501605/5df7a669-c793-7626-067e-60b7e36e97ec.png)

# 手順
## 設定値の指定
```
#インスタンスIDの指定
INS_ID="i-xxxx "
#インスタンス名の指定
INS_NAME="cli_ami"
```
## 設定値の確認
```
cat << END

INS_ID="i-xxxx"
INS_ID="${INS_ID}"

INS_NAME="cli-ami"
INS_NAME="${INS_NAME}"

END
```
## 処理の実行
* `create-image`コマンドを使います。
* `no-reboot`オプションを指定することで、AMI作成時にEC2の再起動を設定を**オフ**にします。

```
aws ec2 create-image \
    --instance-id ${INS_ID} \
    --name ${INS_NAME} \
    --no-reboot
```
[【参考】AMI 作成時に意図しない再起動がありました。なぜでしょうか？](https://dev.classmethod.jp/articles/tsnote-ec2-create-image-no-reboot/)

# #7 作成したAMIから2つ目のEC2インスタンス起動
* [AWS CLIでスケーラブルウェブサイトを構築する](https://qiita.com/kokichi8/items/fedb84896a286b70cc23)
* [#7 作成したAMIから2つ目のEC2インスタンス起動](https://qiita.com/kokichi8/items/9a3e21e798b2535d90a6)
Git 状況別 取り消し系コマンド

## 目次
- git add していない変更を取り消すコマンド
- git add を取り消すコマンド
- git commit を取り消すコマンド
- git push を取り消すコマンド
- git merge を取り消すコマンド
- commitメッセージの修正コマンド

## git add していない変更を取り消すコマンド
- ### git clean
    新規作成ファイルを消す

- ### git restore
    新規作成済み（push済）ファイルでの差分を取り消す
    （リモート追跡ブランチ"origin/ブランチ名"との差分を取り消す）

## git add を取り消すコマンド
- ### git restore --staged

- ### git reset

## git commit を取り消すコマンド
- ### git reset --soft HEAD^
    ステージング状態まで戻す
    （HEAD^ は現在地"HEAD"の一つ前"^"という意味）
- ### git reset --mixed HEAD^
    ステージング前（変更中）の状態まで戻す
- ### git reset --hard HEAD^
    変更内容をすべて取り消して戻す


## git push を取り消すコマンド
- ### git revert　と　git push
    打消しコミットを作った上で元に戻す

- ### git reset --soft　と　git push -f
    コミット履歴ごと消す

## git merge を取り消すコマンド
マージされた（受け取った側）のブランチで
- ### git reset --hard ORIG_HEAD
    履歴を残さない取り消し
- ### git revert -m 1または2 commit-hash
    打消しコミットを残す取り消し
    「1または2」 親コミットを指定する
    commit-hash　打ち消したいコミットハッシュを指定する　


## commitメッセージの修正コマンド
pushする前のみ有効！！
- ### git commit --amend -m "修正後のメッセージ"
cscの作法 その385

# 概要
cscの作法、調べてみた。
練習問題、やってみた。

# 練習問題
mdiでサブフォームから、メインフォームのボタンを消せ。

# サンプルコード

```
using System;
using System.IO;
using System.Windows.Forms;
using System.Drawing;

namespace App
{
	public partial class formSub: Form {
		TextBox bo1;
		public formMain fm;
		public formSub() {
			Text = "formSub";
			ClientSize = new Size(300, 200);
			bo1 = new TextBox();
			bo1.Location = new Point(50, 20);
			bo1.Text = "test desu";
			bo1.Size = new Size(100, 20);
			Controls.Add(bo1);
			Button btn1 = new Button();
			btn1.Location = new Point(50, 100);
			btn1.Text = "test";
			btn1.Click += btn1_Click;
			Controls.Add(btn1);
		}
		void btn1_Click(object sender, System.EventArgs e) {
			if (fm != null)
			{
				fm.ReceiveData = bo1.Text;
			}
		}
	}
	public partial class formMain: Form {
		public string ReceiveData {
			set {
				receiveData = value;
				bo1.Text = receiveData;
				Controls.Remove(btn2);
			}
			get {
				return receiveData;
			}
		}
		TextBox bo1;
		Button btn1;
		Button btn2;
		private string receiveData = "";
		public formMain() {
			Text = "formMain";
			BackColor = Color.Azure;
			ClientSize = new Size(120, 500);
			bo1 = new TextBox();
			bo1.Location = new Point(10, 20);
			bo1.Size = new Size(100, 20);
			Controls.Add(bo1);
			btn1 = new Button();
			btn1.Location = new Point(10, 70);
			btn1.Text = "sub";
			btn1.Click += btn1_Click;
			Controls.Add(btn1);
			btn2 = new Button();
			btn2.Location = new Point(10, 470);
			btn2.Text = "exit";
			btn2.Click += btn2_Click;
			Controls.Add(btn2);
		}
		void btn1_Click(object sender, System.EventArgs e) {
			formSub sub = new formSub();
			sub.MdiParent = MdiParent;
			sub.fm = this;
			sub.Show();
		}
		void btn2_Click(object sender, System.EventArgs e) {
			Application.Exit();
		}
	}
	public partial class formMdi: Form {
		public formMdi() {
			Text = "formMdi";
			ClientSize = new Size(900, 550);
			IsMdiContainer = true;
			formMain ma = new formMain();
			ma.MdiParent = this;
			ma.Show();
		}
		[STAThread]
		public static void Main() {
			Application.Run(new formMdi());
		}
	}
}





```

以上。
jetpack composeを使ったDeepLinkのNavigation実装方法

# はじめに
今回はjetpack composeでのNavigationを使ったDeepLinkの実装を紹介していきます
### 本文
まず、`Firebase`をアプリ内に`implement`します
```gradle
dependencies {
    // Import the BoM for the Firebase platform
    implementation(platform("com.google.firebase:firebase-bom:32.1.1"))

    // Add the dependencies for the Dynamic Links and Analytics libraries
    // When using the BoM, you don't specify versions in Firebase library dependencies
    implementation 'com.google.firebase:firebase-dynamic-links-ktx'
    implementation 'com.google.firebase:firebase-analytics-ktx'
}
```
次に`Activity`の`onCreate`内で下記のようにして`DeepLink`を取得します
```kotlin
Firebase.dynamicLinks
    .getDynamicLink(intent)
    .addOnSuccessListener(this) { pendingDynamicLinkData: PendingDynamicLinkData? ->
        // Get deep link from result (may be null if no link is found)
        var deepLink: Uri? = null
        if (pendingDynamicLinkData != null) {
            deepLink = pendingDynamicLinkData.link
        }
    }
    .addOnFailureListener(this) { e -> Log.w(TAG, "getDynamicLink:onFailure", e) }
```
あとは取得した`DeepLink`を`Navigation`に渡すだけです
### 最後に
今回は備忘録として最近の技術を諸々使った遷移方法を紹介していきました
どなたかのお役に立てれば幸いです
C#でChartクラスを使用してグラフを描画する方法


はじめに
-------------
以前、chartクラスを使用して、グラフを作成する時があり、いろいろと苦労したので、
使用方法について、記事を残します。

実行環境
-------------
- .NET Framework 4.7.2


目次
-------------
1. Chartクラスの概要
2. Chartコントロールの追加
3. データの追加
4. グラフのカスタマイズ
5. グラフの表示

Chartクラスの概要
-------------
Chartクラスは、System.Windows.Forms.DataVisualization.Charting名前空間に含まれています。これを使用することで、様々な種類のグラフ（折れ線グラフ、棒グラフ、円グラフなど）を描画することができます。

Chartコントロールの追加
-------------
まずはじめに、Chartコントロールをフォームに追加する必要があります。Visual Studioを使用している場合は、ツールボックスからChartコントロールをドラッグアンドドロップすることで簡単に追加できます。

データの追加
-------------
Chartコントロールにデータを追加するには、Seriesオブジェクトを作成し、Pointsコレクションにデータポイントを追加します。以下のコードは、折れ線グラフにデータを追加する例です。

```csharp
Chart chart = new Chart();
Series series = new Series("Sample Series");

// データの追加
series.Points.AddXY(1, 10);
series.Points.AddXY(2, 20);
series.Points.AddXY(3, 15);
// ...

chart.Series.Add(series);
```

グラフのカスタマイズ
-------------
Chartクラスを使用して、グラフの外観やスタイルをカスタマイズすることができます。例えば、グラフのタイトルや軸のラベル、グリッド線の表示などを設定できます。以下のコードは、グラフのタイトルと軸のラベルを設定する例です。

```csharp
chart.Titles.Add("Sample Chart");
chart.ChartAreas[0].AxisX.Title = "X軸";
chart.ChartAreas[0].AxisY.Title = "Y軸";
```

グラフの表示
-------------
Chartコントロールを

フォーム上に表示するには、適切な配置とサイズを設定する必要があります。以下のコードは、Chartコントロールをフォーム上に表示する例です。

```csharp
chart.Location = new Point(10, 10);
chart.Size = new Size(500, 300);
this.Controls.Add(chart);
```

おわりに
-------------
以上で、C#でChartクラスを使用してグラフを描画する方法についての説明が終わります。
簡単なグラフであればChartクラスで作成することが出来ますが、用意されているもの設定以外の細かいカスタマイズを行いたい場合はGraphicsを直接いじる必要が出てきます。

以上
Python,VScodeインストール

# Pythonインストール
https://www.python.org/downloads/
Download Pythonを押す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959926/0e20622f-abac-b9c1-a085-4bf71c41c478.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959926/07dbd89b-68ed-fdaa-301f-95b1827275a0.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959926/8b279e0f-892c-8103-2b81-618eb77393b3.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959926/a11f4a52-be8d-351c-6657-d7d209cbd0da.png)

うまくインストールできてるとコマンドプロンプトで`python --version`するとインストールしたPythonのバージョンを確認できます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959926/e323b585-1bb0-6922-1810-24070f0181f3.png)




# VScodeインストール
https://code.visualstudio.com/
Download for Windowsを押す
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959926/2694d5c1-83f6-5912-5b33-47119d6dc08e.png)

ダウンロードしたインストーラを開く
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959926/dff8ef06-9ee4-6db2-9ec9-bd6cee7a9f5c.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959926/f11bda41-1bf6-9f82-d1b6-5c5d6484e45a.png)

インストール場所は変えずに表示されたままで大丈夫です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959926/1e0de8c4-6235-67c2-23ac-b05a2bd8e50e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959926/2ac1966a-5d4a-c9d8-b575-3d40794f87bf.png)

デフォルトのままでも大丈夫ですが今回は全部選択します。（お好みに合わせて選択してください。PAHTの追加はあったほうが良いと思います）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959926/ae85e6c5-3e86-0bb0-8c00-a5e81fbf42c2.png)

インストールを押すと完了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959926/6178679c-140a-9e8a-cb0e-d948a45cc7a3.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959926/2e145262-578f-2042-c52e-4080a7ffdf48.png)

日本語化
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959926/1c971fee-9a05-84b8-f47b-22e7bc29d796.png)

Python拡張機能のインストール
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959926/f6a2708b-a127-3650-4a32-7ab9778fa45a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2959926/2f501f66-2074-c1f2-e650-7533d18f595f.png)
Stable Diffusionをローカルで使う方法（個人メモ）

![00121-4199940851.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/609493/12eef53c-92d7-f963-3faa-ddde4a6bb7a0.png)
Stable Diffusionをローカル環境で動かすためのメモです。

必要なもの
GPU RTX3090
Python 3.10.6
git
Fork
VScode
stable-diffusion-webui
( https://github.com/AUTOMATIC1111/stable-diffusion-webui.git )

※Python 3.10.6のバージョンは間違わないように気をつけてください。
python --version
でpathされているバージョンをチェック。
もし、間違えて他のバージョンでstable-diffusion-webuiを起動してしまった場合は他のpythonバージョンを削除し、stable-diffusion-webuiをgitから落とし直す

Python 3.10.6のダウンロード
https://www.python.org/downloads/release/python-3106/

gitのダウンロード
https://git-scm.com/download/win

Forkを使ってcloneする。（ローカルDにしました。）
https://github.com/AUTOMATIC1111/stable-diffusion-webui.git

プロンプト（cmd）でpip install xformersインストール

cloneしたstable-diffusion-webuiディレクトリ内の、
webui-user.bat
を書き換える。

set COMMANDLINE_ARGS= --autolaunch --xformers //ハイスペックGPUの場合。スペックによって書き換えるものが変わる。

webui-user.batをWcrickするとインストールが始まる。

しばらく、待っているとブラウザでstable-diffusionが起動する。
windowsでbundle install mysql2について

エンジニア学習4か月目に入りましてようやく自作ポートフォリオの設計をし始めたのですが、
その前にrailsのRDBとしてデフォルトのSQliteではなく、MySQLを使うのにとても苦労したのでもし初学者や未経験の方で同じような壁にぶつかっているwindowsユーザーの助けになればと思います！

※ぶっちゃけ最初のプログラミング言語としてrails触るならMacの方が簡単です(笑)
バージョン管理が優秀ですし…。


## MySQLのインストール
まずは[このページ](https://dev.mysql.com/downloads/)よりMySQL Community Serverをインストールします。
※インストールする具体的な設定は[こちらのサイト「](https://www.javadrive.jp/mysql/install/index2.html)を参考にしました。

次にWindows PowerShellで下記コマンドを打ってパスワードを設定します。
すでに設定済みの場合は、パスワードを入力してください。
```
mysql -u root -p
```
そうするとウェルカムメッセージが出るのでexitで終了します。
```
Welcome to the MySQL monitor.  Commands end with ; or \g.
・
・
・
mysql>
```
ここからデータベースをMySQLから作成することもできますが、railsから作成する方が流れとしてはいいと思います。
今回はあくまでWindowsにおけるrailsの環境構築の一環のため、データベースの作成などは割愛します。


## railsでMySQLを使えるように設定する。
#### まずは、MySQL Connector/Cのインストールが必要になります。
MySQLをインストールするだけではMySQLのgemがインストールされません。
gem mysql2のインストールには、MySQL Connector/Cが必要です。
MySQLの公式ウェブサイト（https://dev.mysql.com/downloads/connector/c/）から、Windows用のMySQL Connector/Cをダウンロードします。
ダウンロードしたファイルを実行し、MySQL Connector/Cをインストールします。

#### Gemfileにgem 'mysql2'を記載してbundle installする。
これでうまくいく人はそのまま進んでください。

#### gem 'mysql2'がうまくインストールできない人は下記を試してみてください。
```
gem install mysql2 -- --with-mysql-dir="C:\MySQLの保存場所" --with-mysql-lib="C:\MySQLライブラリの保存場所"
```
MySQLとMySQLライブラリの保存場所は各自で保存した場所を指定してください。

原因ははっきりしていないのですが、rubyのインストール場所だったり、ファイルの権限だったり、環境変数の関係で、bundle installだけではインストールできないことがあるので、インストールするファイルのパスを明示的にインストールするコマンドになります。

これでgem 'mysql2'がインストールできたかと思います。

最後に、dabaseyamlファイルをMySQLを指定してください。
```
development:
  adapter: mysql2
  encoding: utf8
  reconnect: false
  database: DB_NAME(環境変数)
  pool: 5
  username:USERNAME(環境変数)
  password:PASSWORD(環境変数)
  host: localhost

test:
  adapter: mysql2
  encoding: utf8
  reconnect: false
  database: DB_NAME(環境変数)
  pool: 5
  username:USERNAME(環境変数)
  password:PASSWORD(環境変数)
  host: localhost

production:
  adapter: mysql2
  encoding: utf8
  reconnect: false
  database: DB_NAME(環境変数)
  pool: 5
  username:USERNAME(環境変数)
  password:PASSWORD(環境変数)
  host: localhost
```

これでwindowsでもrailsでMySQLを使えるようになるはずです！

ちなみに自分はMacのPCを注文しました(笑)
会社などで支給されるPCもMac多いので今のうちに慣れておきたい！
Dockerのnetworkとvolumeの基本

Dockerのnetworkとvolumeについて学んだので備忘録です。

## 用語解説
公式の用語解説がシンプルに難しいので、小学2年生向けに執筆しています。

### Dockerのネットワークって何？
Dockerのネットワークは、Dockerのコンテナが他のコンテナやインターネットと通信するための道路のようなものです。コンテナはそれぞれ独立して動くので、それぞれが他のコンテナと情報を交換するためには、ネットワークが必要です。

おもちゃの車と道路を使って、Dockerのネットワークを考えてみましょう。あなたがおもちゃの車で遊んでいて、一つの部屋から別の部屋に移動したいとき、どうしますか？道路を使って移動しますよね。Dockerのネットワークも同じです。一つのコンテナ（おもちゃの車）が他のコンテナと情報を交換するためには、ネットワーク（道路）を通って移動します。

#### Dockerのネットワークの種類
Dockerのネットワークにはいくつかの種類があります。それぞれが異なる特性を持っています。

1. bridge: これは最も基本的なネットワークで、Dockerを使うときによく使います。このネットワークを使うと、コンテナは自分専用の小さなネットワーク空間を持つことができます。
2. host: これはホストマシン（Dockerを動かしているコンピュータ）のネットワークを直接使う方法です。このネットワークを使うと、コンテナはホストマシンと同じネットワーク空間を共有します。
3. none: これはネットワークを全く使わない設定です。このネットワークを使うと、コンテナはネットワークに接続せずに動くことができます。

### Dockerのボリュームって何？
Dockerのボリュームは、Dockerの中で動くプログラム（これをコンテナと呼びます）が作ったデータを保存するための箱のようなものです。コンテナは一時的なもので、消えるとその中のデータも一緒に消えてしまいます。でも、大切なデータは消えてしまうと困りますよね。そこでボリュームが役立ちます。ボリュームにデータを保存しておけば、コンテナが消えてもデータは消えません。

おもちゃの箱とおもちゃを使って、Dockerのボリュームを考えてみましょう。あなたがおもちゃで遊んだ後、おもちゃをどこにしまいますか？おもちゃの箱にしまいますよね。そして、また遊びたくなったら、その箱からおもちゃを取り出します。Dockerのボリュームも同じです。コンテナがデータ（おもちゃ）を作ったら、それをボリューム（おもちゃの箱）に保存します。そして、またそのデータが必要になったら、ボリュームからデータを取り出します。

#### Dockerのボリュームの種類
Dockerのボリュームにはいくつかの種類があります。

1. Data Volume: これはDockerのコンテナが直接使うデータを保存するためのボリュームです。コンテナが消えても、このボリュームに保存されたデータは消えません。
2. Data Volume Container: これは他のコンテナが作ったデータを共有するためのボリュームです。一つのコンテナがデータを作ってボリュームに保存すれば、他のコンテナもそのデータを使うことができます。

## コマンド紹介

### Dockerのネットワークコマンド
Dockerのネットワークは、コンテナ間の通信を管理します。以下に、Dockerのネットワークに関連する基本的なコマンドを紹介します。

* ネットワークの一覧を表示する

```bash
docker network ls
```

* 新しいネットワークを作成する

```bash
docker network create [ネットワーク名]
```

* ネットワークの詳細情報を表示する

```bash
docker network inspect [ネットワーク名]
```

* ネットワークを削除する

```bash
docker network rm [ネットワーク名]
```

### Dockerのボリュームコマンド
Dockerのボリュームは、コンテナのデータを永続的に保存します。以下に、Dockerのボリュームに関連する基本的なコマンドを紹介します。

* ボリュームの一覧を表示する

```bash
docker volume ls
```

* 新しいボリュームを作成する

```bash
docker volume create [ボリューム名]
```

* ボリュームの詳細情報を表示する

```bash
docker volume inspect [ボリューム名]
```

* ボリュームを削除する

```bash
docker volume rm [ボリューム名]
```


以上です！
M5Stack CM4Stack　メモ

# Windowsでの書き込み
## ダウンロード
実際にダウンロードする時期でリンク先が変わっていると思うので、下記URLを参考にしてください。

https://docs.m5stack.com/en/quick_start/cm4stack/arduino

### Download the official burning tool, Raspberry Pi Imager
- https://www.raspberrypi.com/software/ のDownload for Windowsをクリックしてダウンロード
- ダブルクリックしてインストール
- アンインストールは設定のアプリから
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97061/c0ceb0c2-fa6e-3f58-0891-5e9e5f9bb560.png)

### rpiboot.exe

- ダウンロードしたrpiboot_setup.exeをダブルクリックでインストール
- インストールは時間がかかります。
- スタートメニューのRaspberry Piにrpibootがインストールされます。
- Uninstallはスタートメニューのuninstall rpiboot

https://github.com/raspberrypi/usbboot/raw/master/win32/rpiboot_setup.exe

### Official SDFormatter 
- ダウンロードしたZIPファイルを展開する。実行ファイルはその中

https://www.waveshare.net/w/upload/d/d7/Panasonic_SDFormatter.zip

### custom image of cm4stack
- M5StackのCM4カスタマイズファイルをダウンロード
https://m5stack.oss-cn-shenzhen.aliyuncs.com/cm4stack/cm4stack_images/canary/2023-03-08-CM4StackOS-canary-2023-02-21-raspios-bullseye-armhf.gz

## 書き込み
1. Bootボタンを押しながらUSB-CをPCと接続すると、PCに検出される。
    - ACアダプターは不要
    - Bootボタンの長押しは不明。。
    - 下記の表示が出るとき・出ないときもある。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97061/6dcef3b7-8daf-5e35-7df3-1bf13d28b221.png)

1. スタートメニューのrpibootを実行
    - 処理に少し時間がかかる
    - 実行が終わるとドライブとして認識される。
    - rpibootを実行すると、ドライブが割り当てられる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97061/010d858e-8ca6-1854-7316-78be6246e9fa.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97061/7ac53e3c-fe6e-b6cc-e9d6-572672dbad16.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97061/b06176d3-5afb-32dc-72c4-bfc24c97e0ce.png)

4. SDFormatterを起動
    - Optionをクリックして"quick"とAjustmentを”ON"
    - 初回のみだけでよさそう
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97061/84fd24ec-53f5-9dd2-354b-a5ebe5e402c1.png)
    Formatをクリックする。ワーニングが出るけど無視
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97061/df74be34-cb81-43d3-03d5-47c272a25c65.png)
    完了
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97061/c122f1a0-8fe6-3ec3-c07a-aa2776f78cd1.png)

5. Raspberry Pi Imagerを起動
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97061/d6382151-f4c0-5eca-33ce-54754a576c3e.png)
OSを選ぶは　カスタムでダウンロードしたイメージに設定します。
あとはドライブを指定して書き込む
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/97061/fa7a5cd4-fc3a-f874-5fd8-2e2cf12f1cc7.png)

## 起動
ACアダプターを挿したら起動。
- 起動まで少し時間がかかる。
- FANが回るのでわかる

# 1-Wire
- M5Stackのカスタムイメージでは動かず
- オリジナルをインストール
    - 画面が表示されない
    - 音が出ない
    - ファンをPin13で60度に設定。60度以下は設定画面でできないので/boot/config.txtを書き換える必要あり。
## ハードウェア
- DB18B20を使用
- VCC 5VだとIOも5Vになるので動作しない（最悪壊れる）==>秋月のK-16055を使用し3.3Vにした。
- Port2(UART)の2pinがちょうどデフォルトのPin4
- PullUp抵抗は4.7K
## OS
- 設定で１Wireを有効に。
## Node-Red
- パレットで"node-red-contrib-ds18b20-sensor"をインストール
jira-python ライブラリを使うときに調べたこと

ライブラリのドキュメント：
https://jira.readthedocs.io/en/master/index.html

```python:認証
# SSL 認証無効化したい場合   options = {'server':'https://jira.vplatf.com', 'verify':'False'}
options = {'server':settings.JIRA_URL}
jira = JIRA(options, basic_auth=(settings.JIRA_USER, settings.JIRA_PW))
```

```python:example.py
issue = jira.issue('XXX')  # 特定チケット選択, XXXはjiraのキー
a = issue.fields.summary  # タイトル
b = issue.fields.priority # 型 <class 'jira.resources.Priority'>
b_str = issue.fields.priority
c = issue.fields.components  # 型 [<JIRA Component: name='XXX', id='000'>]
print(c[0])  # XXX
print(c[1])  # 000
d = issue.fields.customfield_XXX  # カスタムフィールドの値、XXXはフィールドID
e = issue.fields.reporter  # 型 <class 'jira.resources.User'>
f = issue.key  # チケットのキー
g = issue.fields.issuetype  # チケットのタイプ



# チケット検索例:プロジェクト名が「ABC」 + カスタムフィールドIDが145の値が「空欄/ない」 + チケットタイプが「バグ」
list_jira = jira.search_issues('project=ABC and cf[145]~None and issuetype=Bug')
(rails)modelの作成と命名規約について

(確認)modelとは
modelとは簡単言うと、データベースのテーブルとアプリケーションのビジネスロジックの間でデータのやり取りを行うためのクラスです。

modelを作成するためのコマンドは以下の通りです
```
rails g model モデル名 [属性名:データ型 ...]
```
例文は以下の通りです
```rb
rails g model Post title:string content:text
```

このコマンドを実行することによってモデルファイルと共に、マイグレーションファイルも作成されるので `rails g migration` でマイグレーションファイルを作成する必要がない


また、modelに名前をつける際に以下のような規約があります。
1.モデル名は単数形で記述します。
例: User, Post, Product

2.モデル名はキャメルケース（単語の先頭を大文字にする）で記述します。
例: ArticleCategory, OrderItem, UserProfile

3.モデルに対応するデータベーステーブル名は、モデル名の複数形で表現します。
例: Userモデルはusersテーブルに対応し、Postモデルはpostsテーブルに対応します。

4.モデルファイル名は、モデル名をスネークケース（単語をアンダースコアで区切る）に変換して命名します。
例: Userモデルはuser.rbというファイル名になります。

5.モデルクラスはApplicationRecordクラスを継承します。
例: class User < ApplicationRecord

6.モデルの属性（カラム）はスネークケースで記述します。
例: first_name, email, created_at

気づいた方もいらっしゃるかもしれませんが、命名の際、コントローラは複数形で、モデルは単数形で命名されます。
ESP32-WROOM-32のLIGHT-SLEEPとタイマーでの復帰で省電力化する

# はじめに
WiFiモジュールとして有名なESP32-WROOM-32を、WiFiは使わずに単なる高性能CPUとして使うことがあったので、その際に見つけたことを書いておきます。

# 省電力化の方法
WiFiは不要な案件であったので、WiFiをオフにします。

  WiFi.disconnect(true);

更に電流を抑えるため、ライトスリープを併用することにしました。
スリープから目覚めるのには、タイマーを使います。
  esp_sleep_enable_timer_wakeup(50 * 1000UL);
としておけば、50msごとに起動するはずなのですが、これでは目覚めません。
何故かWiFiを接続してconfigTzTimeで時刻合わせをすれば大丈夫なのですが、最初からWiFiをオフにすると目覚めなくなります。
色々試したのですが、
  sntp_set_system_time(1688564256, 0);
のように、現時刻を設定すると目覚めるようになりました。
この時、1688564256の値を0とか1ではなぜだか目覚めません。


```esp32.ino
#include <WiFi.h>

int keyState = 1;

void setup()
{
  Serial.begin(115200);
  while (!Serial);

  pinMode(GPIO_NUM_0, INPUT_PULLUP);

  // WiFi をオフさせる
  WiFi.disconnect(true);

  sntp_set_system_time(1688564256, 0);

  // 50 ms のタイマー設定
  esp_sleep_enable_timer_wakeup(50 * 1000UL);
}

void loop()
{
  int key = digitalRead(0);
  if (key == 0 && keyState != 0)
    Serial.println("Press");
  keyState = key;
  Serial.flush();
  esp_light_sleep_start();
}
```

これで、loop()の頭の部分に必要な処理を追加すれば、50ms間隔で処理をした後ライトスリープして消費電流が格段に減ります。
ライトスリープなので、ディープスリープのように小難しいことをしなくとも気楽に使えます。
通常の処理が軽めの案件では使えると思います。

# 消費電流計測値
テスターで計った消費電流です。ESP-WROOM-32E CPU単体で計測しています。
(1)WiFiに接続した場合
　　66~74mA
(2)WiFiをオフした場合
　　55.3mA
(3)上記サンプルプログラム（インターバル50m秒）の場合
　　2.8mA
インターバルを長くしても消費電流は減りませんでした。
Rails&AWSの学習でチャットGPTを使ってみた



今回の記事はこちらの記事の続きです。
https://qiita.com/tomo178/items/6b191f359979d4292d52

あれからRailsとAWSの基礎学習をやり、チャットGPTを使って頑張ってみたのでその検証結果を残しておきます。

# ◆検証結果

結論から言うとどちらもチャットGPTの活用は難しいことが分かりました。

理由は以下のような感じです
①AWSはいろんな画面をまたいで設定する必要があるのでチャットGPTに相談するのは難しい。
②Railsアプリケーションはディレクトリがたくさんあるので一度に指示を出すのが難しい。

特にAWSの方はGoogle検索をして調べた情報を参考にしたほうが全然速いことが多かったです。
GPT4を使っていても、満足な回答が得られないことが非常に多かったです。
AWSの学習は独学で自力でやっていくのは難しいので、要所要所でMENTAなどでコーチを付けることをオススメします。

# ◆有用だったアイデア

・treeコマンド
これ地味に結構便利でした。
treeコマンドとはLinuxのコマンドで、例えば
tree apps
と入力するとそのディレクトリ以下のディレクトリとファイルの構造をテキスト化してくれます。
Railsのディレクトリの位置が正しいかどうかをチャットGPTに質問する時などに使えます。
ディレクトリの配置は言語化するのが難しいポイントなので、とても便利です。

・公式ドキュメントを参照させて説明させる
Railsの学習で公式のドキュメントが分かりづらい時、チャットGPTに解説してもらうという技は有効でした。
公式の一次情報が推奨されることがありますが、分かりづらいことが多かったという問題を緩和することができるようになってきました。


# ◆まだ発展途上のアイデア

・sceneXplainというチャットGPTのpluginがあります。
これはチャットGPTに画像を読み込ませて回答をしてくれるものなのですが、
AWSの操作画面をキャプチャに取ってアップロードをしてみましたが、現時点では有用な回答は得られませんでした。



どなたかの参考になれば幸いです。
短いですが今回はこれくらいで。
PostgreSQLでテーブルごとのサイズ情報などを得る

ある日、データ転送の依頼があり、現状のPostgreSQLのテーブルごとの大体のサイズなどを知りたいことがあったので、調べたメモ。

## 確認環境

PostgreSQL 12系

## SQL

```SQL
SELECT
    relname as table_name,
    (select count(column_name) from information_schema.columns c where c.table_name = pg.relname and c.table_schema = 'public') as column_count,
    reltuples as rows,
    pg_size_pretty(relpages::bigint * 8 * 1024) as size,
    pg_size_pretty(cast(case reltuples when 0 then 0 else relpages::bigint * 8192 / reltuples end as bigint)) as average_row_size
FROM pg_class pg,
     pg_namespace pgn
WHERE pg.relnamespace = pgn.oid
  AND pgn.nspname = 'public'
  AND relkind = 'r'
ORDER BY relname ASC;
```

reltuplesやrelpagesは推定のサイズであるが、ある程度のサイズ感が分かればよかったのでこれで。


## 参考

https://qiita.com/awakia/items/99c3d114aa16099e825d
IEのJavaScriptで別ドメインへの接続ができない場合の対応

## 参考

* [ドメイン間でのデータ ソースのアクセス](https://admx.help/?Category=Windows_10_2016&Policy=Microsoft.Policies.InternetExplorer::IZ_PolicyAccessDataSourcesAcrossDomains_1&Language=ja-jp)
* https://www.pine4.net/Memo/Article/Archives/387

## IEでJavaScriptで別ドメインへの接続ができない場合の対応

* ドメイン間でのデータ ソースのアクセス
    * 有効にする

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25728/21db4ac8-9755-b80a-022a-758c3b15dded.png)


## レジストリ

```ini
Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\2]
; "1406"=dword:00000003 ; disabled
"1406"=dword:00000000 ; enabled
```

![123.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/25728/c1dd3e73-5d39-402b-9240-5b21f432e367.png)
【備忘録】AWSCLIでユーザデータを指定する

# AWS CLIでユーザデータを指定する

* `run-instances`コマンドと`--user-date`パラメータを使用する

```
aws ec2 run-instances \
--image-id ami-xxxxxxxx \
--count 1 \
--instance-type m3.medium \
--key-name xxxxxxxx \
--subnet-id xxxxxxxx \
--security-group-ids sg-xxxxxxxx \
--user-data file://xxxxxxxx.bash
```

* ユーザデータとして入力されたものは**rootユーザとして実行される**ので`sudo`コマンドは使わないこと
* `-y`指定のない`yum update`は指定できない（ユーザフィードバック：確認が必要となるコマンドは使えない）
* AWS CLIを使用するには、**インスタンス起動時にインスタンスプロファイルを使用する**
(ユーザデータのスクリプトが必要とする認証情報が提供される)
* `var/lib/cloud/instances/instance-id/`にコピーされて実行される
**実行後にスクリプトは削除できない**ので、インスタンスからAMIを作る際は削除すること。

# (補足)インスタンスメタデータ
* 実行中のインスタンス設定・インスタンス管理のために使われる
* ホスト名・イベント・セキュリティグループなどでカテゴリ分けされる
* 起動時に指定したユーザデータにアクセスできる

## (補足)実行中のインスタンス内からユーザデータを取得する
IMDSのIPv4アドレス`169.254.169.254`を使用する
```
http://169.254.169.254/latest/user-data

```
```
[ec2-user ~]$ TOKEN=`curl -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600"` \
&& curl -H "X-aws-ec2-metadata-token: $TOKEN" -v http://169.254.169.254/latest/user-data
#!/bin/bash
yum update -y
service httpd start
chkconfig httpd on
```
このあたりは、別の機会に調べてみることにします。

# 参考

* [起動時に Linux インスタンスでコマンドを実行する
](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/user-data.html)
* [インスタンスメタデータとユーザーデータ
](https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/ec2-instance-metadata.html)
ゲストログイン機能について

## はじめに

ポートフォリオにゲストログインを実装したので
忘備録として残します。

## その1.ルーティング

routes.rbにゲストログイン用のアクションを設定します。
`SessionsController`に新しいアクション`guest_sign_in`を準備します。

```ruby:config/routes.rb
devise_scope :user do
  post 'users/guest_sign_in', to: 'users/sessions#guest_sign_in'
end
```

## その2.コントローラ
`guest_sign_in`アクションを設定するため，app/controllersにusersディレクトリを作成し，その中に次のsessions_controller.rbを作成します。

```ruby:app/controllers/users/sessions_controller.rb
# ゲストログイン機能
  def guest_sign_in
    user = User.guest
    sign_in user
    redirect_to parent_path(current_user.id)
  end
```

## その3.モデル

`find_or_create_by!`で'guest@example.com' であるユーザーを検索します。
もし見つかればそのユーザーを返すが、無ければ作成します。
`SecureRandom.alphanumeric(8)`を使って、8文字のランダムな
英数字のパスワードを作成しています。

```ruby:app/models/user.rb
# ゲストログイン機能
   def self.guest
     find_or_create_by!(email: 'guest@example.com') do |user|
      # 8文字の英数字のランダムパスワードを指定
       user.password = SecureRandom.alphanumeric(8)
       user.name = "guest_user"
     end
   end
```

## その4.ビュー
今回はtopページにログインボタンを実装するので以下を追加して下さい!

```ruby:app/views/homes/top.html.erb
<%= link_to 'ゲストログイン（閲覧用）', users_guest_sign_in_path, method: :post%>
```
Cookieとセッションの違い

Webに関する基礎知識をつけるべく、これまでなんとなくで把握していたCookieとセッションについて、改めて学習しました。その記録をこちらに残しておきます。

どなたかのお役に立てれば幸いです。

# Cookieとは？
```
Cookieとは、WEBサイトを閲覧したときに、
訪問者が訪れたサイトや入力したデータ、
利用環境などの情報が記録されたファイル（仕組み）を指します。
```
引用：https://gmotech.jp/semlabo/webmarketing/blog/cookie/

# セッションとは？
```
セッションはユーザーがWEBサイトにアクセスしてから、
最終的に離脱するまでの行動を示す指標です。
ユーザーがWEBサイトへ訪問した回数とも捉えられ、
自サイトにおけるユーザーの訪問回数を確認する場面でも役立つはずです。
```
引用：https://www.switchitmaker2.com/web-analytics/web-session/


# Cookieとセッションの違い

- どちらも情報・データを保存する機能である。
- 保存場所や、保存期間に違いがある。
- 『クッキーはブラウザに、セッションはWebサーバーに保持されている。』と覚えておく。




|   |保存場所  |保存期間  |無効化|
|---|---     |---      |---  |
|Cookie |ブラウザ |永続的・一定期間保存する| できる|
|セッション |ブラウザ・サーバー  |サイトを離脱すると削除|できない|


# Cookieとセッションの利用例
- Cookieの例として、データに基づいてユーザーの興味をひくような広告を出せるようにするなどがある。
- セッションの例として、ログイン機能などがある。
- システムにログイン後、他のページにアクセスする度にクッキーをサーバーに送っているため、他のページにアクセスする度にログインするのは不要となる。





# その他Cookieやセッションに関する情報
- Cookieの最大容量は4キロバイトです。そのため、Cookieを利用して多くの情報をやり取りすることはできない。
- クッキーは、HTTPメッセージのリクエストヘッダーとレスポンスヘッダーで送受信される。
- クッキーの有効期限はconfig/initializers/session_store.rbで設定できる。


```
クッキーの有効期限を1日に設定している例。

Rails.application.config.session_store :cookie_store, key: '_runteq_normal_session', expire_after: 1.day
```

参考サイト：

https://gmotech.jp/semlabo/webmarketing/blog/cookie/

https://www.switchitmaker2.com/web-analytics/web-session/