OmniAICreator/Qiita-1.07M huggingface dataset:
StreamlitCallbackHandlerがLangGraphなどMulti-Threadで使えない

## Example code

```py
import streamlit as st
from langchain.callbacks.streamlit import StreamlitCallbackHandler

from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from langgraph.graph import END, MessageGraph


handler = StreamlitCallbackHandler(st.container())

model = ChatOpenAI(temperature=0)

graph = MessageGraph()

graph.add_node("oracle", model)
graph.add_edge("oracle", END)

graph.set_entry_point("oracle")

runnable = graph.compile()

runnable.invoke(HumanMessage("What is 1 + 1?"), {"callbacks": [handler]})
```

## Error

```
2024-05-24 11:50:06.661 Thread 'ThreadPoolExecutor-1_0': missing ScriptRunContext
Error in StreamlitCallbackHandler.on_llm_start callback: NoSessionContext()
Error in StreamlitCallbackHandler.on_llm_end callback: RuntimeError('Current LLMThought is unexpectedly None!')
```

## Workaround?

https://github.com/streamlit/streamlit/issues/1326#issuecomment-2103233217

試してみたが、一部動いたケースもあったが、全てのケースに対してカバーはできなそうだった。


## Related Issues

1. [[StreamlitCallbackHandler] - Not compatible with LangGraph #101](https://github.com/langchain-ai/langgraph/issues/101) in [langchain-ai/langgraph](https://github.com/langchain-ai/langgraph)
1. [Feature Request: Add support for multi-threading/multi-processing in Streamlit #8490](https://github.com/streamlit/streamlit/issues/8490) in [streamlit/streamlit](https://github.com/streamlit/streamlit)
1. [Stream Chat LLM Token By Token is not working #78](https://github.com/langchain-ai/langgraph/issues/78)
テキストメッセージの画像ジェネレータをつくりました

https://github.com/Kohki-Takatama/message_image_generator

<p class="codepen" data-height="483.6015625" data-default-tab="result" data-slug-hash="Rwmmxra" data-pen-title="Untitled" data-user="Kohki-Takatama" style="height: 483.6015625px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;">
  <span>See the Pen <a href="https://codepen.io/Kohki-Takatama/pen/Rwmmxra">
  Untitled</a> by Kohki_Takatama (<a href="https://codepen.io/Kohki-Takatama">@Kohki-Takatama</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://cpwebassets.codepen.io/assets/embed/ei.js"></script>

※左のメッセージは<font color="red">画像</font>です。

| ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457770/6130d044-e946-8948-508e-cf1a7cb1a20e.png) | ![スクリーンショット 2024-06-29 18.50.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457770/bb1ecd81-367d-0637-a616-7401dfd8dbf3.png) |
|:-:|:-:|




# はじめに

メッセージを画像として（動的に）生成する機能です。

Xシェアとリンクカードでの仕様を想定しています。

# ディレクトリ構造

https://github.com/Kohki-Takatama/message_image_generator

```
.
├── canvas.js
├── index.html
├── main.js
└── style.css
```

`canvas.js`は見やすさのために分けています。
実質 `index.html` / `main.js` / `style.css` の３ファイルです。

# 使用技術
| 技術 | 補足など |
|:-:|:-:|
| Canvas API  | HTML/JSを使用しグラフィックを描画します（詳しくは [Canvas API](https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API)）  |
| Konva.js  | Canvas APIのライブラリです  |

- バニラのCanvas APIは使いにくいため、Konva.jsを使用しています（CDN）
使い方：`.html`ファイルに以下の記述をするだけ
`<script src="https://unpkg.com/konva@9/konva.min.js"></script>`
- Konva.jsでできることの例
    - 丸角のボックスを簡単に描画できる
        ```js
        // 丸角の白いボックスの描画
        const textbox = new Konva.Rect({
        x: textbox_mx,
        y: textbox_mt,
        width: stage.width() - textbox_mx * 2,
        height: textbox_height,
        fill: 'white',
        cornerRadius: 10,
        });
        layer.add(textbox);
        ```
    - 中央揃えを簡単に実装できる
        ```js
        align: 'center',
        verticalAlign: 'middle',
        ```
    - 自動改行に対応
- 画像生成に使用できる [html2canvas](https://github.com/niklasvh/html2canvas) は以下の点から採用を見送りました
    - ブラウザによって表示が変わってしまう可能性がある（参考：[html2canvasを試す](https://qiita.com/7shi/items/ba7089e864fefac69808)）
    - 公式で「本番環境に使用するのは非推奨」と書かれている（開発途中のため）

# 機能紹介
|![動的入力2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457770/5b2808f3-5cb8-8a05-296b-3e193f95e0e8.gif)  |  ![画像保存.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457770/ac5149d9-2fb6-8289-82bd-3f02a4ac503c.gif)|
|:-:|:-:|
| 動的に画像を生成  | 画像として保存が可能  |
|   |   |

- `textarea`へ`input`があるたびに画像生成をすることで、動的生成を実現しています
    ```js
    const input = document.getElementById('input');
    
    input.addEventListener('input', () => {
      generate_image(input.value.trim());
    });
    ```

# 今後の展望
- Xシェア用 画像のアップロード（クラウド？）
    - ユーザー操作なしで画像をツイートするには、画像を事前にサーバーへアップロードしなければいけない
    - リンクカードに画像を設定するのも同様。さらに、リンクカードごとにページを分ける必要がある
    - 現状、画像を保存しなくても逐次生成できる。そのメリットを消すことになってしまうのが難点

# 参考

https://qiita.com/kira_puka/items/1720d3cc7fc887836dae

https://konvajs.org/docs/shapes/Text.html
動画を一定フレーム毎に指定した枚数分だけ画像として保存するPythonPGM

<h3>動画を一定フレーム毎に指定した枚数分だけ画像として保存するPythonPGM</h3>

動画を一定フレーム毎に指定した枚数分だけ画像として保存するPGMをPythonで作成しました.

2024.07.01:動画の総フレーム数を自動取得しそれを超えない分だけ画像として保存するようにPGMを変更しました.

<hr>
ソースコード

```python
import cv2
import time

if __name__ == '__main__':
    cap = cv2.VideoCapture('C:\動画ファイルのパス')

    if (cap.isOpened()== False):
        print("File Open Error") 

    fps = int(cap.get(cv2.CAP_PROP_FPS))

    totalframecount = int(cap.get(cv2.CAP_PROP_FRAME_COUNT))

    print("このビデオの合計フレーム数は：" , totalframecount)

    FPS = 1/(fps*1)
    #読みだす最初のフレーム数
    startIdx = 600
    Idx = startIdx
    #何フレーム刻みで画像として保存するのか
    diff1 = 90
    
    #何枚分画像として保存するか
    #cutCount = 510;
    #cutMax =516
    
    cutCount = 1;
    #totalframecount = 1000
    cap.set(cv2.CAP_PROP_POS_FRAMES, startIdx)
    while(Idx < totalframecount):
        ret, frame2 = cap.read()
        if ret == True:
            frame = cv2.resize(frame2 , (int(960/2), int(540/2)))

            cv2.imshow("Video", frame)
            if cv2.waitKey(1) & 0xFF == ord('q'): 
                break
            
            Idx = Idx + diff1    
            cap.set(cv2.CAP_PROP_POS_FRAMES, Idx)
            str1 = '.\\画像を保存するフォルダのパス\\'
            str1 = str1  + str(cutCount) + '.jpg'
            print(str1)
            cv2.imwrite(str1, frame)
            cutCount = cutCount + 1
            time.sleep(3)
        else:
            print("Cap Read Error")

    cap.release()

    cv2.destroyAllWindows()
```


<hr>
参考にしたサイト

<a href="https://elsammit-beginnerblg.hatenablog.com/entry/2021/05/03/225730">OpenCVで特定時間から動画再生や早送り・巻き戻し - Elsaの技術日記(徒然なるままに)</a><br>

<a href="https://note.nkmk.me/python-opencv-video-to-still-image/">Python, OpenCVで動画ファイルからフレームを切り出して保存 | note.nkmk.me</a><br>

<a href="https://laboratory.kazuuu.net/find-the-total-number-of-video-frames-in-python-using-opencv/">OpenCVを使用してPythonでビデオのフレームの総数を見つける | Men of Letters（メン・オブ・レターズ） – 論理的思考/業務改善/プログラミング</a><br>
SnowFlakeの一時テーブルと仮テーブルの違い　SQL有り

# 一時テーブルと仮テーブルの違い

主な違いとして、持続性とアクセスの違いです

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526914/eada6b49-9fbc-1726-554a-7bb7e9cd1cfd.png)

## 持続性とアクセス

TEMPORARY(一時テーブル)については、セッション中のユーザーのみに対してアクセスができるので
共有することができません。

対して、TRANSIENT（仮テーブル）については、他ユーザーに対して共有することができます。

また、TEMPORARYについてはセッションが終われば削除されますが
TRANSIENTについては明示的に削除しないと残り続けます。


## Fail-safe期間

こちらについては、非仮テーブルのため
どちらについてもFail-safe期間は0になります。

## 命名競合について

こちらについては、結論から言うと非仮テーブルと同じテーブル名にすることは可能です

ただし、セッションでは、同じスキーマ内の同じ名前の他のテーブルよりも仮テーブルが優先されることに注意してください。これにより、特に仮テーブルと非仮テーブルの両方で DDL を実行する場合、潜在的な競合と予期しない動作が発生する可能性があります。

## 一時テーブルはETLなどには使いたいテーブル

TRANSIENT（仮テーブル）は、セッション外でもデータが残り続けると言いました。

ETLそのもののデータはセッション中のみ欲しいので、この場合は
TEMPOLALYテーブルを使うことをお勧めします。

あとは、MERGE INTOなどで仮テーブルと本番テーブルを結合したりするのにも適しているかなと思います。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2526914/4e7a6592-2698-7c96-799c-e4d505548ff9.png)


実際のSQLはこんな感じになります。

```SQL
-- LIKEを使って、本番テーブルとスキーマを一致させる
CREATE OR REPLACE TEMPORARY TABLE staging_table_SNOW LIKE SNOW_TRANSACTIONS;

-- ステージングテーブルにデータをロード
COPY INTO staging_table_SNOW
FROM @snow_flake_stock_price_transactions
FILE_FORMAT=(TYPE = CSV FIELD_DELIMITER=',',SKIP_HEADER=1)
PATTERN='.*\.csv';

/* マージコマンドを使用して本番テーブルにデータを追加または更新
 DATETIMEがキー列で重複がないので、一致してなければ追加する
 そうすることで、同一のデータが入らなくなる。　*/
MERGE INTO SNOW_TRANSACTIONS AS target
USING staging_table_SNOW AS source
ON target.DATETIME = source.DATETIME
WHEN NOT MATCHED THEN INSERT (DATETIME,OPEN,HIGH,LOW,CLOSE,VOLUME,DIVIDENDS,STOCK_SPLITS)
VALUES (source.DATETIME,source.OPEN,source.HIGH,source.LOW,source.CLOSE,source.VOLUME,source.DIVIDENDS,source.STOCK_SPLITS);

```

以上です。参考になれば幸いです。
ITパスポート 企画・要件定義プロセス

・ソフトウェアライフサイクルプロセス（SLCP）
ソフトウェアの企画・要件定義・開発・運用・保守までの一連の活動

・共通フレーム
共通の物差しとなるガイドライン
システムの開発や保守の各工程の作業項目を含む

・企画プロセス
システム全体の構想や計画を策定するプロセス
導入するシステムは、経営のニーズに基づいたものであることが重要
・システム化構想
経営上のニーズや課題を確認し、業務と情報システムの将来像を明確にした上で、全体最適化を図る
・システム化計画
システム化の基本方針を策定する。管理体制や開発スケジュール、概算コスト、費用対効果などを検討する
・費用対効果（ROI）
投資額に対する利益の割合を表した指標
ROI（%）＝利益÷投資額x100

・要件定義プロセス
情報システムの機能や性能を明確にするプロセス
導入するシステムは、利用者を含めた利害関係者のニーズに基づいたものであることが重要
業務パッケージの標準機能だけでは実現できないので、追加開発が必要なシステム機能の範囲を決定する
・業務要件定義
日々の業務に必要な要件。業務手順、関係する組織の責任や権限など
・システム要件
・機能要件定義
システムに必要な機能。必要なデータの項目、処理内容、ユーザインタフェースなど
・非機能要件定義
システムに必要な性能。稼働時間、応答時間、セキュリティなど

・調達
・RFI
ベンダに対して、情報提供を依頼する文書
・RFP
ベンダに対して、提案書の提出を依頼する文書
システム開発が終わると、「システムが完成したので納めます」という意味の納品書や、「注文通りのものを確かに受け取りました」という意味の検収書、そして請求書が交わされる

・NDA
秘密保持契約

・重み付け評価

・一般競争入札
入札情報を広報やホームページなどに公示した上で、一定の参加資格を有する参加者から、最も安い有利な条件を提示した入札者と契約する方式
pythonコマンドは使えるが、python3コマンドが使えない

## はじめに
pythonを使って共同で開発をしていたところ、以下のようなsubprocessで`python3`コマンドを使って非同期でファイル実行するコードが動かなかった。
```python
subprocess.run(["python3", os.path.join(script_dir, "hogehoge.py")], check=True)
```

調べてみたところ、私の環境で`python3`コマンドではなく、`python`コマンドでしかpythonを動かすことができない事が原因でした。

一人で開発する分にはコードのpython3という記述をpythonに変えてやるだけで良いのですが、共同開発なのでむやみに変更するわけにもいきません。

そこで今回は`python3`コマンドが動かない原因と解決策を調べたのでまとめいきます

## python3が動かない原因
基本、windowsではpythonがpython.exeがインストールされていることが一般的です。
一方でLinuxやMac OSのようなUnix系システムではpython2.Xとpython3.Xコマンドが共存することが多いため、`python`はpython2.Xを、`python3`はpython3.Xを指すよう設定されていることが多いです。

そのため、windows環境では`python`コマンドが、Mac,Linux環境では`python3`コマンドが使われ、これが今回のエラーの原因のようです。

## 解決策
python3コマンドを実行したとき、python.exeを叩くよう設定します。
管理者権限を持ったコマンドプロンプトで以下のコマンドを実行します。
```cmd
where python
```

ここで出てきたpython.exeのパスをコピー
そして

```cmd
cd C:\Windows\System32
mklink python3.exe C:\Users\{ユーザーネーム}\AppData\Local\Programs\Python\Python310\python.exe
```

これでpython3コマンドがpython.exeに紐づけられ、`python3`コマンドが使えるようになります。
良いインターフェースにするには 〜インタラクションコストを低減しよう〜

# はじめに

良いインターフェース、インタラクションコスト（操作を通してユーザーにかかる負荷のこと）を抑えることが大切です。
改善例を載せながら、良いインターフェースの理解を深めましょう！

# 良いインターフェースにするためには…
画面を操作して自身の目標を達成する中で、受ける負荷やエネルギー（ストレス）が少ないほど、そのサービスは「使いやすい」と感じます。

そのため、**インタラクションコストをいかに減らすことができるかが重要**となります。

# インタラクションコストを低減するために

インタラクションコストを低減するためには以下の 3 つのことを意識しましょう。

1. **シンプルさ**
余計な解釈がなく、明快なこと
2. **一貫性**
規則性によって予測がつくこと
3. **共通概念**
誰もが知っている文化的ルールに従うこと


## シンプルさ

意図が明快であるため理解しやすいです。（単純とは少し意味合いが違います。）
シンプルであればあるほど、何が起こっているか、何が起こるのかを即座に理解でき、次にどうするかスムーズに決定することができます。

意識例としては以下のようなものです。

- 明快な文言にすること
- 分かりやすいコンポーネントを使用すること
- 幅を揃え、グルーピングすること

### 実例

実例を見ていきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2369683/863d319f-0d90-f10d-2b3a-c68a34c4bdd3.png)
※ ↑ 補足：環境名、スケジュール名は入力必須項目

こちらの入力フォームを見て、みなさんは何を感じるでしょうか。

・・・

・・・

さて、僕が感じる違和感を以下に記載しました。

#### 違和感

- Placeholder の文言とラベル名が同じ
- フォーム幅がまばら
- 必須項目であるフォームに必須マークが無い

#### 解決策

これらの違和感を改善するために、以下のような修正を加えましょう。
これによりシンプルさ（明快さ）が加わります。

- **Placeholder の文言に入力例を設定する！**
- **フォーム幅をそろえる！**
- **必須項目には必須マークを表示する！**

改善された画面は以下になります。スッキリしたのがわかるでしょうか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2369683/46cc8408-22ea-27b9-b6da-816055da538a.png)

## 一貫性

一貫性が保たれたサイトは、ユーザーが操作を予測しやすくなります。
それにより、ユーザーの学習コストが抑えられる効果もあります。


意識例としては以下のようなものです。

- ボタン、アイコン、文言の統一
- 操作アクションの統一

### 実例
例えば、同じシステムで更新ボタンがいくつかあったとします。
そのボタンそれぞれの色が異なっていたり、アイコンが異なっていたり、、、画面によってまばらな設定だとします。
すると、ユーザーは混乱し、操作を予測しづらくなります。

またはダイアログを閉じたいときに枠外をクリックしたら閉じるのか、閉じないのかが統一されていないと、ユーザーはストレスを感じます。

同じシステムでも画面によって開発者は異なることは多いので、開発後に一度すべての画面の文言や操作感を確認して、統一するように心がけましょう。

## 共通概念

一般的に認知されている標準的なルールの事です。
共通概念に基づいた UI にすることで、解釈にかかる負担を小さくする効果があります。

意識例としては、以下のようなものです。

- 下線テキストはリンク
- 色のイメージや意味合い（赤は危険、黄色は注意、など）

### 実例 1

実例を見ていきましょう。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2369683/a12e821e-2bd3-ca8e-2e71-6a438b53b7eb.png)

