OmniAICreator/Qiita-1.07M huggingface dataset:
rebase 教から脱退します

rebase で色々あったので、備忘録として簡単に書いていきます。

# 前提背景
開発作業中、元のブランチに変更があった場合、私は変更を取り込むために常に rebase を使用します。これを選ぶ主な理由は「コミットログが見やすく保たれるため」です。

Gitには同様のコマンドとして merge がありますが、これは変更を取り込む際にマージコミットを作成する点が異なります。私はマージコミットによってコミットログが煩雑になると感じています。

このような理由から、私はrebaseを積極的に使用しています。

# 何があったのか
簡単に言うと、レビュー中にブランチ元の変更があったので、 `git rebase` からの `git push -f origin [ブランチ名]` やったらレビュアーのコメントが吹き飛びました。

いやー、めっちゃ怒られたよね💦

# 原因
「レビュー中」という状況がまずかった。
コードを共有している状況で `git push -f`、つまり force push すると以下の問題が発生します。


## レビューコメントの紐付けが失われる
GitHub上でレビュアーが特定のコミットに対してコメントを残している場合、そのコミットを書き換えると、コメントが「outdated」とマークされます。force push により元のコミットがリポジトリの履歴から削除されると、それに関連するコメントも適切なコンテキストを失うことがあり、レビュー過程での参照が難しくなります。

## レビューの進行がリセットされる
force push により、既に検討されていた変更点が上書きされるため、レビュアーが再度同じコードを確認し直さなければならないことがあります。これはレビュープロセスを遅らせ、追加の作業を発生させます。

## 履歴の整合性が損なわれる
force push は、リポジトリの公開履歴を書き換えるため、他の開発者が既にチェックアウトまたはブランチに基づいて作業していた場合、彼らのローカル履歴との整合性が崩れます。これにより、不必要なコンフリクトが発生し、チーム内での混乱が発生します。

# どうすれば良いか・対処法
共有しているブランチ、特にレビュー中のものに対しては、force push は避けるべきだというのが私の結論です。

## ブランチ元で変更があった場合
ブランチ元で変更があった場合は以下のように対応した方が良いです。
- レビュー前、push前：git rebase
- レビュー中、push後：git merge

↑で変更を取得するようにすると、今回のような問題にはならないと思います。

# 最後に
rebase と、force push は計画的に・・・
【GAS】スプレッドシートにカスタムメニューを追加する方法

以下のように記述するとファイルを開いた際にカスタムメニューが追加されます。

```js
function onOpen() {
  const customMenu = SpreadsheetApp.getUi()
  customMenu.createMenu('カスタムメニュー')  //メニューバーに表示するメニュー名
      .addItem('追加メニューアイテム1', 'functionName')  //メニューに項目を追加
      .addToUi()
}

function functionName() {
  // ...
}

```

![スクリーンショット 2024-04-19 23.57.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342443/11b15756-afce-3170-68c6-dd3538a1c05a.png)
リーダブルコード13章 14章

# 13章　短いコードを書く
- 最も読みやすいコードは、何も書かれていないコード
- 質問と要求の分割
- できるかぎり要求を簡単にすることで必要なコードを少なくできる
- プロジェクトが成長しても、コードはできるだけ小さくして軽量に維持する
# 14章　テストと読みやすさ
- 最小のテストを作る
- テストを簡潔にすることでテストの追加が容易になる
- 入力値を単純化する
    - 小数点など減らす
    - マイナス要素
    - 境界値を用いる
VSCodeから開くファイルの言語モードとエンコーディングを設定する


# はじめに
VSCodeは、開くファイルの言語モードやエンコーディングを判別して、そのファイルに合う言語モードと文字のエンコードを自動に設定します。

しかし、開いたファイルの設定が考え通りではなかった時があり、特定の拡張子を持つファイルは、常に指定した言語モードでファイルを開きたいと思った時がありました。また、言語モードによって文字コードも指定したいと思いました。(特にVBAのソースコードファイルをVSCodeで開いた時でした。)

そのため、今回はVSCodeで指定した言語モードとエンコーディングでファイルを開く方法を整理しようと思います。

# 特定の拡張子ファイルの言語モードを設定
## 設定方法
1. VSCodeから設定画面を開く
1. 「files.associations」で設定を検索する
1. 設定したい項目(拡張子と言語の識別子)を追加する

言語の識別子は以下のリンクで確認してください。

https://code.visualstudio.com/docs/languages/identifiers#_known-language-identifiers

## 設定例
サンプル用のファイルは以下のようになります。
ファイル名は、「sample.sample」で、言語モードは「Plain Text」で開きます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1184971/4e0247be-8d71-ce37-84af-dbd9bffa5659.png)

設定画面を開いて、「files.associations」を検索し、該当する拡張子と言語モードの識別子を入力します。
例として「.sample」拡張子ファイルを「Visual Basic」(識別子：vb)に指定しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1184971/f9fb0749-c88e-7022-8712-c008439573bf.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1184971/3c96afb3-7642-2540-ef90-cfda6afcecd9.png)

「sample.sample」ファイルの言語モードが「Visual Basic」になりました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1184971/85220ea0-3423-efa5-7189-0ac218293930.png)


# 特定の言語モードのエンコーディングを設定
## 設定方法
1. VSCodeから設定のJSONファイル(settings.json)を開く
1. 設定したい言語についてエンコーディング設定コードを追加する
1. ファイルを再度開く

追加するコードは以下のようになります。
```json:settings.json
...(省略)...
"[{言語の識別子}]": {
    "files.encoding": "{文字コードの識別子}"
  }
```

## 設定例

設定のJSONファイル(settings.json)を開いて、設定のコードを追加します。
例には「Visual Basic」言語モードはデフォルトで「Shift-JIS」(shiftjis)の文字コードで開くようにコードを追加しました。

```json:settings.json
...(省略)...
"[vb]": {
    "files.encoding": "shiftjis"
  }
```

# 参考

https://code.visualstudio.com/docs/languages/identifiers
callbacksコマンドメモ

![キャプチャ.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1412809/b466cc35-e7ca-9805-d0a8-32488e7de3a2.png)

右クリックメニューのcallbacksにフック
```mel:mel
global proc dagMenuItemDelete(string $object){ // 標準のMayaフック addRMBBakingMenuItemsの引数を持つプロシージャ
    string $popupMenuName = `setParent -q -m`;
    string $popupMenuItemList[] = `popupMenu -q -ia $popupMenuName`;
    
    // ラジアル位置の指定の無いmenuItemを削除
    for($popupMenuItemName in $popupMenuItemList){
        $popupMenuItemName = $popupMenuName + "|" + $popupMenuItemName;
        string $menuItemRp = `menuItem -q -rp $popupMenuItemName`;
        if($menuItemRp == ""){
            deleteUI -mi $popupMenuItemName;
        }
    }
}
callbacks -addCallback "dagMenuItemDelete" -hook addRMBBakingMenuItems -owner "kohaku";
```
PlatformIOでUnknown board ID 'm5stack-atoms3' の対策

PlatformIOでM5StackAtomS3を選択するとエラーが出た場合の対策方法

![スクリーンショット 2024-04-19 232219.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/34295/b8897e30-f316-684b-d0f7-62bfd35142ad.png)

PlatformsのEspressif32を最新版にすると直る。
（Uninstallしてもう一度Install）
Salesforce Platform デベロッパー 備忘録 #過去問

設問に対して解答と考察(解説ではなくあくまで僕の調べた結果)を書いていく.

### Q1.  値「高」「中」「低」は異なるオブジェクトにわたる複数の選択リストの共通値として識別される.  選択リストとその値のメンテナンスを合理化すると同時に,  値を上記ものに制限するために開発者がとることができるアプローチとは？

A.各オブジェクトに選択リストを作成し、値を含むグローバル選択リスト値セットを使用します。
B.各オブジェクトの選択リストを必須項目として作成し、[入力した順序ではなくアルファベット順に値を表示する]を選択します。
C.それぞれに選択リストを作成し、入力規則を追加してデータの整合性を確保します。
D.各オブジェクトに選択リストを作成し、「選択リストを値セットで定義された値に制限する」を選択します。

解答: A

考察: 
設問では,  Salesforce内で異なるオブジェクトにまたがる複数の選択リストで共通の値を効率的に管理する方法について聞いている. そこで考えられるアプローチには,  複数のオブジェクトにわたって同一の選択リスト値を再利用するのに最適なグローバル選択リストがある.  このアプローチは選択リスト値の一貫性を保ちつつ,  異なるオブジェクト間でのデータの整合性を高める効果がある.

### Q2.  開発者がキュー可能なインターフェイスを持つクラスのエンキュージョブのステータスを取得できる2つの方法は何ですか？

A.apexジョブページを表示する
B.AsyncApexJobeオブジェクトを照会します
C.apex flexキューを表示します
D.apexステータスページの表示

解答:  B, C

考察: 
キュー可能=Queueableで非同期処理を行うためのApexインターフェース. これを使用することで開発者は長時間実行されるプロセスや非同期タスクをバックグラウンドで実行するためのジョブを作成できる.

エンキューとは,  Apexジョブに実行キューを追加することを指す.

Apex Flexキューは,  バッチApexジョブの待機行列を表示するためのもの.
非同期処理の一部として,  バッチApexジョブの実行順序を管理するために使用される.

