OmniAICreator/Qiita-1.07M huggingface dataset:
Bevy 0.8に伴う小さい変更

# 概要
先日（2022年7月30日）、RustのゲームエンジンBevyの新バージョン[Bevy 0.8][1]がリリースされた。
移行ガイド等はリンク先にあり、多くの変更があったことがわかるが、そこで触れられていないことで少し嬉しい副作用があったので記録しておく。

# 依存クレートglamのバージョンアップ
Bevyが依存している`bevy_math`0.7の依存クレートはこのようになっていた。
![スクリーンショット 2022-08-16 21.50.29.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912476/5ab033b7-fe30-a475-2488-19bb7dda164d.png)

バージョン0.8では次のようになっている。
![スクリーンショット 2022-08-16 21.51.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/912476/e4e39530-8dc1-e647-1ef5-917239e7eaee.png)

変更として、bevy_reflectというクレートが削除されているが、今回主眼に置くのはglamの依存バージョン変更である。

[glam][2]はゲームやグラフィクス向けの優れた数学ライブラリである。
Bevyはベクトルを扱う機能等をglamに依存して計算を行い、開発者もglamで定義されている機能を用いてUI構築等をできるようになっている。

主な機能を解説してくれている記事は[こちら][3]だが、ここで解説されているものの一部はglamのバージョン0.21から追加されたものであり、Bevy 0.7では使うことができなかった。
今回のバージョンアップでこれらが使用可能になった。
特にベクトルの回転を行う`.rotate()`メソッドが使用可能になったのはとても便利（このメソッド自体は微妙に分かりづらい仕様なので[ドキュメント][4]を読むことを推奨する）。

# 最後に
Bevy 0.8は0.7のリリースから3ヶ月程度しか空かなかった割に大きめの変更が多く、カメラのような根幹の機能にも手が加わっている（UIカメラが削除されている）。
その中で依存ライブラリのバージョンアップによる機能追加がされているのが興味深かったため簡単な記事として残した。
大した記事ではないが、私はまだRust自体にもBevyにも誤解がしている点が多いと思われるのでなにかおかしかったら教えていただきたい。

# 参考文献
* [Bevy 0.8][1]
* [GitHub - glam-rs][2]
* [[Rust] nannou のベクトルライブラリ glam の作り込みがやばかった][3]
* [Struct glam::f32::Vec2 #method.rotate][4]

[1]:https://bevyengine.org/news/bevy-0-8/
[2]:https://github.com/bitshifter/glam-rs
[3]:https://zenn.dev/megeton/articles/b407350ad51562
[4]:https://docs.rs/glam/latest/glam/f32/struct.Vec2.html#method.rotate
Security Hub の AWS Foundational Security Best Practices を眺めた感想

# はじめに
Security HubのルールのAWS Foundational Security Best Practicesの内容を眺める機会がありました。
https://docs.aws.amazon.com/ja_jp/securityhub/latest/userguide/securityhub-standards-fsbp-controls.html
こういうルールがあるのかぁと意識できていなかった部分もあるので、それらを中心に感じた分類分け書いてみます
なお、セキュリティ要件やシステム要件と照合し、適切に設定することが本筋ではあるので、一旦、個人の感想と思ってもらえればと思います。


# 書く内容
ルールを個人的な観点として以下3個にグループ化してみました。

- だいたい組み込んでいるんじゃないか系
- あんまり意識されていなくてやってないんじゃないか系
- へぇこれもベストプラクティスに含まれるんだ系

またこれらに分類されていないものは特段コメントはいらないかな、と思ったものです。
主に含めていないものは、暗号化、パブリックIPやパブリックセグメントへの配置、WAFの設置など、
構成で気をつけているだろうな、と思うものや暗号化要件、権限の要件によるな、と思ったものは記載していません。

# だいたいやってるんじゃないか系

## Guard Duty
- 機能を有効化する

## AWS Config
- 機能を有効化する

## CloudFront
- OAIを有効化

## ALB
- HTTP ヘッダーを削除するように設定する
- Desyncモードを防御的、最も厳格に設定する

## AutoScaling(EC2)
- LBに紐付けられている場合、ヘルスチェックはALBに設定する
    - インスタンスではなくLBをヘルスチェックに設定

## IAM
- IAMカスタムマネージドポリシーにはワイルドカードで許可してはいけない
    - "Effect": "Allow" と "Action": "Service:"でワイルドカードで許可はしない

## RDS
- 拡張モニタリングを有効化する
- ログの記録を有効化する

## ECS
- シークレットな環境変数は暗号化パラメタストアで渡す

## API Gateway
- ログを有効化する
- X-Rayトレースの有効化する

# あんまり意識されていなくてやってないんじゃないか系

## Lambda
- VPC Lambdaは複数AZに紐付ける

## CodeBuild
- 特権モードを有効化にしない

## EC2
- VPCのセキュリティグループはインバウンドとアウトバウンドはルールを含めない
    - 利用しないだけでなく、そもそもルールを入れない

## ECS
- ContainerInsightを有効化にする
    - 何をみたいか次第ですが、、、

## EFS
- バックアップを有効化する

## IAM
- IAMユーザ単体にIAMポリシーをつけない
    - GroupやRoleから継承させるのがベストプラクティスでIAMユーザにはつけない

# へぇこれもベストプラクティスに含まれるんだ系

## CloudFront
- オリジンフェイルオーバーを設定する
    - オリジン自体を冗長化しようというもので個人的には意識したことありませんでした。

## CodeBuild
- GitHubやBitBucketのURLにOAuthを利用する

## DynamoDB
- オートスケーリングを有効化する

## TGW
- 自動でアタッチメントで許可する
    - 意図しないルールをいれないため、、ですかね

## ECR
- イメージスキャンを有効化する
    - コンテナのセキュリティの意識付けるものが多くなってきたので組み込まれたんですかね(2022/7の直近のアップデートで入っています)
- ライフサイクルポリシーを設定する
    - リソース利用料の抑止することを意識しているのでしょうか

## NetworkFirewall
- NWFWのポリシーには必ずルールを1つ入れる
    - 意図としては空のルールを入れずに混乱を招かないような形

## RDS
- IAM認証を設定する
    - 設定している案件に出会ったこと無いので意外でした
- RDSのポートはデフォルトのポートを利用しない
    - デフォルトのポートを利用しないって考えたことありませんでした

## S3
- ライフサイクルポリシーを設定する
    - 必要があれば設定するイメージでリソースの過剰利用を抑えたりするために含めてるんでしょうかね

## EC2
- SSMで管理する
- コンプライアンスステータスがCOMPLIANTである
    - ここらへんも含まれるんですねぇ、パッチ適用など厳密に管理する仕組みを設計しないといけないですね

# 感想
当たり前にやっているよ、という部分もあるかと思いますが、
RDS、ECR、SG、S3のところはそうなんだぁと思いました。
特に暗号化、権限周りを意識したものが中心に組み込まれていました。
中でもCloudFrontのオリジンフェイルオーバーについてはそもそも機能自体イメージしたことありませんでしたが、
新たな情報を学ぶきっかけにもなりました。
通常の標準化のルールとして組み込むにはもちろん過剰かな、と思うところもありましたが、
当たり前にやってもいいんじゃないかな、と思う部分もあったので良い学びでした。
【Google App Script】配列内の要素を検索してインデックス番号を取得する方法

GoogleAppScriptにおける、配列の検索方法について記載します。
配列オブジェクトに対して、indexOf()メソッドを使います。

配列の先頭から検索していき、最初に見つかったインデックス番号（0始まり）を返します。
見つからなかった場合は、-1を返します。

## 例
~~~index_of.gs
// 変数arrayに配列を定義する
array = ["りんご", "みかん", "いちご", "りんご"];

// 配列に対してindexOf()メソッドを適用
console.log(array.indexOf("りんご")); // -> 0
console.log(array.indexOf("みかん")); // -> 1
console.log(array.indexOf("いちご")); // -> 2
console.log(array.indexOf("ぶどう")); // -> -1
~~~

## 参考
https://tonari-it.com/gas-array-indexof-lastindexof/
RDKitを使ってペプチドをアミノ酸単位で色付けしてみた

# はじめに
ペプチドを描画してみたけど、~~化学になじみがないため~~、アミノ酸の区切りが良く分からないためアミノ酸単位で色付けしてみたメモ。

# まずは普通に描画してみよう

まずはHELM形式で書かれたペプチドをMolに変換し描画してみよう。

``` python
mol = Chem.MolFromHELM("PEPTIDE1{C.Y.I.Q.N.C.P.L.G}$$$$")
rdCoordGen.AddCoords(mol)
d2d = rdMolDraw2D.MolDraw2DSVG(450,400)
d2d.DrawMolecule(mol)
d2d.FinishDrawing()
SVG(d2d.GetDrawingText())
```

はい、どん。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329816/f2512ccd-1ec9-8d8b-d821-dd76f6aa7f2e.png)

複数のアミノ酸がペプチド結合でつながっているのは分かるが、どうつながっているのかがいまいちわからない。そこで、アミノ酸単位でハイライトしてみよう。

# アミノ酸単位でハイライトしよう

はい、どん。解説はソースのコメントを見てくれ（手抜き）

