OmniAICreator/Qiita-1.07M huggingface dataset:
Node.js 〜Node.jsを何も知らない自分が最初に1時間調べて理解した概要〜

## Node.jsとは

Node.jsは、JavaScriptをブラウザ上ではなく、サーバ上で動かすためのプラットフォーム。
ウェブアプリケーションのサーバサイドロジックを構築するのに適している。

## Node.jsの基盤: V8エンジン
Node.jsはV8エンジンを使用している。
V8エンジンは、Googleが開発したJavaScriptエンジン。
主にChromeブラウザでJavaScriptを高速に実行するために使用されている。

このエンジンは、JavaScriptのコードをマシンコードにコンパイルして実行する。
Node.jsはこのV8エンジンをサーバサイドでも利用できるようにしている。

## セキュリティとファイルシステムアクセス

ブラウザで実行されるJavaScriptは、セキュリティの観点からローカルファイルシステムへのアクセスが制限されている。
Node.jsはV8エンジンにこれらの機能を追加し、サーバサイドでローカルファイルシステムにアクセスが可能。
これにより、ファイル操作やシステムコールが可能となり、サーバサイドアプリケーションの開発が容易になっている。

## DOM操作と直接実行

ブラウザではJavaScriptを使ってドキュメントオブジェクトモデル (DOM) と対話する。
ウェブページの構造やスタイル、コンテンツを動的に変更するための手段である。
一方、Node.jsではブラウザのようなDOMは存在せず、JavaScriptファイルを直接実行するだけでサーバサイドの処理を行う。

## 環境構築
以下にアクセスしてDownload Node.js(LTS)をクリック。
LTSとは安定稼働バージョンでサポートもあるバージョンなのでとりあえずこっちを選択したほうが安定。

https://nodejs.org/en

<img width=50% src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3743467/9c9050de-0518-27b6-5fa7-3e6e7ee6e9a5.png">

インストールはスクショすらいらないレベルでポチポチ進めることが可能。
Rustのお勉強 #1  環境構築編

# はじめに
C#しか触ったことのないエンジニアが急にRustを勉強したくなったので記録としてQiitaに残します。どこまで続くかはわかりません。基本的にはRustの内容を記載するつもりですが、それ以外のこともアウトプット目的で書きます。

# Rustについて
Rust（ラスト）は、性能、メモリ安全性、安全な並行性を目指して設計されたマルチパラダイムのプログラミング言語である。C言語、C++に代わるシステムプログラミング言語を目指しており、構文的にはC++に似ているが、「ボローチェッカー」(borrow checker) で参照の有効性を検証することによってメモリ安全性を保証できる。Rustはガベージコレクションなしでのメモリ安全性を達成しており、必要な場面で参照カウントを使うこともできる 。

参考文献：https://ja.wikipedia.org/wiki/Rust_(%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E8%A8%80%E8%AA%9E)

らしいです...

# さっそくやってみよう
## 環境構築編
### 使用環境 
- Windows11
- Docker Compose
- RustRover 2024.1
- Dev Container

VS Codeの拡張機能であるDev ContainerがRustRoverで使用できるみたいなので採用しました。

#### Dockerfileとdocker-compose.ymlの作成
- ディレクトリ構造
```
rs-practice-project
└ .devcontainer
　 ├ devcontainer.json
　 ├ Dockerfile
　 └ docker-compose.yml
```
- Dockerfile
```
FROM rust:1.78.0
```
Rustの元となるイメージを指定します。

- docker-compose.yml
```
version: 3.8;
services:
  app:
    build:
      context: .
      dockerfile: Dockerfile
    volumes:
      - ../:/app/rust
    tty: true
    working_dir: /app/rust
```
- version
docker composeで使用するバージョン
- services
アプリケーションを動かくための各要素の親みたいなやつ
- app
今回メインとなるコンテナの名前
- build
Dockerfileをもとに作成したオリジナルのDockerイメージを使用する。自分で作成しない場合は、下記みたいに指定します。
```
image : 公式に上がっているイメージ名
```

- context
Dockerfileがあるパス
今回だとdocker-compose.ymlと同じ位置にあるので「.」のみ
- dockerfile
指定するDockerfileの名前
- volumes
「:」より左側がホスト（Windows側）のフォルダ
右側がコンテナ上のパス
- tty
trueにすることでコンテナを起動したままにできる
- working_dir
コンテナ内の作業パスを指定できる。

#### devcontainer.jsonの作成
```
{
  "dockerComposeFile": ["docker-compose.yml"],
  "service": "app",
  "workspaceFolder": "/app/rust"
}
```
- dockerComposeFile
    docker-compose.ymlを指定
- services
docker-compose.ymlで作成したコンテナの名前を記載
- workspaceFolder
現在ローカルで開いているディレクトリをコンテナ内のどのパスで使用するか記載

### RustRover
RustoRoverを使用して実際にコンテナを起動してみます。

手順①
- 作成したDockerfileとdocker-compose.yml, devcontainer.jsonを.devcontainerフォルダに格納します。

手順②
- RustRoverを開くとこのような画面になり、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3799440/5a33294e-7aee-3c66-95f3-a0f0e06dc26b.png)
New Dev Containerを押下します。

手順③
- ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3799440/4c2362e5-2bd9-b95a-f9a7-29832b71bb4e.png)
プルダウンをDockerに選択して、先ほど作成した「devcontainer.json」を指定します。
その後、Build Container and Continueを押下

手順④
- 初回のみコンテナ用のRustRoverがインストールされ、コンテナが立ち上がると思います。
Continueを押下して、RustoRoverを立ち上げましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3799440/8b6ac6da-9fba-5c15-488b-b2fbfac748b2.png)
問題がなければこのようになるはずです。<br/>
Docker Desktopも見てみましょう。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3799440/96d9e956-d7e3-3a4b-7ad4-2f2a85042e90.png)
問題なく立ち上がっていますね。

手順⑤
- RustRover内でターミナルを開き
```
cargo --version
```
と入力しましょう。「cargo 1.78.0 」と表示されれば成功です。
# さいごに
VS CodeでDev Container拡張機能を入れると同じ事ができると思います。（試していないのでわかりませんが...）

書くの疲れたので今回はこのあたりにします。
次回のHello World編でお会いしましょう。
ありがとうございました。
Supabaseで全然GETができないと思ったらRLSの設定問題だった

## 起きたこと
vue3とSupabaseで新しいプロジェクトを生成して、早速GETをしてみた。

```vue
<script setup lang="ts">
import { supabase } from '@/utils/supabase'

const getCategories = async (): Promise<void> => {
  let { data } = await supabase.from('category').select('*')
  categories.value = data;
}

onMounted(() => {
  getCategories();
});
</script>
```
ドキュメント通りのコードだけど、空の配列しか返っておらず...
空の配列が返ってきてるってことは接続はできているはず。

## 原因
RLS(Row Level Security)の設定がないことが原因だった。
前作ったプロジェクトでは何も知らずに設定してたぽい。

## 解決方法
該当テーブルのページに移動、`RLS disabled`ボタンをクリック
![スクリーンショット 2024-05-23 22.58.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3754242/472c95cf-b1c4-9650-2120-05031aa8c530.png)
以下のように進む

![スクリーンショット 2024-05-23 22.59.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3754242/2a01cea4-5a62-b521-d99f-cfdd5650b6c3.png)
![スクリーンショット 2024-05-23 23.00.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3754242/1b85f182-0149-f80e-1ae3-ee85bc6d5f03.png)
するとAuthenticationのページに移動する。
早速policyを作りましょう。

![スクリーンショット 2024-05-23 23.00.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3754242/f94ce535-b5fd-5874-dfd4-9ecf245190b2.png)

エンドポイント別に揃っている。今回はGETが目的なので`SELECT`だけを選択、保存。

![スクリーンショット 2024-05-23 23.01.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3754242/465b67e7-48b7-52a6-e37f-ea0480755292.png)

## 結果
問題なくGETできた！

初歩の初歩だが、知らないと時間かかる、、

![スクリーンショット 2024-05-23 23.02.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3754242/2ad08956-521a-f1d9-a7a1-d4f192c9afac.png)
オブジェクト指向を学ばなかった話

C言語の時代は例えばソースファイルが10個あったらそれを10個のオブジェクトファイルへとコンパイルし、その10個のオブジェクトファイルを1つの実行ファイルへとリンクするという感じだった。つまり「最小コンパイル単位は個別のソースファイル」だった。
1つのソースファイルに変更を加えたらそのソースファイルだけを再コンパイルすればよく、他の9個のオブジェクトファイルは再利用が利いた。俺は「この原則はオブジェクト指向言語においても保たれるべきだ」と考えたが、苦難の道を歩むこととなった。
実際にはオブジェクト指向は、ソースファイルがツリー状になるため最小コンパイル単位は「変更を加えたファイルとそこから派生する枝葉ソースファイルすべて」となってしまった。俺にとっては意外なことだったが誰もそのことに対して文句を言わなかった。
歳月が流れ、Rustという言語が現れた。この言語は「一つの実行ファイル（またはライブラリファイル）に関わる全てのソースファイルを毎回コンパイルしなさい」と言っている。そうしても大丈夫なほどハードウェアが速くなったのだ。
俺はようやく重い荷物を降ろすことができた。Rustと同等の最小コンパイル単位でよければもっと見通しのいいオブジェクト指向言語はあり得たというのが証拠なのだ。
・関数は仮想関数の方がデフォルトであるようなC++
・メッセージオブジェクトは文字列ではなくメソッドテーブルのオフセットであるようなObjectiveC
・あるいはガベージコレクタは必要なかったかもしれないJava
そういったあり得たはずの言語と比べればコンパイラ型のオブジェクト指向言語はおかしなものばかりだ。そしてC言語を正しく、そして一挙に継承するのがRustなのだ。
【Azure】西日本リージョンでは申請しないとAzure Database for MySQLフレキシブルサーバがデプロイできない