こちらのボタンを見て、みなさんは何を感じるでしょうか。

・・・

・・・

さて、僕が感じる違和感を以下に記載しました。


#### 違和感

- 黄色は ”注意” のイメージ
- 実行するのに注意が必要なのか、、？


### 実例 2

もう一つ、実例を見ていきましょう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2369683/45e216a7-8ddd-ad9c-1a08-dfa6636a4520.png)

こちらのボタンを見て、みなさんは何を感じるでしょうか。

・・・

・・・

さて、僕が感じる違和感を以下に記載しました。


#### 違和感

- グレーや半透明は ”非活性” のイメージ
- 非活性？今は押せないのか？？（実際は押せる）

#### 解決策

これらの違和感を改善するために、以下のような修正を加えましょう。
この共通概念に基づいたボタン色にすることで、ユーザーは解釈に負担をかけることなく操作することができるでしょう。

- 実行と非公開ボタンは青色にした

 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2369683/7d6741d8-1e5a-97d5-7592-d5c7a74b0cef.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2369683/c5d4b05b-9201-d0ac-694e-7afe4a1ed5ac.png)


# まとめ
インタラクションコスト、とかいう言葉は実際はどうでもいいのですが、要は
**操作するときに負荷が少ないものが「使いやすい」と感じる→良いインターフェースにつながる**
ということです。

