OmniAICreator/Qiita-1.07M huggingface dataset:
[画像処理]バイキュービック補間法では画像はどう補間されるのか？

# はじめに
先日、画像の補間方法について記事を書いた。

https://qiita.com/sasshi_i/items/71189dc3a5c637fda3a4

本記事で紹介するバイキュービック補間法も画像を拡大、縮小、回転する時に利用できる補間法の1つである。バイキュービック補間法ではどのような処理が行われるのか解説していく。

# バイキュービック補間法とは？
バイキュービック補間法は、16個の画素値を用いて補間する画素の画素値を求める方法である。16個の画素の加重平均を基に補間する画素の画素値を求める。処理の流れを以下に示す。

## 処理の流れ
変換後の画像の座標$(x', y')$における画素値を求める手順は以下の通り。
1. 拡大率を$α$とした時、$(\frac{x'}{α}, \frac{y'}{α})$を求める
2. $(\frac{x'}{α}, \frac{y'}{α})$の整数部の座標の画素値を$Src(0,0)$とすると、$x$軸方向および$y$軸方向の-1~+2の範囲における16画素を得る
3. 周囲16画素それぞれと$(\frac{x'}{α}, \frac{y'}{α})$の距離$d$を計算する
4. $(x', y')$における画素値は、周囲16画素の加重平均とする。加重平均の計算に用いる各画素に対する重み$k$は距離$d$に応じて、下式で求める

$$
\begin{equation}  \label{eq: cases f}
k=
    \begin{cases}
        1-(a+3)d^2+(a+2)d^3   &   (0\le d < 1)  \\\\
        -4a+8ad-5ad^2+ad^3        &   (1 \le d < 2) \\\\
        0 &(d \ge 2)
    \end{cases}
\end{equation}
$$

$a$は-0.5~-1.0付近の値を使用することが多い

![スクリーンショット 2024-02-06 235852.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324032/c8022d06-23bb-fbe7-5bfd-ef984260a459.png)

画像を回転する場合は、回転行列を用いて補間前の座標を求める。

## 関連記事
https://qiita.com/sasshi_i/items/71189dc3a5c637fda3a4

https://qiita.com/sasshi_i/items/aef1465189fcaa6e7638

https://qiita.com/sasshi_i/items/2af0a76b95176437f923
json使ってみた

jsonとは？JavaScript Object Notationの略
* テキストフォーマットの一種で、シンプルかつ軽量
* null、真偽値、文字列、配列、オブジェクト（連想配列）のデータを文字列で表現できる
* 配列とオブジェクトはネスト構造で表現することができる
* XMLと比べて比較的仕様が簡単なため導入が容易

今回はJsonを使って簡単なバリデーションを実装してみた。