:::note warn
2024年5月時点の情報です。
:::

直近で西日本リージョンにAzure Database for MySQLフレキシブルサーバを作る要件がありました。西日本リージョンは一時リソース枯渇問題で新規リソースをデプロイできない事態に陥っていましたが、最近はリソース不足も落ち着いた印象で、Azure Database for MySQLフレキシブルサーバも新規デプロイできるだろうと思っていました。しかし、念のため事前にデプロイできるか確認してみたところ、エラーが出てデプロイできないことが分かりました…。

↓ 西日本リージョンを選択した途端、こんなエラーが出ます
![01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2850747/f6f9cc5e-93d5-7742-22b0-e13884adb0e1.png)

このエラーが出るとサポートに連絡して制限解除してもらうしかありません。

### サポートに伝えるべき情報
ケースオープン時に以下を伝えましょう。
* 対象テナント名（～.onmicrosoft.com）
* 対象テナントID
* 対象サブスクリプションID　※ 制限解除はサブスクリプション単位です
* 何のサービスについて制限解除してほしいか
※ 今回の例だとAzure Database for MySQLフレキシブルサーバ
* 何個リソースをデプロイできる必要があるか

実際に制限解除の申請をしてみたところ、申請からデプロイできるようになるまで8営業日ほどかかりました。
※ ケースオープン時に必要な情報が不足していたり、解除連絡を受けて試したらエラー解消されておらず追加でエラー画面を送付したりといった対応があったので時間がかかりました。

申請してから実際に使えるようになるまでのリードタイムはケースバイケースだと思われるので、8営業日以上の時間がかかることも考えられます。西日本リージョンを使う際は事前にデプロイできるか確認しておくのがよいでしょう。サブスクリプション単位での許可になるので、サブスクリプションごとにデプロイできるか確認しておく必要があります。

### まとめ
西日本リージョンのリソース不足問題もだいぶ解消されてきたので、今後このような問題はなくなっていくと思われますが、Azure Database for MySQLフレキシブルサーバに限らず、デプロイしたことがないサービスを使う場合は西日本にデプロイできるのか？という確認を事前にしておくのがまだ無難そうです。
PC で利用できるマイクなどのオーディオ入力のリストで特定の名前のもののみ取得（p5.js Web Editor上で試す）

以下の内容の、続きのような記事です。

●PC で利用できるマイクなどのオーディオ入力のリストを p5.js Web Editor上で見てみる（ブラウザの MediaDevices） - Qiita
　https://qiita.com/youtoy/items/8339313b561c503ffe39

上記の記事では、以下のように全てのオーディオ入力を取得していたのを、今回は特定の名称のもののみ取り出します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/7aecac56-fa61-40d0-e921-be2728969a0b.png)

それをまた、p5.js Web Editor上で試します。

## 前回のプログラムの編集版
今回の内容のベースになるものとして、前回の記事のプログラムを少し編集した内容を掲載します。

```javascript
function setup() {
  createCanvas(400, 400);

  navigator.mediaDevices.enumerateDevices().then((devices) => {
    const inputList = devices.filter((device) => device.kind === "audioinput");
    console.log(inputList);
  });
}
```

まずは、以下の「filter()」を使ってオーディオ入力のみを取り出している状態です。

●Array.prototype.filter() - JavaScript | MDN
　https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/filter

## 特定の名前のもののみ取得
さらに、特定の名前のもののみ取得してみます。

### 特定の名称に完全一致するものを取得
まずは、特定の名称に完全一致するものを取得してみます。

とりあえず、名称は 2つとして実装してみます。

```javascript
let audioInputs;

function setup() {
  createCanvas(400, 400);

  const desiredLabels = [
    "【名称の文字列1（完全一致させる）】",
    "【名称の文字列2（完全一致させる）】",
  ];

  navigator.mediaDevices.enumerateDevices().then((devices) => {
    audioInputs = devices.filter(
      (device) =>
        device.kind === "audioinput" && desiredLabels.includes(device.label)
    );

    if (audioInputs.length < 2) {
      throw new Error("想定した2つのデバイスが見つかりません");
    }

    console.log(audioInputs);
  });
}
```

ここでは、desiredLabels として用意した名称のリストと、以下の「includes()」を組み合わせて、特定の名称のものを取り出しました。

●Array.prototype.includes() - JavaScript | MDN
　https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/includes

### 特定の名称に部分一致するものを取得
まずは、特定の名称に部分一致するものを取得してみます。

今度も、名称は 2つとして実装してみます。

```javascript
let audioInputs;

function setup() {
  createCanvas(400, 400);

  const desiredLabels = ["【名称の文字列1（部分一致させる）】", "【名称の文字列2（部分一致させる）】"];

  navigator.mediaDevices.enumerateDevices().then((devices) => {
    audioInputs = devices.filter(
      (device) =>
        device.kind === "audioinput" &&
        desiredLabels.some((label) => device.label.includes(label))
    );

    if (audioInputs.length < 2) {
      console.log(audioInputs.length);
      throw new Error("想定した2つのデバイスが見つかりません");
    }

    console.log(audioInputs);
  });
}
```

部分一致で取得するほうは、先ほども使った「[includes()](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/includes)」と合わせて、以下の「some()」を組み合わせました。

●Array.prototype.some() - JavaScript | MDN
　https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Array/some
SASプログラムをDatabricksに移行する際の注意点 - データ移行編

最近、SASからDatabricksへ移行する案件をいくつかこなしているが、移行に関する情報は非常に少ない。おそらく両方使える人材が少ないこと、難易度が高いなどの理由があると思われる。

そこでSASからDatabricksに移行する際の注意点をまとめてみる。SAS→Databricks移行のためには大きく分けてデータ移行とプログラム移行の2つある（気が向いたら管理周りについても書きます）。今回はデータ移行について書く。

## 前提
そもそもSASからDatabricksの移行とは、「SAS v9がインストールされたSASサーバーで管理されたデータセットおよびETLプログラムなどのアセットをDatabricksに移行する」ことを指す場合がほとんどだろう。本記事もその前提に立つが、SAS v9の製品群は非常に複雑なので、スコープをSAS言語で書かれたプログラム、Enterprise Guide（以下EG）で作られたプロセス（データフロー）とSASデータセットを移行対象とすることを前提とする。あとSAS Data Integrationは単純に使ったことが無いので対象外。

またユーザー管理やセキュリティも移行・もしくは再設定が必要だが、それについては言及しない。ただしデータ管理についての考慮事項は記載する。
現在のSASはSAS Viya環境も存在しているが、SAS ViyaからDatabricks移行については本記事の対象外とする。代わりに後日SAS ViyaとDatabricksの比較記事でも書く（多分ニーズはない）。

## データセットの移行
SASデータセットはライブラリ→データセットという階層で管理される。これはDatabricksでいうところのスキーマ→テーブルという階層に近い。階層構造はそのまま継承してしまって良いだろう。

問題はデータセット。SASデータセットはデータ型が数値か文字しかない、ガバガバな構造になっている。数値型に整数型、ブール（0,1のフラグ変数）、小数点型、さらには日付も含まれる。なので、データ移行の際には各テーブルの変数をどの変数型で定義するか、マッピングする必要がある。

## 厄介な文字変数のブランク
SASの文字変数はNULLが存在せず、欠損値は全て長さゼロのブランクとなる。安直に考えるとこのブランクは全てNULLにしてしまえば良いのだが、ここに落とし穴が潜む。SASではこのブランク値はNULLではないので、ブランク同士の結合ができてしまう他、文字列操作の関数の結果もNULLではなく普通に結果が返ってくる。
また、SASでは`WHERE var1 IS NULL` と`WHERE var1 =''`は同義なので、抽出結果は同じになるが、Databricksでは両者で結果が異なってくるので、ブランクをNULLに置換して移行する際はプログラムも精査が必要となる。


## フォーマット・ラベル
SASデータセットに設定されている変数ラベルやフォーマットは、Databricksに対応する機能は存在しない。ラベルについては必要に応じてコメントで対応する。フォーマットは残念ながら諦めるしかない。

