OmniAICreator/Qiita-1.07M huggingface dataset:
Kaggle モデル保存と読み出し

# 概要
kaggle初心者が学習済みモデルなどの保存の仕方をまとめてみました。
Kaggle notebookで作業しモデルを保存しひと段落。
後日Kernelを更新すると保存したはずのデータが消えている！なぜ！！

どうやったらモデルが半永久的に保存され続け、どのようにモデルを読み出すのかをまとめています。
私と同じくkaggle初心者向けの記事です。

# 保存ディレクトリ
まずnotebookで使用できるディレクトリを紹介します。
* ユーザが使用できる作業ディレクトリ："./kaggle/working/"
* ユーザが一時的に使用できる作業ディレクトリ："./kaggle/tmp/"
* 追加したデータセットが保存されているディレクトリ："./kaggle/input/"

モデルは、
./kaggle/working/
に保存することになります。

# 保存方法
では本題の保存方法を解説していきます。
以下は学習済みモデルが該当ディレクトリに保存されていることが前提です。
``` save model
file = 'trained_model.pkl'
pickle.dump(trained_model, open(file, 'wb'))

path ='/kaggle/working/'
fileList = os.listdir(path) 
for f in fileList:
    print(f)
```


1. 右上の ( >| )マークを選択して、Toggle Sidebarを表示させる。
2. Outputを選択し、学習済みモデルがあることを確認する。
3. Save Versionを選択し現在のnotebookをcommitする。
3. Toggle Sidebar右上に表示されている、Add dataを選択する。
3. 上部Tool bar右に表示されている、Notebook Output Filesを選択する。
3. Your Workでフィルタリングをして保存したいnotebookをADDする。

以上で保存が完了します。

モデルの読み出しは該当ファイルのpathを使用するAPIに従って渡せばOKです。

