OmniAICreator/Qiita-1.07M huggingface dataset:
【個人開発】Android アプリ開発初心者が Google Play に 14 回リジェクトされた話

# はじめに
初めまして、まみめです。
私は 2023 年の 9 月に Android アプリ開発を始めて今日まで 3 回リリースを行ってきました。
初めてのアプリ開発は大変難しく試行錯誤しながらなんとかリリースを行ってきました。
しかし、3 個目のアプリのリリースでなんと Google Play に 10 回以上のリジェクトを受けてしまい、かなり解決に苦しみました。
多くは私の知識・技術・注意力の不足によるものです。Android アプリ開発に慣れている人であれば当たり前の内容ですが、始めたばかりの人にとって少しでも助けになればと思い、この記事を書きました。皆様の助けになれば幸いです。

# リリースしたアプリの概要
###  リリースしたアプリ
『カラオケ楽曲ガチャ』

https://play.google.com/store/apps/details?id=com.karaoke19.karaokegacha&hl=ja&gl=US

![スクリーンショット1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551352/d7ec233d-29e1-af50-64a9-994fc70bb967.png)

カラオケで歌う曲をランダム選んでくれるアプリです。
リストに曲名、アーティスト名、キー、動画の URL などを登録することができます。
ぜひダウンロードしてみてください！

# リジェクトの経緯

### 1 回目のリジェクト

実はこのリジェクトはすでにリリースしたアプリをアップデートするときにおこりました。
いつものようにアップデートしようとすると、Google Play からこのようなメールが来たのです。

![スクリーンショット (197).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3551352/b028632c-b8d1-cbb1-8e79-fbb29ed49cbe.png)

「否承認」という言葉にギョッとしました。
しかし、このメールを見た時、原因はすぐに思い当たりました。
なぜなら、ユーザーの入力の仕方によってはリアクションが発生しないボタンもあることにすぐ気づけたからです。
修正して再度アップデートしたバージョンをリリースします。

### 2 回目のリジェクト

次に来たのは、「アプリがクラッシュする」という内容に加えて、「プライバシーポリシーに不備がある」という内容です。
プライバシーポリシーの Web サイトのスクリーンショットも送られてきたのでびっくりしました。
これは Web サイトにコメント機能があり、編集可能であると判断されてしまったみたいでした。
これは Web サイト側でコメント機能を停止すると解決しました。
クラッシュの件は少し悩みましたが思い当たりました。データベースの扱いで少し不備があったのです。
これも修正し、再びアップデートバージョンを Google Play Console にアップロードします。

### 3 回目のリジェクト

次に来たのは、「反応しないボタンがある」という内容です。
すでに修正したのになぜ？？
この問題は以降 14 回目まで悩みました。
これ以降「反応しないボタンがある」と「クラッシュする」という内容が交互に来たのです。
私は実機でテストするときに Logcat で挙動が怪しい部分を特定しようとしました。
しかし、何回やっても怪しいところは見つかりません。
しかし、Toast を使うときに少しおかしなメッセージが出てきて、このメッセージに惑わされていました。
これは Android 12 仕様のバグであり、今も解決していないみたいです。

https://issuetracker.google.com/issues/202863198

### 4～14 回目のリジェクト

めぼしい成果が得られず、アップデート自体を断念しようと思ってたところ、ついにその時は訪れました。
普段は実機でテストしていたのですが、今回は少しバージョンが古い Android エミューレータでテストしたときボタンを押すとクラッシュしたのです。
原因は、Kotlin のコードが 1 文字間違っていたことでした。
すぐに修正し、審査に出すと難なく通過しました。

なぜ、「反応しないボタンがある」と「クラッシュする」という内容が交互に来たのでしょうか？
これはおそらくですが、ボタンを押したときにクラッシュすると、Google Play の審査で「反応しないボタンがある」のか「クラッシュしている」のか区別ができないのでないかということだと思います。
これをどちらかに決める方法は、ボタンすべてに Toast を付けることです。
これによって反応しないボタンがあるという状況がなくなり、否承認にメッセージは「クラッシュする」しかなくなり、特定が容易になります。
推理小説みたいですが、私はこんな風にして原因を特定していきました。

# 教訓
今回のたくさんのリジェクトで私が感じた教訓は以下の通りです。

### 実機だけでなく、エミュレータで複数のバージョンでテストする

実機だけでは単一の環境下での実験になり、挙動の状態を確かめるのに不十分でした。今度から Android Studio 上で様々なバージョンの Android でテストしていきたいと思います。

### 他の人にテストを依頼する

私は一人でテストをしていたため、気づくのが遅れてしまいました。知り合いの Android ユーザにテストを依頼するべきでした。テストをするだけであれば、特別な技術がなくてもできるのですから。

### もっと Android の知識を増やす

思えば、Android のデータベース周りの知識不足が今回のリジェクトにつながったと思います。さらに勉強する必要がありそうです。

ちなみに、異議申し立ては全くしませんでした。一般的に実装のバグで Google Play Store に異議申し立てをしても解決する可能性は低いと思います。
それだったら自分のミスを追跡する方に時間を割いた方が結果的に早くリリースできることにつながりそうです。
しかし、ネットで異議申し立てが受理されたケースもちらほら見たので、もしどうしてもおかしいと思った時は異議申し立てをするのも一つの手段かと思います。

# 最後に

拙い初心者の記事を読んでくださりありがとうございました。
皆さんもぜひリリースするときは気を付けてみてください！
unity"アイテムに近づいたら取得ボタンを出す方法"

# 完成イメージ
![スクリーンショット 2024-04-29 151758.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3780171/ee6b9484-53d4-3b42-cccc-8010fdbf98b7.png)
近づいたらボタンが表示され、離れたらボタンの表示は消える

## 実装
近づく判定→OnTriggerStay(Collider other)
離れる判定→void OnTriggerExit(Collider other)
を使って実装していきます。
これらはcolliderが反応している間だけ実行を行ってくれる関数です。


1.鍵の方にplayerを判定するためのcolliderをセット
2.playerのtagを"Player"へ変更
3.鍵の子オブジェクトにパネルを非アクティブ状態で作成。
4.パネルにボタンをセット。
5.以下のコードを鍵側へ実装し、Panelに表示したいパネルをセット。
```
public GameObject Panel_1;
    public GameObject Key;
    void OnTriggerStay(Collider other)
    {
        if (other.CompareTag("Player"))
        {
            //プレイヤーが圏内にいるときの処理
            //ボタン等を設置したPanelを表示させる。
            Panel_1.SetActive(true);
        }
    }
    void OnTriggerExit(Collider other)
    {
        if (other.CompareTag("Player"))
        {
            //プレイヤーが圏内から出た時の処理
            //Panelを非表示にする。
            Panel_1.SetActive(false);
        }
    }

    public void GetKey()
    {
        Destroy(Key);
    }
```
今回は後に取得したアイテムを破壊したかったためGetKey関数を設定しています。
割り当てられたGameObject(今回はKey)を破壊するための関数です。

### 完成
これで近づいたらボタン表示がされるようなシステムの完成です。
Player側に空のオブジェクトを作り、そこにアイテムとのcolliderの判定を実装もしてみましたがこちらはなぜかうまくいきませんでした。とりあえず現時点で不都合はないのでこれで良しとします。
githubのコミットを削除方法

## 問題
githubのコミットを削除したい

## 対処法
https://notion-fan.com/posts/git-delete-commit/

## 補足
https://qiita.com/ChaaaBooo/items/459d5417ff4cf815abce

https://qiita.com/shuntaro_tamura/items/db1aef9cf9d78db50ffe
GithubでSSH認証に失敗した時

## 問題
githubでsshkeyの公開鍵を登録しようとした時にエラーが出た場合

![スクリーンショット 2024-04-29 15.19.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3726068/6dfc6496-88f1-3329-dcae-59b97a07aff5.png)

## 対処法2つ
①SSHkey自体をリセットする方法
②デプロイ用のSSHkeyを複数作る方法

## ①SSHkey自体をリセットする方法
ssh -i [○○.key] [サーバーID]@[サーバー情報].xserver.jp -p 10022
↓
ll -a
ll .ssh
で中身w確認
↓
以下2つのファイルを削除
/ssh/id_rsa.pub
/ssh/id_rsa
→rm /ssh/id_rsa.pub
→rm /ssh/id_rsa
↓
新しく上二つのファイルを作成
ssh-keygen
→質問全てenter
↓
ll -a
でできている確認


削除の参考

https://qiita.com/muscle_eng/items/9e6633c57fc9c36ae268

## ②デプロイ用のSSHkeyを複数作る方法
https://a1026302.hatenablog.com/entry/2021/02/04/111443#%E5%AF%BE%E5%BF%9C%E6%96%B9%E6%B3%95
法律相談できるLINE bot「LegalAI」をリリースしました

# LegalAIとは
残業やパワハラの問題を抱えているけど、法的な知識がない人が法律相談できるLINE botを開発しました。
友達追加はこちら↓
https://lin.ee/KmWvcGp
![ScreenShot01.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3780785/213af21d-98d8-4717-8494-7686caacd55b.jpeg)

# 技術
インフラ：firebase
サーバー：Node.js
AI：openai（BaseModel:gpt-3.5）

# 開発の流れ
firebase + Node.jsでLINE botを作る記事は山程あるので、割愛。

初めてopenaiのfine-tuningと作ったプログラムとの連携を行ったので、そちらをメインで書きます。