![](https://)![image](https://firebasestorage.googleapis.com/v0/b/ciita-c6ea6.appspot.com/o/user%2Fimages%2F5tCN0fBoPbZrCPsr?alt=media&token=03b8f8d0-f24a-4237-b371-52e9a4c18052)

![image](https://firebasestorage.googleapis.com/v0/b/ciita-c6ea6.appspot.com/o/user%2Fimages%2FCR9bj8MTieCTNXTy?alt=media&token=7d98ba64-4a63-4d84-9bcf-8d5788749480)
![image](https://firebasestorage.googleapis.com/v0/b/ciita-c6ea6.appspot.com/o/user%2Fimages%2FjXNclLTssNMK3beA?alt=media&token=56adc764-6cb7-4301-b6b4-33607913f148)
JSで複数のCanvasをPNGにしてZipでまとめてダウンロード

ダミーの画像を量産したいなと思って、JavaScriptのみで画像を沢山作成してZipでダウンロードさせてみました。
Canvasから画像を生成してZipでまとめる部分が肝になると思いますのでその個所を一部抜粋。
jQuery使ってます。

```js
$(function(){
  $("#download_btn").on("click", () => {
    // JSZipを読み込む
    const zip = new JSZip();

    // #chanvas_areaに入っているcanvasを取り出す
    const child_nodes = $("#canvas_area").children("canvas");

    // blobの生成が非同期になるのでPromiseで終了を捕捉する
    let add_to_zip = (zip_obj, canvas, filename) => {
      return new Promise((resolve, reject) => {
        canvas.toBlob(blob => {
          zip_obj.file(filename, blob);
          resolve();
        }, "image/png", 1);
      });
    }

    // 一旦add_to_zipを配列に入れておく
    add_to_zip_array = [];
    for (var i = 0; i < child_nodes.length; i++) {
      var name = $(child_nodes[i]).attr("name");
      add_to_zip_array.push(add_to_zip(zip, child_nodes[i], name + ".png"));
    }

    // 全てのblobができたらzipにまとめてダウンロードさせる
    Promise.all(add_to_zip_array).then(() => {
      zip.generateAsync({type:"blob"}).then(function(compressData) {
        var zip_blob = new Blob([compressData], { 'type': 'application/zip' });
        const a = document.createElement("a");
        a.href = URL.createObjectURL(zip_blob);
        a.download = "dummy.zip";
        a.click();
      });
    });
  });
});
```
ここで動作を確認できます
https://kensukesakakibara.github.io/DummyImageGenerator/

githubに上げてます
https://github.com/KensukeSakakibara/DummyImageGenerator

以上
ターミナルでエイリアスを作成する方法_.zshrcファイル

<h1>はじめに</h1>
ターミナルでPythonを立ち上げる際、クラウドサーバーを使っています。<br>
毎回、ターミナルでクラウドサーバーに移動してから、Cursorを立ち上げるのが手間でした。<br>
ここでは、クラウドサーバーへの移動が簡単になるよう、キーワードで呼び出せるようにしていきます。<br>
Pathを打ち込む、あるいはコピペする必要をなくします。

<h1>ファイルにエイリアス(ショートカット)を作成</h1>

```
terminalで.zshrcファイルを開く。
open -e ~/.zshrc
```

```
ファイルが開いたら、以下のエイリアスを追加して、保存して閉じる。
alias cdwork='cd /your/path'
```
```
terminalで変更を有効にする。
source ~/.zshrc
```

```
以降、terminalで以下コマンドで呼び出せるようになる。
cdwork
```
importの中括弧とは [javascript]

# 中括弧{}がいる時といらん時

| {}がいる時 | {}がいらんとき |
| --- | --- |
| exportの時 | export defaultの時 |

「importの中括弧が必要な時/不要な時の違い」を理解するには、```export```と```expor default```の違いについて知る必要がある。

## exportのdefaultがある時とない時の違い

| export文 | import時の{} | import時の名前 | 使用回数 |
| --- | --- | --- | --- |
| デフォルトexport | 不要 | 好きな名前 | 一ファイルにつき一回まで |
| 名前つきexport | 必要 | 定義した時のコンポーネント名（asを使って名前変更可能） | 何回でも |

##### 使用例

| export文 | import文 | 備考 |
| --- | --- | --- |
| export default Button(); | import Submit from ‘components/Button.js’; | Submitという名前でインポートしているが、別にAppleとかでもいい。 |
| export Button(); | import { Button } from ‘components/Button.js’; | Buttonでexportしたので、呼び出し時もButton |

## デフォルトエクスポートと名前付きエクスポートの使い分け

どちらを使うにしても、意味がわかりやすい名前をつけること。

**補足（よくある使い分け）**

> **ファイルがコンポーネントを 1 つだけエクスポートする場合はデフォルトエクスポートが、複数のコンポーネントや値をエクスポートする場合は名前付きエクスポートがよく使われます**。どちらのコーディングスタイルが好みの場合でも、コンポーネントやそれが入るファイルには、常に意味の通った名前を付けるようにしてください。
> 
引用（https://ja.react.dev/learn/importing-and-exporting-components）


### 参考文献

[コンポーネントのインポートとエクスポート – React](https://ja.react.dev/learn/importing-and-exporting-components)
【TailwindCSS】hoverに対応している場合にのみスタイルを適用する方法

以下のように記述するとデバイスが`hover`に対応している場合にのみスタイルが適用されます。

```html
<button class="[@media(hover:hover)]:focus-visible:bg-blue-500">
  ボタン
</button>
```
エンジニア(仮)がただChatGPTをおちょくるだけ Vol.1

第1回は **「ChatGPTはプロレスラーを理解しているのか？」** です。
<br>
早速会話をご覧ください。
<br>
![vol1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3693011/15d2c691-303e-9d45-0fda-838639e07a9d.png)
<br>
昨日降った雪が通勤路にも積もっていた今朝、運動神経がお粗末な私は何度も足を取られつつ必死に駅まで歩きながら考えていました。
 **「私がプロレスラーだったら、たとえ滑り転んだとしてもきっと平気なのに」** と。
<br>
上記はそんな私が電車に揺られながらChatGPT氏と会話した履歴です。
<br>
ちなみに私はまったくプロレスに明るくありません。
少し前からハマっている某プロレスラー異世界転生漫画の影響で、最近の私はすっかり「プロレスラーなら大丈夫！だってプロレスラーだもんね！」という思考回路になっているだけです。
でもChatGPT氏だってプロレスそんなに詳しくないんじゃない？なんて疑ってかかってみたのですが、予想に反して私よりよっぽどプロレスを理解していそうなChatGPT氏。
演出から運営サイドまで考えてくれました。
<br>
コンテクスト解釈的な部分でも、「刺激が足りないからスパイスを足せ」という、ここだけを切り取れば料理の話ともとれる一文を、「３つのシナリオに」という目的語やそれまでのやり取りから推測したのか、しっかりこちらの意図を汲み取って「先ほど作成した文章に過激性を追加して再構成する」というアクションを返してくれました。さすがです先輩。
<br>
<br>
残念ながらこれがリアルなプロレス運営に則しているのかいないのか判断できるだけの知識が私側に無いので、改めてChatGPTを使う側のリテラシーの重要性を感じましたとさ。
<br>
おしまい
エンジニア(仮)がただChatGPTをおちょくるだけ Vol.0

## エンジニア(仮)によるChatGPTおちょくり試行　序文
<br>
はじめまして、エンジニア(仮)こと"さこちゃん"です。<br>
割とハチャメチャな人生なので経歴的な自己紹介は割愛します(プロフィールにざっくり書いてあります)が、今回なぜChatGPTをおちょくり倒してみたいと思ったのか、未来の私が初心に帰る手立てを残しておくという役目も兼ねて、ここに動機を記しておきたいと思います。<br>
<br>
プロフィールにも記載の通り、日本語が好きすぎて大学の専攻も日本語学を選んだ人間なので、ChatGPT発表後エンジニアになる前からChatGPT氏との対話にも大変興味を持っておりました。<br>
実際、日頃エンジニアとして業務やら個人的な学習やらに取り組む中でもとってもとっても助けてもらっているのですが…私段々とふざけてしまいたくなる悪癖がありまして。<br>
まじめに学習しよう、クリーンな精神で無駄なく活用しよう、と心がけてはみてもつい要らんことを言ってしまう…<br>

 **じゃあいっそ開き直って普通に遊べばよくない？** 

と思ったのです。<br>
そしてそれを自分の記録としてアカウント内に積み上げて終わっても良いのだけども、崇高な精神の方々が見たら案外何らかの素敵な研究の種になる可能性も無きにしも非ずじゃない？とも思いまして。<br>
<br>
<br>
そんなあまりにもふざけた経緯で勢いのままに筆をとったのですが、逆に何の気負いもないので、楽しく続けられたら良いなぁと思っています。<br>
<br>
<br>
ちなみに学生時代からよく「日本語を専門的に勉強してたってことは日本語の誤用とかに厳しいの？」と言われてきたのですが、そんなことはありません。(実際私の使う日本語が正しいなんて思ったことありません)<br>
私の場合はむしろ日本語の可能性を模索したいので、誤用は誤用で<br>
「なぜそのような使い方をするのか？」
「正しい使い方と誤用とではニュアンスや用法に違いはあるのか？それとも単純な言い間違いか？」
「誤用の方が口当たりが良いのか？」
「今後こちらがスタンダードになっていくか？」<br>
などなど考察の余地があって楽しいです。<br>
<br>
<br>
私にとって言語は、思考整理や意思伝達の道具であり、無形文化であり、目に見えないものを空に描いて他者と共有する手段です。<br>
そのため、真実性と受け取り手への配慮(その人が意図した通りに他者に伝わってほしいと思うこと、また、伝わるよう努めること)という意味では、確かに口うるさいかもしれません。<br>
そしてそんな視線で眺めていると、ChatGPTの発話内容に感じる「一見自然に見えるけど何となく覚える違和感」の正体が気になったりもして。<br>
<br>
その「はてな」を解き明かす…というもっともらしいテーマを掲げて、ひとまずこの序文の〆とさせていただきます。
アクセルキャンプ復習動画　Roblox Lua ②  変数　for文

この記事は岩手県八幡平市のプログラミング教室「アクセルキャンプ」の公開教材です。転載・編集等は自由ですが、ご一報いただけると助かります。
また、教材作成依頼等も承っておりますので、リンク先のお問い合わせ欄からお願いします。
https://docs.google.com/forms/d/e/1FAIpQLSdNTZFrWGFZoM7QIFM1AuBOTnJ6b1sTn3KMHhIDa58bqw271g/viewform
___
当記事はyoutubeショート動画の補足資料になります。
ショート動画リンク：https://youtu.be/JyKExERWXdI

①変数名の宣言
「これからこの変数使うよ」という一文を、「変数宣言」といいます
```lua
local name = "リッキー先生"

print(name)
-- リッキー先生
```
あとからデータを入れ直しり、編集することもできます

```lua
local name = "リッキー先生"
name = "イケメンリッキー"
print(name)
-- イケメンリッキー

local suuji = 1
print(suuji + 2)
-- 3
```

②for文
そこにiはあるんか
```lua
for i = 1, 10 do
    print(i)
end
```

階段をたくさん（１００段）作るコード
```lua
-- 一段目のx y zを決める
local x = 5
local y = 5
local z = 5

for i = 1, 100 do 
    local part = Instance.new("Part", game.Workspace)
    part.Size = vector3.new(5, 5, 5)
    part.Position = vector3.new(x, y, z)
    y = y + 5
    -- y += 1 でもok 
    z = z + 5
    -- z += 1 でもok
end
```
【個人的備忘】久しぶりにDocker入れてみた

何年かぶりに真面目にDocker環境を作ったら、かなり浦島だったので、備忘。
(ちなみに今回あまり時間を掛けられないのでMarkDownは無視)
●環境
MBP(Intel)2018,MacOSX 11.6.4
VMwareFusionPlayer 13.5.0
Ubuntu20.04.6

1.Docker
Dockerはここで普通に入る
https://docs.docker.com/engine/install/ubuntu/
2.docker-compose
問題はdocker-compose
https://docs.docker.com/compose/install/
DockerDesktopを入れろというが、手順通りに進めて、いざアイコンから起動すると、エラーのウィンドゥが開くんですよ、曰く、
｢Virtualisation support KVM is not enabled on host,see https://docs.docker.com/desktop/install/linux-install/#kvm-virtualization-support on how to configure it｣
といわれる。
で、kvm-intelが無いんです。(あるけど無いOperation not supported)

DockerDesktopを入れるのが今回の目的ではないので、さっさとInstall Compose standaloneに進む。
https://docs.docker.com/compose/install/standalone/

以上です。
matplotlibを使ってレーダーチャートを作成する

## やりたいこと

- 以下のようなレーダーチャートが描けるようになる

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253735/81852238-8213-9313-c932-527140a1b0d0.png)

## matplotlibを使う方法

### 1. 必要なパッケージのインストール

- まずは基本的なパッケージをインストールする
```python
import matplotlib.pyplot as plt
import numpy as np
import japanize_matplotlib
```

### 2. データを準備する

- 物体検知モデルの各ラベルごとの精度を可視化することを想定して適当なクラスおよび精度を指定
```python
label_list = ['車', '人', '自転車', '犬', '猫', 'バス', 'トラック', '信号']
acc_list = [0.90, 0.85, 0.75, 0.80, 0.95, 0.70, 0.85, 0.88]  # 例えば、これらは各クラスの精度を表します。
acc_list += acc_list[:1]  # チャートを閉じるために最初の値を末尾に追加
```

- それぞれのクラスを表現する角度を指定
```python
# レーダーチャートを描画するためのangle_listを計算
angle_list = [n / float(len(label_list)) * 2 * np.pi for n in range(len(label_list))]
angle_list += angle_list[:1]
```

:::note warn
- N個のラベルを持つレーダーチャートの場合、N+1個のラベルが必要である点に注意
- 以下のように、閉じていないチャートとなり不恰好となってしまう
:::


|データ点数が不足|データの長さが十分|
|--|--|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253735/4fd0964a-f7cd-caa2-d333-1cfadd2b5faf.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253735/56071ae8-15a7-ca4e-1a91-b84e2a990d9d.png)|

### 3. polar形式で描画する

```python
# matplotlibでレーダーチャートを描画
fig, ax = plt.subplots(figsize=(8, 8), subplot_kw=dict(polar=True))
```

- `subplot_kw=dict(polar=True)`を指定するだけでレーダーチャートのベーシックな構図が表現される模様

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253735/72b07b97-378b-31d7-8b81-dd017a837a86.png)

- ラベルを付与してデータをプロットする

```python
# matplotlibでレーダーチャートを描画
fig, ax = plt.subplots(figsize=(8, 8), subplot_kw=dict(polar=True))
plt.xticks(angle_list[:-1], label_list, color='grey', size=12)

# プロット線を描画
ax.plot(angle_list, acc_list, linewidth=1, linestyle='solid', label='モデル精度')

```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253735/286f8a2a-97a8-1d6f-4cd2-520771d68d34.png)