ただ、設計者・開発者であればその多面を見慣れているので、負荷を感じずに操作できてしまい気づきと思います。
**もし知識が無い状態で自分がこの画面を使うとしたらどう思うか**、を意識するのが大事だと思います。
難しい場合は、開発に関与していない仕事仲間に触ってもらうのも良いかもしれません。

# 参考
https://fastcoding.jp/blog/all/direction/interactioncost/
iOSアプリ開発中のコンフリクト対応

# はじめに
Gitを使用して開発していると、どうしてもコンフリクトが発生する可能性がある。
この辺りのコンフリクト解消手順について記載していく。
特にiOSアプリ開発なのに、Android Studioを使用してコンフリクト解消する方法も最後の方で記載するので、面白いかも。

前提とする環境は以下の通り
・MAC OS: 14.5 (Sonoma)
・Xcode：15.4
・git version 2.39.3 (Apple Git-146)
・Source tree：4.2.8(265)
・Android Studio：Koala | 2024.1.1

# コンフリクト発生時の確認の仕方
例を見た方が手っ取り早いと思うので、コンフリクトを意図的に発生させてみた。
コードはコンフリクトが発生させれば何でも良いのスタンスなので、適当です。
![スクリーンショット 2024-06-29 19.39.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3782130/9f73c7d7-ba03-44a2-52e9-c56a2adf9b1a.png)

・<<<<<<< HEAD
　現在のブランチの内容
 ・>>>>>>> マージ先の他ブランチ名
 　マージ先の他ブランチ名の内容

上記のように、<<<<<<<　とか　HEAD　とかのように検索するとコンフリクト箇所が分かると思います。

# コンフリクト対応の方法
### 1.手作業で解消
アナログなようだが一つの方法ではある。
GitHubの公式手順を確認したが、手作業でやる手順について記載されていたので以下参照。
　<a href="https://docs.github.com/ja/pull-requests/collaborating-with-pull-requests/addressing-merge-conflicts/resolving-a-merge-conflict-using-the-command-line">コマンド ラインを使用してマージ コンフリクトを解決する</a>

コンフリクト発生時はビルドエラーが発生する状態のため、
<<<<<<< HEAD　と　>>>>>>> マージ先の他ブランチ名　の部分を確認し、適切に手作業でマージする必要がある。

### 2.Source Treeを使って解消
右クリック > 競合を解決 > 外部マージツールを起動
![スクリーンショット 2024-06-29 20.24.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3782130/69fe8817-f230-94c6-4a33-c5defdbb1276.png)

おそらくデフォルトのマージツールが起動するはず

この外部マージツールをカスタマイズすることで任意のツールを使用することができる。
しかし、WindowsであるならFile Mergeとか優秀な無料ツールが転がっているが、
Macだと有料版も結構多いし無料版のアプリは微妙なやつ多し。。

無料版だと、Xcodeに付属しているFileMergeが可もなく不可もなく、

### 3.Android Studioを使って解消
①iOSアプリのビルド環境一式（何なら.gitがある先頭ディレクトリ）をAndroid Studioで開く

②Git > Resolbe Conflicts...
![スクリーンショット 2024-06-29 19.41.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3782130/606ae0ec-6fc3-8007-2c5a-18a0610a849c.png)

③Conflicts画面でコンフリクト対象のファイル一覧が表示されるので、マージしたファイルを選択し"Merge"ボタンをクリック
![スクリーンショット 2024-06-29 19.42.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3782130/c62f1694-65f6-4306-ebf6-560854fc5405.png)

④３面で比較しながらマージすることができる
・左：現在のブランチの内容
・真ん中：マージ結果
・右：マージ先の他ブランチの内容
![スクリーンショット 2024-06-29 19.43.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3782130/172df2e9-e73b-0140-4a86-eb1332659a50.png)

⑤マージ
・現在のブランチの内容を取り込む場合は　>>　、もしくは　マージ先の他ブランチの内容を取り込む場合は　<<　をクリックし、マージする
・（採用しない場合はバツボタンを押下）
・真ん中のマージ結果は普通に編集できるので、左右の内容を目視で確認しながらマージすることも可能
![スクリーンショット 2024-06-29 19.43.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3782130/86f0e3f7-cb7e-a288-2a8b-f8220000f48e.png)

以上！
[Siv3D] 市松模様と排他的論理和 

# この記事について

- この記事は、プログラミング初心者である筆者がSiv3Dのチュートリアルを進めている時にふと気づいたことについて書いた記事です
    - チュートリアルで紹介されていたある実装について、ちょっとした別解を思いついた！と言う感じの記事です
    - ついでに、Siv3Dというツールの紹介もしてみようという記事でもあります

# Siv3Dとは？