``` python

from rdkit import Chem
from rdkit.Chem import AllChem, Draw, Descriptors, PandasTools,rdCoordGen
from rdkit.Chem.Draw import rdMolDraw2D
from IPython.display import SVG
from io import BytesIO
from PIL import Image
from matplotlib.colors import ColorConverter
import matplotlib
import random

# 指定数分の色を生成する関数
def choose_colors(num_colors):
    # matplotlib.colors.CSS4_COLORSの値だけをリストにする
    tmp = list(matplotlib.colors.CSS4_COLORS.values())
    random.shuffle(tmp)
    # 必要な数だけ先頭から取り出してreturnする
    label2color = tmp[:num_colors]
    return label2color

# とりあえず20色分の色を生成
colorList = [ColorConverter.to_rgb(color) for color in choose_colors(20)]    

# ペプチドをMolオブジェクトに変換
mol = Chem.MolFromHELM("PEPTIDE1{C.Y.I.Q.N.C.P.L.G}$$$$")
rdCoordGen.AddCoords(mol)

# molの結合を読み込み、両端の原子のPDBResidueInfoオブジェクトのResidueNumberを元にハイライトを設定

highlightBonds = []
highlightColors = {}
for bond in mol.GetBonds():
    infoBegin = bond.GetBeginAtom().GetPDBResidueInfo()
    residueNumberBegin = infoBegin.GetResidueNumber()
    nameBegin = infoBegin.GetResidueName()

    infoEnd = bond.GetEndAtom().GetPDBResidueInfo()
    residueNumberEnd = infoEnd.GetResidueNumber()
    nameEnd = infoEnd.GetResidueName()
    
    # 両端の原子が同じResidueNumberを持つ場合のみハイライト
    # (別のResidueNumberを持つ場合、ペプチド結合部であるため)
    if residueNumberBegin == residueNumberEnd:
        highlightBonds.append(bond.GetIdx())
        highlightColors[bond.GetIdx()] = colorList[residueNumberBegin]
    
view = rdMolDraw2D.MolDraw2DSVG(500, 400)
tm = rdMolDraw2D.PrepareMolForDrawing(mol)

# オプションの設定
option = view.drawOptions()
option.multipleBondOffset = 0.09
option.padding = 0.11

# 描画
view.DrawMolecule(tm, highlightAtoms=[], highlightBonds=highlightBonds, highlightBondColors=highlightColors)
view.FinishDrawing()
SVG(view.GetDrawingText())
```

はい、こんな感じ。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329816/cecdcda1-8906-129e-e377-f743b748afcb.png)

うーん、微妙。
その原因は以下の通り

- ハイライトの色のコントラストが弱い。
- ハイライトの幅が狭い。
- ハイライトが下の原子や結合と重なって分かりづらい。
- これら改善点が分かっているにもかかわらず、筆者のRDKitのハンドンリングスキルが弱い(笑)。

ということで、またいつか、リベンジしたいと思います。

# 参考
- [RDKitでの構造式描画を詳しく解説](https://future-chem.com/rdkit-2ddraw-svg/)
- [[matplotlib] 任意の数の色を使って色分けする](https://qiita.com/y629/items/afa82bd4145f940cb28b)
【Unity収益化】AdMobのアプリID設定

AdMobで取得したアプリIDをUnityアプリにを設定する方法メモ

## AdMobからアプリIDを取得する
1. AdMobにログインする
1. 画面左の`[アプリ]`からIDを取得したいアプリをクリック
1. `[アプリの設定]`をクリックするとアプリIDが表示されるので、**コピー**
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719666/406cfe03-d828-67e0-f3c3-448f4f449f08.png)

## Unityの設定
1. `[Assets]>[Google Mobile Ads]>[Settings...]`をクリック
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719666/82b36b1a-da9a-847e-ec83-1d15792f8288.png)
1. InspectorビューでコピーしたアプリIDを設定する
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1719666/b3b69407-5b66-3e14-bb97-0a9537cb85aa.png)
Alembic で create table する migration ファイルを auto gen する際にカラムの順番を調整する

# 困ったこと

下記のような model を定義した際に...

``` python3
class Base:
    created_at = Column(DateTime, default=func.now(), nullable=False)
    updated_at = Column(DateTime, default=func.now(), onupdate=func.now(), nullable=False)

BaseModel = declarative_base(metadata=metadata, cls=Base)

class Tenant(BaseModel):
    __tablename__ = "tenant"
    __table_args__ = (UniqueConstraint("name"), {})
    id = Column(String(36), primary_key=True, default=uuid.uuid4)
    name = Column(String(255), nullable=False)
```

以下のように、ベースに定義したカラムが先にくる migration ファイルが出力されてしまう

``` python3
op.create_table(
    "tenant",
    sa.Column("created_at", sa.DateTime(), nullable=False),
    sa.Column("updated_at", sa.DateTime(), nullable=False),
    sa.Column("id", sa.String(length=36), nullable=False),
    sa.Column("name", sa.String(length=255), nullable=False),
    sa.PrimaryKeyConstraint("id", name=op.f("pk_tenant")),
    sa.UniqueConstraint("name", name=op.f("uq_tenant_name")),
)
```

# 対応方法

migration ファイルを作成するタイミングでリライトする仕組みがあります。
https://alembic.sqlalchemy.org/en/latest/cookbook.html#cookbook-custom-sorting-create-table

ただし、 cook book の通りには動かず、以下のコードに落ち着きました。
エラー ```AttributeError: 'NoneType' object has no attribute 'schema'``` が出たのと、
UniqueConstraint が重複して出力される事象が発生しました。

```# order_columns``` の部分でカラムの順番を調整しています。
```# UniqueConstraint の columns_combination が重複した場合、一つに絞り込む``` の部分で UniqueConstraint の重複を排除しています。  


``` python3
@writer.rewrites(ops.CreateTableOp)
def order_columns(_context, _revision, op):
    # order_columns
    special_names = {"id": -100, "created_at": 1001, "updated_at": 1002}
    cols_by_key = [
        (
            special_names.get(col.key, index) if isinstance(col, Column) else 2000,
            col.copy(),
        )
        for index, col in enumerate(op.columns)
    ]
    columns_or_constraints = [
        col for idx, col in sorted(cols_by_key, key=lambda entry: entry[0])
    ]

    # UniqueConstraint の columns_combination が重複した場合、一つに絞り込む
    uc_columns_combinations = []
    for index, column_or_constraint in enumerate(columns_or_constraints):
        if isinstance(column_or_constraint, UniqueConstraint):
            columns_combination = set([col.name for col in column_or_constraint.columns])
            if columns_combination in uc_columns_combinations:
                del columns_or_constraints[index]
            else:
                uc_columns_combinations.append(columns_combination)

    op.columns = columns_or_constraints
    return op
```

以上です。
GOT2000のファイル転送機能(GOT内部転送)

GOT2000からはファイル転送機能が追加されており、GOT内のファイルをFTPやネットワークドライブやUSBメモリに設定だけで転送する機能です。
ここではSDカードに保存したスクリーンショットをUSBメモリに転送する例で説明します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/a69277b8-e538-cedc-b9c2-85fad2462ca5.png)

ファイルはここでダウンロードできます。

https://github.com/fa-yoshinobu/fa-yoshinobu.github.io/tree/main/Qiita%20Sample/20220816

スクリプトのXCOPYでもワイルドカード指定で一括ファイルコピーはできますが、ファイルサイズや点数が多いと途中で停止してしまうのと、コピーの進捗がわからないのでコピーが数秒で終わるとき以外はあまりおすすめしません。
ファイル転送機能の問題点はSimulatorでは動作しないことですね。実機が必要です。

スクリーンショットはハードコピー設定で使用可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/5b13e30f-d0a4-4b29-470e-66c433914605.png)


# ファイル転送機能
設定はここにあります。ネットワークの項目にあります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/b06b6987-deec-d251-48da-e5ed6b6e7ac0.png)

転送種別:GOT内部転送
GOTのドライブ、フォルダ間の移動なので内部転送です

転送方法:移動

ファイル転送トリガ:立上り GB100
ここは転送開始ボタンに割り付けるアドレス、ここではGB100を使ってますが空きのBITならどこでもよいです。

転送元フォルダ:A:\SNAP
ここはスクリーンショットのSDカード保存先です。フォルダ一覧を押すと候補が出るので打ち込まなくても選択できます。

転送元フォルダ:E:\SNAP
USBメモリのコピー先です。Eドライブを指定します。

転送ファイル名:＊.＊
ワイルドカード指定ができます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/3ae2b233-443f-d1f9-7b78-873fcd0edae7.png)

# 画面
作った画面はこんな感じです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/1679671a-bcd1-1d3b-0928-d36fafb019ad.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/dc8e8046-d98e-0b05-9c32-9b299799a843.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/a69277b8-e538-cedc-b9c2-85fad2462ca5.png)