## openaiのfine-tuning
最初にBaseModelを選定します。

> https://platform.openai.com/docs/guides/fine-tuning
> We expect gpt-3.5-turbo to be the right model for most users in terms of results and ease of use.
> gpt-3.5-turbo は、結果と使いやすさの点で、ほとんどのユーザーにとって適切なモデルであると考えられます。

openaiのfine-tuningのドキュメントにこう記載があったので、gpt-3.5-turboに決めました。

次にfine-tuingのデータを用意します。
LegalAIのデータはこんな感じで用意しました。
```
{"messages": [{"role": "system", "content": "あなたは法律のスペシャリストで、残業やパワハラに悩んでいる人が相談するチャットボットです"}, {"role": "user", "content": "労働時間の問題をどう対処すればいい？"}, {"role": "assistant", "content": "労働時間の問題に対処するためには、まず問題の詳細を理解することが重要です。具体的には、以下のような情報が必要になります：\n\n労働時間の状況：労働基準法で定められている労働時間を超えて働かされているのか、または残業代が支払われていないのかなど、具体的な状況を教えてください。\n労働契約書：労働契約書や就業規則には、労働時間や残業に関する取り決めが記載されていることがあります。これらの文書の内容を確認してください。\n証拠の収集：残業時間の記録、労働時間の記録、給与明細など、問題を証明するための証拠を集めてください。\n対処方法には以下のようなものがあります：\n\n会社との話し合い：まずは直接、上司や人事部門に問題を報告し、解決を求めることが重要です。合理的な解決策を提案し、話し合いで解決を図りましょう。\n労働基準監督署への相談：会社との話し合いで解決しない場合、労働基準監督署に相談することができます。労働基準監督署は、労働基準法に基づく労働条件の適正な運用を監督し、労働者の権利保護に関わる問題の相談に応じています。\n労働組合への加入または相談：労働組合に加入している場合は、組合を通じて問題解決を図ることができます。組合がない場合でも、地域の労働組合や労働相談機関に相談することが可能です。\n法的措置の検討：上記の手段で解決しない場合は、法的措置を検討することも一つの選択肢です。労働問題に詳しい弁護士に相談し、訴訟などの法的手段を取るべきか検討してください。\nこれらのステップを踏む前に、具体的な状況についてもっと教えていただけますか？それによって、より具体的なアドバイスを提供できるかもしれません。"}]}
```
ファイルの形式は、jsonl形式です。
fine-tuningは最低10件以上のデータが必要なので、10件以上はデータを作りましょう。

次に、fine-tuningを実行します。
fine-tuningは、openai apiのダッシュボードから実行しました。
画面をポチポチして、ファイルをアップロードして待つだけでfine-tuningが完了です。

CloudFunctionsのNode.jsからfine-tuingしたモデルを使います。
```
export const sendOpenAi = async (message) {
    const url = 'https://api.openai.com/v1/chat/completions';
    const headers = {
        "Content-Type": "application/json",
        "Authorization": "Bearer **************",
    };
    const data = {
        model: "ft:gpt-3.5-turbo-1106:personal::****",
        messages: [],
        temperature: 0.2
    };
    data.messages.push({
        role: "user",
        content: message,
    });

    const res = await axios({
        url,
        method: 'post',
        headers,
        data
    });

    if (res.status !== 200) {
        return '通信に失敗しました';
    }

    return res.data.choices[0].message.content;
}
```

# 今後の課題
過去の会話の流れを汲み取るようにしたいけど、その分お金がかかっちゃう。
料金を抑える方法があったらコメントください。

# まとめ
開発自体はサクッと終わったので、openaiに興味がある人の参考になれば嬉しいです。
仕事で悩んでいる人が周りにいたら、共有してあげてください。
【CSS】複数行のテキストを三点リーダーで省略する方法

以下のように記述すると複数行のテキストを三点リーダーで省略できます。

```css
p {
  display: -webkit-box;
  -webkit-line-clamp: 4;
  -webkit-box-orient: vertical;
  overflow: hidden; 
}

```

上記の場合には4行を超えると三点リーダーで省略されます。
AWS S3 年月日パーティションの圧縮ログファイルを Athena で効率的に検索する方法

## S3 年月日形式の階層構成

AWS S3に大量のログデータを効率的に管理する際、年月日形式でのパーティション分割が選択肢に挙がります。  
本記事では、HiveスタイルでAthenaから参照できるようにする方法を紹介します。  
この設定により、データクエリが高速化し、運用管理がスムーズになります。  

```bash
# ログファイルS3の指定パスにアップロードします。
aws s3 cp your-log-file.log s3://your-bucket/logs/year=$(date +%Y)/month=$(date +%m)/day=$(date +%d)/
```

このコマンドは、現在の年月日に基づいて階層を自動的に作成し、ログファイルをその場所にコピーします。  
この状態でも後述手順でパーティション分割が行えるので効果が得られます。  
また、次のログファイルの圧縮化も行うことで、さらなる効率化にもつなげられます。  

## S3 に圧縮したログファイルを配置する方法

ログファイルを圧縮することで、ストレージコストを削減し、転送時間を短縮することができます。

```bash
# ログファイルを圧縮した後、S3の指定パスにアップロードします。
gzip your-log-file.log
aws s3 cp your-log-file.log.gz s3://your-bucket/logs/year=$(date +%Y)/month=$(date +%m)/day=$(date +%d)/
```

## Athena で参照するために Partition を付与したテーブルを作成する方法

Amazon Athena で S3 上のデータを効率的に検索するためには、Partition を設定する必要があります。

```sql
-- 上の手順でS3に準備した年月日階層とPARTITIONED指定を一致させる
CREATE EXTERNAL TABLE IF NOT EXISTS your_log_table (
    example_field STRING,
    another_field INT
)
PARTITIONED BY (year STRING, month STRING, day STRING)
STORED AS PARQUET
LOCATION 's3://your-bucket/logs/';
```

次に、Partition を追加・反映する必要があります。  
コマンドの主な目的は、テーブルのメタデータを更新し、新しく追加されたパーティションを認識させることです。  
具体的には、このコマンドは S3 のデータをスキャンし、新しいパーティションを追加します。

```sql
-- 実行によりPartition を追加・反映する
MSCK REPAIR TABLE your_log_table;
```

## Athena で検索する手順

Partition が設定されたテーブルを Athena で検索するには、以下の SQL クエリを使用します。

```sql
SELECT * FROM your_log_table
WHERE year = '2023' AND month = '04' AND day = '26';
```

このクエリは、指定した日付のデータを検索します。


## LambdaとPythonでのboto3を利用したパーティション管理方法

S3 年月日形式の階層は増えることが予想されます。  
そうした変化に対応したLambdaとPythonを使用して、自動的に新しいパーティションを追加し、管理する方法を紹介します。  
boto3ライブラリを活用して、定期的にパーティションの追加を行うスクリプトを作成します。  

```python
import boto3

def lambda_handler(event, context):
    athena_client = boto3.client('athena')
    query = "MSCK REPAIR TABLE your_log_table;"
    
    response = athena_client.start_query_execution(
        QueryString=query,
        QueryExecutionContext={
            'Database': 'your_database'
        },
        ResultConfiguration={
            'OutputLocation': 's3://your-query-results-bucket/'
        }
    )
    
    return response
```
【電子工作メモ】3端子レギュレータ 3.3V出力

# 3端子レギュレータのメモ
:sunny:メモですがコメント貰えれば自分の知っている知識の範囲で答えます！:sunny:

モチベーションはESP32を動作させること
* ESP-WROOM-32[[秋月電子]](https://akizukidenshi.com/catalog/g/g111647/)

試す3端子レギュレータは以下
1. 低損失三端子レギュレーター 3.3V1A LM2940T-3.3
購入先：[秋月](https://akizukidenshi.com/catalog/g/g114505/)
データシート：[PDF](https://akizukidenshi.com/goodsaffix/lm2940.pdf)
1. 三端子レギュレーター 3.3V AZ1117CH-3.3TRG1
購入先：[秋月](https://akizukidenshi.com/catalog/g/g116155/)
データシート：[PDF](https://akizukidenshi.com/goodsaffix/AZ1117C.pdf)

| | LM2940T-3.3|AZ1117CH-3.3TRG1|
|:-:|:-:|:-:|
|出力電圧|3.3 V|3.3 V|
|出力電圧精度|±1%|±1%|
|出力電流max|1 A|1.35 A|
|入力電圧max|26 V|15 V|
|出力正負|正電源|正電源|
|出力タイプ|定電圧|定電圧|
|ドロップアウト電圧|0.4 V|1.2 V|
|動作温度|-40〜85℃|-20〜125℃|
|パッケージ|TO220-3L![Screenshot from 2024-04-29 14-39-15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/625784/ff3be72c-a365-715d-80b4-8827492c1e0b.png)|SOT223/H![Screenshot from 2024-04-29 14-43-31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/625784/bf748ec9-5f4e-4c08-c389-fa997081e6a5.png)|
|ピンアサイン|1-VIN<br>2-GND<br>3-VOUT|1-GND<br>2-VOUT<br>3-VIN|

## LM2940T-3.3の動作回路
* 入力側Vinには少なくとも1 uF以上のCが必要
* 出力側Voutには少なくとも10 uF以上のCが必要

![Screenshot from 2024-04-29 14-45-04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/625784/85849f6b-6def-935c-cc4c-1109c3d858e1.png)

## AZ1117CH-3.3TRG1の動作回路
* 積層(ESR)セラミックコンデンサに対応
* 出力側コンデンサのセラコンの抵抗は10 Ω以下
* コンデンサは10 uF以上
![Screenshot from 2024-04-29 14-54-01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/625784/12481bcc-7547-b273-3361-4dd66ea0a82b.png)
VMware Workstation Player の VMware DHCP Service が起動しない(No subnet declaration for VMnet1)

# ■事象
`VMware DHCP Service`が起動しておらず、手動で起動しても失敗します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/205a704f-9073-12d5-d481-c9f6707e44ff.png)


起動失敗時にWindowsイベントログ/Systemに以下の内容が表示されます。
```
ログの名前　システム
ソース　　　VMnetDHCP
レベル　　　エラー

No subnet declaration for VMnet1 (192.168.152.1).  
Please write a subnet declaration for the network segment to which interface VMnet1 is attached.
```

```
ログの名前　システム
ソース　　　VMnetDHCP
レベル　　　エラー

No subnet declaration for VMnet8 (192.168.254.1).  
Please write a subnet declaration for the network segment to which interface VMnet8 is attached.
```



# ■対応
## 1.ネットワークアダプタ設定
ネットワークアダプタの設定にて、`vmnet1`および`vmnet8`のNIC設定をDHCP化します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/4529bad7-436e-ffa9-0972-750136c4305c.png)

## 2.レジストリ設定
vmnet1とvmnet8に対して同様の作業を実施します。

### 2.1 vmnetdhcp.confの確認
以下ファイルを開いて設定値を確認します。
`C:\ProgramData\VMware\vmnetdhcp.conf`
→`host VMnet1`の`fixed-address`に記載されている値を確認。

```text:vmnetdhcp.conf
host VMnet1 {
    hardware ethernet 00:50:56:C0:00:01;
    fixed-address 192.168.48.1;　　　　　　　←ここのIPアドレス
    option domain-name-servers 0.0.0.0;
    option domain-name "";
}
```


### 2.2 値の作成
レジストリに設定する値を作成します。
IPアドレスを16進数に変換し、オクテットを逆順に並べたものを作成します。

```text:convertion
192.168.48.1    #元IPアドレス
↓
c0.a8.30.1      #16進数変換
↓
1.30.a8.c0      #オクテットを逆順
↓
130a8c0         #ドットを除去
```



### 2.3 レジストリ修正
レジストリ設定で以下を開きます。
`\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\VMnetDHCP\Parameters\VirtualEthernetSegments\1`

パラメータ`HostIpAddress`の値を、2.2で作成した値に



![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/a8d71607-1033-efe6-ad3e-080cc094d7ba.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/201787/228af22e-feeb-3a13-8cff-6e1b0c9417f0.png)


## 3.サービス再起動
サービス画面から`VMware DHCP Service`を起動します。

# ■環境
Windows 11
VMware Workstation 17 Player

固定IP利用のため`vmnetdhcp.conf`は編集済み。
事象発生前にvmnet1およびvmnet8相当のネットワークアダプタが消失。
VMware workstastion playerの再インストール後に事象発生。



# ■参考
https://randronov.blogspot.com/2015/10/vmware-error-vmnetdhcp-no-subnet.html

https://munini.622style.com/munini/blog/?e=1737
SpringBootのThymeleafでEntityのForEach例外起きた時の対応

:::note warn
根本の原因調査までしてないです。
参考情報として見て下さい。
:::

# 現象

Entityは下記のようにlombokのDataを使ってgetterとsetterをコーディング不要にしていました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/0b19d028-a1a4-6723-1702-3fc1144641d4.png)

そのEntityをリストに詰めて、ThymeleafでForEachした時に例外になりました。
『idというフィールドは存在しませんよ』的な例外の文言でした。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/3e41d0cc-01cd-1584-75f1-01a9f0e75f34.png)

# 対応

おそらくSTS4にlombokがインストールされていなかったのが原因のようでした。
コーディング中、『Entityのインスタンスを入れた変数Ctrl + Spaceをしてもgetterやsetterの候補が出てこなかった』です。

STS4のプロジェクト下の『Project and External Dependencies』。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/07a0b4dd-2745-2d3b-a2b2-104bff6442bb.png)

lombokのjarを見つけて…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/3ac1a184-060f-8083-f716-da6dc6c47b8b.png)

右クリックから『Run AS』 ⇒ 『Java Application』。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/e1c94db4-0c36-acb6-9be3-5844b3dbe09b.png)

lombokのインストーラー起動します。
しばらく待つと「STS4にインストールするよ」ってチェックボックス出るので、『Install/Update』。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/a16b912a-c1d2-0712-7d65-a7685adbf164.png)

STS4をいったん終了して再度起動。

# 蛇足

人の手で『STS4のlombokバージョン』と『プロジェクトのlombokバージョン』を揃える必要がありそうな予感…。

予想だけどThymeleafの動作はプロジェクトに依存すると思います。
だからSTS4にlombokインストールして直ることが不自然な気が…。
うーん…。

# 参考サイトさん

https://qiita.com/Naka_You/items/432d7a8ed7eb127b53f1

# バージョン

Microsoft Windows [Version 10.0.22631.3527]
JAVA 17.0.10
Spring Boot v3.1.11
【Git】 よく使うgitコマンドまとめ

メモ用。都度追加する。

## バージョン確認
```bash
git --version 
```

## ユーザー名の確認
**グローバル設定**：システム全体に適用されるデフォルト設定
```bash
git config --global user.name 
```
**ローカル設定**：特定のリポジトリでのみ適用される設定
```bash
git config user.name
```

## 今いるブランチを確認
```bash
git branch
```

## ブランチ作成
```bash
git checkout -b [新しいブランチ名]
```

### リモートの特定のブランチから作りたいとき
```bash
git checkout -b [新しいブランチ名] [リモートリポジトリ名/リモートのブランチ名]
```

## pull
```bash
git pull [リモート名] [ブランチ名]
```

## ブランチ名を間違えた時
```bash
git branch -m worng_branch_name correct_branch_name
```

## ファイルごとの差分を見たい時
```bash
git diff ファイルのパス
```

## addした内容を消したい時
```bash
git checkout ファイルのパス
```
## add して staged（インデックス）状態になっている変更を元に戻したい
```bash
git restore --staged . //全部
git restore --staged ファイルパス //こb
```

## 現在のブランチとリモートブランチの差分を確認する（どのコミットがpushされるか確認する）
```bash
git log origin/<ブランチ名>..HEAD
```

## PR出したあとにコミットを取り消したい時
```bash
git reset {戻りたいコミット番号}
```

※「変更があるよ。」と怒られたら
```bash
git checkout -- . 
```

やりたい変更をやる
```bash
git add .
git commit -m ""
```

リモートにpush　（だいたい「最新じゃないよ」と怒られるので、-f で 強制push）
```bash
git push -f origin ブランチ名
```

## PR出したあとにコミットを修正したい時
修正したいファイルを編集し、内容を保存

```bash
git add ファイル名
```

コミットを上書きする
```bash
git commit --amend
```

修正したコミットを強制push
```bash
git push origin ブランチ名 --force
```


## 短縮コマンドを作成する

```terminal
git config --global alias.(設定したい省略系) (元のコマンド)
```

```bash
#例
git config --global alias.co checkout
git config --global alias.bv ‘branch -vv’
```
## コミットメッセージを変更したい時
```bash
git add -A
git commit -m "htmlを修正"

#今のコミット、cssの修正だった！書き直したい…！
git commit --amend -m "cssを修正"
```

## stash 作業内容を退避したい
### 現在の変更を退避する 

```bash
git stash 

# 名前をつけたい時
git stash save "最強のmemo" 
```


### 退避した作業一覧を確認する　　
```bash
git stash list
```

新しいものから上に表示される。

```bash
stash@{0}: WIP on master: 36af2d1 added index.txt 
stash@{1}: WIP on master: f0d73fe added readme
stash@{2}: WIP on master: 3faa214 feat readme
```

### 対比した作業を元に戻す
```bash
git stash pop

# 指定する
git stash pop stash@{1}
```

## ブランチ切って作業始めるとき
```bash
git fetch 
git pull upstream master  
git checkout -b new_branch
```

## ローカルで他人のPRを動作確認したい時
```bash
git fetch upstream pull/{PR番号}/head:{ブランチ名}
git co {ブランチ名}
```

## revert
過去のコミットを打ち消す新しいコミットを作る
```bash
git revert abc1234
```
- コミット abc1234 を打ち消す新しいコミットを作成
- 履歴を残しつつ、変更を取り消す
- 変更を元に戻す「逆の操作」をする新しいコミットの追加

## reset
コミット履歴自体をなかったことにする
```bash
git reset --hard HEAD~1
```
- コミット履歴を書き換える
- ローカルでしか使わないのが基本（force push しないと共有リポジトリに反映できない）
- -hard は要注意！消える！

## cherry-pick
ある特定のコミットだけを、今のブランチに取り込む
```bash
git cherry-pick <コミットハッシュ>
```
使いどころ
- 他のブランチの特定の修正だけを取り込みたい
- リリースブランチに、特定のバグ修正だけ反映したい

- 間違って別のブランチにコミットしてしまった内容を移したい
Raspberry Pi 3 B+ 3.5inch LCD problems Fix 



Back up the config file before trying.

## Issue 1
Just showing white screen.

