OmniAICreator/Qiita-1.07M huggingface dataset:
ESP32: フラッシュメモリーの使い方

こちらのページを参考にしました。
[ESP32-WROOM-32のFlashへファイルI/Oすると便利](https://dev.classmethod.jp/articles/esp32_flash_io/)

## プログラム

```text:flash_memory.ino
// ---------------------------------------------------------------------
/*
	flash_memory.ino

						Aug/05/2024
*/
// ---------------------------------------------------------------------
#include "FS.h"
#include "SPIFFS.h"

#define FORMAT_SPIFFS_IF_FAILED true
// ---------------------------------------------------------------------
int count = 0;
void setup()
{
	// put your setup code here, to run once:

	Serial.begin(115200);

	delay(100);
	SPIFFS.begin(FORMAT_SPIFFS_IF_FAILED);	
	delay(100);
	Serial.println("*** start ***");
	delay(100);

	fs::FS fs = SPIFFS;
	File file = fs.open("/hello.txt", FILE_WRITE);
	file.print("*** Flash Memory ***\r\n");
	file.print("Aug/05/2024\r\n");
	file.print("*** テスト ***\r\n");
	file.print("*** end ***\r\n");
	delay(100);


	Serial.println("*** setup end ***");
}

// ---------------------------------------------------------------------
void loop() 
{
	Serial.println("Hello World! " + String(count));
	delay(1000);
	Serial.println("Good Morning! " + String(count));
	delay(1000);
	Serial.println("こんにちは " + String(count));
	Serial.println();
	delay(1000);

	fs::FS fs = SPIFFS;
	File file = fs.open("/hello.txt");
	while(file.available()){
	Serial.write(file.read());
	}

	delay(2000);
	count++;
}

// ---------------------------------------------------------------------
```

## 実行結果

```text
$ cu -l /dev/ttyUSB0 -s 115200
Connected.
(省略）
*** start ***
*** setup end ***
Hello World! 0
Good Morning! 0
こんにちは 0

*** Flash Memory ***
Aug/05/2024
*** テスト ***
*** end ***
Hello World! 1
Good Morning! 1
こんにちは 1

*** Flash Memory ***
Aug/05/2024
*** テスト ***
*** end ***
```
【備忘録】LibreOffice Impress で作成した図を eps で保存する方法

# はじめに

LinuxユーザでもLaTeXに貼り付ける図をパワーポイントのようなツールで作成することは少なくないだろう．しかし，スクリーンショットを使って画像保存をすると，解像度に限りがあるため，拡大すると画像が粗くなってしまう．本記事では，パワーポイントツール LibreOffice Impress で作成した図を eps に変換して保存する小技を紹介する．備忘録的なものなので，以下の４ステップのみを書いておく．

# 主題

1. Eps化したい図（領域）を選択＆コピー
1. Gimp を立ち上げる（Gimpは `sudo apt install gimp` でインストール）
1. Gimp にペースト
1. [ファイル]->[エクスポート]->ファイルの保存拡張子を eps にする
[paiza]神経衰弱 (paizaランク B 相当)

# 問題文を読み解くのがめんどい。。。

https://paiza.jp/works/mondai/b_rank_skillcheck_archive/concentration

# 面倒くさいので画面パタメータをListでもらった後のFunctionだけ
ちなみに引数のListを作ってる箇所は[こっちの記事](https://qiita.com/AsilHatake/items/842548e48ae4a0eae119)に書いてあるYO!!

```javascript:concentration.js
// [問題文（原文）]
// 神経衰弱と呼ばれるトランプゲームのシミュレーションをしましょう。
// 今回は数字が書かれたトランプのみを考え、ジョーカーは考えません。
// 
// まず、トランプを縦 H 枚、横 W 枚の長方形の形に並べた状態でスタートします。
// H × W 枚のトランプには 1 〜 13 の数字のうちどれか1つが書かれています。
// また、同じ数字が書かれたトランプが複数あります。
// 
// プレイヤーが N 人おり、それぞれ 1 〜 N で番号付けられています。
// ゲームが始まると、1番の人から、このような手順でプレイしていきます。
// 
// ・並べられたトランプから2枚のトランプを選び、めくります。
// ・めくった2枚のトランプに異なる数字が書かれていれば、次のプレイヤーの手番となります。同じ数字であれば、次の操作をおこないます。
// ・まず、2枚のトランプはめくったプレーヤーのものとなり、取り除かれます。
// ・トランプがすべて取り除かれた場合、ゲームは終了となります。
// ・トランプが残っている場合、同じプレーヤーがまた最初の手順に戻り、トランプをめくります。
// 
// ここで、N 番のプレイヤーの次のプレイヤーは 1 番のプレイヤーであるとします。
// 
// ゲームの初期状態におけるトランプの配置と、ゲームが終わるまでに捲られたトランプに関する時系列順の記録が与えられます。
// その記録を用いて、各プレイヤーが取り除いたトランプの枚数を求めてください。
function concentration(lines) {
  // 入力は以下のフォーマットで与えられます。
  // 
  // H W N
  // t_{1,1} t_{1,2} ... t_{1,W}
  // t_{2,1} t_{2,2} ... t_{2,W}
  // ...
  // t_{H,1} t_{H,2} ... t_{H,W}
  // L
  // a_1 b_1 A_1 B_1
  // a_2 b_2 A_2 B_2
  // ...
  // a_L b_L A_L B_L
  // 
  // 1行目には3つの整数 H, W, Nが入力されます。
  // H と W はそれぞれ並べられたトランプの縦方向の枚数と横方向の枚数で、N はプレイヤーの数を表します。
  // 
  // 続く H 行には、配置されたトランプに書かれた数字が入力されます。
  // t_{i,j} は i 行 j 列に置かれたトランプに書かれた数字を表します。
  // 
  // 次の行には、記録の長さ L が与えられます。
  // 
  // 続く L 行には、捲られたトランプの記録が時系列順で与えられます。
  // これは、a_i 行 b_i 列のトランプと A_i 行 B_i 列のトランプが捲られたことを表します。

  // カードの配置はJSONで管理（連想配列でもいいけどJSON）
  const cards = {};
  // プレイヤー（取得枚数を管理）
  const players = [];
  // 現在処理中のプレイヤー番号
  let palyerIndex = 0;

  // 入力チェック面倒くさいので正常系のみ対応するよ
  const [row, col, playerCnt] = lines[0].split(" ").map(v => Number(v));
  // カード情報を設定
  lines.slice(1, row + 1).forEach((line, rowIndex) =>
    line.split(" ").forEach((card, colIndex) => cards[`${rowIndex + 1}_${colIndex + 1}`] = card)
  );
  // プレイヤーの取得枚数の初期化
  Array.from({ length: playerCnt }, (_, i) => players[i] = 0);
  // 取得状況ループ
  lines.slice(row + 2).forEach((line) => {
    const [a, b, A, B] = line.split(" ");
    if (cards[`${a}_${b}`] === cards[`${A}_${B}`]) {
      players[palyerIndex] = (players[palyerIndex] || 0) + 2;
    } else {
      // プレイヤーチェンジ
      palyerIndex = (palyerIndex + 1) === playerCnt ? 0 : palyerIndex + 1;
    }
  });
  // i 行目には i 番目のプレイヤーが取り除いたトランプの枚数を出力してください。
  // 各行の最後は改行し、余計な文字、空行を含んではいけません。
  players.forEach(cnt => console.log(cnt));
}

module.exports = {
  concentration
};
```

問題文は小難しく書いてあるけど、やることって
・1行目にカードの枚数（Row*Col）とプレイヤー数
・2行目～カード情報
・1行目のRow + 2行目からプレイヤーの取得情報
・組み合わせがあってるかどうかのチェック、合ってたらプレイヤーに集計・外れてたらプレイヤーチェンジ

ココらへんがキチンと理解・整理できればプログラムなんてスグに組めるよね（ﾆｯｺﾘ
Cloud Storage バケットの削除（復元可能）（Soft Delete）機能、2024/9/1から課金が開始されます

こんにちは、京セラコミュニケーションシステム 石関 (@kccs_daisuke-ishizeki)です。

2024/3 Cloud Storage の削除（復元可能）（Soft Delete）機能がリリースされました。

すべての既存および新規のバケットでこの機能が自動的に有効化されます。

:::note info
記事執筆時点で本機能はプロモーション期間中のため無償となっていますが、2024/8/31 でプロモーション期間が終わり 2024/9/1 から課金が発生します。
:::
:::note warn
当該機能が不要でコストを発生させたくない場合、2024/8/25までに無効化する必要があります。
:::

リマインドも含め簡単に記事にしてみます。

あと個人的に`削除（復元可能）`の表現が頭に入ってこないので英語ドキュメントに倣って`Soft Delete`とこの記事では併記します。

## Cloud Storage の削除（復元可能）（Soft Delete）機能とは

オブジェクトの削除や上書きから指定期間は保持し必要に応じて復元を可能とする機能です。

「オブジェクトが削除されるとごみ箱に一定期間入り、その後削除される。誤って削除してしまった場合でも期間内であれば復元できる！」とイメージいただければ良いと思います。

デフォルトでは7日間に設定され、最大は90日間、最小は7日間です。

削除済みオブジェクトは、指定した削除（復元可能）の保持期間が経過すると完全に削除されます。

https://cloud.google.com/resources/storage/soft-delete-announce?e=48754805&hl=ja

## 削除（復元可能）（Soft Delete）の料金

料金を簡単にまとめてみました。

- 復元可能な指定期間のストレージ料金がかかる
（ごみ箱に入ってる間ストレージ料金が課金される）
- ストレージ料金とはオブジェクト削除時のストレージSKUが基準
  - 例: `Standard Storage Tokyo (1845-1496-2891)`
- 2024/3 のリリースから 2024/8/31 までのプロモーション期間のため料金は発生しない
- 2024/9/1 から課金が開始される

## 金額感

金額感をつかむためざっくり試算してみます。

目安として、東京リージョンの Standard ストレージで 0.023USドル（1GB / 1month) です。
`Standard Storage Tokyo(1845-1496-2891)	0.023 USD per 1 gibibyte month`

その他前提条件

- 1ヶ月は730Hとします
- バケットのトータルサイズ: 1,000 GiB
- 為替レート: 157円/$

### 試算その1: バケットの1%（10GiB）を削除

デフォルトの場合7日間（24×7=168H）オブジェクトが保持されるので、当該機能によるコストは以下になります。

`$0.023 × 10GiB / 730H × 168H = $0.052932`
`$0.052932 × ¥157 = ¥8.31`

日本円にして約 8 円 が追加でかかるコストになります。

### 試算その2: オブジェクトが短時間で削除されるような使い方の場合

オブジェクトが短時間（下記の例では24時間）で削除されるような使い方の場合、もともと想定していた金額に加え、デフォルト保持期間分のストレージ料金が追加になります。

**1,000GiBを24時間保管し、削除した場合**
`$0.023 × 1,000GiB / 730H × 24H = $0.756164`
`$0.756164 × ¥157 = ¥118.72`

**デフォルト保持期間による追加コスト**
`$0.023 × 1,000GiB / 730H × 168H = $5.29315`
`$5.29315 × ¥157 = ¥831.03`

1,000GiBの場合、上記の金額ですんでいますが、規模が大きくなると影響もでてきそうです。
ユースケースや要件に応じて削除（復元可能）（Soft Delete）を無効にすることも検討した方が良いかと思います。

## まとめ

削除（復元可能）（Soft Delete）は偶発的・悪意的なデータ削除から保護するのに非常に役立つ機能です。
一方ユースケースによっては想定していないコストが発生しうりますので、プロモーション期間中に設定を見直しはいかがでしょうか。

また、複数のバケットに対して一括で無効にする機能が[リリース](https://cloud.google.com/storage/docs/release-notes#July_02_2024)されましたので合わせてご参照ください。

https://cloud.google.com/storage/docs/use-soft-delete?hl=ja#bulk-turn-off-soft-delete-policy

## 参考ドキュメント

https://cloud.google.com/resources/storage/soft-delete-announce?e=48754805&hl=ja

https://cloud.google.com/storage/docs/soft-delete?hl=ja
NumpyのFFT入門(3) FFTを用いた基本的な信号解析

# FFTを用いた基本的な信号解析

この章では、NumPyを用いて基本的な信号解析を行う方法について解説します。具体的には、サンプル信号を生成し、FFTを実行してその結果を解釈し、さらに逆FFTを用いて信号を再構成する手順を示します。

## サンプル信号の生成

まずは、解析対象となるサンプル信号を生成します。ここでは、異なる周波数成分を持つサイン波を組み合わせた信号を例にします。

### コード例

```python
import numpy as np
import matplotlib.pyplot as plt

# サンプリング設定
sampling_rate = 1000  # サンプリング周波数（Hz）
T = 1.0 / sampling_rate  # サンプリング間隔
t = np.arange(0, 1.0, T)  # 時間ベクトル

# 信号生成（50Hzと120Hzのサイン波を重ねたもの）
f1 = 50  # Hz
f2 = 120  # Hz
signal = np.sin(2 * np.pi * f1 * t) + 0.5 * np.sin(2 * np.pi * f2 * t)

# 信号のプロット
plt.figure(figsize=(12, 4))
plt.plot(t, signal)
plt.title('Sample Signal')
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.grid(True)
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/009f253e-014a-2455-a6a4-0a4ccfe23367.png)


### 解説

- **サンプリング周波数（sampling_rate）**: 信号をデジタル化する際のサンプリング頻度です。
- **時間ベクトル（t）**: 1秒間にわたる時間の配列を生成します。
- **信号の生成**: 50 Hzと120 Hzの2つの異なる周波数成分を持つサイン波を重ねた信号を作成しました。



## FFTの実行と結果の解釈

次に、生成した信号に対してFFTを実行し、周波数成分を解析します。

### コード例

```python
# FFT実行
fft_result = np.fft.fft(signal)

# 周波数軸の計算
freqs = np.fft.fftfreq(len(signal), T)

# FFT結果のプロット
plt.figure(figsize=(12, 6))
plt.subplot(2, 1, 1)
plt.plot(t, signal)
plt.title('Original Signal')
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.grid(True)

plt.subplot(2, 1, 2)
plt.stem(freqs[:len(freqs)//2], np.abs(fft_result)[:len(freqs)//2], 'b', markerfmt=" ", basefmt="-b")
plt.title('FFT Result')
plt.xlabel('Frequency (Hz)')
plt.ylabel('Magnitude')
plt.grid(True)
plt.tight_layout()
plt.show()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/4668beb8-8eae-26f5-831f-3a0b56d02679.png)




### 解説

- **FFTの実行**: `np.fft.fft`を用いて信号のFFTを計算し、周波数領域での信号の大きさと位相を取得します。
- **周波数軸の計算**: `np.fft.fftfreq`を使用して、FFTの結果に対応する周波数成分を計算します。
- **結果の解釈**: プロットの下部には、信号に含まれる周波数のスペクトルが表示され、50 Hzと120 Hzのピークが確認できます。これは、信号がこれらの周波数成分を持つことを示しています。

## 逆FFTによる信号の再構成

FFTの結果を用いて、元の時間領域の信号を再構成します。これにより、FFTを用いた信号処理が正しく行われたことを確認できます。

### コード例

```python
# 逆FFT実行
ifft_result = np.fft.ifft(fft_result)

# 元の信号と再構築信号のプロット
plt.figure(figsize=(12, 4))
plt.plot(t, ifft_result.real)
plt.title('Reconstructed Signal from IFFT')
plt.xlabel('Time (s)')
plt.ylabel('Amplitude')
plt.grid(True)
plt.show()
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/208980/a1e24e7c-06e7-2d7f-dbd6-52ec488167e7.png)



### 解説

- **逆FFTの実行**: `np.fft.ifft`を使用して、周波数領域のデータを時間領域に逆変換します。
- **信号の再構成**: 再構成された信号をプロットすることで、元の信号と一致していることが確認できます。これにより、FFTおよび逆FFTの正確さを検証できます。


## 関連資料

https://qiita.com/kotai2003/items/05b4f17948c20c31e15a

https://qiita.com/kotai2003/items/364cb155f25a0646af17

https://qiita.com/kotai2003/items/f5528f70bffa51816c78
初めての実務でSQLを書いてみて大事だと思ったこと

## はじめに
初めての実務で、クエリをかくことになり、自分の中で意識したいなということを記述してみました。

## 1. エイリアスを活用する

テーブル名が長くなると、読みづらくなりますね。短くしましょう。

```.sql
SELECT 
  t.name,
  t.age,
  o.order_date
FROM 
  customers t
JOIN 
  orders o ON t.customer_id = o.customer_id;

```

### 省略方法1
こちらは、簡潔に記入する省略方法です。
```.sql
FROM customer t
```

### 省略方法2
こちらはASをつける省略方法です。

```.sql
FROM customer AS ct
```


## 2. SQLクエリで、2つのカラムを結合するには
たとえば、郵便番号のように二つの値を結合するときはどういうクエリを記述するのか？

<b>A. CONCAT関数を用いましょう</b>


```.sql
SELECT CONCAT(column_name,column_name) FROM table_name;
```

さらに列と列の間に半角スペースなどの任意の文字列を含める場
```.sql
SELECT CONCAT(column_name,' ',column_name) FROM table_name;
```

郵便番号を結合してみた
```.sql
SELECT CONCAT(住所1, '-', 住所2) AS 郵便番号
FROM テーブル名;

// result: 123-4132
```

## 3.　NOT EXIST使い倒せ！！

employees（従業員テーブル）

|employee_id|name|department_id|
|:---:|:---:|:---:|
|1|佐藤|23|
|2|高橋|21|
|3|鈴木|34|
|4|松橋|15|
|5|棚橋|12|

departments（部署テーブル）

|department_id|department_name|
|:---:|:---:|
|23|惣菜|
|21|肉|
|34|魚|
|15|レジ|

これがあると仮定します。
そこで今回、従業員が所属していない部署を調べたいとするときに、どんなクエリを投げるのか？

```.sql
SELECT department_name

FROM departments d

WHERE NOT EXISTS (
    SELECT 1
    FROM employees e
    WHERE e.department_id = d.department_id
);

```

従業員が所属していない部署を探すのに、`NOT EXISTS`を使いましょう。

### 軽く補足

#### WHERE句の外側のクエリ

`SELECT department_name FROM departments d`

- departmentsテーブルからdepartment_nameをカラムを取得しています

#### WHERE句の内側のサブクエリ

`SELECT 1 FROM employees e WHERE e.department_id = d.department_id`

- employeesテーブルのdepartment_idと、departmentsテーブルのdepartment_idと一致するものの１行を取得しています

#### NOT EXISTS

- 内側のサブクエリが結果を返さない場合にTRUEを返却します



## おわりに
SQLは、どのテーブルのどの項目をもってきたいかが定まれば、ガツガツかけると思います。目的を明確にし、言語化してからかいてみるといいかもですね。
畳み込みの挙動 (24/8/5/月)

畳み込みの挙動を確認。
Oh = {(Ih - Fh + 2D)/S} + 1
(Google Colaboratoryで学ぶ! あたらしい人工知能技術の教科書 機械学習・深層学習・強化学習で学ぶAIの基礎技術 p223より)

Fh,Fw フィルタ 高幅
Oh,Ow 出力 高幅
Ih,Iw 入力 高幅
D     パディング幅
S     ストライド幅

```
import torch.nn as nn
class CnnTest(nn.Module):
  def __init__(self):
    super().__init__()

    self.conv1 = nn.Conv2d(
        in_channels=3,
        out_channels=64,
        kernel_size=3,
        padding=(1, 1),
        padding_mode='zeros'
    )

  def forward(self, x):
    output = self.conv1(x)

    return output
```
```
x = torch.rand(1, 3, 224, 224)
A = CnnTest()
output = A(x)
print(output.shape)
```
```
出力は↓(式通り)
torch.Size([1, 64, 224, 224])
```
必要最低限の英語を学ぶ！エンジニアのためのリーディング

おはようございます！:sunny:
駆け出しエンジニアのさわまりんです。

当たり前ではありますが、エンジニアと英文って切っても切れないものですよね。。。
エラーや説明文、技術ブログ、ニュースなど、様々な場面で英語を目にします。

ということで今回は、英語に苦手意識がある人に向けて、
英文を読みやすくするためにすべきことをご紹介します。

> ▼今回参考にした本
[現場で困らない！ITエンジニアのための英語リーディング](https://amzn.asia/d/04vyqPzX)


# 私の英語力
高校時代、英検2級をかろうじて取得しています。
大学時代は、1人で頻繁に海外旅行をしていたのですが、
必要最低限の英語力で乗り切った記憶です。

そんな私の正直な感想ですが、この本は内容が物足りませんでした。
もっと劇的に変わるテクニックやIT必須の単語の紹介があると期待していましたが、そんなことはなく、継続して勉強するほかないと改めて認識させられました。
それでも得ることはありましたのでご紹介します。

# リーディングのスキルを伸ばすためにすべきこと
- 単語を学ぶ
- 生の英文を読む
- 専門知識は日本語で仕入れておく

私がこの本を読み、さっそく始めようと思ったのが以上の3つです。

とにかく「知っている単語量」がカギになりそうです。
高校までで学んだ単語量では足りないんですね。
さらに業務で生の英文を読み、「この単語またあったな」となる経験が大切です。

学生時代の英語学習法にも似たようなことが書いてありそうですが、
結局こうやって学ぶしかないということですね。

# 英文が読みやすくなるヒント
継続して学ぶしかない現実は変わらないものの、
そのジャンル特有の文章の構造はあるようです。
コミットメッセージ、ニュース、仕様書の構造を順にご紹介します。

### コミットメッセージ
- 頻出単語を押さえればOK
- 自動生成の固定表現も見られる

本に掲載されている単語をそのまま掲載するのははばかられますが、
調べたところ[**こちら**](https://qiita.com/gogotanaka/items/b65e1b081fa976e5d754)のQiita記事がとても参考になりそうです。

### ニュース
- 概要 → 詳細 という構造がほとんど
- タイトルは文法を無視しがち

### 仕様書
- 目次で全体の内容が理解できるようになっている


# ツール
自分の英語力を底上げするためにツールを使用するのもおすすめです。
実際に入れてみたのですが[weblio ポップアップ辞典](https://ejje.weblio.jp/chrome-extention/)というツールは使い勝手がよかったです。もし利用される場合は「英単語を選択状態にしてポップアップ」という設定がおすすめです。

またこのような英語の辞書に関しては、「定義や品詞」まで見るのが重要です。
なぜなら、IT分野、経済分野で単語の意味が違うこともあるからです。
正しい知識を学びましょう。

# おわりに
継続して学ぶことは正義だと改めて感じました。
今回 ご紹介した書籍には、他にもたくさんのテクニックが掲載されていますので、興味のある方は読んでみてくださいね。
ありがとうございました～:shamrock:
セマンティックとは。用語を調べる。

## はじめに
mainタグやdivタグを調べている中、「HTML5で導入されたセマンティックな要素」の「セマンティック」という用語についてどういう意味なのか分からなかったので調べてみました。

## セマンティック
セマンティックとは、「意味に関すること」を指す言葉です。日本語で言い換えても少しわかりにくいのですが、言語やデータの意味や解釈に関して使われることが多いそうです

### セマンティックな意味の例

1. **`<header>` タグ**:
    - **意味**: ページの「ヘッダー部分」を示します。通常、ページのタイトルやナビゲーションメニューが含まれます。
    - **セマンティックな役割**: ページの上部に位置するセクションとしての意味を持ちます。
2. **`<footer>` タグ**:
    - **意味**: ページの「フッター部分」を示します。通常、著作権情報や連絡先情報が含まれます。
    - **セマンティックな役割**: ページの下部に位置するセクションとしての意味を持ちます。
3. **`<main>` タグ**:
    - **意味**: ページの「主要なコンテンツ部分」を示します。ページ内で最も重要なコンテンツが含まれます。
    - **セマンティックな役割**: ページの主な内容としての意味を持ちます。
4. **`<article>` タグ**:
    - **意味**: 独立したコンテンツの塊を示します。例えば、ブログの投稿やニュース記事など。
    - **セマンティックな役割**: 特定のトピックやテーマに関するコンテンツとしての意味を持ちます。
5. **`<section>` タグ**:
    - **意味**: ページ内の「セクション」や「章」を示します。例えば、製品の説明セクションやサービスの紹介セクションなど。
    - **セマンティックな役割**: ページ内の異なる内容のブロックとしての意味を持ちます。

### セマンティックな意味の利点

1. **アクセシビリティの向上**:
    - スクリーンリーダーなどの支援技術が、ページの構造やコンテンツを理解しやすくなります。例えば、スクリーンリーダーは `<header>` タグを見て、ページの上部にナビゲーションが含まれていることを理解できます。
2. **SEO（検索エンジン最適化）**:
    - 検索エンジンはページの構造を理解しやすくなり、コンテンツの重要性や関連性をより正確に評価することができます。セマンティックな要素を使用することで、検索エンジンがページの内容を正確に把握できます。
3. **コードの可読性**:
    - セマンティックな要素を使うことで、HTMLのコードが意味を持ち、他の開発者がコードを理解しやすくなります。たとえば、`<footer>` タグがあれば、コードを見ただけでその部分がフッターであると分かります。

## まとめ
セマンティックな意味を持たせることは、ウェブページの構造をより意味的に正確にし、アクセシビリティやSEOの面での利便性を高めるために重要です。HTMLタグを適切に使うことで、ページの内容がより分かりやすく、使いやすくなります。
自分だけでなく、ほかの人にもわかりやすい意味を持たせるために大切なんだな、と思いました。
メモ10

有一种更简洁的方法可以处理你的需求，那就是使用纯HTML和JavaScript来实现这个功能。这种方法避开了JSF的复杂性，并且可以更灵活地与后端API交互。下面是一个示例，展示如何使用HTML按钮和JavaScript来实现你描述的功能：

### 1. HTML页面代码

```html
<!DOCTYPE html>
<html>
<head>
    <title>Open New Window Example</title>
</head>
<body>
    <button id="submitButton">Submit</button>
    
    <script type="text/javascript">
        document.getElementById('submitButton').addEventListener('click', function() {
            fetch('api/call')  // 假设你的API URL是/api/call
                .then(response => response.json())
                .then(data => {
                    if (data.url) {
                        window.open(data.url, '_blank');
                    }
                })
                .catch(error => console.error('Error:', error));
        });
    </script>
</body>
</html>
```

### 2. Java后端代码

你需要创建一个独立的API端点来返回URL。这里使用了一个简单的RESTful API示例。

#### 示例1: 使用JAX-RS（如果你的项目中已经有JAX-RS）

```java
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;

@Path("/api")
public class ApiResource {

    @GET
    @Path("/call")
    @Produces(MediaType.APPLICATION_JSON)
    public Response callApi() {
        // 模拟调用API并获取URL
        String url = "https://example.com"; // 假设这是从API返回的URL
        return Response.ok("{\"url\": \"" + url + "\"}").build();
    }
}
```

#### 示例2: 使用普通Servlet

```java
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebServlet("/api/call")
public class ApiServlet extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 模拟调用API并获取URL
        String url = "https://example.com"; // 假设这是从API返回的URL
        response.setContentType("application/json");
        response.getWriter().write("{\"url\": \"" + url + "\"}");
    }
}
```

### 流程解释

1. 用户点击按钮时，JavaScript事件监听器被触发。
2. JavaScript使用`fetch` API异步调用后端API。
3. 后端API返回包含URL的JSON响应。
4. JavaScript从响应中提取URL，并在新窗口中打开该URL。
5. 这种方法完全避免了JSF的复杂性和潜在的问题，同时确保原页面不会变成空白页。

这种方法简单明了，使用纯HTML和JavaScript实现了你描述的功能，适合需要更灵活和简洁解决方案的情况。如果你有其他问题或需要进一步的帮助，请告诉我。
APIのテストをStepCIで実装した際のナレッジ

APIのリグレッションテストが元々はJMeterで実装されていたが、人によりJAVAの環境が違って動かなかったり、スクリプトがjavascriptで書かれていたり(古いJAVAでしか動かない)など運用が面倒になっていたので、StepCIで実装しなおす事に。

- StepCI
https://stepci.com/

テストコードがyamlで記述でき可読性、メンテ性も良くなる。かつCIの実装も簡単になるのでヨシ。
ただ動的なテストデータを使用したい場合は多少スクリプトを書く必要があるため、それを書いた際のナレッジ。

##### 以下、実装サンプル　
```js:stepci.js
import { run } from '@stepci/runner'

// Workflow定義
const workflow = {
  version: "1.0",
  name: "Sample Test Workflow",
  // includeしているworkflow共通で使用する変数
  env: {
    host: "localhost:8080",
    header: {
      "access-key": "sample-test-key",
      "access-token": "hogehogehogehogehogehogehogehogehogehoge",
      "content-type": "application/json",
    },
    "api-path": "/v1/dummy-api",
    body: {
      user_id: 111,
    },
  },
  include: [
    // 複数のworkflowを定義できる
    "sample_workflow.yml",
    ...
  ],
}

// テスト実行
run(workflow).then(console.log)
```

```yaml:sample_workflow.yaml
tests:
  SampleTest:
    # このworkflowだけで使う変数
    env:
      "action": "create_user",
    steps:
      - name: "Create SP User"
        http:
          # js,yamlどちらで定義した変数も `env.~` で使用
          url: http://${{env.host}}/${{env.api-path}}/${{env.action}}
          method: POST
          headers:
            X-access-key: ${{env.header.access-key}}
            X-access-token: ${{env.header.access-token}}
            Content-Type: ${{env.header.content-type}}
          body: ${{env.body.user_id}}
          check:
            status: 200
```
未経験からエンジニア転職、、、

初めまして‼︎

転職活動がひと段落ついたので、自分と同じく未経験から転職活動を行っている方の支えとなればいいなと思い書かせていただきます。

# 自己紹介

- 年齢: `27歳`
- 学歴: `高校卒業`
- 前職: `広告マーケティング`
- 社会人歴: `高校卒業してから`
- 使用言語: `HTML,CSS` `JavaScript` `Java`
- きっかけ: `世界で通用する技術に憧れて`


# 使用媒体

転職するにあたって、
いろんな転職サイトに登録して応募しまくってました。

- doda
- indeed
- Qiita jobs
- マイナビ
- リクナビ
- レバテックキャリア
- Wantedlly

これらを用いて求人を探していました。


# 企業選び

選んでいた特徴としては

- 自社開発企業
- 大阪
- 未経験可、もしくは何かしらの開発経験を求めている会社
最悪自社開発じゃなくてもいいと思っていました orz

こんな感じの企業を選んでいました。
未経験じゃなく、何かしらの言語を用いた開発経験を求めている企業には応募メッセージに加えてポートフォリオを送っていました。
こうすることで書類選考が通過する確率があがるとワザップに書いてありました。
ある程度条件にマッチする企業があればどんどん応募していました！

あんまり覚えていませんが

| 応募 | 書類選考通過 | 1次面接通過 | 内定 |
| :----: | :----: | :----: | :----: |
| 約40社 | 15社 | 10社 | 2社 |

一応これがリアルな数字です。
会社によっては最終面接、社長面接とありましたが、内定いただいた企業様では最終的に社長面接でしたorz
未経験可がないところにも思い切って応募していたのでお祈りメールがどんどんきていましたw

# 面接での質問

平均的に面接の時間は40分くらいです。
カテゴリごとに聞かれた質問を分けますね

#### 前職の質問

- 前職を辞めた理由
当たり前ですが聞かれます。納得のいく理由じゃないと、「こいつこの会社でもすぐ辞めそうだな」と思われるので気を付けてください
- 前職で学んだこと
前職でのスキルが直に転職先に活かせるならそれも踏まえて答える。
無ければ、学習する習慣や学ぶ姿勢を身につけた。のように自走力があることを示してください。
- 前職で頑張ったこと
頑張ったこと、それから得たものがどうな風にエンジニアとして活かせるかを示してください。
- 前職での失敗/成功談
これもその経験からどんな風に立ち直ったか、反省したか、次はどうするか、どう対処したかを答えます

#### 自分についての質問

- 経歴を踏まえての自己紹介
職務経歴書などを見ながらでもいいので簡単に自己紹介

- 自己PR
自走力を示すことがいいと思います。

- 強み/弱み
自分は「弱みは、経験のなさからくる慎重さ」と答えました。

- 趣味
なんでもいいと思いますが、ギャンブル系は辞めましょうw

- どんなエンジニアになりたいか
具体的なイメージができないのであれば、3~5年目安でどこまでのスキルを身につけたいかを考える

- 希望の勤務地
派遣などが主の企業は聞かれると思います。
転勤が多いが大丈夫か？みたいな

#### スクールに通っている人への質問（私はスクールには通っていません）

- なんでそのスクールを選んだのか
- カリキュラムはどうだったのか
- 何を学んだのか

などが想定されるかと思います。

#### ポートフォリオの質問

- コンセプトは何か or なんで作ったか
簡単な理由でも構わないと思います。

- 拘ったところ
苦戦したのではなく、拘ったところを答えてください(デザインや使用感、機能など)

- 苦戦したところ
実装に苦労したところ、どう解決したのかなど
- 今後運用はしていくか
私の場合は学習用として運用していくつもりだったので、そう答えました。

こんな感じで聞かれました。
みなさんの面接対策として使っていただければ幸いです。

# まとめ

大雑把ですが、転職にあたって取り組んできたことをまとめました。
面接など、苦戦しそうなら想定される質問を自分で書き出して答えを用意することがいいです！
リモートなのでカンニングし放題!!!!!

ただ適性検査などがある企業もあるので練習しておくのもアリだと思います
[SPI対策問題集 produced by CareerMine](https://spi.careermine.jp/)

エンジニアとしてようやくスタートラインに立つことができたのでこれからスキルアップに勤しんでいきたいと思っています。
ここまでご覧いただきありがとうございました！！！
犬と猫はどっちが注目されている？Wikipediaの更新頻度を調べる

# 概要
生き物への興味・関心の方向性も様々あると思いますが、Wikipediaの記事の更新頻度を見ると、その傾向の一端がうかがえるのではと思い、調べることにしました。
今回は例として、"Cat","Dog"の比較を行います。

# 検索ボリューム
直近の関心度合いを調査するとき、検索された量は一つの参考になるので、Wikipediaを身肉前に、Google Trendsにて見てみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/580614/36102f3d-cc4f-6146-17ea-ae1185659ecd.png)
過去5年、ほぼ定常的に、全世界だとDogが優位ですね。
ルーマニア、インドネシアはCat優位など、地域差もありそうなのでこれも調べると面白いかもです。

# Wikipediaの更新頻度
ChatGPTさんに、更新頻度の調べ方を聞いたら、次のコードを出してきたので、ひとまずこのまま走らせます。

```python
import requests
import pandas as pd
from datetime import datetime

def get_article_revisions(article_title):
    url = "https://en.wikipedia.org/w/api.php"
    params = {
        "action": "query",
        "prop": "revisions",
        "titles": article_title,
        "rvlimit": "max",
        "rvprop": "timestamp|user",
        "format": "json"
    }
    response = requests.get(url, params=params)
    data = response.json()
    revisions = data['query']['pages']
    revs = []
    for page_id in revisions:
        revs.extend(revisions[page_id]['revisions'])
    return revs

def calculate_edit_frequency(revisions):
    dates = [datetime.strptime(rev['timestamp'], '%Y-%m-%dT%H:%M:%SZ') for rev in revisions]
    df = pd.DataFrame(dates, columns=['timestamp'])
    df['year_month'] = df['timestamp'].dt.to_period('M')
    frequency = df['year_month'].value_counts().sort_index()
    return frequency

article_title = "Cat"  # Example article title
revisions = get_article_revisions(article_title)
frequency = calculate_edit_frequency(revisions)
print(frequency)
```

現時点（2024/8/5）で走らせると、

```
year_month
2022-06    16
2022-07     8
2022-08     9
2022-09    20
2022-10    13
2022-11    23
2022-12    33
2023-01    21
2023-02    22
2023-03     4
2023-04     7
2023-05     6
2023-06     8
2023-07     5
2023-08    25
2023-09    22
2023-10    44
2023-11    44
2023-12    21
2024-01    13
2024-02    17
2024-03    22
2024-04    22
2024-05    33
2024-06    10
2024-07    25
2024-08     7
Freq: M, Name: count, dtype: int64

```

このように出力されます。合計が500なのでAPIから最新の500件の更新日時が取れているようです。問題なさそうなので、["Cat","Dog"]でそれぞれ出力して、比較します。

```python
recent_updates = pd.DataFrame([])
articles = ["Cat","Dog"]
for article_title in articles:
    revisions = get_article_revisions(article_title)
    frequency = calculate_edit_frequency(revisions)
    df = pd.DataFrame(frequency)
    df.columns=[article_title]
    recent_updates = pd.concat([recent_updates,df],axis=1)
recent_updates = recent_updates.sort_index()
```

```python
recent_updates.plot()
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/580614/e08c3a5b-cce3-ff6b-6861-bd1607311060.png)