```:使用アドレス
GB100	ファイル転送開始ボタン
GD111.b0	ドライブAファイルアクセス(システム信号2-2.b0)
GD121.b0	ドライブEファイルアクセス(拡張ドライブ情報.b0)
GS401.b0	ファイル転送エラークリア信号
GS990.b0	ファイル書込み中通知信号
GS990.b14	ワーニング通知信号
GS990.b15	ファイル転送エラー通知信号
GS1024.b8	USBドライブ共通情報 Eドライブ使用可能
GS1824.b0	USBドライブ共通情報 Eドライブ取外し指令
GD0	ダミー
GD122	システム情報:ドライブA空き容量情報(32ビット)
GD130	システム情報:ドライブE空き容量情報(32ビット)
GS402	ファイル転送タイムアウト時間
GS987	実行中ファイル転送ID通知
GS989	ファイル転送エラー番号通知
GS990	ファイル転送状態通知
GS991	ファイル転送対象ファイル数通知
GS992	ファイル転送完了ファイル数通知
```
GSデバイスの詳細はマニュアルを読んでください。
[SH-081219 GT Designer3 (GOT2000) 画面設計マニュアル]

GDはすべてシステム情報なので設定により好きなアドレスに割り付け可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/06ea34d1-2e4b-5c9a-101d-2b733ddbc777.png)

ドライブ空き容量情報は単位をMBにするために初期実行用のスクリプト内でGS466に2を書き込んでいます。(0=Byte,1=KByte,2=MByteの3択)
※GB40の立上り設定で起動後1回だけ実行するスクリプトを作ることが可能です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2146151/2e5e26c5-f28e-56c1-5a0c-6023956603f1.png)
各DNSのルートサーバって誰が管理してるの？

## 勉強前イメージ

各国の人...？

## 調査

### DNSのルートサーバってまずどこにあるの？

世界に存在するDNSのルートサーバは13箇所にあります。

- アメリカ : a.root-servers.net : ベリサイン
- アメリカ : b.root-servers.net : 南カリフォルニア大学情報科学研究所
- アメリカ : c.root-servers.net : コジェント・コミュニケーションズ
- アメリカ : d.root-servers.net : メリーランド大学
- アメリカ : e.root-servers.net : アメリカ航空宇宙局
- アメリカ : f.root-servers.net : インターネット・システムズ・コンソーシアム(ISC)
- アメリカ : g.root-servers.net : 国防総省ネットワークインフォメーションセンター
- アメリカ : h.root-servers.net : 陸軍研究所
- スウェーデン : i.root-servers.net : Netnod
- アメリカ : j.root-servers.net : ベリサイン
- オランダ : k.root-servers.net : RIPE NCC
- アメリカ : l.root-servers.net : ICANN
- 日本 : m.root-servers.net : WIDEプロジェクト/JPRS

13箇所である理由は
DNSはUDPを使用しており512バイト以内の応答が必要なので応答情報は13個までしか入れれないためです。
512バイトに制限されないDNSの拡張プロトコル `EDNS0` が増えてきていますが現在は過渡期になります。
もちろん1箇所1台のサーバではなく、冗長化やいろんな技術を利用して地域分散も行っており200箇所以上でルートサーバが稼働しています。
アジアではJPRSしかないですが、実際には他のルートサーバが40箇所程度アジア地域で稼働しています。
世界中色んなところで稼働しています。

### DNSルートサーバの管理者は誰？

ルートサーバはICANNが管理をしていますが
実際にはa~mまでの組織が運用を行っています。
各組織が連携し合いながら運用を行っております。

## 勉強後イメージ

日本にもルートサーバの拠点があるのか・・・知らなかった
JPRSが管理してるのね

## 参考

