OmniAICreator/Qiita-1.07M huggingface dataset:
Vueで配列をtable表示するには配列をグループ化する

# はじめに
Vueで配列をtableに表示ようとしていたのですが、"Element is missing end tag"エラーが出てしまいました。色々試したのですがうまくいかない。
### こういうやつ
+ 16個の配列があります。
```json:Array
[ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ]
```
+ これを単純にtableで4行4列表示させたい
<table>
  <tr><td>1</td> <td>2</td><td>3</td> <td>4</td></tr>
  <tr><td>5</td> <td>6</td><td>7</td> <td>8</td></tr>
  <tr><td>9</td> <td>10</td><td>11</td> <td>12</td></tr>
  <tr><td>13</td> <td>14</td><td>15</td> <td>16</td></tr>
  </tr>
</table>

+ vueで以下のように書いてみました。
 v-ifで4の倍数ごとに`</tr><tr>`を入れるものです。
 これでは"Element is missing end tag"エラーになります。
 
```vue:Column.vue
<template>
  <div>
    <div>{{nArray}}</div>
    <table>
      <tr>
        <template v-for="(n, i) in nArray" :key="i">
          <td>{{n}}</td>
          <template v-if="((i + 1) % 4 === 0) && (i !== 0)">
          　　　　<!--　カラム４つごとにtrで閉じたい -->
            </tr><tr>
          </template>
        </template>
      </tr>
    </table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      nArray: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16 ], 
    };
  }
};
</script>
```
### Qiitaで質問してみた
cssでの解決方法の回答がありました。

https://qiita.com/devzooiiooz/questions/9ecbb169139a7e121834#answer-8a46a8605ad3772c6c6a


```html:
<ul style="display:grid; grid-template-columns:repeat(4,1fr)">
  <li v-for="item in nArray" :key="item" style="list-style:none;">{{item}}</li>
</ul>
```
うーん、これでもいいのだが…。

### chatGPT
「配列の要素を4つごとに新しい行`<tr>`で閉じるためには、配列をグループ化するのが一般的なアプローチです」だそうです。
```vue:Column.vue
<template>
  <div>
    <div>{{ nArray }}</div>
    <div>{{ chunkedArray }}</div>
    <table>
      <tbody>
        <tr v-for="(row, rowIndex) in chunkedArray" :key="rowIndex">
          <td v-for="(n, i) in row" :key="i">{{ n }}</td>
        </tr>
      </tbody>
    </table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      nArray: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16],
    };
  },
  computed: {
    chunkedArray() {
      const chunkSize = 4;
      const result = [];
      for (let i = 0; i < this.nArray.length; i += chunkSize) {
        result.push(this.nArray.slice(i, i + chunkSize));
      }
      return result;
    },
  },
};
</script>
```
なるほど～。vueでは配列をグループ化してから処理するのが一般的なんだって。
```html:chunkedArray
[ [ 1, 2, 3, 4 ], [ 5, 6, 7, 8 ], [ 9, 10, 11, 12 ], [ 13, 14, 15, 16 ] ]
```
マルコフ連鎖でしかのこのこのここしたんたん

こんな動画があったのでPythonで実際にマルコフ連鎖してみる。

https://youtu.be/Xkq13ZthmA0

# ソースコード
```python:shikanokonokonokokoshitantan.py
import random

# 遷移
transitions = {
    'し': ['か', 'た'],
    'か': ['の'],
    'の': ['こ'],
    'こ': ['の', 'こ', 'し'],
    'た': ['ん'],
    'ん': [' ', 'た'],
}

# 重み
weights = {
    'し': [0.5, 0.5],
    'こ': [0.5, 0.25, 0.25],
    'ん': [0.5, 0.5],
}

# 初期状態
state = 'し'
result = [state]

# 空白が出たら終了
while True:
    next_state = random.choices(
        transitions[state],
        weights = weights.get(state, [1])
    )[0]
    result.append(next_state)
    state = next_state
    if state == ' ':
        break

# 結果出力
print(''.join(result))

```
# 状態遷移図

```mermaid
stateDiagram-v2
    [*] --> し
    し --> か : 0.5
    し --> た : 0.5
    か --> の : 1
    の --> こ : 1
    こ --> の : 0.5
    こ --> こ : 0.25
    こ --> し : 0.25
    た --> ん : 1
    ん --> た: 0.5
    ん --> [*] : 0.5
```

