OmniAICreator/Qiita-1.07M huggingface dataset:
Pico WのWi-FiをUSBマスストレージクラスで設定する

表題のとおり。Raspberry Pi Pico Wとても便利なのですが、Wi-FiのSSIDとパスワードをソースコードに埋め込んだり、ビルド時の引数で埋め込む方式を取ることが多いようです。ですが、これ、Picoを普通の人に渡した時「えぇ！？自宅のWi-Fiに繋ぎたいだけなのに、ぼくが開発環境を用意するのかい！？」的な破綻を迎えてしまうので、あまり良い方法ではないと考えています。

そこで、Picoのオンボードフラッシュメモリ2MBの一部を、USBマスストレージクラスのファイル`WIFI.TXT`として扱えるようにします。PicoをPCにUSB接続してマウントし、Wi-Fiの設定を普通のテキストファイルとして編集できるようにしてみました。

https://github.com/oyama/pico-msc-wifi-setting

ホストPCからはFAT12ファイルシステムとして認識されます。Pico上ではUSBを介した`WIFI.TXT`への読み書きをフックして、Wi-Fiの設定値の構造体をフラッシュメモリに出し入れする擬似FAT12として動作しています。読み書き動作はmacos 14.4.1, Windows 11 Homeで動作確認しています。

![screenshot.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108691/1564b05d-037c-cf0e-86ea-be41666317d4.png)

Wi-Fiの接続に成功するとPico WのLEDを点灯します。
![_DSC0624.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/108691/1f72e9e8-46be-4650-094d-d5ab4963a544.jpeg)


ファイルシステムの容量は32KBにしています。もっと小さく8KBとかにしても良いのですが、macosのテキストエディットで編集すると、中間ファイルなどが容量を食い潰してしまう問題があったためこのサイズにしました。
`WIFI.TXT`以外のファイルはPicoのSRAMに展開するだけで、フラッシュメモリには保存していません。なので見た目ではファイルを追加して保存できますが、Picoを電源オフすると消えます。無常。

ファイルシステムまわり以外のつまずきポイントは`cyw43_arch_wifi_connect_timeout_ms()` などブロックする類のAPIでWi-Fi接続しようとすると、USBデバイスとしては止まってしまうあたり。`cyw43_arch_wifi_connect_async()`を使ってブロックせずすぐにループに復帰するようにしています。USBのポーリングなどと関係あるのかや、ブロックせずにcore1で処理したら良いのかとか、不慣れなのでよく知りません。

## 追記

擬似FAT12はフットプリントが軽いけど扱う側の脳に優しくないなぁと考えて、Picoのオンボードフラッシュメモリの末尾64KBをFAT12でフォーマットする構成も試してみました。この構成であれば:

- Pico内部から[FatFs](http://elm-chan.org/fsw/ff/)でファイル操作
- PCからUSBマスストレージクラスでフラッシュメモリデバイスとしてファイル操作

と、双方からファイルシステム全体を読み書き永続化できます。

https://github.com/oyama/pico-usb-flash-drive

pico-sdkには元々FatFsがバンドルされているので、追加要素は無し。擬似FAT12版の容量が32KBだったのに正規FAT12版が64KBに増えているのは、FatFsの[ミニマムのボリュームサイズ](http://elm-chan.org/fsw/ff/doc/appnote.html#limits)が128セクターな故です。

ちなみに、Picoのようなデバイスは突然の電源切断がありうるので、「Picoで収集したセンサーデータの保存」といった用途には[littlefs](https://github.com/littlefs-project/littlefs)など耐性のあるモダンなファイルシステムを使うのがより好ましいでしょう。しかし「ホストPCからファイルとして手軽に操作したい」類のユースケースでは現状難があるので、しばらくはFATが第一候補になります。
UTM製品はNWのどこに置くのか

UTMをどこにおくか気になったので調査

○UTMを置く位置
◆ブリッジモード　透過モード
この構成だとRTの下にUTMを置いている。ブリッジモード(FORTIGATEをL2SWで使うみたいな話かな？)を使う。
internet------RT-----※utm----lan
https://network.yamaha.com/setting/utm_appliance/introduction_utm/initial_setting_utm/initial_setting_bridge

◆ルーターモード　
ルーターのように使う
https://network.yamaha.com/setting/utm_appliance/introduction_utm/initial_setting_utm/initial_setting_router


○ルーター機能もあるからRTいらなくない？UTMでよくないか？
UTMは保守費用があるからRTのほうがいいかもな。
会社の方針によるという微妙な答えに辿り着いた。


◆機能について
https://network.yamaha.com/products/firewalls/utx200/index

○結局UTMって必要なの？
脆弱性があった時にrtとutmがあったら安心だよねってことだ。
Typescript入門2(Webアプリケーション開発環境構築：React編)

## 備忘録 Web：Typescript②
※調べた内容を後日見返せるように本記事を作成しています。

今回はReact、Typescriptを使用したWebアプリ開発環境の構築を行っていく。
仕様ツール類は以下の通り。
1. Node.js
1. Webpack
1. React

#### 仕様ツール類説明
##### 1. Node.js
JavaScriptを、PythonやRubyのようにパソコン上で動かせるようにしてくれるもの。
ただし、あくまでもフレームワークではなく、Javascript実行環境である。

###### 特徴
- JavascriptでOS機能へのアクセスを可能
- Node.js を使うことで、クライアントとサーバサイド両方を慣れ親しんだJavaScriptで言語を統一して記述でき、プログラマの負担も軽減

###### Node.jsを利用してできること
- Webサーバ構築
- Webアプリケーション開発
- スマホアプリ開発
- IoT分野の開発
- CSSへの変換

そんな便利なNode.jsにも以下のようなデメリットはある。
- Node.js非対応のサーバーがあること
- フロントエンドのプログラムがそのまま使用できないこと
- 大規模開発に不向きなこと

##### 2. Webpack
複数のJavaScriptファイルを一つのファイルにまとめて出力するツール。
###### 特徴
- 依存関係のあるJavaScriptのモジュールを解決
- 読み込み順を気にせず、1回のリクエストで済むため効率的
- JavaScriptモジュールをブラウザで扱える形に簡単に変換できる
- ローダやプラグインなどが豊富
- 様々なフレームワークで採用されているのでライブラリからサンプルを見つけやすい

##### 3. React
UI用のJavaScriptライブラリ。
可読性やパフォーマンスを高められることから開発品質の向上が可能。

###### 特徴
- 「宣言的」でソースコードを理解しやすい
- スマホアプリでも活用可能
- 「仮想DOM」採用で処理が高速
- 最先端のユーザーインターフェースを作りやすい

#### 環境構築手順
React、Typescriptの開発環境構築手順は以下の通り。
1. Node.jsをインストール
[公式のHP](https://nodejs.org/en/download)からインストールファイルをダウンロードし、ダウンロードしたファイルをダブルクリックでインストールする。

1. Typescriptをインストール
以下コマンドを実行し、Typescriptをインストールする。
`npm install -g typescript`

  実行確認コマンド
`tsc -v`

1. プロジェクト作成
コマンドプロンプト上でプロジェクトを作成したいフォルダに移動し、以下コマンドを実行する。
` npx create-react-app プロジェクト名 --template typescript`
ここではプロジェクト名を「test」とする。

以下フォルダ構成が作成されていれば成功。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761023/d0b2cb0b-1f45-e118-262c-7ef047459d15.png)

1. プロジェクト起動
プロジェクトフォルダに移動し、以下コマンドを実行する。
`npm start`

ブラウザ上に以下のような画面が表示されれば成功。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3761023/e3437ae9-c2a0-1ecf-4270-676b9dfca0b7.png)

以上が、React、Typescriptの環境構築手順となる。

#### 補足
Web画面の修正を行いたい場合は「src」フォルダのファイルを修正、保存により、自動で画面の情報が移り変わる。
主に「index.tsx」「App.tsx」を修正することで画面の内容を変えることができる。

ファイルの内容を以下に示す。
|ファイル名  |概要  |
|---|---|
|index.tsx|実行開始ファイル。プロジェクト上で最初に実行するファイル|
|index.css|index.tsxのCSSファイル  |
|App.tsx|APP関数定義ファイル。画面中央の内容を表示している。|
|App.css|App.tsxのCSSファイル|
|App.text.tsx|画面の「Learn React」を表示するファイル。|
VirtualBox(Windows10)でLinux Mintを動かす

## はじめに
最近、Linuxのディストリビューションを調査していたところ、Linux Mintというディストリビューションがあることを知りました。Ubuntuを使うことが多い僕としてはDebian系のディストリビューションであることもポイントが高い！まずは使ってみたいと思い、まずはWindows10のVirtualBoxの仮想環境上でLinux Mintを動かしてみました。

## 事前準備
VirtualBoxとLinux Mintのisoファイルをダウンロードします。

[VirtualBox](https://www.virtualbox.org/wiki/Downloads)
[Linux Mintのisoファイル](https://ftp.riken.jp/Linux/linuxmint/isos/stable/21.3/)

isoファイルは2024/03/31現在最新の安定バージョンの21.3をダウンロードします。

## インストール
まずはVirtualBoxをマネージャーを開きます。
![virtualbox_main.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3750854/b308e414-f4ac-e35c-3cae-c7f8e806d2f0.png)
図の上部にある **[新規(N)]** ボタンを押下します。

![virtualbox_os.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3750854/414e4d82-ad1d-8ff5-df72-244aa8c08082.png)
**[名前(N)]** エディットボックスに任意の名前、**[ISO イメージ(I)]** エディットボックスにダウンロードしたisoファイルを指定します。**[タイプ(T)]** エディットボックスと **[バージョン(V)]** エディットボックスは自動で選択され、選択が無効化されます。**[次へ(N)]** ボタンを押下します。

![virtualbox_vboxuser.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3750854/47661422-1659-9f81-0c6c-9c0165ced65f.png)
ユーザー名とパスワードは自動で入力されているので、そのままにします。
**[Guest Additions]** のチェックボックスにチェックしてGuest Additionsのインストールを有効にします。**[次へ(N)]** ボタンを押下します。

![virtualbox_hardware.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3750854/276dca7e-288b-c7ef-429f-cec2e2bb01c9.png)
任意のメモリー量と仮想CPU数を指定します。**[次へ(N)]** ボタンを押下します。

![virtualbox_harddisk.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3750854/070614de-2946-12a9-b0cf-f82732f57d43.png)
任意のディスクサイズを指定します。**[次へ(N)]** ボタンを押下します。

![virtualbox_abst.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3750854/c6a69b6c-b467-98c3-67b8-82d5b6ffcb55.png)
**[完了(F)]** ボタンを押下します。仮想マシン **mint21** がVirtualBoxに追加されます。

![virtualbox_vmon.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3750854/457669bd-afcf-a38a-1000-35b15756657a.png)
追加された仮想マシン **mint21** をダブルクリックします。画面のようにVMの電源をオン...が表示されます。

![virtualbox_fail.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3750854/5d52df3b-4a66-ee73-5dcf-8c1f4cd0ac55.png)
なぜか仮想マシンのブートに失敗します。ISOファイルが見つからないのかもしれません。DVDのコンボボックスからダウンロードしたisoファイルを指定して、**[マウントとブートのリトライ(M)]** を押下します。
isoイメージからのLinuxをロードすることになるので起動に時間がかかりますが、起動完了後、Linux Mint OSのメイン画面が表示されます。
ここでLinux Mintをインストールするため、Install Linux MintのCD-ROMのアイコンをダブルクリックします。あとは画面の指示に従って進めるとLinux Mintをインストールできます。
[PowerShell] Az PowerShell module の Debug

# はじめに
もう春ですね:cherry_blossom:

折角なので新しいことを始めよう（`トライしよう`）かと、 Azure の管理を CLI ではなく PowerShell で始めてみました。
そこで早速躓いたので Debug をしたくなったのですが、なかなか検索ヒットしませんでした。ので、ここに記します。

# 早速答え(How to)


>One of the first steps you should take in troubleshooting a problem with the Azure Az PowerShell module is to enable debug logging.
To enable debug logging on a per command basis, specify the Debug parameter.

`Get-AzResource -Name 'DoesNotExist' -Debug`

>To enable debug logging for an entire PowerShell session, you set the value of the DebugPreference variable to Continue.

`$DebugPreference = 'Continue'`


# 反省
検索してもなかなか辿り着かなかったのでメモとしてここに記しました。
`PowerShell debug`で検索してたけど、Az PowerShell module限定の方法っぽいから見つけられなかったのかもなぁ。
公式読むの大事ですね。


https://learn.microsoft.com/en-us/powershell/azure/troubleshooting?view=azps-11.4.0#enable-debug-logging


# おわりに
最後まで見ていただき、ありがとうございます。
少しでも読んでいただいた方の役に立てたら幸いです。
よかったらチャンネル登録（フォロー）、高評価（いいね:heart_decoration:）、コメント:pen_fountain:などなど何でもお願いします:pray:

週一で投稿目指しています。もし何かリアクションをしてくれるとやる気が出るので、次週は投稿することを約束します。今のところ約束は履行されています（投稿なかった週はリアクションが前週になかったのだからと正当化して休んでます:grimacing:）
python+tkinterでダイアログを使うためのテンプレート

# python+tkinterでダイアログを作るためのテンプレート
ダイアログを作るときに、複数ページを調べ歩かなくても済むように。欲しいwidgetと機能が入ったテンプレートを作りました。python標準ライブラリのtkinter対応です。モーダルダイアログ、モードレスダイアログの両方に対応しています。自分用なので、一部のwidgetはテンプレートに入っていません。

## モーダルダイアログ
モーダルダイアログを表示している間は、呼び出し元(メイン画面)の操作はできません。ダイアログを閉じた後に、ダイアログの設定値を取り出して使います。

## モードレスダイアログ
モードレスダイアログを表示している間も呼び出し元(メイン画面)の操作ができます。ダイアログを閉じても「非表示」になっているだけで、再表示すれば途中までの入力内容も残っています。

## ダイアログテンプレートにあるwidget一覧
コメントにはgrid指定によるwidget配置のコツの表記もあります。
テンプレートの中で必要なものはコピーしたり配置を変える。テンプレートに無いMenuやProgressbar等のwidgetは、自分がダイアログでは使わないので入っていません(メイン画面では使います)。「tkinter widget 一覧」で検索すれば見つかりますので、必要があればコピペしてください。

<table>
<tr><th>widget</th><th>備考</th></tr>
<tr><td>Label</td><td></td></tr>
<tr><td>Button</td><td></td></tr>
<tr><td>Checkbutton</td><td></td></tr>
<tr><td>Labelframe</td><td>いわゆるグループボックス</td></tr>
<tr><td>Radiobutton</td><td></td></tr>
<tr><td>Combobox</td><td>選択したときのメソッドあり</td></tr>
<tr><td>Listbox</td><td>選択したときのメソッドあり</td></tr>
<tr><td>Text</td><td></td></tr>
<tr><td>Scrollbar</td><td>Listbox/Textと連動する</td></tr>
<tr><td>Scrolledtext</td><td>Text+Scrollbarの代わり</td></tr>
<tr><td>filedialog</td><td>ファイル名入力(フォルダ名入力はコメントで)</td></tr>
<tr><td>Entry</td><td>パスワードやAPIキー入力でのポイントはコメントで</td></tr>
<tr><td>Spinbox</td><td></td>間違えやすいポイントの指摘あり</tr>
</table>

## テンプレートダイアログは以下のような機能をサポートしています。

<ol>
<li>モーダル、モードレスの切り替え</li>
フラグ引数(modalflag=<b>True</b>あるいは<b>False</b>)で指定することができます。<br>
途中で切り替えることはできません。<br>
<br>
<li>モーダルダイアログの終了時。OK、Cancel、☒でのクローズの判別</li>
ダイアログを閉じた場合に、ラジオボタン等の設定値を取り出して利用します。<br>
<br>

<li>モーダルダイアログとモードレスダイアログ、それぞれの単体テストサンプル</li>
呼び出し側のテンプレートクラス付きです。<br>
<br>
<li>ダイアログから呼び出し側のインスタンスメソッドを呼び出し可能</li>
モードレスダイアログの表示と非表示の切り替えに使っています。
</ol>

## ダイアログテンプレート　ソースコード
ご自由にお使いください。

https://gist.github.com/TakanobuSaito/7c3d47675d18df912a1aa5d9748a3954
イーロン・マスクの最新AIチャットボット、ある重要な分野でChatGPTを凌駕

本記事は[Entrepreneur](https://www.entrepreneur.com/business-news/how-elon-musks-new-grok-ai-stacks-up-against-competitors/471906)により翻訳されたものです。
![QQ20240403175442.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2298092/d0ab422c-afd7-5885-e2bb-f13d0b7bbf15.png)

イーロン・マスクのxAIスタートアップがGrokのAIモデルを一般公開してから約2週間、同社のAIチャットボットがアップグレードされることになりました。

同社は木曜日にGrok-1.5を発表し、その最新モデルはより長い文書を理解し、より複雑なプロンプトを処理し、より高度な推論を行うことができると主張しました。

Grok-1.5は、コーディングと数学のスキルが向上し、オリジナルの1.0からステップアップしているように見えますが、その発表の投稿によると、いくつかのベンチマークテストでは、GoogleのGemini Pro 1.5 AI、OpenAIのGPT-4、AnthropicのClaude 3 Opusに遅れをとっていますが、ある重要なHumanEvalテストではOpenAIを上回っています。

Grok-1.5は、HumanEvalベンチマークでGPT-4よりも高いスコアを獲得しました。HumanEvalベンチマークは、AIモデルのトレーニングデータには含まれていない164の挑戦的なプログラミング問題で構成されています。GPT-4のスコアは67%、Gemini Pro 1.5のスコアは71.9%、Grok-1.5のスコアは74.1%でした。

Grok-1.5は、初級から上級までの57科目の知識をカバーするMMLUテストで81.3%を記録し、Google Geminiのスコア（83.7%）に迫る成績を収めました。

また、小学生から高校生までの数学の競技問題をカバーするベンチマークであるMATHテストでは50.6%を記録し、GPT-4のスコア52.9%に迫るスコアを記録しました。
マスク氏は金曜日のソーシャルメディア投稿で、Grok 1.5は来週までにX（旧Twitter）で利用可能になるはずだと述べました。

Xのオーナーは次世代のGrokに大きな期待を寄せており、Grok-1.5の次のステップは現在利用可能なAIを「すべての指標で」上回るだろうと書いています。Grok 2は「現在トレーニング中」と彼は投稿に書いています。

Grok AIは現在、Xで月額16ドル以上のPremium+に加入している人だけが利用できます。

マスクは今月初め、xAIの競合であるOpenAIを提訴し、OpenAIにそのAIの背後にある研究と技術を公開させる判決を求めました。

以上、イーロン・マスクの最新AIチャットボットについて伝えました。ほかのAIトレンドに関心を持つ方に、低画質な画像を一瞬で[高画質化](https://vanceai.com/ja/image-enhancer/)するVanceAI画質向上や、最高な[ぼかし除去 サイト](https://vanceai.com/ja/sharpen-ai/)を目標に働いているVanceAI画像鮮明化という二つのAI製品を試すのをお勧めします。
研修開催時の庶務を自動化してくれるPowerAutomateクラウドフローアイデア

# とにかく楽をしたい！
研修開催時の地味な事務処理（事前課題通知、予定表設定、Teams追加等…）を自動化してしまおうというアイデア
一つ作っておけば流用できるのでご参考まで

# 前提：
### Formsで研修参加用フォームを作成しておく
ただ参加登録するだけではなく、
参加者のニーズやレベル感など把握できるのでアンケートはやったほうがよきです✨

### 研修用Teamを作成しておく
お好みだけど

### 研修会議予定をOutlook等でたてておく
Teamsや日程等を設定して保存しておきましょう


# 構成：Forms投稿をトリガーに作成

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629241/e6ad09d6-1cd2-3310-835e-eb0a8466d600.png)

※なんか英語ですみません。。
Forms投稿をトリガーに以下発動します
- 参加者へTeamsでの通知
- 研修用TeamへのTeam追加
- 予定表への追加

## Forms部分について
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629241/a298e980-50cd-0ad9-4a98-32bcfd2e507e.png)

### 「新しい応答が送信されるとき」アクション
フォームID→研修応募フォームを追加

### 「応答の詳細を取得する」アクション
フォームID→研修応募フォームを追加
応答ID→「新しい応答が送信されるとき」アクションのトリガー結果のIDを追加

## Teams部分について

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629241/08cc308c-2bba-c620-c9ae-faec62346f66.png)

### 「チャットまたはチャネルでメッセージを投稿する」アクション
受信者→Formsの回答者メール
メッセージ→事前課題や当日URL等を記載

### 「チームにメンバーを追加する」アクション
チーム→研修用Teamsの追加
追加するユーザ→Formsの回答者メール


## 予定表部分について

※Outlookアクションに「イベントの更新」があるのですがこれで更新を行ってしまうと、人が追加されるごとに全員へ通知がなされてしまうため、メールの転送アクションを使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3629241/d780639e-fa16-b321-74e8-b55c04192139.png)

### 「電子メールの取得（V2)」アクション
フォルダー→送信済みアイテム
未読メッセージのみを取得する→いいえ
検索クエリ→Subject :'予定名'
上：1

あらかじめたてておいた研修の予定がここで活躍！
件名で検索をかけることで、送信済みアイテムに残っている最新の予定のアイテムをとってきます。
もし送信済みアイテムに残っていない場合はあらかじめ予定表を自分等に送付しておきましょう。

### 「メールの転送（V2）」アクション
メッセージID→上記「電子メールの取得（V2)」で取得したメッセージIDを追加（一応検索クエリの「上」1にしてるので実質対象が一つしか出てこないですが、結果が配列となっているため繰り返しアクションに入ってしまいます）
宛先→Formsの回答者メール

これで完成✨
いちいち参加者集計をすることなく庶務を自動化できます(^^♪
Proxy経由のOCP bastionサーバーで必要な設定

OCPのbastion(踏み台)サーバーをProxy経由でのインターネット接続環境として構築した場合、目的に応じて複数個所でProxyの設定が必要です。実際にやってみたところ、設定ミスも重なり苦戦したため備忘として記録しておきます。

検証環境：
- bastionサーバー：RHEL8
- OCP 4.12

bastionサーバーをProxy経由でのインターネットアクセスにする場合、必要な設定は次の3項目です。
１．RHELサブスクリプション用設定 -- `/etc/rhsm/rhsm.conf`
２．RHELマシン自体のhttp proxy設定 -- `/etc/profile.d/proxy.sh`
３．OCPクラスターのProxy設定 -- `install-config.yaml`

以下では、Proxyサーバーが次のとおりと仮定した場合の設定例を示します。
- IPアドレス: a.b.c.d
- ポート番号: 80

## 1. RHELサブスクリプション用設定
Red Hat Subscription Manager Configuration Fileである `/etc/rhsm/rhsm.conf` ファイル内の次のパラメーターを設定します。このファイルはシステムにデフォルトで存在しています。該当部分を編集します。

- proxy_hostname
- proxy_port

/etc/rhsm/rhsm.conf 抜粋例：
```
proxy_hostname = a.b.c.d

proxy_port = 80
```

上の設定をしたうえで、subscription-manager コマンドを実行します。
```
# subscription-manager register
```

## 2. RHELマシン自体のhttp proxy設定

（環境変数に設定するので、当設定方法は一例です。）
このファイルはデフォルトでは存在しません。新規作成します。
/etc/profile.d/proxy.sh の例：

```
http_proxy="http://a.b.c.d:80/"
https_proxy="http://a.b.c.d:80/"
no_proxy="192.168.10.10,.lab.example.com,172.30.117.80,.svc"
export http_proxy https_proxy no_proxy
```

環境変数に反映します。
```
# source /etc/profile.d/proxy.sh

# env | grep proxy
http_proxy=http://a.b.c.d:80/
https_proxy=http://a.b.c.d:80/
no_proxy=192.168.10.10,.lab.example.com,172.30.117.80,.svc
```

## 3. OCPクラスターのProxy設定
新規クラスターの install-config.yaml ファイルでProxy設定を行うことにより、OpenShift Container Platform が Proxy を使用するように設定できます。

`install-config.yaml`ファイルにProxy設定をします。

install-config.yaml 抜粋例：
```
apiVersion: v1
baseDomain: example.com
proxy:
  httpProxy: http://a.b.c.d:80/ 
  httpsProxy: http://a.b.c.d:80/ 
  noProxy: example.com,127.0.0.1,localhost,192.168.10.0/24
:
```



## 参考
https://qiita.com/gold-kou/items/c71793bb931b3f365a48

https://qiita.com/chidakiyo/items/95cbc263f8157cfa5cd7

https://access.redhat.com/documentation/ja-jp/openshift_container_platform/4.12/html/networking/enable-cluster-wide-proxy

https://rheb.hatenablog.com/entry/openshift42-proxy-upi
macでpipをインストール

webスクレイピングをしたいのでpipをインストールする。
pythonのversion
```
$ python3 --version
Python 3.8.6
```

pipをダウンロードする。
```
$ sudo easy_install pip 
sudo: easy_install: command not found
```
ダウンロードできない（泣）。

# 対処

参考

https://qiita.com/sho03/items/bafdf19592f6f9d71135

「get-pip.py」は事前にダウンロードしていたのでその後の作業をする。
```
$ python3 get-pip.py
...
Successfully installed pip-24.0 wheel-0.43.0
```

インストールに成功する。

```
$ which pip
/Library/Frameworks/Python.framework/Versions/3.8/bin/pip
```
pipがの場所を確認できたら終了！
Microsoft365にプライバシー声明を追加する

Microsoft Formsなどを使っていると、画面の一番下に
`このフォームの所有者は、応答データの使用方法についてのプライバシーに関する声明を提供していません。個人情報や機密情報を記載しないでください。`
等と表示される。

プライバシーポリシーに関する宣言を行えばよいのだが、
この文言でググっても古い情報しか出なかったので、2024年4月現在の方法をメモしておく。

### 管理画面センター

管理センターから「ID」を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666388/2fdc77f9-9e67-6f68-715a-4864955353d1.png)
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666388/f7227483-cf7a-7192-4349-5df2e6f3a19b.png)