```python
# クラスのlabel_listと精度の値を設定
label_list = ['車', '人', '自転車', '犬', '猫', 'バス', 'トラック', '信号']
acc_list = [0.90, 0.85, 0.75, 0.80, 0.95, 0.70, 0.85, 0.88]  # 例えば、これらは各クラスの精度を表します。
acc_list += acc_list[:1]  # チャートを閉じるために最初の値を末尾に追加

acc_list_2 = [0.50, 0.55, 0.85, 0.60, 0.85, 0.50, 0.65, 0.48]  # 例えば、これらは各クラスの精度を表します。
acc_list_2 += acc_list_2[:1]  # チャートを閉じるために最初の値を末尾に追加

# レーダーチャートを描画するためのangle_listを計算
angle_list = [n / float(len(label_list)) * 2 * np.pi for n in range(len(label_list))]
angle_list += angle_list[:1]

# matplotlibでレーダーチャートを描画
fig, ax = plt.subplots(figsize=(8, 8), subplot_kw=dict(polar=True))
plt.xticks(angle_list[:-1], label_list, color='grey', size=12)

# # プロット線を描画
ax.plot(angle_list, acc_list, linewidth=1, linestyle='solid', label='Model A')
ax.fill(angle_list, acc_list, 'blue', alpha=0.1)

ax.plot(angle_list, acc_list_2, linewidth=1, linestyle='solid', label='Model B')
ax.fill(angle_list, acc_list_2, 'red', alpha=0.1)


# # チャートを表示
plt.legend(loc='upper right')
plt.show()


```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253735/81852238-8213-9313-c932-527140a1b0d0.png)


## 結論
- 棒グラフも良いけれども、より直感的に理解しやすいレーダーチャートも選択肢の一つとしてあり
p5.js でイベント検知して行う処理に関して検知後に一定時間は処理をブロックする時間を設ける：setTimeout() or millis() を使う

この記事の内容は、p5.js　で「常時イベントの発生を見張り、イベントを検知した時に特定の処理を実行する」ということを行う場合の話です。
もう少し補足すると、「そのイベント検知は随時行うものの、イベントが検知された場合には、その検知後の一定時間はイベント検知に連動した処理をブロックする」ということを実現する方法です。

これを実装するのに試した 2つの方法を、メモ的に記事にします。

## 2種類の実現方法
今回の記事でメモする実現方法は 2つあります。
具体的には、setTimeout() を使う方法と、millis() を使う方法です。

それぞれの事例について、シンプルな内容で例を示します。

### setTimeout() を使う
まずは、setTimeout() を使うやり方です。

ここでやることは、イベント検知に連動して行われる処理の有効化／無効化を制御するフラグを用意し、そのフラグの切り替えに setTimeout() を使うというやり方です。setTimeout() を使う部分は、フラグを使ってイベント検知に連動した処理が行われない状態を作った後に、一定時間が経過したらフラグを切り替えるというところです。

マウスクリックを検知する対象のイベントとして、そのイベント検知に連動する処理は背景色の変更、という内容で、以下のようなプログラムを作ってみました。

```javascript
let eventDetected = false;

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

function draw() {
  if (eventDetected) {
    background(100, 150, 200);
  } else {
    background(220);
  }
}

function mouseClicked() {
  if (!eventDetected) {
    console.log("イベント検知に連動した処理を実行");
    eventDetected = true;

    setTimeout(() => {
      eventDetected = false;
      console.log("再度、イベント検知と連動した処理を実行可能にする");
    }, 3000);
  }
}
```

上記のプログラムを実行してマウスをクリックすると、「イベント検知に連動した処理を実行」というテキストがログに出力されます。そのテキストが出力されてから、3秒間は、再度クリックをしてもテキストはログ出力されません。

そして、3秒が経過した後は、再度、クリック時に「イベント検知に連動した処理を実行」というテキストがログに出力される状態に戻ります。

そして、上記の「フラグが true になって、3秒間維持される間」は、背景の色が変化するようにしています。

### millis() を使う
次は、millis() を使うやり方です。

先ほどは setTimeout() で実現したのと同じような処理を、millis() を使って以下のように実装します。

millis() を使った方法では、イベント検知が行われた時間を記録し、その記録した時間から3秒以上時間が経過したところで、フラグの切り替えを行っています。

```javascript
let eventDetected = false;
let lastEventTime = 0;

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

function draw() {
  if (eventDetected) {
    background(100, 150, 200);

    const currentTimeVal = millis();
    if (currentTimeVal - lastEventTime > 3000) {
      eventDetected = false;
      console.log("再度、イベント検知と連動した処理を実行可能にする");
    }
  } else {
    background(220);
  }
}

function mouseClicked() {
  if (!eventDetected) {
    console.log("イベント検知に連動した処理を実行");
    eventDetected = true;
    lastEventTime = millis();
  }
}
```

こちらのプログラムも、1つ目のものと同じような処理が行われることを確認できました。
GitHub Actionsのドキュメントに感動した話。

最近GitHub Actionsの公式ドキュメントを読んで感動した。
読み手への気遣いに溢れている。

これはまさに私のために書かれた文章だ。そう思わせてくれる。

お手本にしたいと思ったので、まとめてみる

https://docs.github.com/ja/actions/learn-github-actions/understanding-github-actions


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1199917/b109d49d-776f-cd56-4a34-18ce000fe86b.png)



## 良いと思ったポイント


### 1. タイトルが明快である
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1199917/1ace6ab4-fce3-4c56-06ce-7c8c861f7810.png)

タイトルが短く明快だ。

### 2. イントロで何が書かれているか端的に記載されている
よく見ると少し色が薄くなっている。
コントラストで情報に強弱を付けて読みやすくしてくれている。
もし仮にタイトルと同じスタイルだったら、平坦な印象を受けていただろう。


### 3. サイドバーでその記事が、ドキュメント全体で、どこに位置するか示されている。
全体像と現在地が分かる。
何を学んでいるか俯瞰できる。迷子にならない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1199917/61d2be2a-dc50-e40e-66a5-120fa39eaf1a.png)



### 4. 記事に目次が用意されている

冒頭部でその記事の構造がわかる。
基本的なことだが、あるとやっぱり便利。
そして、タイトルとイントロの真下にある、
この文章は何を目的として、何が書かれているか一瞬で把握できてしまう

### 5. ひとつひとつの文章が短く読みやすい

認知的負荷がまったくない。
パラグラフ・ライティングのお手本のようだ
特に一文目に注目してほしい。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1199917/ecd1f1bd-3fee-f8ca-8251-43f98fb31f03.png)

ページと図のカラーコントラストも良い

### 6. 前提知識が必要な概念や用語に、リンクが設定されている。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1199917/b34262dc-77ce-15f3-22cd-346fc509667a.png)


しかも、ポップアップで概要が表示される仕掛けまである。

嬉しい。気遣いに涙が溢れそうだ。


### 7. 全体として文章量が抑えられている

スクロールバーが極小になったドキュメントを見る事がある。

ぺちゃんこになったスクロールバーに何度心を折られたことか。
「これなら全部読みきれる、読もう。」

そう感じさせてくれる文量だ。

非常に考えられている。

### 8. 読み手の行動を予測して、必要な情報が書かれている。

ワークフローを定義するYAMLファイルの説明が非常に秀逸。

左側にコードスペース、右側に解説文。
構造を示しながら、一個一個理解できる作りになっている

しかも、前セクションでサンプルワークフローを表示し、
ハンズオン形式でサンプルワークフローを作成する流れになっている。

指示に従い手を動かしてみると、ワークフローが設定できるが、
その次には、「どういう構造だろう？」と疑問が浮かぶ。

これがフックになって、内容に興味が向く仕掛けだ。

気づいたら、思考誘導されていた。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1199917/192cb5f1-650e-d5bc-44bc-ec43bdd00a0b.png)
からの

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1199917/45c9b79b-e82b-9a02-d172-746c4810f9d0.png)
である


### 9. 最後に読み手が次に取るべきアクションを示唆してくれている
つぎに読むべきドキュメントを、予めサジェストしてくれている。
本当に何から何まで、至れり尽くせりだ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1199917/2c2f8eaf-8fc2-f62d-dd71-8b3156f385df.png)



## 感想

この芸術的なドキュメントを書いた方に、盛大な拍手を送りたい。
Dockerコマンド チートシート(composeコマンドあり)


使わないと忘れてしまうのでチートシートとしてメモする。

# 前提