-- キュー可能Apex --
https://developer.salesforce.com/docs/atlas.ja-jp.apexcode.meta/apexcode/apex_queueing_jobs.htm

### Q3.  Salesforce Lightning Platform上で実行されるApexクラスにおいて,  開発者が非同期処理を実装する必要が生じた.  この処理は大量のデータに対する時間がかかる操作を行う.  システムのパフォーマンスに影響を与えずに完了する必要があります。この要件を満たすために開発者が選択すべき最適なアプローチとは？

A. @futureアノテーションを使用してメソッドを非同期に実行します。
B. Queueable Apexを使用してジョブをキューに追加します。
C. Batch Apexを使用してデータをバッチ処理します。
D. Schedulable Apexを使用して定期的なジョブをスケジュールします。

解答:  C

考察: 
Batch Apexは大量のデータを分割して処理するのに最適な方法である.  
特に大規模なデータセットを順次処理する必要がある場合や,  データ処理に時間がかかる操作を行う場合に適している.

この設問では,  大量のデータに対する時間がかかる操作を行う必要があるため,  Batch Apexを使用してデータをバッチ処理した方がいい.  Batch Apexを使用すると大規模なデータセットを小さな単位に分割して順次処理することができ,  各バッチ処理に対するガバナ制限がリセットされるため,  システムのパフォーマンスに影響を与えずに処理を完了させることができる.

### Q4.  匿名Apexコードを実行するための2つのユースケースは？

A.展開後の1回のトランザクションで15,000の非アクティブな取引先を削除するには
B.Apexクラスを定期的に実行するようにスケジュールする
C.ユニットテストコードカバレッジを組織に追加する
D.バッチApexクラスを実行して、すべての取引先責任者を更新

解答:  B, D

