OmniAICreator/Qiita-1.07M huggingface dataset:
【たった 3 ステップで完了】Next.js + Storybook で SCSS を使用する。

- [始めに注意点](#始めに注意点)
- [実行環境](#実行環境)
- [実行内容](#実行内容)
  - [Dart Sass パッケージのインストール](#dart-sass-パッケージのインストール)
  - [Storybook Addon Next パッケージのインストール](#storybook-addon-next-パッケージのインストール)
  - [Storybook に Addon 機能を追加する](#storybook-に-addon-機能を追加する)

# はじめに注意点

https://github.com/RyanClementsHax/storybook-addon-next

に記載されている通り、Storybook のバージョンが 7.0 以上の場合、インストールするパッケージが異なります。  
執筆時点でのバージョン 7.0 は RC 版ですが、皆様がこの記事を読む時点で正式版になっている可能性があります。  
なのでインストールされている Storybook のバージョン確認をお願いします。

# 実行環境

- Next.js : 13.2.4
- Storybook : 6.5.16

にて確認済み。

それぞれのバージョン確認方法

```sh
# Next.js のバージョン確認
npm info next version

# Storybook のバージョン確認
npm info storybook version
# もしくは
# npx start-storybook -V
```

# 実行内容

以下の 3 ステップです。

## Dart Sass パッケージのインストール

https://nextjs.org/docs/basic-features/built-in-css-support#sass-support  
にしたがってパッケージのインストールを行います。

```sh
npm install --save-dev sass
```

## Storybook Addon Next パッケージのインストール

https://storybook.js.org/addons/storybook-addon-next/  
にしたがってパッケージのインストールを行います。

```sh
npm install --save-dev storybook-addon-next
```

## Storybook に Addon の機能を追加する

.storybook/main.js の addons キーに storybook-addon-next を追加します。

```diff:.storybook/main.js
module.exports = {
  // other config ommited for brevity
  addons: [
    // ...
+   'storybook-addon-next'
    // ...
  ]
}
```
よく使うHTMLの特殊文字一覧

サイト制作の際によく使う特殊文字です。あまり使わないものは省いています。

|表示|文字実体参照|備考|
|----|----|----|
|&|\&amp;|アンド|
|<|\&lt;|より小さい記号|
|>|\&gt;|より大きい記号|
|"|\&quot|ダブルクオーテーション |
|'|\&apos;|シングルクオーテーション|
|©|\&copy;|著作権記号|
|®|\&reg;|登録商標記号|
|　|\&nbsp|空白文字|
rails newでアプリを作成したらすること

# Rpecの導入
[Rpecの導入 - Qiita](https://qiita.com/amatouyamahodo/items/221fdf40d12fe346cb6d)

# Rubocopの導入
[Rubocopの導入 - Qiita](https://qiita.com/amatouyamahodo/items/34723b0589f0d867324f)

# データベースのパスワード設定
```config/database.yml
# MySQL. Versions 5.5.8 and up are supported.
#
# Install the MySQL driver
#   gem install mysql2
#
# Ensure the MySQL gem is defined in your Gemfile
#   gem "mysql2"
#
# And be sure to use new-style password hashing:
#   https://dev.mysql.com/doc/refman/5.7/en/password-hashing.html
#
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: パスワード
  socket: /tmp/mysql.sock

development:
  <<: *default
  database: アプリ名_development

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: アプリ名_test

# As with config/credentials.yml, you never want to store sensitive information,
# like your database password, in your source code. If your source code is
# ever seen by anyone, they now have access to your database.
#
# Instead, provide the password or a full connection URL as an environment
# variable when you boot the app. For example:
#
#   DATABASE_URL="mysql2://myuser:mypass@localhost/somedatabase"
#
# If the connection URL is provided in the special DATABASE_URL environment
# variable, Rails will automatically merge its configuration values on top of
# the values provided in this file. Alternatively, you can specify a connection
# URL environment variable explicitly:
#
#   production:
#     url: <%= ENV["MY_APP_DATABASE_URL"] %>
#
# Read https://guides.rubyonrails.org/configuring.html#configuring-a-database
# for a full overview on how database connection configuration can be specified.
#
production:
  <<: *default
  database: アプリ名_production
  username: アプリ名
  password: パスワード
```

# .gitignoreに以下を追加
```.gitignore
/config/database.yml
```

# データベースの作成
```:ターミナル
$ rails db:create
Created database 'アプリ名_development'
Created database 'アプリ名_test'
$ rails db:migrate
== 20230325044645 CreateUsers: migrating ======================================
-- create_table(:users)
   -> 0.0100s
== 20230325044645 CreateUsers: migrated (0.0101s) =============================
```

### 参考記事
* [【Rails入門】mysqlとの接続方法まとめ](https://www.sejuku.net/blog/28403)
オブジェクトの値取り出し方

オブジェクトの値を取り出そうとしたときに、for/in分で取り出していたが、調べてみるとfor/of文の方が最適なケースの方があるので、メモする。
#### for/in文
```js
let o = { x: 1, y: 2, z: 3 };
for (let i in o) {
  console.log(i);
}
// x
// y
// z
for (let i in o) {
  console.log(o[i]);
}
// 1
// 2
// 3
```
実際にはfor/in文で、オブジェクトの全てのプロパティが調べられることはなく、**列挙可能**なプロパティだけが調べられる。JavaScriptで定義されているさまざまな組み込みメソッドは調べられない。例えば、すべてのオブジェクトは`toString()`メソッドを持っているが、for/in文では`toString`プロパティは調べられない。組み込みメソッド以外にも、組み込みオブジェクトの多くのプロパティも調べられない。
ただし、コード中で定義されたプロパティやメソッドについてはデフォルトでは全て調べられる。列挙可能な継承プロパティやも、for/in文で調べられる。つまり、すべてのオブジェクトで継承されるようなプロパティを定義するコードの中で、for/in文を使うとループが思ったような動きをしない場合がある。
なので、特別な条件がない限りは下に書いてあるfor/of文を利用した方が良い。
#### for/of分
```js
let o = { x: 1, y: 2, z: 3 };
for (let i of Object.keys(o)) {
  console.log(i);
}
// x
// y
// z
for (let i of Object.values(o)) {
  console.log(i);
}
// 1
// 2
// 3
for (let [i, j] of Object.entries(o)) {
  console.log(i, j);
}
// x 1
// y 2
// z 3
```
#### 参考
https://1-notes.com/javascript-for-of-key-and-value/

https://zenn.dev/yuji6523/articles/fd63e3ec01271e

https://zukucode.com/2017/04/javascript-object-length.html
ローカル圏内でマシン同士を通信する方法

## 前提
下記のような、ホストマシン上に仮想マシンが2つ存在する場合の通信方法についてです。
![スクリーンショット 2023-03-25 16.48.00.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1215249/97d7f624-ea26-b26f-3868-b0da6b8bf1db.png)
>OS：Ubuntu-20.04.3LTSを想定しています

何の設定もせずに、仮想マシン同士を通信させようとすると、「名前解決エラー」が発生します。
＞当時このエラーに苦しみました。

当時は、確かに通信相手のホスト名をどこにも設定していない、ということはわかっていました。
しかし、どこのファイルに設定をするのか？ということがわかりませんでした。

## 解決方法
>/etc/hosts

こちらのパス上に存在するファイルに通信相手の「IPアドレス」と「ホスト名」を
記載することで、別ホストと通信することができます!

以上です。
最後まで読んでいただきありがとうございます。
【Linux】コマンドの履歴を残さない方法

# 例えばどんな時？

パスワードを生成したりする際にhistoryに履歴が残ってしまうと困ることがあります。
そんなときに履歴を残さずにコマンドを実行する方法があります。

# HISTCONTROL=ignorespace

以下の設定をすると、先頭に半角スペースが入ったコマンドは履歴に残らないようになります。

```bash
HISTCONTROL=ignorespace
```
これは履歴が残ります。
```bash
echo "test" | base64 
```
先頭に半角スペースを入れると履歴に残りません。
```bash
 echo "test" | base64 
```

# 環境変数をunset

以下のコマンドでアンセットできます。

```
unset HISTCONTROL
```

# まとめ

Linuxで作業をしていると、履歴が残ると困るコマンドがたまにあります。
そういったときにはこの方法を試してみると良いでしょう。
自己紹介


# Introduction

閲覧頂きありがとうございます。
今後記事を書いていく上で、こんな人物が書いているんだよとお伝えできればと思い投稿します。

## 自己紹介

`kagami_t`と申します。
業務 4 年目のソフトウェアエンジニアです。
言語は主に`Python`です。
レガシーな環境の方が歴が長いため、モダン環境とのギャップを受けながら何とか食らいついています。
プライベートでは`Python`で便利なスクリプトを書いたり、Web 開発について勉強しています。

## 最後に

色々触ってはいますが、広く浅すぎる知識ばかりついてしまっているのが最近の悩みです。
2023 年中に転職予定です。
モダンな自社開発企業に転職して活躍したいですね。
そのためにも、業務やプライベートで学んだ内容をアウトプットしていきます。
よろしくお願いします。
Pythonで自作モジュール（別階層）を呼び出す時

# やりたいこと
Pythonで自作モジュールを作って、呼び出す場合について。
特に、カレントではないところにモジュールフォルダがある場合にそこからの呼び出し方。

# 参考文献
https://www.tohoho-web.com/python/module.html

// HTML4.0の頃から見てるけど、とほほさんはずっとWebを更新していて偉いなあ。

# ソリューション
~~~
# 以下のような階層で入っているとする

(current)
 ├─ test.py       # 呼び出し元のコード　
 └─ /hapy_mod     # モジュールが入っているフォルダ
　　　 └─ ham.py   # モジュールそのもの
~~~

```Python:ham.py
# 必要なモジュールのインポート
import pandas as pd
#-------------------------------------------------
# Excelの指定行からデータを取り出しDataFrameにして返す
# f_name:   Excelのファイル名
# s_name:   Excelのシート名
# row_num:  Excelの行番号(1オリジン)
#-------------------------------------------------
def get_df_from_excel(f_name, s_name, row_num=1):
    print(" -> get_df_from_excel: " + f_name + ", " + s_name + ", " +  str(row_num))
    work_df = pd.read_excel(f_name, sheet_name=s_name)
    # 読込み位置とindexを指定
    index = row_num - 1
    ret_df = work_df[index:]
    if (index >= 1):
        col_lists   = work_df.iloc[(index-1)]
    ret_df.columns = col_lists
    return ret_df
```

```Python:test.py
# 階層は . で表す
import hapy_mod.ham as hapy

in_file   = 'myリスト_0.xlsx'
in_sheet1 = 'リスト'

df1 = hapy.get_df_from_excel(in_file, in_sheet1,  4)
```
Anaconda (Miniconda) サイレントインストール (Windows)

# 概要
GUIでのインストール時のオプション指定が面倒なので、サイレントインストールでAnaconda (Miniconda)をインストール


# コマンド プロンプトの起動方法
- 「Windowsキー」+ 「Rキー」でファイル名を指定して実行ウインドウを表示
- cmd　と入力して「OK」を押下
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3297641/71c6d195-8c23-47e3-9cbb-cbab7d2427f8.png)




# Anacondaの場合
- ダウンロード
```
curl -O https://repo.anaconda.com/archive/Anaconda3-2023.03-Windows-x86_64.exe
```
※最新版をインストールする場合は、ファイル名の変更が必要
最新ファイル名は下記で確認
https://repo.anaconda.com/archive/

- インストール
```
start /wait "" Anaconda3-2023.03-Windows-x86_64.exe /InstallationType=JustMe /AddToPath=0 /RegisterPython=1 /S /D=%UserProfile%\Anaconda3
```

# Minicondaの場合
- ダウンロード
```
curl -O https://repo.anaconda.com/miniconda/Miniconda3-latest-Windows-x86_64.exe
```
※latestになっているので常に最新版がインストールされる

- インストール
```
start /wait "" Miniconda3-latest-Windows-x86_64.exe /InstallationType=JustMe /AddToPath=0 /RegisterPython=1 /S /D=%UserProfile%\Miniconda3
```

- Anaconda Promptの起動
```
%windir%\System32\cmd.exe "/K" %UserProfile%\Miniconda3\Scripts\activate.bat %UserProfile%\Miniconda3
```

- Jupyter Labのインストール
```
conda install jupyterlab
```

---
# 備考


上記に記載したインストール方法はGUIで下記オプションを指定してインストールした場合と同等
- Select Installation Type
※/InstallationType=JustMe
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3297641/9e63a907-ba2e-c0b9-c612-244a9eb2e70a.png)


- Choose Install Location
※/D=%UserProfile%\Miniconda3
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3297641/bc83faae-fe5e-7fba-40b3-80048deef5b2.png)


- Advanced Installation Options
※/AddToPath=0
※/RegisterPython=1
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3297641/07a32ac7-983c-2a8a-1b02-4b0edd552223.png)


---
参照 (公式ドキュメント)
https://docs.anaconda.com/anaconda/install/silent-mode/
[Python]Discord Botのつくりかた

## 前書き
Discord Botコトハジメ。趣味記事です。
メンションするとあらかじめ設定された数パターンの中からランダムに返信を返すDiscord Botを実装します。

## 下準備
Python実行環境の構築

https://qiita.com/shown_it/items/51ef6bcb36869863c59b


## Discord側の準備

### Botアカウントの作成

DEVELOPER PORTALにアクセス、ログインします。

https://discord.com/developers/applications/

右上の[New Application]をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201780/e7d39d37-a446-82b8-0a96-29a0435cbf3c.png)

モーダルウィンドウが開くのでbot名を入力し、[Create]ボタンをクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201780/e0dc978c-140f-d8cb-ed1d-80fec32720a5.png)

[Bot]タブを開き、[Add Bot]をクリック。その後、[Yes, do it!]をクリックして続行します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201780/279f326f-bb45-dddf-c971-58c8ab38b631.png)

今回は個人用のデモBotのため、[PUBLIC BOT]はオフに設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201780/19312781-78a1-3a2c-634b-93a658671829.png)

Privileged Gateway Intentsにもチェックを入れておきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201780/1c38d739-e25b-4b5c-840c-8b0e4a0581c3.png)


