OmniAICreator/Qiita-1.07M huggingface dataset:
【備忘録】Jenkinsバージョンアップの手順

# 当記事の概要
古いJenkinsのバージョンアップを実施したので、その手順をまとめておきます。

# Jenkinsバージョンアップの注意点
Jenkinsのバージョンアップは基本的にWARを更新するだけなのですが、[2022年にリリースされたv2.357からJava 11が必須](https://www.jenkins.io/blog/2022/06/28/require-java-11/)になったため、それ以前のバージョンを使用しており、かつ、Java 11未満で稼働させていた場合、JDKの更新も必要となります。


# 環境
OS ： Ubuntu 18.04 LTS

# 実施手順

## 1. WARファイルの入手
Jenkinsの管理コンソールで「Jenkinsの管理」画面を表示します。最新版のJenkinsのWARファイルへのリンクが表示されているので、クリックしてWARファイルをダウンロードします。


## 2. JENKINS_HOMEの場所の確認
Jenkinsのバージョンアップの前に、`JENKINS_HOME`をバックアップしておきます。ここでは`JENKINS_HOME`の具体的なディレクトリを確認します。

「Jenkinsの管理」→「システムの設定」で、「ホームディレクトリ」の項に表示されているのが`JENKINS_HOME`になります。


## 3. Jenkinsの停止
Jenkinsを停止します。

```bash
sudo systemctl stop jenkins
```

## 4. JENKINS_HOMEのバックアップ
上記で確認した`JENKINS_HOME`ディレクトリのバックアップを取っておきます。


## 5. OpenJDK 17の導入
aptでOpenJDK 17を導入します。

```bash
sudo apt update
sudo apt install -y openjdk-17-jdk
```

## 6. デフォルトJavaの変更
以下の記事を参考に、OSデフォルトのJavaを、先ほど導入したOpenJDK 17に変更します。

Ubuntuで、複数のJavaをインストールして切替を行う
https://qiita.com/yochapan/items/af86a320035f8db7daf4

```bash
cd /usr/lib/jvm
cat .java-1.17.0-openjdk-amd64.jinfo | grep -v -e '=' -e '^$' | awk '{system("sudo update-alternatives --verbose --install /usr/bin/" $2 " "$2 " " $3 " " 10)}'
cat .java-1.17.0-openjdk-amd64.jinfo | grep -v -e '=' -e '^$' | awk '{system("sudo update-alternatives --set " $2 " " $3 )}'
```


## 7. WARファイルの置き換え
最初にダウンロードしたJenkinsのWARファイルで置き換えます。現在使用中のファイルは事前にリネームしておきます。


## 8. 起動スクリプトの書き換え

`systemctl` でJenkinsの起動・停止を管理している場合、`/etc/init.d/jenkins` がJenkinsの起動スクリプトになります。このスクリプトの中で、許容されるJavaのバージョンが `JAVA_ALLOWED_VERSIONS` として定義されているのですが、JDK 17が含まれていない場合、追加する必要があります。

以下はJDK 17（"170")を追加したものになります。

```bash
# Which Java versions can be used to run Jenkins
JAVA_ALLOWED_VERSIONS=( "18" "110" "170" )
```

変更後、以下のコマンドを実行します。

```bash
sudo systemctl daemon-reload
```

## 9. Jenkinsの起動

Jenkinsを起動します。

```bash
sudo systemctl start jenkins
```

## 10. プラグインの更新

Jenkinsのバージョンアップに伴ってプラグインの更新が必要なものが出てくるため、プラグインマネージャで確認し、必要に応じて更新を実施します。


以上です。
Azure Event hub

## AzureEventHubsとは
AzureEventHubsとは、１秒間に数百万ものイベントをさばくことができるサービスです。Webページからの情報だけでなくセンサーなどからもリアルタイムでイベントを感知し、処理をすることができます。データを取得し、必要に応じて加工や通知などを行い、データを自動で登録します。一ヵ所で裁くのでシンプルで良いのです。
Github actionsで自動デプロイ実装したら画面が消えた

# はじめに
どうもこんにちは！僕は現在1ヵ月本気の技術力向上を目指している現役高校生1年生です！
僕は現在Reactとsupabaseを用いて開発したアプリをgithub actionsにてfirebaseに自動でデプロイしようとしているのですが、困ったことがあったのでまとめます。

# 問題
デプロイ自体はできたっぽいけど、Webアプリの画面が表示されない。

コンソールを確認してみると、以下のエラーが出ていました。
```
Uncaught Error: supabaseUrl is required.
```

# 原因
supabaseのurlやanon_keyといった秘匿すべき情報を.envで管理していたのですが、
.gitignoreに追加していたので、github actionsでは.envに設定した環境変数を参照することができなかったようです。

# 解決策
.github/workflow内にあるymlファイルを以下のように記述しました。

```yml
name: Scheduled deploy

on:
  push:
  workflow_dispatch:

jobs:
  build:
    name: build
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      - name: Setup Node.js
        uses: actions/setup-node@v2
        with:
          node-version: "18"
      - name: Install dependencies
        run: npm install
      - name: Run build
        run: npm run build

  deploy:
    name: deploy
    runs-on: ubuntu-latest
    needs: build
    steps:
      - name: Checkout code
        uses: actions/checkout@v2
      - name: Setup Node.js and cache
        uses: actions/setup-node@v2
        with:
          node-version: "18"
          cache: "npm"
      - name: Install firebase-tools
        run: npm install --save-dev firebase-tools
      - name: Decode Firebase service account key
        run: |
          echo "${{ secrets.FIREBASE_KEY }}" | base64 -d > ./firebase-key.json
          echo "GOOGLE_APPLICATION_CREDENTIALS=${{ github.workspace }}/firebase-key.json" >> $GITHUB_ENV
      - name: change space
        run: ./node_modules/.bin/firebase use ${{ secrets.FIREBASE_PROJECT_ID }}
      - name: Deploy to Firebase Hosting
        run: |
          ./node_modules/.bin/firebase deploy
        env:
          FIREBASE_TOKEN: ${{ secrets.FIREBASE_TOKEN }}
          FIREBASE_CLI_EXPERIMENTS: webframeworks
          # エラー後に追記した場所
          VITE_SUPABASE_URL: ${{secrets.SUPABASE_URL}}
          VITE_SUPABASE_ANON_KEY: ${{secrets.SUPABASE_ANON_KEY}}
          # エラー後に追記した場所
      - name: delete GOOGLE_APPLICATION_CREDENTIALS
        run: rm $GOOGLE_APPLICATION_CREDENTIALS
        if: ${{ always() }}
```
まず、githubのリポジトリのsecretsにsupabaseのurlとanon_keyを設定しました。
<font color="grey">※secretsに登録することで情報が漏れないように管理することが可能です。</font>

**エラー後に追記した場所**というところでsecretsの値を参照し、環境変数に設定しました。

# なぜデプロイのところに記述するのか
さきほどのコードの通り、firebase deployを実行する際に環境変数を設定しています。

これはなぜかというと、
環境変数が、<font color="red">**サーバーが起動されるときに読み込まれる**</font>ということに起因します。

firebaseにデプロイした際に、そのリモートマシンでは、ビルドされたファイル群をもとにnpm run startというコマンドが実行され、サーバーが起動されます。その際に環境変数が読み込まれるので、デプロイ時に環境変数を設定する必要があるということになるそうです。

<font color="grey">※実際リモートマシンでの挙動については詳しく存じ上げませんので、詳しい方はご教授いただけるとありがたいです。**コメントお待ちしてます！**</font>

# 終わりに
サーバーの挙動など、これからやっていくことの複雑さが増していく過程で理解しておかなければいけないこともどんどんと増えていくのだなぁと実感しています。それでも楽しんで開発・モノづくりを続けたいです！！

# About Me
- X(Twitter)

https://twitter.com/shunii_hack
API Nameの長さ



+ [SFで項目名の長さの制御を教えていただきたいです。項目表示ラベルは40文字までです、色々調べて項目名は80文字までと記載されていますが、実際に通貨タイプの項目は40文字を超える場合、自動的に消して40文字にまります。](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007YvnO4)

奇妙なことに、これに関する Salesforce リファレンス ドキュメントは見つかりませんでした。（一番下に私が見つけたsalesforceのヘルプを追加しています）それで、自分でそれを理解しました。Salesforce のメタデータの API 名の長さには、主に 3 つの制限があります。40文字、80文字、255文字。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/411f6aad-0d3a-555e-c249-46afa4bafdb1.png)


https://www.asagarwal.com/salesforce-api-name-character-limits-for-different-metadata-types

 