- [公式サイト](https://siv3d.github.io/ja-jp/)
    - 曰く、「創造のための C++ フレームワーク」
    - オープンソース
- C++を使ってゲーム開発ができるツールで、オープンソースのゲームエンジンのようなものだと筆者は認識しています
    - ゲーム以外の用途にも使えそうです

## なぜSiv3Dを触り始めたのか？

- 筆者がいずれ(趣味で)ゲーム開発をしたいなぁとぼんやり考えているからです
- UnityやUnreal Engineで作ることも考えましたが、以下の点からSiv3Dに行きつきました
    - UnityやUnreal Engineは大企業のゲーム開発にも用いられるツールであるため、良くも悪くも高機能過ぎる。初心者がちょっと触ってみるには少し重い
    - そんな中何か良いゲームエンジンがないかと探しあぐねていた時に、たまたま見つけたのがSiv3D。環境設定さえ済ませてしまえば、ちょこっとC++のコードを書くだけですぐにゲーム(未満のプログラム)を動かせるという点が、プログラミング初心者には魅力的でした
- 上記の理由に加えて、ゲーム開発のついでにC++の勉強をしたいという意図もありました。なので、開発言語がC++であることは必須であり、その点でもSiv3Dは自分が求めている条件に合致するものでした

## Siv3Dのチュートリアル

- Siv3Dは[公式のチュートリアル](https://siv3d.github.io/ja-jp/tutorial/hello/)が充実しています
    - プログラミング初心者でもC++の基礎的なところさえ抑えていればそこまで読み進めるのに苦労はしない
    - 読みながらゲームプログラミングに特有の考え方を学ぶことができて、読み物としても面白い
    - ライブラリ等の説明用に置いてあるサンプルコードも豊富で、実際にコードを動かしながら挙動を理解することができる(楽しい)
- ※筆者はこの公式のチュートリアルを読み進めている途中です。今はチュートリアル2がそろそろ終わるところです
    - チュートリアルは全部で4つあります。つまり筆者の進度はざっくり全体の半分くらい

# 本題

- 今回の記事では、Siv3Dのチュートリアルで触れられているお題について、ちょっとした別解を思いついたのでご紹介させて頂きます

## 市松模様を描くというお題

- 今回紹介するお題は、チュートリアル1の「9.模様を描く」の中にある、[9.3少し複雑にする](https://siv3d.github.io/ja-jp/tutorial/pattern/#93-%E5%B0%91%E3%81%97%E8%A4%87%E9%9B%91%E3%81%AB%E3%81%99%E3%82%8B)という項目です
- お題は至ってシンプル。以下のように、水玉模様に少し複雑なパターンを入れてみようというものです。要は市松模様(鬼滅の炭治郎のアレ)に似たパターンを実装しろという話です

![3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2726845/15b0b9de-758e-223e-93c3-edbee112d2ce.png)
(↑公式チュートリアルから引用したスクショ)

- 1つ前の項目 「9.2二重ループで円を並べる」にて円をタイル状に並べる方法は既に学んでいたので、コードを少しいじれば「市松模様」を実装できるのでは……？と思い至りました
- そのため、あえて実装例のコードを見ずに自力で実装してみました

## 思考過程

実装に至るまでの思考過程をメモしていたので、以下にそのメモをほぼそのまま貼り付けます。

- i, j の偶奇に応じて円の大きさを変えれば良い
- お手本通りにするなら……？

二重ループ
x座標：i
y座標：j

i,j 奇数 → 小円
i偶数、j奇数→大円
j偶数、i奇数→大円
i,j偶数→小円

論理記号で言うと 

0 0 0
0 1 1
1 0 1
1 1 0

0が奇数、小円

このパターンは論理記号で言うとXORなのでは？
→ググったら、C++では `^`記号でXORを表現できるらしい

```c++
# include <Siv3D.hpp>

void Main()
{
	Scene::SetBackground(Palette::White);

	while (System::Update())
	{
		for (int j=0; j < 5; j++){			
			for (int32 i = 0; i < 5; i++){
				if (i % 2 == 0 ^ j % 2 ==0) {
					Circle{100*i, 100*j, 30}.draw(Palette::Skyblue);
				} else {
					Circle{100*i, 100*j, 5}.draw(Palette::Skyblue);
				}
			}
		}
	}
}
```

- ↑で実装できた！嬉しい！
- 市松模様というパターンと、排他的論理和が紐つくのは何か面白い

## 余談

- Siv3D公式チュートリアルの答えを見たら、排他的論理和ではなくi+jの和の偶奇で分岐させていた
    - どちらの実装でも問題ない気はするが、排他的論理和で実装する方がロジックをより直接的に表現できているような気がする……？
    - デザインパターンとかも勉強して、どちらの実装の方が望ましいか判断できるようになりたいな(この程度の規模のコードならマジでどちらでもいい気はするが)

# おわりに

以上です。
結局のところこの記事で言いたかったのは、「市松模様というパターンと、排他的論理和が紐つくのは何か面白い」というだけのことでした。

## Siv3Dについて
Siv3Dはゲーム開発抜きにしても面白いツール(筆者主観)ですので、C++に触れたことのある方は是非Siv3Dも触ってみてはいかがでしょうか。公式チュートリアルのサンプルコードを適当に動かしたり弄ったりするだけでも結構面白いですよ。
最高のエンジニアになるために1：ポケモン事件から学んでいた

## ★はじめに★
前回、[自分が考える最高のエンジニアライフプランについて紹介しよう](https://qiita.com/official-events/23e94ba994880fab50be)のテーマで少し記事を書きましたが、もっと深ぼっていきたいし、これからの転職活動のためになるかも、と思い若干シリーズ化を目論んでいるわーちゃんと申します。

https://qiita.com/wa-chan222/items/0b0d73549bbca281d06e

今回は**仕事の中だけじゃない、幼いころの出来事から自分を客観的に見てみよう**という観点から掘り下げていきたいと思います。


## ★幼いころのあの出来事、後から考えると学んでいたんだなぁ★
皆さん、小さいころ好きだったゲームはありますか？
私のころはゲームボーイがありカービィーやマリオ、ポケモンが人気だったのかな？

ポケモンで一番記憶に残っている強烈の思い出があります。

私が小学生のころ世界的有名作「ポケモン」を楽しんでいました。
マスターボールをゲットし、これから伝説のポケモンを捕まえに行くというのに…何を血迷ったのか1個しかない大切なボールを「メタモン」~~なんか~~に使ってしまい…。
そこで一度電源を消せばよかったものの、またまた何を血迷ったのか、セーブをしてしまいました:scream:

当時は、悔しくてたまりませんでした。絶望的でした。泣き崩れました。
自分が犯した過ちだとわかりつつも、どうしようもなく、どうすればいいのかもわからなく、プレイすると思い出してしまい、プレイするのをやめました。

※ポケモンシリーズにおいて、毎回四天王直前でなぜかプレイできなくなり、一度も全クリしたことはありません。

## ★学んだこと★
さて、この話から何を学べるというのか不思議に思う方もいらっしゃるかと思います。
いくつか私なりに学んだことを挙げていきます
- やばいことが起こってもそのまま突っ走らない
- 自分を過信しすぎて「なんとかなる！」で進めない
- 問題が起こったところで一度手を止め、大きな深呼吸。気持ちを落ち着かせる

実際この嘘みたいな強烈なポケモン事件があったからこそ、「これ絶対やばい！」という問題が起こった時、一度大きな深呼吸をして気持ちを落ち着かせ、周りの人に報告・相談する事を欠かしたことはありません。

## ★アピールするとしたらこんな感じ★
<details><summary>冷静な問題解決能力</summary>

「重大な問題が発生したときでも、冷静に対処することができるようになりました。具体的には、過去の経験から一度立ち止まり状況を見直すことの重要性を学びました。」
</details>

<details><summary>自己反省と学習力</summary>

「失敗から学び、自己改善することの重要性を理解しています。この経験を通じて、自己反省の習慣が身につき、次回同様の状況に対処するための戦略を考える力を養いました。」
※ポケモンの例でいうと、高価なボールはすぐ取り出せる位置に置かないとか
</details>

<details><summary>ストレス管理能力</summary>

「ストレスフルな状況でも冷静さを保つことができるようになりました。この経験を通じて、問題が発生したときに冷静に対応する力が身につきました。」
</details>

<details><summary>適切な報告・相談スキル</summary>

「問題が発生した際に、迅速に周囲に報告・相談することの重要性を学びました。現在では、問題を独りで抱え込まず、適切なタイミングでチームや上司に相談する習慣を持っています。」
</details>



## ★さいごに★
面接のときって、割と直近の内容から自己分析して活かせる点や強味・弱み等を探していくかと思います。
あえて、幼いころの経験から探してみるのもいいアイデアなのではないかと思います。
転職・就職活動、自己分析で、今回の記事が参考になれば幸いです。
２次関数の最大値(その2/2)「数学チャレンジ問題 長方形DBFEの面積の最大値は？」をとsympyとFreeCADとChatGPTとWolframAlphaでやってみたい。 ChatGPT-3.5先生へ。共通テ対応?

・(その1/2)より。
https://qiita.com/mrrclb48z/items/c396c61c835888a00de4
・何だかすごすぎる。
・画像を読めるAIがあるらしい？

# (再)オリジナル
・YUUU0123 様 (0:00〜3:36) 

https://youtu.be/xiO8jWwel2c

# ChatGPT-3.5先生へ。
```
底辺の長さ4,高さ2の直角三角形内の長方形の面積の最大値はいくつか？
```
...
底辺の長さが4、高さが2の直角三角形内の長方形の面積の最大値は 2 です。


# ChatGPT-3.5先生へ。共通テスト対応?
```
底辺の長さ4,高さ2の直角三角形内の長方形の面積の最大値はいくつか？
上の問題文を、共通テストみたいな太郎さんと花子さんの会話に変換して下さい。
```
(抜粋?)
...
これでどうでしょうか？
AWSにDjangoの環境を整えていく04(CloudFront)

こんにちは。
株式会社クラスアクト インフラストラクチャ事業部の大塚です。

今まで作成したALB (ELB)+ EC2 + RDS環境に対してCloudFrontをデプロイし、CDN環境を作っていきます。

今までの環境構築の流れは以下にまとめておりますので、適宜ご覧ください。

https://qiita.com/ohtsuka-shota/items/4b80538569b81f8c19a6

# 環境イメージ
前回デプロイしたALBに対してCloudFrontを紐づけていき、CloudFront経由でDjangoにアクセスできる環境を作っていきたいと思います。
※セキュリティの事に関しては特にここでは考慮をしておりませんのでご認識おきください。

![aws-django01-ページ8.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/aabb504c-49a1-799f-55d5-d970468177e7.png)

# CloudFront(CDN)環境を用意するメリット
例えば今回のDjango環境の場合、EC2が2つありALBで負荷分散をしているとはいえ、たくさん通信が来てしまうと処理が間に合わずユーザへのサービス提供に遅延が出てしまう可能性があります。
例えば、満席のレストランをイメージするとわかりやすいのではないでしょうか？料理が来ない…的な？
![島田様01-ページ32.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/866b22d4-9f07-f22a-e9be-cf1dbb692839.png)

CDNがあることでEC2に関わる負荷を分散することが出来ます。
具体的にはCloudFront(≒サーバ)が以前問い合わせた情報を保存(≒キャッシュ)することで
EC2への問い合わせ(≒通信)が減り、その結果EC2の負荷が下がるイメージです。
ALBが横に負荷分散しているのであれば、CloudFrontは縦に負荷分散しているイメージ？ですかね…
![島田様01-ページ33.drawio.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/14556b13-bbb8-bf25-d814-34651242a074.png)

# 構築
AWSコンソールでCloudFront管理画面を開くと、以下のような画面が表示されます。
右側にあるCloudFrontディストリビューションを作成するを押下します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/688e0293-c3b8-ec14-6902-aa062987591f.png)
今回は以下のように設定を入れていきました。
Originのところで作成したALBを指定してあげると、今から立ち上げようとしているCLoudForntと紐づけることが出来ます。
設定入力後、作成を押下します。
![screencapture-us-east-1-console-aws-amazon-cloudfront-v4-home-2024-06-29-19_51_33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/3603628d-4749-94f8-fe0a-abb5da7351bd.png)
作成を押下して正常に終了すると、以下のような画面が表示されます。
ディストリビューションドメイン名をコピーします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/d77c7e40-f979-3ab2-51f2-3869b8532284.png)
ディストリビューションドメイン名をコピーするとhttps\://d3b1f0p67o7047.cloudfront.netみたいな文字列を入手することが出来ます。
このままWebブラウザに適当にコピペして検索をかけると、いつも通りDjangoの画面を見ることが出来るようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3219385/2022056d-2355-76d7-db60-a5acd708e36e.png)
DockerでRedmine環境を構築する


## はじめに

Dockerを使えば手軽にRedmineをセットアップし、すぐに利用できる状態にできます。ローカル環境で使えるようにするまでを書いておきます。

前提条件：
- `docker`コマンドが使用できること

## コンテナのプル

Redmineの公式Dockerイメージを使用します。以下のコマンドで最新のイメージを取得します。

```
docker pull redmine:latest
```

## コンテナ起動

```
docker run -d --name my-redmine -p 3000:3000 redmine:latest
```

上記のコマンドで、Dockerコンテナがバックグラウンドで実行され、ポート3000でRedmineにアクセスできるようになります。`my-redmine`という名前のコンテナを作成して起動しています。

## Redmineへアクセス！

ウェブブラウザを開き、 http://localhost:3000 にアクセスします。 初期状態のRedmineでは、デフォルトで管理者アカウントが設定されており、以下でログインすることができます。

ユーザー名（Username）: admin
パスワード（Password）: admin

ログインすると管理者アカウントのパスワード変更を求められるので任意のパスワードを設定します。管理者アカウントは、ユーザーの承認や設定の変更を行うことができます。

あとはご自由にどうぞ。

## コンテナの停止

```
docker stop my-redmine
```

`my-redmine` はコンテナの名前です。自分が指定した名前に置き換えてください。

## 次回の起動方法

コンテナを停止した後、再び起動するには以下のコマンドを使用します。

```
docker start my-redmine
```


## コンテナの削除

コンテナを完全に削除する場合は、停止後に以下のコマンドを実行します。

```
docker rm my-redmine
```
UbuntuのVRChatで動画が再生できない！

# ある日...
(深夜〜早朝 in JTR)
会話相手「うわぁこんな時間に飯テロ動画なんて流しやがって...」
えっiwaSync真っ黒ですけど()
会話相手「え？今流れてるけど。...後ろのポスターは？」
...イベカレとバチャマガは見える、間のは黒い
会話相手「あ〜...それ確かQuestでも見れるように動画形式のはず」
つまり"動画"が観れない、と。

**致命傷じゃねぇかぁぁぁぁぁぁぁぁぁぁ！！！！！！！！！！(発狂)**

# 概要
...なんて事があったのでどげんかせんといかんわけですよ。
(というか3月頃までは普通に動いてたような...謎)
飯テロぐらいならまだ観れなくても何とかなりますが、これが懐かしのミームだったりボカロだったり、そもそも音楽イベントだったりすると死ぬわけですよ。
というわけでログ(`%LocalLowAppData%\VRChat\VRChat\output_log_%Y-%m-%d_%H-%M-%S.txt`)を読んでそれっぽいエラーでggって見ると、やはり似たような症状の海外ニキがスレを立ててくれてて(↓)、その最後のレスがまさに解決への鍵でした。

[実際のスレ(最終レスへのリンク)](https://ask.vrchat.com/t/no-playback-on-video-player-on-linux/13639/11)

要はコーデック周りが原因らしい(大雑把翻訳による独自解釈の可能性あり)のでProtonの代わりにProton GEを使えばいい、ということのようです。
貼られているのはストリーミング向けにRTSPパッチが当たったProton GEのフォークみたいですね。

# 解決方法
要するにProton GE(のフォーク)をダウンロードしてぶいちゃがこれを使うようにすればいいわけです。

## ダウンロード
まず、[ここ](https://github.com/SpookySkeletons/proton-ge-rtsp/releases)から最新版のtarballを落とします。以降バージョン名を`PROTON_GE_RTSP`、tarballの名前を`PROTON_GE_RTSP.tar.gz`として進めます。

## 展開先の作成
今までにProton GEなどのカスタム版Protonを入れたことがないなら多分ないので作ります。
```
$ mkdir ~/.steam/steam/compatibilitytools.d
```

## 展開
```
$ cd ~/.steam/steam/compatibilitytools.d
$ tar xf ~/Downloads/PROTON_GE_RTSP.tar.gz
```

## Steam側の設定
まずSteamを再起動します。新しいProton GEを認識させるためです。
そしたらVRChatのプロパティを開き、互換性タブで「特定のSteam Play互換ツールの使用を強制する」にチェックを入れ、ドロップダウンから`PROTON_GE_RTSP`を選択します。

# 最後に
実はこの海外ニキのスレに至るまで色々やってて、winetricksでコーデック片っ端から入れてったりdxvk入れてみたり色々してたら**起動すらしなくなっちゃって**泣く泣く再インスコしたりしてるので、
ゴチャゴチャいじる前に一旦ggってみると幸せになれるかもしれません...
ブループリントで作る射撃管制システム（FCS）

![SpaceCombatSB - Unreal Editor 2024_06_29 13_49_26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752748/75cdb29f-fbc8-e066-9dd6-6636a56ca42f.png)
# 概要
現在鋭意制作中のプロジェクトで使用予定のPDC(CIWS)用の制御機構。
外部アセット参照を極力しない、自己完結した管制システムを目指したもの。

# 基本要件
作成にあたって要求したのは主に次の3つ
- 偏差射撃が行えること
- 標的の速度を内部で算出できること
    (速度を直接与えることはしたくない)
- あまり処理負荷をかけないこと(大事)

# アプローチ
最終的にこの図のようなアプローチで処理を作ることにした。
```mermaid
graph TD;
    A[一定間隔で標的の位置を取得する]-->B[時間あたりの位置差分から目標の速度と移動方向を算出する];
    B-->C[発射する弾の速度（固定）と標的までの距離から到達までの所要時間を算出する];
    C-->D[標的の移動ベクトルに到達所要時間を乗算して未来位置を算出];
    D-->E[砲を指向して発砲する]
```
# 結果
実際に組んだブループリントはこんな感じ。
全体
![BP_PDC1 2024_06_29 14_12_40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752748/d72c5900-62bf-a472-fd62-05aea824fa94.png)


### 照準位置算出部分
全景
![BP_PDC1 2024_06_29 18_51_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752748/27572e78-1400-ecd7-3bd1-db6db794fa7d.png)
計算サイクルの動作クロックはTimerを使って20fpsに制限しているため、各種計算は1秒を基準にしている。
- 速度算出
![SpaceCombatSB - Unreal Editor 2024_06_29 19_01_30 - コピー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752748/7f891801-e7b5-aa9c-1589-4b9665bc4ed9.png)

    - 変数"TGT-LastPos"が前サイクルでの標的の位置、"TGT-CurrentPos"が現在のサイクルでの標的の位置情報を格納している。
    - ここで計算した2座標間の距離は1/20秒あたりのものなので、一度20倍して1秒あたりの移動距離に寒山する。

- 到達時間計算～未来位置算出
![SpaceCombatSB - Unreal Editor 2024_06_29 19_01_37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752748/5dbff66a-3ed4-7883-e53e-9c2d593f3fd2.png)
    - 自身と標的の距離を計算し、発射する弾の速度(現在は120unit/flameで固定)から到達所要時間(秒)を計算する
    - 標的の進行方向ベクトルに速度と所要時間を乗算し、未来位置を算出する
- 未来位置出力～次サイクルへの受け渡し
![SpaceCombatSB - Unreal Editor 2024_06_29 19_01_30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3752748/818f3c33-fba8-86ca-1b37-a2816b7fa7f3.png)
    - "AimPosition"に算出した未来位置座標を入力することで測距は完了とし、次の計算サイクルのために"TGT-LastPos"を上書きする。

# 補足
### 標的の選定
標的の探知はBigin Overlapイベントを使用しているが、同時に複数の標的が射程内に存在している場合が高い。そのため探知した標的は配列に格納し、順番に狙う形式を取る。
### 最初の計算サイクル
標的を選定した最初の測距処理には前回の位置というものが存在しないので、ターゲット切り替え時は現在位置をそのままTGT-LastPosとして流用する。
## 照準
砲塔と砲身を指向する方向についてはこの記事を参照されたし

https://qiita.com/Shiryu_grca/items/c09300e2fde47f6908e1　

# 実行
画面左中央から飛来するミサイルを迎撃させてみる
※デバッグのためLine Traceで算出した未来位置を表示してある
<iframe width="560" height="315" src="https://www.youtube.com/embed/hCqKpv1K0eo?si=dJ9plx1gUf8ou_d6" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>


# 終わりに
モジュラー化とまではいかないが処理ごとにまとまった構造になっているため、特に測距部分はかなり汎用性の高いものができた。
自分への注意点として、制御処理と実際の発射処理のノードが直結してしまったため制御処理の動作クロックを下げると発射レートもそれに伴って低下してしまう。実際の制作に使用する際には発射処理だけは分離しておかなければならない。
Fedora 40 で Qt をアカウントを作らずに使用する

## なんでもかんでもアカウントを作成するのが面倒くさい人向けです
* パッケージマネージャでいいんじゃないだろうか？
## 1.検索
```
$ dnf search qt-creator
qt-creator.x86_64 : Cross-platform IDE for Qt
qt-creator.i686 : Cross-platform IDE for Qt

$ dnf search qt6-designer
qt6-designer.x86_64 : Design GUIs for Qt6 applications

# インストール
$ sudo dnf install qt-creator qt6-designer
```

# 2.Qt Creator
```
# コマンドで起動できます
$ qtcreator
```

# 3.Qt Designer
```
# コマンドで起動できません
$ designer
bash: designer: コマンドが見つかりませんでした...
$ qtdesigner
bash: qtdesigner: コマンドが見つかりませんでした...

# フルパスで起動できます
$ /usr/lib64/qt6/bin/designer
```
apt/get/dpkgやその他のパッケージ管理ツールについて

apt/apt-get/dpkgの違いがわからなかったので、まとめました！

- apt/apt-get/dpkgの共通点
    - Debian系の*パッケージ管理ツール
        - *ソフトウェアのインストール、アップデート、削除、依存関係の解決を効率的に行うためのツールやシステム
    - ソフトウェアパッケージのインストール、アップグレード、削除、依存関係の管理を行う
- apt
    - 高レベルツール（自動で依存関係を処理をする）
    - apt-getやapt-cacheの機能を統合したツール
    - フロントエンドツール
- dpkg
    - 低レベルツール（パッケージファイルを直接インストール、削除、検査ができたり、依存関係の管理が手動）
    - バックエンドツール

- その他
    - Debian系の他に、Redhat系が存在する
        - yum/dnf/rpmがRedhat系のパッケージ管理ツール
            - yum：rpmの上に構築された高レベルのパッケージ管理ツールでフロントエンドツール
            - dnf:yumの後継で、より高速で効率的なパッケージ管理ツールでフロントエンドツール
            - rpm:低レベルツール、Debian系のdpkgに相当するでバックエンドツール
                - rpm<yum<dnf
Laravelのキャッシュクリアコマンド

## チートシート

```bash:Laravelのキャッシュクリアコマンド
php artisan cache:clear
php artisan config:clear
php artisan route:clear
php artisan view:clear
```

```bash:とりあえずクリアコマンド
composer dump-autoload
php artisan config:clear
php artisan route:clear
php artisan view:clear
```

## キャッシュとは

キャッシュとは、データを一時的に保存しておき、次回同じデータを必要とする時に素早くアクセスできるようにする仕組みです。Laravelでも、いくつかのタイプのキャッシュが利用されています。例えば、設定キャッシュ、ルートキャッシュ、ビューキャッシュなどです。

## コマンドの詳細

### php artisan cache:clear

```bash
php artisan cache:clear
```

このコマンドは、**アプリケーションのキャッシュを全てクリアします**。アプリケーションがキャッシュしたデータを削除し、次回のリクエストで新たにキャッシュを作成するようにします。

### php artisan config:clear

```bash
php artisan config:clear
```

このコマンドは、**設定キャッシュをクリアします**。Laravelでは、設定ファイル（configディレクトリ内のファイル）の内容をキャッシュすることで、パフォーマンスを向上させています。しかし、設定を変更した後にキャッシュが更新されないと、変更が反映されません。

### php artisan route:clear

```bash
php artisan route:clear
```

このコマンドは、**ルートキャッシュをクリアします**。Laravelでは、ルート（routes/web.phpやroutes/api.phpで定義されるURLのマッピング）をキャッシュすることで、パフォーマンスを向上させています。

### php artisan view:clear

```bash
php artisan view:clear
```

このコマンドは、**ビューキャッシュをクリアします**。ビューとは、ユーザーに表示されるHTMLテンプレートのことです。Laravelでは、これらのテンプレートもキャッシュすることで、パフォーマンスを向上させています。

## 参考記事

[Laravel キャッシュクリア系コマンドなど](https://qiita.com/Ping/items/10ada8d069e13d729701)
[Laravelのキャッシュクリアコマンド集](https://qiita.com/Hashimoto-Noriaki/items/6f19515b8094f5d9aa6a)
[Laravel キャッシュクリアコマンド](https://grune.co.jp/blog/laravel-cache-clear-command/)
SAP S/4 HANA 管理会計（CO）全体概要（財務会計と管理会計の位置づけ）

### 財務会計と管理会計の比較
![CO1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3432463/eca9bfc5-4cfa-44b2-9623-bf0dacad53a9.png)
![CO2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3432463/6d35bc4c-1ea4-abfe-21f1-3c7e4478f756.png)
![CO3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3432463/4eedd73f-8a7b-2270-d1a1-4457dbd12317.png)

### 管理会計内のコンポーネント
![CO4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3432463/9fc72c50-fd09-bf5b-a187-07b20e3107e2.png)
![CO5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3432463/0c6c4e97-d190-4397-387d-fd2980824d5b.png)
![CO6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3432463/32749ee7-2c54-5df3-3b88-eea205892089.png)

### SAPシステム内での統合
![CO7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3432463/226bd668-7d62-ddc1-44a2-773fcfadc863.png)

【参考文献】
TS4F03 SAP S4HANA の管理会計  アカデミーパート1・2
【React】リロードや戻るボタンを押す前に警告メッセージを表示する

## はじめに
リロードや戻るボタンを押下時に、警告メッセージが出たりしますよね。あれってどうやって実装しているんだろうと思ったので、今回記事にしてみました。

## 成果物
![reload_stop.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/6acb5d07-ecc1-ed98-9292-f9e03592590c.gif)

## ソースコード
```tsx:src/Home.tsx
import type React from "react";
import { useState } from "react";
import { type SubmitHandler, useForm } from "react-hook-form";
import { useBlocker } from "./hooks";

interface FormValues {
	name: string;
}

export const Home: React.FC = () => {
	const { register, handleSubmit } = useForm<FormValues>();
	const [isBlocked, setIsBlocked] = useState<boolean>(false);

	const onSubmit: SubmitHandler<FormValues> = (data) => {
		console.log(data);
		setIsBlocked(false);
		alert("保存されました");
	};

	useBlocker(() => {}, isBlocked);

	return (
		<div>
			<h1>ホーム</h1>
			<form onSubmit={handleSubmit(onSubmit)}>
				<input
					{...register("name", { required: true })}
					placeholder="名前を入力"
					onChange={() => setIsBlocked(true)} // フォームが変更されたらブロックを有効にする
				/>
				<button type="submit">保存</button>
			</form>
		</div>
	);
};
```

```ts:src/hooks.ts
import { useEffect } from "react";

export const useBlocker = (blocker: () => void, when = true) => {
	useEffect(() => {
		if (!when) return;

		const handleBeforeUnload = (event: BeforeUnloadEvent) => {
			event.preventDefault();
			blocker();
		};

		const handleBeforeRoute = (event: Event) => {
			event.preventDefault();
			blocker();
		};

		window.addEventListener("beforeunload", handleBeforeUnload);
		window.addEventListener("popstate", handleBeforeRoute);

		return () => {
			window.removeEventListener("beforeunload", handleBeforeUnload);
			window.removeEventListener("popstate", handleBeforeRoute);
		};
	}, [blocker, when]);
};

```
Goで値渡しをするかポインタ渡しをするかの基準

構造体のフィールドの値を変更する必要があるなら、構造体のポインタで渡す。

そうでないなら、値で渡す。

値で渡した場合は、構造体のコピーが作られるため、元のデータが変更されない。

なので、基本的にポインタで渡しておき、フィールドのデータを書き換える必要がない場合や明確に元のデータを書き換えたくない場合には値で渡す。

```go
package main

import "fmt"

type mutableUser struct {
	ID   int
	Name string
	Age  int
}

func (u *mutableUser) SetAge(age int) {
	u.Age = age
}

type immutableUser struct {
	ID   int
	Name string
	Age  int
}

func (u immutableUser) SetAge(age int) {
	u.Age = age
}

func main() {
	// Mutable
	mutableUser := mutableUser{ID: 1, Name: "Test", Age: 25}
	mutableUser.SetAge(10)
	fmt.Printf("Mutable user age: %d\n", mutableUser.Age)
	// 10

	// Immutable
	immutableUser := immutableUser{ID: 1, Name: "Test", Age: 25}
	immutableUser.SetAge(10)
	fmt.Printf("Immutable user age: %d\n", immutableUser.Age)
	// 25
}
```
HTTPリクエストをトリガーに効果音がなる仕組みを Node.js で作ってみる（play-sound と Node.jsの標準モジュールの組み合わせ）

以下の記事を書いた際に行っていた、Node.js の「play-sound」による効果音の再生を、Web経由で行う簡易な仕組みを試作してみます。

●Mac で Node.js ＋ play-sound を使って MP3ファイルを再生してみる - Qiita
　https://qiita.com/youtoy/items/a744e3ca1d52cc9609c5

リアルタイムなやりとりをするなら MQTT などを使うのが良さそうなところではあるのですが、今回は思いつきで HTTPリクエストを使ったものにしてみます。
その HTTPリクエストを受けとるサーバーの部分は、Node.js の標準モジュールを使う形で実装してみます。

## HTTPリクエストを受けとる部分のシンプルな実装
まずは、 HTTPリクエストを受けとるサーバーの部分を、簡易な実装で用意してみます。
それに関しては、上記のとおり Node.js の標準モジュールを使って実装します。

HTTPリクエストは、GETリクエストを用いる形にしました。

```javascript
const http = require("node:http");

const port = 3000;
const server = http.createServer((req, res) => {
  if (req.url === "/httpsound" && req.method === "GET") {
    console.log("HTTPリクエストを受け取りました。");

    res.writeHead(200, { "Content-Type": "text/plain" });
    res.end("Hello, World!");
  } else {
    res.writeHead(404, { "Content-Type": "text/plain" });
    res.end("404 Not Found");
  }
});

server.listen(port, () => {
  console.log(`Server running at http://localhost:${port}`);
});
```

### 動作確認
上記を実行して、ブラウザから `http://localhost:3000/httpsound` にアクセスしてみます。
ブラウザからアクセスした後、Node.js側では以下のようにメッセージが出力されたのが確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/14559dbf-67a3-768e-e03f-6141a9e17ce3.png)

またブラウザ側では、以下のとおりレスポンスが返ってきていることが確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/50868/1dd277f4-9399-79ad-ae1a-9bb8edc46d23.png)

## 効果音を再生する部分を加える
上記の HTTPリクエストを受けとった時に、効果音を鳴らす処理を追加してみます。
ここで鳴らす効果音は、冒頭に掲載していた記事で使った効果音と同じものを用いることにします。

効果音を鳴らす処理を追加した内容は以下のとおりです。
以下を実行する前には、npmコマンドで play-sound をインストールしておいてください。

また、play-sound を使って再生する MP3ファイルは、同じフォルダに置いて以下を実行してください。

```javascript
const http = require("node:http");
const player = require("play-sound")((opts = {}));

const filePath = "焚き火短.mp3";

const port = 3000;
const server = http.createServer((req, res) => {
  if (req.url === "/httpsound" && req.method === "GET") {
    res.writeHead(200, { "Content-Type": "text/plain" });
    res.end("Hello, World!");

    player.play(filePath, (err) => {
      if (err) {
        console.log(`エラーが発生しました: ${err}`);
      } else {
        console.log("再生が完了しました");
      }
    });
    console.log("再生を開始しました");
  } else {
    res.writeHead(404, { "Content-Type": "text/plain" });
    res.end("404 Not Found");
  }
});

server.listen(port, () => {
  console.log(`Server running at http://localhost:${port}`);
});
```

上記を実行して、ブラウザで `http://localhost:3000/httpsound` にアクセスしてみます。
ブラウザでアクセスするたびに、効果音が再生されることが確認できました。