考察:  
開発者コンソールの,  Anonymousウィンドウのことかな.
使用ケースは, 　作成したApexクラスを試しに実行しデバッグを取ったり,  設定した時間外でバッチを実行したり,  簡単なDML操作などを行うとき(https://sbs-sfdc.hatenablog.com/entry/2022/07/06/172738).

### Q5.  A社は開発者を雇ってユーザーが必要な取引先を見つけるのに役立つカスタム検索ページを作成する.  ユーザーは名前,  説明およびカスタムコメント項目で検索できるようになる.
SOQLとSOSLのどちらを使用するかを決定する際に,  開発者はどの考慮事項に注意する必要がありますか？

A.SOQLはテキスト検索の方が高速です。
B.SOSLはより多くのレコードを返すことができます。
C.テキスト検索ではSOSLの方が高速です。
D.SOQLはより多くのレコードを返すことができます。

解答:  C, D

考察:  
テキスト検索はSOSLの方が速くて柔軟.  具体的なオブジェクトを抽出したい場合はSOQLの方が向いていいかも.(https://qiita.com/kakakaori830/items/09d42d7135c567b011ef)

SOQLはSOSLより多くのレコードを返す(https://tyoshikawa1106.hatenablog.com/entry/2014/11/28/232440#:~:text=Apex%E3%81%A7SOQL%E3%82%AF%E3%82%A8%E3%83%AA%E3%82%92,%E3%83%AC%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E5%8F%96%E5%BE%97%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82).
Pythonの便利技と注意点:コードスタイル規約編

[目次に戻る](https://qiita.com/yuta-sanjyo/items/416b05f8498df1ed3f49)

# コードスタイルをチェックするライブラリ
- pycodestyle
- flake8
- pylint

```Py:Terminalでの使用方法
>>> pycodestyle xxxx.py
>>> flake8 xxxx.py
>>> pylint xxxx.py
```
```Py:Jupyter Notebookでの使用方法
!pycodestyle xxxx.py
!flake8 xxxx.py
!pylint xxxx.py
```

# importの規約
```Py:インポートの際、複数のライブラリをカンマでつなげない
import os, csv, sys
```

```Py:1ライブラリ1行使う
# アルファベット順
# 標準・サードパーティ・ローカルパッケージ・ローカルファイルのグループ順
# 各グループごとに1行空ける

# 標準ライブラリ
import csv
import os
import sys

# サードパーティライブラリ
import flask

# ローカルパッケージ
import mylib.funcA

# ローカルファイル
import local_settings
```

```Py:関数のみをインポートしない
# 関数のみをインポートすると、その関数がどのパッケージに属するのかわからなくなる
from pkgA.modA.modB import funcA

# モジュールごとインポートする
import pkgA.modA.modB
```

# スペースの規約
```Py:インデントはスペース4つ
if a == b:
    print('yes')
```

```Py:辞書型の「:」の後はスペース1つ
d = {'A': 1000}
```

```Py:変数が2つ以上ある場合、「,」の後ろにスペース1つ
def example_func(a, b, c):
    print('example')

valA, valB = valB, valA
```

```Py:演算子の前後にスペース
x = y
a = b + c

# ただし、関数宣言時のデフォルト引数はイコール前後にスペース不要
def example_func(a, b, c='test'):
    print('example')
```

# 行の規約
```Py:行のあけ方
# importの下は2行空ける
# グローバルで宣言された関数やクラスの間は2行空ける
# クラス定義内メソッドの間は1行空ける

import os


class ExampleClass(a):
    """ExampleClass"""

    def __init__(self, b):
    self.b = b


def example_func(c):
    """example_func"""
    def sub_example_func(d):
        print('hello')
```

```Py:行の終わりにセミコロンをつけない
a = 1;
b = 2:
```

```Py:行の長さは80字を超えない
long_phrase = '80字以上でコーディングすると、非常に長くなって見にくくなるので、80字以内に収めるように記載して、長くなる場合は改行を活用してみやすくすること。'

# 但し、関数内docstringやコメントにURLを掲載する場合は80字超えてもOK
# URLについては改行するとコピーして貼り付けにくくなる
def example_func(a, b, c,
                 so_long_long_value_should_be_written_by_line_feed='example'):
    """
    :param a:
    :param b:
    :param c:
    :param so_long_long_value_should_be_written_by_line_feed:
    :return:

    Please refer to: http://www.abcdefg.com/hijklmn/opqrstu/vwxyz/again/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/
    """    
```

# 命名規約
```Py:クラスや関数の命名規則
# グローバル変数は、全て大文字で単語を「_」でつなぐ
# 大文字を使うことで間違えてグローバル変数を書き換えない目印にする
EXAMPLE_GLOBAL_NAME = 'example'

# クラス名は単語の頭を大文字にして「_」を使わずにつなぐキャメルケース
class ExampleClass(a):

# 関数は小文字の単語を「_」でつなぐスネークケース
def example_func(a):

# プロパティは処理を表すのではなく変数名のように書く
# returnで返すものは「_」をつける
@property
def func_name(self):
    return self._func_name
```

# 条件文の規約
```Py:ifの条件文で()を使う必要がない場合は使わない
if (xxx and yyy):
    print('True')

# 但し条件を見やすくする場合は()を使う
if (xxx and yyy) or (aaa or bbb):
    print('True')
```

# 例外処理の規約
```Py:全例外をExceptionにまとめない
# エラーの詳細がわからない
try:
    example_func()
except Exception as e:
    print(e)
```

```Py:クラスで独自の例外を作成してraiseする
# クラスの名前が例外処理に表示される
# どのファイルでエラーが発生したかわかりやすくなる

class ExampleError(Exception):
    def example_err_func(self):
        print('Example Error')

def example_func():
    try:
        func_a()
        raise ExampleError
```

# TODOコメントの規約
```Py:あとでコード変更予定する時のコメント
# 「#」のあとに1スペースあけてTODOと書く
# さらに1スペースあけて「()」に自分を示す文字列（メアドの@の前等）を入れる
# このコメントにより該当箇所について担当者に質問しやすい上、他の人による書き換えを防ぐ
# 合同プロジェクトやオープンソースプロジェクトなどではメールアドレスをそのまま記載

# TODO (your_name) Will change for fixinig bug no.15
```

[目次に戻る](https://qiita.com/yuta-sanjyo/items/416b05f8498df1ed3f49)
[過去記事]Vue.js v-text 指定された文字列中にhtml要素があっても文字列として表示する

## やること

v-textディレクティブを用いてhtml要素を文字列として表示する。

## 準備

下記の内容尾任意のhtmlファイルに記載して保存します。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <title>Document</title>
</head>
<body>
    <header></header>
    <main>
        <div id="app">

        </div>
    </main>
    <footer></footer>
    <script>
        let app = new Vue({
            el: '#app',
            data(){
                return {

                }
            },
        })
    </script>
</body>
</html>
```

## 方法

まずはscript要素内で「str」キーに紐づく「あああ <br> いいい」を定義します。

```html
<script>
    let app = new Vue({
        el: '#app',
        data(){
            return {
                str: 'あああ <br> いいい'
            }
        },
    })
</script>
```

div要素内を下記のように記載してv-textディレクティブにてキー「str」を指定して呼び出します。

```html
<div id="app">
    <div v-text="str"></div>
</div>
```

ファイル全体は下記の様になっています。

```html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://cdn.jsdelivr.net/npm/vue@2/dist/vue.js"></script>
    <title>Document</title>
</head>
<body>
    <header></header>
    <main>
        <div id="app">
            <div v-text="str"></div>
        </div>
    </main>
    <footer></footer>
    <script>
        let app = new Vue({
            el: '#app',
            data(){
                return {
                    str: 'あああ <br> いいい'
                }
            },
        })
    </script>
</body>
</html>
```

コレで準備は完了です！当該のhtmlファイルをブラウザで開くと下記の様に<br>タグがそのまま表示されていることが分かります。

![Document-21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/cf50f1fd-570a-57c7-e1d1-746986f0a90f.png)
テスト時に起きるエラー対処: Execution failed for task ':app:processDebugLocalGoogleServices'. 

## 概要
Androidのモバイルアプリ開発において、CD/CIの設定などで ./gradlew test　コマンドによるユニットテストを実行したい時がありました。しかし、Firebaseのエラーが起きてテストを実行できませんでした。
エラー内容は
```
Execution failed for task ':app:processDebugLocalGoogleServices'. 
```

バリアントを複数設定しているときにパッケージ名をうまく探せないようで起きてしまうようです。

## 解決策

環境を指定してテストを実行すると直りました。

```shell:
 ./gradlew testDebugUnitTest  
```

debugの箇所は設定しているビルドバリアントです。
例えば stage バリアントがあるのならば次のようでも良いです。


```shell:
 ./gradlew testStageUnitTest  
```

<参考>

https://developer.android.com/studio/test/command-line?hl=ja#run-tests-build-variants

## NGな解決策

次で紹介されているappレベルのgradleから
```gradle:gradle
apply plugin: 'com.google.gms.google-services'
```
を消す方法でもテストは動くようになるのですが、これはNGです。理由は明白で、Firebaseの公式で設定時に追加するように明記されているためです。消さないようにしましょう....

https://stackoverflow.com/questions/33572465/gradle-errorexecution-failed-for-task-appprocessdebuggoogleservices



https://firebase.google.com/docs/android/setup?hl=ja#add-config-file
【PHPフレームワークFlow】登録日や更新日などのカラムを自動で更新する

# 初めに
テーブルにデータを登録/更新する際、登録日や更新日のデータは自動で更新されるようにしたいですよね。DDLでTBLを定義している場合は簡単ですが、ORMの場合は独自の設定が必要になる場合がしばしばあるかと思います。
今回は、Flowで使われているORM「Doctrine」にて、登録日/更新日の自動設定方法を解説します。

# 環境
|技術スタック|バージョン|
|--|--|
|PHP|8.2.17|
|Flow|8.3.8|
|doctrine-orm|2.19.3|

# TL;DR
実装はこんな感じです。

```php
<?php
namespace Medi\Vega\Domain\Trait;

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\HasLifecycleCallbacks
 */
trait CommonTableTrait
{
    /**
     * @ORM\Column(type="datetime", nullable=false)
     * @var \DateTime
     */
    protected $createdAt;

    /**
     * @ORM\Column(type="datetime", nullable=false)
     * @var \DateTime
     */
    protected $updatedAt;

    /**
     * @ORM\PrePersist
     */
    public function onPrePersist()
    {
        $this->createdAt = new \DateTime("now");
        $this->updatedAt = new \DateTime("now");
    }

    /**
     * @ORM\PreUpdate
     */
    public function onPreUpdate()
    {
        $this->updatedAt = new \DateTime("now");
    }
}
```

ポイントは以下。
* クラスに、**`@ORM\HasLifecycleCallbacks`アノテーションを付与**
* 登録日と更新日のカラムを定義
* 登録日と更新日に現在時刻を入れるメソッドを用意し、**`@ORM\PrePersist`や`@ORM\PreUpdate`のアノテーションを付与**

複数のTBLで使えるようにtraitで表現しますが、ポイントを押さえれば各Entityでも同じことは可能です。


# 解説
Doctrineには「イベント管理」という機能があり、今回はそちらを利用しています。

https://www.doctrine-project.org/projects/doctrine-orm/en/current/reference/events.html#reference-events-lifecycle-events

Doctrineは、DBへのデータ登録前後や更新前後など、エンティティが特定のライフサイクルに達した際にトリガーされる処理（Lifecycle Callback）を定義することができます。
処理とトリガーはアノテーションによって紐づけることができ、例でつけている```@ORM\PrePersist```と```@ORM\PreUpdate```はそれぞれ「登録前」と「登録後」にトリガーされるイベントとして登録されます。

|トリガー|タイミング|
|--|--|
|```@ORM\PrePersist```|DBに保存される前のトリガー|
|```@ORM\PostPersist```|DBに保存された後のトリガー|
|```@ORM\PreUpdate```|DB更新される前のトリガー|
|```@ORM\PostUpdate```|更新された後のトリガー|
|```@ORM\PreRemove```|削除される前のトリガー|
|```@ORM\PostRemove```|削除された後のトリガー|
|```@ORM\PostLoad```|読み込まれた後のトリガー|

クラスについている```@ORM\HasLifecycleCallbacks```は、このクラスがライフサイクルコールバックを持っているということを明示的に表すためのアノテーションです。これにより、効率的なイベントハンドリングを実現しています。


# 終わりに

今回は登録日や更新日など、カラムを自動で更新する方法を解説しました。
独自でトリガーを作ったり、エンティティ外からトリガーする方法もあるみたいなので、今度試してみようと思います。
ここまで読んでいただきありがとうございました！
【Python】sys.exit(1)を使ってTrackback情報等を出力させずにプログラムを終了させる

# 概要
例外エラーの捕捉やエラーメッセージの出力を実装していると、余計な情報は出さすにプログラムを終了させたい場合や更なるエラーが出さないようにしたいケースがあります。そんな時に、`sys.exit(1)`を使うと有用だったので紹介します。

# サンプルコード

Pythonの組み込みモジュールである`sys`の`exit`関数を利用することで、プログラムを終了させることができます。
こちらを利用したコードが以下。

```py
import sys

def divide(x, y):
    if y == 0:
        print("エラー: 0で除算することはできません。")
        sys.exit(1)

    return x / y

print(divide(10, 0))
# エラー: 0で除算することはできません。
```

もし、`sys.exit(1)`がない状態だとどうなるでしょうか？

```py
import sys

def divide(x, y):
    if y == 0:
        print("エラー: 0で除算することはできません。")
        # sys.exit(1)

    return x / y

print(divide(10, 0))
# エラー: 0で除算することはできません。
# Traceback (most recent call last):
#   File "/Users/xxx/xxx/xxxx/test-sys-exit.py", line 10, in <module>
#     print(divide(10, 0))
#   File "/Users/xxx/xxx/xxxx/test-sys-exit.py", line 8, in divide
#     return x / y
# ZeroDivisionError: division by zero
```

上記の通り、Traceback情報（Pythonのデバッグ情報）などがずらずらと出力されます。

## 終了ステータスコードと`SystemExit`例外
指定した`sys.exit(1)`の引数`1`は終了ステータスコードと呼び、プログラムをエラー状態で終了させることを意味しています。

> 整数を指定した場合、シェル等は 0 は "正常終了"、0 以外の整数を "異常終了" として扱います。

`sys.exit([arg])`の公式ドキュメントはこちら。

https://docs.python.org/ja/3.7/library/sys.html#sys.exit

また。`sys.exit()`は、`SystemExit`例外を発生させ、これが捕捉されない場合は、Pythonインタープリタを終了する、という点も大事です。

> この例外は sys.exit() 関数から送出されます。Exception をキャッチするコードに誤ってキャッチされないように、Exception ではなく BaseException を継承しています。これにより例外は上の階層に適切に伝わり、インタープリタを終了させます。この例外が処理されなかった場合はスタックのトレースバックを表示せずに Python インタープリタは終了します。
> https://docs.python.org/ja/3.7/library/exceptions.html#SystemExit

つまり、以下のように実装することも可能ではあります。
本来プログラムを終了させるためのものなので、あまり使われるようなケースはないと思いますが。

```py
import sys

def divide(x, y):
    try:
        if y == 0:
            sys.exit("エラー: 0で除算することはできません。")
    except SystemExit as e:
        print("SystemExit例外が捕捉されました：", e)
        return "エラー"

    return x / y

print(divide(10, 0))
# SystemExit例外が捕捉されました： エラー: 0で除算することはできません。
# エラー
```

上記の場合、`SystemExit`例外が`try/except`ブロックで捕捉され、エラーメッセージが表示されますので、プログラムは終了せずに続行します。

## 引数にエラーメッセージ指定も可能

ちなみに、`sys.exit()`の引数にエラーメッセージを直接指定してあげることも可能です。

```py
import sys

def divide(x, y):
    if y == 0:
        sys.exit("エラー: 0で除算することはできません。")

    return x / y

print(divide(10, 0))
# エラー: 0で除算することはできません。
```


とはいえエラーの特定をしたい場合はTraceback情報のエラーメッセージやスタックトレースが大事になるので、その際はもちろん`sys.exit`を使用せずにデバッグ情報を確認できるようが有用でしょう。

https://docs.python.org/ja/3/library/traceback.html
コスパ最強の自作NASへの道 その1

※暇なときに定期的に気づいたこと追記してます。部品がそろってきたら追記します
　なお動作検証が十分でないためまだ使える製品なのかはわからないです。

# 安鯖の思い出

2007～2012年には、安鯖と呼ばれるOS無しのサーバ用PCが2万円程度で販売されていました。これらの製品には、通常OSは含まれず、500GB HDDとECCメモリ2GBほどのスペックが標準でした。価格が安いため、CPUはPentiumGなどの低スペック製品でしたがNAS用途としては十分な性能でした。なぜこれほどまでに超低価格で販売されていたのかは謎ですが、富士通、NEC、HPなどからは、サーバ市場でのシェア獲得を狙って、採算度外視で販売されたと言われています。代表的な製品の一つが、NEC Express5800シリーズの鼻毛鯖です。私もこのブームに乗り、当時約10台ほど各社の安鯖を購入した記憶があります。

初代HP ProLiant MicroServerは、CPUがAMD Turion II Neoという性能の低いものでしたが、HDDを4台搭載できる点が特徴でした。これをNASとして利用した方も多いと思います。私も予備機を5台購入し、10年以上にわたりこのMicroServerをNASとして愛用してきました。しかし、予備も使い果たし、いつ壊れてしまうかわからないため、新しいNASを探すことにしました。


# 最もコスパがいい機種は？

既製品の中にはコスパの高いNASも存在します。QNAP、Synology、ASUSTORなどのメーカーがその例です。しかし、CPUがx86アーキテクチャであったり、HDDを5台以上搭載できる機種はかなり高価です。安鯖と比較すると、やはり高額であり、購入をためらって数年が経過しました。ある時、海外の記事「DIY NAS: 2023 Edition」を見つけました。

https://blog.briancmoses.com/2023/03/diy-nas-2023-edition.html

この記事では、安価な中華製のマザーボード(Celeron N5105でSATAが6ポート搭載)を使用し、5台のHDDを搭載したコスパ最強の自作NASが紹介されています。これこそが私が求めていたものだと感じ、同様の構成を作ることを決意しました。

# 低価格CPU Intel N100の時代

現在、N100搭載の低価格ミニPCが大量に販売されています。NASとしてはこの程度のスペックでも十分です。N100でSATAが多数搭載されたマザーボードが発売されるのを待っていました。2024年になり、倍控科技(BKHD)よりBKHD-1264-NASマザーボードが発売されたため、これを購入してみることにしました。まだケースとHDDが手元にないため、今回はマザーボード単体のレビューとなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/f3948b49-f070-f834-7da0-2d14da6e3543.png)

※メモリやSSDが付属しないものを購入しまともなメーカーのメモリを購入するのがお勧めです

https://ja.aliexpress.com/item/1005006806004598.html

https://www.bkipc.com/en/product/1264-NAS-MB.html

# 6PortのSerialATA

このマザーボードには6つのSATAポートが搭載されています。1つはIntel Alder Lake-N PCH - SATA Controller (AHCI)であり、残りの5つはJMicron TechnologyのJMB585です。JMB585のポートを使って複数のHDDを同時に読み書きすると速度低下が生じる可能性があることを、下記の記事で確認しましたがまあ安いので気にしない。

https://forum.openmediavault.org/index.php?thread/51416-bkhd-1264-nas-intel-n100-mini-itx/

# 基板
CPUグリスは塗り直しました。(ainex HTC-03)
メモリやSSDはLenovoのPCから流用したあまり部品です

![DSC_4696.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/5078ffc7-2038-499a-88e4-61cac91969cd.jpeg)

![DSC_4674.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/c2dd5365-c54b-3062-707f-2ec8ab48921b.jpeg)

![DSC_4703.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/8e1155ae-5b2a-e7cb-cb71-433fb14a195c.jpeg)

![DSC_4724.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/413ff3b6-def6-a58a-adbd-1fa6782c277f.jpeg)

![DSC_4726.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/323b89a9-35db-7639-11b9-1276bfd5e35c.jpeg)

![DSC_4728.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/b32044e7-6f56-bd5a-75cf-f09a979214de.jpeg)

# ベンチマークと発熱

ファンがあるので負荷をかけても最大65℃程度でした。
ベンチマークの数値は一般的なN100 PCと同程度です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/ff134a75-6f71-9e0e-cfb8-56a57cb9a153.png)

# Forum
こちらのフォーラムがBKHDやCWWKマザーの情報が多いです。

https://forums.servethehome.com/index.php?threads/12gen-n-series-nas-motherboard-topton-cwwk.42432/
What's App Router?? What's Server Component?? What?? What??

## 簡単に自己紹介

著者のイナバくんは、某会社でフロントエンドエンジニアをしてます。

最近~~クソ暇~~とても時間があるので、自習がてらApp Routerを学んでいます。


## この記事で話すこと

- App RouterとPages Routerの違い
- Server ComponentとClient Componentの違い

## この記事で話さないこと

- App Routerの色んな便利な機能の解説
    - Dynamic Routerとか
    - Private Folderとかとか
- App Routerで使えるキャッシュ周り (勉強中)

## 今までのPages Routerは何だったの？

今までのNext.jsのルーティングは「Pages Router」を使っていました。

`/page` ディレクトリに`index.tsx`を置いて、`default export` するとページとしてルーティングされるアレです。
初めてNext.js触った時は簡単にSPAが作れて感動したのを覚えています。

しかし、Page Routerは古い機能となってしまいました。
Reactの新機能の`Server Component`に対応するため、Next.jsでApp Routerが開発されました。

## App Routerとは？

App RouterはPages Routerに代わる新しいルーティング方式です。
Pages Routerとはルールが全く異なります。

まず、`/app` ディレクトリに`page.tsx` を置くことでページとしてルーティングされます。
そして、App Routerではコンポーネントはデフォルトで**サーバーコンポーネント**となります。

## サーバーコンポーネントとは？

サーバーコンポーネントとは**サーバーサイドでのみレンダリングされるコンポーネント**になります。

サーバーコンポーネントとクライアントコンポーネントの違いは、下記になります。

- サーバーコンポーネント(RSC): **サーバーでのみ**レンダリングされるコンポーネント
    - 主にAPIリクエスト実装に使われる
    - クライアントサイドでレンダリングされないので、API keyなどを含めることができる
    - `useState`などのhooksを使えない(= ハイドレーションされない)
    - クライアントコンポーネントを含んで使うことができる
- クライアントコンポーネント: クライアント + サーバー でレンダリングされるコンポーネント
    - `useState`などのhooksを使える(= ハイドレーションされる)
    - サーバーコンポーネントは含められない

紛らわしいのが、従来のSSR(サーバーサイドレンダリング)ですが、**SSRはクライアントコンポーネントをサーバーサイドでレンダリングすること**なので、サーバーでのみレンダリングされるサーバーコンポーネント(RSC)とは異なります。

また、App Routerでデフォルトがサーバーコンポーネントなので、クライアントコンポーネントを使いたい場合は、ファイルの頭に

```tsx:/app/page.tsx:1
`use client`;
```
と記述する必要があります。

## サーバーコンポーネントで出来ること

- クライアントコンポーネントよりデータフェッチが速い(サーバーで実行されるため)
- コンポーネントをasync関数にすることで、内で直接データフェッチできる(`getServerSideProps`を使わない)
- クライアントサイドでレンダリングされないのでセキュリティ的にGood
- 様々なキャッシュを使えるらしい(よく知らん)

## まとめ

App Routerと使うと、Pages Routerの時よりもコンポーネントをサーバー or クライアント どちらでレンダリングするかを意識する必要があります。

また、今まではGraphQLのApolloとかTanStack QueryのようなAPIクライアントのライブラリでサーバーキャッシュを使っていましたが、App Routerではデフォルトでキャッシュがあるので、開発者はキャッシュ周りの戦略を考えないといけないですね。
Power Automate で フローボット(Workflows) を使って躓く前に

## はじめに
今回の記事では、Power AutomateでTeamsに投稿するフローを作成する際にやりがちなフローボットの使用時の躓きポイント・制限をご紹介します。
※フローボットは Teams 上では Workflows という名前で表示されます。本記事では「フローボット」と呼びます。

## フローボットとは
「チャットまたはチャネルでメッセージを投稿する」アクションでは、
投稿元として「ユーザー」「フローボット」「Power Virtual Agents」から選択することができます。
ユーザーとフローボットは既定で気軽に使用できるため、実際に使用されている方も多いのではないでしょうか。
フローボットからの投稿は「Workflows 経由のXXXX(接続元ユーザー名)」という形式で表示されます。
一方でユーザーからの投稿は通常の投稿と同様に表示されるため、自動投稿か手動投稿か区別がつきにくいことがあります。フローボットからの投稿は視覚的に自動投稿されたものであることや、接続元ユーザー名が明記されており、混乱を避けることができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457767/8dad4b7c-92c0-b1e7-0e6e-439a1ed4675a.png)


## 躓きがちなポイントとその解決策
フローボットからの投稿は上記のように大変便利ではありますが、Power Automate で Teams 投稿を作成する際にいくつか注意するポイントがあります。
作成時に出るエラーではなく、実際にフローが動かないと判別できないため、
「フローを作成したけど投稿ができない！何故！」とならないよう以下のポイントに注意してください。

### a. タグを使用したメンションができない
フローボット投稿の場合、タグを使ったメンションを使用することができません。
投稿内容に「@mention タグ」を入れた場合、以下のエラーとなります。

***「現在、タグのメンションはユーザーとして投稿する場合にのみサポートされています。投稿者の種類を「ユーザー」に変更するか、タグの「at」のメンションを削除してください。」***
```plaintext
Mentioning tags is currently only supported when posting as a user. Please change the poster type to user or remove the at mentions for tags.
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457767/ecd360c9-4fc1-7827-cbf3-bb49e20796fc.png)


タグはNGですが、ユーザーメンションは使用できます。
広範囲の人にメンション付き投稿をしたい場合は、ユーザー投稿でタグメンションするか、どうしてもフローボットからの投稿が良い場合はユーザー個別に指定する必要があります。

### b. プライベートチャネルへの投稿ができない
フローボット投稿の場合、プライベートチャネルへの投稿ができません。
投稿先にプライベートチャネルを指定した場合、以下のエラーとなります。

***「ボットは会話の一覧に含まれていません」***
```plaintext
Bot Framework に対する要求がエラーにより失敗しました: '{"error":{"code":"BotNotInConversationRoster","message":"The bot is not part of the conversation roster."}}'。
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457767/f2fef848-2929-71ca-b3e7-28754cc2e19f.png)


ボットがプライベートチャネルのメンバーではないので投稿できないようです。
接続元ユーザー名が入っていても、ボット自体がメンバーではないのでNGです。
プライベートチャネルにボットを追加することもできないため、残念ながらプライベートチャネルでは投稿をすることができません。
プライベートチャネルで投稿を自動化したい場合は、ユーザーからの投稿であることが必須となります。

### c. 件名入力が出来ない
エラーは出ず、視覚的にも分かりやすく、対策も可能ですが、フローボットからチャネルに投稿する際、アクションに件名欄がありません。そのため、件名を直接入力することができません。
件名のように見せたい場合は、***「18pt 太字」*** で書くとユーザー投稿で件名を入力したときとほぼ同じ見栄えの投稿が可能です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457767/064f0e63-e794-f9dc-b2ea-85246bf128b3.png)



## おわりに
私もフローを作成する際についつい忘れてエラーを出しがちなので、その悔しみの気持ちを何とかするために記事にしました。
少しでもフローボットを使用してつまずいた方の参考になれば幸いです。

また、Power Automate から Teams でメッセージを投稿する際の基本操作については、以下の公式ドキュメントをご覧ください。
[Power Automate を使用して Teams でメッセージを送信する - Power Automate | Microsoft Learn](https://docs.microsoft.com/ja-jp/power-automate/send-messages-teams)
Meta-Llama-3-8Bは『雪国』の冒頭をどうトークナイズするのか

「[Meta-Llama-3-8B](https://huggingface.co/meta-llama/Meta-Llama-3-8B)」がリリースされたので、早速トークナイザを試してみることにした。が、HuggingFaceの[token](https://huggingface.co/settings/tokens)がなかなか降りてこなくて、かなり時間がかかってしまった。

```
>>> from transformers import AutoTokenizer
>>> tkz=AutoTokenizer.from_pretrained("meta-llama/Meta-Llama-3-8B",token="hf_this_token_is_dummy_use_your_own_one")
>>> print(tkz.convert_ids_to_tokens(tkz("国境の長いトンネルを抜けると雪国であった。","夜の底が白くなった。")["input_ids"]))
['åĽ½', 'å¢ĥ', 'ãģ®', 'éķ·', 'ãģĦ', 'ãĥĪ', 'ãĥ³ãĥ', 'į', 'ãĥ«', 'ãĤĴ', 'æĬľ', 'ãģĳ', 'ãĤĭãģ¨', 'éĽª', 'åĽ½', 'ãģ§ãģĤãģ£ãģŁ', 'ãĢĤ', 'å¤ľ', 'ãģ®', 'åºķ', 'ãģĮ', 'çĻ½', 'ãģıãģª', 'ãģ£ãģŁ', 'ãĢĤ']
```

う、「国」が「åĽ½」ということは、もしや

```
>>> text="国境の長いトンネルを抜けると雪国であった。夜の底が白くなった。"
>>> x=tkz(text,return_offsets_mapping=True)
>>> for i,(s,e) in zip(x["input_ids"],x["offset_mapping"]):
...   print(text[s:e],text[s:e].encode("utf-8"),ascii(tkz.convert_ids_to_tokens(i)))
...
国 b'\xe5\x9b\xbd' '\xe5\u013d\xbd'
境 b'\xe5\xa2\x83' '\xe5\xa2\u0125'
の b'\xe3\x81\xae' '\xe3\u0123\xae'
長 b'\xe9\x95\xb7' '\xe9\u0137\xb7'
い b'\xe3\x81\x84' '\xe3\u0123\u0126'
ト b'\xe3\x83\x88' '\xe3\u0125\u012a'
ンネ b'\xe3\x83\xb3\xe3\x83\x8d' '\xe3\u0125\xb3\xe3\u0125'
ネ b'\xe3\x83\x8d' '\u012f'
ル b'\xe3\x83\xab' '\xe3\u0125\xab'
を b'\xe3\x82\x92' '\xe3\u0124\u0134'
抜 b'\xe6\x8a\x9c' '\xe6\u012c\u013e'
け b'\xe3\x81\x91' '\xe3\u0123\u0133'
ると b'\xe3\x82\x8b\xe3\x81\xa8' '\xe3\u0124\u012d\xe3\u0123\xa8'
雪 b'\xe9\x9b\xaa' '\xe9\u013d\xaa'
国 b'\xe5\x9b\xbd' '\xe5\u013d\xbd'
であった b'\xe3\x81\xa7\xe3\x81\x82\xe3\x81\xa3\xe3\x81\x9f' '\xe3\u0123\xa7\xe3\u0123\u0124\xe3\u0123\xa3\xe3\u0123\u0141'
。 b'\xe3\x80\x82' '\xe3\u0122\u0124'
夜 b'\xe5\xa4\x9c' '\xe5\xa4\u013e'
の b'\xe3\x81\xae' '\xe3\u0123\xae'
底 b'\xe5\xba\x95' '\xe5\xba\u0137'
が b'\xe3\x81\x8c' '\xe3\u0123\u012e'
白 b'\xe7\x99\xbd' '\xe7\u013b\xbd'
くな b'\xe3\x81\x8f\xe3\x81\xaa' '\xe3\u0123\u0131\xe3\u0123\xaa'
った b'\xe3\x81\xa3\xe3\x81\x9f' '\xe3\u0123\xa3\xe3\u0123\u0141'
。 b'\xe3\x80\x82' '\xe3\u0122\u0124'
```

ううう、UTF-8の[Byte-Pair Encoding](https://huggingface.co/learn/nlp-course/chapter6/5)がナマで使われてる(ただし0x00～0x20・0x7f～0x9fはU+0100～U+0120・U+0121～U+0141へマッピング)上に、「トンネル」が3バイト・5バイト・1バイト・3バイトの4トークンにブッタ切られている。カタカナが文字の途中で千切れちゃうとなると、とてもじゃないけど、このままじゃ使えない。しかも、そこを直しても「抜」「け」「ると」とか「白」「くな」「った」とか、こんなの日本語化どうしたらいいんだろ。
StreamlitとAltairを組み合わせた動的なグラフの作成方法

お疲れ様です。

今日は「StreamlitとAltairを組み合わせた動的なグラフの作成方法」について部分いたします。

AltairはPythonで動的なグラフを作成するためのライブラリです。

データの視覚化を宣言的に記述することができ、コードがシンプルで理解しやすいのが特徴です。

以下で解説していきます。

# Altairでの基本的なグラフ作成

AltairはPlotlyと同じく、良く使われるPythonのライブラリです。

直感的にデータの可視化を行えることから、初学者の方にも扱いやすいライブラリになります。

以下が、Altairを用いたデータの可視化のコードです。

```python
import streamlit as st
import altair as alt
import pandas as pd

# データの作成
data = pd.DataFrame({
    'a': list('CCCDDDEEE'),
    'b': [2, 7, 4, 1, 2, 6, 8, 4, 7]
})

# Altairでグラフを作成
chart = alt.Chart(data).mark_bar().encode(
    x='a',
    y='b'
)

# Streamlitで表示
st.altair_chart(chart)
```

上記のコードでは、Altairを使用して基本的な棒グラフを作成し、Streamlitで表示します。

`alt.Chart`でデータを指定し、mark_barでグラフのタイプを棒グラフに指定、encodeで軸を設定しています。

実際に上記のコードを実行し、右上の拡大するボタンを押すと、以下のグラフが算出されます。

![Screenshot 2024-04-19 at 8.31.46 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3507088/6b61a6ec-c928-5ed9-9921-dfe44a270da9.png)

簡単でデザインに優れたグラフを出力できるのが、Altairの強みです。

以下では、Streamlitとの組み合わせ方について解説していきます。

# StreamlitとAltairの組み合わせ方の例

StreamlitとAltairの組み合わせ方として、タブで表示するグラフのデザインを変えることを行ってみましょう。

Streamlitでは、タブ機能があり、表示する情報をタブで変更できます。

実際にタブを用いて、デザインを変更するコードは以下の通りです。

```python
import streamlit as st
import altair as alt
import plotly.express as px

df = px.data.iris()

chart = alt.Chart(df).mark_circle().encode(
    x='sepal_width',
    y='sepal_length',
    color='species',
).interactive()

tab1, tab2 = st.tabs(["Streamlit", "Altair"])

with tab1:
    st.altair_chart(chart, theme="streamlit", use_container_width=True)
with tab2:
    st.altair_chart(chart, theme=None, use_container_width=True)
```

`Chart`でグラフを作成し、`st.tabs`でタブを作成しています。

そして、`with`でそれぞれのタブの挙動を書いていき、`theme`を2つのタブで変更している状況です。

実際にコードを実行すると、以下のような表示になります。
![Screenshot 2024-04-19 at 8.33.05 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3507088/ee332a14-ab1b-8797-a98c-868481596179.png)

![Screenshot 2024-04-19 at 8.33.20 PM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3507088/807a393e-b44f-fb6f-38bd-4c9a63e4709a.png)

上記の2つの画像を比べると、グラフのデザインが少し異なっていますよね。

デフォルトでは上の方の画像であるStreamlitのデザインが採用されることを覚えておきましょう。

このように、ライブラリを使用する方法も覚えておくと、より多くのグラフを可視化できるようになります。

今日は以上です。

ありがとうございました。
よろしくお願いいたします。
未経験エンジニアが約3，4カ月目で初めて書いたマクロ

急に、そういえばこんなの作ったな～というのを思い出したので、
10年後くらいの自分が見て懐かしめるように、記憶を頼りに再現してみました。

## マクロを書いたきっかけ
・初めて参画したPJで、ある事象が発生した際に時間を記録する必要があった
・参画時は、全員が時間を手書きでメモってた
　→事象が多発した時大変だし、絶対手じゃない方がいいよね？と思い、書いてみよう！となりました。

## 作ったもの
たしかコードはこんな感じだった気がする...
```
Sub SetTime()

    Dim currentTime As Double
    Dim LastRow As Long
    
    currentTime = Time ' 現在の時間を取得
    
    HH = Format(currentTime, "HH:00")
    
    Select Case HH
    
    Case "9:00"
        LastRow = Cells(Rows.Count, 2).End(xlUp).Row
        Cells(LastRow + 1, 2).Value = Format(currentTime, "HH:MM:SS")
    Case "10:00"
        LastRow = Cells(Rows.Count, 3).End(xlUp).Row
        Cells(LastRow + 1, 3).Value = Format(currentTime, "HH:MM:SS")
    Case "11:00"
        LastRow = Cells(Rows.Count, 4).End(xlUp).Row
        Cells(LastRow + 1, 4).Value = Format(currentTime, "HH:MM:SS")
    Case "12:00"
        LastRow = Cells(Rows.Count, 5).End(xlUp).Row
        Cells(LastRow + 1, 5).Value = Format(currentTime, "HH:MM:SS")
    Case "13:00"
        LastRow = Cells(Rows.Count, 6).End(xlUp).Row
        Cells(LastRow + 1, 6).Value = Format(currentTime, "HH:MM:SS")
    Case "14:00"
        LastRow = Cells(Rows.Count, 7).End(xlUp).Row
        Cells(LastRow + 1, 7).Value = Format(currentTime, "HH:MM:SS")
    Case "15:00"
        LastRow = Cells(Rows.Count, 8).End(xlUp).Row
        Cells(LastRow + 1, 8).Value = Format(currentTime, "HH:MM:SS")
    Case "16:00"
        LastRow = Cells(Rows.Count, 9).End(xlUp).Row
        Cells(LastRow + 1, 9).Value = Format(currentTime, "HH:MM:SS")
    Case "17:00"
        LastRow = Cells(Rows.Count, 10).End(xlUp).Row
        Cells(LastRow + 1, 10).Value = Format(currentTime, "HH:MM:SS")
    
    End Select

End Sub

```

たしかデザインはこんな感じだった気がする...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3726019/aaccdaeb-d697-35eb-dcc3-a62eba344da2.png)
で、業務中はポチポチするだけだったから、こんな感じにして画面の片隅に配置して使っていた気がする...
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3726019/923a226e-97ff-e89b-66e7-3d145210d1e7.png)

## 作成したその後
周りの人も使ってくださいました！

50行にも満たないマクロですが、業後の時間を使って家で1~2日ヒイヒイ言っていた記憶があります。
無知ながら、セキュリティを気にしたり、99%勉強事を現場でやる罪悪感から、家で試行錯誤して出来上がったものを写真撮って、ちょっと早めに出勤して書き写していました笑

今は300行くらいで業後1日ヒイヒイしているので、だいぶ成長が見られます。嬉しい。

## ちなみに今書くのであれば・・・
```
Sub SetTime()

    Dim current_time As Double
    Dim last_row As Long
    Dim times() As Variant
    Dim col As Long
    
    current_time = Time ' 現在の時間を取得
    times = Array("9:00", "10:00", "11:00", "12:00", "13:00", "14:00", "15:00", "16:00", "17:00", "18:00", "19:00", "20:00") ' 記録したい時間を設定
    now_hour = Format(current_time, "HH:00")
    
    col = 2 ' 記録はB列から
    
    For Each time_item In times
    
        If time_item = now_hour Then
            last_row = Cells(Rows.Count, col).End(xlUp).Row
            Cells(last_row + 1, col).Value = Format(current_time, "HH:MM:SS")
            
            Exit For
        End If
        
        col = col + 1
    Next
    
End Sub
```
記録したい時間はセルから取得しようかとも思いましたが、そうそう変わるものでもないし、いっか。ということになりました。

## さいごに
VBAよりGASの方が好きです。
閉じなきゃいけないの多すぎて、よく怒られます。
ひろゆき語録でプログラミング


## 動機

「はいかいいえで答えてください」という語録がプログラミングっぽいなと感じたから。

## 代入

```
AはBであると見抜ける人でないと（プログラミングは）難しい
```

## 分岐

```:その1
例えば A だったとして
  hogehoge
って思うんすよ

例えば A だったとして
  hogehoge
そうでないなら
  fugafuga
って思うんすよ

例えば A だったとして
  hogehoge
そうでないなら
例えば B だったとして
  fugafuga
そうでないなら
  piyopiyo
って思うんすよ
```

```:その2
Aっていうのがあったりするので、
  hogehoge
するといいんじゃないかなぁと思いまーす

Aっていうのがあったりするので、
  hogehoge
そうでないなら
  fugafuga
するといいんじゃないかなぁと思いまーす

Aっていうのがあったりするので、
  hogehoge
そうでないなら
Bっていうのがあったりするので、
  fugafuga
そうでないなら
  piyopiyo
するといいんじゃないかなぁと思いまーす
```

## 反復

なし

## 関数定義とreturn

```
function-name(param1, param2, ...)ってあなたの関数ですよね？
  hogehoge
  なんだろう、実行するのやめてもらっていいですか return-value
```

## 関数実行

```
function-name(param1, param2, ...)すればいいと思いまーす
```

## 標準出力へ出力

```
Aだと思ってます。はい。
```

## その他

四則演算や余剰演算はC言語と同じ
（そんな真面目に考えないで）

## FizzBuzz

```
FizzBuzz(num)ってあなたの関数ですよね？

  (num > 100)っていうのがあったりするので、
    なんだろう、実行するのやめてもらっていいですか
  するといいんじゃないかなぁと思いまーす

  例えば(num % 15 == 0)だったとして、
    "FizzBuzz"だと思ってます。はい。
  そうでないなら
  例えば(num % 3 == 0)だったとして、
    "Fizz"だと思ってます。はい。
  そうでないなら
  例えば(num % 5 == 0)だったとして、
    "Buzz"だと思ってます。はい。
  そうでないなら
    numだと思ってます。はい。
  って思うんすよ

  FizzBuzz(num + 1)すればいいと思いまーす
  なんだろう、実行するのやめてもらっていいですか

FizzBuzz(1)すればいいと思いまーす
```

## その他、使えそうな語録

* なんすか写像って
* なんかそういうデータあるんですか？
* はいかいいえで答えてください
* データなんかねえよ
* 拳こそが正義
Archlinuxでパッケージのインストールに詰まった　404error

# 問題点
archlinuxでファイルのインストールを行ったところ。
壊れたファイルのインストールになり
```
404 error
```
が発生します

# 原因
原因はインストールしたファイルが古いことで現在のバージョンで対応しなかったことです。
正しいファイルはアップデートして取得する必要があります。

# 解決策
pacman -Syuは古いパッケージをアップデートして取得することができます。
```
pacmna -Syu [your-libraly]
```
これで解決しました。
【AWS Lambda + SES】令和以降にサーバレスアーキテクチャーにチャレンジしたらハマった

１．はじめに
久々の投稿ですが駄文だし半分ポエムです(8ねんぶりじゃないかこれ・・・)

オンライン注文フォームを以下技術要素で作りたかったのですが、CoPxlotさんが提供してくれるサンプルが（大マジで）軒並み動かずハマったのでその話です
  - AWS S3 静的Webサイトホスティング(にindex.htmlとorder.js)
  - AWS API Gateway
  - AWS Lambda (Runtime nodejs 18.x)
  - AWS SES

実装したかったのは、ブラウザからオンライン注文フォームにアクセスして、注文項目をメール本文に転写して送ってくれる物体です


２．何にハマったのか
Lambda関数がKickするはずのSESをサッパリkickしてくれなかった

３．結局何だったのか
nodejs 16.x以前と18.X以降でコードの書き方が違っていたのでリライトが必要だった
具体的には、AWS-SDK for Javascript v2からv3に移行が必要だった

＃別に実行ロールをへぼってたとかパラメータの渡し方が下手だったとかそういう話ではなかった

AWS公式のこの件に近い情報はこれ
https://repost.aws/ja/knowledge-center/lambda-send-email-ses
Qiita上の記事だとほぼドンズバはこれ
https://qiita.com/AAA0125/items/a15495a7b9e1137ef018

他にもいろいろなところでこの件に言及した情報が見つかったが、
サーバレスアーキテクチャでアプリケーション構築！という文脈ではめっちゃreachしづらかった

４．最大の学び
**コケたらCloud watchを穴が開くほど見つめれ**
**常に何を実装したいかに回帰しれ**
**ハマったら確認検証範囲が小さく小さくなるように実装範囲を分解しれ**

５．そのほか
+ 「～すごいことが作れる～」みたいなクリップをみたけど「すぐ」作るにはハードル高いのを実感
    + エンジニアとしての素養の下積みが超要る
    + しかもそれをちょっとひねって亜種として再吸収した方が使いやすい
    + アーンド、前提がすげー多い
    + 前提はしばしば使いたいSaaSの外にある
+ という苦労を突破するには、複数のサンプルコードを複数回試すとよいと思う
    + 複数回ですよ！同じのを！複数回！で、別のサンプルに移ってまた複数回！
    + 試している間は、前進している感じが全然ない
    + が、この前進している感じがないのが恐らく肝要
    + センター試験とかの点数水準アップ時の感覚に似ている（伝われ）
        + すでにできる問題を繰り返し解いても一切点数アップに繋がらない
        + 今できない問題に病みながら繰り返しアタックするとそのうち点数に繋がってくる


もしお読みいただいた方がいらっしゃれば、ご拝読心より深謝いたします
archlinuxでobs使って画面共有してみた

# 問題
archlinuxでdiscordの画面共有しようと、xlandvideobridgeのインストールしたら失敗したため、OBSを使って画面共有します。
下のコマンドでOBSをインストールしておいてください。
```
sudo pacman -S obs-studio
```
# 解決策
今回は仮装カメラを使った共有を行います。
そのため、下のコマンドでv4l2loopback-dkmsをインストールしてください。
```
sudo pacman -S v4l2loopback-dkms
```
これでOBSを開けると仮想カメラという項目が追加されています。
その状態でdiscordのカメラ共有を行うことでパソコンの画面が共有できます。
DB操作を行う機能で「Bad value for type long」が出た際の原因と解決策


# 事象
Webアプリケーションのある機能で例外が発生し、ログを確認すると「**Bad value for type long**」と出力されている。
この機能はDB操作を伴う機能であるため、発行したSQLをデバッグしてみたところ正常に動作し、SQL自体は間違ってなさそうなことが分かっている。
# 解決策
DBにTimestampで定義されているカラムをUNIX秒(Long型)で取得したい場合
SQL発行後にまずは`java.sql.ResultSet.getTimestamp()`で値を取得する。
その後、`java.sql.Timestamp.getTime()`を呼び出して、java.sql.Timestamp型からLong型のUNIX秒に変換することができる。
```java:修正後
Long lUnixTime = resultSet.getTimestamp("DATETIME").getTime();
```
# 原因
アプリケーション内でDBへのSELECT文発行結果を取得する部分のコードに問題があった。
DB内にTimestamp型で格納されているカラムに対して`resultSet.getLong()`してたことが原因であった。
```java:修正前
Long lUnixTime = resultSet.getLong("DATETIME");
```
上記のコードでは、タイムスタンプをUNIX秒に変換して取得したかったので`getLong()`を呼び出しているが、
この方法では例外が発生したため、実装の修正が必要であった。

ログ出力された`Bad value for type long`を検索してみたところ、`SQLException`によるものだということが分かった(`SQLException`が握りつぶされていることも分かった)。
Git Commit Messageで見える化しよう！

Git コミットをする際に適切なメッセージを書くことで
コードベースの理解と保守を容易にし、レビューもしやすくなり作業が円滑に進めやすくなります。

以下は、最近使っているGitのコミット・メッセージのテンプレートです。
Semantic Commit Messagesを参考にしています。

## 環境
モダン開発環境ではなく、No Frameworkのクラシックな環境で使っています。:older_man:
## Template
```text
[Title]<必須>
[Type]: [Subject] <必須>
# 空白行
[Description]
```
## ルール
### 厳守
[Type]の後ろに `:` コロンを付け半角スペースを１つ入れ、[Subject]を記載します。
```text
[Type]: [Subject]
```
[Type]と[Description]の間に１行分の空白行を入れます。
```text
[Type]: [Subject]

[Description]
```

### Title
Backlogの課題キーと件名やGitHub Issuesなど記載します。
この項目は必須とします。
### Type
プレフィックスを付ける形でコミットの種別を記載します。
最初の文字は大文字にする。
この項目は必須とします。
#### Typeの種類

- `Chore:` Projectに影響のないもの、直接な関係のないもの
- `Feat:` 機能の追加
- `Fix:` 軽微な修正からバグ修正まで含む
- `Add:` ファイルの追加
- `Change:` リネーム、ファイルの削除、フォルダ構成の変更など
- `Refactor:` 非推奨のコード、パフォーマンス改善のためにコードを変更した時
- `Format:` 空白行消したり、セミコロン付けたり、コードの整形した時など

### Subject
変更内容を短く簡潔に書く。
```
Feat: モーダルの実装
```
英語の場合は命令形で書くといいそうです。（検索しやすくなるとか）
```
Feat: Add Implement Modal
```
### Description
やったことを記載します。Subject同様に、短く簡潔にまとめる。
（長くても2行くらいまでに）
この項目は任意ですが、自分が後で見返したり、他の方（レビュワー）が見たときに
タスクの流れが理解できるようにしてください。

## 背景
私のチームでは人数が少ない事、業務もそこまで複雑でないこともあり、それまで
明確なコミット・ルールはありませんでした。
決まりごとはGitとBacklogの課題を紐づける位です。
そして人数が増え、業務も多岐に渡りと変化していきました。

業務上、過去のプロジェクトを遡る必要があったのですが、課題は発見できるが
「なぜこうしたのか？」が人の記憶に頼る状態であり、尋ねても案の定おぼえてないと。
このままではマズいのでルールを決める流れになりました。

![moses_umi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/84446/48b45aad-b33c-acd8-4301-f1a0c8cc8fab.png)

### 工夫したこと
[Type]の種類は多すぎて、コミットする際に迷いが生じては困るので絞りました。
例えば、修正を意味する`Fix:`も軽微な修正とバグ修正`BugFix:`とで分かれてたり、
文言修正は`docs:`でと紹介しているものが多かった印象ですが、業務の性質など考慮し
修正は`Fix:`でまとめるようにし、[Description]で補足すれば理解できるはず。

また、コード整形などを行った場合は`style:`を使うところが多かったのですが、
こちらも業務の性質上CSSと見間違う可能性があるのでシンプルに`Format:`にしました。

## 感想
まだ使い始めたばかりですが、コミット回数は増えるが**今、自分のやってる事**が
理解しやすく、戻りが減った感じはあります。

また、チーム・ディレクターからは変更点がわかるので確認しやすくなったと好評。
職種に関係なくチーム全員がプロジェクトの流れを掴みやすくなるって大切ですよね。

---------------------------------------
テンプレートを使用して、明確で一貫性のあるコミットメッセージを書くことで、
コードベースの保守性を向上させることができます！

## 参考
[Gitのコミットメッセージの書き方（2023年ver.）](https://zenn.dev/itosho/articles/git-commit-message-2023)
[Gitのコミットメッセージの書き方](https://qiita.com/itosho/items/9565c6ad2ffc24c09364)
[Semantic Commit Messages](https://gist.github.com/joshbuchea/6f47e86d2510bce28f8e7f42ae84c716) -  Josh Buchea
[Semantic Commit Messages](https://sparkbox.com/foundry/semantic_commit_messages) - Katie Jennings
Copilot for Microsoft 365で具体的に何ができるのか試してみる ～Power Point編～

# はじめに
Copilot for Microsoft 365がすごいらしい…ということで、具体的にどんなことができるのか試してみたので共有します。<b>～Power Point編～<b>

## この記事の対象者
- Copilot for Microsoft 365がPower Pointで出来ることを知りたい人

<b>👇Wordで出来ることを知りたい人はこっち<b>

https://qiita.com/matoia/items/51e9a9aaf760ca3f598f

## Copilot for Microsoft 365とは
- GPT-4をTeams、Word、Excel、PowerPoint、Outlookに組み込んで利用できる
- 生産性と創造性が高まる＆作業時間を節約できる（下図参照）

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713212/75b309ca-2ad4-04f2-1e4d-4d8b31710f9a.png)
※ 上図は下記サイトから引用

https://news.microsoft.com/ja-jp/features/231124-copilots-earliest-users-teach-us-about-generative-ai-at-work/

# Copilot for Microsoft 365がPower Pointで出来ること
大きく分けて下記の３つが出来ます。
①プレゼンテーションを作成する
②プレゼンテーションを編集する
③プレゼンテーションについて理解する

## ① プレゼンテーションを作成する
### A. ユーザー指示でスライド複数生成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713212/1dd47c2a-0e1d-9ef5-82eb-43fd83907f9e.png)

### B. 別ファイル参照でスライド複数生成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713212/4fda5e81-1396-e4ba-c57d-bcd68fa468ee.png)

:::note warn
別ファイルはOne Driveの中に保存してください。ローカルだと参照できません。
:::

### C. ユーザー指示でスライド１枚生成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713212/79a568b8-d23b-0a67-5f62-4bd83d1b6a2e.png)

### D. その他テクニック
:::note info
Copilotが勝手にプレゼンテーションの見た目を作ってしまうので、デザイナーで選んで、見た目を調整することをお勧めします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713212/bd848536-0e03-f7b2-ff41-f68adead7cd4.png)
:::

:::note info
最初にテンプレートを選んでから、Copilotでプレゼンテーションを作成すれば、全てのスライドで統一したテンプレートにすることができます。
<b>※ 会社で決まったテンプレートがある場合は、この手順で作ってみてください。<b>
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713212/609f3a22-9d93-34c8-1a57-d23ce4dca48d.png)
:::

## ② プレゼンテーションを編集する
:::note warn
スライドの文章はCopilotで修正できないため、下記のどちらかで対応します。
- Copilotを使わずに、通常のPowerPoint操作で文字を編集する
- ①-C「ユーザー指示でスライド１枚生成」した後に、既存のスライドを削除する

※海外の動画ではCopilotで文章修正していたので、そのうち使えるようになるかも
:::

### A. イメージを追加する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713212/cb32600f-5f60-e475-d26c-d293f95e3d9b.png)

### B. 構成を整理する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713212/2e386e15-8dd4-5484-c97e-fa9c87715481.png)

## ③プレゼンテーションについて理解する
### A. 要約する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713212/936f031c-fe69-e225-5145-0463e17b3dbd.png)

### B. キースライドを表示する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713212/d61d6098-8464-5f25-7457-29f59570bcef.png)

### C. 質問する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713212/f78d0b0b-4005-9e30-c84d-97974c28ebcc.png)

:::note info
プレゼンテーションの①作成/②編集/③理解のプロンプトテンプレが他にもあるので、それを使うこともできます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3713212/bb1a60bc-1e76-8e60-e043-6310abb57eb8.png)
:::

# おわりに
WordのCopilotと同じく、一回作った文章をCopilotで修正できないのがちょっと惜しいなぁ・・・とは思いました。すぐその機能は実装される気はしますが。
なので、Copilotで最初にプレゼンテーションを作り上げてしまって、その後の編集はPower Pointで操作するとかで、ボチボチ使い続けてみます。
[個人開発] オリジナルの図鑑を作って共有できるサービスを作りました

## はじめに
はじめまして。フルコシと申します。
2023年1月よりプログラミングスクールRUNTEQにてRuby on Railsを中心に学習し、現在はエンジニア転職活動中です。
この度ポートフォリオとして「[図鑑クリエイト](URL "https://www.illustrated-books-create.com
")」をリリースいたしましたのでご紹介させていただきます。

https://www.illustrated-books-create.com

このサービスは生物オタクの私が **「みんなちゃんと生き物見てる？」** という問いを皆さんに投げかけるため作成したものです。
見て・考えて・調べて・アウトプットすることで生き物可愛い！の先へ到達する助けになれば幸いです。

:::note warn
初学者です。
内容に誤りがある場合があります。
また、このサービスはPC操作推奨です。
:::

## サービス概要
### 図鑑クリエイト
URL: https://www.illustrated-books-create.com
GitHub: https://github.com/furukoshi12/zukan-create
![top.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3536453/da18f86f-377f-17cc-346e-f6298fb318cb.png)

- 画像１枚と複数のテキストボックスを使用して図鑑を作成
- 自分が投稿した図鑑と他のユーザーが作成した図鑑を表示
- 図鑑の編集やお気に入り登録(要ユーザー登録）

## 使用技術
Ruby on Rails 7.0.0
React 18.2.0
Heroku
Amazon s3
Google　Cloud Vision　API

## ER図
![ER図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3536453/fbac9ca2-4a8f-134e-3399-8e633f3d4525.png)
※薄字は実装予定


## 機能
### 図鑑作成
テンプレート選択＋個々でテキストボックスを追加できます。
これらはリサイズとドラッグ&ドロップができます。
画像は１枚のみ追加できます。
追加した各要素は×ボタンで削除できます。
![template.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3536453/b96ee5a0-acba-fea8-7711-7daff619f590.gif)
![field.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3536453/0ad65ed1-e8dd-9a03-3534-e940f7eae74a.gif)

図鑑にはタグを追加できます。
入力後エンターキーを押すと追加でき、×ボタンで削除できます。
![tag.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3536453/af43845f-a2f3-cab5-bb07-82bee4d33d10.gif)


TextReaderというモーダルを開くと画像を解析し、テキストを抽出することができます。
抽出されたテキストはコピーボタンを押せばワンクリックでコピーできます。
これは[Google Cloud Vision API](https://cloud.google.com/vision?hl=ja)を使用しました。

簡単に実装方法を説明します。
Rails側でフォームから送られてきた画像のurlをエンコード
``` 
def fetch_encode_image(uploaded_file)
    begin
      file_path = uploaded_file.tempfile.path
      image = MiniMagick::Image.open(file_path)
      blob = image.to_blob
      Base64.strict_encode64(blob)
    rescue => e
      nil
    end
  end
```
エンコードしたものをリクエストとしてAPIに投げます。
```
  def analyze_image
    encoded_image = fetch_encode_image(@image)
    return nil unless encoded_image

    url = URI("#{@base_url}?key=#{@api_key}")
    http = Net::HTTP.new(url.host, url.port)
    http.use_ssl = true
    request = Net::HTTP::Post.new(url)
    request["Content-Type"] = "application/json"
    request.body = request_body(encoded_image).to_json

    response = http.request(request)
    JSON.parse(response.body)
  end

  private

  def request_body(encoded_image)
    {
      requests: [
        {
          image: {
            content: encoded_image
          },
          features: [
            {
              type: 'TEXT_DETECTION',
              maxResults: 10
            }
          ]
        }
      ]
    }
  end
```
この後、レスポンスはjson形式でReact側に送信してます。（controllerに記述）

### 一覧表示
マイページで自分の作成した図鑑一覧とお気に入り登録した図鑑が見られます。
みんなの図鑑では自分と他のユーザーの投稿した図鑑が見られます。
タイトルか画像をクリックすると詳細ページに遷移します。

### 編集
マイページから図鑑の編集ができます。
入力内容、テキストボックス関係、タイトル、画像、タグの編集ができます。

### Xへ共有
マイページの共有ボタンからXに投稿を共有できます。

## おわりに
完全未経験から学習をはじめ、１つのサービスを作るところまでいくのはなかなか骨が折れる作業でした。
ですが、自分の考えたものを形にすることは楽しく、出来れば嬉しいものです。
このサービスで生き物との出会いがもっと面白くなったらいいなと思います。
最後までご覧いただき、ありがとうございました。
ReactのPopperButton内でAPI通信を実施する

## 成果物
![Apr-19-2024 18-40-26.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599049/745ea14b-a27e-3a83-d940-aac72e5f1643.gif)

## ソースコード
```tsx:src/components/PopperButton.tsx
/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useState, useEffect } from 'react';
import Button from '@material-ui/core/Button';
import Popper from '@material-ui/core/Popper';
import Paper from '@material-ui/core/Paper';
import Typography from '@material-ui/core/Typography';
import TextField from '@material-ui/core/TextField';
import { makeStyles } from '@material-ui/core/styles';

const useStyles = makeStyles((theme) => ({
  paper: {
    border: '1px solid',
    display: 'flex',
    flexDirection: 'column',
    padding: theme.spacing(1),
    backgroundColor: theme.palette.background.paper,
  },
}));

export const PopperButton = () => {
  const classes = useStyles();
  const [anchorEl, setAnchorEl] = useState<HTMLButtonElement | null>(null);
  const [open, setOpen] = useState(false);
  const [data, setData] = useState<any[]>([]);
  const [searchText, setSearchText] = useState('');

  // eslint-disable-next-line react-hooks/exhaustive-deps
  const fetchData = async () => {
    const url = `https://jsonplaceholder.typicode.com/posts${searchText ? `?userId=${searchText}` : ''}`;
    const response = await fetch(url);

    const jsonData = await response.json();
    setData(jsonData);
  };


  useEffect(() => {
    const delayDebounce = setTimeout(() => {
      if (searchText) {
        fetchData();
      }
    }, 500); // 500ミリ秒のディレイでAPIコール

    return () => clearTimeout(delayDebounce); // コンポーネントアンマウント時にタイマーをクリア
  }, [fetchData, searchText]);


  const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
    setAnchorEl(anchorEl ? null : event.currentTarget);
    setOpen((prev) => !prev);
  };

  const handleInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    setSearchText(event.target.value);
  };

  return (
    <div>
      <Button variant="contained" color="primary" onClick={handleClick}>
        Toggle Popper
      </Button>
      <Popper open={open} anchorEl={anchorEl} placement="bottom" transition>
        {() => (
          <Paper className={classes.paper}>
            <TextField
              value={searchText}
              onChange={handleInputChange}
              label="User ID"
              variant="outlined"
              fullWidth
            />
            <Typography variant="h6">API Data:</Typography>
            {data.map((item, index) => (
              <Typography key={index}>{item.title}</Typography>
            ))}
          </Paper>
        )}
      </Popper>
    </div>
  );
};

```