### 実際の移行
移行を実施する際にはまずクラウドストレージにSASデータセットをアップロードし、Databricksからクラウドストレージ上のSASデータセットを読みにいく方法がシンプル。
SASデータセットを読み込む際は[spark-sas7bdat](https://github.com/saurfang/spark-sas7bdat)を使用することをお勧めする。`pandas.read_sas`はSASデータセットの読み込みが非常に遅いので、移行にかなり時間を取られる可能性がある。なおspark-sas7bdatはクラスターにインストールが必要なライブラリなので、事前にシステム管理者との調整が必要な点に注意。
買って来てすぐの管理者権限の無いPCでも動くPowerShellスクリプトの作り方

# 結論

```PowerChangePolicy.bat
<# :
@echo off & setlocal EnableDelayedExpansion
set BATCH_ARGS=%*
for %%A in (!BATCH_ARGS!) do set "ARG=%%~A" & set "ARG=!ARG:'=''!" & set "PWSH_ARGS=!PWSH_ARGS! "'!ARG!'""
endlocal & Powershell -ExecutionPolicy RemoteSigned -NoProfile -Command "$input|&([ScriptBlock]::Create((gc '%~f0'|Out-String)))" %PWSH_ARGS%
exit/b
: #>

ここ以降にPowerShellコードを書く
```

上記を作成し、拡張子`.bat`で保存して実行。

2024/11/08 追記: 実は`ExecutionPolicy`を変えなくても、`Powershell`コマンドへの外部流し込みの場合は動くようです。また、プロファイル（末尾URL参照）で環境設定などを特に設定していなければ、`-NoProfile`も必要無いそうです。それで良ければ以下のように少し短くできる。

```Power.bat
<# :
@echo off & setlocal EnableDelayedExpansion
set BATCH_ARGS=%*
for %%A in (!BATCH_ARGS!) do set "ARG=%%~A" & set "ARG=!ARG:'=''!" & set "PWSH_ARGS=!PWSH_ARGS! "'!ARG!'""
endlocal & Powershell -Command "$input|&([ScriptBlock]::Create((gc '%~f0'|Out-String)))" %PWSH_ARGS%
exit/b
: #>

ここ以降にPowerShellコードを書く
```

# 説明

PowerShell スクリプトをバッチファイル感覚で作ろうとすると・・・。
デフォルトだと、 PowerShell の ExecutionPolicy が`Restricted`になっていて`.ps1`スクリプトを実行できない。

```PowerShell:PowerShell
Get-ExecutionPolicy
# → Restricted
```

管理者権限があり、`RemoteSigned`以下に設定を落とせば、以後実行出来るようになる。ただし、セキュリティに懸念があるとか、そもそも管理者権限が無い事もある。

```PowerShell:PowerShell (管理者)
Set-ExecutionPolicy RemoteSigned
```

ExecutionPolicy にはスコープがあり、 Process スコープ（システム全体ではなくそのプロセスのみに有効）であれば管理者権限が無くても変更可能。また、 PowerShell 起動時にそれを指定出来る。
つまり、バッチファイルから ExecutionPolicy が RemoteSigned な PowerShell を起動し、そこにスクリプトを流し込めばいけそう。
その方法として、「バッチファイルとして起動し、自分自身を PowerShell スクリプトとして起動し直す （この時に PowerShell の設定を変えて起動）」という方法がある。
（セキュリティ的にダメな気がするんですが、出来てしまう。）

※ 本スクリプトは、特殊文字非対応だったり、一部のPathが空になるようです。もっと踏み込みたい方は、以下の参考元をお読みください。

# 別解

どこかで知りましたが、バッチファイルから`type`コマンドからのリダイレクトで流し込んでも動くそうです。

```PowerType.bat
rem `hoge.ps1`を用意した上で、以下を実行
type hoge.ps1 | powershell
```

ワンラインならこういうのもアリ

```PowerGetPolicy.bat
echo Get-ExecutionPolicy | powershell
```

`;`(セミコロン)で区切れば、複数コマンドも流し込めます。

```PowerGetPolicyDir.bat
echo Get-ExecutionPolicy; dir | powershell
```

# とっっても参考にさせて頂いた記事

マイナー加工してますが、ほぼ、以下の焼き直しというレベルです。
前者のスクリプトに、後者のポリシー変更を組み込んだだけです。

https://qiita.com/earthdiver1/items/cab769aad623a03a0f2d

https://qiita.com/kikuchi/items/59f219eae2a172880ba6

https://qiita.com/baku2san/items/dc8982a3cf9e7161d2ae
知っておくと便利なgitコマンドまとめ

# 初めに
本稿では、初心を大切にgitのコマンドでたまに利用する便利なコマンドを目的別に紹介します。

# ブランチの操作系

### ブランチのコピーを作成
ブランチのバックアップ作成時に利用できます。
```
git branch -c <コピー先ブランチ名>
```

### ローカルブランチの削除
```
git branch -d <ブランチ名>
```

### ブランチ名を変更する
```
git branch -m <変更後のブランチ名>
```

### リモートで削除されたブランチをローカルにも削除反映する
リポートリポジトリの最新情報を取得し、リモートに存在しないブランチは削除する。
⇒不要な作業ブランチの削除に便利
※一度リモートへプッシュしたブランチが対象
```
git fetch --prune
```

追跡されていないブランチも消したい場合は以下を実行
```
git remote prune origin
```


### ローカルの作業内容を捨ててリモートのブランチに合わせる
まずはgit fetchで最新のリモートブランチ情報をローカルに反映させます。
```
git fetch origin 
git reset --hard origin/<ブランチ名>
```

### 既存のブランチ一覧を確認する
ローカルのブランチを確認する場合は以下のコマンドで確認できます。
```
git branch または git branch --list
```

### リモート含めて既存のブランチ一覧を確認する
git fetchでまずは最新のリモートブランチ情報をローカルに反映させます。
-aオプションで、すべてのブランチ情報を確認できます。
```
git fetch
git branch -a
```

# コミットの操作系

### 直前のコミットを修正する
遡及的に最新コミットが変更されるため、pushする前に実施するか、或いは ```git push --force-with-lease origin <ブランチ名>``` でリモートのブランチを上書きする必要があります。

--amendオプションが先。間違えるとおかしなコミットメッセージになってしまうため、順番は注意が必要です。

```
git commit --amend -m "<コミットメッセージ>"
```

また、rebase コマンドを利用することで一つ以上前のコミットを修正することが出来ます。

```
git rebase -i HEAD~4
```

⇒ 表示されたコミット一覧の内で、pick をsquashに書き換えることで、一つ前のコミットにまとめることが出来ます。
git rebaseは以下のようにブランチの派生元を付け替えることにも利用できるので、慣れると非常に便利なコマンドです。

★今のブランチの作業内容（コミット群)について、mainブランチがベース(派生元)となるように付け替える場合
※リモートへプッシュする前に、このコマンドを実行して最新のブランチへ付け替えることで、マージする際にコンフリクトが発生することを防ぎ、スムーズな開発につなげることが出来ます。
```
git rebase main
```

### コミットメッセージを変えずに最新コミットに変更内容を追加する
コミットするほどでもないけど一箇所修正忘れていた！という場合などに便利です。
```
git commit --amend --no-edit
```

# 履歴確認系

### 過去のコミットごとの変更ファイルを確認する

```
git log --stat
```

### 特定のファイルの過去の変更履歴を確認する

```
 git log -p <ファイルパス>
```

### 過去のコミットを完結に確認する
```
git log --oneline
```

### 過去のコミットをブランチの分岐含め視覚的に確認する
```
git log --graph
```

# 差分確認系

### ステージングしている内容(git add 済)との差分を確認する
add する前に、どこに変更を入れたか確認するのに便利です。
```
git diff
```

### ステージングしている内容とローカルリポジトリの内容との差分を確認する
addした後に、どこに変更を入れたか確認するのに便利です。
```
git diff --staged
```
上記コマンドで確認し、add した内容をやっぱり取り消したい！となった場合は以下のコマンドでaddする前に戻すことが出来ます。
```
git restore --staged <ファイルパス>
```

### リモートリポジトリの特定のブランチとの差分を確認する
git fetchで最新のリモート資材情報を取得した上で実行。
```
git fetch
git diff origin/main hotfix_branch
```

### ブランチ間の差分確認を確認する
branch1を基準にブランチ2と比較します。
⇒ branch1の方が古いブランチとして扱われ、branch2で入った変更を差分として確認できます。
```
git diff branch1 branch2
```

### 変更量の取得
--shortstatオプションが利用できます。
```
git diff branch1 branch2 --shortstat
```

# まとめ
本記事でご紹介した主要コマンド・オプションを一部省略してまとめます。
|用途 |コマンド|
|--|--|
|ブランチのコピー|git branch -c |
|ブランチの削除|git branch -d |
|ブランチ名の変更|git branch -m|
|リモートの削除反映|git fetch --prune, git remote prune origin|
|ブランチ確認 | git branch --list, git branch -a |
|コミット修正 | git commit --amend |
|コミット履歴確認|git log --stat, --oneline, --graph |
|差分確認| git diff, git diff --staged, git diff --shortstat |

上記以外にも、cherry-pickやstash など開発に必須なコマンドは沢山ありますので、またの機会にまとめたいと思います。

皆様おすすめコマンドがございましたら是非ご教示いただければ幸いです。

最後までお読みいただきありがとうございました。

# 参考サイト
https://tracpath.com/docs/git-branch/
UbuntuにSlackを入れる

# UbuntuにSlackを入れる方法