## Solution
0. cd LCD-show  sudo ./LCD35-show
1. raspi-config > advanced > Wayland > select X11
2. sudo nano /boot/firmware/config.txt
     Add 
        dtoverlay=piscreen,drm
    at the end of lines.
3. reboot

## Issue 2
Touch screen XY works as inverted.

## Solution
1. cd /usr/share/X11/xorg.conf.d/
2. Open and edit 99-calibration.conf or something similar.
3. Add 
    Option "TransformationMatrix" "-1 0 1 0 -1 1 0 0 1"
   at Section.
4. Reboot 

90° = "0 -1 1 1 0 0 0 0 1"
180° = "-1 0 1 0 -1 1 0 0 1"
270° = "0 1 0 -1 0 1 0 0 1"

## /boot/firmware/config.txt
On working condition with LCD35-show

    hdmi_force_hotplug=1

    dtparam=i2c_arm=on
    
    dtparam=spi=on
    enable_uart=1
    
    dtparam=audio=on
    dtoverlay=waveshare35a
    
    hdmi_force_hotplug=1
    
    hdmi_group=2
    hdmi_mode=1
    hdmi_mode=87
    hdmi_cvt 480 320 60 6 0 0 0
    hdmi_drive=2
    dtoverlay=piscreen,drm

On working condition with LCD-hdmi(Goes wrong in my case just dtparam=audio=on remaining)
    
    hdmi_force_hotplug=1
    dtparam=audio=on
    camera_auto_detect=1
    display_auto_detect=1
    auto_initramfs=1
    dtoverlay=vc4-kms-v3d
    max_framebuffers=2
    disable_fw_kms_setup=1
    disable_overscan=1
    arm_boost=1
    [cm4]
    otg_mode=1
    [all]

Manually pasted it into the config.txt every sudo ./LCD-hdmi
And add dtoverlay=piscreen,drm again if want to sudo ./LCDxx-show(add after sudo ./LCDxx-show)

## Comment
使用品はKuman 3.5inch touch screen と3 B+
Raspberry Pi OS 32-bit 2024-03-15
CompTIA CySA+（CS0-002）受験記録

## 概要
表題の通り、CySA+（CS0-002）の受験記録・勉強方法となります。
参考になれば幸いです。

- 取得済み資格：FE, AP, SC, CompTIA Security+

- 勉強時間：4時間

- 使用(購入)した教材：
・TAC CySA+（CS0-002）Web模擬試験コース（リテイクチケット付）
・Udemy問題集 (購入したが使用せず)