# おわりに
[MITライセンス](https://github.com/raptech-jp/shikanokonokonokokoshitantan/blob/main/LICENSE)にするので面白いことに使ってほしい。
【Python3】十億連勝 (paizaランク S 相当)をやってみた

https://paiza.jp/works/mondai/real_event/continuous_winning

**問題: ゲームの隠し要素を満たす連勝数を数える**

**入力**: N個のステージと各ステージでの試合数A_i、最大連勝数X
**出力**: X連勝を達成する方法の通り数の下9桁

**アプローチ**:
1. ステージを順に進み、各試合結果を考慮して連勝数を更新。
2. 特定の連勝数に達する方法を動的計画法(DP)で記録。
3. DP状態は (連勝数, X連勝達成フラグ) の組み合わせ。
4. 最終結果を計算し、9桁に収める。

**コード**:
```python
def push(dic, key, val):
    """辞書に値を追加または更新するヘルパー関数"""
    if key in dic:
        dic[key] += val
    else:
        dic[key] = val

# 入力を読み込む
N, X = map(int, input().split())
A = [int(input()) for _ in range(N)]
mod = 10 ** 9

# 状態を初期化 (初期状態: 連勝0, X連勝未達成)
states = {}
states[(0, False)] = 1

# 各ステージを順に処理
for i in range(N):
    new_states = {}
    for (wins, cond), comb in states.items():
        if wins + A[i] <= X:
            # ステージiの試合全てに勝つ場合
            push(new_states, (wins + A[i], cond), comb)
            # ステージiの試合中どこかで負ける場合
            push(new_states, (0, cond), comb * A[i])
        else:
            # ちょうどX連勝で負ける場合
            push(new_states, (0, True), comb)
            # X連勝未満で負ける場合
            push(new_states, (0, cond), comb * (X - wins))

    # 組み合わせの数を下9桁に制限
    for key in new_states:
        new_states[key] %= mod

    states = new_states

# 最終的な結果を計算
ans = 0
for (wins, cond), comb in states.items():
    if cond or wins == X:
        ans += comb

# 下9桁の結果を出力
print(ans % mod)
```

**コードの説明**:
1. **push関数**:
    - `key` (状態) に対応する `val` (通り数) を辞書 `dic` に追加または更新。
    
2. **入力の読み込み**:
    - `N` と `X` を読み込み、各ステージの試合数 `A` をリストに格納。

3. **状態の初期化**:
    - 初期状態 `(連勝数0, X連勝未達成)` に対応する通り数を1とする。

4. **各ステージの処理**:
    - 各ステージで新しい状態を計算し、連勝数と条件達成フラグを更新。
    - 全勝と途中負けのケースを考慮し、新しい状態と通り数を更新。

5. **結果の計算**:
    - 最終的な状態から、X連勝達成または条件達成フラグが真の通り数を合計し、9桁の結果を出力。

このプログラムは、与えられた試合数と連勝条件に対して、隠し要素を満たすプレイの仕方の総数を計算します。
Python UIアーキテクチャ編

# Python UIアーキテクチャ編（UI Architecture）
PythonのUIアーキテクチャについて解説します。

### 目次
1. MVC（Model-View-Controller）
2. MVP（Model-View-Presenter）
3. MVVM（Model-View-ViewModel）

**1. MVC（Model-View-Controller）**
MVCは、アプリケーションをモデル（Model）、ビュー（View）、コントローラ（Controller）の3つのコンポーネントに分割するアーキテクチャです。
```python:MVC.py
# 簡単な例
class Model:
    def __init__(self):
        self.data = "Hello, MVC!"

class View:
    def display(self, data):
        print(data)

class Controller:
    def __init__(self, model, view):
        self.model = model
        self.view = view

    def update_view(self):
        self.view.display(self.model.data)

model = Model()
view = View()
controller = Controller(model, view)
controller.update_view()  # Output: Hello, MVC!
```

**2. MVP（Model-View-Presenter）**
MVPは、MVCに似ていますが、ビューの論理をプレゼンター（Presenter）に移すことで、ビューのテストを容易にするアーキテクチャです。
```python:MVP.py
# 簡単な例
class Model:
    def __init__(self):
        self.data = "Hello, MVP!"

class View:
    def display(self, data):
        print(data)

class Presenter:
    def __init__(self, model, view):
        self.model = model
        self.view = view

    def update_view(self):
        self.view.display(self.model.data)

model = Model()
view = View()
presenter = Presenter(model, view)
presenter.update_view()  # Output: Hello, MVP!
```

**MVVM（Model-View-ViewModel）**
MVVMは、モデル（Model）、ビュー（View）、およびビューの状態とロジックを管理するビュー・モデル（ViewModel）で構成されるアーキテクチャです。
```python:MVVM.py
# 簡単な例
# 簡単な例
class Model:
    def __init__(self):
        self.data = "Hello, MVVM!"

class ViewModel:
    def __init__(self, model):
        self.model = model

    def get_data(self):
        return self.model.data

class View:
    def display(self, data):
        print(data)

model = Model()
view_model = ViewModel(model)
view = View()
view.display(view_model.get_data())  # Output: Hello, MVVM!
```

最後までお読みいただきありがとうございました。
【Python3】ハノイの塔 (paizaランク A 相当)をやってみた

https://paiza.jp/works/mondai/real_event/hanoi

**ハノイの塔**のプログラムは、与えられた `n` 個の円盤を杭Aから杭Cに最短手順で移動する際の `t` 回目の状態を出力します。

**コードの手順**:
1. ハノイの塔の状態を再帰的にシミュレート。
2. 特定の手順数 `t` における状態を記録。
3. 各杭の状態を出力。

```python
def hanoi(n, t):
    # 初期状態の設定
    pegs = [list(range(n, 0, -1)), [], []]
    moves = []

    # ハノイの塔の再帰的な移動関数
    def move_disks(num_disks, from_peg, to_peg, aux_peg):
        if num_disks == 0:
            return
        move_disks(num_disks - 1, from_peg, aux_peg, to_peg)
        if len(moves) < t:
            disk = pegs[from_peg].pop()
            pegs[to_peg].append(disk)
            moves.append((from_peg, to_peg))
        move_disks(num_disks - 1, aux_peg, to_peg, from_peg)

    # 再帰的に円盤を動かす
    move_disks(n, 0, 2, 1)

    # 各杭の状態を出力
    for peg in pegs:
        if peg:
            print(" ".join(map(str, peg)))
        else:
            print("-")

# 標準入力からの入力を受け取る
import sys
input = sys.stdin.read().strip()
n, t = map(int, input.split())

# ハノイの塔のシミュレーションと出力
hanoi(n, t)
```

**コードの説明**:
1. **初期状態の設定**:
    - `pegs` というリストを使って各杭の状態を表現します。最初は杭Aに `n` 個の円盤が大きい順に積まれています。
    - `moves` というリストで移動を記録します。

2. **ハノイの塔の再帰的な移動関数 `move_disks`**:
    - `num_disks` 個の円盤を `from_peg` から `to_peg` に `aux_peg` を使って移動します。
    - 基本ケースでは何もしません (`num_disks == 0`)。
    - 再帰的に `num_disks - 1` 個の円盤を補助の杭に移動し、一番大きい円盤を目的の杭に移動します。
    - `moves` リストが `t` より短い場合のみ円盤を移動し、その状態を記録します。
    - 残りの `num_disks - 1` 個の円盤を補助の杭から目的の杭に移動します。

3. **各杭の状態を出力**:
    - 各杭 (`peg`) の状態を出力します。空の杭は `"-"` と表示します。

このプログラムを実行すると、指定された `n` と `t` に対して、各杭の状態を適切に出力します。
CodeSandboxのコンソール表記（HTML）のやり方

## 初めに
初めて使った[CodeSandbox](https://codesandbox.io/)のコンソールのHTML表記方法でつまづいたのでまとめます
## 問題
[CodeSandbox](https://codesandbox.io/)でコンソールを表示した際、HTMLの形で表示されず困りました
例）
```JavaScript:exsample.js
const li = document.createElement("li");
console.log(li)
```
↓ 理想のコンソール表記
```:理想
<li> </li>
```
↓ 現実のコンソール表記
```:現実
▶ HTMLLIElement

-----------------------------
”▶” から展開が可能で以下のようになる（一部抜粋）
type: ""
value: 0
ATTRIBUTE_NODE: 2
CDATA_SECTION_NODE: 4
COMMENT_NODE: 8
・
・
・
```
## (一応）解決
Udemyを使って勉強をしていたため、Q&Aを見たところ答えがありました
console.logを出す際に、**.outerHTML**をつけよ！！とのことでした
先ほどの例だと以下のようになります
```JavaScript:exsample.js
const li = document.createElement("li");
console.log(li.outerHTML)
```
これで解決かと思ったら、なぜか ” li ” タグの子に設定していた" p "タグが出力されました。なんでやねん
これに関してはブラウザのキャッシュを削除したことろ解消しました

また、せっかくHTML表記で出せるなら下記のようにしたいですよね！ね？
```:もっと理想
<li>
    <div>
        <p> </P>
    </div>
</li>
```
```:現実
<li><div><p> </P></div></li>
```
これについては問題解決を諦めました
誰かすごい人がなんとかしてくれることを祈ります
（その前に私はちゃんとした開発環境を整えたいと思います。）
## 終わりに
ここで1時間以上を使ってFXで有り金全部溶かしたような顔になっていましたが、UdemyのQ&Aに救われました。
同じ問題にぶち当たる人が5分で解決できるように記事を作成いたしました（届きますように）
引き続き勉強を頑張ります。
Python 構文編

# Python 構文編
Pythonの構文について解説します。

### 目次
1. 文（Statement）
2. 式（Expression）
3. リテラル（Literal）
4. 条件分岐（Conditional Statements）
5. ループ（Loop）
6. 再帰（Recursion）

**1. 文（Statement）**
文は、Pythonコードの最小の実行単位です。文は通常、一つのアクションを表します。
```python:Statement.py
x = 10  # これは代入文です
print(x)  # これは関数呼び出し文です
```

**2. 式（Expression）**
式は、値を生成するコードの一部です。例えば、計算や関数呼び出しが含まれます。
```python:Expression.py
y = 3 + 5  # 3 + 5 は式です
print(y)  # Output: 8
```

**3. リテラル（Literal）**
リテラルは、ソースコード中に直接記述された値です。
```python:Literal.py
num = 42  # 数値リテラル
text = "Hello"  # 文字列リテラル
```

**4. 条件分岐（Conditional Statements）**
条件分岐は、条件に基づいて異なるコードブロックを実行するための構文です。
```python:Conditional Statements.py
x = 10
if x > 5:
    print("x is greater than 5")
else:
    print("x is 5 or less")
# Output: x is greater than 5
```

**5. ループ（Loop）**
ループは、コードブロックを繰り返し実行するための構文です。
```python:Loop.py
# for ループ
for i in range(5):
    print(i)
# Output: 0 1 2 3 4

# while ループ
count = 0
while count < 5:
    print(count)
    count += 1
# Output: 0 1 2 3 4
```

**6. 再帰（Recursion）**
再帰は、関数が自分自身を呼び出すことです。
```python:Recursion.py
def factorial(n):
    if n == 1:
        return 1
    else:
        return n * factorial(n - 1)

print(factorial(5))  # Output: 120
```
最後までお読みいただきありがとうございました。
fortigate 60F ファームウェアバージョンアップ(v6.2.9→v7.0.11)

Fortinet Fortigate 60Fのバージョンアップを実施します。
Internet接続環境にて、WebGUIを使用したバージョンアップになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/debdbed3-22f8-8882-189e-8725fab90048.png)


# 1.機器ログイン
Fortigeteの初期状態では、port1にLANケーブルを接続し、PC側のNIC設定をDHCP設定にすることでWeb接続ができます。

設定 | 入力内容
--- | ---
URL | https://192.168.1.99
User | admin
Password | (空白)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/cd73ab89-499e-c066-e791-fe35bc2a1a61.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/4e9c6594-558c-724b-ec9b-abb9ed06bb64.png)

:::note warn
初期パスワードは変更しておくことを推奨します。
:::



# 2.バージョンアップパスの確認
Fortigateでは目的のバージョンまでに複数のバージョンを辿るバージョンアップパスを意識する必要があります。
以下のサイトで確認できるとともに、WebGUIから実施する場合はアップグレードパスが表示されるため、それぞれに従ってバージョンアップを進めます。

https://docs.fortinet.com/upgrade-tool/fortigate

:::note info
v6.2.9からv7.0.11の場合は、`v6.2.9` → `v6.2.11` → `v6.4.11` → `v7.0.11`を辿ります。
:::

<br>

■Fortinet社Webサイトから確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/5cd308d4-24b9-8b73-cc57-187af11f11f0.png)

<br>

■WebGUIから確認
左ペインの `システム` - `ファームウェア` から確認
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/23750195-a472-08f1-4092-7c3ea4b719c5.png)


<br>

# 3.ファームウェアバージョンアップ
`設定のバックアップとアップグレード`を押下してバージョンアップを実行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/a5acee5c-954e-afb1-6572-725ff2fe8d70.png)

`続ける`を押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/8cd1bfe1-96d4-0ace-ad4c-c84f08d5ea55.png)

:::note info
その後、configファイルがダウンロードされます。
:::

数分後、機器が再起動しログイン画面が表示されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/7edb5fbe-c7ac-9d04-b8a6-18ec22a242a3.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/c0287c7c-df26-a194-e0b1-ef2a1cb5c343.png)