1. [Slack](https://slack.com/intl/ja-jp/downloads/instructions/linux?ddl=1&build=deb)のページから`.deb`ファイルをダウンロードする
1. ターミナルを起動し`apt install`を実行する
    ```terminal
    sudo apt install path/to/slack.deb
    ```
1. インストールが完了するので、アプリを立ち上げサインインする
Godot アイコンの設定箇所(Windowsバイナリ用)

## 要約
- アプリケーションアイコンとWindows用アイコンのそれぞれに設定箇所がある
- プロジェクト設定とエクスポート設定の2か所です

環境：Godot4.2＋ C#(.Net Framework6.0)

## はじめに
　Windowsプラットフォーム向けのバイナリを作成する場合、GodotEngineで設定するアイコンは2種類あります。アプリケーションアイコンと出力先プラットフォーム用アイコン(Windowsなのでデスクトップ、エクスプローラー、etcに使われる)の２つです。

　１プロジェクトに1回しか設定しないので、良く忘れる自分用のメモ記事です。


## 設定箇所
　アプリケーション用アイコンはプロジェクト設定にあります。ここはではPNG等の画像ファィルを指定します。
 <img width="600" src="https://pbs.twimg.com/media/GOKbrE9bgAAFcMx?format=png&name=900x900">

　プラットフォーム毎のアイコンはエクスポートの設定にあります。ここではICOファィルを指定します。
 <img width="600" src="https://pbs.twimg.com/media/GOKcvGGa4AADtY2?format=jpg&name=medium">





## 設定結果
　アプリケーションアイコンはプロジェクト選択時とバイナリ実行時のウィンドウ左上、タスクバーに表示されます。
 （例としてアプリケーションアイコンにはBOSSの画像、Windows用アイコンにはザコ敵の画像を充てています）

  <img width="600" src="https://pbs.twimg.com/media/GORGbNLaUAAIRvr?format=png&name=medium">


プロジェクト選択時のアイコン

  <img width="600" src="https://pbs.twimg.com/media/GOKc-I4aQAAdwNj?format=jpg&name=medium">

ウインドウ左上のアイコン


  <img width="600" src="https://pbs.twimg.com/media/GOKjHCFbgAAV_IG?format=png&name=small">

タスクバーのアイコン

  <img width="600" src="https://pbs.twimg.com/media/GOKkDkoaoAEGeLA?format=png&name=360x360">

エクスプローラーで表示した時のアイコン

  <img width="600" src="https://pbs.twimg.com/media/GOKdpwOaYAAaIEH?format=jpg&name=medium">

 


## Windows用アイコン作成ツール
　私が使っているのはQuick Any2Icoです。
　作成ツールは何でも良いと思いますが、利用実績のあるものとして紹介しておきます。

 https://www.carifred.com/quick_any2ico/

　アイコンに使用する元画像を指定して16×16, 32×32, 48×48, 64×64, 128×128, 256×256のサイズを指定しておけば充分でしょう。 (ここでは元の画像が32×32なので、128×128の表示サンプルがぼやけてます）


  <img width="600" src="https://pbs.twimg.com/media/GOKmCW_aAAAZ626?format=png&name=900x900">
tailwindcss,reactで画像を背景として使用するための方法

# はじめに
どうも！フリーエンジニアKeiです
探してもなかなか使いたい内容が見つけられなかったのでまとめました。

# 問題
react+tailwindcssの環境で
背景（セクションごと）として画像を使いたいときの設定方法

# 解決方法
①tailwind.config.jsに追記
②App.jsxに追記

### ①tailwind.config.jsに追記
extend{}の中にbackgroundImage{}を追記する
・Bg1は関係者が分かりやすい名称
・url('```中は参照したい画像のリンク``` ')
・```my-img-name```は参照する画像の名称
今回はsrcにimg(ディレクトリ)を作成して、ディレクトリ内に画像を入れています。



```ruby:tailwind.config.js
theme: {
    extend: {
      backgroundImage: {
        Bg1: "url('../src/img/my-img-name.jpg')",
      },
    },
  },
```

### ②App.jsxに追記
挿入したいセクションに
```<div className=>bg-[url('../src/img/my-img-name.jpg')]</div>```を追記する
あとはcssで好きなようにいじってください。


```ruby:App.jsx
        <section id="#">
          <div className="bg-[url('../src/img/cat1.jpg')] min-h-screen bg-cover bg-center object-cover opacity-70">
            <div>
          </div>
        </section>

```



# おわりに
基礎だからか意外と探しても出てこないので簡単にまとめました。
外部キー制約と運用について

## 外部キーを導入するプロジェクトに入ってメリデメについて思ったことがあったのでメモ


## メリット
- 参照整合制
    - 参照先のデータが存在することを保証できる
- 削除制約
    - 子テーブルにレコードが存在する状態で親テーブルの関連行の削除を禁止できる
- ER図がリレーション含めて生成できる
    - 外部キーを貼っているとER生成するツールでそれも含めて図にしてくれる

## デメリット
- 開発・運用時のコスト増
    - `truncate` ができない(親テーブル及び子テーブル)
        - 一度外部キーを無効化するなどして対応が必要
    - 外部キーが貼られているカラムの変更ができない
        - 例えばintからstringに型変える場合などに、外部キーを無効化が必要
    - migrationツールなどがうまく適用できないケースが生まれる
        - 上記同様、外部キー制約で素直にALTER文を当てられない時など、じゃあここだけ手動で適用だな・・・みたいなことになる可能性がある。
- 単体テストで親のデータを必ず作成する必要がある(DBの部分をモックでなく直接DBに繋ぐ場合)
    - 子テーブルだけデータを入れると親にキーはないよと怒られるので、親テーブルもセットで作成が必要になる


## まとめ
基本的には外部キーは貼らない。貼っておきたい部分が出てきたらそこは採用する。それくらいの温度感が良いのかなと思いました。

チーム内で上記のようなメリット・デメリットを理解した上で方針を決めることも大事だと思います。外部キー制約による運用の工数はそれなりに高いかなぁと思っているので、それでも制約を入れるべきかですかね。(運用については実際の経験がないと、なかなか伝わりづらいところはありますが。。)SQLアンチパターンでは、外部キーを貼ることが推奨されていますが、あくまで一意見として受け取っておいて、自分たちで判断するのが大事かと思います。
また、外部キーを採用する場合、存在するテーブルで外部キーが貼れるテーブルすべてに採用するのではなく、ここは整合性担保しないと大変なことになるぞみたいなそういう部分について採用していく方が良いのかなぁと思いました。

参考記事
https://zenn.dev/praha/articles/2667cbb1ab7233?redirected=1
メモ：satisfiesで型推論を残すメリット

## 結論

ケースによって型アノテーションとas const satisfiesを使い分けましょう

現状の雑な理解：
- プリミティブ型の定数やオブジェクトリテラルはas const satisfiesで
- それ以外は型アノテーション

## 型推論の場合


```typescript
type Colors = "red" | "green" | "blue";
type RGB = [red: number, green: number, blue: number];

const palette = {
    red: [255, 0, 0],
    green: "#00ff00",
    blue: [0, 0, 255]
}
// 型アノテーションや型アサーションがなければTypeScriptのコンパイラがpalette変数の型を推論してくれる

const redComponent = palette.red
// -> redはnumber[]で推論される
// なのでインデックス外へのアクセスがコンパイルエラーにならない 😭
const outOfIndex = palette.red[3]
// -> undefined

const greenComponent = palette.green
// -> greenはstringに推論される
// なのでstringのプロパティの使用がコンパイルエラーにならない 😁
const upperCase = palette.green.toUpperCase();
// -> "#00FF00"
```


## 型アノテーションの場合

```typescript
type Colors = "red" | "green" | "blue";
type RGB = [red: number, green: number, blue: number];

const palette: Record<Colors, string | RGB> = {
//             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^　こういうやつ
    red: [255, 0, 0],
    green: "#00ff00",
    blue: [0, 0, 255]
}

const redComponent = palette.red
// -> redはstring | RGB に推論される
// なのでインデックス外へのアクセスがコンパイルエラーにならない 😭
const outOfIndex = palette.red[3]
// -> undefined

const greenComponent = palette.green
// -> greenはstring | RGB に推論される
// なのでstringのプロパティの使用がコンパイルエラーになる 😭
const upperCase = palette.green.toUpperCase();
//                             ^^^^^^^^^^^^^^^^
```

## satisfies演算子の場合

```typescript
type Colors = "red" | "green" | "blue";
type RGB = [number, number, number];

const palette = {
    red: [255, 0, 0],
    green: "#00ff00",
    blue: [0, 0, 255]
} satisfies Record<Colors, string | RGB>;
//^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ こういうやつ

const redComponent = palette.red
// -> redは[number, number, number]で推論される
// なのでインデックス外へのアクセスがコンパイルエラー 😁
const outOfIndex = palette.red[3]
//                            ^^^

const greenComponent = palette.green
// -> greenはstringに推論される
// なのでstringのプロパティの使用がコンパイルエラーにならない 😁
const upperCase = palette.green.toUpperCase();
// -> "#00FF00"
```

### 補足

定数として使う場合には`satisfies`単独よりも`as const satisfies`で使った方がimportしたファイル側でwideningしなくてよいらしい

https://zenn.dev/tonkotsuboy_com/articles/typescript-as-const-satisfies#import%E3%83%BBexport-%E3%81%A7%E3%81%AE-widening
AWS CDK開発に必要なスキルセット

AWS CDK開発に必要なスキルセットをまとめました。メンバーのアサイン時や、CDK学習時に参考にしてください。

## 全開発者に必須
#### ・git に関する基礎知識
日々のコード管理に必須です。

#### ・使用するプログラム言語に関する基礎知識
配列、辞書、（型）、繰り返し、条件分岐、関数、クラス、オブジェクト指向等の理解が必要です。フレームワーク等に関する知識は不要です。プログラムに関する基礎知識のないメンバーがいると、独自クラス等の利用が困難になります。この場合、プログラム言語を用いた抽象化という、CDKの良さを生かせません。

#### ・AWSサービスに関する基本的な知識
（AWS Certified Solutions Architect - Associate　レベル）
AWSの各サービスの公式サイトを読んで設定内容を理解するために必要です。

#### ・CDKおよびCloudFormationに関する基礎知識
[CDK](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib-readme.html)および[Cloudformation](https://docs.aws.amazon.com/ja_jp/AWSCloudFormation/latest/UserGuide/aws-template-resource-type-ref.html)の公式サイトを読んで適切な値を設定するために必要です。


#### ・エラーログを読んで解決する力
大量のコードを書いて一度にデプロイすると、cdk synth時やcdk deploy時によくエラーが発生します。エラー解決経験が浅い場合は、頻繁にデプロイして成功体験を積みましょう。

#### ・（あると嬉しい）英語リーディング力
AWSやCDKの公式ドキュメントの日本語は分かりにくい表現が多いです。時にはドキュメントが英語だけのこともあります。TOEIC800点レベルのリーディング力があれば、ストレスなく開発できます。


## 技術リーダークラスに必要な力
アプリと基盤両方の知識が必要です。1人ですべてカバーできない場合、アプリ経験者と基盤経験者でタッグを組むと良いでしょう。

#### ・AWSのベストプラクティスに関する知識
（AWS Certified Solutions Architect - Professional　レベル）
複数の選択肢からプロジェクトの特性に合わせ、適切なAWSアーキテクチャを選択する力が必要です。

#### ・AWSの構築経験、レビュー経験
メンバーの成果物をレビューし、設定が誤っている点を指摘できる力が必要です。

#### ・CDKのベストプラクティスに関する知識
[cdkのベストプラクティス](https://docs.aws.amazon.com/prescriptive-guidance/latest/best-practices-cdk-typescript-iac/introduction.html)とプロジェクトの制約を踏まえ、適切なコード規約やコードの構造、実装方法やテスト方法の選択をする必要があります。例えば、適切なスタック間参照方式を利用しないと、エラーが多発します。

https://dev.classmethod.jp/articles/aws-cdk-props-cross-stack-reference-problem-and-handle/


#### ・アプリケーションの詳細設計、製造経験
アプリ開発で利用するコード規約やクラス図等を作成しないと、メンバーがバラバラにコードを書いてしまうため、良いCDKコードはできません。

#### ・開発環境構築力（リーダでなくても誰か一人あれば可）
CDK開発ではエディタやgit等のツールを利用します。社内のセキュリティ規制が強い場合、ネットワーク関連の申請が必要になることもあります。開発環境構築方法を手順化し、開発前に全メンバーに共有しましょう。開発環境の構築が遅れると、全員の開発が遅延します。

#### ・ネットワーク構築経験（リーダでなくても誰か一人あれば可）
ネットワークの構築には、専門的な知識が必要です。誤った設定をすると、疎通トラブルやセキュリティ上のリスクが発生します。ネットワーク構築経験がある人を一人以上アサインしましょう。

#### ・AWS上でのデータベース構築経験（リーダでなくても誰か一人あれば可）
データベースのデプロイには、１回あたり１時間近くの時間がかかり、簡単に何度もトライ＆エラーを繰り返せません。データベース構築経験者がいると構築が楽になるでしょう。

#### ・IAMに関する有識者（リーダでなくても誰か一人あれば可）
CDKではgrantメソッドが使えるものの、IAMの権限漏れエラーは良く発生します。経験者がいると安心です。

#### ・CDK構築経験
CDK構築経験者がいると、前回のアセットを再利用し、よくあるエラーなどを回避できます。経験者がいればベストですが、CDK経験者がいない場合、上記のスキルを持った人をアサインして、リスクを最小化しましょう。

#### ・マネジメント経験
日々の進捗やプログラムの製造で発生する課題を管理する力が必要です。特にCDK開発経験が浅い場合、大量の課題が発生します。進捗状況や課題解決状況を自動で視覚化できるツールが利用できると便利です。


## まとめ
CDK開発においては、CDKコードを書けることが何よりも重要です。コードをかけない人をアサインしても、遅延が拡大するだけです。基盤メンバーとアプリ開発経験者を組み合わせ、コードを書ける人を増やしましょう。

本記事で必要なスキルを学んだら、以下記事を参考にＣＤＫ学習をスタートしましょう！

https://qiita.com/Figure_Skate_IT/items/83a395d26567f01b3ae2
Amazon OpenSearch ServiceをVPC内に配置して、Cloud9でProxyしてOpenSearch Dashboardにアクセスする

Cloud9のNginxのプロキシサーバを立てるだけです。すいません。

```conf:default.conf
server {
    listen       80;
    server_name  localhost;

    location / {
        proxy_pass https://vpc-***.***.es.amazonaws.com/;
    }
}
```

Nginx起動

```shell
docker run \
 -p 8080:80 \
 -v `pwd`/default.conf:/etc/nginx/conf.d/default.conf:ro nginx
```

`Preview`メニューの`Preview Running Application`を選択

ログインダイアログが出るのでログイン

URLの末尾に`_dashboards`を追加

`https://*****.vfs.cloud9.*****.amazonaws.com/_dashboards`

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/b2ccbc72-da4e-5204-44a9-69228a380cab.png)

理論上は、VPC内に立ち上げたWebアプリであれば、何でもアクセスできると思われます。
ChatGPTにクライアントのロールプレイをしてもらってウォーターフォール開発の練習をしたい(プロンプト編)

## 対象読者
- ChatGPTを使った事がある。使っている
- システム開発の手順を理解しているが、実践した経験はない
- 実際に(疑似)クライアントと会話をしながらシステム開発を進めていく練習がしたい

以上に当てはまる方を対象に書いています。

## まずはChatGPTにロールプレイをしてもらって話を切り出します

以下の記事を参考にさせていただきました。
[ChatGPTにキャラクター性を与えるプロンプト集](https://note.com/clirea/n/n3f8f5c7ea547)

クライアント役のプロンプトはこちら
```
======== 命令 ========
以下の「指示」と「条件」に従い、「倉井 安人」と言う名前でロールプレイを行ってください。

======== 指示 ========
あなたはChatbotとして、以下の「条件」に従い「倉井 安人」を演じます。

======== 条件 ========
* Chatbotの名前は、倉井 安人です。
* 倉井 安人は一般的なビジネスパーソンです。
* 倉井 安人は今回作るTODOアプリのユーザーです。
* 倉井 安人は提示されたドキュメントを一般的なビジネスパーソンの視点で読みます。
* 倉井 安人は提案された内容に関して、意見を返します。
* 倉井 安人はユーザーなので、「受け身の姿勢」で話します。

[チャットなのでできるだけ30文字以下で1文で答えてください。]
```

ChatGPTとの会話の最初にプロンプトを直接投げればロールプレイが始まります。

そして上司役もChatGPTにやってもらい、サポートを受けましょう。
プロンプトはこちらです。
````
======== 命令 ========
以下の「指示」と「条件」に従い、「水谷 富緒流」と言う名前でロールプレイを行ってください。

======== 指示 ========
あなたはChatbotとして、以下の「条件」に従い「水谷 富緒流」を演じます。

======== 条件 ========
* Chatbotの名前は、水谷 富緒流です。
* 水谷 富緒流はシステムエンジニアです。
* 水谷 富緒流は上司です。
* 水谷 富緒流は上司としてウォーターフォールの手順を教えます。
* 水谷 富緒流は上司としてウォーターフォールに沿って必要なドキュメントを教えます。
* 水谷 富緒流は上司なので、「手順を踏んで丁寧に教えます」。

======== 条件の補足 ========
[ウォーターフォールの各ステップに沿って思考を進めます]
[必要に応じて各ステップの間に100文字程度のアドバイスをします]
[以下の「形式1,形式2,形式3」の中から、必要な形式を選んで手順を教えます]
======== [形式1] ========
# 現在のステップ
現在のステップの概要
## 現在のステップの成果物
- 成果物のリスト

### 成果物の名前
- 成果物の説明

```
成果物の一例
```

## 次のステップ

======== [形式2] ========
# 前回のステップ
前回のステップの成果物の評価


======== [形式3] ========
疑問・評価・回答・アドバイス等
````
こちらもChatGPTとの会話の最初にプロンプトを直接投げればロールプレイが始まります。

上司役のChatGPTと相談しつつウォーターフォールの成果物とされるドキュメントを作って、クライアント役のChatGPTにヒアリングをしながら作ったドキュメントを基に話を進めて行くことを想定しています。

まだウォーターフォールの具体的な内容は話していないので、進めていくにつれ、プロンプトに変更があるかもしれません。予めご了承ください。
プロンプトは改良を加えられる部分が多いと思いますので、よろしかったらどのような改良を加えたかコメントで教えていただけると嬉しいです。

## 参考文献
[ChatGPTにキャラクター性を与えるプロンプト集](https://note.com/clirea/n/n3f8f5c7ea547)
ハッシュ探索法

## ハッシュ探索法とは

探索しやすいようにあらかじめ関数を使ってデータを格納しておき、探索の際は格納するのに使った関数を使ってデータを探す探索法。

### データを格納するアルゴリズム

#### 対象データ

```php
$arrayD = [12, 25, 36, 20, 30, 8, 42];
```

#### ハッシュ関数
ハッシュ値 = `$arrayD`のデータ % 11

#### ハッシュ化したデータを格納する配列

0が11個の配列

```php
$arrayH = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];
```
#### フローチャート

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2733055/2c864156-27d4-ac94-f10b-681119f28ee7.png)

#### 処理結果

結果として、以下のような配列が作られる
```php
$arrayH = [42, 12, 0, 25, 36, 0, 0, 0, 30, 20, 8];
```

### データを探索するアルゴリズム

#### 対象データ

ハッシュ化された配列 `$arrayH`
```php
$arrayH = [42, 12, 0, 25, 36, 0, 0, 0, 30, 20, 8];
```

#### 探すデータ

x：標準入力から入力


#### フローチャート

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2733055/88e1f0c2-36b8-b214-7fe3-f989b7341cce.png)
Amazonの商品情報をクリップボードにコピーするブックマークレット

メルカリで出品する時にいちいち商品情報を入力するのが面倒なので、開いているAmazonのページから商品情報を取得してクリップボードにコピーするブックマークレットを作りました。

Amazonのページを開いた状態で、ブックマークレットをクリックすると、クリップボードに保存されます。
そのままメルカリなどにペーストしてください。
Apple製品同士だとMacからiPhoneにコピペできるから便利ですよね。

## 取得できる情報

- 商品名
- 現在価格
- 商品概要

など

## ブックマークレット

CodePenのページからリンクをブックマークバーにドラッグ&ドロップすれば簡単です。

<p class="codepen" data-height="300" data-default-tab="html,result" data-slug-hash="dyrMbov" data-pen-title="Amazonの商品情報をクリップボードにコピーするブックマークレット" data-user="Int314" style="height: 300px; 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/Int314/pen/dyrMbov">
  Amazonの商品情報をクリップボードにコピーするブックマークレット</a> by Usakoro (<a href="https://codepen.io/Int314">@Int314</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

## コード
```
javascript: (function () {
  const getTextContent = (selector, parent = document) => {
    const element = parent.querySelector(selector);
    return element ? element.innerText.trim() : '';
  };

  const getPrice = () => {
    const selectors = [
      '#tmm-grid-swatch-HARDCOVER .slot-price',
      '#tmm-grid-swatch-PAPERBACK .slot-price',
      '#tmm-grid-swatch-OTHER .slot-price',
      '#corePriceDisplay_desktop_feature_div .a-price-whole',
      '#corePrice_desktop .a-price > .a-offscreen',
    ];
    for (const selector of selectors) {
      const price = getTextContent(selector);
      if (price) return price;
    }
    return '';
  };

  const getAuthors = (withHash = false) => {
    return Array.from(document.querySelectorAll('#bylineInfo > span > a'))
      .map(author => withHash ? `#${author.innerText.replace(/\s+/g, '')}` : author.innerText.trim())
      .join(withHash ? ' ' : ' / ');
  };

  const getProductOverview = () => {
    return Array.from(document.querySelectorAll('#productOverview_feature_div tr'))
      .map(row => {
        const header = row.querySelector('td.a-span3')?.innerText.trim() || '';
        const value = row.querySelector('td.a-span9')?.innerText.trim() || '';
        if (header && value) {
          return `${header}：${value}`;
        }
      })
      .join('\n');
  };

  const output = [
    // `URL：${window.location.href}`,
    `「${getTextContent('#productTitle')}」`,
    `Amazon価格：${getPrice()}`,
    `${getAuthors()}\n`,
    `${getProductOverview()}\n`,
    `${getTextContent('#drengr_desktopTabbedDescriptionOverviewContent_feature_div')}\n`,
    `${getTextContent('#drengr_DesktopTabbedDescriptionOverviewContent_feature_div')}\n`,
    `${getTextContent('#bookDescription_feature_div')}\n`,
    `${getTextContent('#feature-bullets ul')}\n`,
    `${getAuthors(true)}\n`,
  ].filter(line => line).join('\n').replace(/\n{2,}/g, '\n\n');

  navigator.clipboard.writeText(output).then(() => {
    alert('コピーしました！');
  }).catch(err => {
    console.error('コピーに失敗しました：', err);
  });
})();
```
大文字、小文字を区別して変換するプログラム

大文字、小文字を区別して変換するプログラミング課題です。


>問題
>あなたは、とある会社で文字列の校正を行う仕事をしています。
>
>校正ルール表と校正ルールに則って、校正を行います。
>校正ルール表は以下の条件を満たす、長さ 26 の文字列です。
>
>・i 番目の文字は英字アルファベット順で i 番目の文字の大文字または小文字
>
>校正ルールは以下のとおりです。
>
>・校正する文章に登場する全ての英字アルファベットについて、校正ルール表に書かれた英字アルファベットと 大文字/小文字 が一致するように書き換える
>
>校正ルール表および校正前の文字列が与えられるので、校正ルールに従って校正を行った後の文字列を出力するプログラムを作成してください。


という問題です。
Rubyで実装しました。

```Ruby
str1 = gets.chomp
str2 = gets.chomp
array1 = str1.split("")
array2 = str2.split("")
num1 = array1.length
num2 = array2.length
ans = ""
for i in 0...num2 do
    char1 = array2[i]
    char2 = char1.upcase
    char3 = char2.downcase
    for j in 0...num1 do
        target_s = array1[j]
        if char2 == target_s then
            ans = ans + char2
        elsif char3 == target_s then
            ans = ans + char3
        end

    end
end

puts ans
```

実行した結果です。

![aaaaaa.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710584/1e3ed9c9-542a-9744-251c-0fbaa8da0661.jpeg)
お邪魔なalt、使ってない？

- 「altはないより、ある方がいいでしょ。」
- 「リンターに怒られるから、取りえずalt入れてるよ。」
- 「altナニソレオイシイノ」

このどれかに共感した方、この先へお進みください。

## ようこそ （たいしたこと言ってないので読み飛ばしてどうぞ）

まず、ここまで進んで来てくれてありがとうございます。
初手でこんなことを書いておいてなんですが、かく言う僕もリンターとかに怒られるからとりあえずエイヤッでそれっぽいaltをつけていました。
が、去年からアクセシビリティに関するサービスに携わるにあたってaltと少し仲良くなれたので、今まで僕が勘違いしていたことを通して少しでもアクセシブルなウェブの世界に貢献できたら幸いです。

## altとは （ここから本題）
まず、みなさん承知の上だと思いますが、altとはなんぞやっていうのをMDNさんで見てみましょう。

> HTMLImageElement の alt プロパティは、<img> 要素で指定された画像が読み込まれない場合に表示する予備（代替）テキストを指定します。

参考: [HTMLImageElement: alt プロパティ](https://developer.mozilla.org/ja/docs/Web/API/HTMLImageElement/alt)

そうです、altは代替テキストです。
**画像の代替**として、主にスクリーンリーダーなどの支援技術によって使用されます。

## お邪魔なaltとは

画像の代替なのにお邪魔もなにもあるの？って思いますよね。
なので、わかりやすいようにサンプルサイトでスクリーンリーダーを使った場合を見ていきます。

このようなニュース記事一覧のページがあるとします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210755/decb93ce-ff08-cfbe-7140-03af31b15fe9.png)

HTMLはこんな感じ。（style等は削除してます）
```html
    <section>
      <h2>ニュース</h2>

      <ul>
        <li>
          <img src="img/feature1.jpg" alt="すっごくいい感じのニュース記事のタイトルです">
          <p>すっごくいい感じのニュース記事のタイトルです</p>
        </li>

        <li>
          <img src="img/feature2.jpg" alt="ほんとにすっごくいい感じのニュース記事のタイトルです">
          <p>ほんとにすっごくいい感じのニュース記事のタイトルです</p>
        </li>

        <li>
          <img src="img/feature3.jpg" alt="めちゃくちゃすっごくいい感じのニュース記事のタイトルです">
          <p>めちゃくちゃすっごくいい感じのニュース記事のタイトルです</p>
        </li>
      </ul>
    </section>
```


一見すると、ちゃんと記事のタイトルaltにも入ってるしいいじゃん。と思うコードですが...
スクリーンリーダーで読み上げてみると、このようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210755/64dd0052-82ef-6d04-bbf2-d14080e1b9b6.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/210755/229ae867-c4e5-4f71-26a4-7f1b74fdc236.png)

「すっごくいい感じのニュース記事のタイトルです、イメージ」
と読み上げられ、次の要素へ行くと
「すっごくいい感じのニュース記事のタイトルです」
と読み上げられてます。

同じことが二回続いて読み上げられてますよね。
スクリーンリーダーでは要素を読み飛ばしていくことが難しいため、同じことを二回読み上げられることで少なからずアクセシビリティは下がってしまいます。
なので、対処法としては、このようになります。
1. 読み上げなくても良い画像の場合は、altを空文字に設定する
1. 意味のある画像の場合は、その画像を説明できるaltを設定する

例）altを空文字にする場合
```html
    <section>
      <h2>ニュース</h2>

      <ul>
        <li>
          <img src="img/feature1.jpg" alt="">
          <p>すっごくいい感じのニュース記事のタイトルです</p>
        </li>

        <li>
          <img src="img/feature2.jpg" alt="">
          <p>ほんとにすっごくいい感じのニュース記事のタイトルです</p>
        </li>

        <li>
          <img src="img/feature3.jpg" alt="">
          <p>めちゃくちゃすっごくいい感じのニュース記事のタイトルです</p>
        </li>
      </ul>
    </section>
```
このように空文字を設定した場合は、画像の読み上げはスキップされるので必要な文言のみスクリーンリーダーが読み上げてくれるようになります。

## じゃあaltはどうつけるの

そう思いますよね。正直正解なんてものはないと思ってるので難しいですが、
「画像自体がなにかの情報のときや、画像を読み上げることで意味が生まれるもの、なにかの説明となるもの」
に対してつけるっていうイメージが個人的にはしっくり来てます。

とはいえ、感覚でしか言ってないので、ちゃんとした条件がしりたい！っていう人はW3Cが出してるこちらのページがとっても参考になると思います。
https://www.w3.org/WAI/tutorials/images/decision-tree/ja

## まとめ

僕自身まだまだ足りない知識の中手探りでやってますが、altなど、アクセシビリティに関することは、自分でスクリーンリーダーを使用してみたり、キーボードのみで操作してみたりすると解像度がグンと深まるのでおすすめです。
ほんの触り的な内容ですが、これで少しでもaltと仲良くなれた人がいたら嬉しいです。

最後まで読んでいただきありがとうございました。

## おまけ
現在在籍している株式会社Kivaでは延長保証サービスや、ウェブアクセシビリティに関するサービス（[ユニウェブ](https://hellouniweb.com/)）を運営しています。
ウェブアクセシビリティに興味がある方は一緒に働きましょう！！
【SQL】AccessのSQLで3つ4つテーブルを結合する【Access】

AccessのSQLでは、2つのテーブルを結合する場合は普通に`LEFT JOIN`するだけです。

ところが3つ以上のテーブルを普通に結合しようとするとエラーになります。

なんだこれと思って調べてみると、AccessのSQLだけ一筋縄ではいかないようです。

分かりにくくて難しいです…

## 3つのテーブルを結合
3つのテーブルを結合（LEFT JOIN）する場合はこうなります。

```sql
SELECT *
FROM (TABLE01 AS T1
LEFT JOIN TABLE02 AS T2 ON (T2.No = T1.No))
LEFT JOIN TABLE03 AS T3 ON (T3.No = T2.No)
;
```
1つ目のテーブルと2つ目のテーブルをカッコでくくってやる必要があるようですね。

## 4つのテーブルを結合

4つのテーブルを結合する場合だとこうなります。
```sql
SELECT *
FROM ((TABLE01 AS T1
LEFT JOIN TABLE02 AS T2 ON (T2.No = T1.No))
LEFT JOIN TABLE03 AS T3 ON (T3.No = T2.No))
LEFT JOIN TABLE04 AS T4 ON (T4.No = T3.No)
;
```

## 5つのテーブルを結合
5つのテーブルを結合する場合はこうなります。
これ以降は括弧がどんどん増えていくことになります。

```sql
SELECT *
FROM (((TABLE01 AS T1
LEFT JOIN TABLE02 AS T2 ON (T2.No = T1.No))
LEFT JOIN TABLE03 AS T3 ON (T3.No = T2.No))
LEFT JOIN TABLE04 AS T4 ON (T4.No = T3.No))
LEFT JOIN TABLE05 AS T5 ON (T5.No = T4.No)
;
```
【Powershell】フォルダの容量を取得する

WindowsのPowershellで、サブフォルダも含めたフォルダのサイズを調べたいとき。

指定されたフォルダパスのフォルダサイズを再帰的に取得し、その結果をファイルに出力します。

## スクリプト

```powershell
#容量を取得するフォルダパス
$filePath = "C:\"

#スクリプトのパス
$scriptRoot = Split-Path -Path $MyInvocation.MyCommand.Path
#スクリプトのパスに移動
cd $scriptRoot

"処理開始：" + (Get-Date).ToString("yyyy/MM/dd HH:mm:ss")

#指定パスのフォルダサイズを再帰的に取得（サブフォルダも含む）
#取得結果はスクリプトパスに出力
Get-ChildItem $filePath `
    | Sort LastWriteTime `
    | Select-Object Name, LastWriteTime, @{ name = "Size"; expression = { `
            [math]::round((Get-ChildItem $_.FullName -Recurse -Force `
                | Measure-Object Length -Sum `
            ).Sum ) `
    } } | Format-Table -AutoSize > FolderSize.txt

"処理終了：" + (Get-Date).ToString("yyyy/MM/dd HH:mm:ss")
```

## 詳しい説明

### 容量を取得するフォルダパス
```powershell
$filePath = "C:\"
```
C:\ ドライブのサイズを取得するためのフォルダパスを設定します。
取得したいフォルダパスをここで指定します。

### スクリプトのパスを取得し、そのパスに移動

```powershell
$scriptRoot = Split-Path -Path $MyInvocation.MyCommand.Path
cd $scriptRoot
```
スクリプトのパスを取得し、そのディレクトリに移動します。

### タイムスタンプを表示

```powershell
"処理開始：" + (Get-Date).ToString("yyyy/MM/dd HH:mm:ss")
```
```powershell
"処理終了：" + (Get-Date).ToString("yyyy/MM/dd HH:mm:ss")
```
処理の開始時刻と終了時間を表示します。

### 指定パスのフォルダサイズを再帰的に取得

```powershell
Get-ChildItem $filePath `
    | Sort LastWriteTime `
    | Select-Object Name, LastWriteTime, @{ name = "Size"; expression = { `
            [math]::round((Get-ChildItem $_.FullName -Recurse -Force `
                | Measure-Object Length -Sum `
            ).Sum ) `
    } } | Format-Table -AutoSize > FolderSize.txt
```

- `Get-ChildItem`コマンドレットを使用して、指定されたパス（$filePath）のフォルダとファイルを取得します
- 取得したアイテムは`LastWriteTime`でソートします
- `Select-Object`を使用して、フォルダ名 (Name)、最終更新日時 (LastWriteTime)、フォルダサイズ (Size) を選択します
- `Size`は、フォルダ内のすべてのファイルのサイズの合計を再帰的に計算し、四捨五入しています
- 最後に、これらの情報をテーブル形式`Format-Table`でファイル（FolderSize.txt）に出力します
Railsでの予約システムにおける非同期スロット更新とエラーハンドリング

## はじめに
今回は、Railsでの予約システムにおける非同期スロット更新の実装方法と、発生したエラーの解決方法について共有します。

## 問題点

予約システムの開発中に、日付を変更するたびにスロット情報を非同期で更新しようとしましたが、以下のエラーが発生しました。

```plaintext
ActiveRecord::RecordNotFound - Couldn't find Company with 'id'=undefined:
  app/controllers/reservations_controller.rb:207:in `update_slots'
```

## 解決方法

エラーの原因は、JavaScriptで`company_id`が正しく取得されず、リクエストに`undefined`が渡されていたことです。以下の手順で解決しました。

### ビューの修正

`company_id`を含むデータ属性を日付入力フィールドに追加します。

```erb
<%= form.date_field :date, id: 'for_customer_index_date_field', value: @date.strftime('%Y-%m-%d'), class: 'form-control', data: { company_id: @company.id } %> <!-- ここでdata-company-id属性を追加 -->
```

### JavaScriptの修正

データ属性から`company_id`を取得し、リクエストに含めるようにします。

```javascript
document.addEventListener('turbo:load', function() {
  const dateField = document.querySelector('#for_customer_index_date_field');
  if (dateField) {
    const companyId = dateField.dataset.companyId;
    const scheduleTitle = document.querySelector('.page-index');
    let companyName = '';

    if (scheduleTitle && scheduleTitle.textContent) {
      const match = scheduleTitle.textContent.match(/^(.*?)の/);
      if (match) {
        companyName = match[1];
      }
    }

    const debouncedFetch = debounce(function(date) {
      fetch(`/reservations/update_slots?date=${date}&company_id=${companyId}`, { // company_idをリクエストに含める
        headers: {
          'Accept': 'application/json',
          'X-Requested-With': 'XMLHttpRequest',
          'X-CSRF-Token': document.querySelector("[name='csrf-token']").content
        },
        credentials: 'include'
      })
      .then(response => response.json())
      .then(data => {
        if (data && data.services && data.time_slots !== undefined) {
          updateTable({ ...data, date });
        } else {
          console.error("Invalid data format received:", data);
        }
      })
      .catch(error => console.error('Error fetching data:', error));
    }, 500);

    dateField.addEventListener('input', function() {
      debouncedFetch(this.value);
    });
  }
});
```

### コントローラーの修正

`update_slots`アクションで`company_id`を正しく取得し、処理を行います。

```ruby
def update_slots
  date = params[:date]
  @date = Date.parse(date)
  @company = Company.find(params[:company_id])
  @time_slots = generate_time_slots(@company)
  @non_business_day = ScheduleService.is_non_business_day?(@company, @date)
  @services = @company.services
  @service_availability = ScheduleService.calculate_availability(@company, @date, @services, @time_slots)
  response_data = {
    services: @services.map { |service|
      {
        name: service.name,
        duration: format_duration(service.duration),
        time_slots: @time_slots.map { |slot| { availability: @service_availability.dig(service.id, slot) || '-' } },
      }
    },
    time_slots: @time_slots,
    non_business_day: @non_business_day
  }
  render json: response_data
end
```
初めてのQiita

# あらすじ
俺はWEBエンジニアになるべくプログラミングの勉強をする男。
これまでGithubで勉強記録を書いてきたが、ブログのように書きたいと思い立ちQiitaに行き着く。
ここならなにか有益な情報を書いた時に、悩める人のもとにヒットするはず…。

# 自己紹介
二十代半ば、鬱病と社交不安障害がだいぶ良くなってきてアクティブになってきた。
あれだけ生きることが面倒だったのに、今や週5で筋トレと勉強をする日々。シャワーもほぼ毎日浴びるようになった。
これだけアクティブになれたのには家族、環境、病院、薬の他に、とある憧れができたからだ。
1. [peppy](https://ppy.sh/)と兄に対する憧れ
2. 稼いで家を建てたい
3. リモートワークへの憧れ

特に1が大きい。
兄に対する尊敬はさておき、peppyは人生で一度は会いたいと思っている。
ダメ元で会いたい旨をメールで伝えたこともあり、「今度そっち行く機会があったら連絡するよ」と肯定的な返信を頂けた。
きっと今更覚えていないだろうが、返事がもらえて本当に嬉しかった…。

自己紹介がクソ長くなりそうで読む人も「知らねえよ！」となるのでこれにて終わります。
俺がいかに[peppy](https://ppy.sh/)が好きかということだけ覚えていただければと思います。

それでは　ノシ

[suzukidog | GitHub](https://github.com/suzukidog)
【SQLServer】SQLで指定時間待機（Sleep）させる

ストアド内で時間がかかる処理を再現したい。

C#でいう`Thread.Sleep`処理のような、指定時間の間ブロックさせたい時は「WAITFOR」が使えます。

- 指定時間の間待機させたい時は：`WAITFOR DELAY`
- 指定時間まで待機させたい時は：`WAITFOR TIME`

```sql
-- 30秒待機したいとき
WAITFOR DELAY '00:00:30';

-- 12時まで待機したいとき
WAITFOR TIME '12:00:00';
```
【C#】foreachでカウンタ変数を使わずにindexが知りたい

`for`や`foreach`でループの回数を知りたい場合、こんな感じでループの外にカウンタ変数を定義してループ内でインクリメントしてました。

```csharp
List<string> list = new List<string> { "a", "i", "u", "e", "o" };
int index = 0;

foreach (var value in list)
{
    Console.WriteLine("value = {0}, index = {1}", value, index);
    index++;
}
```

この`index`を定義せずにループの回数を調べる方法です。

### LINQのSelectメソッドを使用する

DataTableで使う時は`dataTable.AsEnumerable()`とするとSelectが使えます。

```csharp
using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        List<string> list = new List<string> { "a", "i", "u", "e", "o" };

        // 値とインデックスのペアを作成
        var indexedList = list.Select((value, index) => new { value, index });

        foreach (var item in indexedList)
        {
            Console.WriteLine($"value = {item.value}, index = {item.index}");
        }
    }
}
```

実行結果
```
value = a, index = 0
value = i, index = 1
value = u, index = 2
value = e, index = 3
value = o, index = 4
```

### LINQのZipメソッドを使用する

Zipメソッドは2つの要素を結合して、それらの要素をペアにした新しい要素を作成してくれます。
1つ目のシーケンスとしてリストを、2つ目のシーケンスとしてインデックスの範囲を使用してます。

`Enumerable.Range(0, list.Count)`で、0からリストの要素数までの連続した数字を作成します。

`Select`の方がわかりやすいと思う。

```csharp
using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        List<string> list = new List<string> { "a", "i", "u", "e", "o" };

        // リストとインデックスの範囲をZipメソッドで結合
        var zipped = list.Zip(Enumerable.Range(0, list.Count), (value, index) => new { value, index });

        foreach (var item in zipped)
        {
            Console.WriteLine($"value = {item.value}, index = {item.index}");
        }
    }
}
```

```
value = a, index = 0
value = i, index = 1
value = u, index = 2
value = e, index = 3
value = o, index = 4
```

### タプルを使用する

タプルはC#7.0以降（.NET Framework 4.7以降、または.NET Core以降）で使用できます。
古い環境では`Linq`の`Select`を使う方法が無難でしょうか。

`Select`ではこうだったところが、
`list.Select((value, index) => new { value, index });`
タプルを使うとこうなります。
`list.Select((value, index) => (value, index));`
少しスッキリして良いのではないでしょうか！

```csharp
using System;
using System.Collections.Generic;
using System.Linq;

class Program
{
    static void Main()
    {
        List<string> list = new List<string> { "a", "i", "u", "e", "o" };

        // 値とインデックスのタプルを作成
        var indexedList = list.Select((value, index) => (value, index));

        foreach (var item in indexedList)
        {
            Console.WriteLine($"value = {item.value}, index = {item.index}");
        }
    }
}
```

コメントで教えてもらったんですけど、タプルの場合はこんな感じでもっとシンプルに書けました😊
```csharp
foreach ((string value, int index) in list.Select((v, i) => (v, i)))
{
    Console.WriteLine($"value = {value}, index = {index}");
}
```

実行結果
```
value = a, index = 0
value = i, index = 1
value = u, index = 2
value = e, index = 3
value = o, index = 4
```
UE勉強日記2

blueprintのcharacterにAI to Moveを追加し、
SpawnActorでcharacterを生成したが、AI to Move作動しませんでした。

原因はPawn中のAuto　Prossess AIのデフォルト値はPlaced in worldです。
![スクリーンショット 2024-05-23 183556.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574567/2627b37a-64a9-c13b-f10f-95e7c3670d44.png)

Auto　Prossess AIの値をPlaced in World or Spawnedで解決
![スクリーンショット 2024-05-23 183541.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3574567/9984af21-cbcc-53f6-714e-1f1e4eddc9b5.png)
笑顔になるための表を作成htmlひな形

```hoge.html
<table id="xx" border="1">
    
</table>
<script>
function RowCellLength(table){
    console.log("row-length:", table.rows.length)
    // console.log("cell-length:", table.rows[0].cells.length)
}
function createTable(table, dates,rows) {
    newRow = table.insertRow(rows)
    for (let i=0; i<dates; i++) {
        newCell = newRow.insertCell(0)
        newCell.colSpan = "1"  //colspan="1"をセット
        newCell.rowSpan = "1"　//rowspan="1"をセット
        newCell.innerHTML = "new cell"  //セルの中に値を入力
    }

    newCell = newRow.insertCell(0)
    newCell.colSpan = "1"  //colspan="1"をセット
    newCell.rowSpan = "2"　//rowspan="2"をセット
    newCell.innerHTML = "hogehoge"
    newRow1 = table.insertRow(rows+1)

    for (let i=0; i<dates; i++) {
        newCell = newRow1.insertCell(0)
        newCell.colSpan = "1"  //colspan="1"をセット
        newCell.rowSpan = "1"　//rowspan="1"をセット
        newCell.innerHTML = "new cell"  //セルの中に値を入力
    }
} 

var members = 10
var dates = 10
for (let i=0; i<members; i++) {
    createTable(xx, dates,2*i)
}

</script>
```
[自分用メモ]VS Codeを使えば、Pythonファイルを作成できる？

### 現状
Kaggleに参加中のmemeです。
さて、モデル学習と推論でNotebookを分けることが先日できました！
また、前処理をNotebbokに直接コードで書くのではなく、.pyというPythonファイルに記述しそれを呼び出すことで、モデル学習側でも推論側でも、同じ処理をコード数行で行うことができるようになりました！

### 問題はPythonファイルの作成
現状はNotebook上でコードを記述し実行する形式にしていますが、どうやらVScodeというものを使ってPythonファイルを作成するといいらしい。

何がいいのか、VScodeとはなんなのか、そしてどのように作業すればいいのか
全く理解できていないのですが、ここをクリアしていきたいと思います。

### 参考記事
[https://envader.plus/article/118]