### tokenの取得
[Copy]ボタンをクリックするとクリップボードにtokenがコピーされます。tokenはこの画面から何度でもリセット可能です。

### Discordサーバへの招待
この時点ではbotはどのサーバにも参加していない状態です。自分が管理権限を持っているサーバへの招待を行います。

[OAuth2]タブの[URLGenerator]に移動し、[SCOPES]の[bot]にチェックを入れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201780/2a5ba64f-7f2c-126f-75a6-9937716d710f.png)

[BOT PERMISSIONS]が表示されるので、必要な権限にチェックを入れます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201780/49307258-10b6-5ef0-c441-952ecbb02ce7.png)

設定した権限に対応した招待用URLが生成されるので、URLをコピーして別タブで開き、任意のサーバに招待します。



## 実装
### 環境変数の設定
python-dotenvを使います。
解説・導入方法は↓

https://qiita.com/shown_it/items/2b85434e4e2658c484f4

```.env
DISCORD_TOKEN=コピーしたtoken
```

```config.py
from dotenv import load_dotenv
load_dotenv()

import os
DISCORD_TOKEN = os.getenv('DISCORD_TOKEN')
```

### discord.pyパッケージをインストール

```
$ pip install discord.py
```

### 実装

```discordbot.py
import discord
import config
import random

intents = discord.Intents.all()
client = discord.Client(intents=intents)

# Bot起動時に呼び出される関数
@client.event
async def on_ready():
    print("Ready!")

# メッセージの検知
@client.event
async def on_message(message):
    # 自身が送信したメッセージには反応しない
    if message.author == client.user:
        return

    # ユーザーからのメンションを受け取った場合、あらかじめ用意された配列からランダムに返信を返す
    if client.user in message.mentions:

        ansewr_list = ["さすがですね！","知らなかったです！","すごいですね！","センスが違いますね！","そうなんですか？"]
        answer = random.choice(ansewr_list)
        print(answer)
        await message.channel.send(answer)

# Bot起動
client.run(config.DISCORD_TOKEN)
```

### Botをオンラインにする

この時点ではBotはオフラインです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201780/ecf2c2ff-7bb2-c76d-aadd-bdb10dd11de6.png)

実装したdiscordbot.pyを実行すると…

```
$ python discordbot.py
```

無事オンラインになりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201780/86eb737a-0dc7-ddaf-80d8-f6a2358fba2a.png)

### 動作確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201780/5e70df47-c476-4682-7bb0-a4b44fbedd74.png)

動きました！！！！
「Waiting for a runner to pick up this job...」と表示されてGithub Actionsがスタートしない

## 「Waiting for a runner to pick up this job...」と表示されてGithub Actionsがスタートしない

せっかくGithub Actionsを設置したのに、「Waiting for a runner to pick up this job...」と言われて、いつまでたってもCIがスタートしないことがあります。↓実際のエラー

```
Requested labels: ubuntu
Job defined at: kojix2/chatgpt-cli/.github/workflows/build.yml@refs/heads/main
Waiting for a runner to pick up this job...
```

```
Requested labels: macos
Job defined at: kojix2/chatgpt-cli/.github/workflows/build.yml@refs/heads/main
Waiting for a runner to pick up this job...
```

特にエラーも出ないため、これだけ見ると、GitHub側の障害のようにも見えてしまいますが違います。
実はこれ、OSの指定が間違っていました。`ubuntu-latest` `macos-latest` などとしなければならないところが、単に `ubuntu` `macos` などとなっていたんです。OSの指定を直すと、すぐに動くようになりました。

この記事は以上です。
これに気がつくのに１日以上かかりました。誰かの役に立ちますように。
AWSで公開したWebサイトとGoogleサーチコンソールを連携する

