OmniAICreator/Qiita-1.07M huggingface dataset:
ubuntu20.04環境でsimplescreenrecorderをインストールする


ubuntu20.04環境でsimplescreenrecorderをインストールする
simplescreenrecorderが画面キャプチャツールの１つであり、何かと便利に使えるので今回もインストール

以下を参考にすればOK
https://www.maartenbaert.be/simplescreenrecorder/
https://zenn.dev/tasada038/scraps/cbcd721ee0a47b

```
sudo apt install -y ppa-purge
sudo ppa-purge ppa:maarten-baert/simplescreenrecorder
```


# (update)

https://www.maartenbaert.be/simplescreenrecorder/

> If you are using Debian 9 or newer, you can install the 'simplescreenrecorder' package from the official repositories. You can install it with:

```
sudo apt-get update
sudo apt-get install simplescreenrecorder
```

# 参照
https://www.maartenbaert.be/simplescreenrecorder/
https://zenn.dev/tasada038/scraps/cbcd721ee0a47b
do-release-upgrade中にエラーが起きた（E:リポジトリ  https://dl.cloudsmith.io/public/balena/etcher/deb/ubuntu focal  InRelease はもう署名されていません。）場合の解決策


https://qiita.com/TTG/items/1a1646c3e81c1406724b
https://takimoto-blog.com/ubuntu22-04/
https://swytel.hatenablog.com/entry/2018/05/07/154034

```
$ do-release-upgrade
...
アップデート中にエラーが発生しました 
アップデート中に問題が発生しました。通常、これはネットワークにおける何らかの問題です。ネットワーク接続が可能であることをチェックし、あらためて試してください。 
E:https://dl.cloudsmith.io/public/balena/etcher/deb/ubuntu/dists/focal/InRelease 
の取得に失敗しました 402 Payment Required [IP: 18.65.116.124 443], E:リポジトリ 
https://dl.cloudsmith.io/public/balena/etcher/deb/ubuntu focal 
InRelease はもう署名されていません。 
```


```
$ sudo apt update -y
ヒット:1 https://download.docker.com/linux/ubuntu focal InRelease
エラー:2 https://dl.cloudsmith.io/public/balena/etcher/deb/ubuntu focal InRelease                   
  402  Payment Required [IP: 18.65.116.58 443]
ヒット:3 http://jp.archive.ubuntu.com/ubuntu focal InRelease                                        
ヒット:4 http://security.ubuntu.com/ubuntu focal-security InRelease                         
ヒット:5 http://ppa.launchpad.net/deadsnakes/ppa/ubuntu focal InRelease
ヒット:6 http://jp.archive.ubuntu.com/ubuntu focal-updates InRelease                          
ヒット:7 http://jp.archive.ubuntu.com/ubuntu focal-backports InRelease                        
ヒット:8 http://ppa.launchpad.net/maarten-baert/simplescreenrecorder/ubuntu focal InRelease
パッケージリストを読み込んでいます... 完了
E: https://dl.cloudsmith.io/public/balena/etcher/deb/ubuntu/dists/focal/InRelease の取得に失敗しました  402  Payment Required [IP: 18.65.116.58 443]
E: リポジトリ https://dl.cloudsmith.io/public/balena/etcher/deb/ubuntu focal InRelease はもう署名されていません。
N: このようなリポジトリから更新を安全に行うことができないので、デフォルトでは更新が無効になっています。
N: リポジトリの作成とユーザ設定の詳細は、apt-secure(8) man ページを参照してください。
```

`apt update`時のetcherのアップデートが走るも古いことが原因と思われるので該当部分を削除したら回避でした（恒久対策ではないかもしれないが、とりあえずOK..）

```
$ sudo ls -l /etc/apt/sources.list.d/
合計 44
-rw-r--r-- 1 root root 450  8月 15 16:05 balena-etcher.list
-rw-r--r-- 1 root root 450  8月 15 16:04 balena-etcher.list.distUpgrade
-rw-r--r-- 1 root root 450  3月 27 22:28 balena-etcher.list.save
```
etcherのインストール（ubuntu20.04で）

etcherのインストール（ubuntu20.04で）

以下からインストーラをダウンロードしてインストールすればOK

https://github.com/balena-io/etcher 
https://github.com/balena-io/etcher/releases/tag/v1.18.11

```bash
# download
wget https://github.com/balena-io/etcher/releases/download/v1.18.11/balena-etcher_1.18.11_amd64.deb
# install
sudo apt install ~/Downloads/balena-etcher_1.18.11_amd64.deb 
```

以上です！
VoiceText Web APIをpythonから呼び出してみる

VoiceText Web APIをpythonから呼び出してみる際の備忘メモです

## VoiceText Web API 登録&APIキー取得