アップグレードパスの一段上までファームウェアバージョンアップしたことが確認されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/1fea87da-ff32-797b-538e-07f23d2e05aa.png)

<br>

同様に`6.4.11`および`v7.0.11`へのファームウェアバージョンアップを実施します。

<br>


最終的に、`v7.0.11` にファームウェアバージョンアップしたことが確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/63e6ba29-a914-c07d-750f-1a365db418d2.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/4a011a69-5d4c-718d-6f6d-b6a2e80424f7.png)

# 参考
https://docs.fortinet.com/upgrade-tool/fortigate

https://www.fortinet.com/content/dam/fortinet/assets/deployment-guides/ja_jp/FOS_verup_DeploymentGuide.pdf

https://gold.nvc.co.jp/document/fortinet/tech/tech_doc/FortiGate%E3%82%A2%E3%83%83%E3%83%97%E3%82%B0%E3%83%AC%E3%83%BC%E3%83%89%E6%89%8B%E9%A0%86%E6%9B%B8.pdf
Python データ型編

# Python データ型編
Pythonで使用するデータ型について解説します。

### 目次
1. 文字列（String）
2. 整数（Integer）
3. 浮動小数点数（Float）
4. 論理値（Boolean）
5. None型（NoneType）

**1. 文字列（String）**
文字列は、テキストデータを格納するためのデータ型です。シングルクォートかダブルクォートで囲んで表します。
```python:String1.py
text = "Hello, World!"
print(text)  # Output: Hello, World!
```
**文字列の操作例**
文字列は、テキストデータを格納するためのデータ型です。シングルクォートかダブルクォートで囲んで表します。
```python:String2.py
# 文字列の連結
greeting = "Hello"
name = "Alice"
message = greeting + ", " + name + "!"
print(message)  # Output: Hello, Alice!

# 文字列の部分抽出
substring = message[7:12]
print(substring)  # Output: Alice
```

**2. 整数（Integer）**
整数は、小数点を含まない数値です。
```python:Integer.py
a = 10
b = -5
print(a + b)  # Output: 5
```

**3. 浮動小数点数（Float）**
浮動小数点数は、小数点を含む数値です。
```python:Float.py
pi = 3.14
radius = 2.5
area = pi * (radius ** 2)
print(area)  # Output: 19.625
```

**4. 論理値（Boolean）**
論理値は、True または False の2値を持つデータ型です。
```python:Boolean.py
is_sunny = True
is_raining = False
print(is_sunny and is_raining)  # Output: False
```

**5. None型（NoneType）**
論理値は、True または False の2値を持つデータ型です。
```python:NoneType.py
value = None
print(value)  # Output: None
```

最後までお読みいただきありがとうございました。
名前解決の設定方法(/etc/hosts)

# 困りごと
- https://sample.com にリクエストを送ると`210.XXX.XXX.XXX`に接続された
- 本当は`172.XXX.XXX.XXX`に接続してほしい

# 原因
- 誤って名前解決されていた
    - 想定
        - ドメイン　sample.com
        - IP `172.XXX.XXX.XXX`
    - 実際
        - ドメイン sample.com
        - IP `210.XXX.XXX.XXX`
# 解決策
接続元サーバー(Linux)の`/etc/hosts`ファイルに正しい名前解決を記載すれば良い
``` text:/etc/hosts
# IPアドレス ドメイン名
172.XXX.XXX.XXX sample.com
```
# 結果
試しに https://sample.com にリクエストを送ると、しっかり`172.XXX.XXX.XXX`に接続されていることが確認できた。

# その他
- LinuxだけでなくMacやWindowsにも名前解決を設定できるファイルがある(参考文献1を参照)
- /etc/hostsに記載された名前解決はDNSサーバーより優先されるらしい(参考文献2を参照)
- 前述のケースで誤って名前解決されていた理由は調べていないが、DNSサーバーに誤った名前解決が設定されていたと予想している