直近500件の更新でみると、Catのほうが直近に更新されていて、Dogのほうが500件前の更新日が過去にさかのぼります。Dogが2024に追い越しているようにも見えます。

検索ボリュームと比べると、記事の更新は変動が大きいです。一方で、検索とは異なる傾向が見えているので、とらえている現象もおそらく別で、使い道もありそうかと思います。

今回は以上です。
scriptタグのtype="module"属性

## JavaScriptファイルを読み込む際に使用する`<script>`タグの2つの方法

**伝統的な方法**

```
<script src="script.js"></script>
```

**モジュール方式**
```
<script type="module" src="script.js"></script>
```

**2つの方法の主な違い**
伝統的な方法：
ブラウザが伝統的な`<script>`タグに出会うと、すぐにそのJavaScriptコードを読み込み、実行します。これは、コード内のグローバル変数や関数がグローバルスコープを汚染し、名前の競合や変数の上書きなどの問題を引き起こす可能性があることを意味します。

**モジュール方式：**
type="module"属性を使用すると、ブラウザにこの`<script>`タグ内のJavaScriptコードがモジュールであることを伝えます。ブラウザはモジュール方式でコードを読み込み、実行します。これには依存関係の解析や非同期読み込みが含まれます。モジュールは独自のスコープを持ち、モジュール内の変数や関数はグローバルスコープを汚染せず、他のモジュールの影響も受けません。

