OmniAICreator/Qiita-1.07M huggingface dataset:
データ拡張2

## はじめに
前回の[記事](https://qiita.com/cnysntry/items/617aba4829ed7b3c08ac)で少ない画像データを回転や平行移動などを行い、増やす手段について記載したが、これをそのままディープラーニングの教師データとして使うことができないことが発覚した。
今回は備忘録として何に躓き、どうやって解決したかについて記載する。

## 問題点
前回は教師データとして以下のデータ拡張方法を採用した。
- 画像の反転
- 画像の回転
- 平行移動
- グレースケール

データ拡張後のデータには、RGBの**24ビットデータ**とグレースケールの**8ビットデータ**が混在した状態になってしまいました。KerasのConv2DやInputオブジェクトでは入力画像のshape（縦, 横, チャンネル数)を指定してモデルを作成しますが、チャンネル数が混在した教師データを使うと学習中にエラー終了してしまいます。
また、データ拡張を行うために使用した入力画像データは、画像サイズがバラバラで混在していてこれらの画像データも教師データに紛れていました。

## 解決策
データ拡張を行うpythonスクリプトに以下の修正を行いました。
- グレースケール変換を削除
- データ拡張後、入力に使った画像データを削除
- データ拡張の出力ディレクトリoutからラベルとして使うディレクトリ配下に移動し、outディレクトリは削除

以下は使用したpythonスクリプトです。
```python
import glob
import os
import shutil
from PIL import Image

IMAGE_PATH = '/home/cnysntry/resize/liquor/'
LIQUOR_CLASS = ["beer",
                "bourbon",
                "brandy",
                "japanese_sake",
                "shochu",
                "whisky",
                "wine"]
IMG_WIDTH = 64
IMG_HEIGHT = 64

for base_class in LIQUOR_CLASS:
    search_dir = IMAGE_PATH + base_class + '/*.jpg'
    search_result = glob.glob(search_dir, recursive=True)
    for cur_jpg in search_result:
        basename = os.path.basename(cur_jpg)
        img = Image.open(cur_jpg)
        img_resized = img.resize((IMG_WIDTH, IMG_HEIGHT))
        index = cur_jpg.find(base_class)
        out_dir_name = cur_jpg[0:index+len(base_class)] + '/out/'
        if os.path.isdir(out_dir_name) == False:
            os.mkdir(out_dir_name)
        out_jpg = out_dir_name + basename
        img_resized.save(out_jpg)

        # transpose
        img_trans = img_resized.transpose(Image.FLIP_LEFT_RIGHT)
        index2 = out_jpg.find(".jpg")
        out_trans_jpg = out_jpg[0:index2] + '_trans.jpg'
        img_trans.save(out_trans_jpg)
        
        # rotate
        num = 1
        trans_arrays = [[10, 10], [-10, 10], [-10, -10], [10, -10]]
        for angle in range(-90, 90, 5):
            img_rotate = img_resized.rotate(angle)
            out_rotate_jpg = out_jpg[0:index2] + '_rotate' + str(num) + '.jpg'
            img_rotate.save(out_rotate_jpg)

            # translate
            num2 = 1
            for trans in trans_arrays:
                img_translate = img_rotate.rotate(0, translate=(trans[0], trans[1]))
                index3 = out_rotate_jpg.find(".jpg")
                out_translate_jpg = out_rotate_jpg[0:index3] + '_translate' + str(num2) + '.jpg'
                img_translate.save(out_translate_jpg)
                num2 += 1

            num += 1
        os.remove(cur_jpg)

    postdir = IMAGE_PATH + base_class
    search_dir2 = postdir + '/out/*.jpg'
    for search_result2 in glob.glob(search_dir2, recursive=True):
        shutil.move(search_result2, postdir)
    os.rmdir(postdir + '/out')
```

他の前回との変化点
- 学習時間を短縮するため、入力画像サイズを64x64に設定
- 入力画像サイズを小さくしたため、平行移動の距離を小さくした

## 終わりに
上記のスクリプトを使ってデータ拡張した結果、ディープラーニングにより学習することができました。
Azure Pipelines - パイプライン実行時にレビューと承認を行うプロセスを組み込む

実行そのものや、入力されたランタイムパラメータのチェックを行うプロセスをパイプラインに組み込みます。