##### 参考文献
1. [「/etc/hosts」ファイルについて](https://qiita.com/hirokik-0076/items/830f18ed733e65defa58)
1. [Linux豆知識 176「/etc/hosts」ファイル](https://linuc.org/study/knowledge/506/)
【GitHub】プルリクエストに必須のステータスチェックを設定する方法

GitHubの保護ブランチの設定でプルリクエストに必須のステータスチェックを設定できます。

GitHubのリポジトリを開き、Settings > Branches > Add branch rulesetを開きます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/9063fc34-25a4-387b-8c47-2f8698ea0d36.png)

Require status checks to passを探し、チェックを入れます。
Add checksをクリックしてワークフローを追加します。

![スクリーンショット 2024-07-20 23.46.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/1d60c411-7c02-314c-54c4-666a3523c0ba.png)
投稿 20 クロスサイトスクリプティング 2024/07/20

本日の勉強時間　5H

クロスサイトスクリプティングとは、攻撃者が送り込んだ悪意のコードをそのページを閲覧した不特定多数のユーザーに、スクリプト（簡易的なプログラム）として実行させる可能性があることを指します。![IMG_6321.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3579814/5c9254d2-0e25-c8aa-569b-d57dd38a07aa.png)
JavaScript + React + Viteで.envファイルを参照する方法

# はじめに

.envファイルで環境変数を設定する際、Viteでの方法が少し異なるそうなので記録します

# 問題

.envを読み込むには「dotenv」というライブラリを使います（Viteにはデフォルトで入っているようです）
その際、通常は`process.env.`を用いて参照するようです。


``` javascript
const env = process.env.TEST;
```

ですが、Vite環境下では`undefind`となってしまいます。

# 解決方法

Vite環境でEnvファイルを参照する際は、以下のルールがあるようです。

1. `import.meta.env.`を用いる
1. 定数名の頭に`VITE_`を付ける（例：VITE_URL）

``` javascript
  const env1 = import.meta.env.VITE_ANPANMAN; // .envファイルの値が取得できる
  const env2 = import.meta.env.CURRYPANMAN; // undifind
```

https://stackblitz.com/edit/vitejs-vite-4cxxh9?file=src%2FEnvSupply.jsx

# 最後に

フロントの環境による細かい差もあるのだと実感しました、今後気を付けます。

# 参考サイト

https://blog.okaryo.io/20220319-env-variables-for-vite

https://ja.vitejs.dev/guide/env-and-mode.html
CH341A ROMライターの使い方　ハードウェア版

## はじめに
　ジャンクマザーボード復活法の１つにBiosロム直接書き込みがある。この操作の定番がRomライターCH341aである。アマゾンなどで1000円くらいで購入できるのだがマニュアルもなく使い方を調べるのに苦労したり、ハードの問題で苦労したりなど色々なことがあったのでメモ書きを残す。

## 品物について
　今回使ったもの一式。CH341A本体、チップクリップケーブル（左）とオンボードピン用ケーブル（右）。![一式.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636689/44e3ccdb-dab1-b331-de8b-da19213243ff.jpeg)
CH341a本体は基盤むき出しのUSB取り付け本体を使う。マザーボードにはんだ付けされたBiosからデータを読み出したりするにはクリップ型のケーブルを使う。ネットでも情報があふれているがクリップがうまくつかずデータ読みに失敗することが多々起きる。ネットでは何度も取り付けなおせと書かれている。
　私のケースでは本体をPCに取り付け、クリップを使ってマザーボード上のBiosチップと接続したとたんCH341aデバイスが認識されなくなるエラーを起こす不良品に当たったり、クリップ経由でデータが読み込めない不良品に当たった。結局２セット購入し本体とケーブルを２個１にしてようやく動くようになった。正常な組み合わせならデータ読み込みは簡単だ。読み込みに苦労する場合はハードに問題があるかもしれないと考えるべきだ。

## 使う前の下準備
　CH341aは24系ROMと25系ROMによってケーブル端子の取り付け位置が異なる。そのため読み込みたいチップの型番を事前に調べる必要がある。マザーボードの場合、チップが小さい。さらに表面に記載されている型番が読みにくい。これはスマホのLEDを点灯しながら表面型番記載が浮かび上がる角度を見つけ撮影すれば読み出しやすい。型番頭付近に24とあるものが24系、25とあるものが25系である。画像はMSI Z390-S01のBios表面の写真。![Bios_pic.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636689/f282f74d-88bd-bb10-70ac-b4eb6501c131.jpeg)
 MX25L12872Fとあるので25系であることがわかる。またMSI Z390-S01の場合はBiosチップの横に10-1ピンの接続端子があるので専用ケーブルを使えばこちらからも接続できる。専用ケーブルはアリエクで販売されているので購入するか自作する。私はアリエク購入品を使った。

## CH341Aとケーブルの接続
　CH341A本体裏面写真。上部８端子が25系用、下部8端子が24系用である。
 ![裏面1.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636689/ce82c0d2-3443-0aa7-0d0a-b00bd56e4a64.jpeg)
 またピン番号は左下が1番、右下が8番なのでそれに合わせてケーブルをつなぐ。CH341A表面にケーブルをさした写真が下。
 ![DSC_0912~2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636689/9e08b5da-eba3-ea02-7ada-2683ccdf9ccd.jpeg)
この状態でクリップを挟んだ写真。Bios表面の黒丸マークが1番ピンになるように挟む。
![DSC_0906~2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636689/f5a04124-9b39-e7ad-d3c0-ea6ba58e76e9.jpeg)
Bios端子に接続した写真。
![DSC_0908~2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636689/fef77e75-fe80-341b-65c1-65d137cf61c6.jpeg)

正常動作品で正しく接続してソフトで読み込みするとBiosチップの候補一覧がでるので該当するものを選択すれば内容の読み書きができる。
![IC_candicates.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3636689/f696bcc9-db3c-6a93-eae2-aa91e533ab84.jpeg)
これはROMチップのデータ読み書き電圧が3.3 V品の場合のみ。1.6 V品の場合はCH341A本体とケーブルの間に降圧部品を挟む。

前述したがうまく接続できない場合は何度もクリップで挟みなおすとの説明が多いがチップ端子の導通はテスターなどで容易に測定できることを考えると、頻繁に読み込み失敗する場合は本体かケーブルに不具合がある可能性も考えた方が良さそうだ。私の場合、2セット購入し部品組み合わせを変えて正常に動作したことを考えると製品品質へ過剰な期待をすべきでない。

## 注意
　よくよく見直すと購入本体に保証書などついていない。CH341Aセットを購入すればわかるが保証書どころか説明書もなにも付属せず本体、付属端子とケーブルのみが入っているのみだ。製品は自己責任で使用せよという意味だろう。ソフトも自分で探せというスタンスなので全て自分で調べて自己責任で使うことになる。ソフトは対応するものであれば何を使っても同じなので好きなものを使えばよい。ソフトについては検索すれば容易に見つかるので、今回書いた内容がわかれば使うのは簡単だと思う。
AtCoder 勉強記録[C++] その26

AtCoderで入茶を目指して勉強しています。
勉強を継続するために投稿を始めました。
もともとアカウントを作成していましたが、今年の4月から本格的に勉強を始めました。
一応自分用に解法を書いていますが雑です、自分で読み返して困ったら修正します。
[私のアカウント](https://atcoder.jp/users/nanami_brother)
[解いた問題](https://kenkoooo.com/atcoder/#/table/nanami_brother)
# 本日解いた問題
本日は`AtCoder Beginner Contest 363`に参加した。今回のコンテストではC問題まで解くことができ、さらにD問題に取り組むことができた。また、初めて緑パフォーマンスを出せた。
## A - Piling Up
[A - Piling Up](https://atcoder.jp/contests/abc363/tasks/abc363_a)
解答
```c++
#include <bits/stdc++.h>
using namespace std;
using ll = unsigned long long;
using vec = vector<ll>;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)

int main() {
  ll n;
  cin >> n;
  if(n < 100) cout << 100 - n << endl;
  else if(100 <= n && n < 200) cout << 200 - n << endl;
  else if(200 <= n && n < 300) cout << 300 - n << endl;
}
```
### 解法
100未満、100以上200未満、200以上300未満の場合に分けて処理することで結果が求まる。
## B - Japanese Cursed Doll
[B - Japanese Cursed Doll](https://atcoder.jp/contests/abc363/tasks/abc363_b)
解答
```c++
#include <bits/stdc++.h>
using namespace std;
using ll = unsigned long long;
using vec = vector<ll>;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)

int main() {
  ll n, t, p;
  cin >> n >> t >> p;
  vec l(n);
  rep(i, n) cin >> l[i];
  ll res = 0;
  while(1){
    ll cnt = 0;
    for(int i = 0; i < n; i++){
      if(l[i] >= t) cnt++;
      l[i]++;
    }
    if(cnt >= p) break;
    res++;
  }
  cout << res << endl;
}
```
### 解法
1日ごとに髪の毛の長さが`T`以上の人数と日数をカウントして、人数が`P`以上になったときにループを抜ける。その際の日数を出力する。
## C - Avoid K Palindrome 2
[C - Avoid K Palindrome 2](https://atcoder.jp/contests/abc363/tasks/abc363_c)
解答
```c++
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using vec = vector<ll>;
#define rep(i, n) for (int i = 0; i < (int)(n); i++)

int main() {
  ll n, k;
  cin >> n >> k;
  string s;
  cin >> s;
  ll cnt = 0;
  ll res = 0;
  sort(s.begin(), s.end());
  do {
    
    for(int i = 0; i <= n-k; i++){
      bool jud = true;
      for(int j = 0; j < k; j++){
       if(s[i+j] != s[i+k-1-j]) jud = false;
     }
     if(jud) {
       cnt++;
       break;
     }
    }
    res++;
  } while (next_permutation(s.begin(), s.end()));
  cout << res - cnt << endl;
}
```
### 解法
この問題では文字列`S`を並び替えてえられる文字列を順列全探索で求める。並び替えた文字列に対して、長さ`K`の部分文字列が回文かどうかを判定して、回文の場合をカウントしておく。その後、並び替えたすべての文字列の数から回文を含む数を引くことで結果が求まる。
GitHubでcherry-pickしたときのメモ

# 経緯

- 新機能追加

https://github.com/sympy/sympy/pull/26471

- SymPy 1.13.0 リリース

https://github.com/sympy/sympy/releases/tag/1.13.0

- なんかハングアップするんですけど(怒)

https://github.com/sympy/sympy/issues/26832

- 修正します(汗) (master ブランチ)

https://github.com/sympy/sympy/pull/26840

この修正を`1.13`ブランチにも追加しなければならない。ということで、cherry-pickを実施

# 手順
upstreamとは何ぞや、と思ったけど、そういう名前を付けるだけみたい。
```
git remote add upstream https://github.com/sympy/sympy.git
```

`branch_name`は好きな名前を付ける。
```
git fetch upstream
git checkout upstream/1.13
git checkout -b branch_name
```

そして、cherry-pick。今回は2つのコミットだったのでハッシュを2つ書く。短縮形でも大丈夫らしいのだが、安全を期して全部書く。
```
git cherry-pick 3135fab741d62fcbe4f8efd2850f52c51c1cb98a f7a05483e8a79ad1928c89a28244c1db7a31e11b
```

pushした後は、GitHubの画面でpull requestを作成する。このとき、"master"ではなく"base: 1.13"をプルダウンで選択することで"Able to merge"となる。これで完了。

# できたpull request

https://github.com/sympy/sympy/pull/26841

# 参考文献

- [forkしたレポジトリの更新とupstreamについて](https://qiita.com/fami-ki-chi/items/5a75fb14c696a6a2fe64)
- [【Git】cherry-pickで解決🍒](https://qiita.com/okmtz/items/62aa5a25f75b1754a861)
Windowsの.lnkや.url(ショートカットの拡張子)を表示させないと危ないって話。

注意：大したことないこと書いてます。お急ぎの方はTL;DRだけを読むことをおすすめします。記事自体そんな長くないけど。  
# TL;DR
.lnkとか.urlを表示するようにしたほうがいいよーって話。以上。  

# 経緯  

## .urlについて

https://forest.watch.impress.co.jp/docs/serial/yajiuma/1608997.html

上記記事において、.urlがデフォルトで非表示になっている事でセキュリティホールとなりうるということがありました。

現在のWindowsでは、まだIEのエンジンが残っていることが原因で、`mshtml:`から開始するURLにリンクするインターネットショートカットファイルからIEが開けてしまうという脆弱性です。これを利用して、他ファイルタイプに偽装して.htaファイル（WSHを利用したアプリケーション。簡単に言えばOSネイティブに動くHTML。）の実行が可能になってしまうんです。これにより任意コードの実行が可能になります。

## .lnkについて

それに加え最近は落ち着いていますが、2022夏頃まで、Emotetが大流行していました。Emotetの主な感染経路は本物そっくりな偽メールだと色々なところで言われていました。  

毎回新しい手法で感染させようとしてくるらしいのですが、そのうちの一つが、  
**メールの添付文書に.lnk(ショートカット)ファイルを付けて、開くと感染**  
ってのがあるらしい。  

これらは、Windowsの仕様を逆手に(?)取ったものですね。  
拡張子を表示する設定にしていても、.lnk、.urlをはじめとした、各種ショートカットなどは拡張子が表示されない。なんでなんでしょうね。  
まあ、通常ならショートカットファイルには左下にちっちゃい矢印がつくようになってるんですが、まあ慌ててると気づかない人もいるでしょうし、気づく要因は増やしといて損はない、と僕は思います。  

# .lnkと.urlを表示させよう  
簡単です。レジストリをいじります。  
バックアップしといた方が無難です。まあこの辺りいじっても壊れることはなさそうだけど念の為。
1. Win+R(名前を指定して実行/Run)に```regedit```入力してレジストリエディタを開く。  
1. ```HKEY_CLASS_ROOT\lnkfile```に移動し、```NeverShowExt```を削除。![NeverShowExtを削除](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2819245/e03bfdc6-a2bb-d2ec-a577-96af480606f8.png)  
（コンテクストメニューで文字列隠れちゃってますね...すいません。）  
1. .urlも表示させるなら、```HKEY_CLASS_ROOT\InternetShortcuts```でも同じ作業をします。  
（たまに調べると他のキーって書いてあることもあるんですが、僕が両方試してみたところ、InternetShortcutsの方でしか確認できませんでした。）  

# まとめ  
これ、悪用するといろいろできるんですよね。なんかしらの手段でexeのキーに```NeverShowExt```つけちゃえば、```FILE.docx.exe```とか言う名前のファイルで偽装できちゃうし...。  
まあ気をつけましょう。
ABC363をPythonで（A～C,E）

AtCoder Beginner Contest 363の解答等のまとめ

https://atcoder.jp/contests/abc363



# A問題
https://atcoder.jp/contests/abc363/tasks/abc363_a

次の対象のレートを求めて差を出力。
```python:A
r = int(input())
n = ((r // 100) + 1) * 100
print(n - r)
```

# B問題
https://atcoder.jp/contests/abc363/tasks/abc363_b

$L$を降順でソートして、$p$番目が何日あれば$t$になるか計算する。
```python:B
n, t, p = map(int, input().split())
L = sorted(map(int, input().split()), reverse=True)

print(max(0, t - L[p - 1]))
```

# C問題
https://atcoder.jp/contests/abc363/tasks/abc363_c

Pythonで通らなかったのでchatGPTでC++に変換した[このコード](https://atcoder.jp/contests/abc363/submissions/55808802)でACした。

並べ替え全通りにおいて、長さ$k$の回文があるか全部調べるといいはず。

変換前のPythonコード↓
```python:C
from itertools import permutations

def in_palindrome(s):
    for now in range(n - k + 1):
        if all(s[now + i] == s[now + k - i - 1]for i in range(k // 2)):
            return True
    return False


n, k = map(int, input().split())
s = input()
check = set()
ans = 0
for p in permutations(s):
    if p in check:
        continue
    check.add(p)
    if not in_palindrome(p):
        ans += 1

print(ans)
```

# D問題
https://atcoder.jp/contests/abc363/tasks/abc363_d

＜考察・感想＞
各桁内にある回文数は$9 \times 10 ^{(桁数-1)}$個。
あとは端数調整すればいいはずだと思っているが、うまくいかなかった。

# E問題
https://atcoder.jp/contests/abc363/tasks/abc363_e

これもPythonで通らなかったのでchatGPTでC++に変換した[このコード](https://atcoder.jp/contests/abc363/submissions/55804595)でACした。

各年ごとに、その区画からその隣接区画へ水が流れるようにUnionFindで繋げる。
外側の区画は高さが一致したら海として設定した1マスとUnionFindで繋げる。
そして海と繋がっていない区画の数が答えになる。

変換前のpythonコード↓
```python:E
from collections import defaultdict

class UnionFind:
    """
    URL : https://note.nkmk.me/python-union-find/
    """
    def __init__(self, n):
        self.n = n
        self.parents = [-1] * n

    def find(self, x):
        if self.parents[x] < 0:
            return x
        else:
            self.parents[x] = self.find(self.parents[x])
            return self.parents[x]

    def union(self, x, y):
        x = self.find(x)
        y = self.find(y)

        if x == y:
            return

        if self.parents[x] > self.parents[y]:
            x, y = y, x

        self.parents[x] += self.parents[y]
        self.parents[y] = x

    def size(self, x):
        return -self.parents[self.find(x)]

    def roots(self):
        return [i for i, x in enumerate(self.parents) if x < 0]


def main():
    h, w, y = map(int, input().split())
    a = [list(map(int, input().split())) for _ in range(h)]

    UF = UnionFind(h * w + 10)
    sea = h * w + 5
    data = []
    for i in range(h):
        for j in range(w):
            data.append([a[i][j], i, j])

    data.sort()

    now = 0
    for i in range(1, y + 1):
        while now < h * w and data[now][0] <= i:
            _, x_i, y_i = data[now]
            for x_j, y_j in [(x_i + 1, y_i), (x_i - 1, y_i), (x_i, y_i + 1), (x_i, y_i - 1)]:
                if 0 <= x_j < h and 0 <= y_j < w:
                    if a[x_i][y_i] >= a[x_j][y_j]:
                        UF.union(x_i * w + y_i, x_j * w + y_j)
                else:
                    UF.union(x_i * w + y_i, sea)

            now += 1
        print(h * w - UF.size(sea) + 1)


main()
```
ABC363参加記録　by Python

# リアルタイムに解けた問題

## [A - Piling Up](https://atcoder.jp/contests/abc363/tasks/abc363_a)

### 問題文
AtCoderでは、ユーザーのレートは正の整数で与えられ、その値に応じて<font color="Red">`^`</font>がいくつか表示されます。特に、レートが１以上399以下のときは次のようになります。
- レートが１以上99以下のときは<font color="Red">`^`</font>は１個表示される。
- レートが100以上199以下のときは<font color="Red">`^`</font>は2個表示される。
- レートが200以上299以下のときは<font color="Red">`^`</font>は3個表示される。
- レートが300以上399以下のときは<font color="Red">`^`</font>は4個表示される。

現在の高橋くんのレートは$R$です。ここで、$R$は1以上299以下の整数であることが保証されます。
表示される<font color="Red">`^`</font>の数を現在より増やすために、高橋くんは少なくともレートをいくつ上げる必要があるか答えてください。
なお、本問題の制約下で、高橋くんはレートを400以上にあげることなく<font color="Red">`^`</font>の数を増やすことができることが証明できます。


### 制約
- $1 \leq R \leq 299$
- $R$は整数

### アルゴリズム
問題文で定義されている範囲を条件分岐の基準とし、含まれる範囲の最大値＋1-$R$を出力。

### ソースコード
``` python
R = int(input())

if 1 <= R <= 99:
  print(100-R)
elif 100 <= R <= 199:
  print(200-R)
elif 200 <= R <= 299:
  print(300-R)
else:
  print(400-R)
```

## [B - Japanese Cursed Doll](https://atcoder.jp/contests/abc363/tasks/abc363_b)

### 問題文
$N$人の人がおり、$i$人目($1 \leq i \leq N$)の現在の髪の長さは$L_{i}$です。
すべての人は１日経つごとに髪の長さが１づつ増えます。

髪の長さが$T$以上の人が初めて$P$人以上になるのは現在から何日後か出力してください。
ただし、現在の時点ですでに髪の長さが$T$以上の人が$P$人以上いる場合は０を出力してください。

### 制約
- $1 \leq N \leq 100$
- $1 \leq L_{i} \leq 100$
- $1 \leq T \leq 100$
- $1 \leq P \leq N$
- 入力はすべての整数

### アルゴリズム
whileでループを回し、各ループごとに髪の長さが$T$以上の人数をカウントする。その値が$P$以上であれば、そのときの経過日数を出力し、プログラムを終了する。$P$以下であれば、経過日数とそれぞれの髪の長さを1増やす。

### ソースコード
``` python
N, T, P = map(int, input().split())

L = list(map(int, input().split()))

day = 0

while True:
  count = 0

  for l in L:
    if l >= T:
      count += 1

  if count >= P:
    print(day)
    exit()
  else:
    day += 1

  for i in range(N):
    L[i] = L[i] + 1

```
Oracleのローカル接続に関して

## はじめに

ある時、Oracleのローカル接続時にプロセスがどうなっているのか、
ふと気になったので調べてみました。環境はRHEL7.2です。

専有サーバ接続の場合、サーバプロセスとクライアントプロセスが１：１になるので、
下図のような構成図になっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3837246/cee242cc-92e7-0652-8f4c-b17f5aa903fd.png)

ローカル接続をした状態でpstree -pコマンドを実行すると、下図のような
親子関係が確認できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3837246/c2bbb8da-1a7a-db03-3eef-0fa6cc6d5da0.png)

つまり、プロセスID 4713 のクライアントプロセス（sqlplus）が、プロセスID 4714 の 
Oracle サーバプロセス（oracle）を fork しているように見えます。

▼こんな感じ・・・？
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3837246/12f0cae4-be44-6f72-27ee-81bf6ff8ef99.png)

実際、sqlplusコマンドをstraceで見てみると、こんな感じで親子が execve を実行していました↓。
-f は子プロセスのシステムコールも出力するオプションです。

```Bash:strace -f -e execve sqlplus / as sysdba
[oracle@ホスト名 ~]$ strace -f -e execve sqlplus / as sysdba
execve("/oracle/product/11.2.0/dbhome_1/bin/sqlplus", ["sqlplus", "/", "as", "sysdba"], [/* 25 vars */]) = 0

SQL*Plus: Release 11.2.0.4.0 Production on Fri Aug 2 21:28:56 2024

Copyright (c) 1982, 2013, Oracle.  All rights reserved.

Process 4714 attached
[pid  4714] execve("/oracle/product/11.2.0/dbhome_1/bin/oracle", ["oracle[SID]", "(DESCRIPTION=(LOCAL=YES)(ADDRESS"...], [/* 26 vars */]) = 0

↑ 子プロセス(4714)がoracleを実行しているのが分かります。

(略)
```

## 環境変数について

ローカル接続をする際は、通常 ORACLE_SID と ORACLE_HOME を環境変数に
設定したうえで接続しますが、ログインシェルで設定したこれらの環境変数が
確かに子プロセスに引き継がれていることを確認してみました。

```Bash:子プロセス(PID:4714)の環境変数
[oracle@ホスト名 ~]$ cat /proc/4714/environ
(略)
ORACLE_SID=～～
(略)
ORACLE_HOME=/oracle/product/11.2.0/dbhome_1
(略)
[oracle@ホスト名 ~]
```
確かに引き継がれてますね。

これらの環境変数をexecveがどのように使用しているのかについて、strace結果を
読み込んでより詳細に理解したかったのですが、よく分かりませんでした。

少なくとも、環境変数ORACLE_HOMEに関しては$ORACLE_HOME/lib配下にある
共有ライブラリ（libclntsh.soとか、libnsl.soとか）をロードするためなどに
使っているみたいです。

ちなみに、リモート接続の場合もクライアントプロセスが接続要求をして
サーバプロセスが生成されるので親子関係がありそうな気がしましたが、
実際にはそのような親子関係は確認できませんでした。

## おわりに

当たり前のことなのかもしれませんが、調べてみて少しだけローカル接続の理解が
深まったような気がします。

・追記（20240729）

ローカル接続時に、効率化のために、環境変数を記述したsetup.shのようなファイルを
/home/oracle に配置して実行することが多いですが、この投稿を書いた後、[この記事](https://qiita.com/akase244/items/4832e9d332c276720f0e)を
読んだところで、なぜ./setup.shではなく. ./setup.sh（もしくはsource ./setup.sh）を
実行すべきなのか改めて理解できました。

```setup.shの一例
#!/bin/bash

export ORACLE_BASE=/oracle
export ORACLE_SID=[SID]
export ORACLE_HOME=/oracle/product/11.2.0/dbhome_1
export PATH=$PATH:$ORACLE_HOME/bin:$ORACLE_HOME/OPatch
```
【AWS】Cognitoのカスタムメッセージ Lambda トリガーでメール本文が変わらない問題

# カスタムメッセージとは

以下は Cognito の Lambda トリガーでカスタマイズしたメール：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/c15a9194-adde-46ab-6540-e1c52612bf33.png)

カスタムメッセージ Lambda トリガーでメール件名・本文のカスタマイズを行うことができる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/8b04df32-ab4a-a7fd-c7f2-840d086cd86f.png)

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/user-pool-lambda-custom-message.html

# 発生事象

メール件名は変更できたが、メール本文は変わらなかった。

# カスタムメッセージ Lambda トリガーを使用するときの注意点

## 注意点1

- メールアドレス検証メッセージは「Code」を選択していること

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/6ca1dda0-0689-b8b6-fe26-8cb197f7c469.png)

## 注意点2

- レスポンスに `event.request.codeParameter` を含めること

> **emailMessage**
> ユーザーに送信するカスタム E メールメッセージ。emailMessage パラメータで HTML 書式を使用できます。リクエストで受信される codeParameter 値を変数 {####} として含める必要があります。

下記ドキュメントの **カスタムメッセージレスポンスパラメータ** 参照。

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/user-pool-lambda-custom-message.html#cognito-user-pools-lambda-trigger-syntax-custom-message

## イベントとレスポンス

- Lambda 関数が受け取るイベント

```js
{
  version: '1',
  region: 'your-region',
  userPoolId: 'your-user-pool-id',
  userName: 'user-name',
  callerContext: {
    awsSdkVersion: 'aws-sdk-js-3.614.0',
    clientId: 'your-user-pool-client-id'
  },
  triggerSource: 'CustomMessage_SignUp',
  request: {
    userAttributes: {
      sub: 'user-sub-value',
      email_verified: 'false',
      'cognito:user_status': 'UNCONFIRMED',
      email: 'your@email.address'
    },
    codeParameter: '{####}', // この値を event.response.emailMessage に含める必要がある
    linkParameter: '{##Click Here##}',
    usernameParameter: null
  },
  response: { smsMessage: null, emailMessage: null, emailSubject: null }
}
```

サンプルコードは以下参照：

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/user-pool-lambda-custom-message.html#aws-lambda-triggers-custom-message-example

# 参考

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/user-pool-lambda-custom-message.html
親Routeのpathをネストした配下のRouteにデフォルトルートとして設定したい

## react-router v6で親Routeのpathをネストした配下のRouteにデフォルトルートとして設定したい

# バージョン5からバージョン6で変更された書き方
* Switch ⇒Routes
* render(関数の中でコンポーネントを返すようにする) 
⇒element（直接コンポーネントを指定して表示する）

# v6で配下のRouteにデフォルトルートとしてpathを確定させるには
render関数ではデフォルトでpropsを受け取っているがv6ではrenderを使用していない。
その為propsを使用せずに親RouteのPathをネストしたRouteにデフォルトルートとして設定するのにindexを使用する方法がある。
※{match: { url }}はpropsの中にあるmatchというオブジェクトの中にあるurlというプロパティを指している

# v5での書き方
```App.jsx
<BrowserRouter>
    <div className="App">
      <Link to="/">Home</Link>
      <br />
      <Link to="/Page1">Page1</Link>
      <br />
      <Link to="/Page2">Page2</Link>
    </div>
    <Switch>
      <Route exact path="/" >
        <Home />
      </Route>
      <Route 
      path="/page1"
      render={({match: { url }}) => (
        <Switch>
            {console.log(url)}
            <Route exact path={url}>
            <Page1 />
            </Route>
            <Route  path={`${url}/detailA`}>
            <Page1DetailA />
            </Route>
            <Route  path={`${url}/detailB`}>
            <Page1DetailB />
            </Route>
        </Switch>
      )}
      />
      <Route path="/page2">
      <Page2 />
      </Route>
    </Switch>
    </BrowserRouter>

```
# v6での書き方
```App.jsx
<BrowserRouter>
    <div className="App">
      <Link to="/">Home</Link>
      <br />
      <Link to="/Page1">Page1</Link>
      <br />
      <Link to="/Page2">Page2</Link>
    </div>
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="/page1">
        <Route index={true} element={<Page1 />} />
            <Route path="detailA" element={<Page1DetailA />} />
            <Route path="detailB" element={<Page1DetailB />} />
        </Route>
      <Route path="/page2" element={<Page2 />} />
    </Routes>
    </BrowserRouter>
```
【備忘録】初めてのiOSアプリ開発 第5回 メインスレッドとは

## メインスレッド（Main thread）とは
ブラウザーがユーザーのイベントや描画を処理する。
・UIの更新やユーザーの入力処理はメインスレッドで行われる。

### 1. UIの更新
mainスレッドで行う必要があり、UIの変更がユーザーインターフェースに即座に反映されるようにするため

### 2. レスポンスの確保
メインスレッドで長時間のブロッキング操作を行わないようにする。

バックグラウンドスレッドで長時間かかる処理を実行する。
・ネットワーク通信
・データベースのクエリ

結果が得られたらmainスレッドに戻してUIを更新する。

### 3.イベント処理
ユーザーのタップやスワイプといったイベントをメインスレッドで処理することで、一貫性を保つ。メインスレッドの負荷を軽減し、アプリケーション全体のパフォーマンスを向上させることができる

## 参考サイト

https://jp-seemore.com/app/15513/
【セッションレポート】「Google Cloud の RDB を徹底比較！ 選び方と最新機能紹介」に参加しました

# はじめに
本記事はClassmethod Odyssey(DevelopersIO 2024)のリアルイベント(7/20)において Classmethod の大栗さんが講演された「Google Cloud の RDB を徹底比較！ 選び方と最新機能紹介」のレポートです。

# 公式セッション紹介
> Google Cloud はリレーショナルデータベースのサービスを多数提供していますが、どのように使い分けるか難しい問題になっています。本セッションでは各種リレーショナルデータベースサービスの機能やパフォーマンスを比較しつつ、ユースケースをご紹介します。

# 発表資料
- [Google Cloud の RDB を徹底比較！ 選び方と最新機能紹介 #devio2024](https://speakerdeck.com/maroon1st/google-cloud-no-rdb-woche-di-bi-jiao-xuan-bifang-tozui-xin-ji-neng-shao-jie-number-devio2024)

# 概要／おすすめポイント
Google CloudのRDBとしては以下のサービスがあります。
- CloudSQL Enterprise (MySQL, PostgreSQL, SQL Server)
- CloudSQL Enterprise Plus (MySQL, PostgreSQL)
- AlloyDB (PostgreSQL)
- Cloud Spanner
- BigQuery
- (Bare Metal Solution(Oracle Database))

本セッションでは、CloudSQL Enterprise(MySQL, PostgreSQL)、CloudSQL Enterprise Plus(MySQL, PostgreSQL)、AlloyDB(PostgreSQL)について、性能面・機能面での比較を行われていました。特に性能面では実際にHammerDBでの負荷がけを行い、結果を評価されていたのが興味深かったです。
※性能評価は一例なのでご自身のワークロードで試してくださいね、と何度か強調されてました

# セッション内容の紹介
性能面以外の比較という面では、機能面においてそれぞれのデータベースの特長が紹介されていました。
- メンテナンスにおける1秒未満のダウンタイム(スケールアップ・ダウン)
    - CloudSQL Enterprise Plus, AlloyDB
- カラム型エンジン
    - AlloyDB
- Vector検索
    - CloudSQL(PostgreSQL) ← pgvector
    - AlloyDB ← pgvector互換 SCaNNインデックス
    - CloudSQL(MySQL) ← Google独自実装
- クロスリージョン Zero Data Loss スイッチオーバー
    - AlloyDB

Gemini in Databaseもプレビューですが実装されてきているとのことでした。この辺は今後拡充されそうですね。

Spannerについてもアーキテクチャから触れられてましたが、[クラウドネイティブなデータベースとSpanner](https://gihyo.jp/article/2023/12/modern-app-development-on-google-cloud-04) や [Regional, dual-region, and multi-region configurations](https://cloud.google.com/spanner/docs/instance-configurations) 辺りを確認すると良いかと思います。

また、6月に発表されたOracle社との提携の話にも触れられていました。
Azureでもまだ日本では相互接続はできますがOracle Database@Azure(Azureのリージョン内でOCIのOracle Databaseサービスを利用すること)は利用できないので、今後実際にどのような使われ方をされるのかは興味あります。。

最後に、どのような場合にどのデータベースを選択するのか、をフローチャート化されており、とても興味深かったです。発表でも触れられてましたが、ワークロードの他にもいろいろなファクターがあるので、なかなかチャートのようにはいかないと思いますが、考え方として覚えておくと参考になるのでは、と思いました。

# まとめ

それぞれのリレーショナルデータベースを横並びで比較する、というのはAWSなどでも実施されてますが、比較軸を定めるだけでも難しく、さらに性能評価は条件やワークロードによっても違った結果になるのでなかなか難しい試みですが、一つの結果としてとても興味深かったです。

大変そうですが、Query Insightsの結果等で何がネックになっているのかとかが見られると、さらにおもしろそうだと思いました。
Django Ninjaでキャッシュを使う

# 初めに
Djangoには元々キャッシュ機能があり
```python
from django.views.decorators.cache import cache_page

@cache_page(60 * 15)
def my_view(request): ...
```
こんな感じでデコレータを使うことでビューの結果をキャッシュできます
Django Ninjaと同じRESTフレームワークであるDjango REST Frameworkではこの`cache_page`のデコレータが使えたのですがDjango Ninjaでは使えなかったので使えるデコレータを実装しました

# コード
```python:cache.py
from django.core.cache import cache
from typing import Any
from django.http import HttpRequest
from functools import wraps


class Cache:
    @classmethod
    def get(cls, key):
        return cache.get(key)

    @classmethod
    def set(cls, key, value, time_out = 60 * 60):
        cache.set(key, value, time_out)


def cache_request(timeout, *, cache_key = None):
    def wrapper(func):
        def inject(func):
            @wraps(func)
            def request(request: HttpRequest, **kwargs: Any) -> Any:
                key = cache_key
                if not key:
                    # パスとクエリをキャッシュキーにする
                    # クエリの並び順が変わっても同じキャッシュを使うためにソートしている
                    path_key = request.path
                    query_string_key = ""
                    if request.META.get("QUERY_STRING", ""):
                        query = {}
                        for query_string in request.META.get("QUERY_STRING", "").split('&'):
                            query_data = query_string.split('=')
                            query[query_data[0]] = query_data[1]
                        query = sorted(query.items())
                        query = dict((x, y) for x, y in query) 
                        for k, v in query.items():
                            if query_string_key:
                                query_string_key += "&"
                            query_string_key += f"{k}={v}"
                    key = f"{path_key}?{query_string_key}"
                if key:
                    cache = Cache.get(key)
                    if cache:
                        return cache
                res = func(request, **kwargs)
                if key:
                    Cache.set(key, res, timeout)
                return res
            return request
        return inject(func)
    return wrapper
```

# 使い方

前提としてDjangoのキャッシュが利用できる状態にしてください

https://docs.djangoproject.com/en/5.0/topics/cache/

その後こんな感じで利用できます
```python
from ninja.pagination import paginate
from xxxx.cache import cache_request

router = Router()

@router.get("/")
@cache_request(60*60)
@paginate
def list(request): ...
```
一つ注意点としては`@paginate`がある場合`@paginate`よりも上に設定してください
下だとクエリにOFFSET、LIMITがつかない結果がキャッシュされます
キャッシュのキーはパスになっているので、何か他のものにしたい場合は、以下のように指定することもできます
```python
@cache_request(60*60, cache_key='items')
```
転職ドラフトの転職談

## はじめに
こちらの記事は[転職ドラフト体験談投稿キャンペーン](https://job-draft.jp/articles/251)に参加しています 。

友達紹介コードは`FQPW`なので、是非とも登録いただいて一緒にオライリーの本読めたら嬉しいです！

## 結果
若干の年収DOWNを受け入れつつ、やりたいことへチャレンジすることを決めました。
個人的には業務内容、ドメイン、待遇について満足な結果となります。

- 年収は若干DOWN
- 職種は新規事業開発→自社開発(SaaS)
- ロールはフルスタックになんでもやる → フロントエンドに軸置きながらフルスタックにも動く(かも)
- フルリモート → フルリモート
    - 1Qに1回オフラインで会うのは転職前後で変化なし

## 経歴
ざっくり書くと以下のような感じ

- 今年度36歳
- 大学は電気電子工学を専攻
- 半導体の開発エンジニア：7年
    - 組み込みに近い領域で回路設計、プログラミング(C,C++,Perl)
- 自社SaaSの導入コンサル：2年弱
    - クライアントワークメイン。自社アプリの導入設計とかWebディレクションとか
- Web開発エンジニア:3年
    - フロントエンド、バックエンド、インフラを必要な箇所を必要なだけ触ってきた
    - ガリガリ実装するというよりは、PMだったり設計だったりの比重が大きかった
    - 触ったことがあるのは以下の通り。ちょっとしか触ってないのもある
        - React,Vue,TypeScript,PHP,Laravel,Python,Node,AWS
- 職務経歴書上の所属会社は10社ぐらい。ハイパーやばい奴にしか見えない
- 業務外
    - OSSのかなーり簡単なissueへコントリビュートしてみたり
    - LeetCodeとかAtCoder(緑)やったり
    - 気になるフレームワーク触って遊んだり

## 転職の動機
ガツガツ開発をやる、若しくはプロダクトを成長させる取り組みをしたいが、現在所属している組織では難しいというのが一番大きい理由。
所属している組織のやり方には納得感があるので、単にやりたいことと現状とのGAPが大きかった。

給与、就業環境、部署の方々など、総合的な環境はとてもよかったです。業務内容だけがネックでした。

## 転職活動開始
まさに地獄でした。気持ちいいぐらいまったく内定もらえませんでした。理由は後述するのですが、自分が採用側に立ったことを仮定すると納得させる理由づけがとても弱いと思っています。
ただ、職歴がやばいのにも関わらず、意外と書類だったり技術面接以外は通るんですよね。ここは個人的に意外でした。

何社ぐらい受けたかなと数えてみたんですが、多すぎて途中で諦めました。面接だけだと30社ぐらいでしょうか。カジュアル面談だと倍以上はいってそうですね。

転職活動にはかなりエネルギーを使うことは皆さんご存知かと思うのですが、私は途中で一回力尽きました。
そんな人間にとって転職ドラフトは魅力的でしたね。レジュメ書いたら後はエントリーボタンぽちーでいいですもん。

ということで、複数の媒体を利用していましたが、途中から転職ドラフトのみにしていました。
最終的には「いい企業と会えたらいいなー」ぐらいの温度感に着地して、気合を入れて転職するという感じではなくなってました。

## 苦労した点
活動中に苦労した点は主に期待値ギャップでした
- 現在の年収がキャリアと比較して高いが、転職後の年収は据え置く必要がある
- Webエンジニアのキャリアとしては3年ぐらいなのに、マネージャーやエキスパートの一歩手前ぐらい貰ってしまっている
- もちろん希望年収に対する企業の技術的な要求水準には満たしていないケースが大半と思われる(ロールとしてはテックリード以上になる)
- フルリモートで探す必要があり、企業の母数が必然的に減ってしまう

## 評価された点
そんな中でもオファーレターをいただいた企業には一定の共通項がありそうでした。
ビジネススキルや技術的なベーシックスキルがあるかどうかを判断軸にしている会社さんにはウケがよかったかもしれません。

- クライアントワークやマネージャー業務をやった経験があること(ゼネラリスト志向とのマッチ)
- スタートアップ所属時、社員エンジニア1人で運用を回していたこと
- LeetCodeやAtCoderへ参加していたこと
- コーディング試験があり、ぼちぼちな結果を残せたこと
    - フレームワークでの実装が試験に含まれるともちろん弱いのですが、アルゴリズムやシステムデザインは無難な結果でした
    - 逆にフレームワークやライブラリについて聞かれたら無理ゲーでした

## 選考内容
よくある質問をピックアップしておきます。ほぼ聞かれる内容だったので参考にしていただければと思います。
- 今までで技術的に困難だった課題は？
- チーム開発で気をつけていることは？
- コードレビュー時に気をつけていることは？
- チーム内でハレーションが起こっていたらどう解決する？
- いまのアプリケーションをゼロから作り上げていいよと言われたらどのようなアーキテクチャにする？
- コーディング試験があれば
    - なぜこのような実装にしたのか？
    - さらに質を上げるにはどのように改善するか？
- システムデザイン試験があれば
    - なぜこのような設計にしたのか？
- どのように技術をキャッチアップしているか？
- 最近読んだ書籍は何か？なぜそれを選択したのか？

## 転職ドラフト 良かった点
前置き長くなっちゃいました。ここから転職ドラフトの詳細体験談になります。
個人的な必須要件は以下のようになりますが、これらは面談や面説を通して決めるものになると思います。JDのレンジはあるけど選考が進まないと不明確ですしね。
この問題をボタンぽちーしておくだけで解消できると考えると、素敵なサービスだと個人的には思います。

- 年収は据え置き(家庭持ちなので下がると妻に怒られる)
- 開発ができること
- 原則フルリモート

個人的に一番ネックだった「年収とスキルレベルのギャップ確認」というのを、オファー段階である程度スクリーニングしてもらえるのは嬉しかったです。
もちろんレジュメだけでは読み取れない部分が多分にあるので、めちゃくちゃ高度な要求のスカウトがあったりもしました。それはそれ。

## 転職ドラフト オファー内容
経歴的に何でもやっていたせいで、オファーの内容も多岐に渡っています。フルスタックエンジニアでのオファーが一番多かったかなと思います。事業規模が大きい会社さんではバックエンドでのオファーって感じでした。

- バックエンドエンジニア
- インフラエンジニア
- フルスタックエンジニア
- プロジェクトマネージャー
- プロダクトマネージャー

## 転職ドラフト 審査について
レジュメの審査は厳しくないと思うので登録だけしておくといいのでは？と思ってます。構造化した文章が書ければさくっと通るんじゃないでしょうか。
私の場合、1回目は雑に書いたらNGで、2回目でOKになりました。ただ、2回目に書いたものもペラッペラだったと記憶しているので審査基準はあまり厳しくないと思ってます(厳しくなっていたらすみません...)
【AWS】CognitoのForgotPassword API実行時、UserNotFoundExceptionがスローされない

# 原因

「ユーザー存在エラーの防止」が有効化されているため。

![スクリーンショット 2024-07-20 213630.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/1daf408a-f779-0796-a731-01df3ee188ab.png)

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pool-managing-errors.html

# 解決方法

「アプリケーションクライアントに関する情報」から「編集」を押下し、下記のチェックボックスのチェックを外す。

![スクリーンショット 2024-07-20 214009.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/621414/71810f60-7e6e-20e4-7d8a-38b92b930550.png)

# 動作確認

存在しないユーザーを指定して ForgotPassword API を実行した場合、下記の例外がスローされた。

```
UserNotFoundException: Username/client id combination not found.
```

# 参考

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/cognito-user-pool-managing-errors.html
Pythonの仮想環境を簡単に構築する方法【venv】

この記事では、Pythonの仮想環境を簡単に構築し、管理する方法を解説します。

# 1. Pythonのインストール
まずはPythonがシステムにインストールされているか確認します。ターミナルまたはコマンドプロンプトを開き、以下のコマンドを実行します。

```terminal
python --version
```

Pythonがインストールされていない場合は、Python公式サイトから最新バージョンをダウンロードしてインストールしてください。

## 2. 仮想環境の作成
Python 3.3以降では、venvモジュールを使用して仮想環境を簡単に作成できます。以下のコマンドを実行して仮想環境を作成しましょう。

```terminal
python -m venv myenv
```

ここで、myenvは仮想環境の名前です。自分のプロジェクトに合わせて適宜変更してください。

## 3. 仮想環境の有効化
次に、仮想環境を有効化します。使用しているオペレーティングシステムに応じて、以下のコマンドを実行します。

・ Windows:
```
myenv\Scripts\activate
```

・macOS/Linux:
```
source myenv/bin/activate
```
仮想環境が有効になると、コマンドラインのプロンプトに仮想環境の名前が表示されるようになります。
```
(myenv) $ 
上のように表示されます。
```

## 4. パッケージのインストール
仮想環境が有効化された状態で、必要なパッケージをインストールします。例えば、requestsパッケージをインストールするには以下のコマンドを実行します。

```
pip install requests
```

## 5. 仮想環境の無効化
作業が終わったら、仮想環境を無効化します。以下のコマンドを実行するだけです。

```
deactivate
```

これで仮想環境が無効化され、元のシステム環境に戻ります。

## まとめ
Pythonの仮想環境を使用することで、プロジェクトごとに異なるパッケージや依存関係を簡単に管理できます。これにより、プロジェクト間の衝突を防ぎ、開発作業を効率化することができます。仮想環境の作成と管理は非常に簡単なので、ぜひ活用してみてください。
[Django] GmailとYahooメールが要求するワンクリック解除を実装する方法について

## 概要
2024年の2月からGmailとYahooでは1日5000通以上のメールを送信する場合はワンクリックによるメール購読解除機能を実装することが求められるようになりました
実装する際は以下の情報をメールのヘッダに追加する必要があるのでその方法について解説していきます

```
List-Unsubscribe-Post: List-Unsubscribe=One-Click
List-Unsubscribe: <https://some-system.jp/unsubscribe/example>
````

## 前提
- Djangoのプロジェクトを作成およびデプロイ済み
- Djangoのプロジェクト内でメール送信設定が完了済み
- AWS SESの設定が完了済み

## 実装
今回はDjangoのEmailMultiAlternativesクラスを使ってメールを送信します
EmailMultiAlternativesクラス内にヘッダを指定できるので以下のように記載します

```emails.py
from django.core.mail import EmailMultiAlternatives
from django.template.loader import render_to_string


def send_email(email):
    """メールを送信する

    Args:
        email (str): 送信先Eメール
        name (str): ユーザ名
    """
    plaintext = render_to_string(
        "send_email.txt",
    )

    msg = EmailMultiAlternatives(
        subject="テスト",
        body=plaintext,
        from_email="noreply@example.com",
        to=[email],
        headers={
            "List-Unsubscribe-Post": "List-Unsubscribe=One-Click",
            "List-Unsubscribe": "<https://example.com>",
        },
    )

    # 送信
    msg.send()
```


```django/core/mail/message.py
class EmailMultiAlternatives(EmailMessage):
    """
    A version of EmailMessage that makes it easy to send multipart/alternative
    messages. For example, including text and HTML versions of the text is
    made easier.
    """

    alternative_subtype = "alternative"

    def __init__(
        self,
        subject="",
        body="",
        from_email=None,
        to=None,
        bcc=None,
        connection=None,
        attachments=None,
        headers=None,
        alternatives=None,
        cc=None,
        reply_to=None,
    ):
```

## 実際に送信してみよう！
以下のように送信したメールにヘッダが表示されたら成功です

![スクリーンショット 2024-07-20 21.07.20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/625980/beb75eb3-ad34-c1a4-8ccb-b66e5a72a3a0.png)


## 登録解除リンクが表示されない場合は
登録解除リンクについてはGoogle側でドメインが信頼できるかどうかと一定数以上の購読者がいるかどうかを確認してから表示させる仕様だそうです

https://support.google.com/mail/thread/49653586?hl=en&msgid=49801705

## 参考
https://mailmarketinglab.jp/gmail-one-click-unsubscribe/

https://note.shiftinc.jp/n/n37e894acbcf3

https://support.google.com/a/answer/81126?sjid=8610702489842767047-AP

https://datatracker.ietf.org/doc/html/rfc8058

https://support.google.com/mail/thread/49653586?hl=en&msgid=49801705