## type="module"を使用してJavaScriptモジュールを読み込む際の一般的なエラーとその原因

・**SyntaxError: Unexpected token**

原因：モジュールのインポート文で旧バージョンのブラウザがモジュール構文をサポートしていないか、モジュールに文法エラーが含まれています。
解決方法：モジュール構文をサポートする最新バージョンのブラウザを使用し、モジュールの文法エラーを確認してください。

・**TypeError: Cannot use import statement outside a module**

原因：import文を使用しているが、コードがモジュール内で実行されていない、またはtype="module"が正しく指定されていません。
解決方法：HTMLの`<script>`タグにtype="module"属性を追加し、そのスクリプトがモジュールであることをブラウザに伝えてください。

・**404 Not Found - Failed to load module script**

原因：ブラウザが指定されたパスのモジュールファイルを見つけることができません。
解決方法：モジュールファイルのパスが正しいことを確認し、サーバーがそのファイルを正しく提供できることを確認してください。

・**CORSエラー**

原因：クロスオリジンリソース共有（CORS）ポリシーにより、他のドメインからのモジュールの読み込みがブロックされています。
解決方法：サーバーが正しいCORSヘッダーを設定し、クロスオリジンリクエストを許可するようにしてください。

・**NetworkError when attempting to fetch resource**

