OmniAICreator/Qiita-1.07M huggingface dataset:
【MLOps】docker/mlflowを使った競馬AI開発環境構築（2021年版）

## どうしてこの記事を書こうと思ったのか
*自分専用のAI開発環境が欲しい！*
きっと誰もが思うことでしょう。かくいう私も競馬AIを開発したいというモチベーションのもと、AI開発環境の構築で試行錯誤を繰り返しました。そしてこの度納得のいく環境を構築できたので備忘も含めて構築手順を残そうと思います。
今回はDockerとmlflowが主な技術要素なんですが、その理由はクラウドを意識したからです。もともとクラウドに構築したかったけど課金されるのは嫌だな→Dockerならクラウド移行が簡単だ、という思いからDockerを採用することにしました。そして、機械学習開発の課題の「実験結果の管理」と「学習済みモデルの管理」を解決するために、mlflowの導入を決めました。この環境を構想から構築するまでに3ヶ月ほど費やしたので、きっと同じ環境を作りたい人がいるだろうなと思い、この記事を執筆することにしました。

※2021/5/12現在、記事は執筆中で順次コンテンツを増やしていく予定です。
※2021/11/17記事を更新しました。

## 今回構築する環境構成

![全体アーキテクチャ (1).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69277/ce23ec94-0ed9-61be-e75b-fd7a550fba43.png)

今回構築する環境は、UIとしてWebブラウザ（Chromeなど）とVScodeを使い、ローカル環境にPython実行環境を構築し、コンテナ環境にデータベースや機械学習の実験モデル管理環境を構築しています。
この環境を構築することで、AIの開発から運用までスムーズに回すことができるようになります。

今回はMac上に環境を構築しましたが、anacondaもdockerもWindowsやLinux環境でも同様に構築できると思われます。（稼働確認は行なっていません）

## 環境構築手順

次のような流れで環境構築を行います。

