OmniAICreator/Qiita-1.07M huggingface dataset:
commit時に含まれて欲しくない文字列を検査して入っていたらコミットさせない方法(git hook)

# はじめに
config.ini等の設定ファイルに保存された値(パス等)でコミットしたくないものを誤ってコミットする前に気づけたらいいなと思って作りました。

# 環境
Windows 11
Powershell 5.1

# 手順

※以下の手順はすべて仮想環境上で行っています
```
mkdir pre_commit_test
cd pre_commti_test
python -m venv .venv
.\.venv\Scripts\activate
git init
```

## 1. pre-commitファイルの作成
```
new-item .git/hooks/pre-commit 
```

```.git/hooks/pre-commit
#!/bin/bash

# 1. git addされたファイルを取得
added_files=$(git diff --name-only --cached)

# 2. config.iniからkey=valueのvalueの値を取得
target_value=$(grep '=' config.ini | awk -F'=' '{ print $2 }')

# 3. 1で取得したファイル内のコードから2で取得した値と一致する値を表示する
exit_code=0
for file in $added_files; do
    for v in $target_value; do
        
        row_nums=$(grep -n $v $file | awk -F ':' '{ print $1}')
        
        if [ "$row_nums" ]; then
            # git add されたファイル内にconfig.iniで定義された値が存在している=エラー
            exit_code=1
            echo "- should not exist value: $v"
        fi
        for row_num in $row_nums; do
            echo "row: ${row_num}"
        done

    done
done

if [ $exit_code = 1 ]; then
    echo -n -e "\n"
    echo "- status     : Check information that should not exist ..... Failed"
    echo "- description: Checks if there is information that should not be present in the file"
    echo "- exit code  : ${exit_code}"
    echo -n -e "\n"
    echo "File has been refused commit to this hook"
else
    echo "- status     : Check information that should not exist ..... Passed"
    echo "- exit code  : ${exit_code}"
fi

exit $exit_code
```

## 2. config.ini 作成
```config.ini
[Test1]
key1=string1
key2=string2
key3=string3

[Test2]
key4=string4
```

## 3. sample.py 作成
```sample.py
"test"
"test"
"test"
"string1"
```

## 4. コミットする
```
git add sample.py
git commit -m "pre-commit test sample.py"
```

## 結果

```
- should not exist value: string1
row: 4

- status     : Check information that should not exist ..... Failed
- description: Checks if there is information that should not be present in the file
- exit code  : 1

File has been refused commit to this hook
```

"string1"という文字列が4行目に存在するのでコミットができなくなっている

なので、エラー文字列をsample.pyから削除して再コミットしてみる

```sample.py
"test"
"test"
"test"

```

↓再実行結果　無事コミットされている。
```
- status     : Check information that should not exist ..... Passed
- exit code  : 0
[master (root-commit) 0dba68e] pre-commit test sample.py
 1 file changed, 1 insertion(+)
 create mode 100644 sample.py
```

# おわりに
シェルスクリプトは書き慣れていなかったのですが、chatGPTで調べたらめちゃくちゃいい感じのコマンドが出力されたのでそれをうまいこと使って作りました。
それでも、あまりきれいな感じではないですが、、
単純なスクリプトを考えるときはchatGPTを使いまくろうと思いました。
ROS2 + pipenv install