dockerコマンドは現在移行中のため中には古い書き方のコマンドがあります。
古い書き方は、Aliasとして機能するので動作します。
最新のコマンドが知りたい場合は、[参考](#参考)欄に記載のリファレンスを参照してください。

# Dockerコマンド

## docker ps

立ち上がっているコンテナを確認する

```bash
$ docker ps
```

作成したコンテナの一覧を起動・停止限らず表示する

```bash
$ docker ps -a
```

## docker pull

imageをプルする

:::note info
`:`より後ろは省略可能。
指定することにより任意のタグを取得できる。
:::

```bash
$ docker pull ubuntu:latest
```


## docker run

:::note info
`-d`オプションを指定するとバックグラウンドで実行
runコマンドを使用する際は大体`-d`オプション付きで実行することになる。
:::

フォアグラウンド実行

```bash
$ docker run ubuntu:latest
```

バックグラウンド実行

```bash
$ docker run -d ubuntu:latest
```

## docker stop

実行しているコンテナを停止する

```bash
$ docker stop <container name>
```

## docker images

保存しているイメージの一覧を表示する

```bash
$ docker images
```

## docker rm

作成したコンテナを削除する

```bash
$ docker rm <コンテナ名 or コンテナID>...
```

## docker rmi

保存しているイメージを削除する

```bash
$ docker rmi <イメージID>...
```
# docker compose コマンド

docker-compose.ymlを読み取り実行する

## docker compose up

ymlに紐づくコンテナを立ち上げる

```bash
$ docker compose up -d
```

## docker compose down

ymlに紐づくコンテナの削除をする
docker composeで立ち上げたときに作成したネットワークも削除する

```bash
$ docker compose down
```

## docker compose start

ymlに紐づくコンテナを立ち上げる

```bash
$ docker compose start
```

## docker compose stop

ymlに紐づくコンテナを停止する

```bash
$ docker compose stop
```

## docker compose restart

ymlに紐づくコンテナを再起動する

```bash
$ docker compose restart
```

# 参考

## Docker 公式リファレンス

https://docs.docker.com/reference/
C# ログ出力 その2 Serilog

Serilogを使用してデバッグ、コンソール、およびファイルへのログ出力を実現するには、以下のような手順を踏みます。

1. **Serilogのインストール:**
   - プロジェクトにSerilogおよび出力先に関するパッケージを追加します。例えば、NuGet パッケージ マネージャーから以下のコマンドを実行します。
     ```bash
     Install-Package Serilog
     Install-Package Serilog.Sinks.Console
     Install-Package Serilog.Sinks.Debug
     Install-Package Serilog.Sinks.File
     ```

2. **Serilogの設定:**
   - アプリケーションの開始時にSerilogを設定します。通常、`Main`メソッドやアプリケーションの初期化時に設定を行います。
   ```csharp
   using Serilog;

   class Program
   {
       static void Main()
       {
           Log.Logger = new LoggerConfiguration()
               .WriteTo.Console()
               .WriteTo.Debug()
               .WriteTo.File("log.txt", rollingInterval: RollingInterval.Day)
               .CreateLogger();

           // ここからアプリケーションの起動処理を開始する
       }
   }
   ```

3. **ログの使用:**
   - アプリケーションのどこかで `Log` クラスを使用してログを出力します。
   ```csharp
   Log.Information("This is an information message.");
   Log.Debug("This is a debug message.");
   Log.Error("An error occurred: {ErrorMessage}", exception.Message);
   ```

   上記の例では、`Console`にログを表示し、`Debug`出力にもログを出力し、毎日新しいファイルにログを書き込むように設定されています。

4. **注意:**
   - Serilogは非常に柔軟であり、様々な設定が可能です。ログのフォーマットや出力先、レベルなどをプロジェクトの要件に合わせて調整することができます。 Serilogの公式ドキュメントを参照することをお勧めします。

これにより、Serilogを使用してアプリケーションがデバッグ、コンソール、およびファイルにログを出力できるようになります。
C# ログ出力 その1 NLog、log4net、Serilog

### 1. NLog:

- **特徴:**
  - 柔軟でカスタマイズ可能なログライブラリ。
  - 豊富なログターゲットやフィルタリングの機能があります。
  - 設定はXMLまたはC#コードで行えます。

- **基本的な使用法:**
  - NLogをプロジェクトに統合し、ログ出力先やログレベルを設定します。
  - ロギングは`LogManager.GetCurrentClassLogger()`を使用して行います。

- **サンプル設定:**
  ```xml
  <nlog>
    <targets>
      <target name="file" xsi:type="File" fileName="log.txt" />
    </targets>
    <rules>
      <logger name="*" minlevel="Info" writeTo="file" />
    </rules>
  </nlog>
  ```

### 2. log4net:

- **特徴:**
  - Apache log4netは柔軟性があり、様々なアペンダー（出力先）やレイアウトが利用可能です。
  - XMLまたはC#コードで設定が可能です。

- **基本的な使用法:**
  - log4netをプロジェクトに統合し、`log4net.Config.XmlConfigurator.Configure()`で設定を読み込みます。
  - ロギングは`ILog`インターフェースを使用します。

- **サンプル設定:**
  ```xml
  <log4net>
    <appender name="ConsoleAppender" type="log4net.Appender.ConsoleAppender">
      <layout type="log4net.Layout.PatternLayout">
        <conversionPattern value="%date [%thread] %-5level %logger - %message%newline" />
      </layout>
    </appender>
    <root>
      <level value="INFO" />
      <appender-ref ref="ConsoleAppender" />
    </root>
  </log4net>
  ```

### 3. Serilog:

- **特徴:**
  - 構造化されたログを重視し、豊富なエコシステムを持つログライブラリ。
  - Fluent APIを使用して設定を行います。

- **基本的な使用法:**
  - Serilogをプロジェクトに導入し、設定を行います。
  - `Log`クラスを使用してログを出力します。

- **サンプル設定:**
  ```csharp
  Log.Logger = new LoggerConfiguration()
      .WriteTo.Console()
      .WriteTo.File("log.txt")
      .CreateLogger();
  ```

これらのログライブラリはいずれも設定方法やカスタマイズの手法が異なります。プロジェクトの要件や開発者の好みによって選択することが一般的です。それぞれの公式ドキュメントを参照しながら、プロジェクトに最適なログライブラリを選択し、設定することが重要です。
【エラー解決】AlmaLinuxでFTP通信ができない

### はじめに
AlmaLinuxの設定中、FTP通信の部分で躓いたので備忘録も兼ねて纏めます。

### エラー状況
```dnf install vsftpd```でのインストールは正常に完了し、```vi /etc/vsftpd/vsftpd.conf```で設定ファイルを変更したにも関わらずFTP通信ができない状態でした。

### 原因
サーバーでIpv6を使わない設定になっているにも関わらず、それをvsftpdに設定していなかったこと

### 解決策
```vi /etc/vsftpd/vsftpd.conf```を編集し直すことでFTP通信ができるようになりました。

```
# the behaviour when these options are disabled.
# Beware that on some FTP servers, ASCII support allows a denial of service
# attack (DoS) via the command "SIZE /big/file" in ASCII mode. vsftpd
# predicted this attack and has always been safe, reporting the size of the
# raw file.
# ASCII mangling is a horrible feature of the protocol.
#ascii_upload_enable=YES
#ascii_download_enable=YES
#
# You may fully customise the login banner string:
#ftpd_banner=Welcome to blah FTP service.
#
# You may specify a file of disallowed anonymous e-mail addresses. Apparently
# useful for combatting certain DoS attacks.
#deny_email_enable=YES
# (default follows)
#banned_email_file=/etc/vsftpd/banned_emails
#
# You may specify an explicit list of local users to chroot() to their home
# directory. If chroot_local_user is YES, then this list becomes a list of
# users to NOT chroot().
# (Warning! chroot'ing can be very dangerous. If using chroot, make sure that
# the user does not have write access to the top level directory within the
# chroot)
chroot_local_user=YES
chroot_list_enable=YES
allow_writeable_chroot=YES
# (default follows)
chroot_list_file=/etc/vsftpd/chroot_list
#
# You may activate the "-R" option to the builtin ls. This is disabled by
# default to avoid remote users being able to cause excessive I/O on large
# sites. However, some broken FTP clients such as "ncftp" and "mirror" assume
# the presence of the "-R" option, so there is a strong case for enabling it.
ls_recurse_enable=YES
#
# When "listen" directive is enabled, vsftpd runs in standalone mode and
# listens on IPv4 sockets. This directive cannot be used in conjunction
# with the listen_ipv6 directive.
#ipv6を使わない設定に変更↓
listen=YES
#
# This directive enables listening on IPv6 sockets. By default, listening
# on the IPv6 "any" address (::) will accept connections from both IPv6
# and IPv4 clients. It is not necessary to listen on *both* IPv4 and IPv6
# sockets. If you want that (perhaps because you want to listen on specific
# addresses) then you must run two copies of vsftpd with two configuration
# files.
# Make sure, that one of the listen options is commented !!
#ipv6を使わない設定に変更↓
listen_ipv6=NO

pam_service_name=vsftpd
userlist_enable=YES
local_root=public_html
use_localtime=YES
```
Go言語における「nil」とゼロ値の理解

Go言語では、変数に初期値を割り当てずに宣言すると、その型のゼロ値が自動的に割り当てられます。この機能は、プログラムのバグを減らし、初期化の手間を省くことに役立ちます。

### 基本型のゼロ値

- 整数型（int, int8, int16, int32, int64 など）: ゼロ値は 0 です。
- 浮動小数点型（float32, float64）: ゼロ値は 0.0 です。
- 論理型（bool）: ゼロ値は false です。
- 文字列型（string）: ゼロ値は空文字列 "" です。

### 複合型のゼロ値

- ポインタ: ゼロ値は nil です。これは、何も指していないことを意味します。
- スライス: ゼロ値も nil です。長さと容量がともに0のスライスを表します。
- マップ: ゼロ値は nil で、キーと値のペアが存在しないことを意味します。
- チャネル: ゼロ値は nil で、初期化されていないチャネルを表します。
- インターフェース: ゼロ値は nil で、どの具体的な値も保持していないことを意味します。
- 構造体（struct）: 各フィールドがその型のゼロ値を持つ状態です。

### ゼロ値の利用例

ゼロ値の概念は、Go言語のプログラムでエラーを防ぐために役立ちます。例えば、マップに値を追加する前に初期化を忘れていた場合、プログラムはパニックを起こす代わりに、nil マップを操作しようとして静かに失敗します。しかし、この振る舞いを適切に扱うことが重要です。

```go
package main

import "fmt"

func main() {
	var s string   // ゼロ値は ""
	fmt.Println(s) // 出力: ""

	var i int      // ゼロ値は 0
	fmt.Println(i) // 出力: 0

	var p *int     // ゼロ値は nil
	fmt.Println(p) // 出力: <nil>

	var myMap map[string]int // ゼロ値は nil
	// myMap["one"] = 1 // この行はパニックを引き起こす可能性があります。
	// マップを使用する前に初期化が必要です。
	myMap = make(map[string]int)
	myMap["one"] = 1
	fmt.Println(myMap) // 出力: map[one:1]
}

```
ゼロ値は、関数が期待した型の値を返さない場合にも役立ちます。例えば、エラーが発生した場合、関数はその型のゼロ値を返すことがよくあります。

### ゼロ値練習

下記のコードに、`if val, ok := m[key]; ok {...}else{...}` の意味は何でしょうか？


```go
package main

import "fmt"

func main() {
	m := map[string]int{"one": 1, "two": 2}

	if val, ok := m["two"]; ok {
		fmt.Println("値:", val) // キーが存在する場合、値を出力
	} else {
		fmt.Println("キーが存在しません。")
	}

	if val, ok := m["three"]; ok {
		fmt.Println("値:", val)
	} else {
		fmt.Println("キーが存在しません。") // キーが存在しない場合の出力
	}
}
```

`if val, ok := m[key]; ok {...}` という構文は、マップ（map）から特定のキーに対応する値を安全に取得するためによく使用されます。この構文は、マップから値を取得し、そのキーが存在するかどうかを同時にチェックします。

- m[key]は、マップmからキーkeyに対応する値を取得します。
- val, ok := m[key]は、2つの値を返します。
- valは、キーに対応する値です（キーが存在する場合）。キーが存在しない場合、valはその型のゼロ値になります（例えば、intの場合は0、stringの場合は""など）。
- okは、bool型で、キーがマップに存在する場合はtrue、存在しない場合はfalseを返します。
- ifステートメントは、okの値をチェックし、true（キーが存在する）の場合はifブロック内のコードを実行し、false（キーが存在しない）の場合はelseブロック内のコードを実行します。

いかかでしょうか？


https://go.dev/play/
僕はベストを目指さない、受け入れ可能を目指す

# 主旨：
僕はベストを目指さない。受け入れ可能なレベルを目指す。

## ベストを目指さない理由
- 必要なのは、目的に対して受け入れ可能であるかどうか、受け入れ可能ならば、それ以上を目指す必然性はない。
- ベストとは、ベストの基準を選ぶことによる極めて恣意的なもの。
- 恣意的なベストは、安定して使えるとは限らない。

## 単一なベストがあるという幻想、必要なのは受け入れ可能なこと
### 例：クルマを購入するということを考えてみよう
- 目的：マイカーとして使う。
- 用途：自分が使う他に、配偶者が買い物に使う。長距離ドライブはしない。
- 人数：４人家族である。
- 燃費がよいコンパクトカーは、駐車場の出し入れもしやすい。
- 予算は◯◯◯万円以下にしたい。
- そういった条件をリストアップしていって、市販されているクルマから自分たちにとっていいものを選ぶ。

決して、単一値の大小関係で、どれがいいかを選ぶことはしないでしょう。

### 例：業務で使用する機材の購入
- これも、目的があって、予算があって、さまざまな条件があって、機材を購入する。
- けして、一番安いものでは選ばないはずだ。
- 安いの望ましいといっても、極端に安いものは、問題を含むことが多い。
- 「１円入札」などをしている業者にろくなものはない。
- しっかりしたものを購入するには、それ相応の費用がかかる。
- 目的にそった条件を満たしているかどうか。
    - これを満たしていないと無意味になってしまう必須の条件
    - なるべくなら満たしているとよい追加の条件
    - それを区別することが必要。


### 例：自分たちで開発するもの
- 目的を明確にすること
- 必須の要件を絞り込むこと
MVP(Minimum Viable Product, 実用最小限の製品)
を明らかにすること。
- 早い段階で、必須の要件を実現して、利用者に提供してフィードバックを得ること。

### 受け入れ可能な水準に達することこそが大事
- 受け入れ可能な水準を目指すことは、製品として成り立つためのバランスが必要になる。
- なるべくなら満たしているとよい追加の条件は後回しにしよう。
- 値が高いほうがいいものも追加の作業にしよう。

### 追加の作業は、終わりがない。
- 値が高いほどよいものは、作業の切りがない。
- 例：画像認識での検出率
    - ある程度、検出率があがったあとは、検出率をさらに上げるのはだんだん難しい作業、あるいは手間のかかる作業になっていく。
- 例：画像認識での処理速度
    - 高速化の常套手段を実行した後は、その後の高速化は割に合わなくなる。


### その追加の作業は、それだけのコストをかける価値がありますか。
- 追加の作業をする前に:
	- 今の状態でも早めに使ってもらってフィードバックを得たほうがよくないか。
	- 実はもう十分だったりしないか？
	- その追加の作業よりも、実はもっと優先順序が高い作業があったりしないか？

## 結論
- だから、僕はベストを目指さない。
- 受け入れ可能を目指す。
【OCI】APEXでAutonomous Database上の他のテーブルを参照・追加・更新する方法

## はじめに
Oracle Cloud Infrastructure（OCI）のAutonomous Database (ADB)でテーブルを作成した際に、そのテーブルをAPEXでも参照や変更する方法についてご紹介します。

## アクセス権限の付与
今回ご紹介する方法としては、APEXのユーザーに対象のテーブルのアクセス権限を付与することです。
アクセス権限の付与に関しては、ADBやAPEXに限った話ではなく、データベースに従事する方であれば基本的な操作になります。

**■ 前提条件**
・ADB上で対象のテーブルを作成
　ADMINユーザーで対象のテーブル「Test_Table」を作成しています。
　カラムは、Test_ID（CHAR型）、Name（VARCHAR型）で設定しています。

・APEXでワークスペースを作成
　APEXでワークスペース作成時のユーザー名を「TestUser」と設定しています。


**1. アクセス権の付与**
今回は、ADBの機能のDatabase ActionsでSQL文を実行し、アクセス権の付与を行っていきます。
まず、OCIのコンソール上で利用するADBのDatabase Actionsを開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072173/c3678efa-a324-15d8-0d32-4e4b876323b7.png)