[VoiceText Web API](https://cloud.voicetext.jp/webapi)
> ※利用の際は以下に留意しましょう
> 利用規約をご確認の上、本サービスをご利用ください。

## お試し（curl）

[introduction](https://cloud.voicetext.jp/webapi/docs/introduction)が充実しており参照させて頂く

 
>```sh
>curl "https://api.voicetext.jp/v1/tts" \
>     -o "test.wav" \
>     -u "YOUR_API_KEY:" \
>     -d "text=おはようございます" \
>     -d "speaker=hikari"
>```


## お試し（python）

ライブラリのインストール

https://cloud.voicetext.jp/webapi/docs/libraries

```
pip install python-voicetext
```

[VoiceText Web APIをPythonで使ってみる](https://bty.sakura.ne.jp/wp/archives/1223)

```python
#coding:utf-8
 
from requests_toolbelt import SSLAdapter
import requests
import ssl
import sys
 
url = 'https://api.voicetext.jp/v1/tts'
API_KEY = 'YOUR API KEY'
 
payload = {
    'text': 'おはようございます',
    'speaker': 'hikari',
    }
 
s = requests.Session()
s.mount(url, SSLAdapter(ssl.PROTOCOL_TLSv1))
r = s.post(url, params=payload, auth=(API_KEY,''))
 
print "status code:", r.status_code
if r.status_code != 200:
    print "error:", r.json()['error']['message']
    sys.exit()
 
f = open("test.wav", 'wb')
f.write(r.content)
f.close()
```


# 参考

https://cloud.voicetext.jp/webapi

https://bty.sakura.ne.jp/wp/archives/1223
【備忘録】PostgresSQL と　Knexを使ったバックエンド開発の初期設定

データベースを伴ったバックエンド開発・練習する際に、毎回
「アレ？なにターミナルに打ち込めばいいんだっけ？」ってなってしまうので、備忘録というかメモ
（ぶっちゃけGPTに聞けばいいんだろうけど、一応）

まず、管理者として実行したターミナルで
（VCStudioのターミナルだとたまにCONNECTIONだのあーだこーだエラー吐くので）




・PostgreSQLのセットアップ
    psql -U ユーザー名でログインして 
    CREATE DATABASE データベース名; 
    データベースをデータベースを作成。
    \l（エル）＊たまにバックスペースじゃなくて￥とか出るけど気にするな！
　　でデータベースのリストを表示して確認。





・Knexのセットアップ:
    mkdirとかカッコいい作り方あるけど、自分はいまだに右クリ＝＞新しいフォルダーを作る
    パスをコピーするか手入力で、ターミナル（この際VSでも管理者ターミナルどちらでも）にて 
    cd プロジェクトディレクトリ
    npm init -y
    npm install express knex pg dotenv 
    をインストール
    ＊ここで少々時間かかるので一息いれる





・ディレクトリ内のknexfile.js を作成・設定。

多分中こんな感じになってるから
module.exports = {
  development: {
    client: 'pg',
    connection: {
      host: '127.0.0.1'
      user: 'ユーザー名',＜＝ここ
      password: 'パスワード',＜＝ここ
      database: 'データベース名'＜＝ここ
    },
    migrations: {
      directory: './migrations'
    }
  }
};
を入力してあげれば、データベースと接続するんだけど、
後々本格的に外部サーバーにアップロードするさいに、このHost,User,Password書いたまんまアップするのは好ましくない（というかパスワードを全世界に公開するな）ので、
.envファイルを使うのもアリ
例
DB_HOST=127.0.0.1
DB_USER=ユーザー名
DB_PASSWORD=パスワード
DB_NAME=データベース

それでknexfile.jsの冒頭に
require('dotenv').config(｛path: "./.env.local",})；とか入れとく





・マイグレーション
knex migrate:make テーブル名 でマイグレーションファイルを作成。

中はGPTさんの例だと
exports.up = function(knex) {
  return knex.schema.createTable('users', (table) => {
    table.increments('id').primary(); // 自動増加のID
    table.string('name').notNullable(); // 名前カラム
    table.string('email').unique().notNullable(); // ユニークなメールアドレスカラム
    table.timestamps(true, true); // created_at と updated_at のカラムを追加
  });
};

exports.down = function(knex) {
  return knex.schema.dropTable('users'); // users テーブルの削除
};

になるんだけど
UpとDownのreturn 以降に色々とキーを書いていく。
ちなみに複数テーブル作る場合は、テーブルを書き終わった後に、さらに.createTableとつなげることできる。

マイグレーション実行するには
npx knex migrate:latest
＊めったにないけど万が一ロールバック（なかったことにする）には
npx knex migrate:rollback




・Seedする場合
Knex seed:make 番号　タイトル
でつくってあげる。
タイトル前に番号入れるのは、一応見やすくするため

中身は（GPTさんありがとう）

exports.seed = function(knex) {
  // Deletes ALL existing entries
  return knex('users').del()
    .then(function() {
      // Inserts seed entries
      return knex('users').insert([
        { id: 1, name: 'John', email: 'john@example.com' },
        { id: 2, name: 'Jane', email: 'jane@example.com' },
        { id: 3, name: 'Doe', email: 'doe@example.com' }
      ]);
    });
};
で必ずオブジェクトのキーと値（NotNullで設定してるところ等）がキチンと入力されてるか、空欄でもいいかを注意しつつ入力

シード実行は
npx knex seed:run


****
後々プロジェクトをネット・サイバー空間にアップ・デプロイするときに
このマイグレーションとシードを実行とかいろいろめんどいことあるんだけど、
今はLOCALHOSTでのことだけ書いてます


見にくいし読みにくいけど、自分メモメインだから自分しか読まない体で書いた。
すまんそ
フィールドの数が動的に変わるフォームの作成

## はじめに
ボタンをクリックすることでフォームの下部にテキストフィールドが追加されるような挙動を実装してみます。

![スクリーンショット 2023-10-22 15.56.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/551042/38758791-81ca-f208-27b9-fce99760ae7e.png)

## 環境
`vue 3.2.13`
`typescript 4.5.5`
`naive-ui 2.35.0`

## 実装
一つのフィールドで一つの構造体を表現するようにします。
仮で`Hoge`という構造体を作りました。

```vue
<template>
    <NFormItem
      v-for="(product, index) in productionsRef"
      :key="index"
      :ref="(el) => setRef(el, index)"
    >
      <NInput />
    </NFormItem>
</template>

<script setup lang="ts">
class Hoge {
}

const productionForms = ref<FormItemInst[]>([])
</script>
```

フィールドの参照には関数を渡します。

```vuejs
<template>
      <NFormItem 
        v-for="(product, index) in productionsRef"
        :key="index"
        :ref="(el) => setRef(el, index)"
      >
      <NInput />
    </NFormItem>
</template>

<script setup lang="ts">

const setRef = (el: FormItemInst, index: number) => {
  productionForms.value[index] = el
}  

</script>
```

あとはボタンクリック時にフィールドが増えるようにします。

```vuejs
<script setup lang="ts">

const onClickAddField = () => {
  productionsRef.value = productionsRef.value.concat(new Hoge())
}

</script>
```

ソースコードはこちらです。

```vuejs
<template>
  <div style="width: 600px;">
    <NForm ref="formRef">
      <NFormItem 
        v-for="(product, index) in productionsRef"
        :key="index"
        :ref="(el) => setRef(el, index)"
      >
      <NInput />
    </NFormItem>
  
      <NRow :gutter="[0, 24]">
        <NCol :span="24">
          <div style="display: flex; justify-content: flex-end;">
            <NButton 
              round
              type="primary"
              @click="onClickAddField"
            >
              ADD FIELD
            </NButton>
          </div>
        </NCol>
      </NRow>
    </NForm>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { 
  FormInst,
  FormItemInst,
  NForm,
  NFormItem,
  NInput,
  NRow,
  NCol,
  NButton,
} from 'naive-ui'

class Hoge {
}

const formRef = ref<FormInst | null>(null)
const productionsRef = ref<Hoge[]>([new Hoge()])
const productionForms = ref<FormItemInst[]>([])

const onClickAddField = () => {
  productionsRef.value = productionsRef.value.concat(new Hoge())
}

const setRef = (el: FormItemInst, index: number) => {
  productionForms.value[index] = el
}
</script>
```
Rubyの数値比較について

# 背景
最近仕事でrubyを使うようになり、ついでならと思いAtCoderも始めてみました。
C問題をやったときに、数値比較の処理を書き換えたらめちゃくちゃ早くなったという話。

# どういった処理をしたかったのか
計算自体はシンプルで、特定の値が最小値と最大値の間に含まれているかを判定したかった。

```ruby:ruby
k = 100
min = 0
max = arr.length - 1

# arrのキーの範囲外を指定しないようにする
if k >= min && k <= max
    puts arr[k]
end
```

ここの `k >= min && k <= max` をどう書くのかという話。

## 結論
早い順で並べると

**比較演算子(>=, <=)で判定：一番早い**
```ruby:ruby
k >= min k <= max
```

**intの`between?`関数を使う**
```ruby:ruby
if k.between?(0, max)
```
※betweenの引数両方を含むので注意
https://docs.ruby-lang.org/ja/latest/method/Comparable/i/between=3f.html

**setの`include?`を使う:一番遅い**
```ruby:ruby
if (0..max).include?(k)
```


## 検証
以下のコードを実行し、処理時間を比較
```ruby:ruby
times = 10**8

max = 100000

cnt = 0
start_time = Time.now
times.times do
  v = rand((max * 2.1).to_i) - (max*0.1).to_i
  if v >= 0 && v <= max
    cnt += 1
  end
end

p cnt
p "#{Time.now - start_time} sec"

cnt = 0
start_time = Time.now
times.times do
  v = rand((max * 2.1).to_i) - (max*0.1).to_i
  if v.between?(0, max)
    cnt += 1
  end
end

p cnt
p "#{Time.now - start_time} sec"


cnt = 0
start_time = Time.now
times.times do
  v = rand((max * 2.1).to_i) - (max*0.1).to_i
  if (0..max).include?(v)
    cnt += 1
  end
end

p cnt
p "#{Time.now - start_time} sec"
```

結果は以下の通り
```

47620057
"14.872639993 sec"
47616640
"16.715346883 sec"
47621783
"20.541049745 sec"
```

最小値、最大値の幅を狭めてみてもあまり結果は変わらず。
単純に数値が一定の範囲内に収まっているかどうかは、比較演算子を使うのが一番高速に動くみたい。

# 余談
AtCoderでinclude?を使って提出していたところ、2200msでTLEを食らったが、比較演算子に書き直したところ1300msになりました。
結構ループや再帰処理をしたので、こんなに変わるとは・・・、
【XCode 15.0】プロジェクトからStoryBoardを抹消する方法が変わった

## 1.　記事の概要
XCodeを15.0にアップデート後、Main.StoryBoardを使わずにコードを実装しようとしたところ、若干ハマったので手順をまとめた

## 2.　実行環境
* 実行日：　2023年10月22日
<table>
  <thead>
    <tr>
      <th>カテゴリ</th> <th>バージョン情報</th>
    </tr>
  </thead>
  <tr>
    <td> XCode </td> <td> Version 15.0 beta 2 (15A5161b) </td>
  </tr>
  <tr>
    <td> Swift </td> <td> Swift 5 </td>
  </tr>
</table>

## 3.　手順のまとめ（結論）
1. `Info.Plist`の編集
2. `Main.StoryBoard`を抹消
3. `AppDelegate.swift`を編集

## 4.　手順

### 4-1.　Info.Plistの編集
`Info.Plist`内の`Build Settings` -> `Info.plist Values` の中にある`UIKit Main StoryBoard File Base Name`にデフォルトで`Main`が設定されているので削除

![スクリーンショット 2023-10-22 15.29.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3087199/f5ee0059-562e-07da-cd6d-1c9e6db89b21.png)

### 4-2.　Main.StoryBoardを抹消
* プロジェクトナビゲータ内の`Main.StoryBoard`ファイルを右クリック->[Delete]を選択して抹消する

### 4-3.　`AppDelegate.swift`を編集
* AppDelegate.swift内の`didFinishLaunchingWithOptions`メソッドを以下のように書き換えます

```Swift    
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.

    let rootVC = ViewController() // 起動時に表示したいViewController
    window = UIWindow(frame: UIScreen.main.bounds)
    window?.rootViewController = rootVC
    window?.makeKeyAndVisible()
    return true
}
```
* sceneDelegate.swiftを編集する場合とAppDelegate.swiftを編集する方法がありますが、前者はここでは割愛します


## 5.　従来のバージョンとの変更点
* 従来のバージョンだとInfo.plistのStoryboard Nameを削除したり、Main Interfaceを削除しないといけなかったですが、XCode15.0からは若干シンプルになったように感じます
todoアプリのテストをする時、何をテストすればいいのか

todoアプリを作成していて、どんなテストをすれば良いのかわからなかったので、それを軽くまとめてみました。

現状のテストコードはこんな感じ

```python
<?php

namespace Tests\Feature;

use Illuminate\Support\Facades\DB;
use Illuminate\Foundation\Testing\DatabaseTransactions;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Foundation\Testing\WithFaker;
use App\Models\Todo;
use Tests\TestCase;

class APITest extends TestCase
{
    use DatabaseTransactions;
    private $todo;

    public function setUp() :void
    {
        parent::setUp();

        $this->todo = Todo::factory()->create();
    }
    public function test_todo作成(): void
    {
		$response = $this->postJson('/todosCreated');
		$response->assertStatus(200);
    }
...
}
```

todoを作成したことを確認して、そのレスポンスが200を返すことを確認するだけのテスト。

※とりあえず、テストが通っていることを確認するテストだったので、あまり意味はない。

todoが実際に作成されているか、レスポンスの中身についてはテストができていないので、その辺を追加する必要がありそう。

# やってみたこと

- Todoがデータベースに正しく保存されているかの確認
- レスポンスの内容を検証

## Todoがデータベースに正しく保存されているかの確認

assertDatabaseHasを使用する事で、todoの期待される属性を指定。

```php
public function test_todo作成(): void
    {
        $aaa = $this->assertDatabaseHas('todos', [
            'title' => $this->todo->title,
            'status' => $this->todo->status,
            'created_at' => $this->todo->created_at,
            'updated_at' => $this->todo->updated_at
        ]);

		$response = $this->postJson('/todosCreated');
		$response->assertStatus(200);
    }
```

コマンドを実行。

```php
php artisan test --filter test_todo作成 tests/Feature/APITest.php --testdox
```

**`--filter`**オプションを使用すると、特定のテストメソッドを実行できます。それには特定のテストメソッド名を指定する必要がある。

**`--testdox`**オプションを使用すると、テストメソッド名の代わりに、テストの説明文が表示されます。これにより、テストの結果がわかりやすく表示される。

実行結果↓↓↓

![スクリーンショット 2023-10-22 13.33.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2597943/2b46c4d3-470a-98d9-790c-6398501c28e9.png)


## レスポンスの内容を確認


レスポンスの中身を確認するには、assertJsonStructureを使用して、期待されるレスポンスをJSON構造で確認します。

```php
public function test_todo作成(): void
    {
        $this->assertDatabaseHas('todos', [
            'title' => $this->todo->title,
            'status' => $this->todo->status,
            'created_at' => $this->todo->created_at,
            'updated_at' => $this->todo->updated_at
        ]);

		$response = $this->postJson('/todosCreated');
		$response->assertStatus(200);

        $response->assertJsonStructure([
            'title',
            'status',
            'created_at',
            'updated_at',
            'id'
        ]);
    }
...
```

※コマンドはさっきと同じ。

実行結果↓↓↓

![スクリーンショット 2023-10-22 15.36.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2597943/64958949-3a3b-deab-2f8a-9dcc76efb043.png)


これで、一先ずtodo作成時のテスト実装は完了！！！
React useReducerの使い方

# React useReducerのアウトプット
専門学校１年生のnaoyukiです。Reactの勉強のアウトプットのために初めて記事を書きたいと思います。
## useReducerとは
状態管理に使用されるReact Hooksの一つ。useStateよりも複雑なロジックを管理するのに使われる。
## 完成品
inputの値をuseReducerで管理するもの
![スクリーンショット 2023-10-22 150208.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3582046/89bb7b9e-6c51-ee73-09f8-31b075c1beca.png)
```diff_javascript
import { useReducer } from "react";

const reducer = (state, { type, payload }) => {
  switch (type) {
    case "change":
      return { ...state, [payload.name]: payload.value };
    default:
      return state;
  }
};

const Example = () => {
  const [state, dispatch] = useReducer(reducer, { a: 0, b: 0 });

  const ChangeHandler = (e) => {
    dispatch({
      type: "change",
      payload: { name: e.target.name, value: e.target.value },
    });
  };

  return (
    <>
      <div>
        a:
        <input type="text" value={state.a} name="a" onChange={ChangeHandler} />
      </div>
      <div>
        b:
        <input type="text" value={state.b} name="b" onChange={ChangeHandler} />
      </div>
      <h1>
        a:{state.a}
        <br></br>
        b:{state.b}
      </h1>
    </>
  );
};

export default Example;

```
## 解説
### useReducerをインポートする
```diff_javascript
import { useReducer } from "react";
```
### useReducerを定義する
stateの中に{a : 0 , b : 0}が入っている
dispatch()によってreducer関数が呼び出される
```diff_javascript
const [state, dispatch] = useReducer(reducer, { a: 0, b: 0 });
```
### input要素を定義する
valueにそれぞれstateの中のaとbを入れる
nameに"a"と"b"
onChangeでChangeHandler関数を呼び出す

```diff_javascript
    <>
      <div>
        a:
        <input type="text" value={state.a} name="a" onChange={ChangeHandler} />
      </div>
      <div>
        b:
        <input type="text" value={state.b} name="b" onChange={ChangeHandler} />
      </div>
      <h1>
        a:{state.a}
        <br></br>
        b:{state.b}
      </h1>
    </>
```
### ChangeHandler関数を定義する
dispatch()でreducer関数を呼び出している
引数にはオブジェクトとして、typeとpayloadを渡すことが一般的らしい
typeには処理の内容を記述
payloadにはその処理をするために必要な値をオブジェクトで記述する

```diff_javascript
  const ChangeHandler = (e) => {
    dispatch({
      type: "change",
      payload: { name: e.target.name, value: e.target.value },
    });
  };
```
### reducer関数を定義する
stateで現在のaとbの値を取得する
typeとpayloadにChangeHandlerで記述した値が入る
switch文でtypeが"change"だった場合、スプレッド演算子で現在のstateを返し、受け取ったnameとvalueを変更する
```diff_javascript
const reducer = (state, { type, payload }) => {
  switch (type) {
    case "change":
      return { ...state, [payload.name]: payload.value };
    default:
      return state;
  }
};
```
MySQL5.7のアップグレード作業でGTP-4にわからされた話

※ MySQL5.7のアップグレードに関しての実際の作業内容に関する話はありません。

# どういう話？
最近課金したGTP-4を使用して、自分で終了させたタスクに対して、生成AIならどういう答えを出すのか確認してみたところ、**自分では2日かかった答えを秒で出力してしまい**、自分の頑張りは何だったのかという失念と、やっぱり生成AIは今後活用しないといけないな、という教訓を遅ればせながら得た時のお話です。

# 経緯
サポートが終了するMysql5.7のアップグレード作業を依頼されたときの話です。
細かい詳細は省きますが、当時の内容としては以下の通りです。

### 投稿者スペック
・Railsの基本知識はある。（実務３年）
・データベースの基本知識はある。（実務３年）
・Mysqlアップグレードはやったことがない。

### Missionと聞き取り調査
・Mysql5.7をMySQL8.0にして、Railsアプリが正常に動作する部分までもっていく。
・本番環境でアップグレードする際のダウンタイムを許容できる。
・DBのエンドポイントが変わることは許容できない。
・テスト環境からイメージを作ってインスタンスを立ち上げて実験してもよい。
・同じアーキテクチャのアプリも作業を行いたいので作業フローをまとめて報告する。
・**アプリにドキュメントなどない。( ‘ᾥ’ )**

といった感じでした。

# アップグレード作業の計画する(投稿者)
初めての作業となるので、色々と調べながら実際に行う作業計画を確立していきました。
他業務の割り込みもあるので正確な時間は出せませんが、アップグレード作業の計画については、少なくとも着手から終了まで2日かかりました。

結果から言ってしまえば、ここの作業が**わからされた部分**になります。

ちなみに僕が考えた計画は以下のような感じでした。

### 1.バックアップは必ず取る
→　とにもかくにもバックアップを取る。いつでも戻せる状態にする。

### 2.テスト環境の構築
→　色々いじる必要があっため、テスト環境のイメージを作成。実験場作成。

### 3.実験開始

**■ 方向性**
アプリのコードを読んで問題点を洗い出すのではなく
アップグレードで発生したエラーを問題点として洗い出す

→　テスト環境でいきなりMysqlをアップグレードする。
→　発生するエラーを逐次解消する。その全てをメモする。
→　この作業でアプリとMysqlとの互換性を取り除く。
→　アプリの改修（コード修正）を行う。
→　逐次テストをする。
→　上記内容をくり返し、全てのエラーや不具合を解消。
→　完全に解消できた作業をフローとして確立させる。

### 4.スケジュールの設定
→　本番環境で実施するスケジュールを立てる。

### 5.本番での実施
→　全てが確立出来たら実施する。

### 6.経過観察
→　問題ないかの確認。

言語化しつつ、色々頭の中をまとめながら「この流れでやります」と報告。
そのままGOが出たので、上記の作業を開始しました。

ちなみに、このタスクはすでに完了したタスクになります。
しかし反省や興味本位の気持ちもあり、後日GPT-4に同じ内容で計画をしてもらいました。

# アップグレード作業の計画する(GTP-4)

**■ 投稿者**
~省略~ おすすめなアップグレードのフローを教えてください。

**■ GTP-4**
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/387747/aeee6ce7-4ef8-58ef-ac86-68500797427b.png)

# GTP-4の結果を見て
**ほぼ同じじゃん。**

**ぼくがかんがえたさいきょーのけいかく**を、GPT-4は一瞬で生成してしまいました。

正解を導けた自分を褒めるべきか、2日もかけた自分を貶すべきか...
この結果には目が点になってしまいました。

# まとめ
今回は本当にきれいに「よもやここまで」といった体験をすることが出来ましたので記事として投稿しました。

上記の質問も、GPTがブラウジングしてMySQLの公式リファレンスを見に行っていることもあり、情報に関しても正確なものになっていると思います。

今後の生成AIの動向については、今まで以上にアンテナを張って活用していかなかればならないな。と感じた1日になりました。(遅い)

終わり。
Vue.js, Composition APIについてまとめ

ずっとReactで開発してきましたが、最近転職した会社で４年ぶりにVueを触りました。
その復習と**Composition API**は自分がVueを触っていた2018-2019年当時はまだなく、今回新しく学ぶ事になったので備忘録として記事にしました。

## Vueの基本

**まずプロジェクトの作り方**
最新事情ではVue/CLIは不要で、create-vueというツールですぐにプロジェクトをセットアップできます。
（vue@2に変えればVue2のプロジェクトも作れます）

```
// 事前インストールは不要でこのコマンドを実行すればOK
npm init vue@3 my-vue3-project
```

**Vueコンポーネント（.vueファイル）の構成**
template：DOM要素
script：ロジック
style：CSS
の3つのトップレベル言語のブロックで構成。

**データの受け渡し**
親から子へprops downでデータを渡す
子から親へ$emit upでデータを渡す（カスタムイベント）
＝子供が親のデータを変えれなくしてる、親が子供のデータに依存するデータをもてない様にしてる

*子から親へのデータの受け渡しはemitじゃなくて、propsでメソッドを渡しても同じ

単方向データフローの原則に基づいてVueは作られてる
この制限をなくすとアプリケーションは複雑化してしまう！

親でAPIからデータを取得して、子コンポーネントに渡すスタイルはVueもReactも同じですね。

**双方向データバインディング**
v-model
v-modelを使うことで、画面上でフォームの入力値を変更するとコンポーネント内のデータも自動的に変更してくれる。

これはReactにはない、Vueの便利な機能です。


## Composition APIとは
ざっくり言うとリアクティブな値やそれらに関連した処理をComposition APIのおかげで切り出して扱えるようになった。

Reactを例にとると、useStateなどのHookでロジックをカスタムフックに切り出せるようになったのと似ている。

ReactがClassコンポーネントから関数コンポーネント+Hookへ移行が加速したように、VueにおいてもComposition APIが現在は主流となっているそうです。

**実際どう書くのか？**
Composition APIでは基本的にsetup関数内で必要なデータ、処理を**まとめて**定義してreturnします。

setup関数内でref・reactiveを使用してリアクティブな変数を生成
ref・reactiveで生成したリアクティブな変数をsetup関数からreturn
template内でsetup関数からreturnしたリアクティブな変数を利用

```typescript
import { ref, Ref, reactive } from '@vue/composition-api'
export default defineComponent({
  name: 'ComponentName',
  setup() {
    const message: Ref<string | null> = ref(null)
    const isHogeUsed: Ref<boolean> = ref(false)

    const state = reactive({
      title: '',
      message: '',
    })

    return {
      message,
      isHogeUsed,
      state
    }
  },
})
```

**関数の定義**
さらに、setUp関数内では、関数の定義を行えます(従来通りmethodsでもできる)

**Computed**
算出プロパティ
従来はComputedプロパティで行っていたこの処理もsetup関数内で行います。

## リアクティブシステム
値を監視して、値が変わるとビューを書き換えてくれる**リアクティブシステム**がVueの重要な鍵です。

**リアクティブなデータを扱うメソッド**
reactive: オブジェクト
ref: 単一の値
どちらもリアクティブな値にするメソッドで、ReactのuseStateと似てます。

reactive, refで参照の仕方が違うので注意！！

```
// refはvalueプロパティが必要
const count = ref(0)
const onClick = () => {
  count.value += 1
}
```

```
// reactiveはvalueプロパティが不要
const data = reactive({
  current: 0
})
const onClick = () => {
  data.current += 1
}
```


取り急ぎ、簡単にまとめました。今後も随時追加していきます。
KubernetesのClusterIP／ExternalIP／NodePort／LoadBalancer Service超概念図


# 記事の内容

- Kubernetesでサービスを公開するための超初歩的で代表的なServiceについて整理。


# ClusterIP／ExternalIP／NodePort／LoadBalancer Serviceに関する超概念図

![スクリーンショット 2023-10-22 14.58.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/142483/eb7fcc65-fd45-afbe-936c-135ff6c7fcfc.png)  

## ClusterIP Service  

- K8sクラスタ内からのみ疎通性がありInternal Networkに作り出され、仮想IPが割り当てられる。
- ClusterIP宛の通信は、各Node上で実行しているkube-proxyがPod向けに転送を行う。  


## ExternalIP Service

- 特定のK8sNodeのIPアドレス:Portで受信したトラフィックをコンテナに転送する形で外部疎通性を確立
- 特別な事情がない限り、NodePortを使う。（NodePortは基本的に機能は同じで全K8sNode宛通信をコンテナに転送）  


## NodePort Service

- 全てのK8sNodeのIPアドレス:Portで受信したトラフィックをコンテナ（ClusterIP Service）に転送する形で、外部疎通性を確立。
- 指定可能なPortの範囲は、30000〜32767。全てのK8sNodeで利用可能なPortを指定する必要がある。


※NodePort ServiceもExternalIP Serviceも、いずれかのK8sNodeに割り当てられたIP宛に通信を行う。
　つまりリクエストを飛ばしたNodeがダウンしていた場合、エラーが返る。（そのため障害耐性を高めるため、LoadBalancer Serviceを使う）


## LoadBalancer Service

- K8sクラスタ外のロードバランサに外部疎通性のある仮想IPを払い出す。
- 実体は、各クラウドベンダのLBサービス等。（AWS ELB／GCP GCLB etc... ）




理解が誤っていれば、正しい情報が記載されたリンクと共にご指摘いただけますと幸いです。
【Mac】GUI操作の小ネタ

# はじめに
私は地味にWindowsよりMacの方が使用歴が長いですが、
仕事でWindowsを使うようになってからは、MacとWindows間の操作のちょっとした差異に戸惑うことが多々あります。
特に私はマウスなどを使わず、キーボード上の操作だけで完結できるのが好きなため、この戸惑いが地味にストレスになります、、、

そんな私の備忘も兼ねて、
今回はGUI操作に関する小ネタを残しておきます。

今後のGUI関連の操作メモとして随時更新していこうと思います。

# MacのGUI操作にう関する小ネタ集

## ダイアログボックスをキーボード操作する

以下は編集済みのファイルを保存せずに閉じようとした場合のダイアログです。

<img width="300" alt="ダイアログ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3292417/164fa16c-1f67-435f-821e-2003c4b6571c.png">

紫で　塗りつぶされている「保存」が目立ちますが、
よく見ると「保存しない」という選択肢も紫の枠で囲まれてフォーカスされています。

:::note info
※色に関してはユーザ設定により異なる場合があります。
:::

尚、この枠はtabキーで選択対象を操作することができます。

では「保存しない」にフォーカスされている状態でEnterキーを押してみます。

<img width="499" alt="保存ダイアログ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3292417/139a6f8f-4421-1758-eb70-1c40f4edbcc1.png">

**！？**

ファイル保存のウィンドウが立ち上がりました...

その後色々と試してみるも中々うまくいかず、
ただわざわざフォーカスを移動できる仕組みがあるわけで、必ずキーボード上の何らかの操作で「　保存」以外を選択することもできるはず

結果...

**Spaceキー**の押下で反応しました。

つまり、こういうことなのでしょう。
<table>
  <caption>ダイアログボックスのキーボード操作</caption>
  <thead>
  </thead>
  <tr>
    <td> 色が塗り潰されている選択肢 </td> <td>Enterキーを押した際に選択される</td>
  </tr>
  <tr>
    <td> 枠で囲まれている選択肢 </td> <td>Spaceキーを押下した際に選択される</td>
  </tr>
</table>


前者に「保存」　後者に「保存しない」が設定された状態でダイアログが出現するので、慣れるとフォーカルの切り替えなしに二者択一の選択ができて便利かもしれません。

## アプリのキーボードショートカットを変更する

画面上部に表示されているメニューバーに割り当てられてキーボードショートカットは任意のショートカットに切り替えることが可能です。

Windowsのショートカットと所々違う点があるので、これをうまく使い環境差異を減らすことが可能です。

- [設定]-[キーボード]-[キーボードショートカット]-[アプリケーション]を選択
-  [+]を押下
 <img width="400" alt="ダイアログ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3292417/91a45eb6-03f6-33bb-d5ce-fbd6827d20d9.png">

-  [アプリケーション]-[すべてのアプリケーション]を選択して任意のアプリを選択
<img width="400" alt="ダイアログ" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3292417/de19c25d-7ae3-404a-4393-eca10ae80fcf.png">

-  [メニュータイトル]にメニューバーの表記通り記述していく。
この時、メニュ間の階層を[->]で区切る点がポイント
<img width="300" alt="アプリケーションショートカット" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3292417/6da009ae-046c-04d0-d08d-dc02c1e0157a.png">

1.ショートカットが変更されている
<img width="100" alt="メニューバー" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3292417/f500fe33-1aef-2a09-d064-4b09841dcfb5.png">

自分はエクスプローラのアドレスバーを操作対象となる、[Ctrl+L]のショートカットにちなんで、
Finderのフォルダへ移動ショートカットを[⌘+L]に設定している。

あまり標準の設定からカスタムし過ぎると訳わからなくなるため注意が必要ですが、
使用頻度が高い割に打ちにくいショートカットややWindowsとの差異あり過ぎるものはこんな感じで対処しています。
【Jenkins】Jenkinsの設定に直接Pipeline Scriptをかきたい

サクッと試験的にパイプライン作ってみたい時はこちらの方がおすすめ。
わざわざJenkinsfileをリモートリポジトリに用意しなくていい。
デメリットは誤ってこのジョブを消しちゃったら何も残らないくらい。
![スクリーンショット 2023-10-22 14.47.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/377231/ca92d360-54a5-a0ca-6ee7-46f6a1b32a26.png)

設定->パイプライン->Scriptに直接書けばいい


例
```groovy
pipeline {
    agent any
    
    triggers {
        cron('H * * * *')
    }
    
    environment {
        USERNAME = 'USERNAME'
        HOSTNAME = 'HOSTNAME'

    }
  
    stages {
        
        stage('SSH and run script') {
            steps {
                sshagent(credentials: ['CREDENTIAL-ID']) {
                    sh '''
                        ssh ${USERNAME}@${HOSTNAME} "cd /PATH/ && bash FILENAME"
                    '''
                  }
            }
        }
        stage('Clean Workspace') {
            steps {
                cleanWs()
            }
        }
    }
}
```

USERNAME
HOSTNAME
CREDENTIAL-ID
PATH
FILENAME
だけ書き換えれば動きます。

内容は
1時間に１回、
`USERNAME`アカウントで`HOSTNAME`へSSH接続し（この際`CREDENTIAL-ID`によりJenkinsサーバと`HOSTNAME`間のノンパス設定済）、
`PATH`にcdして`FILENAME`を実行、
最後にワークスペースをクリア
です。
Youtubeで「未経験 30代 エンジニア転職」と検索した上位動画の内容まとめ（2023年10月22日現在）

# はじめに
「YouTubeで「未経験 30代 エンジニア転職」と検索し、結果として10本の動画を視聴しました。（2023年10月22日現在）

結論は「可能」でした。以下、要点を簡単に羅列して、まとめてみます。

## ◆結論
・エンジニアとしての転職は可能だが、難しい
・戦略は大事（正攻法では返り討ちに）

## ◆前提
・実際にSEとして活躍されている方からスクールや転職エージェントの動画が混在しているため、ポジショントーク等の可能性も否定できない点。
・今回対象の動画は主にフロントエンド、バックエンドエンジニア向けの動画が多い。
・動画のアップロード日付が3年前など、現状況とのギャップがある可能性がある。

## ◆詳細まとめ
### 【可能な理由】
・実際に30代未経験で転職を実現し、現在も活躍している方が多くいる。
テックキャンプ卒業生のうち、4500人以上の中で1000人以上が30代である点など。
また、50代未経験で転職した猛者も。
・現在は成功した経験者のロードマップがネット上にあり、諦めずになぞっていけば実現も可能。

### 【難しい理由】
・年齢・未経験という大きな壁
20代で10年経験を積んだエンジニアと30代未経験のエンジニアがどのようにギャップを埋めるのか。
・年下上司との関係性（働きづらさ、気を遣わせてしまう）
なめられても大丈夫か、上から目線で発言されても大丈夫か。
一方で、「前の会社ではこうだった」という押し付けや、歳下上司へのタメ口はレッドカード。

## 【具体的な方法】
### ◆就活時のポイントを抑える
・面接対策の徹底（長く、やる気を持って働いてくれるか）
・とにかく実務経験を積むことを最優先
・20代と異なるアプローチを模索することが重要。
特に、働き方のこだわり（リモートワーク）や福利厚生、自社開発企業のみなどの個人的なこだわりはまずは捨てる。
現実的に選択できる求人数は減ることもあるため、特に1つ目の会社では経験を積むことに焦点を当てる。
「ゼロからイチ」という最初のハードルを乗り越え、理想のキャリアに近づくために経験が必要。
・実務経験を積むために、アルバイトや無料のプロジェクトでもよいので参加する。とにかく実務経験を積む。

### ◆ポートフォリオの準備
・未経験でも、個人レベルでは経験済みにする。
・プログラミングスキルの向上に重点を置き、高いプログラミングスキルを証明できるポートフォリオ（や資格）を取得。
・スクールでのポートフォリオだけではなく、（特に前職に関連した）別のポートフォリオを作成するのが吉。また、作成時も目的地から逆算したロードマップに沿って効率的に行う。

### ◆30代としてのアピールポイントを徹底的に活かす
・若いエンジニアとの技術的なギャップを克服するために、対人関係構築など、持っているスキルを活かすことが大切。
・特にコミュニケーション能力、タスク管理、マネジメントスキルなどがエンジニアとしての成功に寄与する。

### ◆メンタル面
・エンジニアとしての人生を築くために諦めずに努力を続ける。
・自分の人生を変える覚悟を持つ。諦めないで挑戦し続ければ最終的には叶う
・年齢と未経験というマイナス要素があることを受け入れ、100社受けて2,3社回答があれば良い方。事前に心構えをしておく
・収入が減少する可能性が高いため、将来的な収益を見据えて収入の低下を乗り越える覚悟と準備
・採用された場合、教育コスト等を踏まえても新人は赤字。そこのビジネス的な観点は30代として適切な行動・発言を慎む。

### ◆その他の行動
・多くの企業に応募し、会社説明会に参加して、採用の機会を増やす
・選ぶべき言語やスキルに注意する。例えば、競争力の低いPHPなど。Rubyは新しいが、活躍年代が20代と若いなど、
・プロのエンジニアやIT企業とのネットワーキングを構築する（勉強会、オフ会、セミナー、講座、スクールなど）
・情報発信を積極的に行う
・資格取得は実務に直結するような特定の分野に特化した資格（AWSなど）が良い

## ◆その他
・エンジニアに向いている人
凝り性（没頭するくらいゲーム好き、新技術へ好奇心）
ガリ勉タイプ（勉強好きがそのまま活きる仕事）
理屈っぽい人（論理的）
エンジニアという職種は「地に足」をつけて、マラソン。継続した人が最終的に強い
・20代vs30代で成長スピードに差はあるのか、結局個人次第では？

## ◆感想
結局、どの業界も未経験転職の心得は同じなのかなと。
しかし、この業界に限っては専門性とスキルがものをいうため、合う・合わないが顕著。
事前に入社後のギャップを最小限にするために、入社前の個人的経験（ポートフォリオ作成）などは徹底的に行う。
そして、入社前・後も目的・熱意・信念を見失わずに進んでいくこと。それが一番大事。

## 最後に
最後に、有益な動画を作成していただいたことにお礼申し上げます。
随時、実体験も踏まえて、更新していければと思います。

※参考動画
===============================================
30代未経験からエンジニア転職は目指せる！？
https://www.youtube.com/watch?v=Of8_cHuqGxs&t=767s

30代未経験のエンジニア転職の真実を教えます
https://www.youtube.com/watch?v=hJB7OE0OmyQ

【IT転職】未経験３０代のIT転職について思うこと。後悔しないためには？
https://www.youtube.com/watch?v=y5hwDXquwHE

完全未経験からのエンジニア転職は無理だと思う…。
https://www.youtube.com/watch?v=tWGwxuZGQYE

プログラマーに転職するな！30代未経験から転職は無理ゲー
https://www.youtube.com/watch?v=6I8dnrB4WMI	

30代後半からエンジニア転職！プログラミングの魅力&勉強法は？
https://www.youtube.com/watch?v=cLFfBR9qWzk

40代未経験からエンジニア転職成功！勉強法&面接対策は？
https://www.youtube.com/watch?v=a8Q2jbi7nu8

「30代やめとけ」というYouTuberに怒っています【未経験エンジニア転職】
https://www.youtube.com/watch?v=LFTil0h-OmE

【物申す】30代が未経験からエンジニア転職は無理と決めつけるな！！
https://www.youtube.com/watch?v=YBt2OkWyANk

【完全網羅】未経験からエンジニアへ転職するための完全ロードマップ
https://www.youtube.com/watch?v=foR4PyGx3lk
Vue2からVue3への移行手順

Vue2のサポートが2023年12月31日をもって終了するため、業務で使用しているVue2のサービスをVue3へアップデートしました。この記事では、実際の移行手順をまとめています。

## 移行手順

1. Vue-loaderのアップデート:
   ```shell
   npm i -D vue-loader@16
   ```
    vue-loaderとは、Vue コンポーネントをプレーンな JavaScript モジュールに変換する webpack の loaderのことです。
　
2. Vue3のインストール:
   ```shell
   npm i vue@3
   ```

3. package.jsonからVue2に関連するライブラリを削除:
   使用しているライブラリがVue3対応か確認し、必要に応じて更新または新しいライブラリをインストール。
　

4. Vue 2 互換性ライブラリのインストール:
   ```shell
   npm i @vue/compat@3
   ```
   このライブラリは、Vue 2 のコードを Vue 3 と互換性を持たせるため。

   vue.config.jsに以下の設定：
   ```vue.config.js
   module.exports = {
     chainWebpack: (config) => {
       config.resolve.alias.set('vue', '@vue/compat')
       config.module.rule('vue').use('vue-loader').tap((options) => {
         return {
           ...options,
           compilerOptions: {
             compatConfig: {
               MODE: 2
             }
           }
         }
       })
     }
   }
   ```

5. 単一ファイルコンポーネントコンパイラのインストール:
   ```shell
   npm i -D @vue/compiler-sfc@3
   ```
   このパッケージは、ファイルごとに分けられたコンポーネントを使用できるようにします。
　

6. Eslintとeslint-plugin-vueのアップデート
　

7. Vue CLI関連のライブラリのアップデート:
   package.jsonを更新して、関連するライブラリをVue 3に対応したバージョンに更新します。
　
8. Eslintのparserをbabel-parserから@babel/eslint-parserに変更
　
9. ビルド時のエラーを修正:
   ```shell
   npm run build
   ```
   ターミナルにエラーや警告メッセージが表示されるかもしれませんが、それらを地道に解決します。eslint関連のエラーで修正が多く致命的なエラーではない場合、一時的にeslintルールをオフにすることも検討します。
　
10. サービスを起動:
    ```shell
    npm run serve
    ```
    - vue-routerをバージョン4にアップデートし、修正を適用します。
    - Vuexをバージョン4にアップデートし、修正を適用します。
    - Vue2からVue3へのアップデートに伴う破壊的な変更を修正します。
    - DevToolsに表示されているエラーを解決します。
    - 使用しているライブラリの仕様変更に対応します。
　
11. DevToolsでエラーが消えたらvue-compatを削除:
    ```shell
    npm uninstall @vue/compat@3
    ```
    vue.config.jsを元に戻す。
　

12. 動作確認

## 移行時に使用した資料

- [Vue 3 移行ガイド](https://v3-migration.vuejs.org/ja/)
- [フレームワークレベルでの新しい推奨事項](https://v3-migration.vuejs.org/ja/recommendations.html)
- [破壊的変更](https://v3-migration.vuejs.org/ja/breaking-changes/)
- [移行ビルド](https://v3-migration.vuejs.org/ja/migration-build.html#compat-configuration)
- [Migrating from Vue 2](https://router.vuejs.org/guide/migration/)
- [Migrating to 4.0 from 3.x](https://vuex.vuejs.org/guide/migrating-to-4-0-from-3-x.html)
GitHub Discussion のテンプレートの作り方

# はじめに

自分が所属しているコミュニティで、GitHub Discussion を使ってテンプレートを作成したので、その作成方法をまとめます。

非エンジニアの方でもエンジニアとの話し合いの場に GitHub Discussion はとても役立つのでぜひ試してみて下さい。

## GitHub Discussion とは

GitHub Discussion は、OSS(Open Source Software) または内部プロジェクトに関するコミュニティの共同コミュニケーションフォーラムです。
画像は、GitHub Community の Discussion 画面です。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/707293/c9f78db2-4e6e-ee85-8a57-9b401a38935f.png" url="https://github.com/orgs/community/discussions/">

 Discussion を始める方法については、[GitHub Discussions のクイックスタート](https://docs.github.com/ja/discussions/quickstart)をご覧ください。

## テンプレートを使うメリット

GitHub Discussion では、テンプレートを使うことができます。
テンプレートを使うことによって、議論の内容を明確にできます。
特に質問の場合は、テンプレートを使うことで回答者が必要な情報をまとめやすく、回答者の負担を減らすことができます。

## テンプレートの作成方法

### 1. Discussion のカテゴリを用意する

### 2. テンプレートファイルを作成する

フィールドごとに、入力の種類、検証、および既定のラベルを定義できます。

コミュニティメンバーがディスカッションフォームに入力すると、それぞれの入力に対する回答が Markdown に変換され、ディスカッションの本文に追加されます。

Discussion のテンプレートを作成・追加するには、`.github/DISCUSSION_TEMPLATE/` ディレクトリを作成します。
`.github/DISCUSSION_TEMPLATE/` ディレクトリには、カテゴリごとにテンプレートファイルを作成します。**複数のカテゴリを跨いで作成できないので注意して下さい。**
テンプレートファイルは　YAML 形式で記述します。

例えば、Flutter というカテゴリのテンプレートを作成する場合は、`.github/DISCUSSION_TEMPLATE/flutter.yml` というファイルを作成します。次に、`labels`に　Flutter を追加して、`labels: [Flutter]`のようにします。


ディスカッションカテゴリフォームの構成ファイルには、`body` キーが含まれている必要があり、`body` には少なくとも 1 つの Markdown 以外のフィールドが含まれている必要があります。例えば、`type: input` や `type: textarea`, `type: toggle` などが挙げられます。

以下のコードは、[GitHub Community の Actions に関する Discussion カテゴリーのテンプレートファイル](https://github.com/community/community/blob/main/.github/DISCUSSION_TEMPLATE/actions.yml?plain=1)です。

```yaml: actions.yml
labels: [Actions]
body:
- type: dropdown
  attributes:
    label: Select Topic Area
    description: What would you like to discuss?
    options:
      - "Question"
      - "Product Feedback"
      - "Bug"
      - "Show & Tell"
      - "General"
  validations:
    required: true
- type: textarea
  attributes:
    label: Body
    description: Start your discussion!
  validations:
    required: true
```

上記の書き方だと、以下のようなフォームになります。

![スクリーンショット 2023-10-22 14.35.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/707293/62205f75-e8dd-d33e-6805-8187dc2100ec.png)

`validations: required: true` で入力を必須項目にすることができるので、おすすめです。

### デフォルトブランチにマージ

ファイルが作成できたら、`main` などのデフォルトブランチにマージしたら作成完了です。


## GitHub Discussionsのテンプレート使用方法

テンプレートを作成できたら、GitHub Discussions で該当カテゴリを選択してディスカッションを作成します。テンプレートで用意した入力フォームが表示されているので入力して、ディスカッションを始めましょう!


# 参考文献
- [GitHub Discussions のテンプレートを作成してみた - エキサイト株式会社](https://tech.excite.co.jp/entry/2023/07/24/100404)
- [ディスカッションカテゴリフォームの構文 - GitHub Docs](https://docs.github.com/ja/discussions/managing-discussions-for-your-community/syntax-for-discussion-category-forms)
Windows上のPythonとJupyter/Ipythonでのマルチプロセッシング - 動作させるために（翻訳まとめ）

記事：[Multiprocessing in Python on Windows and Jupyter/Ipython — Making it work](https://medium.com/@grvsinghal/speed-up-your-python-code-using-multiprocessing-on-windows-and-jupyter-or-ipython-2714b49d6fac)

# 現象
Windows上だとpythonシェル(cmd → python)で実行すると以下のエラーが発生する。
```
Can't get attribute 'worker' on <module '__main__' (built-in)> 
```
Windows + Jupyter上でこのコードを実行すると、永遠に固まったまま処理が完了しない。

# なぜ、うまく動作しないのか？
JupyterとWindowsの相性が悪い

# 解決法は？
２つの簡単な修正を加えるだけで問題なく動くようになる。

* 関数などの定義を ___別の .py ファイルで定義___ してインポートする。
* 呼び出す前に ``` if __name == '__main__' ``` 節を追加する。
[@profview]Juliaで関数の速度を調べたい

今回は、関数の実行速度を改善したい時に使える@profviewを紹介します。これを使うと、どこの関数の実行速度で問題になっているか調べることができて結構便利です。

前提として、関数の実行速度に不満がある、もっと速くしたいなどに使えます。

## VScodeに紐づいたjuliaを開く。

まずはVSCodeで紐づいているjuliaを開きます。手順は以下の通りです。

```shift + command + p``` でjuliaを開いて、以下の画面を通じて、juliaを起動します（以下の画面のStart REPLをクリック）。


![startrepl.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224085/0741b706-c0a8-9b29-7558-6e6cec9040b4.jpeg)



## @profviewを使ってみよう。
そうして開いたターミナルで、@profview 関数名で調べてみよう

（なお、VSCodeで使いたい人向けのパッケージもあるみたいだが、ターミナルの方を推奨します。
https://github.com/kimikage/ProfileSVG.jl）


### 具体的な実行例

ここでは、複数の関数からなるmain()というものについて適用する。
```julia
@profview main()
```

実行結果はこんな感じとなる。
main関数より下が実際にコントロール可能な関数群となる。下の図の左側の画面の長方形の横幅が大きいほど時間がかかっているということになる。ここでは、solve関数がボトルネックになっているとわかる。そこで、`Command　＋クリック`で該当するコードの箇所における（図を参照）

さらにupdate関数という関数に薄く青色の背景がついている。これは、`型推論が成功しているけど、単純に時間がかかってますよ！`という意味である。

![main.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224085/c7795182-806d-98d7-9c34-8a98fad44241.jpeg)


次に、update関数をクリックすると、

![update_prof.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224085/44de315e-0185-d55f-8c94-ac42cc805784.jpeg)


右側を見ると、inserglobalpivotに時間がかかっていそうだとわかる。詳細は以下のようである

![insertglobalpibot_prof.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/224085/1e00cadc-d748-98cb-4511-94fb33727294.jpeg)


`赤い背景は、ループ内でランタイムディスパッチが起きている。`
今回は、for文の中でerrという関数がfor文の中で作られているので、型不安定を引き起こしているようであった。
for文の中の外に出すかerrという関数を作らずに、optfirstpivotの中にerrを入れて直接誤差を計算する関数を作るという二つの解決策が考えられそうだ。

## まとめ
以上のような感じで、関数の実行速度のボトルネックを発見、解決していくことができる。
sesseract OCRにて画像からの文字認識を試したメモ

Anacondaにて tesseract-ocr を試したメモ。

環境
=
Anacondaを使って、Opencvも入れている前提だが、
TesseractというオープンソースのOCRエンジンと、それをPythonで使えるようにしたライブラリであるpytesseractを使用します。

それぞれAnaconda.orgで調べたコマンドにて仮想環境にインストールする
~~~~~~~~~~~~~~~~~~~~
conda install -c conda-forge pytesseract
~~~~~~~~~~~~~~~~~~~~
すぐにインストールできた。

~~~~~~~~~~~~~~~~~~~~
conda install -c conda-forge tesseract
~~~~~~~~~~~~~~~~~~~~
試したが、tesseractはインストール完了できず、exeでインストールする方法に作戦変更。手順は以下。

1.download,install
https://github.com/UB-Mannheim/tesseract/wiki

からtesseractのセットアップ.exeファイルを直接downloadしてインストールする方法に変更。
tesseract-ocr-w64-setup-5.3.3.20231005.exe (64 bit)をDownloadして、ガイダンス通りに言語を選択した上、インストール。

![スクリーンショット 2023-10-22 130213.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3181208/c1a69b97-0d6b-0338-50c4-4523753a4e73.png)

2.環境変数を設定
pathの中に"C:\Program Files\Tesseract-OCR"を追加する

![スクリーンショット 2023-10-22 133407.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3181208/6ddb78d9-c294-8407-9217-8fbca2ff8d35.png)

TESSDATA_PREFIX変数を新規作り、"C:\Program Files\Tesseract-OCR\tessdata"を追加しておく。
![スクリーンショット 2023-10-22 133331.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3181208/aa31faa6-0b2e-9200-0e48-ca7f68b4f7a9.png)

3.環境変数設定できたかを試す。AnacondaでTesseractを認識できたかをチェック。
![スクリーンショット 2023-10-22 133723.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3181208/8586da45-e51b-2ba9-64e9-5d4f26104e7d.png)
このようにVersionがちゃんと表示できたらOK。そうじゃなければ、内部コマンドじゃないと弾かれる。

4.最後、pytesseractのpytesseract.py中身を変える。tesseract_cmd = ‘tesseract’，をtesseract_cmd =r’C:\Program Files\Tesseract-OCR\tesseract.exe’に直せば、importできるようになるはず。

![スクリーンショット 2023-10-22 135310.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3181208/9b08efb1-5d27-8368-2e1b-4f75aaf79d93.png)

実行してみる
=
~~~~~~~~~~~~~~test.py
import sys
import pytesseract
from PIL import Image

# 画像を読み込む
image = Image.open('C:/Users/ryuka/Documents/test.png')

# 画像からテキストを探して取り出す、langは英語、日本語などのパラメータ設定できる
text = pytesseract.image_to_string(image, lang='eng')

print(text)
~~~~~~~~~~~~~~
使う画像
![test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3181208/4384d7fb-2ea0-676c-7466-f6004c1d5a94.png)

実行結果
=
![スクリーンショット 2023-10-22 142553.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3181208/4ef21078-e60c-8fde-a011-d2d7d6f759e9.png)

完璧ではないが、9割以上の識別はできた感じ。

memo:荒い画像で試した結果、sesseractよりもeasyOCRの方がよりよい結果が得られましたので、インストールも扱いもeasyOCRの方がおすすめ。
kubeadmで作成したk8sクラスタをresetする時に気を付けること

# はじめに

kubeadmでk8sクラスタを作って、「クラスタ作り直したいな…」と思ったことは誰にでもあるはずです。
そこでよく出るのがkubeadm resetというコマンド。
あまたのWebサイトではこのコマンドだけでk8sクラスタを削除できるという話が書いていたり書いていなかったりするわけですが、このコマンドで削除した後、再度同じノード上でクラスタを組もうとするとエラーが発生したりします。そうです。実はkubeadm resetコマンドだけでは不足しているのです。

あくまで私の環境でではありますが、kubeadm resetからクラスタ再生成までの手順を備忘録的なノリで書いていきたいと思います。

私のk8sクラスタは以下の手順で作っております。

https://qiita.com/ohtsuka-shota/items/7618cd98a12c8b2e4e07


# 手順

workerノード側でまず以下を実行。
全workerでこの対応をしたら、マスター側でも同様に実行。
```
kubeadm reset --force
systemctl stop kubelet
rm -rf /etc/kubernetes/
rm -rf ~/.kube/
rm -rf /var/lib/kubelet/
rm -rf /var/lib/cni/
rm -rf /etc/cni/
rm -rf /var/lib/etcd/
iptables -F && iptables -X
reboot
```
なおこのコマンドセットは以下のサイトを参考にしています。
ありがとうございます。

https://blog.turai.work/entry/20220729/1659055307



再起動後、以下を実行
```
swapoff -a
```

masterで以下を実行
```
kubeadm init --control-plane-endpoint=k8s-master
mkdir -p $HOME/.kube
cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
chown $(id -u):$(id -g) $HOME/.kube/config
kubectl cluster-info
```

masterで出力されたkubeadm joinコマンドをworker上で実行。
(以下はsampleコマンド)
```
kubeadm join k8s-master:6443 --token k3jlcm.s0qo8inmbrspbhf0 \
        --discovery-token-ca-cert-hash sha256:82f316967584a51282f500875c6b53c02b99ac4d002d1de13d656820f0c2d6da
```

最後にmasterで以下を実行
k8sクラスタをまっさらな状態に作り直せました。ちゃんちゃん。
```
kubectl apply -f https://raw.githubusercontent.com/projectcalico/calico/v3.25.0/manifests/calico.yaml
```
macOSでflutter_blue_plusのサンプルアプリを動かす

# macOSでflutter_blue_plusのサンプルアプリを動かす

FlutterのパッケージでiOS, Android, macOSでBLEのプラグイン[flutter_blue_plus](https://pub.dev/packages/flutter_blue_plus)を見つけたので使ってみました。
macOS向けの記事がなかったので、備忘録的な記録です。

## アプリ起動

Flutterの開発環境は構築済みの前提です。
flutter_blue_plusリポジトリをクローンして、exampleアプリを起動します。

```bash
git clone git@github.com:boskokg/flutter_blue_plus.git
cd flutter_blue_plus/example
flutter pub get
flutter run -d macos
```

起動すると以下のようなBLEデバイスのスキャン画面が表示されます。

![スクリーンショット 2023-10-22 14.14.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180910/df1ed6cc-0428-f28f-5838-3f5e09fa706b.png)

実際にBLE接続して、CharacteristicのREADやWRITEも動作確認できました。

![スクリーンショット 2023-10-22 14.15.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180910/d9888ac3-9982-0391-1852-a1d6173c8715.png)


## BLE Adapter not availableと表示される時

flutter_blue_plusをパッケージとして使用する場合、bluetoothのEntitlementsを設定する必要があります。

Runner/DebugProfile.entitlementsとRelease.entitlementsに以下を追記すると解消されます。

```xml
<key>com.apple.security.device.bluetooth</key>
<true/>
```

引用元: https://github.com/pauldemarco/flutter_blue/issues/788#issuecomment-796831258

## まとめ

macOSでも使用できるBLEパッケージで、BLEの基本的な動作は実現できることがわかりました。
macOSでBLEの動作確認できるアプリがないので、サンプルアプリを改良してmacOS向けアプリを作ってみたいです。
Miniforgeを使っている人mamba知らなすぎ問題。

Miniforgeの導入記事をみるとどの記事もcondaを利用してpackageのインストールをしています。

もったいない！
標準搭載のmambaがあるのに！

mambaの方がcondaより高速です！

普段「conda」と入力しているところを「mamba」に置換するだけです。

詳しくは自身でお調べになるか、
この辺りでもみてください。

https://mamba.readthedocs.io/en/latest/

https://github.com/mamba-org/mamba
【2023年10月作業メモ】Chromebookをセットアップしたときにしたこと一覧

# 買った
Chromebookを追加しました。
追加したあとに設定したことをメモしていきます。
（後にPowerwash[^Powerwash]とかしたときに便利なので）
[^Powerwash]: Chromebookの初期化。何もかもが消える

# 初期設定

## Wi-Fi
Chromebookのレビューじゃないので画面とかは省きますが、まずは、Wi-Fiの設定をします。
お家でいつも使っているやつを設定してあげると、Googleアカウントの入力ができるようになります。

## Google アカウント
特段書くことはないのですが、まあ、普通にID/PWDを入れれば完了です。
豆知識としては、@gmail.comのアカウントであれば、@の左側だけを入れても設定できます。
（たとえば``your-account@gmail.com``であれば、``your-account``と入れれば完了）

# 起動
普通にブラウジングするだけであれば、もうこれで使えます。
ChromeOS Flexを使ってた人や2台目以降の人であれば、シェルフが他のChromebookで使っていたのと同じショートカットが並び、今までと同じ使い心地で使えるようになっているはず。

しばし使っているとOSのアップデートがなされ、再起動しろとのメッセージが出るので再起動します。
っで、最新版になります。

## 追加設定
Dropboxに書類（メモ書きのMarkdownなど）を置いとくことが多いので、
Chromebookでも直接読めるようにしました。
具体的には
[File System for ChromeOS](https://chrome.google.com/webstore/detail/file-system-for-dropbox/hlffpaajmfllggclnjppbblobdhokjhe?hl=ja)
を追加します。


# Linux環境追加
主な作業は、ターミナルを追加して、パッケージのアップデートとかして、VS Codeを追加するってだけです。

Chromebookのバージョンによっては状況も変わるかと思うので、ひとまず今の環境を記載します。
本体
FUJITSU FMV-Chromebook 14F FCB141FB
Intel Tiger Lake Chromebook (chronicler、バージョン 117.15572.0)
![Screenshot 2023-10-22 13.17.10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3248470/06a7e9fc-3f6e-c26c-b9e7-231e24b3bf37.png)


## ターミナル追加
Linux開発環境をオンにする
![Screenshot 2023-10-22 10.57.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3248470/59aeb6d0-b7ca-a78a-4ef6-3d26d8769698.png)

セットアップが開始されるので、眺める
![Screenshot 2023-10-22 10.58.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3248470/edda2dbf-0b98-e944-eb71-6c2b68bfbe9a.png)

名前を入れろと言われるので入力
※途中で気が変わってユーザー名はhamaにしました
![Screenshot 2023-10-22 10.59.36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3248470/1fba55ab-ca48-f00c-6190-6d15fd9a0d87.png)

眺める
![Screenshot 2023-10-22 11.00.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3248470/0ba3cf26-4dd7-75f8-0232-dd84aa7c6089.png)

![Screenshot 2023-10-22 11.01.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3248470/0a131e00-5434-217a-11ff-76aa313db22f.png)

手元の環境だと一分ぐらいでセットアップ完了しました。
終わるとターミナルが立ち上がります。
![Screenshot 2023-10-22 11.02.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3248470/2815121e-f10e-b5cf-f363-e690e99055bd.png)


### Linux環境の更新など
```bash
 sudo apt update && sudo apt -y full-upgrade && sudo apt -y autoremove && sudo apt -y autoclean
```
複雑そうにも見えますが、ちょっとだけ見方がわかると簡単な内容です。
文法的には```&&```が入ることで、その処理が終わったら次の処理をやってね、って書き方で、
```command_a && command_b```というのがあるとすると、
```command_a```の処理が全部終わったら、```command_b```を実行してね。という意味になります。
今回書いた内容は```apt```[^apt]というパッケージ管理ツールでで```update```と```full-upgrade```、
それに```autoremove```の上で```autoclean```を```sudo```で実行してねって内容で、
入っているコマンド（パッケージ）のリストを最新版にして、
すべてを最新版にしたうえで、不要なパッケージは削除、
念のために依存関係上不要になったものも消しといてという４コマンドを一行にしたものです。
おそらく、やっていることが冗長なのですが、
半分趣味なので私はこれで良しとしています。

[^apt]: [apt と apt-get - Linux のパッケージ管理ツール間の違い - AWS](https://aws.amazon.com/jp/compare/the-difference-between-apt-and-apt-get/)

### VS Codeのインストール
下記の記事で別立てでまとめてあります。
よろしければご一読ください。
https://qiita.com/hama-caffeine/items/42674f563a271f4c16f3
DynamoDB + Railsで投稿のもっと見るボタンを作る

# 0. はじめに
SNSツールを作るにあたり、key-valueストアであるDynamoDBを選択した。これは、大量のデータの中から任意のキーで検索をかけることで、より高速にデータを取得できるDBになっている。今回はRailsアプリを使って実装した内容を書いていく。

# 1. Dynamo DBの設計
RailsのgemであるDyanmoidを利用して、DynamoDBにデータを保存する。
その際、hasy_keyを`timeline_id`に設定した。hasy_keyはデフォルトでユニークな文字列型となっている。

```
module Dynamo
  class Timeline
    include Dynamoid::Document

    table name: :timelines, key: :timeline_id, capacity_mode: :on_demand, timestamps: false

    field :body, :string
    field :created_user_id, :integer
    field :process_at, :integer, default: -> { Time.zone.now.to_i }
  end
end
```

https://github.com/Dynamoid/dynamoid

# 2. GSIの設定
DynamoDBではhash_keyに指定した属性以外で検索をかけると、SCAN操作（DB全体の検索）になってしまう。
そのため、QUERY操作（hash_keyでの検索）になるように、GSI(Global Secondary Index)
を設定する。
今回はTOPページで全ての投稿を表示するため、created_user_idなど、固定値のhash_keyが存在しない。そのため、固定値である`SORT_HASH_KEY`をhash_keyとして新たに設定することでQUERY操作を実現した。
なお、range_keyはItemの並び替えに使われる。
```
    SORT_HASH_KEY = 1

    field :sort_hash_key, :integer, default: SORT_HASH_KEY

    global_secondary_index hash_key: :sort_hash_key, range_key: :process_at,
                           name: :sort_hash_key_process_at_index, projected_attributes: :all
```

https://docs.aws.amazon.com/ja_jp/amazondynamodb/latest/developerguide/GSI.html

# 3. 指定した値分の投稿を取得
まずは、初回リクエストでper_page分だけ、投稿を取得するメソッドを実装。
最後に評価した値（last_evaluated_key）をreturnする必要があるため、`find_by_pages`メソッドを使う。
なお、`scan_index_forward(false)`メソッドで、range_keyの降順に並び替えられる。
今回は最新の投稿から取得するように実装している。
```
    class << self
      def next_page_of_timelines(per_page)
          paginate_items(by_sort_hash_key, per_page)
      end

      private

      def by_sort_hash_key
        where(sort_hash_key: SORT_HASH_KEY).scan_index_forward(false)
      end

      def paginate_items(chain, per_page)
        chain.record_limit(per_page).find_by_pages.first
      end
    end
```

# 4. もっと見る機能の実装
続いて、2回目以降のリクエスト時には、`last_evaluated_key`がparamsとして渡されるので、`last_evaluated_key + 1`番目から、per_page分の投稿を取得する。
そのため`start_key`メソッドを活用して、取得する最初の値を特定する。

```
    class << self
      def next_page_of_timelines(timeline_id, process_at, per_page)
        if timeline_id.blank? && process_at.zero?
          paginate_items(by_sort_hash_key, per_page)
        else
          paginate_items(start_key(by_sort_hash_key, timeline_id, process_at), per_page)
        end
      end

      private

      def by_sort_hash_key
        where(sort_hash_key: SORT_HASH_KEY).scan_index_forward(false)
      end

      def start_key(chain, timeline_id, process_at)
        chain.start(timeline_id:, process_at:, sort_hash_key: SORT_HASH_KEY)
      end

      def paginate_items(chain, per_page)
        chain.record_limit(per_page).find_by_pages.first
      end
    end
```
これで、DynamoDBから任意の値を適切に取得するRails側の実装ができる。
なお、`created_user_id`など、固定のhash_keyがあれば、今回のように`SORT_HASH_KEY`のような固定値をわざわざ設ける必要性がなくなる。