原因：ブラウザがサーバーからモジュールリソースを取得できない、ネットワークの問題やサーバーの障害が原因です。
解決方法：ネットワーク接続を確認し、サーバーが正常に動作していることを確認してください。

・**Circular dependency detected**

原因：モジュール間に循環依存関係が存在し、モジュールを正しく読み込むことができません。
解決方法：コードをリファクタリングして循環依存関係を解消するか、モジュール構造を再設計してください。

・**ReferenceError: exports is not defined**

原因：モジュール内でCommonJSのexportsキーワードを使用していますが、ES6モジュールではexportsは定義されていません。
解決方法：ES6のexportキーワードを使用してモジュールの内容をエクスポートしてください。

・**SyntaxError: Cannot use import statement '...' in a script before the module containing the import is loaded**

原因：現在のモジュールが読み込まれる前に、他のモジュールをimportしようとしています。
解決方法：モジュール内でimportを使用する位置が正しいことを確認し、モジュール外のコードでimportを使用しないでください。
SRE Next 参加してみて

## はじめに
先日初めてSRE NEXTにオンライン参加しました。その感想を記していきたいと思います。

**↓ 公式ホームページはこちら ↓**
 
https://sre-next.dev/2024/

## SRE NEXTとは
[公式ホームページ](https://sre-next.dev/2024/)より引用
> 信頼性に関するプラクティスに深い関心を持つエンジニアのためのカンファレンスです。 同じくコミュニティベースのSRE勉強会である「[SRE Lounge](https://sre-lounge.connpass.com/)」のメンバーが中心となり運営・開催されます。 
>
> SRE NEXT 2024のテーマは「Beyond NEXT」です。SRE NEXT 2023で掲げた価値観 Diversity、Interactivity、Empathyを大切にしつつ、SREの担う幅広い技術領域のトピックや組織、人材育成に対してディスカッションやコミュニケーションを通じて、新たな知見や発見を得られる場にします。




## セッション

2つのセッションに参加したので、その感想を書いていきます。

### [Enabling Client-side SLO](https://sre-next.dev/2024/schedule/#jp005)
> Luupでは、電動アシスト自転車、電動キックボードなどの電動マイクロモビリティのシェアリングサービス「LUUP」を提供しています。Luup SREチームでは、各開発チームがSREを実践しSLI/SLOを自律的に設計・実装・運用できるようにEnabling SREを進めています。「Enabling SREを進める」とはいえSREのプラクティスは多くあるため、まずはSREのコアとなる要素であるSLOをEnablingすることにしました。
>
> これまでは、開発組織全体とIoT開発チームに対してEnabling SLOをおこなってきました（SRE NEXT 2023の登壇）。この活動をさらに拡大するため、クライアントサイド（iOS, Android）のSLOを計測し始めた話を共有します。
> 
> AndroidやiOSの開発チームを巻き込みながら、プロダクトマネージャーと共にクライアントサイドのSLOを運用し始めるまでの取り組みを、スタートアップ独特の企業の特性や課題を踏まえて共有します。

https://luup.sc/

#### 登壇資料
https://speakerdeck.com/grimoh/enabling-client-side-slo


**＜ 感想 ＞**
SLIやSLO、それにかかわるメトリクスや使用サービスなどの、選定根拠や意思決定プロセスを知ることができて、興味深かったです。

1つのSLIに複数のSLOを設定したり、Weeklyで簡易ダッシュボード確認しながら議論したりと、適切なSLI・SLO設定だけでなく、分かりやすさや定着の観点でも工夫されていて素晴らしいなと思いました。

#### 用語メモ
- **SLI（サービスレベル指標）**
    - サービスの稼働状況を数値化した指標のこと
        - [【用語解説】SLI(サービスレベル指標）とは？](https://sreake.com/glossary/sli/) より引用
- **SLO（サービスレベル目標）**
    - 事業者が自社のサービスレベル（サービス品質）に関する目標・評価基準を定めたもの
        - [サービスレベルの評価基準「SLO」とは](https://pfs.nifcloud.com/navi/tech/slo.htm) より引用
- **CUJ（クリティカルユーザージャーニー）**
    - ユーザー体験を見える化したカスタマージャーニーの中でも、特にビジネスの観点から重要とされる部分にフォーカスしたカスタマージャーニー
        - [現役Google UXマネージャー直伝 クリティカルユーザージャーニー徹底解説【セミナーダイジェスト】](https://popinsight.jp/blog/?p=40141) より引用
- **Core Web Vitals**
    - 2021年にGoogleが発表した「Webページ上でのユーザーエクスペリエンス（UX）を向上させるための重要な指標」
        - [コアウェブバイタル（CoreWebVitals）とは？LCP・FID・CLSの3つ指標と改善方法](https://emma.tools/magazine/seo/search-engine/what-core-web-vitals-is/) より引用


### [スタートアップの急成長に寄り添うOn-Call体制構築とその変遷](https://sre-next.dev/2024/schedule/#jp066)
> サービスの信頼性を維持しユーザに機能提供を続ける上でOn-Callの運用は必要不可欠であり、業務でこれに参加し関わっている人も多いでしょう。一方で体制の構築から運用フロー整備までは比較的泥臭い側面も多く、組織やフェーズによって要件も異なるためノウハウが多く流通していないように感じます。
>
> 私が所属する株式会社10Xでは、サービスや組織の急拡大に合わせてゼロからOn-Call体制の構築を行ってきました。そこで弊社のSREチームがどのようなプロセスを経て体制の導入と安定化を実現したか、組織の変遷をなぞりつつ事例を紹介します。
> 
> 本発表ではPagerDutyやDataDogによるモニタリングからTerraformによる自動化といった技術的トピックだけでなく、実際にOn-Callを行っていくにあたって組織内でどのような取り組みを行ったか、技術面以外でのチャレンジについても重点的にお話しします。

https://10x.co.jp/

#### 登壇資料
https://speakerdeck.com/horimislime/sutatoatupunoji-cheng-chang-niji-ritian-uon-callti-zhi-gou-zhu-tosonobian-qian

**＜ 感想 ＞**
私が弊社のSREで働き始めたときには、既にモニタリングや対応体制がある程度整っている状態でした。
なので、このLTを通じて、モニタリングの黎明期から成熟期に至るまでに行ったことや苦労を知ることができて、とても興味深かったです。

発表の中にあった、「『アラートが多すぎる』という問題があったときに、アラートを減らす方に飛びついてしまいそうになるが、そうではなく担当のチームや詳しいチームにOwnershipを委譲することを行った。」というのが印象的でした。また、「組織全員で向き合い、負担を分散していくべき」というのも印象に残りました。Ownershipをどのチームにするのかを考える際も、誰かが指示する形式よりも皆で向き合うことで、「押し付け合い」のような構図を防げそうで、良い取り組み・心構えだなと感じました。

## 全体を通しての感想

今までAWS系の勉強会には何回も参加させていただきましたが、SRE系の勉強会は初めてでした。他の会社さんがぶつかった課題や、行った施策について学べて、とても良い機会になりました。次回は現地でも参加してみたいです。

以上です。ありがとうございました。
ChatGPTの最強の使い方

# はじめに
アイデアを具現化するのは、多くのクリエイターにとって大きな課題です。ChatGPTを使えば、アイデアを具体化し、実際のプロジェクトとして進める手助けをしてくれます。本記事では、「follyのミドルウェアの作り方」を例に、ChatGPTを活用してアイデアを具現化する手法を紹介します。

一連のやり方のプロンプトは以下を参照。
[ChatGPTでのアイデアの具現化方法プロンプト](https://chatgpt.com/share/c77f2f20-079f-453e-8c47-79e7909f1ebc)

# 第1ステップ：アイデアを出す
最初のステップは、アイデアの種を見つけることです。ChatGPTに対して「xxxについてのアイデアを出してください」と依頼し、複数の選択肢を提供してもらいます。

#### 例：follyのミドルウェア
「follyのミドルウェアについてのアイデアを出してください」

ChatGPTの回答：
- ログ管理のミドルウェア
- エラーハンドリングのミドルウェア
- パフォーマンスモニタリングのミドルウェア
- セキュリティ強化のミドルウェア

# 第2ステップ：アイデアを具体化する
次に、選んだアイデアをさらに具体的にするために、「xxxのアイデアをもう少し具体的にしてください」とChatGPTに依頼します。

#### 例：ログ管理のミドルウェア
「ログ管理のミドルウェアのアイデアをもう少し具体的にしてください」

ChatGPTの回答：
- リクエストとレスポンスのログを収集
- ログのフィルタリングと分類
- 分析用のメタデータ付加
- 異常検知機能

# 第3ステップ：具体的な作り方を学ぶ
アイデアが具体化されたら、実際の実装方法を学びます。「xxxの具体的な作り方を教えてください」とChatGPTに依頼します。

#### 例：ログ管理のミドルウェアの作り方
「ログ管理のミドルウェアの具体的な作り方を教えてください」

ChatGPTの回答：
```cpp
#include <folly/logging/xlog.h>
#include <folly/Singleton.h>

class LogMiddleware {
public:
    void logRequest(const std::string& request) {
        XLOG(INFO) << "Request: " << request;
    }

    void logResponse(const std::string& response) {
        XLOG(INFO) << "Response: " << response;
    }
};

// Singletonインスタンスの作成
auto logMiddleware = folly::Singleton<LogMiddleware>::try_get();
```
このコードは、follyを使ってシンプルなログ管理ミドルウェアを実装する方法を示しています。

# 第4ステップ：READMEファイルを作成する
最後に、プロジェクトの概要と使い方を記載したREADMEファイルを作成します。「xxxをの解説と、使い方を記したREADMEファイルを作成してください」とChatGPTに依頼します。

#### 例：ログ管理ミドルウェアのREADME
「ログ管理のミドルウェアの解説と、使い方を記したREADMEファイルを作成してください」

ChatGPTの回答：
```markdown
# LogMiddleware

## 概要
LogMiddlewareは、follyを使用してリクエストとレスポンスのログを管理するミドルウェアです。このミドルウェアは、情報を収集、フィルタリング、分類し、分析のためのメタデータを付加する機能を提供します。

## インストール
follyライブラリが必要です。インストール方法は[follyの公式ドキュメント](https://github.com/facebook/folly)を参照してください。

## 使用方法
```cpp
#include <folly/logging/xlog.h>
#include <folly/Singleton.h>
#include "LogMiddleware.h"

int main() {
    auto logMiddleware = folly::Singleton<LogMiddleware>::try_get();
    logMiddleware->logRequest("GET /api/data");
    logMiddleware->logResponse("{ 'status': 'ok' }");
    return 0;
}
```

## ライセンス
```
MITライセンスの下で公開されています。
```

# 終わりに
以上の手順を通じて、ChatGPTを活用してアイデアを具現化する方法を紹介しました。ChatGPTは、初期のアイデア出しから具体的な実装、さらにはドキュメント作成まで幅広くサポートしてくれます。ぜひ試してみてください。

そして、この記事もChatGPTによって作成されています。
SharePoint リストの列にツールヒントを設定する


+ [ Require tool tip for each column of SharePoint list](https://techcommunity.microsoft.com/t5/sharepoint/require-tool-tip-for-each-column-of-sharepoint-list/m-p/2278099)

:::note

``` Json
{
  "elmType": "div",
  "style": {
    "font-size": "12px"
  },
  "txtContent": "@currentField",
  "customCardProps": {
    "formatter": {
      "elmType": "div",
      "txtContent": "@currentField",
      "style": {
        "font-size": "12px",
        "color": "green",
        "padding": "5px"
      }
    },
    "openOnEvent": "hover",
    "directionalHint": "bottomCenter",
    "isBeakVisible": true,
    "beakStyle": {
      "backgroundColor": "white"
    }
  }
}
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/85f2b2c3-4d23-a47f-e6c3-4e14545f7ac7.png)

:::

例題では入力した値を表示しているので、"txtContent": "@currentField"のところを変えたらいいんだろうな
yukicoder No.57 ミリオンダイス　解説

[問題文](https://yukicoder.me/problems/no/57)

# 解説
まず、一つのサイコロを振った目の期待値は$3.5$である（入出力例1にもあるように）。
また、さいころの出る目は独立なので$x$個サイコロを振った時の期待値は$3.5x$となる。
これをそのまま計算すればOK。
```C++
#include <bits/stdc++.h>
using namespace std;

int main(){
  double N;cin>>N;
  cout<<fixed<<setprecision(5);
  cout<<N*3.5<<endl;
}
```
yukicoder No.56 消費税　解説

[問題文](https://yukicoder.me/problems/no/56)

# 解説
まず、答えは$\lfloor D*\frac{100+P}{100}\rfloor$となる（$\lfloor x\rfloor$は$x$以下の最大の整数のこと）。
ただし、$\frac{100+P}{100}$をそのまま小数にして計算すると、精度落ちでWAになってしまう。
よって、すべて整数で計算する工夫が必要。
$\lfloor D*\frac{100+P}{100}\rfloor$は$\lfloor D*(100+P)\div 100\rfloor$と同値なので、先に$D*(100+P)$を求めてから100で割る（切り捨て）というのがいい方法。
こういう最後に整数で割る計算の時の誤差が不安な場合、私は次のような関数を使っている。
```C++
using ll=long long;
ll downfloor(ll x,ll m) {
  ll r=(x%m+m)%m;
  return (x-r)/m;
}
```
これは確実に正しい計算ができるので、最後は上記の計算式に従って解けばよい。
$D=10^7,P=100$みたいな大きな数字だと、$D*(100+P)\geqq 2^{31}$と32bitをオーバーするので、long long型を使うこと。

# C++での解答例
```C++
#include <bits/stdc++.h>
using namespace std;
using ll=long long;

ll downfloor(ll x,ll m){
  //floor(x/m)を正確に求める
  ll r=(x%m+m)%m;
  return (x-r)/m;
}

int main(){
  ll D,P;cin>>D>>P;
  cout<<downfloor(D*(100+P),100LL)<<endl;
}
```
MidiGon説明

# MIDI再生アプリMidiGonの説明
MidiGonというMidiファイルを再生するアプリを作成してみました
# MidiGonの画面![MidiGon説明.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853222/faa35743-14ba-d518-3ef3-232dae3f390f.png)
# ボタンの説明

<dl>
  <dt><img width="50" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853222/75246ba3-fd25-feae-f079-42db7b16afc0.png"></dt>
  <dd> Midiファイルを選択します</dd>
  <br>
  
  <dt><img width="50" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853222/d5cba8f7-9e79-1159-5fb6-759578c81524.png"></dt>
  <dd>Midiファイルを再生します</dd>
  <br>

  <dt><img width="50" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853222/4a5c497d-d175-5d6f-c273-be5a653ff12d.png"></dt>
  <dd>ピアノ練習モードで再生します</dd>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853222/e09a4a80-d9e2-d8c4-8268-83c990fa48fc.png)
<dd>Midi情報ボタンをクリックするとMidi情報が表示されます</dd>
<dd>Midi情報はMidファイルが選択されていないと表示されません</dd>
<dd>練習モードはMidi情報の練習チャンネルが選択されていないとピアノロールが停止できません</dd>
  <br>
  
  <dt><img width="50" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853222/ff6a1aa5-c06a-64bf-1d20-6ffadfb4ea00.png"></dt>
  <dd>再生を一時停止します</dd><br>
  
  <dt><img width="50" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853222/e113f9e8-c446-01f9-783a-e5c7ee517bd0.png"></dt>
  <dd>再生を停止します</dd><br>

  <dt><img width="50" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853222/5c98c471-ae8d-dd1f-cd9c-f89770974bb3.png"></dt>
  <dd>MidiGonを終了します</dd><br>

   <dt><img width="50" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853222/66d1837f-4df3-bfb4-2b89-19f004424d10.png"></dt>
  <dd>Midi情報を表示します</dd>

　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853222/021517d9-f8e1-66e6-6324-8a6a37468e23.png)
  もう一度Midi情報ボタンを押すとMidi情報を閉じる事ができます
　Roolはピアノロールを流すか流さないかを選択できます
　PlayはMidi音を出す出さないを選択できます

<dd>

# カラーボタンでチャンネル毎に色を変更できます
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853222/9836e65f-4c11-0566-913e-103a5e42792a.png)
この色はピアノロールに反映されます<br>
</dd>

<dt><img width="50" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853222/0150ee21-0b9c-ac7f-23ec-79a8b3528e5b.png"></dt>
  <dd>オプション設定ができます</dd>

<dd>

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3853222/29d032ec-ba82-65e2-610e-65b8e395d0e0.png)
  <dd>もう一度このボタンを押すとオプション設定を閉じる事ができます</dd>
<dd>Midi In Listで接続されているMidi入力リストから使用するMidi機器を選択できます</dd>
<dd>Midi Out Listで利用可能なMidi出力デバイスを選択できます</dd>
<dd>Transparentにチェックをするとピアノロールの背景を透明にする事ができます
<dd>Chord Delayをピアノ練習する時に、Midiデータの和音のブロックを指定します
<dd>Midiデータは音を単発で出力されますので和音の場合、若干のタイムラグが発生します。このタイムラグを吸収する事ができます。標準では100msecとなっています
  </dl>

     
# 背景の透明化について
背景を透明にし、再生ボタンを押すと即座に透明化します
この時、ボタンも消えてしまいますが、マウスカーソルをピアノの鍵盤に合わせるとボタンを表示します
5秒経つと再びボタンが消えますが、マウスカーソルをもう一度ピアノ鍵盤に合わせるか
再生を停止するとボタンが表示されます
IoT百葉箱プロジェクト再構築

百葉箱ラズパイが7/21 0時半に逝きました。
百葉箱の中といえ外気に触れるのが良くなかったのか、もしくは雷がなっていた影響か？
[最後のtweet](https://x.com/weathertakz2001/status/1814684367450742845)

対策としてこんなの買いました。
屋外で使える耐久ボックスです。
https://amzn.asia/d/2vbMrPt


![1000001274.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587072/6d7f1496-ae9a-af3e-3e00-3bc39249d155.jpeg)

そして百葉箱内に設置しました。
![1000001275.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587072/d0d693df-b7ee-4e5c-eeb8-dee0529bc49c.jpeg)

![1000001276.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587072/23ac9eb8-d496-c873-0ff1-61db8f2fcdb9.jpeg)
![1000001278.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/587072/8e2d0baf-e423-3a65-38c8-7079d39e56cf.jpeg)

tweetしてるかで稼働確認しているので、zabbixで死活監視しようかと妄想中です。
Oepen AI のプロンプトに対する攻撃とその対策リンク集


## 大規模言語モデル (LLM) のシステム メッセージのフレームワークとテンプレートに関する推奨事項

https://learn.microsoft.com/ja-jp/azure/ai-services/openai/concepts/system-message

## ChatGPT - LLMシステム開発大全

https://speakerdeck.com/hirosatogamo/chatgpt-azure-openai-da-quan
Blender Python で  FBX ファイルを GLB ファイルに変換する

FBX ファイルを GLB ファイルに変換する際、Blender を立ち上げて GUI で操作するのが面倒だったので Python スクリプトで実現する方法を試してみました。

## 環境
- Ubuntu 22.04.3 LTS
- Blender 4.2.0
- Python 3.11

## インストール
Blender をインストールします。

いくつか方法がありそうですが、[公式ページ](https://docs.blender.org/manual/ja/latest/getting_started/installing/linux.html#install-from-snap) で紹介されていた **Snap** からインストールする方法が簡単そうです。

```bash
apt install snapd
snap install blender --classic
```

## Python スクリプトの作成
"作成"と書いていますが、今回は、**Khronos Group**（glTF/GLB の開発元）の github リポジトリで公開されているスクリプトを使用します。

https://github.com/KhronosGroup/glTF-Tutorials/blob/main/BlenderGltfConverter/blender_gltf_converter.py

このスクリプトは、変換元の形式が FBX ではなく **Wavefront .obj** になっているため、スクリプトを2箇所ほど書き換えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/759747/15f7ba30-8175-ed6b-c3cb-8b36877d2d6d.png)

glb ファイルを出力する`bpy.ops.export_scene.gltf()`のオプションは公式ページに記載があります。
https://docs.blender.org/api/current/bpy.ops.export_scene.html

## Python スクリプトの実行
ターミナルを立ち上げて、以下のコマンドを実行します。
※スクリプトの3行目（コメント部分）に実行コマンドの記載があります。

```bash
/snap/bin/blender -b -P blender_gltf_converter.py -- -mp "FBXファイルのパス"
```

`-b`(`--background`) で、Blender をヘッドレスモードで実行します。
`-P`(`--python`)で、Python スクリプトを Blender に渡すことができます。
`--` 後の `-mp` で、変換元となる FBX ファイルのパスを指定します。

## さいごに
Blender Python は、最初は「この操作って、スクリプトではどう書くの？」となりますが、慣れてくれば作業効率化に繋がりそうですし、興味を持たれた方は試してみてください。
IBM i でANONYMOUSユーザーを使用可能化する

## IBM i はデフォルトではANONYMOUSユーザーが使用できません


ということで、ユーザー・プロファイルANONYMOUS を作ってみます。

[マニュアルはこちら](https://www.ibm.com/docs/ja/i/7.5?topic=ftp-creating-i-user-profile-anonymous)

### ①最初にANONYMOUSが無い状態

```
WRKUSRPRF ANONY*  
```

上記コマンドでユーザーANONYMOUSが見つかりません。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/0adfa64c-f90b-3a4a-e499-5cdc7645d9c8.png)


当然、FTPでANONYMOUSログインすると・・・エラーになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/1ddbdd58-ef0d-e1fe-e6ea-592ec2bba123.png)


### ②ユーザー・プロファイル ANONYMOUSを作成する

```
CRTUSRPRF USRPRF(ANONYMOUS) PASSWORD(*NONE) CURLIB(PUBLIC) TEXT('ANONYMOUS USER') HOMEDIR('/home/ANONYMOUS')
```

- マニュアルにはユーザークラスの指定はありませんが、一般的に考えると（権限の低い）*USERでいいと思います。


実行例です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/3a2c1f4a-024f-38fd-e75c-f7a98f49787f.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1011394/fd5ed302-953a-ed37-2816-9adab5156e14.png)


以上でユーザーANONYMOUSが作成され、使用可能になりました。

## IBM i FTPサーバーでANONYMOUSを使用可能化するための追加の設定作業

**FTPでANONYMOUSユーザーでアクセスするためにはさらに以下の2つの出口プログラム（Exit Program）を作成・登録が必要です。**

[マニュアル：ANONYMOUS FTPの構成](https://www.ibm.com/docs/ja/i/7.5?topic=i-configuring-anonymous-ftp)
[マニュアル：FTP 出口Programの作成](https://www.ibm.com/docs/ja/i/7.5?topic=ftp-writing-exit-programs-anonymous)



|機能  |出口点（Exit Point) |機能名  |
|---|---|---|
|サーバー・ログオン|QIBM_QTMF_SVR_LOGON |TCPL0100 TCPL0200 TCPL0300 (いずれか1つ) |
|妥当性検査 |QIBM_QTMF_SERVER_REQ|VLRQ0100|

つづく・・
【Polars】ハマり小ネタ集

最近よく使うPolars。
ハマったものを追記していくハマり小ネタ集を作る。

## pl.whenで文字列型列が作れない

Polarsを使用していて以下エラーに遭遇

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3605750/58a5e83a-0e8c-1c33-e097-9495aaeecf3d.png)


dateカラムから月の条件で四季の文字列が入った列を追加したかったのですがなぜか既存カラムを射影(pl.col(’spring’)みたいな)しにいってるような挙動のエラーが。

### 解決手段: pl.lit(’spring’)で記載

thenとotherwiseの文字列をpl.lit()でラップしたら解決。
確か前は文字列だけでもいけてた気がするんだけど破壊的変更の一つだったのかも。docには文字列例がないのでなかなか気づけなかった。

```python
    df = df.with_columns(
        pl.when(pl.col('date').dt.month().is_in([4, 5, 6])).then(pl.lit("spring"))
        .when(pl.col('date').dt.month().is_in([7, 8, 9])).then(pl.lit("summer"))
        .when(pl.col('date').dt.month().is_in([10, 11, 12])).then(pl.lit("autumn"))
        .otherwise(pl.lit("winter"))
        .alias('season')
    )

```

エラー自体あるべき挙動なのかも少し謎。

## lazyframeでconcatされない

pahtsにはリスト型でparquetファイルまでのパスが格納されている

データ定義は同じなので縦に結合して一つのlazyframeを作成しようとした
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3605750/abe5074e-75cf-3d5e-c8ef-1e4ef7c70bf8.png)

データが存在しない。

データフレームでやってみるとデータは存在。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3605750/1a1b2bfd-a9c7-d252-dd06-a0f4ccd74ec3.png)



### 解決手段: lazyframeをリスト格納してまとめてconcat

以下の実装に変更すると想定通りに
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3605750/8ee0a4cf-1552-ab1c-7c42-1e210122b5cb.png)

空のlazyframeがダメ？


## import polarsするとカーネルがクラッシュする
レンタルサーバで環境構築してjupyterで実行するとクラッシュ。
原因調べていると`import polars`でクラッシュしていたことが分かった。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3605750/1ac05f41-0dfb-da89-d149-6687019b2830.png)