# おわりに
Kaggle内のDiscussionに同じ様な悩みを抱えている方が大勢いました。
周りにわからない人を見つけたら教えてあげてください。
参考にさせていただいたサイト[Saving and loading Pytorch models in Kaggle](https://towardsdatascience.com/saving-and-loading-pytorch-models-in-kaggle-3dadc0af1bd9)では、動画付きの解説記事になっていますので是非確認してみてください。

お役に立つことを願っています。
nginxのコンフィグファイル

#はじめに
nginxのコンフィグファイルについての備忘録です。

##nginxのコンフィグファイルのパス
```
$cd /etc/nginx
$pwd
/etc/nginx
$ls
conf.d                  koi-utf             scgi_params
default.d               koi-win             scgi_params.default
fastcgi.conf            mime.types          uwsgi_params
fastcgi.conf.default    mime.types.default  uwsgi_params.default
fastcgi_params          nginx.conf          win-utf
fastcgi_params.default  nginx.conf.default
```

##nginxのコンフィグファイルの中身
```
$cat nginx.conf
# For more information on configuration, see:
#   * Official English Documentation: http://nginx.org/en/docs/
#   * Official Russian Documentation: http://nginx.org/ru/docs/

user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/doc/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 4096;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

    # Load modular configuration files from the /etc/nginx/conf.d directory.
    # See http://nginx.org/en/docs/ngx_core_module.html#include
    # for more information.
    include /etc/nginx/conf.d/*.conf;

    server {
        listen       80;
        listen       [::]:80;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        error_page 404 /404.html;
        location = /404.html {
        }

        error_page 500 502 503 504 /50x.html;
        location = /50x.html {
        }
    }

# Settings for a TLS enabled server.
#
#    server {
#        listen       443 ssl http2;
#        listen       [::]:443 ssl http2;
#        server_name  _;
#        root         /usr/share/nginx/html;
#
#        ssl_certificate "/etc/pki/nginx/server.crt";
#        ssl_certificate_key "/etc/pki/nginx/private/server.key";
#        ssl_session_cache shared:SSL:1m;
#        ssl_session_timeout  10m;
#        ssl_ciphers PROFILE=SYSTEM;
#        ssl_prefer_server_ciphers on;
#
#        # Load configuration files for the default server block.
#        include /etc/nginx/default.d/*.conf;
#
#        error_page 404 /404.html;
#            location = /40x.html {
#        }
#
#        error_page 500 502 503 504 /50x.html;
#            location = /50x.html {
#        }
#    }

}
```

#おわりに
nginxのコンフィグファイルの備忘録は以上です。
STM32F767とKeil Studioでどうやったか覚えていないがLチカ



目的
Mbedが終了の為


![o_con234.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/40f8d3c7-dc89-2f8b-6dd5-e74b15c9b03a.jpeg)



```c++


#include "mbed.h"

DigitalOut myled(LED1);

int main() {
  while(1) {
    myled = 1;
    wait(1);
    myled = 0;
    wait(1);
  }
}



```
【小ネタ】サクラエディタを使ってファイル名の一覧を作成する

# 簡単にファイル名の一覧を作成できる方法をご紹介

## 操作手順

1. マウスの __右ボタンで__ ファイルをドラッグ&ドロップする  
  ![GetPath1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2115724/d6e8e321-034a-e569-19c9-a02652926ca9.png)
1. コンテキストメニューの「ファイル名貼り付け(F)」を選択  
  ![GetPath2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2115724/12dbbb39-b4d2-0652-d0e7-edb289c94861.png)
1. ファイル名の一覧が表示される  
  ![GetPath3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2115724/3c6095ef-6603-c07b-e304-f10035eccebd.png)


### メリット

* 「パスのコピー(A)」をしてから、ファイル名だけ残すより簡単  
  → 「パスのコピー(A)」は、[Shift]+マウスの右ボタンで選択できる
  ![GetPath8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2115724/3c3e0491-a803-482d-e781-ce0e782ab921.png)

### デメリット

* 再帰的な処理はできない  
  → cf. [再帰的に処理したい場合](#再帰的に処理したい場合)
  ![GetPath4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2115724/a0d87609-f3d5-f38b-6d32-9141085475c9.png)  

### 再帰的に処理したい場合

1. 「検索(S) > Grep(G)...」を使用する方法がある  
  ![GetPath6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2115724/230a3a08-c2de-4c94-333a-ea1d4900944d.png)  
1. 最後に、行末の[SJIS]等を削除するのが面倒  
  ![GetPath7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2115724/b735d11a-d5aa-51e7-3135-8bce9f13ff7b.png)
【Go】http.ResponseWriterのメソッドw.Header().Add()が効かない

http.ResponseWriterのメソッドHeader().Add()でヘッダーを追加したはずなのに、レスポンスのヘッダーを見ると付与されていない。

単純にw.Header().Addの前に、w.Writeを呼び出していたことが原因だった。順番を逆にすると、想定どおり動作する。

```go:改修前
func(w http.ResponseWriter, _ *http.Request) {
	w.Write([]byte("Hello, world"))

	w.Header().Add("Access-Control-Allow-Origin", "http://localhost:3000")
	w.Header().Add("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token")
	w.Header().Add("Access-Control-Allow-Methods", "POST, OPTIONS")
	w.Header().Add("Access-Control-Expose-Headers", "X-CSRF-Token")
	w.Header().Add("Access-Control-Allow-Credentials", "true")
}
```
```go:改修後
func(w http.ResponseWriter, _ *http.Request) {
	w.Header().Add("Access-Control-Allow-Origin", "http://localhost:3000")
	w.Header().Add("Access-Control-Allow-Headers", "Content-Type, X-CSRF-Token")
	w.Header().Add("Access-Control-Allow-Methods", "POST, OPTIONS")
	w.Header().Add("Access-Control-Expose-Headers", "X-CSRF-Token")
	w.Header().Add("Access-Control-Allow-Credentials", "true")

	w.Write([]byte("Hello, world"))
}
```
Bitnami Mattermost 6.4でmmctlを使いやすくする

Bitnami Mattermost 5.xから移行しようと思っていましたが、過去の柵をいろいろ引きずるのが面倒になったので新しいVMを立てました。5.xからの移行にあたり、公式のチャンネルくらいは同じものを用意してあげたいな、と思ったのですが、チャンネル情報を取得するのにちょっと苦労したので自分用メモです。

VMをBitnamiから拝借して立てるところは大した手順ではないので省略。

# 概要

* 6.xになり、従来 `mattermost` だったコマンドは `mmctl`になった。
* `mmctl` を実行するにはいくつかの条件が必要。たぶん数日したら忘れる。
* 忘れないように、`.bash_aliases`に追記することで忘れる権利を取得。

# mmctlを実行する条件

## ユーザ認証が必要。だけどローカル実行という回避策がある。

以下のいずれかの方法で認証しないと、`mmctl`は実行できません。
* `mmctl auth login`であらかじめ認証しておく
* `mmctl --local` をつけて、ローカル実行であることを明示する

私は`mmctl auth login`で指定する「Connection Name」が何を指すのかよくわからなかったので、後者の作戦にしました。エラー文言もイマイチ把握できておらず)

## ローカル実行にはmmctl の実行ユーザをソケットと合わせる必要がある。

mattermostを起動した際にソケットファイル  `/var/tmp/mattermost_local.socket` ができます。
ローカル実行はこのソケットファイルの作成者の権限を使うので、mmtlの実行ユーザは `/var/tmp/mattermost_local.socket` のユーザーと同一である必要があります。
bitnami のVMの場合は、`mattermost` ユーザですね。

# 条件を忘れてもいいようにする

上記のような条件を覚えておくのが負担なので、aliasにしてしまいました。
bitnami ユーザでそのまま使えるように。

`alias mmctl="sudo -u mattermost mmctl --local`

ログインのたびに打つのも面倒なので、これをbitnamiユーザの`.bash_aliases` に追加して終わり。

次はチャンネル情報設定とか、ユーザ設定とかの移行方法を調べます。
STM32F303とKeil Studioでどうやったか覚えていないがLチカ



目的
Mbedが終了の為


![o_con233.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/840352/111e7dce-ea28-7527-300b-e3b8b33c2479.jpeg)



```c++


#include "mbed.h"

DigitalOut myled(LED1);

int main() {
  while(1) {
    myled = 1;
    wait(1);
    myled = 0;
    wait(1);
  }
}



```
【Terraform de Azure】 DevTest Labs の Windows10 VM に Artifacts からアプリをインストールしてみました

### 概要
「Infrastructer as Code」ということで、Terraform を用いて Azure環境上の DevTest Labs にある Windows10Pro を作成した後、Azure CLI から Artifacts を利用して アプリケーションをインストールする方法の説明となります。

### ローカル環境

- macOS Monterey 12.1
- python 3.8.12
- Azure CLI 2.33.0
- terraform v1.0.11

### 前提条件

1. Azure環境がすでに用意されていること（テナント/サブスクリプション）
2. ローカル環境に「azure cli」がインストールされていること
3. ローカル環境に「terraform」環境が構成されていること
4. TerraformでAzure上に環境構築するための[サービスプリンシパルが作成](https://qiita.com/turupon/items/5d024d43a60cd101be94)されており、Terraform のためのローカル環境変数が定義されていること

### 事前実施事項

[この記事](https://qiita.com/turupon/items/b2d9034760341b51f180)の定義ファイルを利用して terraform apply することにより、Azure上に  Windows10 Pro の VM を作成していること

- リソースグループ名：rg_ituru_dt02
- DevTest Labs 名：dt_ituru_lab02
- 仮想マシン名：vm-ituru-vsc02

---

## Windows10 Pro への アプリのインストール

### 仮想マシンの起動

```
$ az lab vm start --lab-name dt_ituru_lab02 --resource-group rg_ituru_dt02 --name vm-ituru-vsc02
```

### Artifactsの適応

導入するアプリケーションの定義します。 Artifacts の public repo を利用します。

```artifacts.json
[
    {
        "artifactId": "/artifactSources/public repo/artifacts/windows-git",
        "parameters": []
    },
    {
        "artifactId": "/artifactSources/public repo/artifacts/windows-vscode64",
        "parameters": []
    }
]
```

仮想マシンへのArtifactsの適応

```
$ az lab vm apply-artifacts --lab-name dt_ituru_lab02 --resource-group rg_ituru_dt02 --name vm-ituru-vsc02  --artifacts '@artifacts.json'
```

---

## 仮想マシンへ接続しての確認

### RDP接続

仮想マシンへの RDP接続については、[この記事](https://docs.microsoft.com/ja-jp/azure/virtual-machines/windows/connect-logon) を参考にして実施ください。

### 接続（ログイン）後の確認

コンソール（PowerShell）を立ち上げ、アプリケーションがインストールされていることを確認します。

```
PS C:\Users\adminuser> Get-ItemProperty HKLM:Software\Microsoft\Windows\CurrentVersion\Uninstall\* | Select-Object DisplayName, DisplayVersion, Publisher, InstallDate | Format-Table -AutoSize

DisplayName                  DisplayVersion Publisher                     InstallDate
-----------                  -------------- ---------                     -----------
Git                          2.35.1.2       The Git Development Community 20220217
Microsoft Visual Studio Code 1.64.2         Microsoft Corporation         20220217
```

Artifactsでインストールされたファイルのありかは以下となります

```
## VScode
  Directory: C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.10.12\Downloads\2\PublicRepo\master\8d2ed1d40ceea3d4ef64bf085ceceb83d1be0002\Artifacts

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----         2/17/2022   4:21 PM                windows-vscode64

## Git
  Directory: C:\Packages\Plugins\Microsoft.Compute.CustomScriptExtension\1.10.12\Downloads\1\PublicRepo\master\8d2ed1d40ceea3d4ef64bf085ceceb83d1be0002\Artifacts

Mode                 LastWriteTime         Length Name
----                 -------------         ------ ----
d-----         2/17/2022   4:18 PM                windows-git

```

---

### まとめ

これで、Terraform でサクッと Azure環境上に作成した  DevTest Labs にある Windows10Pro に対し、Azure CLI から Artifacts を利用して アプリケーションをインストールする方法を確認できました。　Artifacts 部分も含め、すべて Terraform で実施するのはどうすればよいのでしょう、、、、、、
SEOとは

# SEOとは
**SEO（Search Engine Optimization：検索エンジン最適化）**
検索エンジンを使ったマーケティング戦略のひとつです。Googleなどの自然検索結果で自社のページが表示されることにより、サービスや商品を知らないユーザーがページを見て検討する機会を創出します。

**検索エンジン**
検索ユーザーが情報を探すためのプラットフォーム（媒体）のことです。検索ボックスにキーワードを入力することで、そのキーワードと関連性の高いWebページや画像などの情報を検索結果に返し、一覧表示する仕組みを持つシステムのことを指します。
例　Google、Yahooなど
日本における検索エンジンの具体的なシェアは下記のとおりです。
※現在YahooはGoogleの検索技術を使っており、双方の検索結果はほとんど同じなため、実質としてはGoogleがほぼすべてを占めていると言えます。


- 1位　Google：77.06％
- 2位　Yahoo：17.94％
- 3位　bing：4.57％
- 4位　DuckDuckGo：0.16%
- 5位　Baidu：0.15％
- 6位　YANDEX：0.03％


## SEOメリット・デメリット
- メリット
 - 検索結果を通じて自社のサービスや商品を知らないユーザーと接点を持つこと
 - ユーザーのアクセス数に応じて費用が発生しない
- デメリット
 - 世の中に存在しない新しい商品は検索されるキーワードが存在しない
 - 改善施策を実装しても検索エンジンに評価されるのに時間がかかる
 - 外的要因（他のサイトの施策）によって順位が変動してしまう

## ユーザーの行動を理解する
ユーザーは情報を取得しながら検索を繰り返し、店舗予約や購入など目的を達成できれば検索を終了します。

商品購入までの流れの例
https://www.irep.co.jp/img/2019/0722/20190722_blog_img2.png
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1888749/da1c6590-6121-41c2-1f16-55e4adbb730f.png)
ユーザーがどのようなキーワードで検索してどのような情報を得たのかという過程を理解し、ユーザーが検索行動に移るきっかけを考えることが重要です。

## 検索エンジンの仕組み
検索エンジンは「クローラー（ボット）」「インデックス」「検索アルゴリズム」の3つのステップで決定しています。

- クローラー（ボット）<br>
インターネット上に存在する無数のWebページを、リンクを辿ることで情報収集（おもにhtmlなど）する仕組みのこと
- インデックス<br>
収集した情報をデータベースに分類・登録すること。インデックスされていなければランキングの対象にはならず、検索結果として表示もされません。
- 検索アルゴリズム<br>
インデックスした情報をそれぞれの検索キーワードに合わせて最も適切だと判断した順番に並べる仕組みのこと。キーワードに対して関連性が強く、かつ評価の高いページから順番に検索結果に並べられる。

## Google検索エンジンの様々な機能

- オーガニック（自然）検索<br>ユーザーが検索したキーワードに対するリスティング広告を除いた検索結果のことです。
検索エンジンによる純粋な検索結果を指しており、上記で説明してきたGoogleの評価に基づいてランキングされ、表示されています。

- リスティング広告<br>あるキーワードを検索した際、連動して検索結果に表示される「検索連動型広告」を指し、検索ユーザーのクリック数に応じて課金される広告「PPC広告（クリック課金型広告）」の一種です。
オーガニック検索と自然検索は下記のように表示されます。

## Webページの作成で意識すべきこと
ページはこのようにヒエラルキー構造で成り立っています。上に行くほど影響は大きいですが、下に行くほど対策すべき内容が多くなるので、結果としてどれも重要だと考えられます。
https://keywordmap.jp/academy/wp-content/uploads/2021/02/7-20210202190454.png
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1888749/f6447e98-c6fc-7f00-8b87-9a160c098007.png)

### タイトル（titleタグ）
検索結果にも表示されることからも分かるように、検索エンジンがページ内容を理解する上で大切な要素です。

- タイトルに目標キーワードを入れることで、順位獲得を図る
- 対策キーワードはタイトルの左寄せが原則
- タイトルの文字数は40文字程度（あくまでも目安）

### 見出し（h2〜h6タグ）
ピラミッドの真ん中に位置する見出しは、章や節として機能しています。以下のようにコンテンツの要素を論理的に分割して使用することで、各セクションのテーマを把握します。

- コンテンツをセクションごとに分割する役割の「大見出し（段落）」（h2）を使用
- 大見出し（段落）に付随したグループは「小見出し（節）」（h3～h6）を使用


### 本文
ページの大部分を占める要素です。
本文は検索エンジンのコンテンツ解析力が向上しているため、重要度が上がってきています。そして、記事を下支えしている本文が見出しの評価を上げて、見出しの評価が上がるとタイトルの評価が上がります。パワーは下から上に繋がると考えてください。
上位表示できる記事は、タイトルに対して見出しがしっかりと構成され、見出しに対して本文が作りこまれた、ロジカルに土台のできた記事です。
このようにヒエラルキー構造を理解したページを作成しましょう。

# 参考
https://www.irep.co.jp/knowledge/blog/detail/id=45926/

https://keywordmap.jp/academy/how-search-engine-work/

https://ferret-plus.com/11151
Asyncをつけた時と、Asyncを付けずにFuture.valueを返す際にFuture<T>からTに変換するサンプル


```dart

void main() async {  
  usingFutureValue().then((value) {
    print(value);
  });
  
  print(await usingFutureValue());
  
  usingAsync().then((value) {
    print(value);
  });
  
  print(await usingAsync());
}

Future<String> usingFutureValue() {
  return Future.value('Future.valueで値を返しました');
}

Future<String> usingAsync() async {  
  return "Asyncで値を返しました。";
}

```

結果

```
Future.valueで値を返しました
Future.valueで値を返しました
Asyncで値を返しました。
Asyncで値を返しました。

```

asyncをつけるとFuture.valueを付けなくてもFuture型を返してくれるようになる。
Lichee RVを使ってみる

# はじめに

最近RISC-Vの話題が賑やかになっています。RISC-V関連のニュースでは、ルネサス、Intel、Appleなどの名前も挙がったりして興味が尽きません。
M5Stick-Vで使われているK210もRISC-Vで、M5Stick-Vは非常に面白いデバイスですが、リソースの制約などからLinuxを動かすことはできません。Sipeed社からLinuxを動かすこともできるLichee RVがでているので今回試してみました。

# Lichee RV

Lichee RVはSipeed社のマイコンボードです。CPUはAllWinner社のD1プロセッサを搭載しています。スペックや価格は入手困難なRaspberry Pi Zero 2 Wに近いかなという気がします。Raspberry Pi Zero 2 Wの代わりという意味でも入手してみました。

こちらの [Lichee RV Dock](https://ja.aliexpress.com/item/1005003741287162.html) を使用しました。

# 使い方

@airpocket　さんの次の記事を参考にさせていただきました。ありがとうございます。

- [Lichee RVでubuntu20.04を動かす](https://qiita.com/airpocket/items/9c7c6a746ff6fed3a01f)
- [Sipeed LicheeRV セットアップまとめ](https://qiita.com/airpocket/items/5cac38eb2a6cc3223c40)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555397/dc4f6821-b3a3-ac20-902f-30dffa991728.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555397/482d23b6-5cc0-58ef-3727-d888ba512355.png)


# 困ったところ/はまったところ

- SDカードの書き込み

上記記事にも書かれていますが、SDカードの準備にはPhoenixCardというWindowsデスクトップアプリを使用します。Windows PCが必要です。手元にMacしかなかったので古いWindows PCを引っ張り出してきました。

- コンソール接続

Lichee RV DockにはUSB-C、USB-Aコネクタが付いていますが、PCをUSB-Cに接続してもシリアル変換されるわけではないようです。JTAGデバッガーなどでシリアルでボードのUARTピンに接続する必要がありました。

```text:
Debugger      Lichee RV Dock
GND     <->   GND
TX      <->   RX
RX      <->   TX
```
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555397/2530e6c6-d26e-0eb3-f888-8246b9a5d76c.png)

# イメージについて

Sipeed社のサイトからDebian等のイメージがダウンロードできます。このイメージはAllwinner社のA10イメージと呼ばれる形式のようで、Raspberry Piのイメージとは互換性がありません。上記のようにPhoenixCardというWindowsアプリを使用する必要があり、Mac等からddコマンドでsdカードを作成することはできません。

ブートプロセスなど

- https://riscv.or.jp/wp-content/uploads/Linux_distros_on_RISC-V_Day_Tokyo_2021_draft_c.pdf
- https://linux-sunxi.org/Allwinner_Nezha

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555397/68e498f0-948a-f6f4-6a19-044b1feabcfb.png)

A10イメージについて

- https://github.com/xypron/awutils

# リソース

- [WiKi](https://wiki.sipeed.com/hardware/zh/lichee/RV/RV.html)
- [Lichee RV 仕様](https://dl.sipeed.com/shareURL/LICHEE/D1/Lichee_RV/HDK/1_Specifications)
- [Lichee RV 回路図](https://dl.sipeed.com/shareURL/LICHEE/D1/Lichee_RV/HDK/2_Schematic)
Docker Desktop for Mac の遅い問題を解決 2022

Docker for Mac が遅い！ docker-sync や、 docker on Vagrant や、cached オプション、delegated オプションなどなど、いろんな解決策が紹介されましたが、それでもフレームワークなどを使っていると遅い、、、

というわけで Docker Desktop for Macの遅い問題を解決する、2022版バージョンをお届けします。

## マウント先がドキュメントルートだから遅い

よくローカルのファイルをコンテナ内から参照するためのマウント先をドキュメントルート、要するに、 `/var/html/html` に設定する例を多く見ますが、これが問題の根源となっている。

ドキュメントルートをマウントせずにファイルを設置すると普通に早い。

## ドキュメントルート以外にマウントして同期する

今回は、 `/var/www/shared` にマウントして、 lsyncd で同期する方法を選択しました。

## 以下、やったこと

ファイル構成は次のとおり。
ファイル中のパスなどは適宜ご自分の環境に合わせて調整してください。

```
- docker/
  - apache/
   - init.sh
  - dockerfile/
   - Dockerfile-php7.4
  - lsyncd/
    - lsyncd.conf.lua
  - docker-compose.yml
```

　
### lsyncd の設定ファイルを準備

双方向に同期する設定にします。
delay 0 だと即同期なんですが、双方向ともに 0 に設定するとコンフリクトっぽい感じになってうまく同期されないので、ドキュメントルート側からローカル側に同期する設定だけ、delay 1 に設定します。

```javascript
// docker/lsyncd/lsyncd.conf.lua

settings {
        logfile    = "/tmp/lsyncd.log",
        statusFile = "/tmp/lsyncd.status",
        nodaemon = false
}
sync {
        default.rsync,
        source = "/var/www/shared/",
        target = "/var/www/html/",
        delay = 0,
        delete="running",
        exclude={"node_modules", "tmp", ".git", ".idea", ".DS_Store", "docker"}
}
sync {
        default.rsync,
        source = "/var/www/html/",
        target = "/var/www/shared/",
        delay = 1,
        delete="running",
        exclude={"node_modules", "tmp", ".git", ".idea", ".DS_Store", "docker"}
}
```

　
### Dockerfile に rsync と lsyncd を追加

lsyncd の設定ファイルをコンテナにコピーする設定も追加。

```ssh
# docker/dockerfile/Dockerfile-php7.4

RUN apt-get update rsync lsyncd
COPY lsyncd/lsyncd.conf.lua /etc/lsyncd/lsyncd.conf.lua
```

　
### イメージをビルド

M1対応もついでに。
ここでは「5-php7.4」というタグを付けてプッシュしています。

```ssh
docker buildx build --platform linux/amd64,linux/arm64 -t baserproject/basercms:5-php7.4 -f dockerfile/Dockerfile-php7.4 --push .
```

　
### 初期化ファイルを準備

コンテナを立ち上げる際に一発目の rsync を実行しておきます。こちらは初回のみ実行されるよう、checkファイルの有無で制御しています。
また、ここで、毎回 lsyncd を立ち上げることに。

```ssh
# docker/apache/init.sh

if [ ! -e '/var/www/shared/docker/check' ]; then
    rsync -av /var/www/shared/ /var/www/html --exclude='node_modules' --exclude='tmp' --exclude='.git' --exclude='.idea' --exclude='.DS_Store' --exclude='docker'
    touch /var/www/shared/docker/check
    echo "container setup is complete"
fi
service lsyncd start
```

　
### docker-compose.yml にて shared にマウントするように設定

`/var/www/html` でなく、 `/var/www/shared` にマウントするように設定します。
また、先ほど作った初期化ファイルの実行設定を書きます。

```yml
// docker/docker-compose.yml

services:
  bc5-php:
    container_name: bc5-php
    image: baserproject/basercms:5-php7.4
    volumes:
      - ../:/var/www/shared:delegated
    ports:
      - "80:80"
      - "443:443"
    command: bash -c "/var/www/shared/docker/apache/init.sh && apache2-foreground"
```

### コンテナ実行

コンテナを立ち上げます。

```ssh
docker-compose up -d
```

以上でおわり。お疲れさまでした。
これまでからするとかなり爆速！
　
　
baserCMSの CakePHP４系化の開発プロジェクト「ucmitz」でのコミットログはこちら。

https://github.com/baserproject/ucmitz/commit/554d97a174a383f6a13780787e610fe90d6f38c1
【TypeScript入門 #6】Intersection Types（交差型）、Union Types（共用型・合併型）

# 概要

オンラインサロン IT_KINGDOM で、typescript について学んだことを備忘録としてメモしていきます。

# 学習内容

- Intersection Types（交差型）
- Union Types（共用体型・合併型）

# Intersection Types（交差型）

- 複数の型を結合する
- オブジェクトなどで用いられることが多い
- プリミティブ型（number, string）などでも構文上は使えるが、意味不明となるため実務上で使うことはない

```typescript
// 結合された型では、すべての要素を保持する必要がある
type Foo = {
  a: number;
  b: string;
};
type Bar = {
  c: boolean;
};
type FooBar = Foo & Bar; // 2つの型の結合 Intersection Types

// 型Fooの要素a, b、型Barの要素cをすべて保持しているためOK
const Test: FooBar = {
  a: 2,
  b: "test",
  c: false,
};

// 型Fooの要素bを保持していないためエラー
const Test2: FooBar = {
  a: 2,
  c: false,
};
```

# Union Types（共用体型・合併型）

- 複数の型のいずれかが成立すれば OK
- Intersection Types と違い、プリミティブ型や Literal Types でも使われる
- `if`文などで型の識別がされていれば、識別された型が適用される

```typescript
// Union Types
// 結合した片方の型が成立すればOK
type Foo = {
  a: number;
  b: string;
};
type Bar = {
  a: string;
  c: boolean;
};
type FooBar = Foo | Bar; // Union Types

// Barの型を満たすためOK
const test: FooBar = {
  a: "1",
  b: "",
  c: true,
};
// Fooの型を満たすためOK
const test2: FooBar = {
  a: 1,
  b: "1",
};

if ("c" in test2) {
  // test2のkeyに'c'が存在するので、type2の型はBarとなる
  test2.a = "11";
} else {
  // test2のkeyに'c'が存在しないので、type2の型はFooとなる
  test2.b = "a";
}

if ("b" in test) {
  test.a = 1;
}
```

# 参考資料

[IT Kingdom](https://it-kingdom.com/)<br>
[Typescript 公式 HP Everyday Types](https://www.typescriptlang.org/docs/handbook/2/everyday-types.html#union-types)
三本指でアイテムをドラッグしたままスクロールするには

Mac を使っていて、ファイルやブラウザ上のアイテムをドラッグすることがよくあります。

特に、ブラウザ上で長いページの下から上、または上から下までアイテムをドラッグしたまま運ぶ際に、今までずっと、ドラッグしながらページの外に少しずつ動かして自動スクロールを待つという方法で作業していました。

しかし、今日、<span style="color: red;"><strong>ドラッグしたままキーボードの「上下キー」を押し続けることで、簡単にドラッグしたままスクロールできる</strong></span>ということに気づきました。

・・・今まで、こんな簡単なことに気づかず、面倒な作業に時間をかけていたことを反省する意味も込めて、また、これを読む人の作業ストレスが緩和されることを願い、記事を投稿します。
レビューで指摘を受けた際には作成者が水平調査の責務を負う

チームで何かに取り組んでいる時には、執筆物や製作物を他の誰かに確認・指摘をしてもらう場面があると思います。

その際に何か指摘を受けた際には、指摘された箇所だけでなく同成果物の中で同様に変更が必要な箇所がないかどうかについて「作成者が網羅的に確認する責務を負うフローが良い」と**私は考えています。**

## 文章確認の場合
こんな文章をクライアントに送るから内容を見て欲しいと言われたら、

```
いつもお世話になっております。

1/5の打ち合わせにて議題に挙がっておりました〇〇に関しまして、
社内で確認しましたところ、2/5に△△の対応を予定しております。

よろしくお願いいたします。
```

下記のようなコメントを送ります。

①「1/5」という表現だと読者に日付と伝わりにくい恐れがある為、「1/3(水)」や「1月3日」の方が良いでしょう。
②「2/5」という表現だと読者に日付と伝わりにくい恐れがある為、「2/5(土)」や「2月5日」の方が良いでしょう。←

同じ内容の指摘になるので②は言及しません。

## コードレビューの場合（サンプルコードは割愛）

①`sendThanksMailFunction`関数の関数名に`Function`は不要です
②`putInformation`関数の処理内容に変更を加えているので、関数Docのコメントも変更してください
③`getCoupons`関数で取得期間を指定する際にUTCと日本時間の9時間ズレを考慮してください
④`sendPromotionMailFunction`関数の関数名に`Function`は不要です。←
⑤`getUsers`関数の処理内容に変更を加えているので、関数Docのコメントも変更してください←
⑥`createUser`関数の登録日がUTC基準になっているので、9時間ズレを修正してください。←


同じ内容の指摘になるので④⑤⑥は言及しません。

**「ここも同様」とか「ここも修正して」みたいなことも敢えて言及しません。**
心を鬼にして「水平調査をしてください」という抽象的なメッセージを伝えます。

## 作成者に水平調査を求める理由

- 作成者に確認者の意図が定着する
嫌でも確認者から聞いた意図を頭に置きながら成果物を網羅的に見直す作業が発生する為です。

- 作成者に水平調査の意識が根付く
「言われたところだけ直せばいい」という考え方から、「他にも同様の問題が起こっていないか」と一考する習慣が身に付きます。

- 指摘事項がコンパクトになる
ただ指摘事項は少なくなりますが、再提出内容に対応漏れが出てくることも多々あります。
水平調査が習慣化するまで作成者、確認者とも時間が掛かると思います。

## 補足

敢えてコメントしないとは言うものの、本当に急いでいる時やお客様に迷惑をかけられない時はスピード感を求めて細かく指摘を送ることもあります。
CDK Pipelines管理下のStackをローカルから直接デプロイする

[AWS CDK Tipsシリーズ](https://tmokmss.hatenablog.com/entry/aws_cdk_tips)の記事。

## はじめに
CDK Pipelinesで管理されたStackを、Pipelineを介さずにローカル環境などから直にデプロイしたいことがある。
本記事ではこの方法を紹介する。

## 方法
次のように `cdk deploy` コマンドを実行すれば良い。

```sh
npx cdk deploy <PipelineがあるStackの名前>/<Stageの名前>/<Stackの名前>
```

例えば以下のコードを考える。

```ts
import * as cdk from '@aws-cdk/core';
import * as pipelines from 'aws-cdk-lib/aws-pipelines';
const app = new cdk.App();

// CDK Pipelinesを定義するStack
const pipelineStack = new cdk.Stack(app, 'PipelineStack');
const pipeline = new pipelines.CodePipeline(pipelineStack, 'Pipeline', {
    synth, //put some step here,
});

// CDK PipelinesでデプロイするStage
const devStage = new cdk.Stage(pipelineStack, "DevStage");
// Stageの中のStack
new cdk.Stack(devStage, "SomeStack");

pipeline.addStage(devStage);
```

この時`DevStage` の中の `SomeStack` は通常 CDK Pipelines からデプロイされる。
一方で、以下のコマンドにより、ローカル環境からも直接 Stack をデプロイすることができる。

```sh
npx cdk deploy PipelineStack/DevStage/SomeStack
```

なお、もちろんコマンドを実行する環境に適切な権限が必要なことには注意！

また、StageのConstruct tree内の位置によっては、上記のコマンドではうまく行かない場合がある。
例えば、下記のようにStageをStack直下ではなく他のConstructの下に配置した場合を考える。

```ts
...
// pipelineStack直下に何らかのConstructがあると仮定する
const someConstruct = new Construct(pipelineStack, "SomeConstruct");
// devStageのscopeをSomeConstructにしたとする
const devStage = new cdk.Stage(someConstruct, "DevStage");
...
```

この場合、デプロイコマンドは下記のようにする必要がある。


```sh
npx cdk deploy PipelineStack/SomeConstruct/DevStage/SomeStack
```

[Construct treeについての詳細は話はこちら！](https://github.com/aws/aws-cdk/blob/master/design/construct-tree.md)

## 但し書き
CI/CD パイプラインで管理されたものをローカルからいじると、ややこしい考慮事項が増えそう。
最後の手段にとどめておいたほうが良いと思う。
CDKでは稀に、Stack間の依存関係を無視して特定のStackだけをデプロイしたい状況が発生するので、そのようなときに使えるかも。 ([参考: `--exclusively` を付ける](https://github.com/aws/aws-cdk/issues/6215#issuecomment-590064979))


## その他
この方法は[ドキュメント](https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.pipelines-readme.html)には記載されていない気がするが、[ここで見つけた](https://github.com/aws/aws-cdk/discussions/18983)。
[cdk.dev](https://cdk.dev/)のSlackチャンネルに入っているとこういった情報を見つけやすくなるので、良い！
LINQでの左部外部結合とon句の条件が複数ある場合の結合方法

## やりたいこと
仕事でＣ＃を使う機会があり、LINQの少し沼にはまったのでメモとして残しておきます。
C#とLINQを使うのは初めですので間違いがございましたらご指摘をお願い申し上げます。

以下のような左部外部結合のSQLをLINQでも書きたい。
### SQL

```
SELECT
      M2010.STOCKCD
    , M2010.STOCKNM
    , D5020.STOCKQTY
    , MS080.UNITNM 
FROM
    M2010
    LEFT OUTER JOIN D5020 
        ON M2010.STOCKCD = D5020.STOCKCD
         AND D5020.HMNO = 'aa'
         AND d5020.LOTNO =  'bb' 
    LEFT JOIN M0010 
        ON D5020.HMNO = M0010.HMNO 
    LEFT JOIN MS080 
        ON M0010.UNIT = MS080.UNIT
WHERE M2010.STOCKCD = 'cc'
```



## 手順
### 1.左部外部結合
https://qiita.com/H-Takayama/items/0e0a962f9ebc59547e4d
こちらの記事で左部外部結合のやり方が説明されています。

### 2.on句に複数条件の書き方
```
on new
{
    メインテーブルカラム
　　＊今回は変数もここに入れています
} equals new
{
　　結合するテーブルのカラム
}
``` 
この構文の後に外部結合のinto〜from句を記述する



### 二つの構文を組み合わせたLINQ

```
var result = (from m2010 in db.M2010
              join d5020 in db.D5020 on
              new
              {
                  m2010.STOCKCD,
                  hmno,（上記SQLの’aa’）
                  lotno（上記SQLの’bb’）
              }
              equals new
              {
                  d5020.STOCKCD,
                  d5020.HMNO,
                  d5020.LOTNO
              } into d5020null
              from subd5020 in d5020null.DefaultIfEmpty()
              join m0010 in db.M0010 on subd5020.HMNO equals m0010.HMNO into m0010null
              from subm0010 in m0010null.DefaultIfEmpty()
              join ms080 in db.MS080 on subm0010.UNIT equals ms080.UNIT into ms080null
              from subms080 in ms080null.DefaultIfEmpty()
              where m2010.STOCKCD == ex_storage（上記SQLの’cc’）
              select new
              {
                  m2010.STOCKCD,
                  m2010.STOCKNM,
                  subd5020.STOCKQTY,
                  subms080.UNITNM
              });
```

## エラー:「join句のいずれかの式の型が正しくありません。'GroupJoin'の呼び出しで型を推論できませんでした。」
上記のLINQを実行してみたところエラーが発生。

### エラーの原因
on句の複数条件の箇所で比較条件の名前が違うので比較できずにエラーが発生。
なので条件を変数に入れて比較対象の名前を同じにして再び実行。
（http://piyopiyocs.blog115.fc2.com/blog-entry-72.html　を参照）


### エラー修正後　LINQ
```
var result = (from m2010 in db.M2010
              join d5020 in db.D5020 on
              new
              {
                  a = m2010.STOCKCD,
                  b = hmno,
                  c = lotno
              }
              equals new
              {
                  a = d5020.STOCKCD,
                  b = d5020.HMNO,
                  c = d5020.LOTNO
              } into d5020null
              from subd5020 in d5020null.DefaultIfEmpty()
              join m0010 in db.M0010 on subd5020.HMNO equals m0010.HMNO into m0010null
              from subm0010 in m0010null.DefaultIfEmpty()
              join ms080 in db.MS080 on subm0010.UNIT equals ms080.UNIT into ms080null
              from subms080 in ms080null.DefaultIfEmpty()
              where m2010.STOCKCD == ex_storage
              select new
              {
                  m2010.STOCKCD,
                  m2010.STOCKNM,
                  subd5020.STOCKQTY,
                  subms080.UNITNM
              });
```
gem bullet による警告が消えても必ずログを確認する必要がある

初学者向けに書きました。
これはまだ自分が初学者だった頃のメモです。もはや何番煎じかわからない内容ですが、何か記事を書きたかったので書きました。
間違い等ありましたら申し訳ございません。ボコボコにコメントしてやってください。すぐ直します。

# 伝えたいこと
- bulletを使用していても検知されないN+1問題がある。
    - bulletによる警告が出ていないからといって満足して終わらないように。ちゃんとチェックしよう。
- N+1問題が解決していても本当にそれが最適な処理になっているのか吟味しよう。

# bullet とは
- N+1問題を検知するgemです。詳しくは[公式](https://github.com/flyerhzm/bullet)で。

# 問題①：検知できないN+1問題
例えば、以下のコードはN＋1問題を発生させますが、この問題はbulletでは検知できません。

```ruby
class Post < ApplicationRecord
  has_many :comments
end

@posts = Post.preload(:comments).all

@posts.each do |post|
  post.comments.count
end
```

これはcomments自体はキャッシュしているので、`post.comments`とした時にSQLが実行されることはありません。
しかし、この`count`メソッド(ActiveRecord::Associations::CollectionProxy)は、実行の度にSQLを必ず実行するためN＋1問題が発生します。仕様は[ここ](https://api.rubyonrails.org/classes/ActiveRecord/Associations/CollectionProxy.html#method-i-count)。

この`count`メソッドような、**モデルのアソシエーションの自動ロードの仕様に関わらず、そのメソッド自身の仕様によるクエリによって発生するN＋1問題はbulletでは検知できません**。ActiveRecord::FinderMethodsのfindメソッド等を使用した場合でも同様の問題が発生します。

なので、bulletの警告が無くなったとしてもちゃんとログを確認するようにしましょう。

# 問題②：アソシエーションの「キャッシュの方法」については言及しない
例えば、以下のコードの時、N+1問題は発生せず問題無いように見えます。

```ruby
class Post < ApplicationRecord
  belongs_to :user
end

@posts = Post.preload(:user).all

@posts.each do |post|
  p post.user
end
```

ところがこれは、完璧ではありません。
基本的にこのような場合は、`preload`ではなく`eager_load`をすべきです。例外は除きます。
この例のような処理だけではどっちでキャッシュしても大差ないですが、大規模なデータを処理する場合は処理に係るコストがまったく異なります。
詳しい説明はこの記事の本旨と異なるので行いませんが、簡単に言うと以下の２点です。

- 外部結合と内部結合の違い
- 実行クエリ数の違い


**こういった問題はbulletでは警告を受けません。あくまで`eager loading`に対して警告をするだけです。**
警告内容は、`includes@メソッドを使用するかしないかで抽象的に指摘しているに過ぎません。

なので、`eager_load`と`preload`の仕様を理解することを初めに、ログの実行時間や全体の処理内容を考慮しつつ最適化を行うよう気をつけてください。bulletによる警告が無くなったからといって油断しては行けません。

# まとめ
- bulletを使用していても検知されないN+1問題がある。
  - モデルのアソシエーションの自動ロードの仕様に関わらず、そのメソッド自身の仕様によるクエリによって発生するN＋1問題はbulletでは検知できない。
  - `eager loading`の方法については言及されない。
- ActiveRecordに係るメソッドの仕様は正確に捉えられるようにしよう。
CPUタイマ性能評価(C++)

# 概要
CPUタイマは，CPUが処理した時間(CPU時間)のことを指している．
複数個のCPUを用いて実行した場合は，それぞれのCPUの実行時間を加算した時間がCPU時間となる．
そのため，CPU時間は実際の経過時間より長くなることがある．
経過時間とCPU時間を用いることで並列処理の効率を評価できるので便利である．

CPUタイマにも測定可能粒度があるので使用する上ではCPUタイマの性能を知っておく必要がある．
そこで，C++のCPUタイマに対してタイマの評価指標である分解能とレイテンシーを測定してみた．

:::note info
**分解能**
タイマが測定可能な時間粒度を指す．
:::
:::note info
**レイテンシー**
タイマの測定遅延時間を指す．
:::

# CPU時間

CPU時間にはシステムCPU時間とユーザCPU時間の二種類ある．

:::note info
**ユーザCPU時間**
プログラムの実行でCPUに費やされた時間
:::

:::note info
**システムCPU時間**
システムコール(ファイルI/O, ディスクI/O, メモリ管理等)の実行でCPUに費やされた時間
:::

スリープしている時間と入力待ちの時間はCPUを使用していないことになるのでCPU時間としてカウントされない．

## CPU使用率

CPU時間と経過時間を用いてCPU使用率を算出する方法(boostのcpu_timer)がある．

```math
CPU使用率=100.000*(\frac{ユーザCPU時間+システムCPU時間}{経過時間})
```

複数のCPUで並列処理を行っている場合は100%を超えることがある．

# 測定

C++で提供されているCPUタイマに対して分解能とレイテンシーを測定した．
測定対象のCPUタイマはユーザCPU時間とシステムCPU時間を計測できるCPUタイマに限定する．
ただし，レイテンシーの測定は経過時間系タイマで測定する必要があるのでchronoを使用した．

:::note info
分解能とレイテンシーの測定方法は参考資料[2]の3章に記載されている方法を参考にした．
:::

**使用プログラム**

https://github.com/Ocean00000/CPUTimeEvaluation

**分解能とレイテンシーの測定方法**

https://qiita.com/Ocean0000/items/19d9a81a2af12fe890e0

## Windows

WindowsのVisual Studio 2020 communityを用いてGetProcessTimesの性能評価を行った．

:::note info
GetProcessTimesはboostのcpu_timerの実装で使用されている．
:::

* 開発環境：
    * Visual Studio 2022 community
* PCスペック：
    * OS:Windows10 Pro(64bit)
    * CPU:Intel(R)Core(TM) i9-109000X 3.70Ghz(物理コア数：10, 論理コア数：20)
    * メインメモリ:128GB

### 分解能

分解能は15,625,000ナノ秒でブレることがなく安定していた．

タイマ |単位| 分解能
---|---|---
GetProcessTimes(ユーザCPU時間) |100ナノ秒| 15,625,000ナノ秒
GetProcessTimes(システムCPU時間) |100ナノ秒| 15,625,000ナノ秒

### レイテンシー

タイマ | レイテンシー
---|---
GetProcessTimes | 135.918ナノ秒

### 結論

分解能とレイテンシーを考慮するとWindowsのCPUタイマは秒単位なら問題なく使用できるのではないかと考えている．

## Mac

MacのXcodeを用いてtimesの性能評価を行った．

:::note info
timesはboostのcpu_timerの実装で使用されている．
:::


* 開発環境：
    * Xcode 13.2.1
* PCスペック：
    * OS:macOS Montery(Mac mini(M1 2020))
    * CPU:Apple M1
    * メインメモリ:16GB

### 分解能

分解能は1.000マイクロ秒でブレることがなく安定していた．

タイマ | 単位 |分解能
---|---|---
times(ユーザCPU時間) |マイクロ秒| 1.000マイクロ秒
times(システムCPU時間) |マイクロ秒| 1.000マイクロ秒

### レイテンシー

タイマ | レイテンシー
---|---
times | 397.201ナノ秒

### 結論

分解能とレイテンシーを考慮すると，MacのCPUタイマはミリ秒単位なら問題なく使用できるではないかと考えている．
レイテンシーがひどくなければマイクロ秒でも問題なく使用できていたかもしれない．

# 参考資料
1. boostjp,https://boostjp.github.io/
1. boost github, https://github.com/boostorg
1. CPU Timer(boost), https://www.boost.org/doc/libs/1_54_0/libs/timer/doc/cpu_timers.html
1. Optimized C++ ―最適化、高速化のためのプログラミングテクニック, Kurt Guntheroth (著), 島 敏博 (監修), 黒川 利明 (翻訳), オライリージャパン, 2017年2月.
1. 性能評価プログラム, https://github.com/Ocean00000/CPUTimeEvaluation
freezed の toJson で Firebase の createdAt と updatedAt を実現する

# やりたいこと
freezed (というか内部の json_serializable) では ``toJson``, ``fromJson`` 時に変換する関数を定義することが出来る。（以下参照）

https://qiita.com/GleamingCake/items/97a5e1460c7c9f51547c

それを使って、プロパティにつけるだけで Firebase での ``updatedAt（書き込み時にタイムスタンプ更新）`` と ``createdAt（作成時のみタイムスタンプ）`` の挙動を実現したい。

```dart
@freezed
class Group with _$Group {
  const factory Group({
    // 作成日
    @CreatedAtField() DateTime? createdAt,
    // 更新日
    @UpdatedAtField() DateTime? updatedAt,
    // 名前
    required String name,
  }) = _Group;

  const Group._();

  factory Group.fromJson(Map<String, dynamic> json) => _$GroupFromJson(json);
}

```

# 要件
toJson 時に以下のような出力になればいい
* ``CreatedAtField`` はその値を見て
    * ``null`` の時は ``FieldValue.serverTimestamp()`` を出力（初回時のみ書き込み）
    * ``DateTime`` の時はその値を ``Timestamp`` に変換して出力（初回以降はその値のまま）
* ``UpdatedAtField`` は常に ``FieldValue.serverTimestamp()`` を出力（毎回書き込み）

# 実装
以下のようなアノテーションを作れば OK
```dart
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:freezed_annotation/freezed_annotation.dart';

// プロパティにつけると変換時、そのプロパティが null なら FieldValue.serverTimestamp() に変換する
// これにより、クライアント側で作成したときのみサーバー側で時間が書き込まれるため、createdAt の挙動になる
class CreatedAtField implements JsonConverter<DateTime?, dynamic> {
  const CreatedAtField();

  @override
  DateTime? fromJson(dynamic timestamp) {
    timestamp as Timestamp?;
    return timestamp?.toDate();
  }

  // nullの時は toJson 時 FieldValue.serverTimestamp() を返すことで、createdAt の挙動になる
  @override
  dynamic toJson(DateTime? dateTime) {
    if (dateTime == null) return FieldValue.serverTimestamp();
    return dateTime;
  }
}

// プロパティにつけると変換時、必ず FieldValue.serverTimestamp() に変換される
// これにより、updatedAt の挙動になる
class UpdatedAtField implements JsonConverter<DateTime?, dynamic> {
  const UpdatedAtField();

  @override
  DateTime? fromJson(dynamic timestamp) {
    timestamp as Timestamp?;
    return timestamp?.toDate();
  }

  @override
  FieldValue toJson(DateTime? date) {
    return FieldValue.serverTimestamp();
  }
}
```

これで以下のように使えるようになりました！
```dart
// 作成日
@CreatedAtField() DateTime? createdAt,
// 更新日
@UpdatedAtField() DateTime? updatedAt,
```
【メモ】プログラミング初学者がLLVMとJITコンパイルについてまとめてみた


# プログラミング初学者がLLVMとJITコンパイルについてまとめてみた

juliaでプログラミングを学び始めた頃、よく出てくるLLVMとかJITコンパイルの概念や違いがどうも腑に落ちなかった経験があった。そこで、かなり表層的な知識になってしまいますが、ここにメモとしてまとめておく。
間違っていたら、ご指摘いただけると幸いです。


# 目次
コンパイラ、JITコンパイル、LLVMの順で書く。

## コンパイラの仕事

- 機械語への翻訳

- 最適な機械語を出す

- 有限のリソースの管理


## JITコンパイル

JITコンパイルは、コンパイルが行われるタイミングのことである。JITとは、Just In Timeコンパイルの略で、関数が実行されるそのタイミングでコンパイルが行われるように設計されている。

例えば、以下のjuliaコードの場合を例にする。

```julia
function a(x::Int)
   2*x
end

function b(x::Int)
   x + 2
end

function c(x::Int, y::Int)
   a(x) - b(y)　#関数a, bがcの中で呼びだれた時に初めてコンパイルが行われる
end
```

関数の単位で実行されるタイミングでコンパイルが行われる。これをJITコンパイルという。上の例でいうと、関数a, bがcの中で呼びだされた時に初めてコンパイルが行われる
また、bという関数を少しだけ修正した場合、再びcを実行するときは変更がないaはそのままキャッシュから呼び出し、bの部分だけコンパイルが行われることになる。

なお、C言語は，test.cに含まれる関数a, b, cが含まれるmain文を全部実行前にコンパイルしてtest.oを作る。その際に、オプションが合って，許される限りの時間でコードの最適化が行われるようである。そのメリットはプログラム全体のソースコードを最適化できることにある。

一方、Pythonは，上から1行ずつ機械語に翻訳していくインタプリンティングが行われる。コンパイルが不要な分、実行速度が遅くなる。


## LLVM
JITコンパイルとは、コンパイルがいつ行われるのかのタイミングの話であった。
それではコンパイルを誰が行うのであろうか。例えばjuliaでは、LLVMが基本的にコンパイラに相当する。

LLVMは、言語やハードウェアに非依存な共通の言語に移し替える。従来、コンパイラは複数あるが故にコンパイラごとにビルドしたものを複数用意しなければならなかったが、LLVMを使うことでビルドが一回で済むようになったことは画期的であった。

Juliaの関数が実際にどのように変換されているかは、llvm_codeを実行すると表示されるみたいです。

```julia
# 実行
code_llvm(c, (Int, Int))
```

結果

```
define i64 @julia_c_247(i64 signext %0, i64 signext %1) #0 {
top:
;  @ REPL[19]:2 within `c`
; ┌ @ REPL[16]:2 within `a`
; │┌ @ int.jl:88 within `*`
    %2 = shl i64 %0, 1
; └└
; ┌ @ REPL[17]:2 within `b`
; │┌ @ int.jl:87 within `+`
    %.neg1 = sub i64 -2, %1
; └└
; ┌ @ int.jl:86 within `-`
   %3 = add i64 %.neg1, %2
; └
  ret i64 %3
}
```




# 参考

https://qiita.com/Anko_9801/items/df4475fecbddd0d91ccc
【メモ】MacM1Proの性能について調べてみました。 

## MacM1Proの性能について調べてみました
数値計算をやっている身としてパソコンのピーク性能が知っておいた方が良さそうだなと思い、普段研究に使っているパソコンの性能について調べた。ちなみにアーキテクチャなど初心者なので、ほとんどは参考記事を参照しております。

## CPU

>
Model Name:	MacBook Pro
Chip:	Apple M1 Pro
Total Number of Cores:	10 (8 performance and 2 efficiency)
Memory:	32 GB

- 5ナノメートルのプロセステクノロジーを活用

- 337億個のトランジスタを搭載

- 8つの高性能コアと2つの高効率コアから成り、M1よりも最大70パーセント高速

- デバイス上の機械学習を高速化し、カメラのパフォーマンスを向上させる16コアNeural Engineを搭載


### 演算性能
- 演算性能は、最大で5.2TFLOPS。つまり、一秒間で最大5.2兆回浮動小数点演算が実行できるみたい

- CPUコアのピーク時のクロック数は3228MHz

- 一方、高効率コアのピーククロック周波数は2064MHzで



### メモリ性能
- メモリ性能は、「一秒間あたりにどれだけのデータを転送できるか」を示す。

- M1 Proでは最大200GB/sのメモリ帯域幅を提供し、最大32GBのユニファイドメモリに対応


## GPU

>
Chipset Model:	Apple M1 Pro
Type:	GPU
Total Number of Cores:	14


## 容量

>
Capacity:	1 TB (1,000,555,581,440 bytes)
Volumes:
disk0s1:
  Capacity:	524.3 MB (524,288,000 bytes)
  BSD Name:	disk0s1
  Content:	Apple_APFS_ISC
disk0s2:
  Capacity:	994.66 GB (994,662,584,320 bytes)
  BSD Name:	disk0s2
  Content:	Apple_APFS
disk0s3:
  Capacity:	5.37 GB (5,368,664,064 bytes)
  BSD Name:	disk0s3
  Content:	Apple_APFS_Recovery


## AMD EPYC 7702Pの性能
比較としてAMD EPYC 7702Pの性能を載せておきます。

CPUコア数: 64
スレッド数:128
最大ブースト・クロック:最大 3.35GHz
基本クロック:2.0GHz
L3キャッシュ合計: 256MB
https://www.amd.com/ja/products/cpu/amd-epyc-7702p

## 参考
https://www.apple.com/jp/newsroom/2021/10/introducing-m1-pro-and-m1-max-the-most-powerful-chips-apple-has-ever-built/


https://gigazine.net/news/20211026-apple-m1-pro-max/）


https://gigazine.net/news/20211026-apple-m1-pro-max/
【メモ】docstringで\を使ったときのエラー

# docstringで\を使ったときのエラー

コンピュータにとって\があると、\nで改行を表すように、\自身が特別な意味を持っている。

人間が\を使いたいときは、\に対して、\をすると、元々の\自身を表す。

## NG

以下の例では、docstringで、\を使っているためエラーが出る。

```Julia

function gloc_iv(solver::IPTSolver, sigma_iv::Vector{Complex{Float64}})

    """
    Compute G_loc(iv) from Sigma_iv

    G_loc(iv) \simeq \sum_q w_q rho(x_q)/(iv - w_q - \Sigma(iv))
    """


end

```

エラー文章：

syntax: invalid escape sequence


## OK

人間が\を使いたいときは、\に対して、\をする。


```Julia

function gloc_iv(solver::IPTSolver, sigma_iv::Vector{Complex{Float64}})

    """
    Compute G_loc(iv) from Sigma_iv

    G_loc(iv) \\simeq \sum_q w_q rho(x_q)/(iv - w_q - \\Sigma(iv))
    """


end

```
オープンソースを改変する場合の著作権表示について

# はじめに
BSD ライセンスで公開されているオープンソースを改変して公開する機会があったのだが、その際の著作権表示をどのようにすれば良いのか、調べたことを備忘録として残しておく。

なお、この記事では BSD ライセンスを例に挙げるが、原著作者の著作権表示が必要な、他のライセンスを適用する場合も参考になるはず。



# 改変した場合の著作権表示の書き方
2 条項 BSD ライセンスの利用条件一覧を見ると、次のように書かれている。

>ソースコードを再頒布する場合、上記の著作権表示、本条件一覧、および下記免責条項を含めること。

_引用元: https://licenses.opensource.jp/BSD-2-Clause/BSD-2-Clause.html_

改変せずに公開または再頒布するのであれば、著作権表示の箇所は原著作者の名前だけを書けば良い。しかし、改変した場合はどうすれば良いのだろうか。

公開されているソースコードに記載されているライセンスの条文をそのまま記載すると、原著作者のみが記載されることになる。では、改変した場合の自分の著作権はどうなるのだろうか。まさか、改変した人には著作権がないのだろうか。

もちろんそんなことはなかった。これに関して、わかりやすく解説されている記事を見つけた。

[ライセンス(2) - allbsd.org](https://www.allbsd.org/~hrs/blog/2017-01-17-p0004056-2.html)

改変した場合は、著作権表示 (Copyright) の行に自分の名前を追加すれば良いらしい。いくつかのソフトウェアの例を見ると、著作権表示の行が 2 行になっており、それぞれの著作者の名前が書かれている。このような表記方法でも良いらしい。

まとめると次の通り。以下は例であり、正しい表記方法については各ライセンスの条文を参考にすること。

```
Copyright <年> <原著作権者>, <他の改変者 (存在する場合)>, <あなたの名前>
```

または

```
Copyright <年> <原著作権者>
Copyright <年> <他の改変者 (存在する場合)>
Copyright <年> <あなたの名前>
```

ちなみに、原著作者が適用しているライセンスとは異なるライセンスを適用して改変したい場合は、OpenSSH の例のように、原著作者が記載したライセンス条文に続く形で、自分が適用したいライセンス条文を記載すれば良いらしい。

ただし、これはライセンスが変更できる場合に限る。たとえば、GPL ライセンスなどは、改変した場合でも、改変した部分、改変していない部分を含め元のライセンスと同じライセンスにする必要がある点には注意すること。
Python3: Cognito から Access Token を取得

AWS の Cognito から JWT Access Token を取得する方法です。
AuthFlow は ADMIN_USER_PASSWORD_AUTH です。
 （以前は、ADMIN_NO_SRP_AUTH と呼ばれていました。）
次のページを参考にしました。
[PythonでAWS Cognito認証](https://qiita.com/jp_ibis/items/4fffb3c924504f0ce6fb)

```py:get_token.py
#! /usr/bin/python
# -*- coding: utf-8 -*-
#
#	get_token.py
#
#					Feb/27/2022
#
# ------------------------------------------------------------------
import	sys
import	os
import	boto3
from dotenv import load_dotenv
# ------------------------------------------------------------------
def cognito_auth(parameter):
	# 認証開始
	try:
		aws_client = boto3.client('cognito-idp')

		aws_result = aws_client.admin_initiate_auth(
			UserPoolId = parameter["user_pool_id"],
			ClientId = parameter["user_pool_client_id"],
			AuthFlow = "ADMIN_USER_PASSWORD_AUTH",
#			AuthFlow = "USER_SRP_AUTH",
			AuthParameters = {
				"USERNAME": parameter["usr"],
				"PASSWORD": parameter["password"],
			}
		)

		# 認証完了
		sys.stderr.write("*** success ***\n")
		return aws_result

	except Exception as ee:
		# 認証失敗
		sys.stderr.write("*** error ***\n")
		sys.stderr.write(str(ee) + "\n")
#
		return None


# ------------------------------------------------------------------
sys.stderr.write("*** 開始 ***\n")
#
dotenv_path = '.env'
load_dotenv(dotenv_path)
parameter={}
parameter["user_pool_id"]=os.environ.get("USER_POOL_ID")
parameter["user_pool_client_id"]=os.environ.get("USER_POOL_CLIENT_ID")
parameter["usr"]=os.environ.get("USR")
parameter["password"]=os.environ.get("PASSWORD")
print(parameter["usr"])
print(parameter["password"])
result = cognito_auth(parameter)
print(result)
# print(result["AuthenticationResult"]["IdToken"])
sys.stderr.write("*** 終了 ***\n")
# ------------------------------------------------------------------
```

```text:.env
USER_POOL_ID="ap-northeast-1_510w*****"
USER_POOL_CLIENT_ID="818aso07e5pd8dj*******"
USR="aaa@example.com"
PASSWORD="secret123"
```
Amazon Linux 2 と Ubuntu コマンドメモ

走り書きしているので、読みづらさについてはご了承ください。
時間があるときに整理します。

どうも、くっそざこエンジニアけいしーです。
さて書いていきますね。

### Ubuntu バージョン確認
```
cat /etc/issue
```

詳細情報の確認

```
cat /etc/lsb-release
```

### Amazon Linux 2 バージョン確認

```
cat /etc/system-release
```

詳細情報の確認

```
cat /etc/os-release
```

※管理者権限で実行必須

```
cat /etc/image-id
```

### ホスト名の変更

```
passwd ec2-user
```

```
passwd ubuntu
```

###ネットワーク設定の確認【Amazon Linux 2】

```
cat /etc/sysconfig/network-scripts/ifcfg-eth0
```

参考：

https://dev.classmethod.jp/articles/ec2-linux-network-conf-tips/


### ネットワーク設定の確認【共通】

```
ifconfig
```

###ネットワーク設定の確認【Ubuntu】
前提必須

```
apt install net-tools
```
40 代おっさん GASのDateオブジェクトについて学ぶ

## 本記事ついて

本記事は プログラミング初学者の私が学習していく中でわからない単語や概要をなるべくわかりやすい様にまとめたものです。
もし誤りなどありましたらコメントにてお知らせいただけるとありがたいです。

## Dateオブジェクト

日付、時刻を取り扱うオブジェクト

構文

```javascript
new Date()
```

指定の日時でインスタンスを生成するのであれば

```javascript
new Date(年, 月, 日[, 時, 分, 秒, ミリ秒])
```

「set 05 2022 23:20:15」といった英文形式を指定することも可能

```javascript
new Date(日時文字列)
```

1970/01/01 00:00:00からの経過ミリ秒をタイムスタンプ値と言う
それを指定することもできる

```javascript
new Date(タイムスタンプ値)
```

Dateオブジェクトに変更を加える櫃王があり、かつ、もとのDateオブジェクトものこしておきたいときには

```javascript
new Date(Dateオブジェクト)
```

お試し

```javascript
function toshiki() {
  console.log(new Date());
  console.log(new Date(2022, 2, 18, 11, 30, 15 ));
  console.log(new Date('2022/02/18 11:30:15'));
  console.log(new Date(1645151415));

  const d = new Date();
  console.log(new Date(d));
}
```

|分類|メンバー|戻り値|説明|
|----|--------|------|----|
|メソッド|getTullYear()|Integer|年（4桁までの年）を返す|
|メソッド|getMonth()|Integer|月（0-11）を返す|
|メソッド|getDate()|Integer|日（1-31）を返す|
|メソッド|getDay()|Integer|曜日（0-6）を返す|
|メソッド|getHours()|Integer|時（0-23）を返す|
|メソッド|getMinutes()|Integer|分（0-59）を返す|
|メソッド|getSeconds()|Integer|秒（0-59）を返す|
|メソッド|getMilliseconds()|Integer|ミリ秒（0-999）を返す|
|メソッド|getTime()|Integer|1970年1月1日00:00:00からの経過ミリ秒を返す|
|メソッド|getTimezoneOffset()|Integer|現地の時刻と協定世界時刻（UTC)との差を返す|
|メソッド|setFullYear(y)|Integer|年をy(4桁までの年)に設定する|
|メソッド|setMonth(m)|Integer|月をm(0-11)に設定する|
|メソッド|setDate(d)|Integer|日をd(1-31)に設定する|
|メソッド|setHours(h)|Integer|時をh(0-23)に設定する|
|メソッド|setMinutes(m)|Integer|分をm(0-59)に設定する|
|メソッド|setSeconds(s)|Integer|秒をs(0-59)に設定する|
|メソッド|setMilliseconds(ms)|Integer|ミリ秒をms(0-999)に設定する|
|メソッド|setTime(ts)|Integer|1970年1月1日00:00:00からの経過ミリ秒をtsに設定する|
|メソッド|toString()|String|日時を文字列に変換したものを返す|
|メソッド|toDateString()|String|日付部分を文字列に変換したものを返す|
|メソッド|toTimeString()|String|時刻部分を文字列に変換したものを返す|
|メソッド|toJSON()|String|日時をJSON文字列に変換したものを返す|

お試し

```javascript
function toshiki2() {
  const d = new Date(2022, 2, 19, 14, 38, 15, 555);

  console.log(d.getFullYear());
  console.log(d.getMonth());
  console.log(d.getDate());
  console.log(d.getDay());
  console.log(d.getHours());
  console.log(d.getMinutes());
  console.log(d.getSeconds());
  console.log(d.getMilliseconds());
  console.log(d.getTime());
  console.log(d.getTimezoneOffset());

  d.setFullYear(2022);
  d.setMonth(0);
  d.setDate(1);
  d.setHours(1);
  d.setMinutes(11);
  d.setSeconds(11);
  d.setMilliseconds(111);

  console.log(d.toString());
  console.log(d.toDateString());
  console.log(d.toTimeString());
  console.log(d.toJSON());
}
```

## 日時の演算と複製

Dateオブジェクトには日時の演算をするメソッドは用意されていない。
要素を取り出し計算を行う。

```javascript
function toshiki2() {
  const start = new Date('2022/2/19 14:00');
  const end   = new Date(start);

  end.setMinutes(start.getMinutes() + 120);
  console.log(start);
  console.log(end);
}
```

## 参考資料

<https://www.amazon.co.jp/s?k=google+apps+script+%E5%AE%8C%E5%85%A8%E5%85%A5%E9%96%80&adgrpid=110264232688&gclid=CjwKCAiA9aKQBhBREiwAyGP5lSl7AJJLCvOEHb4wQgMlyqW1fll5X8GDTT_Rkd1_soUAyIPMXQr26hoClHEQAvD_BwE&hvadid=553833563682&hvdev=c&hvlocphy=1009076&hvnetw=g&hvqmt=b&hvrand=4378489642044417389&hvtargid=kwd-594191211348&hydadcr=4106_13159878&jp-ad-ap=0&tag=googhydr-22&ref=pd_sl_2x1owglv0s_b_p52>
NuxtJS 雛型プロジェクト作成

### まえがき
公式ドキュメントを毎回見に行かないと忘れてしまうので、この際自分用に手順としてまとめておく。
https://nuxtjs.org/ja/docs/get-started/installation/

---
### 作業手順

#### o1. プロジェクト作成

```
yarn create nuxt-app <project-name>
```

各種設定を選択する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/460227bd-e70d-9b20-c53d-98bdbb244f29.png)

#### o2. 不要なフォルダ/ファイルを一旦消す
・layouts配下のファイル
・components配下のファイル
・page配下のファイル
・static配下の画像ファイル
・storeフォルダ

#### o3. 依存関係追加

```
yarn add axios
yarn add nuxt-property-decorator
```


#### o4. レイアウト/トップ画面を追加

レイアウト layouts/default.vue

```typescript
<template>
  <v-app>
    <v-app-bar absolute> </v-app-bar>
    <v-main class="mt-15">
      <v-container>
        <Nuxt />
      </v-container>
    </v-main>
  </v-app>
</template>
<script lang="ts">
import { Vue, Component } from "nuxt-property-decorator";
@Component
export default class Default extends Vue {}
</script>
```

トップ画面 pages/index.vue

```typescript
<template>
  <v-container>
    <v-row>
      <h1>HELLO NUXT APP</h1>
    </v-row>
  </v-container>
</template>
<script lang="ts">
import { Vue, Component } from "nuxt-property-decorator";
@Component
export default class Top extends Vue {}
</script>

```

yarn devで起動 http://localhost:3000 にアクセスするとこんな感じ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/653955/b302ed34-7a65-1201-5b6d-ac9aea7398c6.png)


### o5. AxiosFactoryクラス
・シングルトンでaxiosオブジェクトを管理するためのFactoryクラス

```typescript
import axios, { AxiosError } from "axios";

// MEMO：「import axios from "axios"」で取得したaxiosにdefaults.baseURL等の設定をまず行う。
//       この「初期設定を行ったインスタンス」は★シングルトン★として使い回したい。
//       https://stackoverflow.com/questions/30174078/how-to-define-singleton-in-typescript
//
// ポイント：importする度に「axios」が初期化されるから、axiosはAxiosFactory内のみで生成＆管理する。
//          他クラスからこの「axios」を参照できるようにexportする。
class AxiosFactory {
  private static initialized = false;

  // MEMO：staticの理由：最終行で「AxiosFactory.axios」でstatic参照するため
  static get axios() {
    if (!this.initialized) {
      // 各APIのレスポンスに対して実施する処理
      // axios.interceptors.response.use(
      //    第１引数：正常終了時に行う処理
      //    第２引数：異常終了時に行う処理
      axios.interceptors.response.use(
        // 正常終了時...そのままレスポンスを返す
        (response) => response,
        // 異常終了時...エラーログ出力＆NuxtJSヘルパーを使ってエラー画面遷移
        // ★「$nuxt ＝＝ Context」のこと。
        // context($nuxt).error({messagge,statusCode})でNuxtエラーページに誘導できる
        // https://nuxtjs.org/docs/concepts/context-helpers/
        async (error: AxiosError) => {
          window.$nuxt.error({
            message: error.response?.data.console.error,
            statusCode: error.response?.status,
          });
          return false;
        }
      );
    }
    return axios;
  }
}

// MEMO：JS/TSでは、ゲッターを「instance.getXxxx()」と書かない。「instance.xxxx」と書く。
//       なので↓は「AxiosFactoryクラス内の"static get axios()」を呼び出している。
export default AxiosFactory.axios;

```

・JWTをAuthorizationヘッダに格納したい場合は、Factoryクラスから取得したaxiosオブジェクトに対して↓のようにセットすればいい。


```typescript
import axios from 'AxiosFactory';

axios.defaults.headers.common.Authorization = `Bearer ${token}`;
```