OmniAICreator/Qiita-1.07M huggingface dataset:
もう知ったかはできない！ChatGPT×LINEbot～不動産の専門用語教えてくれるLINEBot作ってみた～

すっかり真夏になり、国内で39℃を記録しておりましたが、みなさまいかがお過ごしでしょうか。しっかり水分を取ったつもりでも気づいたら脱水なんてこともあるので気を付けて過ごしたいですね！

# 入社7年目、「知りません」と言いづらくなる
私は商業施設の運営管理を行う会社に入社して7年目になります。まだペーペーですが、気づけば後輩もでき、恐れ多いことに誰かに指導するという機会もあります。そんな中で、知らない言葉が出たときに「知りません」と言いにくなってきました(もちろん知らない言葉が出てきたら調べる、分からなければ聞く！ということは大事です)。

私が就いている商業ディベロッパーという職業は不動産業なので、不動産の用語が使われることもあります。打ち合わせ中に分からない言葉が出てきたとき、WEB検索をして、自分が求めている内容が書いてあるページを探して…というのは時間がもったいない！その言葉入力したら端的に意味を教えてくれるツールがあればいいのに。

# LINE×ChatGPTで不動産の専門用語教えてくれるツールを作ってみよう
仕事柄、スマホを使用する機会が多く、しかも自社で公式LINEの運用もしており、何かと馴染みがあるLINEとChatGPTを使用して不動産の専門用語を教えてくれるLINE botを作成したいと思います。

# 使用したツール
[Make](https://eu2.make.com/organization/747122)
[LINE](https://developers.line.biz/ja/)
[ChatGPT](https://chat.openai.com/auth/login)

# 作成方法について
不動産に関する言葉をLINEに送る→ChatGPT→LINEbotが回答してくれる
というものです。

 **①ChatGPTでAPIキーを取得する** 
まずは、ChatGPTでの操作です。

![無題２.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3474647/c0d37d62-462a-ea0b-032f-a95f337305d3.png)


`+ Create new secret key`を選択し、名称を設定。

:::note warn
後で分からなくならないよう、識別しやすい名前にしましょう
:::

APIキーが表示されるのでコピーボタンをクリックしてメモする

:::note warn
コピーしたAPIキーが分からなくなると再発行できないのでメモ帳等に張り付けしておきましょう
:::

 **②Makeでの操作** 

＋ボタンから`LINE→Wach Events`を選択。名称は後で識別しやすいもので設定します。

![LINE4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3474647/ef8ee628-3987-ed93-015c-f86bb05ec8c0.png)

続いて、OpenAI(DALL-E & ChatGPT)を選択し、
Cpnnection name：名称を設定
APIKey：先ほどメモ帳に貼り付けたAPIキーをそのまま貼り付け
Origanization ID：ChatGPTのマイページ、Settingsからコピーしてそのまま貼り付け

![AI.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3474647/efbc638a-e237-c6ec-678d-2ac125378c75.png)


Model： GPT-3.5 Turboを選択
Messages
Add item：Role: Userを選択
　　　　　Message Content: Events[ ]: Message.Text を選択

画面左下にある▷Run onceをクリックしてLINEに不動産に関する言葉を送ってみます。

# 試運転
https://twitter.com/iztm8111/status/1693552976898760794?s=20

# 反省
言葉によっては説明文がボリューミーなものもあるため、「○○字以内に要約する」、「箇条書きで回答する」など条件を盛り込みたかったのですが、事前準備が足りず…。あとは回答に時間を要するときもあるので速度アップできないか、もう少し触ってみたいと思います。

改良していけば自分の業務ツールとしても、誰かに指導する際も便利なものになると思うのでこの後も試行錯誤していきたいと思います。
vimのショートカット（よく使うやつ）


普段は VSCode を使うけど、操作感は vim にしたいときは以下の vim プラグインを入れてみる。

- https://marketplace.visualstudio.com/items?itemName=vscodevim.vim

以下のページを参考に VSCode の設定を変更する。

- https://note.com/5mingame2/n/n88d7ceae5a95

```sh
"vim.autoSwitchInputMethod.defaultIM": "com.apple.keylayout.ABC",
"vim.autoSwitchInputMethod.obtainIMCmd": "/usr/bin/true",
"vim.autoSwitchInputMethod.switchIMCmd": "/usr/local/bin/im-select {im}",
"vim.autoSwitchInputMethod.enable": true,
```

- https://github.com/VSCodeVim/Vim#input-method

vim のショートカットを覚えればホームポジションでコーディングができるようになるのでメモしておく。

### カーソル移動

| コマンド | 内容               |
| -------- | ------------------ |
| w        | 次の単語に移動     |
| b        | 前の単語に移動     |
| %        | 対応する括弧に移動 |
| nG       | n行目にジャンプ    |

### 削除

| コマンド | 内容                           |
| -------- | ------------------------------ |
| x        | １文字削除                     |
| dw       | １単語削除                     |
| dd       | １行削除                       |
| D        | 現在のカーソルから行末まで削除 |

### コピペ

| コマンド | 内容         |
| -------- | ------------ |
| yw       | １単語コピー |
| yy       | １行コピー   |
| p        | ペースト     |

### 検索

| コマンド | 内容                     |
| -------- | ------------------------ |
| \*       | カーソルの単語を下に検索 |
| #        | カーソルの単語を上に検索 |

### 編集

| コマンド              | 内容                                                                     |
| --------------------- | ------------------------------------------------------------------------ |
| u                     | 元に戻す                                                                 |
| ctrl+r                | 進む                                                                     |
| o                     | 行末にカーソル移動して改行する                                           |
| cc                    | 現在の行を削除して編集モードに入る                                       |
| cw                    | 現在の単語を削除して編集モードに入る                                     |
| ctrl + v -> shift + i | 矩形選択して編集                                                         |
| g -> b -> shift + i   | マルチカーソルして編集。visual mode でカーソル移動する場合は、 g → b → v |

### 便利

| コマンド | 内容                                                      |
| -------- | --------------------------------------------------------- |
| J        | 複数行を連結<br>n + J で n 行分をまとめて連結する         |
| =        | インデントを直す。該当行を修正する場合は = を２回入力する |

### その他

| コマンド                | 内容                         |
| ----------------------- | ---------------------------- |
| :edit ++encoding=euc-jp | 指定した文字コードに変換する |

### 複数行頭に文字を挿入する

```sh
0 で行頭にフォーカスを移動する。
Ctrl-v で矩形選択モードにし、j を数回押して、対象範囲の行の行頭がすべて選択された状態にする。
Shift-i で行頭への挿入モードに入り、任意の文字を入力。
Esc で挿入モードを抜ける。
```

### 参考

その他基本的なところは以下を参考。

https://qiita.com/HiromuMasuda0228/items/5ef842ee50e7ac5e85f2
gitアカウントの切り替え方法


あまり需要はないが、ひとつのパソコンで複数の github アカウントを使い分ける場合は、以下のようにすると切り替えができる。

#### 1. SSH キーを切り替える

```sh:~/.ssh/config
Host github.com
  IdentityFile ~/.ssh/github_userA_keyA
  #IdentityFile ~/.ssh/github_userB_keyB
```

#### 2. メールアドレスを切り替える

```sh:~/.gitconfig
[user]
  name = userA
  email = userA@example.com
  #name = userB
  #email = userB@example.com
```
変数について

# 変数についてめちゃくちゃ簡易的に書いてみた。

変数とは何かの計算結果を保存したり、値を保存する`箱`のようなもの。

＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊
※8/19追記 @shiracamus(しらかみゅ)さんからのアドバイスいただいたので記載します！

Pythonでは箱と覚えると苦労します。
Pythonの値はすべてオブジェクトで、変数はオブジェクトがどこにあるかの参照値を保持します。
変数代入は値に名前を付ける、と覚えるといいですよ。言語仕様には、名前への束縛 (name binding) と書かれています。
＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊


```py
a = 3
```
<font color="MediumSlateBlue">「=」は代入という意味です。</font>
testに3を格納(代入)する。という意味です。

＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊
8/19追記 @shiracamus(しらかみゅ)さんからのアドバイスいただいたので記載します！
a = 3 では、まず 3 という値を持つint型オブジェクトを生成します。
その後、変数aを用意して、int型オブジェクト3がどこにあるのかを参照します。
＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊


```py
# aに3を代入する
a = 3  
# bに4を代入する       
b = 4
# 代入された変数を足すと7という結果が出てくる
print(a + b)
```

```py
7
```
---
```py:文字を使った変数代入
name = '田中'
old = 23

print(name + str(old)) 
```
```py:実行結果
田中23
```
型が違うと足すことが出来ないので、old(23と数値となっている)をstrで`文字列に変換`させて、name(文字列)と足します。
これで、<font color="YellowGreen">name(文字列) + old(数値)</font>になっていたところを
　　　　<font color="Orange">`name(文字列) + old(文字列)`</font>とすることが出来き足すことが出来ます。

-----

田中さんは、23歳です。と大体場合は

```py
name = '田中'
old = 23

print(name + 'さんは、' + str(old) + '歳です。')
```

```py
田中さんは、23歳です。
```

文字列は、<font color="YellowGreen">ダブルクォーテーション("")</font>か<font color="Orange">シングルクォーテーション('')</font>
で囲まなければエラーになります。そういう決まりになっています。


＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊
8/19 shiracamus(しらかみゅ)さんからのアドバイスいただいたので記載します！
いまどきのPythonは + を使わずに f-strings を使うのが楽ですよ。
```py
name = '田中'
old = 23

print(f'{name}{old}') 
print(f'{name}さんは、{old}歳です。')
```
＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊＊
react-native-sqlite-storageとexpo-sqliteの罠

react-nativeでiosアプリを開発していた時の話
最初は楽だからexpoで開発を進めていたが、ネイティブコードを記述する必要が生じたため急遽RNCLIでの開発環境に移行することになった。
expoではデータベースを用いるのに"expo-sqlite"というライブラリを利用していたが、RNCLIでは使用できないため、"react-native-sqlite-storage"を用いる必要があった。

# 生じたトラブル
_SELECTしてきたデータを正しく変数に格納できていない_

具体的に見ていきましょう。まず、expo-sqliteのコードを示します。

```javascript:expo-sqlite
const db = SQLite.openDatabase('DATABASENEME.db');
db.transaction((tx) => {
    tx.excuteSql(
        "SELECT * FROM TABLENAME;",
        [],
        (_, resultSet) => {
            var result = resultSet.rows._array;
        },
        () => {
            console.log(error);
            return true;
        }
    );
});
```
上記のresultにSELECTしてきた結果が格納され、それをFlatListで表示するというものでした。
しかし、react-native-sqlite-storageでは同様にしてもなかなか表示されない・・・
エラーは出ていないので、ちゃんとSELECTされているはず、どこかがおかしい・・・

# 解決策
以下のように訂正します
```javascript:react-native-sqlite-storage
const db = SQLite.openDatabase('DATABASENEME.db');
db.transaction((tx) => {
    tx.excuteSql(
        "SELECT * FROM TABLENAME;",
        [],
        (_, resultSet) => {
            //._array を .raw()　に変更
            var result = resultSet.rows.raw();
        },
        () => {
            console.log(error);
            return true;
        }
    );
});
```

単なるコマンドの違いである。

## 参考にしたリンク
https://naoto-kagaya.com/articles/2023-02-21-react-native-sqlite-storage-chatgpt
Nucleo F303K8 ADC と DAC の基本を知る w/STM32CubeIDE

良いお手本がありました。

https://wiki.st.com/stm32mcu/wiki/Getting_started_with_ADC


Nucleo F303K8 でなぞってみます。

UM1956を見て，使うピンを決めました。PA4 と PA3 を接続することにします。隣り合っているのでショートピンが便利ですね。
```
PIN Arduino STM32
 7    A5     PA6 DAC2_OUT1 UnBuffer
 8    A2     PA5 DAC1_OUT2 UnBuffer
 9    A3     PA4 DAC1_OUT1
10    A2     PA3 ADC1_IN4
...
```

# ioc設定
まだきちんと設定していないので黄色くなっています

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533274/397f29a7-c200-9c0f-037c-e077a0b5ce07.png)

# DAC設定

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533274/8dbafe77-8ea8-e0fd-995b-d311dfd98ce3.png)

# ADC設定

- IN4 が Single とのことですがまあOKとしましょう。
- ADCを繰り返したり，複数チャンネル一気に変換したりといろいろできますが，一番基本的なものとしておきます。
- Clock Prescaler だけ変更しました。多分一番精度が良い，低速のものにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533274/4906f895-95bb-a99c-ee74-933519784d99.png)