### インターネットから音を鳴らせるようにする
最後に、上記の 1台の PC内で完結した形でなく、インターネット経由でスマホなどから音を鳴らせるようにしてみます。

その方法の 1つとして、以前、以下の記事に書いた仕組みを用いることもできますが、今回はそれと同じことができる ngrok を使った方法を使うことにします。

●VSCode の新機能「built-in port forwarding」を使いローカルサーバーにインターネット側からアクセス - Qiita
　https://qiita.com/youtoy/items/56e48c84d6a2f0549155

上記のサーバー側のプログラムを実行した後、別途、以下を実行します。

```zsh
ngrok http 3000
```

これで、ローカルで 3000番ボートで立てたサーバーに、インターネットからアクセスできるようになります。
上記の ngrokコマンドを使った処理を行った後、インターネットからアクセスする `https://【文字列】.ngrok-free.app/` といった URL が表示されるかと思いますので、それに httpsound を付け加えた `https://【文字列】.ngrok-free.app/httpsound` という URL にアクセスしてください。

動作確認を行ったところ、スマホなどからインターネット越しに効果音を再生させることができました。
Python 練習

## 今日の進捗

PythonでPDFを操作していたら勉強する体力を失いました。

https://www.youtube.com/watch?v=U-_43dmdV_I&t=600s