`import polars`後のエラー
```error
The following required CPU features were not detected:
    avx2, fma, bmi1, bmi2, lzcnt, movbe
Continuing to use this version of Polars on this processor will likely result in a crash.
Install the `polars-lts-cpu` package instead of `polars` to run Polars with better compatibility.
```  

### 解決手段: `polars-lts-cpu`をインストール
エラーから、polarsでは特定のcpu機能が必要とのことでそちらがなければ使えないとのこと。
調べてみるとレンタルサーバのCPUがXeon® E5-2690 というものらしく古いものだったかららしい？

代わりに`polars-lts-cpu`をインストールすればいつも通り`import polars`が通るようになった。
キャプチャのように「カーネルがクラッシュしました。」しかでないし、エラーログ見ても`undefined, Reason`で何が原因かも分からず原因特定かなり苦労した。
レンタルサーバで開発環境整えているかたは注意。
リストビューからアンケートを送信する


+ [現在、本番環境でアンケートが有効になっており、そのための Experience Cloud サイトがあります。そのため、サンドボックスもそのように設定されています。サイトはアクティブで、アンケートはアクティブで、リスト ビューの連絡先はアクティブで、私のメール アドレスを持っています (したがって、確認済みで、オプトアウトされていません)。ここでのリスト ビュー オプションがサンドボックスで制限されているのか、またはアクセスできない機能なのかはわかりません (アンケート レスポンス パックはあると思いますが、フィードバック管理スターター パックはないと思います)。私はこの分野に不慣れなので、他にどのような情報を提供できるかわかりません。](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007eqKe4)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/635868f6-e2a9-7bd0-0811-0dadb3bf6da5.png)

