OmniAICreator/Qiita-1.07M huggingface dataset:
【関数不要】Firestoreの定期バックアップを作成する【備忘録】

CloudScheduler + CloudFirestoreAPIでコードを書かずに定期バックアップデータが作成できたので紹介します。

[WorkFlows](https://medium.com/google-cloud/firestore-backups-the-easy-way-with-cloud-workflows-3a96a434d3c7)を使った記事もありましたが、WorkFlowsをはさまなくてもバックアップ可能でした。

[公式](https://firebase.google.com/docs/firestore/solutions/schedule-export?hl=ja)ではScheduler + Functionsを使う方法が紹介されいます。各プロジェクトに適した方法を採用してください。

1. Google Cloud プロジェクトに対する課金を有効
2. [バックアップ用のストレージ作成](https://qiita.com/drafts#2-%E3%83%90%E3%83%83%E3%82%AF%E3%82%A2%E3%83%83%E3%83%97%E7%94%A8%E3%81%AE%E3%82%B9%E3%83%88%E3%83%AC%E3%83%BC%E3%82%B8%E4%BD%9C%E6%88%90)
3. [サービスアカウントに権限を付与](https://qiita.com/drafts#3-%E3%82%B5%E3%83%BC%E3%83%93%E3%82%B9%E3%82%A2%E3%82%AB%E3%82%A6%E3%83%B3%E3%83%88%E3%81%AB%E6%A8%A9%E9%99%90%E3%82%92%E4%BB%98%E4%B8%8E)
4. [スケジューラーの設定](https://qiita.com/drafts#4-%E3%82%B9%E3%82%B1%E3%82%B8%E3%83%A5%E3%83%BC%E3%83%A9%E3%83%BC%E3%81%AE%E8%A8%AD%E5%AE%9A)

## 2. バックアップ用のストレージ作成

![スクリーンショット 2022-01-18 13.49.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510204/908dd729-bc07-fe4c-d05e-9faa3920551b.png)

プロジェクトと別のロケーションを選択するとAPI実行時に`status: "INVALID_ARGUMENT"`というエラーがでるので注意。

## 3. サービスアカウントに権限を付与

https://firebase.google.com/docs/firestore/solutions/schedule-export?hl=ja#configure_access_permissions

管理画面からサービスアカウントを新規で作成する場合は以下のロールを付与。

- Cloud Datastore インポート / エクスポート管理者
- Storage オブジェクト 作成者（公式は管理者）

## 4. スケジューラーの設定

[Cloud FireStoreのexportDocumentsAPI](https://firebase.google.com/docs/firestore/reference/rest/v1/projects.databases/exportDocuments)を定期実行します。

|  TH  |  TH  |
| ---- | ---- |
|  頻度 |  0 1 * * *  |
|  ターゲットタイプ  | HTTP |
|  URL |  https://firestore.googleapis.com/v1/projects/${PROJECT_ID}/databases/(default):exportDocuments  |
|  HTTPメソッド |POST |
|  本文 | {"outputUriPrefix":"gs://${バケット名}"} |
|  Authヘッダー | Authトークンを追加 |
|  サービスアカウント  |  サービスアカウントに権限を付与でアクセス権限を付与したアカウントを指定  |
|  範囲  |  https://www.googleapis.com/auth/cloud-platform  |

## 確認する

Cloud Schedulerの今すぐ実行ボタンを押す。

![スクリーンショット 2022-01-18 11.42.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510204/932fa0f6-ac0e-82d9-9100-db5b3108669c.png)

Cloud Storageに実行した日時のフォルダができていればOK。

![スクリーンショット 2022-01-18 11.44.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/510204/5da8df62-aaeb-f83b-8437-a7694d2b86ce.png)

以上です。
neosでlogix その49

# 概要

niosでlogixやってみる。
Global Transformノード
Local Transformノード、使ってみた。

# 写真

![2022-01-18 03.41.29.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/ddb4fb7b-8523-9d44-aa28-5373afc3a12b.jpeg)


# Global Transform

```
Inputs
 	Slot	Instance
Outputs
 	Float3	GlobalPosition
 	FloatQ	GlobalRotation
 	Float3	GlobalScale
```




# Local Transform

```
Inputs
 	Slot	Instance
Outputs
 	Float3	LocalPosition
 	FloatQ	LocalRotation
 	Float3	LocalScale
```

# 手順
- logixtooltipを装着。
- ノードブラウザを表示
- Slots>Root Slotをスポーン
- Slots>Find Child By Nameをスポーン
- Transform>Global Transformをスポーン
- Transform>Local Transformをスポーン
- Input>Stringをスポーン
- つなぐ 


以上。
GKE(autopilot) + locust で数万qpsが可能な負荷テスト環境作ってみたメモ

# 背景
- 数万qps程度のGETリクエストの負荷テストを行いたい
- GKE(autopilot) と locustで作成してみる

# locustとは

https://locust.io/

負荷テストツール。Pythonでシナリオ定義できる。また、分散環境構築対応しており、GKEとの相性良い。

### Getting Started
https://docs.locust.io/en/stable/quickstart.html

- `locust` ってコマンド打つだけでサーバー起動し、実行用のUIにアクセスできる
    - そこからoption設定入力して実行可能
- コマンドだけでも実行可能

```locust -H https://onesdata-prod.appspot.com --headless --users 10 --spawn-rate 1```

### シナリオサンプル(paramに現在時刻を入れたGETリクエスト)

```locustfile.py
from locust import HttpUser, task
import time

class RequestUser(HttpUser):

    @task
    def request(self):
        self.client.get(f"/path?timestamp={int(time.time())}&name=munaita")
```
- GET投げるだけならこれでok
- Pythonなのでなんでもできそう

# GKE + locust
- 数万qpsというシナリオを実行するためには単体のマシンでは無理(ローカル実行で300qps程度)
- 分散環境で実行する
- GKE(autopilot)で管理が楽
- GCPの公式ドキュメントに GKE + locustのチュートリアルがあるのでこれに沿って作業する

https://cloud.google.com/architecture/distributed-load-testing-using-gke

### 作業メモ
- 基本上記のチュートリアルで動くが、一部クラスタの作成箇所はautopilot用に変更する必要あり

```sh
export REGION=asia-northeast1
export PROJECT=$(gcloud config get-value project)
export CLUSTER=your-cluster-name
export TARGET=${PROJECT}.appspot.com
export SCOPE="https://www.googleapis.com/auth/cloud-platform"

# クラスタ(autopilot)の作成
gcloud container clusters create-auto $CLUSTER --region $REGION --scopes=logging-write,storage-ro
# クラスタの接続
gcloud container clusters get-credentials $CLUSTER --region $REGION --project $PROJECT
```

### 注意
- このチュートリアルのコードは古いのですでにlocustの最新チュートリアルコードが動かなかったりする
  - requirements.txtを最新に更新すると便利
  - バージョンの更新に伴い、locustコマンドの `--slave`オプションが `--worker` に変更されているのでrun.shを変更する必要がある
- 35,000qpsを実現するための構成
  - ワーカーレプリカ: 1000 add_user: 10 
  - 1ユーザー10回リクエストするシナリオ
SalesforceCLI プラグイン作成の参考サイト

Salesforce CLIプラグインを作成した際に参考にしたサイトです。

## 公式ドキュメント
Salesforce CLI プラグイン開発者ガイド

https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_plugins.meta/sfdx_cli_plugins/cli_plugins_resources.htm

## 参考になるリポジトリ

標準のSalesforce CLIコマンドのリポジトリの一覧。標準のコマンドはオープンソースです。自分でプラグインを作成するときに参考になります。

https://github.com/salesforcecli/status

## プラグイン作成ツール
Salesforce CLI プラグインの雛形を作成するツール

https://github.com/forcedotcom/sfdx-plugin-generate

## 実装について
### フック
https://oclif.io/docs/hooks

https://github.com/salesforcecli/plugin-metadata-hook-demo/blob/master/README.md

### テスト

モックと単体テストのユーティリティが提供されています。

https://github.com/forcedotcom/sfdx-core

NUT(non-unit-tests)を作成するためのライブラリ。

https://github.com/salesforcecli/cli-plugins-testkit/blob/main/SAMPLES.md

## デバッグ
下記のようにsfdxの前に「DEBUG=*」をつけると実行ログがコンソールに表示されます。


```console
DEBUG=* sfdx force:source:retreve
```

https://developer.salesforce.com/docs/atlas.en-us.sfdx_cli_plugins.meta/sfdx_cli_plugins/cli_plugins_debug.htm

<!-- 
メモ
デバッグ
yarn install
yarn prepack
sfdx plugins:link

env DEBUG=* sfdx force:source:retreve -p xxxx
表示されるのはsalesforce/coreのLoggerを使って書き込んだもの。console.logは表示されない。
-->
[C++] listの中間要素の削除はvectorより遅い場合がある、という勘違い

※割と駄文です。

# やはり中間要素の挿入や削除をしたい場合はlistを使った方が無難らしい

そりゃそうだろう、という声が聞こえてきそうですが、そもそものきっかけはとある海外のベンチマークテストの記事でした。

- [C++ benchmark – std::vector VS std::list VS std::deque](https://baptiste-wicht.com/posts/2012/12/cpp-benchmark-vector-list-deque.html)

この記事の中に「8バイトのデータを一定数格納した各コンテナから要素を1000個Random Removeしたときの速度（ms）差」として以下のグラフが記載されています。

![68747470733a2f2f71696974612d696d6167652d73746f72652e73332e61702d6e6f727468656173742d312e616d617a6f6e6177732e636f6d2f302f3136393132332f66613066376539392d633638302d653935322d356536642d6439326630376530633136662e706e67.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169123/37d98c61-60be-2fa8-f7a6-49fe34e33715.png)

このグラフを鵜呑みにして、中間要素の削除が必要な場面でもdequeやvectorを使用していたのですが、最近listじゃないと遅くなるパターンに遭遇しました。

[Submission #136682542 - Codeforces](https://codeforces.com/contest/1610/submission/136682542)
（ソースが汚くてすいません…）

長いので一部抜粋＆見やすくなるように編集します。

```c++
// 問題のlist
std::list<int> remove_list(a.begin(), a.end());
    
for (auto it = remove_list.begin(); it != remove_list.end();) {
    if (*it == i)
        remove_list.erase(it++);     // ここで中間要素の削除を行なっている
    else ++it;
}
```

上記のコードは、listをdequeに変更すると実行時間超過でジャッジを通らなくなります。

元記事の本文を読み直してみるとわかるのですが、「**挿入、削除する箇所の選択はO(n)の線型探索を用いて決めている**」と書いてあり、1回の挿入or削除ごとに全要素間のイテレーションを行ってテストしていることがわかります。
listのイテレーションはdequeに比べてやや遅いことが記載されている[^1]ので、上記のグラフは挿入・削除回数分のイテレーションの速度差を加味した上でのベンチマークである、ということになります。
前述のコード例では1回しかイテレーションを行っていません。実際にこの条件でdequeの3倍以上の速度が出ているので[^2]、グラフをそのまま適用して「listの中間要素の削除は8バイト以下だとdequeより遅い」とするのは誤りである、と考えた方が良さそうです。

[^1]: 実際にlistのイテレーションはvectorやdequeに比べて多少遅いです。といっても実用にならないほどではないですが。

[^2]: Codeforcesのこの問題における実行時間制限は1秒です。

# まとめ

こう言う記事は本文までちゃんと読まないとダメですね。
今読み返してみたらコメント欄で指摘されてるし…
古いPC(Win7)でSalesforce DXの環境設定する


[クイックスタート: Lightning Web コンポーネント](https://trailhead.salesforce.com/ja/content/learn/projects/quick-start-lightning-web-components)

上記プロジェクトを試しにお古のPCで試してみようと思ったら... あれ...updateできません。

```
sfdx-cli: Updating CLI from 7.66.2-4f159a1d07 to 7.94.3-a4e7c7955b... done
Node.js is only supported on Windows 8.1, Windows Server 2012 R2, or higher.
Setting the NODE_SKIP_PLATFORM_CHECK environment variable to 1 skips this
check, but Node.js might not execute correctly. Any issues encountered on
unsupported platforms will not be fixed.
```

あっ、このPCはWindows7だったなぁ。

調べてみるとNode v14はwindows7では使えないようです。

[Windows7 で Node.js 14 を使う](http://var.blog.jp/archives/83981887.html)
[Node.js 14.x supported on windows 8.1 and above? ](https://github.com/nodejs/node/issues/33000)

この通りの設定をするんだけど...

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/5f70ffcb-9a31-ebc8-e80c-fc8dc43a3050.png)

困ったなぁ。ここに紹介されているnode-v14.18.3-win-x86では動きません。

色々調べてみるとv14.10なら動くらしい。さっそくZIPファイルを入手。
しかし起動しません。一瞬画面が出るけど中身が分からん。

さらに調べるとnode-v13.6.0-win-x86ならwin7でも動くみたい。
確かにエラーはでない。ではこれを使いましょう。

1. 取りあえずこの解凍結果をC:\Program Filesにコピーします。
2. 環境変数pathにこのフォルダーを追加
   スタート --> コンピュータで右クリック --> プロパティ --> システムの詳細設定 --> 環境変数
　 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/98b09431-0fd0-6e1c-5ca5-8414c4791048.png)

3. NODE_SKIP_PLATFORM_CHECK 1　この変数もセットします。

　![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/250fcbec-8d35-9328-53de-7d29e09a576a.png)

しかし、この作業まではできるけど、次に進めないですね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/45a8d0f7-6d37-43a6-1593-2411c76f3ca2.png)

しかし、コマンドプロンプトから、以下のコマンドを叩くとちゃんと接続できています。
また、設定 --> ユーザ からログイン履歴を検索してみるとちゃんと成功してました。
中途半端な設定をしているんで、プレイグランドからの結果を戻せないのかもしれませんね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/328136/71d8b5df-cab3-b54c-06d1-2f4250504334.png)

続行します。

# We couldn't find a Lightning web component named 'helloWorld'.　

確認を押すとエラー

あああ、検証先のプレイグランドの選択を間違えてました。
VSCodeで使ったプレイグランドを指定してませんでした。

---------------------------------------------------------------
参考資料
[Salesforce CLIコマンド（開発用）](https://qiita.com/TaigerAndDragon/items/e25fff8d032abfbf5144)

[Salesforce CLI チートシート](https://qiita.com/TaaaZyyy/items/d6780394df2b12dfce64)

[Executing the SFDX Update command to update the CLI to latest version is not working](https://salesforce.stackexchange.com/questions/343471/executing-the-sfdx-update-command-to-update-the-cli-to-latest-version-is-not-wor)

[NodeJS for windows 7 [duplicate]](https://stackoverflow.com/questions/62212754/nodejs-for-windows-7/64626035#64626035)

[Node v14.15.0 (LTS)](https://nodejs.org/ja/blog/release/v14.15.0/)
【Android 12】Google Play ストアでFlutter製アプリがインストールできなかった問題を解消

#はじめに
今回Flutterで実装したアプリで、Androidユーザーから「プレイストアからアプリがインストールできないよ〜！」とフィードバックを受けまして、解消法を自分への備忘録として残しておきます。

#ストアからアプリがインストールできない

<img width="400![Screenshot_1642392810.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329865/5f7ec0d7-dc06-f082-2e16-c377de6d99fa.png)
" alt="dlerror" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/329865/946d5ae0-f86c-6006-fa2b-4ea44a4c9c20.png">

今回ユーザーからフィードバックいただいた内容として、画像のように「<アプリ名>をインストールできません」とダイアログが表示されている状態です。
インストールできない場合のガイドラインとして、Google公式では下記のような対処法を紹介しています。

https://support.google.com/googleplay/answer/7513003?visit_id=637780739221673675-476363519&rd=1
>1. ストレージの空き容量を確認する
>2. データ接続を確認する
>3. SD カードを確認する
>4. ダウンロード マネージャーからキャッシュとデータを削除する
>5. Google Play 開発者サービスからキャッシュとデータを削除する
>6. Play ストアのアップデートをアンインストールして再インストールする

今回は上記内容をすべて確認してもらって、対応していただきましたが、それでもダウンロードできない。
また、自分の開発環境・Android実機(Pixel3a Android9 API Level29)の環境では再現しない。
インストールできない旨のダイアログが出るだけで、それ以上のエラーログや何らかのメッセージが出るわけでもなく苦労しました・・・:disappointed:

# 原因
どうやら直近のコミットで、compileSdkVersionを31に上げたことにより、Android 12の端末でビルドした際にエラーが発生していました。

### エラーログ
```
adb: failed to install /Users/example_app/build/app/outputs/flutter-apk/app.apk: 
Failure [INSTALL_PARSE_FAILED_MANIFEST_MALFORMED: Failed parse during installPackageLI: 
/data/app/vmdl1651832701.tmp/base.apk (at Binary XML file line #49): 
com.example.example_app.MainActivity: Targeting S+ (version 31 and above) requires that an explicit value for android:exported be defined when intent filters are present]
Error launching application on sdk gphone64 x86 64.
```

#解消法
結論として、AndroidManifestファイルに`android:exported="true"`を設定することでビルドにも成功し、Android 12ユーザーでもストアからもインストールできるようになりました。


```android/app/src/main/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    package="com.sample.example.example_app">
    <uses-permission android:name="android.permission.INTERNET" />
    <application
        tools:replace="android:label"
        android:usesCleartextTraffic="true" 
        android:allowBackup="false"
        android:label="@string/app_name"
        android:icon="@mipmap/ic_launcher">
        <activity
            android:exported="true" // activityタグ内に書く
            android:name=".MainActivity"
```


#リファレンス
https://developer.android.com/about/versions/12/behavior-changes-12?hl=ja

https://minpro.net/apps-targeting-android-12-and-higher-are-required-to-specify-an-explicit-value-for-androidexported
リクルートとかリボソームとか無限にありそう

　あとリブロースとか。

　調べてみました。

　Wikipediaのタイトル一覧（https://dumps.wikimedia.org/jawiki/latest/ ）をDLして、正規表現で調べます。小文字を含むものは除外します。

```python
import re

f = open('jawiki-latest-all-titles','r',encoding='utf-8')

ans = []

for r in f.readlines():
    r = r.split('\n')[0]
    title = r.split('\t')[1]
    pat = r'^リ[\ア-\ヴ]{2}ー[\ア-\ヴ]$'
    res = re.match(pat,title)
    ng = 'ァィゥェォャュョッ'
    if res:
        isFail = False
        for n in ng:
            if n in res.group():
                isFail = True
        if isFail:
            continue
        ans.append(res.group())

ans = list(set(ans))
ans.sort()

print(len(ans))

for a in ans:
    print(a)
```

```
66
リアソール
リエゾーン
リエパーヤ
リオタール
リオトール
リガトーニ
リキソース
リキメール
リクルート
リグベーダ
リゴラート
リスカーテ
リスタート
リステージ
リスナーズ
リスバーン
リスポーン
リズバーン
リズミーン
リソソーム
リソゾーム
リゾチーム
リツイート
リトリート
リドワーン
リナロール
リナローロ
リノリース
リバテープ
リバプール
リビエール
リビトール
リビドーズ
リピエーノ
リピトール
リピドーム
リフレーン
リブロース
リプリース
リプレーザ
リベラーラ
リベリーノ
リボソーム
リボゾーム
リポソーム
リポゾーム
リマソール
リムパーザ
リモサーノ
リモザーノ
リリアーヌ
リリアーノ
リリアール
リルネード
リルバーン
リンカーズ
リンカーン
リンギーサ
リンギーネ
リンケージ
リンゲール
リンシード
リンダール
リンモール
リンモーン
リヴラーガ
```
# 結論

　無限にはなかった。66項目だった。

# 発展

　しりとりにおける定石「る攻め」に対する「る返し」を列挙する。人名や「～の～」といった形式は避けた。

```
96
ルアルドール
ルアーブル
ルアーヴル
ルイスビル
ルイビル
ルイル
ルオンノタル
ルガーピストル
ルガール
ルクオイル
ルクソル
ルクソール
ルクプル
ルクル
ルクレル
ルクレール
ルクール
ルゴール
ルサイル
ルサル
ルサール
ルシエル
ルシオール
ルシノール
ルシル
ルシール
ルジオミール
ルジンドール
ルスアル
ルスツリゾートホテル
ルセル
ルセール
ルゼル
ルダースダル
ルチル
ルテメレール
ルテリブル
ルテル
ルトスワフスキ国際コンクール
ルナアル
ルナスタイル
ルナホール
ルナーボール
ルナール
ルネスホール
ルノアール
ルノワルユースホステル
ルノワール
ルノースポール
ルフーブル
ルブリントライアングル
ルブル
ルベザル
ルベルM1886ライフル
ルミエール
ルミキシル
ルミステル
ルミネスモバイル
ルミノール
ルメートル
ルメール
ルリイカル
ルリユール
ルル
ルルアブール
ルルベル
ルルル
ルルルル
ルングニル
ルングニール
ルンケル
ルンゲ-レンツベクトル
ルーガンビル
ルーキーパステル
ルーザスデール
ルーザーガール
ルーセル
ルーテル
ルーデル
ルートシグナル
ルートダブル
ルートビール
ルートベクトル
ルートール
ルードナイル
ルービン因果モデル
ルーフレール
ルーブル
ループトンネル
ルーベナール
ルーベル
ルーメン毎平方メートル
ルーラル
ルール
ルールルール
ルーヴル
```
お気に入りのPythonライブラリ

# Logger

## loguru

- 超簡単に使えるロガー
- https://github.com/Delgan/loguru

## icecream

- どちらかというと Pretty-printing だけど、ロガーとセットで使うからここでいいや
- https://github.com/gruns/icecream

# Web Server

## FastAPI

- 素晴らしい
- 型指定な REST API用ウェブサーバー
- https://fastapi.tiangolo.com/ja/

# CLI

## Typer

- 素晴らしい
- 型指定で コマンドライン引数解析などをやってくれるCLI作成用ライブラリ
- https://typer.tiangolo.com/

# IoC, DI

## lagom

- 超簡単に使える dependency injection コンテナ
- https://lagom-di.readthedocs.io/en/latest/
zshrcの編集をミスってあらゆるコマンドが使えなくなった場合の解決方法

## 経緯
typescriptの `tsc` コマンドを使いたくて、`.zshrc` の最終行に以下のようにパスを追記しました。

```
export PATH="/Users/xxx/.nodebrew/node/v15.9.0/bin$PATH"
```


## 現状
とにかく  `ls` や `vi` 等、あらゆるコマンドが使えません。

```
% cd ~
% ls
zsh: command not found: ls
% vi .zshrc
zsh: command not found: vi
```

`cd` は使える。

## 解決方法
直し方はシンプルで、`.zshrc`の最終行に追加したパスを消すだけです。

`vi` 等のあらゆるコマンドが使えなくて詰んでいたのですが、finderから開いて編集・保存すれば解決でした。

▼参考にさせていただいた記事
[zsh: command not found: ls](https://qiita.com/yamauchi_k/items/0f17442bee61b8a921fa )
[Finderで隠しファイル、隠しフォルダの表示／非表示
](https://support.borndigital.co.jp/hc/ja/articles/360005975154-Finder%E3%81%A7%E9%9A%A0%E3%81%97%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB-%E9%9A%A0%E3%81%97%E3%83%95%E3%82%A9%E3%83%AB%E3%83%80%E3%81%AE%E8%A1%A8%E7%A4%BA-%E9%9D%9E%E8%A1%A8%E7%A4%BA)

目からウロコ！

## 教訓
トラブルが起こると焦りがちですが、基本的なところを忘れないよう冷静に対処していきたいと思いました。
あと、パスはよく確認する。
Docker Swarmの証明書周りについてちょっとだけまとめてみた

先日久々に起動したSwarm環境が何やらおかしいということで調べてみると、証明書切れによりSwarmが構成されていない状態になっていました。実はこの辺りの詳細な仕組みを理解していないなと思い、自分の理解のためにも、これを機に少し調べたのでまとめてみました。

# 検証環境

今回の検証環境については下記の通りです。

* OS: Linux (CentOS7)
* docker: docker-ce 20.10.5

# 検証

## Swarmを構成

構成手順については割愛します。証明書関係は`/var/lib/docker/swarm/certificates`配下に作成されます。証明書のデフォルトの有効期間は`2160h0m0s`、つまり90日間です。

## 期限を短くして基本動作確認

基本動作を確認するため、証明書の有効期限を下記のコマンドで短くし、再生成してみます。

```
# docker swarm update --cert-expiry 1h
# docker swarm ca --rotate
# openssl x509 -text -noout -in /var/lib/docker/swarm/certificates/swarm-node.crt | grep Not
            Not Before: Jan 18 00:08:00 2022 GMT
            Not After : Jan 18 02:08:00 2022 GMT
```

これを少し待つと、、、

```
# openssl x509 -text -noout -in /var/lib/docker/swarm/certificates/swarm-node.crt | grep Not
            Not Before: Jan 18 00:38:00 2022 GMT
            Not After : Jan 18 02:38:00 2022 GMT
```

の通り自動で更新されているのが確認できます。ログとしてはこんな感じです。

```
 1月 17 20:38:00 swarm1 dockerd[14932]: time="2022-01-17T20:38:00.000127970-05:00" level=info msg="renewing certificate" module=node/tls node.id=a5v3geh16xvqejhirx3bb2586 node.role=swarm-manager
```

自動更新のタイミングは「Random retry time between 50% and 80% of the total time to expiration」となっており、Not BeforeとNot Afterをベースに算出されます。例えば、今回の例ですと、`Jan 18 01:08:00 2022 GMT`から`Jan 18 01:44:00 2022 GMT`の間で実行されることになります。コードとしては[このあたり](https://github.com/moby/moby/blob/363e9a88a11be517d9e8c65c998ff56f774eb4dc/vendor/github.com/docker/swarmkit/ca/renewer.go#L108)を参照ください。

## エラーの再現

最初の話に戻り、今回落ち入った事象を再現させるためにdockerを一時的に落として、再度起動してみます。起動時は証明書が失効するまで待つか、時間を調整してください。`docker info`を実行すると下記のようにエラーが出るのが確認できます。

```
 Swarm: error
  NodeID:
  Error: error while loading TLS certificate in /var/lib/docker/swarm/certificates/swarm-node.crt: certificate (1 - a5v3geh16xvqejhirx3bb2586) not valid after Tue, 18 Jan 2022 04:31:00 UTC, and it is currently Tue, 18 Jan 2022 22:00:08 EST: x509: certificate has expired or is not yet valid
```

こうなるとmanagerであってもrotateしようにも下記の通りのエラーになります。

```
# docker swarm ca --rotate
Error response from daemon: This node is not a swarm manager. Use "docker swarm init" or "docker swarm join" to connect this node to swarm and try again.
```

## 復旧方法

復旧手順としては
1. すべてのノードの時間を証明書の有効期限内に変更
2. dockerの再起動でSwarmが再構成されていることを確認
3. `docker swarm ca --rotate --cert-expiry <duration>`を実行し、現在時刻でも有効となるように証明書の有効期限を延長
4. すべてのノードの時間を現在時刻に修正
となります。もしくは、許されるならばSwarm再構成です。

# 最後に

今回の話はそもそも長期間運用していれば自動で更新してくれるため、あまり気にしなくても良いですが、検証環境等で久々に起動した際に同じ事象を観測した人の助けに少しでもなればなと思います。
【シンボリックリンク】Linuxで、ファイルを別名ファイルとして使うよう設定したい

#ファイルを別名ファイルとして使うよう設定したい
仕事で上司がやっていました。
シンボリックリンクと言うそうです。
↓こういう表示のやつです。

```1月 18 12:50 test.php -> test_dev.php```

この場合は、test.phpが呼ばれるとき、test_dev.phpが使われます。
windowsにおけるショートカットの、Linux版的なものらしいです。



#こういう理由で使いたい
開発時に**test.php**というファイルを、複数パターン用意したいとします。

試験環境用→ **test_dev.php**
本番環境用→ **test_prod.php**

をそれぞれ用意したとして、
いちいちファイル名を変更して切り替えるやり方だと、
2つファイル名を変更しないといけないので面倒です。

例) **test_dev.phpからtest_prod.phpに名前変更して切り替える**

1. test.phpをtest_dev.phpに名前変更
1. test_prod.phpをtest.phpに名前変更

今どっちを使っているかの確認も、
名前変更されていない方という認識で見ないといけないので、直感的じゃないと思います。

#Linux上でのシンボリックリンクのやり方

Linux上でシンボリックリンクを設定する場合、

```ln 元のファイル名 使いたい名前```

該当ファイルの置いてあるディレクトリに入って上記を実行すると、できます。
上記test.phpの例の場合、

```ln test_prod.php test.php```

と実行すると、

linux上の該当ディレクトリで、以下が表示されます。

```test.php -> test_prod.php```

これで、test.phpが呼ばれるとき、test_prod.phpが使われるようになります。
どちらを使っているかの確認も、上記シンボリックリンクの表記を見れば一目瞭然です。

上書きしたい場合は、-f(-forceでも可)を付けて実行すると、上書きできます。
-fを付けないと、もうあるよ！って怒られます。

```ln -f test_prod.php test.php```
```ln -force test_prod.php test.php```

コマンドについて詳細に知りたい場合は、参考サイトの一番下をご覧ください。

#参考サイト

https://wa3.i-3-i.info/word1151.html

https://qiita.com/takeoverjp/items/bb1576e90a8a495db4b3

https://atmarkit.itmedia.co.jp/ait/articles/1605/30/news022.html
TinkerCADではじめる電子回路

# TinkerCADではじめる電子回路
![tinkercad01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199447/5d1d81e4-ecde-a6f3-0c6e-3a2879a988ac.png)
「Tinkercad は、次世代のデザイナーやエンジニアがイノベーションの基本スキル(3D デザイン、電子機器、コーディング)を身につけるための使いやすい無償 Web アプリです。」という説明文で開始されるAUTODESKが提供しているCAD環境に電子回路シミュレータが存在することをご存じだろうか。
ここでは、LEDとボタンを配置して点滅させる入門回路から、ロジックICを利用した論理回路のシミュレーションまでを紹介する

[TinkerCAD Topページ](https://www.tinkercad.com/)

#目次
[1.はじめての電子回路](#1-はじめての電子回路)
[2.論理回路シミュレータ](#2-論理回路シミュレータ)
[3.NOT AND OR 回路](#3-NOT AND OR 回路)
[4.論理回路応用](#4-論理回路応用)
[5.おわりに](#5-おわりに)

---

#1. はじめての電子回路
(1) Tinkercadで電子回路エディタを開く
① 自分の利用する環境を選んでサインインする
![tinkercad02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199447/5a36fb42-b1fb-2685-66b4-214c13997138.png)
② ダッシュボードから「回路」を選ぶ
![tinkercad03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199447/518ea83d-813c-6cb8-cb9e-e68206304573.png)
④ 「新しい回路を作成」を選択
![tinkercad04 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199447/bceb1ee0-626c-7b7d-2da5-efbebee3be06.png)

(2) 部品を配置する
ブレッドボードにLED　抵抗　ボタンを配置し　電源（ボタン電池など）を用意する
![tinkercad05 (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199447/3859e761-2c69-65b1-a5c9-d2e307d75afe.png)

(3) 結線する
配置した部品の接続箇所をクリックして結線する
必要に応じて線の色を整える
![tinkercad06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199447/58345c01-60b6-8f66-2db1-98d9ae8b545e.png)

(4) 回路を動かす
右上の「シミュレーション開始」をクリック
![tinkercad07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/199447/33d2718a-7ac7-4a95-1c94-b2b514b89758.png)


#2. 基本の電子パーツ
(1) ブレッドボード
(2) 入力
(3) 出力
#3. Arduino
(1) Ｌチカ
(2) センサ
(3) アクチュエータ
#4. 論理回路
(1) AND OR NOT
(2) フリップフロップ
(3) カウンタ
#5. おわりに
MATLABのSymbolic Mathを利用して微分方程式の特殊解を求める(未定係数法で)

MATLABのSymbolic Math，いいですよね．

https://twitter.com/michio_MWJ/status/1482521949628923904

実は担当している微分方程式の授業で，課題や試験問題の検算に利用しています．「対数の発明は天文学者の寿命を延ばした」と称されたように，Symbolic Mathも研究者の寿命をもりもり伸ばしてくれます．

https://twitter.com/konakalab/status/1483008517741363203

ということで，微分方程式の特殊解を求めるサンプルを作って公開しました．数値例は電気回路でお馴染みの，みんな大好きRL回路です．コードはFileExchangeに置きましたのでご自由にどうぞ．

https://jp.mathworks.com/matlabcentral/fileexchange/105345

## コード説明

**RL回路の微分方程式に対する特殊解を求めるサンプル**
変数を定義
`syms Is E_0 R L t A B omega`

特殊解の解の候補を決める
`Is=A*cos(omega*t)+B*sin(omega*t)`

解の候補を回路方程式に代入する．
`diffEq=L*(diff(Is,t))+R*Is-E_0*sin(omega*t)`

$t$の適当な2点で条件式を評価し，未定係数に対する連立方程式を導く．
`tmpEq(1)=subs(diffEq,t,0)`
`tmpEq(2)=subs(diffEq,t,pi/(2*omega))`

未定係数に対する連立方程式を解く
`Asol=solve(tmpEq,{A,B}).A
Bsol=solve(tmpEq,{A,B}).B`

未定係数を代入して簡略化する
`simplify(subs(Is,{A,B},{Asol,Bsol}))`

### 出力の抜粋

Is = $\displaystyle A\,\cos \left(\omega \,t\right)+B\,\sin \left(\omega \,t\right)$

diffEq = $\displaystyle L\,{\left(B\,\omega \,\cos \left(\omega \,t\right)-A\,\omega \,\sin \left(\omega \,t\right)\right)}-E_0 \,\sin \left(\omega \,t\right)+R\,{\left(A\,\cos \left(\omega \,t\right)+B\,\sin \left(\omega \,t\right)\right)}$

tmpEq = $\displaystyle A\,R+B\,L\,\omega $

tmpEq = $\displaystyle \left(\begin{array}{cc}
A\,R+B\,L\,\omega  & B\,R-E_0 -A\,L\,\omega 
\end{array}\right)$

Asol = $\displaystyle -\frac{E_0 \,L\,\omega }{L^2 \,\omega^2 +R^2 }$
Bsol = $\displaystyle \frac{E_0 \,R}{L^2 \,\omega^2 +R^2 }$

ans = $\frac{E_{0}\,\left(R\,\sin\left(\omega \,t\right)-L\,\omega \,\cos\left(\omega \,t\right)\right)}{L^2\,\omega ^2+R^2}$

なるべく手計算の順序をそのまま自動化するように書いてあります．
一般解と特殊解の重複があったりすると面倒なのですが，本稿では言及しません．
もっといい方法をご存じの方はおしえてください．
Payara Micro Gradle PluginのmicroBundleタスクのイケてなさ

# イケてなさ

バンドルされるwarファイルがwarプラグインのデフォルト設定で作られるので、プロジェクト名がそのままコンテキストパスになる。

## なんとかする

```build.gradle
war {
  // /apiをコンテキストパスにするためにwarファイル名を設定する
  archiveBaseName = 'api'
  archiveVersion = ''
}

microBundle.doLast {
  // UberJARの名前はwarではなくプロジェクト設定からとる
  def uberName = "${project.archivesBaseName}-${project.version}"
  def newPath = delegate.getUberJarPath().replace("${war.archiveBaseName.get()}", uberName)
  delegate.getUberJar().renameTo(newPath)
}
```
[rails]楽天APIの商品検索でエラー（Application ID is not defined）

#はじめに
railsのポートフォリオを作成している際に、楽天APIを用いた商品検索機能を実装しようとした際に、
エラーに引っかかってしまい、時間を食われてしまったので備忘録として残します。

#開発環境
ruby '3.0.0'
Rails 6.0.3.4

#参考サイト
以下のサイトを参考にし、楽天API商品検索機能を実装しようと試みました。
https://www.tom08.net/2016-08-29-133539/
https://takoyarosan.hatenablog.com/entry/2018/07/28/211709
https://freesworder.net/rakuten-api-rails/

#エラー内容
![スクリーンショット 2022-01-18 12.07.28（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1874224/e9f0ae4f-0dcb-3e55-e77d-8d2f1812d85d.png)

binding.pryで、でバックしたものの、@itemsの値がエラーとなっており商品検索ができませんでした。

エラーに記載してある「Application ID is not defined 」でググり色々調べたところ、原因は以下でした。

#原因と解決方法
```ruby:config/rakuten.rb
RakutenWebService.configuration do |c|
    c.application_id = '自分のもの'
    c.affiliate_id = '自分のもの'
end
```
「.configuration」の部分の記述がどうやら古いらしく、正しくは「.configure」

```ruby:config/rakuten.rb
RakutenWebService.configure do |c|   #ここの記述
    c.application_id = '自分のもの'
    c.affiliate_id = '自分のもの'
end
```

変更後、サーバを再起動し検索すると楽天API商品検索ができるようになりました。

![スクリーンショット 2022-01-18 12.28.21（2）.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1874224/d26da32c-24aa-a343-dacf-c69f520f9ac7.png)



#終わりに
今回、機能を実装する際に参考にするドキュメントが最新であることを確認することの重要性を学びました。
ただ、エラーに対して対応することができたことについては、自身になりました。
自分と同じようなエラーで困っている方の参考になれば幸いです。
憧れの大画面 ARで１００インチのモニターを出現させる

#ARで巨大な動画再生モニターを出現させる方法です

<img src=https://user-images.githubusercontent.com/23278992/123641992-c8f52800-d85d-11eb-918a-4619071a54c9.gif>

#現実で大きなスクリーンを設置するには制約がある


#ARならどこまでも巨大にできる

#手順

垂直面にはりつくアンカーを作成し、100インチサイズのボックスを設置します。

```swift
let anchorEntity = AnchorEntity(plane: .vertical)
let monitorEntity = ModelEntity(mesh: .generateBox(size: [2.21,0.2,1.24],cornerRadius: 0.02))
anchorEntity.addChild(displayEntity)
arView.scene.addAnchor(anchorEntity)
```

ビデオマテリアルをモニターに貼り付けます。

```swift
let asset = AVURLAsset(url: videoURL)
let playerItem = AVPlayerItem(asset: asset)
let player = AVQueuePlayer(playerItem: playerItem)
let videoMaterial = VideoMaterial(avPlayer: player)
monitorEntity.model?.materials = [videoMaterial]
player.play()
```

#動画と現実のものをインタラクトすることで新しい効果が生まれるかも？

![Jan-18-2022 12-29-53.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/235259/e574cfca-c4a9-194e-6078-c17de291a0e1.gif)


#サンプルコードはこちら

https://github.com/john-rocky/RealityKit-Sampler

🐣
****
フリーランスエンジニアです。
お仕事のご相談こちらまで
rockyshikoku@gmail.com

Core MLやARKitを使ったアプリを作っています。
機械学習／AR関連の情報を発信しています。

[Twitter](https://twitter.com/JackdeS11)
[Medium](https://medium.com/@rockyshikoku)
Wordpressのcode snippetsでhtml,phpを併用する

# 導入
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/698479/ce29897c-10d5-9757-259f-1cfad694d6e1.png)
おなじみのWordpressのPost Snippets機能
直接，function.phpとかわからなくてもphpでコードがかける機能
でもそのままじゃ,phpの機能しか使えない
つまりは
`<?php ?>`
の中身だけしか書けない
phpを書いてたらやりたくなるのは
`<p><?php echo 'Hello world!';?></p>`
である
でもこれを書くと`Error`を吐くのだ

# 本編

たぶん，自動で記入した部分の前後にphpの囲いのコードが書かれるのだと思う

`<? php [自分で書いたコード]?>`

こんな感じ

ということで，
```php
?>
<p><?php echo 'Hello world!';?></p>
<?php 
```

と書けばうまくいく
もしかしたらダメなことかもしれない
ダメだったら教えてください
とりあえず，動いたので，コード書くときにとても楽になりました
[py2rb] split


# はじめに
移植やってます。
( from python 3.7 to ruby 2.7 )
# split (Python)
```python
s = 'a,b,c,d,e'
print(s.split(',', 1))

# ['a', 'b,c,d,e']
```
https://docs.python.org/ja/3.7/library/stdtypes.html#str.split

`Python`の`split`の引数`maxsplit`は**分割する回数**を指定します。

https://www.shoeisha.co.jp/book/detail/9784798163642

独習Python 170p
# split (Ruby)
```ruby
s = 'a,b,c,d,e'
print(s.split(',', 2))

# ["a", "b,c,d,e"]
```
https://docs.ruby-lang.org/ja/latest/method/String/i/split.html

`Ruby`の`split`の引数`limit`は**分割後の個数**を指定します。
「プロを目指す」と「独習」には載っていない情報でした。
# メモ
+ Python の split を学習した
+ 道のりは遠そう
MarkdownをVSCodeで編集する際のオススメ拡張機能

# おすすめ拡張機能

* [Markdown All in One](https://marketplace.visualstudio.com/items?itemName=yzhang.markdown-all-in-one)
    色んな機能が入っている  
    TableのFormatも行ってくれる

* [markdownlint](https://marketplace.visualstudio.com/items?itemName=DavidAnson.vscode-markdownlint)
    割かし厳しめのLinterなので、いちいち警告が表示されて鬱陶しい場合もあるが、
    Markdownを雑に書いてしまうと、GitLabに上げたりPDFに変換したりした際に表示が崩れてしまうことがよくあるので、
    Markdownの書式に則って綺麗に書いた方がトータルでは良いと思う

* [Markdown Table](https://marketplace.visualstudio.com/items?itemName=TakumiI.markdowntable)
    テーブルを良い感じにFormatしてくれる
    日本語の文字列を含む場合はVSCodeのフォントを等幅フォントにしておけば、表示がズレない

## 設定例

```.vscode/setting.json
{
    "editor.fontFamily": "BIZ UDゴシック",
    "editor.formatOnType": true,
    "editor.formatOnSave": true,
    "[markdown]": {
        "editor.defaultFormatter": "yzhang.markdown-all-in-one"
    },
    "editor.codeActionsOnSave": {
        "source.fixAll.markdownlint": true,
    },
    "markdown.copyFiles.destination": {
        "*": "images/${documentBaseName}/"
      }
}
```

### 追記

以前は [Paste Image](https://marketplace.visualstudio.com/items?itemName=mushan.vscode-paste-image) というクリップボードの画像をMarkdownにそのまま貼り付けられる拡張機能も載せていたんですが、  
VSCodeのアップデートで類似機能が標準搭載されたため、不要になりました。  
"markdown.copyFiles.destination" がその設定項目となります。  
設定例を詳しく知りたい人は  
<https://code.visualstudio.com/updates/v1_79#_copy-external-media-files-into-workspace-on-drop-or-paste-for-markdown>
を見てください。
フォームを作ったつもりなのにUnknown field(s) (yyy) specified for XXXが出てしまった時の対処法

## 概説

Modelをうまく使えるよう、特定のフィールドをFormコントロールにしようとして`django.core.exceptions.FieldError: Unknown field(s) (yyy) specified for XXX`が出てしまった時は、XXXのフィールドに対して、Formコントロールで扱っているfield名yyyが存在しているか確認し、どちらか一方に合わせると良いです。

## 環境

環境は以下の通りです。

| 名称 | バージョン |
|:-:|:-:|
| macOS | Monterey(12.1) |
|django|4.0.1|
|  Python | 3.8.9 |
|  brew | 3.3.10 |
|  pip |  21.3.1 |
|  psycopg2-binary | 2.9.3  |
|PostgreSQL|14.1|

## 対処法

`models.py`で定義したフィールド名と、`forms.py`で使っているフィールド名が一致（または存在）しているか確認し、一致（または存在）していない場合は書き換えてFormとModel間で同じフィールド名を使う。

### エラー例

```models.py
class XXX(models.Model):
    title = models.CharField(...)
    content = models.CharField(...)
    photo = models.ImageField(...)

```

```forms.py
class XXXCreateForm(forms.ModelForm):
    class Meta:
        model = XXX
        # ここで'content'の代わりに存在しないfieldである'overview'を入れてしまっている
        fields = ('title', 'overview', 'photo')

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'

```

## 補足

何かの作成画面など、FormクラスからDBへ登録するような仕組みを作りたいとき、
URL->View->Form->Modelの流れを実現しようとします。ModelとFormの間で重複が多い場合はわざわざ`fields`を自作せず、FormModelを継承してフォームを作成した方が楽です。そういった場合に、ModelとForm間で異なったフィールド名を使おうとすると、このエラーが出力されてしまいます。上記の場合、'fields'と敢えて分離した上で、for文で回しているためパッと見わかりにくく注意が必要です。
LaTeXで全角記号を一括で字詰め(疑似カーニング)する方法

# 気になること
日本語論文では、かっこ・コロン等の記号(約物)は全角記号を使います。

ここでは（かっこ）について考えます。

全角かっこを使うとなんだか間延びして見えます。
一方で半角かっこを使うと、ベースラインが下にあるのであまり囲めている感じがしません。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490087/8d5c12c8-7f5e-35e8-b0ba-13831a5f649a.png)


記号を字詰めして、スッキリ見えるようにしましょう。

# newunicodecharを使う
preamble (`\begin{document}`より前の部分) に以下のように記述します。

```latex:preamble部分
\usepackage{newunicodechar}
\newunicodechar{（}{\hspace{-5pt}（} % 全角括弧の空きを詰め、間延び感をなくす
\newunicodechar{）}{）\hspace{-5pt}}
```

と書きます。
`newunicodechar`では、

```latex
\newunicodechar{元の文字}{置換後の文字}
```
で文字が自動置換されるパッケージです。
`\hspace{-5pt}`によって、負のスペース、つまり字詰めを実現できます。

つまり、左全角かっこの前と、右全角かっこの後が字詰めされるというわけです。

これを実行すれば、以下のようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490087/e28814e8-3477-cad9-f1fd-254428fbaa64.png)
前後の空きが小さくなり、バランスが良くなりました！！

# 他にも置換するといいもの
同様に簡単に置換できるものがあります。
## 句読点を置換する
本体ファイルでは「。、」の句読点を使用し、
コンパイルしたPDFでは「．，」と全角ピリオド・全角カンマを使いたいときがあります。
そんなときは、

```latex
\usepackage{newunicodechar}
\newunicodechar{。}{．}
\newunicodechar{、}{，}
```

とプリアンブルに記入すれば、自動で置換されます。

```latex
\documentclass[11pt]{jlreq}
\usepackage{newunicodechar}
\newunicodechar{（}{\hspace{-5pt}（} % 全角括弧の空きを詰め、間延び感をなくす
\newunicodechar{）}{）\hspace{-5pt}}
\newunicodechar{。}{．}
\newunicodechar{、}{，}
\begin{document}
全角かっこは（こんな感じ）になります。

半角かっこは(こんな感じ)になります。

句点を「。」で表し、読点を「、」で表します。
\end{document}
```
句読点を「。、」のままファイルに書いています。

これをコンパイルすると、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/490087/0204bcf4-bbee-ef48-8b7b-2802363fb4e8.png)


このように、句読点が変換されていることがわかります。

句読点が混在しなくなるので、非常に便利ですね！

非常に汎用性が高い方法なので、ぜひ活用してみてください！
WC_Order から取り出せる商品データ

WooCommerce の注文データとなる [wc_get_order($order_id)](https://woocommerce.github.io/code-reference/files/woocommerce-includes-wc-order-functions.html#function_wc_get_order)の関数によって取得できる商品データがちょっとややこしい部分があるので内容を説明します。

## 商品データの取得方法

```php
$order = wc_get_order( $order_id );// $order_id is Post object or post ID of the order.
$products = $order->get_items( 'line_item' );// default is line_item

```

まずは、注文データを注文IDか注文オブジェクトで呼び出します。
そして、WC_Order の[get_items](https://woocommerce.github.io/code-reference/classes/WC-Abstract-Order.html#method_get_items)を利用して注文に含まれる商品データ一覧を取得します。パラメーターに line_item を入れると商品データとなりますが、ブランクでもデフォルトが line_item なので商品データが出されます。ちなみに商品データ以外に以下の変数で注文内のそれぞれが抜き出せます。

* **line_item** 商品データ
* **tax** 税データ
* **shipping** 送料データ
* **fee** 手数料
* **coupon** クーポン

**woocommerce_order_type_to_group**　のフィルターフックで上記の項目が増やすことができますので、拡張プラグイン等で増やされている場合もあるので、フックを使っていないかどうかのチェックをする必要があります。

## 取得できる商品データの詳細

```php
foreach($products as $product){
    echo $product->get_product_id();
}
```
商品データの詳細は上記のように CRUD を利用して取得するのが良いです。
取得できるデータは以下です。

* **id** 注文内での id
* **name** 商品名
* **prodcut_id** 商品ID
* **variation_id** バリエーションID
* **quantity** 数
* **subtotal** 小計
* **subtotal_tax** 小計の税金（四捨五入などの処理済みの数値）
* **total** 合計
* **total_tax** 合計の税金（四捨五入などの処理済みの数値） 
* **taxes** 税金の内訳で四捨五入などの処理がされる前の数値**（_配列_）**

取得できる情報は上記だけですので、カテゴリーやタグなどを取得したい場合はproduct_id から商品データを取得し直す必要があります。
ちなみに商品単価は取得できないので、合計を数量で割るなどの処理が必要になります。

:::note warn
小計(subtotal)と合計(total)は基本的には同じ数値になることが多いですが、様々な処理をする際に異なることがあるので、凝った処理をしている場合などは注意が必要です。
:::

:::note alert
消費税などの処理が、内税などにすると商品単価が小数点の長い数値になったりする（合計が数量で割り切れない）場合もありますので、注意が必要です。なので、個人的には外税設定にすることをお勧めします。
:::

## ちなみに対象の注文 ID が無かった場合

bool で false が返されます。
メモ

#英語で検索するときに使えるフレーズ・記号のメモ#

思い出したくなった時にみる、個人用のメモです。


|  フレーズ  |  英語・記号  |
| ---- | ---- |
|  サービス・ライブラリの比較 | vs <br> alternative  |
|  代替のもの |  alternative|
|  定義 |  definition |
|意味　| meaning | 
| 概念(数学的)| intuitive | 
| サイト内　| site: |
|完全一致　| "" |
|除外　　　|　- |



|  記号  |  英語  |
| ---- | ---- |
|  ! | exclamation mark<br>exclamation point  |
|  @ | at sign<br>at symbol|
| # | number sign<br>hash mark|
|$ | dollar sign|
|% |percent sign|
|^ | circumflex accent<br>caret<br>hat|
|& | ampersand|
|*	|asterisk |
|/	|slash|
|?	|question mark|
|+	|plus sign|
|-	|minus sign<br>hyphen-minus<br>hyphen|
|"	|double quotation mark|
|'	|apostrophe<br>single quotation mark|
|,	|comma|
|:	|colon|
|;	|semicolon|
|~	|tilde|
|_	|underscore<br>underline<br>low line|
|`	|grave accent<br>backquote|
|\	|backslash<br>reverse solidus|
|¥	|yen sign|
|<|	left angle bracket<br>opening angle bracket<br>less-than sign|
|>	|right angle bracket<br>closing angle bracket<br>greater-than sign|
|[	|left square bracket<br>opening <br> square bracket |
|]	|right square bracket<br>closing square bracket
|{	|left curly bracket<br>opening curly bracket
|}	|right curly bracket<br>closing curly bracket
|(	|left round bracket<br>left parenthesis<br>opening parenthesis
|)	| right round bracket<br>right parenthesis<br>closing parenthesis
laravelBreezeにvue.js3ど導入しようとした件

laravel breezeをインストールしたんだけれど、breezeのJSは**Alpine.js**で**Vue.js**が入ってなかったので、
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/22462408-3268-5294-ef66-0252fb115be6.png" width=33%>
####じゃあ、alpine.jsを使えばいいじゃん！####

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/69e2cf87-bce4-443c-1c2d-49daeb4c1192.png" width=33%>

資料が少なすぎて無理・・・

ということで、breezeにvue.js3を導入します！
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/877911b6-fdcc-a491-e774-3b9dd9528bc7.png" width=33%>
###えっ？素直にlaravel/uiを導入しないのかって？###

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/32a9250c-849d-ab36-e4a3-ae08efd3e5d0.png" width=33%">
###ワイは、breezeをつかいたいんや！！###

#というわけで書きました#
```js:app.js
import './bootstrap'

import { createApp } from 'vue'
import flash-message from './components/flash-message.vue'
Vue.createApp(flash-message).mount('#app')

createApp({
    components: {
        flash-message
    }
}).mount('#app')

```

```js:flash-massage.vue
<template>
    <div>
        <h1>Sample Component</h1>
    </div>
</template>

<script>
    export default {
        mounted() {
            console.log('Component mounted.')
        }
    }
</script>

```

```php:app.blade.php
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="csrf-token" content="{{ csrf_token() }}">

        <title>{{ config('app.name', 'Laravel') }}</title>

        <!-- Fonts -->
        <link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700&display=swap">

        <!-- Styles -->
        <link rel="stylesheet" href="{{ asset('css/app.css') }}">

        <!-- Scripts -->
        <script src="{{ asset('js/app.js') }}" defer></script>
        <script src="https://unpkg.com/vue@next"></script>

    </head>
```

```php:dashborad.blade.php
<x-app-layout>
    <x-slot name="header">
        <h2 class="font-semibold text-xl text-gray-800 leading-tight">
            {{ __('Dashboard') }}
        </h2>
    </x-slot>


    <script src="{{ mix('/js/app.js') }}"></script>
    <div class="py-12">
        <div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
            <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg">
                <div class="p-6 bg-white border-b border-gray-200">
                    <div id="app">
                        <flash-message></flash-message>
                    </div>
                </div>
            </div>
        </div>
    </div>
    <script src="{{ mix('/js/app.js') }}"></script>
</x-app-layout>

```
![FireShot Capture 001 - Laravel_ - 4915cddba6f64106825ded0b56bbc2c3.vfs.cloud9.ap-northeast-1.amazonaws.com.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2281946/bb0dff80-ee8d-aa12-a203-4ef95d41cbac.png)

###やったぜ###


#感想#
スマートにnpmでインストールしたかったけれど、エラー多発で断念
vue.js2ならたぶん行けそうな気もするけれど、おとなしくCDN パッケージ で我慢します

これからvueをいじっていじっていじりまくるぞおおおおお

もし、もっといい方法があったら教えてください
AWS LightSailについて インスタンスの立て方とか



#目次
[1.AWS LightSailについて](#aws-lightsailについて)
[2.IAM権限を作成する](#iam権限を作成する)
[3.LightSailインスタンスを立ち上げる](#lightsailインスタンスを立ち上げる)
[4.固定IPアドレス作成](#固定ipアドレス作成)
[5.snapshotsでのバックアップ](#snapshotsでのバックアップ)

#AWS LightSailについて

比較的簡単にwordpress(wp)の環境を作成することができ、全世界に公開できるサービスです。
AWSのよく知られているEC2インスタンスを立ち上げるよりも簡単で、楽に運用が可能になっています。
さらに、EC2は従来課金制ですが、LightSailは定額課金の違いがあります。安いプランで月¥500ぐらいで使用できます。

LightSailの特徴

https://aws.amazon.com/jp/lightsail/features/


#IAM権限を作成する
まず、AWSアカウントを作成したらやることとして、AWS Identity and Access Management (IAM)を作成します。これは、権限を制限したユーザーを作成することができ、仮にアカウント情報が漏洩した場合被害を最小限に抑えることにつながります。

IAMユーザーを作成し、ポリシーをアタッチします。(権限を付与すること)
ポリシーはjsonで、以下のような形になります。

```json
{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "lightsail:*"
            ],
            "Resource": "*"
        }
    ]
}
```
このポリシーを作成しIAMユーザーに付与すれば、LightSailの全てのリソースにアクセスできるようになります。
ユーザーにアタッチの他に、グループにアタッチすることも可能です。
これ以降は、Rootアカウントではなく、IAMユーザーでログインし作業するようにしましょう。

https://lightsail.aws.amazon.com/ls/docs/ja_jp/articles/amazon-lightsail-managing-access-for-an-iam-user

#LightSailインスタンスを立ち上げる
LightSailのインスタンスを立ち上げましょう。
やることはそれぞれ、インスタンスを置く地域、OSの選択、使用するサービス、使用プラン、LightSailの名前を決めます。

例)
Instance location →tokyo region1 (他にOregonやParisに作成できます。)
Pick your instance image →Linux
Select a blueprint →wordpress
Choose your instance plan →$3.5USD (最安値のプランです。)
Identify your instance →wordpress_1

こちらを決めcreate instanceをクリックすればインスタンス作成は完了です。

https://aws.amazon.com/jp/getting-started/hands-on/launch-a-virtual-machine/?sc_icampaign=acq_jp_getting-started-handson-202010-build-web-app-s3-lambda-api-gateway-dynamodb&sc_language=jp&sc_icontent=awssm-6337&sc_iplace=ribbon&trk=ha_ribbon_acq_jp_getting-started-handson-202010-launch-a-virtual-machine

#固定IPアドレス作成
作成すると、123.48.56のようなURL(IPアドレス)が作成され、wpをweb上で見ることが可能になります。
しかし、このURLは一度インスタンスを再起動するとURLが変わってしまいます。
そうならないために、URLを固定します。
LightSailのHomeから、Networkingを選択、staticIPを選択します。
staticIPに固有の名前をつけて作成を行うとすぐに固定されます。
作成することにより、アタッチもできているため、IPアドレスが固定されました。


https://lightsail.aws.amazon.com/ls/webapp/create/static-ip?region=ap-northeast-1

#snapshotsでのバックアップ
snapshotsとはインスタンスのバックアップ機能で、wpとサーバーの両方をバックアップしてくれるものです。
lightSailのHomeからインスタンスを選択後、Snapshotsを選択。
create snapshotsをクリックすれば、手動でバックアップを作成することができます。
Automatic snapshotsのボタンをオンにすれば、自動で作成することができます。
Automatic snapshotsは最大7枚ためておくことが可能で、自動作成にしておくと1日おきに作成されます。
つまり、1週間分のバックアップの作成が可能になります。
最大7枚なので、古いものから自動削除されていきますので、ご安心を。(今日が月曜なら、先週の月曜は自動削除されるようになっています。)

最初、ec2と一緒で自動削除の設定をしないとダメなのかと思っていたが、やらなくてよかった。。。
ただ、毎日撮るので、1週間に一回取るとかは無理ぽいよね。


#感想
light sailの情報がいまいちなかったのが、やはり一番だなぁと思った。aws公式の情報もあったので助かったが、やはりligt sail使っているところは少ないんだろうなぁ。
ec2使ってました。はいいけど、light sail使ってました！はキャリアになるのだろうか..
neosでlogix その48

#概要

niosでlogixやってみる。
Impulse Multiplexerノード、使ってみた。

#写真

![2022-01-18 00.30.18.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18104/bcb9c729-86af-e080-3a20-d121077c20ca.jpeg)

#Impulse Multiplexer

```
Inputs
 	Impulse	Trigger
 	Int	Index
Outputs
 	Impulse	[0]
 	Impulse	[1]
```

以上。
pkを設定しているはずなのに何故かAttributeError at /XXX/<値>/が出てしまう時の対処法

## 概説

pk(id)は渡っているはずなのになぜか`AttributeError at /XXX/<値>/`が出てしまう時は
views.pyの`pk_url_kwarg`とurls.pyのパス間で整合性が取れていないことが原因です。

## 環境

環境は以下の通りです。

| 名称 | バージョン |
|:-:|:-:|
| macOS | Monterey(12.1) |
|django|4.0.1|
|  Python | 3.8.9 |
|  brew | 3.3.10 |
|  pip |  21.3.1 |
|  psycopg2-binary | 2.9.3  |
|PostgreSQL|14.1|

## 起きている問題

idはきちんと受け渡して、URLは通っているはずなのに開けずエラーとなってしまいます。
`AttributeError at /XXX/<値>/`
例外を確認すると`Generic detail view XXXView must be called with either an object pk or a slug in the URLconf.`と出ていますがhtmlでは`{{ オブジェクト.pk }}`を読み出せています。

```urls.py
from django.urls import path
from . import views

app_name = 'XXX'
urlpatterns = [
...
    path('YYY/<pk>/', views.XXXDetailView.as_view(), name='YYY'),
]
```

## 対処法

`pk_url_kwarg`とpkとして扱いたい文字を統一します。具体的には、`urlpatterns`のpath内で使うpkをそのまま使いたい時はView内のフィールド`pk_url_kwarg`に変更を加える記述を削除します。また、固有の名称(idなど)としたい場合は`urlpatterns`のpath内のpkを固有の名称に書き換えます。


## 補足

### 修正例（pkをそのまま使いたい場合）

```views.py
...
class XXXDetailView(LoginRequiredMixin, generic.DetailView):
    model = Bike
    template_name = 'xxx_detail.html'

```


```urls.py
from django.urls import path
from . import views

app_name = 'XXX'
urlpatterns = [
...
    path('YYY/<pk>/', views.XXXDetailView.as_view(), name='YYY'),
]
```

### 修正例2（固有の名称を使いたい場合）

```views.py

...
class XXXDetailView(LoginRequiredMixin, generic.DetailView):
    model = XXX
    template_name = 'xxx_detail.html'
    pk_url_kwarg = 'id' #固有の名称を使いたい場合
```


```urls.py
from django.urls import path
from . import views

app_name = 'XXX'
urlpatterns = [
...
    path('YYY/<id>/', views.XXXDetailView.as_view(), name='YYY'),
]
```
qt creator c++ slot

qtcreator signal/slotについて色々

# 1.slotをqtcreatorで設定した時のプログラムの変化

qtcreatorでslotを設定したときのプログラムの変化を調べました.

1.qtcreatorでプロジェクトを作成しbuttonを配置
2.buttonのクリック時,リリース時のスロットを生成

1と2でのプログラム変化は以下のようになりました.

```patch:diff

--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -13,3 +13,13 @@ MainWindow::~MainWindow()
     delete ui;
 }
 
+
+void MainWindow::on_pushButton_clicked()
+{
+
+}
+
+void MainWindow::on_pushButton_released()
+{
+
+}
diff --git a/mainwindow.h b/mainwindow.h
index 4643e32..92e4836 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -15,6 +15,11 @@ public:
     MainWindow(QWidget *parent = nullptr);
     ~MainWindow();
 
+private slots:
+    void on_pushButton_clicked();
+
+    void on_pushButton_released();
+
 private:
     Ui::MainWindow *ui;
```

uiからのsignalを受け取る時はconnectなど必要なく,on_"部品名"_"signal名"のslotを作成するだけで良いみたいです.

# 2.originalのslotを定義して使う方法

1.pushButtonを配置
2.プログラムを以下のように変更して,slotの設定 + pushButtonのsignalをつなぐ

```patch:diff
diff --git a/mainwindow.cpp b/mainwindow.cpp
index a06fc8e..8460b1d 100644
--- a/mainwindow.cpp
+++ b/mainwindow.cpp
@@ -6,6 +6,10 @@ MainWindow::MainWindow(QWidget *parent)
     , ui(new Ui::MainWindow)
 {
     ui->setupUi(this);
+
+    connect(ui->pushButton, SIGNAL(clicked()),
+            this, SLOT(print_hello()));
+
 }
 
 MainWindow::~MainWindow()
diff --git a/mainwindow.h b/mainwindow.h
index 4643e32..a38d7fd 100644
--- a/mainwindow.h
+++ b/mainwindow.h
@@ -3,6 +3,8 @@
 
 #include <QMainWindow>
 
+#include <iostream>
+
 QT_BEGIN_NAMESPACE
 namespace Ui { class MainWindow; }
 QT_END_NAMESPACE
@@ -17,5 +19,10 @@ public:
 
 private:
     Ui::MainWindow *ui;
+
+private slots:
+    void print_hello(){
+        std::cout << "hello!" << std::endl;
+    }
 };
 #endif // MAINWINDOW_H
```


(参考)

https://qiita.com/argama147/items/30da69e4c1088917d522

# 最後に

qtの部品間の接続にはsignal/slotを使用するという感覚を持っていると部品ごとの切り分け開発ができてきて,
大きいプログラムも作成できるようになりそうです.

また,qtcretorで生成されるmainwindowもqtの部品であるという感覚も重要そうです.

そして,その中に部品を入れていく場合もsignal/slotで接続するという感じみたいです.
ユーザーのキーボード配列を変更する

# 目的
Windowsでログインしているユーザーのみ、キーボード配列をUS配列へ変更する。

# 手順
regeditを開く。

`\HKEY_CURRENT_USER\Keyboard Layout\Preload`へ移動する。

名前:`1`のキーのデータを`00000409`へ変更する。

完了。再起動後に反映されるかも。

# US配列以外を利用する
regeditで`\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Keyboard Layouts`を見れば対応するデータがわかる。

# 参考
https://social.technet.microsoft.com/Forums/en-US/40a3e9f9-f1ed-45cf-9969-95b866ec8f8c/changing-the-keyboard-layout-for-the-computer-and-user-as-a-whole?forum=winserverGP
Macbook ProのUSBアダプタを6個も破壊した話

#うちのMacにはUSB-Aのアダプタがない
Appleは流行りに乗りたがる企業である。時に自分で流行りを作ることもあるし、乗っかった流れがいまいち大きくならずに会社を傾けたこともしばしばだ。
そんなわけで私のMacbook Pro 2017にはUSB-A（普通のUSB端子のこと）がないのである。だからUSBメモリその他を使うために変換アダプタが必要なのだが、最初は公式のアダプタを買って使っていたのである。しかし使っているうちに中の配線がいかれてしまい、断線してしまった。公式のやつはグニャグニャと曲がるタイプだったので曲がりにくいやつを今度は買おうと思った。
アマゾンで色々な商品を見ているうちに最小サイズの変換アダプタが見つかった。
こいつなら断線はしないだろうと思って買ったところ、コンパクトで使いやすかったのだが、USB-C特有の問題が見つかった。
それは小さな端子の穴に挿すアダプタなので、てこの原理で小さな衝撃で簡単に端子部分が曲がってしまうということだ。私は布団の上なんかでもよくMacをいじるので雑な体勢でよくアダプタをぶつけてしまい、この一年間で計6個も壊してしまったわけである。
そんなわけで初心に帰り、最初の公式のやつと同様のグニャグニャとした余裕のあるアダプタに変えたところ、接触不良や端子の破損などが起こることなく使えている。もちろん公式のやつではない。
まあ要するに、TypeーCはでかい機器を繋げるとてこの原理で壊れやすいということだ。
userSettingsを暗号化する

## 事前準備
1. プロジェクトの参照設定に`System.Configuration`を追加
1. プロジェクトの設定に設定項目を追加
    * 暗号化されるのはスコープがユーザーの項目が対象です。

## 暗号化
アプリケーションの起動のどこかで以下を行ってください。

```vb:Form1.vb
Public Class Form1
	Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
		Const provider As String = "DataProtectionConfigurationProvider"
		Dim config As Configuration.Configuration = Configuration.ConfigurationManager.OpenExeConfiguration(System.Configuration.ConfigurationUserLevel.PerUserRoamingAndLocal)
		Dim sectiongroup As Configuration.ConfigurationSectionGroup = config.GetSectionGroup("userSettings")
		Dim count As Integer = 0

		For Each section As Configuration.ClientSettingsSection In sectiongroup.Sections
			If Not section.SectionInformation.IsProtected Then
				section.SectionInformation.ProtectSection(provider)
				count += 1
			End If
		Next

		If count > 0 Then
			config.Save()
		End If
	End Sub
End Class
```

## user.configの場所
上記コードで`config.Save()`を呼び出されると以下のフォルダに暗号化されたファイルが作成されます。
> %localappdata%\\`AssemblyCompany`\\`AssemblyTitle`.exe_Url_`生成された値`\\`version`\\user.config

内容を確認すると暗号化されているのがわかります。

```xml:user.config
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <userSettings>
        <CnfgEncrypt.My.MySettings configProtectionProvider="DataProtectionConfigurationProvider">
            <EncryptedData>
                <CipherData>
                    <CipherValue>AQAAANCMnd8BFdERjHoAwE/Cl+sBAAAA1xVCoWYWb0q+dkt7bnGzegQAAAACAAAAAAADZgAAwAAAABAAAABAlOcG9igpl6nbcz1EaIGNAAAAAASAAACgAAAAEAAAAPVGNEE0CHNXArdywbF5sNtAAAAAJp2f/6iMcPoxietfeA6tgPS0KFT3U/U0JscezvRRIEhZW/xO1OLzjwVRC6ZQyt0fkeApI5OvV2xmx9S/7bUd2hQAAACS4J1y2jOoJ8zV2OZUGGVomg1NUQ==</CipherValue>
                </CipherData>
            </EncryptedData>
        </CnfgEncrypt.My.MySettings>
    </userSettings>
</configuration>
```

## 免責
ドシロートなんで、暗号の強度なんかについてはわかりません。
よって、責任も持てません。
【python】psycopg2のcpyコマンドで詰まった



# 概要

``` python
sio.seek(0)
cur.copy_from(sio , table='hoge.hogehoge', sep=',', null='') 

# Raise Exception: relation "hoge.hogehoge" does not exist
```

search_pathにもスキーマあるやろ！
selectできるのになんでや！

# 解決（してないけど）

``` python
sio.seek(0)
cur.copy_from(sio , table='hogehoge', sep=',', null='') 

# Raise Exception: value too long for type character(12)
```

スキーマ名抜かしたらあっさり動いた。
そういうもんなんですか？？？
エラー出てるけどこれはテーブル直すのでヨシ。

tmpテーブルでやりくりするほうがいいのかしらん。
本日もデータベースと仲良くなれない。
plotly.py を使用したときに ModuleNotFoundError: No module named 'statsmodels'

## 現象
plotly.py を使ってチャートを書こうとしたときに ModuleNotFoundError: No module named 'statsmodels' というエラーがでる

## 原因
plotly.py では依存するモジュールを必要最低限だけインストールするようになっており、利用するチャートの機能のよっては追加でモジュールをインストールする必要がある。例えば、散布図にトレンドラインを引くときには statsmodels パッケージが必要となるが自動ではインストールされないために別途インストールする必要がある

## 対応方法
statsmodels を別途インストールする

```
pip install statsmodels
```

## 参考
https://community.plotly.com/t/solved-unable-to-add-trendlines-to-subplots-following-the-same-code-on-plotly-website/38020