3問目を写経しました。

![2024.6.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3812230/85f8027b-7fba-e3e1-ffc0-b82fa72fb2a8.png)

いつの間にかVSCodeのtotoroがいなくなっていたので、再度追加しました。
realVNCが、ubuntu 22.04 で動作しなかったので

# realVNCが、ubuntu 22.04.4 で動作しなかったのですが、動いたので。

![タイトルなし.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/866173/1df79755-31db-5c0a-6a79-97c0bc3d79b5.jpeg)

# 環境
OS: Ubuntu 22.04.4
CPU: 8th gen Intel i7
GPU: nVIDIA RTX-3060
Network; TP-Link, TG-3468, RTL8168H
Memory:  16GB

# サーバー版（GUIの無いやつ）でインストール
ubuntuのデスクトップ版でインストールするとエラーで死ぬのでサーバー版（GUIの無いやつ）でインストール
ubuntu-22.04.4-live-server-amd64.iso
https://releases.ubuntu.com/jammy/ubuntu-22.04.4-live-server-amd64.iso

RUFUSでISOディスクとしてUSBメモリに焼いてインストール開始
_aptのくだりは、22.04のバグらしい

$ ssh -l owner 192.168.40.105
$ sudo apt update
$ sudo apt upgrade
$ sudo chown _apt /var/lib/update-notifier/package-data-downloads/partial/
$ sudo apt update