### Entra管理センター

概要→プロパティ
のところにプライバシーに関する声明のURLがあるのでここに記載
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/666388/7215d74c-0852-8f31-a4e9-47c2cf4fa0ea.png)

### まとめ

要はActive DirectoryがEntraに変わったということを知らなかったので見つけられなかったというオチ
EFSとは？

## 初めに:star2:
先日はブロックストレージサービスである、EBSについて書かせて頂きました。
EBSの学習をされる際に、セットで出てくるのが今回ご紹介させて頂く「EFS」です。
聞いた事ある人も多いのではないでしょうか？

EBSと何が違うの？特徴って何？など解説させて頂きます。

## EFSとは？
EFSとは、NFSをサポートするファイルストレージサービスです。
EC2インスタンスから、EFSファイルシステムをマウントして使用していきます。

※用語解説:thumbsup_tone1:
・ファイルストレージ
　データをファイルやフォルダ単位で階層構造として管理するストレージ
 
・NFS(Network File System：ファイル共有プロトコル）

・マウント
　ストレージなどをOSに認識させて利用可能にすること

## マウント方法
EBSでは、複数インスタンスからの同時アクセスや他AZからのアタッチは
出来ませんでしたね:no_good:

###### しかし

EFSでは、複数EC2インスタンスからアクセスと他AZからのアクセスが可能です！
![スクリーンショット 2024-04-03 17.26.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3744656/17fe62c1-446d-d97e-6029-7e601d6a035e.png)

じゃあどっちを使っていけばいいの？と疑問が...
そちらに関してもまとめていきます！

## EFSの特徴

| 項目 | 内容 |
|:-:|:-:|
| 可用性  | 複数のAZにまたがっているので、<br>特定のAZに障害が発生しても他のAZでサービスが継続できる。<br>AWS Backupがデフォルトで有効になっている。 |
| 安全性  | デフォルトで保管時のデータ暗号化が有効になっている |
| 排他制御  | NFSを使用した場合、ロック機構(排他制御)が使用されている。  |
| 容量のスケーリング  | 保存したデータ量に伴って自動的にスケーリング(拡張・縮小)  |

## ユースケース
###### EFS
| 項目 | 内容 |
|:-:|:-:|
| WEBアプリの共有  | 複数のWEBサーバーが同じファイルや画像に<br>アクセスする必要がある場合  |
| データ共有  | データを共有するアプリケーション  |
| ログファイル  | 複数のEC2インスタンスが同じログファイルに<br>書き込む必要がある場合  |
| バックアップ  | 複数のバックアップジョブが同じバックアップストレージに<br>アクセス可能  |


###### EBS
| 項目 | 内容 |
|:-:|:-:|
| DB  | DBファイルやログファイルなどの永続ストレージとして使用  |
| ブートストラップ  | EC2インスタンスのブートストラップボリュームとして使用  |
| 高可用性  | スナップショットを使用して定期的に<br>バックアップし高可用性を確保  |


## つまり...
![スクリーンショット 2024-04-03 17.56.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3744656/25cd89a3-694a-8bdd-5a16-0dcbc23a5b45.png)
複数のEC2インスタンスを使用していたり、自動的なスケーリングをしたい時は、
### EFS

特定のEC2インスタンスに固有のデータを保存したり、手動でスケーリングしたい時は、
### EBS


## 最後に
今回、EFSとEBSについて学習をしてみて、どの場面で使用したらいいのか？という疑問が少し解消出来たと思います。
似た様な言葉が多く、頭が混乱してくる時もありますが、一つずつ整理していきたいと思います！
久しぶりのターミナルでpythonを起動させる方法を忘れた。

①自分のmacでpythonを起動する際にpythonがインストールされているか確認する。

```
$ python3 --version
Python 3.8.6
```

②pythonを起動させる
```
$ python 3.8.6
-bash: python: command not found
```
あれ？
```
$ python
-bash: python: command not found
```
なんで？
```
$ python -v
-bash: python: command not found
```
ターミナルでpythonを起動させる方法を忘れてしまった。

# 対処

参考

https://genchan.net/it/programming/python/25739/

```
$ python3
Python 3.8.6 (v3.8.6:db455296be, Sep 23 2020, 13:31:39) 
[Clang 6.0 (clang-600.0.57)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>>
```

無事起動できました！
引数をちゃんと理解されてますか？！



## はじめに
位置引数とキーワード引数についてに関しての備忘録。

## 🦁<INS>**結論**</INS>🦁
位置引数とキーワード引数がある。
位置引数とキーワード引数の記入方法やルールを理解をすることでコードの全体の理解が深まる。

それによりプログラミングが楽しくなる！

:::note info
押さえておくべき点
* 位置引数は先に書いた引数から順番に関数に渡される。
* 基本はキーワード引数の方がよい。
* キーワード引数はエディタとも連携してる（VSコード）
* キーワード引数は引数の名前を指定することで、どの引数に何を渡しているのかが一目でわかる。
* キーワード引数にはデフォルト値を設定することで省略可能。
:::  
***

:::note alert
注意点
* 位置引数とキーワード引数を混在させる場合には位置引数を先に書く必要がある。
* 呼び出す際に同じキーワード引数は使うとエラーになる
:::  

***

## <INS>**書き方一覧**</INS>

## <INS>**位置引数の書き方**</INS>
引数を書かれた順番（位置）に合わせて引数を置く
nameが1番目→Alice
ageが2番目→28
```py
def print_user_info(name, age):
    print(f"Name: {name}, Age: {age}")

print_user_info("Alice", 28)
```


## <INS>**キーワード引数の書き方**</INS>
キーワードを指定して引数を渡す。
age=28、name="Alice"
順不同で大丈夫。
```py
def print_user_info(name, age):
    print(f"Name: {name}, Age: {age}")

print_user_info(age=28, name="Alice")
```


## <INS>**キーワード引数でデフォルト値を設定**</INS>

```py
def print_user_info(name, age=30):
    print(f"Name: {name}, Age: {age}")

print_user_info(name="Alice")
print_user_info(age=28, name="Alice")
# この場合、ageは上書きされる
```

## まとめ✍️
キーワード引数を理解するだけでPythonのコードをかなり読めるようになると思う。
あとは引数名が、わかりやすければ可読性が上がって、コードを書くことが楽しくなりますね。
[Google Calendar API(V3)] Javaでカレンダーのメタデータを取得する

Google Calendar API V3(Java)を使用して、
Googleカレンダーのメタデータを取得する方法
についてご紹介します。

:::note info
APIを利用する環境の準備から始める場合や、コードを実行する際は、
[⧉[Google Calendar API(V3)] JavaでCalendar APIを使う](https://qiita.com/doran/items/c735c7b05c0a2ed4bfdb)
を参照ください。
:::

|No||目次|
|---|---|---|
|1||[メタデータを取得](#1-メタデータを取得)|
||1|[スコープ](#11-スコープ)|
||2|[実行](#12-実行)|
||3|[レスポンスの内容](#13-レスポンスの内容)|

## 1. メタデータを取得
Googleカレンダーメタデータを取得します。

### 1.1. スコープ
このAPIを実行するには、以下のいずれかのスコープを指定してください。
```
CalendarScopes.CALENDAR
CalendarScopes.CALENDAR_READONLY
```
[⧉[Google Calendar API(V3)] JavaでCalendar APIを使う(2.2 Driveインスタンスを取得)](https://qiita.com/doran/items/c735c7b05c0a2ed4bfdb#22-calendarインスタンスを取得)
でスコープを指定してください。

### 1.2. 実行

```java
public static void main(String[] args) throws Exception{
    Calendar calendar = getCalendar();
    
    Calendar.Calendars calendars = calendar.calendars();
    Calendar.Calendars.Get get = calendars.get("カレンダーID");
    
    com.google.api.services.calendar.model.Calendar res = get.execute();
    System.out.println(res);
}
```
#### 1.2.1. HTTPリクエスト
GET:　https:\//www.googleapis.com/calendar/v3/calendars/{カレンダーID}
が実行されます。

#### 1.2.2. クエリパラメータ
クエリパラメータはありません。

### 1.3. レスポンスの内容

#### Calendar
(com.google.api.services.calendar.model)

|メソッド|戻り値|説明|
|---|---|---|
|getId|String|カレンダーID|
|getEtag|String|Eタグ|
|getKind|String|リソースの種類<br>固定文字列："calendar#calendar"|
|getConferenceProperties|[ConferenceProperties](#conferenceproperties)|会議タイプ|
|getDescription|String|説明|
|getLocation|String|カレンダーの地域|
|getSummary|String|名前|
|getTimeZone|String|タイムゾーン|

#### ConferenceProperties
|メソッド|戻り値|説明|
|---|---|---|
|getAllowedConferenceSolutionTypes|List\<String\>|サポートされている会議タイプ<br>"eventHangout"、"eventNamedHangout"、"hangoutsMeet"|

<br>
<br>
おしまい。。
【Laravel】upsertにはunique indexが必要

## 背景
複数のレコードの更新をかける際に、`キーとなるカラムに値が重複している場合は更新、なければ挿入`という処理をしたかったが、うまくいかずにハマってもた。

どんどんレコードが増えていくのでなんでやん！って叫びながら作業をしていた。

そんな自分が悲しくて、備忘録として残す。

## upsertにはunique indexが必要

単純に第２引数に重複チェックしたいカラム名を渡すだけではアカンのです。

とある記事には

>第２引数にはユニークキーやプライマリキーを指定すると良い

という旨の記載があったが、語弊があるんやん？

* ❌ すると良い (should)
* ⭕️ せなあかん (must)

つまり、**ユニークキーあるいはプライマリキーを指定しなければ重複チェックは行われない**が正しい。

ちゃんとドキュメントにも記載がある。

> SQL Server以外のすべてのデータベースでは、upsertメソッドの第２引数のカラムへ"primary"、または"unique"インデックスを指定する必要があります。さらに、MySQLデータベースドライバは、upsertメソッドの第２引数を無視し、常にテーブルの"primary"および"unique"インデックスを既存レコードの検出に使用します。

わいの使ってるのはMySQLやったさかい、必要。shouldやなくてmustやったって話や。

```php:第２引数のdepartureとdestinationはprimary OR uniqueインデックスが必要ということ
Flight::upsert([
    ['departure' => 'Oakland', 'destination' => 'San Diego', 'price' => 99],
    ['departure' => 'Chicago', 'destination' => 'New York', 'price' => 150]
], ['departure', 'destination'], ['price']);
```

もし今はインデックスがないテーブルに対してupsertを使いたければインデックスを追加する必要がある。せなあかん。

```sql:uniqueインデックスを追加する
ALTER TABLE flights ADD UNIQUE departure (departure);
ALTER TABLE flights ADD UNIQUE destination (destination);

// 実行する前に、重複したデータが存在しないことを確認する必要あり。重複しているとインデックスが追加できない。
```

もしインデックスを追加できなければupdateOrCreateであるしかないかな。
bulk処理ができへんけども、、、
GitHub リモートリポジトリにプッシュされているコミットを削除する方法

## まずは戻したいコミットIDを特定します。
コミットIDの取得方法については
以下のリンクもしくはGUIツールにてご確認してください。

https://akizora.tech/git-commit-4520#google_vignette

## ローカルブランチを目的のコミットにハードリセットします。
```shell:
git reset --hard <コミットID>
```
その後、リモートリポジトリに以下のコマンドで強制プッシュします。
```shell]
git push --force
```

:::note warn
注意
・`--force` オプションはリモートリポジトリの履歴を書き換えるため、変更は元に戻せません。

・この操作は慎重に行う必要があります。強制プッシュは個人のリポジトリ、または他の開発者と合意がある場合に限って使用することを推奨します。
:::

# 告知
最後にお知らせとなりますが、イーディーエーでは一緒に働くエンジニアを
募集しております。詳しくは採用情報ページをご確認ください。

https://eda-inc.jp/recruit/

みなさまからのご応募をお待ちしております。
【Azure】(Syncing triggers...)で関数アプリが同期できない

# 何が起こったか
1. `terraform import`で抽出した`AzureFunctions`の情報を抽出。
1. 1で抽出した情報を元に、別リソースグループにて`AzureFunctions`を作成。
1. `VSCode`を使用してソースコードデプロイ
1. デプロイには成功するのだが、同期に失敗する。

## デプロイには成功するのだが、同期に失敗する
上記の「4」について、もう少し詳細状況を残します。
`VSCode`を使用してソースコードをデプロイし、待つこと数分経ちますと、ログに以下のような出力がされます。
```bash
Deployment successful. deployer = ms-azuretools-vscode deploymentPath = Functions App ZipDeploy. Extract zip. Remote build.
Syncing triggers...
Syncing triggers (Attempt 2/6)...
Syncing triggers (Attempt 3/6)...
Syncing triggers (Attempt 4/6)...
Syncing triggers (Attempt 5/6)...
Syncing triggers (Attempt 6/6)...
Encountered an error (ServiceUnavailable) from host runtime.
```

デプロイには成功したが、同期には失敗しました、と表示されています、、、。

# 結論
私の場合は、`AzureFunctions`のランタイムのバージョンを 

python3.11 ⇒ **python3.9**

に変更したら解決しました。

# 修正までの経緯
なぜ上記のような修正に至ったかをご紹介していきます。

## ログの確認
[Kudu Service Overview](https://learn.microsoft.com/en-us/azure/app-service/resources-kudu)にあるように、`Kudu`を使用することでデプロイ時のログを確認することが出来ます。

`App Service`のリソース名を使用して、`https://<app-name>.scm.azurewebsites.net` にアクセスすると、以下のような画面に遷移できます。
![画像1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3337466/d213282a-d212-f2d7-192f-9a4da4c5abb1.png)

右上の「Log stream」から、`App Service`のログを確認できます。

そちらのログで、以下のような出力が確認できました。
```bash
ImportError: cannot import name 'Sequence' from 'collections' 
```

上記のエラー文をそのまま検索し、一番最初に引っかかった[ISSUE](https://github.com/skorch-dev/skorch/issues/895)にて、「`python 3.10`でデプロイすると同様のインポートエラーが発生する」と記載されていたので、もしやと思い バージョンを `python 3.9` にしたら、解決しました、、。

# 最後に
`Azure Functions`デプロイ時の同期エラーに関して、備忘録がてら残させていただきました。
本事象について、根本原因はさらに調査が必要となりますが、今回この方法で暫定的に対処ができたため、ご紹介させていただきました。
もし、本事象についての知見がある方がいらっしゃいましたら、コメント頂けると幸いです。

# ご紹介
株式会社ジールでは、「ITリテラシーがない」「初期費用がかけられない」「親切・丁寧な支援がほしい」「ノーコード・ローコードがよい」「運用・保守の手間をかけられない」などのお客様の声を受けて、オールインワン型データ活用プラットフォーム「ZEUSCloud」を月額利用料にてご提供しております。
ご興味がある方は是非下記のリンクをご覧ください：
https://www.zdh.co.jp/products-services/cloud-data/zeuscloud/?utm_source=qiita&utm_medium=referral&utm_campaign=qiita_zeuscloud_content-area
[Typescript] JSDocを作る

# はじめに
JSDocを使用すると、関数、クラス、メソッド、パラメータ、戻り値などに関する情報をコメントとして記述できます。これにより、コード自体がその動作や使用方法を説明するドキュメントとして機能し、後からコードを読む人が理解しやすくなります。

Typescript初めての方下記がおすすめです。

https://qiita.com/oharu121/items/3aadafdd5daa8dc53c64
# 実装
### type
TypeScriptでは型エイリアスやインターフェースを定義する際にJSDocコメントを使用できます。これは、その型が何を表しているのか、どのような場合に使用されるのかを説明するのに役立ちます。
```typescript:example.d.ts
/**
 * ユーザーを表す型。
 * @typedef {Object} User
 * @property {number} id ユーザーの一意の識別子。
 * @property {string} name ユーザーのフルネーム。
 * @property {string} email ユーザーのメールアドレス。
 */
export type User = {
  id: number;
  name: string;
  email: string;
};
```
### function
関数にJSDocコメントを付けることで、その関数が何をするのか、どのようなパラメータを取り、何を返すのかを明確にすることができます。
```typescript:example.ts
/**
 * 二つの数値の合計を計算します。
 * @param {number} a 最初の数値。
 * @param {number} b 二番目の数値。
 * @returns {number} 二つの数値の合計。
 */
const add = (x: number, y: number): number => {
  return x + y;
};
```
関数にカーソルを合わせると説明文が出ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760374/95e20174-d500-8a13-bc53-bb5673916890.png)

### class
クラスとそのメソッドにJSDocコメントを付けることで、クラスの目的、使用方法、各メソッドの機能を文書化できます。
```typescript:example.ts
/**
 * Personクラスは人物を表します。
 */
class Person {
  /**
   * Personのインスタンスを作成します。
   * @param {string} name 人物の名前。
   * @param {number} age 人物の年齢。
   */
  constructor(name, age) {
    /** @type {string} */
    this.name = name;
    /** @type {number} */
    this.age = age;
  }

  /**
   * 人物の自己紹介をします。
   * @returns {string} 自己紹介の文。
   */
  introduce() {
    return `こんにちは、私の名前は${this.name}です。${this.age}歳です。`;
  }
}
```
# まとめ
この記事では、JSDocを使用してTypeScriptコードにドキュメントコメントを追加する方法とその利点について説明しました。JSDocは、関数、クラス、メソッド、パラメータ、戻り値などのコード要素に対して豊富な情報を提供することができる強力なツールです。これにより、コードが自己文書化され、後からコードを読む人がその動作や使用方法を容易に理解できるようになります。
Kotlinでの文字列ゼロ埋めやスペース埋め

## はじめに

Kotlinでゼロ埋めをする話です。初めてやろうとしたときに意外と手間取ったので、昔のブログで記事にしました。

https://fuji-tak.hateblo.jp/entry/2019/08/03/235558

何人かの方に読んで頂けているようだったので、アップデートした版を改めてこちらでも書いてみます。

ちなみに今回実行してるKotlinの環境は`1.9.23`です。

## Javaだとこう

`number`の値を4桁までゼロ埋めする

```java
String.format("%04d", number);
```

## Kotlinだとこう

### java.lang.String

ググって最初に見つけたのはこれ。
要はKotlinではなくJavaの`String`を使えば、当然Javaの書き方でいけるよというものです。が、流石に毎回完全修飾名を書くのは微妙すぎます。

```kotlin
java.lang.String.format("%04d", number)
```

### padStart

ちゃんと英語で`kotlin zero padding`とか調べると`padStart`という最適なものが出てきます。

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/pad-start.html

```kotlin
"12".padStart(4, '0')
```

ちゃんと4桁まで0埋めされる

```
0012
```

### padEnd

後ろを埋めたかったら`padEnd`がある。

https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/pad-end.html

```kotlin
"12".padEnd(4, '0')
```

```
1200
```

### スペース埋め

リファレンスに書いてある通りですが、`padStart`や`padEnd`の第二引数はデフォルトがスペースなので、省略すればスペース埋めになります。

```kotlin
"12".padStart(4)
```

```
  12
```

### 必要なら拡張関数とか自作

「自分がやりたいのはゼロ埋めだけだから、毎回`0`を指定したくない」とかであれば、そういう関数を作ってしまってもいいかもしれないですね。

```kotlin
fun CharSequence.padZeroStart(length: Int): CharSequence = padStart(length, '0')

"12".padZeroStart(4)
```

```
0012
```

あとは数値からゼロ埋めの文字列を作ったりも。

```kotlin
fun Int.padZeroStart(length: Int): String = toString().padStart(length, '0')

12.padZeroStart(4)
```

```
0012
```

### 余談

久しぶりにKotlinのリファレンス見に行きましたけど、サンプルがWeb上でそのまま実行できるようになっていて驚きました。サクッと動かして試せて便利ですね。
[Typescript] 型アサーション（Type Assertion）

# はじめに
型アサーションは、開発者がTypeScriptの型チェッカーに対して、「私はこの値の型を知っている、そしてそれを特定の型として扱いたい」と伝える方法です。

Typescript初めての方下記がおすすめです。

https://qiita.com/oharu121/items/3aadafdd5daa8dc53c64
# 実装
```diff_typescript:example.ts
(async () => {
+  type User = {
+    name: string;
+    age: number;
+  };

  const result = await fetch("www.example.com/getUser/taro");
  if (!response.ok) {
    throw new Error(`HTTP error! status: ${response.status}`);
  }
  
-  const user = await result.json();
+  const user = (await result.json()) as User;

  console.log(`こんにちは、私の名前は${user.name}です。${user.age}歳です。`);
})();
```
# まとめ
この記事では、TypeScriptにおける型アサーションの概念とその使用方法について説明しました。型アサーションは、開発者がコンパイラに対して「この値の型を私は知っている」と宣言し、特定の型として扱うことを指示する方法です。これは、特に動的なデータを扱う際や、既存のJavaScriptコードをTypeScriptに移行する際に有用です。
[Typescript] 複雑なtypeを分解する

# はじめに
型を分解して定義することのメリットは以下の通りです：

1. **可読性の向上**: 型が単純化され、各型の責務が明確になるため、コードの読みやすさが向上します。
2. **再利用性の向上**:分解されたtypeは他のコンテキストでも再利用しやすくなります。
3. **メンテナンスの容易さ**: 型が分割されていると、特定の部分の変更が他の部分に影響を与えにくくなり、メンテナンスが容易になります。

Typescript初めての方下記がおすすめです。

https://qiita.com/oharu121/items/3aadafdd5daa8dc53c64

以下の例を考えてください。どうtypeを定義しますか？
```typescript:example.ts
const user = {
  id: 1,
  name: "Alice Johnson",
  contact:{
    phone: "080-8466-5622",
    email: "alice.johnson@example.com",
  },
  posts: [
    {
      id: 101,
      title: "My First Post",
      content: "This is the content of my first post.",
      comments: [
        {
          id: 1001,
          content: "Great post!",
          author: "Bob Smith",
        },
        {
          id: 1002,
          content: "Thanks for sharing.",
          author: "Charlie Davis",
        },
      ],
    },
  ],
};
```
# 実装
下記に定義してみたところ、読みにくいですね。
```typescript:types.d.ts
type User = {
  id: number;
  name: string;
  contact: {
    phone: string;
    email: string;
  };
  posts: {
    id: number;
    title: string;
    content: string;
    comments: {
      id: number;
      content: string;
      author: string;
    }[];
  }[];
};
```
:::note info
慣習としてはtypesを「.d.ts」ファイルで定義します
:::
分解して定義すれば読みやすくなります。
```typescript:types.d.ts
type User = {
  id: number;
  name: string;
  contact: Contact; // Contact typeを利用する
  posts: Post[]; // Post typeを利用する
};

type Contact = {
  phone: string;
  email: string;
};

type Post = {
  id: number;
  title: string;
  content: string;
  comments: Comment[]; // Comment typeを利用する
};

type Comment = {
  id: number;
  content: string;
  author: string;
};
```
# まとめ
この記事では、TypeScriptで複雑なオブジェクトの型を定義する方法について説明しました。具体的なユーザー情報の例を通じて、ネストされたオブジェクトや配列を含む型の定義方法を見てきました。最初に示した一つの大きなUser型は、読みにくさと管理の難しさを示しています。これを改善するために、Contact、Post、Commentといった個別の型に分解し、User型の中でこれらを参照する方法を紹介しました。
OpsRamp - VMWare vCenter イベントの取り込み方法

本記事では、OpsRampでVMWare vCenterのイベントを取り込む方法について説明します。

# 概要
OpsRampでは、連携したVMWare vCenterからイベントをアラートとして取り込むことが可能です。

::: note warn
VMWare vCenterの[VMWare インテグレーション](https://docs.opsramp.com/integrations/virtualization/vmware/)設定が必要です。
:::

# 設定方法
VMWare vCenterのイベントを取り込むためには、「VMware vCenter Events」テンプレートを使用します。

- すべてのイベントを取得する場合

オンボードしたvCenterに「VMware vCenter Events」テンプレートを割り当てます。
「VMware vCenter Events」テンプレートは、コレクターがGatewayのモニタになります。
![assign-template.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/a32c5bfa-727a-c955-4f3b-7f15a23d7d96.png)

「vmware events policy」を「include」から「all」に編集してください。
![assign-template2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/bddd318d-0d8b-d7d3-73c1-2731457a83b2.png)

- 特定のイベントを取得する場合

特定のイベントを取得する場合、取り込みたいイベントをOpsRamp上に設定し、テンプレートにそのイベント設定を追加します。

https://docs.opsramp.com/integrations/virtualization/vmware/#vmware-events-monitoring

イベントの設定は、Setup > Monitoring > Eventsから可能です。
「Adaptor Type」に「Vmware」を選択、「Alarm name」にvCenterイベントのID (イベントIDに「.」が含まれる場合、「_」に置き換えてください。) を入力、「ディスプレイ名」にイベント名(任意の文字列でも可)を入力します。
![events-setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/2831c58c-8d44-7f75-c4bb-a9b25eb1d3bc.png)

作成したイベントを含めたモニタを、Setup > Monitoring > Monitorsから設定します。
既存の「VMware vCenter Events」モニタをコピーし、作成したイベントを追加します。
![monitor-setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/0524ebe4-eb5f-3b6d-cb00-1017e4070d76.png)

作成したモニタを含めたテンプレートを、Setup > Monitoring > テンプレートの管理から設定します。
既存の「VMware vCenter Events」テンプレートをコピーし、作成したモニタに置き換えます。
割り当てられたモニタをすべて削除し、作成したモニタを割り当てます。
![template-setting.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/06e493e2-8526-7127-030f-9db3cbe360bb.png)

オンボードしたvCenterに作成したテンプレートを割り当てます。

---------------------------------------
[<img width = 250 height = 200 src = "https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2344939/111de30a-4d33-aa33-5c7d-83d9015093e6.png">](https://www.vistanet.jp/knowledge-base/)
AuthenticationFailed: SCRAM authentication failed, storedKey mismatch

# 問題背景

サーバーAのmongodb replica setに、サーバーBにある新しいmongod4をsecondary nodeとして追加した。

そして、mongod4のログを見ると、下記のエラーが出ている。

`AuthenticationFailed: SCRAM authentication failed, storedKey mismatch`

```
{"t":{"$date":"2024-04-03T16:44:30.213+09:00"},"s":"I",  "c":"ACCESS",   "id":20249,   "ctx":"conn11504","msg":"Authentication failed","attr":{"mechanism":"SCRAM-SHA-256","speculative":false,"principalName":"__system","authenticationDatabase":"local","remote":"xxx","extraInfo":{},"error":"AuthenticationFailed: SCRAM authentication failed, storedKey mismatch"}}
```

# エラー原因
二つDBが使っているkeyFileが違っていた。


# 解決案

keyfileを一緒にすればよい。

ただ私の場合は、` md5sum /var/lib/keyfile` で確認して、hashが一緒になってるけど、
エラーが解消されず、別の原因があった。

新しいサーバーにDBを構築したとき、keyFileの保存場所を間違っていた。

サーバーAで`vi /etc/mongo1.conf`で見ると、
```
keyFile: /var/lib/mongo/keyfile
```

サーバーBで`vi /etc/mongo4.conf`で見ると、
```
keyFile: /var/lib/keyfile
```
になっていることがわかった。


保存場所を同じにしたら、問題解消。
kintoneで「時刻」フィールドの値を固定の時刻と比較する方法

ヘルプにもなく、カスタマイズじゃない情報なのでデベロッパーコミュニティにも情報がなかったので、チャットサポートに問い合わせてようやくわかった情報なので、ここに記録しておきます。

”指定時刻”フィールドに入力された時刻が、例えば朝の8時より前か後か、というIF文を書く場合。

**IF(指定時刻＜8＊60＊60,"以前","以降")**

教えてもらって「なるほど！」とコメントしてしまいました。

ヘルプの至るところにも、外部情報にも「kintoneの日時計算は”秒”」という記載は見知っていて、それこそ百も承知だったのに、時刻では基準がわからなかったのでこの「8＊60＊60」が全く出てこなかった。

書いてみれば、意外とあっさり、というか「そりゃ、そうだわ！」と解決しました。
あまりこういう式は使わないのでしょうかね？
【備忘録】初めてさくらサーバを触ってぶつかった壁

# はじめに

所用でさくらサーバを使って色々していた間にぶつかった壁の備忘録です。
都度更新します。

#### SSH接続方法
```terminal
ssh アカウント名@初期ドメイン(xxx.sakura.ne.jp)
```

## Pythonで使われるopensslの問題
仮想環境をつくってみたのでとりあえず何か動かそうとimport requestsをする際に,

```terminal
ImportError: urllib3 v2 only supports OpenSSL 1.1.1+, currently the 'ssl' module is compiled with 'OpenSSL 1.0.2-chacha (1.0.2k-dev)'. See: https://github.com/urllib3/urllib3/issues/2168
```
というエラーが出た。
urllib3のバージョンが新しく、サーバ環境のopensslに合わないらしい。

調べたところ、サーバのopensslは1.1.1であるが、
```terminal
% openssl version
OpenSSL 1.1.1k-freebsd  24 Aug 2021
```
pythonにコンパイルされているopensslは1.0.2となっていた。
```terminal
>>> import ssl
>>> print(ssl.OPENSSL_VERSION)
OpenSSL 1.0.2-chacha (1.0.2k-dev)
```



### 解決策
とりあえずurllib3をダウングレードして解決。
```terminal
% pip install urllib3==1.25.11
```
[Typescript] 関数に型をつける

# はじめに
関数のパラメータと戻り値に型を指定することで、TypeScriptのコンパイラは型の不一致や潜在的なエラーをコードが実行される前に検出できます。これにより、ランタイムエラーのリスクが減少し、デバッグ時間が短縮されます。

Typescript初めての方下記がおすすめです。

https://qiita.com/oharu121/items/3aadafdd5daa8dc53c64
# 実装
下記関数addはnumber typeの引数x, yを受け入れて、number typeの値を返却します。
```typescript:example.ts
const add = (x: number, y: number): number => {
  return x + y;
};

const result = add(5, 10); // 15
```
addにstringを渡すとエラーが出ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760374/7364a72c-f9a9-1771-bcaa-34a248deb97f.png)
また返却値をstringとして扱うとエラーが出ます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3760374/dd0b37a4-a066-e595-3dfc-29b6531c284b.png)
# まとめ
この記事では、TypeScriptを使用して関数のパラメータと戻り値に型を指定する方法とそのメリットについて説明しました。型注釈を適用することで、コンパイラがコードの型安全性を保証し、開発者がより信頼性の高いコードを書くことができるようになります。具体的な例として、add関数を紹介し、正しい型の引数が渡された場合の挙動と、型が不一致の場合にコンパイラがエラーを出す様子を示しました。
LPIC102対策：ポート番号の上限のおぼえかた

LPIC Level1を取得したときの思い出です。

# ポート番号の上限が覚えられない

LPICの2つ目の試験、102ではポート番号が出題範囲にあります。
ポート番号って、

|  | 範囲 |
|:-:|:-:|
|  Well known port number |  0 ～ 1024   | 
|   Registered port number |  1025 ～ 49151   | 
|  Dynamic port number  | 49152 ～ 65535  | 




なんですが、Well known portの上限1024はなんとか覚えるとして、Registered portとDynamic portの上限が覚えられませんでした。
一桁目の **4** 、 **6** は、なんとか。でも、それ以下の桁が…選択肢に似たような数字を並べられたらアウト。
２進数が頭に入っているような人ならするっと出てくるのかもしれませんが、わたしはそうじゃありません。
 **この数字の並びをまるっと覚えられたら！**
そう思って、

# 絵にしました
不適切な表現があるかもしれません。あくまで試験突破のための語呂合わせです。人を揶揄したり差別する目的はありません。合格したら忘れてください。

## Registered port number （予約済みポート、登録済みポート）の上限
49151→し、く、いちごいち→しっくいこい。

![しっくいこい.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2736931/d70dbb43-e3c4-cd32-ac50-eed0c10d422a.png)

sick（病気な）い（池の）鯉。
池の鯉が病気で、ふとんをかぶって寝ています。

## Dynamic port number （エフェメラルポート、動的ポート）
65535→む、ご、ご、み、ご→むこ、ごみこ。
![むこごみこ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2736931/7a69c5cf-2c7e-1750-6291-69b0766994be.png)


彼女のお婿さんはゴミです。ゴミと結婚する心優しい女性。婿ゴミ子、と呼ばれるようになりました。

# おわりに
語呂合わせって、覚えること自体が苦痛になると意味がないですよね...
わたしは絵で覚えるやり方をよく使います。へんな絵であるほど、効果的です。
pip listにあるbeautifulsoup4とbs4の違い

Webスクレイピングをするさいにモジュールをインストール。
```py
import requests
from bs4 import BeautifulSoup
```

モジュールは一体どこからきたのかをコマンドプロンプトで探す。
```
>pip list
beautifulsoup4            4.12.2
bs4                       0.0.1
```

beautifulsoupとbs4の違いはなに？


# 調べる
参考　

https://jobcode.jp/python-beautifulsoup4/


引用
どちらでもBeautifulSoup4が利用できますが、”pip install BeautifulSoup”の場合、古いバージョン（3）がインストールされ可能性があります。

BeautifulSoup4は、bs4ライブラリのモジュール（一部）であるためbs4でインストールすれば間違いはないです。


結論
bs4のなかにBeautifulSoup4が存在する。
Flutter　targetSdkVersionを見つけるのが面倒なので

これも個人メモ
## 経緯
カメラロールの画像を取得したい時に`targetSdkVersion`を設定しなさいとの記事があったので、そのバージョン数を探す旅を行ってました。
ただその記述されているファイルがどこにあるのか分からなく、すぐに発見できなかったので自分用に記載。

## 記載場所

`flutter/packages/flutter_tools/gradle/src/main/groovy/flutter.groovy`

↓設定してねって記載されてる記事

https://nobushiueshi.com/flutter%E6%92%AE%E5%BD%B1%E3%81%97%E3%81%9F%E5%86%99%E7%9C%9F%E3%82%84%E5%8B%95%E7%94%BB%E3%80%81%E3%82%AB%E3%83%A1%E3%83%A9%E3%83%AD%E3%83%BC%E3%83%AB%E3%81%8B%E3%82%89%E7%94%BB%E5%83%8F%E3%82%84/
TypeScript+Next.jsのフロントエンドをVSCodeでブレークポイントデバッグする設定

# プロジェクトのフォルダ構成例

```
myapp
├── .vscode
|   ├── launch.json
|   └── tasks.json
├── Dockerfile
├── README.md
├── next.config.js
├── node_modules
├── package-lock.json
├── package.json
├── public
├── src
|   └── ...
└── tsconfig.json
```

# 設定
## package.json
```json
{
  "name": "myapp",
  "version": "0.1.0",
...
...
...
  "scripts": {
    "dev": "npx next dev",  <-- devでnextを起動する設定が "なければ" 追加
...
...
...
```

## .vscode/tasks.json
```json
{
{
  "version": "2.0.0",
  "tasks": [
    {
...
...
...
    }, 
    ↓↓↓↓↓↓ これを追加 ↓↓↓↓↓↓
    {
      "type": "shell",
      "command": "npm run dev",
      "label": "next: dev",
      "detail": "react-scripts start",
      "group": {
        "kind": "test",
        "isDefault": true
      },
      "isBackground": true,
      "problemMatcher": {
        "owner": "custom",
        "pattern": {
          "regexp": "^$"
        },
        "background": {
          "activeOnStart": true,
          "beginsPattern": "Compiling...",
          "endsPattern": "Compiled .*"
        }
      }
    }
  ]
}
```

## .vscode/launch.json
```json
{
  "version": "0.2.0",
  "configurations": [
    {
...
...
...
    }, 
    ↓↓↓↓↓↓ これを追加 ↓↓↓↓↓↓
    {
      "type": "chrome",
      "request": "launch",
      "preLaunchTask": "next: dev",
      "name": "Launch Chrome for frontweb debug",
      "url": "http://localhost:3000",
      "webRoot": "${workspaceFolder}",
      "sourceMaps": true,
      "sourceMapPathOverrides": {
        "webpack://_N_E/./*": "${webRoot}/*"
      }
    }
  ]
}
```

#### 余談
```json
        "webpack://_N_E/./*": "${webRoot}/*"
```
を↓こうするとダメだった
VSCode側のソースコードとブレークポイントがリンクしない
```json
        "webpack://_N_E/./*": "${webRoot}/src/*"
```

## 実行
### 起動
`実行とデバッグ` で `Launch Chrome for frontweb debug` を実行する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/dcc163f2-c81b-eb00-ccef-441069f0e4c1.png)

```bash
> myapp@0.1.0 dev
> npx next dev

  ▲ Next.js 13.5.6
  - Local:        http://localhost:3000

 ✓ Ready in 1385ms
```
とVSCodeのターミナルに表示されたら `http://localhost:3000` をブラウザで開く（※そのブラウザウィンドウはもう使用しない）

そうするとこう続いた後、
```bash
 ○ Compiling / ...
 ✓ Compiled / in 9.1s (14126 modules)
```

VSCodeのフッターがオレンジ（実行中状態）になり、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/c3275772-8c50-3485-39f7-22caf7b2f996.png)

別個にまっさらのChromeウィンドウが別個に `http://localhost:3000` で起動する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/ac12e123-0f65-258e-ca15-b67513670276.png)

このChromeウィンドウを操作すれば、VSCode側のソースコードにブレークポイントを仕掛けたところで処理を止めたりできるようになる

### 終了
VSCodeのターミナル上で `Cmd + c` (`Ctrl + c`)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/553715/bb8a71b1-c786-f75d-7f2c-2f2f249bd50f.png)
WinActorの辞書と和解せよ

## 概要
WinActorにおける「辞書操作(情報取得)」ライブラリに納得いかない部分がある。
納得いかないのでちょっと確認する。

## 動機
WinActorの辞書について気になる部分があるが、Web上に十分な記述を発見できなかったため、忘備録を兼ねて記事にしておく。

## 辞書とは
プログラミングにおけるデータ構造のひとつ。
言語によっては、連想配列、Map、Hash、辞書型などと呼ばれる。

KeyとValueをセットで登録し、Keyを指定することでそのKeyに対応したValueを取り出すことができる、というもの。

## WinActorにおける辞書
ライブラリ ＞ 03_変数 ＞ 02_辞書と配列 フォルダに
+ 辞書操作(ダンプ)
+ 辞書操作(初期化)
+ 辞書操作(情報削除)
+ 辞書操作(情報取得)
+ 辞書操作(情報更新)

上記の5つのライブラリが存在する。
**辞書操作(初期化)** を経由したうえで、 **辞書操作(情報更新)** でキー(Key)と情報(Value)のセットを登録しておき、 **辞書操作(情報取得)** で指定したキー(Key)に紐づいた情報(Value)を取得する、というのが基本的な使用方法。

細かい使用方法は別の記事を参照してもらうとして、当記事では **辞書操作(情報取得)** ライブラリについて論じていく。

## 辞書操作(情報取得)の仕様について
WinActorの **辞書操作(情報取得)** ライブラリの注釈は以下の通り。

> 辞書から指定されたキーの情報を取得します。
>
> 「辞書名」  ：辞書名を指定する。
　　　　　  ：「値⇒」に辞書名を指定してください。
「キー」　  ：キーとなる文字列を指定する。
　　　　　  ：「値⇒」にキーを指定してください。
>「取得情報」：辞書から取得した値を格納する変数を指定する。

「キー」にキー(Key)を指定すると、対応した情報(Value)を「取得情報」で指定した変数に格納する、と説明されています。

なるほど。

**指定したキー(Key)が登録されていない場合はどうなるんだ？**

ExcelVBAにおいては、重複のチェックにディクショナリオブジェクト(VBAにおける辞書のこと)を用いることが珍しくない。
つまり、このKeyは登録済みなのか未登録なのかの判定に辞書を使用するのだ。ValueにはTrueでも入れておけばいい。どうせ使わないし。

WinActorでも辞書をこの使い方がしたいのだが、注釈には未登録のキー(Key)を指定したケースについて言及がない。困った。

困ったので、公式のドキュメント(WinActor_User_Library_Sample_Manual.pdf)を確認した。
Ver7.4に同梱されていたものなので、現時点では最新のドキュメントだろう。
P.27に辞書操作(情報取得)ライブラリについての書かれていた。
基本的には注釈と同じことが書いてあるが、1点だけ相違点がある。

> 指定されたキーが存在しない場合、「取得情報」の変数値は変化しません

**ええー？ ほんとにござるかぁ？**

そんなことある？
それじゃあ「取得情報」に指定した変数の値が、元から変化していないものなのか、キー(Key)に対応した情報(Value)なのか、分かりづらくない？
使い物にならないとは言わないが、結構嫌な仕様だ。というか、かなり重要なことなので、ドキュメントだけではなくライブラリの注釈にも書いておいて欲しい。ドキュメントにこの記述を発見した途端に仕事のモチベーションが大きく減退した。

俄かに信じ難い記述なので、念のためスクリプトも確認しておこう。
 **辞書操作(情報取得)** ライブラリをワークフローに設置して、スクリプトタブを確認する。
```VB:辞書操作(情報取得)ライブラリより一部抜粋
	If objDictionary.Exists(key) Then
		Call SetUMSVariable(value, objDictionary.Item(key))
	Else
		Call SetUMSVariable(value, "")
	End If
```
うん？

**君なんかドキュメントと違わない？**

指定したKeyが登録されていない場合はValueとして""を返す、と書かれている。
ドキュメントの記述は誤りであり、
指定されたキーが存在しない場合、「取得情報」に指定した変数には空白(0文字の文字列)が格納される。
というのが正しそうだ。

## 結論
#### 辞書操作(情報取得)ライブラリは、存在しないキー(Key)を指定した場合、「取得情報」に空白を返す。

よって、辞書をキー(Key)の重複チェックの用途に使用する場合、情報(Value)には空白以外(Trueとか)を指定しておき、辞書操作(情報取得)ライブラリ後に空白かどうかをチェックするのが良さそうだ。

## 補足①
公式の配布しているサンプルシナリオの中に「[辞書の作成と検索を行うサンプルシナリオ](https://winactor.biz/samplescenario/2020/05/27_2217.html "辞書の作成と検索を行うサンプルシナリオ")」が存在する。
こちらにも辞書操作(情報取得)ライブラリの動きについて直接の言及はないものの、シナリオの動きとして「辞書内に該当するキーがなければ、取得情報は空白」といった解説がある。

## 補足②
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/887728/dffe984b-5c44-ac7c-61c2-bbffe99d3b8c.png)
実際に試してみた。
変数値設定で「情報取得」で指定する変数に初期値を設定しておく。
辞書操作(情報更新)ライブラリを使用していないので、登録されているキー(Key)は存在しない。
辞書操作(情報取得)ライブラリでキー(Key)に適当な値を設定し、実行する。

結果、情報取得で指定した変数の初期値は上書きされ、""となっていた。
当たり前だが、スクリプト通りの結果だ。
expressで非同期なviewを扱う

小ネタです。

古典的なMVC2の実装をNode(Express)でするとなった時、Viewでモデルの振る舞いを呼び出すときにその処理が非同期だった場合詰みがちである。

故に振る舞いをもったモデルの代わりに解決済みのプレーンなデータをViewに渡す実装や設計が多くみられるのだが、そうなるとやはりプレゼンター的な層ができあがってしまいView内とそこで似たようなループ処理などが発生しがちである。

こういった問題を避けるためにそもそもMVC2じゃないアーキテクチャを使ったりいろんな解決方法があるわけだが、そもそもViewが非同期に対応してればいいんじゃないという漠然とした思いがあった。

ってわけで色々調べてるとどうやら古き懐かしの**ejs**には非同期を扱うためのオプションがあるらしい。

https://ejs.co/#docs

> async: When true, EJS will use an async function for rendering. (Depends on async/await support in the JS runtime.


ただexpress-generatorで単純にejsを指定するだけで使えるってわけではなかったのでExpressのソースを読みながらasyncオプションを設定できるようちょっと調整した

```javascript:app.js
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');
/* ここのところ追加 */
app.engine('ejs', async function (path, option, callback) {
  try {
    let html = await ejs.renderFile(path, option, {async: true});
    return callback(null, html);
  } catch (err) {
    return callback(err);
  }
});
```

で、試しにとテンプレ描画するところで非同期関数を渡してみる

1秒後にomataseって文字列返すように実装

```javascript: routes/index.js
router.get('/', function(req, res, next) {
  res.render('index', { asyncMethod: function () {
    return new Promise((resolve, reject) => {
      setTimeout(() => resolve('omatase!'), 1000);
    });
  }});
});
```

で、テンプレ内でawaitしてみる。

うーん、viewでawait見るとキモくて草

```javascript:views/index.js
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>テストアプリ</title>
    <link rel='stylesheet' href='/stylesheets/style.css'>
  </head>
  <body>
    <p><%= await asyncMethod(); %></p>
    <%- await include('./_partial', {asyncMethod}) %>
  </body>
</html>
```

パーシャルもpromise返すようになるのでawaitしてあげる

partialの中でも当然awaitつかえるんだよねって確認コード

```javascript:views/_partial.ejs
<p>hello</p>
<p><%= await asyncMethod() %></p>
```


試してみたらちゃんと動いてて草

![スクリーンショット 2024-04-03 15.52.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/12806/268514eb-6c5d-e4e8-69fe-f5a44faba6d2.png)

レスポンスもちょうどawait2回分で2秒になっていた。

ほーん、やるじゃん。　railsつかお。
vs2019を 単独インストールしても GenerateResource タスクが見つからない

リソース用プロジェクト message.resource.proj

```xml
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0" DefaultTarget="All">
  <PropertyGroup>
    <OutDir>message.resources</OutDir>
    <SourceDirectory>..\message</SourceDirectory>
  </PropertyGroup>

  <ItemGroup>
    <ResourceText Include="$(SourceDirectory)\common.txt" />
  </ItemGroup>

  <Target Name="Build">
    <MakeDir  Directories="$(OutDir)" />

	<GenerateResource
	    Sources="@(ResourceText)"
	    OutputResources="@(ResourceText->'$(OutDir)\%(FileName).resources')">
		<Output
	        TaskParameter="OutputResources"
	        ItemName="Resources"/>
	</GenerateResource>	    
	<AL
	    EmbedResources="@(Resources)"
	    OutputAssembly="$(OutDir)\Message.Resources.dll">

	    <Output TaskParameter="OutputAssembly"
	        ItemName="SatelliteAssemblies"/>
	</AL>
  </Target>
  <Target Name="Clean">
    <RemoveDir Directories="$(OutDir)" />
  </Target>
  
</Project>
```

実行

```sh
msbuild message.resource.proj
```

出力

```
"C:\usr\src\craft\craft\cfw-dotnet\src\build\message.resource.proj" (既定のターゲット) (1) ->
(Build ターゲット) ->
  C:\usr\src\craft\craft\cfw-dotnet\src\build\message.resource.proj(39,2): error MSB3091: "resgen.exe" が見つからなかっ たため、または正
しい Microsoft Windows SDK がインストールされていないため、タスクが失敗しました。タスクは、レジストリ キー HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft SDKs\
Windows\v8.0A\WinSDK-NetFx35Tools-x86 の値 InstallationFolder で指定された場所の下にある "bin" サブディレクトリで "resgen.exe" を探しています。次のいずれかを行
うことによって問題を解決できる可能性があります。1) Microsoft Windows SDK をインストールする。2) Visual Studio 2010 をイ ンストールする。3) 上記のレジストリ キーを正しい場所に手動で設定する
。4) 正しい場所をタスクの "ToolPath" パラメーターに渡す。
```

## 解決

PATH に以下を追加してみた

```
 C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\
```

ダメだった。

SdkToolsPath を直接指定してみた

```
	<GenerateResource
    SdkToolsPath="C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8.1 Tools\"
	    Sources="@(ResourceText)"
	    OutputResources="@(ResourceText->'$(OutDir)\%(FileName).resources')">
		<Output
	        TaskParameter="OutputResources"
	        ItemName="Resources"/>
	</GenerateResource>	    

```

うまくいった…かもしれない