## 受験の流れ
基本的な流れは Security+ と同じです。
勉強方法について、今回はTAC模擬試験を1周したのみです。
詳細については以下を参照ください。
　
※本試験より2カ月ほど前に Security+ を受験
[CompTIA Security+（SY0-601）受験記録](https://qiita.com/nagi4472/items/3d50c69f9fe644595447)	
　
結果
合格ライン：750
スコア：780/900
合格


## 感想
試験配信終了1週間前 (https://www.comptia.jp/certif/cybersecurity/comptia_cysa/) というギリギリのタイミングでなんとか一発合格ができました。こちらの記事を見ている方は CompTIA CySA+（CS0-003） を受験するのかと思いますが、流れは同じであると予想されるため、十分に参考になるかと思います。

基本的な流れは Security+ 受験の際に学んでいたので、勉強時間も非常に少なく抑えることができました。しかし、余裕があるならば、TAC模擬問題集だけではなく、Udemyの問題集も抑えておいた方が確実であるかと思います。事実、今回はTAC模擬問題集だけであったため、スコアも合格ラインを少し越えた辺りでした。

問題の傾向についても、Security+ の延長といった程度の問題で、そちらの試験に合格している人なら、特に苦労せず合格できるかと思います。( Security+ とは範囲も違うため、CySA+の勉強は必須ではある)


次回は PenTest+ を受験する予定です。
そちらも合格次第、同じように記事にできればと思います。
それでは、以上で失礼します。
【Python】住所から郵便番号を特定しちゃおう

# はじめに

マーケティングの会社で働くデータサイエンティストのRyo_kinaです。

ある日、こんな相談を受けました💡
　ダイレクトメールを送りたい！
　宛名と住所一覧はあるけど、郵便番号がない！
　⇒15,000件くらいあって、なんとかならない？？

それくらい、すぐつけれるっしょ！　Qiitaさ～ん！と思って調べてみると、出てくるのは
「郵便番号を住所に変換する方法」ばかり・・・

世の中、住所がわかる＝郵便番号もわかる　のケースが多数派なのか・・・( ;∀;)



# 試行錯誤の結果

https://zipcoda.net/doc

こちらのAPIは
住所を投げると、郵便番号候補を返してくれる
という、とっても素敵なAPIです✨

（他力でクリアすることにしました。作者様に感謝です！）

# APIの利用テスト

```python:Post_transrate.py
# API呼び出し用のrequestsライブラリを準備
!pip install requests

import requests

# 住所⇒郵便番号　　住所は前方一致で検索してくれるので、番地のフォーマットが適当でも問題ない。
# 途中の市区町村が間違っていると導出できない。
url = " https://zipcoda.net/api"
payload = {"address": "愛知県名古屋市昭和区鶴舞一丁目1番155号"}
# 名古屋市で最も大きい「鶴舞図書館」の住所を入力

r = requests.get(url, params=payload)

print(r.text)
```

実行すると、下記のようなアウトプットになります。

```python
'{"status":200,"length":2,"items":[{"zipcode":"4660064","pref":"愛知県","components":["愛知県","名古屋市昭和区","鶴舞"],"address":"名古屋市昭和区鶴舞"},{"zipcode":"4660000","pref":"愛知県","components":["愛知県","名古屋市昭和区"],"address":"名古屋市昭和区"}]}'
```

ここから、最も可能性が高い郵便番号を取り出すことで、完了です。

```python
r.json()["items"][0]["zipcode"]
# '4660064'
```

# 複数住所に、郵便番号を付与する

上記動作を関数にし、Pandasのデータフレームに対して、まとめて動作するようにします。

今回は、例として「名古屋市の図書館を対象いに、住所から郵便番号を検索するコード」を作成します。

```python
import pandas as pd

# データの作成
data_list = [
    {'館名': '鶴舞中央図書館', '郵便番号\u3000正解': '〒466-0064', '所在地': '名古屋市昭和区鶴舞一丁目1番155号'},
    {'館名': '千種図書館', '郵便番号\u3000正解': '〒464-0803', '所在地': '名古屋市千種区田代町字瓶杁1番地の137'},
    {'館名': '東図書館', '郵便番号\u3000正解': '〒461-0047', '所在地': '名古屋市東区大幸南一丁目1番10号'},
    {'館名': '北図書館', '郵便番号\u3000正解': '〒462-0037', '所在地': '名古屋市北区志賀町4丁目60番地の31'},
    {'館名': '楠図書館', '郵便番号\u3000正解': '〒462-0012', '所在地': '名古屋市北区楠二丁目974番地'},
]

# list を DataFrame　に変換
data_df = pd.DataFrame(data_list)

# DataFrameを表示
print(data_df)
```

こんな感じになります。正解付きです。
```
        館名    郵便番号　正解                   所在地
0  鶴舞中央図書館  〒466-0064    名古屋市昭和区鶴舞一丁目1番155号
1    千種図書館  〒464-0803  名古屋市千種区田代町字瓶杁1番地の137
2     東図書館  〒461-0047     名古屋市東区大幸南一丁目1番10号
3     北図書館  〒462-0037   名古屋市北区志賀町4丁目60番地の31
4     楠図書館  〒462-0012       名古屋市北区楠二丁目974番地
```

次に、住所を郵便番号に変換する関数　を記載します。

```Python
# 住所を渡すと、郵便番号を返してくれるコード
def ret_post(target):
    import time
    import requests
    
    url = " https://zipcoda.net/api"
    payload = {"address": target}

    r = requests.get(url, params=payload)

    time.sleep(0.1) # 連続アクセスによる負荷を抑えるため、0.1秒待つ。
    return r.json()["items"][0]["zipcode"]

# 関数の実行確認
ret_post("名古屋市昭和区鶴舞一丁目1番155号")

# '4660064'
```
ret_postに、住所を渡すと、郵便番号を返してくれます💡
いい感じですね！


では、最後にデータフレームの所在地列に対して、実行していきましょう。

```Python
# apply関数を使用して郵便番号を取得し、新しい列として追加
data_df['郵便番号'] = data_df['所在地'].apply(ret_post)

data_df
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3091004/9737fd03-b75a-7e0f-fb3a-35d1a1c37ebf.png)

以上のように、無事に郵便番号が付与できているのが確認できました！

:::note warn
注意
#### "字"が入っていると、うまくいかないケースもあるようです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3091004/689081ab-d2ce-72d1-2d4d-493700c95b6a.png)
:::

# 最後に
いかがでしたでしょうか？
無事に、郵便番号を付与することができましたね！



【参考にしたサイト】
・名古屋市図書館の住所一覧
　https://www.library.city.nagoya.jp/guide/map.html
・PythonでWeb APIを叩く
　https://qiita.com/hisshi00/items/9806eceeee2237624222
Rubyの等値を判断するメソッド･演算子の理解[equal?, ==, eql?, ===]の違い

# equal?, ==, eql?, ===の違いを解説

この記事は下記のような人を対象にしています。
- 駆け出しRubyエンジニア
- プログラミング初学者

## 結論
equal・・・同じobject_idの場合にtrueを返す
== ・・・人の目で見て自然であればtrueを返す
eql・・・同じクラスで中身も同じならtrue、中身が同じでも別クラスだったらfalseを返す
=== ・・・case文のwhen節で暗黙的に使われている

## equal?
equal?メソッドは、同じobject_idの場合にtrueを返します。
```ruby
a = "apple"
b = "apple"
a.equal?(b)
=> false
```
文字列の内容が同じappleなのに、falseなのは、aとbそれぞれが持つobject_idが異なるから。

では、object_idとは？？

オブジェクトにランダムに割り当てられる、重複しない整数のこと！

Rubyでは、重複しない整数が、各オブジェクトに1つずつ割り当てられています。
各オブジェクトに対して一意な整数を返します。あるオブジェクトに対してどのような整数が割り当てられるかは不定です。
```ruby
a.object_id
=> 76740
b.object_id
=> 92660
```
一方で、変更できないImmutable(変更不可)なオブジェクトの一部は、同じ内容ならば必ず同じobject_idを返します。↓

TrueClass, FalseClass, NilClass, Symbol, Integer クラスのインスタンス。

## ==
==はオブジェクトの内容が等しいかどうかを判断します。
当然以下はtrueになります。
```ruby
"apple" == "apple"
=> true
```
さらに、データ型が違っても、人の目で見て自然であればtrueを返すことがあります。
1はinteger(整数型)、1.0はfloat(浮動小数点数型) 
```ruby
1 == 1.0
=> true
```

## eql?
クラスによっては、==よりも厳しい等値判定をします。
```ruby
1.eql?(1.0)
=> false
```
同じクラス同士、つまりintegerはinteger、floatならfloat同士でしかtrueを返さないのです。
```ruby
1.eql?(1)
=> true
1.0.eql?(1.0)
=> true
```
eql?は、ハッシュキーを区別するときに利用されます。
```ruby
1.hash
=> 928416955
1.0.hash
=> 952311353

# 1と1.0は別のハッシュ値になるため、それぞれ異なるキーとして扱われる
{1 => "James", 1.0 => "Steph"}
```
ハッシュ値とは、あるデータから特定の計算手順によって求められる、一見ランダムな整数値のこと。

## ===
===(トリプルイコール)は、case文のwhen節で暗黙的に使われています。
以下のコードの内部的には、

String === value
Array === value
Hash === value

の結果が評価されています。
```ruby
value = {
  "red" => "apple",
  "yellow" => "pineapple",
  "blue" => "blueberry"
}

case value
when String
  puts "文字列です"
when Array
  puts "配列です"
when Hash
  puts "ハッシュです"
end
#=>ハッシュです
```

# おわりに
今回はrubyの等値を判断するメソッドや演算子をまとめてみました。
この記事が、rubyの初学者の方や、ruby silverの受験を考えている方達に、少しでもお役に立てたら幸いです！！

# 参考
[プロを目指す人のためのRuby入門[改訂2版]](https://www.amazon.co.jp/E3-83-97-E3-83-AD-E3-82-92-E7-9B-AE-E6-8C-87-E3-81-99-E4-BA-BA-E3-81-AE-E3-81-9F-E3-82-81-E3-81-AE/dp/4297124378/ref=dp_ob_title_bk)　

https://docs.ruby-lang.org/ja/latest/method/Object/i/object_id.html

https://techracho.bpsinc.jp/hachi8833/2018_07_04/58707

https://qiita.com/tbpgr/items/a1f231999910cd48ec58
Azure Arc data servers、Azure IoT Operationsの3月アップデートまとめ

# はじめに
4/3に行われたアダプティブクラウドコミュニティコールよりAzure Arc data servers、Azure IoT Operationsのアップデートを紹介します。レコーディングは以下で公開されています。

https://www.youtube.com/watch?v=owggYf4SZMQ&list=PLZuSmETs0xIaKJAgANkDuspTO4HG3y_xs&index=10

# Arc-enabled data servicesのアップデート

- 2023年11月更新
    - 自動バックアップ、リストア
        - デフォルト無効だが、Azure Portalから構成可能
        - バックアップをAzureにするものではなくローカルで保持可能。ポリシーの管理をAzureから行う
    - 可用性グループ インベントリ・状態（パブリックプレビュー）
        - Azure Portalから可用性グループのインベントリと状態を確認可能
    - Azure Arcを活用したSQLパフォーマンス監視
        - Azure PortalからSQL Serverの性能（約70のメトリクス）情報を確認
- 2024年2月更新
    - Migration Readiness アセスメント for SQL server（パブリックプレビュー）
        - 追加コストなしで、Azure SQL マネージドインスタンス、SQL Server on Azure VM、Azure SQL DBへの移行の準備ができているかアセスメントを行うことが可能
        - 稼働中のワークロードからおすすめのSKUがわかる

# Azure IoT Operationsのアップデート
- Azure IoT Operations パートナー・カスタマープログラム
    - パブリックプレビュー中のAzure IoT Operationsについて、製品へのアーリーアクセスやロードマップ共有を通じて市場を先取りした製品やソリューション開発

# まとめ
Azure Arc data servers、Azure IoT Operationsについての3月アップデートをまとめました。Azure IoT Operationsは昨年のIgniteで発表された比較的新しい製品・サービスでありパートナープログラムを通じて実際の利用シーンにおける要望を製品チームにフィードバックすることで、製品開発に反映してもらうことができそうです。
タスク管理アプリ③ 〜ログインユーザーのid・nameを取得し、ビューに表示させる〜

### はじめに

今回は、前回作成したログイン機能でログインしたユーザーのid・nameを取得し、リダイレクト先のviewに表示させることを目的としています。
※学習用なので、セキュリティ面は考慮できていません。

前回の記事↓
https://qiita.com/kuro_maru/items/7662cd11603ac70d70f9

### 初回アクセス時の遷移先をログイン画面にする

ユーザーが初回アクセスした際に、デフォルトだとwelcomeページへ遷移するようになっているので、`/login`に遷移するよう設定していきます。

```routes/web.php
// ログインページへリダイレクト設定
Route::get('/', function () {
    return redirect()->route('login');
});
```

:::note info
redirect()->route();
route()で指定したルートにリダイレクトさせることができます。
:::

▼以下の記事が分かりやすかったです
https://qiita.com/manbolila/items/767e1dae399de16813fb

### ログインユーザーのidとnameを取得する

ログイン処理は、laravel bleezeを使用し行っているので、すでにcontrollerなどは存在している状態です。

```app/Http/Controllers/Auth/AuthenticatedSessionController.php
public function store(LoginRequest $request): RedirectResponse
    {
        $request->authenticate();

        $request->session()->regenerate();

        // ログイン情報からidを取得
        $userId = Auth::user()->id;

        // リダイレクト先に、user idを渡す
        return redirect()->route('dashboard', ['id' => $userId]);
    }
```

:::note info
Auth::user()
ログイン情報からユーザーのデータを取得します。
※Auth::user()->id; これでユーザーidの取得を行っています。
:::

### ルートに関連したコントローラーが呼び出される

```routes/web.php
// dashboardへのルート設定 ※今後増やす予定なのでgroupにする
Route::middleware('auth')->group(function () {
  Route::get('/dashboard', [DashboardController::class, 'getUserData'])->name('dashboard');
});
```

:::note info
routes/web.php, routes/api.phpを参照するタイミングについて
① リクエストがアプリケーション(laravel)に到達
② アプリケーションは、リクエストURLと一致したルートを探す
③ 一致したルートに関連したコントローラーを呼び出す
④ コントローラーアクションによってレスポンスを返す
:::


### リダイレクト先で、httpリクエストからユーザー名を取得する

```app/Http/Controllers/Auth/DashboardController.php
// ログインリクエストが送られてきたら、リクエスト内からデータを取得
    public function getUserData(Request $request)
    {
      // $requestからユーザー名を取得
      $userName = $request->user()->name;

      return view('dashboard', ['userName' => $userName]);
    }
```

:::note info
$requestは、httpリクエストのことでユーザー名を取得しています。
取得したユーザー名をviewに指定したdashboardへ渡しています。
:::

### viewで渡ってきたデータを表示させる

```resources/views/dashboard.blade.php
<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Dashboard') }}
        </h2>
    </x-slot>

    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                <div class="p-6 text-gray-900">
                    <!-- ユーザー名を表示させる -->
                    こんにちは, {{ $userName }}さん!
                </div>
            </div>
        </div>
    </div>
</x-app-layout>
```

※デフォルトのを使用

これでログイン後、ダッシュボードにログインしたユーザー名が表示されるようになりました。
外部ライブラリを利用せずにGroup ByやGroup By Countを実現したい

Rustで外部ライブラリを利用せず、Group ByやGroup By Countを実現する方法をいくつか紹介します。

# Group By

例: 頭文字ごとに名前を分類する

```rust
use std::collections::HashMap;

let names = vec!["David", "Carlos", "Charlie", "Dan", "Carol", "Alice"];
let mut map = HashMap::new();
for name in names {
    map.entry(&name[0..1]).or_insert(Vec::new()).push(name);
}

println!("{:?}", map);
//=> {"C": ["Carlos", "Charlie", "Carol"], "A": ["Alice"], "D": ["David", "Dan"]}
```

foldを利用することもできます。

```rust
use std::collections::HashMap;

let names = vec!["David", "Carlos", "Charlie", "Dan", "Carol", "Alice"];
let map = names.into_iter().fold(HashMap::new(), |mut acc, name| {
    acc.entry(&name[0..1]).or_insert(Vec::new()).push(name);
    acc
});
println!("{:?}", map);
// => {"D": ["David", "Dan"], "C": ["Carlos", "Charlie", "Carol"], "A": ["Alice"]}
```

# Group By Count

例: 名前ごとに登場回数を求める

```rust
use std::collections::HashMap;

let names = vec!["Carol", "Alice", "Bob", "Carol", "Carol", "Alice"];
let mut map = HashMap::new();
for name in names {
    *map.entry(name).or_insert(0) += 1
}
println!("{:?}", map);
//=> {"Carol": 3, "Alice": 2, "Bob": 1}
```

こちらもfoldを利用することができます。

```rust
use std::collections::HashMap;

let map = names.into_iter().fold(HashMap::new(), |mut acc, name| {
    *acc.entry(name).or_insert(0) += 1;
    acc
});
println!("{:?}", map);
//=> {"Bob": 1, "Carol": 3, "Alice": 2}
```

# 環境情報

```
$ cargo --version
cargo 1.76.0 (c84b36747 2024-01-18)
$ rustc -V
rustc 1.76.0 (07dca489a 2024-02-04)
```
useEffectについて

### useEffectの特徴
- 副作用の実行
	- コンポーネントが画面に表示された後に、何かしらの作業をさせたい時に使用する。例えば、Webサイトからデータを取得したり、画面の一部を直接変更したりする場合。
- クリーンアップ
	- useEffect内で設定したイベントリスナやタイマーなどを、不要になった時にきちんと片付けるために使用する。
- 依存配列
	- コンポーネントの特定の値が変わった時だけuseEffectを実行したい場合に使う。依存配列が空の場合は、最初に画面に表示される時と、画面から消える時にのみ実行される。


### useEffectを使用する理由
- 純粋なコンポーネントを保つ
	- useEffectを使用することで「副作用」をコンポーネントの外に出すことができる。これによりコンポーネント本体は純粋なままである
- ライフサイクルの制御
	- 状態に応じて特定の作業をしたい場合がある。useEffectを使用すればこれらの「ライフサイクルイベント」を簡単に管理して正しいタイミングで副作用を実行することができる
- 自動クリーンアップ
	- 外部データを購読している場合や、タイマーを使用している場合は、コンポーネントが画面から消える場合はそれらを停止させる必要がある。useEffect内でクリーンアップ関数を返すことでこれらの後始末を自動的に行える。
- コードの整理
	- UIを表示することと、データ取得やイベントリスナの設定などの副作用は関心が異なる。こuseEffectを使用することで、これらの関心ごとを分離することができる。

### そもそもReactにおけるエフェクトとは
> エフェクトは特定のイベントによってではなく、レンダー自体によって引き起こされる副作用を指定するためのものです。

例えば、チャットの送信メッセージなどはボタンをクリックすることで実行されるためイベントだが、チャットサーバ接続のセットアップは、何かしらのインタラクションに関係なく行われるべきであるためエフェクトである。

### そもそもレンダーとは
> 「レンダー」とは、Reactがコンポーネントを呼び出すことです。


### エフェクトの書き方
以下の3ステップで行う。

1. エフェクトを宣言する
2. エフェクトの依存値の配列を指定する
3. 必要に応じてクリーンアップを追加する

### 関連の公式ドキュメント
- [エフェクトは何であり、イベントとどう異なるのか](https://ja.react.dev/learn/synchronizing-with-effects#what-are-effects-and-how-are-they-different-from-events)
- [エフェクトの書き方](https://ja.react.dev/learn/synchronizing-with-effects#how-to-write-an-effect)
Mac版RStudioのスクリプトエディタでカーソルがズレる問題と回避方法


どのタイミングからなのかわかりませんが、macOS版RStudioのスクリプトエディタで、一部の日本語フォントを指定した場合、一部の記号類（→や□など）を含む行でカーソルがズレるようになりました。

## 問題の詳細
例えば、「神田→東京→有楽町」という文字列があって、カーソルが「神田」の「田」の右にあるとします。（スクリーンショットのフォントはOsaka-Monoです。）
![rstudio1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3780694/643e16d1-b0ac-d21c-0c9f-5dc51702b4c1.png)

右カーソルキーを押すと、カーソルが「→」と「東」の間に来てくれることを期待しますが、実際には矢印の上にカーソルが移動します。この状態でdeleteキー（WindowsのBackSpaceに相当）を押すと、「→」が消えます。
![rstudio2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3780694/addba901-026e-d391-a982-af26ae7d15d4.png)

「→」ひとつにつきカーソルがおよそ0.5文字分ずれるようで、右カーソルキーを押してもカーソルは「有楽町」の「楽」と「町」の間までしか進みません。その状態でdeleteキーを押すと、「町」が消えます。
![rstudio3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3780694/d7b0df13-ba52-1bbe-7a24-384fc18a46ba.png)

# 原因
→や□などの記号文字は欧文フォントと日本語フォントで同じユニコードが当てられており、欧文フォントでは半角、日本語フォントでは全角で表示されるのが一般的ですが、RStudioがこれを適切に処理できないことが原因のようです。

## 回避方法
### ①欧文フォントを使う
Andale Mono、CourierNewPSMTなどの欧文フォントを使えば、カーソルがずれることはなくなります。日本語の文字は（おそらく別の日本語フォントで）表示されますが、文字間隔が空いてしまい、見栄えが悪くなります。（下の画像はCourierNewPSMT）
![rstudio_courier.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3780694/4e97a3cf-ee80-42db-e494-e7fce1da4f30.png)

### ②記号類を半角表示する日本語フォントを使う
たとえば、[UDEV Gothic](https://github.com/yuru7/udev-gothic)のユニバーサルデザインフォントの BIZ UDゴシック と、 開発者向けフォントの JetBrains Mono を合成したフォントですが、これの「文字幅比率 半角1:全角2版」を使うとカーソルがずれなくなります。自分はこちらの方法をとっています。

問題としては、フォントのインストールが必要なことと、記号類が半角表示されて詰まり気味になることででしょうか。

![rstudio_udev.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3780694/17f0bda0-179a-09ff-c4a0-681c7096f185.png)

## 自分の環境
- macOS Sonoma 14.4.1
- RStudio 2023.12.1+402
- R version 4.4.0
Windows版 Ollama と Ollama-ui を使ってPhi3-mini を試してみた


![_dd0fe805-53a1-42d3-b297-e3b2deeea643.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3775985/d63b7f02-db2b-d49a-9d47-4a135e5ca4ab.jpeg)

Linux上でOllama を使った記事はQiita上に沢山見つかりますが、Windows用の Ollama とChrome拡張機能の Ollama-ui を組み合わせた事例が見つからなかったので、本記事を作成しました。

## Ollama の概要とダウンロード先
Ollama は、オープンソースの大規模言語モデル（LLM）をローカルPC上で実行できるOSSツールです。
Linux版、Mac版は以前からありましたが、2024年2月15日からWindows版（プレビュー版）が利用できるようになりました。

Windows版は [Ollama公式サイト](https://ollama.com/) からもダウンロード可能ですが、プレビュー版の専用サイトが用意されており、詳しい情報が記載されているため、こちらを紹介しておきます。

https://ollama.com/blog/windows-preview

次のインストーラーがダウンロードされるので、実行してください。
```
OllamaSetup.exe
```

## Ollama-ui の概要とダウンロード先
Ollama はCUIが前提のツールであるため、使い勝手はあまり良くありません。そこでWebアプリとして Ollama を利用できるようにしたのが Ollama-ui です。

Git からダウンロードして使うことも可能ですが、Chrome の拡張機能として用意されているため、普通にChatとして使うにはこちらの方が便利です。

Chrome で下記のURLを開いて拡張機能を追加してください。

https://chromewebstore.google.com/detail/ollama-ui/cmgdpmlhgjhoadnonobjeekmfcehffco?hl=ja

## Phi3-mini の概要とダウンロード先
Phi3-mini は、2024年4月23日にMicrosoft が発表した 小規模言語モデル(SML)です。

関連記事：[小さくても強力: 小規模言語モデル Phi-3 の大きな可能性](https://news.microsoft.com/ja-jp/2024/04/24/240424-the-phi-3-small-language-models-with-big-potential/)

パラメータが 38億のPhi-3-mini、70億の Phi-3-small、140億の Phi-3-medium が用意されていますが、2024年4月29日において、Phi3-mini だけがリリースされています。

Phi-3-min のモデルは　[HuggingfaceのPhi-3-mini-128k-instruct](https://huggingface.co/microsoft/Phi-3-mini-128k-instruct)　からダウンロード可能ですが、Ollama を実行することで自動でダウンロードされため、手動でダウンロードする必要はありません。


## Windows版 Ollama を実行する

Ollama をインストールし、かつChrome に Ollama-ui の拡張機能が追加されている状態で、コマンドプロンプトから次の１行を実行します。

```
ollama run phi3
```

初回実行時は phi3 のモデルが自動ダウンロードされ、入力待ちの状態になります。ollama-ui を使うには、ollama が起動している必要があるため、コマンドプロンプトはこのままにしておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3775985/18afeecb-e0a5-45f9-aeab-9f84d98ad6d8.png)

## Ollama-ui で Phi3 を使ってみる

chrome の拡張機能から ollama-ui を選択すると下記の画面が表示されます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3775985/4f778230-2e8d-c76f-1abe-2a2126f7495d.png)

画面下部に質問を入力し「Send」を押すとPhi3 が回答してくれます。試しに「あなたの名前は？」と質問したら、次の答えが返ってきました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3775985/d0e331db-f8e3-f1bb-0995-cc613b85697f.png)


## Ollama で利用できる LLM、SLM
2024年4月において、 Ollama から利用可能なLLM,SLM は次の通りです。

|モデル|パラメータ数|サイズ|コマンド|
|---|---|---|---|
|Llama 3|8B|4.7GB|ollama run llama3|
|Llama 3|70B|40GB|ollama run llama3:70b|
|Phi-3|3,8B|2.3GB|ollama run phi3|
|Mistral|7B|4.1GB|ollama run mistral|
|Neural Chat|7B|4.1GB|ollama run neural-chat|
|Starling|7B|4.1GB|ollama run starling-lm|
|Code Llama|7B|3.8GB|ollama run codellama|
|Llama 2 Uncensored|7B|3.8GB|ollama run llama2-uncensored|
|LLaVA|7B|4.5GB|ollama run llava|
|Gemma|2B|1.4GB|ollama run gemma:2b|
|Gemma|7B|4.8GB|ollama run gemma:7b|


## まとめ
OllamaとOllama-ui を組み合わせれば、オープンソースの大規模言語モデルでも比較的小規模なもの、あるいは小規模言語モデルを、ローカルPC上で簡単に試すことができます。

今までは、LinuxかMacでしか利用できなかった Ollama ですが、2024年2月にようやくWindows に対応してくれました。

LLM、SLM は頻繁にバージョンアップが繰り返されており、また突然新しいモデルが発表されるなど、性能の向上も日進月歩なので、Windowsユーザーも気軽に試していきましょう。
リッジ回帰とラッソ回帰の使い分けの基準(メモレベル)

リッジ回帰とラッソ回帰はどう使い分けるのだろう？
調べた際の勉強メモ。

最初はリッジ回帰とラッソ回帰って何？という基本的な話です。
不要な方はスキップ。

続いて、使い分けを扱います。

# リッジ回帰とラッソ回帰とは何か
- 回帰モデルを作る時に使う手法。
- 回帰係数を推定する際、損失関数を最小化するように係数を決める。
その時に過学習を防ぐための工夫がこらされている。
- どんな手法？ー損失関数に正則化項を仕込む。


# その正則化項には2種類ある
L1正則化項とL2正則化項である。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1081780/44ccf8b9-bc89-0e3f-9c2f-b8897329a278.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1081780/d37d27d5-406e-c029-3032-2eef1e592aa5.png)

L1正則化項は、回帰係数の絶対値の和
L2正則化項は、回帰係数の自乗の和
λ(ラムダと読む)はコストパラメータ。
つまりλが大きくなる
→回帰係数は0に近づく
→バリアンスが小さくなっていく
→過学習を回避しやすくなる

↓ラッソ回帰のみ記載だがリッジ回帰も同様
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1081780/1df8043c-a4a3-a61d-6b3e-c5078a1948fa.png)

# じゃあどうリッジ回帰とラッソ回帰はどう使い分けるの？

使い分けは以下のとおり
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1081780/dc574f16-4b3d-5a35-4539-6d5ea409bc2f.png)


補足1:ラッソ回帰は係数を0にすることがあるのに、リッジ回帰で係数が0にならないのはなぜ？
と思った方は、最後の参考サイトにあるヨビノリさんの動画をご参照ください。図形で解説しており納得しやすい。

補足2:多重共線性のある場合にリッジ回帰が有効？
これは、以下サイトによると機械学習を用いて予測を行うという目的の場合のみかもしれない。
すなわち、統計学の文脈では有効とは限らない。
詳細は[こちらのサイト](https://www.bananarian.net/entry/multico-ridge)を参照ください。


### 余談：記憶の助け
ラッソ(**ら**)とL1(**わ**ん)はaの音で韻を踏める。
リッジは自乗(じ)なので、じが共通している。
それで無理やり紐づけて記憶する。




# 参考サイト
[Ridge Regression vs Lasso Regression](https://www.shiksha.com/online-courses/articles/ridge-regression-vs-lasso-regression/)

[Lasso vs Ridge vs Elastic Net | ML](https://www.geeksforgeeks.org/lasso-vs-ridge-vs-elastic-net-ml/)

[(再掲)L1/L2正則化の意味【機械学習】youtube動画](https://www.youtube.com/watch?v=3vfiMRjgzZ8)

[(再掲)リッジ回帰(ridge)で解決できる多重共線性問題と注意すべき点](https://www.bananarian.net/entry/multico-ridge)
私のsmartphone_reset.css(編集中)

<h1>私のsmartphone_reset.css </h1>
後日記載
<ul>
<li>ios_reset.css
<li>android_reset.css　後日記載
</ul>

<h2>ios_reset.css</h2>
ios_reset.css 参考リンク　忘れました。(思い出したらリンク乗っけます。)


```
*,
*::before,
*::after {
	margin: 0; /* マージンは0に */
	padding: 0; /* パディングも常に0に */
	box-sizing: border-box; /* もちろん、より直感的なbox-sizingに設定 */
}
 
:where([hidden]:not([hidden='until-found'])) {
	display: none !important;  /* hiddenは非表示を意味します */
}
 
:where(html) {
	-webkit-text-size-adjust: none; /* iOSのランドスケープでテキストが調整されないようにする */
	color-scheme: dark light; /* ユーザーがダークテーマを好む場合、自動的にダークテーマになる */
}
 
@supports not (min-block-size: 100dvb) {
	:where(html) {
		block-size: 100%;
	}
}
 
@media (prefers-reduced-motion: no-preference) {
	:where(html:focus-within) {
		scroll-behavior: smooth; /* 何かにフォーカスがある場合のみスムーズスクロール */
	}
}
 
:where(body) {
	block-size: 100%; /* サファリ以外のブラウザのフォールバック */
	block-size: 100dvb; /* 1dvbは動的ビューポートの長さの1%、100dvbで高さいっぱいに */
	line-height: 1.5; /* アクセシブルな行の高さ */
	font-family: system-ui, sans-serif; /* timeの代わりにシステムフォントを使用 */
	-webkit-font-smoothing: antialiased; /* テキストのレンダリングを改善 */
}
 
:where(input, button, textarea, select) {
	font: inherit; /* フォーム コントロールは親フォントを継承 */
	color: inherit; /* カラーも継承 */
}
 
:where(textarea) {
	resize: vertical; /* テキストエリアの水平リサイズを無効に */
	resize: block;
}
 
:where(button, label, select, summary, [role='button'], [role='option']) {
	cursor: pointer; /* インタラクティブなものにカーソルを合わせる */
}
 
:where(:disabled) {
	cursor: not-allowed; /* フォームコントロール無効時のカーソルを許可しない */
}
 
:where(label:has(> input:disabled), label:has(+ input:disabled)) {
	cursor: not-allowed; /* ラベルにもカーソルを許可しない */
}
 
:where(button) {
	border-style: solid; /* ボタンのボーダーのスタイルを設定しやすくする */
}
 
:where(a) {
	text-underline-offset: 0.2ex; /* 下線の上にスペースを追加する */
}
 
:where(ul, ol) {
	list-style: none; /* ビュレットを削除、必要に応じて手動で追加する */
}
 
:where(img, svg, video, canvas, audio, iframe, embed, object) {
	display: block; /* 置換された要素をより予測可能にする */
}
 
:where(img, picture, svg) {
	max-inline-size: 100%; /* images should never overflow past the available space */
	block-size: auto; /* アスペクト比を保持 */
}
 
:where(p, h1, h2, h3, h4, h5, h6) {
	overflow-wrap: break-word; /* 長い単語は改行 */
}
 
:where(h1, h2, h3) {
	line-height: calc(1em + 0.5rem); /* 見出しの行の高さを減らす */
}
 
:where(hr) { /* より一貫性のある、スタイリッシュなhr */
	border: none;
	border-block-start: 1px solid;
	color: inherit;
	block-size: 0;
	overflow: visible;
}
 
:where(:focus-visible) { /* より一貫性のある、カスタマイズ可能なフォーカスのアウトライン */
	outline: 2px solid var(--focus-color, Highlight);
	outline-offset: 2px;
}
 
 /* .visually-hiddenは後のカスケードレイヤーを上書きするために!importantを使用 */
:where(.visually-hidden:not(:focus, :active, :focus-within, .not-visually-hidden)) {
	clip-path: inset(50%) !important;
	height: 1px !important;
	width: 1px !important;
	overflow: hidden !important;
	position: absolute !important;
	white-space: nowrap !important;
	border: 0 !important;
}
```

<h2>android_reset.css</h2>

```
後日記載
```
コンストラクター、デストラクター

## はじめに

コンストラクター、デストラクターについてまとめる

### コンストラクター

インスタンス化のタイミングで実行されるメソッド。
主にプロパティの初期化、外部リソースの初期化などの処理に使われる。

```php:Person.php
class Person
{
    public $firstName;
    public $lastName;

    public function __construct($firstName, $lastName)
    {
        $this->firstName = $firstName;
        $this->lastName = $lastName;
    }

    public function getFullName()
    {
        print "<p>僕の名前は{$this->lastName}{$this->firstName}です。</p>";
    }
}
```

```php:construct.php
<?php
require_once 'Person.php';

$p = new Person('太郎', '山田');
$p->show(); // 結果：僕の名前は山田太郎です。
```

### デストラクター

オブジェクトを参照するリファレンスがひとつもなくなったときにコールされるメソッド。
クラスの中で使ったリソースを破棄するときなど、主に終了するときの処理に使われる。

```php:Person.php
<?php
class Person
{
    public $firstName;
    public $lastName;

    public function __construct($firstName, $lastName)
    {
        $this->firstName = $firstName;
        $this->lastName = $lastName;
    }

    public function __destruct()
    {
        print '<p>'.__CLASS__.'オブジェクトが破棄されました。</p>';
    
    }

    public function getFullName()
    {
        print "<p>僕の名前は{$this->lastName}{$this->firstName}です。</p>";
    }
}
```

```php:destruct.php
<?php
require_once 'Person.php';

$p = new Person('太郎', '山田');
$p->show(); 
// 結果：
// 僕の名前は山田太郎です。
// Personオブジェクトは破棄されました。
```
SvelteKit SPA Apacheサブディレクトリーに配置する

メモ

対象とするサブディレクトリー`sveltekit-example`
```
https://localhost/sveltekit-example
```

基本的にドキュメントの通りSPAアプリケーションを構築する
[https://kit.svelte.jp/docs/single-page-apps](https://kit.svelte.jp/docs/single-page-apps)

確認時のバージョン
```
"svelte": "^4.0.5",
"@sveltejs/adapter-static": "^3.0.0",
"@sveltejs/kit": "^2.0.0",
"@sveltejs/vite-plugin-svelte": "^3.0.0",
```

# 設定
## svelte.config.js
フォールバック時のページを指定＆paths.baseに`/サブディレクトリー`を指定する。
```js
import adapter from '@sveltejs/adapter-static';
import { vitePreprocess } from '@sveltejs/vite-plugin-svelte';

const dev = process.argv.includes('dev');

/** @type {import('@sveltejs/kit').Config} */
const config = {
	preprocess: vitePreprocess(),

	kit: {
		adapter: adapter(
			{ fallback: "index.html" }
		),
		paths: {
			base: dev ? "" : "/sveltekit-example"
		}
	}
};

export default config;

```

## static/.htaccess
[公式ドキュメント](https://kit.svelte.jp/docs/single-page-apps#apache)の内容にサブディレクトリー追記

```
<IfModule mod_rewrite.c>
	RewriteEngine On
	RewriteBase /sveltekit-example
	RewriteRule ^/sveltekit-example/index\.html$ - [L]
	RewriteCond %{REQUEST_FILENAME} !-f
	RewriteCond %{REQUEST_FILENAME} !-d
	RewriteRule . /sveltekit-example/index.html [L]
</IfModule>

```

# 配置

ビルドしたファイルを配置
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92451/cf2453cf-3833-ca26-8801-e9b26c69fa13.png)


SPAでルーティングするパスへGETリクエスト（Rewriteしないと単純に404）
↓
304（フォールバックページのHTML）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92451/734fb19b-5c58-ed54-cc8b-0eac21199adc.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92451/a1f36673-e193-843f-a879-9cdeda45f24e.png)
↓
SPA初期化
↓
要求パスへルーティング
↓
表示

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/92451/694cd335-e456-01e0-673e-6f665e98e285.png)
【ChatGPT＋DALL-E3】設問に対しての返答画像に関して。


　どうも、カーブミラーです。

　今回は、設問に対しての返答画像に関して。私の場合、ということで。

:::note info
本記事は、ChatGPT（有料版）
＋DALL-E3で行なっております。
画像はあります。　あしからず。
:::

　みなさん、
　　ゴールデンウイーク、
　　　いかがお過ごしでしょうか。

　私は
　　昨日一昨日（2024/04/27〜28）と
　　　自宅で寝たり起きたりを
　　　　繰り返していました。

　もちろん、
　　Qiitaへのアップもしましたし、
　　　X-Twitterでの活動も
　　　　していました😸

　今日は、
　　昭和の日で、
　　　お休みですが、
　　　　出掛けます。
***
　さて、
　　本題です。

　X-TwitterのAI絵師（AI術師）は
　　ときおり（というか毎日）
　　　設問を投げかけてきます。
　　　　かくいう私もそれに倣います。

　まるで、
　　算額奉納のようにです。

　ちなみに
　　算額奉納とは
　　　Google SGEによると
>江戸時代に神社や仏閣に奉納された和算の問題や解法を記した額や絵馬のことです。

　本当にそんな感じで
　　設問が出され、
　　　それに対して、
　　　　自作品をポストするのです。
　　　　　多くの人がポストします。

　これ、意外と
　　その人の癖が出てきます。

　単純な設問であればあるほど、
　　出やすいようです。

　“猫”という設問は
　　簡単ですが、
　　　その種類や仕草、状況、
　　　　そして、画風、と
　　　　　さまざまな要素を
　　　　　　必要とします。

　私は、
　　自分のアカウントのアイコンを
　　　“黒猫”にしているので、
　　　　猫を描画することも多いので
　　　　　つい、反応してしまいます。

　さて、問題は、
　　仕草、状況、画風。

　もちろん、
　　これらは
　　　設問により
　　　　変わってきます。

　私は、状況によって、
　　リアル、アニメ、油絵、マンガ等を
　　　使い分けていますが、
　　　　これは人それぞれですね。
***
　“猫”で、黒猫と決めますが、
　　黒猫だけを出してもつまらない。

　一緒に登場させるのは
　　魔法少女とします。

　私はたいていの場合、
　　あまり考えずに
　　　ChatGPTに投げることが
　　　　多いです。

　これも性格なんですかね😅

　“魔法少女と黒猫。描画”で
　　ChatGPTに依頼を出します。

　出てきたのは、こちら。
![1714358143064.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457238/be1480d3-1c5d-d830-0090-b3fec5e00a87.jpeg)

　通常、リアルな人物が
　　出てくるのですが、
　　　ここではアニメチックです。

　おそらく、
　　“魔法少女”というキーワードが
　　　そう判断させたのでしょう。

　実験として
　　新しくセッションを開いて、
　　　“魔女と黒猫。描画”と送信。

　出てきたのは、こちら。
![A_witch_and_a_black_cat_in_a_mystical_forest_setti.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3457238/a51ac41a-5d38-4ad2-9928-2cb9ca67cf44.jpeg)

　セミリアル、
　　という感じでしょうか。

　プロンプトを確認しても
　　それらしい文言は
　　　ありませんでした。

　ChatGPTに尋ねたところ、
　　“ファンタジーをテーマにした
　　　現代のデジタル・イラスト”との
　　　　返答が。

　まぁ、悪い作品でもないので
　　気にするだけ、無駄ですかね。

　みなさんの
　　作品制作の糧に
　　　なれば、幸いです。

　今回は、ここまで。
kintone リッチエディター項目の未入力判定についてのメモ

kintone リッチエディター項目について、PC版とモバイル版で未入力の値が異なっているのでメモ

# 概要

レコード追加やテーブル行追加時のリッチエディター項目について、PC版とモバイル版で未入力の値が異なっている。
PC版とモバイル版でリッチエディター項目の実装方法が異なっているのが原因と思われます。
JavaScript カスタマイズでリッチエディター項目の未入力の判定を行う場合、PC版とモバイル版のそれぞれの値に対応する必要がある。

| イベント | PC | モバイル |
|:-:|:-:|:-:|
| レコード追加 | "\<div>\<br />\</div>"  | undefined |
| レコード編集 | "\<div>\<br />\</div>" または ""  | undefined または "\<div>\<br />\</div>" |
| レコード保存 | "\<div>\<br />\</div>"  | undefined または "\<div>\<br />\</div>" |
| テーブル行追加 | "\<div>\<br />\</div>"  | undefined |
| 空文字入力 | "\<div>\<br />\</div>"  | undefined または "\<br />"　または "\<br>"　または "\<div>\<br>\</div>" |

PC版とモバイル版で、交互に追加・編集を行うとリッチエディター項目の未入力の値は、いろいろ遷移する。

リッチエディター項目が下記の値の場合、未入力（表示上）として考えられる。
ただし、いろいろな文字修飾後に空文字とした場合、他のタグが残るケースも考えられる。

- undefined
- ""
- "\<div>\<br />\</div>" 
- "\<div>\<br>\</div>" 
- "\<br />" 
- "\<br>" 

# 検証用アプリ

リッチエディター項目を配置

![2024-04-29_11h40_24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/100572/d4b91456-13b3-b286-25d4-d0cd7766341b.png)

# 検証用JavaScript

```pc.js
(function() {
  'use strict';
  kintone.events.on(['app.record.create.show','app.record.edit.show','app.record.index.edit.show','app.record.create.change.テーブル','app.record.edit.change.テーブル','app.record.create.submit','app.record.edit.submit','app.record.index.edit.submit'], function(event) {
    const record = event.record;
    console.log('t:'+event.type, record);
    return event;
  });
})();
```

```.mobile.js
(function() {
  'use strict';
  kintone.events.on(['mobile.app.record.create.show','mobile.app.record.edit.show','mobile.app.record.index.edit.show','mobile.app.record.create.change.テーブル','mobile.app.record.edit.change.テーブル','mobile.app.record.create.submit','mobile.app.record.edit.submit','mobile.app.record.index.edit.submit'], function(event) {
    const record = event.record;
    console.log('t:'+event.type, record);
    return event;
  });
})();
```