# デスクトップ環境を構築

$ sudo apt install ubuntu-desktop
$ sudo apt install xterm
$ sudo reboot

// タイムゾーンを日本に設定
// 私の都合で、自動ログインを設定
// 電源管理で、スリープしないように設定

# 「設定」でLAN設定を変更できないのを修正。
「設定」の「ネットワーク」にLANが出てこないので、以下を修正。
サーバー版でインストールすると、ネットワーク設定がnetworkdになる。
ubuntu-desktopをインストールしたときに、NetworkManager に書き換えて欲しいのですが、それはやってくれない。
ので、以下にあるファイルを書き換える

/etc/netplan/xxxxx_config.yaml
GUI用が以下
renderer: NetworkManager
テキスト用が以下
renderer: networkd

# エラーが出るので、GTKをインストール
たぶん、全部入れる必要は無いと思うけれど検証する時間なし

$ sudo apt-get install libcanberra-gtk*

# realVNCのサーバーモジュールをインストール
aptではなくて、必ず、dpkgで実施すること

$ cd /tmp
$ wget https://downloads.realvnc.com/download/file/vnc.files/VNC-Server-7.12.0-Linux-x64.deb
$ sudo dpkg -i  ./VNC-Server-7.12.0-Linux-x64.deb

realVNCをサーバーとして登録
$ sudo systemctl enable  vncserver-x11-serviced.service
$ sudo systemctl start   vncserver-x11-serviced.service
$ sudo systemctl status  vncserver-x11-serviced.service

realvncのアカウント情報で、このPCをrealVNCサーバーに登録。
アクセスするのには、私の場合は、realVNC独自のパスワードを使いたかった。

$ sudo vnclicensewiz
$ sudo vncpasswd -service
//  "himitsu"など
echo 'Authentication=VncAuth' | sudo tee -a /root/.vnc/config.d/vncserver-x11
sudo cat /root/.vnc/config.d/vncserver-x11
// Check GUI Security Authentication = "VNC password"

# nVIDIAのドライバーをインストール
nVIDIAのカードを入れている場合のみ！
標準のNouveauでは、動作しない。
22.04から採用された仕組みであるWayLandに、realVNCが対応していないので、WayLand=Enableの環境では、画面を共有できないし、WayLand=Falseにすると画面が黄色くなる。

$ sudo systemctl stop gdm
$ cd /tmp
$ wget https://us.download.nvidia.com/XFree86/Linux-x86_64/550.90.07/NVIDIA-Linux-x86_64-550.90.07.run
$ sudo su
\# bash ./NVIDIA-Linux-x86_64-550.90.07.run
\# reboot

$ sudo vi /etc/gdm3/custom.conf
  WaylandEnable=false

# 以上で、外部からアクセスすれば利用できる。

# 参照サイト
https://askubuntu.com/questions/1403337/download-is-performed-unsandboxed-as-root-as-file/1416892#1416892
https://qiita.com/Domao/items/8350ac97ea9fabc48555
https://help.realvnc.com/hc/en-us/articles/360002253818-Scripting-Deployment-and-Start-up-on-Linux
https://bugs.launchpad.net/ubuntu/+source/aptitude/+bug/1543280
https://help.realvnc.com/hc/en-us/community/posts/5021325365917-VNC-Server-wont-work-on-a-new-Ubuntu-Desktop-Install
https://techviewleo.com/install-realvnc-server-and-client-on-ubuntu/
https://github.com/RealVNC/vncsetup-helper-linux
OracleとNode.jsを使って無料でWebサイトを公開しようとしたら躓いた話