### 使用するタスク
[ManualValidation@0](https://learn.microsoft.com/ja-jp/azure/devops/pipelines/tasks/reference/manual-validation-v0?view=azure-pipelines)


### ポイント
1. プールは、エージェントレスじゃないとダメ
1. タイムアウトした際の挙動は、下記より選択する
    - reject : 拒否=パイプラインを実行中止する *デフォルト値
    - resume : 再開=パイプラインを実行する

### コード例

```yaml:pipeline.yml
parameters:
- name: paramA
  type: string
  default: ''

- name: paramB
  type: string
  default: ''

trigger: none

stages:
  - stage: A
    jobs:
      - job: A1
        pool: server # ポイント1:エージェントレスにする
        steps:
          - task: ManualValidation@1
            timeoutInMinutes: 1
            inputs:
              notifyUsers: '<通知先のメールアドレス>'
              approvers: '<承認者>'
              instructions: |
                Please review runtime parameters below.
                  paramA: ${{ parameters.paramA }}
                  paramB: ${{ parameters.paramB }}
              onTimeout: 'reject' # ポイント2:タイムアウト時の挙動

      - job: A1
〜後略〜
```
ケースコメントのまとめ



+ [ケースコメントレポートのあるケースには、すべてのコメントが表示されません](https://qiita.com/Keiji_otsubo/items/609391d45481ef63668f)
+ [ケースコメント通知にコメントがありません](https://qiita.com/Keiji_otsubo/items/b26dd19deb16bba471a1)
+ [ケース作成時のコメント反映不具合とSLAの関連について](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007ifkmhSAA)
Astroでデザインとしての絵文字をフォントサブセットして利用する

Font Awesomeに衝撃を受けてこれさえあればリッチな見た目との様々なトレードオフとはおさらばだ！やったぜ！と盛り上がってしまった過去を持つ（インターネット老人会へ片足を突っ込んでしまってもいる）皆様、こんにちわ！

どうでもいい前置きはまあいいとして、大変わかりづらいであろうタイトルをまずは補足します。

つまり、Astroで、デザインの一部として絵文字（😣←こういうの）を用いるために、専用のフォントを使いデバイス間で表示を揃え、フォントのサブセット化を施しダウンロードサイズを小さくしたい、という話になります。

「[Noto EmojiがiOSで表示されない問題を調査する【異体字セレクタ】](https://zenn.dev/kazzyfrog/articles/736651a7430a19)」を参考にしながらやってみます。

[NotoEmoji](https://fonts.google.com/noto/specimen/Noto+Emoji)のZipから取り出したNotoEmoji~.ttfをWSL上のAstroプロジェクトにコピーします。
```
$ cd some_astro_project
$ cp /mnt/c/Users/~/Downloads/NotoEmoji-VariableFont_wght.ttf ./public/fonts/
```

pyftsubsetは「[フォントファイルをサブセット化して軽量化したい #Python - Qiita](https://qiita.com/Momijinn/items/89e86a3d7ce8023ec7cf)」を参考に。

```
$ cat chars.txt
😀😱
```

```
$ pyftsubset ./public/fonts/NotoEmoji-VariableFont_wght.ttf --text-file=./chars.txt --layout-features='*' --flavor=woff2 --output-file=./public/fonts/NotoEmoji-VariableFont_wght.min.woff2
```

```
$ cat src/styles/global.css
~
@font-face {
  font-family: "Noto Emoji";
  src: url("/fonts/NotoEmoji-VariableFont_wght.min.woff2") format("woff2");
}

.notoEmoji {
  font-family: 'Noto Emoji', serif;
}
~
```

```
$ cat src/pages/index.astro
~
    <h1 class="notoEmoji">☺😀😗😱</h1>
~
```
※異体字セレクタ未適用

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3733061/f61a68fb-dd17-ec45-1680-c89425fb175f.png)

ブラウザーでの表示を確認すると、NotoEmojiのサブセット化で選択した絵文字「のみ」、NotoEmojiで上書きされていることが分かります。

肝心のファイルサイズは、サブセット化前が約２Mバイトに対し、サブセット化後は約３Kバイトと、実用的なサイズとなりました。


PS
ChatGPTに「😃をWebで使えるsvgに変換してください」的なリクエストをすると、以下のような感じのsvgを７８０バイトで返してくれました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3733061/80bf8995-d95b-6aeb-b92b-4b1d05e556d5.png)
・・・
まあこれでもいいのかな・・・
【OutSystems】Associate Reactive Developerサンプル問題の解説１～５

概要
・社内資料としてまとめたものをリタッチ
・公式ドキュメントでは解答のみがあり、この記事ではなぜその選択になるのかを解説
・自分が取り組んだ時に、欲しかった解説をイメージして作成


### 問題と解説

#### 問題１
![image (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882879/36228940-11c6-9abd-c9cc-fdf895af931e.png)

答え：A

解説：多対多のリレーションを行うには、中間テーブル（3つ目のエンティティ）に関係を持ちたいエンティティのIdを持たせるのが効果的です。
![image (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882879/dcda4a76-150d-87af-73ea-181e486063bb.png)

B：Orderエンティティの主キーを利用した場合、Orderエンティティに複数のレコードが登録できない。

C、D：関係を持たせることは出来るが多対多を実現できない。

公式の解説
[多対多のリレーション](https://learn.outsystems.com/ja-jp/training/journeys/web-developer-662/many-to-many-relationship/o11/448)

#### 問題２
![image (3).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882879/db8b0c32-d47c-3799-3ab4-1ce8b45baf98.png)


答え：C

解説：エンティティ識別子＝サロゲートキーの考えで良い
[サロゲートキーの解説](https://wa3.i-3-i.info/word1995.html)


A、B、D：可能なため、不正解
→データ型を変更できること、手動で指定できること、自動作成されるアトリビュートだが削除可能なことを、それぞれ覚えておきたい。

公式の解説
[データベースエンティティ](https://learn.outsystems.com/ja-jp/training/journeys/web-developer-662/database-entities/o11/384)

#### 問題３
![image (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882879/9a9e7142-4e01-ca6c-aec2-704779fdc441.png)


答え：A

解説：集約関数を使用した場合、集約関数がレコードのアトリビュートになる。

考え方はSQLと同じで、集約した項目と元のアトリビュートの項目は同時に出力できない。

公式の解説
[デモ: Aggregateで集約関数を作成する方法](https://learn.outsystems.com/ja-jp/training/journeys/web-developer-662/demo-how-to-create-aggregation-functions-in-aggregates/o11/24)

#### 問題４
![image (5).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882879/d3ec3d0d-4efa-d4d7-a0e5-d0de80d05ad2.png)


答え：D

解説：
　Priorityがあり → [Order] Only With [Priority]
　Employeeが０人以上の → [Order]  With or Without [Employee]

　With or Withoutは、SQLのLEFT JOIN、
　Only WithはINNER JOINの考え。

A：
　[Order]  With or Without [Employee]は、LEFT JOINとなるため、
　OrderにEmployeeが紐づいていないOrderも取得する。
　「Employeeがある」とはならないため違う。

B：
　[Order] Only With [OrderStatus]は、INNER JOINとなるため、
　OrderにOrderStatusが紐づくレコードしか取得できない。
　「Statusもない」とはならないため違う。

C：Aと同様

ややこしいが、結合条件とN件以上の条件に切り分けて考えると解きやすい。

公式の解説
[高度なAggregate](https://learn.outsystems.com/ja-jp/training/journeys/web-developer-662/advanced-aggregates/o11/18)


#### 問題５
![image (6).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3882879/a190fc65-2dbc-cb11-e1b7-77c97ea34eba.png)


答え：B

解説：
　PriorityごとにOrderの数を取得したい。

　→Priority.IdでGroup化し、Order.Descriptionの数を数える（Orderのアトリビュートはどのアトリビュートでも良い）

A：OnlyWithでは、Priorityに対してOrderが0件のPriorityが取得できない

　　Priority.Idではなく、Orderの数を数えたいため違う。

C：Priority.Idではなく、Orderの数を数えたいため違う。

D：Priority.Idではなく、Orderの数を数えたいため違う。
図解 Swift Concurrency

# この記事について
## 注意
あくまで私の解釈です。
間違っているところがあるかもなので、その際には指摘してほしいです。
## 目的
- Swift Concurrencyの全体像を把握する
- 主な登場人物とその役割を把握することで、今後のSwiftConcurrencyについての学習をよりしやすくする
- [Swift.org](https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/)の一部を抜粋し図解する
## 離さないこと
- 細々としたコーディングの仕方
- Swift Concurrency外のこと
    - Architectureなど
    - SwiftUI or UIKitの画面との繋ぎこみ

# まずは図から
![Swift Concurrency 全体像.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/566476/2cb5fd3a-5c91-be59-d7bb-c2b63eb923ea.jpeg)

- Task: スレッドのイメージ
- actor: Task同士でデータを共有するオブジェクト
- Sendable: actorにデータを受け渡すためのオブジェクト

# Swift Concurrencyの目的
非同期処理を並列に実行すること。
並列実行において
- それぞれの処理を完全に分離させること
- 値の競合を起こさせないこと

# 主な登場人物
## Task
- 非同期処理を実行する
- Taskを起こすことで処理を完全に分けて実行できる
## actor
- Taskで処理を分けたとしても、Task同士で値をやり取りしたい場合がある。その両者からのアクセスをできるようにする役割を持つ
## Sendable
- Task間で安全にデータを渡すために使用する
- 参照型のオブジェクトだと競合などの問題を回避できない
何故CSSでは空のルールセットがダメなのか

# 始めに
こんにちは<(_ _)>

今日も元気にコーディングをしています。
三連休ですからね。😄

さて、先ほど Cursor で CSS をぺちぺちたたいていたら、以下のような警告が出ました：

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3674822/65ebe254-991a-1203-4ab7-d7710c771118.png)


"Do not use empty rulesets" == 「空のルールセットを書くな」

と警告が出ています。

今までも何度も目にしていますが、**なんで出てんだよ**ということがいまいちわかっていませんでした。

そこまで重要な問題なのでしょうか？

**理由もよくわからないのに怒られるのは嫌だ**

どうやら最近は google という、この世の情報を検索することができるサービスがあるらしいです。

というわけで、理由を調べてみました。

追記（読まなくてもよいモノ）：
スクリーンショットでは行数が3000越えになっています。
具体的に何をやっていたかとかは言いませんが、2，3分だけEnterキーをひたすら押していただけです。
行数を増やすのに便利な方法を知っている方がいましたら、ぜひコメントよろしくお願いします<(_ _)>

# 五つの観点

### 1. ファイルサイズの無駄増加
空のルールセットは何の効果もないにもかかわらず、ファイルサイズを増加させてしまいます。これは大規模なプロジェクトで大きな問題になる場合があります。（ひどい言われようです。空のルールセット君が可哀そうになります😃）

### 2. コードの可読性の低下
空のルールセットがあると、実際に使用されているスタイルを見つけにくくなります。これはコードの保守性を低下させる原因となります。

### 3. 意図しない動作の可能性
空のルールセットが残っていると、後で誤ってそこにスタイルを追加してしまう可能性があります。これは予期せぬスタイルの適用につながってしまう可能性があります。

### 4. CSSの最適化の妨げ
多くのCSSミニファイツールは空のルールセットを自動的に削除しますが、そのプロセスに余計な時間がかかってしまいます。（問答無用で削除されるんですね。可哀そうに😃）

### 5. コーディング規約違反
多くの開発チームやプロジェクトでは、空のルールセットを書かないことがコーディング規約として定められています。（空のルールセット君はハブるというおきてが存在しました😃）

以上の観点から、空のルールセット君は削除するべき存在なようです😃



# 最後に

もしも、ほんとに、どうしても、ほんとに、どうしても、空のルールセット君の存在が必要であれば、コメントアウトがおすすめだそうです。（コンパイラに認知されない存在に...😃）

ということで、空のルールセットは書かない方がいいモノだということが改めて理解できました。


はっきりと「空のルールセットの使用は禁止」と書かれたコーディング規約は見つけられませんでしたが、以下のように、多くのコーディング規約では、**簡潔性**そして**可読性**が重視されています。

- [Google C++ Style Guid](https://ttsuki.github.io/styleguide/cppguide.ja.html)
Googleのスタイルガイドは、コードの可読性と保守性を重視しています。特に、コメントの書き方や命名規則、フォーマットなどに関する詳細なガイドラインを提供しています
--
- [Microsoft .NET Coding Convention](https://learn.microsoft.com/en-us/dotnet/csharp/fundamentals/coding-style/coding-conventions)
Microsoftの.NETコーディング規約は、コードの一貫性と可読性を重視しています。特に、インデントやコメントのスタイル、レイアウトの規則などが詳細に記載されています
--
- [The Complete Guide to Readable Code](https://fellow.app/blog/engineering/the-complete-guide-to-readable-code/)
このガイドでは、読みやすいコードを書くための11の原則を提示しています。コードの構造化、テスト、簡素化、整列などの重要性を強調しています
--
- [Code Commenting Best Practice](https://daily.dev/ja/blog/10-code-commenting-best-practices-for-developers)
このガイドでは、コードのコメントに関するベストプラクティスを10項目挙げています。コメントの簡潔さと明確さを重視しています
--
- [Concise Code Isn't Always Clean Code](https://www.freecodecamp.org/news/concise-code-isnt-always-clean-code/)
このリソースでは、簡潔なコードが必ずしも読みやすいコードではないことを説明しています。コードの可読性と再利用性のバランスの重要性を強調しています
--
- [Coding Standards by Codacy](https://blog.codacy.com/coding-standards)
このガイドでは、コーディング規約の重要性と、それを自動化ツールで維持する方法について説明しています。コードの一貫性、可読性、セキュリティを重視しています

最後まで読んでくださり、ありがとうございました<(_ _)>

今日があなたの**空のルールセット君の命日**になりますことを嬉しく思います😃（わーい）
Ubuntu24.04でIsacSimを動かそうとした話① Omniverse Launcher編

# 概要
仕事でIsacSimを触ることになったので, お勉強でIsacSimの環境を作ることを目指す。
この記事では, IsacSimを起動するためのOmniverse Launcherをインストールするところまで説明します.

# 環境
- Ubuntu 24.04.LTS
- Omniverse Launcher 1.9.13

# 手順の前に...
この後, 詳細な手順を記載していますが, 四苦八苦している途中に何故か動き始めたという
ぐだぐだな感じになったので, 先にとりあえず行ったことをまとめます.

・NVIDIA アカウントを作成:必須
・Omniverse LauncherのAppimageをダウンロード:必須
・Appimageに実行権限をつける:必須
・fuseをインストール:必須
・rootフォルダの所有者を自分に変える:必須か不明
・一回コンソールを閉じる:必須か不明
・新しく開いたコンソールで
./omniverse-launcher-linux.AppImage --no-sandbox
を実行(sudoはいらない):必須

とりあえず, 上記の手順でOmniverseのLauncherは起動までは行けました。


# 詳細な手順
https://www.nvidia.com/en-us/omniverse/download/
こちらから, OmniverseのAppimageをダウンロード


落としてきたAppImageに実行権限を付与
※Executable as Programにチェック入れる
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3870090/a39ce26a-10a1-e882-71d0-485fc0b0fcbb.png" width=30%>

何故か実行してもコンソールから叩いても動かない...
fuseというのが必要な模様
![Screenshot from 2024-09-15 08-05-55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3870090/7d5f3fa2-67b4-dcda-7c24-e341c4e5d14a.png)



FUSEについてはこちらを参照
https://docs.appimage.org/user-guide/troubleshooting/fuse.html

FUSEをインストール

```
sudo apt install libfuse2
```

実行したら以下のエラー(このへんでキレそうになる)
![Screenshot from 2024-09-15 08-11-23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3870090/cbefd89b-473e-b362-edee-cbdf71b957c2.png)

--no-sandboxというオプションをつけろとのことだが, 
これはChromeでセキュリテイ機能を無視するときに使うとのこと(何でChromeが出てくんだよ)

--no-sandoboxをつけたところまたエラー
![Screenshot from 2024-09-15 08-21-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3870090/9c30c205-bf75-ae28-bd7f-c80e4841ec0e.png)

update-desktop-database ~/.local/share/applicationsというコマンドが動かせていない模様
このディレクトリを見に行くとrootディレクトリの権限がロックされていた
![Screenshot from 2024-09-15 08-26-11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3870090/3f0ce086-3958-4260-c803-386930e3eefe.png)

こちらのコマンドでrootディレクトリの権限を開放する "username"はもちろん自分の名前
-Rのオプションはrootの配下のディレクトリにも全て同じ開放を行っていまする

```
sudo chown -R "username" root
```

なんやねん！って思ってたが
一回コンソールを閉じ, 新しいコンソールで

```
./omniverse-launcher-linux.AppImage --no-sandbox
```

を実行したら普通に起動

# 実行結果
![Screenshot from 2024-09-15 08-38-59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3870090/045194d8-f3e7-9b98-bd7d-f7ff85ae2e78.png)

# 所感
ぐっと疲れた...
前に別のOmniverse関連ツールを動かしたことがあるんですが, それよりはマシな気もする...
クローズドテストの審査に通らない

## 8月30日、審査落ち
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844767/14456411-d125-f77a-0e6f-3492bd3d1566.png" width=50%>

この時、落ちた理由はアンケートの回答が雑すぎたからだと勝手に思ってました。
リリース要件を満たすことの難しさからGoogleにひどく腹を立てていて、当て付けで雑に回答をしたのです。
それからは何もしないでも「20人以上のテスターで審査日から●日以上」の日数が再びカウントされていったのでとりあえず放置することにしました。
そして2週間後、今度は丁寧にアンケートに回答しました。

## 9月15日、審査落ち
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3844767/7e51d751-0cbc-31ed-067e-b6bd42aec496.png" width=50%>

また落ちました。
明らかにおかしいと思い、この状況になって初めてグーグルからのメールをちゃんと読みました。
そこには落ちた理由がこのように書かれていました。
>・Testers were not engaged with your app during your closed test
>・You didn't follow testing best practices, which may include gathering and acting on user feedback through updates to your app

日本語に訳すと
>・クローズドテスト中にテスターがあなたのアプリに十分に関与しなかった
>・アプリの更新を通じてユーザーフィードバックを収集し、それに基づいて行動するなど、テストのベストプラクティスに従わなかった

とのことでした。
アンケートが直接的な原因ではなかったようです。
1つ目は、クローズドテストを依頼した業者がアプリを起動していないということだと思います。
2つ目は、テスト期間中に何度もバグ修正をしてアプリを提出し直したのでよくわかりません。これについては異議ありです。

一応問い合わせてみようかと思いましたが、テスターのアプリ起動状況はGoogleが把握しているでしょうし異議を申し立ててどうにかなるとも思えません。

## これから
結局のところ他の方法でテスターを募集し直す必要があるのですが、なんだかもう完全にAndroidアプリ開発に対するモチベーションが切れてしまいました。
テストを海外の謎業者に依頼したり、審査に落ちた時メールを読まなかったり、色々と自業自得なのは承知していますが...。
以前にその謎業者を紹介した責任もありますし、せめて他の個人開発者さんたちの役に立てればと思い情報共有させていただきました。
Unboundを使ってDNSサーバーを構築する手順

# はじめに
この記事では、Unbound を使用して簡単なDNSサーバーを構築する方法を紹介します。Amazon EC2のプライベートネットワークを使って、DNSの動作確認を行います。

### 1. Unboundのインストール
```
apt install unbound
```
インストール後、Unboundの状態を確認し、自動起動を有効にします。
```
systemctl status unbound
systemctl enable unbound
```

### 2. systemd-resolved の停止
systemd-resolved がポート 53 を使用している可能性があるため、unbound との競合を避けるために停止・無効化します。

```
systemctl stop systemd-resolved
systemctl disable systemd-resolved
```

### 3. Unbound 設定ファイルの編集
次に、Unboundの設定ファイルを編集して、必要なDNS情報を追加します。

```bash
vi /etc/unbound/unbound.conf
```

ファイルの内容は以下になります。

```
server:
    logfile: "/var/log/unbound.log"
    use-syslog: no
    verbosity: 1

    interface: 0.0.0.0
    access-control: 0.0.0.0/0 allow
    access-control: 127.0.0.0/8 allow
    access-control: 172.31.0.0/16 allow

    local-zone: "example01.com." static  
    local-data: "example01.com. 3600 IN A 172.31.94.223"  

forward-zone:
    name: "."  
    forward-addr: 8.8.8.8
```

### 4. 設定ファイルを保存して、Unboundを再起動します。

```
systemctl restart unbound
```

### 5. /etc/resolv.conf の更新
次に、DNSのルックアップにUnboundを使用するように、/etc/resolv.conf を編集します。

```
vi /etc/resolv.conf
```

以下の内容に変更します。

```
nameserver 127.0.0.1
```

### 5. DNSの動作確認

最後に、Unboundが正しく設定されているかどうかを確認するため、dig コマンドを使って名前解決のテストを行います。

```bash
dig example01.com @127.0.0.1
dig example01.com @172.31.94.223
```


### 以上で、Unboundを使用したDNSサーバーの構築と設定は完了です！

UnboundによるDNSサービスが無事に稼働している場合、設定したドメイン (example01.com) に対して正しいIPアドレスが返されます。
[p5.js]VSCodeとp5.jsの開発環境 #1

[Cursor](https://www.cursor.com/) or [Visual Studioc Code(VSCode）]()で、[p5.js](https://p5js.org/)をコーディングする開発環境の作り方。

p5.jsは、"p5.js Web Editor"でコーディングするのが定番ですが、VSCodeでコーディングした方が断然便利ですので、こちらで進めてみます。今回は、JavaScriptを使ったとてもVanila（デフォルト、プレーン）な環境をつくります。


# 拡張機能
p5.jsをコーディングするのに便利な機能拡張をインストールします

### 定番 の拡張機能
- Live Server
    - HTMLとJSを自動的にリロード（ホットリロード）してくれて、更新を確認しやすくなります
- Prettier - Code formatter
    - コードを自動的に整形。きれいなコードを保てます

### for p5.js
いくつかp5.js向けの機能拡張がありますが、ここではこちらの3点を紹介します。どちらかをインストール。
- p5.vscode
    - DL数が一番多い。TypeScript使用時、オートコンプリート機能が使える。サードパーティーのライブラリを選択してインストールすることも可能。
- P5 Snippets and Tools
    -　スニペットが充実している。また、TypeScript使用時、オートコンプリート機能が使える。 
- p5js Snippets
    - 主にスニペットのみ。

# 新規作成（テンプレート）

### CDNのp5.jsライブラリを利用する
CDNからライブラリをロードするため、オンラインの必要があります。

#### ディレクトリ構造
シンプルなファイル構成です
```
├── index.html
└── sketch.js
```
sketch.jsにスケッチのコードを記述します
#### index.html
``` html:index.html
<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/p5@1.4.1/lib/p5.js"></script>
    <script src="sketch.js"></script>
    <title>P5.js Project</title>
  </head>
  <body>
    <main>
    </main>
  </body>
</html>
```
#### sketch.js
``` js:sketch.js
function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(220);
}
```

### p5.jsライブラリをローカルで利用する

#### テンプレートから
機能拡張を使って、テンプレートから新規作成できます。
コマンドパレット（command + shift + P)を開いて、`p5.vscode`の場合は`Create p5.js Project`を選択。
![Settings_—_p5js.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/4dbaaf2a-0318-6aeb-577f-fd26b384d4c1.png)
#### ディレクトリ構造
次のようなディレクトリとファイルが生成されます
```
.
├── index.html
├── jsconfig.json
├── libraries
│   ├── p5.min.js
│   └── p5.sound.min.js
├── sketch.js
└── style.css
```
sketch.jsにスケッチのコードを記述します
#### index.html
``` html:index.html
<html>
  <head>
    <script src="https://cdn.jsdelivr.net/npm/p5@1.4.1/lib/p5.js"></script>
    <script src="sketch.js"></script>
    <title>P5.js Project</title>
  </head>
  <body>
    <main>
    </main>
  </body>
</html>
```
#### sketch.js
``` js:sketch.js
function setup() {
  createCanvas(400, 400);
}

function draw() {
  background(220);
}
```

# プレビュー
インストールした`Live Server`を使うと、sketch.jsやindex.htmlの更新（上書き保存）結果を自動的にリロード（ホットリロード）しながら確認することができます。

ウィンドウ右下が赤枠の様な表示だった場合は、サーバーが起動していないので、起動します。
![startliveserver.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/d1ac950d-39d3-9393-1a79-3490421210d6.png)
赤枠をクリックするとサーバーの起動が始まります。
![startserver.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/c7abb3aa-d89a-afab-5c50-4cdc9263e0bb.png)
このように`Go Live`と表示されたらサーバー起動が成功です。

次に、エディタのExplorer（ウィンドウ左端にあるファイルの一覧）から、確認したい`index.html`を選択し、右クリックして、`Open with Live Server`を選択します。

![openwithliveserver.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/2a78730d-71d2-308a-7a6b-be6b4a3f99bc.png)

ブラウザの新規タブもしくは新規ウィンドウに、選択したファイルが表示されます。エディタに戻って、編集して上書き保存すると、自動的にブラウザも更新されます。

![twowindow.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/55390/cbb1576a-f4a9-442a-af75-dbadfd0503b4.png)

コーディング時は、エディタとブラウザを左右に並べて表示すると、更新内容を確認しながらコーディングができて便利です。
【Node.js】カスタムエラークラスを書く

エラークラスを独自に実装したい場合, 次のように記載する.

```js
class NotFoundError extends Error {
  constructor(message) {
    super(message);
    this.name = 'NotFoundError';
    this.status = 404;
  }
}
```

これをテストしてみよう. 上記コードで test.js というファイルを作成し, 最後に以下を記述する.

```js
// ここに上記エラークラスを記載

throw new NotFoundError('カスタムエラークラスが呼び出されました');
```

実行する.

```bash
$ node test.js
/home/oizumi/next-blog-app-backend/test.js:9
throw new NotFoundError('カスタムエラークラスが呼び出されました');
^

NotFoundError: カスタムエラークラスが呼び出されました
    ...スタックトレースは省略...
  status: 404
}
```

ここで次の書き方は正しいだろうか.

```js
class NotFoundError extends Error {
  constructor() {
    this.message = 'カスタムエラークラスが呼び出されました';
    this.name = 'NotFoundError';
    this.status = 404;
  }
}
```

実行してみよう.

```bash
$ node test.js
/home/oizumi/next-blog-app-backend/test.js:3
    this.message = 'カスタムエラークラスが呼び出されました';
    ^

ReferenceError: Must call super constructor in derived class before accessing 'this' or returning from derived constructor
```

super() を呼び出していないためにエラーが発生する.
Error クラスを継承する際には親クラス (Error) のコンストラクタを呼び出すために, super() を使う必要がある.

# 参考

https://oizumi-yuta.com/blog/9
AWS EC2、Azure VM、べからず設定01

みなさんこんにちわ。
前回の記事からまたしても期間が開いてしまいました。
仕事が忙しかったのと風邪を引いていたのとでなかなか時間がとれませんでした。

これまでお仕事で色々な方の構築した色々な環境を見てきて、ホントに会社ごとに様々な環境があるもんだな、と思います。
今でも覚えているなかなかパンチのある環境が、社内のローカルIPアドレスが全部他社のグローバルIPアドレスを勝手に利用している、という環境だったり、自社で持っているグローバルIPアドレスと他社のグローバルIPアドレスが混在した環境だったり、いくつものグループ会社を束ねたドメイン環境下で1つOUに全オブジェクトが入っていたり、
**「それ運用できます？」**
みたいな環境がありました。
会社の大小関係なく、スタートから躓いていてそれを今でも継承している環境が多いのではないかと思います。

こういった環境にならないため、Azure VMやAWS EC2でもたまに見る**やってはいけない設定＝べからず設定**を理由を含めて記載します。

# プライベートIPアドレスをOSの機能で固定IPアドレス設定を行う
はい。
これやっちゃいけないべからず設定の代表です。
まず用語の整理ですが、パブリッククラウドでプライベートIPアドレスと呼ばれているIPアドレス群は一般のネットワーク機器ではローカルIPアドレスとも言われているので、プライベートIPアドレスと言われてピンとこない方はローカルIPアドレスとして読み替えてください。
意味は同じです。
公式サイトを確認すると
AWS EC2
https://docs.aws.amazon.com/ja_jp/AWSEC2/latest/UserGuide/using-instance-addressing.html
Azure VM
https://learn.microsoft.com/ja-jp/azure/virtual-network/ip-services/virtual-networks-static-private-ip-arm-pportal
AWSもAzureも共にご覧の通りOSの機能で固定IPアドレス化せず、AWSはAWS Console上で、AzureはAzure Portal上で固定IPアドレス化しています。

## なんでプライベートIPアドレスをOSの機能で固定化しちゃダメなの？
もちろん理由があります。
基本的に**Azureの仮想ネットワーク(Vnet)もAWSのVPCもDHCP環境です。**
なぜDHCPなのか？よりも、OSで固定IPアドレス設定を行ってはいけない根本的な理由から説明します。

## OSの機能でプライベートIPアドレスを固定化してはいけない理由
AzureはMicrosoft、AWSはAmazonがクラウドキャリアですが、それぞれ同様に**VMのマネージドディスク、EC2のEBS、ともに内部の情報はお客様(クラウド利用者)の情報資産なのでクラウドキャリアが勝手に中身を除くことはできない**というポリシーを敷いています。
これは通信の内容に関しても同様で、**VnetやVPC内を流れる通信の内容もお客様の許可なく勝手に覗き見ることを自ら禁じています。**
当たり前ですね。
**ディスクの中身も通信の中身もクラウド利用者の情報資産であるため、例えクラウドキャリアと言えども勝手に覗き見ることができてしまっては情報漏洩になります。**
このことから反射効として**OSの設定でプライベートIPアドレスを固定化してしまった場合、その固定化したIPアドレスをクラウドキャリアが判別することはできません。**
ですので有事の際、例えばDHCPで配布しているIPアドレスとOSの機能で固定IPアドレスとして使用したIPアドレスが重複した際など、原因の切り分けが難しくなります。
DHCPサーバのログを確認すればIPアドレスの重複は確認できるかもしれませんが、切り分けが複雑になることは間違いないです。
しかしAWS Consoleの機能やAzure Portalの機能でプライベートIPアドレスを固定化しているとAWSサポートやAzureサポートのエンジニアもIPアドレスの指定を確認できます。

# DHCP関連の設定がダメってことは、デフォルトゲートウェイの設定やサブネット、DNSもダメなの？
はい。
その通りです。
デフォルトゲートウェイはVnetもVCPもサブネット毎に仮想ルーターが暗黙的に設置されており、このプライベートIPアドレスが指定されていますが、これは実は公開されていません。
ですのでデフォルトゲートウェイの設定も手動で設定することはNGです。
現在指定してうまく動いていたとしても、公開されていない情報を**何の根拠があって設定しているのか？また公開されていない情報なのでクラウドキャリアが仕様変更を行っても通知する謂れが無いので、ある日突然通信が途絶えてしまいRDPやSSH接続できなくなったとしても誰にも文句は言えません。**
なかなか致命的な状況になり得ますね。

ちなみにDNSの設定はAzureであればVnetのオプション、AWSであればVPCの設定追加でDHCPオプションで設定します。
AWSだとここですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/e94fd87f-24ac-47d4-2c5d-d4d427b943cf.png)

Azureだとここですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/643392/3ff634e6-d5b5-60d0-3780-430312feb342.png)

Azureはわかりづらいですが、複数のDNSサーバを登録することももちろん可能です。

本日は小ネタですがこの辺で。
【JavaScript】 ISO 8601形式 から年月日時分秒を取得する


1999-12-31T15:00:00.000Z という日付文字列の Z は、ISO 8601 形式で表された日時における「Zulu Time」の略で、協定世界時 (UTC: Coordinated Universal Time) を示している.
これを日本時間で取得してみよう.

```js:test.js
const date = new Date('1999-12-31T15:00:00.000Z');

// タイムゾーンを確認
const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
console.log('タイムゾーン: ', timeZone);

// JST に変換してから年・月・日を取得
const localYear = date.getFullYear(); // JST 基準の年を取得
const localMonth = date.getMonth() + 1; // JST 基準の月を取得 (0-based なので+1)
const localDay = date.getDate(); // JST 基準の日を取得

// JST に変換してから時・分・秒を取得
const localHours = date.getHours(); // JST 基準の時間を取得
const localMinutes = date.getMinutes(); // JST 基準の分を取得
const localSeconds = date.getSeconds(); // JST 基準の秒を取得

console.log('年: ', localYear);
console.log('月: ', localMonth);
console.log('日: ', localDay);
console.log('時: ', localHours);
console.log('分: ', localMinutes);
console.log('秒: ', localSeconds);
```

実行する.

```bash
$ node test.js
タイムゾーン:  Asia/Tokyo
年:  2000
月:  1
日:  1
時:  0
分:  0
秒:  0
```

タイムゾーンはユーザーのブラウザが設定されているタイムゾーンが表示されるため, 既に日本時間となっていることがわかる. すなわち特別な変換は必要ないのである.

ついでに UTC のまま取得する方法も記載しておこう.

```js:test.js
const date = new Date('1999-12-31T15:00:00.000Z');

const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
console.log('タイムゾーン: ', timeZone);

// UTC基準で年・月・日を取得
const utcYear = date.getUTCFullYear(); // UTC基準の年を取得
const utcMonth = date.getUTCMonth() + 1; // UTC基準の月を取得 (0-basedなので+1)
const utcDay = date.getUTCDate(); // UTC基準の日を取得

// UTC基準で時・分・秒を取得
const utcHours = date.getUTCHours(); // UTC基準の時間を取得
const utcMinutes = date.getUTCMinutes(); // UTC基準の分を取得
const utcSeconds = date.getUTCSeconds(); // UTC基準の秒を取得

console.log('年: ', utcYear);
console.log('月: ', utcMonth);
console.log('日: ', utcDay);
console.log('時: ', utcHours);
console.log('分: ', utcMinutes);
console.log('秒: ', utcSeconds);
```

実行する.

```bash
$ node test.js
タイムゾーン:  Asia/Tokyo
年:  1999
月:  12
日:  31
時:  15
分:  0
秒:  0
```

タイムゾーンは日本時間のまま UTC で取得できていることが確認できた.

# 参考

https://oizumi-yuta.com/blog/11
【AWS】Cognito ロックアウト概要

ロックアウトとは, 何回かパスワードを間違えることで一定期間正しいパスワードを入力してもログインできなくなる仕組みである。

Cognito の仕様少々長いが全文を引用する.

> ### サインインの試行の失敗に対する、Amazon Cognito ロックアウト動作
>
> 認証されていないサインインまたは IAM 認証されたサインイン試行が 5 回失敗すると、Amazon Cognito は 1 秒間ユーザーをロックアウトします。ロックアウトの期間は、試行が 1 回失敗するたびに 2 倍になり、最大で約 15 分になります。ロックアウト期間中に試行すると Password attempts exceeded 例外が生成され、その後のロックアウト期間の長さには影響しません。サインイン試行の累積失敗回数 n (Password attempts exceeded 例外を含まない) に対して、Amazon Cognito はユーザーを 2^(n-5) 秒間ロックアウトします。ロックアウトを n=0 初期状態にリセットするには、ユーザーは、ロックアウト期間後にサインインに成功するか、連続 15 分間、サインイン試行を開始してはなりません。この動作は変更される可能性があります。この動作は、パスワードベースの認証も実行しない限り、カスタムチャレンジに適用されません。

これを表で整理する.

| パスワード誤入力回数 | ロックアウト期間 (秒) | 備考                             |
| -------------------- | --------------------- | -------------------------------- |
| 1 ～ 4               | なし                  | ロックアウトはまだ開始されない   |
| 5                    | 1 秒                  | ロックアウト開始                 |
| 6                    | 2 秒                  |                                  |
| 7                    | 4 秒                  |                                  |
| 8                    | 8 秒                  |                                  |
| 9                    | 16 秒                 |                                  |
| 10                   | 32 秒                 |                                  |
| 11                   | 64 秒                 |                                  |
| 12                   | 128 秒                | 約 2 分                          |
| 13                   | 256 秒                | 約 4 分                          |
| 14                   | 512 秒                | 約 8 分                          |
| 15                   | 900 秒                | 約 15 分（最大ロックアウト期間） |

まとめると以下の通りである.

- 累積のサインイン失敗回数を n とし、最初の n= 5 回の失敗後にロックアウトが始まる
- ロックアウト期間は、2^(n-5) 秒間で計算され、失敗ごとに 2 倍になる
  - ただし Password attempts exceeded 例外は失敗にカウントしない
- ロックアウト期間は最大約 15 分（900 秒）である

# 参考

https://docs.aws.amazon.com/ja_jp/cognito/latest/developerguide/amazon-cognito-user-pools-authentication-flow.html

https://oizumi-yuta.com/blog/10
Railsの仕様によるエラーの解決方法

Rails７のAPIモードで発生したエラーをメモ。
必要に応じて随時追加していきます。

## authenticate_user!がundefindになる現象

ルーティングで下記のように名前空間を設定しているため
```
Rails.application.routes.draw do
  namespace :api do
    namespace :v1 do
      
        devise_scope :api_v1_user do
          post 'auth/guest_sign_in', to: 'auth/sessions#guest_sign_in'
          get '/auth/sessions', to: 'auth/sessions#index'
        end

        mount_devise_token_auth_for 'User', at: 'auth', skip: [:omniauth_callbacks], controllers: {
          registrations: 'api/v1/auth/registrations',
          sessions: 'api/v1/auth/sessions',
          confirmations: 'api/v1/auth/confirmations',
          passwords: 'api/v1/auth/passwords'
        }
        resources :boards, only: %i[index create update destroy] 
```

コントローラーの方でも記述を合わせる必要がある。
```
before_action :authenticate_api_v1_user!
#名前空間に会わせる

    def index
        boards = current_api_v1_user.boards
        #アクションも名前空間に合わせる
        render json: boards, status: :ok
    end
```
参考記事

https://qiita.com/kamome_ss/items/38cb953df0d6c2c7167c



## 存在しないコールバックを探すせいでエラーになる

アクションがない状態でコールバックを設定しているとエラーになる。
「先にindexアクションだけ作って後からupdateアクションを作ろう。でもコールバックだけ先に全部のアクションに設定しておくか」と考えて作業してたらエラーになった。
```
before_action :set_board, only: %i[update destroy] 
#上記の場合、updateアクションがないとエラーになる
#この場合はupdateとdestroyアクションの両方があれば成功する
```
参考記事

https://qiita.com/scivola/items/4a216b130de1f8880907
AWSで簡易なWebアプリをデプロイ ~ 全体概要編 ~

お疲れ様です．Tomです．

表題の通り以前，AWSで簡易なWebアプリをインターネット環境にデプロイしました．
記録として，考えたことややったことなどを記載していきたいと思います．

記事全体としては編で構成されており，アプリのコンポーネントごとに詳細を書いております．ここではアーキテクチャや思ったことも含めて”全体”を振り返るマテリアルになっています．

:::note warn
下の【今回の動機】からは私の話になり，技術は関係なくなるので，飛ばしていただいて問題ありません．
:::

# 概要
実施したことは大きく下記のようになっており，非常にシンプルです．
- **アーキテクチャの構想と設計**
- **インフラの構築**
┗ サーバ構築： スペック選定から必要パッケージインストールまで
┗ DB関連： 構築からテーブル作成まで
┗ ネットワーク： VPC（ACL等も含む），ドメイン取得（Route 53），証明書関連
- **アプリケーションのビルド**
※ Javaのソースをビルドしてテストを行い，不具合や改善点があればソースコードを修正するということを繰り返しました．言うなればなんちゃってアジャイルです．

直面したエラーやその対処法，技術的な詳細はそれぞれ個別の編でまとめようと思います．


# Webアプリの概要とそのアーキテクチャ
アプリの概要としてはECアプリのようなものを想定しています．在庫管理はもちろん，ユーザがアカウントを作って出品し，購入することもできます．いろいろ自分なりに試行錯誤した結果，下記のようになりました．インターネットに公開したとはいえ，主にセキュリティ面でアプリそのものが本番のクオリティではないため，試験環境ということにし，凝りすぎずに自分で勝手に決めた納期ファーストで作りました．（※ サーバ名は念の為マスクしました．あしからず…）

![Screenshot from 2024-09-08 09-23-49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2918716/73ad4204-ebad-1695-a3ac-0a1fe66a086c.png)

玄人の方はいろいろ思うところがあると思いますが，RDSのレプリケーションの挙動を学習する時間を確保する前に納期が来てしまったことと，`アプリケーション編`で後述する`application.properties`にてDB接続を設定するのですが，このときにエンドポイントごとで設定を書き分けなければならず，今回はこのような設定と記載を落とし所としてみました．なにかアドバイスがありましたらコメントでご教示いただけますと幸いです．

# それぞれの記事
- **インフラの構築** (各リンクをご参照ください)
┗ [サーバ構築](https://qiita.com/tomtech_2024/items/bf4f69b97a73ad7ac703)
┗ [DB関連](https://qiita.com/tomtech_2024/items/70be82d0963cee0f2870)
┗ [ネットワーク](https://qiita.com/tomtech_2024/items/45b7a6531e0c47147e4b)

- **アプリケーションのビルド**

https://qiita.com/tomtech_2024/items/f82e6c3a1c8c2b06b875

:::note info
これ以降，技術関係なくなります．
:::

# 今回の動機
数年前に参加したHackathonでWebアプリを書いたのですが，そのチームのメンバーを驚かせたいと思ったことがきっかけです．

:::note warn
したがって今回3分クッキングのようにポッとアプリが出てきます(笑) 
アプリに関して今回僕がやったのは，そのときに時間切れで実装できなかったデバッグや改修がメインです．
:::

学生時代は授業にてやらされたC言語で挫折し，興味はあったものの「IT技術には縁がない人生なんだな」と諦めていました．しかし，Hackathonでプロダクト企画から実装までを行い，「こんなに夢中になれるものがあるのか」と自分でも驚くほどのめり込んでしまいました．結果決勝まで残ってプレゼンした記憶があります．

Hackathonが終わったあとも，「このアプリがスマホでアクセスできるようになったら，みんなおもろがってくれるだろうな」と思い，ちまちまインターネットの原理からサーバの原理まで学習しているうちに，年月が経ち，気がつくとエンジニアになっていました🤯

それが僕の技術キャリアを始めた原点です．ダラダラと生きていく予定だったのが，ハリと目的を与えてくれた気がします．そんなこともあって，恩返しではないですが原点のフラグを回収して，一つのチェックポイントとしてやってみようというのが今回の試みでございます．

またその調べていた一環でAWSやLPIC,Javaの資格をとっていたこともあり，その知識を自分なりにアウトプットしてみようというのも動機のひとつです．

P.S.) C言語は今思うととても大事な言語であり勉強中ですが，ただのしょうもない演算をさせたりするのはCSの入口としてふさわしくないと考えます．

# 実装を終えて（所感）
勉強を進めてなんとなく仕組みがわかるようになり，環境構築を終えて数年ぶりにアプリが動いたときはとても興奮しました．
屋根裏部屋のホコリかぶった箱を開けて，学生時代の思い出の品を眺めているようなノスタルジーがありました（笑）

今回自分でデプロイしてみて，サービスとして公開するのはこういう感じなんだと勉強になりました．うまく言葉では表現できないのですが，公開することは作業のほんの一部であって大したことではないものの，境界をまたぐ責任と興奮を感じました．

またぶっちゃけやりたいとは思っていたものの，「できないだろうし，できても何年もあとのことだな」と心のどこかで思っていました．しかし実際に実現させてみて，「難しそうだなと感じてしまっているものも，期限決めてやれば案外すぐにできることがある」ということも実感できました．私が漫画から学んだ大切な教訓を身を持って再認識しました．（※1）（※2）

![D_Er2PsU4AAwdNA.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2918716/49823d6e-107c-8cb0-fcc9-261fc2c01ed5.jpeg)

編にまとめた記事でも体験して学んだことはあらかた書けました．これらが何か皆さんの活動のヒントになれば幸いです．

お読みいただき，ありがとうございました！

## 出典
（※1）「ジョジョの奇妙な冒険 Part7 スティール・ボール・ラン」より引用
（※2）https://note.com/nabeharuj/n/n1946d7a1ae0b
Tauri 2.0でtauri-controlsを使うときに気をつけること

## はじめに

Tauriには`tauri-controls`というライブラリがあります。これはフレームレスウィンドウでOS固有のコントロールボタンを再現[^window-controls-overlay]してくれるもので、アプリのタイトルバーをカスタマイズしたいときに非常に便利なライブラリです。

[^window-controls-overlay]: いわゆる https://github.com/WICG/window-controls-overlay のやつです。

しかし、Tauri 2.0で使用するときにいくつかハマるポイントがあったので概要と対策方法を書きます。今後のバージョンアップで必要なくなるかもしれません。

## `` error[E0432]: unresolved import `tauri::Icon` ``系

Rustのコンパイルエラーです。これは`tauri-controls`のドキュメントでインストールすると書いてある`tauri-plugin-window`クレートが原因です。

https://github.com/agmmnn/tauri-controls/issues/30#issuecomment-2339206922

### 対処法

```shell
cargo rm tauri-plugin-window
```
```diff_rust
tauri::Builder::default()
    .plugin(tauri_plugin_os::init())
-   .plugin(tauri_plugin_window::init())
    ...
```

依存関係とBuilderのプラグインから削除します。

ドキュメントではインストールすると書いてありますが、Tauri側のアップデートに伴って不要になりました。[^tauri-plugin-window-archived]

[^tauri-plugin-window-archived]: GitHubのリポジトリも[アーカイブされています](https://github.com/tauri-apps/tauri-plugin-window)。

## `Command os_type not found`

WebView側に表示されるエラーです。`tauri-controls`は自動でプラットフォームを検出しますが、このエラーが出る状態では検出できなくなってしまいます。

https://github.com/agmmnn/tauri-controls/issues/30#issuecomment-2268731763

これは`tauri-controls`が依存している`@tauri-apps/plugin-os`とRust側のプラグインである`tauri-plugin-os`クレートのバージョンが合っていない（互換性がない）ことが原因です。ドキュメントにしたがって`cargo add tauri-plugin-os`すると最新バージョンが入ってくるのでこのエラーに遭遇しやすいと思います。

### 対処法

```shell
cargo add tauri-plugin-os@=2.0.0-beta.6
```

Rust側のプラグインのバージョンを`2.0.0-beta.6`に固定してインストールします。`=`がないとすでに他にバージョンがインストールされている場合に置き換わらない可能性があるので注意。

## コントロールが反応しない / `abc not allowed. Permissions associated with this command: abc`[^abc]

[^abc]: この項目に出てくる`abc`という部分は適宜読み替えてください。

これは`src-tauri/capabilities/abc.json`で権限が許可されていない状態でAPIを呼び出したときにWebView側で表示されます。コントロール（ウィンドウのボタン）を押しても反応しないという場合もおそらくこれが原因です。

### 対処法

`src-tauri/capabilities/abc.json`の`permissions`に以下の権限を設定する。

```json
    "core:window:allow-start-dragging",
    "core:window:allow-toggle-maximize",
    "core:window:allow-internal-toggle-maximize",
    "core:window:allow-minimize",
    "core:window:allow-is-maximized",
    "core:window:allow-set-fullscreen",
    "core:window:allow-is-fullscreen",
    "core:window:allow-close",
    "core:event:allow-listen",
    "os:allow-os-type",
```

<details><summary>おまけ：各権限の用途</summary>

コードをすべて読んだわけではないので、確実ではないですが各権限の用途のまとめです。
```jsonc
    // タイトルバーをドラッグしたとき
    "core:window:allow-start-dragging",

    // 最大化/もとに戻すボタンが押されたとき
    "core:window:allow-toggle-maximize",
    
    // タイトルバーをダブルクリックで最大化/もとに戻す用（Windows限定？）
    "core:window:allow-internal-toggle-maximize",
    
    // 最小化ボタンが押されたとき
    "core:window:allow-minimize",
    
    // 最大化されたかどうかの検出用（アイコンが変わる）
    "core:window:allow-is-maximized",
    
    // macosで拡大ボタンが押されたとき
    "core:window:allow-set-fullscreen",
    "core:window:allow-is-fullscreen",
    
    // 閉じるボタンが押されたとき
    "core:window:allow-close",
    
    // リサイズ検知用
    "core:event:allow-listen",
    
    // platformが指定されなかった場合の自動検出用
    "os:allow-os-type",
```
</details>

## さいごに

Tauri 2.0はまだrcバージョンであり、API自体は安定しているものの、`tauri-controls`のようなサードパーティのライブラリ側の対応が追いついていない現状があります。それでも今使いたいという方の助けになれば幸いです。

わからない点や解決しなかった場合は、コメントしていただければ、同じライブラリ利用者として解決できるようにお答えします。
【React】onClickで複数の関数を実行する方法

# はじめに
私は現在、Reactを使用して開発を行っています。今回は、onClickで複数の関数を実行したい場合にどのように実装すれば良いのか悩んだため、その解決方法を紹介します。

# 実装方法
今回、Chakra UIを使ってボタンを作成しており、onClickにonClickAddTodoとonOpenの2つの処理を追加したいと思っていました。（最終的には、この処理は不要になり、1つの関数にまとまりましたが、経緯を説明します。）

最初に書いていたコードは以下のようなものでした。onClickに対して関数を2つ渡せば動作するだろうと考えていたのですが、これではうまく動きませんでした。
```react
<Button colorScheme="blue" onClick={onClickAddTodo,onOpen}>
 登録
</Button>

```

そこで、下記のように修正しました。

```react
<Button colorScheme="blue" onClick={() => { onOpen(); onClickAddTodo(); }}>
 登録
</Button>

```

onClickで複数の関数を実行する場合、1つの関数にまとめて、その中で複数の関数を呼び出す必要があります。これにより、onClickで2つ以上の処理を実行できるようになります。

# 終わりに
結果的に、この処理は不要となりましたが、今後の開発でもこのようなケースに対応できるよう、引き続き頑張っていきたいと思います。
admbmの2port uart

bcmbmと同じように2ポート対応してみました。

まずターゲットから信号線を引っ張り出します。

![写真（2024-09-13 8.54）.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/5b0f2dd0-a0df-9e3c-985a-b08db82ffd5e.jpeg)

![写真（2024-09-13 8.53）.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/bcb4ca98-cdde-582d-1ebd-1bf8ae87b4dd.jpeg)

半田ごてを足元にあてて、カッターの刃を入れて浮かせました。うまくやれば浮かせずにはんだ付けできるかもしれませんが、失敗するとブリッジするので、浮かせた方がいいです。もとじょんじょらしくできるといいのですが。（庄内弁）

テストは二つ目のポートのRX,TXをつないで行います。

![TWOPORT (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/104066/985ddcad-2049-c34a-633e-c94c53f126c6.png)

このスクリプトでテストします。

https://github.com/yamori813/mruby-yabm-samples/blob/main/uart/twoports.rb

実行結果

```
Hit any key to stop autoboot:  0 
## Booting image at bfc40000 ...
   Image Name:   mruby on YABM
   Created:      2024-09-14  22:43:55 UTC
   Image Type:   MIPS Linux Kernel Image (bzip2 compressed)
   Data Size:    364637 Bytes = 356.1 kB
   Load Address: 80010000
   Entry Point:  80010000
   Verifying Checksum ... OK
   Uncompressing Kernel Image ... OK

Starting kernel ...

admbm-mruby aedc59a(master) Sun Sep 15 07:43:54 JST 2024
mruby 320c757(master) - 3.3.0
Clock : 175000000
MRB SIZE 423
MRB SHA256 90e414b5651bbb1438e089f0e2a8a0ad36af2f2af52a5bacffc1f8a857f6df76
UART two port test
abc
```
Salesforce からカレンダー招待を送信する


今一つ解決策がよくわからない。


+ [LWC を使用せずに Salesforce からカレンダー招待を送信する](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007erT4FSAU)

イベントの詳細を含む .ics ファイルを作成します。
Apex を使用して、.ics ファイルを添付した電子メールを送信します。
.ics ファイルを添付して電子メールを送信する Apex コードを記述します。


1. イベントを作成する: Salesforce を使用して、件名や時間などの詳細を含むイベントを設定します。
2. 招待者の追加:イベントの「出席者」セクションに連絡先またはリードを含めます。
3. 招待状の送信: Salesforce はカレンダーを添付した招待状を自動的にメールで送信します。
4. ワークフローまたはプロセス ビルダーを使用して自動化:必要に応じてイベントを作成して送信するための自動化を設定します。

+ [Salesforce で新しい商談が作成されると、カレンダーの招待状が自動的に作成され、顧客に送信されます。](https://trailhead.salesforce.com/trailblazer-community/feed/0D54S00000BsPg5SAF)

プロセス ビルダーも、「レコードが作成されたとき」の条件で使用でき、「アクション」タブで「イベント」を選択すれば準備完了です。

つまり、フローを使用する必要がない場合です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/589b0a55-1caf-a2cf-9c4f-6ffa4dc36f76.png)
(3.3V)M5Stamp S3で温度センサー、Grove - Temperature Sensor V1.2で遊ぶ


x かころぐみよ

参考

![o_coq399.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/b44c7791-eb6f-abd9-dd34-9e3cbb9de773.jpeg)


https://wiki.seeedstudio.com/Grove-Temperature_Sensor/


結果

![o_coq402.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/9db3da0a-cecb-2370-b490-9bf360b6adf3.jpeg)

![o_coq401.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/1597c828-7587-0229-56b3-677feac39a44.jpeg)

![o_coq403.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/af57c0c2-6fc3-5e59-0281-3844734ce334.jpeg)


プログラム(おもに3.3V化)



```c++



// Demo code for Grove - Temperature Sensor V1.1/1.2
// Loovee @ 2015-8-26

#include <math.h>

const int B = 4275;               // B value of the thermistor
const int R0 = 100000;            // R0 = 100k

//const int pinTempSensor = A0;     // Grove - Temperature Sensor connect to A0
const int pinTempSensor = 15;     // Grove - Temperature Sensor connect to A0


#if defined(ARDUINO_ARCH_AVR)
#define debug  Serial
#elif defined(ARDUINO_ARCH_SAMD) ||  defined(ARDUINO_ARCH_SAM)
#define debug  SerialUSB
#else
#define debug  Serial
#endif

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    int a = analogRead(pinTempSensor);

    //float R = 1023.0/a-1.0;
    //R = R0*R;
    float R,v1,a1,o1; 
    v1 = ((float)a)*(3.3/4096.0); //電圧を求める
    a1 = v1 / 100000.0;           //電流を求める
    o1 = 5.0 / a1;                //全体の抵抗を求める
    R = o1 - 100000.0;    //全体の抵抗から検出抵抗を引く

    float temperature = 1.0/(log(R/R0)/B+1/298.15)-273.15; // convert to temperature via datasheet

    Serial.print("temperature = ");
    Serial.println(temperature);

    delay(100);
}



```
wslでmanim その22

# 概要
wslでmanimやってみた。
練習問題やってみた。

# 練習問題
おっぱい関数を表示せよ。

# 参考にしたページ

https://qiita.com/otupy/items/56887ddcd52c6bd2f07f


# 成果物

![ManimGraph_ManimCE_v0.18.1.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/4a14fa65-b8e6-8fae-14f1-007ef82da600.gif)


# サンプルコード

```

from manim import *

def oppai(y, t):
	x_1 = (1.5 * np.exp((0.12 * np.sin(t) - 0.5) * (y + 0.16 * np.sin(t)) ** 2)) / (1 + np.exp(-20 * (5 * y + np.sin(t))))
	x_2 = ((1.5 + 0.8 * (y + 0.2 * np.sin(t)) ** 3) * (1 + np.exp(20 * (5 * y + np.sin(t)))) ** -1)
	x_3 = (1 + np.exp(-(100 * (y + 1) + 16 * np.sin(t))))
	x_4 = (0.2 * (np.exp(-(y + 1) ** 2) + 1)) / (1 + np.exp(100 * (y + 1) + 16 * np.sin(t)))
	x_5 = (0.1 / np.exp(2 * (10 * y + 1.2 * (2 + np.sin(t)) * np.sin(t)) ** 4))
	x = x_1 + (x_2 / x_3) + x_4 + x_5
	return x

class ManimGraph(Scene):
	def construct(self):
		t = np.arange(300) / 50 - 3.0
		y = oppai(t, 0.1)
		axes = Axes(x_range = (-3.2, 3.2, 0.4), y_range = (0, 2, 0.4), tips = False)
		axes.add_coordinates() 
		name = Text("Graph").next_to(axes, UP, buff = 0)
		graph = axes.plot_line_graph(t[1:300], y[1:300], add_vertex_dots = False, line_color = RED)
		self.play(Write(axes), Write(name))
		self.play(Write(graph))
		self.wait()



```

以上。
Arduino UNO 3で温度センサー、Grove - Temperature Sensor V1.2で遊ぶ


x 過去ログを見よ!!

参考

![o_coq399.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/3e0aa190-b18f-ff9f-6500-62032758db3e.jpeg)


https://wiki.seeedstudio.com/Grove-Temperature_Sensor_V1.2/



結果

![o_coq398.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/1767cf9a-7593-c306-7a3c-795d42695ee7.jpeg)

![o_coq401.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/c3ec8fdb-88ca-5fd1-efbf-386186ad4c79.jpeg)



![o_coq400.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/576cc1a6-8195-f2e3-30e3-c64bab5bc3f2.jpeg)


プログラム(サンプルそのまま)



```c++


// Demo code for Grove - Temperature Sensor V1.1/1.2
// Loovee @ 2015-8-26

#include <math.h>

const int B = 4275;               // B value of the thermistor
const int R0 = 100000;            // R0 = 100k
const int pinTempSensor = A0;     // Grove - Temperature Sensor connect to A0

#if defined(ARDUINO_ARCH_AVR)
#define debug  Serial
#elif defined(ARDUINO_ARCH_SAMD) ||  defined(ARDUINO_ARCH_SAM)
#define debug  SerialUSB
#else
#define debug  Serial
#endif

void setup()
{
    Serial.begin(9600);
}

void loop()
{
    int a = analogRead(pinTempSensor);

    float R = 1023.0/a-1.0;
    R = R0*R;

    float temperature = 1.0/(log(R/R0)/B+1/298.15)-273.15; // convert to temperature via datasheet

    Serial.print("temperature = ");
    Serial.println(temperature);

    delay(100);
}



```
How to show React API Data

import { useEffect, useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'

function App() {
  const [user, setUser] = useState([])
  const [ageValue,setAgeValue] =useState('')
  const url = 'https://dummyjson.com/products';
  // const url = 'https://dummyapi.online/api/movies';

  const updateAgeValue = (e) => {
    console.log(e.target.value)
    setAgeValue(e.target.value)
  }


  async function fetchApi(url) {
    const res = await fetch(url)
    const data = await res.json();
    console.log(data.products)
    setUser(data.products)
    // console.log(user,"user")
  }

  useEffect(() => {
    fetchApi(url)
  }, [])

  if (ageValue < 20) {
    return (
      <>
        <h1>Hello frind</h1>
        <input type='number' onChange={updateAgeValue}  />
      </>
    )
  } else {
    return (
      <>
        <h1>Hello Vite Project</h1>
        <input type='number' onChange={updateAgeValue}  />
        {
          user.map((item) =>
            <>
              <div className='d-flex'>
                <div>
                  <h1>{item.id}</h1>
                  <p>{item.brand}</p>
                  <p>{item.returnPolicy}</p>
                  <img src={item.images} width="200px" />
                  <img src={item.imdb_url} />
                  <p>{item.rating}</p>
                  <a href="https://tech.pokoreel.com/react-api-data-with-condition-example/">React API Data With Condition</a>
                </div>
              </div>
            </>
          )
        }
      </>
    )
  }
}

export default App
transformersのTrainerのSchedulerについて

## 本記事の目的
Hugging FaceのTrainerを最近使用しているが、Schedulerについての説明記事が少ないのでメモ用として記載する。

## Version
```shell
transformers==4.39.3
torch==2.3.1
```

## 設定方法
```python
from transformers import TrainingArguments

TrainingArguments(
    optim='adamw_torch' # adamw_torch, adamw_tf, sgd, ...
    lr_scheduler_type=<ここにstring型で設定>
)
```
下記項目が設定可能

- linear
- cosine
- cosine_with_restarts
- polynomial
- constant
- constant_with_warmup
- inverse_sqrt
- reduce_lr_on_plateau

## スケジューラの説明
名前だけではどれがなんのスケジューラと対応しているのかわかりずらいのでメモ用に記載

### linear
- get_linear_schedule_with_warmup

### cosine
- get_cosine_schedule_with_warmup

### cosine_with_restarts
- get_cosine_with_hard_restarts_schedule_with_warmup

### polynomial
- get_polynomial_decay_schedule_with_warmup

### constant
- get_constant_schedule

### constant_with_warmup
- get_constant_schedule_with_warmup

### inverse_sqrt
- get_inverse_sqrt_schedule

### reduce_lr_on_plateau
- get_reduce_on_plateau_schedule


## 引数について
`num_training_steps`については、Trainer内部で自動に与えられるため、与える必要はない

`num_warmup_steps`については、下記のように`TrainingArguments`の`warmup_ratio`で設定可能
```python
TrainingArguments(
    warmup_ratio=0.1
)
```

その他スケジューラによっての固有の引数は`TrainingArguments`の`lr_scheduler_kwargs`で設定
```python
TrainingArguments(
    lr_scheduler_kwargs={"num_cycles": 4.5},
)
```
HTML とCSS の振る舞いを確認できるページを自作サイトで公開するまで

## やったこと
mdn 先生の↓みたいなボタン押したらCSS が切り替わるやつを作った。  

https://developer.mozilla.org/ja/docs/Web/CSS/line-height

## 前提
- 自作サイトはAngular でできている。
- ページの枠は作成済み。
- 詳しくは前々回記事参照

https://qiita.com/magurouhiru/items/300a8641b28a419eab98

## 本編
さっくり`ng g component`で作成したコンポを`Route`に登録する。  
で、早速迷った。  
どうやってHTML のタグとかそのまま表示するのだろう？  

最初は`<code>`タグ使えばいいのかなあと思っていたらダメだった。  
調べたら`<`は`&lt;`に`>`は`&gt;`に置き換えたらいけるらしい。

https://naruweb.com/coding/htmltag/

pipe とか作りたっかったけど、HTML テンプレートだと複数行の文字列がダメだった。  
なので、おとなしくコンポ内で文字列を定義して、関数で置き換えるやり方にする。  
と思って関数作って表示させたものがこちら。  
![1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/822099/4728931a-9f77-7493-7c0f-99a9b6c7e5b7.png)

あれ？思ってたのと違う...  
置換せずに表示させたら期待通りに表示された  
![2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/822099/01061192-50d0-2304-cdd1-50b2f5c1b095.png)

### ちょっと脱線
↑で置換させずに済む理由が分かるようなわからないような。  
なので調べてみたら書き記事を見つけました。  

https://atmarkit.itmedia.co.jp/ait/articles/1702/13/news127.html

曰く、Angular ではデフォルトでサニタイズされる。  
そのため、HTML として表示させたい場合は適当なタグに`[innerHTML]="表示させたい文字列"`を追加させればよい。  
とのこと。  

確かに、API 通じて取ってきた文字列表示させようとしたら、なんかスクリプトが動いた！  
なんてなったら大変ですね。  
Angular はデフォルトでセキュア！みたいな記事をどっかで見ましたが、こういうところでしょうか。  

### 話を戻して
これで元のHTML を表示させることはできるようになりました。  
次はCSS を動的に変える仕組みが必要です。  

参考サイトは以下の通り。

https://blog.officekoma.co.jp/2019/08/classangular.html

https://qiita.com/gongon143/items/b7ec50cf6417110c1879

いくつか方法があるようですね。  
- ngStyle: コンポ内で定義したスタイルを適応する感じ。
- style binding: コンポ内で定義したスタイルを適応する感じ。
- ngIf: 要素ごと入れ替える感じ。
- `[class.xxxxx]`: `true` を入れたらそのクラスが適用される感じ。
- `[ngClass]`: ↑が連想配列形式になった感じ。
- `[class]`: 適用したいクラス名を入れる感じ。class と一緒には使えない。

まずは`[class.xxxxx]`を使おうかなと考えました。  
私の思いとしては、CSS で定義したクラスを変更する動きにしたい。  
デフォルトのスタイルを充てるためにclass と一緒に使いたいの2点です。  
なので、残りは↑と`[ngClass]`になります。  
ただ、`[ngClass]`は変えたいクラスが複数ある時に横に長くなってしまいそうと思いました。  
したがって↑の考えになったということです。  

やってみて気づきましたが、`[class.xxxxx]`はエディタの補完が聞きました。  
とても助かる。  
と思いましたが、ボタンとか大量に作るので、結局CSS からスニペットを出力する関数作りました。  

そしてなんやかんやで完成。  
成果物のリンクはこちら。  

https://magurouhiru.github.io/mysite/#/study/BoxModel

## 感想
楽しかったです。  
あと、自作サイトがだんだん大きくなってきた感じがしてうれしいです。  

今回の記事は以上です。  

前回記事はこちら  

https://qiita.com/magurouhiru/items/acce141010141ac37098

詳細見たい人はこちら  

https://github.com/magurouhiru/mysite/pull/32

https://magurouhiru.github.io/mysite/