OmniAICreator/Qiita-1.07M huggingface dataset:
HSP3 hgimg4 gpbプレビューVSCode拡張機能を作ってみた

# 概要

Hot Soup Processor 3 hgimg4では、gameplay3Dを採用しており3Dファイルとしてgpbファイルを採用しています。このgpbファイル用の簡易プレビュー機能を提供するVSCode機能拡張を作ってみました。

# 提供機能
## gpbファイルの簡易プレビュー機能
gpbファイル選択時に簡易表示します。

|プレビュー例|
|:-:|
|![vscode_pre1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213145/95380fbb-a448-fede-458d-43e3b223ca0b.png)|
|gpbファイルを選択するとロードが始まりプレビュー表示します|


## materialファイルのテキストエディタにおけるハイライト機能

|materialファイルハイライト例|
|:-:|
|![vscode_mtl5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213145/3783e502-b2cf-4408-deaf-a6c6022a977a.png)|
|色がつきます|

## (オマケ) gpbフォントファイルのプレビュー機能

フォントを格納したgpbファイルを選択するとフォントビットマップを表示します。

|フォントプレビュー例|
|:-:|
|![vscode_font2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213145/dffd7cf0-8086-ab27-2834-44674dfd8628.png)|
|フォントビットマップをグレースケール表示します|

## (オマケ) gpbファイルのパース機能
gpbファイルを右クリックして「HSPGPB: gpbファイルをパースする」を選択するとHSP3とは関係なくバイナリをパースしてワイヤーフレーム表示します。材質、ノード変形などは適用されません。

|gpbファイルパースの選択方法|
|:-:|
|![vscode_context3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213145/4d182118-d2a8-e29d-693a-71f436ca97fa.png)|
|gpbファイルで右クリックする|

|gpbファイルパース例|
|:-:|
|![vscode_parse4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/213145/7102542c-0b00-1bac-2a82-0d6d558a251f.png)|
|格納されているメッシュをワイヤーフレーム表示します|


# インストール

以下から hspgpb-vscode-0.1.1.vsixファイルをダウンロードして
VSCode拡張機能としてインストールしてください。

https://github.com/hta393939/hspgpb-vscode/releases/tag/0.1.1



# 内部動作

プレビュー機能はHSP3.7β8 hgimg4のWebGL版を使用して実現しています。
ワイヤーフレーム表示機能は threejs を使用しています。

## パス探索

プレビュー機能において`path/to/piyo.gpb` というファイルを選択した場合、`path/to/piyo.material` ファイルを探します。

その後、`piyo.material` に記載されている画像ファイルを探します。
`path = res/hoge.png` と記載されていた場合
- `path/to/res/hoge.png`
- `path/res/hoge.png`
- `res/hoge.png`
の順に探します。


# リンク

リポジトリ
https://github.com/hta393939/hspgpb-vscode/

プログラミング言語HSP3
https://hsp.tv

Visual Studio Code
https://code.visualstudio.com/

GamePlay v3.0.0
https://github.com/gameplay3d/gameplay
CloudFlareにデプロイするためのRemixプロジェクトでテーブルの要素をリクエストからの情報で絞り込んで表示する（クエリパラメーター）

## 概要

リクエストの情報（クエリパラメーター）で表示する要素を絞り込んでみる。

## 前提

下記が完了していること。

https://qiita.com/miriwo/items/b904d6bfbad2fa561c87

## 方法

今回はレコードのis_completeがtrueのものだけをパスパラメーターを利用した絞り込みで取得できるように実装してみる。また、前回実装した「deleted_atがnullのもののみ取得」の条件はそのままに実装してみる。

1. 前回修正した`app/routes/tasks-loader.ts`を下記のように修正

    ```app/routes/tasks-loader.ts
    import type { LoaderFunctionArgs } from "@remix-run/cloudflare";
    
    export const loader = async ({ context, request }: LoaderFunctionArgs) => {
      const url = new URL(request.url);
      const searchParams = url.searchParams;
      const isCompleteParam = searchParams.get("isComplete");
      const isComplete = isCompleteParam === "true" ? true : false;
    
      const isTrue = 1; // フラグONの意味 マジックナンバー申し訳ない。。
    
      try {
        const tasks = await context.db.tasks.findMany({
          where: {
            deleted_at: null,
            ...(isComplete && { is_complete: isTrue }),
          },
        });
        return tasks;
      } catch (error) {
        console.error("Failed to load tasks:", error);
        throw new Response("Internal Server Error", { status: 500 });
      }
    }
    ```

1. これで実装そのものは完了なので`npm run dev`を実行して/tasks?isComplete=true にアクセスし、下記のようにidが2, 6, 10のものが表示されるか確認

    ![CleanShot 2024-06-15 at 16.42.02@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/8a37b7c7-6fd3-90e0-29b9-42bb4c5d2cdf.png)

1. その後、クエリパラメーターを削除し/tasksにアクセスし、deleted_atがnullのものだけが表示されていることを確認

    ![CleanShot 2024-06-15 at 16.42.57@2x.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/6ce2b6bf-d53b-050d-4202-56cd1516ba23.png)

ここまで同一の挙動なら一旦実装完了

次の実装に備えて、各種値のクエリパラメーターに対応させてみたコードが下記である。（「完了・未完了」「削除済み・未削除」「タスクのカテゴリID」でそれぞれ組み合わせて絞り込めるようになった）

`/tasks?isDeleted=true&taskCategoryMasterId=3`などにアクセスしてみると挙動が体験できると思う。

```app/routes/tasks-loader.ts
import type { LoaderFunctionArgs } from "@remix-run/cloudflare";

export const loader = async ({ context, request }: LoaderFunctionArgs) => {
  const url = new URL(request.url);
  const searchParams = url.searchParams;

  const isCompleteParam: unknown = searchParams.get("isComplete");
  const isComplete: boolean = isCompleteParam === "true" ? true : false;

  const isDeletedParam: unknown = searchParams.get("isDeleted");
  const isDeleted: boolean = isDeletedParam === "true" ? true : false;

  const taskCategoryMasterIdParam: unknown = searchParams.get("taskCategoryMasterId");
  const taskCategoryMasterId: number|null = taskCategoryMasterIdParam ? Number(taskCategoryMasterIdParam) : null;

  const isTrue = 1; // フラグONの意味 マジックナンバー申し訳ない。。

  try {
    const tasks = await context.db.tasks.findMany({
      where: {
        ...(isComplete && { is_complete: isTrue }),
        ...(isDeleted && { deleted_at: {not: null}}),
        ...(taskCategoryMasterId !== null && { task_category_master_id: taskCategoryMasterId }),
      },
    });
    return tasks;
  } catch (error) {
    console.error("Failed to load tasks:", error);
    throw new Response("Internal Server Error", { status: 500 });
  }
}
```
try except raiseを使った例外エラー処理

https://qiita.com/baby-0105/items/0e30348589fa0a9d2424

まずtryで例外が発生するかもしれない処理を書いてあげる
そして例外エラーが発生したらexceptで受け止めてあげて別の処理を行う
そしてraiseは意図的に例外エラーを発生させる。そしてそのエラーに名前を付けられる
すると名付けたエラーをexceptでエラー名を受け取ることでエラー別の処理が可能となる

サンプルコード
```
@app.route('/add_employee', methods=['POST'])
def add_employee():
    data = request.get_json()
    try:
        # データ型のチェック
        if not isinstance(data['id'], int):
            raise ValueError('ID must be an integer')
        if not isinstance(data['name'], str):
            raise ValueError('Name must be a string')
        
        # データの挿入
        insert_employee(data['id'], data['name'])
        return jsonify({"message": "Employee added successfully"}), 200

    except (KeyError, ValueError) as e:
        return jsonify({"error": str(e)}), 400

    except sqlite3.IntegrityError as e:
        return jsonify({"error": "Database integrity error"}), 400

    except Exception as e:
        return jsonify({"error": "An unexpected error occurred"}), 500
```
【競技プログラミング】ABC358 E - Alphabet Tiles を母関数＆FFTで想定解より早く解く