+ [Error when trying to Send Salesforce Survey: We couldn't find the record you're trying to access](https://salesforce.stackexchange.com/questions/266661/error-when-trying-to-send-salesforce-survey-we-couldnt-find-the-record-youre)
+ [管理者以外のユーザーが「招待を送信」ボタンをクリックしたときに、このエラーが発生した人は他にいますか?](https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000Jfzdg)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/a3349367-550e-0943-f1c9-d50890d97594.png)

調べてみると、すべて正しく実行したようですが、コメントの 1 つで、サンドボックスから一般的なメールを送信するだけで、何とか機能したと誰かが言っていました。そこで、私も同じことをしてみたところ、同じように機能しました...理由はわかりませんが、それでうまくいきました! 

Experience Cloud サイトにアンケート コンポーネントを追加しましたが、「このアンケートに回答するアクセス権がないか、デフォルトのアンケート招待が生成されていません」という エラーが表示されます。私の目標は、認証されたパートナー ユーザーがコミュニティにログインし、アンケート コンポーネントがホームページに表示されるようにすることです。ヘルプ ドキュメントを調べましたが、これが機能しない理由を示すものは見つかりませんでした。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/89b08da5-1dbb-88a1-9b23-6b63dfc7a2a6.png)


2 番目の問題については、パートナー ユーザーがアンケート オブジェクトにアクセスするための適切な権限を持っていることを確認してください。権限は、ゲストユーザーの場合と同じである必要があります (こちらを参照)。 