# テスト結果

STのwikiでは1変数のDMAを使っていましたが，ここでは変換終了を待つ方式にします。
0x800 をDACに設定し，0x7fc が得られました。

```main.c
  /* USER CODE BEGIN 2 */

  HAL_DAC_SetValue(&hdac1, DAC1_CHANNEL_1, DAC_ALIGN_12B_R, 0x800);
  HAL_DAC_Start(&hdac1, DAC1_CHANNEL_1);

  HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);

  HAL_ADC_Start(&hadc1);

  HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);

  uint32_t d=HAL_ADC_GetValue(&hadc1);
  
  /* USER CODE END 2 */
```

ADC変換終了待ちと結果を取ってくる部分，以下のようにレジスターを直接見ても大丈夫でした。

```main.c
  while ( !(ADC1->ISR & (1<<2)) ) {} // wait until EOC is 1
  uint32_t d=ADC1->DR;
```

# 変換精度と時間

クロック設定で値が変わるのでしょうか。やってみます。CPUは8MHz動作です。

|クロック設定|変換時間|ADC結果|
|:--:|:--:|:--:|
|clock div 4|56usec|0x7fc|
|clock div 2|49usec|0x800|
|clock div 1|49usec|0x809|
|ADC専用クロック 16MHz|45usec|0x7e4|
|ADC専用クロック 32MHz|45usec|0x7fd|
|ADC専用クロック 64MHz|45usec|0x81b|

CPUのクロックスピードを64MHzにしてみます。

|クロック設定|変換時間|ADC結果|
|:--:|:--:|:--:|
|ADC専用クロック 4MHz|16usec|0x7fe|
|ADC専用クロック 8MHz|11usec|0x807|
|ADC専用クロック 16MHz|9usec|0x7e8|
|ADC専用クロック 32MHz|8usec|0x7fb|
|ADC専用クロック 64MHz|8usec|0x7e9|

ものすごく乱暴にまとめますと，
本件では，クロック設定でそれほど変換精度が変わるものではなく，変換スピードはクロックスピードに大きく依存する，と言えそうです。

DS9866によれば，12ビットADCのサンプリングレートは最大5.14Mspsとのことですから，もっと速くする設定があるようです。

## 補足

ADC専用クロックを使う場合，

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533274/72b01993-0189-d088-a9d4-e033072c2330.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/533274/8e00c7ab-b50d-051e-33aa-dae02e4e025d.png)

こんな感じにします。

変換時間はTIM6を使いました。8MHz動作のときは，TIM6のプリスケーラーを8に，64MHz動作のときは64に設定しました。`TIM6->CNT`に経過時間 usec が入ります。`HAL_ADC_Start()`の時間は無視しています。

- https://qiita.com/numeru55/items/c52cad5c053e28a9326d

```main.c
  HAL_ADCEx_Calibration_Start(&hadc1, ADC_SINGLE_ENDED);

  HAL_TIM_Base_Start(&htim6);

  uint32_t s0=TIM6->CNT;

  HAL_ADC_Start(&hadc1);

  HAL_ADC_PollForConversion(&hadc1, HAL_MAX_DELAY);

  s0=TIM6->CNT - s0;
```
AWS SSOのIDプロバイダを消してしまったら、許可セットを再保存すれば復旧できる

`AWSSSO_*_DO_NO_DELETE`というID Providerをうっかり消してSSOログインできなくなった方へ。

1. SSO自体を管理しているAWSアカウントにログインし、IAM Identity Centerへ向かう
2. AWSアカウントの一覧からID Providerを削除してしまったアカウントを探す
3. ユーザーとグループのうち、行のどれかのラジオボタンを選択し、「許可セットを変更」を選択する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37384/cabf702c-986b-6e46-a8d5-33d5ce66c11b.png)
4. 変更用画面にて、なにもせずそのまま「変更を保存」する

これで復旧できます


# 参考記事

下記記事は旧AWS SSOサービスの画面であり、IAM Identity Centerには復旧ボタンがありません

https://dev.classmethod.jp/articles/aws-sso-repair-identity-provider/