[python実行環境構築] (https://qiita.com/kazuki502/items/70825438fdf02979f890)
1. ローカル環境にanacondaをインストールする
2. tensorflowをインストールする
3. optunaをインストールする
4. mlflowをインストールする

[VScode環境構築](https://qiita.com/kazuki502/items/e3e8a555c14c5b9bd628)
1. ローカル環境にVScodeをインストールする
2. 拡張機能をインストールする

[docker実行環境構築](https://qiita.com/kazuki502/items/10147bdeabb5c17e06cf)
1. ローカル環境にdockerをインストールする
2. 分析用データ保存用MySQLとデータ参照用Adminerを設定する(docker-compose)
3. mlflowとデータベース・リポジトリを設定する（docker-compose）
S3導入時に発生したSSHKit::Command::Failed: bundle exit status: 1の解決策

#はじめに
S3を導入した際にエラーが発生したため，その経緯と解決策を備忘録として残します．

#作業環境
- Ruby 2.6.5
- Ruby on Rails 6.0.0
- 本番環境(EC2)
  - NGINX
  - Unicorn
  - MySQL

#経緯
S3を導入し，本番環境に反映させようとしたときに下記のエラーが発生した．
```SSHKit::Command::Failed: bundle exit status: 1
```

#エラー発生の原因
本番環境にAWSのアクセスキーとシークレットキーを追加していなかった．

#解決策
本番環境で環境変数を保存しているファイル(アプリ名/etc/environment)にAWSのアクセスキーとシークレットキーを追加する．
こうすると無事本番環境にデプロイすることができた．

#おわりに
環境変数の設定は忘れやすいので注意したい．
【Javascript】高階関数(higher order functions)をかじってみた

# 初めに
javascriptを学習したら、高階関数という概念が出てきたのでそれについて自分なりに理解した内容をまとめてみました。

※内容に間違いなどがある場合はご指摘をよろしくお願いします。

# 高階関数とは
関数の引数に関数を取る関数のこと。英語ではhigher order functionsという。

# なぜ使うのか
一度定義した関数に自分なりに味付けをして細かい内容を追加したりしてカスタマイズができるメリットがあるから。その他にも理由があるらしい。

# 取り敢えず使ってみた
数字をブラウザに表示するシンプルな関数を定義しました。

```javascript
  <script>

    function printNumber(inputNumber) {
      document.write(inputNumber);
    }

    printNumber(1)
  </script>
```
![スクリーンショット 2021-05-12 8.23.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966135/5977e64f-da42-ec5f-39c2-e7830e892d6d.png)
ブラウザに数字を表示するこの関数の機能をそのままに残して、足し算や掛け算した結果を表示するように拡張してみたいという場面がありますよね。早速高階関数を定義して使ってみます。

```javascript
function calcNumber(val1, val2, printNumber) {
      const sumNumber = val1 + val2
      const multiNumber = val1 * val2
      printNumber(sumNumber);
      printNumber(multiNumber);
    }

    calcNumber(3, 4, printNumber);
```
![スクリーンショット 2021-05-12 8.30.40.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966135/abeb85c4-7b34-8864-4242-92c1f5fd8422.png)
calcNumberという高階関数を定義して、その引数としてval1,val2と先ほど定義したprintNumberを入れました。printNumberはただ数字をブラウザーに表示するだけですが、calcNumberは足し算した結果や掛け算した結果が表示されます。

# 高階関数はjavascriptだけのものではない
実はこの高階関数という概念は他の言語にもあります。pythonで高階関数を試してみました。

```python
def printVal(callId):
  print(f"プリントするのは{callId}ですね。")

def calc(val,height, printVal):
  printVal(val * height)

calc( 2 , 3, printVal)
```
画面に文字列を出力する普通の関数printValを定義します。そしてval,heightという引数の他にprintValという関数も引数にしています。
![スクリーンショット 2021-05-12 8.35.04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/966135/28041491-3aca-f53d-56e8-582bcad39785.png)
結果はちゃんと掛け算した結果を出力してくれます。

#終わりに
高階関数は関数を引数として使える便利なものです。いろいろと応用できそうなので、試してみてはどうでしょうか。

# 参考記事
https://se-tomo.com/2019/02/06/%E3%80%90javascript%E3%80%91%E9%AB%98%E9%9A%8E%E9%96%A2%E6%95%B0%E3%81%A8%E3%82%B3%E3%83%BC%E3%83%AB%E3%83%90%E3%83%83%E3%82%AF%E9%96%A2%E6%95%B0%E3%81%AB%E3%81%A4%E3%81%84%E3%81%A6%E7%90%86%E8%A7%A3/
https://qiita.com/elipmoc101/items/c34b38ddbbd9b52d0a4f
https://qiita.com/may88seiji/items/8f7e42353b6904af5e9a
【LT】エンジニア目線で考える「サービスの質」を考える

コンテンツは陳腐化しましたが、考え方は重要だなと思ったんで公開します。
体調を崩してしまったため、予定していたテーマは体調が戻り次第執筆します。
スライド形式にしているため、読み物としては情報が少なくなってしまっている事をご了承ください。

##### 自己紹介

```
{
  id: "@nomurasan",
  name: "中　翔",
  job: "フルスタックリードPM",
  hobby: "イクメンおぢさん",
}
```

---

# 「サービスの質」とは

---

##### 今まで学んだこと

- Java : 動くアプリの作り方
- SQL  : DBを使ったデータ管理
- HTML : 画面設計・UI(ユーザーインターフェース)
- インフラ: コマンド、IP・ポート
- その他、新人研修

---

##### 今日から学ぶこと

- JSP: HTMLとJavaをつなぐもの
- Servlet: JavaをWebアプリケーションにするもの

どちらもJavaの(※)フレームワークです。

---

##### やること

1. 環境構築
1. ブラウザ接続（JSP-Servlet）
1. ブラウザからJavaへデータを渡す
1. Javaからブラウザへデータを渡す
1. ojdbc（ServletでDB接続）

---

##### ちょっと待った！

- システムの作り方は分かった
- 画面の作り方は分かった
- 画面とシステムの連携はこれから勉強する

システム開発はできそう
システムは作ったら終わりなのか？

---

# Q. 良いサービスとは何か:thinking:

---

# A. カリキュラムでは教えてくれません

---

### では、どうするか？

自分たちで考えるしかない

---

# テーマ

Web開発演習で書籍管理システムを作る時にどういう事を考えれば使ってもらいやすいものになるのか

---

### 今日のヒント

Servlet/JSP開発はVSCodeでもできます

参考: 

- [Mac](https://qiita.com/harhogefoo/items/2fa52ecee90b7a25e4c9)
- [Windowsの場合は、各インストーラの情報をこちらに読み替えてください](https://www.suzu6.net/posts/131-vscode-for-maven/)

---

## 次の記事
- [(13日目) ソフトウェア開発者目線で見る、among us界隈のMOD作者の氏名BAN問題にみるソフトウェアの取り扱い](https://qiita.com/nomurasan/items/1c690d8455a26aa22b8a)
- [カレンダーページ](https://qiita.com/advent-calendar/2022/oreno_nomurasan2022)
UiPathの個人的備忘録

自分が過去に「そうだったのか…」と思ったUiPathの技術・事柄をまとめた記事です。

#1.「Send Hotkey」の特殊入力
・[k(enter)] → enterキーを押す
・[d(enter)] → enterキーを押しっぱなしにする
・[u(enter)] → enterキーを離す
・[d(ctrl)]ac[u(ctrl)] → 全選択してコピーする
・Shift + f10 → 右クリックメニューを表示する

#2.入力ダイアログをアクティブ状態にする
InputDialogの直前に、「Windowsデスクトップをターゲットに、”k(ctrl)”を入力するTypeInto」を置くと、入力ダイアログがアクティブ状態になり、ダイアログへの記入が楽になる。
![入力ダイアログ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1401870/fd45a06a-7980-47a2-94fb-515d77fbcada.jpeg)

#3.WriteCellを使った連続記入
![WriteCell.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1401870/62a809df-cfe5-8a2d-129f-f880fa1d9ca4.png)

#4.相対パスの注意点
Studioからの実行時とRobotトレイからの実行時では、ルートパス(=起点)が異なっている。
そのため、相対パスでファイル指定を行うと「Studio環境では問題なかったのに、Robot環境でやるとエラーになる」という現象が発生する。よってフルパス指定が望ましい。
![相対パス.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1401870/0300a379-70e0-4fc5-b0c9-03b770ea0818.jpeg)

#5.似てるけど全然違うものなんです
人間の目では同じ文字に見えても、UiPathから見ると全く違う文字である…という事は多々ある。(例：「円マークの『\』と『¥』(※半角と環境依存)」「-とｰ(※半角ハイフンと半角長音)」)
それらの見分け方の１つとして、普段使わないエンコードで書き出してみる…というのがある。
![円マーク.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1401870/a23d900a-b6bb-6441-e11b-8c38728b2250.jpeg)

#6.列のindex数→列名に変換する
「Chr(65)=A, Chr(66)=B,…」を利用して、1桁ずつASCII文字コードで変換させる。
![インデックス.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1401870/b6f7ead3-4794-c883-2b57-8ab09b687d0e.jpeg)

#7.日付の小ネタ
※「Today=4月30日」であると定義する。
・Today.ToString("MM") → 04
・Today.ToString("M月") → 4月
・Today.ToString("M") → April 30
・Cint(Today.ToString("MM")) → 4 (※日付の「月」単体で、且つ1桁で出力したい時に)

#8.保護シートへの編集
保護されたエクセルシートに書き込みを行いたい場合、Workbook配下のアクティビティを使えば上手くいく事がある。
![保護シート.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1401870/3f6002fa-bb92-37e2-3712-2c23e9b8c583.jpeg)

#9.範囲指定の小ネタ
Range系アクティビティの範囲指定では、「,」を使えば個別にセルを、「数字:数字」と記載すれば行自体を指定する事ができる。
![範囲指定.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1401870/387da0b8-2f36-1a53-e9e5-6c16ba12ebe4.jpeg)

#10.配列型→データテーブル型に変換
配列型データをデータテーブル型に変換したい場合、「Generate DataTable」を使うのが手軽です。
![配列１.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1401870/40048f50-bcc7-9a84-55c4-fe4b5c6b63ba.jpeg)
例えば、System.IO.Directory.GetFilesで取得したファイルのフルパス情報(※配列型)を、
データテーブルとしてエクセルに書く事もできる。
![配列２.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1401870/5b71fbc1-094e-d6d2-0e9b-a13838605652.jpeg)
【JavaScript】「背景黒」ボタンを押すと、ページ全体の背景色が黒で文字色が白、「背景白」ボタンを押すと、ページ全体の背景色が白で文字色が黒となるようプログラムを書け

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954863/7b359ce3-a7a2-b3fe-eebb-1ed086b04c67.png)![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954863/964baf6b-d9b2-83f1-4102-6f755dbfd07c.png)


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/954863/9ebf5581-d7e7-703f-cb68-65d84407e1ce.png)

```javascript 
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>実行タイミング</title>
    <script>
        /* 課題処理を追加 */
        function background_black(){
        document.body.style.color='white';
        document.body.style.backgroundColor = "black";
        }
        function background_white(){
        document.body.style.color='black';
        document.body.style.backgroundColor = "white";

        }
        window.onload = function(){
        var black = document.getElementById("black");
        var white = document.getElementById("white");
        black.addEventListener("click",background_black,false);
        white.addEventListener("click",background_white,false);
        }

    </script>
</head>
<body>
    <p>ボタンクリックにより背景色と文字色を変化</p>
    <button id="black">背景黒</button>
    <button id="white">背景白</button>
</body>
</html>
```
ReactでLinkタグにstyled-componentでスタイルをあてたいとき

React開発でstyled-componentを使用してスタイリングしていた際に、react-routerのLinkを使った時にどのようにCSSをあてるのか少し詰まったので自分用で記事にしておきます。

## styled-componentの書き方
基本の書き方 

```react
<ThreadTitle>スレッド一覧</ThreadTitle>

export const ThreadTitle =styled.h2`
font-weight:bold;
font-size:20px;
`

```

ThreadTitleという定数を定義して、ラップしてあげる。
今回でいうと`ThreadTitle`でラップされたスレッド一覧が`h2`タグで囲まれたことになり、スタイルがあたります。

`a`タグでも同様のことができると考え、下記のように記述しましたが、うまくいかず、、

```react
<ThreadLink>リンク先</ThreadLink>

export const ThreadLink = styled.a`
	text-decoration: none;
	color: blue;
	font-size: 20px;
	margin: 5px 0;
	&:hover {
		opacity: 0.7;
		color: red;
	}
`;
```

結論、下記のように書き換えることで正しくCSSをあてることができました。

```react
<ThreadLink>リンク先</ThreadLink>

export const ThreadLink = styled(Link)`
	text-decoration: none;
	color: blue;
	font-size: 20px;
	margin: 5px 0;
	&:hover {
		opacity: 0.7;
		color: red;
	}
`;
```


■参考
https://styled-components.com/docs/basics#styling-any-component
git reset と git checkout って何が違うの？

こんにちは。
先日タイトルの2つの違いがわからなかったので、色々調べた。
実務で使ったことないので、ズレは正直あるかもしれないが、今の理解をまとめる場とする。
また、ごちゃつかないように、端的にを意識して記述している。

# どこで使うか
この2つのコマンドは「インデックスやワークツリーの①何かを②何かの状態に戻すとき」に使われる。

①と②はそれぞれ下記の候補が出てくる。

①
- 特定のファイル
- 全てのファイル

②
- HEAD or インデックス
- 過去のコミット

# 各コマンドをgit helpで見ると

### git reset --help

```
NAME
       git-reset - Reset current HEAD to the specified state

SYNOPSIS
       git reset [-q] [<tree-ish>] [--] <pathspec>...
       git reset [-q] [--pathspec-from-file=<file> [--pathspec-file-nul]] [<tree-ish>]
       git reset (--patch | -p) [<tree-ish>] [--] [<pathspec>...]
       git reset [--soft | --mixed [-N] | --hard | --merge | --keep] [-q] [<commit>]

DESCRIPTION
       In the first three forms, copy entries from <tree-ish> to the index. In the last form, set the
       current branch head (HEAD) to <commit>, optionally modifying index and working tree to match. The
       <tree-ish>/<commit> defaults to HEAD in all forms.
```

要約すると、

1. HEADからインデックスへコピーをし、**オプション**で作業ツリーへコピーする。
1. HEADを指定のコミットに移動し、**オプション**で移動先のコミットからインデックスとワークツリーへコピーする。

もっと簡単に言えば、今の**HEADを特定された状態へ移動**し、それをオプションでインデックスとワークツリーにコピーする。
オプションとは

1. --hard : インデックスとワークツリーに反映（今あるファイルが指定されたHEADに同期されるので注意）
1. --mixed(default) : インデックスのみに反映
1. --soft : 反映しない。（HEADだけが動く。)


### git checkout --help

```
NAME
       git-checkout - Switch branches or restore working tree files

SYNOPSIS
       git checkout [-q] [-f] [-m] [<branch>]
       git checkout [-q] [-f] [-m] --detach [<branch>]
       git checkout [-q] [-f] [-m] [--detach] <commit>
       git checkout [-q] [-f] [-m] [[-b|-B|--orphan] <new_branch>] [<start_point>]
       git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] [--] <pathspec>...
       git checkout [-f|--ours|--theirs|-m|--conflict=<style>] [<tree-ish>] --pathspec-from-file=<file> [--pathspec-file-nul]
       git checkout (-p|--patch) [<tree-ish>] [--] [<pathspec>...]

DESCRIPTION
       Updates files in the working tree to match the version in the index or the specified tree. If no
       pathspec was given, git checkout will also update HEAD to set the specified branch as the current
       branch.
```

要約すると

1. **ワークツリーのファイル**をインデックス、ま他は指定されたコミットのバージョンと**一致**させる。

# 違いのキーワード

- reset
    - **過去のコミット**から**反映**させるとき
    - インデックス**のみ**の変更
- checkout
    - ワークツリー**まで**変更する。
    - ワークツリー・インデックス・HEADの3つを**一致**させる。
    - 過去のコミットからの状態を**「試したい」**とき。
        - ブランチに**影響を与えたくない**とき。


# 図でまとめた

![スクリーンショット 2021-05-12 7.03.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255565/7bc47afa-86cd-63a2-5801-1f1cce252813.png)


ここまで理解すれば、あとは実践あるのみだと私は感じている。
今後実務で更新点があれば随時行っていこうと思う。
実務で使っている方でぜひ違う場所や、アドバイスがあればいただきたい。

以上。
自己教師あり学習(SSL)関連。論文『Barlow Twins: Self-Supervised Learning via Redundancy Reduction』(2021)を、SSLに慣れるために眺める。

# SSL(Self-Supervised Learning)に慣れるために眺める

以下の論文(**Yann LeCun**含む)をSSLに慣れるために眺める。

Barlow Twins: Self-Supervised Learning via Redundancy Reduction
https://arxiv.org/abs/2103.03230

Zbontar, J., Jing, L., Misra, I., LeCun, Y., & Deny, S. (2021). Barlow twins: Self-supervised learning via redundancy reduction. arXiv preprint arXiv:2103.03230.

以下、論文の概要抜粋
><font color="orange">**A successful approach to SSL is to learn representations which are invariant to distortions of the input sample. However, a recurring issue with this approach is the existence of trivial constant solutions.**</font>

上記のような観点の場合、
素人（ワタシ）が、読める領域はあまりないと思いますが。。。


# まず、論文の概要
以下、論文引用。

>Self-supervised learning (SSL) is rapidly closing the gap with supervised methods on large computer vision benchmarks. A successful approach to SSL is to learn representations which are invariant to distortions of the input sample. However, a recurring issue with this approach is the existence of trivial constant solutions. Most current methods avoid such solutions by careful implementation details. We propose an objective function that naturally avoids such collapse by measuring the cross-correlation matrix between the outputs of two identical networks fed with distorted versions of a sample, and making it as close to the identity matrix as possible. This causes the representation vectors of distorted versions of a sample to be similar, while minimizing the redundancy between the components of these vectors. The method is called BARLOW TWINS, owing to neuroscientist H. Barlow’s redundancy-reduction principle applied to a pair of identical networks. BARLOW TWINS does not require large batches nor asymmetry between the network twins such as a predictor network, gradient stopping, or a moving average on the weight updates. It allows the use of very high-dimensional output vectors. BARLOW TWINS outperforms previous methods on ImageNet for semi-supervised classification in the low-data regime, and is on par with current state of the art for ImageNet classification with a linear classifier head, and for transfer tasks of classification and object detection.

DeepL訳
>自己教師付き学習（Self-Supervised Learning: SSL）は，大規模なコンピュータビジョンのベンチマークにおいて，教師付き手法との差を急速に縮めている．SSLでは，入力サンプルの歪みに影響されない表現を学習することが成功の秘訣です．しかし，この手法では，些細な定数解が存在するという問題が繰り返し発生します．現在のほとんどの手法では、実装の詳細に注意することで、このような解を回避している。我々は、サンプルの歪んだバージョンを与えられた2つの同一のネットワークの出力間の相互相関行列を測定し、それを可能な限り恒等行列に近づけることで、このような崩壊を自然に回避する目的関数を提案する。これにより、歪曲されたサンプルの表現ベクトルは、その成分間の冗長性を最小限に抑えつつ、類似したものになります。この方法は、神経科学者H.Barlowの冗長性削減の原理を、同一のネットワークのペアに適用したことから、「BARLOW TWINS」と呼ばれています。BARLOW TWINSは、大規模なバッチを必要とせず、予測ネットワーク、勾配停止、重み更新の移動平均など、双子のネットワーク間の非対称性も必要ありません。また、非常に高次元の出力ベクトルを使用することができます。BARLOW TWINSは、ImageNetにおける低データ領域での半教師付き分類において、従来の手法よりも優れた性能を発揮し、ImageNetの線形分類器ヘッドによる分類や、分類と物体検出の転送タスクにおいて、現在の技術水準と同等の性能を発揮しています。


# まとめ
特にありません。
（まだ、書き始めです。）
AWS Athenaの奇妙な仕様（ADDとDROPで構文が違う？）


### ADD PARTITIONとDROP PARTITIONの構文違いに注意
AWS Athenaの奇妙な仕様（？）を発見しました。

ALTER TABLE 構文の ADD PARTITIONとDROP PARTITIONで構文が違う（ADDはスペース区切り、DROPはカンマ区切り）じゃないと通らないようでした。
＊2021/5/12時点

ADDは通るのに、DROPが落ちていることがわかり、行き着いた結果。

```SQL
'ADD PARTITIONはスペース区切り'
ALTER TABLE ADD PARTITION(pt=1) PARTITION(pt=2)
'DROPはカンマ区切り'
ALTER TABLE DROP PARTITION(pt=1),PARTITION(pt=2)
```

AWS公式ドキュメント
▼ALTER TABLE ADD 
https://docs.aws.amazon.com/ja_jp/athena/latest/ug/alter-table-add-partition.html

```SQL
ALTER TABLE orders ADD
  PARTITION (dt = '2016-05-14', country = 'IN') LOCATION 's3://mystorage/path/to/INDIA_14_May_2016/'
  PARTITION (dt = '2016-05-15', country = 'IN') LOCATION 's3://mystorage/path/to/INDIA_15_May_2016/';
```

▼ALTER TABLE DROP 
https://docs.aws.amazon.com/ja_jp/athena/latest/ug/alter-table-drop-partition.html

```SQL
ALTER TABLE orders 
DROP PARTITION (dt = '2014-05-14', country = 'IN'), PARTITION (dt = '2014-05-15', country = 'IN');
```

ご注意ください。
sqlite3でsplit（もっと簡単に）

## この記事で説明すること

SQLアンチパターンでいうところのジェイウォークパターンになっているデータを、SQLiteを使って正規化する方法を説明します。

次の記事では再帰クエリを使って実現していますが、SQLite で JSON1 拡張が使える場合はもう少し簡単に実現可能です。

https://qiita.com/qoAop/items/528d24cad2dced436811

## 方法

- 関数を駆使してJSON配列に変換する。
    - `\` を `\\` に、 `"` を `\"` に置き換える。
    - 区切り記号 `,` を `","` に置き換える。
    - 周りを `["` と `"]` で括る。
- `json_each` テーブル値関数[^1]を使ってJSON配列の値を読み出す。

[^1]: SQLiteのテーブル値関数 (table-valued function) とは、関数のように使える仮想テーブルのことで、FROM節中でJOINして使うことができます。 [The Virtual Table Mechanism Of SQLite 2.1.2. Table-valued functions](https://sqlite.org/vtab.html#table_valued_functions)

```
CREATE TABLE kv (k, v);

INSERT INTO kv VALUES
    ('key1', 'this,is,a,pen'),
    ('key2', 'hello,world'),
    ('key3', 'I,am,a,boy'),
    ('symbols', '",\,'''),
    ('null', NULL);

SELECT * FROM kv;

SELECT kv.k, sv.value AS v
FROM kv
LEFT JOIN json_each(
    '["'
    || replace(replace(replace(
            kv.v,
            '\', '\\'),
            '"', '\"'),
            ',', '","')
    || '"]'
) as sv;

```
組織管理されているAWS環境下のアクセスアクティビティからIAMポリシーを自動生成する

IAMユーザーやIAMロールの CloudTrailログ から実際に利用されたIAMポリシーを生成する機能。
CloudTrail の設定によってはうまく動作しないので調査メモと回避策を書いておきます。

- [IAM Access Analyzer makes it easier to implement least privilege permissions by generating IAM policies based on access activity \| AWS Security Blog](https://aws.amazon.com/jp/blogs/security/iam-access-analyzer-makes-it-easier-to-implement-least-privilege-permissions-by-generating-iam-policies-based-on-access-activity/)

## IAM Access Analyzer

まず前提知識として。
よく理解できていないが、IAM Access Analyzer は最低でも２種類の機能を持っていて、それぞれで求められる権限が違う。今回はポリシー生成のほうの話。

- アナライザーがリソースをモニタリングしてポリシーを分析する機能
  - [AWS IAM Access Analyzer を使用する \- AWS Identity and Access Management](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/what-is-access-analyzer.html)
- CloudTrailログを確認してポリシーを生成する機能
  - [アクセスアクティビティに基づくポリシーの生成 \- AWS Identity and Access Management](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_generate-policy.html?icmpid=docs_iam_console)


## Control Tower 管理下

CloudFormation StackSet `AWSControlTowerBP-BASELINE-CLOUDTRAIL` によって CloudTrail の有効化が行われています。
CloudTrail ログの保存先は `Log archiveアカウント` の S3バケット が指定されています。

そのため IAM Access Analyzer はクロスアカウントで CloudTrailログ を読み取る必要がでてきます。

権限がない状態でポリシーを生成しようとするとエラーとなります。

![access_policies_generate_error.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135041/f881afb0-cca5-1240-69d5-5fee61e8c691.png)

`Log archiveアカウント` の S3バケットポリシー を変更してクロスアカウントアクセスを許可すればいけそうですが、さらに問題が。

[必須のガードレール](https://docs.aws.amazon.com/ja_jp/controltower/latest/userguide/mandatory-guardrails.html#disallow-policy-changes-s3-buckets-created) である `ログアーカイブで AWS Control Tower が作成した S3 バケットのバケットポリシーの変更を許可しない` があり、 SCP によりバケットポリシーの変更ができません。

つまりSCPを一時的に解除してバケットポリシーを編集、クロスアカウントアクセスを可能にして、ようやくポリシーが生成できます。
どうにもAWSの推奨設定から外れまくっている感じしますね。

## Organization 管理下

- [AWS CloudTrail と AWS Organizations \- AWS Organizations](https://docs.aws.amazon.com/ja_jp/organizations/latest/userguide/services-that-can-integrate-cloudtrail.html)
- [アクセスアクティビティに基づくポリシーの生成 \- AWS Identity and Access Management](https://docs.aws.amazon.com/ja_jp/IAM/latest/UserGuide/access_policies_generate-policy.html?icmpid=docs_iam_console)

> AWS Organizations – Organizations CloudTrail 証跡を使用してポリシーを生成することはできません。

なんと、Organizations CloudTrail 証跡を使っているとポリシー生成できません！
たまにある推奨構成で組むと新機能が使えないジレンマ。

![access_policies_generate_error2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135041/14ce4f6b-6948-69d0-df79-6bf91aec4e00.png)

## 回避策

AWSの推奨事項を抑えつつポリシー生成を行うには、別途 CloudTrail証跡 を作るしかないかと思います。

2つ目なので費用がかかってしまいますが、常時チェックし続けるものでもないので、短期間だけ証跡作ってポリシー生成したら削除するのがよさそうです。

## 思うところ

各アカウントの CloudTrail が90日間のログを持っているはずなので（GuardDutyなどが見ているやつ）、S3 の CloudTrailログ へアクセスしなくてもポリシー生成できそうなものなんですが、それができない仕組みなんですかね。
まだ出て日が浅い機能なので今後のアップデートに期待しましょうか。
VSCodeを再起動して更新したいのにJS/TS言語機能の初期化が終わらない！

(VSCodeを使ってたある日のこと...)
おっと、どうやら更新の通知が来たようだ。..ってアレ？ステータスバーに**「JS/TS 言語機能を初期化しています」** てのがずっとグルグルしてるんだけど、これいつ終わるんだ？？
そのまま再起動していいのかもしんないけどちょっと怖いな...
でもこのまま終わるの待ってるのも嫌だな...

という訳で、何かしらの解決方法があるかもしれないのでググってみた。



# 結論
どうやらこれは、VSCodeにデフォルトで搭載されている**組み込み拡張機能**というものらしい。これが一体どういうものなのか、なぜ初期化の作業がずっと続いているのかは不明。しかし、無効/有効を切り替える方法は分かったので、一旦はこれで対処することにした。



# 前提条件
- macOS Catalina ( 10.15.7 )
- [Visual Studio Code.app](https://code.visualstudio.com/) ( 1.56.1 )

※あくまでこのブログ執筆時のバージョン


# 本題
### 組み込み拡張機能を無効化
1. `commnad + shift + x`で拡張機能のサイドバーを開く。
2. 検索ボックスに`@builtin`と入力。
3. 今回はJS/TSの言語機能なので、『機能』項目内の**TypeScript and JavaScript Language Features**というものを選択。
4. 他の拡張機能と同様に、ワークスペース内、若しくはVSCode全体で無効/有効の切替可能。
5. 無効にしたら一度再起動して、上記1~3を繰り返し有効にし直す。

---
一度これも「初期化してまーす」と通知が来たんだけど、ステータスバーを非表示にしていたら多分気付かずにそのまま再起動してたんだろうなぁ。まぁ警告メッセージとかは出たのかもしんないけど。



# 参考記事
- [特定のプロジェクトで「JS/TS言語機能の初期化」を無効にするにはどうすればよいですか？](https://www.fixes.pub/program/184682.html)



# 関連投稿記事
- [VSCode関連で独自にちょっとだけカスタマイズした環境設定を晒す(part. 1/3) - Qiita](https://qiita.com/tsutsumin_pro/items/d03a61db36577ad23686)
- [VSCode関連で独自にちょっとだけカスタマイズした環境設定を晒す(part. 3/3) - Qiita](https://qiita.com/tsutsumin_pro/items/79ef5eaac6a38d4af085)
Hexドキュメントでnを押そう、?を押そう、gは便利ですよ(Elixir)

# はじめに
- [Elixir](https://elixir-lang.org/)楽しんでいますか:bangbang::bangbang::bangbang:
- 小ネタです
- 2021/04/22に開催された「[Elixir Digitalization Implementors #4：OSSお披露目会](https://fukuokaex.connpass.com/event/208331/)」で聞いたのがはじめてだとおもいます
    - おもいます
    - とにかく[Elixir](https://elixir-lang.org/)の会合で見聞きしたのがはじめてです
- 私は`g`が便利なのでよく使うようになりました

# なにはともあれ、`n`を押してみましょう
- https://hexdocs.pm/elixir/Kernel.html

![スクリーンショット 2021-05-12 2.52.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131808/e610efef-2afe-f087-9da6-1651d7da0ca3.png)

![スクリーンショット 2021-05-12 2.52.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131808/f5e188c8-0115-04d0-2e9c-a5faf7f4c76d.png)

- `n`を押すたびにNight mode or notが切り替わります

# 他にどんなショートカットがあるの？
- `?`を入力してみましょう

![スクリーンショット 2021-05-12 2.53.53.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131808/005c3869-9c52-ab29-0e8f-d5c7946922bc.png)

- ショートカットの一覧がでてきます

# `g`は便利ですよ
- よくありそうなシーンとして[HTTPoison](https://hexdocs.pm/httpoison/readme.html)をみてて、[Jason](https://hexdocs.pm/jason/readme.html)をみたくなることありませんか
    - 私は、ブラウザタブをもうひとつ開いて、`jason elixir`と打ち込んで検索するというような操作をしていました
- そんなときは`g`を押してみてください
- 検索窓がでてくるので`jason`と打ち込むと候補がでてきます
- もちろん`jason`を押すと、[Jason](https://hexdocs.pm/jason/readme.html)のドキュメントに飛びます

![スクリーンショット 2021-05-12 2.55.45.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131808/bb080d29-5013-6f0c-49d8-4b3156f54bc6.png)

# `?`自体を忘れてしまった
- スクロールして画面の下まで行ってください
- `Display keyboard shortcuts`リンクでキーボードショートカットの一覧が表示されます

![スクリーンショット 2021-05-12 2.58.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/131808/9ce8837c-01c7-9ea6-77d4-e3450924f96b.png)

# Wrapping up :lgtm::lgtm::lgtm::lgtm: 
- `n`でお好みのモードでドキュメントを確認してください
- `g`であっちのHexもみたいんだよなー　へすっとジャンプしてください
- Enjoy [Elixir](https://elixir-lang.org/) :bangbang::bangbang::bangbang:
Visual Studio 2005

## 参考

http://www.devdrv.co.jp/hidaka/node/71

## Visual Studio

### バージョン一覧

| version | 備考 |
|:--|:--|
| 97 | version 5.0 |
| 6.0 | - |
| 2002 | Javaのライセンスの問題で入手困難 |
| 2003 | Javaのライセンスの問題で入手困難 | 
| 2005 | 64bit対応とエラーチェックエンジンの変更により、2005より前のソースは修正しないとビルド出来ない。 | 
| 2008 | - | 
| 2010 | XPにインストールできる最後のバージョン | 

### Visual Studio 2005 でのエラーについて

ライブラリのAPIが64bit対応向けに一部更新されたため修正が必要。
エラーチェックも厳しくなった。
このバージョンからMSDNから入手できる。 

> 例えば afx_msg のリターン型が UINT から LRESULT に変わったので、次の定義と関連するすべての部分する必要がある。

```diff
- afx_msg UINT OnNcHitTest(CPoint point);
+ afx_msg LRESULT /*UINT*/ OnNcHitTest(CPoint point);
```

> コンパイルのエラー関連の動作は、この Visual Studio 2005 で一度大きく変わっただけで、その後はあまり変化がない


## ドライバー

> Windows XP には今でも署名が無いドライバーがインストール可能である。また WDF に関しては KMDF/UMDF ともに、Windows 7 用の V1.9 までが Windows XP SP3 にはインストールして動作させることが可能だ。しかし一方で Windows Vista 以降では、カーネル共有メモリーやユーザー空間へのDMA不可などの制約がある等、ドライバーのアーキテクチャが一部変わったため、全面的に作成し直しが必要なドライバーもある。

Windows Driver framework (WDF)

* Windows 7 用の V1.9 までが XP にインストールして動作させることが可能。
* Vista 以降ではドライバーが大きく変わった。
phpでTrying to access array offset on value of type null エラーと軽めの対処法

dev環境では起きず、手元のローカル環境でのみ起きていた事象。
phpのバージョンにより発生するもので、mampでphp7.3環境では起きず、7.4では発生しておりました。
エラー名で原因はすぐ見つかりましたが、直し方どうするかな〜とちょっと迷いました。

###◼️原因
特定の変数のデータを取得しようとした際に、
その変数の中身が入っていないにもかかわらずデータをとろうとすると、
7.3以前では nullを返し、
7.4以降では タイトルのエラーが出てしまう状態でした。

```php
//発生の例

$test; //test変数を作成

if($test["hoge"] == "huge"){ //そもそも$testには何も入れていない状態
 echo $test["hoge"];
}

```

この場合、タイトルのエラー
#### ◼️Trying to access array offset on value of type null
が画面に表示します。

この場合、まずは$testにデータが存在しているかを判定すると良いです。

```php

$test; //test変数を作成

if(isset($test)){ //ここでまず$testにデータが入っているかを判定
 if($test["hoge"] == "huge"){ //そもそも$testには何も入れていない状態。なので
  echo $test["hoge"]; //ここは通りません。
 }
}

```

ただ、これだとちょっとネストで読みづらいので下記の方法の方がわかりやすいです。

```php
//例

$test; //test変数を作成

if(isset($test) && $test["hoge"] == "huge"){ 
  echo $test["hoge"]; 
}

```
phpの&&の複数条件式は左側から評価をし、falseとなった時点でその式を抜けます。
この例なら「isset($test)」の時点でfalseなので、次へ進むということになります。
AWS EC2でwordpressをユーザーデータで立ち上げる


#この記事でできること
  後半に記載の前準備後に、ユーザーデータを利用して、wordpress AWS EC2でコピペだけでを立ち上げられる

###先に結論
これをユーザーデータにコピペするだけ

```:ECインスタンスのユーザーデータにコピペするbash（バッシュ）


#!/bin/bash

echo "===sudu su -==="
#以下、管理者権限
sudu su -

echo "===yum -y update==="
# インストールされている　すべてのソフトの　アップデート
yum -y update

echo “===amazon-linux-extras install php7.2 -y===”
# レポジトリからphp7.2 install yes
amazon-linux-extras install php7.2 -y

# wordpress(PHP)に必要なソフトをインストールする
echo “===yum -y install mysql httpd php-mbstring php-xml gd php-gd===”
yum -y install mysql httpd php-mbstring php-xml gd php-gd

echo "===systemctl start httpd.service==="
#アパッチを起動
systemctl start httpd.service

echo "===systemctl enable httpd.service==="
#アパッチ再起動後も実行を有効
systemctl enable httpd.service

# wget(HTTP/(S)でダウンロード) 最新のwordpress tar.gz形式
#　オプション-O がないと保存先がひとつ前になる、なぜ(?)
echo “===wget http://ja.wordpress.org/latest-ja.tar.gz -O /usr/local/src/latest-ja.tar.gz===”
wget http://ja.wordpress.org/latest-ja.tar.gz -O /usr/local/src/latest-ja.tar.gz

echo "===cd /usr/local/src/==="
#ディレクトリ移動  ダウンロードは一般にここに入れるらしい
cd /usr/local/src/

#tar.gz形式の最新ワードプレスjpを展開。 z(gzipファイル)をx(展開)v(詳細を表示する）f(ファイルの指定）
echo”===tar zxvf latest-ja.tar.gz===”
tar zxvf latest-ja.tar.gz

#カレントディレクトリにあるwordpressとサブディレクトリすべてを /var/www/html/のしたにコピーする -r(コピー元にディレクトリを指定した場合、再帰的に（サブディレクトリも含めて）コピーする
echo”===cp -r wordpress/* /var/www/html/===”
cp -r wordpress/* /var/www/html/

#/var/www/htmlの権限をapacheの所有者apacheのユーザーにする　-R(再帰的に変更)
echo”===chown apache:apach -R /var/www/html===”
chown apache:apache -R /var/www/html
```

インスタンス起動後数分すると・・・
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/646649/313f9f87-a1dc-f666-c065-5b8749ff6241.png)

***

上記を実行する前には、下記の準備を行っておく必要があります。


##前準備
あくまで例です

###VPC
10.0.0.0/21
###subnet
(サブネット把握しやすいように第3オクテットをNameの末尾の数字とした）

RDSでマルチAZ構成のため異なるAZにpublic,privateサブネットをそれぞれ用意する

 ap-northeast-1a　Name:PublicSubnet0   10.0.0.0/24
 ap-northeast-1a　Name:PrivateSubnet2  10.0.2.0/24
 ap-northeast-1c　Name:PublicSubnet1   10.0.1.0/24
 ap-northeast-1c　Name:PrivateSubnet3  10.0.3.0/24

###インターネットゲートウェイ
VPCにアタッチ

###ルートテーブル
サブネット　PublicSubnet0　のルートテーブルで　0.0.0.0/0　でインターネットゲートウェイを指定

###RDS

サブネットグループを作成




RDSを作成

WordPressにはMySQLが必須。WordPressではMySQLまたはMariaDBが推奨されています。 多くのレンタルサーバーはデータベースにMySQLを採用しています。
ということなので、MySQLを選択

VPCセキュリティグループは、新しく　RDS-SG　の名称で作成する　 
(マスターユーザー名：wordpress、インスタンス安いのに変更、vpc指定、サブネットグループ指定、Azは1a、あとは大体デフォルト）


###セキュリティグループ
テストなので今回はHTTPのみ。本番はHTTPS＋AWS HTTPS 証明書 ACM＋(ELB or Route53)ご利用ください。

Name:Web-SG で以下で作成
インバウンドルールを編集
HTTP	TCP	80	0.0.0.0/0	–
HTTP	TCP	80	::/0	–
SSH	TCP	22	0.0.0.0/0	–

RDSで作成したセキュリティグループ　RDS-SG　を以下に変更
インバウンドルールを編集
既存IPアドレス削除し、Web-SGを指定する。

###EC2
（やっと出番です）
最初に示したバッシュをユーザーデータにコピペして、セキュリティグループWeb-SG でインスタンスを起動！！！！


#はまったこと
sudo su - がないと権限が足りない
wget http://ja.wordpress.org/latest-ja.tar.gz -O /usr/local/src/latest-ja.tar.gz のオプションOがないとcloud-init-output.log でScheme missing.表示で失敗する。


#まとめ
sudo su
cat /var/log/cloud-init-output.log
がめっちゃ役立つのよくわかった



【ハンズオン1】基本的なブログサービスを構築する（シングル構成）
AWSCloudTech
molecule.ymlにinventoryで設定している値を取り込む方法の検討(検討中)

# TBD(検討中)

test-kitchenではkitchen.ymlにおいてerbテンプレートエンジンが使えたので、外部から構造化された値を取り込んで利用できた。
moleculeで同様の事を行うにはどうすれば良いか。

#環境変数
環境変数は参照して使用できる様子。
https://molecule.readthedocs.io/en/latest/configuration.html#variable-substitution

しかし環境変数では構造化されたデータを扱うことは困難か。
フロースタイルYAMLなどとして1行にして連携すればできない事もないか。。

#moleculeコマンドへの引数
引数をわたせる様子


https://molecule.readthedocs.io/en/latest/usage.html#cmdoption-molecule-env-file-foo.yml-converge-arg-molecule

```
# molecule --env-file foo.yml converge
```

プロセス置換がつかえるシェルなら、一時ファイルを使用せずにインベントリーの内容を連携できそう

```
# molecule --env-file <(ansible-inventory -i ../../inventory --yaml --list) converge
```
【令和3年5月11日公表値反映】「消費支出」と「ネットショッピングの⽀出額」を、Tableauの複数テーブルのリレーションシップを使い可視化

##2021年3月分更新
先日、[データ集計の準備をしました](https://qiita.com/text_owleyes_0214/items/c734720fec72ee5f2d99)。
5月11日に総務省統計局から、**[「家計調査報告」（消費支出）](https://www.stat.go.jp/data/kakei/sokuhou/tsuki/index.html)**と**[「家計消費状況調査」（ネットショッピングの支出額）](https://www.stat.go.jp/data/joukyou/12.html)**の3月分が公表されました。
[「e-Stat」（政府統計の総合窓口）](https://www.e-stat.go.jp/)からデータを取り出し、Tableau Public上で更新しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1143963/33c2cd70-9765-71dc-6cc5-4d821180d9d3.png)

折れ線グラフも更新されました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1143963/e7f9732e-ea67-e543-b62f-564832d18ae3.png)

**「簡易計算表」**を使い、**前年同月比**も計算しました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1143963/50d6246c-95bc-3ffe-ba77-1685aaf2bdd4.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1143963/54f01cd1-238e-0061-596a-7204cd5f4c0f.png)

**了**
UE4.25～26でのエディタ表示不具合（ちらつき）の解消法

#はじめに
揮発性の話題になると思いますが、これはエディタを10分～1時間ほど使い続けたときに発生する、

+ メニューウインドウやツールチップ・右下の通知ウインドウの表示がチラついたり、一瞬しか表示されない
+ サブウインドウ上でメニューウインドウを出すと、メニューウインドウのサイズでサブウインドウがくり抜かれる
+ メインウインドウのサイズを変えると真っ黒になる

以上の現象を解消するために行った対処法です。

約3ヶ月ほど苦しめられたので情報共有として記事にした次第です。
※`ue4 menu flickering` (flickering は "ちらつく" のこと)で検索すると、様々なユーザーが悩まされているのを確認することができます。

#発生する現象の対象環境

+ Windows10
+ NVIDIAグラフィックドライバーの v457.09 **より後**のバージョン

#解消法
対策はいくつかあるようですが、どうやら環境依存が強いようです。
Windows Update や NVIDIA グラフィックドライバーの状況によって起きたり起きなかったりするようです。
※ UE4.12 でも似たような現象が起こっていたとの情報もあり

##対処その1 - グラフィックドライバーのダウングレード

https://answers.unrealengine.com/questions/1009277/view.html

上の回答内では**NVIDIAグラフィックドライバーを v457.09 にダウングレード（クリーンインストール）する**ことで解消できたようです。
私の環境ではRTX3060ですので、そもそもそのバージョンがありませんでした...
なお、**最近のグラフィックドライバーで脆弱性が発見されている**ので、ダウングレードはおすすめできません。

##対処その２ - UE4をDirectX12で動作させる

https://forums.unrealengine.com/t/how-to-active-directx-12/66814/5

上のトピックは単純にDirectX12を有効にするにはどうすればよいか？の内容ですが、**私が解決できた対処**になります。

+ ue4editor.exe の起動コマンドラインに `-dx12` を追加する
+ もしくは、UE4エディターの プロジェクト設定 → プラットフォーム → Windows → ターゲットのRHI → デフォルトのRHI を、"DirectX 12" に指定し、**エディターを再起動する**

以上でエディターが DirectX 12 で動作します。（DirectX 11では再現しました）
ただし、グラフィックボードによっては負荷が高く、ファンが唸るかもしれません。レイトレーシング対応グラフィックボード(RTXシリーズ)であれば大丈夫っぽいです。

試していませんが、他の RHI（Vulkan など）でも解消すると思います。
もしかしたら環境によっては逆（DirextX 11を指定しないと解消しない）かもしれません。

ちなみに解消はされているものの、大体同じようなタイミング以降、特定のポップアップ検索ウインドウのサイズが微妙に（1pxほど）震える現象を確認しています。

##対処その３ - 最終兵器「OS再インストール」

https://forums.unrealengine.com/t/context-window-flickers-when-right-clicking/157303

上のトピック主は、最終的に OS を再インストールしたら直ったようですが、解決の最終手段と見るべきでしょう。（UE4 Editorだけの不具合ですので我慢できるならしたほうが良いかもしれませんし）

##対処？その４ - エディタを再起動
解消するまでだましだまし使う方法ですね。
現象が発生したら、早やかに編集を全て保存し再起動するだけです。

操作が面倒な方は、以下のプラグインの使用を検討しても良いかもしれません。(Ctrl+Shift+Rで一発)

https://www.unrealengine.com/marketplace/ja/product/restart-editor

**無料**ですが、執筆時点で UE4.25 までしか対応していませんので、以下の記事を参考にすれば UE4.26 でも使えます。

https://qiita.com/Mitsunagi/items/c76f29fa224594c98c55


#最後に
今回発生している現象は、[Unreal Engine Issues](https://issues.unrealengine.com/) で報告( [UE-113639](https://issues.unrealengine.com/issue/UE-113639) ) されてはいるものの、**再現せず**となっており、修正が困難、もしくは放置される可能性があります。

おそらくシェーダーコードに潜在的バグが入り込んでいて、ふとした時に直ったり再発するんじゃないかなーと個人的に思っています。
Codeforces Round #510 (Div. 2) D. Petya and Array 座圧とセグメントツリー

https://codeforces.com/contest/1042/problem/D

思ったより実装に時間かかった。

# 題意
- $n \leq 10^5$個の各要素 $- 10^9 \leq a_i \leq 10^9 $からなる配列$a$が与えられます。
- この連続している部分配列の和が、$- 10^{12} \leq t \leq 10^{12} $未満のものは全部でいくつあるか？（配列は重複あり、ソートなし)

# こう考えた
問題は言い換えると、区間$[l, r)$の部分和が$t$未満の部分和となる、$l,r$の数を求めたい。と言えます。

累積和を取りセグメントツリーに入れます。つまり、$a_0$からある$a_i$までの和をindexとして、出現回数を値にするのセグメントツリー$st$を持ちます。ただし、値は負を取りうることと、値の範囲がとても大きいため、座圧を行います。例えば、
例題を例にとります。$[5,-1,3,4,-1]$であれば、累積和は$[5,4,7,11,10]$となるので、座圧のためのテーブル$[4,5,7,10,11]$を持ち、$1,0,2,4,3$に変換します。
この時、stは$[1,1,1,1,1]$というテーブルになります。

ここで、$l=0$と固定したとします。この時、$t=4$であると、累積和のテーブルの中でt未満となる数を列挙すればよいので、上記で計算した(累積和のカウントを座圧した数値をセグメントツリーにしている)stにクエリすればよいです。クエリの対象は、$t=4$未満ですが、座圧後の値にする必要があります。$t=0$とは、座圧するとindexが$0$です。$st.query[0,0) = 0$が答えになります。

つぎに、$l=1$と固定したとしましょう。まず、この処理に入る前に、$i=0$に対応する累積和の要素を消して、セグメントツリーの該当要素を$-1$します。
この時、再度、累積和を計算したくなりますが、これを繰り返すと$O(N^2)$の時間がかかります。このため、逆に、$t$を累積和に合わせて変更します。さて、最初に計算した累積和$[5,4,7,11,10]$ですが、$l=1$から累積和を行うと$[-1, 2, 6, 5]$です。さて、これを見ると、最初の累積和の2要素目以降から1要素目の$5$を引いたものがわかります。累積和の性質を考えると1要素目が抜けたため、明らかです。
ということは、$l=1$のとき<b>最初に求めた各要素からa_0の要素の値(この場合は5)を引いて、t=4未満</b>の要素であればいいので、言い換えれば、<b>最初に求めた各要素からa_0の要素の値がt=9未満</b>であればよいです。
ここで、座圧テーブル$[4,5,7,10,11]$で$9$未満のindexを考えると$3$です。この際、lower bound(Pythonならbisect left)で考えます。このまま、[l, index)のクエリを行うと、まるで、$l=0$の時のクエリと同じように、$l=1$の時の組み合わせが求められます。

図に示すと次のようになります。
<img src=https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30938/c2be2009-c613-a6d5-e509-9048fc468f68.png width=50%>

# 実装

```Python3
def do():
    st = segmentTreeSum()
    n, t = map(int, input().split())
    dat = list(map(int, input().split()))
    dattotal = []
    total = 0
    segtreeList = [0] * (200000 + 10)
    zatsu = set()
    for x in dat:
        total += x
        dattotal.append(total)
        zatsu.add(total)
    zatsu = list(zatsu)
    zatsu.sort()
    zatsuTable = dict()
    zatsuTableRev = dict()
    for ind, val in enumerate(zatsu):
        zatsuTable[val] = ind
        zatsuTableRev[ind] = val
    buf = []
    for x in dattotal:
        buf.append(zatsuTable[x])
        segtreeList[zatsuTable[x]] += 1
    st.load(segtreeList)

    from bisect import bisect_left, bisect_right

    offset = 0
    res = 0
    for i in range(n): # x = total from 0 to curren
        x = dattotal[i]
        curvalind = zatsuTable[x]
        targetval = t + offset# target val
        targetind = bisect_left(zatsu, targetval)
        cnt = st.query(0, targetind )
        res += cnt
        st.addValue(curvalind, -1)
        offset += dat[i] # for next offset
    print(res)
do()
```
https://codeforces.com/contest/1042/submission/115954571
【iOS】Safariの広告をブロックしたい 。

#hostsを編集。【要牢破り】


#### hostsファイル
[**hosts**](https://ja.m.wikipedia.org/wiki/Hosts)ファイルは[DNS](https://ja.m.wikipedia.org/wiki/Domain_Name_System)より優先されるので代替サーバーのアドレスを記載したりしますが、目的のサーバーをデバイス自身にする事で、ネット上にサーバーが見つからないようにできます。

<details><summary> 例 </summary>
例えば[**Qiita**](https://qiita.com/)のアドレスを次のようにhostsに書き加えると、Qiitaにアクセスできなくなります。
`
127.0.0.1 localhost
::1 localhost
127.0.0.1 qiita.com
::1 qiita.com`
<details><summary> 解説 </summary>
- IPv4のアドレス`127.0.0.1`とIPv6のアドレス`::1`を`localhost`として宣言してるので、`qiita.com`へ接続する際に`localhost`を探すことになりますが、デバイス自身である`localhost`には`qiita.com`の情報がないので、Safariで[**Qiita**](https://qiita.com/)にアクセスすると、
**「ページを開けません。Safariはサーバに接続できませんでした。」**
と表示されQiitaにアクセスできなくなります。
</details></details>

このしくみを応用して、**埋め込まれた広告**のアドレスを`localhost`に書き換えるように記述すると、WEBページは閲覧できてるのに**埋め込まれた広告**は表示されなくなります。

***

#### hostsファイルの編集
iOSの場合`/etc/hosts`にあり、プロパティは以下の内容です。

| 所有者 | グループ | パーミッション |
|:-:|:-:|:-:|
| root | wheel | 0100644 |

バックアップをとって、新しくhostsファイルを作っても反映しなかったので、追記することにしました。

広告ドメインのリストからhostsファイルにコピペして保存します。

以下の例では、**amazon**が表示されなくなります。

```:etc/hosts
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting. Do not change this entry.
##
127.0.0.1 localhost
255.255.255.255 broadcasthost
::1 localhost

# ここから追記
127.0.0.1 www.amazon.co.jp
::1 www.amazon.co.jp
```
**ReSpring**すると反映されます。

***


#### Filza File Manager
超便利なファイルブラウザ。
[<img width="72" alt="image.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280129/c1e6bd21-37e7-e2c0-d59f-abe2143bb2a7.png">　　[<img width="128" alt="Image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280129/5e24d42c-b9a1-7b27-b096-b091f3735f06.png">](//cydia.saurik.com/api/share#?source=http://tigisoftware.com/cydia/)](http://tigisoftware.com/cydia/)





***
#### 広告ドメインのリスト
ありがたいことに、広告のドメインをまとめたファイルを配布してるサイトがたくさんあります。

- [GitHub - iVmj2ef/hosts_iphone](https://github.com/iVmj2ef/hosts_iphone/)
- [日本環境向け 広告除去用hosts - ログろいど](http://logroid.blogspot.com/2012/06/adaway-hosts-for-japan.html?m=1)
- [悪いインターネット | 広告ホスト](https://warui.intaa.net/adhosts/)


***
####LetMeBlock
hostsファイルへの変更を反映させるモノ。
**unc0ver**で牢破りするとインストールされてるみたい。
[<img width="72" alt="logo" title="logo" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280129/f232a9f9-213b-8728-551f-1b44cfa2be88.png">　　[<img width="128" alt="Image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280129/5e24d42c-b9a1-7b27-b096-b091f3735f06.png">](//cydia.saurik.com/api/share#?source=https://poomsmart.github.io/repo/)](https://poomsmart.github.io/repo/)

***




####Mega UHB IPv4+6 iOS 9/10/11/12 - MUHB (Mega Untrusted Hosts Blocker)
広告ブロッカー【牢破り用】
広告ブロックが反映しない場合は、これをインストールして、広告ブロックが反映されてる
ことが確認できれば、アンインストールしても反映は残る、


[<img width="72" alt="Image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280129/d81b67eb-acd5-4f1a-44d2-90a2eb619c4d.png"> 　　[<img width="128" alt="Image" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/280129/5e24d42c-b9a1-7b27-b096-b091f3735f06.png">](//cydia.saurik.com/api/share#?source= https://repo.thireus.com/)](https://repo.thireus.com/)
Discourse のバージョンアップ 作業メモ ( v2.5 → 2.7.0.beta9 )

Discourse のインストール 作業メモ [21/05/11]

- OS: Ubuntu 16.04 Lts
 - Discourse v2.5 →　2.7.0.beta9

######※最近のバージョンは日本語の検索がかなり改善されていて、便利だと聞きバージョンアップしました。


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166558/82e43c93-663f-434a-c0c2-251b08af162d.png)
######※右上のUpgrade All　は怖くて試していません。
----
　
Upgrade ボタン押下後、しばらく、画面を眺めていまして、
終わった感じでしたので、そのまま動かしていましたが
バージョーン表記が変わっていませんでした。

再度、バージョン確認画面を見てみると、以下の表示に変わりました。
　

----
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166558/43f002f6-c9fb-2b67-ca81-e3419084291c.png)
----

言われる通りに、以下コマンドを実行しました。

~~~
cd /var/discourse
git pull
./launcher rebuild app
~~~

####ですが、Discourse が起動しません。。。。

ログに、"パッケージのアップグレードが必要"みたいな記述があったので
念のため、以下のコマンドを実行し

~~~
apt-get update
apt-get upgrade
~~~

再度、リビルドを行いました。

~~~
cd /var/discourse
git pull
./launcher rebuild app
~~~

　

####■結果、無事に起動しました。(失敗したのかと途中でかなり焦りました､､､) 
----

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166558/7afaa703-dbbf-c2c6-0fcb-3d90f94f5167.png)

----



##■Discourse の日本語検索設定

管理者設定画面で、"CJK"で検索し、項目にチェックします。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/166558/c4988a2f-25f0-f0eb-9a17-ed300e6a1dcb.png)



##■その後
この作業後、Ubuntu16.04　→　Ubuntu18.04　のバージョンアップも行いましたが、
特に問題は発生していません。
バーチャル背景ライブラリbackscrubをラズパイ４Ｂに実装

#はじめに
Deep learningを使用しているバーチャル背景ライブラリ、backscrubをラズパイで実装します。

https://github.com/floe/backscrub
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287677/1d2f3ad7-43f7-6d24-0364-4549eef4005f.png)


#backscrubとは
MeetやZoomのようなバーチャル背景を作成するライブラリです。
人物の切り取りにtensorflowにを使用しています。
v4l2loopbackで仮想videoデバイスを作ることができます。
使用する学習済みモデルによって差異はありますが、ラズパイ４Ｂでは７～２０ＦＰＳぐらいで動きます。

#結　論
READMEどおりやってもうまくいかないので、Makefileを修正した後にmakeする必要がある。

```bash:install_backscrub.sh
sudo apt-get update
sudo apt install libopencv-dev build-essential v4l2loopback-dkms curl
git clone https://github.com/floe/backscrub.git
cd backscrub
git clone https://github.com/tensorflow/tensorflow/
sed -i -e "s/build_lib.sh/build_rpi_lib.sh/" Makefile #ラズパイ用のスクリプトに変更
sed -i -e "s/linux_x86_64/rpi_armv7l/" Makefile #ラズパイ用のアーキテクチャに変更
make
```


#実装環境
ハード：Raspberry Pi 4 Model B

|Revision|Release Date|Model|PCB Revision|Memory|
|---|---|---|---|---|
|c03112|Q2 2019|4 Model B|1.2|4 GB|
OS：Raspbian

```
$ lsb_release -a
No LSB modules are available.
Distributor ID:	Raspbian
Description:	Raspbian GNU/Linux 10 (buster)
Release:	10
Codename:	buster
```

#解　説
backscrubはtensorflowを使用しているので、Tensorflow公式ページの[Raspberry Pi でネイティブにコンパイルする](https://www.tensorflow.org/lite/guide/build_rpi?hl=ja#raspberry_pi_%E3%81%A7%E3%83%8D%E3%82%A4%E3%83%86%E3%82%A3%E3%83%96%E3%81%AB%E3%82%B3%E3%83%B3%E3%83%91%E3%82%A4%E3%83%AB%E3%81%99%E3%82%8B)を参考にしてコンパイルする。


backscrubフォルダ内のMakefileを２箇所変更する必要がある。
①ラスパイ用のビルドスクリプトに変更。

```makefile:Makefile
#cd $(TFLITE) && ./download_dependencies.sh && ./build_lib.sh
cd $(TFLITE) && ./download_dependencies.sh && ./build_rpi_lib.sh
```

②linux_x86_64をrpi_armv7lに置換する。
```makefile:Makefile
#TFLIBS=$(TFLITE)/gen/linux_x86_64/lib/
TFLIBS=$(TFLITE)/gen/rpi_armv7l/lib/
```

#実　行

```
$ sudo modprobe v4l2loopback devices=1 max_buffers=2 exclusive_caps=1 card_label="VirtualCam"
$ ./deepseg -d -d -c /dev/video0 -v /dev/video1 -m models/selfiesegmentation_mlkit-256x256-2021_01_19-v1215.f16.tflite
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287677/c73daf13-d5b3-5319-44ca-913e0e4cb5ff.png)
※目隠しは自分で加工しました。

だいたい7FPSぐらいで動きます。
ラズパイでこのクオリティはすごいのではないでしょうか。

#おわりに
ラズパイ4B熱くなりすぎでは？
Wordpressのメモ

# TIPS

## あるURLの時だけ一部のHTMLを表示する


```php:
	<?php 
	　if ( $_SERVER["REQUEST_URI"] == '/' ):
	?>

	<section class="header">
	　ヘッダー
	</section>

	<?php
	　endif;
	?>
```

https://www.adam.ne.jp/support/tips/url-normalization/


## WordpressでJavaScriptを読み込む

パターンは何個かあるけど、wp_enqueue_scriptsで読み込むパターン。functions.phpに書いた。



```php:テーマ直下/functions.php
//テーマディレクトリまでのパスの定数
define("DIRE", get_template_directory_uri());

function add_files(){

//     wp_enqueue_style('my_style',DIRE.'/style.css');
    wp_enqueue_script('common_script', DIRE.'/assets/js/common.js');

}

add_action('wp_enqueue_scripts', 'add_files');

```


参考↓

[WordPressでファイルを読み込むためのwp_enqueue_styleとwp_enqueue_scriptの使い方と引数の意味を理解しておこう](https://wemo.tech/205)


# エラー処理

## Call to undefined function wp()

wp-config.phpの設定ミスか、パーミッションの問題。

[参考](https://wp-doctor.jp/blog/2016/03/08/wordpress-%E3%83%AF%E3%83%BC%E3%83%89%E3%83%97%E3%83%AC%E3%82%B9%E3%81%AE%E3%82%A8%E3%83%A9%E3%83%BC%E8%A7%A3%E8%AA%AC%EF%BC%9Afatal-error-call-to-undefined-function-wp/)
Discourse のインストール 作業メモ

Discourse のインストール 作業メモ [21/05/10]

- OS: Ubuntu 20.04.2 LTS
 - Discourse v2.6.7 

※最初､Dockerでないインストール方法を模索しましたが、失敗に終わりました。
　Discourseは、素直に､Dockerでインストールするものと考えた方が良さそうです。

######※本当は､ApacheでSubdirectry運用を行いたいのですが、上手く設定できず断念しています。

---
#####■気を付ける点
#####①有効なドメインアカウントが設定時に必要
#####②送信用のメールサーバーとアカウントが、インストール前に設定必須
  ↑これらの設定が間違っていると、インストール完了後のログイン画面から進むことができません。

---

~~~
sudo mkdir /var/discourse
sudo git clone https://github.com/discourse/discourse_docker.git /var/discourse
cd /var/discourse
~~~

~~~
./discourse-setup
~~~

####■以下内容の入力後、インストールが行われます。

~~~
Hostname for your Discourse? [discourse.example.com]: 
Email address for admin account(s)? [me@example.com,you@example.com]: 
SMTP server address? [smtp.example.com]: 
SMTP port? [587]: 
SMTP user name? [user@example.com]: 
SMTP password? [pa$$word]: 
Let's Encrypt account email? (ENTER to skip) [me@example.com]: 
~~~

####■SMTP関連の追加設定

私の環境では、メールの送信に

- SMTP user
- SMTP password　　
   
　　を使用しませんので、追加設定を行います。


~~~
sudo vi ./containers/app.yml
~~~

以下の部分をコメントアウトします。

~~~
### DISCOURSE_SMTP_USER_NAME:user@example.com
### DISCOURSE_SMTP_PASSWORD:"pa$$word"
~~~

####■修正後、以下のコマンドで、リビルド＆サービス起動が行われます。

~~~
./launcher rebuild app
~~~

Dockerなので､正直､失敗する事はあまりなさそうです。
データ分析で使うRの基本操作

# ライブラリのインポート

```R
library(aaaaa)
```

# csvの読み込み

[ファイル読み込みの全てはこちら](http://cse.naro.affrc.go.jp/takezawa/r-tips/r/40.html)

```R
data = read.table("data.csv", sep=",", skip=6, header=T)[, c(1,2,3)]
```

### オプションの説明
- `sep=","` csvファイルなので
- `skip=6` 6行目から読み込む
- `header=T` headerを認識する
- `[, c(1,2,3)]` 1,2,3列目だけを抽出


### tips
- `read.csv`でもいいかも。ただしこの場合はデフォルトが`header=T`となっているので注意
- マルチバイトエラーが出た時は、`fileEncoding="CP932"`をオプションとして付け足す

# データの可視化

## データの先頭を表示
```R
> head(data)

    NS   EW    UD
1 0.00 0.03  0.00
2 0.00 0.00  0.00
3 0.03 0.00 -0.03
4 0.03 0.00 -0.03
5 0.03 0.00 -0.03
6 0.03 0.00 -0.03
```
## 列ごとの特徴を見る
```R
> summary(data)

       NS                  EW                  UD           
 Min.   :-818.0160   Min.   :-476.8670   Min.   :-315.1300  
 1st Qu.:  -0.5380   1st Qu.:  -0.6580   1st Qu.:  -0.3890  
 Median :   0.0000   Median :  -0.0300   Median :   0.0000  
 Mean   :  -0.0042   Mean   :  -0.1191   Mean   :  -0.0157  
 3rd Qu.:   0.5380   3rd Qu.:   0.3890   3rd Qu.:   0.3590  
 Max.   : 578.9700   Max.   : 617.2800   Max.   : 332.2370  
```

## 特定の列を抽出
```R
> sub_data = data$NS
```

## 配列の長さ
```R
> sample_size = length(sub_data)
```

## 変数に代入

```R
# 両方同じ
> sampling_rate = 50
> sampling_rate <- 50
```

## 配列を作成
つまりはPythonでいうところの`numpy.linspace`をしたい

```R
> t = seq(0, sample_size/sampling_rate, length=sample_size)
```

## グラフに描画
```R
# 文字の大きさとサイズの指定
> par(ps = 15, pin = c(5,3))
# 描画
> plot(t, ns)
```

# 配列で詰まったところ
Rの配列からインデックスを指定してデータを抽出しようとしたが、`*`で掛け算しようとすると上手くいかなかった

```R
> array[20*sampling_rate : 100*sampling_rate]
```
面倒だが、変数に代入してから抽出するしかなさそう

```R
> start = 20*sampling_rate
> end = 100*sampling_rate
> array[start:end]
```
GitHubでのマージの種類

GitHubのマージに関して、簡単にまとめてみました。

##Auto Merge
このAuto Mergeが最も一般的なマージ方法となります。
これは別々のブランチでそれぞれが開発している時に、別ブランチの変更内容を自分のブランチに取り込むマージとなります。

![スクリーンショット 2021-05-11 23.14.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1340120/777addc2-604d-fc43-fd5f-9de586fff553.png)

上の図は、AのコミットからBとB'のコミットに分岐してそれぞれが開発していると想定しています。
ここでmasterブランチの変更内容を取り込む場合、「git merge master」コマンドにて、新しくCコミットが作成されます。
このCコミットは、Bコミットの内容をベースにして、B'コミットの内容を取り込んでいることになります。
特徴として、CコミットはBコミットとB'コミットの2つを親ファイルとして持っていることです。


##Fast Foward merge

これは先ほどと違って分岐していない時のマージとなります。
![スクリーンショット 2021-05-11 23.21.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1340120/9e6af6c6-18db-4575-1538-467929764c31.png)
元々開発をしていたAコミットに、新しくBコミットをマージして、変更内容が統合される形です。
するとAコミットを指していたmasterブランチはBコミットを指すようになります。

##conflict merge

conflictは衝突するという意味です。
![スクリーンショット 2021-05-11 23.29.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1340120/fdedd698-c537-2834-5120-00bf84b04dc9.png)
別々のコミットで、同箇所を変更や追記してマージ場合に、Git側はどっちの方を正と判断すれば良いか分かりません。
このコンフリクトが発生した際は、該当のファイルを開いて正しい記述に修正する必要があります。
Serverless Frameworkで環境変数を扱いたい

# 背景
Serverless Frameworkを使っていて秘匿情報どうしようって悩んだときに、公式でサポートされていたのでその設定方法を書く。

# 環境
Serverless Framework 2.41.0

# 設定方法
## 環境変数用のファイルを用意

- `.env`や`.env.dev`など
- `.gitignore`に追記するのも忘れずに

```
AWS_ACCOUNT_ID=xxxxxxxxxxxx
MY_SECRET_KEY=xxxxx
```

## serverless.ymlでの設定

```yaml
useDotenv: true

...

iam:
  role:
    statements:
      - Effect: "Allow"
        Action:
          - dynamodb:Query
          - dynamodb:Scan
        Resource: !Join
          - ":"
          - - "arn:aws:dynamodb:ap-northeast-1"
            - ${env:AWS_ACCOUNT_ID}
            - "table/myTable"

...

functions:
  my-function:
    handler: src/my_function.lambda_handler
    # Lambdaの環境変数に渡したい場合
    environment:
      MY_SECRET_KEY: ${env:MY_SECRET_KEY}
```

- `useDotenv: true`を指定して
- `${env:変数名}`で使うことができる

バージョン3.0.0以降はデフォルトになり、`useDotenv: true`の指定をする必要がなくなるらしい

# Links

https://www.serverless.com/framework/docs/environment-variables/