## はじめに
[ABC358のE問題](https://atcoder.jp/contests/abc358/tasks/abc358_e)を解いているときに母関数を使って解く方法を思いついたので書きたいと思います。


:::note info
コンテスト中に通せませんでした（泣）
:::


## 本題

### 問題文（前半のストーリーを省略）

長さ $1$ 以上 $K$ 以下の英大文字からなる文字列であって、以下の条件を満たすものの個数を $998244353$ で割った余りを求めてください。
* $1\le i\le 26$ を満たす任意の整数 $i$ について以下が成立する。
  * 辞書順で $i$ 番目の英大文字を $a_i$ とおく。例えば、$a_1=$`A`, $a_5=$`E`, $a_{26}=$`Z`である。
  * 文字列の中に含まれている $a_i$ の個数は $0$ 個以上 $C_i$ 個以下である。

### 制約
* $1\le K\le 1000$
* $0\le C_i\le 1000$
* 入力される値はすべて整数

<br>

### 解法

簡単に言えば、$26$ 種類のモノの重複順列であって、並べる長さとそれぞれのモノが含まれる個数に制限があるパターンを考えることになります。突然ですが、$f_i\ (i=1,2,\cdots ,26)$ と $F$ を以下のように定義してみましょう。 $$f_i(x)=\sum_{n=0}^{C_i}\dfrac{x^n}{n!},\quad F(x)=\prod_{i=1}^{26}f_i(x)$$ このとき、解答すべき数値は $\displaystyle\sum_{n=1}^{K}\ \lbrack x^n \rbrack F(x) \cdot n!$ を $998244353$ で割った余りであることがわかります。

### 説明

まずは具体的な状態で考えます。`A`を$5$個、`B`を$7$個、`C`を$4$個並べるとします。この並べ方の総数は簡単な高校数学で求まりますね。$\dfrac{(5+7+4)!}{5!\ 7!\ 4!}$ 通りとなります。ここで分子（＝文字列の長さ）を $n$ と固定してみましょう。こうすると文字列の長さが $n$ のときの並べ方の総数は $0\le x_i\le C_i\ (i=1,2,\cdots ,26)$ かつ $x_1+x_2+\cdots +x_{26}=n$ を満たしながら $x_1,x_2,\cdots ,x_{26}$ を動かしたときの $\dfrac{n!}{x_1!x_2!\cdots x_{26}!}$ の総和となることがわかります。これは文字列長 $n$ を $x$ の指数で管理する母関数で表現できそうです。『 $\dfrac{1}{x_1!x_2!\cdots x_{26}!}$ の総和に $n!$ を掛ける』とするとうまくいきます。実際、$F(x)$ の $x^n$ の係数は $\dfrac{1}{x_1!x_2!\cdots x_{26}!}$ の総和となっています。

## ACコード（Python）

* 多項式の畳み込みはナイーブなアルゴリズムでは $O(\sigma K^2)$ となります。（本問題では$K\le 1000$を満たすためこの方法でも間に合います。）このコードではより強い制約にも対応できるようにshakayami様の[ACL-for-pythonのconvolution](https://github.com/shakayami/ACL-for-python/blob/master/convolution.py)をお借りしてNTTというアルゴリズムで畳み込み計算を行っているため $O(\sigma K\log K)$ で計算が実行できています。（アルファベットの種類を $\sigma$ とおいています。）
* また、モジュラ逆元などの知識があるとよいです。
* このコードではおそらく $K=2\times 10^5$ などの場合でも間に合います。


https://atcoder.jp/contests/abc358/submissions/54628328



```main.py
def main():
  MOD = 998244353

  # 階乗計算
  fac = [1]*(1000 + 9)
  for a in range(1, 1000 + 4):
    fac[a] = (fac[a-1]*a)%MOD

  # 逆元計算
  inv = [None, 1]
  for a in range(2, 1000 + 9):
    inv.append((-(MOD//a)*inv[MOD%a])%MOD)

  # 階乗の逆元計算
  facinv = [1]*(1000 + 9)
  for a in range(2, 1000 + 4):
    facinv[a] = (facinv[a-1]*inv[a])%MOD

  fft = FFT(MOD)
  K = int(input())
  C = list(map(int,input().split()))

  f = [1]
  for i in range(26):
    g = [facinv[n] for n in range(min(K+1, C[i]+1))]
    f = fft.convolution(f, g)[:K+9]

  ans = 0
  for n in range(1, min(K+1, len(f))):
    ans += f[n]*fac[n]
    ans %= MOD

  print(ans)

"""
ACL-for-python の FFT をコピペ
"""

if __name__ == "__main__":
  main()
```

## おわりに
* コンテスト時間中に考察を完了できる考察力を鍛えたいです。
* FFTと母関数を使えば本問題の並び替えでないバージョンなど、様々なバリエーションのある問題にも対応できそうだと思いました。
Mercariの商品画像を使ってファッションカテゴリー分類モデルを作ってみる!_スクレイピング編

![スクリーンショット 2024-06-16 005122.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2664262/3c336b99-2493-8015-d151-93a6792cb2bc.png)

 # 概要
Mercariの商品画像をスクレピングし、ファッションをカテゴリーごとに分類する深層学習モデルを作ります。
ただし、今回はメンズのトップスのみに限定させていただきます。

####  調査の流れ
1. スクレイピング
2. データチェック
3. モデル作成 

 # 1.　スクレイピング
スクレイピングの流れは、
商品ごとに写真のURLをスクレイピング→URLから商品の画像をダウンロードというものです。

以下は商品画像のサンプルです。

![m97553311374_2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2664262/c8537e0c-ae12-83af-d3d0-496fc164863b.jpeg)

画像のURLを確認すると
https\://static.mercdn.net/item/detail/orig/photos/m97553311374_2.jpg?1718502543でした。

画像のURLはhttps\://static.mercdn.net/item/detail/orig/photos/{商品のID}?1718502543という構造であることがわかったので商品IDを取得すればいいことがわかりました。

実際のサイトにて検証するとmerItemThumbnail fluid__a6f874a2のid部分にて商品idが格納されていました。
![スクリーンショット 2024-06-16 110728.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2664262/40198bf0-2ff0-8244-6300-80ee42e5411f.png)

今回はこの部分をサイトからスクレイピングしていきます。

各カテゴリーごとに50ページスクレピングします。
1ページに120商品あるので、1カテゴリーごとに最大で6000枚の写真を取得できる計算になります。

以下がスクレピングした情報で作成されたデータフレームです。
![スクリーンショット 2024-06-16 112136.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2664262/a3f3aa22-62d8-6097-ac2d-e649f52e297d.png)


カテゴリーごとに何枚写真があるかも集計してみました。

![スクリーンショット 2024-06-16 112620.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2664262/0152c389-71c3-f934-dde8-683d00f1b3a2.png)

どのカテゴリーもだいたい6000枚あることがわかりました。

 # 次回予告
 今回の記事は情報の集め方のみ取り扱いました。
 次回は取得した画像を各カテゴリーごとに分析し、しっかり画像を取れているかや服の特徴等を調べようと思います。
next.jsとsupabaseでコメントをリアルタイム表示するアプリを作る　その3

こんばんは。
アウトプット太郎です。

前回に引き続き、改良を加えていきます！

前回：
https://qiita.com/output_tarou/items/8c994608c75cb7b65002

今見た目はこんな感じ

![スクリーンショット 2024-06-14 20.49.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583082/9c3d8807-ff06-5ed9-6fc4-e43e48fe7a46.png)

レイアウトを整えるとともに、
コメント日時を表示し、削除ボタンもつけてみます。

日時の取得自体はすでにcommentオブジェクトが
値としてcreated_atを保持しているので、呼び出せば即出せます。

ただしデータの形がDBに入れているようなtimestamp型になっているので、
2024-06-13 14:12:29.489131+00
関数をかましてやることでJSTにフォーマットさせます。

```
const formatDate = (dateString: string) => {
  const date = new Date(dateString);
  const options: Intl.DateTimeFormatOptions = {
    year: 'numeric',
    month: 'long',
    day: 'numeric',
    hour: '2-digit',
    minute: '2-digit',
    hour12: false,
    timeZone: 'Asia/Tokyo',
  };
  return date.toLocaleDateString('ja-JP', options);
};
```
引数にtimestampを受け取り、
Data型のオブジェクトとしてインスタンス化し、
optionとして視覚的に見やすい形にしてリターンさせています。

次に、削除ボタンですが、
まずsupabase側でdeleteのRLS policyを追加します。

そして、ボタンを設置するとともに、
クリック時の関数を定義します。

```
  const handleCommentDelete = async (id: number) => {
    const { error } = await supabase
      .from('comments')
      .delete()
      .eq('id', id);

    if (error) console.error('Error deleting comment', error);
    else fetchComments();
  };
```

引数のidはcommentオブジェクトが持つ値で
supabase側で一意に特定できます。

![スクリーンショット 2024-06-16 11.33.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3583082/d50c1b97-bf59-d2a4-777c-07cd263c594c.png)

こんな感じで日時、内容、削除ボタンが設置できました！
僕なりの Apache 備忘録

# 再起動

```bash
sudo apachectl configtest && sudo apachectl graceful
```

- `apachectl configtest`
    - Apache の設定ファイル（通常は *httpd.conf* や関連するファイル群）が正しく構文解析できるかどうかをテスト
    - 実際にはサーバーを再起動または停止しない
    - 設定ファイルに構文上のエラーがないかをチェックする
    - 再起動の前に行うと安全
    - エラーがある場合は、そのエラーが報告され、コマンドは非ゼロのステータスで終了
- `apachectl graceful`
    - Apache に対して「優雅な」再起動を要求する
    - これは、既存の接続を中断せずに、新しい設定でサーバーを再起動する方法
    - 実際には、現在処理中のリクエストはその完了まで待たれ、新しいリクエストは更新された設定で処理される
    - サーバーのダウンタイムを最小限に抑えつつ設定の変更を適用するのに適している。
- sudo 権限が必要



# Basic認証

## 普通の掛け方

`.htaccess`

```htaccess
AuthType Basic
AuthName "Input ID and Password."
AuthUserFile フルパス/.htpasswd
require valid-user
```

`.htpasswd`

- `htpasswd`で作成する
- basic認証作成サイトで作成する





## 特定のディレクトリだけかける

```htaccess
<Directory "D:/pg/Apache/Apache24/htdocs/private"> # かけたいディレクトリ
  AuthType Basic
  AuthName "Input ID and Password."
  AuthBasicProvider file
  AuthUserFile D:/pg/Apache/Apache24/password/passwords
</Directory>
```

↑これではできない。`<Directory`ディレクティブがhtaccessでは使えない。Apacheの設定ファイルで記述するなら行ける。

これが正解。

```htaccess
Satisfy Any

AuthType Basic
AuthName "Input your ID and Password."
AuthUserFile /var/www/.htpasswd
require valid-user

SetEnvIf Request_URI "/member/" secure_dir
Order Allow,Deny
Allow from all
Deny from env=secure_dir
```

> https://qiita.com/shotets/items/17bed9029140a6d925d2



## 特定のファイルを除外する

```htaccess
<FilesMatch "(cominsoon\.php)$"> # ここをファイル名にする
Satisfy Any
Order allow,deny
Allow from all
Deny from none
</FilesMatch>
```


## ブラウザから対象ファイルにアクセスできないようにする

```htaccess
<Files ~ "^.(htpasswd|htaccess)$">
  deny from all
</Files>
```
RTK Queryのプロパティをざっくり理解する

# はじめに
RTKQueryを使用する機会があり、備忘録も兼ねてまとめました。
メモ的に書いているためわかりづらい点についてはご了承ください。

# RTKQueryとは
RTK（Redux ToolKit）の機能であり、データ取得とキャッシュを行ってくれるツールです。

## なぜ必要か
RTK Queryを使用しない場合は読み込み状態やキャッシュされたデータの管理をするためにはかなりの量のロジックをかかなければならなかたっため、それらのロジックを簡素化したい場合に使用します。

## API Slice

```Typescript:apiSlice.ts
import { createApi, fetchBaseQuery } from "@reduxjs/toolkit/query/react";

export const apiSlice = createApi({
  reducerPath: "api",
  baseQuery: fetchBaseQuery({
    baseUrl: "https://jsonplaceholder.typicode.com",
  }),
  endpoints: (builder) => ({
    getAllUser: builder.query({
      query: () => "/users",
    }),
    getAllAlbums: builder.query({
      query: () => "/albums",
    }),
  }),
});

export const { useGetAllUserQuery, useGetAllAlbumsQuery } = apiSlice;
```
## プロパティの概要
### createApi
エンドポイントを定義するための関数です。

### reducerPath
Reduxのstoreに登録されるreducerの名前を指定します。
Redux DevToolsなどでstateを識別する際に使用します。

### store
storeはアプリケーション全体で共有されるstateを管理するためのオブジェクトです。

### reducer
store内でstateを更新するための関数です。
createApi関数を実行することで自動的に生成されて、createApi関数の引数に指定したendpoinsを元にそれぞれのエンドポイントに対応するstateを生成してくれます。

### baseQuery
APIリクエストを送信するための関数で、fetchBaseQuery関数を使用して生成されます。
fetchBaseQuery関数はfetch関数をラップした関数のため、fetch関数の引数と同じものを受け取ることができます。

### endpoints
エンドポイントを定義するためのオブジェクトです。
APIのURLやリクエストメソッドを指定します。
データを取得したい場合は、`build.query()`を、データを更新したい場合は`build.mutation()`を指定します。
また、hooksはendpointsを元に自動で生成してくれます。
例：getAllUserを記述 → 自動でuseGetAllUserQueryを生成

エンドポイントではなく、自身で書いたロジックを用いてデータ取得を行いたい場合はqueryの代わりにqueryFn関数を指定します。例えば、firestoreからのデータ取得を行いたい場合などに使用します。
戻り値は、data または errorオブジェクトが返されることを期待されています。

```Typescript:apiSlice.ts
  endpoints: (builder) => ({
    getCollection: builder.query<Collection, string>({
      async queryFn(collectionId: string) {
        try {
          // こちらに処理を記載
          // const ref = collection()
          // ...略
          return { data: data };
        } catch (error) {
          return { error: error };
        }
      },
    }),
  }),
```

### injectEndpoints
createApi関数の第2引数に指定する関数です。
エンドポイントを追加するための関数を受け取り、その関数を実行してエンドポイントを追加します。
参考：https://qiita.com/kana-wwib/items/2928c395608aa4318acf#injectendpoints

## 参考
- https://redux-toolkit.js.org/rtk-query/api/created-api/overview
- https://redux-toolkit.js.org/rtk-query/api/createApi
- https://redux-toolkit.js.org/rtk-query/api/createApi#queryfn
lsの出力をJSON配列にする

### 問題

`ls`の出力結果をJSON配列にしたいとします。たとえば、

```bash
$ ls
bin  games  include  lib  lib32  lib64  libexec  libx32  local  sbin  share  src
```

から、

```json
["bin", "games", "include", ..., "src"]
```

を生成するわけです。


### `jq`を使う

いろいろ試してみましたが、コマンドライン志向のJSONパーザーである`jq`を使うのが一番楽でしょう。こうです。

```bash
$ ls | jq -Rsc 'split("\n")[0:-1]'
["bin","games","include","lib","lib32","lib64","libexec","libx32","local","sbin","share","src"]
```

配列化してしまえば、要素単位の処理は簡単です。たとえば、ファイル/ディレクトリ名を項目にしたHTMLリスト（`<li></li>`）を作成するなら、こうします。

```bash
$ ls | jq -Rsr 'split("\n")[0:-1] | map("<li>" + . + "</li>")[]'
<li>bin</li>
<li>games</li>
...
<li>share</li>
<li>src</li>
```

### 解題

`ls`のファイル名は二重引用符で囲まれていないので、JSON文字列としては文法違反です。そこで、`-R`オプション（ロングフォーマットは`--raw-input`）を使って、強制的に文字列として読み込ませます。

```bash
$ ls | jq -R '.'
"bin"
"games"
...
"share"
"src"
```

この結果では、各ファイル名がそれぞれ独立したJSONテキストとして扱われます。配列化するにはこれらをひとまとめにしなければなりません。それには`-s`（`--slurp`）を使います。複数のテキストが入力されたら、それらを1つの大きな文字列として扱えという指示です。

```bash
$ ls | jq -Rs '.'
"bin\ngames\ninclude\nlib\nlib32\nlib64\nlibexec\nlibx32\nlocal\nsbin\nshare\nsrc\n"
```

ばらばらだった行が`\n`をデリミタに連結されました。あとは、これを`split`で分解し、配列にします。

```bash
$ ls | jq -Rs 'split("\n")'
[
  "bin",
  "games",
  ...
  "share",
  "src",
  ""                                       # ここ余分
]
```

末尾に空文字の要素があります。これは、最後の行の改行コード（`"src\n"`）の次の文字を指しています。不要なので、末尾の要素はスライスで取り除きます（形式はPythonとかでおなじみです）。

```bash
$ ls | jq -Rs 'split("\n")[0:-1]'
[
  "bin",
  "games",
  ...
  "share",
  "src"
]
```

コマンドオプションに`-c`（`--compact-output`）を加えると、横1列に出力してくれます。ま、見栄えの話です。

```bash
$ ls | jq -Rsc 'split("\n")[0:-1]'
["bin","games","include","lib","lib32","lib64","libexec","libx32","local","sbin","share","src"]
```


### おわりに

最初は`awk`を使ってみましたが、最後のカンマが面倒で途中で挫折しました。`if`とか入れるとややこしくなるし...

```bash
$ ls | awk 'BEGIN {print "["} {print "\""$0"\","} END{print "]"}'
[
"bin",
"games",
...
"share",
"src",                                     # あああ、これが。
]
```

`readarray`とかも考えたけど、これもループを組むのが面倒なので、やっぱり挫折しました。イテレータが使える`jq`はやっぱりこういうときに便利です。

### 参考

- [./jq](https://stedolan.github.io/jq/) ... `jq`のオフィシャルサイトです（英文）。
- 『[jqハンドブック](http://www.cutt.co.jp/book/978-4-87783-491-3.html)』（2021）... コマンドオプションや`split`などの関数を解説しています。ご購入はこちらから【[出版社](http://www.cutt.co.jp/book/978-4-87783-491-3.html) | [honto](https://honto.jp/netstore/pd-book_31037910.html) | [amazon.co.jp](https://www.amazon.co.jp/jq/dp/4877834915) | [ヨドバシカメラ](https://www.yodobashi.com/product/100000009003450000/)】。
- 「[jqのむだづかい](https://qiita.com/stoyosawa)」 ... `jq`ではまずやろうとは思わない処理の探求記録です。完成版は他の（通常機能な）レシピとともに『[jqクックブック](https://www.cutt.co.jp/book/978-4-87783-508-8.html)』（2023）に掲載しました。ご購入はこちらから【[出版社](https://www.cutt.co.jp/book/978-4-87783-508-8.html) | [honto](https://honto.jp/netstore/pd-book_32427461.html) | [amazon.co.jp](https://www.amazon.co.jp/dp/4877835083) | [ヨドバシカメラ](https://www.yodobashi.com/product/100000009003710680/)】。

<img src="http://www.cutt.co.jp/book/images/978-4-87783-491-3.png" width="200" alt="表紙"></img>　<img src="https://www.cutt.co.jp/book/images/978-4-87783-508-8.png" width="200" alt="表紙"></img>
ふりかえり・レトロスペクティブ関連情報まとめ

# はじめに
この記事では、私がふりかえり・レトロスペクティブを行う際、参考にしている情報源をまとめています。
※今後も随時更新予定

# Esther Derby、Diana Larsen 著、角征典 訳「アジャイルレトロスペクティブズ　強いチームを育てる「ふりかえり」の手引き」オーム社、2007

https://shop.ohmsha.co.jp/shopdetail/000000001770/

具体例をベースにふりかえりの目的や準備、進め方などを幅広く体系的に学ぶことができます。

## 森一樹 「アジャイルなチームをつくる ふりかえりガイドブック 始め方・ふりかえりの型・手法・マインドセット」 翔泳社、2021

https://www.shoeisha.co.jp/book/detail/9784798168814

こちらもふりかえりを体系的に学ぶことができます。ふりかえり時の切り口やプラクティスがたくさん載っています。

# ふりかえりカタログ / Retrospective Catalog

https://speakerdeck.com/viva_tweet_x/retrospective-catalog-59bd3a29-314c-45dd-911b-f8e5f1308333

ふりかえりのプラクティスがたくさん載っています。手っ取り早く様々なプラクティスの概要をつかみたいときに便利です。

# ふりかえりam

https://open.spotify.com/show/1MvWCMmcH3zy0LJ6vERTi9

様々なふりかえり手法を試している Podcast。先ほど紹介した「アジャイルなチームをつくる ふりかえりガイドブック 始め方・ふりかえりの型・手法・マインドセット」 の著者が出演しています。

# ぶっちゃけて話すための場作り

https://speakerdeck.com/piyonakajima/creating-an-environment-for-open-and-honest-communication
Fedora 40でAPM Plannerを実行

# 1.Git clone
```bash
git clone https://github.com/hexaforce/apm_planner.git
cd apm_planner
```
# 2.Build on Docker
```bash
# Fedora37以降はQt6なのでFedora36(Qt5)ビルドする
docker run --rm -it -v ./:/apm_planner hexaforce/apm_planner_builder
```
# 3.Run on Fedora40
```bash
# Fedora40にはQt5のライブラリがインストールされていないので追加
sudo dnf install qt5-qtbase qt5-qtserialport

# releaseにファイルが作られているので実行する
./release/apmplanner2
```

### ＊もし起動できない場合は不足しているライブラリを調べます
```bash
ldd release/apmplanner2
```
![Screenshot from 2024-06-16 10-27-42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117164/eaa1a4fc-e7b9-e025-13e3-aff114ed0cac.png)
js スプレッド構文について知ろう

## 概要

条件付きでオブジェクトや配列を展開できるスプレッド構文なるものを知ったので簡単にまとめる。

## 例

まずは記載方法の例をあげる

```
...(真偽値 && {真偽値がtrueの場合に展開されるオブジェクトや配列})
```

## 使用例

下記のようにクエリパラメーターから得られた値によってDBからデータ取得するときの発行クエリを制御したいとき

```ts
    const tasks = await context.db.tasks.findMany({
      where: {
        deleted_at: null,
        ...(isComplete !== null && { is_complete: isTrue }),
      },
    });
```

## 参考文献

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax
Android15の重要そうな差分まとめ

# はじめに
Android14に関する記事を上げてから、もう一年近く経つとは、、、。

https://qiita.com/YuukiYoshida/items/0ed791f87834e8c70e9b

本当に驚くほど早く時間が経過していますが、現在Android15のベータ版が公開されている状況です。

https://developer.android.com/about/versions/15/get?hl=ja#on_pixel

例年通りであればもう少しで正式リリースされると思いますが、Android15に関してもいくつか修正が必要になりそうです。
フォアグラウンドサービスに関しては、Androidバージョンが上がる度に厳しくなり、サービスによってはもはやWorkerへの置き換えを強制されているような状況に陥るかもしれません。
ご自身が対応されているプロジェクトがどの程度影響がありそうか、一つの判断材料にしてもらえれば幸いです。

# 最小ターゲット SDK バージョンを 23 から 24 に引き上げ
Android15端末では、一律で下限バージョンが24に引き上げられます。
24未満を対象としているアプリはインストールができなくなるので、注意が必要です。
ちなみに24未満のアプリをインストールしようとすると、以下エラーが表示されます。

```
INSTALL_FAILED_DEPRECATED_SDK_VERSION: App package must target at least SDK version 24, but found 7
```

# フォアグラウンド サービスの変更：新しいサービスタイプの追加

今回もフォアグラウンドサービスに関して差分がいくつかあります。
そのひとつが、Android15より新しくサービスタイプに追加された**mediaProcessing**です。
このタイプは名前の通り主にメディア系のアプリが対象となり、設定しておくと動画ファイルや音声ファイルをダウンロードし変換する際にアプリがバックグラウンドにいたとしても続行可能となるようです。

ただし、このタイプを設定した場合、実行可能なのは過去24日間で合計6時間のみなので、かなり使用条件が限られている印象です。
また、**mediaProcessing**を設定しているサービスを重複して起動することはできません、これを行うと**ForegroundServiceStartNotAllowedException**をスローします。

# フォアグラウンド サービスの変更：BOOT_COMPLETEDブロードキャストレシーバに関する制限
これは対象となるアプリ、多いのではないでしょうか？

BOOT_COMPLETEDを使用する場合、以下サービスタイプを設定しているフォアグラウンドサービスは起動できなくなります。
- dataSync
- camera
- mediaPlayback
- phoneCall
- mediaProjection
- microphone（この制限は Android 14 以降 microphone に適用されています）

BOOT_COMPLETEDは主に端末を再起動した際にサービスを起動させたい場合などに使用している印象ですね。
もし該当する場合はサービスタイプを変えるか、Workerに処理を置き換える必要がありそうです。

# より安全なインテント
Intentに関してAndroid14に続き、Android15でさらに安全性と堅牢性を高めるための変更が含まれています。

**ターゲットのインテントフィルタと一致する**
これ今まで一致してなくても起動できたのかと驚きましたが、Android15より完全に一致していないと別アプリのActivityを起動させられなくなったようですね。

**インテントにはアクションが必要**
Intentを使用する際にはactionが必須になったようですね。
このactionが指定されていない、どのインテントフィルタにも一致しないということはactionのないIntentは全く機能しなくなるということのようです。

また、事前にこれらのIntentに関する問題を見つけたい場合は厳格モードを有効にすると便利です。

https://developer.android.com/reference/android/os/StrictMode

Intentが上記に違反しているかどうかを知りたい場合は以下を実装することで、違反した際に詳細なログを表示することが可能です。
```main.kt
fun onCreate() {
    StrictMode.setVmPolicy(VmPolicy.Builder()
        .detectUnsafeIntentLaunch()
        .build()
    )
}
```

Android15対応をする前に一度厳格モードを有効にして一通り違反がないかどうか確認しておくと安心ですね。

# emojiTextHeight 属性のデフォルトを true に設定
タイトルの通りですが、TextViewが持つemojiTextHeightがデフォルトでtrueになったようです。
これまでアラビア語、ラオ語、ミャンマー、タミル語、グジャラート語、カンナダ語、マラヤーラム語、オディア語、テルグ語、タイ語などをサポートしている場合、上下の高さぴったりのサイズとなっていましたが、Android15より上下に余白ができる形となります。

![スクリーンショット 2024-06-16 10.44.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/354522/241f3b5c-d831-c377-3e22-0deb96eba325.png)

これはUIによってはこの余白分崩れる可能性があるので、上記言語を対象としているアプリはAndroid15対応前にこのフラグを有効にしてみてUIが崩れていないかどうか確認が必要だと思います。

# さいごに
Android15も古いサービスを運用していて該当する場合はなかなか大変そうですね、、、
あとで慌てないように、なるべく早めに対応が必要かどうかご確認されることをオススメします。
「システム構築はどこから始めるべきだろうか。システム構築が終わったらこうなる、というストーリーを語るところからだ。」

## はじめに

◆この記事は何？
アジャイル開発における「要求」や「ユーザーストーリー」を細分化する記事です。

◆対象は？
要求やユーザーストーリーを整理する方
アジャイル開発に関わる方

◆ねらいは？
アジャイル開発に関わる方が、何気なく使っている「要求」や「ユーザーストーリー」の解像度を上げること

## エンジニア人生に影響を与えたフレーズ

「**システム構築はどこから始めるべきだろうか。システム構築が終わったらこうなる、というストーリーを語るところからだ。**」は、書籍[『テスト駆動開発』](https://amzn.asia/d/exYz8od)に出てくるフレーズです。

そして書籍『テスト駆動開発』の中で、私が最も印象に残っている文章です。

この文章に出会ってから、私は「言われた通りにシステムを作る」から脱却して、「システムを作ってユーザーの体験をどう変えるか？」を考えられるようになりました。システム開発におけるストーリーの重要性を実感できました。

たった一つのフレーズでしたが、私のエンジニア人生に大きな影響を与えました。

## 「要求」と「ユーザーストーリー」

「要求」は「何がしたいか？」であり、ソフトウェア開発の課題の根源です。

[『MORE EFFECTIVE AGILE』](https://amzn.asia/d/55QRydV)では、次のように言及されています。

> プロジェクトの課題や失敗の原因を分析する研究は幅広く行われているが、筆者がソフトウェア開発に携わるようになった最初の25年間に目にしたものはどれも、問題の主要な原因が要求のまずさにあったことを示していた。つまり、要求が完全ではなかったり、正しくなかったり、矛盾していたりする、というのである。

「課題を掘り上げていくと、要求に問題があった」という状況は、多くの開発者が経験されているかと思います。

逆に言えば、開発の関係者が「要求」を理解することで、多くの問題を回避できます。

要求のライフサイクルは「要求の整理」を細分化できます。

アジャイル要求のライフサイクルは次のとおりです。

1. 獲得：要求の発見
1. 分析：要求に対する理解を深める
1. 仕様：要求を一貫した形式で表現
1. 検証：要求が正しい要求であることと正しく表現されていることを確認する

アジャイル要求は、次のようなストーリー形式で表現します。

> 私は<ユーザーの種類>として、<利益>のために<目標/要求>を望んでいる

例えば、「私はWikiユーザーとして、同僚と共有するために、ファイルをアップロードする機能を望んでいる」のように表現します。

## ユーザーストーリーの3つのC

ユーザーストーリーに関する考え方に、「3つのC」があります。

### カード(Card)

ユーザーストーリーはストーリー、ルール、完了基準の簡単な説明が記載されたカードに書かれています。

内容を簡潔にするために、わざと小さなカードを使うこともあります。
小さなカードに書かれたユーザーストーリーは、これからの議論のきっかけとなります。

### 対話(Conversation)

ユーザーストーリーの利点の一つが、対話をもたらすことです。

要件の詳細は、開発者とステークホルダーの間で交わされる対話によって具体化され共有されていきます。

対話を通じて、ストーリーを洗練化させていきます。
例えば、ストーリー間の矛盾に気づいたり、技術知識を使って、新しいストーリーを生み出したり、代替ストーリーを考えたりすることができます。

そのため、開発者とステークホルダーの対話は継続的に続きます。

参考：

https://bliki-ja.github.io/ConversationalStories

### 確認(Confirmation)

ユーザーストーリーには満足条件という形で確認のための情報も含まれます。
「受け入れ条件」といいます。

「受け入れ条件」は、概要レベルの受け入れテストとして表現されます。
開発中に実行されるのはこのテストだけではなく、実際にはもっとたくさんの技術的なテストが実行されます。

ユーザーストーリーに関連する受け入れテストがある理由の一つは、プロダクトオーナーがユーザーストーリーを正しく実装されていることを確認できる点です。

## 良いユーザーストーリーのINVEST原則

ユーザーストーリーがうまく書けたかを評価するために有用なのが「INVEST」です。

1. **I**ndependent(独立している)
1. **N**egoriable(交渉できる)
1. **V**aluable(価値がある)
1. **E**stimatable(見積もり可能)
1. **S**mall(小さい)
1. **T**estable(テスト可能)

私が理解するまでに時間を要したのが「Negoriable(交渉できる)」です。ウォーターフォール開発の経験をアンラーニングすることで、私は「Negotiable(交渉できる)」の概念を理解できるようになりました。

「Negoriable(交渉できる)」はユーザーや開発者などで話し合いができることを意味します。

ユーザーストーリーは、要件定義書のような形式ではなく、その詳細の交渉をするためのプレースホルダ（一時的なもの）です。これは前述の「対話(Conversation)」と繋がります。

交渉することで、ユーザーストーリーを洗練化できます。

ユーザーストーリーが交渉可能なおかげで、事前に要件定義書が作成されている場合に陥りがちな責任の押し付けを回避しやすくなります。

## おわりに

この記事では、要求の細分化、ユーザーストーリーの表現方法、ユーザーストーリーの評価方法について紹介しました。

アジャイル開発に関わる方の参考になれば幸いです。

## 参考

- [『テスト駆動開発』](https://amzn.asia/d/exYz8od)
- [『MORE EFFECTIVE AGILE』](https://amzn.asia/d/55QRydV)
- [『エッセンシャルスクラム』](https://amzn.asia/d/0rnBUXf)
- [「ユーザーストーリー」](https://www.agile-studio.jp/post/apm-user-story)
String.codepoints/1 と String.graphemes/1 の比較

こんにちは！
プログラミング未経験文系出身、Elixirの国に迷い込んだ？！見習いアルケミストのaliceと申します。
今回はString.codepoints/1 と String.graphemes/1 の比較を比較して学んだことをまとめます。2024/6/16に開催したイベント[piyopiyo.ex #35：もくもく作業タイム](https://piyopiyoex.connpass.com/event/319681/)の成果です。

# 目的
[`String.codepoints/1`](https://hexdocs.pm/elixir/1.16.2/String.html#codepoints/1
) と [`String.graphemes/1`](https://hexdocs.pm/elixir/1.16.2/String.html#graphemes/1) の違いを理解する

# 前提
String.codepoints/1 と String.graphemes/1それぞれに対して下記の変数を使用します。
```diff_elixir
greeting = "Hello World!"
string = "é"
```
:::note info
"é"はU+0065 と U+0301 の結合文字列です。[^1]
:::


## 実行環境
Windows 11 ＋ WSL2 ＋ Ubuntu 22.04
Elixir v1.16.2-otp-26
Erlang v26.0.2

# 結果
## String.codepoints/1はUTF-8のコードポイントごとに分割

### 結合文字列なし
String.graphemes/1と結果は同じです。
```diff_elixir
String.codepoints(greeting)
["H", "e", "l", "l", "o", " ", "W", "o", "r", "l", "d", "!"]
```

### 結合文字列あり
結合文字列はコードポイントが2つなので、リストの要素数は2になります。
```diff_elixir
String.codepoints(string)
["e", "́"]
```



## String.graphemes/1はユーザーが文字として認識する単位ごとに分割
:::note info
ユーザーが文字として認識する単位のことを書記素(grapheme)と呼びます。[^2]
:::

### 結合文字列なし
String.codepoints/1と結果は同じです。
```diff_elixir
String.graphemes(greeting)
["H", "e", "l", "l", "o", " ", "W", "o", "r", "l", "d", "!"]
```

### 結合文字列あり
結合文字列は書記素が1つなので、リストの要素数が1になります。
```diff_elixir
String.graphemes(string)
["é"]
```


# ～Elixirの国のご案内～
↓Elixirって何ぞや？と思ったらこちらもどぞ。Elixirは先端のアレコレをだいたい全部できちゃいます:laughing::sparkles::sparkles:

https://qiita.com/piacerex/items/09876caa1e17169ec5e1

↓ゼロからElixirを始めるなら「エリクサーチ」がおすすめ！私もエンジニア未経験から学習中です。

https://elixir-lang.info/topics/entry_column


↓[We Are The Alchemists, my friends!:bouquet:](https://qiita.com/torifukukaiou/items/6bcd5bbb80df9e594e9e "Awesome-san's Awesome article")[^10]
Elixirコミュニティは本当に優しくて温かい人たちばかり！
私が挫折せずにいられるのもこの恵まれた環境のおかげです。
まずは気軽にコミュニティを訪れてみてください。[^11]


https://speakerdeck.com/elijo/elixirkomiyunitei-falsebu-kifang-guo-nei-onrainbian


[^1]: Elixirは文字列をエンコードするためにUTF-8を使用している https://hexdocs.pm/elixir/1.16.2/String.html#module-utf-8-encoded-and-encodings

[^2]: Elixirにおける書記素とは https://hexdocs.pm/elixir/1.16.2/String.html#module-grapheme-clusters

[^10]: @torifukukaiouさんのAwesomeな名言をお借りしました。Elixirコミュニティを一言で表すと、これに尽きます。


[^11]: @kn339264さんの素敵なスライドをお借りしました。Elixirコミュニティはいろんな形で活動中！
Vue SFCの「ランタイムのコンパイルコストがかからない事前コンパイルされたテンプレート」ってなんだ？？？

今回は、Vue公式ドキュメントのSFCセクションで出てきた文言である「ランタイムのコンパイルコストがかからない事前コンパイルされたテンプレート」について、周辺知識にも軽く触れながら記事にまとめていきたいと思います。

記事に登場してくる単語について詳しくまとめようとするとそれで1つの記事をかけてしまうので、今回は概要のみ触れたいと思います。（いずれ記事としてまとめたい。。。。）

# SFCとは

Vueコンポーネントのテンプレート（template）、ロジック（script）、スタイル（style）を1つのファイルにカプセル化できる特別なファイル形式のこと（下記コード参照）

```jsx
<script setup>
import { ref } from 'vue'
const greeting = ref('Hello World!')
</script>

<template>
  <p class="greeting">{{ greeting }}</p>
</template>

<style>
.greeting {
  color: red;
  font-weight: bold;
}
</style>
```

# 「ビルドステップ」ってなんだよ！
Vueの公式ドキュメントに度々登場してくる「ビルドステップ」
デプロイ前に実行されるものというざっくりとした認識であったため、改めて言語化しておきます。

そもそものデプロイ（本番環境への配置）の挙動を理解しておく必要があります。

デプロイの挙動をまとめると下記です。

1. コンパイル
    1. ソースコードを機械が理解できるコード（バイナリコード）に変換する
    2. 例えば、TypeScriptをJavaScriptに変換する処理
2. バンドル
    1. 複数のファイルを1つにまとめる
3. 最適化
    1. コードの圧縮やデッドコードの除去を行う（パフォーマンスの向上）
4. テスト
    1. ビルドされたコードが正しく動作するかを確認するためのテストの実行
5. デプロイ
    1. 最終的なビルド成果物を運用環境に配置する

**つまりビルドステップとは、デプロイステップにおける1~3のステップの総称です。**

# 余談

ビルドツールとしてよく名前が挙がるものとしてViteがあります。

Viteの特徴として以下があります。

- 高速で動く開発サーバー
    - モジュールの読み込みと更新をESMを利用しているため、変更が即座に反映される（HMR（ホットモジュールリプレースメント））
- 効率的なビルド
    - 内部的にRollupを使用しているため、ビルド時間が短く、生成されたバンドルも効率的

## Rollupってなんだよ！

Viteはバンドラー、兼、開発サーバーを立てるアプリケーションで、RollupはViteが内部的に使っているバンドラーです。

要は、VIteの中でバンドル処理を担ってくれているバンドラーです。

特徴としては以下があります。

- Tree Shaking
    - 未使用のコードを自動で除去してくれるため、最終的なバンドルサイズが小さくなる
- ESM対応
    - モジュールの読み込みを効率的にしてくれる

# ランタイムのコンパイルコストがかからない事前コンパイルされたテンプレートってなんだよ！

今回の記事の本題です。

そもそも、ランタイムでのコンパイルコストとは、アプリケーション実行されている間（ランタイム）にコンパイル（他言語への変換）を行う際に発生するリソース（CPU、メモリ、時間）のこと。

- ランタイムでのコンパイルステップ
  - 動的テンプレート解析
      - テンプレートが実行時に解析されて、JSに変換されるプロセス
      - ブラウザのレンダリング時に行われて、ゆーざーがページをロードする際に時間がかかる要因
  - JSのパースと実行
      - テンプレートの変換だけでなく、変換後にJＳを実行するリソースも必要なので、それを行うプロセス

SFCでは、これらの上記のランタイムでのコンパイルステップのうち、動的テンプレート解析を事前に行うことができます。

- テンプレートの事前コンパイル
    - SFCのテンプレートは、ビルド時にJＳコードに変換される
    - このプロセスは開発中もしくは、デプロイまでに行われるためランタイムでは変換済みのコードが利用されるようになる

ただJSの実行に関しては、ブラウザやNode.jsといったJＳランタイムの環境で行われるため、Viteの責務ではありません。

つまり、「ランタイムのコンパイルコストがかからない事前コンパイルされたテンプレート」とは、 **「ランタイム時に行っていた動的テンプレート解析をデプロイ前のビルドステップであるコンパイル時にすべて終わらせてしまう」** という意味になります。

# 事前コンパイルによる利点
事前コンパイルについて理解できたうえで、それらの利点に最後触れて終わりたいと思います。

- 初期ロードの高速化
  - テンプレートが事前にコンパイルされているため、アプリケーションの初期ロード時にJavaScriptのパースやコンパイルのコストがかからない
  - その結果、ユーザーがアプリケーションをロードする際の待ち時間が短縮される
- 効率的な再レンダリング
  - 動的なコンテンツの変更に対する再レンダリングが効率的に行われる
  - 仮想DOMを使用して変更部分のみを更新することによる、パフォーマンスの向上
    - 事前にコンパイルしていることで差分のみ再れんだりんぐすれば良いため
- リソースを省エネ化できる
  - 事前コンパイルにより、実行時に必要なCPUおよびメモリの使用量が削減される

# まとめ
規模の小さい個人開発でのアプリケーションでは、特に意識しなくても問題のない事前コンパイルによるバンドルサイズの削減ですが、規模の大きいアプリケーションでは事前コンパイルによる恩恵は大きいと感じています。

Vue SFCについて全体的な解像度が上がれば幸いです。
他の言語からRubyを学ぶならTry Rubyがオススメ！

ある日、業務でRubyを使うことになり、Rubyを学ぶ方法を探していたところ、Ruby公式サイトで非常に役立つリソースを発見しました。それが [Try Ruby](https://try.ruby-lang.org/) です。他の言語をある程度触ったことがある人にとってTry RubyがRubyを最短で学ぶのに最適なツールだと思います！

## Try Rubyとは？

[Try Ruby](https://try.ruby-lang.org/) は、ブラウザ上でインタラクティブにRubyを学ぶことができる無料のチュートリアルサイトです。

## いいところ

### 簡単に始められる

他の言語を学んだ経験がある方なら、最初に環境構築に苦労したことがあるかもしれません。しかし、Try Rubyならブラウザだけで完結するため、
インストールや設定は一切不要で、すぐに始められます！

### 可愛いイラスト

![try ruby](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403148/737177ac-c627-17e7-c947-1dc42ebb19af.png)
上の画像は、実際のサイトスクリーンショットです。
ステップバイステップのガイドがわかりやすく、30分ほどでRubyの基礎を把握できるようになっています。

### 短時間で効率的に学べる

30分程度のチュートリアルを完了するだけで、Rubyの基本的な構文や概念を理解することができます。他の言語に精通している方なら、このチュートリアルを終えることで、Rubyの特徴をすぐに掴めると思います！

## Try Rubyで学べる内容

Try Rubyでは、以下のような基本的な内容を学ぶことができます：

- 基本的なデータ型（数値、文字列、配列、ハッシュなど）
- 変数と定数の使い方
- 基本的なメソッドと制御構文（if文、ループなど）
- 簡単なオブジェクト指向プログラミングの概念

個人的にRubyってこんなことできるんだ!?って思ったのが、
```ruby
5.times { print "Odelay! " }
```
で、5回`Odelay! `を出力するコードです。

## その後の学習について

Try Rubyで基礎を学んだ後は、[公式ドキュメント](https://www.ruby-lang.org/ja/documentation/)を参考にさらに深い知識を身につけることができると思います。

Rubyの公式ドキュメントは非常に充実しており、詳細な解説や豊富なサンプルコードが掲載されています。ここで学んだ基礎をもとに、実際の業務やプロジェクトで使えるスキルを磨いていきましょう💪

## 最後に

まだまだRubyを学び始めたばかりなので、有用なリソースあればコメントにお願いします！
LINE Notify for Java 自分用メモ

<h2>Line Notify</h2>

【事前準備】
①[Line Notify公式サイト](https://notify-bot.line.me/ja/)でTOKENを取得
②Line Notifyをグループに招待



```java
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URLEncoder;
import java.net.URL;
import java.util.Scanner;

public class LineNotify {

    // LINE Notify トークン
    private static final String TOKEN = "YOUR_TOKEN";
    // LINE Notify APIエンドポイント
    private static final String URL = "https://notify-api.line.me/api/notify";

    private static String urlencode(String message) throws Exception {
        return URLEncoder.encode(message, "UTF-8");
    }

    public static void main(String[] args) {

        String message = "テスト";

        try {
            String encodedMessage = urlencode(message);
            sendPostRequest(encodedMessage);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private static void sendPostRequest(String message) throws Exception {
        URL url = new URL(URL);
        HttpURLConnection conn = (HttpURLConnection) url.openConnection();
        conn.setRequestMethod("POST");
        conn.setRequestProperty("Authorization", "Bearer " + TOKEN);
        conn.setDoOutput(true);

        // "message="は必須
        String postData = "message=" + message;

        try (OutputStream os = conn.getOutputStream()) {
            os.write(postData.getBytes("UTF-8"));
        } 

        int responseCode = conn.getResponseCode();
        if (responseCode == HttpURLConnection.HTTP_OK) {
            System.out.println("メッセージが送信されました。");
        } else {
            System.out.println("メッセージの送信に失敗しました。HTTP応答コード: " + responseCode);
        }

        conn.disconnect();
    }
}
```


※画像も送れる
| パラメータ名 | 型   | 画像形式  | 
| --------- | ---- | --------- | 
| imageFile | File | png、jpeg |
【Vite】Vite + React + TypeScript + vanilla-extract で開発環境を整える

## はじめに

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/390720/49f8a892-4259-c1b5-2f61-1fd72e7e6518.png)

みなさんは、最近話題の「Vite」というビルドツールを使って、サイトを作りたいと思ったことはありますか？

私は、先日ゼロから開発環境を整える機会があり、その時に「Vite」に「vanilla-extract」を使って開発環境を作ってみました。

この記事では、その時にどのような開発環境を作ったのか、開発してみての感想などをまとめようと思います。

## Vite とは？
> Vite（ヴィート）は、現代の Web プロジェクトのために、より速く無駄のない開発体験を提供することを目的としたビルドツールです。

<font color="gray"> *（引用：[Vite - はじめに](https://ja.vitejs.dev/guide/)）* </font>

https://ja.vitejs.dev


## vanilla-extract とは？
vanilla-extract は、TypeScriptをプリプロセッサとして使用しているゼロランタイムCSSinJSです。

https://vanilla-extract.style/
 
## 開発環境を整える
Vite + React + TypeScript + vanilla-extract の環境を整える方法をまとめます。

### 1. Node.js をインストールする
以下のリンクから、Node.jsをインストールしてください。

https://nodejs.org/en/download/

**【インストールされているか確認する】**
ターミナルで、以下のコマンドを入力して、インストールしているバージョンが表示されたら、インストールが完了しています！

```terminal.
node -v
```

### 2. プロジェクトをつくる
#### ① プロジェクトを作る
以下のコマンドを入力し、画面にしたがって、入力してください。
```terminal.
npm create vite@latest
```

上記のコマンドを入力すると以下のようなことが聞かれます。

+ `Project name`：プロジェクトの名前
    + プロジェクトの名前を決め、入力してください
+ `Select a framework`：フレームワークを決めます
    + `Vanilla`・`Vue`・`React`・`Preact`・`Lit`・`Svelte`・`Solid`・`Qwik`・`Others`からフレームワークを決めます
    + この記事では、 `React` を選択して進めています
+ `Select a variant`：使う言語を決めます
    + `TypeScript`・`TypeScript + SWC`・`JavaScript`・`JavaScript + SWC`・`Remix`から言語を決めます
    + この記事では、`TypeScript + SWC` を選択します

#### ② `@vitejs/plugin-react-swc` のプラグインをインストール
以下のコマンドを入力し、`@vitejs/plugin-react-swc` のプラグインをインストールします。

```terminal.
npm i -D @vitejs/plugin-react-swc
```

### 3. vanilla-extract を使えるようにする
#### ① vanilla-extractをインストール
以下のコマンドを入力し、vanilla-extractをインストールします。

```terminal.
npm install @vanilla-extract/css
```

#### ② Viteとvanilla-extractを統合するプラグインをインストール
以下のコマンドを入力し、Viteとvanilla-extractを統合するプラグインをインストールします。

```terminal.
npm install --save-dev @vanilla-extract/vite-plugin
```

#### ③ 設定ファイルを変更する
`vite.config.ts` というファイルを以下のように書き換えます。

```vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
// ↓追加
import { vanillaExtractPlugin } from '@vanilla-extract/vite-plugin'

export default defineConfig({
  plugins: [
    react(),
    // ↓追加
    vanillaExtractPlugin()
  ],
})
```

### 4. 開発環境を立ち上げ、スタイルを変更する
#### ① 開発環境を立ち上げる
以下のコマンドを入力し、開発環境を立ち上げる

```terminal.
npm run dev
```

#### ② vanilla-extractのファイルを作り、スタイルを作成する
`style.css.ts`のようなファイルを作り以下のようなスタイルを記述します。

```ts
import { style } from "@vanilla-extract/css";

export const Styles = {
  button: style({
    fontWeight: "bold",
  })
}
```

#### ③ スタイルを反映させる
反映したいファイルに、インポートし、`className` にスタイルを反映させる

```tsx
import { Styles } from './App.css.ts'

function App() {
  return {
    <button className={Styles.button}>
  }
}
```

## まとめ
`vite`は、日本語のドキュメントも充実していて、すごく使いやすかったと感じました。

`vanilla-extract` は、今回はじめて使ったのですが、selector周りやメディアクエリ周りが少し直感的ではなく、ややこしく感じました。

---

最後まで読んでくださってありがとうございます！

普段はデザインやフロントエンドを中心にQiitaで記事を投稿しているので、ぜひQiitaのフォローとX(Twitter)のフォローをお願いします。

https://twitter.com/degudegu2510
Twil.vim with init.vimを公開した話

<h2>今回作ったカラースキーム</h2>

<b>Twil.vim (Github)</b> -> https://github.com/Twil3akine/Twil.vim

<h2>背景</h2>
<p>カラースキームを変えるより、作った方が早いことに気付いたので作ってみた。</p>

<h2>Twil.vimの中身</h2>
<ol>
    <b>
        <li>init.vim</li>
        <li>Twil.vim</li>
    </b>
</ol>

<h3><b>1. init.vim</b></h3>
<p>自分が使っているNeovimの設定ファイルです。括弧補完やコンパイルなどの自分があったらいいなぁと思う機能とVim-PlugやNERDTreeと言った使っているプラグイン、プラグインに関するコマンドが入っています。</p>

<h3><b>2. Twil.vim</b></h3>
<p>自作したカラースキームのファイルが入っています。イメージは寒い海でアクセントにピンクやイエローを混ぜました。</p>
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3746729/7864860b-332f-d039-2eb3-3eff03111fb4.png">
<p>こんな感じ</p>

<h2>最後に</h2>
<p>
    今回カラースキームを作ってみて、やはりNeovimは拡張性高すぎる！<br>
    README.mdにも書いてますが、ファイルを開いただけでは括弧に色がつかなく、
</p>

```Any.file
:colorscheme Twil (Normal Modeで)
```
<p>
    を実行してもらうと、括弧にも正しく色が反映されます。<br>
    原因が分かった方はもしよければ、DMなりPull requetsなりで教えてください...<br>
    <br>それではまたどこかで会いましょう。
</p>
【Databricks】コスト管理ダッシュボードをインポートしてみた

# 背景・目的
先日、[Importable cost management dashboard added to account console (Public Preview)](https://docs.databricks.com/ja/release-notes/product/2024/june.html#importable-cost-management-dashboard-added-to-account-console-public-preview)が発表されました。

今までは、コストUsageは、アカウントコンソールだけだったが、各ワークスペースに、インポートできるようになったとのことです。
早速試してみます。

# まとめ
下記に特徴をまとめます

| 特徴 | 説明 |
|:--|:--|
|概要|アカウント管理者は、アカウントコンソールからアカウント内の任意のUnity Catalogワークスペースにカスタマイズ可能なコスト管理ダッシュボードをインポートが可能<BR><BR>ダッシュボードには2つのバージョがある<BR>・アカウント全体の使用状況<BR>・単一のワークスペースの使用状況|
|注意点| 2024/6/16時点でパブリックプレビュー|

# 概要
:::note info 
2024/6/16時点でパブリックプレビューです。今後、変更される可能性あります。
:::

> Account admins can now import a customizable cost management dashboard from the account console into any Unity Catalog workspace in their account. There are two versions of the dashboard, one that monitors account-wide usage, and another for single-workspace usage.

- アカウント管理者は、アカウントコンソールからアカウント内の任意のUnity Catalogワークスペースにカスタマイズ可能なコスト管理ダッシュボードをインポートできるようになった
- ダッシュボードには2つのバージョがある
    - アカウント全体の使用状況
    - 単一のワークスペースの使用状況

# 実践

## Import a usage dashboard
[Import a usage dashboard](https://docs.databricks.com/en/admin/account-settings/usage.html#import-a-usage-dashboard)を基に試します

1. アカウントコンソールにサインインします
1. ナビゲーションペインの使用量をクリックします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/f1354d11-317f-6970-2f0e-81393e08355e.png)
1. ①「消費」タブ、②「ダッシュボードをインポート」をクリックします
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/2337e613-1618-8e0a-f050-28338b2f5e10.png)

### 単一ワークスペース

1. ポップアップが表示されるので、下記を入力し「Import」をクリックします
    - 単一ワークスペースの使用を選択
    - ワークスペースを選択：任意のワークスペース
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/b5e3b32f-80bc-4d63-115b-c662dd327ef6.png)
1. ワークスペースに遷移します
1. 最初は、表示されませんでしたが、SQL Warehouseに接続します
1. 表示されました。実際はグラフや数値が表示されていますが、伏せています。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/41d21bc7-eb90-ee17-1057-ece1327459e6.png)

1. 「データ」タブをクリックすると、データセットとクエリが表示されます
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/f98f5a6c-37bd-e1ca-4da5-039926bba600.png)

### アカウントの使用状況
1. 上記と同様の手順でアカウント全体をインポートします
1. Group byにワークスペースが表示されます
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/206276/a667a618-4e95-ced2-4e32-e2cc4fce4a49.png)

# 考察
今回、コスト管理ダッシュボードを、ワークスペースにインポートしました。今まで、Account Consoleのみ確認可能で、他のメンバーにはキャプチャ等で共有していました。

本機能により、ワークスペースから各利用者自身で確認ができるので助かります。


# 参考

https://docs.databricks.com/en/admin/account-settings/usage.html#import-a-usage-dashboard
Tailwind CSSの@apply警告を解決する方法

# はじめに

Next.jsプロジェクトでTailwind CSSを使って開発している際に、エディタ上に警告が表示される問題に遭遇しました。本記事では、その警告の内容と解決方法を紹介します。スタイルは正しく反映されていたものの、開発中に警告が表示されるのが気になったため、設定を調整して対応しました。

# 前提条件

この記事は、以下の条件に該当する方を対象としています。

- Next.js または React プロジェクトで Tailwind CSS を使用している
- テキストエディタは VSCode
- Tailwind CSS のカスタムルール（例：`@apply`）を CSS ファイル内で使用している

# 問題
CSSファイルで `@apply` を使用した際に、以下のような警告が表示されました。

![スクリーンショット 2024-06-22 19.18.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3536873/1802f449-546a-8732-c025-927b6ab57586.png)

# 解決方法

### ①Tailwind CSS IntelliSenseをインストール

VSCodeで拡張機能の「Tailwind CSS IntelliSense」をインストールします。

![スクリーンショット 2024-06-22 19.40.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3536873/372bf545-3132-4bb6-2a28-5b6b8b282fad.png)


### ②settings.jsonに設定を追加

VSCode左下の歯車マークから「コマンドパレット」を押します。（ショートカットを使用する場合は、command+shift+P）検索バーで「Open User Settings」と入力して、```settings.json``` ファイルで以下の設定を追加します。

このように設定することで、`"*.css"` というパターンに該当する全てのCSSファイルに対して、Tailwind CSSの言語サポートや機能を提供する拡張機能（もしくは設定）を適用することができます。
```
"files.associations": {
    "*.css": "tailwindcss"
}
```

# 原因

この警告の原因は、VSCodeがTailwind CSS特有の構文（`@apply` など）を正しく認識できていないことにあります。

# おわりに

最初はChatGPTを使って原因を探っていましたが、同じような問題に直面した方々の記事を参考にして、最終的に解決に至りました。

Tailwind CSS IntelliSenseは公式の拡張機能です。後から公式ドキュメントも確認したところ、今回の問題について以下のように明記されていました。改めて、一次情報にあたることの大切さを実感しました。

>@tailwindTailwind CSS は、、、@applyなどのカスタム CSS アットルールを多数使用しており@config、多くのエディターではこれらのルールが認識されないため警告やエラーが発生する可能性があります。

※上記はTailwind CSSのドキュメントを日本語に翻訳しています。

# 参考

https://qiita.com/P-man_Brown/items/bf05437afecde268ec15

https://tailwindcss.com/docs/editor-setup#syntax-support
ISBNでAmazonの商品ページに飛ぶ方法

特定の書籍のISBNがわかっている場合に、その書籍のAmazon商品ページに簡単にアクセスする方法を紹介します。この方法を使えば、JavaScriptなどを使ってISBNからAmazonの商品ページURLを生成し、直接アクセスすることができます。

## ISBNからAmazonのURLを生成する
Amazonの商品ページにアクセスするための基本的なURL構造は以下の通りです：
```
https://www.amazon.co.jp/exec/obidos/ASIN/{ISBN}
```
`{ISBN}`の部分を実際のISBN番号に置き換えます。

:::note
ISBN13ではなく、ISBN10に変換する必要があるので注意が必要です。
:::

### JavaScriptを使った実装例
実際にJavaScriptを使ってISBNからAmazonの商品ページに飛ぶ方法を見ていきましょう。
```js
const isbn10 = "4873115655";
const amazonUrl = `https://www.amazon.co.jp/exec/obidos/ASIN/${isbn10}`;
console.log(amazonUrl);
>> https://www.amazon.co.jp/exec/obidos/ASIN/4873115655
```

上記のコードでは、`isbn10`という変数に10桁のISBNを格納し、テンプレートリテラルを使ってAmazonの商品ページURLを生成しています。

### まとめ

ISBNを使用してAmazonの商品ページURLを生成する方法を紹介しました。この方法を使えば、書籍情報を簡単に共有したり、リンクを生成するツールを作成したりすることができます。

ぜひ、自分のプロジェクトやウェブサイトで試してみてください！
短期入院に必要な荷物(逸般人向け)

## 1.入院

最近入院したのですが、その際に持っていった荷物についてのまとめです。

## 2.機材

(1) VoIPルータ YAMAHA NVR-700W

入院にあたって最初の問題が、自宅のWindows PCにどう接続するかです。
実家に帰った時には、自宅と実家のYAMAHA RTX1300同士をL2TPv3で結んでいるため、ただのリモートデスクトップでリモートログオンできます。
よく調べていませんが、昨今のAndroidは(v3でない)L2TPが使えなかったりと聞くのと、うちのルータ設定の問題で、L2TPとL2TPv3の同時利用がうまくいっていません。
数年前にOpenVPNサーバーを立てたこともありますが、久しぶりに確認したら、いつの間にかWindows版がなくなっていました。その代わり、Hyper-V用イメージ等いろいろ公開されていました。最近はWireGuardのほうが使いやすいらしいです。
Chromeリモートデスクトップは使えるようにしていますが、スクロール等の画面描画が遅いのと、半角/全角キーによるIME ON/OFFが効かない点が若干辛いです。

そこで思いついたのが、「NVR-700Wを持っていって、L2TPv3で病院と拠点間接続してしまえばいい」でした。
結局のところ、入院から一週間くらいはChromeリモートデスクトップで繋いでいて、ようやく気力に余裕ができてからChromeリモートデスクトップで自宅側ルータのL2TPv3設定を変更しようとして失敗して自宅側がインターネット接続できなくなって失敗に終わりましたが。
また入院する可能性があるので、それまでに再チャレンジします。

(2) 通信用スマホ(Android) Rakuten mini

病院のWi-Fiサービスでもよかったのですが、セキュリティが大丈夫か分からないという疑り深い性格のため、自前回線で通信しました。
通常、テザリングの周波数設定はデフォルトで2.4GHz帯ですが、それだと医療機器に影響を与える恐れがあるため、5GHz帯に変更しました。病院のWi-Fiサービスも全て5GHz帯でした。
通信しっぱなしにするため、通話用スマホと別です。通話用スマホは通話可能スペースまで持っていくので。
Rakuten miniだとLAN側IPアドレスを指定できず、テザリングを開始する度に第4オクテットが変わるのが難点です。
一定時間無通信でテザリング終了する設定がある場合には無効にする必要あり。

(3) 無線中継機 TP-Link RE330

有線LANポート付きで、Wi-Fiを有線LANに変換できます。
もともと安いですが、Amazonで頻繁にセールされていて、もう少し安くなります。
出力電波強度を指定できるので、最弱にしました。通信用スマホのすぐ隣に置くので。
前出の理由により2.4GHz帯はOFFにして、5GHzのみです。
通信用スマホが給電と有線LANアダプタを一度に使えれば無線中継機が不要になりそうですが、USB HUBを使えばできる？
→2024/07/06追記：Android 13以降のイーサネットテザリングを使うと可能。Galaxy M23 5G＋USB Type-C HUB＋USB3.0 LANアダプタで給電しながらテザリング可能。

(4) 静音キーボード・マウス ロジクール MK295GP

ノートPCだとキータイプ音がうるさい可能性があるため、静音キーボード・マウスで操作します。
入院前に大部屋だとノートPC不可でタブレットならOKで、ノートPCを使う場合には別の部屋でとのことで、最初はiPadだけ使ったり、それからキーボードをこそこそと出していたりしたのですが、静音タイプだったこともあるのか特に怒られず、入院終盤は普通に使っていました。

(5) 小型PC Minisforum UM480

4)の理由によりノートPCでなくてもよいため、小型PCにしました。
主にリモート接続用なので手のひらサイズのCeleron機でもよかったのですが、結局Ryzen 7 4800UのPCにしたので、若干大きめです。
共用スペースまで持っていって作業をするのであれば、バッテリーがあるノートPCかWindowsタブレットがよさそうです。

(6) 15.6インチモバイルモニタ

2つ持っていったのですが、病室のベッドで寝っ転がりながら使うので、1つで十分でした。
ベッドの横側の手すりに立てかけて使いました。
USB-PDケーブルを持って行き忘れて、ケーブル1本での接続はできず、USB3.0 Type A-Cで給電、mini HDMIで表示と、2本で接続しました。

(7) iPad

ないと不便。

(8) ウェットティッシュ(液晶用) サンワサプライ CD-WT4KS

容器が小さめ。

(9) 電源タップ Panasonic ザ・タップX 6口タイプ

1mのものが気に入っているのですが、入院直前に追加購入しようとしたら在庫切れで、諦めて2mのものを購入。
6口のが2個あると、ACアダプタの干渉を加味してもだいぶ使えます。

## 3. 機材配線

[通信用スマホ]………[無線中継機]───[NVR700W]───[PC]

## 4.一般人と共通の荷物

普通の荷物です。

(1) USB充電アダプタ 複数ポートタイプ

10ポートのを使いました。

(2) 各種ケーブル

Lightningケーブルは5mの長いもの。

(3) 介護靴

(4) ジッパー付きファイル袋

(5) 筆記用具

## 5.持っていったけど使わなかったもの

(1) セキュリティワイヤー

(2) 錠前、鍵

(3) 鍵を手首につける輪っか

(4) USB接続地デジ・BSチューナー(TS抜き)、カードリーダー、B-CASカード(赤)、アンテナ線(2.5C)、分波ケーブル、アンテナ線延長コネクタ

テレビ見放題の病室になったので結局使わず。
室内アンテナで地デジを見れなかったら有料テレビのアンテナ線を拝借しようかとひどいことを考えていましたが。
普段は画質優先で太くて取り回ししづらい5Cのアンテナケーブルを使うのですが、持ち運び用に2.5Cの細い取り回しやすいケーブルを持って行きました。
結局テレビは見ずにiPadでニュース記事を見ていました。

(5) モバイルモニタをベッドに設置するための金具

ベッドに傷をつけたら怒られるので、結局使わず。

(6) SIMカード

NVR700W用に持っていきましたが、通信用スマホの使い放題プランと無線中継機で通信しました。
NVR700Wは標準SIMなので、microSIMだとSIM変換アダプタが必要です。

(7) S字フック

ゴミ袋を吊り下げたりするのに便利という話を見かけたのですが、ゴミ箱もあるし、結局使わず。

## 6.あったほうがよかったもの

(1) マイナンバー保険証

申し込もうとした時に電子証明書が切れていて申し込めず、まだ連携していませんでした。
マイナ保険証だと、限度額申請書(入院前に準備が必要)の提出や高額療養費制度の申し込み(退院後に手続きする場合)とかをしなくても自動適用されるらしい？
SentenceTransformersを用いた画像類似度検索

## 背景
画像の類似度を検索する方法を試してみようと思い、sentence_transformers を用いて6つの画像から、検索された文字列 "Alessandro Squarzi Jacket Style" に最も近い画像を検索することにしてみた。

## やったこと

### 画像の準備
![スクリーンショット 2024-06-16 9.49.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/634885/3890bd03-10a2-27d1-df59-d7f06ebbb1ae.png)


### 必要なライブラリインポート
```
import os
import pprint
from PIL import Image
from sentence_transformers import SentenceTransformer
```

### 画像をベクトル化
```
# モデルのロード
model = SentenceTransformer('clip-ViT-B-32')

# 画像ディレクトリの設定
image_folder = 'backend/images'

# 画像のリストを取得
image_files = [f for f in os.listdir(image_folder) if f.endswith(('.png', '.jpg', '.jpeg'))]

# 画像のベクトル化
image_vectors = {}
for image_name in image_files:
    image_path = os.path.join(image_folder, image_name)
    image = Image.open(image_path)
    image_vector = model.encode([image])
    image_vectors[image_name] = image_vector
```

### 入力された文字から画像を検索
```
def find_matching_image(prompt, model, image_vectors):
    text_emb = model.encode([prompt])
    similarities = {}
    for image_name, img_emb in image_vectors.items():
        similarity = model.similarity(img_emb, text_emb).item()
        similarities[image_name] = similarity
    pprint.pprint(similarities)
    return max(similarities, key=similarities.get)


# 例: ユーザー入力プロンプト
user_prompt = "Alessandro Squarzi Jacket Style"
selected_image = find_matching_image(user_prompt, model, image_vectors)
if selected_image:
    print(f"正解: alessandro_squarzi3.jpg, 選択された画像: {selected_image}")
else:
    print("一致する画像が見つかりませんでした。")
```

## 結果
最終的な結果は以下のようになった
```
正解: alessandro_squarzi3.jpg, 選択された画像: unknown2.jpg
```

類似度も以下の通り。
```
{'alessandro_squarzi1.jpg': 0.22763186693191528,
 'alessandro_squarzi2.jpg': 0.2647595703601837,
 'alessandro_squarzi3.jpg': 0.27001553773880005,
 'junk1.jpg': 0.15288576483726501,
 'unknown1.jpg': 0.2811787724494934,
 'unknown2.jpg': 0.2970343232154846}
```

### まとめ
期待通りの結果が得られなかった。
原因調べていきます。