次に対象のテーブルへのアクセス権限を保有するユーザーを選択し、ワークシートにSQLを入力していきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072173/00f043f0-9b67-4d5e-f177-965071ef091c.png)

アクセス権限を付与するためのSQL文は以下の通りです。
~~~SQL:アクセス権限の付加
GRANT <権限名> ON <オブジェクト名>
      TO <スキーマ名>;
~~~
また権限名については以下を参照してください。
| 権限名 | 説明 |
|:-----------|:------------:|
| SELECT     | データの参照      |
| UPDATE     | データの更新    |
| INSERT     | データの追加  |
| DELETE     | データの削除        |
| REFERENCES | 他テーブルの外部キーを作成   | 
| ALL        | 全ての操作    |

今回はAPEX上で、別スキーマのADBの表からデータを **参照・更新・追加** するための権限を付与していきます。
ここで注意する点としては、APEXでワークスペースを作成するときに入力したユーザーへ権限を付与するのではなく、そのユーザー名の<font color="red">**接頭辞に「WKSP_」と付いたユーザー**</font>に権限を付与する必要があります。
APEXのユーザーの詳細については、以下の記事の「APEXで使用されるユーザーについて」の項をご参照ください。

https://qiita.com/yunagash/items/1980117b224c10c03a84

上記を元にSQL文を作成し、Database ActionsでSQL文を実行していきます。
~~~SQL:参照・更新・追加の権限の付加
GRANT SELECT, UPDATE, INSERT ON ADMIN.Test_Table
      TO WKSP_TestUser;
~~~

**2. APEX上で表へのアクセスができるか確認する**
APEXのワークスペースを開き、SQLワークショップ ＞ SQLコマンド を開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072173/1426f7c2-5584-a439-caf8-fffbe4e7f021.png)

以下のINSERT文を実行し、「ADMIN」ユーザーの「Test_ID」表にデータを追加します。
~~~SQL:
INSERT INTO ADMIN.Test_Table VALUES(1, 'APEX');
~~~
INSERT文が正しく実行されているか確認するために、SELECT文でデータを確認します。
~~~SQL
SELECT * FROM ADMIN.Test_Table;
~~~
表にデータが格納されていることが分かりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072173/f6525590-17d8-8de2-871a-173127ddfdbc.png)