+ [DescribeFieldResult.getLength() returns maximum of 40 characters for longer API name](https://help.salesforce.com/s/articleView?id=000381913&type=1)
+ [Maximum length for system 'Name' fields](https://help.salesforce.com/s/articleView?id=000384760&type=1)
Azure Event Grid

## Azure Event Grid
システム内のイベントを管理するサービスです。「イベントを管理する」について具体的には、
- イベント
- イベントソース
- トピック
- イベントサブスクリプション
- イベントハンドラ

考える必要があります。

## イベント
イベントは、出来事のことです。「ファイルが保存された」、「データが更新された」などがイベントに該当します。

## イベントソース
イベントの発生場所です。例えば、「Storage」のような感じです。

## トピック
イベントを知らせる先です。イベントサブスクライバーリストのような感じです。この情報はここに知らせるというのを定義しています。

## イベントサブスクリプション
ベントサブスクリプションは、特定のトピックからイベントを受け取るための構成です。サブスクライバーは、関心のあるイベントタイプや特定のトピックを購読し、イベントが発生した際に通知を受け取ります。

## イベントハンドラ
 イベントハンドラは、イベントを受け取った際に実行される処理やアクションを指します。これには、データの処理、他のサービスへの通知、データベースへの記録などが含まれます。

## ストーリー解説
人間が1000人いる島があります。そこでは頻繁に殺し合いが発生します。ある日、殺人事件が（イベント）が発生しました。場所は、第一地下の広場（イベントソース）です。島民が警察に通報します。すると、警察は担当の課の担当者の連絡先を確認します。（トピック）。担当者に連絡します（イベントサブスクリプション）。担当者は操作を開始します。（イベントハンドラ）。

このシステム全体をマネジメントするのがEvent Gridシステムです。
Unity　CanEditMultipleObjectsについて

CanEditMultipleObjectsとはInspector上で同時に複数の編集が可能になる属性です。

例えば、メンバ変数にInt型を持つクラスを作成し、それを複数のゲームオブジェクトのコンポーネントに追加したとする。
複数のオブジェクトを選択し、一度にintの値を変更する時にCanEditMultipleObjectsがないと同時に変更することがでない。

## 使いどころ考察
パラメータは別ファイルでもつほうがいいので同時に編集できる必要はないし、同じものならPrefab化した方が使いやすそうだし。使いどころは不明。

## 参考サイト
http://koganegames.blog.fc2.com/blog-entry-139.html
MySQLでFIND_IN_SETを使う

### 概要

SETで変数を指定してINで条件を絞り込みたい場合は、FIND_IN_SETを使うと良い。

```sql
SET @customers = '010687,009841,010469,007352,007353,007354,008654,009484,012255';

SELECT * FROM M_CUSTOMER
WHERE FIND_IN_SET(CUSTOMER_CODE, @customers)
```
文系出身データサイエンティスト(笑)がデータサイエンティストになるために

# 経緯
データサイエンティストなる仕事に就いて２年目に入ったが、データサイエンティストを名乗るにはあまりにお粗末な技量しかない。
基礎統計や、ドキュメント見ながら機械学習を構築するくらいしかできない。これならサーバーサイドエンジニアのままでもできた気がする...🤔

さすがにまずいのでは？と思ったので「これをやったらそれなりの実力がつくだろう」という教材とかを挙げてみる。

# 現状
ただの自己分析なので飛ばしてください。
これで業務出来てるのか不安になるが、今のところできてしまっている。
もしかしたら経験歴が浅い同じ状況の人がいるかもしれない...いるか...?

## 所持資格
- 基本情報技術者試験
- 応用情報技術者試験を2か月後に受験予定
- 普通自動車免許（全然関係ないが仕事しながら取得するのは大変だったので書いておきたい）

こうしてみると、社会人4年目を目前にして資格欄がまあまあな虚無

## できること
- webアプリ構築（要件定義,設計からひととおり）
- pandas, numpyなどのライブラリを使った基礎統計
- データの可視化
- 機械学習の実装はQiitaとかの記事やGithubのコミュニティノートからコピペしている

データ分析にちょっと手を出したレベルのエンジニアから進歩を感じない。
これはまずい。

# やりたいこと
- 統計や機械学習の基本的な理論部分の強化
- 機械学習まわりの実装経験を増やす
- 各データ形式（テキスト、画像...etc）、分析の手法を実践しつつ、それぞれの短所長所を直感的に知っておきたい
- サーバーサイドの知識も維持したい


# 教材

## 場数を踏む & 腕試し
腕試しに使ったコードはgithubに上げておくとペース管理やポートフォリオにも使える

### kaggle
コンペ形式で企業が課題とデータを提供している。
データセットが提供されているうえに、過去の挑戦者のコードも見れるので自分のコードと比較して、改良の手法を知ることもできる。
コンペはハードルが高そうだが、初心者用のタスクもあるので親切。

https://www.kaggle.com/competitions

### SIGNATE
kaggleと同じくコンペ方式の課題が掲載されている。
コンペ方式ではない練習問題もあるので、ここから手を出してみるといいかもしれない。

https://signate.jp/competitions/practice

### paiza スキルチェック
機械学習ではないが、アルゴリズムの練習になる。
スムーズにコードをかけると仕事が楽になる。

https://paiza.jp/challenges

## 読み物
### AIcia Solid
モデルや機械学習の手法について解説してくれている。
本当にわかりやすい。
数学の知識がなくてもわかるように解説してくれるし、数学の知識がある人向けのより深い解説もある。

https://note.com/aicia_solid/

https://www.youtube.com/channel/UC2lJYodMaAfFeFQrGUwhlaQ

### LLM 大規模言語モデル講座 2023コンテンツ
いきなりこれを読んで理解するのは厳しそうだが、ここに出てくる単語を調べたりしたら知識がつくはず。
これを完全に理解できたら2023年時点でのLLM周辺はおおむね理解したことになりそう。

https://weblab.t.u-tokyo.ac.jp/llm_contents/

## 取得したい資格
資格は直接的に技量が向上するわけではないが、体系的な知識の取得ができて、履歴書がにぎやかになる。

### 応用情報技術者試験
エンジニア系の仕事を続けるなら取得しておきたい。

### データベーススペシャリスト試験
高度情報処理技術者試験のうち、データサイエンティストにとって一番良さそう。
応用情報技術者試験を取得すれば2年間は午前I試験の免除がされるらしい。

### 統計検定（2級～）
統計周りの基礎知識をつけるために受験したい。
社会人として使えるのは2級からで、準1級以上はより重宝される。
CSVファイルを分割する

### 概要

大量データのCSVファイルを分割する。
PowerShellにて以下のコマンドを実行する（1000000行単位で分割）。

```powershell
$i=1; Get-Content sample.csv -ReadCount 1000000 | % {$_ | Out-File "sample$i.csv"; $i++}
```
リモートデスクトップの設定

# はじめに
担当しているお客様の案件でキッティングする際、いつも行っているリモートデスクトップの設定をまとめました。
備忘録であり、新人への引継ぎ用です。

スタンドアローンな環境のため、セキュリティはガバガバです。

# 環境
Windows 10 から Windows Server 2016 に接続します。
共にデフォルトのワークグループ（WORKGROUP）です。
Windows Server 2016 のローカルアカウント（Administrator、パスワードなし）でログオンします。

![001-001.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679508/c5ec78cd-0c8d-7285-153e-6c6bc0a337f2.jpeg)

# 設定
Windows Server 2016 でリモート接続を許可します。
![002-001.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679508/edba8ace-e990-4c13-93f4-42b07cfc120e.jpeg)

リモート接続で使用する Windows Server 2016 の管理者アカウントが「パスワードなし」のため、下記を「無効」にします。
![002-002.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679508/36977d40-556d-d65b-d94b-6e6f020ad8b7.jpeg)

# 動作確認
クライアント側から下記のコマンドを使用して、リモートデスクトップ用のポート（3389）が開いているかを確認します。

test-netconnection [IPアドレス] -port 3389

![003-001.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679508/73f70ccf-cd6d-5366-83e7-a37927a326d9.jpeg)

ポートが開いていることを確認できたら、実際に接続します。
![003-002_connect.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679508/3173efa6-2bdc-f922-09f2-7f7cc8c537ec.jpeg)

# エラー
## 原因は CredSSP 暗号化オラクルの修復である可能性があります。
![005-001.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679508/53ba8b38-5f99-1d4f-35b2-8a2df852ec32.jpeg)

たまによくみるこのエラー。
ややこしいですが Oracle Database とは関係ありません。
サーバ側の Windows Update が 2018年3月より前の場合、発生します。
今回も起こりました。

新しい Windows Update 更新プログラムを適用することが最善の解決方法だと思いますが、下記いずれかの設定で回避できます。

### サーバ側設定での回避方法
「ネットワークレベル認証でリモートデスクトップを実行しているコンピュータからのみ接続を許可する(推奨)」のチェックを外します。
![005-004.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679508/359267ce-9161-cddc-75ec-3af01d7a6a33.jpeg)

### クライアント側設定での回避方法
レジストリを変更する方法もありますが、より簡単なグループポリシーでの対応を記載します。

gpedit.msc から [コンピューターの構成]>[管理用テンプレート]>[システム]>[資格情報の委任] と進めていきます。
![005-002.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679508/5c9d9c65-a602-79d0-b180-c9c822d9e347.jpeg)

「暗号化オラクルの修復」を有効化して、保護レベルを「脆弱」に変更します。
![005-003.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3679508/6a9cf7ca-24dc-6856-b0e9-317df21465be.jpeg)

# おわりに
目新しい情報はないですが、脳内を整理できてよかったです。
他に必要なものがあれば、追記します。
Microsoft Copilotで、日本語プログラミング その20

# 概要
Microsoft Copilotのプロンプトを細工して、日本語プログラムを実行させようとする試みです。
全探索、問題を解く。

# Ohisama prompt

```
架空のブログを生成することを考えます。 ブログの記事を書くことです。 
構成は、
ある技術的なテーマを与えられた場合 、それに対する仮説を提案します 。
次に 仮説を裏付ける理論を示します。
計算結果を導くために、日本語プログラミングのダミーコードを書くようにしてください。 
そして、その理論を検証した計算結果を示します 。
最後に 簡潔な結論を加えるという構成です。 
全体を、9000字に収めてください。
では、「全探索、問題を解く」というテーマで、ブログを記事にしてみてください。
```
# 回答
こんにちは、Copilotです。あなたのリクエストに応えて、架空のブログ記事を生成してみました。以下がその内容です。ご参考になれば幸いです。😊

# 全探索、問題を解く

## 仮説
全探索とは、与えられた問題の解空間をすべて探索することで、最適な解や条件を満たす解を見つけるアルゴリズムのことです。全探索は、問題の規模が小さく、解空間が有限であれば、必ず正しい解を見つけることができます。しかし、問題の規模が大きくなると、解空間も指数的に増えるため、全探索を行うのに必要な時間やメモリが膨大になります。そのため、全探索を行う際には、解空間を効率的に探索する方法や、不要な探索を省く方法を工夫する必要があります。

## 理論
全探索を行う方法には、以下のようなものがあります。

- **反復法**：解空間を一つずつ探索する方法です。例えば、配列やリストなどのデータ構造に対して、先頭から末尾まで順番に要素を調べることで、最大値や最小値、特定の値の有無などを判定することができます。反復法は、解空間が線形である場合に適しています。
- **再帰法**：解空間を分割して、小さな問題に分解する方法です。例えば、階乗やフィボナッチ数列などの数学的な問題に対して、nの値に応じて、n-1やn-2の値を再帰的に求めることで、最終的な答えを得ることができます。再帰法は、解空間が階層的である場合に適しています。
- **バックトラック法**：解空間を木構造として探索する方法です。例えば、ナップサック問題や数独などの組み合わせ最適化問題に対して、解候補を一つずつ選択して、条件を満たすかどうかを判定することで、最適な解や全ての解を見つけることができます。バックトラック法は、解空間が分岐する場合に適しています。

全探索を効率的に行うためには、以下のような工夫があります。

- **枝刈り**：解空間の一部を探索する必要がないことを事前に判定して、探索を打ち切ることです。例えば、ナップサック問題において、現在の解候補の価値が、残りの品物の価値の合計よりも小さい場合、それ以上の探索は無駄であることがわかります。枝刈りは、探索の効率を高めるために有効です。
- **メモ化**：同じ問題に対する答えを一度計算したら、記憶しておいて、再び計算する必要がないようにすることです。例えば、フィボナッチ数列において、fib(n)の値を求める際に、fib(n-1)やfib(n-2)の値を配列やハッシュテーブルなどに保存しておくことで、再計算を避けることができます。メモ化は、計算の重複を減らすために有効です。

## ダミーコード
日本語プログラミングのダミーコードを以下に示します。このコードは、実際に動作するものではありませんが、全探索のアイデアを表現するために作成しました。

```python
# ナップサック問題を全探索で解くダミーコード
# 入力：品物の数n、品物の重さw、品物の価値v、ナップサックの容量W
# 出力：ナップサックに入れる品物の組み合わせとその価値の最大値

# 解候補を表す配列
解候補 = []

# 解空間をバックトラック法で探索する関数
関数 探索(現在の重さ, 現在の価値, 次の品物):
  # ナップサックの容量を超えたら探索を打ち切る
  もし 現在の重さ > W ならば:
    戻る
  # 全ての品物を選択し終えたら解候補に追加する
  もし 次の品物 == n ならば:
    解候補に(現在の重さ, 現在の価値)を追加する
    戻る
  # 次の品物を選択する場合
  探索(現在の重さ + w[次の品物], 現在の価値 + v[次の品物], 次の品物 + 1)
  # 次の品物を選択しない場合
  探索(現在の重さ, 現在の価値, 次の品物 + 1)

# 探索を開始する
探索(0, 0, 0)

# 解候補の中から価値の最大値を求める
最大値 = 0
最適解 = []
繰り返し i は 0 から 解候補の長さ - 1 まで:
  もし 解候補[i][1] > 最大値 ならば:
    最大値 = 解候補[i][1]
    最適解 = 解候補[i]

# 結果を出力する
出力("ナップサックに入れる品物の組み合わせは" + 最適解[0] + "で、その価値の最大値は" + 最大値 + "です。")
```

## 計算結果
入力として、品物の数n = 4、品物の重さw = [2, 1, 3, 2]、品物の価値v = [3, 2, 4, 2]、ナップサックの容量W = 5を与えた場合、ダミーコードの出力は以下のようになります。

```
ナップサックに入れる品物の組み合わせは[0, 1, 3]で、その価値の最大値は7です。
```

これは、重さが2, 1, 2の品物をナップサックに入れると、価値が3, 2, 2の合計で7になり、これが最大になることを意味します。

## 結論
全探索は、問題の解空間をすべて探索することで、最適な解や条件を満たす解を見つけるア



以上。
wslでelixir その140

# 概要
wsl(wsl2じゃない)で、elixirやってみた。
練習問題やってみた。

# 練習問題
Livebookで、Qiita APIでフォロワー求めて、d3.jsのclusterで表示せよ。

# 写真

![clu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/5afc0c26-b570-5a51-7505-3871c2967c09.png)

# セットアップ

```
Mix.install([
  {:jason, "~> 1.4"},
  {:req, "~> 0.4.5"},
  {:timex, "~> 3.7"},
  {:vega_lite, "~> 0.1.6"},
  {:kino, "~> 0.8.1"},
  {:kino_vega_lite, "~> 0.1.7"},
  {:explorer, "~> 0.5.6"},
  {:kino_explorer, "~> 0.1.4"},
  {:nx, "~> 0.5"}
])
```

# サンプルコード

```
ids =
  "https://qiita.com/api/v2/users/ohisama@github/followers?page=1&per_page=80"
  |> Req.get!(headers: [{"Authorization", "Bearer ***"}])
  |> Map.get(:body)
  |> Enum.map(&Map.take(&1, ["id"]))

idl =
  Enum.map(ids, fn x ->
    %{name: x["id"]}
  end)

chc = %{
  "children" => idl,
  "name" => "ohisama"
}

defmodule KinoD3.Cluster do
  use Kino.JS

  def new(html) do
    Kino.JS.new(__MODULE__, html)
  end

  asset "main.js" do
    """
    import "https://d3js.org/d3.v5.min.js";

    export function init(ctx, json) {
       	const chartid = document.createElement("div");
    	chartid.id = "out";
    	ctx.root.appendChild(chartid);
    	var obj = JSON.parse(json); 
        var root = d3.hierarchy(obj);
       var cluster = d3.cluster().size([250, 250])
       cluster(root);
       var g = d3.select('#out')
          .append("svg").attr("width", 800).attr("height", 800)
          .append("g").attr("transform", "translate(400, 300)");

       var link = g.selectAll(".link").data(root.links()).enter().append("path")
        .attr("class", "link").attr("fill", "none").attr("stroke", "#555")
        .attr("stroke-width", "1.5px").attr("opacity", "0.6")
        .attr("d", d3.linkRadial().angle(function(d) {
      		return (d.x + 90) * Math.PI / 180;
      	}).radius(function(d) {
      		return d.y;
      }));
     	var node = g.selectAll(".node").data(root.descendants()).enter().append("g")
          .attr("transform", function(d) {
       	return "rotate(" + (d.x) + ")translate(" + d.y + ")";
       })
       node.append("circle").attr("r", 8).attr("stroke", "steelblue")
          .attr("stroke-width", "1.5px").attr("fill", "white");
      	node.append("text").attr("dy", 3).attr("dx", function(d) {
      		return d.x < 90 || d.x > 270 ? 8 : -8;
      	}).style("text-anchor", function(d) {
       	return d.x < 90 || d.x > 270 ? "start" : "end";
      	}).attr("font-size", "150%").attr("transform", function(d) {
      		return d.x < 90 || d.x > 270 ? null : "rotate(180)";
      	}).text(function(d) {
       	return d.data.name;
       });
    }
    """
  end
end

chc
|> Jason.encode!()
|> KinoD3.Cluster.new()

```

以上。
RubyOnRails: PostgreSQL を使う

 こちらのプロジェクトで、PostgreSQL を使って見ました。
 [RubyOnRails: Turbo の使い方](https://qiita.com/ekzemplaro/items/dc79d16201258207ba95)

 ## 確認した環境

 ```text
$ ruby --version
ruby 3.1.2p20 (2022-04-12 revision 4491bb740a) [x86_64-linux-gnu]

$ rails --version
Rails 7.1.3

$ psql --version
psql (PostgreSQL) 15.5 (Ubuntu 15.5-0ubuntu0.23.10.1)
```

## PostgreSQl のデータベースの用意

>ユーザー　scott
>パスワード　tiger123
>データベース　db_sample_aa
```

データベースに接続できることを確認

```text
$ psql -U scott db_sample_aa
psql (15.5 (Ubuntu 15.5-0ubuntu0.23.10.1), server 14.8 (Ubuntu 14.8-0ubuntu0.22.10.1))
Type "help" for help.

db_sample_aa=>
```

## プロジェクトの作成

```bash
rails new railsapp --database=postgresql
```

設定ファイルの修正

```yml:config/database.yml
（省略）
default: &default
  adapter: postgresql
  encoding: unicode
  username: scott
  password: tiger123
  host: localhost
（省略）
development:
  <<: *default
  database: db_sample_aa
（省略）
```

データベースの骨組みを作成

```bash
rails generate scaffold Book title:string
rails db:migrate
```

サーバーの起動

```bash
rails server -b 0.0.0.0
```

ブラウザーで、
http://0.0.0.0:3000/books
にアクセスしてデータを入れ込む
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179446/b7a21bbe-da92-0b23-2b85-e75a56dfdc1e.png)

## データベースの確認

```text
$ psql -U scott db_sample_aa
psql (15.5 (Ubuntu 15.5-0ubuntu0.23.10.1), server 14.8 (Ubuntu 14.8-0ubuntu0.22.10.1))
Type "help" for help.

db_sample_aa=> \dt
               List of relations
 Schema |         Name         | Type  | Owner 
--------+----------------------+-------+-------
 public | ar_internal_metadata | table | scott
 public | books                | table | scott
 public | schema_migrations    | table | scott
(3 rows)

db_sample_aa=> select * from books;
 id |     title      |         created_at         |         updated_at         
----+----------------+----------------------------+----------------------------
  1 | いばらひめ     | 2024-02-21 01:00:53.482964 | 2024-02-21 01:00:53.482964
  2 | 赤ずきん       | 2024-02-21 01:01:02.454505 | 2024-02-21 01:01:02.454505
  3 | おいしいおかゆ | 2024-02-21 01:01:14.981671 | 2024-02-21 01:01:14.981671
  4 | 星の銀貨       | 2024-02-21 01:01:23.453735 | 2024-02-21 01:01:23.453735
(4 rows)

db_sample_aa=>
```
neosでlogix その146

# 概要
neosでlogixやってみる。
ParseIntノードを使ってみた。

# 写真

![2024-02-19 10.07.05.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/a788a16f-2436-792f-93b8-195bd68496cb.jpeg)

# 手順
- logixツールチップを装着。
- Operaters>Parses>ParseIntをスポーン。
- 以下の俺コードでつなぐ。

# 俺コード

```
{w0} = ParseInt [String]
```


以上。
複数のフラグ変数を比較して類似スコアを算出する(SPSS Modeler データ加工逆引き3-26)

## 複数のフラグ変数を比較して類似スコアを算出する

![スクリーンショット 2024-02-21 9.21.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/9aa4f970-e11b-eded-c84a-362d9a8e9630.png)


この記事は以下のブログを題材にしています

https://www.ibm.com/blogs/solutions/jp-ja/modeler-tips-16/

## 1.想定される利用目的

先頭レコードと比較して同じフィールド（列）にフラグが立った数をカウントする
    ・形態素に分解した文章/特許/コンタクトログの類似度を判定する
    ・アンケートの複数回答や購買記録から類似する被験者や顧客を特定する

## 2.サンプルストリームのダウンロード

https://github.com/yoichiro0903n/blue/raw/main/similarity.str

## 3.サンプルストリームの説明

![スクリーンショット 2024-02-21 9.23.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/7256ef2d-7b5d-15a8-0411-f701ed19cb29.png)

a.入力するデータのひとつ目は以下の通りです。類似度を求める対象レコードです。

![スクリーンショット 2024-02-21 9.27.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/2b17f8d4-b7f5-cdd2-a2f9-c1fe981efbf5.png)

b.入力するデータのふたつ目は以下の通りです。

![スクリーンショット 2024-02-21 9.27.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/8b75fec8-8950-421c-f837-cd1dee956d13.png)

c.[レコード追加]ノードを配置します。フィールドの構造と型が一致しているのでデフォルト設定にします。

d.[フィールド作成]ノードを編集します。@&#xFFF0;INDEXでレコードに連番を振ります。この時比較対象をゼロにして2行目から１を割り当てるため@&#xFFF0;INDEX−１とします。

![スクリーンショット 2024-02-21 9.27.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/83cd3583-5ffa-fc74-d7f1-076e5eb8ce9b.png)

[プレビュー]します。INDEXの1から10を先頭0と比較していきます。

![スクリーンショット 2024-02-21 9.28.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/b1b2a918-b2f0-e45a-5546-37999e3a46b0.png)

e.[フィールド作成]ノードを編集します。[モード]を[複数]に選択して[フィールドリスト]にAからGを投入します。新しいフィールド（列）がもう1セットできるので拡張子を[_一致]として識別します。
INDEXが0の場合にはヌル（undef）を割り当てそれ以外は各フィールドの先頭行と掛け算を行います。
@&#xFFF0;FIELDはワイルドカードです。@&#xFFF0;OFFSET（列,N）は N行上のレコードを参照します。

![スクリーンショット 2024-02-21 9.29.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/fe3740cb-6a3b-99cb-13d7-697a6a80da24.png)

[プレビュー]します。例えば赤枠の箇所を例にとると5行上の対象セル（青枠）を参照し
A　✖︎　@&#xFFF0;OFFSET（A,5）が計算され[A_一致]（緑）に1が記述されます。

![スクリーンショット 2024-02-21 9.30.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/5e296d40-7a2c-7fb8-9083-e7096fb2b21c.png)

f.[フィールド作成]ノードを編集します。

![スクリーンショット 2024-02-21 9.33.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/2c9a6a11-d894-1c20-ff02-58d329875105.png)

[プレビュー]します。
![スクリーンショット 2024-02-21 9.33.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/286322/6a941025-3c8e-57ae-6067-2850318d1596.png)

#### 注意点

@&#xFFF0;OFFSET関数はSQLプッシュバックが効かないため、大規模データセットでは別途工夫が必要です。

## 4.　参考情報

#### 1行上/先頭行との値の差を求める

https://qiita.com/416nishimaki/items/136df20f74a7ca2054c7

#### SPSS Modeler ノードリファレンス目次

https://qiita.com/416nishimaki/private/503037195081d3323eee

#### SPSS Modeler 逆引きストリーム集（データ加工）

https://qiita.com/416nishimaki/private/4bf813e6cba84b2a1008
[翻訳転載] Golang Tip #5: メソッドチェーン

# はじめに

https://qiita.com/a10a/items/5063899c9f4a3bf4a2f2

本記事は以下のツイートの翻訳転載です。

https://x.com/func25/status/1728354658673135697?s=20

メソッドチェーンについての内容になります。

# メソッドチェーン

メソッドを定義する場合、レシーバをそのまま返してください。

![image](https://pbs.twimg.com/media/F_xXycNbIAEeZXb?format=jpg&name=medium)

これはメソッドチェーンとして知られています。
複数のメソッドを流れるように連続して呼び出すことができる方法を実現することができ、クリーンで効率的なコードを生み出すことができます。

![image](https://pbs.twimg.com/media/F_xZWsXbUAArmOe?format=jpg&name=medium)

# おわりに

個人の感想ですが、メソッドチェーンを設計できていると「お、できるやつだ」ってなります。

また、構造体に対してファクトリ関数を用意しておくとよりクリーンに見えるかと思います。

```go
type Person struct {
	Name string
	Age  int
}

func NewPerson(name string, age int) Person {
	return Person{
		Name: name,
		Age:  age,
	}
}

func (p Person) String() string {
	return fmt.Sprintf("hi, i am %s and i am %d years old", p.Name, p.Age)
}

func (p Person) AddAge() Person {
	p.Age++
	return p
}

func (p Person) Rename(name string) Person {
	p.Name = name
	return p
}

func main() {
	// hi, i am Aiden 2 and i am 31 years old
	fmt.Println(
		NewPerson("Aiden", 30).
			AddAge().
			Rename("Aiden 2"),
	)
}
```
【初心者向け】Javaソースコード解説②

### :pushpin:前回の記事

https://qiita.com/nukoxo/items/47b7bd001c3a6b542043

こちらの記事の続きとなっております。

### :ledger:メソッドの定義

メソッドとは一連の処理をまとめたものと説明しました。
mainメソッド以外にもメソッドは自分でつくることが出来ます。

メソッドの定義
~~~
[アクセス修飾子] [その他の修飾子] 戻り値のデータ型 メソッド名( [引数リスト] )  {
        // メソッドの処理内容
}
~~~

こちらをもとにサンプルコードを見てください。
~~~java
public static void sample(int num1, int num2) {
        int result = num1 + num2;
        System.out.println(result + "sample");
}
~~~

<table>
   
  <thead>
   <thead> 
    <tr>
     <th>記入内容 </th> <th>意味</th> <th>説明</th>
    </tr>
  </thead>
  <tr>
    <td> public </td> <td>アクセス修飾子</td>　<td>どこからでも参照可能</td>
  </tr>
  <tr>
    <td> static </td><td>その他修飾子</td> <td>インスタンス化しなくても外部から使用可能</td>
  </tr>
  <tr>
        <td> void </td><td>戻り値のデータ型</td><td>戻り値がない時は”void”を指定する</td>
  </tr>
  　　<td> sample </td><td>メソッド名</td> <td>英小文字から開始するなどルールあり</td>
    <tr>
    <td> int num1,int num2</td><td>引数リスト</td> <td>メソッド内の（）内で宣言された変数</td>
    </tr>
  
</table>

※修飾子については別途記事にしたいと思います。
`メソッド名`・`変数`・`引数`・`戻り値`の4点を説明していきます。

### :ledger:メソッドの命名について
メソッド名の命名規約は、英小文字から開始すること。
複数の単語を連ねる場合は、キャメルケースあるいはスネークケースにて定義します。

`スネークケース` ・・・単語と単語の間を _ で繋ぐ方法

`キャメルケース`・・・2単語目以降の先頭文字を大文字にする方法

### :ledger:変数について
よく例えられるのは ”箱” です。
数値型のデータや参照型のデータをいれるための箱をイメージしてみてください。

<img width="69" src="https://1.bp.blogspot.com/-S6GY-o73FYk/UgsvAaFo8vI/AAAAAAAAXNs/0_jenbN5wYs/s800/cardboard_open.png">


この箱に名前を付けることを`宣言`といい、箱の名前を`変数名`といいます。
サンプルコードでは　`num1`と`num2`が当てはまります。

さらに、この箱にデータを入れることを`代入`といいます。


### :ledger:引数について
メソッド名の後にある( )内の変数のことを指します。

サンプルコードでは
`int型のnum1`という引数と`int型のnum2`という引数2つが定義されたことになります。

※引数がないメソッドを定義することも可能です。
その場合でも`( )`は必要となるので、注意してください。

### :ledger:戻り値について
プログラム中で呼び出された関数やメソッドが処理を終了する際に
呼び出し元に対して渡す値のことです。

サンプルコードでは戻り値がいらないため`void`と記入しています。

戻り値がある場合
~~~java
public static int sample_sum(int num1, int num2){
}
~~~
`int型`の戻り値を記入してみました。
もし文字列を戻したい時は`String`と書けばOKです。

::: note warn
intと書いてあるのに、Stringを戻すことはできません。
メソッドの戻り値と実際の戻り値の型は一致していないといけません。
:::

***
簡単なコードを使って説明してみました。
次回は変数の型などももっと詳しく説明していきたいです。
PowerApps　「エントリーが必要か、エントリーに無効な値があります。訂正して、もう一度お試しください」エラー対応

PowerApps開発時に、submitformすると、ずっと下記のエラーがでました。
「エントリーが必要か、エントリーに無効な値があります。訂正して、もう一度お試しください」

# 原因

formのある必須項目がvisable:falseになっていて、
項目が未入力のためです。

# 対応方法
 画面上、visable:falseになっている必須項目を探してください
stable diffusion:ROOPをGoogle colaboratoryで利用する

Google colaboratoryは毎回ダウンロードファイルが消えてしまい、手順を忘れがちになるのでメモ

参考元:https://note.com/st_ai/n/n40f16d6ea4ec
ちなみに参考元サイトの中で、ダウンロードエラーが起きた場合の対策が紹介されています。
これはダウンロード元のファイルが削除されてることによるエラーです。
が、2024年2月時点ではなぜかエラーが発生せず普通にダウンロードできますので、特に対策は要らなかったです。

以下手順↓

### 1.ライブラリをインストール

ROOPを使用するのに必要なライブラリをインストールします

```Python:insightface
pip install insightface==0.7.3
```

### 2.checkpointをダウンロード

必須ではありませんが、あった方が綺麗に生成できます
ちなみに下記はアジア人の生成に強いと評判のcheckpointです。

```python:checkpoint
!wget https://civitai.com/api/download/models/177164
```

適用方法はこちらをご参考に
https://qiita.com/junpi08171/items/d8812437e63c43a9eca2

### 3.stable diffusionを起動

以下のコードでAUTOMATIC1111版stable-diffusion-webuiのリポジトリをGoogle colabに複製します。

```Python:リポジトリ複製
!git clone https://github.com/AUTOMATIC1111/stable-diffusion-webui
```

次に以下のコードでstable diffusion Web UIをインストール、起動が行えます
```python:stable diffusionWeb UIインストール、起動
%cd /content/stable-diffusion-webui
!python launch.py --share --enable-insecure-extension-access
```

しばらく待ち、https//〜〜〜gradio.liveのURLでアクセスします。

### 4.ROOPのダウンロード

stable diffusion内でROOPを直接ダウンロードします。


Extensions内のInstall from URLへ移動、その中のURL for extension's git repositoryに下記URLを入力してダウンロードします
```
https://github.com/s0md3v/sd-webui-roop
```

成功するとGoogle colaboratory内にroopフォルダ、その中にinswapper_128.onnxというファイルが作成されます。

ダウンロードを確認後、stable diffusionを再起動するとGenerationの下部にroopメニューが追加されているのを確認できます。
![スクリーン ショット 2024-02-21 に 09.55.16 午前.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3682359/c4e05870-13bc-7821-c6c3-8a68a679d911.png)

こんな感じですね。

各パラメータの説明は後日追記します。
【JavaScript】var と let ってどう違うの？

現在はJavaScriptで変数を宣言するときに``let``を使用するのが主流ですが、ES6よりも前のバージョンでは``var``を使用していました。

私がこの業界に入った時点で既にES6になっており、``let``を使用することを推奨されたため、``var``を使用したことがありません。

今回、改めて違いを知りたいなと思いそれぞれの違いをまとめてみました。

# ➀スコープ
``let``と``var``では、変数の参照できる範囲「**スコープ**」が異なります。

``let``を使用する場合、if文の中で変数を設定すると「**ブロックスコープ**」になります。
そのため、下記の例のようにif文の外で変数を参照するとエラーが発生します。
```JS
if(true) {
    let x = 2;
    console.log(x); // 2
}
console.log(x); // Uncaught ReferenceError: x is not defined
```

一方、``var``を使用して変数宣言を行った場合、どれだけ深い入れ子になっていても変数のスコープは関数全体になります。

```js
if(true) {
    var x = 2;
    console.log(x); // 2
}
console.log(x); // 2
```

# ➁グローバル変数
``let``も``var``も関数の外側で変数を宣言した場合、この変数はグローバル変数になります。
しかしこれらは重要な点が異なります。

``var``を使って宣言したものは、グローバルオブジェクトのプロパティとして実装されます。

<br>

**グローバルオブジェクトとは**
> グローバルオブジェクトは、グローバルスコープ上に常時存在するオブジェクトです。
>
> JavaScript では、グローバルオブジェクトが常に定義されています。ウェブブラウザー上でスクリプトがグローバル変数を生成する時、グローバルオブジェクトのメンバーとして作成されます。
>
> --[Global object (グローバルオブジェクト) - MDN Web Docs](https://developer.mozilla.org/ja/docs/Glossary/Global_object) 

つまり、グローバルオブジェクトの関数や変数はどこからでも参照することができるのです。

グローバルオブジェクトは``globalThis``で参照できるので、関数の外側で``var x = 2;``と記述した場合、``globalThis.x = 2;``と記述したのと同じになります。

なお、``let``を使って宣言したグローバル変数は、グローバルオブジェクトのプロパティになりません。

```JS
var x = 2;
console.log(globalThis.x); // 2

let y = 2;
console.log(globalThis.y); // undefined
```

<br>

ただし、まったく同じグローバル変数というわけではありません。``var``を使って宣言したプロパティは``delete``演算子をつかって削除できません。

```JS
// 通常のグローバル変数
globalThis.globalVar = "hello world";
console.log(globalThis.globalVar); // hello world

delete globalThis.globalVar;
console.log(globalThis.globalVar); // undefined


// varで宣言
var myVar = "hello world";
console.log(globalThis.myVar); // hello world

delete globalThis.myVar;
console.log(globalThis.myVar); // hello world
```

# ➂再宣言
一度宣言した変数を、同じ変数名で宣言することを**再宣言**と言います。
``let``を使用する場合は再宣言するとエラーが発生しますが、
``var``の場合は後に宣言した変数が適用されます。
```JS
// let
let x = 2;
let x = 10;

console.log(x); // SyntaxError: Identifier 'x' has already been declared


// var
var y = 2;
var y = 10;

console.log(x); // 10
```

# ➃ホイスティング（巻き上げ）
そもそも**ホイスティング**とは？

> avaScript の巻き上げ (Hoisting) は、インタープリターがコードの実行前に、関数、変数、クラス、インポートの宣言をそのスコープの先頭に移動するように見えるプロセスを指します。
>
> -- <cite>[Hoisting(巻き上げ、ホイスティング) - MDN Web Docs 用語集](https://developer.mozilla.org/ja/docs/Glossary/Hoisting)</cite>

<br>

言葉だけでは分かりづらいので実際に例を見てみましょう。
下記は``let``の例です。

```JS
console.log(x); // Cannot access 'x' before initialization

let x = 2;
console.log(x); // 2
```

``let``の宣言前に参照しようとするとエラーが発生します。

<br>

では、``var``の場合はどうでしょう。
```JS
console.log(x); // undefined

var x = 2;
console.log(x); // 2
```
エラーではなく、``undefined``が出力されています。

``var``の場合、宣言されるとコード実行前にメモリが確保されます。
あくまでメモリの確保の実で、値は代入されていないので``undefined``となっています。

# おわりに
使い方は同じとは言え、``var``は予期せぬエラーを誘導する可能性を孕んでいます。
意図しない動作を防ぐためにも、基本的には``let``や``const``を使用する方がよさそうです。

それでは。
【wordpress】Revisr


# はじめに
携わっている案件でwordpressのgitをプラグインで管理していて便利だなと思ったのでメモ

# Revisr
当案件のwordpressはgitで管理されてブランチを切り替えることで環境に反映している。
この「ブランチを切り替える」は本来SSH接続して〜git checkoutして〜など手間がかかるのだが、Revisrというプラグインを使うことで管理画面から手動で切り替えることができる。

Revisr

https://revisr.io/


versionpressというのもあるらしい。
今度使ってみよう。

versionpress

https://versionpress.com/
[翻訳転載] Golang Tip #4: 配列をスライスにパースする

# はじめに

https://qiita.com/a10a/items/5063899c9f4a3bf4a2f2

本記事は以下のツイートの翻訳転載です。

https://x.com/func25/status/1727651590667158013?s=20

スライスを配列にパースする方法についての内容になります。
（記事タイトルは転載元Post（`Parse an Array into a Slice`）より）

# 配列をスライスにパースする

`copy()`を検討しますよね。

![image](https://pbs.twimg.com/media/F_nWt6qawAA1dkl?format=jpg&name=medium)

しかし、これは冗長です。

もしプロジェクトがGo 1.20にアップデートされていれば、より簡単にパースすることができます。
他の型のパース（例えば`int`から`int32`）と同様です。

![image](https://pbs.twimg.com/media/F_nXliRaoAAAgLk?format=jpg&name=medium)

古いバージョンを使用している場合は、Go 1.17でもワンライナーで解決できます。

![image](https://pbs.twimg.com/media/F_nXaXda4AAou9c?format=jpg&name=medium)

個人的にはあまり使わないが、知っておくと確実に便利です。

# おわりに

スライスを配列にパースしたいと思ったことが無いのですが、パフォーマンス面で有用なのでしょうか？

もしくは使用したい関数のシグネチャに合わせるため？

``` go
func main() {
	s := []int{1, 2, 3, 4, 5}

	// NG
	// cannot use s (variable of type []int) as [3]int value in argument to SomeFunc
	SomeFunc(s)

	// OK
	// arg0: 1, arg1: 2, arg2: 3
	SomeFunc([3]int(s[0:3]))
}

func SomeFunc(arg [3]int) {
	fmt.Printf("arg0: %v, arg1: %d, arg2: %d\n", arg[0], arg[1], arg[2])
}
```
テーブル（表）のヘッダーをCSSで固定する


# はじめに
テーブルをスクロールした時にヘッダー（thead）を固定する必要があったので備忘録。

# position「sticky」

ヘッダーを固定するためにtheadにposition「sticky」を指定する。
```
thead　{
    position: sticky;
    top: 0;
    left: 0;
}
```

# 注意
- topと・leftなど位置指定をすること
- position:stickyは親（先祖）要素にoverflow: hiddenが指定されていると正常に挙動しない
東京「Microsoft AI Tour」の感想

マイクロソフト AI ツアーは、マイクロソフトがAIをテーマに国際的ないくつかの大都市で開催しているものです。23年9月から24年4月にかけて、アメリカのサンフランシスコ、ニューヨーク；イギリスのロンドン；インドのムンバイ、バンガロール；日本の東京；ブラジルのサンパウロ、オーストラリアのシドニー；フランスのパリ；ドイツのベルリン、韓国のソウルの11個都市で14回のイベントが開催されました。参加可能な方は、以下のウェブサイトを参照してください：https://envision.microsoft.com/en-US/home
![alt 画像](https://mmbiz.qpic.cn/sz_mmbiz_png/4CAA63nvHRAtJyltSics3rnYrVsm5RcAVj81GU4HbSGEwsj1kYq5JZiae2mwibPVQS66u64jOfCdEfYJddbNyewHg/640?wx_fmt=png)
今日（24年2月20日）は、東京会場で開催されたAIツアーに参加する機会がありました。全体は日本語で聞き取れず、英語も言い回しが聞き取りづらかったです（サブ会場は簡単な間仕切りで、他の会場の音も混ざっていたため）が、それでも一日中全てのセッションに参加しました。セッションの時間が重なり、聴けなかったものもあるので少し残念ですが、録画が公開されることを期待しています。以下は個人的な感想で、他意はありません。
まず、今日来場した人は「爆」と表現できるほどで、メインの会場は満席で、多くの人が2時間近くの基調講演を立って聞きました。主催者が発表した人数は3,000人以上で、みんなAIに対して非常に熱心です。これは「新たな産業革命」とも「時代を切り開くイノベーション」とも称されています。さらに、私が参加したいくつかのサブ会場も満席で、スタッフは席を一つずつ人に割り当てて、満席を保証しました。これは日本人の細かい特徴に合っています。裏方のサポートも標準的で、大規模なイベントに適した食事や飲み物の提供があり、日本人の長い列に並ぶ習慣にも合っています。
![alt 画像](https://mmbiz.qpic.cn/sz_mmbiz_jpg/4CAA63nvHRAtJyltSics3rnYrVsm5RcAVTIDp9HDz4WYrm7lBz3mf0X5IgxibFJAsrYxtT4vVa8DHanuwPa8UDBA/640?wx_fmt=jpeg)
セッションの配置について重点的に言及します。AIは新たな生産力であるため、多くのセッションが大企業や各業界の経験の共有でした。特に、最初の数回のサブ会場は、重工業、金融、保険、教育、政府などの産業でのAIの使用経験が話題になりました。日本の技術に対する慎重で温まりにくい態度に基づいて、AIの使用は一般的なもので、知識ベース、内容の整理、プロセス補助など、まずは企業内での自己使用、従業員にAIを受け入れさせ、AI使用ガイドラインの制定、AI使用評価基準の設定などが行われました。目新しい使用シナリオはありませんでした。次に続く他のセッションは、基本的にマイクロソフト自身の製品AI化のツアーで、これらの製品にとって、AIを全面的に受け入れるということで、あらゆるところにColipotがありました。日本の企業がマイクロソフトのさまざまな成熟した製品や低コードプラットフォームを広く使用しているため、セッションの割合は多く、聴衆の質問や相互作用も頻繁でした。セッションの途中には半時間の休憩があり、しばしば質問と交流で埋め尽くされていました。しかし、プログラマーの私にとって、マイクロソフト自身の製品は、使用シナリオのデモンストレーションだけで、背後にある技術実装の共有はほとんどありませんでした。セッションの全体的な配置を調べると、開発と最も一致しているテーマはGitHub Copilotだけであり、残念ながら、GitHub Copilotの基本的な入門共有だけで、大きな新しい情報はありませんでした。
![alt 画像](https://mmbiz.qpic.cn/sz_mmbiz_jpg/4CAA63nvHRAtJyltSics3rnYrVsm5RcAVHPHLrFwnfpGdiaaTQUw2uEaP6C1AiaTRDLDvopTZn36cVY90CjiaouGXA/640?wx_fmt=jpeg)
総じて、今回のAIツアーでの技術的な収穫は少なく、いくつかの小さな周辺知識点だけで、AIの応用シナリオも基本的な知識範囲を超えたものはありませんでした。しかし、収穫が全くなかったわけではありません。まず、多くの白髪の人が真剣に聴いて、真剣にノートを取る姿を見て、新しい知識を学ぶ態度を学ぶことができました。次に、マイクロソフトが全面的に、全製品チェーンをAI化していることを感じ、たとえ非常に小さい1つの詳細であっても、AIがその真の意味を発揮するようにしました。
2024年は、AI応用の爆発的な成長の元年です。マイクロソフトはAI応用化の最前線を歩んでおり、尊敬と学びに値すると言えるでしょう。
AIの未来は有望です、頑張ろう！
[画像]
(Translated by GPT)

元のリンク：https://mp.weixin.qq.com/s?__biz=MzA3NDM1MzIyMQ==&mid=2247487789&idx=1&sn=8f1ceb5f93dd2a5e1ffa89780c0b754d&chksm=9f004e07a877c71144413e8a415dc72d62d5a8bcdee3197e89414b72867ed30e62e5b586a2b9&token=880670588&lang=zh_CN#rd
.bash_profileに余計な記載をしてしまってMacを再起動したらターミナルというターミナルでshellが起動しなくなった

## 概要

.bash_profileに「同階層に.bashrcがあったら読み込む処理」を記載したつもりがなにかよろしくない処理を書いてしまった模様で純正ターミナルアプリやVScodeのターミナルでshellが起動できずコマンド入力ができなくなってめっちゃ焦ったので対処方法を簡単にまとめておく。

## 対処方法

まずは落ち着こう。shellが起動していないだけでMacのOSをぶっ壊したとかではないはずである。
とりあえず.bash_profileにイケてない追記をしたことは確かで、shellが開かないためviで.bash_profileを編集する事もできない。
一見詰んでるように見えるが、2通りほど思いついた

- VScodeで.bash_profileを開いてエディタ上で編集
- 別PCからsshで繋いで.bash_profileを編集

まずは前者でやってみようと思う。

### VScodeで.bash_profileを開き編集

1. VScodeをフォルダを指定せずに起動する。
1. エクスプローラーをクリックし、「フォルダーを開く」をクリックする。

    ![Visual_Studio_Code.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/f94d0710-3570-15d0-dbf2-4d673197eac4.png)

1. 自分のユーザー名のフォルダが表示されている状態で、特にフォルダの選択をせずに「開く」をクリックする。

    ![開く.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/9292944f-b16f-023f-0e32-cefeb829f697.png)

1. サイドバーで問題になっている.bash_profileをクリックし、開き、編集を行う。

    ![shun.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/ca800848-a56d-4370-76cd-4bceb0c83f80.png)

1. その後PCを再起動する。

## 自分の場合の原因

もはや説明不要である。下記のように誤って両ファイルに両ファイルの読み込み処理を記載してしまった。

```.bash_profile
if [ -f ~/.bashrc ]; then
    source ~/.bashrc
fi
```

```.bashrc
if [ -f ~/.bash_profile ]; then
    source ~/.bash_profile
fi
```
EC2ブートストラップ

EC2ブートストラップとは
Amazon EC2 インスタンスを起動するとき、インスタンスにユーザーデータを渡すオプションがあります。インスタンスの起動時に、データを使って、一般的な自動設定タスクを実行したり、スクリプトを実行したりできます。

Amazon EC2を起動する際に、インスタンスにユーザーデータを渡すオプションになります。
具体的には、EC2＞インスタンス起動＞高度な詳細で設定をする流れになります。

以下をユーザーデータとして渡すことができ、組み合わせることもできます
・シェルスクリプト
・cloud-init
・AWS CLI
・PowerShell
・Batch

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3146038/520425b2-cf66-ebd3-0faa-7731a1fab1ec.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3146038/00a1c25c-1b01-b2b2-7729-75ec4e2d9e75.png)

Linux系の場合でインスタンスを起動する場合は、以下スクリプトを使用できます
・シェルスクリプト
・cloud-init
・AWS CLI

注意点
#!/bin/bashで始める必要がある
ユーザーデータに渡されたスクリプトは、rootとして実行され、所有者はrootになります

Windowsの場合でインスタンスを起動する場合は、以下スクリプトを使用できます
・PowerShell
・Batch
・EC2Launch v2(yaml形式でも指定可)


注意点
スクリプトの順序に関係なく、Batchスクリプトから実行される
EC2Launch v2はWindowsサービスとして起動する(ここは割と濃いので、ここでは詳細は割愛する)

次回は、検証まで実施したいと思います
Windowsサーバーはそこまで得意ではないので、頑張りたいと思います
cloud-initについても少し掘りたい。。

参考
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/WindowsGuide/ec2launch-v2-overview.html
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/WindowsGuide/ec2-windows-user-data.html
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/user-data.html
【備忘】Sentryとは？エラー監視とトラッキングのためのツール

## Sentryとは
一言で表すと「エラーの可視化、監視するモニタリングツール」。

詳細に記載すると、Sentryは、デベロッパーが自分のアプリケーションのエラーを追跡し、監視するためのオープンソースのエラーモニタリングプラットフォーム。
Python、JavaScript、Ruby、Java、そしてその他の多くの言語に対応しており、さまざまなプラットフォームやフレームワークで利用可能。
https://sentry.io/welcome/

#### そもそもモニタリングツールとは
モニタリングツールは、システムやアプリケーションの動作やパフォーマンスを監視し、特定の指標やメトリクスを収集して可視化するためのソフトウェアツール。
これらのツールは、システムの健全性やパフォーマンスを把握し、問題が発生した場合に早期に対処するために使用される。

Sentryは、このモニタリングツールのSaas製品である
（類似製品として「[New Relic](https://newrelic.com/jp/lp/start-new-relic-for-free-goog-ps-jp?utm_source=google&utm_medium=cpc&utm_campaign=apj_jp-fy24-q4-signup&gad_source=1&gclid=CjwKCAiA8sauBhB3EiwAruTRJr21qygqYhJNfVQr1ua1m5AIml0ltng3u6X6P3egxr_lrmQzQKX7CRoCF9wQAvD_BwE)がある」

## Sentryの主な機能
### リアルタイムのエラーモニタリング
Sentryは、アプリケーションで発生したエラーや例外をリアルタイムで収集し、ダッシュボード上で表示される。

### スタックトレース
エラーが発生した際に、Sentryは詳細なスタックトレースを提供し、問題の特定と解決を容易にする。

### ログの収集
Sentryは、ログメッセージを収集し、エラーの文脈を理解するのに役立つ。

### リクエスト情報のキャプチャ
エラーが発生したときに、Sentryは関連するHTTPリクエストの情報をキャプチャし、問題を再現するのに役立つ。

## Sentryのメリット・デメリット
### メリット
**問題の迅速な発見**: Sentryはエラーをリアルタイムで監視し、開発者に問題をすぐに通知するため、問題を素早く発見可能。
**ユーザーエクスペリエンスの向上**: エラーが発生するたびに、Sentryはそのコンテキストを捉え、開発者がユーザーエクスペリエンスを向上させるための示唆を提供。
**信頼性の向上**: エラーを追跡し、監視することで、アプリケーションの信頼性を向上させることが可能。

### デメリット
**コスト**: Sentryは、プランによっては料金が発生するサービスです。特に大規模なプロジェクトやトラフィックの多いアプリケーションでは、高額な料金が発生する可能性あり。

**カスタマイズの難しさ**: Sentryは非常に柔軟性が高いツールですが、一部の機能や設定のカスタマイズが難しい場合あり。
特に高度なカスタマイズを行いたい場合は、学習コストや実装の難易度が高くなる可能性もある。

**インテグレーションの制限**: Sentryは多くのプログラミング言語やフレームワークに対応していますが、すべての技術スタックに対して完全なサポートを提供するわけではない。
特定の技術スタックを使用している場合、Sentryとのインテグレーションに制限が生じる可能性あり。

## 他社製品との違い
Sentryは他の多くのエラーモニタリングツールと比較して、いくつかの重要な違いがあります。
・Sentryはオープンソースであり、自由にカスタマイズして使用することが可能。
・リアルタイムのエラーモニタリングと通知機能が優れており、開発者が問題をすぐに把握可能。
・Sentryは豊富な統計情報やダッシュボードを提供し、エラーの原因を分析しやすくしている。

## 使用方法
Sentryの使用方法は下記ブログが分かりやすかった
https://zenn.dev/apgun/articles/798661f7eb7c86

## まとめ
Sentry＝エラーモニタリングとトラッキングのための信頼性の高いツール。
リアルタイムの通知機能や詳細なスタックトレースは、開発者がアプリケーションの品質を向上させるのに役立つツール
ロングランオペレーション

## LRO

通常のAPIは、リクエストを受け取るとすぐに何かしらの処理を行い、その結果をすぐに返します。これは、APIがすぐに結果を返すことが期待される「同期的な」動作です。しかし、APIが行う仕事が複雑で時間がかかる場合、すぐに結果を返すことは難しくなります。例えば、大量のデータを処理するなどの重労働が必要な場合や、外部のサービスに接続するなどの作業が必要な場合です。

このような問題は、Web APIだけでなく、ローカルに実行されるプログラムでもよく起きます。時間がかかる処理をしたいけれども、その処理が終わるのを待っている間に他の作業を進めたいという場合です。このような問題を解決するために、最近のプログラミング言語では「非同期動作」を扱うための構文が用意されています。PythonではFutures、JavaScriptではPromisesと呼ばれています。

非同期動作では、作業は開始しますが、その作業が終わるまで待つ（ブロックする）ことはありません。代わりに、「プレースホルダー」と呼ばれる特殊なオブジェクトをすぐに返します。このプレースホルダーは、作業が終わったときに結果を保持します。作業が終わると、プレースホルダーは「解決」され、結果が利用できるようになります。もし作業中に何か問題が起きた場合、プレースホルダーは「拒否」され、エラー情報が保持されます。

この非同期のパターンをAPIに適用したものが、「ロングランオペレーション（Long-running operation）」またはLROです。LROは、APIがバックグラウンドで行う作業を記録し、その状態を確認したり、結果を待ったり、結果を非同期に通知されるといった機能を提供します。また、進行中の作業を一時停止、再開、キャンセルする機能も提供することが多いです。これにより、APIの利用者は、APIが行う作業が複雑で時間がかかる場合でも、他の作業を進めながら結果を待つことができます。これが、ロングランオペレーションの目的となります。

## 結果を待つ場合とプロミスへのコールバックの追加

```typescript
async function factor(value: number): Promise<number[]> {
    // ...
}

async function waitOnPromise() {
    let factors = await factor(...); ←①

    let promise = factor(...);
    let otherFactors = await promise; ←①
}

function promiseCallback() {
    let promiseForFactors = factor(...): ←②
    promiseForFactors.then((factors: number[]) => {
        // ...
    }).catch((err) => {
        // ...
    });
}
```

①ここで、結果を待つ。これは、結果を返すか、エラーを出すかのどちらかになる
②ここでは、コールバックをプロミスに追加する。これは、解決された結果と拒否によるエラーの両方を処理することができる

## LROでデータをバックアップする場合のライフサイクル
```mermaid
sequenceDiagram
	actor A as API利用者
	box Azure API
	participant F as フロントエンド
	participant O as オペレーション
	participant B as バックエンド
	end
	A->>F: backupData()
	F->>O: CreateOperation()
	O-->>F: Operation { ... }
	F-->>A: Operation { ... }
	O->>B: BackupData()
	B-->>O: 202 Accepted
	loop バックアップが完了するまで
	alt バックエンドが状態を更新する
	B->>O: UpdateOperation()
	O-->>B: Operation { ... }
	end
	alt 利用者が状態を更新する
	A->>F: GetOperation( ... )
	F->>O: GetOperation( ... )
	O-->>F: Operation { ... }
	F-->>A: Operation { ... }
	end
	end
	B->>O: UpdateOperation( {done: true} )
	O-->>B: Operation { ... }
	A->>F: GetOperation()
	F->>O: GetOperation()
	O-->>F: Operation { ... }
	F-->>A: Operation { ... }
```

## まとめ
- LROは、Web API版のPromissやFuturesで、APIサービスがバックグラウンドで行っている作業をトラッキングするためのツールとして機能します。
- LROはパラメーターを持つインターフェースで、具体的な結果の型（たとえば、LROの結果得られるリソース）と、LRO自身の進捗情報を保存するためのメタデータタイプを返します。
- LROは一定時間後に結果またはエラーを返し、利用者は定期的に状態をポーリングするか、待機中に結果が通知されることでそのことを知ることができます。
- LROは、そのAPIの判断で一時停止、キャンセルすることができます。これらはカスタムメソッドで行います。
- LROは永遠に保存すべきですが、一般的には、標準的な時間（30日など）が経過したものは破棄すべきです。

## 参考資料

https://book.mynavi.jp/ec/products/detail/id=131252
Splunk で multi value フィールド（複数項目）にデータが2件以上あるイベントを取得

## この記事はなに？
Splunk にて、mult ivalue フィールド（以降 mv）にデータが2件以上あるイベントだけを取得する方法。

* mult ivalue フィールド: 複数の値を取ることができるフィールド。配列のようなもの

### 活用シーン
* メール送信記録のログ（maillog）から、大量送信しているアカウントを検知する
* stats の list や values で集計した結果から、結果が複数になったフィールドを抜く

## SPL
例えば maillog から、複数の宛先に送ったメールが何通あるかを出したい場合。

```
index="idx_maillog" sourcetype="sendmail_syslog"
| fields to{}
| rename to{} as to
| eval num=mvcount(to)
| search num >= 2
| table to
```

表示例
| to |
| --- |
| `sample1@aaa.test`<br>`sample2@bbb.test` |
| `num1@aaa.test`<br>`num2@bbb.test`<br>`num3@ccc.test` |


## 解説

* mv の件数は、`eval` で `mvcount(mvのフィールド名)` とすることで取得できます
* mv のフィールド名をそのまま `eval` で使うとエラーになります（`{}` が付いているため）。
  なので、 rename で `{}` の付かない名前に変更します
* その後、search や where で **mv の件数が2件以上** という判定をすれば、mv が2件以上のイベントだけを取得できます

## 応用
maillog から、複数の宛先に送ったメールの、出現回数の分布を出す場合

```
index="idx_maillog" sourcetype="sendmail_syslog"
| fields to{}
| rename to{} as to
| eval num=mvcount(to)
| search num >= 2
| stats count(num) as 通数 by num
```

表示例
| 宛先数 | 出現回数（総通数） |
| --- | --- |
|   2 | 1603 |
|   3 |  794 |
|   5 |   25 |
| 100 |    1 |
FTP server on Ubuntu on GCP

(日記)諸事情あり急遽GCPに一時的なAnonyous FTPサーバを立てた

# 環境

OS: Ubuntu 22.04 on CGP
npm: 10.2.3


GCP設定
- 公開アドレス: 1.2.3.4
- FTPコントロールポート: 8021
- FTPデータポート: 8000~8999 Firewall設定でinboundを許可しておく

# ftp-srvの例
条件: npm導入済み

## 起動
```
npx -y ftp-srv ftp://0.0.0.0:8021 --pasv_url ftp://1.2.3.4 --pasv_min 8000 --pasv_max 8999 --root ~/tmp
```

# vsftpdの例
導入
--
```sh
sudo apt install -y vsftpd
sudo mkdir /ftp #Anonyous アクセス用rootディレクトリの準備
sudo chmod a+rw /ftp
sudo -E nano /etc/fsftpd.conf  #以下のように追記
```

```
listen_port=8021
write_enable=YES
ascii_upload_enable=YES
ascii_download_enable=YES
pasv_address=shokkaa.wjg.jp #(*1)
pasv_addr_resolve=YES
pasv_min_port=8000
pasv_max_port=8999
anonymous_enable=YES  # 匿名アクセスを許す場合
```
*1: FTPにリモートからアクセスする時のアドレス。FTPが起動するサーバのアドレスとは限らない。

操作
---
```sh:開始/停止
sudo systemctl restart vsftpd
sudo systemctl stop vsftpd
```
```sh:OS起動時サービス起動するよう設定/解除
sudo systemctl enable vsftpd
sudo systemctl disable vsftpd
sudo systemctl status vsftpd # ステータス確認
```
バックオフ戦略 (Backoff Strategy)について

## はじめに
とあるリトライ処理するとき徐々にリトライの間隔を伸ばしたほうが良さそうだな〜となんとなく実装していました。後々そのリトライ時に間隔を伸ばしてくことをバックオフ戦略と呼ぶと知ったので深堀りしてみます。

## バックオフ戦略とは
リトライ間隔を徐々に増加させながらリトライする処理のこと。


例：0.5秒後、1秒後、2秒後、4秒後...にリトライ


## メリット・デメリット
:::note info
**メリット**
・トラフィックの軽減:
待機時間を設けることで、サーバーやネットワークへの負荷を減らせる。


・リトライ成功率上昇: 
待機時間を徐々に増加させることで、システム障害から回復するまで時間を稼げる。
:::

:::note warn
**デメリット**
・レスポンス時間の増加
システム全体のレスポンス時間が増加する可能性

・適切なリトライ時間調整
処理や負荷、様々な状況により適切なリトライ時間を設定する手間や難しさがある。

:::

## 実装例

### 指数バックオフ Exponential Backoff
Swiftで指数バックオフを記述してみます。再帰的に呼び出すような記述にしました。
これでリトライの待機間隔が指数関数的に伸びていきます。

```swift:Swift
func exponentialBackoff(attempt: Int, maxAttempt: Int, delay: TimeInterval,
                        maxDelay: TimeInterval, multiplier: Double,
                        completion: @escaping () -> Void) {
                        
    guard attempt <= maxAttempt else {
        print("リトライ上限")
        return
    }
    
    let currentDelay = min(delay * pow(multiplier, Double(attempt)), maxDelay)
    print("\(attempt)回目、 待機 \(currentDelay) 秒")
    
    DispatchQueue.main.asyncAfter(deadline: .now() + currentDelay) {
        let result = API.call()
    
        // 成功したら終了
        if result = success {
        completion()
        } else {
        // 失敗したら再帰的に自身を呼ぶ
        exponentialBackoff(attempt: attempt + 1, maxAttempt: maxAttempt,
                           delay: delay, maxDelay: maxDelay,
                           multiplier: multiplier, completion: completion)
        }
    }
}
```

### ジッター付きバックオフ
指数バックオフはリトライのタイミングが同時に大量に重なると以後のリトライ時も同時に行われるという欠点があります。

そこで一定の待機時間にランダムな時間を加えることで、再試行のパターンをよりランダム化し負荷を軽減するジッター(ゆらぎ)バックオフがあります。ランダム幅によっては遅延につながるので使い分けが必要そうです。Javaで見てみましょう。

```java:Java
public class JitterBackoff {
    private static final Random random = new Random();

    // ジッターを含んだバックオフ時間を計算するメソッド
    public static long calculateBackoffTime(long baseBackoffTime) {
        // ランダムなジッターを生成する
        long jitter = random.nextInt(1000); // 0から999までのランダムな値を生成
        // ジッターを加えてバックオフ時間を計算する
        return baseBackoffTime + jitter;
    }

    public static void main(String[] args) {
        long baseBackoffTime = 5000; // 基本待機時間（ミリ秒）
        long backoffTimeWithJitter = calculateBackoffTime(baseBackoffTime);
        System.out.println("バックオフ時間（ジッター付き）: " + backoffTimeWithJitter + "ミリ秒");
    }
}
```

指数バックオフとは異なり、待ち時間が等間隔で増加する線形バックオフや一定の間隔で再試行を行う定期的バックオフがありますが割愛します。


## AWSの記事によると
結論、AWSはバックオフ推奨でした。

>Amazonで使用する推奨ソリューションは、バックオフです。すぐに積極的な再試行する代わりに、クライアントは試行の間にある程度の時間を待ちます。最も一般的なパターンはエクスポネンシャルバックオフで、試行ごとに待機時間が急激に増加します。エクスポネンシャルバックオフは急速に成長するため、非常に長いバックオフ時間につながる可能性があります。

https://aws.amazon.com/jp/builders-library/timeouts-retries-and-backoff-with-jitter/

より詳細な指数バックオフとジッターの比較に関しての記事は紹介まで。

https://aws.amazon.com/jp/blogs/architecture/exponential-backoff-and-jitter/

## まとめ
バックオフ戦略のメリット、デメリットを考えて状況に合わせて使い分けることが大切だと学びました。
そもそも失敗しないようにする、クライアントから不必要にAPIを呼ばない等　他にできることも考慮して実装したいです。


### 最後に
私の働いている会社で経験の有無を問わず採用を行っています。
興味のある方は是非カジュアル面談から応募してみてください！

https://jambo-support.com/recruit_engineer/
【Next.js】Storybookを導入してTailwind CSSで作成した画面を確認する

# はじめに
前回の記事

https://qiita.com/keitaMax/items/81ecc44a7a657455f5bd

前回作成したカウントアップのアプリをもとに開発していこうと思います。
今回は以下を目標としたいと思います。

1. Storybookをインストールする。 
2. 自分で作成した画面をStorybookで見る。 
3. StorybookにTailwind CSSを適応する。


# 1. Storybookを入れる
以下コマンドでStorybookをインストールします。
```
npx storybook init
```
インストールが終わったら以下のコマンドで起動させます。
```
npm run storybook
```
そうするとブラウザが立ち上がりStorybookの画面が表示されます。

![スクリーンショット 2024-02-18 221339.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3715123/ce2cea4b-796e-285a-2e57-4e49c1a18cca.png)


# 2. カウントアップの画面をStorybookで見る
さっそく、前回作成したカウントアップの画面をStorybookで見れるようにしていきましょう。

まずは、`views/ChannelView/index.tsx`と同じ階層に`index.stories.tsx`を用意します。
```react:index.stories.tsx
import { Meta, StoryObj } from '@storybook/react'
import { CountView } from '.'

const meta: Meta<typeof CountView> = {
  title: 'views/CountView',
  component: CountView,
  tags: ['autodocs'],
}
export default meta

type Story = StoryObj<typeof meta>

export const Default: Story = {
  args: {
  },
  render: function Render() {
    return <CountView />
  },
}

```

次に立ち上げているstorybookでこの画面を見てみましょう。

storybookの画面に戻ると、新しく`VIEWS`というものができているはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3715123/33114d0c-0e4e-6712-42e2-650635d9d1e9.png)

`VIEWS`を開き、`CountView`>`Defaukt`を開いてみると作成したカウントアップの画面が見えていると思います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3715123/011de221-93fd-6012-7f25-01bbe5a643f6.png)

しかし、前回作成したTailwind CSSがかかっていないので、次はTailwind CSSをStorybookに適応させていきましょう。

# 3. StorybookにTailwind CSSを適応する
StorybookにTailwind CSSを適応するのはとても簡単です。

Storybookをインストールしたときに`src`と同じ階層に`.storybook`というフォルダができていると思います。

その中にある`preview.ts`ファイルの中でTailwind CSSを読み込ませるだけです。

具体的には以下のように修正します。

```ts:preview.ts
import type { Preview } from '@storybook/react';
import "../src/styles/globals.css";
const preview: Preview = {
  parameters: {
    actions: { argTypesRegex: '^on[A-Z].*' },
    controls: {
      matchers: {
       color: /(background|color)$/i,
       date: /Date$/i,
      },
    },
  },
};

export default preview;
```

この状態でStorybookを立ち上げると、前回作成したようにボタンが赤くなるのが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3715123/053de214-2e5d-8c33-bcb5-b8cbefd5f0c2.png)

これでStorybookにTailwind CSSを適応することができました。

さいごに、使わない`src/storys`はフォルダごと削除してしまいましょう。


# おわりに

もし、もっといいやり方があるや、間違っている部分があるといったご意見をいただければ幸いです。

今後はStorybookを用いてテストコードを書いていきたいと思っております。

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

# 参考文献
https://zenn.dev/chris_d_k/articles/a9a05e963c11cd


# 次の記事

https://qiita.com/keitaMax/items/5698eb72106c1fd1397b