AWSで公開した静的サイトにGoogleSearchConsoleを連携する方法です。
※ドメインを取得し、サイトを公開済みであることが前提です。

1.GoogleSearchConsoleにアクセスし、「今すぐ開始」をクリックします。
https://search.google.com/search-console/about?hl=ja

2.左側のドメインに、サイトのドメインを入力し、続行をクリック。
![スクリーンショット (112).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3276718/cc4daab0-f441-2d4f-b71e-25b705a2efda.png)

3.レコードタイプはTXT（推奨）を選択し、③のTXTレコードをコピーします。
![スクリーンショット (112).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3276718/68194ec7-0704-807d-d180-15fe55823bb5.jpeg)

4.AWSに移動し、Route53>ホストゾーンで、2で入力したドメインをクリック。その後「レコードを作成」をクリック。
![スクリーンショット (113).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3276718/1daff539-d656-bc6b-0db2-8db17dbfef22.jpeg)

5.右上3でコピーしたTXTレコードを貼り付ける。右上のレコードタイプはTXTを選択。他はデフォルトのまま、右下の「レコードを作成」をクリック。
![スクリーンショット (114).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3276718/32089858-dc4d-b339-e0cb-6e94d98cf0fb.jpeg)

6.しばらくするとTXTレコードが追加されるので確認します。
![スクリーンショット (115).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3276718/40f4921a-fc63-4281-651b-29a43a4f90e2.jpeg)

7.サーチコンソールの方で同期が完了するとメッセージが表示されます。
![スクリーンショット (114).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3276718/8b3684de-d08b-4330-bc69-40a276796e07.png)

これで設定は完了です、サーチコンソールの利用を開始しましょう！

設定したサイト
うごくえほんのowl-light
https://www.owl-light.net/
pandas to_excelのシート追加

# やりたい事
pandasのto_excelで既存のExcelシートにシートを追加してでDataFrameを書き込みたい。
良く忘れるのでメモ

# コード
下記でOK
```python
with pd.ExcelWriter('hogehoge.xlsx',engine='openpyxl', mode='a') as writer:
    df.to_excel(writer, sheet_name='追加したいシート名')
```

おわり
【Salesforce】【LWC】LWC備忘録

LWCの画面をイチから作るのを久しぶりにやって
色々忘れてるなあと思ったので備忘録として書き残します。

# LWCの作成方法

VSCodeで\force-app\main\default下の lwcフォルダで右クリック
「SFDC:Lightning Web コンポーネントを作成」 を選択しコンポーネント名を入力する 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1461666/eb1523a4-fe4c-a591-9540-da5d2d0f8dc6.png)

ちなみに開発者コンソールで「Lightning Component」を新規作成すると
Auraコンポーネントが作成されるので注意。

# Apexクラスのメソッドにアクセスできない場合
呼び出すメソッドに`@AuraEnabled`アノテーションを付与する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1461666/98906f7d-44e0-f7ca-1ee3-174815b772c4.png)
LWCのJavaScriptにimportを記述する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1461666/ff7e488b-3a93-c5e9-ec5f-a1f1611c79fa.png)