# はじめに
Oraclecloudを使ってWebサイトを公開しようとしたら、躓いたので記事にして残しておきます。

# 原因
ポートの解放をUbuntu側でしてなかった

# 経緯
OraclecloudのalwayfreeとNode.jsを使ってWebサイトを公開しようとしたが、WebサイトにアクセスすることやPingを飛ばすこともできなかった。
先輩からのアドバイスでOracle側でのポート開放に加えUbuntu側でも設定が必要だと判明して解決した。

# 解決法

- ファイアーウォール(iptable)の設定を変更してあげる

iptableの設定ファイルを開く
```
sudo nano  /etc/iptables/rules.v4
```
下記の文の--dport XXXXを変更して追加する
```
-A INPUT -p tcp -m state --state NEW -m tcp --dport XXXX -j ACCEPT
```
3000番を開けたい場合
```
-A INPUT -p tcp -m state --state NEW -m tcp --dport 3000 -j ACCEPT
```
- 設定を適用する
```
sudo iptables-restore < /etc/iptables/rules.v4
```
これでNode.jsで動かしているサイトへアクセスできるようになりました。

# 最後に
Ubuntu難しい()



# 参考にしたサイト
https://qiita.com/bellx2/items/6f1d95dc8e1bef0be555
OpenFOAM11のcodeStreamの変更点

# はじめに
interFoamでnotched disc in rotating flowの計算を試みた。
ESI版ではなんとinterIsoFoamにチュートリアルケースがあり、速度場の初期値はsetVelocityなるfunctionObjectで設定が可能である。よってこれを流用すればinterFoamでも計算ができる。
一方、Foundation版には上述の便利な機能がないため、0/UのinternalFieldにcodeStreamを適用することで速度場の初期値を設定することになる。
バージョン11でこのcodeStreamにエラーが発生した。原因の調査および対策を以下に記す。

# 発生状況
バージョン10でのcodeStream.Hでの例は次の通りである。
```codeStream.H(v10)
internalField  #codeStream
{
    code
    #{
        const IOdictionary& d = static_cast<const IOdictionary&>(dict);
        const fvMesh& mesh = refCast<const fvMesh>(d.db());
        scalarField fld(mesh.nCells(), 12.34);
        writeEntry(os, "", fld);
    #};

    //- Optional:
    codeInclude
    #{
        #include "fvCFD.H"
    #};

    //- Optional:
    codeOptions
    #{
        -I$(LIB_SRC)/finiteVolume/lnInclude
    #};
};
```
これがバージョン11では「fvCFD.Hが存在しない」という理由でエラーになる。

# 対策
バージョン11のcodeStream.Hには次のような例が記されている。
```codeStream.H(v11)
nternalField  #codeStream
{
    code
    #{
        const IOdictionary& d = static_cast<const IOdictionary&>(dict);
        const fvMesh& mesh = refCast<const fvMesh>(d.db());
        scalarField fld(mesh.nCells(), 12.34);
        writeEntry(os, "", fld);
    #};

    //- Optional:
    codeInclude
    #{
        #include "volFields.H"
    #};

    //- Optional:
    codeOptions
    #{
        -I$(LIB_SRC)/finiteVolume/lnInclude
    #};
};
```
インクルードするヘッダファイルがfvCFD.HからvolFields.Hに変更されている。
実際に、バージョン11ではインクルードするファイルをvolFields.Hと記述すれば問題なく動く。

# おわりに
本当に一体なんでこんな変更をするのだろう...???
Ruby 2.6.6でRails 6.0.3をインストールする際のトラブルシューティング

### エラー1: nokogiriのバージョンとRubyの互換性に関するエラー
Ruby 2.6.6でRails 6.0.3をインストールしようとすると、次のようなエラーが発生することがあります。

```bash
$ gem install rails -v 6.0.3

ERROR: Error installing rails:
The last version of nokogiri (>= 1.8.5) to support your Ruby & RubyGems was 1.13.10.
Try installing it with `gem install nokogiri -v 1.13.10` and then running the current command again.
nokogiri requires Ruby version >= 3.0.0. The current ruby version is 2.6.6.146.
```
このエラーは、nokogiri gem がRuby 3.0.0以上を要求しているために発生します。解決方法として、nokogiriのサポートしている2.6系列の最新バージョンをインストールします。

### Gemfileの作成とbundle installの実行
Gemfileを作成してRailsとnokogiriの特定バージョンを指定します。

Gemfileの内容:

```ruby
source 'https://rubygems.org'
gem 'rails', '6.0.3.7'
gem 'nokogiri', '1.13.10'
```

これを保存したら、次のコマンドで依存関係をインストールします。

```bash
$ bundle install
```

### Railsコマンドが認識されない問題の解決
依存関係が正常にインストールされた後、Railsコマンドが認識されない場合があります。この場合、rbenvを再初期化してパスを再構築する必要があります。

```bash
$ rbenv rehash
```
これにより、rbenvがインストールしたRailsのバージョンを認識し、コマンドが正しく実行されるようになります。

最後に、次のコマンドでRailsのバージョンを確認してください。

```bash
$ rails -v
Rails 6.0.3.7
```

これでRuby 2.6.6でRails 6.0.3.7を正常にインストールし、動作させることができます。
docker composeの書き方


## docker compose cli v2を利用する方法

docker-compose ではなく、docker compose コマンドも利用可能です。

Docker Desktopでは v3.4.0から利用可能です。

## ファイル監視による自動更新

docker compose 2.20.0からCompose Watchの機能が使えるようになりました。

| モード<br />(compose.yml) |ファイル更新時の挙動 | 利用例 |
| -------------------------- | -------------- | ----------|
| sync | ホスト側のファイル変更がコンテナ側に反映されます | ホットリロード機能を内蔵しているサービス |
| rebuild | イメージをビルドしてコンテナ二入れ替える | イメージの再ビルドが必要な場合 |
| sync + restart | ファイル変更を反映、コンテナをリスタート | データベースのconfig変更 |

記載例は以下のようになります。

``` compose.yml

services:
   web:
     build .
     command: npm start
     develop:
       wetch:
         - action: sync
           path: ./web
           target: /src/web
           ignore:
             - node_modules/
         - action: rebuild
           path: package.json

```

## docker composeファイル名は

以下のファイルが利用可能

- compose.yml
- docker-compose.yml

## docker image名やコンテナ名のプレフィックスをディレクトリ名から変更

通常は ${ディレクトリ名}_${サービス名} でイメージ作成されます。

compse.ymlの一番上に **name: 名前** 記載することで変更が可能です。

``` compose.yml
name :sample
services:

```
## ヘルスチェックの設定

ヘルスチェックを行うことができます。
ヘルスチェックを利用することで依存関係、サービスの完了まで待機することができます。

``` compose.yml

healthcheck:
  target: ["cmd","curl","-f","http://localhost"]
  interval: 1m
  timeout: 10s
  retries: 3
  start_period: 40s

```

## 依存関係の設定

docker compose2.1からサービスがhealthyになった後に起動する、正常終了したあとに実行するなど、複雑な依存関係を定義することができます。

**使用可能な条件は以下**

- service_started: 依存するサービス開始まで待機します。
- service_healthy: 依存するサービスが**healty** になるまで待機します。
- service_completed_successfully: 依存するサービスが正常終了するまで待機します。

```compose.yml

services:
  web:
    build: .
    depends_on:
      db:
        condition: service_healthy
      redis:
        condition: service_started
    redis:
      image: redis
    db:
      image: mysql
      healthcheck:
        test "exit 0"

```
【5分でできる！】自作APIをHTML/JavaScriptで読み書きしたい人が読む記事

## ちゃちゃっと自作APIを使いたいとき
ハッカソンとかでテキトーに作ったDBを読み書きしたいとき、ありますよね。

## ①APIを作りましょう
まずはこの記事を参考にしてAPIを作りましょう。
3分でできるらしいので、残りは2分です！
https://qiita.com/cog1t0/items/b3b4a24236451b666129

## ②以下のコードをコピペ
API URL、キー名、カラム名や値は作ったAPIに合わせて変更してください。
```html:index.html
<button id="button">ボタン</button>
<script>
    window.addEventListener('DOMContentLoaded', async () => {
        const res = await fetch('[httpsから始まるAPI URL]/シート1');
        const data = await res.json();
        // dataはJson形式
        console.log(data);
        // data[n].カラム名 でデータ取得
    })

    const write = async () => {
        const res = await fetch('[httpsから始まるAPI URL]/シート1', {
            method: 'PUT',
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify({
                "condition": { "キー": "りんご" },
                "set": {
                    "メニュー1": "ソーダ",
                    "メニュー2": "サラダ",
                    "メニュー3": "ケーキ",
                }
            })
        });

        const result = await res.json();
        console.log(result);
    };

    const button = document.getElementById('button');
    button.addEventListener('click', write)
</script>
```

このようなデータベースを想定しています。
行、列やセルを指定して取得するのではなく、キー列の値を指定して取得します。
![スクリーンショット 2024-06-29 18.35.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2958610/f8828c53-0701-22bc-4bca-55e84fb75306.png)


以上！