「コンポーネント」により、承認されたユーザー (パートナー ユーザー) は、招待状を受け取らずにアクティブな調査を開始できます。コンポーネントは、パートナー レコードにリンクされた調査招待レコードを生成します。


ただし、アンケートが「送信」されると、パートナー ユーザーは自分の回答を確認できなくなります。 


パートナー ユーザーはアンケートを「一時停止」し、後で回答を変更できる場合があります。ただし、送信すると、パートナー ユーザーはアンケートの回答にアクセスできなくなります。
Wordpressでショートコード内に発生する意図しない<p>タグの無効化

Wordpressで自動生成される\<p>タグ!

wpautopを取り除いたり

```function.php
remove_filter('the_content', 'wpautop'); 
```
CSSで\<p>を非表示にするのは有名ですが、
```style.css
p:empty {
	display: none;
}
```
ショートコードで記述したhtmlには効果がない！！

## 原因1

### 優先度

調べたところ、「自動整形」と「ショートコードの実行」の優先度が関係しているらしい。

順番
①　wpautop
↓
②　do_shortcode

どおりで自動整形の無効化が効かないわけです。

## 原因2

### 'the_content'の本文とは

さらに、remove_filter('the_content', 'wpautop'); の
'the_contentには、ショートコードが、そもそも含まれていない。