【[ROS2関係トップページへ](https://qiita.com/NeK/items/7ac0f4ec10d51dbca084)】

# 概要
Pythonにはpipenvだね([自分とこの環境下におけるPython使用ルール](https://qiita.com/NeK/items/6456664cde0ec125368a))，というわけでROS2+pipenvを使ったらモジュール関係でハマったので記載．

# 現象1
## 何が起こったのか

`pipenv install`でモジュールをインストールし，ROS2で使用しようとすると「module not found」が出てしまう．

## 原因はなんだったのか

`pipenv shell`で仮想環境に入ると，その仮想環境内でインストールしたmoduleは当然見える．具体的には「その仮想環境のディレクトリ/lib/Python*.*/site-packages/」に入っていて，ちゃんと見えている．ちなみに「その仮想環境のディレクトリ」は`pipenv --venv`で確認できる．
しかし，ROS2の実行環境ではこれが見えていない．別環境になっている？
どうもROS2は仮想環境外のpythonを使っていて，仮想環境のパッケージのディレクトリが見えなくなっているっぽい．

## どうすりゃいいのさ
とりあえず力技で解決．`$PYTHONPATH`に仮想環境のディレクトリをつけてしまえ．ただ，仮想環境のディレクトリは仮想環境ごとに違うので流石に少しだけ工夫．

### 解決用ファイル

```py: setup_for_pipenv.py
import os
import glob
import subprocess

venv_dir = subprocess.run(['pipenv','--venv'], capture_output=True, text=True).stdout.replace("\n","")
#print(venv_dir)
search_dir = venv_dir + "/lib/*/site-packages/"
#print(search_dir)
site_dir = glob.glob(search_dir)
#print(site_dir)
pythonpath = os.environ['PYTHONPATH']
#print(pythonpath)
for each_dir in site_dir:
	pythonpath += ":"
	pythonpath += each_dir
print(pythonpath)
```

```bash: setup_for_pipenv.bash
#!/bin/bash
python_path=$(python3 setup_for_pipenv.py)
export PYTHONPATH=$python_path
```

### 解決用ファイルの使い方
新しいターミナルを起動するごとに以下を実行．要はpipenvで仮想環境に入ったら，最初の一回bashファイルを実行する．

```bash
cd ${対象のディレクトリ}
pipenv shell # 仮想環境にはいりましょう
source setup_for_pipenv.bash
```

適当すぎて恥ずかしいので，よりスマートな方法もとむ．

### pipenvのscriptsを利用
色々コマンドうつのが面倒な場合，pipenvのscriptsを利用する手もある．
例えば，ros2の実行を`ros2 launch test test.py`だとすると，Pipfileのscripts,それを使ったpipenvは以下のようになる．

```py: Pipfile
[[source]]
...


...
[scripts]
start = "bash -c '. install/local_setup.bash && source setup_for_pipenv.bash && ros2 launch test test.py'"

```

```bash
$ pipenv run start
```

これで仮想環境に入り，諸々実後までしてくれる．

* 参考
    * [pipenv install したライブラリとそれらのimportについて](https://qiita.com/musatarosu/items/394603957ae41552718e)
    * [Pipenvのscriptsで複数のコマンドを実行させる](https://qiita.com/godan09/items/7aa0a256e8473d766de5)


# 現象2
## 何がおこったのか
C++とpythonを混合していて(例えばノードはpython，メッセージも作成=C++)，仮想環境内でC++のコンパイルをするとき，catkin_pkgがないと怒られる．

```shell: error message
ModuleNotFoundError: No module named 'catkin_pkg'
```

## 原因はなんだったのか
ROSはシステムのPythonを使う．基本，仮想環境は対応していない，らしい([ros answers](https://answers.ros.org/questions/)などに同様のエラーについていくつかポストがある)．

## どうすりゃいいのさ
仮想環境内で必要なモジュールをいれれば動く．

### 解決策

```shell
pipenv install catkin_pkg empy lark
``` 

## 蛇足
### モジュール
他にもあったほうがいいかも？ってモジュールとして

- pandas
- matplotlib
- numpy

などがある．使うなら同じように `pipenv install`でインストール．

### pipenv時のpython version
できればpyenvでインストールしたものではなく，systemつかうといいかも？
なぜならROS2はsystem(OSのやつ)を使うから．

ただ，Pipfileの中に使っているpython version書かれるのでどうしたものか？
SSISとSSDTの違い

## この記事で分かること
SQL Serverを扱うにあたり、SSISとSSDTの違いがいまいち理解できていなかったので、整理してみました。

## 参考

https://deliv.tech/2021/07/21/%E3%80%90etl%E3%83%84%E3%83%BC%E3%83%AB%E3%80%91%E3%80%80%E8%AA%AD%E3%82%80%E3%81%A8%E5%88%86%E3%81%8B%E3%82%8B%EF%BC%81ssis%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%A6%E3%81%BF%E3%81%A6%E6%B3%A8%E6%84%8F/

## SSISとは
:::note info
SQL Server Integration Servicesの略で、Microsoft社のデータベース管理システム「SQL Server」に付随するETLツールの1つ
:::

Microsoft SQL Serverを購入したときに同梱されているツールです。

「ETLツール」ということなので、データベースやフラットファイルからデータを加工し、結果をファイル出力したり、SQL Serverに取り込んだりできます。

SSISパッケージの開発では、`Microsoft Visual Studio`と拡張機能の`SSDT`を用いて行います。

### SSISパッケージの一例
SSISパッケージの一例として、
　① フラットファイルを取り込み、
　② 何らかの加工を行い、
　③ SQL Serverのとあるテーブルに出力する。
というETLプロセスをSSISで作ってみました。

SSISでは、単純なデータの取り込み（上記例だと①と③だけ）であれば、ノーコードでデータの移行が可能です。後述のSSDT ToolBoxから、用意されたタスクをドラッグアンドドロップし、設定するだけでOKなのです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2601070/85fc1203-637b-5729-5961-be812278a248.png)

## SSDTとは

:::note info
SSDTとは、SQL Server Data Toolsの略で、次のような機能を持つ
- SSISパッケージをゼロから開発する。
- 既存のSSISパッケージを編集できる。
:::

SSISの項目で少し出てきましたが、SSDTとは、`SSISを開発するためのツール`のことです。
もう少し詳しく書くと、Microsoft Visual Studioの拡張設定として、`SSDT`を選択しインストールすることで、SSISの開発を行うことができます。

SSISパッケージの開発画面を以下に示します。
`SSIS Toolbox`ペインにタスクのアイコンを選択し、中央の`デザイン部分`にドラッグアンドペーストすると、タスクを定義できます。

基本的にはフローチャートのように、1つ1つのタスクを矢印で結び、矢印の方向にタスクが進んでいくという流れになります。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2601070/78d65636-3e19-cc34-d89b-361bf8107b65.png)

ちなみに、上図の`SQL実行タスク`では、接続先データベースを指定し、実行するSQLを入力することで、任意のテーブルから加工したデータを抽出することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2601070/b2362263-a027-109d-0512-d65c4461c219.png)

また、上図の`データフロータスク`では、前工程で抽出したデータを変換したり、その結果を送り先（任意のデータベースやファイル）に送るなどの細かいプロセスを定義できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2601070/e294cdb7-4f49-7a25-d4c0-35d3d902d66d.png)

## まとめ
SSISとSSDTについてまとめてみました。

SSISは、Microsoft SQL Serverに付随するETLツールのことで、SSIS開発を行うためのツールがSSDTということですね。

SSDTには、まだまだ触ったことのない制御タスクがあるので、すでに出来上がったSSISパッケージを眺めて、1つずつ理解したいと思います。
メソッドについて

<h1>Rubyとは</h1>
日本人が作ったプログラミング言語で初心者の方にも分かりやすい言語になっています！
できる事は主にWebアプリ制作になります！
Webアプリとはアマゾンや、食べログなどインターネット上で使うシステムの事です！

<h2>メソッド（関数）を出力</h2>
<font color="red">メソッド(関数)</font>とは、プログラムの別の場所から呼び出せる処理のまとまりの事です。
メソッドは呼び出されるまで、実行されません。

```ruby:paiza.io
#engineerがメソッド名
#userが引数
  def engineer(user)
    puts "#{user}は基礎を学んでます" #defからendまでメソッド
  end

p engineer("西野") #引数="西野"
```
⇓

```ruby:出力結果
西野は基礎を学んでます
```

- defは、メソッドを作る時に行われて、engineerはメソッド名になります。

- カッコの中(user)は<font color="red">**引数**</font>と言います。
引数はメソッドを呼び出すときにメソッドの外から代入する事ができる変数です。

- engineer("西野")がdefとendの外にあり、engineerの()内の"西野"がuserの引数に当たる所に代入されて、engineer(user)メソッドが呼び出されるという流れ

- engineer(user)というメソッドが呼び出されて、def engineer(user)とendに挟まれている部分の処理が実行される

※メソッドは呼び出さないと処理は実行されません。

<h3>数字を引数に代入した場合</h3>

```ruby:paiza.io
#engineerがメソッド名
#moneyが引数
  def engineer(day)
    puts "基礎を学んで#{day}日目" #defからendまでメソッド
  end

p engineer(3)

```
⇓

```ruby:出力結果
基礎を学んで3日目
```

数字の場合は""（ダブルクォテーション）←がいらなくなります。

<h2>メソッドの引数について</h2>
メソッドに引数がない場合は、引数なしという形になり、空の()は必要ありません。

```ruby:paiza.io
#foodがメソッド名
  def food
    puts "基礎を学ぶのは為になる"
  end

p food

```

```ruby:出力
基礎を学ぶのは為になる
```

複数の引数を受け取る場合はカンマで区切って、複数持たせる事ができます。
呼び出すときもカンマで区切ぎります。

```ruby:paiza.io
#dreamがメソッド名
  def dream(how, want)
    puts "#{how}彼女が#{want}"
  end

dream("可愛い", "欲しい")

```

```ruby:出力
"可愛い彼女が欲しい"
```

引数のデフォルト値というのもあります。
⇓

```ruby:paiza.io
#dreamがメソッド名
  def dream(how="美人な", want="欲しい")
    puts "#{how}彼女が#{want}"
  end

dream
dream(nil, "凄く欲しい")
dream("可愛い", "欲しい")

```

```ruby:出力
美人な彼女が欲しい
彼女が凄く欲しい
可愛い彼女が欲しい
```

<h2>キーワード引数</h2>
キーワード引数とは引数に名前をつける機能の事を言います。

```ruby:paiza.io

  def nishino_age(age: 29)
    puts age
  end

nishino_age
nishino_age(age: 30)
```

```ruby:出力
29
30
```

<font color="red">**引数: 値　で:(コロン)**</font>
を引数の後につけて値を代入する事をキーワード引数と言います。
上記で言うと
def nishino_age(age: 29)の、age:の部分です

メリッドは引数の意味が見た目で分かりやすくなります！
この場合ですと、ageをキーワード引数として渡しているので年齢を渡しているんだな！
と分かりやすくなっています！

<h2>可変長引数</h2>
1つの引数で、複数の値を配列で受け取る事ができる引数を可変長引数と言います

```ruby:paiza.io

  def place(*i)
    puts i
  end

place(1,2,3)
place("千葉","神奈川","東京")
```

```ruby:出力
1
2
3
千葉 
神奈川
東京
```

<h2>オプション引数</h2>
１つの引数で復習の値をハッシュで受け取る引数をオプション引数と言います

- hash値はキーとバリューの組み合わせでデータを表現できる事を言います{:キー=>バリュー}

```ruby:paiza.io

  def country(**japan)
    puts japan
  end

country(place1: "千葉", place2: "神奈川", place3: "東京")
```

```ruby:出力
{:place1=>"千葉", :place2=>"神奈川", :place3=>"東京"}
```

```ruby:paiza.io

  def country(**japan)
    puts japan.keys
    puts japan.values
  end

country(place1: "千葉", place2: "神奈川", place3: "東京")

```

```ruby:出力
place1 
place2
place3
千葉
神奈川 
東京
```
【備忘】Gitコマンド一覧

## はじめに
AWS CodeCommitを使い始めて、Gitコマンドによる操作を行うことが多くなりました。
よく使うGitコマンドを備忘としてまとめます。

## コマンド一覧
|コマンド                              | 内容                                                  |
|:------------------------------------|:-----------------------------------------------------|
|git clone <リモートリポジトリURL>        |対象のリモートリポジトリをローカルにクローン                  |
|git branch                            |ローカルブランチの一覧を表示                               |
|git checkout -b <ブランチ名> origin/<リモートブランチ名> |リモートブランチから新しいブランチを作成してそのブランチに切替 |
|git push -u origin <ブランチ名>        |リモートリポジトリへプッシュして上流ブランチに設定             |
|git status                           |更新状態を確認                                           |
|git add <ファイル>                    |差分をインデックスに登録                                   |
|git commit -m “コメント”              |ローカルリポジトリへコミット                                |
|git push origin <リモートリポジトリ名>  |リモートリポジトリへプッシュ                                |
|git fetch origin <リモートリポジトリ名> |リモートリポジトリの内容を取得                              |
|git merge origin/<リポジトリ名>        |リモートリポジトリから取得してきた内容をローカルリポジトリへ同期 |
|git diff <ブランチ名> <ブランチ名>       |指定したブランチ間の差分を確認                             |


## 最後に
他にもたくさんGitコマンドはあるので、今後使っていく中で使用頻度が高くなったものを追記していこうと思います。

## 参考リンク
* [Gitコマンドリファレンス](https://qiita.com/papi_tokei/items/c9615d618277b5167b79)
SuiteCRMのJSON APIを利用可能にする

SuiteCRMはAPIを持っており、外部システムからアクセスしてデータを取ってきたり、逆に更新ができたりします
もちろんそのためには相手先のシステムを認証する必要があるのでOAuth認証の設定が必要になります

# OAuth認証設定手順
## 大まかな流れ
詳しくは以下に書かれています(英語です)

https://docs.suitecrm.com/developer/api/developer-setup-guide/json-api/#_before_you_start_calling_endpoints

流れとしては以下の通り
1. opensslのSuiteCRMサーバへのインストール
1. SuiteCRMサーバでのキーペアの生成
1. SuiteCRM管理画面でOAuth認証用のIDを生成
1. アクセス元サーバでのアクセストークン取得リクエストのコードを記述
1. アクセストークンを用いてSuiteCRMのAPIを利用する

## アクセス許可の方式の選択
SuiteCRMのOAuthでは以下の二つの方法でアクセストークンを取得できます

https://docs.suitecrm.com/developer/api/developer-setup-guide/configure-authentication/

1. SuiteCRM内のアカウントのIDとパスワードを用いてトークンを生成する方法
1. 独自のクライエントシークレットをSuiteCRMのアカウントに紐付けして、そのシークレットを利用してトークンを生成する方法

どちらを設定するかはSuiteCRMの管理画面でOAuth認証用のIDを生成する際に設定できます。

# SuiteCRM、PHP、JWTのバージョンの問題
SuiteCRMの最新版はPHP8で動きますし、Dockerで提供されているImageもphp8ベースで提供されています。
しかし、SuiteCRMが利用しているJWTのバージョンが古いためにPHP8ではエラーが起きるというバグが発生しているようです。

https://github.com/salesagility/SuiteCRM-Core/issues/185#issuecomment-1364708097

## 対策方法
composerでlcobucci/jwtのバージョンを4.0.0以上に上げればいいようなのですが、lcobucci/jwtのバージョンを上げると、league/oauth2-serverもバージョンを上げる必要があります
league/oauth2-serverのバージョンも上げると今度はSuiteCRM側のコードでDeprecatedエラーが発生します

ってことで仕方なく、SuiteCRMを起動しているDockerイメージをBitnamiが提供しているものからphp7.4-apacheに変更して対応することにしました

# まとめ
約半日ほど調査と対応に追われてしまいましたがなんとかアクセストークンが発行され、APIを利用できるようになりました。
SuiteCRMの公式ドキュメントを参考にしながら、色々やってみたいと思います。
オブジェクト指向について

<h1>オブジェクト指向とは</h1>
オブジェクト指向とは、プログラムを構成するための考え方です。
オブジェクト（もの）同士の相互作用とみなします。

- リモコンでテレビを操作するイメージ
- 自動車というオブジェクトを作りたい場合、ガソリンやブレーキなどの要素が必要です。また、具体的にどのタイミングでブレーキをかけるか、どれくらいの速度で構成するのか、などの命令を加えることで、「モノを動かす」ことができます。

<h2>Rubyのオブジェクト指向</h2>
Rubyはオブジェクト指向型の言語です。

- 「従来の複雑で難しいプログラミング言語を効率的なソフトウェア開発のために考えられた概念」となります。

- 「モノ」のことでさまざまな複雑なデータを1つのモノとし、複雑なプログラミングをオブジェクトにまとめた開発形式です。

<h3>Rubyにおけるオブジェクトの種類</h3>

Rubyのデータは、全てがオブジェクトです。オブジェクトにはさまざまな種類があります。代表的なものは以下の通りです。

- 数値オブジェクト
- 文字列オブジェクト
- 配列オブジェクト
- ハッシュオブジェクト

<h3>オブジェクト指向の使い方</h3>

Rubyでオブジェクト指向を使うには、以下の重要な3つの概念を理解する必要があります。

- クラス
- インスタンス
- メソッド

<h4>クラス</h4>
クラスとは、オブジェクトの設計図のことです。全てのオブジェクト指向は何らかのクラスに属しています。オブジェクト指向は役割と命令がなければ、自分で動けません。

- オブジェクトで「たい焼き」を作りたい場合、たい焼きの形を作る「たい焼き器」（設計図）になる
 
<h4>インスタンス</h4>
インスタンスとは、クラスを具象化したものです。クラスという設計図を元にして、製造されたものがインスタンスです。

- クラスが「たい焼き器」だとすると、インスタンスは「たい焼き」となります。

<h4>メソッド</h4>
オブジェクト指向でのメソッドとは、クラスに命令する動作のことです。

- たい焼きでいうと「味」や「値段」の情報をクラスに命令する動作。

![rails_class_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/630834/3794995c-9326-815d-dd5d-690f348e61d0.png)

<h2>オブジェクト指向のメリット</h2>
オブジェクト指向には、さまざまなメリットがあります。大別して、以下の3つです。

- プログラムの安全性が高い
- プログラムの再利用が高い
- プログラムをテストしやすい


参考記事

オブジェクト指向
https://www.sejuku.net/blog/124336

【Ruby】 たい焼きで理解するオブジェクト指向におけるクラスの概念
https://pikawaka.com/ruby/class
英語の動画に日本語の字幕をつけて勉強する気持ちになった

:::note warn
自分のためのメモとして書きなぐっているので全然まとまっていません
別の勉強についての記事を書くはずが手前のところでかなり躓きました
同じように悩んでいる人がいればなにかの助けになれば幸いです
:::

### 始まり
とある日にぼんやりと学習動画を眺めていると喋っている内容を知りたいのに英語なので『何言ってるかわからないな』と思い、そうだ英会話をやろうと考えオンライン英会話に半年入会していましたが半年で2回しか利用しなかったのでようやく重い腰を上げて休止しました。
しかし英語のチュートリアルを紐解くために何度も動画を止めたり自動生成される字幕の間違いにより学習が進まなかったりしてどうしたものかと落ち込んでいましたが、自分で聞き取れないならwhisperなどにやってもらえばいいんじゃないかと考え最近のAIの恩恵を受けることにしました。

### Whisperへアクセス
参考にさせていただいたWhisperの記事を以下にはらせていただきます。

https://qiita.com/diesekiefer/items/00d8c1507829b58a62ab

https://zenn.dev/miyataka/articles/getting_started_whisper-cpp

https://zenn.dev/k41531/articles/ee46ef1d36a041

手元の環境がwindows10/windows11/M1Mac とバラバラでチュートリアルどおりに構築してそのまま動いたのは一番古いwindows10の環境だけでした。
ただ調子が悪いのと、1080tiでも特に早くなかったのでどうしたものかなと思っていました。
macは未だに zsh: command not found: whisper と出ますし、win11も同じ状況です。
現状pipでのアクセスは諦め、M1Macにcpp版を入れて使っています。
なんか異常に速いし字幕であればオプションに-ovtt をつけるだけでyoutube向けの字幕フォーマットを出してくれるのでこれがいいやと思っています

```text:cpp版whisperへのアクセス
% ./main -m models/ggml-small.bin -l en -ovtt -f 音声データへのパス
```
もともとの講師の喋りがなまりの強い英語だったのですが他にノイズがないこともあり、smallでも精度はいいと思います。


### 文字起こされる音声データ及びffmpeg

使わせていただいた記事

https://fukatsu.tech/install-ffmpeg

https://qiita.com/fujisystem/items/0b3abb5cb1a5671c4d4c

cpp版は自分が調べた限り、16kのwavでないと取り扱ってくれないのでffmpegで変換しています。
ffmpegについてもなんだか挙動がおかしく、コンソールで叩いても
```text:エラー
zsh: command not found: ffmpeg
```
とか出る様だったので、homebrewをアップデートして、
```text:Homebrewを最新に
brew update
```
ffmpegを特に消さずに無理やりインストールしたところ動きました。
```text:ffmpegインストール
brew install ffmpeg
```
多分正しいやり方はあると思うので教えてください。
インターネッツに存在する動画ファイルの現代の音声形式はm4aが割りと一般的らしいことを知りました。
```text:音声フォーマット変換
% ffmpeg -i 元音声データ.m4a -ar 16000 書き出し先とアプトプット名.wav
```
で今のところ一つずつ変換しています。

### 映像データの私的利用について

参考にさせて頂いた記事。

https://qiita.com/tenuteto/items/c2f1978b5c00c3ffb9ac

映像データなどはyt-dlpを利用しています。
```text:動画情報取得
% yt-dlp -F URL
```
で、URL先が所有しているフォーマットが全部見れるのはすごくいいなと思っています。

### 残ったのは英語
手元に動画ファイル、字幕ファイルが揃ったのでDeepLで翻訳すればいいじゃんねと思いましたが無料版だと5000文字という限定されたテキストだったことを思い出して課金しました。
デスクトップ版を用いて翻訳しています。
API使ってコマンドラインからとも思いましたが、結構特殊な用途の動画なので手元で辞書ちまちまつくるかという気持ちのもとコピペしています。

書き出して出来たvttファイルをgoogleDriveに動画とともにアップロードします。
ドライブ上で英語及び日本語の字幕をつけられて便利です

### （蛇足）読み上げさせたい 以降はwindowsの処理
棒読みちゃんなどで読み上げたものを動画にくっつけるかと思いました。

https://booth.pm/ja/items/3717520

v176が欲しい方は以下

https://machanbazaar.com/ダウンロード-2/

そこでサウンドライナーというものの存在を知り、voicevoxやcoeiroinkなど様々な合成音声で読ませる事ができるというソフトウェアを知りました。
ただ、ここではvttは対応していなかったのでsrtというフォーマットに変換する必要があり、これはウェブにある怪しいサイトを使って変換しました。
多分このへんはフォーマットが違うだけなので自分で書けばいいところな気はします。

サウンドライナーを立ち上げ、その後に任意の読み上げソフトを立ち上げなさいと書かれていたので作用になりました。
TTS（text to speach）というwindowsの機能を使っているよなのですが不勉強なのでわかりませんでした。
発話スピードや細かいニュアンスについては操作できないように感じたので、できるようなら教えていただけると助かります。
最近バージョンが改定されていてv174あたりから尺をwavにあわせる形で書き出して（ミックスダウンして）くれるので編集ソフトなどで音声を差し替えるなり加えるなりをすればよいかと思います。

最後にDeepLがかなりのロマンチストであることを皆様にお教えできればと思います。
```text:詩的なDeepL
So all the particles are here on the center and then you have just once and life in tonight.
というフレーズを
つまり全ての粒子はこの中心にあり...そして今夜は一度だけ生命が誕生します。
と翻訳します
```

すべてのツールの作者様にお礼いたします。
これで・・・、英語を学ばなくても世界中のソースをずんだもんが日本語で喋ってくれるようになるのだ。

https://voicevox.hiroshiba.jp

https://coeiroink.com

***推しはCOEIROINKのツクヨミちゃんです。***
paizaラーニング レベルアップ問題集 二分探索メニュー JavaScript upper_bound

upper_bound (paizaランク B 相当)

https://paiza.jp/works/mondai/binary_search/binary_search__basic_step2

# 注釈
解答例が２つありますが、解答例２の方が、lower_boundと関数を共通で使えるので、より好ましいと思われます。

# 解答例
STEP: 2 lower_bound　の"未満"が、本問題では"以下"になったので、そこだけ修正します。

```javascript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");

const n = Number(lines[0]);
const A = lines[1].split(" ").map(Number).sort((a, b) => a - b);

//二分探索の関数定義
/**
    A_i > k を満たす最小の i を返す
    A_i > k を満たす i が存在しない場合は n を返す
*/
const binary_search = (A, n, k) => {//(A : 数列, n : 数列のサイズ, k : 基準)
    // 探索範囲 [left, right]
    let left = 0;
    let right = n;
    
    // 探索範囲を狭めていく
    while (left < right) {
        // 探索範囲の中央
        mid = Math.trunc((left + right) / 2);

        if (A[mid] <= k) {
            // A[0] ~ A[mid] は k 以下なので調べる必要が無い
            left = mid+1;
        } else {
            right = mid;
        }    
    }
    // 狭め終わったらmin_iを返す
    return right;
};

//各kについて調べる
const q = Number(lines[2]);
for (let i = 1; i <= q; i++) {
  const k = Number(lines[i + 2]);
  const min_i = binary_search(A, n, k);
  console.log(n - min_i);
}
```

# 解答例2
また、k点より大きい点数k+1を基準として、その時の生徒の数を求めることもできます。
```javascript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");
//二分探索の関数定義
const binary_search = (A, n, k) => {//(A : 数列, n : 数列のサイズ, k : 基準)
    // 探索範囲 [left, right]
    let left = 0;
    let right = n;
    
    // 探索範囲を狭めていく
    while (left < right) {
        // 探索範囲の中央
        const mid = Math.floor((left + right) / 2);//切り下げ

        if (A[mid] < k) {
            left = mid+1;
        } else {
            right = mid;
        }    
    }
    // 狭め終わったらmin_iを返す
    return right;
};

//メイン
const n = Number(lines[0]);
const A = lines[1].split(" ").map(Number).sort((a, b) => a - b);
const q = Number(lines[2]);

//各kについて調べる
for (let i = 1; i <= q; i++) {
  const k = Number(lines[i + 2]);
  const min_i = binary_search(A, n, k + 1);//k+1はkより大きい数
  console.log(n - min_i);
}
```
各サーバーは分離すべきなのか？ メリット・デメリットまとめ

## きっかけ
　とある案件に携わった時に、APサーバーとDBサーバーは分けても分けなくても良いですねと話しているのを聞いて、「そうなんだ！？」と驚いたので調べてみようと思い立ちました。ネットで調べて情報をまとめました。

## 【前提】Web三層構造について
Webサーバ、APサーバ、DBサーバの3層の事。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072159/1a079ab4-9fe7-28fc-5728-e964bedba607.png)
（参考サイト：https://www.infraexpert.com/info/server17.html）

#### WEBサーバーの役割
静的ページのデータやAPサーバーから転送された動的ページのデータをWebクライアントに転送すること。

#### APサーバーの役割
Webサーバーから送られてきたユーザーからのデータを受け取り、サーバーサイド・プログラムを実行することでそのデータを加工したり、DB(データベース)のデータを検索・加工した後、Webサーバーにレスポンスを返す。

#### DBサーバーの役割
アプリケーションサーバーからの要求に従って処理を行い、結果をアプリケーションサーバーに返却すること。



## サーバーを別立てするメリット
①負荷分散：１つのマシンに負荷がかかり過ぎないようにする
②リスクヘッジ：サーバーが動かなくなるリスクを分散
③セキュリティの高さ
> DBサーバには多くの顧客情報が保存されており、万が一この情報が流出してしまうと大問題になります。Web、AP、DBがすべて1台にインストールされたシステムではクライアントから直接DBサーバにアクセスできてしまいますが、Web3層構造ではクライアントとDBサーバの間にWebサーバ、APサーバ、そしてセキュリティ製品を配置できるため、より堅牢になります。

④視認性の高さ
>使用するハードウェアリソース(CPU、メモリ、ディスクI/O、ネットワークI/O)の論理的分割ができる。障害ポイント(APソフト的なのか、DBソフト的なのか)が区別しやすくなる。

## サーバーを別立てするデメリット
①金銭的コストがかかる
>(論理的な)ハード費用に加えて、AP、DBのソフト的な処理も考慮する必要があり、改修費用を見込む必要がある。ライセンスや保守のコスト増加。

②運用コストがかかる
③ネットワークのオーバーヘッドが発生する

## まとめ
回収できるお金/サービスの重要性とコストを天秤にかけて判断する必要があるようです。


## 参考サイト
WebサーバとDBサーバを別立てにする意味　情報セキュリティ
https://edugineer.jp/2018/10/04/post-536/
Web技術の基本 ~Webアプリケーションの構造~
https://qiita.com/mk185/items/e914683410fd4ecaf0af

https://kitsune.blog/db-server-summary


https://ja.stackoverflow.com/questions/18417/web%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%81%A8ap%E3%82%B5%E3%83%BC%E3%83%90%E3%81%AE%E5%88%86%E9%9B%A2%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6
paizaラーニング レベルアップ問題集 二分探索メニュー JavaScript lower_bound

lower_bound (paizaランク B 相当)

https://paiza.jp/works/mondai/binary_search/binary_search__basic_step1

# 解答例
問題文の擬似コードに沿って実装します。
なお、2023/01/12時点で、問題文の疑似コードで、数列についてAとaが混在していました。
```
binary_search(A : 数列, n : 数列のサイズ, k : 基準)

ー中略ー

 if a[mid] < k then
            // a[0] ~ a[mid] は k 未満なので調べる必要が無い
```
となっている部分です。これはAかaどちらかに決めて実装します。運営に問い合わせ済みです。

```javascript
const fs = require("fs");
const input = fs.readFileSync("/dev/stdin", "utf-8").trim();
const lines = input.split("\n");

//二分探索の関数定義
/**
    A_i >= k を満たす最小の i を返す
    A_i >= k を満たす i が存在しない場合は n を返す
*/
const binary_search = (A, n, k) => {//(A : 数列, n : 数列のサイズ, k : 基準)
    // 探索範囲 [left, right]
    let left = 0;
    let right = n;
    
    // 探索範囲を狭めていく
    while (left < right) {
        // 探索範囲の中央
        const mid = Math.trunc((left + right) / 2);

        if (A[mid] < k) {
            // A[0] ~ A[mid] は k 未満なので調べる必要が無い
            left = mid+1;
        } else {
            right = mid;
        }    
    }
    // 狭め終わったらmin_iを返す
    return right;
};

//メイン
const n = Number(lines[0]);
const A = lines[1].split(" ").map(Number).sort((a, b) => a - b);
const q = Number(lines[2]);
//各kについて調べる
for (let i = 1; i <= q; i++) {
  const k = Number(lines[i + 2]);
  const min_i = binary_search(A, n, k);
  console.log(n - min_i);
}
```
CVE-2022-41099でのWindows回復環境（WindowsRE）に更新プログラムを適用する手順

今回のWindowsUpdateでCVE-2022-41099が発表されました。
Bitlockerのバイパスが可能となってしまうため、パソコン紛失時等にそこを突破されてしまい、情報流出等のインシデントが発生してしまいます。

https://msrc.microsoft.com/update-guide/vulnerability/CVE-2022-41099

今回の対象としてはWindows10/11となっておりかなりの台数となっている上、手順が少しややこしいので備忘録も含めて記載。

https://learn.microsoft.com/ja-jp/windows-hardware/manufacture/desktop/add-update-to-winre?view=windows-11

## PCの状況確認

### 自分のWindowsのバージョンを確認する
[スタート]→[設定]→[システム]→[詳細情報]
![スクリーンショット 2023-01-12 11.14.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546003/94e3d54e-2a11-97fa-50f0-9b2eff0b9a7c.png)
この場合はWindows10の22H2ですね。
### 自分のWindows回復環境（WindowsRE）の状況を確認する
オンラインのWindowsを使っているのでオンラインの手順を使って確認。
```
Microsoft Windows [Version 10.0.19045.2364]
(c) Microsoft Corporation. All rights reserved.

C:\Windows\system32>reagentc /info
Windows 回復環境 (Windows RE) およびシステム リセット構成
情報:

    Windows RE の状態:         Enabled
    Windows RE の場所:         \\?\GLOBALROOT\device\harddisk0\partition4\Recovery\WindowsRE
    ブート構成データ (BCD) ID: 721ee909-c555-11ea-b264-c384ab912fae
    回復イメージの場所:
    回復イメージ インデックス: 0
    カスタム イメージの場所:
    カスタム イメージ インデックス: 0
REAGENTC.EXE: 操作は成功しました。

C:\Windows\system32>Dism /Get-ImageInfo /ImageFile:\\?\GLOBALROOT\device\harddisk0\partition4\Recovery\WindowsRE\winre.wim /index:1

展開イメージのサービスと管理ツール
バージョン: 10.0.19041.844

イメージの詳細: \\?\GLOBALROOT\device\harddisk0\partition4\Recovery\WindowsRE\winre.wim
インデックス: 1
名前: Microsoft Windows Recovery Environment (x64)
説明: Microsoft Windows Recovery Environment (x64)
サイズ: 2,303,741,015 バイト
WIM ブート可能 : いいえ
アーキテクチャ: x64
HAL: <未定義>
バージョン : 10.0.19041
Service Pack のビルド : 1
Service Pack のレベル : 0
エディション : WindowsPE
インストール : WindowsPE
製品の種類 : WinNT
製品群 :
システム ルート : WINDOWS
ディレクトリ : 3677
ファイル : 16562
作成日時 : 2019/12/07 - 22:57:03
変更日時 : 2020/05/11 - 14:20:59
言語 :
        ja-JP (既定)
操作は正常に完了しました。

C:\Windows\system32>
```

### 適用対象のアップデートファイルをダウンロードする
Windows Update カタログにアクセスして最新のアップデートを探す。
https://www.catalog.update.microsoft.com/Search.aspx?q=2023
検索で2023あたりで探すとよいかと。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/546003/3bb293d4-4fcf-719d-52dd-8df6a1b8546e.png)
対象のファイルをダウンロード。私の端末はx64なのでそのファイルを選択する。
セキュリティ問題の修正プログラムを選択しましょう。

### アップデートを適用する
ここからアップデートをWindows回復環境に適用します。

https://learn.microsoft.com/ja-jp/windows-hardware/manufacture/desktop/add-update-to-winre?view=windows-11#apply-the-update-to-a-running-pc

>Dism /Add-Package /Image:C:\mount\ /PackagePath:"c:\downloadedupdate\update.msu" 

ここの``c:\downloadedupdate\update.msu``が``windows10.0-kb5022282-x64_fdb2ea85e921869f0abe1750ac7cee34876a760c.msu``保存されている場所に差し替えてあげればOK。

後は手順通りに進めることで作業が完了します。

Bitlokerを突破されるといろいろ大変だと思いますので、お早めに対応するのが吉かと思います。
21日から翌20日までを１か月とした時の今月だけを抽出

+ [レポートを作成する際、月の途中の日付で範囲指定したい](https://trailhead.salesforce.com/ja/trailblazer-community/feed/0D54S00000JfmYuSAJ)
 
取引先への請求一覧のレポートを作りたく、「20日締め」の取引先には、売上日が「前月の21日から今月の20日まで」の請求のみ表示したいです。

レポートの検索条件の、「売上日」の範囲指定で、「前月の21日から今月の20日まで」といったような範囲指定はできるものでしょうか？

**回答**
以下のような行レベルの数式列を作成して、検索条件に追加すれば範囲指定は可能でした。
考え方としては 売上日と今日をとりあえず 202301（月度） という形に変換して同じかどうかで判断します。


私の場合はテキストで作ったので"1"の場合が範囲内ですね。

```
IF(
IF( DAY( KojiDaicyo2__c.KaishiDate__c ) < 21, TEXT( YEAR( KojiDaicyo2__c.KaishiDate__c ) *100 + MONTH(KojiDaicyo2__c.KaishiDate__c ) ) ,
IF ( MONTH(KojiDaicyo2__c.KaishiDate__c ) = 12, TEXT( YEAR( KojiDaicyo2__c.KaishiDate__c ) + 1) & '01' ,
TEXT( YEAR( KojiDaicyo2__c.KaishiDate__c ) *100 + MONTH(KojiDaicyo2__c.KaishiDate__c ) + 1)
)) =
IF( DAY( TODAY() ) < 21, TEXT( YEAR( TODAY() ) *100 + MONTH(TODAY() ) ) ,
IF ( MONTH(TODAY() ) = 12, TEXT( YEAR( TODAY() ) + 1) & '01' ,
TEXT( YEAR( TODAY() ) *100 + MONTH(TODAY() ) + 1)

)),"1","0")
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/b4b56197-2387-e917-7990-e2e46f574e46.png)
Azure Administrator(AZ-104)：Azure での ID とガバナンスの管理「Azure ADユーザアカウントの一括操作」

Microsoft Learnを用いたMicrosoft Azure Administrator(AZ-104)の試験内容をまとめる投稿です。
この投稿ではMicrosoft Learnで学ぶ内容をできるだけ簡潔にまとめております。
Azureの勉強を一から始め、AZ-900取得後の資格としてAZ-104取得を目指している方の参考になれば幸いです。
※AZ-900の内容は本投稿では省略しております。再度復習したい方は下記をご参照ください。
[Azure Fundamentals(AZ-900)：試験合格に向けた学習内容のまとめ](https://qiita.com/fsd-jume/items/e4ec76959adb547ac5e8)

## 今回の学習内容
**◆「Azure ADユーザアカウントの一括操作」**
◇対応するMicrosoft Learn ラーニングパス
[AZ-104:Azure での ID とガバナンスの管理](https://learn.microsoft.com/ja-jp/training/paths/az-104-manage-identities-governance/)
◇対応するモジュール
[一括ユーザー アカウントを作成する](https://learn.microsoft.com/ja-jp/training/modules/configure-user-group-accounts/4-create-bulk-user-accounts)

※前回の投稿は下記から確認できます。
[Azure Administrator(AZ-104)：Azure での ID とガバナンスの管理「Azure ADユーザアカウントの管理」](https://qiita.com/fsd-jume/items/b21376497d7047620093)

## Azure ADユーザアカウントの一括操作
### ユーザアカウントの一括操作
・一括の作成と削除が可能
・最も一般的な方法はAzure Portal
・PowerShellで一括アップロードも可能

### 一括アカウントの操作について
**◆Azure Portalでユーザアカウントの作成/削除権限を持つユーザ**
・グローバル管理者
・ユーザ管理者

**◆一括操作のテンプレート**
・ユーザアカウントのデータが入力されたCSVファイルを使用
・Azure ADポータルからダウンロード可能
・ユーザアカウントの一覧もダウンロード可能

### ユーザアカウント作成の考慮事項
**◆名前付け規則について検討**
・組織全体で一貫性を保つための名前付け規則
例：ユーザ名は **`性`.`名`** とするなど(Smith.John@～)

**◆初期パスワードの使用について検討**
・新しく作成したユーザの初期パスワード規則
・新しいユーザが安全に初期パスワードを受信できる方法
例：ランダムなパスワードを生成し、新しいユーザか管理者にメールで送信

**◆エラーを最小限に抑えるための戦略について検討**
・エラーがある場合は、Azure Portalの「一括操作の結果」ページで確認可能
⇒各エラーの理由が記載された結果ファイルをダウンロードして表示可能
・エラー例
　-既に作成されているユーザアカウント
　-重複しているアカウントなど
・一度に作成するユーザアカウントの数が少ない方がトラブルシューティングが容易

## 次の学習内容
**◆Azure ADグループアカウント**

https://qiita.com/fsd-jume/items/bd4db1e05dd44ebd4acd

## 学習内容をまとめたページの一覧

https://qiita.com/fsd-jume/items/6eda24728c9c03c36bc3
devcontainer作成時、貯まるdocker関連ファイルを全削除したい時

<br>

初心者向けです

<br><br>

devcontainerで環境構築していてトライアンドエラーしていると、docker関連のファイルが貯まるので
全削除に使うコマンド



## 注意

**全部消えるので注意してください。**

```
WARNING! This will remove:
  - all stopped containers
  - all networks not used by at least one container
  - all volumes not used by at least one container
  - all images without at least one container associated to them
  - all build cache
```

<br><br>

まずは使用量
```
docker system df
```

<br><br>

全削除
```
docker system prune --all --volumes
```

<br><br>

たまに残る時
```
docker rm -f `docker ps -a -q`
docker system prune --all --volumes
```
カルーセル（スライダー）を簡易に作ってみる

カルーセル（スライダー）を簡単に作ってみる
出来上がったやつのサンプルページのリンク、イメージはこんな感じ

https://sueasen.github.io/javascript-exercise/work/slider/

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/51758/e887543a-9ebc-1e33-cbe1-6708286ebecb.png)

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

- HTML に枠を準備
```html:index.html
<!doctype html>
<html>

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <link rel="stylesheet" href="./style.css">
</head>

<body>
    <div class="slide">
        <div id="prev"></div>
        <div class="image_box"><img src="適当な画像のパス"></div>
        <div id="next"></div>
    </div>
    <div class="thumbnail">
        <img src="適当な画像のパス">
        <img src="適当な画像のパス">
        <img src="適当な画像のパス">
        <img src="適当な画像のパス">
    </div>
</body>

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

</html>
```
- CSS で配置を調整
```css:style.css
@charset "utf-8";

.slide,
.thumbnail {
    display: flex;
    justify-content: center;
    align-items: center;
}

.slide img {
    height: 280px;
    width: 280px;
}

.thumbnail img {
    height: 70px;
    width: 70px;
}

#prev {
    width: 30px;
    height: 30px;
    border-bottom: 2px solid black;
    border-left: 2px solid black;
    cursor: pointer;
    transform: rotate(45deg);
}

#next {
    width: 30px;
    height: 30px;
    border-bottom: 2px solid black;
    border-right: 2px solid black;
    cursor: pointer;
    transform: rotate(-45deg);
}
```

- JavaScript で動きを設定
```javascript:script.js
/**
 * スライダー、サムネイルにイベントを設定します
 */
window.addEventListener('load', (e) => {
	const slideDatas = {
		top : document.querySelector('.image_box img')
		, now : document.querySelector('.thumbnail img')
		, list : [...document.querySelectorAll('.thumbnail img')]
	};
	slideDatas.list.forEach(dom => dom.onclick = () => {
		slideDatas.now = dom;
		slideDatas.top.src = slideDatas.now.src;
	});
	slideDatas.top.src = slideDatas.now.src;
	document.querySelector('#prev').onclick = () => slideMoveClick(slideDatas, -1);
	document.querySelector('#next').onclick = () => slideMoveClick(slideDatas, 1);
});

/**
 * slide の prev, next を押下時の処理します
 * @param { {[key: String]: Object} } slideDatas スライダー画像情報
 * @param {Number} shift シフト値
 */
function slideMoveClick(slideDatas, shift) {
	slideDatas.now = slideDatas.list[(slideDatas.list.length + slideDatas.list.indexOf(slideDatas.now) + shift) % slideDatas.list.length];
	slideDatas.top.src = slideDatas.now.src;
	slideDatas.top.animate(
		{
			transform: ['translateX(' + (-30 * shift) + 'px)', 'translateX(0px)']
			, opacity: [0.2, 1]
		}
		, {
			duration: 500,
			iterations: 1
		}
	);
}
```
systemd を有効にした WSL をバックグラウンドに常駐させたい。

# 概要
systemd を動かすことができなかった今までは、WSL で cron や dockerd などのバックグラウンドプロセスを常駐させるために以下のような方法を使っていました。

- 参考記事：[WSL上cronをwindows起動時に自動実行する - Qiita](https://qiita.com/yuinyan/items/4e23d9bd90f93988a88c)

上記の記事で紹介している方法を簡単に説明すると Startup フォルダに cron や dockerd 起動用のコマンドを入れるという方法です。

systemd を有効にできるようになり、上記の仕組みも不要になる！と思ったのですが以下の理由で実際にはそうなりませんでした。

- systemd を有効にしたとしても WSL 自体は Windows 起動時に自動起動してくれない。
- WSL ターミナルを起動しておかないとタイムアウトで WSL インスタンスが自動停止してしまう。
  - [WSL2 initの仕様？ - Qiita](https://qiita.com/shigeokamoto/items/52a486dfffab6a926c54)

上記を解決する方法の１つを紹介します。

# 解決方法
以下のファイルを Startup フォルダに配置します。

```vbs:wsl_startup.vbs
Set ws = CreateObject("Wscript.Shell")
ws.run "cmd /c wsl", vbhide
```

Startup フォルダを開くには「Windows キー + R同時押し」で「ファイル名を指定して実行」ウィンドウを開き、「名前」に「shell:startup」を入力し、OK を押下してください。

コマンドプロンプトウィンドウを非表示にする目的で VBScript を使っています。

wsl コマンドを単に実行し、WSL ターミナルウィンドウをバックグラウンドで起動しています。
タスクマネージャーで「Linux 用 Microsoft Windows サブシステム ランチャー」を発見できると思います。
Laravel 論理削除したデータの復元

# tinkerを使用して復元

- まずは論理削除されたデータの取得

```php
php artisan tinker
Psy Shell v0.10.3 (PHP 7.3.33 — cli) by Justin Hileman
>>> $deleteItem = App\Http\Models\User::onlyTrashed()->get();
↓
うまく取得できれば下記のように一覧が表示される
=> Illuminate\Database\Eloquent\Collection {#3241
     all: [
       App\Http\Models\User {#3258
         id: 3,
         name: "editor",
         email: "editor@example.com",
         created_at: "2022-12-16 06:38:12",
         updated_at: "2022-12-28 05:47:41",
         deleted_at: "2022-12-28 05:47:41",
       },
       App\Http\Models\User {#3276
         id: 4,
         name: "テスト太郎",
         email: "test@example.com",
         created_at: "2022-12-22 00:57:04",
         updated_at: "2022-12-28 05:45:57",
         deleted_at: "2022-12-28 05:45:57",
       },
       …
```

- 取得したデータから所定のIDで復元

```php
>>> $deleteItem->find(3)->restore();
=> true
```
成功すればtrueが表示される。
上記の場合には``id: 3``のユーザーが復元されたことになる。
ChatGPTで、OpenAPI/RDBMS使ってJavaのWebAPIサービスを実装してUT実行してもらう

## 「/customer/{id}で、リクエスト可能な、CRUDを持つOpenAPIの定義をください」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29715/68fbb3a8-72fb-eba1-6828-e8a9efe49f1c.png)

## 「/customerで、一覧を返却する定義が欲しいです。名前で検索出来てページングもしたいです。」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29715/1c57fc0d-51fe-3b2d-552f-38e9ac208950.png)

## 「ついでに、SQLもください」

ページャー用のクエリを考えてくれました

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29715/ae577ec9-ce6b-155c-b60a-53c800a00f27.png)

## 「このSQLを使うJavaのコードをください」

DAOを作ってくれました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29715/1116fb08-6e8d-9b55-3fb9-d3902a161503.png)


## 「CustomerDao を利用して、リクエストに返答できるコードをください」

Servlet使ってWebApi実装してくれます

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29715/9ac5b89e-498b-3925-2114-6d4cb5fbb0a8.png)


## 「CustomerServlet をテストするコードをください」

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29715/c4ae001d-4707-2606-46b0-161b846ff0cb.png)

## 「CustomerServletTest を動かした結果を教えて下さい」

mavenで動くらしい。実行時間は、遅い

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/29715/468dbba4-ff00-b2c3-5387-57e8ede41a7e.png)
AndroidでNavigationを使ったDeep Linkの実装Part4

# 初めに
今回は、Intentに格納したDeepLinkを利用して画面遷移する方法を紹介していきます
### 本文
残りの作業はとても単純で、`intent`から`get`した後に明示的にキャストすることで型を`String`にしてあげた後に`toUri()`をして`navController`に対象のDeepLinkを渡してあげるだけです
```kotlin
val linkUrl = intent.extras?.get(PAYLOAD_NAME) as? String
val deepLink = linkUrl.toUri()
val navHostFragment = suppoirtFragmentManager.findFragmentById(R.id.navigation) as NavHostFragment
val navController = navHostFragment.navCotroller
navController.navigate(deepLink)
```
### 最後に
今回は、Intentに格納したDeepLinkを利用して画面遷移する方法を紹介しました。
これで一連の記事は終了になります。
誰かのお役に立てたら幸いです。
前回の記事は下記になります
https://qiita.com/ryuji_sato/items/5cfe994cfe1fbe0c5976
【Laravel】検索機能を実装する（日付期間検索）

## Laravelでユーザーが打ち込んだ期間の検索を実装したい
画像のようにユーザーが打ち込んだ期間の検索をかけていきます。(いつからいつまでの期間に入力したレコードを表示する形)
※見た目はまだ修正してないので悪しからず。。

![スクリーンショット 2023-01-11 20.21.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/589286/38154933-9dd3-45d1-a04c-a9c1b284fda6.png)

### Controller
```
class TrRecordController extends Controller
{

    public function index(Request $request) {

        $user_id = Auth::id();
        $from = $request->input('from');
        $until = $request->input('until');
        $part_search = $request->input('part_search');
        $menu_search = $request->input('menu_search');
        $q = TrRecord::query();

        // 全件取得(検索かけなかったら全件表示)
        $query = $q->where('user_id', $user_id)
        ->orderBy('tr_date', 'desc');

        // 日付検索
        if (isset($from) && isset($until)) {
            $query = $q->whereBetween("tr_date", [$from, $until])
            ->where('user_id', $user_id);
        }

        $trRecords = $query->paginate(5);

        return view('trrecords.index', compact('trRecords', 'from', 'until'));

    }
```

期間の検索をかけたいので、クエリビルダの`whereBetween句`を使ってあげます。
> whereBetween／orWhereBetween
> whereBetweenメソッドは、カラムの値が２つの値の間にある条件を加えます。
> $users = DB::table('users')
           ->whereBetween('votes', [1, 100])
           ->get();

### View
```
    <form>
        <div>
            <label for="">日付検索</label>
            <input type="date" name="from" placeholder="from_date" value="{{ $from }}">
                <span class="mx-3">~</span>
            <input type="date" name="until" placeholder="until_date" value="{{ $until }}">
        </div>
        <div>
            <button type="submit">検索</button>
        </div>
    </form>
```

期間の検索なので、`input type`を`date`としてあげます。

以上
Amazon Managed Blockchainに2022年10月に追加されたトークンベースアクセス機能でHardhatからスマートコントラクトをデプロイ

2022年10月22日、Amazon Managed Blockchain for Ethereumにおいて、トークンベースアクセス機能が実装されました。
これはEthereumノードのJSON-RPC APIと相互運用可能なアクセス方法です。

今回はこの方法を使って、NFTマーケットプレイスのユースケースである
https://github.com/aws-samples/simple-nft-marketplace
の実装を書き換えたいと思います。

## そもそも何がよくなったのか
Amazon Managed Blockchainはイーサリアムノードをお手軽に立てることのできる「イーサリアムノードのマネージドサービス」の機能を持ちますが、実は今までノードへのアクセスには「署名バージョン4」を利用することしかできませんでした。
これだと、Web3.jsやether.jsなどとのJSON-RPC APIと互換性はなく、スマートコントラクトのデプロイも手軽とは言えない状況でした。

具体的には、AWSの公開しているNFTマーケットプレイスのサンプル
https://github.com/aws-samples/simple-nft-marketplace
のデプロイは、Hardhatの設定ファイルである`hardhat.config.js`を魔改造して作られています。

署名V4を扱うライブラリである`@aws/web3-http-provider`をインポートした上でHardhat Runtime Environmentに介入してる感じですね。

```simple-nft-marketplace/contract/hardhat.config.js
extendEnvironment((hre) => {
  if (hre.network.name === 'amb') {
    const endpoint = getAmbEndpoint();
    const Web3 = require('web3');
    hre.Web3 = Web3;
    hre.web3 = new Web3(new AWSHttpProvider(endpoint));
  }
});
```

これだと今後`AWSHttpProvider`のメンテが止まってしまった時や、不具合があった場合などきっと大変でしょう。

## どう変わったか
署名V4ではなく、トークンベースでアクセスできる方法が提案されました。
これによって`@aws/web3-http-provider`のインポートは不要になり、依存関係もすっきりします。
ただし、これは主にスマートコントラクトのデプロイなど開発において使われるべき機能なようで、たとえばAWS Lambdaとイーサリアムノードを統合する場合などは、署名V4を使うことが推奨されています。

そしてトークンベースアクセスで`extendEnvironment`を書き直した結果が以下。

```simple-nft-marketplace/contract/hardhat.config.js
extendEnvironment((hre) => {
  const Web3 = require("web3");
  hre.Web3 = Web3;
  hre.web3 = new Web3(
    new Web3.providers.HttpProvider(
      "トークン付アクセス先URL"
    )
  );
});
```

## トークン付アクセスURLとは
https://aws.amazon.com/jp/blogs/database/introducing-token-based-access-to-ethereum-node-apis-on-amazon-managed-blockchain/
に記載があるように、
1. リージョン
1. ノードID
1. アクセサートークン

で表されます。

```sample.py
from web3 import Web3

aws_region = "us-east-1"
node_id = "nd-XXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
accessor_token = "asdagehahaegeafaefaegahaeqa343gaegagega"

endpoint_https = "https://{node_id}.t.ethereum.managedblockchain.{aws_region}.amazonaws.com/?billingtoken={accessor_token}".format(node_id=node_id, aws_region=aws_region, accessor_token=accessor_token)

print(endpoint_https)
```

アクセサートークンはAmazon Managed Blockchainの画面から作ることができますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/491627/1aa1c5f9-5263-7b59-b704-1b4edf1eb6cc.png)

## まとめ
`@aws/web3-http-provider`をインポートしなくてよくなっただけでも、だいぶ楽になるのではないでしょうか。特にAWSの資格情報もローカルに必要だったり、面倒なことが多かったので、トークンベースアクセスの実装は大変嬉しいです。
matplotlibとpandasを使ってグラフを作成してみた

大学の研究の際にpythonでグラフを作成する機会があったため，matplotlibとpandasを用いてグラフを作成してみた．
# matplotlibとは
matplotlibは，プログラミング言語Pythonのためのグラフ描画ライブラリである．オブジェクト指向のAPIを提供しており，様々な種類のグラフを描画する能力を持つ．

# グラフの作成に使用したメソッド
|メソッド |説明 |
|---|---|
|figure|figureのインスタンスが複数ある時など，どのfigureに割り当てるかを指定する．|
|subplot |引数によって生成するAxes（サブプロット）の数を変更できる。 |
|twinx|元のAxesオブジェクトを複製する。ただし新たなAxesには横軸がなく縦軸が逆側にある。 |
|set_xlabel |x軸のラベルを設定する |
|set_ylabel |y軸のラベルを設定する |
|get_legend_handles_labels |二つ目のグラフを描写する |
|set_xlim |x軸の上限値を設定する |
|set_ylim |y軸の上限値を設定する |
|savefig |作成したプロットを保存する |


# pandasとは
pandasは，プログラミング言語Pythonにおいて，データ解析を支援する機能を提供するライブラリである．特に，数表および時系列データを操作するためのデータ構造と演算を提供する．

# ソースコード
matplotlibとpandasを使用してグラフを作成した際のソースコード．

```Python
import pandas as pd
import matplotlib as mpl
import matplotlib.pyplot as plt

file = "./log/checkout_stats_history.csv"
df = pd.read_csv(filepath_or_buffer=file,encoding="cp932")
df_time = df.iloc[:, 0]         #タイムスタンプ
df_init_time = df.iloc[0,0]     #開始時間
df_time -= df_init_time         #タイムスタンプの整理，x軸
df_req = df.iloc[:, 4]          #リクエスト/s，y軸1
df_res = df.iloc[:, 22]         #最大応答時間，y軸2

fig = plt.figure()

ax1 = fig.subplots()
ax2 = ax1.twinx()
ax1.plot(df_time, df_res, c="b", label="res")
ax2.plot(df_time, df_req, c="r", label="req")
ax1.set_xlabel("Time Stamp (s)", fontsize=10)
ax1.set_ylabel("Total Max Response Time (s)", fontsize=10)
ax2.set_ylabel("Requests per second", fontsize=10)
h1, l1 = ax1.get_legend_handles_labels()
h2, l2 = ax2.get_legend_handles_labels()
ax1.legend(h1 + h2, l1 + l2)
ax1.set_xlim(0)
ax1.set_ylim(0)
plt.savefig('./graph/checkout_graph_data.png')
plt.close()
```
# 完成したグラフ
今回は負荷試験の結果から最大応答時間と秒間リクエスト量の関係を表すグラフを作成した．
![product_graph_data.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3111732/be96997e-11c2-c3ec-c55f-96838ffc5fa1.png)
Ubuntuでのエラーログの検索方法

今回の検索はgrepコマンドを使い4,500番台の検索を行う方法です．

今回は
https://dataverse.harvard.edu/dataset.xhtml?persistentId=doi:10.7910/DVN/Z834IK
のECログを使用して，アクセスできない原因を探すためにエラーログの検索を行っています．

コマンドは，

grep "[4-5][0-9][0-9]" ファイル名

です．
もちろん，400~600番台の検索をしたいときは，

grep "[4-6][0-9][0-9]" ファイル名

とすれば問題ないです．
検索結果の例としては以下の画像のようになります．

![スクリーンショット 2023-01-12 120859.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3111717/2080229d-7c88-bdae-1ece-2799288edb52.png)

[400]-[599]と書くと検索したいものと全く違う検索が出来てしまうので注意！

![スクリーンショット 2023-01-12 121144.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3111717/fc8d35e4-c243-bc19-d52f-69aa79725825.png)
PHP 虎の巻（あんちょこ）


# PHP のプログラミングに関する情報
> https://www.php.net/manual/ja/appendices.php

| 詳細情報 | 詳細リンク |
| :---- | :---- |
| 関数・メソッドの一覧 | https://www.php.net/manual/ja/indexes.functions.php |
| マニュアル内の全サンプルの一覧 | https://www.php.net/manual/ja/indexes.examples.php |
| PHP 型の比較表 | https://www.php.net/manual/ja/types.comparisons.php |
| リソース型の一覧 | https://www.php.net/manual/ja/resource.php |
| マジック定数 | https://www.php.net/manual/ja/language.constants.magic.php |


# PHP の設定/構築に関する情報
> https://www.php.net/manual/ja/appendices.php


## 製品サポート期間の確認
- [PHP: Supported Versions](https://www.php.net/supported-versions.php)
- [PHP: Unsupported Branches](https://www.php.net/eol.php)


## マイグレーション情報

| 詳細情報 | 詳細リンク |
| :---- | :---- |
| PHP 5.5.x から PHP 5.6.x への移行 | https://www.php.net/manual/ja/migration56.php |
| PHP 5.6.x から PHP 7.0.x への移行 | https://www.php.net/manual/ja/migration70.php |
| PHP 7.0.x から PHP 7.1.x への移行 | https://www.php.net/manual/ja/migration71.php |
| PHP 7.1.x から PHP 7.2.x への移行 | https://www.php.net/manual/ja/migration72.php |
| PHP 7.2.x から PHP 7.3.x への移行 | https://www.php.net/manual/ja/migration73.php |
| PHP 7.3.x から PHP 7.4.x への移行 | https://www.php.net/manual/ja/migration74.php |
| PHP 7.4.x から PHP 8.0.x への移行 | https://www.php.net/manual/ja/migration80.php |
| PHP 8.0.x から PHP 8.1.x への移行 | https://www.php.net/manual/ja/migration81.php |
| PHP 8.1.x から PHP 8.2.x への移行 | https://www.php.net/manual/ja/migration82.php |


## php.ini ディレクティブ情報

| 詳細情報 | 詳細リンク |
| :---- | :---- |
| php.ini ディレクティブのリスト | https://www.php.net/manual/ja/ini.list.php |
| php.ini のセクション一覧 | https://www.php.net/manual/ja/ini.sections.php |
| コア php.ini ディレクティブに関する説明 | https://www.php.net/manual/ja/ini.core.php |


## 拡張モジュール情報

| 詳細情報 | 詳細リンク |
| :---- | :---- |
| アルファベット順のリスト | https://www.php.net/manual/ja/extensions.alphabetical.php |
| 所属別の拡張モジュールリスト | https://www.php.net/manual/ja/extensions.membership.php |
| 廃止予定・実験的モジュールリスト | https://www.php.net/manual/ja/extensions.state.php |
| 画像処理詳細 (ImageMagick) | https://www.php.net/manual/ja/book.imagick.php |

## Windows システムへのインストール
> https://www.php.net/manual/ja/install.windows.php

## PHP ソース・ビルド済み情報
| 詳細情報 | 詳細リンク |
| :---- | :---- |
| 直近のアーカイブ一覧 | https://windows.php.net/downloads/releases/<br>https://windows.php.net/downloads/releases/latest/ |
| 過去のアーカイブ一覧 | https://windows.php.net/downloads/releases/archives/ |


# XAMPP製品に関する情報
| 詳細情報 | 詳細リンク |
| :---- | :---- |
| アーカイブ一覧 | https://sourceforge.net/projects/xampp/files/ |
inotifyでディレクトリ内の監視をしてみた

# inotifyとは
inotifyとは，ファイルシステムイベントを監視するための機構を提供するAPIである．ファイルやディレクトリを個々に監視することもできる．ディレクトリを監視する場合は，ディレクトリ自身とディレクトリ内のファイルのイベントを返す．

# inotifyのイベント一覧
|イベント一覧|説明|
|:---:|:---|
|IN_ACCESS|ファイルがアクセスされた|
|IN_ATTRIB|メタデータが変更された|
|IN_CLOSE_WRITE|書き込みのために開かれたファイルが閉じられた|
|IN_CREATE|監視対象ディレクトリ内でファイルやディレクトリが作成された|
|IN_DELETE|監視対象ディレクトリ内でファイルやディレクトリが削除された|
|IN_MODIFY|ファイルが変更された|
|IN_OPEN|ファイルやディレクトリが開かれた|

# シェルスクリプトでinotifyを使用する
ここからは，シェルスクリプトでinotifyを使用してディレクトリの監視を行う方法を説明する．シェルスクリプトでinotifyを使用する場合は，inotifywaitというコマンドを用いることでイベントを表示させることができる．

# ディレクトリ内にファイルが作成されたことを検知する
ファイルが作成されたことを検知するコードは以下のようになります．
![qiita.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2851869/55f5d11c-969b-a82c-54dd-7f3c9157059d.png)
このコードでは，対象のディレクトリ(/home/mikami/10test)を監視して，もしCREATEを検知したらechoで表示されるようになっています．

if文の中では，「もしCREATEが表示されたら」という条件分岐にすることでファイルの作成を検知した際に処理を行えるようにしています．


# ディレクトリ内でファイルの作成完了を検知する
ファイルの作成完了を検知するコードは以下のようになります．
![qiita1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2851869/d0041a5f-ed75-e5a8-da9c-2c739bd8af72.png)
このコードは，上記の「ファイルが作成されたことを検知する」で説明したコードと構造は同じです．違う部分は，CREATEの部分がファイルの作成完了を意味するCROSE_WRITEに変わる所です．

こうすることで，ファイルの作成完了を検知した際に行いたい処理を実行することができます．

# シェルスクリプトでinotifyを使用する際の注意点
オプションの-mを付けるかを考える．
付ける場合　：実行から実行終了まで監視を続ける
付けない場合：最初のイベントで監視が終了する

# 最後に
今回は，inotifyでのディレクトリの監視をシェルスクリプトを用いて行ってみました．if文の下のコード1行を書き換えてもらえれば，「ファイルが作成された際」と「ファイルが作成完了された際」に行いたい処理を実行することができます．是非，参考にしてみて下さい．
【rails】DM機能からグループチャット作ってみた〜

# 前提
https://qiita.com/nojinoji/items/2b3f8309a31cc6d88d03

この記事でDM機能を作っときましょう
# やり方ー
### Controller
```ruby:rooms_controller.rb
（略）
    def new
        @users = User.all
        @current_room = Room.find(params[:id])
        @entry = Entry.new
    end
    def room_add_user
        entry = Entry.new(entry_params)
        if entry.save
            redirect_to room_path(entry.room_id)
        else
            redirect_to new_room_path(entry.room_id)
        end
    end
（略）
    private
    def entry_params
        params.require(:entry).permit(:user_id, :room_id)
    end
end #一番上の行に対するend
```
### Routing
```ruby:routes.rb
（略）
  get 'rooms/new/:id' => 'rooms#new', as:'new_room'
  post 'entry' => 'rooms#room_add_user'
（略）
```
### Views
```ruby:rooms/show.html.erb
（略）
<%= link_to '参加者を追加する', new_room_path(@room.id) %>
（略）
```
```html:rooms/new.html.erb
<% if user_signed_in? %>
  <h2>User一覧</h2>
  <% @users.each do |u| %>
    <% if @current_room.entries.pluck(:user_id).exclude?(u.id) %>
      <p><%= u.email %> <%= u.name %>さん</p>
      <%= form_for(@entry, :url => {:controller => :rooms, :action => :room_add_user}) do |f| %>
        <%= f.hidden_field :user_id, :value=> u.id %>
        <%= f.hidden_field :room_id, :value=> @current_room.id %>
        <%= f.submit "追加する" %>
      <% end %>
      <hr>
    <% end %>
  <% end %>
<% end %>
```

# 最後に何となく参加中のroom一覧ページ作りました
```ruby:rooms/index.html.erb
<h1>参加中のroom一覧</h1>
<% if user_signed_in? %>
  <h2>Hello <%= current_user.id %></h2>
  <% current_user.entries.each do |e| %>
    <a href="/rooms/<%= e.room_id %>">
      <%= Room.find(e.room_id).name %>
      <% Room.find(e.room_id).entries.each do |u| %>
        <%= User.find(u.user_id).email %>
      <% end %>
      <br>
    </a>
  <% end %>
  <%= link_to "ログアウト", destroy_user_session_path, :method => :delete %>
<% else %>
  <%= link_to "ユーザーを登録する", new_user_registration_path %>
  <br>
  <%= link_to "ログインする", new_user_session_path %>
<% end %>
```
`<%= link_to "room一覧へ", rooms_path %>`
このlink_toで移動できるので適当な場所に書いときましょう！
参考にしたdmの記事が多対多のアソシエーションをしてなくてめんどくさい書き方になっていますが、余力がある方は多対多にしてみると面白いと思います！
それではバイバイ〜