OmniAICreator/Qiita-1.07M huggingface dataset:
Snowflake BUILD.localに参加してきました！

# はじめに

7月11日に渋谷ストリームホールで開催されたSnowflakeのリアルイベント [BUILD.local](https://www.snowflake.com/event/build-local-tokyo/) に参加してきました！

少し時間が経ってしまいましたが、イベントの感想を書いていきたいと思います！

# イベントの概要

- What's New in Snowflake for BUILDers
    - 2023年6月にラスベガスで開催されたSnowflake Summit 2023で発表されたアップデート内容をまとめて紹介
- ハンズオン or Snowflakeで実現可能な機能紹介
    - ハンズオンは「Snowpark＋StreamlitによるMLアプリ実装体験ハンズオン」
    - 機能紹介はアプリケーション開発とデータサイエンス関連の話題　（←私はこちらに参加）
- note株式会社様による事例紹介
- ネットワーキング

# 感想等々

新機能等として紹介されたものは魅力的なものばかりでした！
その中で、私が個人的に気になったものを上げていきたいと思います！
なお、各機能については [こちらのZenn記事](https://zenn.dev/yamnaku/articles/d92e447bb882a1)に非常に詳しくまとめていただいてありすごく参考になります🙇‍♂️

- ドキュメントAI
    - 非構造化データを構造化データに変換してくれる
    - SnowflakeのLLM、オープンソースのLLM、外部LLMと連携させたアプリ開発可能
    - SnowflakeのLLMはファインチューニング可能
        - → とりあえず、すごそう！

- Snowflakeで動作するネイティブアプリ開発環境＆展開・収益化
    - Snowflakeで動作するネイティブの開発環境が整ってきたとのこと
    - Snowflakeマーケットプレイスでiosアプリを販売するように展開・収益化可能
    - 他社のアプリを利用することももちろん可能
        - → ナイトレイとしては[mapbox](https://www.mapbox.jp/blog/snowflake-native-app), cartoなど位置情報解析関連ですぐに使えそうなものもありそうだと思いました！

- streamlitのチュートリアルが日本語化されている
    - [30 days of Streamlit](https://30days.streamlit.app/)が日本語されたとのこと
        - → 社内向けツールとしても利用していけるかもなと思ったりしました！

- snowpark for python
    - どんどん使えるライブラリ増えているとのこと
    - 追加して欲しいライブラリがあればコミュニティサイトからリクエストすればOK
        - → コミュニティサイトからリクエストするスタイルは確かに効率的だなと思いました。

<br>
ネットワーキングイベントは初参加でしたが、沢山の方とお話させていただき、データ活用の幅広さを知ることができました！
お話させていただいた皆様ありがとうございました🙇‍♂️
(リンクだけです)「Q学習」を調べたい。


# wikipedia

https://ja.wikipedia.org/wiki/Q%E5%AD%A6%E7%BF%92

https://ja.wikipedia.org/wiki/%E6%8A%80%E8%A1%93%E7%9A%84%E7%89%B9%E7%95%B0%E7%82%B9

# 参考

https://dse-souken.com/2021/05/18/ai-17/

＞この強化学習とは他の学習方法である教師あり学習、教師なし学習のようにデータを用意して機械学習するものではありません。
＞状態行動価値（Q値）
＞Q(st,at)はある時点tにおける状態s、行動aによるQ値です。
＞学習はニューラルネットワークを利用して進められました。

https://aizine.ai/q-learning0920/

https://note.com/pumonmon/n/n04f9139ad826
Oracle Cloud Infrastructure Architect Associate 勉強メモ（ファイルストレージ)

# ファイルストレージとは？
いわゆるOSなどで利用可能なファイルシステムをOCI上で構築。
複数のインスタンスからアクセス可能な汎用的なファイルシステム。
- **UNIX**と**Windows**に対応。
- 作成、削除、読み取り、書き込み、共有、ロック（一般的なファイルシステムと同等の機能）が利用可能。
- すべての主要なOSおよびハイパーバイザー（コンピューターを仮想化するための制御プログラム）に対応（？）（UNIXとWindowsに対応とあるが、UNIXとWindows以外でも使えるということ？）
- ソフトウェアの追加などは不要
- ネットワーク経由でアクセス可能
- 自動でスケールアウトする
- スナップショットによるファイル保護機能
- 暗号化によるセキュリティ（**TLSトンネル**によりマウントターゲットとインスタンス間で暗号化。oci-fss-utilsをクライアントがインストールする必要あり。）

### OCIファイルストレージサービスの概念
- ファイルシステム
ストレージそのもの
- エクスポート
NFSコントロールレイヤー
- マウントターゲット
エクスポートのエンドポイント

# ファイルストレージのセキュリティ
4つのセキュリティシステムで構成
- IAMサービス
ファイルシステムとマウントターゲットの作成、インスタンスとFSS（File System Service) VCN
- ネットワーク・セキュリティ
サブネットなどを構築した際にセキュリティリストなどを定める
- NFSエクスポート・オプション
ファイルシステム構築後、ファイルシステムのアクセスに関する制限
- UNIXセキュリティ
UNIXのセキュリティ。ファイルシステムに内蔵。

### マウント対象
- NFSエンドポイントである（NFSで繋がる終端？）
- ３つのプライベートIPアドレスとDNS名を持つ（マウントターゲットの作成に２つ、高可用性の担保のために１つ）
**CIDRを30で切ってしまうと足りなくなる**（30だと4つアドレスが降られるが、ネットワークアドレス、ブロードキャストアドレス、ゲートウェイのアドレスで**内部的に３つ使用する**ため１つしか空きがない）

# スナップショットとクローン
### スナップショット
ファイルストレージにはスナップショット機能がある。
.snapshotという隠れフォルダがルートフォルダは以下に作成。

### クローン（クローニング）
スナップショットから新しいファイルシステムを素早く作成可能。スナップショット以下に含まれるスナップショットごとクローンを作成する。
クローンの作成に使用されたスナップショットは**ソース**と呼ばれる。
ソースを含めたファイルシステムを**親のファイルシステム**と呼ぶ。
ソースからクローンを作成すると、作成時にクローンはすぐ利用できるようになり、裏で**ハイドレーション**（メタデータのコピー）が行われる。ハイドレーション中も通常の操作を行うことができる。
同じルートファイルシステムから派生したクローンのグループをクローン・ツリーという。
クローンからクローンを作成することも可能。

# コントロール・アクセス
- Oracle Cloud Infrastructure Identity and Access Management(IAM)
ポリシーにてOracle Cloud Infrastructure内でユーザーが実行できる操作(インスタンス、VCNとそのセキュリティ・ルール、マウント・ターゲット、ファイル・システムの作成など)を制御
- ネットワーク・セキュリティ
ファイル・システムへのアクセスを許可
- NFSエクスポート・オプション
ファイル・システムごとのアクセス制御エクスポート
- NFS v.3 UNIX securityとNFS v.3 Kerberos security
ファイルおよびディレクトリの読取りや書込みなど、ユーザーがインスタンスで実行できる操作を制御
Salesforce Toolまとめ

随時追記中
## ETL Tool
SOQLX
https://www.pocketsoap.com/osx/soqlx/
- SQL Workbenchのようなもの

## DevOps
データ転送ツール：SFDMU
https://qiita.com/takaHAL/items/0d6978269af28eaaf4bd
- Org間やProd ↔ Sandbox間でデータ転送したい
- 主従や依存関係を維持したまま転送したい（Account - Contact等)
- 転送設定ファイルを作って何度も移送したい

Sample export.json
~~~
{
  "objects": [
    {
      "query": "SELECT all, RecordTypeId FROM Account",
      "operation": "Upsert",
      "externalId": "Name"
    },
    {
      "query": "SELECT all, RecordTypeId FROM Contact",
      "operation": "Upsert",
      "externalId": "Name"
    }
  ]
}
~~~

## ログイン管理
Salesforce Logins by Synebo
https://www.synebo.io/products/salesforce_logins_extension_for_google_chrome
- 複数Orgにログインする場合のOrg管理に便利

## その他
Markdown記法
https://qiita.com/tbpgr/items/989c6badefff69377da7
- 書き方忘れすぎ…
ジョブにおけるDatabricks計算資源の活用

[Use Databricks compute with your jobs \| Databricks on AWS](https://docs.databricks.com/workflows/jobs/use-compute.html) [2023/5/31時点]の翻訳です。

:::note warn
本書は抄訳であり内容の正確性を保証するものではありません。正確な内容に関しては原文を参照ください。
:::

Databricksジョブを実行する際、タスクのタイプに応じて、クラスターやSQLウェアハウスのいずれかのDatabricks計算資源上でジョブの一部として設定されたタスクが実行されます。ジョブを運用に乗せる際に、コンピュートタイプや設定オプションを選択することが重要となります。本書では、ジョブを実行するためのDatabricks計算資源の活用ガイドを提供します。

# 共有ジョブクラスターの活用

複数のタスクをオーケストレートするジョブによるリソース使用量を最適化するには、共有ジョブクラスターを使用します。共有ジョブクラスターによって、同じジョブにおける複数のタスクでクラスターを再利用できるようになります。ジョブに含まれる全てのタスクを実行するために単一のジョブクラスターを使うこともできますし、固有のワークロードに最適化された複数のジョブクラスターを使うこともできます。共有ジョブクラスターを使うには:

1. タスクを作成する際に**New Job Clusters**を選択し、[クラスター設定](https://docs.databricks.com/clusters/configure.html)を完了します。
1. 新規ジョブクラスターを作成する際、ジョブにタスクを追加する際に新規クラスターを選択します。**New Job Clusters**を選択して設定した全てのクラスターは当該ジョブのすべてのタスクから利用できます。

共有ジョブクラスターのスコープは単一のジョブ実行であり、他のジョブや同じジョブの別の実行で使用することはできません。

共有ジョブクラスターの設定ではライブラリを宣言することはできません。タスクの設定で依存するライブラリを追加しなくてはなりません。

# ジョブに適したクラスタータイプの選択

- **New Job Clusters**は、ジョブやタスク実行専用のクラスターです。当該クラスターを使用する最初のタスクが起動すると、共有ジョブクラスターが作成・起動され、当該クラスターを使用する最後のタスクが完了した後にジョブクラスターが停止されます。アイドル状態ではこのクラスターは停止されませんが、クラスターを使用するすべてのタスクが完了した後にのみ停止されます。すべてのタスクが完了する前に、共有ジョブクラスターが停止した場合、新たなクラスターが作成されます。タスクが起動した際に単一のタスクスコープのクラスターが作成され、タスクが完了すると停止されます。プロダクション環境では、それぞれのジョブやタスク実行が完全に分離された環境で実行されるように、新規共有クラスターかタスクスコープのクラスターを使用することをお勧めします。
- 新規クラスターでタスクを実行する際、タスクはデータエンジニアリング(タスク)ワークロードとして取り扱われ、タスクワークロードの価格が適用されます。既存のall-purposeクラスターでタスクを実行する際、タスクはデータ分析(all-porpose)ワークロードとして取り扱われ、all-purposeワークロードの価格が適用されます。
- 停止している既存クラスターを選択し、ジョブの所有者に**Can Restart**[権限](https://docs.databricks.com/security/auth-authz/access-control/cluster-acl.html)がある場合、ジョブが実行されるようにスケジュールされた際にはDatabricksがクラスターを起動します。
- 定期的な周期で[ダッシュボード](https://docs.databricks.com/notebooks/dashboards.html)をアップデートするようなタスクでは、既存のall-purposeクラスターがベストな選択肢となります。

# クラスター起動時間を削減するためにプールを活用

新規ジョブクラスターの起動時間を削減するには、[プール](https://docs.databricks.com/clusters/pools.html)を作成し、ジョブクラスターでプールを使うように設定します。

## 自動アベイラビリティゾーン

自動アベイラビリティゾーン(Auto-AZ)を活用するには、[Clusters API](https://docs.databricks.com/api/workspace/clusters)を用いて、`aws_attributes.zone_id = "auto"`を設定する必要があります。[Availability zones](https://docs.databricks.com/clusters/configure.html#availability-zones)をご覧ください。


### Databricksクイックスタートガイド

[Databricksクイックスタートガイド](https://www.amazon.co.jp/dp/B09V1YXFVQ/)


### Databricks無料トライアル

[Databricks無料トライアル](https://databricks.com/jp/try-databricks)
他院から紹介状をテキスト化し初診カルテ記載を楽にする方法（iphone+ChatGPT）

こんにちは、皆さん。東京でクリニックを開業している医師の森　維久郎です。

今日は他の病院から自分のクリニックに届いた紹介状の内容を一瞬でテキスト化し、カルテに貼り付ける方法について共有したいと思います。

これで初診の際の文字起こしが無くなります。

ただし、ご注意いただきたいのは、これがあくまで「技術的に可能」な方法であるということです。

個人情報保護の観点から、実際に運用する際は必要な注意が必要です。

＊この記事もchatGPTを使って書いているのである程度不自然な日本語があるかもしれません。

## 紹介状のテキスト化方法　～使用するのはあの〇〇～
最初に行うべきことは、紙を紹介状を画像にすることです。

一般的な手順は、スキャンをしてPDF化することだと思います。

しかし、今回はこの方法を使用せずにiPhoneを使用する方法を提案します。

理由は3つあります。

1:クリニックで運用する上で、患者さんから紙で受け取った紹介状をPDF化し、PC上で見るという業務フローだと工数がかかり外来は回りづらいから。

2:日本語のOCRにおいてiphoneのカメラが圧倒的に精度が高いから。

*日本語のOCRライブラリTesseract、PaddleOCR、EasyOCRなどを試しました。

3:インターネットに繋がっていないデバイスで使用したいから。



## 手順

### 手順1: iPhoneで紹介状を撮影
まず、iPhone(インターネット未接続)のカメラを起動して紹介状を撮影します。この際、個人情報が含まれる部分はペンで塗りつぶしたり、折って見えないようにするなど、運用上の配慮が必要です。

### 手順2: テキストの読み取り
iPhoneで写真を撮ると、写真に含まれたテキストデータを読み取ってくれる機能が昨年から追加されました。この機能を使ってテキストを全て選択し、コピーします。驚くことに、テキストの95%以上が正確に読み取ることが可能でした。

### 手順3: テキストの保存と共有
iphoneのQRコード作成ツールを使ってテキストの内容をQRコード化します。

そして、カルテが入っているPC端末でQRコードを読み取り、そこに保存されたテキストデータを読み込みます。これでテキスト化した紹介状をカルテに貼り付けることができます。

### 手順4：不自然な日本語や改行をChatGTPで整形する
もしクラウドカルテを使用している場合は不自然な日本語や改行をChatGPTで整形してもらうと良いです。
```
～おすすめプロンプト～
私は医師で、あなたはカルテ記載をする優秀なアシスタントです。

当院宛ての紹介状を初診カルテに書き写す作業を行っています。

PDFをOCRしたのですが、読み取りは不完全で不自然な日本語や改行があります。

テキストを渡すので不自然な日本語や改行を修正してください。

尚、内容はなるべくそのままにして要約したりしないでください。
```


この方法を使用すれば、紹介状の内容を素早くテキスト化し、カルテに反映することが可能です。

## 注意点
個人情報保護の観点から、運用上の注意が必要です。

私は個人情報の取り扱いの専門家ではないので実際運用をされる場合は専門家と必ず相談をしてください。

## 意見募集

以下について詳しい方是非意見ください！

・個人情報保護の取り扱い
・日本語OCRをオンプレミス環境でやるスマートな方法
・iphoneからPCへのテキストのコピーペーストの方法
仕様書の項目まとめてみた

# 仕様書の項目まとめてみた
初めてのアプリ開発、何を決めておけばこの後作りやすいんだっけ？
色んなひとが色んなことを言っている。
わたしなりにまとめてみた。
ここまで決めておけばやりやすい気がする。


#### 開発コード名 【codename】 コードネーム：
ファイル名につけるため、正式名称が決まるまでの呼称、てきとー、Pancakeとかでも。

### 【企画フェーズ】
##### 1.ペルソナ設定
##### 2.悩みや課題
##### 3.課題解決された状態
##### 4.課題解決に必要な機能
##### 5.ユーザーストーリーまとめ（≒要件定義書）：
{whoペルソナ}が、{why課題}を解決するために{how機能}を使って{what課題解決}できるようになる
##### 6.ブランディング：
ブランド（アプリ）名、カラー、アイコン、キャチコピーなど：仮置きでOK、作りながら固めていく

### 【要件定義フェーズ】
##### 7.欲しい機能：
～ができるようになる（ユーザー目線）
##### 8.作らない機能：
あえて最初は作らずに、今後のアップデートにとっておく機能など
ペルソナやアプリの目的からずれる機能は作らないなど
##### 9.画面と画面遷移の流れ：
ワイヤーフレーム（ラフ）

### 【基本設計フェーズ】
##### 10.画面遷移図(文字)：
##### 11.ワイヤーフレーム(絵)：(1)-(5)を画面ごと
(1)画面名：その画面は何のためか（例：トップページ）
(2)機能一覧：その画面でどのような処理が存在するのか
(3)ｲﾝﾌﾟｯﾄ機能要件：　,操作方法（入力/ボタンなど）：　,入力データ（数値/文字列など）： ,出力結果/遷移先：
(4)ﾘﾝｸ機能要件：　,操作方法（入力/ボタンなど）： ,出力結果/遷移先：
(5)表示要件：～を見ることができる
(6)配置図
##### 12.画面設計：(1)-(5)を画面ごと
(1)画面名：その画面は何のためか（例：トップページ）
(2)機能一覧：その画面でどのような処理が存在するのか
(3)ｲﾝﾌﾟｯﾄ機能要件：　,操作方法（入力/ボタンなど）：　,入力データ（数値/文字列など）： ,出力結果/遷移先：
(4)ﾘﾝｸ機能要件：　,操作方法（入力/ボタンなど）： ,出力結果/遷移先：
(5)表示要件：～を見ることができる
(6)配置図

##### 13.データベースの設計：
テーブルにデータが登録されるときに着目

(1)エンティティの種類：扱うデータ：洗い出し⇒カラム名として整理⇒グルーピング（データベースの正規化）
(2)テーブルの種類：何のデータを管理するテーブルが必要か。必要なテーブルを書き出す
(3)テーブルの中身：エンティティのアソシエーションの洗い出しをカラムとアソシエーションをそれぞれのテーブル内で整理
(4)ER図作成：エンティティ同士の関係性（アソシエーション）を図に示す
(5)データの制約、バリデーションの設定、必須かどうか

### 【詳細設計フェーズ】
##### 14.基本設計書の作成：
画面遷移図/画面設計のわかりやすいまとめ
##### 15.クラス図の作成：
11．で書き出した処理をクラスという役割のまとまりにグルーピング
##### 16.シーケンス図の作成：
##### 17.画面(機能?)ごとに設定すべきこと：
ルーティング：　,コントローラ: ,アクション： ,モデル：
##### 18.コードを当てはめる
##### 19.テーブル定義書の作成：
まとめ
dig, host

```
[linuxstudy@localhost ~]$ dig -t a www.qiita.com 

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el7_9.13 <<>> -t a www.qiita.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 48404
;; flags: qr rd ra; QUERY: 1, ANSWER: 3, AUTHORITY: 0, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; MBZ: 0x0005, udp: 512
;; QUESTION SECTION:
;www.qiita.com.			IN	A

;; ANSWER SECTION:
www.qiita.com.		5	IN	A	35.73.125.135
www.qiita.com.		5	IN	A	35.72.104.39
www.qiita.com.		5	IN	A	54.238.73.0

;; Query time: 130 msec
;; SERVER: 192.168.253.2#53(192.168.253.2)
;; WHEN: 火  7月 25 13:06:41 JST 2023
;; MSG SIZE  rcvd: 90

```
```
[linuxstudy@localhost ~]$ host www.qiita.com
www.qiita.com has address 35.73.125.135
www.qiita.com has address 54.238.73.0
www.qiita.com has address 35.72.104.39
```
```
[linuxstudy@localhost ~]$ dig -t ns www.qiita.com 

; <<>> DiG 9.11.4-P2-RedHat-9.11.4-26.P2.el7_9.13 <<>> -t ns www.qiita.com
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 20562
;; flags: qr rd ra; QUERY: 1, ANSWER: 0, AUTHORITY: 1, ADDITIONAL: 1

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; MBZ: 0x0005, udp: 512
;; QUESTION SECTION:
;www.qiita.com.			IN	NS

;; AUTHORITY SECTION:
qiita.com.		5	IN	SOA	ns-1956.awsdns-52.co.uk. awsdns-hostmaster.amazon.com. 1 7200 900 1209600 86400

;; Query time: 138 msec
;; SERVER: 192.168.253.2#53(192.168.253.2)
;; WHEN: 火  7月 25 13:08:07 JST 2023
;; MSG SIZE  rcvd: 126
```
Windows環境のNeovimでrust-analyzerが動いていなかった

Windows環境のNeovim（NvChad）でRustを書こうとしたらLSPがうまく動いていなかったのですが、解決したのでそのメモです。

```
rustup component add rust-analyzer
```

したら動きました。

Rustをインストールするときに最初`x86_64-pc-windows-gnu`を指定し忘れて、アンインストールせずにRustをインストールし直したのが原因かもしれないですが詳しいことは分からないです(´・ω・`)
Surface bookのタッチを無効にする

![スクリーンショット 2023-07-25 125219.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/85010/070e9ce8-8333-72ff-959d-1303100efb14.png)

デバイスマネージャの「Intel(R) Precise Touch Device」を無効にすることで
画面の物理タッチを無効にすることができる。
/etc/resolv.conf

DNSサーバのΙPアドレスをnameserverに記載している。
特定のドメインも記載している。
localdomainのipアドレスは/etc/hostsから読み取る
外部ドメインは、root-servers.net

```
[linuxstudy@localhost ~]$ cat /etc/resolv.conf 
# Generated by NetworkManager
search localdomain
nameserver 192.168.253.2
```
参考資料 https://linuc.org/study/knowledge/507/

```
[linuxstudy@localhost ~]$ grep host /etc/nsswitch.conf 
#hosts:     db files nisplus nis dns
hosts:      files dns myhostname
```
完全自分用TSUBAMEのCUDAとCuDNNの対応表

# 自分でいい組み合わせを見つけ次第ここにメモっていく
本当にバージョン合わせが嫌．
- PythonはConda環境で入れることを想定，CUDAとcuDNNはTSUBAMEのModuleで入れることを想定

|Python |CUDA |cuDNN |備考　|
|---|---|---|---|
|3.8? |10.2.89 |8.1 |`pytorch==1.8.1 torchvision==0.9.1 torchaudio==0.8.1 -c pytorch`で対応するtorchを入れられる<br> rdkitがpython3.8に対応してないかも
|3.7 | 11.6.2 |8.3 | `pytorch==1.13.1 torchvision==0.14.1 torchaudio==0.13.1 pytorch-cuda=11.6 -c pytorch -c nvidia`で対応するtorchを入れられる<br>`conda install -c rdkit rdkit`でrdkitもいける
nmcli

```
[linuxstudy@localhost ~]$ nmcli
ens33: 接続済み to ens33
        "Intel 82545EM"
        ethernet (e1000), 00:0C:29:36:8C:6F, hw, mtu 1500
        ip4 デフォルト
        inet4 192.168.253.139/24
        route4 0.0.0.0/0
        route4 192.168.253.0/24
ens33: 接続済み to ens33
        "Intel 82545EM"
        ethernet (e1000), 00:0C:29:36:8C:6F, hw, mtu 1500
        ip4 デフォルト
        inet4 192.168.253.139/24
        route4 0.0.0.0/0
        route4 192.168.253.0/24
        inet6 fe80::7b18:ba6f:58cb:d149/64
        route6 fe80::/64
        route6 ff00::/8

virbr0: 切断済み
        "virbr0"
        bridge, 52:54:00:DC:97:4D, sw, mtu 1500

lo: 管理無し
        "lo"
        loopback (unknown), 00:00:00:00:00:00, sw, mtu 65536

virbr0-nic: 管理無し
        "virbr0-nic"
        tun, 52:54:00:DC:97:4D, sw, mtu 1500

DNS configuration:
        servers: 192.168.253.2
```
```
[linuxstudy@localhost ~]$ nmcli general
STATE     CONNECTIVITY  WIFI-HW  WIFI  WWAN-HW  WWAN 
接続済み  完全          有効     有効  有効     有効 
[[linuxstudy@localhost ~]$ nmcli device 
DEVICE      TYPE      STATE     CONNECTION 
ens33       ethernet  接続済み  ens33      
virbr0      bridge    切断済み  --         
lo          loopback  管理無し  --         
virbr0-nic  tun       管理無し  --         
[linuxstudy@localhost ~]$ nmcli connection
NAME   UUID                                  TYPE      DEVICE 
ens33  ae703e2b-089a-4424-b099-c003e03946a4  ethernet  ens33  
[linuxstudy@localhost ~]$ nmcli networking
enabled
[linuxstudy@localhost ~]$ nmcli radio
WIFI-HW  WIFI  WWAN-HW  WWAN 
有効     有効  有効     有効 
```
```
[linuxstudy@localhost ~]$ nmcli general hostname xxxx
[linuxstudy@localhost ~]$ nmcli general hostname 
xxxx
[linuxstudy@localhost ~]$ cat /etc/hostname
xxxx
[linuxstudy@localhost ~]$ ping xxxx
PING xxxx (192.168.253.139) 56(84) bytes of data.
64 bytes from xxxx (192.168.253.139): icmp_seq=1 ttl=64 time=0.603 ms
64 bytes from xxxx (192.168.253.139): icmp_seq=2 ttl=64 time=0.174 ms
```
Hello, World!

まず最初に基本である``Hello, World!``をコンソールに出力するプログラムを作成し、ZIOについて大まかに理解します。

# セットアップ
※ビルドツールは``sbt``を使います。
まず設定ファイル``build.sbt``に以下の設定を記述し、ZIOのライブラリを読み込むようにします。

```scala
    libraryDependencies ++= Seq (
      "dev.zio" %% "zio" % "2.0.13"
    )
```

また、IntelliJ IDEAで普段Scala開発をされている方は以下のプラグインをインストールされると良いかもしれません。

https://plugins.jetbrains.com/plugin/13820-zio-for-intellij

# プログラム本体

```scala
object MainApp extends ZIOAppDefault {
  def run: ZIO[Any, Throwable, Unit] = Console.printLine("Hello, World!")
}
```
# 実行
[セットアップ](#セットアップ)と[プログラム本体](#プログラム本体)の記述が終わったら、``sbt run``で実行してください。
以下の様にコンソールに出力されたら成功です。
```shell
Hello, World!
```

# プログラム本体の解説
## ``Console.printLine("Hello, World!")``について
``Console.printLine``の定義を確認すると``IO[IOException, Unit]``を返す関数であることがわかります。
``IO[+E, +A]``は``ZIO[Any, E, A] ``の型エイリアスであるため、``IO[IOException, Unit]``は``ZIO[Any, IOException, Unit] ``を表していることがわかります。

## ZIO型について
上記のZIO型の意味としては以下の様になっています。
```scala
type ZIO[R, E, A] = R => Either[E, A]
```
今回の場合は以下のようになります。
```scala
type ZIO[Any, IOException, Unit] = Any => Either[IOException, Unit]
```
後の章でも説明しますが、これは``Any``型に依存し(Any型に依存するということは何にも依存していないことを意味しています)、``IOException``型で失敗する可能性や処理が成功した場合、``Unit``型を返す可能性があることを意味しています。

## ZIO型を返す関数の実行について
ZIO型自体は処理の宣言の様なものであり、それ自体を直接呼び出したとしても実際の処理は行われません。
今回の場合、``Console.printLine("Hello, World!")``を以下のような形で呼び出したとしてもコンソールに``Hello, World!``は出力されません。
```scala
object HelloWorld {

  def main(args: Array[String]): Unit = {
    Console.printLine("Hello, World!")
  }

}
```
ZIO型を返す関数の処理自体を実行させるためには必ず今回の[プログラム本体](#プログラム本体)のように``ZIOAppDefault``の``run``関数にZIO型を渡す必要があります。

# 終わりに
以上がZIOの``Hello, World!``プログラムとその解説でした。
次章では今回のZIO型の場合、``Any``型で表されるZIOのDI(Dependency Injection)について詳しく見ていきます。

前章:[ZIOについて](https://qiita.com/wc_sangen/private/f428656234ce946448c3)
次章:[ZIOのDIについて](https://qiita.com/wc_sangen/private/38672e5fe7d651ff1533)

# 演習
1. 今回の``Hello, World!``プログラムを実際に動かしてください。
    - 解答例は以下になります。
    https://github.com/hatuda/zio-practice/tree/CHAPTER1
Windows 10上に Apache Sparkをインストール

色々機能が使えなかったので備忘録


# 前提
|プロダクト|バージョン|URL|図|
|--:|--:|--:|--:|
|Windows|10 Pro|-|-|
|Java|18.0.1|-|-|
|Apache Spark|3.4.1|https://spark.apache.org/downloads.html|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569644/30eff5fb-859f-66d8-2fe3-c26fc5377cd8.png)|
|Hadoop|3.3.0|https://hadoop.apache.org/releases.html|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569644/30eff5fb-859f-66d8-2fe3-c26fc5377cd8.png)![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569644/153ac387-d50e-c72d-782f-4db29be93d2c.png)|
|winutils|-|https://github.com/ruslanmv/How-to-install-Hadoop-on-Windows/tree/master/winutils/hadoop-3.3.0-YARN-8246/bin||


# Apache Spark のインストール

spark-3.4.1-bin-hadoop3.tgz　をダウンロードしてきて任意のフォルダで解凍
解凍先を SPARK_HOME として環境変数に追加


# Hadoop のインストール
hadoop-3.3.0.tar.gz をダウンロードしてきて任意のフォルダで解凍
7-zip　だとエラーが出ていたっぽい（気のせいかも）のでコマンドで解凍
```
tar -xvzf hadoop-3.3.0.tar.gz -C 解凍先フォルダ
```
解凍先を HADOOP_HOME として環境変数に追加


# Java のインストール
省略
JAVA_HOME を環境変数に追加しないと動かない機能もあるようだ
例）spark_df=pyspark.sql.dataframe.DataFrame とした場合、spark_df.write　


# winutils のインストール
gitからチェックアウト
```
git clone https://github.com/ruslanmv/How-to-install-Hadoop-on-Windows.git
```

How-to-install-Hadoop-on-Windows\winutils\bin
を　HADOOP_HOMEにコピー（binフォルダを上書き）


# Pathを通す
pathに
SPARK_HOME\bin
HADOOP_HOME\bin
を追加（Javaは通っている前提）


# 確認
## Apache Spark
```
spark-submit --version  
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569644/9b387fb1-387c-2f80-5126-c3e4b5cc3d8e.png)


## Hadoop
```
hadoop version  
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/569644/9d440b78-00ac-d0b4-680d-a92ab7165029.png)


# 動かないときの対策
## pysparkでshow() 実行 に SocketTimeoutException

エラー内容
```
Py4JJavaError: An error occurred while calling o44.showString.
: org.apache.spark.SparkException: Job aborted due to stage failure: Task 0 in stage 0.0 failed 1 times, most recent failure: Lost task 0.0 in stage 0.0 (TID 0) (2112N-14046.jp.misumi.pri executor driver): org.apache.spark.SparkException: Python worker failed to connect back.
<-- 中略 -->
Caused by: java.net.SocketTimeoutException: Accept timed out
```

対策：下記を追加
```
import findspark 

findspark.init()
findspark.find()
```

## to_pandas() 実行時に UnknownTimeZoneError

エラー内容
```
UnknownTimeZoneError Traceback (most recent call last) Input In [43], in <cell line: 1>() ----> 1 df_pandas = spark_df.toPandas()
```

対策：sparkセッション作成時にtimezoneを指定
```
spark = SparkSession.builder.appName("アプリ名").config("spark.sql.session.timeZone", "Asia/Tokyo").getOrCreate()
```


# 参考
https://ruslanmv.com/blog/How-to-install-Hadoop-on-Windows
jetpack composeで遷移先から遷移元にデータを返す

# はじめに
今回は`JetpackCompose`を使って`setFragmentResult`のように遷移先から遷移元にデータを返す方法を紹介していきます
### 本文
やること自体は至極単純です。
まず、遷移先から遷移元に戻る時に下記のようにします
```kotlin
navController.previousBackStackEntry?.run {
    savedStateHandle["result"] = "hoge"
}
```
次に遷移元で結果を受け取るように処理を書きます
```kotlin
val result: String? = navController
    .currentBackStackEntry?.savedStateHandle?.get("result")
```
こうすることで簡単に値を戻すことができます。
ここで注意が必要なのがデータが保持され続けることと何度も`recompose`されてしまう可能性があるということです。
そのため、下記のようにすることで回避することができます。
```kotlin
navController.currentBackStackEntry?.run {
    if (savedStateHandle.contains("result")) {
        val result: String? = savedStateHandle["result"]
        viewModel.someAction(result)
        savedStateHandle.remove<String>("result")
    }
}

LaunchedEffect(Unit) {
    navController.currentBackStackEntry?.run {
        if (savedStateHandle.contains("result")) {
            
        }
    }
}
```
### 最後に
今回はJetpackComposeでのsetFragmentResultのような書き方を紹介しました
どなたかのお役に立てれば幸いです
Markdown表示のコードスニペットのシンタックスハイライトをNuxt.jsアプリに実装してみる

[Markdown表示に対応した前回の記事](https://suzuki-navi.hatenablog.com/entry/2023/07/15/103346)の続きで、今回はコード表示の部分をシンタックスハイライトに対応させます。

パッケージをインストールします。

```plain
$ npm install markdown-it-highlightjs
```

`package.json` の依存性の記述には以下が追加されました。

```plain
    "markdown-it-highlightjs": "^4.0.1"
```

`nuxt.config.js` に以下のような記述を追加します。`css`の中身を追加し、`markdownit` の `use` にも追加します。

```nuxt.config.js
  css: [
    // ref https://highlightjs.org/static/demo/
    'highlight.js/styles/dark.css',
  ],

  markdownit: {
    // ref https://github.com/markdown-it/markdown-it
    linkify: true,
    breaks: true,
    use: [
      'markdown-it-highlightjs',
    ],
  },
```

CSSの配色は選ぶことができ、以下のページから選択し、`nuxt.config.js`に記載するCSSのファイル名を変えればよいようです。

https://highlightjs.org/static/demo/

これだけでシンタックスハイライトに対応できました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490207/752cec12-5e25-b1a9-e898-c4e7e0ecc33a.png)
※サンプルのスクショにある表はChatGPTに県庁所在地を聞いたものですが、埼玉県の県庁所在地は「さいたま」です。

# 追記

次の記事

https://qiita.com/suzuki-navi/items/61b9eda362f28a94c62d
セルの値をセルの名前にしてくれるVBA

# 仕様
選択状態のセルを対象とし、セルの値を当該セルのブックレベルの名前とする。
セルに値が入っていなければスキップ。
セルに入っている値がすでにセルの名前として使用されている場合もスキップ。
設定が完了したセルは値を削除。

セル上でExcel数式を使ってバリエーションを作ったり、オートフィルで連続データを作ったりしたセルに対して使うと少しだけ幸せになれる。

※使い捨て用に書いたのでbookの固定とか細かい処理はしていない
```vb:Module1
Sub setName()
Dim rng As Range
Set rng = Selection
Debug.Print rng.Count
Dim r As Range
For Each r In rng
    If r.Value <> "" Then
        If Not isExistingName(r.Value) Then
            r.name = r.Value
            r.Value = ""
        End If
    End If
Next
End Sub

Function isExistingName(name As String) As Boolean
On Error GoTo Err
Range(name).Select
isExistingName = True
Exit Function
Err:
isExistingName = False
End Function

```


大量にセルの名前を付けなければならないが名前ボックスはチマチマしていてセル上で指定したくなったので作った。
Windowsのコマンドプロンプト（bat）でローカルユーザー削除

# 概要

Administrator権限を持ってしまっている初期アカウントを消したい時など。

# やり方

**管理者権限が必要。**

```bat:
rem 『unko』という名前のユーザーを削除
net user unko /delete
```

# やり方（削除と同時に結果の確認）

```bat:
rem 『unko』という名前のユーザーを削除
net user unko /delete

rem ユーザーアカウントの画面開く（削除できているかの確認用）
control userpasswords
```

```control userpasswords``` で下記画面が開く。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/78ba191b-0a4d-edba-1772-dcd75a6c1f68.png)

そして、『ユーザーアカウントの管理』をクリックで下記画面を開ける。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/706116/73263e02-baf0-71ca-8470-32afe414e4bc.png)

# 蛇足

ドメイン参加後ならActiveDirectoryで何とかしたい！
バッチとかコマンド発行するのも面倒くさい！
グループポリシーとかで何とかしたい！
けど、やり方まだ調べ切れてない…。

あと、『ユーザーアカウントの管理』をクリックした先をコマンドで開きたかったけど、やり方見つからなかった。

# 参考サイトさん

https://atmarkit.itmedia.co.jp/ait/articles/0609/02/news014.html



# バージョン

Windows 10 Pro 22H2 19045.3208
Gauge Pythonでgauge run specsをするとTypeError: Descriptors cannot not be created directlyエラーが出る

# はじめに

初めてGauge Pythonを実行する機会があり、起動がうまくいかなかったのでまとめます

# 問題

PythonでGaugeを実行したところ以下のエラーが出ました

```
$ gauge run specs
```

```
Traceback (most recent call last):
File "/home/watanabejin/.gauge/plugins/python/0.3.17/start.py", line 11, in <module>
from getgauge import handlers, logger, processor
File "/home/watanabejin/workspace/test/.venv/lib/python3.10/site-packages/getgauge/handlers.py", line 4, in <module>
from getgauge import logger, processor
File "/home/watanabejin/workspace/test/.venv/lib/python3.10/site-packages/getgauge/processor.py", line 7, in <module>
from getgauge.executor import (create_execution_status_response,
File "/home/watanabejin/workspace/test/.venv/lib/python3.10/site-packages/getgauge/executor.py", line 7, in <module>
from getgauge.messages.messages_pb2 import ExecutionStatusResponse, Message
File "/home/watanabejin/workspace/test/.venv/lib/python3.10/site-packages/getgauge/messages/messages_pb2.py", line 6, in <module>
import getgauge.messages.spec_pb2 as spec__pb2
File "/home/watanabejin/workspace/test/.venv/lib/python3.10/site-packages/getgauge/messages/spec_pb2.py", line 34, in <module>
_descriptor.EnumValueDescriptor(
File "/home/watanabejin/workspace/test/.venv/lib/python3.10/site-packages/google/protobuf/descriptor.py", line 796, in __new__
_message.Message._CheckCalledFromGeneratedFile()
TypeError: Descriptors cannot not be created directly.
If this call came from a _pb2.py file, your generated code is out of date and must be regenerated with protoc >= 3.19.0.
If you cannot immediately regenerate your protos, some other possible workarounds are:
1. Downgrade the protobuf package to 3.20.x or lower.
2. Set PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python (but this will use pure-Python parsing and will be much slower).
More information: https://developers.google.com/protocol-buffers/docs/news/2022-05-06#python-updates
Error occurred while waiting for runner process to finish.
Error : exit status 1
Error ----------------------------------

[Gauge]
Failed to start gauge API: Error occurred while waiting for runner process to finish.
Error : exit status 1

Get Support ----------------------------
        Docs:          https://docs.gauge.org
        Bugs:          https://github.com/getgauge/gauge/issues
        Chat:          https://github.com/getgauge/gauge/discussions

Your Environment Information -----------
        linux, 1.4.3, f98dd40
        html-report (4.2.0), java (0.9.1), play-cdc (0.1.4), python (0.3.17), screenshot (0.1.0)
```

実行した環境の`gauge python`は`0.3.17`です

# 解決方法

以下を実行したところうまく動くようになりました

```
$ pip install --upgrade protobuf
$ export PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION=python
```

google.protobuf.descriptor.EnumValueDescriptorの直接作成が試みられており、これは新しいprotobufのバージョンでは許可されていないことが原因みたいです

今回は`venv`上でいんすとーるしました

# おわりに

個人的にはGauge Javaよりもつまづくことなく実行できたのでよかったです
Golang 空インターフェイスと型アサーション

# 空のインターフェイス
go言語は全ての型と互換性のある空インターフェイス型を宣言できる。

```golang
var kara interface{}

kara = 100
fmt.Println(kara)
fmt.println(&kara)

kara = "Empty"
fmt.Println(kara)

kara = [4]string{"K", "A", "R" "A"}
fmt.Println(kara)
```

##### 実行結果

```
100
0xc000080ec0
Empty
[K A R A]
```

## 型アサーション
どんな型でも受け取れる空インターフェイスですが、逆に引数で受け渡された値は元の型の情報が欠落しています。
そのためGo言語では型アサーションを提供しており、型アサーションによりその受け取った型が何であるかを以下のように動的にチェックすることができます。


##### 構文
```
t, ok := i.(T)

```
i が T(型名) を保持していれば、 t は基になる値になり、 ok は真(true)になります。

そうでなければok は偽(false)になります。



##### 例


```golang
func assertion(obj interface{}) {
	v1, ok := obj.(int)
	if ok {
		fmt.Println("int", v1)
	} 

	v2, ok := obj.(string)
	if ok {
		fmt.Println("string", v2)
	}
}

func main() {

	assertion(100)
	assertion("ASSERTION")
}
```

##### 実行結果

```
int 100
string ASSERTION
```
Vue3 + vuetifyでトグルっぽいボタン

トグルボタンだと横並びで縦並びができない。
できそうで、できない。きー！くやしー！。
今から別UIに変更するか。嫌だなぁ。
仕方がないので、トグルボタンを自前で実装。

https://play.vuetifyjs.com/#eNqlVU1z2jAQ/SsancgE20mTSx2TSTs99NBTr5iDsNdEjSx7JOE2w/i/d/UBCEOmyZQDWKv3drXvyctyR7/0fTpsgea00JXivSEazLYngsnNoqRGl/SxlITwtu+UITsFzfwnNCNpVNeSkiK3pA8WUXVSW7aAyuQIKbRRXG4eyYIgaVbSkl5FwPXWmE5+Y4b94NrkXw/L5QoZSwvc2S/7EWwNIi+p59yWdL7fGZjYAu7EMVYZPkDeMKEhBMfw+0bGTxcyxrGPZ7y7kDGOfTzj/YWMcezNjKtI9Erw6uU7k7UAhSrPfCJv1FU+dLwmC2d3OIRnyVxu27VjnLqWCpAb8+wKENJ0aibAEH6E3zzwQj7w6+urSW/TOxD5f1ZlyVd7Gm/8kckiBqUuFkpERUiM8fpgcqO2EE5sP/7C+hQnpX1oDxz3D4D6/qOC82BCdD/ui0sDqmEVkGPPkeTedW+Jj8Qm+Ujwet11ApgMmYvMv8DoX2Gg7QUz4LwshoT1vXt0i5ZxGVa4rvnw6DUgOdnt/OM4FpndiFHHnocErcbhcOwdm5qYhq/7AZ+/wOsJPEgbQZ7czURQfENn5xYfKIej+cPhzWZa2yJGJni5DPYIKsyuA3BIcJvkXr7TE/kYElCDKOzcGEdSZI4bVz0VKFpZ7EFju/DqF1nkCg7HVwFEV10PtQOmJyff34aWqQ2XObm96f+4S+uNtmRk0Tn1cxnr9ekv3Umc445Zhg0c3+iqzxWGdY4Pz8b0Os+yqpZIq0HwQaUSTCb7NntCWKa20vAWkrprn+7Su/QeG9QmDqeg22Stut8aFCY5jCJXxvDm9R2lAtJV+OwrhFCCymtb4ix1hvUGUIkCWYOyJr+vowkt7mqyddZZEH5EvQWXL3oidaWdzO4/y67/q2WXzM1tW3CcU3dOrOwIdPUXvTV5Jw==


```vue
<script setup lang="ts">
import {ref,Ref} from "vue";
const select:Ref<string> = ref("");
const buttonDataList:ButtonData[] = [
{
    label:"button1",
    value:"1",
    active:false,
  },
  {
    label:"button2",
    value:"2",
    active:false,
  },
  {
    label:"button3",
    value:"3",
    active:false,
  },
  {
    label:"button4",
    value:"4",
    active:false,
  },
];
const clickHandler = (value:string):void =>
{
  const n:number = buttonDataList.length;
  for(let i:number = 0;i<n;i++)
  {
    const buttonData:ButtonData = buttonDataList[i]
    if(value == buttonData.value)
    {
      buttonData.active = true;
      select.value = buttonData.value
    }
    else{
      buttonData.active = false
    }
  }
}
interface ButtonData
{
  label:string
  value:string
  active:boolean
}
</script>
<template>
  <v-app>
    <v-main>
      <div>select : {{select}}</div>
      <div v-for="buttonData in buttonDataList" :key='buttonData.value' @click="clickHandler(buttonData.value)">
        <div class="btn-container">
          <v-btn :active="buttonData.active">
            {{buttonData.label}}
          </v-btn>
        </div>
      </div>
    </v-main>
  </v-app>
</template>
<style scoped>
.btn-container
{
  margin: 10px;
}
</style>
```

あとはなんとかなりそう
System.FinalException: Record is read-only


このエラーはoldとnewを間違えているなど、トリガーの実行場所が違っているときみたいです。

http://blog.livedoor.jp/volvic_beer/archives/52652611.html


+ [Knowledge__DataCategorySelection のデータをKnowledge__kavの項目に転記する方法について](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007A397y)
GitHub Copilot Chatって何？個人でも利用できるの？

アメリカの企業GitHubが先日、「GitHub Copilot Chat」の限定パブリックベータを開始したそうです。

一体どんなサービス・機能なのか？、個人での利用や利用に関する料金などを確認していきます。

2023/07/20時点での情報についてまとめました。間違っていたら、、、本当に申し訳ないです。



# GitHub Copilot Chatとは？
- GitHub Copilot Chatは、IDE(統合開発環境)で利用するAIチャットインターフェイス
- GitHub Copilotにチャット機能が付与された進化版（超ざっくり）
- 2023/07/20に発表された
- GitHubはアメリカの企業

>パーソナライズされた支援が可能で、状況に応じたサポートを開発者に提供する。エンジニアが特定のコードスニペットで行き詰っている場合でも、特定のプログラミングコンセプトに関するガイダンスが必要な場合でもサポートする。コーディングにおける特定の課題に合わせてベストプラクティスやヒント、解決策をリアルタイムに提供できる。セキュリティスキャンで発見される脆弱性を減らすのに役立つ修正案も提案可能。さらに、コード解析や簡単なトラブルシューティングにも利用できる。

引用：<a href="https://japan.zdnet.com/article/35206840/">ZDNET Japan</a>


# GitHub Copilot Chat の主な機能
- GitHub Copilotの機能にチャットAIアシストが追加されたサービス
- コードを書く際にAIが自動的にコードを補完してくれる
- 開発者はより効率的にコードを書くことができる

# 個人でも使えるの？
- 個人プランには提供されないっぽい。（2023/07/20時点）
- ここでいう個人プランはおそらく、「GitHub Copilot for Individuals」のことだと思う。
- 「GitHub Copilot Chat for Visual Studio/Visual Studio Code」が提供される。
- 提供されるのは「GitHub Copilot」のビジネスプラン「GitHub Copilot for Business」が対象。
- GitHub Copilot Chatの利用は、GitHub Copilot for Businessの管理者が許可することで可能になる。
- 管理者が利用規約に同意すれば、組織内の全ユーザーに「GitHub Copilot Chat」ベータ版のインストール手順のメールが届く。


# GitHub Copilot Chatの利用にお金はかかるの？
- かかる。
- GitHub Copilot for Businessは1ユーザーあたり月額19米ドルである。


# 参考サイト

https://forest.watch.impress.co.jp/docs/news/1518044.html

https://japan.zdnet.com/article/35206840/
The source control system is still processing the change request for one or more of the work items being promoted.

+ [Changes unable to be merged (even though they were)](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007A2GVU)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/13defe60-bff1-383c-6f62-686748095cc3.png)


+ [Getting "Retrieving metadata failed" error when committing changed email template](https://trailhead.salesforce.com/en/trailblazer-community/feed/0D54S00000JeNVdSAN)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/9b18a5be-237d-5fbd-08f9-21456fcc323a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/bce227dc-6343-0c7e-4036-8651ebd2100b.png)

既知の問題みたい。

+ [Unable to run a simple hybrid team flow](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V000079TWSI)

:::note
Looks like the issue is with me having github open in multiple tabs. I then tried again with having github open in only one tab and it appears to have resolved the issue.
:::
AWS ECSでAWS AppConfig Agentを試してみる

# AWS AppConfig Agentとは

AppConfigの値をアプリケーションの代わりに自動でポーリング・データのキャッシュを行ってくれるサービスです。
これを利用することにより、カスタムでAWSのAPIを利用してデータを取りに行ったりキャッシュを構成する必要がなくなりました。

AWS AppConfigについては以下を参照下さい。

https://docs.aws.amazon.com/ja_jp/appconfig/latest/userguide/what-is-appconfig.html

# ためす
以下の手順を参考に試してみます。

https://docs.aws.amazon.com/appconfig/latest/userguide/appconfig-integration-containers-agent.html

## 前提
- 検証用ECRに適当なイメージをPush済み
    - 作成は[コチラ][link-1]を参考にcurlだけインストールしています。
- AppConfig上に簡単なプロファイルを作成して適当な環境にデプロイ済み
![image01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675893/607185c4-693a-285d-eb68-b1edc089e318.png)
- ECSタスクロールは作成済み
    - AppConfigのアクセス権限を付与
    - ssmのアクセス権限を付与(ecs exec用)

## タスク定義の作成
「ECS」→「タスク定義」→「新しいタスク定義の作成」からタスク定義を作成します。
コンテナ- 1に作成したコンテナの設定を行い、「コンテナの追加」を選択します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675893/cd14b43f-fd9a-1de7-7d31-435704dbf710.png" width=80%>

AppConfigAgentのサイドカーコンテナを以下のように定義します。
- 画像URI：`public.ecr.aws/aws-appconfig/aws-appconfig-agent:2.x`
- 必須コンテナ：はい
- ポートマッピング：追加
- コンテナポート2772

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675893/3b73aad1-1f4b-625f-7b4c-398b5ea9f1ae.png" width=80%>

ECSタスクロールでAppConfigへのアクセス権限があるロールを指定し、作成します。

## クラスター/サービスの作成
「ECS」→「クラスター」→「クラスターの作成」からクラスターを作成します。

クラスターが作成出来たらサービスを作成します。
作成したクラスターを選択し、サービスタブから「作成」を選択します。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675893/6b3310ab-edd0-da86-685a-7c69eb245cb8.png" width=80%>


今回は検証の為ネットワーク設定でパブリックサブネットのみを指定しています。確認していませんが、プライベートサブネット内でAgentを利用する場合は恐らくNatGatewayなどが必要となってくると思われます。

サービス作成後、ログ上で以下のようなメッセージが表示されます。
![image06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675893/a2203584-f49f-d1c5-1d78-cb3a3ae6863c.png)


## 値の確認
実際にAppConfigからエージェント経由で値を取得できているか確認します。

ECS Execを利用し、コンテナに入ります。
CloudShellを開き、以下のコマンドでECS Execを有効化します。
```
aws ecs update-service \
    --cluster <ClusterName> \
    --service <ServiceName> \
    --enable-execute-command | grep enableExecuteCommand
```

trueが返ってきたら「新しいデプロイの強制」オプションにチェックを入れてサービスの更新を行い、タスクを入れ替えます。

以下のコマンドでAgentではない方のコンテナに入ります。
```
aws ecs execute-command --cluster <ClusterName> \
    --task <TaskID> \
    --container <ContainerName> \
    --interactive \
    --command "/bin/sh"
```

![image07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675893/1b1642de-fbd2-ac7b-790e-57cab13665ad.png)

以下のコマンドでAppConfigの値を取得することができます。
```
curl "http://localhost:2772/applications/<application_name>/environments/<environment_name>/configurations/<configuration_name>"
```

値が取得できていることを確認できました。
![image08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675893/5204892f-c090-b3bc-b3fb-4fd5f16a3af9.png)


AppConfigから適当に値を変更(dev→stg)してデプロイすると、値が変わっていることも確認できました。
![image10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/675893/0271de20-5a9d-744f-b18f-d6765a3fa643.png)


[link-1]:https://docs.aws.amazon.com/ja_jp/AmazonECS/latest/userguide/create-container-image.html
【Swift】Lazyプロパティとは？

## はじめに
「あれ、Lazyプロパティってなんだっけ？」と疑問に感じたため、再度Lazyについて調べることにしました。

## Lazyプロパティとは？
普通のプロパティ（格納型プロパティ）は、初期化の時に値を与えず必要とされた時に値を決定することができます。
それに対し、Lazyプロパティ（遅延格納型プロパティ）は、参照される時に初めて初期値が設定されるプロパティです。

## 使用方法

``` Swift:普通
var name = "りんご"

class Fruits {
    var name: String = name
}
let fruits = Fruits()
name = "ぶどう"

print(fruits.name) // りんご
```

``` Swift:Lazy
var name = "りんご"

class Fruits {
    lazy var name: String = name
}
let fruits = Fruits()
name = "ぶどう"

print(fruits.name) // ぶどう
```
## 使い所
+ 初期化の処理が重くかつ、使用するまで初期化する必要がない時
+ インスタンスを生成した後の状態によって初期値を変えたい時

## おわりに
Lazyを使用すると、開発の幅が広がると感じましたが、初期のタイミングがわかりにくいため、コードを追うのに苦労しそうだと感じました。
プルリクエストに行き詰まった時にやったこと

## はじめに
今回はプルリクエストで行き詰まってしまったため自分用の備忘録としても活用できるように作成しました。

## ローカルにリポジトリをクローンする
GitHub上に作成したリポジトリをまずはローカル環境にクローンする
```
% git clone https://github.com/xxx(ユーザー情報)/git_tutorial.git
```
GitHub上はこれ
![イメージ .jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3474588/f2889050-9dc8-3087-4416-8bdb68e0ab2f.jpeg)
以下になればOK
![スクリーンショット .jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3474588/b5f41cf8-3dc9-4118-cba9-329b85539a9e.jpeg)

## ブランチの作成
次にクローンしてきたリポジトリからプルリクエスト用のブランチを作成する
① クローンしたフォルダに移動
```
% cd git_tutorial
```
② ブランチを新規作成
```
% git checkout -b pull_request
```
今回は`pull_request`というブランチを作成
```
Switched to a new branch 'pull_request'    
```
上記の表示があればブランチの作成完了◎

:::note 
作成したブランチの確認方法
`git branch`コマンド
:::


## ファイルの修正・コミット
① テキストエディタを使用しファイルを修正する
② ファイルを作成した上でコミットする
```
% git add .
% git commit -m "プルリク用にコミット"
```

## リモートリポジトリにプッシュ
コミットした後は、修正内容をGitHub上のリポジトリに反映させるためプッシュする
```
% git push origin <作成した新規ブランチ名> （今回はpull_request）
```
　
## プルリクエストの作成
GitHubで作業を進めるためGitHubへ移動しリポジトリを確認
画像のような黄色いメッセージがあればOK
![イメージ .jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3474588/73b34688-8819-563f-b78a-752f486a4d6e.jpeg)
① 赤枠をクリック
ローカルからGitHubへプッシュして画像の赤枠のように「Compare & pull request」のボタンが表示される→クリック

:::note warn
時間が経って表示されなくなってしまったら、「Pull requets」タブの「New pull request」ボタンから作成する
:::

②プルリクエスト作成画面の入力
![イメージ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3474588/d4903182-c5b7-d965-68e7-2a5ef087f138.png)
赤枠を確認しマージブランチが正しいかを確認する
下にはプルリクエストのコメントと内容を記述する

:::note
記載内容
Issue番号や対応内容を記述して、なんの目的でどのように修正したかについて
:::

## レビューアを選択
作成したプルリクエストを確認してもらいたい開発者を選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3474588/2f294ea0-71c1-c093-7fc9-930daf70fc67.png)
赤枠**Reviewers**の歯車をクリックして対象の開発者を選択する
レビューアーを設定出来たら`Create pull request`ボタンをクリックして完了

## プルリクエストの確認
画像のようになっていたらOK！！
![スクリーンショット .jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3474588/b5664582-63f5-2cd9-34b1-a392d62a17cc.jpeg)

## エラーの対処方法
### リモートリポジトリの宛先が設定されていなかった
・エラー文
```
fatal: 'origin' does not appear to be a git repository
fatal: Could not read from remote repository.

Please make sure you have the correct access rights
and the repository exists.
```
・対応方法
```
% git remote add origin https://github.com/xxx(ユーザー名)/xxx(リポジトリ名)
```
一番最初にリモートにアップロードする場合はきちんと宛先を指定しないといけないため設定してから再度pushする

### 修正を繰り返したためpushできなかった
・エラー文
```
fatal: remote origin already exists.
```
・対処方法
originを削除
```
% git remote rm origin 
```
その後再度pushを実行

## おわりに
最後まで見ていただいてありがとうございました。

## 参考記事
[fatal: remote origin already exists. と表示された場合の解決法](https://qiita.com/yu-ki0718/items/3c8aae2c81ca3f82f522)
【Cypress】時間制御のテスト方法

| package | version |
| ------- | ------- |
| cypress | 10.0.7 |
| date-fns | 2.29.3 |

```javascript:cypress
cy.clock()
cy.tick()
```

### やりたいこと
- setTimeoutやsetInterval、Dateなど、Cypress上で時間経過が必要な動作のテスト
```javascript
setTimeout
clearTimeout
setInterval
clearInterval
Date
```

### javascript

```javascript:実際のコード
// setIntervalで1秒ごとにHTMLを更新（タイマー）
let seconds = 0;

let secondsDom = document.getElementById("seconds-elapsed");
setInterval(() => {
  secondsDom.innerHTML = ++seconds + ' seconds';
}, 1000);
```
### E2Eテスト
```javascript:cypress
//オブジェクトを生成
cy.clock()

//対象のページにアクセス
cy.visit("/test.html")

//1000ms(1秒)時間を進める
cy.tick(1000)

//タイマーが1秒経過したことを確認
cy.get('#seconds-elapsed').should('have.text', '1 seconds')

//さらに1000ms(1秒)時間を進める
cy.tick(1000)

//タイマーが2秒経過したことを確認
cy.get('#seconds-elapsed').should('have.text', '2 seconds')
```


#### 参照
clock | Cypress Documentation 
https://docs.cypress.io/api/commands/clock
Salesforce Email URL Hack


https://www.salesforceben.com/salesforce-email-url-hack-tutorial/

+ [電子メールの「差出人」フィールド URL ハッキング P26](https://help.salesforce.com/s/articleView?id=000387622&type=1)

+ [Sending Email using Email Author in custom button](https://salesforce.stackexchange.com/questions/78925/sending-email-using-email-author-in-custom-button)


+ p3_lkid – 「関連する」ID。ID に基づいて、Salesforce はオブジェクトとレコードを自動的に選択します。テンプレートも使用している場合、この ID は差し込みフィールドに値を入力するために使用されます。
+ retURL – キャンセルが押された場合に URL を返します。%2Fが続きます。
+ p2_lkid – 宛先 (取引先担当者またはリード ID) 
+ p4 – CC 
+ p5 – BCC 
+ p6 – 件名 
+ p23 – メール本文 
+ p24 – 追加の template_id – Salesforce メールテンプレート ID
+ p26 – 差出人のアドレス 
+ template_id = 使用するテンプレートの ID。このテンプレートには、p3_lkid と同じオブジェクトの差し込みフィールドが含まれている必要があります。テンプレートがすでに p6 と p23 を処理している場合は、p6 と p23 を渡す必要はありません。

前提条件として、p3_lkid と retURL は常に必要になるため、常に追加します。


# Pardot

+ [send account engagement email button - add default sender](https://trailhead.salesforce.com/trailblazer-community/feed/0D54V00007A2Vtr)

指定しているオブジェクトが違う？プロスペクトを設定する？
Oracle Analytics Cloud：ヒストグラムを作ってみる

# はじめに
Oracle Analytics Cloud（**OAC**）は、様々な可視化をサポートしていますが「ヒストグラム」という可視化タイプがありません。
そこで、他の可視化タイプを使ってヒストグラムを作ってみたいと思います。
データは、Excelを使用して適当に作った「あるクラスの生徒の身長」データ（44名）を使います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342206/17e6d8de-a228-52e8-6d03-749d8987445f.png)

# データセットの作成
作成したExcelファイルをアップロードして、データセットとします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342206/f264061e-d8c0-b150-86e2-99639d4f31ae.png)

# ワークブックの作成
「OK」をクリックすると、データセットを作成しておしまいですが、「ビジュアル化」をクリックするとデータセットを作成し、そのデータセットから新規のワークブックを作成してくれます。
ここでは、「ビジュアル化」をクリックします。

## 棒グラフを作ってみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342206/64a47354-1aa2-336f-bc3b-8d711bfbed6b.png)
「出席番号」と「身長」をコントロールキーを押しながら選択し、「ビジュアライゼーションの選択」をクリックします。
「棒グラフ」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342206/a66e319a-178c-fc8d-2615-55a48009dca0.png)
「身長」を「値 (Y軸)」にドラッグ＆ドロップして、「身長」と「出席番号」の両方を「値 (Y軸)」に配置します。
「出席番号」をクリックして、集計のデフォルトを「自動」から「属性として処理」に変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342206/a60432d1-30cd-cf73-fa8c-12830e501f61.png)
続いて、属性に変更した「出席番号」を「カテゴリ (X軸)」にドラッグ＆ドロップします。
これで、このクラスの生徒の身長が棒グラフで表されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342206/7f82bc61-8994-9298-af11-dac462f2d80f.png)

## 階級を作る
「出席番号」を「値 (Y軸)」にドラッグ＆ドロップします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342206/887cadf8-f620-44f5-e911-5baf98277877.png)
「出席番号」の集計方法を「自動」から「件数」に変更します。（「COUNT 出席番号」に名前が変わります）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342206/d9ff986b-6b4b-e46c-bde7-3bd1ccbaf1ba.png)
メジャー項目である「身長」を「カテゴリ (Y軸)」にドラッグ＆ドロップします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342206/f261ec99-d976-a621-83df-dcdfc9bee802.png)
「カテゴリ (X軸)」の出席番号は不要なので、削除します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342206/d982b05d-55ba-5855-e58e-ce1778587c79.png)
「身長」は最小値と最大値を考慮してビン化されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342206/3aa90241-aca6-a6db-ff88-379d39ccf7e8.png)
ビンの数を増やしたいと思います。
「身長 (ビン化)」をクリックして、「ビンの編集」をクリックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342206/d8e69f7e-6fff-bab8-73f2-6508de29ef77.png)
ビンの数を5にしてみたところ、自動的に15cm刻みの階級が作成されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342206/dc658a21-0986-24b9-3e27-ad3dfbe7b7b3.png)
さて、それぞれの縦棒をくっつけたいのですが、それらしきプロパティが棒グラフにはありません。
そこで、別のグラフに変更します。

## 面グラフに変更する
グラフを棒グラフから面グラフに変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342206/cc3a98ce-6f8e-9085-f2c6-480afa1162e0.png)
面グラフの「プロパティ」の「一般」タブで、線のタイプを「直線」から「階段中央」に変更します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342206/a56876fa-afac-9d66-1cf9-1113d8fcc010.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2342206/45fc87a9-a7eb-4845-55b2-04f7ed92aeec.png)
完成です。
React Native: Linkingをモック化し、カスタムフックのテストを行う


React Nativeで開発を行う中で、外部のAPIやReact Nativeのネイティブ機能を使用するカスタムフックを作成することはよくあります。その際、テストを行うためにはモック化が必要となることが多いです。

今回は、`Linking.openURL`を使用するカスタムフックの作成と、そのテストを行うための`Linking.openURL`のモック化について説明します。

## カスタムフックの作成

まずは、指定したURLを開くためのカスタムフック `useOpenURL` を作成します。

```typescript
// useOpenURL.ts
import { useCallback } from 'react';
import { Linking } from 'react-native';

export const useOpenURL = () => {
  const openURL = useCallback(async (url: string) => {
    const supported = await Linking.canOpenURL(url);

    if (supported) {
      await Linking.openURL(url);
    } else {
      console.error(`Can't handle url: ${url}`);
    }
  }, []);

  return { openURL };
};
```

## Linkingのモック化とテスト

このカスタムフックをテストするためには、`Linking.openURL`をモック化する必要があります。モック化を行うことで、外部環境への依存を排除し、関数が期待したとおりの動作をするかどうかを確認することができます。

以下に、`Linking.openURL`をモック化し、それを利用するカスタムフックのテストを作成する例を示します。

```typescript
// useOpenURL.test.ts
import { renderHook, act } from '@testing-library/react-hooks';
import { useOpenURL } from './useOpenURL';
import Linking from 'react-native/Libraries/Linking/Linking';

jest.mock('react-native/Libraries/Linking/Linking', () => ({
  canOpenURL: jest.fn(() => Promise.resolve(true)),
  openURL: jest.fn(() => Promise.resolve()),
}));

describe('useOpenURL', () => {
  beforeEach(() => {
    // モックをリセットします
    Linking.canOpenURL.mockClear();
    Linking.openURL.mockClear();
  });

  it('calls Linking.openURL with correct url', async () => {
    const { result } = renderHook(() => useOpenURL());

    await act(() => {
      return result.current.openURL('https://example.com');
    });

    expect(Linking.openURL).toHaveBeenCalledWith('https://example.com');
  });
});
```

テストの流れは以下の通りです:

1. `jest.mock`を使用して`Linking.canOpenURL`と`Linking.openURL`をモック化します。これにより、これらの関数がテスト中に呼び出されると、実際の処理ではなくモック化した処理が実行されます。
2. `beforeEach`フックで各テストケースの前にモックをリセットします。これにより、各テストケースは他のテストケースの結果に影響されずに実行することができます。
3. `renderHook`と`act`を使用して、カスタムフックの中で`Linking.openURL`が正しく呼び出されることを確認します。

このように、モック化を利用すれば、外部のAPIやネイティブ機能に依存するカスタムフックも確実にテストすることができます。安定した品質のソフトウェアを提供するために、テストの重要性を忘れないようにしましょう。
Redshift Serverless トランザクション実行中の課金について

Redshift Serverlessを使って気づいたことを書きます。
今回はトランザクションの実行とRPUの挙動についてです。  


# 処理をしていないはずなのに、課金が発生  
あるとき何も処理をしていないはずであるにもかかわらず、相当な額の料金が発生していることに気付く。  
AWS Budgetsの予算通知ルールを作成していたため、気づくことができた。 
<br>料金が発生しているServerlessのメトリクスを確認すると、特に処理をしていないはずの時間帯にRPUがBase Capacityの値で張り付いているという挙動が確認できた。 
# 原因  
以下の4つが重なって、Serverless上で"トランザクション"が1週間近く実行されたままだったと推測した。<br> 
1.Redshift Serverlessはトランザクション実行中はRPUが使用される。(=料金が発生する)     <br>https://docs.aws.amazon.com/ja_jp/redshift/latest/mgmt/serverless-billing.html  
2.接続に使っていたツールをauto commit OFFで使っていた。(=selectしただけでトランザクションが開始され、トランザクションはcommit またはrollbackするまでおわらない)
3.ツールを動かしている端末は普段起動したままのPCであり、接続ツールが終了されることがなかった
4.ドキュメント上記載のある、トランザクション制限6時間がきかなかった<br>https://docs.aws.amazon.com/ja_jp/redshift/latest/mgmt/amazon-redshift-limits.html#serverless-limits-account
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/245869/6a447699-f3d1-4d38-67ff-c25751bead87.png)

# 今後のために　
第一に、トランザクションは明示的に終了するガイドするべきと考える。一方操作ミスなども考えられるため、システム側でも何らかの手立てを立てる必要があると考える
<br>要件としては「処理をしていないが、RPUが消費されている状態」を検知できるようなモニタと通知を可能とすること。

<br>まずRedshift Serverlessの機能に「RPUの制限」はあるが、この機能で検知できるのは、総使用RPUであり、必要な処理で消費されたRPUでも検知されてしまうため、要件を満たさない。またAWS　Budgetsも同様に必要な処理で発生した料金でも検知されてしまうため、要件を満たさない。<br>

どちらの機能もそれだけでは「処理をしていないが、RPUが消費されている状態」を検知することはできない。
よって、他の手段を検討する必要があると考える。
<br>
モニタと通知の検討と実装結果を別記事にて記載する。
DeepReadonly などを import なしで使用したい。global.d.ts 拡張

# 結論
ts-essentials の [DeepReadonly](https://github.com/ts-essentials/ts-essentials/tree/master/lib/deep-readonly) など、よく使用する型は毎回 import するのが手間だし、import が積み重なってしまう。


```呼び出し元.ts
import { DeepReadonly } from 'ts-essentials'

type T = DeepReadonly<{ field: string }>
```

この作業に煩わしさを感じたら global の拡張を検討すると良い。

```global.d.ts
import { DeepReadonly } from 'ts-essentials'

declare global {
  type DR<Type> = DeepReadonly<Type>
}
```

```tsconfig.json
{
  "include": ["src/**/*", "global.d.ts"] # src配下にある場合はglobal.d.tsは不要
}
```

```呼び出し元.ts
// import が不要になった
type T = DR<{ field: string }>
```

## ts-essentials
https://github.com/ts-essentials/ts-essentials/tree/master
オブジェクトからunion型をつくる

Union型をオブジェクトのkeyからつくりたいときは `keyof` と `typeof` を使えば良いみたい。

https://gist.github.com/mayognaise/1b256a0063edf62d00f22e79722bfa68

こんな感じになってくれます。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77626/b32425a0-31bb-387c-3d21-99fcb3fba98d.png" width="500" />

ちなみにUnion型をオブジェクトのvalueからつくりたいときは `keyof` と `typeof` を駆使して作る。

https://gist.github.com/mayognaise/0164657deb155fe865ef09b4cf6e6088

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/77626/c9317ac8-70d4-2831-b2d6-4867c4aece26.png" width="600" />

いつ使うん？と言われそうやけど備忘録。。