get_the_contentすると、「本文」には、ショートコードはコードとしてしか表示されていないのが、わかります。

下記のようにショートコードを展開すれば、本文の中に、含むことができるようです。
```function.php
    $content = do_shortcode( get_the_content() );
```

## 原因3

### p:emptyは、スペースや改行、コメントアウトが含まれてると「empty」と認識しない



## 解決策

HTMLからスペース・改行・コメントアウトを削除して、自動生成された\<p>タグをすべて空っぽにする。
そうすれば\<p>が発生しても、cssで非表示にできる。

```function.php

remove_filter( 'the_content', 'wpautop'); 

function noautop( $content ){
    global $post;
    if(!$post -> post_content){
        return $content;
    }
    $before_content = do_shortcode($post -> post_content);//本文内にショートコードを展開させる
    $kaigyo = trim($before_content);//改行を削除
    $str = preg_replace('<<!--.*?-->>','',$kaigyo);//<!--で始まり-->で終わる部分の削除（なぜか削除後に<>ができてしまうので、それも追加）
    $str = str_replace(array("\r\n", "\r", "\n"), '', $str);//これでも、できてしまう改行を削除
	return $str;
}
add_filter( 'the_content', 'noautop', 1 );
```
```style.css
p:empty {
	display: none;
}
```


参考サイト
https://qiita.com/kawamurayuto/items/2ba902834977874d47bc

https://kinocolog.com/wpautop_escape/
flaskアプリのターミナルでの起動，初期設定など

# PowerShellでのコマンド

### アプリケーションの登録（環境変数の登録）
```PowerShell
$env:FLASK_APP = "app.py" # app.pyの場合 
```

#### ファクトリ関数を用いる場合

```PowerShell
$env:FLASK_APP = "flaskr:create_app" # flaskrというパッケージ内のcreate_appというファクトリ関数の場合
```

### デバッグモードをオンにする
```PowerShell
$env:FLASK_ENV = "development"
$env:FLASK_DEBUG = "1"
```

### 環境変数の削除
```PowerShell
Remove-Item Env:FLASK_APP, Env:FLASK_ENV
```

### 環境変数の表示
```PowerShell
Get-ChildItem Env:FLASK_APP, Env:FLASK_ENV
```

### 設定から起動まで一発で
```PowerShell
flask --app flaskr --debug run
```
<br>


# bashでのコマンド
### アプリケーションの登録
```bash
$ export FLASK_APP=app.py # app.pyの場合 
```

#### ファクトリ関数を用いる場合
```bash
$ export FLASK_APP=flaskr:create_app  # flaskrというパッケージ内のcreate_appというファクトリ関数の場合
```

### デバッグモードをオンにする
```bash
$ export FLASK_ENV=development

#または追加で
$ export FLASK_DEBUG=1
```


### 環境変数の削除
```bash
$ unset FLASK_app
```
※この時，`FLASK_app=app.py`のように値を含めてはいけない．


### 環境変数の表示
```bash
$ printenv | grep FLASK
```

### 設定から起動まで一発で
```bash
$ flask --app flaskr --debug run
```

<hr>

### flaskアプリのポートを指定して起動
```powershell
flask run --port 8000
```
CodeceptJSで1シナリオ内で複数セッションを使って検証する

## 初めに
自動化しないままにしてしまった課題を解決し、メモしていきたいと思う。
本記事では複数のアカウントのやり取りで進むシナリオを検証する方法を考える。
（手動テストを繰り返すのは大変…）

## 記事の対象
CodeceptJSを使って複数のアカウントによるやり取りが発生するE2Eシナリオテストを書こうと考えている方。
CodeceptJSの高速化を考えている方。

## この記事のゴール
CodeceptJSの1シナリオ上で複数のブラウザを立ち上げ、それぞれ別のユーザでログインし、それぞれシナリオをすすめ検証を行うことができるようにする。

## 1つのシナリオで複数セッションを使う利点
例えば以下のようなフローがあったとする。

1. メンバーが上司にメッセージを送信する
1. 上司がメッセージを承認する
1. メンバーが承認を確認する


これを1つのブラウザで実施するとメンバーでログイン・ログアウト、上司でログイン・ログアウト～のように何度もログインログアウトを繰り返すことになる。
複数のブラウザを使って検証することでこのログインログアウトの数を削減することができる。
その分テストの高速化を図ることができる。これはCodeceptJSを使う場合も同様と考えられる。

## CodeceptJSで複数のセッションを扱う方法

CodeceptJSのmultiple-sessionsを使用する。
https://codecept.io/basics/#multiple-sessions

```session()```の第一引数にセッションの名称、第二引数にテストの処理を記述する。
新しい名称でセッションを指定すると新しいブラウザが開き、処理が開始される。
すでに指定済みの名称を指定すると、すでに開いていたブラウザ上で処理が行われる。

## サンプルプログラム
上記のケースを簡略化した簡単なプログラムを作成する。

1. 一般ユーザは管理ユーザにメッセージを送ることができる
1. 管理ユーザは一般ユーザからのメッセージを確認して、承認、否認を返信できる
1. 一般ユーザは自身が送信したメッセージの一覧と返信結果を閲覧することができる

## テストコード
先ほど提示したフローを検証するテストコードを書いてみる。

> 1. メンバーが上司にメッセージを送信する
> 1. 上司がメッセージを承認する
> 1. メンバーが承認を確認する


```js
  // hiraアカウントでログイン
  await autoLogin("hiraUser");
  I.amOnPage(sendMessageUrl);
  I.waitForText("メッセージ送信", 10);

  // hiraアカウントがメッセージを送信
  I.fillField("$input-message","この書籍が欲しいです。");
  I.click("$button-send-message");

  // 別のセッションを作成する。
  session('BOSS', async () => {
    // bossアカウントでログイン
    await autoLogin("bossUser");

    // 承認画面に遷移
    I.amOnPage(apploveMessageUrl);
    I.waitForText("メッセージ承認", 10);

    // hiraアカウントから送信された最新のメッセージを確認
    I.see("この書籍が欲しいです。", "$message-0");
    I.see("hira", "$sentby-0");

    // メッセージを承認
    I.click("$buttom-approve-0");
  })

  // hiraアカウント
  I.amOnPage(sendMessageUrl);
  I.waitForText("メッセージ送信", 10);

  // bossに承認されたことを確認
  I.see("この書籍が欲しいです。", "$message-0");
  I.see("boss", "$replyby-0");
  I.see("承認", "$approval-0");

```