公式ドキュメントには復旧方法が素っ気なく書いてあるだけで理解しづらいです

https://docs.aws.amazon.com/ja_jp/singlesignon/latest/userguide/idp.html
Webアプリケーションにコンテキストパスを設定した時のparcelの設定

## 前提

parcelでCSS, JavaScriptをbundleしているSpringBootのWebアプリケーション
CSSでは`url()`の構文で画像を呼び出している
```scss:style.scss
.hoge {
  background-image: url(../img/icon.png);
}
```
こちらをparcelでbundleすると、SCSSからCSSに変換される
```css:bundle.css
.hoge {
  background-image: url(/icon.869a98d2.png);
}
```
* build時に`bundle.css`と同じディレクトリに画像が配置されるため、絶対パス指定になっている
* キャッシュのためにファイル名にハッシュが追加されている [(公式)](https://parceljs.org/#:~:text=Parcel%20automatically%20includes%20content%20hashes%20in%20the%20names%20of%20all%20output%20files.%20This%20enables%20long%2Dterm%20browser%20caching%2C%20because%20the%20output%20is%20guaranteed%20not%20to%20change%20unless%20the%20name%20does.)


## 困りごと
SpringBootのpropertyファイルで、アプリケーションにコンテキストパスを追加した
```properties:application.properties
server.servlet.context-path=/foo
```
この変更により、エンドポイントが以下のように変わる
* (旧) http://localhost:8080/users
* (新) http://localhost:8080/foo/users

CSS等の静的ファイルの配置場所も同様
* http://localhost:8080/foo/bundle.css
* http://localhost:8080/foo/icon.869a98d2.png

一方、上述の通り、bundle後のCSSは画像をルートから絶対パスで指定している
```css:bundle.css
.hoge {
  background-image: url(/icon.869a98d2.png);
}
```
そのため、CSSから画像を呼び出す際にコンテキストパスが加味されず
`/foo`なしの http://localhost:8080/icon.869a98d2.png を見に行ってしまう
しかしコンテキストパス追加によって画像の配置URLは変わっているため、404となる

## 対応

parcelの起動コマンドに`--public-url`オプションを追加した
```
parcel build assets/js/index.js --out-dir target/classes/static --out-file bundle.js
```
↓
```
parcel build assets/js/index.js --out-dir target/classes/static --out-file bundle.js --public-url ./
```

これにより、bundle後のCSSで画像を呼び出す際、ルートからの絶対パス指定がされなくなる
```css:bundle.css
.hoge {
  background-image: url(icon.869a98d2.png);
}
```

foo配下にcssと画像が配置されている状態であるため、
bundle.cssから`url(/icon.869a98d2.png)`と指定しても取得できないが、
相対パス指定で`url(icon.869a98d2.png)`とすれば問題なく取得ができる
```
foo
 |--- bundle.css
 |--- icon.869a98d2.png
```

なお`public-url /foo`とすれば以下のようになるため、絶対パス指定を防ぐ設定というわけではない
こちらでも動くが、propertyファイルで行っているコンテキストパスの設定と同期を取る必要があり、二重管理のリスクが発生する
またテスト環境ではコンテキストパスを変えたい等の場合も面倒ごとが増える
そのため相対パスでの指定が吉と思われる
```css:bundle.css
.hoge {
  background-image: url(/foo/icon.869a98d2.png);
}
```

## 参考
Set the public URL to serve on (parcel公式)
https://en.parceljs.org/cli.html#set-the-public-url-to-serve-on
WebdriverIOのAppiumを1.xから2.xへ移行する

# 概要
この記事はWebdriverIO + Appium1.xをAppium2.xへ移行する際に最低限必要な移行作業をまとめた記事です。

# 前提
業務で1年ほど前からWebdriverIO + Appium + XCUITestを用いていました。
しかし、先日突如テストが実行できなくなってしまいました。
原因を調査したところ、XCodeがarm64前提になったことや、Appiumのnpmパッケージに含まれるWebDriverAgentRunnerのtagetがiOS10であり、最近のXCodeでは対応できないことが判明しました。
どうにか1.x系でテストを実行できないか模索できないか試行錯誤をしましたが、結果的に2.x系への移行を決め、移行作業を行いました。

https://qiita.com/5enxia/items/3e84b3eaaf54fd8e5f00

# 移行作業
## 1. node_modulesの削除
appiumのドライバーなど依存関係をいったんリセットするためにnode_modulesを削除します。
```sh
rm -rf node_modules
```

## 2. package.jsonのappiumのアップデート
まず、package.jsonのappiumパッケージを1.xから2.xにアップデートします。
筆者の環境では
```json
"appium": "^1.2.2"
```
となっていたところを、
```json
"appium": "^2.0.1"
```
と変更しました。

# 3. npmのインストール
バージョンを上げたら`npm install`を実行し、必要なモジュールをインストールします。

# 4. appium-xcuitest-driverのインストール
以下のコマンドで、iOSのUIテストに必要なドライバーを追加します
```sh
appium driver install xcuitest
```

## ※driverについて
appium 2.xからdriverという概念が導入されました。
詳細は公式ページ等の解説に譲りますが、簡潔に説明すると、1.xでは全てのdriver(xcuitest, uiautomator2, chromedriver, etc...)がappiumのパッケージに含まれていたところ、2.xではコアなモジュールとdriverを分離し別個にインストールできるようになりました。これにより、ユーザは必要なモジュールのみをインストールすれば良くなり、パッケージが軽量化されるなどの恩恵を受けることができるようになりました。

http://appium.io/docs/en/2.0/guides/managing-exts/

# 5. wdio.conf.tsのcapabilitiesの修正
http://appium.io/docs/en/2.0/guides/migrating-1-to-2/#capabilities

上記リンクの記述にもあるように、WebDriverの仕様で非標準のcapabilitiesには`vendor prefix`と呼ばれる接頭辞が必要になりました。
そのため、`wdio.conf.ts`内の`capabilities`の`automationName`や`platformName`を
```ts
automationName: "XCUITest",
platformName: "iOS"
```
から
```ts
"appium:automationName": "XCUITest",
"appium:platformName": "iOS"
```
のようします。

以上で移行作業は完了です。


# 参考
## WebdriverIO
https://webdriver.io/
## appium
http://appium.io/docs/en/2.0/
## Appium XCUITest Driver
https://appium.github.io/appium-xcuitest-driver/4.34/
pythonでsqliteを使ってstreamlitに列と数字を条件に表示

<H1>pythonでsqliteを使ってstreamlitに列と数字を条件にデータフレームで表示</H1>

```python
import numpy as np
import pandas as pd
import sqlite3
import seaborn as sns
!pip install pycaret
!pip install streamlit

from pycaret.datasets import get_data
#ボストンデータを取得
df = get_data('boston')

# データベース名とテーブル名
db_name = 'datasets.db'
table_name = 'tips'

# SQLiteに書き込む
with sqlite3.connect(db_name) as conn:
    df.to_sql(table_name, conn)
```

```python:app_training.py
import streamlit as st
import sqlite3
import pandas as pd

#先ほど設定したDBの名前
db_name = 'datasets.db'

select_all_sql = 'select ' + '*' + ' from ' + 'tips'
with sqlite3.connect(db_name) as conn:
    df_from_sql = pd.read_sql(select_all_sql, conn)

#列名を取り出す
df_from_sql_columns = df_from_sql.columns

#####stゾーン開始

option = st.selectbox(
    'どの列がはいっているデータを抽出しますか？',
    (df_from_sql_columns)
)

num = st.text_input('数字を半角で入力してください :例 0.04')
#列と入力値両方が入力されるまで待つためのif文
if not option or not num:
  st.title("列と数字が入力されるまで待ちます")
else:
  st.title("選択されました")
  def sql1(columns, num2):
    sql = 'select ' + '*' + ' from ' + 'tips' + ' where ' + columns + ' > ' + num2
    return sql
  sql2 = sql1(option, num)
  with sqlite3.connect(db_name) as conn:
      df_from_sql2 = pd.read_sql(sql2, conn)
  st.dataframe(df_from_sql2)
#####stゾーン終了
```

```python
streamlit run app_training.py
```
![streamlit.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1614609/932164d4-8a89-cfb9-a353-f46544c9e4cf.jpeg)
NixOS で Mozc の設定画面を起動する

## 前提

`/etc/nixos/configuration.nix` にて fcitx + mozc を使用する設定がされている状態。

NixOS ではないネット上の情報を見ると mozc_tool というコマンドを使うようだったが、ターミナルで mozc と打っても何も補完が聞かず、かつ fcitx-configtool からも Mozc の設定画面に飛べたりはしなかったのでずっと困っていた。  
mozc_tool の起動方法を見つけたのでメモ。

## やり方

```
$ /run/current-system/sw/lib/mozc/mozc_tool
```

これだけ。  
なぜだか PATH を通してくれていないらしい。

このコマンドではバージョン情報とかが表示される画面が出るだけ。  
`--mode` 引数を使って起動モードを指定するスタイルらしい。

例えば辞書ツールを起動するなら以下。

```
$ /run/current-system/sw/lib/mozc/mozc_tool --mode=dictionary_tool
```

全体的なコンフィグ画面なら以下。  
ちなみにこいつからもせっていをたどっｔ辞書ツールに飛べる。

```
$ /run/current-system/sw/lib/mozc/mozc_tool --mode=config_dialog  
```

ドキュメントを読めば他にもあると思う。  
とりあえずメモとして、以上。
テスト自動化ツール「Karate」を導入する

# はじめに

REST APIをテストするため「Karate」を使ってみようと思い、VSCode拡張機能の「Karate Runner」を導入してみた記録です.

## 導入した環境＆インストールしたもの

- Windows 11（Windows 10でも問題ないです）
- Visual Studio Code
- JDK 11以降
- Karate Runner (VSCode拡張機能）
- karate.jar

## インストール

### JDK

下記 `karate.jar` の実行に使用します.
Java 11 以降を必要とするので、今回は Adoptium からJDK 17をダウンロードしました.

https://adoptium.net/temurin/releases/?version=17

複数バージョンの入れ替えが簡単なのでZIPファイル版をダウンロードして使っています.

### Visual Studio Code

https://code.visualstudio.com/download

### karate.jar

「Karate Runner」で 「Karate Jar」の設定でシナリオを実行するときに使用します.

Karate Labsの GitHub から `karate-1.4.0.jar` を任意のディレクトリにダウンロードします.

https://github.com/karatelabs/karate

### Karate Runner

「Karate Runner」は、VSCodeを起動して拡張機能ビューの検索で「karate」と入力すると出てくるので、そこからインストールできます.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31205/9c49d606-cc95-4a7a-3d88-8ebc1eb33d4e.png)
（一番目に表示されている「Karate」は、Karate Labsから出ている拡張機能ですが、ここでは二番目に表示されている「Karate Runner」のほうです。なぜかこちらのほうがダウンロード数が多く評価も高いです. Karate Labsのほうはアカウント登録すると使えるようになるモードがあるようですが支払いが発生するようなので確認してませんが:upside_down_face:）


## インストール後の設定など

### プロジェクトの設定

Karateのfeatureファイルを置くプロジェクトのルートディレクトリに、ファイル名「karate.jar」でファイルを作成します.
初めて触ったときは、ここにはkarate.jar本体を置くものだと思っていたんですが、ファイル名が一致していれば中身がなくても大丈夫なようです(今後のアップデートで変わるかもしれないから要注意).

### Karate Jarの設定

Karate Runner > Karate Jar: Command Line Args

ここでjavaコマンドのパスと、karate.jarのパスを設定します.

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31205/680953c3-4670-561b-e06b-f4a9ebf6e395.png)