次にUPDATE文で格納されているデータを更新します。
~~~SQL
UPDATE ADMIN.Test_Table SET test_id = 2, name = 'Update_Apex' WHERE test_id = 1;
~~~
再度SELECT文で確認してみると、データが更新されていることが分かります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3072173/4f25e76f-3101-1873-6f7f-ca393149853b.png)
## おわりに
今回はAPEXユーザーに権限を付与した後に、SQLコマンドで実際にADB上の他のテーブルへのアクセスを確認しましたが、APEXのアプリケーションでも同様に参照・追加・更新ができるようになっています。
APEXでアプリケーションを作成する上での一助になれば幸いです。
SwiftUIのPhotosPickerの画像が逆さまになってしまった時

# 問題
SwiftUIのPhotosPickerを利用した場合謎に画像が逆さまになってしまっている

```Swift
@State private var transactionImageItem: PhotosPickerItem?
@State private var transactionImage: Image?

var body: some View {
    NavigationStack {
        VStack {
            HStack {
                VStack {
                    if transactionImage == nil {
                        PhotosPicker("Select avatar", selection: $transactionImageItem, matching: .images)
                    } else {
                       transactionImage?
                           .resizable()
                            .scaledToFit()
                            .frame(width: 100, height: 100)
                    }
                }
                .padding(.horizontal, 10)
            }
        }
        .onChange(of: transactionImageItem) {
                Task {
                    if let image = try? await transactionImageItem?.loadTransferable(type: Image.self) {
                        transactionImage = image
                    } else {
                        print("Failed")
                    }
                }
            }
    }
}
```

こうするとこんな感じになってしまう

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147278/656e24c2-473e-36e1-a54f-dd7d9b3d7c3d.png" width="300" height="200" />

# 解決
一旦`Data`型で取得して、Data -> UIImage -> Imageにしたら直りました。なあぜなあぜ :thinking: 

```Swift
let data = try? await transactionImageItem?.loadTransferable(type: Data.self),
let image = UIImage(data: data) {
transactionImage = Image(uiImage: image)
```

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147278/04d8429d-3f2c-4b3c-768c-fea5acfbe367.png" width="300" height="200" />
とりあえずの目標

とりあえず目標を書いてみる。やりながら考えよう。

* 環境構築
    * pythonの環境を作る
    * pythonが使えるようになる
* 一日単位
    * 株のホームページをスクレイピングして取り込む
    * 手元のデータベースによって株を分類する
    * 分類した結果を表とグラフにする
    * 表とグラフをhtmlにする
    * htmlをメールで送付する
    * エラーが発生したらそれもエラーで送る
    * 自動起動させて週1くらいで自動でメール送付する
* 時系列化
    * 取得したデータを保管して時系列データを作る
    * 時系列データをhtmlで保存する
    * エラー発生時さかのぼって修正できるようにする
SpringBootに慣れすぎてしまって、SpringBootの何がいいかよく分からなくなってしまった

# はじめに

特に転職してからSpringBootのフレームワークを用いた開発を2-3年ほど続けてきて、Springの理解はだんだん深くなってきて、ソースもすいすいと読めるようになってきました。
ですが、いざDIとか何が嬉しいんですか？と聞かれると明確に答えられないなと思い、これを機に一度基本から考えてみることにしました。

色々考えていて、SpringBootを使う利点がよくわからなくなってしまいました・・・お恥ずかしい・・・:scream:
コメントなどでこういう場合とか、理解が間違っているなど教えていただきたいです。 :sob:

# DIコンテナ

とりあえず、真っ先にDIコンテナとかIoCコンテナがSpringだったらいい点として挙げられますよね。
自分の理解だと、呼び出す側が呼び出す方の実装に依存しちゃうのを防げるのがいい点だと理解していて、よくリポジトリ層とかで実装が変えたときの変更可用性が高いってのが挙げられると思っています。そんな変更ほとんどしたことないけど。

```kotlin
// Oracle -> MySQLに変更するときに変更が発生する
class SampleService(){
  val sampleRepository = OracleSampleRepository()

  fun find(hogeId: String): hogeModel{
    return sampleRepository.find(hogeId)
  }
}
```

Springだとどうなるか

```kotlin
@Service
class SampleService(
  val sampleRepository: OracleSampleRepository // ここを変更すれば大丈夫
){
  fun find(hogeId: String): hogeModel{
    return sampleRepository.find(hogeId)
  }
}
```

# あれ、結局そんなに手間変わらんのでは・・・

理論上、後者の方がきれいなのは分かるんですが実装したときに結局注入するクラスを変更しなきゃいけないし手間変わらなくね・・・？という気がしてきました。
`MySQLSampleRepository`みたいなクラスを作って、メソッドと引数を同じにしておけば結局書き換える手間としては同じような。

同じメソッドを使うのを強制させたいなら適当な`SampleRepositoryInterface`みたいなクラスをつくってあげれば強制もできるし、実際DIコンテナを使用するメリットがよくわからなくなってきてきました。。

# その他便利かも？と思うユースケースを考えてみる

## テストしやすい

コンストラクタインジェクションしているところに、テストコードだとmockをいれてあげればいいのでテストしやすいです。
が、使わなくても引数にinterfaceをもらうことにすれば解決できそうな気がします。

```kotlin
class SampleService(
  sampleRepository: SampleRepositoryInterface
){
  fun find(hogeId: String): hogeModel{
    return sampleRepository.find(hogeId)
  }
}
```

ただ、こうしてしまうと`SampleService`を呼び出す側で`SampleRepositoryImpl`の実装を意識しないといけなくなりますね。

```kotlin
class SampleServiceImpl: SampleServiceInterface(
  sampleRepository: SampleRepositoryImpl
){
  override fun find(hogeId: String): hogeModel{
    return sampleRepository.find(hogeId)
  }
}
```

これを回避するには`Service`にもinterfaceを用意してあげればよさそうだけど、正直めんどくさい。
テストしやすいのと、変更しやすいを両立しているのは確かにSpringを使うメリットとして挙げてよい気がします。

## `application.yaml`から設定値を引っ張ってこれる

これはたしかに便利ですが、正直DIコンテナじゃなくてもよくて、起動時に適当なファイルから読み込んで定数としてもっちゃえば解決できそうです。

## SpringSecurityなどの認証認可の仕組みが自前で作らなくても使える

SpringSecurityに限らず、色々機能が豊富でやりたいことはライブラリ使えばできるのはたしかにメリットですね。

## だいたいみんな知ってる

処理の流れとか説明しなくても、`Controller`のクラスからたどる〜とか、`Bean`はデフォルトでシングルトンだから気をつけようね〜みたいなことは説明不要。おまけにググったらいろいろ文献でてくる。
これもメリットですね。

# まとめ

自分で色々考えた結果、`SpringBoot`を使うメリットとしては以下なのかなという風に思いました。

- 面倒なことをせずにテストしやすいコードを書ける
- Springの付随する機能を簡単に使うことができる
- 使用者が多いため、文献が多い

DIが手軽に実現できて超嬉しい！と結局納得できるようなメリットが考えられなかったので、冒頭にも書きましたがコメントなのでやさしく教えていただけると大変うれしいです :bow:
docker-desktopとWSL2の内部構造について見ていく

WSLのディストリビューションのデフォルト設定が以下のようにdocker-desktopになっていると、
```
PS C:\Users\○○> wsl -l -v
  NAME                   STATE           VERSION
* docker-desktop         Running         2
  docker-desktop-data    Running         2
  Ubuntu                 Running         2
```
  vscodeで右下のリモートウィンドウの選択で、「WSLへ接続」をおすと「vscode server for WSL closed unexpectedly.check WSL terminal for more details.」とポップアップエラーが出る。
vscodeウィンドウの右下には「starting VS code in WSL(docker-desktop)」というエラーもでる。

docker for windowsをインストールすると、docker独自のWSLのディストリビューションを作成して、WindowsとLinuxを連携するファイルシステムを実現できるよう仮想化がおこなわれるため、

`wsl -l -v`でdecker-desktopがデフォルトになっていると、vscodeのリモートウィンドウ接続でWSLへの接続ができなくなる。

この現象を解決するには、
```
PS C:\Users\○○> wsl -s Ubuntu
この操作を正しく終了しました。
PS C:\Users\○○> wsl config
/bin/bash: line 1: config: command not found
PS C:\Users\○○> wsl -l -v
  NAME                   STATE           VERSION
* Ubuntu                 Running         2
  docker-desktop         Running         2
  docker-desktop-data    Running         2
```
のようにディストリビューションのデフォルトをdockerが独自で利用しているWSLではなく、ユーザーがWSL上でさくせいしたものにきりかえるとかいしょうする。


しかし、docker desktop上での設定など色々関わるうえで、構成の理解が重要そうだったので調べをまとめてみる。
---


まず、基本的なインフラ構成から説明し、その後でwsl -l -vコマンドとDocker Desktopの設定について解説します。

基本的なインフラ構成
1. Docker Desktop for Windows + WSL2:

Windows上でDocker Desktopをインストールすると、WSL2上に特別なDocker環境（docker-desktopとdocker-desktop-data）が作成されます。
この構成では、Linuxカーネルのパフォーマンスを活用しつつ、Windows上でDockerをスムーズに動作させることができます。
2. WSL2 + Linux ディストリビューション（例：Ubuntu-20.04）:

WSL2を使って、UbuntuなどのLinuxディストリビューションをWindows上で動かすことができます。
Dockerを使用する場合、Linuxディストリビューション上で直接Dockerをインストールし、コンテナを管理することも可能です。
wsl -l -v コマンドについて
このコマンドを使用すると、現在インストールされているWSL2のディストリビューションとその状態が表示されます。*マークは、デフォルトで使用されるディストリビューションを示します。

docker-desktopがデフォルトの場合、Docker関連の操作はすべてDocker Desktop経由で行われます。
例えば、Ubuntu-20.04がデフォルトの場合、通常のLinuxコマンドライン操作やDockerコマンドはこのディストリビューション内で実行されます。
Docker Desktopの設定について
General設定:

"use the WSL 2 based engine"がONになっていると、Docker DesktopはWSL2をバックエンドとして使用します。
Resources -> WSL Integration設定:

"Enable integration with my default WSL distro"がONの場合、デフォルトのWSLディストリビューションとDocker Desktopが連携します。
特定のLinuxディストリビューションに対しては、個別に連携のON/OFFを設定できます。
VSCodeからWSLへの接続
VSCodeのリモートコンテナ機能を使用すると、Dockerコンテナ内でコードを開発することができます。
この機能を使用するためには、WSL2上にLinuxディストリビューションがインストールされている必要はありません。Docker DesktopのWSL Integration機能が有効であれば、Windows上の任意のディレクトリでコンテナを起動し、それをVSCodeで操作することが可能です。
ｗindowsでWSL2のフォルダを開く

# はじめに
WSL2を使っていて「Windows側でファイル開きたいな」って時があったので、その方法についてです。なお、**Windows11**を使用しています。
# 問題
一般的に使われているであろうコマンドをたたいたら、失敗してしまいました。
```
$ xdg-open
```
"command not found"とのことで、画面の指示通り"sudo apt install xdg-utils"を実行したものの、"You don't seem to have any mimeinfo.cache files."という警告文が出てしまいます。
# 解決方法
下記のコマンドで成功しました。
```
$ explorer.exe .
```
# おわりに
WSLだと問題なく実行できるようですが、WSL2だとうまくいかないのでしょうか。
明確な理由は謎ですが、上記のコマンドで一発で開けました。
# 参考
https://snowsystem.net/other/windows/wsl2-ubuntu-explorer/
【Swift】負の数でも、数字の部分だけ取得したい（絶対値）

# はじめに
絶対値を取得したい時があったので、調べるとabsを使えばできるようなのでやってみました。

# 実装
```swift
import Foundation

let intPlus: Int = 10
let intMinus: Int = -10

let doublePlus: Double = 10.5
let doubleMinus: Double = -10.5

let floatPlus: Float = 10.5
let floatMinus: Float = -10.5

let cgFloatPlus: CGFloat = 10.5
let cgFloatMinus: CGFloat = -10.5

print(abs(intPlus)) // 10
print(abs(intMinus)) // 10
print(abs(doublePlus)) // 10.5
print(abs(doubleMinus)) // 10.5
print(abs(floatPlus)) // 10.5
print(abs(floatMinus)) // 10.5
print(abs(cgFloatPlus)) // 10.5
print(abs(cgFloatMinus)) // 10.5
```

# ドキュメントコメント
```swift
/// Returns the absolute value of the given number.
///
/// The absolute value of `x` must be representable in the same type. In
/// particular, the absolute value of a signed, fixed-width integer type's
/// minimum cannot be represented.
///
///     let x = Int8.min
///     // x == -128
///     let y = abs(x)
///     // Overflow error
///
/// - Parameter x: A signed number.
/// - Returns: The absolute value of `x`.
@inlinable public func abs<T>(_ x: T) -> T where T : Comparable, T : SignedNumeric
```

# おわり
参考にした記事では`fabs`や`fabsf`などを使っていましたが、全て`abs`でいけました

# 参考記事
https://qiita.com/kapiemon/items/42d9c2385b595e3fba78
【CSS】ホバー時に下向きに消える下線消失アニメーション: CSSトランジションで実現する滑らかな視覚効果

この記事は、ユーザーがリンク上でマウスをホバーした際に下線が下向きに滑らかに消えていくエフェクトに焦点を当てています。

  　![screenshot_25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2711935/a068416d-70cb-4e1d-3509-b0fab22dd895.png)



                                                             
```HTML:HTML
<a href="#">READ MORE</a>
```




   　



```CSS:CSS
/* リンクの基本スタイル設定 */
#pickup .pickupArticle a {
  position: relative;       /* 下線の位置調整のための基準点を設定 */
}

/* ホバー下線のスタイルとアニメーション設定 */
#pickup .pickupArticle a::after {
  position: absolute;       /* リンク要素に対して絶対位置で配置 */
  content: '';              /* 疑似要素の内容を空にして下線のみを表示 */  
  bottom: -5px;             /* リンクテキストの下部から5px下に下線を配置 */
  left: 0;                  /* 左端から配置を始める */
  width: 100%;              /* 下線の幅をリンク要素の幅と同じにする */
  height: 2px;              /* 下線の高さを2pxに設定 */
  background: #000;         /* 下線の色を黒に設定 */
  transition: transform 0.3s ease, opacity 0.3s ease; /* 変形と透明度の変化を0.3秒で滑らかに */
  transform: translateY(0); /* 下線の垂直方向の移動量を0に設定（初期位置） */
  opacity: 1;               /* 下線の透明度を1に設定（完全に不透明） */
}

/* リンクにホバーした際の下線の動きを定義 */
#pickup .pickupArticle a:hover::after {
  transform: translateY(5px); /* ホバー時に下線を5px下に移動させる */
  opacity: 0;                 /* ホバー時に下線を透明にすることで徐々に消えるように見せる */
}
```

■コードの捕捉
1、ransition: transform 0.3s ease, opacity 0.3s ease;

easeはトランジションのタイミング関数です。
この関数は、アニメーションの速度曲線を定義し、easeは開始時と終了時にゆっくりとし、中間では速く動くという、自然で滑らかな動きを実現します。

 transformとopacityという2つのプロパティに対してトランジション（遷移）効果が設定されています。
実践！UML！

# UMLとは
Unified Modeling Language（UML）とは、ソフトウェア開発およびシステム設計において使用される標準的な視覚的なモデリング言語です。  
UMLは、システムの概要を理解し、設計、実装、テストのフェーズでコミュニケーションを効果的に行うために使用されます。

実践的に言えばシステムを説明する際に使用できる図です。

# 体系
UMLに存在する図は以下の通りです。
（赤文字は抜粋して後述）
UML
　┣構造図
　┃ ┣<font color="Red">クラス図</font>
　┃ ┣オブジェクト図
　┃ ┣パッケージ図
　┃ ┣合成構造図
　┃ ┣コンポーネント図
　┃ ┗配置図
　┗振る舞い図
　　┣<font color="Red">ユースケース図</font>
　　┣<font color="Red">アクティビティ図</font>
　　┣ステートマシン図
　　┗相互作用図
　　　┣シーケンス図
　　　┣コミュニケーション図
　　　┣タイミング図
　　　┗相互作用概要図
   

# 実践的な図の利用について
上記のUML図の中から、より実践的に扱いやすいと感じているを図を抜粋して紹介いたします！
システム開発のシナリオを例に実践的に利用してみましょう。
（個人的な主観が含まれます！ご指摘や不足点があればご教示ください。）

### ユースケース図
ユースケース図とは：システムで求められる要件や機能を視覚的に表現するための図
使用タイミング：要件定義
使用例：  
　例1 > 新規システムまたは機能開発において要件の明確化
　例2 > 非エンジニア向けに機能の概要を説明するための資料

#### 図書館管理システムのユースケース図の例：
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1186227/d4a423b9-5df8-f20a-dfd1-800ec887b8cb.png)

| ユースケース名 | 登場人物 | 事前条件 |内容                           |
|--------------|----------|---------|--------------------|
| 本の検索 | 利用者 |  なし | 1. 本のタイトル入力 <br>2.「検索」ボタンを押下| 
| 本の予約 | 利用者 |  なし | 1. 本のタイトル入力 <br>2.「予約」ボタンを押下| 
| 本の貸出 | 利用者 |  なし | 1. 受付に本を渡す<br>2. 本を受け取る| 
| 本の返却 | 利用者 |  貸し出し中の本が存在する | 1. 受付に本を渡す| 
| 新しい本の登録 | 管理者 |  なし | 1. 書籍情報を入力<br> 2.「登録」ボタンを押下| 
| 本の削除 | 管理者 |  登録済みの本が存在する | 1. 本のタイトルを入力<br>2.「削除」ボタンを押下| 

※内容は想像で記載

かなり粗いユースケース図ですが上記によって利用者と要件のイメージが伝わると思います。
図解することで非エンジニアの方でも理解がしやすく、説明資料としても適切な内容となっています。


### アクティビティ図
アクティビティ図とは：処理の流れを視覚的に表現するための図
使用タイミング：基本設計
使用例：  
　例1 > ユースケースの詳細化
　例2 > 非エンジニア向けに機能や手順のフローを説明するための資料
 
#### 利用者が本を貸し出し、または予約するときの手順：
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1186227/54797e71-2ae8-b36f-87ea-c7677227b8de.png)