[参考：@AuraEnabledアノテーション](https://developer.salesforce.com/docs/atlas.ja-jp.lightning.meta/lightning/controllers_server_apex_auraenabled_annotation.htm)
[参考：Apexメソッドのインポート](https://developer.salesforce.com/docs/component-library/documentation/ja-jp/lwc/lwc.reference_salesforce_modules)

# Apexクラスの変数が取得できない場合
`@AuraEnabled`アノテーションを付与する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1461666/f5f45f3e-079b-7f96-8572-6fd02c67a2e7.png)
とくに内部クラスとか忘れがち

[参考：@AuraEnabledアノテーション](https://developer.salesforce.com/docs/atlas.ja-jp.lightning.meta/lightning/controllers_server_apex_auraenabled_annotation.htm)

# Lightningページの編集画面に表示されない場合
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1461666/4b2eb0fb-20f4-db36-1811-bc8c60650f8f.png)
-meta.xmlの`tagets`を設定する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1461666/8abbe3ba-1394-a0e6-c2ad-3655044b1d55.png)
コピーはこちらからどうぞ
```
  <targets>
    <target>lightning__AppPage</target>
    <target>lightning__RecordPage</target>
    <target>lightning__HomePage</target>
  </targets>
```
※用途に合わせてtargetは選んでください。
[参考：LightningComponentBundle](https://developer.salesforce.com/docs/atlas.ja-jp.api_meta.meta/api_meta/meta_lightningcomponentbundle.htm)

# 書く予定のネタ
- モーダルの表示方法について
- 画面遷移について
- 色々なアイコンについて
- lightning-inputでEnterキーが押されたときにfunctionを実行する
かにの網元 トップページへようこそ

# かにの網元 トップページへようこそ

## はじめに
最近Qiitaにて、記事を投稿するようにしたので、
備忘録をまとめます。

## 自己紹介
元ゲームプログラマー（最近はIoTプログラマー）

1987年　PC-9801　タイピング練習ソフト　「パソコン先生」が初めての仕事です！
1988年　PC-9801　アドベンチャーゲームを作りました。
この時は、スクリプトエンジンやマウスドライバー、FDDのプロテクトなど任されました。

その後、コンシューマーゲーム機の開発をはじめます。

はじめてのコンシューマーゲームは、PC-ENGINEのCD-ROM2のプロジェクトでした。
今では540MBというとゴミみたいな容量ですが、当時はお墓位のハードディスに
CD-ROMのイメージを構築し、データーを読みながらゲームを動作させていました。

このCD-ROMは読み込み速度が「線速度一定」で150KByte/sという遅さ！
またレーザー光を当ててCD-ROMからデータを読み出す「ヘッド」という機構がくせ者！
内周と外周では、ディスクの回転速度が違うのでモーターを安定させるために時間がかかります。なので出来るだけヘッドを動かさずにデーターを読む仕組みが必要でした。

その後は、Nintendo GBC,GBA,DS,3DS,Wii-U,Switch
Sony PSP,PS2,PS3
などのプロジェクトのも参加してきました。（途中SEGAとNECもありますが省略します）

Nintendo DSの時は、ファミコン版ロードランナーを
DS上で動作させるクロスプラットフォーム開発を行いました。
(CPU6502の仮想マシンをARM上に構築してあります）

このように、低レベルな環境は慣れているのですが
最近の開発にはなかなか慣れないおじさんプログラマーになっています。


# 最近の開発（趣味）は次の通り
1. M5Stack GameLibrary
1. Arduino IoT 開発
1. RaspberryPi 開発
1. wi-san評価モジュールおよびテスト環境構築
1. 時間があるときには、TypeScriptなども勉強しています。


それでは皆様よろしくお願いいたします。
DocsBotの仕組み

# はじめに
世の中で話題になっているChatGPTですが、ChatGPTの内部で使われているGPT-3.5とGPT-4のモデルはファインチューニング（追加学習）することができません。ファインチューニングできるのは、`davinci`、`curie`、`babbage`、`ada`などの2020年に公開されたGPT-3のモデルのみです（2023年3月時点）。

> Fine-tuning is currently only available for the following base models: davinci, curie, babbage, and ada. These are the original models that do not have any instruction following training (like text-davinci-003 does for example). 

引用元：https://platform.openai.com/docs/guides/fine-tuning

GPT-3のモデルを頑張ってファインチューニングする手もありますが、正直精度的になかなか厳しいです。可能であれば、GPT-3.5やGPT-4のモデルの恩恵を預かりたいところです。これらのモデルをファインチューニングする方法はないかと考えている人も多いと思います。

# DocsBotとは？
DocsBotは、GPT-3.5やGPT-4をあたかもファインチューニングしたかのように使うことができるサービスです。日本語でも使用例が幾つか紹介されています。

https://note.com/u17da/n/nd5a15910e564
https://twitter.com/masahirochaen/status/1635136368300163079

使い方は、文書を登録するだけです。ファイル形式は一般的なものには一通り対応しており、WebページやYouTubeの字幕にも対応しているようです。ChatGPTのプロンプトとは異なり[^1]、登録できる情報に厳しい制約はありません[^2]。そして、文書を登録した上で質問を送ると、登録した文書の情報を考慮した返答が返ってきます。ファインチューニングしているわけでもないのに、不思議です。

# DocsBotの仕組み
ファインチューニングしていないのに、どうしてファインチューニングしたかのような結果が返ってくるのでしょうか？その仕組みを簡単に説明します。

[公式サイトのFAQ](https://docsbot.ai/#faq)を見ると、次のように書かれています。

> **How does DocsBot work?**
> 
> It's a bit technical, but here is a brief overview. We use OpenAI's embedding and ChatGPT APIs, as well as vector databases to store our index. All ingested documentation is cleaned up and divided into smaller chunks and labeled by source. We then use the GPT embedding API to generate a vector representation of each chunk and store it in our vector db index. When a user asks a question, we convert it to an embedding, and perform an advanced semantic search for closest matches to the user's query. Then we take the most relevant chunks, included them as context along with the original question, and use the ChatGPT API to generate a response in markdown format that we then convert to HTML and display to the user.

英語なので分かりにくいかもしれませんが、仕組みは次のように非常にシンプルです。

1. 事前に文書を登録する
    - その際、裏側でチャンクという単位（イメージとしては項目毎）に分けて登録しておく
2. 質問が来た際、質問の内容と最も関連性の高いチャンクを見つける
3. 質問と2で見つけたチャンクをChatGPTに投げて応答を生成する

1と2はDocsBot側で行っています。関連性の高いチャンクを見つけるには、embedding（分散表現）が利用されています。類似文書検索でよく使われる技術です。そして、チャンクはChatGPTに送ることができる最大トークン数などを考慮して分けていると考えられます。ChatGPTが使わているのは3の時だけです。

# さいごに
DocsBotの仕組みを簡単に説明しました。ChatGPTに投げる前にちょっとした処理を挟むことで、GPT-3.5やGPT-4をあたかもファインチューニングしたかのように使うことができます。今後、日本でもDocsBotを利用したサービスがどんどん出てくると思います。

[^1]: gpt-3.5-turboは最大トークン数が4,096、gpt-4は最大トークン数が8,192
[^2]: Powerプランの場合「5k Source Pages」まで登録可能（2023年3月時点）
VSCode 空白（タブ、半角/全角スペース）を区別して見える化する

### 空白文字（タブとスペース）を表示する方法


https://www.javadrive.jp/vscode/setting/index8.html


上記の方法の中で、「all」を使用する。





---
### 全角スペースを可視化する方法

https://jet-blog.com/vscode_zenkaku/

---
### 「タブキー」と「（半角）スペースキー」を区別する方法

https://dianxnao.com/visual-studio-code%E3%81%A7%E3%82%BF%E3%83%96%E3%82%92%E5%8B%9D%E6%89%8B%E3%81%AB%E3%82%B9%E3%83%9A%E3%83%BC%E3%82%B9%E3%81%AB%E5%A4%89%E6%8F%9B%E3%81%97%E3%81%AA%E3%81%84%E8%A8%AD%E5%AE%9A/#:~:text=%E8%A8%AD%E5%AE%9A%E7%94%BB%E9%9D%A2%E3%81%AE%E6%A4%9C%E7%B4%A2%E3%83%9C%E3%83%83%E3%82%AF%E3%82%B9,%E8%A8%AD%E5%AE%9A%E3%81%AF%E4%BB%A5%E4%B8%8A%E3%81%A7%E3%81%99%E3%80%82



---
Inno Setupで作ったインストーラの使用許諾契約書の同意画面で既定で「同意する」をチェックする方法

Inno Setupで作ったインストーラの使用許諾契約書の同意画面で、あらかじめ「同意する」にチェックする方法の説明です。

# はじめに
Inno Setupで作ったインストーラに表示される「使用許諾契約書の同意」画面では、既定値で「同意しない」にチェックされていますが、インストール後もアップデートなどの目的でインストーラが使用される場合などは、「同意する」をチェックする操作が多少煩わしく感じます。
そこで「使用許諾契約書の同意」画面で、あらかじめ「同意する」をチェックすることにします。

# .issファイルの修正

.issファイルの[Code]セッションにInitializeWizardプロシージャを定義して、WizardForm.LICENSEACCEPTEDRADIOのCheckedプロパティをtrueに設定するように改造します。

```
[Setup]
LicenseFile=eula.txt

[Code]
procedure InitializeWizard();
begin
    WizardForm.LICENSEACCEPTEDRADIO.Checked:=True;
end;
```

# 修正後のインストーラ画面
修正後、コンパイルしたインストーラでは、起動時に既定で「同意する」が選択されるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1318796/37d38bfe-3aeb-0d13-e848-94b2673258fe.png)
ChatGPTのセリフ生成はどれぐらい優秀なのか？

言ってないセリフシリーズをご存知でしょうか？
キャラクターや実在人物が言っていないにも関わらず様々な理由で言ったことになっているセリフが存在します．
今回はChatGPTのテキスト生成を用いて，お題に沿ったセリフを生成してみたいと思います．

今回は以下の言ってないセリフシリーズの一部を使用し新しいセリフを作ってもらいました．
↓言っていないセリフシリーズ（参考元）

https://dic.pixiv.net/a/%E8%A8%80%E3%81%A3%E3%81%A6%E3%81%AA%E3%81%84%E3%82%BB%E3%83%AA%E3%83%95%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA#:~:text=%E8%A8%80%E3%81%A3%E3%81%A6%E3%81%AA%E3%81%84%E3%82%BB%E3%83%AA%E3%83%95%E3%82%B7%E3%83%AA%E3%83%BC%E3%82%BA%E3%81%A8%E3%81%AF%E3%80%81%E3%81%9D%E3%81%AE%E4%BA%BA%E7%89%A9%E3%81%8C%E5%AE%9F%E9%9A%9B%E3%81%AB%E3%81%AF%E8%A8%80%E3%81%A3%E3%81%A6%E3%81%AA%E3%81%84%E3%81%8C%E8%A8%80%E3%81%A3%E3%81%A6%E3%81%9D%E3%81%86%E3%81%AA%E3%82%BB%E3%83%AA%E3%83%95%E3%81%AE%E3%81%93%E3%81%A8%E3%81%A7%E3%81%82%E3%82%8B%E3%80%82%20%E4%B8%96%E3%81%AE%E4%B8%AD%E3%81%AB%E3%81%AF%E6%95%B0%E5%A4%9A%E3%81%8F%E3%81%AE%20%E5%90%8D%E8%A8%80,%E3%80%81%20%E5%90%8D%E5%8F%B0%E8%A9%9E%20%E3%81%8C%E5%AD%98%E5%9C%A8%E3%81%99%E3%82%8B%E3%81%8C%E3%80%81%E3%81%9D%E3%81%AE%E4%B8%AD%E3%81%AB%E3%81%AF%E7%99%BA%E8%A8%80%E3%81%97%E3%81%9F%E4%BA%BA%E7%89%A9%E3%81%A8%E3%82%BB%E3%83%AA%E3%83%95%E3%81%8C%E6%8D%BB%E3%81%98%E6%9B%B2%E3%81%92%E3%82%89%E3%82%8C%E3%80%81%E3%81%9D%E3%81%AE%E4%BA%BA%E3%81%8C%E8%A8%80%E3%81%A3%E3%81%9F%E3%81%93%E3%81%A8%E3%81%AB%E3%81%AA%E3%81%A3%E3%81%A6%E3%81%84%E3%82%8B%E3%82%BB%E3%83%AA%E3%83%95%E3%81%8C%E5%B0%91%E3%81%AA%E3%81%8B%E3%82%89%E3%81%9A%E5%AD%98%E5%9C%A8%E3%81%99%E3%82%8B%E3%80%82%20%E5%BA%83%E3%81%BE%E3%81%A3%E3%81%A6%E3%81%97%E3%81%BE%E3%81%A3%E3%81%9F%E5%BE%8C%E3%81%AB%E6%9C%AC%E4%BA%BA%E3%81%8C%E3%81%9D%E3%81%AE%E3%82%BB%E3%83%AA%E3%83%95%E3%82%92%E8%A8%80%E3%81%86%E3%82%B1%E3%83%BC%E3%82%B9%E3%82%82%E3%80%82

以下は実際に使用した言ってないセリフ
![スクリーンショット (3903).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3292130/f1dcf888-284e-8e5b-45db-9d2ce94b0420.png)

そして実際に出てきたものがこちら
![スクリーンショット (3904).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3292130/8b31575c-26f0-5e43-cbe0-3381064327ca.png)

私はお題に沿った内容を作ってくれたと思いますが，皆さんはどう思われましたか？
DBeaverで作成したデータベースが表示されないとき

初歩的な設定ですが日本語で検索に引っかからず少し時間を食ったので、私のようなDBeaver初心者の方に向けて書き留めておきます。
[DBeaver can only see default PostgreSQL database in connection](https://stackoverflow.com/questions/54235029/dbeaver-can-only-see-default-postgresql-database-in-connection)に手順はありますが下記に書いておきます。

1. `Database Navigator上の対象のデータベースを右クリック`
1. `Edit Connection`
1. `Connection settings`
1. `タブのpostgresqlを選択`（おそらくここは使用中のSQLが表示されると思うので都度変わります）
1. `show all databases にチェック`

以上です。
**Good DBeaver Life!!**
Windows11におけるBluetoothが接続できないエラーについて

私が、windows11にアップグレードして数日後に発生したBluetoothトラブルの覚書として、ここに書いておきます。

## 実例

ネットで調べて所、下記リストの様にWindows11でBluetoothに関する同様のトラブルが結構な例あるようです。

・タスクバーのコントロールセンターにBluetoothがない
・設定にBluetoothの有効化ボタン(トグル)がない
・「設定＞システム＞Bluetoothのトラブルシューティング」でも「未解決」
　(検出はできていたが解決には至らなかった)
・Bluetoothの項目がデバイスマネージャーに表示されない
・Bluetoothアダプタのドライバーは最新
・Windows Updateは最新

## 一般的な解決策

さらに解決策として、次のような方法が検索すると引っかかった。
この[サイト](https://www.drivereasy.jp/windows-11-bluetooth-missing/)がよくまとまっていた。

・Bluetoothサポートサービスを開始する
「ファイル名を指定して実行」で`service.msc`を入力し決定、「Bluetooth サポートサービス」を右クリックし、「プロパティ」をクリック。サービスの状態を停止したり開始したりして設定をいじる。

・Bluetoothトラブルシューティングを実行
設定＞システム＞Bluetoothのトラブルシューティング

・システムスキャンを実行
コマンドプロンプトを管理者として実行し、`sfc/scannow`コマンドを実行。

・Windows Updateで更新
普通に更新

・とりあえず再起動

## Windowsサポートで見つけた解決策

しかしどれを試しても私の環境ではうまくいきませんでした。

そこで、[Microsoft サポートへのお問い合わせ](https://support.microsoft.com/ja-jp/home/contact?SourceApp=smc2&ContactUsExperienceEntryPointAssetId=S.MP.RECEIVE-REMOTE-ASSISTANCE)で質問してみた。

そもそもボタンや表示が消えてるからできないことだらけで、`いいえ`を押し続けてたら表示された最後のやつが有効だった。


>変更を行うために、PCを完全にシャットダウンして再起動する必要がある場合があります。これはコールドリブートと呼ばれます。Bluetooth アダプターを含むコンピューターのすべてのコンポーネントを完全にリセットできるため、これらの手順を厳密に実行することが重要です。
>
>**PCを完全にシャットダウンして再起動します(コールドリブート)**
>
>コールドリブートを実行するには、次の手順に従います。
>
>1.**ドキュメントを保存**し、開いている**プログラムをすべて閉じます**。
>
>2.画面がオフになるまでデバイスの電源ボタンを**10秒間押し続けて**から、**電源ボタン**を放します。
>
>3.PCの**電源ケーブルを抜き**、30秒待ってから、PCを接続し直します。ラップトップPCを使用している場合は、充電器も必ず取り外してください。
>
>4.**デバイスの電源を入れ直します**。
>
>5.**Bluetooth設定ページ**に戻り、Bluetoothアイコンが戻ったかどうかを確認します。

私のPCはデスクトップの自作PCですが、これでBluetoothが反応するようになりました。
また、元々登録していたBluetooth機器を再度登録しなおすといった作業も不要でした。

これは電源切るだけで簡単なのでぜひ試してみてください。
Unity Editorの拡張　3/3  隣接する点を線でつなぐ

# 概要
今回は各点を結ぶ線描画を加えます。線を加えることで移動経路がわかりやすくなります。
以下画像

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/a31c75b3-a646-4e58-b586-498685f125aa.png)


# 開発環境
IDE:Rider
Unity:2020.3.42(LTS)
OS:Windows10
# 実装のポイント
GizmoクラスとMonoBehaviourクラスのOnDrawGizmosメソッドを使います。
Gizmosクラスはシーンの視覚的にデバッグしやすくするために使われます。
OnDrawGizmosメソッドはシーンのGUIをいじった時に実行されるメソッドです。再生、非再生問わずに実行されます。


# コード部分
```diff_c_sharp:Waypoint.cs
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;

// 座標を定義しているクラス、オブジェクトにアタッチする
public class Waypoint : MonoBehaviour
{
    // Start is called before the first frame update
    [SerializeField] private Vector3[] points;
    public Vector3[] Points => points;

    void Start()
    {
    }

    // Update is called once per frame
    void Update()
    {
    }

+    private void OnDrawGizmos()
+    {
+        for (int i = 0; i < points.Length; i++)
+        {
+            if (i < points.Length - 1)
+            {
+                Gizmos.color = Color.yellow;
+                // 隣接する２点間の距離を引く
+                Gizmos.DrawLine(points[i], points[i + 1]);
+            }
+        }
+    }
}


```



# 参考
Gizmoクラス

https://docs.unity3d.com/ja/2020.3/ScriptReference/Gizmos.html

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2710623/59fdf831-4c26-84bc-af23-2173745d458d.png)


OnDrawGizmosメソッドについて

https://docs.unity3d.com/ja/2021.1/ScriptReference/MonoBehaviour.OnDrawGizmos.html


github コミット分


https://github.com/RYA234/2DTowerDeffenceMyLearning/commit/d6b8256d8d97559088cd74d0e53b946414be2a41
チュートリアルで始める！データサイエンス基礎

# <font color=royalblue>データサイエンスを始めよう！</font>
これからデータサイエンスを始める人向けに、データサイエンスに必要となるツールの公式のチュートリアルをGoogleコラボ上で動かしながら学べる形式にして集めてみました（著者が以前にQiitaに投稿した記事をマージしたものです。英語版のものは日本語に翻訳し、また分かりにくい箇所に補足を入れています）。
適宜復習にも活用できると思います。

## <font color=green>Pythonチュートリアル(3.9.1)</font>
+ [Python チュートリアル １〜４章](https://colab.research.google.com/drive/1c1AJFZL6-bWlvtrolAbkIgrNYga_CWNu?usp=sharing)
+ [Python チュートリアル 5〜8章](https://colab.research.google.com/drive/1EPnoYJEfpbrUucrkjMwgUXsfWKj6rXXj?usp=sharing)
+ [Python チュートリアル 9〜16章](https://colab.research.google.com/drive/1HwSFYP4FTxfz_698sYpVklWv96z964Df?usp=sharing)

Pythonのチュートリアルに補足したものです。

## <font color=green>Numpyチュートリアル</font>
+ [Numpy quickstart](https://colab.research.google.com/drive/1UgBuTsk1iNmhGgxTfXrRlxZXtZNtl_tU?usp=sharing)
+ [the absolute basics for beginners](https://colab.research.google.com/drive/19r8K3KW7HPPiPhhEWW-peD__oADBc-rl?usp=sharing)

Numpy quickstart、the absolute basics for beginnersのHPを翻訳及び補足したものです。

## <font color=green>Pandasチュートリアル</font>
+ [Getting started tutorials](https://colab.research.google.com/drive/115kBAGUiCqPqWOt7sVP7xKrvIKLMod1y?usp=sharing)

Pandsのチュートリアルを翻訳及び補足したものです。

## <font color=green>Matplotlibチュートリアル</font>
+ [Matplotlib Introductory](https://colab.research.google.com/drive/13-yqL9o2ectX66V_8JDOY9ohUNU7g_G5?usp=sharing) 

Matplotlibの公式ホームページのチュートリアルにある 初級編（Introductory） の５つを翻訳及び補足したものです。
+ クイックスタートガイド
+ Pyplotチュートリアル
+ 画像チュートリリアル
+ プロットのライフサイクル
+ スタイルシートとrcParamsでMatplotlibをカスタマイズする
プライベートなガントチャートを作ってみた

# はじめに
こんにちわ、nkitaoと申します。
35歳にして、急に個人開発に目覚めた漫画、ゲーム好きの社内SEです。
この年齢で今から個人開発？？なんて、今さら感がすごくあるのですが、趣味や何かに没頭することに年齢なんて関係ないよね！

個人開発歴は１年未満のほぼ素人ですが、個人開発したもの、していくものを綴っていきたいと思います！

# 作ったもの
[EasyGanttChart(気軽にガントチャート）](https://egc.nkitao.com/)
![スクリーンショット 2023-03-24 17.34.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237628/653ca877-00aa-0f5c-71bb-e63c45b9c996.png)

## 何ができるのか
* ガントチャートみたいに**日付**、**タイトル**、**進捗率**で管理
* 完全に**プライベード**（誰かと共有なんて素晴らしい機能はありません）
* ブラウザのローカルストレージに保存
時代に逆行して誰とも共有しない、自分だけのガントチャートです。

:::note warn
PC専用です、スマホでアクセスしても残念な感じになっています。
:::

## なぜ作ろうと思ったのか
自分のタスクをWBS的？な形で管理したいなーとふと思いました。
完全個人でタスクと日付と進捗率でざっくりで管理したい（全体的に何日に終わるのかの目処を立てたい）
全体の工程が見えるツールがほしいなぁ、と思った。

## 誰が喜ぶのか
正直この手のツールなんて巷に溢れてるので、需要なんてないと思う（いや、ない）。
だけど自分は実際に使ってみて、動きやデザインはイマイチだけど、いいなと思いながら使っています。

## 開発概要
### 言語
* React
* TypeScript 
### ライブラリ
* [rsuite](https://github.com/rsuite/rsuite)
* [gantt-task-react](https://github.com/MaTeMaTuK/gantt-task-react)
* [dnd](https://github.com/hello-pangea/dnd)
ガントチャート作る際に大変お世話になったライブラリたちです。
このような素晴らしいライブラリが普通に利用できるなんて本当に嬉しい、超リスペクトです。

## 最後に
ほぼ自分のためだけに作ったようなものだけど、やっぱり誰かに共有したくなったのでQiitaに残すことにした。
1人でもこのツールが刺さってくれるとすごく嬉しいです。
【競プロ/Swift】まよコン 2023/03/22

# 注意事項
本記事は復習のための日記的なものであり、丁寧な解説を求めている方には不十分かと思われますので、ご了承のほどお願いいたします。

# 正解済み問題
## B - Who is missing?
**問題文**
[こちら問題ページ](https://atcoder.jp/contests/abc236/tasks/abc236_b)

**解答**
最初は普通に数が足りない値を全探索してTLEになってました。
元の数列の合計と引かれた数列の合計の差分を見れば、簡単に答えが出るので、その方法で正解。

```swift
let N = Int(readLine()!)!
let aArray = readLine()!.split(separator: " ").map{Int($0)!}

let total = Set(aArray).reduce(0, +) * 4
let sum = aArray.reduce(0, +)
print(total - sum)
```
# 誤答及び、未解答問題
## C - Triangle?
**問題文**
[こちら問題ページ](https://atcoder.jp/contests/abc224/tasks/abc224_c)

**正答**
この問題を特には三角形の面積を求める公式を知っている必要がある。
以下公式の解説の引用になります。
![スクリーンショット 2023-03-25 14.30.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357475/fa0b8be6-1dc2-b11d-7d64-f84e10164008.png)

この公式を使って０かどうかを判断すればいいので、あとは判断する3つの座標をループ処理で順番に流せば答えがわかります。


```swift
let N = Int(readLine()!)!
let points = (0..<N).map {_ in readLine()!.split(separator: " ").map {Int(String($0))!}}
 
func ok(_ x: [Int], _ y: [Int], _ z: [Int]) -> Bool {
    return (y[0] - x[0])*(z[1] - x[1]) - (z[0] - x[0])*(y[1] - x[1]) != 0
}
 
var ans = 0
for i in 0..<N {
    for j in (i + 1)..<N {
        for k in (j + 1)..<N {
            if ok(points[i], points[j], points[k]) {
                ans += 1
            }
        }
    }
}
print(ans)
```

# 参考
https://kenkoooo.com/atcoder/#/contest/show/809adda4-26ab-409f-a19f-8ec4e20fcb03?activeTab=Problems
Xcodeで保存時にコードを自動整形する設定（WIP）

# Xcodeのコード整形機能（手動）
こっちは手動で整形する方法です。
## 1. EditorからRe-Indent
- 整形範囲を選択 > メニューバー > Editor > Structure > Re-Indent
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245300/f7364192-05d8-a3e9-3f7f-a92d3da5a4a7.png)

## 2. ショートカットキー
- 整形範囲を選択 > Control + i
（Fix：Control + l はRe-Indent）

# ライブラリによるコード自動整形
完全にタイトル詐欺状態だったので追記中...
## SwiftFormatを利用する
https://github.com/nicklockwood/SwiftFormat

WIP...
Googleデジタルマーケティング＆Eコマース：マーケティング・ゴールのROIを決める

# はじめに

”Googleデジタルマーケティング＆Eコマース プロフェッショナル認定証"　の受講が完了しました(2022/3/3)。私がこのコースから得られた素晴らしい体験を、テーマごとに分割して、わかりやすくご紹介しています。興味を持たれた方は、是非下記を受講してみてください。

https://jp.coursera.org/professional-certificates/google-digital-marketing-ecommerce

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/41012708-dfb2-da45-b1a3-5a683fab51bc.png" width="800">

# ROASとLTV

- ROAS = 生み出された収益/広告費
or 
- ROAS = LTV(顧客生涯価値,一定期間内に顧客1人あたりが生み出した平均的な収益)

ROASとLTVの両方によって、キャンペーンの投資収益率（ROI）を推定することができます。

ROASは、キャンペーンの短期的なパフォーマンスを測定する方法を提供します。これは、基本的な質問に答えるものです。

- キャンペーンに費やした費用以上の収益をもたらしたか？

LTVは、より戦略的な問いに答えるものです。

- キャンペーンは、顧客の「粘着性」を高め、追加購入を促したか？

ROASとLTVの両方を考慮すると、マーケティング活動のROIの数値的側面と戦略的側面の両方に価値を見出すことになります。

# LTVを用いたROI

LTVの定義では、顧客1人あたりに発生する平均収益を計算するための特定の期間を定めていない。その期間は、年、四半期、月とすることができます。小売業では数ヶ月が最も一般的に使用されています。期間が過去から現在までの場合、LTVはトータルLTVと呼ばれることもある。

期間が将来の日付を含む場合、LTVは予測された生涯価値（pLTV）と呼ばれる。pLTVは取引と顧客の行動に依存して将来のLTVを予測するため、購入と顧客とのインタラクションが増えるごとにpLTVはより正確になっていく。

pLTVは、デジタル・マーケティング活動の効果を、売上が発生する前に推定する一般的な方法です。例えば、新規登録した顧客が初めて購入した場合、類似の顧客タイプの過去のパフォーマンスを使用して、その顧客が長期的にもたらす収益額を予測することができます。

:::note info
完了したキャンペーンのROIを決定する場合、pLTVではなく、トータルLTVを使用します。pLTVを使用することで、まだ進行中のキャンペーンのROIを予測することができます。
:::

完了したキャンペーンの成功やROIを測定するためにLTVを使用する方法は、次の2つです。

- LTV by channel
- LTV to CAC ratio (LTV:CAC)

### <font color="MediumBlue">LTV by channel</font>

キャンペーンでチャネルごとにROASを測定できるように、LTVも同様に測定できます。チャネルごとのコンバージョンのアトリビューションは、あらかじめ有効にしておく必要があります。

キャンペーン内の各チャネルについて、以下を計算します。
- **「LTV＝平均受注額（AOV）×購入頻度」**

各チャネルのLTVを比較することで、チャネルごとのROIを洞察することができます。

### <font color="MediumBlue">LTV to CAC ratio</font>

顧客獲得コスト（CAC）は、有料顧客を獲得するための平均的なコストであることも学んだはずだ。LTVとCACは、LTV/CAC比を算出するために使用される。この比率は、キャンペーン中に新規顧客を追加することで得られる価値が、顧客獲得にかかった費用を十分にカバーできるかどうかを判断するのに役立ちます。この比率が高いほど、ROIは高くなります。

キャンペーンまたはチャネルのLTV対CAC比は、以下を使用して計算できます。

- **「LTV to CAC ratio = LTV/CAC」**

通常、2以上が良好とされます。理想的な結果は3程度です。2を下回る結果は、市場シェアを拡大するために意図的に支出を増やしている場合に起こり得ます。しかし、そうでない場合は、CACを減らすために予算を削減し、LTV/CAC比を高める必要があるかもしれません。結果が3以上であれば、ROIは堅実であり、安定した強固な収益源があると言えます。結果が3以上であれば、おそらくビジネスを拡大するのに十分な予算があると思われます。例えば、ブランドと関連する製品の種類を多様化することが目標であれば、十分な資金がある広告キャンペーンでその目標をサポートすることができます。

# マーケティングミックスモデル

マーケティングミックスモデルは、広告主が広告の効果やROIを予測するために用いる統計モデルである。これらのモデルは、過去のキャンペーンから少なくとも2年間の履歴データに依存しています。1960年代に初めて開発されて以来、近年の人工知能（AI）や機械学習の恩恵により、キャンペーンのROIを予測する上でより信頼性の高いものとなっています。
ワイヤレスイヤホンのBluetooth接続ができなくなった

# 初めに
Bluetoothのワイヤレスイヤホンが急に接続できなくなったのでその対処法と原因をメモ程度に残します

# 状況
ワイヤレスイヤホン： [SENNHEISER CX Plus SE True Wireless](https://www.sennheiser-hearing.com/ja-JP/p/cx-plus-se-true-wireless/cl0tctj9q3qli0cw3yx0k550i/)
接続機器： [iPhone13 mini](https://www.apple.com/jp/shop/buy-iphone/iphone-13?afid=p238%7CsTAtNqhwZ-dc_mtid_18707vxu38484_pcrid_649570170942_pgrid_142679420564_pntwk_g_pchan__pexid__&cid=aos-jp-kwgo-iPhone--slid---product-)

この二つがある日繋がらなくなってしまった...

# 対処
* iPhoneのソフトウエアをアップデート
* ワイヤレスイヤホンの充電を確認する
* ワイヤレスイヤホンの設定をリセット

など，いろいろ試しましたが

ワイヤレスイヤホンの設定をリセットし，iPhoneのワイヤレスイヤホンの情報を削除し，繋ぎ直したら解決しました

# 原因
この問題の原因は，**iPhoneの名前を変更した**ことでした．Bluetooth接続は接続先の機器の名前を確認して接続しているようでした． iPhoneの名前を変更するときはお気をつけて...
Googleデジタルマーケティング＆Eコマース：ROI・ROASの計算方法

# はじめに

”Googleデジタルマーケティング＆Eコマース プロフェッショナル認定証"　の受講が完了しました(2022/3/3)。私がこのコースから得られた素晴らしい体験を、テーマごとに分割して、わかりやすくご紹介しています。興味を持たれた方は、是非下記を受講してみてください。

https://jp.coursera.org/professional-certificates/google-digital-marketing-ecommerce

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/41012708-dfb2-da45-b1a3-5a683fab51bc.png" width="800">

Rマーケティング担当者は、マーケティングキャンペーンの成功を判断するために、ROIやROASの計算を利用しています。

# ROI（投資収益率）

***<font color="MediumVioletRed">『マーケティング・キャンペーンから生み出された利益を測る指標』</font>***

具体的には、投資額に対する純利益（儲け）の割合です。

ROIは、2つの方法で計算することができます。

### 1つ目の方法

ROIを計算するために、キャンペーンを実施した期間中の総売上高成長率からマーケティングコストを差し引き、その結果をマーケティングコストで割ります。売上成長率とは、以前の売上と比較して売上がプラスになることです。言い換えれば、売上高のプラス変化、つまりセールスリフトです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/aef5eb8b-24d2-ecbf-eb36-4bb8a85d4dbd.png" width="500">

例えば、売上成長率が20万ドルで、マーケティング費用が5万5千ドルだった場合、マーケティングROIは20万から5万5千を引いたもの、あるいは14万5千を5万5千で割ったものとなります。ROIは2.6である。ROIが1であることを損益分岐点と考えるなら、あるいは売上の伸びがマーケティング費用に等しいとき、ROIが1より大きいことは上向きである。ROIが高ければ高いほど、上昇幅は大きくなります。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/34156f34-6557-ab1b-f0b2-689cb5bf59b7.png" width="500">

### ２つ目の方法

顧客生涯価値（LTV）と呼ばれるものを使用することです。顧客生涯価値とは、一定期間内に顧客1人あたりが生み出した平均的な収益のことです。LTVの測定には、ソフトウェア分析ツールが役立ちます。

Google analytics 4は、さまざまなチャネルで獲得したユーザーのLTVを、指定した獲得日範囲（最大120日）でレポートします。これにより、数ヶ月に渡る顧客行動を監視し、マーケティング・キャンペーンの効果を観察することができます。LTVは、獲得概要レポートで確認することができます。LTVが上昇傾向にあることは、ROIが増加していることを示します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/b5d0b178-ec6c-2986-d39a-795299ab6bb8.png" width="600">

# ROAS（広告費用対効果）

***<font color="MediumVioletRed">『発生した収益を広告に費やした金額で割った数値』</font>***

パフォーマンスゴールは、より高いレベルのマーケティングおよびビジネス目標から作成され、それに結びつけられます。デジタル・チャンネルのROAS目標は、通常、メディア・ミックス全体のキャンペーン・レベルのROASと比較して設定されます。

- 5:1 ROASというマーケティングゴールがあれば、
- 検索広告で3:1 ROAS、
- ディスプレイ広告で4:1 ROAS

というように、各チャネルのROASパフォーマンスゴールが設定されるのが一般的です。チャネルごとのROASの結果は、全メディアにわたる全体的なROASに貢献します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2358573/38127a86-3fa4-c1fb-8806-d1de697cc8b2.png" width="600">

# ROASが目標を達成していない場合の対策

キャンペーンのアナリティクスを監視していると、いつ介入が必要なのか疑問に思うことがあります。ROASが目標を達成していない場合、何ができるのでしょうか？ これが完全なリストではありませんが、いくつかの対策をご紹介します。

- キャンペーン期間を長くすることができます。コンバージョンの測定は、変動があったりノイズが多かったりします。ROASは、市場の変動により、一時的に目標値より低くなることがあります。このような変動はコントロールできない可能性があるため、辛抱強く対応してください。ROASを評価するには、最低50件のコンバージョンが報告された後に行うのがよい方法です。
- 総合的なROASを使用する代わりに、製品グループごとにROAS目標を設定することができます。イブニングドレス、ドレス、タキシード、スーツを販売するアパレルショップが、全体のROASを4：1にしたいが、当初はその目標を達成できなかったとします。ROASを全体の目標にする代わりに、フォーマルウェアとカジュアルウェアのような製品グループごとに目標を分割することができます。商品グループ別のROASは、キャンペーンの成功をより正確に測定することができるかもしれません。
- ROASの目標値の設定方法を見直す。ROASが以前のキャンペーンのROASに基づいていなかったり、あまりにも楽観的なマージンに基づいていた場合は、現在のデータに基づいてROAS目標をより現実的な数値に再設定することができます。
- 最後に、該当する場合は、自動入札戦略を調整して、目標のROASを達成するのに役立つかどうかを確認することができます。これは、自動化されたキャンペーンに対してのみ行われます。

ROASを測定することで、キャンペーンのパフォーマンスと成功を評価することができます。もっと練習が必要かもしれませんが、やがてマーケティングROIとROASの両方を測定することが、あなたの仕事の自然な一部となることでしょう。どちらもマーケティング分析における重要な指標なのです。
[OutSystems]Data Gridを使う画面でWijmoのAPIを呼ぶ方法

[DataGridを使って複数行編集可能なテーブルを実現する](https://qiita.com/jyunji_watanabe/items/61e31ead0acf400f94a8)でおこなったOutSystems Data Grid動作確認の続き。

OutSystems Data Gridは[GrapeCityのFlexGrid](https://demo.grapecity.com/wijmo/demos/Grid/Overview/purejs)をLow-Codeから使えるようにしたものだが、ベースとなっているFlexGridのオブジェクトを取得し、そのAPIを呼び出せる。

# 環境情報
Personal Environment(Version 11.18.1 (Build 37884))
Service Studio(Version 11.54.1)
OutSystems Data Grid(Version 2.10.1

# 実装例
Switchの切り替えで、配置したGridの2行目の表示／非表示を切り替える（この例自体はあくまで例示のみを目的としており、特に意味はない）。
1. Grid BlockのNameプロパティを設定しておく
1. Switchを配置し、OnChangeイベントハンドラーを設定
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/ff40f7e2-0607-aa31-d764-c26a9e8e42b9.png)


1. イベントハンドラーのロジックにJavaScript要素を配置し、以下のコードを記述する
``` js
var grid = GridAPI.GridManager.GetGridById($parameters.GridId).provider;
grid.rows[1].visible = $parameters.IsSecondRowVisible;
```

1行目：FlexGridのオブジェクトを取得している。GetGridByIdに渡すIdは、Grid WidgetのIdを渡す（\<GridのName\>.Idを渡す）
2行目：FlexGridのAPIを使い、Grid内の2行目の可視状態を切り替え。$parameters.IsSecondRowVisibleにはSwitchに紐づけたBoolean型の変数を渡している

基本的には1行目の方法でFlexGridのオブジェクトを取得した後は、FlexGrid自体のAPIを参照して必要な処理を書けばよい。
[FlexGrid.rows](https://demo.grapecity.com/wijmo/api/classes/wijmo_grid.flexgrid.html#rows)：Grid内の行オブジェクトのコレクションを取得
[Row.visible](https://demo.grapecity.com/wijmo/api/classes/wijmo_grid.row.html#visible)：Rowの可視状態を取得／更新できる

# GridAPIとは
これはOutSystemsが用意したオブジェクトで、Data Gridコンポーネント（Low-Code）とJavaScriptライブラリの仲立ちになるもの、らしい。

OutSystems Forumsの質問[Data Grid Reactive - GridFrameworkJS](https://www.outsystems.com/forums/discussion/73790/data-grid-reactive-gridframeworkjs/#Post302706)についていたコメントより。

> The GridAPI is a layer that mediates communication between the DataGrid component and our JS code.

## Data GridのClient Actionで利用
OutSystems Data Gridが提供するClient ActionもGridAPI下のAPIを呼んでいる。

例として、指定の行にValidationを効かせるApplyRowValidations Actionは以下のようにGridAPI下の機能を呼んでいる。

``` js
$parameters.ErrorMessage = OutSystems.GridAPI.Cells.ValidateRow($parameters.GridWidgetId, $parameters.RowNumber);
```

Client ActionはOutSystems.GridAPIのAPIを呼んでいる
「OutSystems.GridAPI」と「GridAPI」は同じ機能を持っていそうだがどう違うかは今のところ不明。念のため、

``` js
OutSystems.GridAPI === GridAPI
```
で比較してみたが、結果はfalseだった。

# GridAPIを利用した他の実装例
Google検索してみるといくつか見つかる。

[OutSystems DataGrid 虎の巻 - DataGridをJavascriptでカスタマイズする](https://qiita.com/sayamaguchi/items/997bcd2b4c8d24636296#datagrid%E3%82%92javascript%E3%81%A7%E3%82%AB%E3%82%B9%E3%82%BF%E3%83%9E%E3%82%A4%E3%82%BA%E3%81%99%E3%82%8B)：IMEやセル結合の設定

[OutSystems ForumでGridAPI.GridManagerで検索](https://www.outsystems.com/forums/Search.aspx?page=1&q=GridAPI.GridManager&scat=forums)
Powershell実行内容のファイル出力方法

# 背景

実行したコマンドをコンソール表示で終わらせるのではなく、
ファイルにログとして残しておきたいと考えていました。


# 実行コマンド

```
コマンド内容 | Out-File 出力したいファイルパス
```

既に作成済のファイルに追記したい場合は、オプションとして「-Append」を追記します。

```
コマンド内容 | Out-File 既に作成済のファイルパス -Append 
```

最初に記載されていた内容の後に追記されます。
Android（Java）パリティチェック

・パリティビットを付加する。

```java test.java

/**
 *   SetParity
 *   バイト列の最上位ビットにパリティ情報を付加する
 *
 *    pData      Byte()           I   バイト列
 *    intLen     Integer          I   対象の長さ
 *     pType      Integer          I   パリティ種類(0:Even 1:Od
 *
 *     戻り値　なし
 */
private static void SetParity(byte[] pData, int intLen,int pType) {

    int I;
    int J;
    int C;
    for (I = 0; I < intLen - 1; I++) {
        if (I >= pData.length) {
            break;
        }
        pData[I] = (byte) (pData[I] & Byte.MAX_VALUE);
        J = 1;
        C = (pType & 1);
        while( J >= Byte.MIN_VALUE) {
            if((pData[I] & J) != 0) {
                // ^  => VB XOR
                C = C ^ 1;
            }
            J *= 2;
        }
        if (C == 1) {
            // |  , ビットOR
            pData[I] = (byte) (pData[I] | Byte.MIN_VALUE);
        }
    }

}
```

・パリティビットをチェックする。
```java test_02.java

private static boolean RemoveParity(byte[] pData, int intLen, int pType) {

    int I;
    int J;
    int C;

    boolean removeParity = false;
    // 'パリティチェック
    for(I = 0; I < pData.length - 1; I++) {
        if (I >= pData.length) {
            break;
        }
        J = 1;
        // & , XOR
        C = (pType & 1);
        while (J >= Byte.MAX_VALUE + 1) {
            if ((pData[I] & J) != 0) {
                C = C ^ 1;
            }
            J *= 2;
            if (C == 1) {
                break;
            }
        }
    } //=== END for
    for(I = 0; I < intLen - 1; I++) {
        if(I >= pData.length) {
            break;
        }
        pData[I] = (byte)(pData[I] & Byte.MIN_VALUE);
    }
    removeParity = true;
    return removeParity;
}
```
銀行api 作成 01。 PHPでcsvをエクスポートして、データベースへ挿入　3,8000件

●銀行のcsvデータは、【銀行コード フリーデータ】などで検索しますと、ダウンロードができるサイトがいくつかでてきます。

●データレイアウト
![スクリーンショット (683).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/478380/750b7ebf-4b97-070a-e366-f22782de8f2c.png)

※支店コードは別テーブル、別CSVで取得しています。

```PHP index.php

<?php 


 // エラーを出力する
 ini_set('display_errors', "On");
 require (dirname(__FILE__) . '/functions.php');


 // *** エックスサーバー用 接続情報
 $dsn = 'サーバー情報';
 $user = 'サーバーのユーザー名';
 $password = 'サーバーのパスワード';

 // ====== CSV データ　インポート ======
$idx = 0;
$arr_1 = [];
$arr_2 = [];
$arr_3 = [];
$arr_4 = [];
$arr_5 = [];

// ================== CSV ファイル　import ===============

$csvfile = file_get_contents(dirname(__FILE__). '/ginkositen.csv');

$csv_utf8_file = mb_convert_encoding($csvfile, 'UTF-8', 'SJIS-win');

// ファイルに書き込む
file_put_contents(dirname(__FILE__) . '/utf8_ginkositen.csv', $csv_utf8_file);

$file_handler = fopen(dirname(__FILE__) .'/utf8_ginkositen.csv', "r");

try {

       // PDO オブジェクト作成
       $pdo = new PDO($dsn, $user, $password);

       // トランザクション開始

       $pdo->beginTransaction();
       $sql = "INSERT INTO test_01_table(
        bank_code, bank_num, bank_kana, bank_name, bank_item
    ) VALUES";

$idx = 0;
    // ========= CSVファイルを 1行ずつ　ファイルに読み込む =========

while($data = fgetcsv($file_handler)) {

    $arr_1[$idx] = $data[0]; 
    $arr_2[$idx] = $data[1];
    $arr_3[$idx] = $data[2];
    $arr_4[$idx] = $data[3];
    $arr_5[$idx] = $data[4];

    // ===== 配列の最後の要素
if ($idx === 31237) {
   
    $sql .= "(:bank_code{$idx},:bank_num{$idx},:bank_kana{$idx},:bank_name{$idx},:bank_item{$idx})";

} else {
    
    $sql .= "(:bank_code{$idx},:bank_num{$idx},:bank_kana{$idx},:bank_name{$idx},:bank_item{$idx}),";

}

// var_dump($sql . "<br /><br />");
$idx += 1;

}

$stmt = $pdo->prepare($sql);

//======= バインド index 用 
$j = 0;

foreach($arr_1 as $arr1) {
    $stmt->bindValue(':bank_code' . (string)$j, $arr1, PDO::PARAM_STR);
    $stmt->bindValue(':bank_num' . (string)$j, $arr_2[$j], PDO::PARAM_STR);
    $stmt->bindValue(':bank_kana' . (string)$j, $arr_3[$j], PDO::PARAM_STR);
    $stmt->bindValue(':bank_name' . (string)$j, $arr_4[$j], PDO::PARAM_STR);
    $stmt->bindValue(':bank_item' . (string)$j, $arr_5[$j], PDO::PARAM_STR);

    $j += 1;
}

var_dump($stmt);

    // SQL 実行
    $res = $stmt->execute();

    var_dump($res);

    // ======= (トランザクション) コミット =========
     if( $res ) {
        $pdo->commit();
        print("コミットOK");
    } else {
        print("コミットNG");
    }

} catch(PDOException $e) {
    print('Error:'.$e->getMessage());

    // ======= (トランザクション) ロールバック =========
     $pdo->rollBack();

   } finally {
    $pdo = null;
}

?>
```
CloudflareStreamで署名付きURLトークンをGoで発行

# はじめに
CloudflareStreamにおける、署名付きURLのトークンを`/token`エンドポイントを使わずに自前で発行する方法。
ドキュメントにはJavaScriptの例しかなかったので、Goで書き換え。
※あくまで一例なので、より適した方法もあるかもしれません。

ドキュメントでいうこの部分

https://developers.cloudflare.com/stream/viewing-videos/securing-your-stream#option-2-generating-signed-tokens-without-calling-the-token-endpoint


また、JWK,JWSなどの前提知識については以下がわかりやすかったです。

https://developer.mamezou-tech.com/blogs/2022/12/08/jwt-auth/#jws%E3%81%A8%E3%81%AF

https://qiita.com/TakahikoKawasaki/items/8f0e422c7edd2d220e06

# 実装

JWKの取り扱いについては、以下のライブラリを使用。

https://github.com/lestrrat-go/jwx/tree/develop/v2/jwk

JWSの生成は、`crypto/rsa`パッケージを利用して、signatureを生成しました。
そのため、JWKのjsonを一度Raw状態にしてから、`rsa.PrivateKey`にキャストしています。

```Go
func streamSignedURL() (string, error) {
	jwkKey := "{PRIVATE-KEY-IN-JWK-FORMAT}"
	keyID := "<KEY_ID>"
    videoUID := "<VIDEO_UID>"

	expiresTimeInS = 3600
	
	header := map[string]any{
		"alg": "RS256",
		"kid": keyID,
	}
	data := map[string]any{
		"sub": streamId,
		"kid": keyID,
		"exp": time.Now().Add(time.Duration(exp) * time.Second).Unix(),
	}

	jwkJson, _ := base64.StdEncoding.DecodeString(jwkKey)
	var raw interface{}
	err = jwk.ParseRawKey(jwkJson, &raw)
	if err != nil {
		return "", fmt.Errorf("cloudflare streamSignedURL failed jwk.ParseRawKey err %w", err)
	}
	rsaKey := raw.(*rsa.PrivateKey)

	token := fmt.Sprintf("%s.%s", objectToBase64url(header), objectToBase64url(data))

	hash := sha256.Sum256([]byte(token))
	buf, err := rsa.SignPKCS1v15(nil, rsaKey, crypto.SHA256, hash[:])
	if err != nil {
		return "", fmt.Errorf("cloudflare streamSignedURL failed rsa.Sign err %w", err)
	}

	signedToken := fmt.Sprintf("%s.%s", token, bufferToBase64Url(buf))

	return signedToken, nil
}

func bufferToBase64Url(buffer []byte) string {
	return base64.RawURLEncoding.EncodeToString(buffer)
}

func objectToBase64url(payload map[string]any) string {
	json, _ := json.Marshal(payload)
	return bufferToBase64Url(json)
}
```