### キーワードのハイライトと補完

「Karate Runner」をインストールしたあとで、拡張子が`*.feature` のファイルを開くとエディタが次の画像のような表示になります.
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31205/4517cfce-8688-07c2-da5a-3b9781f849de.png)

エンターキーを押したり、スペースキーでインデントしようとすると、コード補完できるようです.（タブキーでキーワードが確定される模様。エンターorスペースを入力した時点で補完が始まるので任意の場所で開始できるようにしてほしいな…）
![補完.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31205/1b888f8e-47da-2078-9523-f9ee27e39d85.gif)

追記: 環境によっては補完効かないかもしれない

### 拡張機能の設定について


シナリオを実行するとテスト結果のレポート（HTML）が生成されますが、自動的には開かないので拡張機能の設定でここにチェックを入れておくと、終了時にHTMLを開いてくれます.（実行のたびに開かれるのはイヤなので、ターミナルに表示されるリンクから開けるようになるといいかもしれない🦆）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/31205/1ed4b006-45da-e025-fbee-a567e0cd89e2.png)

## 感想と今後

エディタから直接シナリオを実行できるという点が良かったです.
書いたら即実行できるので、書き方に慣れていなくても試しながら書いていくことができて便利だなと思いました. 最終的にはMavenとかでテストを回していくことになると思いますが、テストが実行されるまで待った挙句、書き方ミスってて実行できなかった、というのは嫌だなと思うので.

今回は拡張機能の導入をしたところまでまとめましたが、今後は実際に使ってみてシナリオファイルの書き方のポイントなどをまとめていきたいと思っています.
【Linux】ログアウト時にいたパスに次回ログイン時に自動で戻る方法

# はじめに
Linuxのサーバを使っていると、**ログアウト時にいたパスに次回ログイン時に戻りたい**場合があると思います。
``cd -``コマンドを使って直前のディレクトリに戻ることができますが、ログアウト後にはリセットされてしまい、この方法ではうまくいきません。
そこで、本記事では、ログイン時に自動で前回いたパス(=ログアウト時のパス)に戻る方法を紹介します。

本記事は、Ubuntu 22.04.2 LTSを用いて動作確認しました。

# 方法
まず、ログアウト時に読まれる``~/.bash_logout``を編集します。vim, nano, emacs等のエディタで``~/.bash_logout``を開き、``pwd > ~/.logout_path.txt``を最終行に追記し、現在のパスを適当なファイル(``~/.logout_path.txt``)に保存できるようにします。
```terminal:~/.bash_logout内に最終行に追記
pwd > ~/.logout_path.txt
```
<br>

次に、ログイン時に読み込まれる``~/.bashrc``を編集します。エディタで``~/.bashrc``を開き、cd "$(cat ~/.logout_path.txt)"を最終行に追記し、パスの入ったファイル名を呼び出し移動します。

```terminal:~/.bashr内に最終行に追記
cd "$(cat ~/.logout_path.txt)"
```

<br>

最後に、以下のコマンドで``~/.bashrc``の変更を反映させます。
```terminal:ターミナル内で実行
source ~/.bashrc
```

これにより、ログアウト時に``~/.bash_logout``が呼ばれ``~/.logout_path.txt``に現在のパスが保存され、次回ログイン時に``~/.bashr``が読み込まれて``cd``でその保存したパスに移動できます。
Paiza Cランク獲得問題（Dランク相当） 論理演算メニュー

paizaのCランク獲得問題の解答がなかったので、備忘録として残します。
明らかに簡単な問題の場合は省略しますが、個人的に少しでも考えたコードを残していきたいと思います。
初心者ですので醜いコードを書きますが、温かい目で見守っていただけると嬉しいです。
また、より良い記述方法などありましたら、コメント等で教えていただけると嬉しいです。

# 目次
- 論理積( AND )の基本 
- 論理和( OR )の基本
- 否定( NOT )の基本 
- NAND 演算の基本
- 半加算器
- 全加算器
- 論理演算を用いた計算のまとめ