#### 本の検索機能のシステムフロー記載：
　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1186227/6227eaae-6f8d-f35c-e907-edcdcbe1dd93.png)

上記のようにアクティビティ図では機能の説明はもちろん、手順の詳細化も行うことができ、表現できることの幅が非常に広いです。
パーティションを適切に区切ることで役割を明確にすることでより分かりやすくなります。
 
### クラス図
クラス図とは：処理の流れを視覚的に表現するための図
使用タイミング：詳細設計、ソースコード理解
使用例：  
　例1 > 新規クラスを作成する際にクラス構造を考える
　例2 > 既存のソースコードのクラス図を確認し設計意図の理解
 
#### 図書館管理システムのクラス図の例：
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1186227/efab7a83-9c12-bcc8-7589-5f8d692482fc.png)

上記のようなクラス図を作成することで、拡張性や保守性に優れたクラス設計を行うことができます。

また、既存のソースコードのクラス図を確認することでクラス設計の意図を知ることができ、適切なプログラムの修正を行うことができるようになります。

クラス図を読むには少々学習が必要になるかとは思いますが、
SOLID原則やデザインパターンや理解をする際にはクラス図の理解が欠かせないため知っておいて損のない知識と考えています。

# 
実践に近い形でUMLの使用の例を挙げてみました。使用イメージは沸いたでしょうか。

特に上記で紹介した、
ユースケース図、アクティビティ図、クラス図が個人的なお気に入りです。

# UMLに関わる紹介
### plantuml
VSコードの拡張機能で、テキストベースの言語でUMLを作成することが可能。
本記事で扱ったUMLはすべてplantumlで記載いたしました。

テキストベースのため差分管理にも対応しており、chatGPTとのやり取りも可能です！
プロンプト例：図書館管理システムのユースケース図をplantumlで記述してください。
　　　　　　　テンプレートメソッドパターンのクラス図をplantumlで記述してください。

### AmaterasUML
UMLを描画するためのEclipseプラグイン。
既存のソースコードをクラス図に変換することが可能。

### UMTP認定試験
UMLの資格です。
L1～L4までレベルがあり、UMLについて体系的に学ぶことができます！
（私はまだL2までしか保持していませんが・・・）

# まとめ
UMLの概要と実践的な扱い方を紹介しました。
個人的な見解を含み、UMLの知識もまだ未熟な状態ではありますが何かの参考となればと思います。
業務のなかでも図解で説明をするタイミングが多くあるように感じています。その際にUMLの使用、または近い図の形で利用を検討してみてください！

chatGPTの登場でUMLの記述コストがぐっと下がりました。
UMLでのコミュニケーションがもっと増えてもいいのになあ・・・と密かに思っております。
windowsにインストールされているドライバの一覧をバージョン付きで出力する

# いきなり結論

powershellで以下を実行するだけ
```
Get-WmiObject Win32_PnPSignedDriver | Select DeviceName,DriverVersion
```

## 実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325308/cb8e40f0-890f-8ffd-7710-f69713cd1897.png)


## 参考
https://tera1707.com/entry/2022/03/04/234647
https://shellgeek.com/get-drivers-version-using-powershell/
PowerAutomate: New Designer での不具合とその対処: 添付したファイルが開けないのは、二重引用符が邪魔している

# 背景

テキストファイル以外を添付すると、添付ファイルが正常に添付できない！という相談があったので、その解決方法の共有

# 概要

以下問題が生じていた

- New Designer で、send-an-email-(v2) で添付ファイルを送付しようとしたら、Excelファイルが壊れている。
    - テキストファイルは問題ない

https://learn.microsoft.com/ja-jp/connectors/office365/#send-an-email-(v2)

# 対処法

1. Old Designerで実装する

1. New Designerでなら、Advanced Mode で、content を設定することで、二重引用符が付かないようにする。

![2024-02-06_17h22_49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645569/ed1392c5-6456-0e43-757a-9445a75d0269.png)


# 原因

- New Designer だと、ContentBytes の設定欄が、以前とは違って二重引用符が付くようになっている。

![2024-02-06_16h22_34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1645569/de83cb5b-2276-9030-8fb2-8438574398b5.png)

ということで、先に記述した対処法が有効になる。

# あとがき

New Designer の改善ってあまり進んでない気がするんだけど、Copilot に注力しているんですかね？
【初心者向け】GithubでPull Requestをdevelopブランチにマージ使用としたら、間違えてmainにマージしてしまった話

# 状況

当方、入社歴1ヶ月の駆け出しエンジニア。
練習として既存のプロジェクトを改修するよう頼まれ、リモート上でmain(default)からdevelopブランチを作成してclone→ローカルにfetch

チームメンバー2人＋自分（PM）で着手。

メンバーの内の一人がissueベースのブランチを切って作業していた。

その後該当メンバーが変更を加えcommit＆push(同時にpull request)→自分が確認→developにマージという流れだった。

Pull Requestが送られてきて、軽微な修正だったため即座にマージしたところ、本来ならばissueのなぜかマージ先がmainに、、、

?????????????????

よく確認すると、メンバーがローカルでdevelopを独自作成して、そこからissueベースのブランチを作成していたのだ。

すなわち、以下のような状況だった。
リモート：
main(default)→develop

メンバーのローカル：
main(default)→develop（リモートのdevelopとは別）

名前は同じdevelopでも、ローカルでmainから切り離したdevelopなら話は別。

リモートから見たらただの１つのmainから切り離されたブランチだったので、そのままmainにマージされてしまった。

# すべきだったこと

そもそも、mainからdevelopブランチを切ってそこで作業するのであれば、developブランチをdefaultブランチに変更しておけばよかった。（クローンのときに、自動的にリモートのdefaultブランチがローカルに作られる。）

すでにmainでクローンしてしまっていた場合は、ローカルからdevelopブランチをフェッチ（リモートをローカルに同期させる）してからissueブランチを切るべきだった。


# 最後に
Github難しいですよね。
一緒に頑張りましょう。
シェルスクリプトと SQLite3 で Nostr のリレーサーバ(っぽいの) (4) おまけ。実践

## wss 化する
Caddy2 サーバで簡単に出来る。ここ( https://github.com/caddyserver/caddy )からバイナリ( Release をクリックして自分のマシンに応じたもの)をDLして解凍。Caddyfile というファイルを作って
```
自分のドメイン {
	handle_path / {
			reverse_proxy localhost:wbsocketdで指定するポート
		}
(その他アイコンなどのパスを指定したければそうする)
}
```
で ssl 化されるので、wss 化される。
```
sudo ./caddy run
websocketd --port=ポート bash out.sh
```
として
wss://自分のドメイン
でアクセスできるようになる。

## 実際にアカウントを作ってみるには
NostrTool ( https://nostrtool.com/ ) で Generate new key をクリックし、秘密鍵/公開鍵を nsec/npub hex ともにコピーペーストなどで保存する。
ブラウザに nos2x 拡張を入れて、option を選び、 Private key に秘密鍵を入力しておく。

## 自分のデータを 自分のサーバに登録する。
NostrCli が便利なので勧める。
```bash
git clone https://github.com/FreeFromJP/NostrCli
cd NostrCli/
npm install
sudo npm link
```
で入る。ただしまだ設定が何もないので
```
touch .env
```
でファイルを作り、それに
```
DEFAULT_RELAYS=wss://自分のサーバ
PRIVATE_KEY=自分の秘密鍵
```
を記し、
```
nostr
```
とコマンドを入れると
```
❯ key 
  decode 
  encode 
  search_by_ids 
  sample 
  DMLike 
  publish 
(Move up and down to reveal more choices)
```
とかそんな感じで表示されるので、publishを選び
```
? Enter event kind:
```
に 0 を入れ、content に
{"picture":"Caddyfileにアイコンのパスを入れてたらそのURL","name":"適当な名前","display_name":"適当な名前","about":"適当な自己紹介"}
を入れエンターを打ち続ければ、自分のサーバの test.db と jsonsディレクトリに自分のアカウントの情報データが入るはずである。これでアカウントが出来る。
次に上と同様に publish を選び、kind で 3 、content は空、
? Would you like to add tags? (y/N)
で y を打ち
? Enter a tag:
に
p,自分の公開鍵
を入れてエンターで自分をフォローするデータが自分のサーバの test.db と jsonsディレクトリに入るはずである。他にも(例えば既に作っている自分の別のアカウントなども)フォローしておきたいならば、それも続けて同様に加えればよい。
(＊ただし
? Enter a tag:
p,自分の公開鍵
? Would you like to add another tag? (y/N)
y
p,他にフォローしたいアカウントの公開鍵
……
と、一続きで入れなければ複数の kind 3 のデータ・ファイルが入ってしまう。大体のクライアントでは最新の kind 3 を見てフォローを判断するようだ
)


おそらくこれで Rabbit ( https://rabbit.syusui.net/ ) などにブラウザでアクセスし、リレーに自分のリレーを加えれば、プロフィールに自分がいるはずである。あとはブラウザから投稿すれば自分のリレーサーバの test.db と jsons ディレクトリにデータとファイルが適切に入り、多少のタイムラグはあるが、ブラウザに反映されると思う。


## 難点
何故か websocketd に ERROR が表示される。ただあまり実践には問題ない様ではある。