- [【図解】DNSルートサーバはどこにある？世界13か所の場所や運用組織を紹介！](https://ryonotes.com/where-is-route-dns-server/)
- [DNSルートサーバ](https://www.nic.ad.jp/ja/newsletter/No45/0800.html)
Mac, Python3 環境で csv の内容を Python の dict リテラルで出力するワンライナー

```
python3 -c "csv_file_path = '<path_to_csv>.csv'; import csv; print([row_dict for row_dict in csv.DictReader(open(csv_file_path))]);" | pbcopy
```

ex.

csv を作成

```
echo "#,Name,Type 1,Type 2,Total,HP,Attack,Defense,Sp. Atk,Sp. Def,Speed,Generation,Legendary\n1,Bulbasaur,Grass,Poison,318,45,49,49,65,65,45,1,False\n2,Ivysaur,Grass,Poison,405,60,62,63,80,80,60,1,False\n3,Venusaur,Grass,Poison,525,80,82,83,100,100,80,1,False" >> pokemon.csv
```

csv to Python dict

```
$ python3 -c "csv_file_path = 'pokemon.csv'; import csv; print([row_dict for row_dict in csv.DictReader(open(csv_file_path))]);" | pbcopy
```

結果

```
[{'#': '1', 'Name': 'Bulbasaur', 'Type 1': 'Grass', 'Type 2': 'Poison', 'Total': '318', 'HP': '45', 'Attack': '49', 'Defense': '49', 'Sp. Atk': '65', 'Sp. Def': '65', 'Speed': '45', 'Generation': '1', 'Legendary': 'False'}, {'#': '2', 'Name': 'Ivysaur', 'Type 1': 'Grass', 'Type 2': 'Poison', 'Total': '405', 'HP': '60', 'Attack': '62', 'Defense': '63', 'Sp. Atk': '80', 'Sp. Def': '80', 'Speed': '60', 'Generation': '1', 'Legendary': 'False'}, {'#': '3', 'Name': 'Venusaur', 'Type 1': 'Grass', 'Type 2': 'Poison', 'Total': '525', 'HP': '80', 'Attack': '82', 'Defense': '83', 'Sp. Atk': '100', 'Sp. Def': '100', 'Speed': '80', 'Generation': '1', 'Legendary': 'False'}]
```
PythonでExcelファイルをパスワード保護する

PythonでExcelファイルにパスワードを付けたいと思って調べても情報が少なかったので共有します。
今回はWindows環境を想定しています。

```python:protect_excel_with_password.py

from win32com.client.gencache import EnsureDispatch
from openpyxl import load_workbook
from pathlib import PurePath

def set_wb_password_with_win32(file_dir_path, password):
    xl_file = EnsureDispatch("Excel.Application")
    wb = xl_file.Workbooks.Open(file_dir_path)
    xl_file.DisplayAlerts = False
    wb.Visiable = False
    wb.SaveAs(file_dir_path, Password=password)
    wb.Close()
    xl_file.Quit()

if __name__ == "__main__":
    dir_path = "DIRECTORY_PATH"
    file_name = "FILE_NAME"
    file_path = PurePath(dir_path, file_name)
    password = "PASSWORD"
    print(file_path)
    set_wb_password_with_win32(str(file_path), password)
    print("Finished")

```

"DIRECTORY_PATH", "FILE_NAME", "PASSWORD"の３カ所を任意の値に変更します。

後はコマンドプロンプトやPowerShellなどから下記のコマンドを実行すると処理ができると思います。

```shell
python protect_excel_with_password.py
```

この記事の内容を試してみて動いた場合や、動かなかった場合、または動かなかったけれども修正して動いた場合など、コメントをいただけますと幸いです。

こちらの記事は下記の動画を元に作成しました。

https://youtu.be/XRUbf2TaS5Q
コード譜投稿アプリを作る #0（開発の背景）

# 開発の背景
エンジニア歴もちょうど３年くらいになり、ポートフォリオと呼べるものがエンジニア転職時に作った拙いものしかなかったので、「業務に追われて手が付けられない」という言い訳をやめて本腰を入れて自作アプリを開発しようと思った。
その備忘録と開発ブログ的な意味で記事を書いていく（つもり）。

# アプリの概要
ピアノとかギターのコード譜が投稿できるようなサイト
下にあげるようなサイトも既にあるが、スマホでの編集がめんどくさいのでスマホでも簡単にコード編集・作成ができるようなサイトを開発する
- [ChordWiki](https://ja.chordwiki.org/)
- [楽器me](https://gakufu.gakki.me/)

# 使う予定の技術
### サーバー
- Ruby on Rails（APIモード）　⇦ 業務で使ったことあるのがRailsしかない
### フロント
- Next.js　⇦ 業務で使ったことがある、Vercel使えばデプロイ簡単。
    - typescript ⇦ もうこれなしじゃコードの書けない体になった
    - chakra.ui ⇦ 使いやすかった。とりあえずいい感じになる
### インフラ
- AWS（サーバー側） ⇦ Fargate使ってみたい
- Vercel（フロント側） ⇦ Next.js使うなら一択？

技術的な目的としては開発環境から本番環境、CI/CDまで自分で構築したい、というのがあるのであまり馴染みのない技術は使わずに構築の学習に集中できるようにする。
コード譜の編集だと小節単位、コード単位での編集が必要なので、フロントとサーバーは分けたい
AWSがしんどそうだったらherokuにするかも…

# 最後に
こんな殴り書きみたいなものでも記事書くのも大変だ…
サービス公開まではちゃんと続けていきたいな

[→次の記事(#1)へ](https://qiita.com/t_sekky/items/e60e5346626df4e49a47)
【Ruby】二次元配列を複製する

結論だけ知りたい方は[コードへジャンプ](#二次元配列の複製方法)！

## 二次元配列を"複製"したのに、コピーを操作するとコピー元も変わってしまう…
AtCoder Beginner Contestにてそんな事象にぶつかってしまい、時間内に解決できませんでした。

たとえば
```ruby
arr = [[1,2,3],[4,5,6],[7,8,9]]
```
このような二次元配列を想定します。
これをdupで複製し、項を操作すると…
```ruby
copy = arr.dup
copy[0][0] = 100

p arr
# => [[100,2,3],[4,5,6],[7,8,9]]
# copyはarrとは別のオブジェクトなのに、copyを操作するとarrも変わってしまった。
```
複製したのになぜコピー元が影響を受けてしまうんだろう？と頭を捻りました。

## なぜコピー元が影響を受けてしまうのか？
copyは確かにarrを複製した別のオブジェクトですが、copy[0]が持っているのはプリミティブな値ではなく、[1,2,3]を格納している参照値(ざっくり言うとメモリアドレスx)を持っています。
つまり、arrでもcopyでも、同一のメモリアドレスxを参照しているのです。
したがってcopyはarrとは別のオブジェクトですが、その中身の配列については、同一の参照先を見るので、同一のオブジェクトです。
```ruby
arr = [メモリアドレスxの参照値, メモリアドレスyの参照値, メモリアドレスzの参照値]
copy = arr.dup
# copyとarrは間違いなく別のオブジェクトだが…
# copy = [メモリアドレスxの参照値, メモリアドレスyの参照値, メモリアドレスzの参照値]である。
# arrもcopyも同じ参照先を見るので、arr[0]とcopy[0]が指すオブジェクトは同一オブジェクト
# 従って、copy[0][0]を操作すれば、arr[0][0]も影響を受けてしまう。
```

## 二次元配列の複製方法
二次元目の配列も複製するためには、一次元目の配列で新しいオブジェクトを用意し、二次元目の配列もdupなどで複製してやる必要があります。
```ruby
arr = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
copy = []
arr.each do |arr2|
  copy.push(arr2.dup)
end

copy[0][0] = 100
p arr
# => [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
# copyの要素を変更したが、arrは影響を受けていない。
```

以上となります。
もしもっと楽に二次元配列を複製する方法がありましたら、ぜひご教授ください。
ご覧いただきありがとうございました！
WSL 2上のUbuntuでNVIDIAのGPUを有効にする方法

Windows 11でWSL 2上のUbuntuでNVIDIAのGPUを有効にする方法を紹介します。自分自身のための備忘録的に書いているので記事のクオリティはご容赦ください。

# 前提
- Windows 11（又はWindows 10のバージョン21H2以上）でWSL 2が有効になっていること
- Ubuntu がWSL上にインストールされていること

# 試した環境
- Windows 11
- Ubuntu 22.04 on WSL

# 手順
## 1. Windows上にCUDAドライバーをインストール
1. [こちらのNVIDIAのページ](https://developer.nvidia.com/cuda/wsl)にアクセスし、 "Get CUDA Driver" をクリック
2. OS・グラフィックボードの種類などを選択してドライバーを検索
![NVIDIAドライバー選択画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654388/487d1465-9f3e-668f-dccd-80a530ebd84e.png)

3. ダウンロード・実行・画面通りにインストールを進める

## 2. WSL上にCUDAドライバーをインストール
[Canonical（Ubuntuの開発元）](https://ubuntu.com/tutorials/enabling-gpu-acceleration-on-ubuntu-on-wsl2-with-the-nvidia-cuda-platform#3-install-nvidia-cuda-on-ubuntu)と[NVIDIA](https://docs.nvidia.com/cuda/wsl-user-guide/index.html)のページを参考にUbuntu上にCUDA Toolkit (今回はバージョン11.7 Update 1) をインストール

![Canonicalページ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654388/ced35a9a-51fd-5b49-8d80-db1dabee6766.png)

私は上記[NVIDIAのページの "3. CUDA Support for WSL 2"](https://docs.nvidia.com/cuda/wsl-user-guide/index.html#cuda-support-for-wsl2)から[CUDA Toolkit 11.7のページ](https://developer.nvidia.com/cuda-downloads?target_os=Linux&target_arch=x86_64&Distribution=WSL-Ubuntu&target_version=2.0)に飛んでそこに記載のコマンドを使いました（下記スクリーンショットハイライト部分）。"Installer Type" は "deb (local)" を選びました。

![NVIDIAページ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/654388/08c346b4-7e60-7996-ac0d-5488edbc4382.png)



以下私が実行したコマンドです。

```
sudo apt-key del 7fa2af80
wget https://developer.download.nvidia.com/compute/cuda/repos/wsl-ubuntu/x86_64/cuda-wsl-ubuntu.pin
sudo mv cuda-wsl-ubuntu.pin /etc/apt/preferences.d/cuda-repository-pin-600
wget https://developer.download.nvidia.com/compute/cuda/11.7.1/local_installers/cuda-repo-wsl-ubuntu-11-7-local_11.7.1-1_amd64.deb
sudo dpkg -i cuda-repo-wsl-ubuntu-11-7-local_11.7.1-1_amd64.deb
sudo cp /var/cuda-repo-wsl-ubuntu-11-7-local/cuda-*-keyring.gpg /usr/share/keyrings/
sudo apt-get update
sudo apt-get -y install cuda
```
CUDAドライバーのバージョンが更新されるとコマンド内のリンクが変わると思うので下記のサイトを参考にしてください。

# 参考にしたサイト
https://ubuntu.com/tutorials/enabling-gpu-acceleration-on-ubuntu-on-wsl2-with-the-nvidia-cuda-platform

https://docs.microsoft.com/ja-jp/windows/ai/directml/gpu-cuda-in-wsl

https://docs.nvidia.com/cuda/wsl-user-guide/index.html
ルートゾーンスケーリングとは

## 勉強前イメージ

DNSのルートゾーンのスケーリング？そのまま？

## 調査

### ルートゾーンスケーリング とは

ルートゾーンが拡張されることによるデータ量および更新頻度の増加の影響を指します。
実際には増加の対処方針も含めて呼ばれることもあります。

ルートゾーンとはDNSにおける頂点のゾーンの事を言います。
なぜスケーリングするかというと [DNSSEC](https://qiita.com/miyuki_samitani/items/0f26423cf54d7bd20c07) やDNSレコードのIPv6の登録、新gTLDが今後導入されることによって
ルートゾーンにおいて更新頻度が増加します。

## 勉強後イメージ

確かにDNSSECが導入されるとめちゃくちゃ多くなりそうな気もする・・・・

## 参考

- [ルートゾーンスケーリングとは](https://www.nic.ad.jp/ja/basics/terms/rootzone-scaling.html#:~:text=%E3%83%AB%E3%83%BC%E3%83%88%E3%82%BE%E3%83%BC%E3%83%B3%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%AA%E3%83%B3%E3%82%B0%E3%81%A8%E3%81%AF%E3%80%81DNSSEC(*1)%E3%80%81,%E3%81%9D%E3%81%AE%E5%A2%97%E5%8A%A0%E3%81%AB%E5%AF%BE%E5%BF%9C%E3%81%99%E3%82%8B)
- [ICANNによるルートゾーンスケーリング調査について](https://www.nic.ad.jp/ja/newsletter/No45/0610.html)
あの最強ハッカー「ウォールナット」の七つ道具の一つ「Internet Automatic Search Program」がGithubで公開されている件

# ウォールナットって？
リコリス・リコイル（以下、リコリコ）というアニメに出てくる金髪ロリ美少女最強ハッカーです。
本名はクルミちゃんといいます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1135021/96fe1c3b-fcf3-42d4-26ab-6ad9ed3319ea.png)

# Internet Automatic Search Programって？
そんな超絶かわいい個人的最推しキャラのクルミちゃんが第７話で見せた七つ道具の一つがInternet Automatic Search Programです。
インターネッツの海から一瞬で目当てのものを自動検索してくれるプログラムみたいですね。
検索しても出てこない情報を探せるのだとか。ダークウェブっぽくてハッカー感ありますね
### 余談
ダークウェブって呼び方よりもウラインターネットとかのほうがしっくりくるのは私だけでしょうか（エグゼ世代）

# プログラム考察
私はアニメに出てくるソースコードは読んでしまう病に罹っているので即一時停止して舐め回すように見てみました。
眺めてみるとJavascriptっぽいし、WebクローラーっぽいのでNode.jsかな？とか考察しました。
これはアニメ制作お得意のどっかのソースコードを丸っと持ってきたパターンか？

## コードを探す
以下ツイート画像を見るとわかるように、最後の方にコメントがありますね。
これを私の七つ道具の一つである「Google.com」にかけます。

https://twitter.com/soh_mitian/status/1559412501888274433

するとGithubのあるRepositoryがヒットしました。
それがこちら

https://github.com/nodejs/readable-stream/blob/v3.6.0/lib/_stream_readable.js#L95

この部分が一致
```javascript
function prependListener(emitter, event, fn) {
  // Sadly this is not cacheable as some libraries bundle their own
  // event emitter implementation with them.
  if (typeof emitter.prependListener === 'function') return emitter.prependListener(event, fn); // This is a hack to make sure that our error handler is attached before any
  // userland ones.  NEVER DO THIS. This is here only because this code needs
  // to continue to work with older versions of Node.js that do not include
  // the prependListener() method. The goal is to eventually remove this hack.

  if (!emitter._events || !emitter._events[event]) emitter.on(event, fn);else if (Array.isArray(emitter._events[event])) emitter._events[event].unshift(fn);else emitter._events[event] = [fn, emitter._events[event]];
}
```

どうやらNode.jsのstreamモジュールですね。

さすが美少女天才ハッカー。
Node.jsのモジュールをその場で実装してしまうとは。

# リコリコ宣伝
そんな超絶美少女ロリハッカーのクルミちゃんが活躍するアニメ、リコリス・リコイルは、毎週土曜日２３：３０から放送中です。
Qiitaユーザなら絶対見ましょう。

https://lycoris-recoil.com/

こういう動画を作るくらいにはハマってます。

https://twitter.com/soh_mitian/status/1559471636239491072
（続）Vision Transformerでダーツの自動計算をやってみる

# この記事の続き
https://qiita.com/magokorokun/items/4acb31131930d89ab84c

前回は、ダーツ1本刺さった状態は95%を超える認識率で、良い感じだったのですが、2本3本の場合、同じところに2本3本刺さった場合（例えば20トリプルに3本とか…まずないけどｗ）の認識率を高める方法が中々見つからないままでした。

最初は出力64モデル型（各数字のシングル・ダブル・トリプル＋インブル、アウトブル、アウトボード、何もなし）でしたが、複数ダーツに対応すべく、64x3=192（同じところに1本、2本、3本）モデル型で学習データを大量に作り学習させました。

結果は思わしくなく、このマルチクラスマルチラベル型モデルは断念しました。
同時にゲーム性を考慮すると、ダーツを1本投げる度に判定ボタンを押すのはイマイチな感じなので、Webカメラでライブ映像を撮影しながら、その映像の変化を捉えて「次のダーツが刺さった」ことを認識するロジックを検討しました。

これは比較的簡単で、前回判定に使った画像をベースとして、その画像との差分が一定以上ある画像に変化した場合に、次のダーツが刺さったとみなす、というものです。

そして、このロジックを組み立てているうちに、閃きました。
差分のマスク画像を作り、最新のダーツだけが残る画像を生成することで、1本64モデル型が活用できるじゃないか！ってことです。

# 最新ダーツのみの合成画像作成
例えばこのように１本2のシングル部分に刺さった状態から
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324631/1266cfa5-a941-7842-2e04-a9ff7fd81cd4.png)

２本目を18のシングル部分に刺すと
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324631/2e933b24-501d-39f5-575a-f2586bb2ca4a.png)

その差分を各ピクセル毎に取得し、一定以上差があるピクセルを白、それ以外を黒に塗ると、このようなマスク画像が完成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324631/7f5fa8fd-6cd8-f9ee-fe93-9250870e9b83.png)

比較用及びマスク画像作成コードはPIL使うとこうなるけど、これってcv2とかのnumpy形式だったら、Python達人はもっと簡単に比較できるコード書けそうｗ
```python
    mask = Image.new("L", small_img.size, 0)
    width, height = mask.size
    mse_cnt = 0
    for y in range(height): 
        for x in range(width):
            a = small_img.getpixel((x, y))
            b = prev_img.getpixel((x, y))
            c = (a[0] - b[0])**2 + (a[1] - b[1])**2 + (a[2] - b[2])**2
            if c > mse_thd:
                mask.putpixel((x, y), 255)
                mse_cnt += 1
            else:
                mask.putpixel((x, y), 0)
```

最後に、何も刺さってないベース画像に最後の２本刺さった画像をマスク画像でマスクして合成するとこのように2本目だけの画像になります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324631/48b597b5-b3e2-c33c-7cec-fe8defab8027.png)

これで画像の変化のタイミング（＝次のダーツを投げたタイミング）と、その差分マスクによる合成で最後のダーツのみの画像が取得できることになりました。

あとは、64モデルに投入して判定するだけです。
もちろん、通常の画像よりも、合成画像は認識率が落ちるため、誤判定も増えます。
この誤判定画像をフォルダに溜めておいて、追加学習に使うという魂胆です！あぁ美しい流れｗ

こんな感じで訂正した画像が、その位置を示すファイル名と共に蓄積されていく。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/324631/c2f2eca2-41f0-24df-84a1-1fe9f9c922dc.png)

ViTモデルに限らずディープラーニングの良いところは、こんなざっくりしたマスク画像で、多少の残骸が残っていても、なんとか学習してくれるところ。ロジックだと、残骸データに振り回されて正解に辿り着かないことも多々あるはず。

# 試しに301ゲーム作ってみた
実際に投げているところを少しだけ動画でどうぞ。
https://youtu.be/ORm_dlB9giI

# 今後の展望
あとは、別の人の部屋など、照明やレイアウト、カメラ角度、ボード種類、ダーツなど微妙に異なる環境でファインチューニングできるかどうかで、使い物になりそうかどうかが決まりますね。

ソフトダーツは自動判定機器がそれなりに入手可能な価格で出回っているけど、ハードダーツはそうもいかないらしい。
ニーズはそれなりにあるかも。

必要な設備はPC（Windows）とWebカメラ。GPUは学習時に必要だから、ファインチューニングに時間をかけたくなければ必要。ゲームを楽しむときは1枚の画像判定なのでGPU無しでも十分利用できそう。

01ゲームは簡単に実装できたので、次はクリケット作るかな。
いや、その前にモデルの認識率を上げなきゃｗ

続く・・・ｗ
【初心者向け】SSHでgithubと通信できるようにする

## はじめに
　gitのローカルリポジトリをSSHを用いてリモートリポジトリにpushできる方法を調べたので記載します。
 SSHを用いることでパスワード入力の手間を省き楽にリモートリポジトリにプッシュできるようになります。

## git、githubとは　
簡単に言うと、ソースコードを管理できるツール。
複数人での開発時にとても便利。
詳しくは以下の記事を参考。
[【超入門】初心者のためのGitとGitHubの使い方](https://tech-blog.rakus.co.jp/entry/20200529/git)

## ローカルリポジトリで通信方法にSSHを指定する
以下の記事を参考
[gitでパスワードを毎回聞かれる問題の解決方法](https://qiita.com/rorensu2236/items/df7d4c2cf621eeddd468)

## 鍵を生成してgithubに公開鍵を設定する
以下の記事を参考
[GitHubにssh接続するまでの手順](https://zenn.dev/schnell/articles/0e1c2e9db5c08d)
[GitやGitHubでSSHに接続する方法をわかりやすく解説！](https://www.sejuku.net/blog/74220)
[viを終了することができない人のためのviの使い方](https://blog.katty.in/853#toc6)

パスワードを設定した場合は、
```
ssh -T github
```
の後にパスワード入力が要求される。
Pythonが嫌いなのでC++版のPytorchで画像認識をやってみる 7日目

# PythonのConv2dの定義をC++に置き換える
　前回より日が経ってしまった。別件でC++を思い出すのを兼ねて監視カメラの録画アプリを残業して作ってたのもある。
　閑話休題、今日から畳み込みのニューラルネットワークを作っていく。実は作るNNには元になるpythonのコードがあるのだが頂きものなので残念ながらまるまる載せるわけにはいかないのでご容赦。つまずきポイントをメモする感じで書き残す。
# Conv2d
Pythonだと↓こんな感じで書くやつである。

```python:
nn.Conv2d(inp, oup, 1, stride, 0, bias = False),
```
何をするものなのかと書こうかと思ったけど↓ここに素晴らしい解説があるので割愛。

https://qiita.com/kenichiro-yamato/items/60affeb7ca9f67c87a17

kerasの定義だとこんな感じ。
```python: keras.layers.Conv2D
keras.layers.Conv2D(
  filters,
  kernel_size,
  strides=(1, 1),
  padding='valid',
  data_format=None,
  dilation_rate=(1, 1),
  activation=None,
  use_bias=True,
  kernel_initializer='glorot_uniform',
  bias_initializer='zeros',
  kernel_regularizer=None,
  bias_regularizer=None,
  activity_regularizer=None,
  kernel_constraint=None,
  bias_constraint=None
)
```
pytorchの定義だと↓こんな感じ。
```:CLASS torch.nn.Conv2d
CLASS torch.nn.Conv2d(in_channels, out_channels, kernel_size, stride=1, 
padding=0, dilation=1, groups=1, bias=True, 
padding_mode='zeros', device=None, dtype=None)
```
どちらにしても引数がいっぱいある。この引数を組み合わせて必要なテンソルを作っていくわけだ。
さてC++のLibtorchだとどうなるかというと…
```C++:https://pytorch.org/cppdocs/api/classtorch_1_1nn_1_1_conv2d.html#classtorch_1_1nn_1_1_conv2d
CLASS torch::nn::Conv2d : PUBLIC torch::nn::ModuleHolder<Conv2dImpl>
```
何だか分からんがな。torch::nn::ModuleHolderというテンプレートクラスにConv2dImplという型がはめ込んである。インスタンス生成にどんなパラメータが入るのか見たいのでConv2dImplの公式解説を見てみる。
# Conv2dImpl
```C++:Conv2dImplコンストラクタ
Conv2dImpl(int64_t input_channels, int64_t output_channels, ExpandingArray<2> kernel_size);
```
コンストラクタの引数が３つしかないやんけ。実はコンストラクタはもう一つある↓。
```C++:Conv2dImplコンストラクタ
Conv2dImpl(Conv2dOptions options_);
```
# Conv2dOptions
Conv2dOptionsというクラスを利用して引数を渡せということらしい。公式にはこんなサンプルが出ている。
```C++:
Conv2d model(Conv2dOptions(3, 2, 3).stride(1).bias(false));
```
メンバ関数を追加して必要な引数を追加していく仕組みらしい。どういうテクニック(テンプレートの回帰処理??)でこんな機構を作っているのか分からないが、これをpythonと比較すると今感じになるようだ。
```
Python
model=Conv2d (3, 2, 3,stride=1, bias=false)
↓
C++
Conv2d model(Conv2dOptions(3, 2, 3).stride(1).bias(false));
```
まあ似ていると言えば似ているけど、こんなに似せないといけないものだろうか。引数が多くなるのでこんな仕組みにしたのだろうけどC++らしくないような。引数はパブリックなメンバ変数にしてinit()で初期化でいいんじゃないの? 1行でテンソルを定義できるのは良いけどデバッグが面倒臭そう。昭和のおじさんはトリッキーなコードが嫌いなのだ。が仕方がない、

# リンク
ここらで参照しているリンクをまとめてみる。
## Libtorch公式サイト
https://pytorch.org/cppdocs/installing.html
## Pytorch公式サイト
https://pytorch.org/docs/stable/torch.html
## 公式サンプルコード?
https://github.com/nuka137/pytorch-cpp-example/tree/6d82b0240af6cb33af015e30b01ee3f0fc3deec2/resnet/cpp
## 日本語Libtorch解説
https://imagingsolution.net/deep-learning/how_to_use_libtorch/
## PyTorchで学習したモデルをC++から使う
https://qiita.com/cashiwamochi/items/51b65d8ab17bebeaa4eb

今日はここまで
PostgreSQLでストアードプロシージャでパラメーターをチェックする

# 目的
ストアードプロシージャを使っていると入力パラメーターのチェックがしたくなります。

# プログラム

```sql
CREATE OR REPLACE FUNCTION uv_set_test (
  p_uuid UUID DEFAULT NULL
  ,p_start_at TIMESTAMPTZ DEFAULT NULL
  ,p_content TEXT DEFAULT NULL
) RETURNS VOID AS $FUNCTION$
DECLARE
BEGIN
  -- パラメーターチェック
  IF NOT (
    p_uuid IS NOT NULL
    AND p_start_at IS NOT NULL AND p_start_at >= '2000-01-01'
    AND p_content IS NOT NULL AND p_content <> ''
  ) THEN
    RAISE SQLSTATE 'U0002' USING MESSAGE = 'Invalid Parameter';
  END IF;

  -- 処理
END;
$FUNCTION$ LANGUAGE plpgsql;
```

毎回書くのは大変なので、共通化したいです。以下のようにします。

```sql
CREATE OR REPLACE FUNCTION uv_set_test (
  p_uuid UUID DEFAULT NULL
  ,p_start_at TIMESTAMPTZ DEFAULT NULL
  ,p_content TEXT DEFAULT NULL
) RETURNS VOID AS $FUNCTION$
DECLARE
BEGIN
  -- パラメーターチェック
  PERFORM uv_check_parameter(
    p_ary := ARRAY[
      ('uuid', p_uuid IS NOT NULL)
      ,('start_at', p_start_at IS NOT NULL AND p_start_at >= '2000-01-01')
      ,('content', p_content IS NOT NULL AND p_content <> '')
    ]::type_uv_check_parameter[]
  );

  -- 処理
END;
$FUNCTION$ LANGUAGE plpgsql;
```

結果は以下の通りです。

```sql
SELECT * FROM uv_set_test();

ERROR:  Invalid parameter uuid, start_at, content
```

p_aryのパラメーターはエラーの時に表示する文字列と満たすべき条件になります。
条件が満たされない場合にペアの文字列が表示されます。
複数件チェックしてエラーを返すようにします。

```sql
DROP TYPE IF EXISTS type_uv_check_parameter CASCADE;
CREATE TYPE type_uv_check_parameter AS (
  name TEXT
  ,value BOOLEAN
);
CREATE OR REPLACE FUNCTION uv_check_parameter(
  p_ary type_uv_check_parameter[] DEFAULT NULL
  ,p_message TEXT DEFAULT 'Invalid parameter '
) RETURNS VOID AS $FUNCTION$
DECLARE
  w_invalid_name_ary TEXT[] := ARRAY[]::TEXT[];
  w_length BIGINT := COALESCE(array_length(p_ary, 1), 0);
BEGIN
  -- 入力パラメーターチェック
  IF w_length = 0 THEN
    RAISE SQLSTATE 'U0002' USING MESSAGE = p_message || 'ary is empty';
  END IF;

  FOR i IN 1..w_length LOOP
    IF p_ary[i].value IS TRUE THEN
      NULL;
    ELSE
      w_invalid_name_ary:= w_invalid_name_ary || p_ary[i].name;
    END IF;
  END LOOP;

  IF COALESCE(array_length(w_invalid_name_ary, 1), 0) > 0 THEN
    RAISE SQLSTATE 'U0002' USING MESSAGE = p_message || array_to_string(w_invalid_name_ary, ', ');
  END IF;
END;
$FUNCTION$ LANGUAGE plpgsql;
```
alibaba cloud RDSの高頻度バックアップはなぜかRTOが短縮できるのか

## RTOとは
RTO（Recovery Time Objective）とは、障害発生時「どのくらいの時間で（いつまでに）」 復旧させるかを定めた目標値である。
言い換えると、RTOは「システム停止やサービス中断が許される時間」とも言える。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/f0861d5a-d587-0558-678a-24423fc8487a.png)

## RDSの高頻度バックアップについて
RDSのバックアップには以下の二種類があります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/28664/c67af1ea-38ee-67ba-17d1-b02525ac8f5f.png)

- 普通のバックアップ(普通の頻度)： １日一回の頻度、１週間では最低でも二日間以上を選択する必要がある
- 高頻度バックアップ： バックアップの頻度をもっと短くできて、最短15分一回バックアップできる。

## 高頻度バックアップはなぜかRTOが短縮できるのか
障害発生する時に、復旧するには以下の２つのステップが必要です。
1. バックアップされていたデータ(物理バックアップでもいいし、スナップショットでもよい)から新しいインスタンスを起動する
2. 前回のバックアップ時点からコミットされていたトランザクションに伴って生成されていたbinlogを取得し、binlog中のクエリを順次実行する(当たり前ですが、内容が多ければ時間もかかる)

https://shiro-16.hatenablog.com/entry/2016/06/12/154343

- 障害発生する時には以下に上記の2の時間を短縮するのかはRTO短縮のポイントになります。
- 高頻度バックアップ利用する場合は、最短15分一回バックアップ取得できて、そこから長くても15分間のbinlogしかなくて、binlogの中のクエリ量も少なくて、2の時間が短縮できる。
- 結果として障害発生後の復旧のRTOが短縮できることになります。
GORMのUpdateで構造体を使う場合「文字：""、数値：0、ブール：false」のフィールドが更新されない。

既出だったのですが、情報が古かったので記事にしました。

# ハマりポイント 
構造体で更新する場合、以下のフィールドが更新されない。
- 文字列：""
- 数値：0
- ブール値：false

■更新されない例
以下は「Name, Age, Active」全て更新されない。

```golang
db.Model(&user).Updates(User{Name: "", Age: 0, Active: false})
```

# どうすればいいか
２パターンあるようです。

1) mapを使用して更新する：*map[string]interface{}{"name": "hello", "age": 18, "active": false}*

    ```golang
    // Select with Map
    db.Model(&user).Updates(map[string]interface{}{"name": "", "age": 0, "active": false})
    ```

2) Select を使用して更新する：*Select("Name", "Age", "Active")*

    ```golang
    // Select with Struct (select zero value fields)
    db.Model(&user).Select("Name", "Age", "Active").Updates(User{Name: "", Age: 0, Active: false})
    ```

# LINK
[Link: Extra-Updating-option](https://gorm.io/docs/update.html#Extra-Updating-option)

>NOTE When update with struct, GORM will only update non-zero fields,
>you might want to use map to update attributes or use Select to specify fields to update
PoetryはColaboratoryで使えない？？(2022.08現在)

- Poetryはgoogle Colaboratoryでは使えない様子。
    - venv非作成モードで、packageのインストール箇所がわかれば、解決できる可能性はあるかもしれない。
    - 解決策が分かる人がいたら教えていただきたい。
- 症状
    - 一見、インストールも行われるのだが、vevn-pythonに切り替えられない
    - 手動で無理やり使えるかもしれないが、再利用のたびにコード修正は対象外としているため。


## venvに切り替わらない
故に、折角パッケージをインストールできても、コードでvenvに切り替えて実行ができない

```bash

# 期待通り表示される。このPATHを「手動」で指定してのvenv-pythonでのrunは可能だろう
!poetry env info --path

# 切替1: セルのrunが終わらない
!poetry shell

# 切替2: 処理にエラー表示されないが、次のwhich pythonでは/usr/local/bin/pythonを指し、venvのpyを差さない。
!source $(poetry env info --path)/bin/activate

!which python # /usr/local/bin/pythonと表示されてしまう
```

## venvを非作成に指定

下記を実行すれば、venvを作らないようにできる。
```bash
poetry config virtualenvs.create false
```

筆者の環境では、installに失敗する事も多々あるが、インストールした後は次の通りになる。

```bash
!poetry config virtualenvs.create false 
#
!poetry instal
```
上記の出力結果が下記。
```bash
...
Skipping virtualenv creation, as specified in config file.
Installing dependencies from lock file

Package operations: 1 install, 0 updates, 0 removals

  • Installing strenum (0.4.8): Pending...
  • Installing strenum (0.4.8): Installing...
  • Installing strenum (0.4.8)
```

```
!poetry show       
```
上記の出力結果が下記。

```
Skipping virtualenv creation, as specified in config file.
strenum 0.4.8 An Enum that inherits from str.
```

```
!pip freeze | grep -i strenum # 何も表示されない。
```

```
!poetry env info --path
%ls -lga /usr
!poetry shell
!source $(poetry env info --path)/bin/activate
!which python
!pip freeze | grep -i strenum
```
上記の出力結果が下記。
```
total 60
drwxr-xr-x 1 root 4096 Aug  3 20:14 ./
drwxr-xr-x 1 root 4096 Aug 16 15:13 ../
drwxr-xr-x 1 root 4096 Aug 16 15:26 bin/
drwxr-xr-x 2 root 4096 Apr 24  2018 games/
drwxr-xr-x 1 root 4096 Aug  3 20:14 grte/
drwxr-xr-x 1 root 4096 Aug  3 20:13 include/
drwxr-xr-x 1 root 4096 Aug  3 20:13 lib/
drwxr-xr-x 3 root 4096 Aug  3 19:50 lib32/
drwxr-xr-x 1 root 4096 Aug 12 13:32 local/
drwxr-xr-x 1 root 4096 Aug  3 20:12 sbin/
drwxr-xr-x 1 root 4096 Aug  3 20:19 share/
drwxr-xr-x 1 root 4096 Aug  3 20:13 src/
Skipping virtualenv creation, as specified in config file.
Virtual environment already activated: /usr
/bin/bash: /bin/activate: No such file or directory
/usr/local/bin/python
```

```
import strenum # ModuleNotFoundError: No module named 'strenum'
```

仮想環境を作成しないに指定した場合、既に居るvenvか.localにインストールされると思っているのだが、 違うみたい。このPATHを正しく指定し直せれば、使えるのだろうか....
Zipのパスワードを忘れた時

zipのパスワードを忘れてしまった時

# fcrackzip

パスワード解析ツールの`fcrackzip`を利用

http://oldhome.schmorp.de/marc/fcrackzip.html

> fcrackzip is a zip password cracker, similar to fzc, zipcrack and others.

# brew install
```bash:
brew install fcrackzip
```

# fcrackzip コマンド
```bash:
fcrackzip -u ~/xxx.zip -p 000000 -v -c Aa1
```

## オプション
* p: パスワード総当たりの開始位置
* v: 途中経過の表示
* c: 文字種

※他にもある程度予測がつく場合には高速で検索できるオプションが多々あります

# さいごに
パスワード忘れたzipでてきたら試してみてください

zipにパスワードかけても、一瞬で解析できてしまう
シェルスクリプトで日付のループ処理 

日付のループ処理のテンプレートです
環境: centos, bash 

## なにをするスクリプトか
```
このスクリプト.sh $開始日付 $終了日付
```
と叩くと開始日～終了日まで日付の範囲を echo します。

```bash:使用例
$ ./date_range_loop.sh 20220729 20220803
20220729
20220730
20220731
20220801
20220802
20220803
Done!
```

date_range_loop.sh コード内の次の部分は、任意の処理に書き変えて使います。
```
    # 何かの処理
    echo ${YYYYMMDD}
```

■ おすすめポイント
誰が使っても安全で、かつシンプルなコードを心がけました。
・不正な日付が渡されると即時終了します。
・問題なければループ処理します。

```date_range_loop.sh
#!/bin/bash
STARTDATE=$1
ENDDATE=$2
TIMEDELTA="1 days"

#バリデーション
if [[ $# -ne 2 ]] ; then
    # How to use.
    echo "sh date_range_loop.sh \$STARTDATE \$ENDDATE"
    exit 1
fi
#書式は日付か
date -d "${STARTDATE}" "+%Y%m%d" > /dev/null  2>&1
is_valid=$?
if [ ${is_valid} -ne 0 ]; then
    echo "STARTDATE Error: Invaild date format -> ${STARTDATE}"
    exit 1
fi
date -d "${ENDDATE}" "+%Y%m%d" > /dev/null  2>&1
is_valid=$?
if [ ${is_valid} -ne 0 ]; then
    echo "ENDDATE Error: Invaild date format -> ${ENDDATE}"
    exit 1
fi
#日付範囲は適切か
if [[ ${STARTDATE} -ge $(date "+%Y%m%d") ]]; then
    echo "STARTDATE Error: Today or future day does not allowed -> ${STARTDATE}"
    exit 1
fi
if [[ ${ENDDATE} -ge $(date "+%Y%m%d") ]]; then
    echo "ENDDATE Error: Today or future day does not allowed  -> ${ENDDATE}"
    exit 1
fi
if [[ ${STARTDATE} -gt ${ENDDATE} ]]; then
    TIMEDELTA="1 days ago"
fi

YYYYMMDD=${STARTDATE}
while true; do
    # 何かの処理
    echo ${YYYYMMDD}

    # ENDDATE分まで処理したら終わり
    if [ ${YYYYMMDD} = ${ENDDATE} ]; then
        break
    fi

    # 日付をインクリメント
    YYYYMMDD=$(date -d "${YYYYMMDD} ${TIMEDELTA}" "+%Y%m%d")
done

echo "Done!"
exit 0
```

筆者は過去のwebアクセスログを処理するのに使っています。
この記事は以上です。
【molcloud】分子でお絵描き

## はじめに
シルエットとなる画像とSMILESから分子で絵を描いてみたいと思います。
このmolcloudは[横山トモヤス様のツイート](https://twitter.com/yoko_materialDX/status/1556233697682067456)で紹介されておりました。元ネタは[こちら](https://twitter.com/andrewwhite01/status/1543039727821115392?s=21&t=_rDMgnmhwgiVQShI7JCtYg)です。

※マテリアルズインフォマティクス関係の内容を他にも投稿していますので、よろしければ[こちら](https://qiita.com/oki_kosuke/items/34b1071224953776c211#%E6%8A%95%E7%A8%BF%E4%B8%80%E8%A6%A7)の一覧から他の投稿も見て頂けますと幸いです。

### 環境
- anaconda
- python 3.7.13
- rdkit 2022.03.4

## molcloudの実行
[公式のGithub](https://github.com/whitead/molcloud)を参考にしています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/97ed9d36-bb96-6542-b904-5acaf8e0cb64.png)
ZIPファイルをダウンロードします。

```:Anaconda Prompt
cd ファイル(molcloud-main)を置いている場所
molcloud tests/test.smi --template tests/shapes/beaker.png --width 15
```
これを実行すると、cloud.pngという画像ファイルが生成されます。
試しにアヒルの画像を使ってお絵描きしてみました。

変換前
![ahiru.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/3c69d2c1-702b-9050-f1a7-1f2ff547ac2a.jpeg)

変換後
![cloud.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1057114/a8671a37-e460-563e-c980-618ca7f06b90.png)

何となくですが、似ていますね！非常に面白い機能です。
【Power Automate】ワークフロー内にあらかじめ失敗したら通知を飛ばすよう設定しておく

スコープアクションを用いた「Try、Catch、Finally」を実装するのがおすすめです。

![スクリーンショット 2022-08-16 103225.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1469675/e2487534-ef3e-e1ab-b376-b7e5d466b535.png)


これはMSから提供されているテンプレートの中にあり、フローの実行履歴へのリンクを取得する式があらかじめ埋め込まれています。
（Catchの中に通知用アクションを予め設定してあるひな形をマイフローに置いておき、新たにフローを作成する際にコピー新規でワークフローを作成するのがよさそうです）

https://jn.hateblo.jp/entry/2020/10/31/174431


さらに、メールやチャットの通知系アクションの中に記載しているフロー名を変更し忘れて混乱するのを避けるためにひな形の時点でワークフローのDisplayNameを取得する式を追加しておくのもおすすめです
（フロー名直書き時代は通知文にコピー元のフロー名のままで修正を忘れて、運用時に混乱することが多々ありました）

https://iyemon018.hatenablog.com/entry/2020/09/11/234247
pillow+numpyで入力した画像からdlibで顔検知しようとしてfor文2枚目で止まった

# はじめに
よくあるpythonのdlibを使用して顔を検出するやつ、だいたいがopencv2を使用して`cv2.imread`とかして読み込んでいるけど、opencv2を使いたくなかった。
そこでpillowで読み込んだ画像をnumpy形式に変換してdlibに食わせようとした。

で、画像が1枚の時は無事に動作、画像をfor文で複数与えようとすると2枚目で顔検出しなくなった。


# どういう状況？
コードを一部
入力する画像は同じ人物を同じ角度から撮った顔の画像。
```python
import dlib
from PIL import Image 
import numpy as np

face_detector = dlib.get_frontal_face_detector()
image_list=["01.jpg","02.jpg","03.jpg","04.jpg","05.jpg"]
for img_path in image_list:
    img = np.array(Image.open(img_path),dtype=np.uint8) ##画像を読み込む
    img = img[:, :, [2, 1, 0]] ##opencvとおなじBGR順にする

    faces = face_detector(img, 1)
    #(後処理)
```
このとき、`01.jpg`はちゃんと顔検出する。`02.jpg`が顔検出しない。
同じ画像を5枚並べてもやっぱり2枚目で検出しない。
順番を入れ替えても1枚目成功、2枚目失敗となる。

```
IndexError: index 0 is out of bounds for axis 0 with size 0
```
エラーとしてはfacesで取得できるはずの顔検出の領域が入っていないと怒られている。つまり顔検出できていない。

# 解決した方法
①opencvを使用する
```python 
    img = cv2.imread(img_path)
```
画像の読み込みをopencvにすれば問題ない。でもこれは根本的な解決になっていない。

②変数を新規宣言する
```python
    img = np.array(Image.open(img_path),dtype=np.uint8)
    img_p = img[:, :, [2, 1, 0]]
```
読み込んだあとに、BGR順に変更するがこの時に同じ変数を用いず、新規変数にする。
こうすることで解決した。

追記
ちゃんとDeepCopyしたほうが安定する。


# なぜこんなことが起こったのか？
おそらくnumpy内部でのポインタ管理が云々かんぬんだと思われる。
自身を参照するとなると見た目上は変わっていてもdlibに入れる際にちゃんとした参照になっていないんじゃないかと。
このあたりはちゃんと調査していない。
ダメだった時におけるimg配列を`data`や`shape`や`strides`とかを使ってポインタを追えばわかるかもしれない。

# 余談
```python
>> img_p = np.array(Image.open(img_path),dtype=np.uint8)　##PILで読み込む
>> img_p = img_p[:, :, [2, 1, 0]]
>> img_c = cv2.imread(img_path) ##OpenCVで読み込む
>> print(np.allclose(img_p,img_c)) ##配列の一致を確認する
   True
```
numpy的には新規変数を宣言しない場合でも、データは一致という。なおこの状態で動かすと5枚とも顔検出する。np.allcloseを挟むことによってポインタ参照が再整理されたのか？
また途中でほかのnumpyの処理を挟むと同じように再整理されたりされなかったりする。
【MySQL】複合インデックスを設定してみる

## はじめに
https://qiita.com/tatsuya_1995/items/864a61677e523a4197bd

上記の記事で単一インデックスを作成して速度が80倍になったことを確認しました。
次に複合インデックス作成してみます。


単一インデックスは1つのカラムを用いて、検索の速度を向上させることが目的でしたが、複数インデックスは複数のカラムを組み合わせて1つのインデックスを作成します。

## 準備
兎にも角にもやってみます。データは上記のQiitaの記事で準備したsampleテーブルをそのまま使います。データのイメージは下記のようになります。
![index_image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/969534/8a7c7f3d-96da-6f31-5ff9-6feddaf46079.png)


また、複数インデックスを作成するときのクエリは下記のようになります。
```
CREATE INDEX インデックス名 ON データベース名 (カラム名1, カラム名2, カラム名3...);
```
こちらを用いてsampleテーブルに`index_sample`というインデックス名で`point, type, flagカラムの順番`で作成します↓
```
CREATE INDEX index_sample ON sample (point, type, flag);
```
インデックスが作成されていることを確認できました↓
![index_0816_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/969534/fbd4416c-57fc-faae-e81a-f8e2a3bc4741.png)

## インデックスの効果確認
下記のような検索のクエリを投げて、インデックスの効果を確認します。
```
SELECT * FROM sample WHERE point = 99;
```

◆速度調査
作成前と作成後の速度はそれぞれ同じクエリで検索したときの5回の平均速度、速度比は`作成後/作成前`を示します。(半端な値は適度に丸めています)
|パターン|indexの効果|作成前(ms)|作成後(ms)|速度比|
|:---:|:---:|:---:|:---:|:---:|
|WHERE point|○|15.4|5.2|3.0|
|WHERE point AND type|○|53.2|5.5|9.7|
|WHERE point AND flag|○|45.3|5.6|8.1|
|WHERE point AND type AND flag|○|51.9|7.4|8.1|
|WHERE type|×|5.1|5.2|1.0|
|WHERE type AND flag|×|6.5|5.6|1.2|
|WHERE type AND point|○|47.4|4.5|10.5|
|WHERE flag|×|5.1|6.1|0.8|

## 速度調査から分かること
- 複合インデックスを作成するときは指定する順番が重要である
 pointカラムを使用して検索を行なっている時はインデックスが効いていますが、pointカラムを使用しない場合はインデックスが効いていません。
```
CREATE INDEX index_sample ON sample (point, type, flag);
```
上記のようなクエリでインデックスを作成しているためpointカラムを使わないと上手くインデックスが使えません。
- `WHERE type AND point`ではインデックスが使えているように、WHERE句内の順番が異なっていてもよしなにインデックスを使ってくれる。ただし、type, pointの順でインデックスを設定した方が実際は早くなるらしい
- 1つの絞り込み条件で検索するのであれば、複合インデックスより単一インデックスの方が早い([こちらの記事](https://qiita.com/tatsuya_1995/items/864a61677e523a4197bd)で調査した単一インデックスの速度より明らか)

|パターン|インデックス作成前(ms)|単一インデックス作成後(ms)|複合インデックス作成後(ms)|
|:---:|:---:|:---:|:---:|
|WHERE point|19.2|0.24|5.2|

## おわりに
複合インデックスを上手く設定する際には、実際に発行されるSQLを踏まえる必要があります。次回書く記事では`カーディナリティ`という特定の列に含まれるデータの一意性の指標に関して書いていきます。
下記の記事が大変参考になりますので、よろしければmm

https://qiita.com/kiyodori/items/f66a545a47dc59dd8839
Ubuntu 20.04 に .NET 6.0 SDK をインストール

.NET 6.0 は 古いOSだと `windows 7 sp1` から対応していてmacでもLinuxでも動いて良いかも、と思いました。

## インストール

```sh
curl -O -L -s "https://packages.microsoft.com/config/ubuntu/20.04/packages-microsoft-prod.deb"

sudo apt update && sudo apt install -y apt-transport-https

sudo apt install -y dotnet-sdk-6.0

dotnet --version
```

## プロジェクトの作成

```sh
mkdir console && cd console

# dotnet new プロジェクト名
dotnet new console
```

## サンプル

```sh
dotnet run
```

* hello world が表示されることを確認。9秒くらいかかる。

## build

```sh
dotnet build
```

* 8秒くらいかかった。

```sh
# ビルドされたものを実行
bin/Debug/net6.0/console
```

* 瞬時に実行された。

### ランタイム同梱

64 MB 程になる。

```sh
dotnet build --sc -r linux-x64
dotnet build --sc -r win-x86
dotnet build --sc -r win-x64
dotnet build --sc -r osx-x64

dotnet build --sc -r win
```

-r は以下指定

https://docs.microsoft.com/ja-jp/dotnet/core/rid-catalog



### ランタイム同梱しない場合

* 150 kb 程度で済む。

```sh
dotnet build -r win-x86 --sc=false
```
【Mac】SSH接続で秘密鍵ファイルの権限エラーPermissions 0664 for 'xxx.pem' are too open.

# はじめに
前提となる全体像を先にざっくりと説明しますと、
目的は「**pemファイルを使ってSSHでAWSのEC2に接続する**」

です。

※「pemファイル」は鍵や証明書を格納するファイルで、AWSのEC2インスタンス作成時にダウンロードします。

秘密鍵ファイルは他にも種類があるので「私の場合はpemファイルだった」というだけです！:relieved:




# ターミナルでサーバーにSSH接続しようとすると権限エラー

```console
$ ssh prod
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@         WARNING: UNPROTECTED PRIVATE KEY FILE!          @
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
Permissions 0664 for '/Users/user/.ssh/xxx.pem' are too open.
It is required that your private key files are NOT accessible by others.
This private key will be ignored.
Load key "/Users/user/.ssh/xxx.pem": bad permissions
ec2-user@11.11.11.11: Permission denied (publickey).
```

権限の問題っぽいですね・・・。


(ssh接続のコマンドが短いぞ！と思った方は👇を参考に)

https://qiita.com/blendthink/items/d31e5c19751644129efc

# 秘密鍵ファイルの権限を変更する
結論から言うと、該当ファイルを置いてあるディレクトリで以下のコマンドを実行すればOKです。

```console
$ chmod 600 XXX.pem 
```

これが一体何をやっているか？

まず`chmod`は「change　mode」の略でファイル or ディレクトリに対する権限を設定するLinuxコマンドです。

「600」の意味については👇を参考に！

https://qiita.com/aaabb6211/items/30ddad48773f37affcb9


ついでに、「なぜLinuxコマンドなの？」については👇を参考に！

https://qiita.com/andrew954/items/94eeb1296ea2928f5a67