## 論理積( AND )の基本
[問題文](https://paiza.jp/works/mondai/logical_operation/logical_operation__basic_step1/edit?language_uid=c-sharp)
```c#
using System;
class Program
{
    static void Main()
    {
        string []AB=Console.ReadLine().Split();
        int A=int.Parse(AB[0]);
        int B=int.Parse(AB[1]);
        int result=A & B;
        Console.WriteLine(result);
    }
}
```
論理和orについては、A | B
排他的論理和XORについては　 A ^ B
で実装できます。



## 否定( NOT )の基本
[問題文](https://paiza.jp/works/mondai/logical_operation/logical_operation__basic_step3/edit?language_uid=c-sharp)
```c#
using System;
class Program
{
    static void Main()
    {
        int A=int.Parse(Console.ReadLine());
        int result= 0;
        if(A==0){
            result=1;
        }
        
        Console.WriteLine(result);
    }
}
```
題意には沿っていません
##　NAND 演算の基本
[問題文](https://paiza.jp/works/mondai/logical_operation/logical_operation__basic_step5/edit?language_uid=c-sharp)
```c#
using System;
class Program
{
    static void Main()
    {
        string []AB=Console.ReadLine().Split();
        int A=int.Parse(AB[0]);
        int B=int.Parse(AB[1]);
        int result = (A & B) == 0 ? 1 : 0; 
        Console.WriteLine(result);
    }
}
```
否定もこれを元に出来たと思います。
NORは(A | B) == 0 ? 1 : 0; 
XNORは(A ^ B) == 0 ? 1 : 0; で実装できます。

## 半加算器
[問題文](https://paiza.jp/works/mondai/logical_operation/logical_operation__basic_step8?language_uid=c-sharp)
```c#
using System;
class Program
{
    static void Main()
    {
        string []AB=Console.ReadLine().Split();
        int A=int.Parse(AB[0]);
        int B=int.Parse(AB[1]);
        int S = A ^ B;  
        int C = A & B;
        Console.WriteLine(C+" "+S);
    }
}
```

## 全加算器
[問題文](https://paiza.jp/works/mondai/logical_operation/logical_operation__basic_step9?language_uid=c-sharp)
```c#
using System;
class Program
{
    static void Main()
    {
        string []ABC1=Console.ReadLine().Split();
        int A=int.Parse(ABC1[0]);
        int B=int.Parse(ABC1[1]);
        int C1=int.Parse(ABC1[2]);
        int S = A ^ B ^ C1; 
        int C2 = (A & B) | (B & C1) | (C1 & A); 
        Console.WriteLine(C2+" "+S);
    }
}
```
題意に沿った形ではない気がします

## 論理演算を用いた計算のまとめ
[問題文](https://paiza.jp/works/mondai/logical_operation/logical_operation__basic_boss?language_uid=c-sharp)
```c#
using System;
class Program
{
    static void Main()
    {
        string []ABCD=Console.ReadLine().Split();
        int A=int.Parse(ABCD[0]);
        int B=int.Parse(ABCD[1]);
        int C=int.Parse(ABCD[2]);
        int D=int.Parse(ABCD[3]);
        int notE=A|B;
        int F=notE &C;
        int G=F^D;
        Console.WriteLine(G);
    }
}
```
ドモルガンを使いました。
代表的なソート~簡単に言うと~

# はじめに
アルゴリズム学習のアウトプットとしてソートの簡単な説明を記事にしました。
言語はPythonです。

## bubbleソート
簡単に言うと→前から順番に比べていく、後ろの方から確定する
```python:
from typing import List
def bubble_sort(numbers: List[int]) -> List[int]:
    len_numbers = len(numbers)
    for i in range(len_numbers):
        for j in range(len_numbers - 1 - i):
            if numbers[j] > numbers[j+1]:
                numbers[j], numbers[j+1] = numbers[j+1],numbers[j]
    return numbers
```

## selectionソート
簡単に言うと→一番小さいデータを保持して左から確定していくソート
```python:
from typing import List
def selection_sort(numbers: List[int]) -> List[int]:
    len_numbers = len(numbers)
    for i in range(len_numbers):
        min_i = i
        for j in range(i+1, len_numbers):
            if numbers[min_i] > numbers[j]:
                min_i = j
        numbers[i], numbers[min_i] = numbers[min_i], numbers[i]
    return numbers
```

## insertionソート
簡単に言うと→並び替えが発生したらその値を適切な位置まで並び替えをするソート
```python:
from typing import List
def insertion_sort(numbers: List[int]) -> List[int]:
    len_numbers = len(numbers)
    for i in range(1, len_numbers):
        temp = numbers[i]
        j = i - 1
        while j >= 0 and numbers[j] > temp:
            numbers[j+1] = numbers[j]
            j = j - 1
        numbers[j+1] = temp
    return numbers
```

## quickソート
簡単に言うと→最後の値を基準として大小でグループ分けをして、そこで並び替えをする
```python:
from typing import List
def partition(numbers: List[int], low: int, high: int) -> int:
    i = low - 1
    pivot = numbers[high]
    for j in range(low, high):
        if numbers[j] <= pivot:
            i += 1
            numbers[i], numbers[j] = numbers[j], numbers[i]
    numbers[i+1], numbers[high] = numbers[high], numbers[i+1]
    return i+1
def quick_sort(numbers: List[int]) -> List[int]:
    def _quick_sort(numbers: List[int], low: int, high: int) -> None:
        if low < high:
            partition_index = partition(numbers, low, high)
            _quick_sort(numbers, low, partition_index-1)
            _quick_sort(numbers, partition_index+1, high)
    _quick_sort(numbers, 0, len(numbers)-1)
    return numbers
```

## mergeソート
簡単に言うと→最初に一つずつ分けて合わせていく際に並び替えをするソート
```python:
from typing import List
def merge_sort(numbers: List[int]) -> List[int]:
    if len(numbers) <= 1:
        return numbers
    center = len(numbers) // 2
    left = numbers[:center]
    right = numbers[center:]
    merge_sort(left)
    merge_sort(right)
    i = j = k = 0
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            numbers[k] = left[i]
            i += 1
        else:
            numbers[k] = right[j]
            j += 1
        k += 1
    while i < len(left):
        numbers[k] = left[i]
        i += 1
        k += 1
    while j < len(right):
        numbers[k] = right[j]
        j += 1
        k += 1
    return numbers
```
# 学習をしてみて
今回は代表的なソートを記事にしてみた。
ソートの種類は少ないものの、仕組みを理解するのにかなり苦戦した。
まずは代表的なソートを何も見ずに書けるように学習を進めてみる。
【Spring Data JDBC】QueryアノテーションのSELECT文からnullが返ってくる

# はじめに
Springの勉強を始めてSpring Data JDBCの@Queryの操作が上手くいかず、
数時間を溶かしたので備忘録として残します。
[参考リンク](https://qiita.com/dkurata38/items/33e43b6cfc6f2f2bb393)

# 問題
エラーを吐くわけではないのに、SELECT文がnullで返ってくる。

```java:Users.java(Domain Object)
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Users {
	/** 識別ID */
	@Id
	private Integer id;
	/** ユーザ名 */
	private String name;
	/** パスワード */
	private String password;
	/** 作成日時 */
	private Timestamp created_at;
	/** 更新日時 */
	private Timestamp updated_at;
}
```

```java:Repository.java
public interface UsersRepository extends CrudRepository<Users,Integer>{	
	//ユーザ重複確認
	@Query("SELECT * FROM USERS WHERE NAME = ':name'")
	List<Users> uniqueUser(String name);
}
```

```java:Application.java
@SpringBootApplication
public class LearningTimerApplication {

	public static void main(String[] args) {
		SpringApplication.run(LearningTimerApplication.class, args)
				.getBean(LearningTimerApplication.class).execute();
	}

	@Autowired
	UsersRepository users_repository;

	private void execute() {
		//ユーザチェック
		checkUser();
	}

	private void checkUser() {

		//ユーザ名,パスワード
		String name = "hogehoge";
		//ユーザ名が存在しているかチェック
		List<Users> users = users_repository.uniqueUser(name);
		System.out.println(users);
	}

```
```java:コンソール
[]
```

# 解決方法
Repository.javaの`WHERE`に''(シングルクォーテーション)が不要でした。

# Repository.javaを見てみる
```java:Repository.java
public interface UsersRepository extends CrudRepository<Users,Integer>{	
	//ユーザ重複確認
	@Query("SELECT * FROM USERS WHERE NAME = ':name'")
	List<Users> uniqueUser(String name);
}
```
SQLに渡したいパラメータは`:引数名`で指定できますが、
`:引数名`を入れると''(シングルクォーテーション)も入るようです。

つまり、 WHERE NAME = `':name'`と指定していますが、
 WHERE NAME = `:name`が正しいです。

```java:Repository.java(修正後)
public interface UsersRepository extends CrudRepository<Users,Integer>{	
	//ユーザ重複確認
	@Query("SELECT * FROM USERS WHERE NAME = :name")
	List<Users> uniqueUser(String name);
}
```
```java:コンソール
[Users(id=10, name=hogehoge, password=hogehoge, created_at=2023-08-16 20:26:41.670788, updated_at=2023-08-16 20:26:41.670788)]
```

# 終わりに
どなたかの参考になれば幸いです。
JAVA応用③

# 本日の課題

問題: 商品名を入力し値段が出るシステムをHashMap用いて制作せよ

## 回答
~~~~~java
package osakana;

import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

public class fish {
	public static void main(String[] args)throws Exception {
		System.out.println("商品を入力してください");
		Scanner s=new Scanner(System.in);
		String Fish=s.next();
		
	Map<String,Integer> map = new HashMap<String,Integer>();

        	map.put("サバ",250);  
        	map.put("マグロ",400);  
        	map.put("ブリ",350);  
        	map.put("サケ",300); 
       
        System.out.println(map.get(Fish)+"円");
        }
	}
~~~~~~

### 結果

~~~~~
商品を入力してください
マグロ
400円
~~~~~~

本日の気付き:　map.get()内に、Fishを入れることで、相方の数字が出てきます。
本日の学び：　map.putで格納できるため検索ツールに使えそうです。
【ChatGPT】でメニュー提案

## お客さまの困りごとに解決策を即提案！

私は北海道の食品小売業で働いています。
小売業の現場にいるとお客さまからの
問い合わせを数多く受けることになります。
簡単にランキングすると・・・
１位：「〇〇ってどこにあるの？」
２位：「この商品どうやって食べたら美味しいの？」
３位：「この商品100個必要なんだけど在庫ある？？」

このうち1位については課題解決にトライしたものを
以前記事として投稿しているので参照ください。

https://qiita.com/takefuji1212/items/b252a3aa0f85f435878c

今回は第２位の「この商品どうやって食べたら美味しいの？」の
課題解決にトライしてみましょう！

## ChatGPT APIを用いて魚料理を教えてもらいます
![ダウンロード (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3467884/96318e18-1ed2-1599-763e-fab6405c2632.jpeg)

## 使用したツール
.<span style="color:blue">**Make**</span>
.<span style="color:green">**ChatGPT**</span>
.<span style="color:red">**LINE**</span> 

今回はMakeシナリオにChatGPTを組み込むことでLINEBotを作成

![スクリーンショット 2023-08-11 194643.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3467884/3f4c7d49-0ea2-ee54-ae2c-448c10f648bc.png)

プロンプトは画像の通り
![スクリーンショット 2023-08-11 194555.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3467884/b7572d54-375a-9e0e-a729-6a12b5127722.png)

私が所属している店舗は海から離れた立地であり
お客さまは魚介類に対する憧れがあるものの
いかんせん調理方法が良くわからず購入に至らない
という課題があります、そこで今回は
ChatGPT先生に魚料理研究家に扮してもらいましょう
魚だけじゃ寂しいので何故か野菜ソムリエも加えます。

完成したLINEBotの動作風景がこちら






https://twitter.com/takefuji1252151/status/1693208385079115885



レシピを引っ張ってきたりしなくて済むので完成が早い。

## 早速現場で活用してもらいましょう

今回こんな仕組みを作ってみたんだけどー

『新人さんとか魚売場以外の従業員が聞かれたときに
お客さまをお待たせしなくて済むとおもうんだよねー』
するとおもむろに従業員のSさんが自身のスマホを取り出し
「真かれい」と話しかけます・・・
ハイ、Google先生は偉大です、レシピなんてアッと言う間ですよね
「これと同じことですよね？？」
そうです、Sさん、よくご存じで・・・何なら画像も出てますね・・・

Sさん含めおよそ20人の方にフィードバックいただいた意見がこちら

**①自分が客の立場なら珍しい食材を購入するときに使いたい**
**②いつもある食材でも新たな食べ方を発見できる**
**③これならデジタルではなく小冊子で良いのではないか？**　
<font color="red">※複数回答あり</font>
**④便利だと思う**
**⑤端末ではなく各売場に一台タブレットのような
機器を置いて検索出来るようにすれば**
<font color="red">※複数回答あり</font>
**⑥POPや品名カードにQRコードを併記して読み取ったら
知りたい情報がわかるようにすれば**　
<font color="red">※複数回答あり</font>
**⑦単なる料理提案だけでなく時短メニューの方が良いかも**
**⑧お客さまはある程度レシピなどを見てから来るのでわざわざ使わないかも**

## フィードバックに対する内省とアクション
上述の通り結構多かったのが・・・

そもそもアナログでいんじゃね？の③
スマホではなく店頭のサイネージの方が良くないか？の⑤
端末に拘らずPOPにQRコードつけたら済むんじゃね？の⑥

たしかにそうですね、POPにQRコードついていたら僕も喜んで
使いますわ・・・従業員/お客さまに双方で時短になるよね～

ただせっかくフィードバックいただいたので一部改修を実施
⑦の単なる料理提案ではなく時短メニューでという機能を
入れ込んでみましょう！

若干のアレンジを加えたプロンプト
![スクリーンショット 2023-08-14 091621.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3467884/6a126607-f62c-f735-72de-aaed379af476.png)
LINEの画像
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3467884/fc9367db-63cc-ae45-8af4-93cb3a4a29cd.jpeg" width=40%>
時短メニューが紹介されましたね♪
ChatGPT先生を使うとほんのひと手間で済むっていうのが楽ちんですね。


## あとがき

ChatGPTでお客さまの役に立つような仕組みをと思い
作成に取り掛かりましたが何でも応えてくれるので
可能性は無限大！と思いましたが自身の課題感が乏しく
ChatGPTの可能性に対して広がりを持たせることが
出来ませんでした、今後も継続してChatGPTを使用し
何が出来るのかを考えることで
業務改善に繋げていければと、考えています。
GitHubにあげる際に環境変数を使用してAPIキーを非表示にしよう！

## 前書き
無知な私はAPIキーをGitHubに公開してしまいました。大きな失敗。
インドの心優しきiOSDevelopperさんが非表示にした方が良いと教えてくれました。ありがとう:relaxed:
そこでAPIキーを非表示にする方法を調べてまとめてみました。

## なぜAPIキーを非表示にするの？
セキュリティで保護されていないAPIキーを一般公開すると、その情報が意図しない形で使用され、アカウントで不測の料金が発生する場合があります。
個人に割り振られているAPIキーを公開すると他の方に使用される恐れがあるんですね:cry:

# GitHubにPushする前にAPIキーを非表示にする方法

OpenWeatherAPIを使用する際の例として、適当に決めたAPIキーを12345678910とすると...
下記のままGitHubで公開すると、個人のAPIキーが他の方も使用できてしまいます。
![420ff0ccb22a24740ff2b5da85fcba0f.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/1964d52b-4f7e-15ab-539d-20796ad15dd6.png)

そこで下記のステップで、非表示にします。
1.下記の EditScheme...をクリックする！
![a4ffcfc96e1195fa4d4d6ef148b5bcf1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/6e290563-a2e1-ef53-cf39-8797d1c771aa.png)
2. Environment VariablesのNameに追加する。
　今回はNameをAPI_KEYと設定しました！　ValueにAPIキーを12345678910をコピペする！
![a562880ca1e10c09209c874e77e65480 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/4ff470a9-1f1a-58ea-fd37-6adc4ece92a2.png)

3.下記のように先ほど実際のAPIキーを入れてしまっていた箇所を下記のように書き換えます！
```swift
let weatherURL = "https://api.openweathermap.org/data/2.5/weather?&lang=ja&appid=\(ProcessInfo.processInfo.environment["API_KEY"] ?? "")&units=metric"
```

![1555715fdbe24d866c39e53eae1d714a.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3090887/45ba1a5f-41b3-eef7-1d8a-da9a47342bbf.png)

以上でAPIキーを隠すことに成功しました！

# 環境変数？いつ使うの？
ほとんどの Swift アプリは環境変数を必要としませんが、
環境変数は一般的にウェブサイトの REST API を呼び出すアプリで使用されます。
環境変数に格納することは、コード内の文字列として格納するよりも安全です。
GitHubも環境変数を使用し、保存することを推奨しています！

# 注意点とメリット

🟥注意点：GitHub上でPublicで公開した場合、クローンでも同じAPIKeyが使用できてしまう！Edit SchemeからAPIKeyが見えてしまうという問題があります:scream:
そのため、PublicでGitHubに公開するには、下記の記事のように.gitignoreを使用してAPIキーを非表示にする方法の方が有効です！

https://qiita.com/hinakko/items/ecabe0cead230d4cd9a8

環境変数を使用するメリット：環境変数でAPIキーを非表示にする方法は、チームのメンバーのみでGitHub上で開発する際や、開発用APIのURLと本番用APIのURLを分けて使用したい際に有効です！



## 参考文献
https://www.swiftdevjournal.com/using-environment-variables-in-swift-apps/

https://developers.google.com/maps/api-key-best-practices?hl=ja


https://qiita.com/takehilo/items/2d222748abd4db3da5e1
[個人開発向け] Google Spread Sheet でデータを管理してプログラムに反映させるシンプルなやり方 (GitHub Actions付き)


# 動機

少し個人開発をしていて、以下のようなことをやりたいと思いました

* データはGoogle Spread Sheet
* コードで扱うデータはjson
* **Spread Sheet → jsonの変換を簡単にそして個人開発だからシンプルにやりたい**
* 面倒くさい手続きでデプロイとか個人開発でやってらんねーのでGitHub Actionsで意識しなくていいようにする

つまりやりたいことはこうです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77177/a30e0b7f-398a-64fa-941f-0ab7692db781.png)

それをやってみたリポジトリがこちらです。
https://github.com/Marimoiro/trpg-data-template


ちょっと規模の大きい個人開発の途中で手習いとして差し込んでみました。
(完成品作らないとモチベが続かない…)

# 戦略

## 達成したいこと

 * Spread Sheetは[こんな感じで](https://docs.google.com/spreadsheets/u/1/d/1vNEaP-Nd96Wscstda9E5Td5JgkYSrUsRdlYRpBZEwDs/edit#gid=1203551871)公開してよい
 * apiキーは作成したくない (設定面倒くさい…)
 * Spread Sheetのjsonはそのままほかのアプリにデータとして読み込ませるのはちょっと大変…
     * そのためいい感じに実データの配列という形で読みたい
 * デプロイ先は今回はgithub pagesへ出したい
 
## 方針

 * Spread Sheet -> csv -> json という変換にすればうまくいきそうだ
 * csv -> jsonの変換が簡単なのはpwshなのでこれを使う
 * データ更新もGitHub Actionsにやってもらえれば幸せになれそう

# 実装

## Spread Sheet をcsvとしてダウンロード

2023/08現在Google Spread Sheetにはcsvを公開する方法があります。

ファイル > 共有 > ウェブに公開

とすると

https://docs.google.com/spreadsheets/d/e/2PACX-1vTm5ulwSRtrDNyck1hsB1EjR6vzNnu-bX3wjSjXGG46J0v7fwxYrrLA7FD48wKXZ7fxtWgLa7N79sgq/pub?gid=1196424371&single=true&output=csv

こんな感じでurl作ってくれます。

データ公開したくない場合はapiキーとってApi経由でとればいいと思います。  
今回は公開してOKだったので公開です。

## csvをDLしてjsonに変換するスクリプト

pwsh書きました。
こんな感じです。

SpreadSheetPublishedUriにcsvの先ほどのurl入れればjsonにしてくれます。


```powershell
param(
    [uri]$SpreadSheetPublishedUri
)

$tempFile = New-TemporaryFile
Invoke-WebRequest -Uri $SpreadSheetPublishedUri -OutFile $tempFile

Get-Content -Raw $tempFile | ConvertFrom-Csv | ConvertTo-Json
```

これをベースにして、必要なデータを一括でDLしてくるスクリプトはこちらです。

https://github.com/Marimoiro/trpg-data-template/blob/main/scripts/Download-DataToJson.ps1

## GitHub Actionsへデプロイ実装

今回は quasar フレームワークを使うのでそのDeployもはいってます。

gh-pagesブランチに成果物をpushするスクリプトです。
後は、設定でGitHub Pagesの対象をgh-pagesにすればページができます。

```yaml
name: Build & Deploy
permissions:
  contents: write
on:
  push:
    branches:
      - main
  workflow_dispatch:
jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v3
      - name: Setup Node
        uses: actions/setup-node@v3
        with:
          node-version: '20.x'
      - name: Cache
        uses: actions/cache@v1
        with:
          path: ~/.npm
          key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
          restore-keys: |
            ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }}
      - name: Install
        run: yarn install
      - name: Download Data
        run: .\scripts\Download-DataToJson.ps1
        shell: pwsh
      - name: Build
        run: npm run build
      - name: List
        run: ls dist/spa
      - name: Deploy
        uses: JamesIves/github-pages-deploy-action@v4
        with:
          branch: gh-pages
          folder: dist/spa
```

で出来上がったページがこんな感じです。

https://marimoiro.github.io/trpg-data-template/#/
エンジニアにとって資格は重要なものか

### 思考の順番
1. 予想
1. 調査
1. まとめ

前提として、「資格取得が直接的な影響を及ぼすか」をテーマとする

## 予想
少しだけ重要だと考える。理由は「<font color="red">**資格があると短期的には信頼性につながる**</font>」から。

この影響は経歴を伝えるときに強く発揮される。資格があることで「この資格を取得する程度には知識・技術がある」証明にはなるので、信頼性につながる。

ただし、この印象は資格取得から時間が経てば経つほど薄まる為、短期的な効力だと考える。


## 調査
１．[エンジニアにとって資格は取得すべき？不要？](https://qiita.com/k1morikawa@github/items/ca37a91532e2f8f8e06b)

・昔はエンジニア資格によって<font color="red">**その人のステータスを示す度合いが高かった**</font>。
　・理由は「**ネットがない以上、知識を付けることが困難だから**」
・今はベンチャー系の企業だと<font color="red">**資格自体あまり意味のないもの**</font>にありつつある。
　・理由は「**ベンチャーは即戦力の実力社会だから**」
　👆でも「即戦力になるか」の１つの証明に資格は有効かもしれない

２．[資格取得をするべき？取得後のメリットは？](https://mynavi-job20s.jp/howto/engineer-certification.html#caption1)
３．[エンジニアに資格は必要？資格の種類とメリット・デメリットも紹介！](https://idh-net.co.jp/recruit/blog/engineer-qualification/)

・エンジニアにとって、必ずしも資格が必要になるとは限らない。ただ、資格の取得によって<font color="red">**対外的なアピールやスキルの証明に役立つ**</font>
　・人事にとって資格無い人よりある人の方が**見栄えがいい**
　・募集要項で**資格が求められる場合**がある
・企業によっては資格取得によって<font color="red">**手当てが付く**</font>

４．[ITエンジニアに資格は必要か？資格を取る必要性とは](https://infla-lab.com/blog/it-certification-necessity/)
・「<font color="red">**資格があればエンジニアとして活躍が出来る**</font>」というのは間違い
　・エンジニアのステップアップの１つに資格取得があり、どちらかというと「**学習方法の１つ**」や「**学習へのモチベーションアップ**」の意味合いが近い
　・「やりたいことがある」⇒「勉強して資格取得」⇒「やりたいことができるプロジェクトに配属」⇒「スキルアップ」⇒「評価が上がり、年収アップ」という流れが非常に多いため、目指すエンジニアがあるのであれば、「やりたい仕事につながる資格」は取得して損は無いといえる

## まとめ
▼共通
エンジニアとしての活躍に直接的に影響するわけではなく、<font color="red">**学習媒体やそのモチベーションアップの意味合いが強い**</font>
資格勉強がステップアップの１つになる場合が多いため、目指すエンジニアにつながる資格であれば、その人個人の「**エンジニアとしての熟練度の向上**」に対する重要度は高いと言える

▼転職者
企業によっては資格が優位に働く場合がある

▼就業者
企業によっては資格手当がつく場合がある
スプレッドシート上でアスペクト比を計算したい時

ECサイト改修の文脈で、競合サイトのトップページにあるバナーやスライダーエリアのアスペクト比を調べようと思い、スプレッドシートで使ったスクリプトをメモしておく。

![スクリーンショット 2023-08-16 19.18.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/240136/6c3bf1a6-6157-4c7a-a5ea-28407bff02a4.png)

```
=IFERROR(ROUND((B2/GCD(B2,C2))/(C2/GCD(B2,C2)),1)&" : 1","")
```

この場合、B列に幅、C列に高さを記入して、高さを1と固定したアスペクト比を表示している。（バナーはだいたい横長であることが多いので）


| 関数名    |                                       |
|:--------:|:--------------------------------------|
| IFERROR  | エラー時、エラーの代わりに指定した関数を表示    |
| ROUND    | 四捨五入                               |
| GCD      | 最大公約数を取得                         |
Vim 矩形ビジュアルモードは難しい。自分にとっては！！

「Control-v」と押下すると、 矩形ビジュアルモードに入ります。ここからの編集機能をすぐ忘れてしまう。

[矩形ビジュアルモードを利用した編集を使いこなす — 名無しのvim使い](https://nanasi.jp/articles/howto/editing/blockwise-operation.html)

このリンクをもとに図解してみたい。
矩形シフトは省略してます。

```mermaid
graph TD;
    a[矩形ビジュアルモード C-v]-->b[矩形挿入 I 入力]-->c[ESC キー];
    a[矩形ビジュアルモード C-v]-->d[矩形追加 A 入力]-->c[ESC キー];
    a[矩形ビジュアルモード C-v]-->e[行末選択 $]-->d[矩形追加 A 入力]-->c[ESC キー];
```
[矩形挿入 I 入力]は行頭に使用する。
[行末選択 $ - 矩形追加 A 入力]は行末に使用する。

なんとか覚えられそう。
Pod Security Policy (PSP) のせいで Pod が起動できないときの対処法

kuberntes には権限が与えられていないアカウントによる Pod のデプロイを防ぐためのセキュリティ機能である Pod Security Policy（通称：PSP）が存在しており、PSP が動作している環境では初期状態でまともに Pod がデプロイできない場合がある。

※PSP 自体は 1.21 から deprecated 扱いになっていて、1.25 では抹消されている。このあたりは以下の記事が参考になります。

https://qiita.com/kiyoshim55/items/0262d42b57320bea2d00

具体的には
* Namespace を作成後、`kubectl run` などで pod を起動しても `Running` にならない
    * `Deployment` や `StatefulSet` がいつまでたってもスケールしてくれない
    * 以下のようなエラーが出ている
      ```bash
      # kubectl -n test-ns describe statefulset hoge
      Events:
        Type     Reason            Age                   From                    Message
        ----     ------            ----                  ----                    -------
        Warning  FailedCreate      8m45s (x18 over 19m)  statefulset-controller  create Pod xxxxxxxxxxxxx in StatefulSet xxxxxxxxxxxxxx failed error: pods "xxxxxxxxxxxx" is forbidden: PodSecurityPolicy: unable to admit pod: []
      ```

# 原因
クラスタに PSP リソースが存在し、`spec.privileged` が `false` 状態の PSP がデフォルト適用されている。
以下は vSphere with Tanzu の TKC 環境のもの：
```bash
# kubectl get psp
Warning: policy/v1beta1 PodSecurityPolicy is deprecated in v1.21+, unavailable in v1.25+
NAME                                PRIV    CAPS   SELINUX    RUNASUSER          FSGROUP     SUPGROUP    READONLYROOTFS   VOLUMES
tanzu-system-kapp-ctrl-restricted   false          RunAsAny   MustRunAsNonRoot   MustRunAs   MustRunAs   false            configMap,emptyDir,projected,secret,downwardAPI,persistentVolumeClaim
vmware-system-privileged            true    *      RunAsAny   RunAsAny           RunAsAny    RunAsAny    false            *
vmware-system-restricted            false          RunAsAny   MustRunAsNonRoot   MustRunAs   MustRunAs   false            configMap,emptyDir,projected,secret,downwardAPI,persistentVolumeClaim
```

# 対処法
`spec.privileged` が `true` となっている PSP を作成するか、すでに存在すればその PSP を使用して当該 Namespace で使用している `ServiceAccount` に割り当てる。
上記の場合は `vmware-system-privileged` が privileged コンテナデプロイを許可する内容になっているため、こちらを使用する。
```bash
# kubectl get psp vmware-system-privileged -ojsonpath='{.spec.privileged}'
true
```

`ClusterRoleBinding` で一括設定する方法もあると思うが、Namespace に対して `RoleBinding` を割り当てるのがミニマム。
```bash
# kubectl create rolebinding <任意の RoleBinding 名> --namespace <対象 Namespace> --clusterrole=psp:<privileged 件がある PSP> --group=system:serviceaccounts
```
パーミッション[権限ないよ！許可されないよ！エラーのときは…]

### パーミッション

パーミッション（permission）とは、「許可・権限」といった意味を持ち、その名の通り「許可されているかどうか」を示します。
要は「ファイルやフォルダへのアクセス権」のことです。

**「誰」に「どういう」許可（パーミッション）を与えているのか**が重要になります！！

::: note
・誰に…「所有者」「グループ」「その他」
・どういう…
　・「r」(read):「読み取り」。ファイルの中身を見る権限。
　・「w」(write):「書き込み」。ファイルの中身を変更する権限。
　・「x」(exec):「実行」。ファイルを実行する（プログラムを実行する）権限。

:::

（下記のようなWinSCPの場合は、状況に応じてチェックボックスにチェックを入れたり、外す。）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1162001/1644b78a-c065-c90d-bf63-1a31438d9598.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1162001/537cbfd2-482f-3341-9474-0c60d9ff64e7.png)
【初心者向け】【Pytest】フィクスチャは引数配置順に実行されることに気を付けよう

# 初めに

冷静に考えると当然だけど、他のUnitテストフレームワークには無い要素なので記事化しました。

# フィクスチャは引数配置の順番に実行される

今回例題とするコードはこんなものになります。

```mermaid
flowchart TD
    subgraph フローチャート
    start("「A」が入った文字列リストを空にする") --> a("空の文字列リストに「B」を入れる")
    a --> end_("文字列リストが[”B”]であることを確認する")
    end
```

フィクスチャは引数配置順に実行されます。

そのためフィクスチャを利用するテスト`testFixture`の引数を`init_strs, add_b`の*順に並べるとフローチャート通りに実行されテストが通ります。*

```python
import pytest

strs: list[str] = ["A"]

@pytest.fixture
def init_strs():
    strs.clear()

@pytest.fixture
def add_b():
    strs.append("B")

def testFixture(init_strs, add_b):
    assert strs == ["B"]

```

しかし`add_b, init_strs`の順に並べるとテストが通らなくなります。

フィクスチャの実行順が逆になり、先に文字列リストに「B」を入れて中身が空になります。

```python
def testFixture(add_b, init_strs):
    assert strs == ["B"]
```

```python
================================================================================================== FAILURES ================================================================================================== 
________________________________________________________________________________________________ testFixture _________________________________________________________________________________________________ 

add_b = None, init_strs = None

    def testFixture(add_b, init_strs):
>       assert strs == ["B"]
E       AssertionError: assert [] == ['B']
E         Right contains one more item: 'B'
E         Use -v to get more diff

test_main.py:18: AssertionError
------------------------------------------------------------------------------------------- Captured stdout setup -------------------------------------------------------------------------------------------- 
['A', 'B']
[]
========================================================================================== short test summary info =========================================================================================== 
FAILED test_main.py::testFixture - AssertionError: assert [] == ['B']
============================================================================================= 1 failed in 0.09s ==============================================================================================
```

# 最後に

他のUnitテストフレームワークではモック関数をテスト関数内で任意実行するのに対して、Pytestは引数配置順で自動実行されるため仕事で戸惑いました。
【flutter】Containerの角を丸くしたい！

今回はflutterで使われるContainerの角を丸くする方法を紹介したいと思います。


## 1.decoration
Containerの背景色、境界線、角の丸み、影などを定義するため[BoxDecoration]のインスタンスを[decoration]プロパティに指定します。
```php
Container(
        decoration:BoxDecoration()
)
```

## 2.borderRadius
borderRadiusを使用して、角を丸くします。
BorderRadius.circular()メソッドに半径の値を指定することで、角が丸くなります。
```php
Container(
        decoration:BoxDecoration(
            borderRadius: BorderRadius.circular(~),//()の中は角の半径を指定
    )
)
```

## 3.例
### 例１
```php
Container(
                height: 150,
                width: 200,
                decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(20), 
                    color: Colors.red),
              ),
```
![スクリーンショット 2023-08-16 19.01.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524127/776dc1aa-e991-b470-42b3-9aafa65d915b.png)


### 例2
```php
Container(
                height: 100,
                width: 250,
                decoration: BoxDecoration(
                    borderRadius: BorderRadius.circular(50),
                    color: Colors.blue),
              ),
```

![スクリーンショット 2023-08-16 19.05.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3524127/4fe6dbcc-472e-8a85-9995-af817e6d563f.png)