OmniAICreator/Qiita-1.07M huggingface dataset:
WSL2でubuntu入れた

# TL;DR
特に目的はないですが、なにか新しいものを触るのが好きなので。
WSL2を入れたので、やったことをOutputしておきます。
基本的にはMSのサイトに書いてあるマニュアルの通り実施すれば問題ありませんでした。

#WSL2を使う前準備
Microsoft Insider Programに登録することが必要です。
https://insider.windows.com/ja-jp/ 

これは正式にリリースされていない機能を先に試し、マイクロソフトにフィードバックをしたりするプログラムです。
別の言い方をすると、今後Windows10に搭載される予定の機能をいち早く体験できるプログラムとも言えます。
もちろんバグがある可能性もありますし、最悪データの喪失も考えられます。
なので、覚悟がある人か予備のPCがある人にお勧めします。

上記のURLから登録して、自分のWindows10から、[コントロールパネル] > [更新とセキュリティ] > [Windows Insider Program]を選択し、その中の「Insider Preview ビルドを受け取る」の「開始する」ボタンをクリックしたらWindowsアップデートが走るので完了後に再起動してください。
Microsoftアカウント必須ですので、あらかじめ登録しておいてください。

#WSL2のインストール
ここに全部書いてあるので、なんにも書くことないぜ。
https://docs.microsoft.com/ja-jp/windows/wsl/wsl2-install

Powershellわからない人（いないだろ）は、「Windowsの機能の有効化または無効化」から「Linux 用 Windows サブシステム」と「仮想マシン プラットフォーム」をインストールします。
元々、上記二つがインストールされていれば必要はないかと思います。

また既に元のWSLでLinuxを入れている場合は、以下のコマンドでコンバートすることが出来ます。
ubuntu 18.04 LTSの場合

```DOS
PS C:\> wsl -l -v
  NAME            STATE           VERSION
* Ubuntu-18.04    Running         1
PS C:\>wsl --set-version ubuntu-18.04 2
```

実はここでハマった。convertは正常に終了しているのに、Versionが変わらない事象に遭遇。
まさかWindowsの再起動が必要だったなんて・・・(上記のMSの手順に書いてある)

#WSL1との違い
- Dockerが普通に使える。以前Windows側にDocker入れてそっちを経由しないとは使えなかった。
- 使えなかったコマンドが何個か使えるようになっている。(tcpdumpなど)
 - こっちは詳しくはあまりわかりません。(Linuxのシステム監視の経験が無いのでどんなコマンドがあるのかワカラナイ）
- systemdが使えない。`sudo systemctl start nginx`とかやっても使えない。
 - エラー`System has not been booted with systemd as init system (PID 1). Can't operate.`出る。systemdがPID 1で動いてない？まあ動いてないんですけど。Linuxの実機が無いので試せない。

```bash
$ ps aux
USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
root         1  0.0  0.0    900   552 ?        Sl   17:15   0:00 /init
root         6  0.0  0.0    900    76 ?        Ss   17:15   0:00 /init
root         7  0.0  0.0    900    76 ?        S    17:15   0:00 /init
root        89  0.0  0.0 141116  1556 ?        Ss   17:16   0:00 nginx: master process /usr/sbin/nginx
www-data    90  0.0  0.0 143788  6204 ?        S    17:16   0:00 nginx: worker process
www-data    91  0.0  0.0 143788  6204 ?        S    17:16   0:00 nginx: worker process
www-data    92  0.0  0.0 143788  6204 ?        S    17:16   0:00 nginx: worker process
www-data    93  0.0  0.0 143788  6204 ?        S    17:16   0:00 nginx: worker process
root      5798  0.3  1.6 1274136 106064 ?      Sl   19:10   0:31 /usr/bin/dockerd -p /var/run/docker.pid
root      5809  0.4  0.7 1203320 51372 ?       Ssl  19:10   0:39 containerd --config /var/run/docker/containerd/containe
user      6101  0.0  0.0  37796  3320 pts/0    R+   21:43   0:00 ps aux
```

- `service nginx start`とかはいける。知ってる人には当たり前の事かもしれないけどここは勉強不足。

# あとがき
以前の職場で、「MacOSでLinux使えてWeb開発に便利だなぁ」と思っていたので非常に興味深い機能なので試してOutputいたしました。
それでは私はこれからまだ仕事なので。

三三三三三(卍^o^)卍　[決戦！星の古戦場！]
よく使うgit stash系コマンド

stashの小ネタです。ふと思い出せないときがあるので、備忘録兼ねて。

gitで作業中に差し込みのタスクが入ってきたときなどに使う、 `git stash` コマンド。

よく使うコマンドをリストアップします。

### 作業中のファイルを退避
```
$ git stash
```

### 退避したリストを確認
```
$ git stash list
stash@{0}: WIP on <branch名>: <コミットハッシュ> <コミットメッセージ>
stash@{1}: WIP on <branch名>: <コミットハッシュ> <コミットメッセージ>
...
```

### 退避した内容を確認する
```
$ git stash show <stash名>
# ex: git stash show stash@{0}
# stashしたファイルのdiffが見れる
```

### 指定した退避内容を全て適用する（適用した内容を破棄しない）
```
$ git stash apply <stash名>
```

### 最新の退避内容を全て適用する（適用した内容は破棄される）
```
$ git stash pop
```

### 指定した退避内容から、一部のファイルのみ適用する
指定したstash名に退避されているファイルの中から、指定したファイルの退避内容を適用する

```
$ git checkout <stash名> <ファイル名>
```
jupyter notebookが起動しなくなった

## jupyter が急に起動しなくなった。
焦ったから自分用の備忘録に

### vscodeのプラグイン が急に起動しなくなった...

**ターミナルでやってみよう**

```
$ jupyte notebook

/usr/local/Cellar/pyenv/1.2.11/libexec/pyenv-exec: /Users/username/.pyenv/versions/3.7.3/bin/jupyter: /Users/username/.pyenv/versions/3.7.3/bin/python3.7: bad interpreter: No such file or directory
/usr/local/Cellar/pyenv/1.2.11/libexec/pyenv-exec: line 47: /Users/username/.pyenv/versions/3.7.3/bin/jupyter: Undefined error: 0

```

訳わからん...

### 下記コマンドで無事解決

```
pip3 install --upgrade --force-reinstall --no-cache-dir jupyter
```

これでPyPiから全て更新されて再installされるらしい。無事起動した。
たぶん古いユーザーでinstallした奴を使ってたからかな？

参考記事
https://stackoverflow.com/questions/42648610/error-when-executing-jupyter-notebook-no-such-file-or-directory
「データ利活用するぞ」と言われた人が考えること〜環境編〜

# データは第二の石油
「うちもデータ利活用するぞ」という上司の一言でデータ分析を任されたシステム部の人を想定して、
本記事では「どうデータを持つか」「実際に構築するモノが何か」を考えてみたいと思います。

「データを使って何かするのはなんとなく分かるけど、何を用意したらいいの？」
という疑問解決の一助となればと思います。

# 注意書き
個人の意見です。経験不足はご指摘ください。

# データはどこにあるのか
もちろん、あなたの会社の屋台骨となるシステムにデータは眠っています。
顧客の購買履歴であったり、ユーザの属性情報であったり。

これを都度、本番サーバルームに入って参照できるならそれでも・・・色々とよくないですよね。

# データをどこに保持するのか

分析ツールを直接本番データに接続するわけにはいきません。
一度データをどこかに出力して保持する必要があります。
ここでは、以下の２パターンを比較することにします。

### 分析作業者の作業PC

主システムに高度なAPIやcsv出力などデータ出力機能を実装して、「欲しいデータを欲しい時にダウンロードする」ケースです。
ここでは主システムのDBを、OracleやPostgreSQLなど、RDBMSの前提で話を進めます。

### データ分析システム(NoSQL)

主システムから特定の形式で定期的にデータ出力し、別にデータ分析システムを構築して、そこにデータを投入して保持するケースです。
ここではjson形式とし、データ分析システムのDBはそれを扱えるNoSQLの前提で話を進めます。
※すみません、本記事では分析方法については解説しません。オススメは「Elastic Stack」というツールです。興味があれば調べてみてください。

# メリデメ比較
有利な方が○です。

|比較観点|分析作業者の作業PC|データ解析システム(NoSQL)|
|:-----:|-----|-----|-----|
|データの自由度|○：取得するデータを選択できるなど、提供API/csv機能を高度化すれば好きなデータをいつでも引っこ抜ける|△：扱えるデータは定義した提供API機能のインターフェースの範囲に限られる|
|分析作業の自由度|△：膨大な長期間のデータを保持し続けられない。また未取得のデータが必要になるたび、当該期間の当該データを本番サーバから都度引っこ抜く必要がある。データを引っこ抜く処理(SQL)が主システムに負荷を与える|○：予め解析対象となりそうなデータを全て引っこ抜くことで、必要なときに迅速に分析作業が行える。分析作業が主システムに影響を与えない|
|セキュリティ対策|△：引っこ抜けるデータに制限(APIに厳密なセキュリティ対策)をしないと引っこ抜いたデータをSR(セキュリティルーム)でしか扱えない|○：APIでセキュリティレベルをハンドリングすれば引っこ抜いたデータを執務室で扱える|
|主システム改修コスト|△：高度なデータ出力機能を主システムに手を加えて実装する必要があるためリリース調整や障害リスクをハンドリングをする必要がある|○：定義したインターフェースだけ開発する|
|データ分析システムの開発コスト|○：不要|×：開発が必要|
|データ分析環境の維持コスト|○：作業PCがあれば分析可能|×：物理または仮想サーバを構築して維持する必要がある|

比べてみていかがでしょうか。私はNoSQL派ですが、所変われば品変わる（？）、どちらを選ぶかは環境次第と思います。

ご参考の一つにでもなれば、と思います。
コードヘルス - コメントをつけるべきか、つけないべきか

*この文書は[Code Health: To Comment or Not to Comment](https://testing.googleblog.com/2017/07/code-health-to-comment-or-not-to-comment.html)の翻訳です。*

コードを読んでいると、**いいコメントほど助けになるものはない。** しかしながら、**コメントは必ずしもいいものだとは限ない。** ときには、コメントが必要ということはそのコードがリファクタリングするべきとういサインかもしれない。

## コード自体で説明できないときにだけコメントを使おう

コードが何をしているか説明するコメントが必要だと考えるなら、まず、次のどれかを試そう。

#### 説明的な変数を使う。

よくない例

```c++
// 割引を価格から引く
finalPrice = (numItems * itemPrice)
    - min(5, numItems) * itemPrice * 0.1;
```

修正後

```c++
price = numItems * itemPrice;
discount =
    min(5, numItems) * itemPrice * 0.1;
finalPrice = price - discount;
```

#### メソッドを抽出する。

よくない例

```c++
// Filter offensive words.
for (String word : words) { ... }
```

修正後

```c++
filterOffensiveWords(words);
```

#### より宣言的な識別子名を使う。

よくない例

```c++
int width = ...; // Width in pixels.
```

修正後

```c++
int widthInPixels = ...;
```

#### コードに仮定があるならチェックを加える

よくない例

```c++
// heightは常に0より大きいので大丈夫
return width / height;
```

修正後

```c++
checkArgument(height > 0);
return width / height;
```

## コメントが役に立つ場合

#### 意図を明らかにする
なぜコードがそうなっているのか（そうなっていないのか）を説明する

```c++
// 計算コストが高いため一度だけ計算する
```

#### よくできた機能をもったエディターが誤ってコードを「修正」しないように

```c++
// Fooはスレッド安全でないため、新たなFooを作る
```

#### 解説: コードレビューの際に出た質問や、コードを読む人がおそらく持つであろう質問

```c++
// 順序に注意。なぜなら、
```

#### ダメなエンジニアがやっていることのようにみえることへの説明

```c++
@SuppressWarnings("unchecked") // このキャストは安全。なぜなら、
```

#### 一方で、コードがやっていることをただ繰り返すコメントは避けよう。


よくない例

```c++
// Get all users.
userService.getAllUsers();
```

よくない例

```c++
// Check if the name is empty.
if (name.isEmpty()) { ... }
```
【UE4】群れを先導したい／第13回UE4ぷちコン進捗 - 2020/02/23

[1]:http://historia.co.jp/ue4petitcon13


[株式会社ヒストリアさん主催の第13回UE4ぷちコン][1]の作業進捗。

この土日、群れを先導する感じを作ろうと四苦八苦。
なかなかええ感じにならない…

# 土曜日

![20200223_1740.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358170/e5864610-df41-e2ca-c4d8-42e063bf890e.gif)

ざっくり群れを二つに分けておいて、プレイヤーキャラが中央を抜けられるように、とか考えてあれこれ試した。

- 群れの恐竜たちを相当速く動かさないと、プレイヤーキャラに追いつけない。
- そのせいで、動物の群れというよりは、虫の群れみたいな感じになって気持ち悪い…

# 日曜日

![20200223_2140.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/358170/40e504ba-f4ff-ee5a-932e-fbaa5fb24b6c.gif)

群れのアルゴリズムをまるっと変た方がよいだろうなと思いつつも、要素をそろえることを優先。

- 個々の恐竜が、それぞれの目的地に向かって自律的に加減速や方向転換するようにした。
- アニメーションはBlend Space 2Dで前後左右方向に走れるようにした。
    - ※プレイヤーキャラはBlend Space 1Dで前方にのみ走る。（自動車的な動き）

あとはひたすらパラメータ調整をした。
とりあえず、現状のアルゴリズムでできる限りの調整はやったつもり。

上の動画はええ感じな部分だけを撮ったもので、例えばプレイヤーキャラが180度回転とかすると結構厳しい絵面になる。

放置して先に進むか、アルゴリズムを考え直すか、とても悩ましい。
一応、群れのコントローラだけ変更すれば、恐竜たちは変更しなくてもよいようには作ってあるが…

今日はここまで。
【Python】Enumでvalue以外のkeyでも値を取得する方法

# Enumでvalue以外のkeyでも値を取得する方法
## はじめに
多くのプログラミング言語にはEnumというものがあります。Pythonにも同様にあります。メンバーの取得はそれぞれの`value`をkeyに取得できますが、それ以外の値をkeyにすることはできません。通常のEnumを拡張すればできたので紹介します。

## 通常のEnumの確認
まずは通常のEnumの利用方法を確認します。

```python
>>> from enum import Enum
>>>
>>> class Color(Enum):
...     RED = 1
...     GREEN = 2
...     BLUE = 3

# 上記のようにEnumを宣言すると
# 1や2といったvalueをkeyにして各メンバーにアクセスできます

>>> color = Color(1)
>>> print(color)
Color.RED

# もちろん直接アクセスすることもできます
>>> print(Color.RED)
Color.RED
>>> print(color == Color.RED)
True
```
## やりたいこと

`Color(1)`で`Color.RED`にアクセスできましたがたとえば`Color('red')`や`Color('赤')`でもアクセスできたら`Enum`の可能性が広がります。

```python
>>> from enum import Enum
>>>
>>> class Color(Enum):
...     RED = 1
...     GREEN = 2
...     BLUE = 3

# 以下のようにアクセスしようとするともちろんエラーになる
>>> color = Color('red')
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/local/lib/python3.6/enum.py", line 293, in __call__
    return cls.__new__(cls, value)
  File "/usr/local/lib/python3.6/enum.py", line 535, in __new__
    return cls._missing_(value)
  File "/usr/local/lib/python3.6/enum.py", line 548, in _missing_
    raise ValueError("%r is not a valid %s" % (value, cls.__name__))
ValueError: 'red' is not a valid Color

>>> color = Color('赤')
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/usr/local/lib/python3.6/enum.py", line 293, in __call__
    return cls.__new__(cls, value)
  File "/usr/local/lib/python3.6/enum.py", line 535, in __new__
    return cls._missing_(value)
  File "/usr/local/lib/python3.6/enum.py", line 548, in _missing_
    raise ValueError("%r is not a valid %s" % (value, cls.__name__))
ValueError: '赤' is not a valid Color

```

## 実装

ではkeyを拡張したEnumの実装を確認します。

```python
# 宣言時に以下のように`_value2member_map_`を更新する
>>> from enum import Enum
>>>
>>> class Color(Enum):
...     def __new__(cls, value, en, ja):
...         obj = object.__new__(cls)
...         obj._value_ = value
...         cls._value2member_map_.update({en: obj, ja: obj})
...         return obj
...     RED = (1, 'red', '赤')
...     GREEN = (2, 'green', '緑')
...     BLUE = (3, 'blue', '青')

# これで先程のように'red'や'赤'でアクセスすると
>>> color = Color('red')
>>> print(color)
Color.RED

>>> red = Color('赤')
>>> print(red)
Color.RED
```

## 最後に
やりすぎるとkeyがかぶったりする可能性もありますが、知っておいて損はないと思います。
DBではint型、APIではstr型とかで使えそうです。
Reactのアプリを簡単にDocker上のnginxに搭載してみた

# 目標
Reactのcreate-react-appで作成したアプリをnginxに載せてみました。
簡単にできたのでメモしておきます。

## Reactアプリの作成
これはcreate-react-appでサクッと作成しましょう。
[DockerでReactの環境を作成してみた](https://qiita.com/penpenta/items/2fa98aa885ba8fa7ed90)でDocker上で構築できるはずです。
アプリが作成できたら`yarn build`でnginxに載せるファイルを出力しましょう。

## niginxのDockerfile

nginxのDockerfileです。
`yarn build`で出力したファイルとnginxの設定ファイルをコピーしています。

```Dockerfile
FROM nginx:1.17

COPY ./app/build /opt/app/

COPY ./nginx.conf /etc/nginx/nginx.conf

CMD ["nginx", "-g", "daemon off;", "-c", "/etc/nginx/nginx.conf"]
```

## nginxの設定ファイル

locationはbuildしたファイルを置く場所に設定しましょう。

```nginx.conf
user  nginx;
worker_processes  auto;

error_log  /var/log/nginx/error.log warn;
pid        /var/run/nginx.pid;

events {
    worker_connections  1024;
}

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

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

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

    sendfile        on;

    keepalive_timeout  75;

    server {
        listen 80;
        charset utf-8;

        location /{
            root   /opt/app/;
            index  index.html;
        }
    }
}
```

## まとめ
あとは、nginxのDockerfileをbuildしてコンテナを立ち上げれば完了です。
CentOS7 に systemd-nspawn用 CentOS7 コンテナを作成する

```
NEW_MACHINE='centos7'
yum --releasever=7 \
    --nogpg \
    --installroot=/var/lib/machines/${NEW_MACHINE} \
    --disablerepo='*' \
    --enablerepo=base \
    install systemd passwd yum vim

sudo systemd-nspawn -D /var/lib/machines/${NEW_MACHINE}
passwd
useradd -m -r user
passwd user
exit

# 起動確認
sudo systemd-nspawn -bD /var/lib/machines/${NEW_MACHINE}
Ctrl+]
Ctrl+]
Ctrl+]

# unit file 
systemctl enable systemd-nspawn@${NEW_MACHINE}.service

(echo '[Service]'
 echo 'ExecStart='
 echo 'ExecStart=/usr/bin/systemd-nspawn --quiet --keep-unit --boot --link-journal=try-guest --machine=%I'
) | SYSTEMD_EDITOR=tee systemctl edit systemd-nspawn@${NEW_MACHINE}.service

cat /usr/lib/systemd/system/systemd-nspawn@.service.
cat /etc/systemd/system/machines.target.wants/systemd-nspawn@${NEW_MACHINE}.service
cat /etc/systemd/system/systemd-nspawn@${NEW_MACHINE}.service.d/override.conf

machinectl start ${NEW_MACHINE}
```
【RSpec】HTTPメソッドDELETEのテストをする時の注意点

## 概要
`"expected #count to have changed by -1, but was changed by 0"`のエラー文の解消話です。

## 前提
* 具体的なコードはほとんど書いていないです。

## テストが落ちたコード

```ruby
    describe OrdersController do
      describe 'delete #destroy' do
        it "deletes the article" do
          order = create(:order)
          expect{
            delete :destroy, id: order
          }.to change(Order,:count).by(-1)
        end
      end
    end
```

## ブルーな気持ち
DELETEについてのテストを書いていたのですが、`"expected #count to have changed by -1, but was changed by 0"`のエラー文が登場して原因がわからず頭を抱えていました。

## 解決方法
DELETEの処理がレコードの**物理削除**ではなく**論理削除**を行なっていたことが原因でした

そのため、レコードの総数ではなく論理削除処理が行われていることをテストするようにテスト方法を見直して解決できました。

## まとめや自己反省
* `"expected #count to have changed by -1, but was changed by 0"`のエラーが発生したら
`物理削除`ではなく`論理削除`ではないか？と疑ってみてください。

* destroyメソッドの中身を読み直すと物理削除ではなく論理削除を行なっていることが気づけただろうから、テスト対象のメソッドを見直してからテストを書くようにしよう。
基本情報技術者　ストラテジ系　第2章 経営戦略①


# ①経営組織

## 経営組織の形態

### 事業部制組織
個々の事業を尊重する。
それぞれ早い反面、連携が取りづらい

### 職能組織
職種ごとに部門を分ける

### マトリクス組織
2つの指揮系統をもつ組織

事業部と職能別のいいとこどり

責任の所在が曖昧になる欠点がある

## 企業の責任者

### CEO
Chief Executive Officer
最高経営責任者

### CIO
Chief Information Officer
最高情報責任者

## コーポレートガバナンス
企業統治

## CSR
Corporate Social Responsibility
企業が果たす社会的な責任

## IR
Investor Relation
企業の経営情報を正確かつ迅速に公表すること

## BCP
Business Continuity Plan: 事業継続計画
災害が発生しても事業を継続させる計画

## 研修技法
研修方法は以下のようになります。

### OJT
On the Job Training
現場で学ぶ

### ロールプレイング
擬似体験、演劇

### ケーススタディ
事例もとにして、討論や研修を行い
事例に対する理解を深める。
初心者でもPythonを学習できるサービス8選(初級者向けから上級者向けまで)

既存の様々なサイトなどで確認したところ、
料金表示などが古くなっていたので改めてまとめてみました。

##プログラミングの入門なら基礎から学べるprogate
[progate](https://prog-8.com)
これからプログラミングを学びたい初心者に最もオススメの学習サイトです。
プログラミング用語も非常に丁寧で分かりやすい説明がされているので、
挫折することなく安心して学習を進めることができるでしょう。
ただ、ここで学べることは基礎中の基礎なので、
ここで学んだからといってすぐに業務に活かすことはできないでしょう。
初級者向け。

##paizaラーニング
[paiza](https://paiza.jp)
progateで学んだ次にここで学ぶと良いでしょう。
pythonの触りを確認した次はここで、もう少し発展的な、
よりpythonらしさを学ぶことができるコースがあります。
初級者向け。

##３分動画でマスターできるプログラミング学習サービス
[ドットインストール](https://dotinstall.com)で学んだことを
コードを書いて確認するには環境構築が必要です。
３分程度の短い動画が基本なので飽きずに、挫折せずに学習を進めることができるでしょう。
初級者向け。

##PythonでAIプログラミングが学べるオンライン学習サービス
[Leagence Programming](https://leagence-prog.com)
今なら完全無料で学習をすることができます。
pythonの基礎からディープラーニングまでを学べます。
初級・中級者向け。

##10秒ではじめられる人工知能学習サービス
[Aidemy](https://aidemy.net)
画像認識からブロックチェーンなどの最先端の技術を学ぶことのできるサイトです。
初級・中級・上級者向け。

##本気でプログラミングを学びたいあなたへ
[PyQ](https://pyq.jp)
実務よりのスキルを身につけることができる初級・中級者向けのサイトです。

##Udemy
[Udemy](https://www.udemy.com)
初級者から上級者まで対応した様々なレベルのコースがあります。

##AI Academy
[AI Academy](https://aiacademy.jp/)
目的別に応じて受講するコースのカスタマイズがされます。
環境構築不要ですが、環境構築テキストにしたがって環境構築することをお勧めします。
初級者から上級者向け。

#料金を比較
| サービス名       |       料金 |環境構築の要否|
|:-----------------|------------------:|------------------:|
|[progate](https://prog-8.com) |一部無料（月額980~）|不要|
|[paiza](https://paiza.jp)     |一部無料（月額1078円~）|不要|
|[ドットインストール](https://dotinstall.com)        |一部無料（月額1080円~）|必要|
|[Leagence Programming](https://leagence-prog.com)|完全無料|不要|
|[Aidemy](https://aidemy.net)  |一部無料（１コース980円~）|不要|
|[PyQ](https://pyq.jp)         |月額3040円〜|不要|
|[Udemy](https://www.udemy.com)|１コース2400円~|必要|
|[AI Academy](https://aiacademy.jp)  |一部無料（月額980円~）|不要|

#環境構築不要でPythonコードを実行する方法
[paiza.io](https://paiza.io/ja)なら29種類の言語を実行することができます。


##Pythonの学習におすすめの書籍

言わずと知れたスッキリわかるシリーズのPython版。
ProgateやPaizaラーニングの後に読むと、
サイトではわからなかった仕組みまでわかるようになります。
[スッキリわかるPython入門](https://amzn.to/3c6cOze)
初級者向け。

Pythonの基本文法だけでは足りない人向け。
プログラマとして必要なスキルがこの本一冊で学ぶことができます。
[独学プログラマー Python言語の基本から仕事のやり方まで](https://amzn.to/39YhpS7)
初級者向け。

progateで学習するよりももっと分かりやすくリーズナブルな書籍が欲しい。
そんな方にはこちらがお勧めです。
[プログラミング超初心者が初心者になるためのPython入門](https://amzn.to/2vaR2JO)
アマゾンプライム会員だと無料で読むことができます。
超初級者向け。

##おまけ
[Techpit](https://www.techpit.jp)
作りながら学べるをコンセプトにしたサイトです。
AidemyやUdemyのように教材(コース)を購入して学習することができるサイトです。
Techpit(テックピット)ではUdemyのように自ら教材を作ることもできます。
環境構築は必要で１コース595円から販売中です。
初級者・中級者向けです。

##感想
もしPythonを全く学んだことがない立場だったら、
progate→paizaかドットインストール→他のどれか
という順番で学ぶと思います。
AtCoder DISCO2020 予選 B - Iron Bar Cutting

# 概要
- 長さa1,a2,..,anの棒がn本ある。
- a1,..amを棒L、am,am + 1...anをつなげて棒


# 考察
いくつかの考え方について考察する。大きく2つのアプローチが思い浮かぶ。

まず、1のように、半分になる地点を計算し、その中点になるように長さを変えていく(この例では4か5の長さにすることが考えられる)が、どちらの地点に合わせるかが悩ましくなる。そして、ここでのポイントは必ず、もともとの長さの中点付近に合わせなくていいところである。
例えば、長さ2, 8の棒があった場合、$2+3=5, 8-3=5$で中点に合わせるとコストは`6`だが、$2+6 = 8,  8+0 = 0 $と片一方の長さを調整してもよい。

このため、2,3の例のように累計和を使って計算をしていく。まず、最初に$O(N)$で$sum(a)$をとっておけば、$Left$からの$Sum$はそれまでの和で、$Right$からの$Sum$はsum(a)から徐々に値を引いていけばよい。そして、その差自身が必要なコストになるのでこのminを取ればいい。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/30938/55ef937d-a930-f3ec-8137-bcb259b4f789.png" width=50%>

```Python
n = int(input())
dat = list(map(int, input().split()))
c = 0
c2 = sum(dat)
res = 99999999999999999
for i in range(n):
    c += dat[i]
    c2 -= dat[i]
    res = min(res, abs(c-c2))
 
print(res)
```
https://atcoder.jp/contests/ddcc2020-qual/submissions/10254909
クッキー(cookie)とは

#クッキーとは
アクセスするそれぞれの他人ごとに保管されるデータとしては、最も基本になるものです。
クッキーは、Webブラウザに用意されている物で、サーバーから送られてきた値を保管しておくための仕組みです。

#クッキーの仕組み
ヘッダー情報として値をやり取りしています。
サーバーからヘッダー情報としてクライアントにクッキーの情報が送られると、Webブラウザはそれをブラウザの中に保管します。
そしてサーバーにアクセスする際には、そのクッキー情報をヘッダーに追加して送ります。
受け取ったサーバー側は、そのクッキーの情報を使って必要な処理を行、またヘッダー情報として送り返す....といった事を繰り返しています。
ubuntuやラズパイのCUI(コマンドライン)でUSBをマウントする

##はじめに
いつもGUIでUSBファイルの操作をしていて特段困ることはなかったのですが、ubuntuからラズパイをssh接続してあれこれやってて、ふと、CUI環境でUSBに入っているファイルが取り出すことやったことがないし出来ない！と思ったので、USBをマウントしてUSB内のファイルを確認する方法を備忘録として残しておきます。

###USBをマウント
USBを差し、`sudo fdisk -l`を実行しUSBが接続されているか確認する。
![Screenshot from 2020-02-23 21-00-55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584794/a0b977fc-2a05-32c9-d834-2c48e0c91646.png)
デバイスのところに表示されたディレクトリを確認し、確認したディレクトリのパスをmount以下に書き込み、マウント先のディレクトリを指定しmountを実行
`sudo mount /dev/sda1 /mnt`
マウント出来たか確認するには、ディスク容量を確認する`df`コマンドを実行して確認する。
![Screenshot from 2020-02-23 21-19-16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/584794/232da9e0-9c88-d4a3-3ed0-9c1a14abd9ea.png)
うん。出来てた。

###USBをアンマウント
`sudo umount /mnt`でアンマウント
`umount: /mnt: target is busy.`というエラーが出たらUSB内のディレクトリを開いていたりするので、閉じてから再試行する。

##参考
エンジニアの入り口「この１ページで一通りわかる！Linuxのマウント(mount)について」
https://eng-entrance.com/linux-mount
docker commit から image を private registry に登録メモ


```
sudo docker ps 

sudo docker commit [containerID] [repository:tag]

sudo docker images

# ローカルにファイルとして保存する。　（大抵の場合必要なし。）
sudo docker save [imageID] -o [filename]

#tag付け
sudo docker tag [imageID] [registry IP:port]/[dirName]:[version]

#registry に push
sudo docker push [registry IP:port]/[dirName]:[version]
```



# 例


```

sudo docker ps 

sudo docker commit aaebff40fcbf python3_gpu:0.1

sudo docker images

# 初めのバージョンなので、0.1かつ　latest
sudo docker tag zzjhskjahck 30.0.0.0:5000/python3_gpu_base:0.1
sudo docker tag zzjhskjahck 30.0.0.0:5000/python3_gpu_base:latest

sudo docker push 30.0.0.0:5000/python3_gpu_base:0.1
sudo docker push 30.0.0.0:5000/python3_gpu_base:latest
```



# 取得の場合
```

docker pull [IPaddress]:[port]/[imageName]:[tagName]

#例
docker pull 30.0.0.0:5000/python3_gpu_base:latest
```


- Docker Private Registry (port 10080番)にいくとUIで見れて、そこにコマンドも載っている
haml,scss画面実装

haml
%input{type: "submit", class: "submit-btn", value: "Send"}
Sendボタン作成

display: none;で非表示にできる

position: relative;　親要素
position: absolute;　子要素
使い方が少しわかった気がする

一様形にはなったけどEditボタンが変だし、そもそも今のやり方であっているのかわからないからメンターにLGTMをもらえる気がしない。
もうGitHubの使い方も覚えてない。
<img width="1304" alt="db406ae259fc59cf8b48fa21db781f22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/588091/f447802d-e337-5b4b-45d1-7b442c56dd89.png">
手探りDocker #3

#前回のエラー解消の続き
前回の記事
[手探りDocker #2](https://qiita.com/GodPhwng/items/6ad8ba74e2fb26f00c50)

最初の記事
[手探りDocker #1](https://qiita.com/GodPhwng/items/2edf8792630674a2f811)

いくつか警告やエラーが出ていたが、警告は無視することに

以下のエラーだけの解消を引き続き目指します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526982/5a8f91e0-441b-ae20-a618-43fda967acc0.png)

サンプルで使用している下記参考サイトの「Start a Redis container」をすっ飛ばしていたので
[docker docs](https://docs.docker.com/docker-for-windows/dashboard/)
`docker run -dt redis`をやってみる　→　うまくいかない

エラー文の「Drive sharing failed for an unknown reason」が手掛かりになりそうなので、この文字列でググってみたところ、以下を見つけた
[WindowsのDockerで「Drive sharing failed for an unknown reason」エラーが出た](https://kdnakt.hatenablog.com/entry/2019/09/12/080000)

設定に問題があるとのこと
ウインドウ右下のDockerのアイコンをクリック　→　Settings
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526982/44ca7e4b-d069-1ba2-8ba3-ec922bfc5bcf.png)

以下の設定画面が表示される。
リンク記事とdockerのバージョンが違うので、手順が異なるが、Resources　→　FILE SHARING　とすすめる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526982/1656b344-95d2-558b-230b-7a5e59eedc32.png)

共有したいドライブを選択する
私の環境はCのみ(そもそもなぜ共有するの・・・汗　理解してなさす)

右下のApply & Restartを選択
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526982/775c54d3-83e4-5576-78f0-97ab7d6e0dcb.png)
すると以下のエラー
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526982/a471311d-f949-16ad-f559-a0c9cd025b2f.png)
原因はわからなかったが、とりあえず現在起動しているコンテナを停止した
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526982/cefb6c69-e5ab-7fb8-afd4-a1faaabc78ba.png)
改めて実行すると、今度はうまくいった
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526982/53c2e3a0-512b-01a0-2cd1-af9aa99301f6.png)

**もう一度`docker-compose up --build`**を実行します
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526982/b31db449-143c-6f96-6bb7-27c3479a696d.png)
どうやらうまくいったらしい・・・。
辛い・・・。
何一つ理解していない気がするが・・・。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526982/79395d0f-e434-a94b-9da2-03e4bd91bf54.png)

**example-voting-app_vote_1**が投票のWebアプリのようなので、ブラウザでアクセスしてみる
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526982/eb3c591c-3b85-638b-4696-d9444f12cb3c.png)
`※「http://localhost:5000/」のURLにアクセスします`
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526982/88fb7bed-0023-5f6d-dc9d-44369d6a258c.png)

うーん・・・CATSとDOGSをクリックするたびにPostgresにデータがインサートされているようなされていないような・・・
[docker docs](https://docs.docker.com/docker-for-windows/dashboard/)には
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/526982/87968b14-f459-eb43-3bca-748679888690.png)
とあるので、複数のコンテナ内のアプリが通信し合ってこのアプリケーションは成り立っているのだと思います。
~~分散アプリケーション？~~

# 理解が全くできていない
とりあえず、Docker公式の入門をやってみるかと挑んだものの、仮想化の基礎知識、ネットワークの知識が不足していて、ほとんどDockerというものを理解できなかったです。また、チャレンジしてみて「こういったことができるようになる」「こういったことが理解できていること」等の目的がなかったことがフワフワした結果を招いたとも言えそうです。

次回以降のDocker系の記事では上記基礎知識をすこしずつ勉強していこうと思います。
そして、今回取得したサンプルアプリに対して以下を行うことを目標としたいと思います。

- 各コンテナに`Tera Term` `WinSCP`で接続、操作する
- 投票アプリのデータを、SQLクライアントアプリでPostgresに接続して、データが溜まっているか確認してみる

今回は以上です。
MuleSoftのSalesforceコネクタの『Upsert Bulk』モジュールを試してみました！

# はじめに
　今回は、Muleの標準コンポーネントでSalesforceへBulkAPIのUpsert処理を実装しました。私があまりMuleSoftに詳しく、Muleの標準コンポーネントがどこまで共通化されているのか分からなかったため、Salesforceコネクタの標準モジュールを組み合わせや、実行した際の挙動を確認しました。

# メッセージフローの設定
　SalesforceコネクタのBulkAPIのUpsertモジュールが、エラーなく動作するところまで設定すると、下記のようになりました。
<img width="752" alt="スクリーンショット 2020-02-23 12.19.55.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76270/cd7b4bf7-79d1-7d01-0d41-1617b9345d64.png">
**メッセージフローの処理の流れ**
1. 『Set Payload』モジュールで、『Upsert bulk』モジュールで送信するデータを設定します。
2. 『Upsert bulk』モジュールで、対象オブジェクト、外部ID、データを設定すると、モジュール内で、Jobの作成、処理仕様の登録、バッチ登録を実行してくれます。
3. 『Until Successful』モジュールで、バッチが完了するまでポーリングを実施します。
4. 『Bulk result』モジュールで、バッチの結果を取得します。
5. 『Transform Message』モジュールで、バッチ結果のレコード登録結果のみを取得します。
6. そのほかにJobの終了モジュールが必要ですが今回は基本的な流れが確認できればよかったので省略しました。

**バッチステータスのポーリング処理** 
 『Until Successful』モジュールは、スコープ内で例外をキャッチすると処理を繰り返します。このフローでは、『Bulk result』モジュールで、**バッチステータスが未完了**の場合に例外を発生し（下記のエラー）、**バッチステータスが完了**になるまで繰り返し『Bulk result』モジュールが実行されます。

```
org.mule.runtime.core.internal.exception.OnErrorPropagateHandler: 
```
<img width="1222" alt="スクリーンショット 2020-02-23 20.19.40.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76270/c5f7e471-e03c-393d-8a93-6c00b15b72c2.png">

**レコード処理結果の取得処理**
　『Transform Message』モジュールで、『Bulk result』モジュールの結果を取得するロジックは以下の通りです。
　<img width="435" alt="スクリーンショット 2020-02-23 20.16.56.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76270/2dca3eb8-67e2-0c33-3da3-7d3c0b883afc.png">

# 動作を確認
　Salesforceの『一括読み込みジョブ』で実行された結果を確認すると、ジョブが登録されバッチが正常に実行できていることを確認できました。ただ、同じメッセージフロー内でも『Upsert bulk』モジュールが分かれてしまうとJobが複数できてしまうので、運用面を考えると考慮が必要ですね。
<img width="1178" alt="スクリーンショット 2020-02-23 20.05.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76270/dbf8659c-eea0-a530-150c-c7937b5ad64f.png">

**外部IDで参照項目設定**
JSON形式で、リレーション項目と外部ID項目を下の図のように定義します。
<img width="636" alt="スクリーンショット 2020-02-25 5.18.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76270/b43a1668-b956-6979-f774-b46ab9ec14c7.png">
正常にUpsertされると、下記の図のように親レコードと参照関係が設定されます。
<img width="388" alt="スクリーンショット 2020-02-25 5.16.14.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/76270/1b6ec820-b017-1223-a845-9436047a9c46.png">

# 最後に
　標準モジュールを組み合わせると簡単にBulkAPIの処理を実装する事ができました。
　同じメッセージフローないでも複数のJobが分かれてしまうのは、運用面で障害分析や切り戻しが難しくなるので、次は、SalesforceコネクタのJobとBatchモジュールを使って、引き続き調査します。
　まずは、動くパターンを一つ見つける事ができたのでよかったです。

# 参考資料
[Creating a batch ETL integration](https://developer.mulesoft.com/tutorials-and-howtos/integrations/creating-batch-etl-integration)
機械学習でフルマラソンのゴールタイムを予測～①：RuntasticのデータをGarmin Connectに取込～

#背景

ランニングの練習時に走った距離やペースなどを記録するスマホアプリ「Runtastic」や
ウォッチ型ウェアラブルデバイスの「Garmin」を用いて収集してきた
膨大な練習データ（といっても70件ほど）を利用して何かできないかと考え、
ひらめいたのが "フルマラソンのゴールタイム予測" でした。

走った距離やペース、心拍数など練習時に収集するデータのみならず、
練習内容の良しあしを左右すると考えられる様々な因子（天気、気温、体調など）を特徴量に含めて学習データを作成し、未だ走ったことのないフルマラソンのゴールタイムを予測することで今後の練習内容の改善検討に生かすことを目的として行います。
![キャプチャ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498362/250e4f21-dd96-68ab-73db-c023cdf9a0cf.jpeg)
（[pixtabay](https://pixabay.com/ja/photos/%E3%83%81%E3%83%BC%E3%82%BF%E3%83%BC-%E3%82%A2%E3%83%95%E3%83%AA%E3%82%AB-%E3%83%8A%E3%83%9F%E3%83%93%E3%82%A2-2859581/)より）

#概要
一般的なデータ分析のプロセスに沿って、以下のように進めていきます。
![キャプチャ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498362/fbec3cbd-86ee-4864-13d2-c267ff331a89.jpeg)
#データの収集
約１年半分のランニング練習データのうち、
最初の１年分はRuntasticで、
残りの半年分はGarminで記録しているため、
今後のデータ加工作業や分析作業を行いやすくするために
まずはデータをGarmin１つにまとめます。

方法はいたって簡単。
Runtasticのエクスポート機能を使ってgpxファイル形式で練習データをエクスポートし、
Garmin Connectのインポート機能を使って、Runtasticからエクスポートしたgpxファイルをインポートするだけです。

gpxファイルとは、GPS（位置情報）を含むデータ形式のことで、ランニングをした時のルートやペースなど、様々な情報が含まれています。

####Runtasticのアクティビティデータをエクスポート
①[Runtastic](https://www.runtastic.com/)の公式サイトを開く
②公式サイトの右上にあるログインボタンからログインする
![キャプチャ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498362/f85a8284-cd66-874d-ca92-57fb1a2d4515.jpeg)
③ログインしたら、ホーム->Exportメニューを選択
![キャプチャ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498362/c7de9153-2f98-e92d-0e21-ac5214a498f2.jpeg)
④エクスポートボタンをクリック
![キャプチャ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498362/8602fa5f-304b-4cb9-1ffe-f63723cbb95f.jpeg)
⑤エクスポートが終わるとダウンロードボタンが出てくるので、ダウンロードボタンをクリック
![キャプチャ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498362/d7967a84-d1ee-3a6d-f3dd-9e062c8f5b53.jpeg)
⑥所定のフォルダにzipファイルがダウンロードされます
　Sport-sessions -> GPS-data にgpxファイルが格納されています
![キャプチャ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498362/56ac3f75-42df-d112-de85-c77c33606d89.jpeg)
####GarminにRuntasticのgpxファイルをインポート

①[Garmin Connect](https://connect.garmin.com/)を開く
②画面右上にあるサインインボタンからサインインする
![キャプチャ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498362/8aa19e31-4ab9-0592-8acf-162b8c027295.jpeg)
③右上の雲のマークからデータのインポートを選択
![キャプチャ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498362/684d588d-ee78-4bfa-f18c-eb6c0da0af8a.jpeg)
④データのインポート画面に遷移するので、Runtasticからエクスポートしたgpxファイルをドラッグ&ドロップする
※zipファイルをドラッグ&ドロップしても取り込めないのでここは１ファイルずつ地道にやっていく
![キャプチャ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498362/ed064da6-5920-bb26-902e-bcb3686b6d5c.jpeg)
⑤取り込みたいgpxファイルを全てドラッグ&ドロップしたら「データのインポート」ボタンをクリック
⑥gpxファイルのインポートが完了すると、「インポートが完了しました」と表示されます
![キャプチャ.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/498362/9fe784c6-e08b-6df3-659f-fa2d8ea5e4ba.jpeg)

これでRuntasticからGarmin Connectへのデータ取込が完了しました！
ということで、ランニングの練習データがGarmin Connectにまとまりました。

次回はデータ加工として
Garmin Connectからアクティビティデータをcsv形式でエクスポートし、
データ項目の削除・追加と欠損値の補完を行っていきます。
デスクトップマスコットを作ってみる。【アプリの終了編】

#変更履歴

- 2020/2/26
    - 前の記事へのリンクを追加

- 2021/3/28
    - 内容をjavaのコードに変更
    - 記述内容をシステムトレイへのアイコン追加のみに変更
    - タイトルの変更
    - リンクの追加










#初めに

前回でマスコットの表示を作成しましたが、
現状ではアプリケーションの開始を行なった後に終了する手段がありません。

デスクトップで常駐するアプリケーションになることを考えると、
システムトレイにアイコンを追加してアプリを終了できるようにしようと思います。













#システムトレイにアイコン追加

システムトレイへの追加用のクラスを作成していきます。


```java:JDSystemTray.java

public final class JDSystemTray {
    
    // 中身はまだ

}

```

システムトレイにアイコンを追加する処理は以下の通りです。
アイコン追加する際はシステムトレイがサポートされているかどうかチェックしたうえで追加します。

```JDSystemTray.java

/** システムトレイのアイコン場所 プロジェクト配下からディレクトリを作成しておく*/
private static final String PATH = "/resource/icon/JDSystemTrayIcon.png";

public static void addSystemTray() {
    try {
        // システムアイコンがサポートされている場合
        if (SystemTray.isSupported()) {
            // アイコンの生成
            var img = ImageIO.read(JDSystemTray.class.getResource(PATH));
            // システムトレイの生成
            var trayIcon = new TrayIcon(img);
            // 画像の自動サイズ調整をする
            trayIcon.setImageAutoSize(true);
            // ツールチップの設定
            trayIcon.setToolTip("デスクトップマスコットシステム");
            // システムトレイの取得
            var systemTray = SystemTray.getSystemTray();
            // システムトレイに追加
            systemTray.add(trayIcon);
        }
    } catch(Throwable e) {
        // 何かしらの処理
    }
}

```

これで、システムトレイにアイコンは追加できました。

ただ、これでは追加しただけでクリックしようが何しようが
反応しない置物になっているため、右クリックでメニューが起動できるようにします。

まずは、メニューの作成を行います。

メニューはシステムトレイのアイコンのイベントとして追加するため、
引数でシステムトレイのアイコンをもらうようにします。

```JDSystemTray.java

private static void crateMenuBer(TrayIcon trayIcon) {
    // ポップアップメニューの作成
    var popup = new PopupMenu();
    // メニューのアイテム
    var exit = new MenuItem("アプリケーションの終了");

    //  メニューのクリックイベントの定義
    exit.addActionListener(e -> { System.exit(0); });

    // 作成したメニューアイテムを追加
    popup.add(exit);

    // アイコンに作成したポップアップメニューに追加
    trayIcon.setPopupMenu(popup);
}

```

これで、右クリックした際にメニューが起動するようになりました。
**addSystemTray**メソッドに**crateMenuBer**メソッドを呼び出す処理を追加することを忘れずに。

# テストコードの作成

では、テストコードを作成して動きを確認しましょう。
システムトレイに使用するアイコンはイラストやさんのアイコンをテスト用に使用しました。

- [ひらがな50音の丸型・白抜きイラスト文字](https://www.irasutoya.com/2013/04/hiragana.html)


```java:JDSystemTrayTest.java

public class JDSystemTrayTest {

    public static void main(String[] args) {
        JDSystemTray.addSystemTray();
    }
}

```

![001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555408/cffde067-8f0e-7082-8c57-269f38cffd20.png)
↓右クリック↓
![002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/555408/08fec94b-863e-adf1-c307-1157be00b720.png)

うむ。













#終わりに
今回はマスコットアプリケーションの終了方法について、実装しました。
次はマウス操作で投げたりすることができるようにしてみようと思います。

# リンク

- [デスクトップマスコットを作ってみる。【目次】](https://qiita.com/urado/items/4a810c457cb862e370e8)
AWS CodeCommit CodeDeploy CodePipeline 自動デプロイ

#####1. CodeDeploy用IAMロールを作る
#####2. EC2インスタンス用IAMロールを作る
#####3. 2で作ったIAMロールをEC2インスタンスに割り当てる
#####4. CodeDeployアプリケーションの作成
#####5. EC2にCodeDeployエージェントインストール

## CodeDeploy用IAMロールを作る
<img width="1437" alt="スクリーンショット 2020-02-23 20.32.12.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239663/c4692a52-ed4a-6fab-9f52-4ca50d99110a.png">
ロールの作成　
↓
AWSサービス 選択
↓
ユースケースの選択=> CodeDeploy 
↓
<img width="1439" alt="スクリーンショット 2020-02-23 20.33.10.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239663/3f3b5f92-a5d7-50ec-b1d6-c240191c529a.png">
↓
次のステップ：アクセス権限
↓
AWSCodeDeployRoleを追加
↓
タグの追加 →　特になし
↓
<img width="1440" alt="スクリーンショット 2020-02-23 20.36.37.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239663/b4ec38a0-155d-a6ee-2b2e-c5719785a943.png">
ロールの作成

## EC2インスタンス用IAMロールを作る
上記と同じように
ユースケースの選択 => `EC2`
Attach アクセス権限ポリシー＝＞`AmazonEC2RoleforAWSCodeDeploy`
タグ=> `特になし`
を選択
ロール名は`CodeDeploy-for_ec2`にしました。
<img width="1440" alt="スクリーンショット 2020-02-23 20.40.48.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239663/6ceee48b-3e97-229e-9222-543287e96dbf.png">
ロールの作成

## CodeDeploy-for_ec2をEC2インスタンスに割り当てる
EC2の管理画面を開き、インスタンスを選択し、CodeComitの更新をしたいインスタンスの
アクション/インスタンスの設定/IAMロールの割り当てをクリック
<img width="1437" alt="スクリーンショット 2020-02-23 20.45.03.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239663/11f4d0b8-7d29-c409-0c25-426ad5699639.png">
そうすると、
<img width="1440" alt="スクリーンショット 2020-02-23 20.47.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239663/1223997b-3d2e-4273-2a53-961861a763f4.png">
で適用。

## CodeDeployアプリケーションの作成
awsコンソールから`CodeDeploy`管理者画面にいく
左上の`開発者用ツール` /`CodeDeploy`をクリック
アプリケーションの作成をクリック

<img width="1440" alt="スクリーンショット 2020-02-23 20.51.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/239663/b05e5472-a003-808c-d4ec-ce408ccdb271.png">

アプリケーションの作成

#### 次にデプロイグループを作成する。
サービスロールは１で作ったロールを選択
デプロイタイプ　`インプレース`
環境設定 `Amazon EC2 インスタンス`

ロードバランサー `チェック外す`
## EC2にCodeDeployエージェントインストール
インストール

```console:ec2のターミナル
[ec2-user@ip-10-0-1-147 src]$ sudo yum -y update
[ec2-user@ip-10-0-1-147 src]$ sudo yum -y install ruby wget
[ec2-user@ip-10-0-1-147 src]$ wget https://aws-codedeploy-us-east-1.s3.amazonaws.com/latest/install
[ec2-user@ip-10-0-1-147 src]$ chmod +x ./install
[ec2-user@ip-10-0-1-147 src]$ sudo ./install auto
```

起動

```console:ec2のターミナル
[ec2-user@ip-10-0-1-147 src]$ sudo service codedeploy-agent start
Starting codedeploy-agent:[ec2-user@ip-10-0-1-147 src]$  sudo chkconfig codedeploy-agent on
情報:'systemctl enable codedeploy-agent.service'へ転送しています
```
XWindow の GUI アプリを CUI で操作

「VNC をセカンドディスプレイとして使う on Ubuntu 18.04」
https://qiita.com/nanbuwks/items/d12a98c8d41630c31a55

では、いろいろヘンなことをして実現しているため、いろいろヘンなことが起こります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/c410529e-0e6c-b1bf-4a10-b8498103158f.png)

一番には、VNCサブディスプレイ内で残像が無限に更新し続けるという問題があります。
これをごまかすため、適当なアプリウィンドウをVNCサブディスプレイ内で最大化しています。
背景を表示しないようにすればこの問題は解決します。

# スライドショー
適当なアプリということで、壁紙ライブラリをスライドショーで表示することにしました。

壁紙アプリとしてristrettoを使います。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/048d683a-d36e-335f-80bb-e344d2ddf5aa.png)
Photo Claudio Schwarz | @purzlbaum

スライドショーアプリとして、以下を選びました。

- スライドショー時に全画面表示にならない
- VNCサブディスプレイ内でウィンドウ位置制御ができる(xdotool使用)
- スライドショー間隔の調整ができる

意外と条件を満たすものが無かった。（選定の詳細はAPPENDIXにて。）

手作業だとめんどくさいのでスクリプトにします。しかしながら本来

```

 wmctrl -ir  $window_id  -e 0,0,0,1920,1080 # 
```
だけで大丈夫なはずが、VNCサブディスプレイ内にスライドショーアプリが入ってないとうまくいきません。

起動時にはリアルディスプレイ内部にしか表示されないようなので、以下のようにめんどくさい手順を踏むことにしました。

- ristrettoを起動して
- 最大化になっていれば解除
- 画面境界に移動
- ristrettoのスライドショーをF5で開始
- Alt+Spaceでウィンドウのキーボード操作を可能にする
- 移動メニューを選ぶ
- 矢印キーで移動
- エンターで位置確定
- ウィンドウをVNCサブディスプレイの背景を覆うサイズに変更

このような操作をキーボードから行うのと同じようにスクリプトから操作します。
ristrettoのスライドショーオプションもうまく動かないので、F5キーを押してスライドショーを開始する操作も自動化します。

# 環境

- Ubuntu 18.04
- 使用するツール
 - xdotool
 - wmctrl

画面構成
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139524/f918fa92-803b-2966-56d4-f608e47df0ff.png)

# スクリプト


```

ristretto /home/nanbuwks/Downloads/unsplash -s & # ristrettoを壁紙フォルダを指定して起動
xdotool sleep 2 # 起動するまでの待ち時間
window_id=$(xdotool getactivewindow) # restrettoウィンドウIDを取得
echo $window_id # デバッグ用
wmctrl -ir  $window_id -b remove,maximized_vert,maximized_horz # 最大化で起動した場合位置移動ができないため最大化を解除
wmctrl -ir  $window_id  -e 0,0,200,500,500 # 位置を画面端に移動（本来これでVNCサブディスプレイに移動するはずがウィンドウの左端座標が1920位置にしかならない）
xdotool sleep 0.4 
xdotool key F5 # スライドショー開始
xdotool sleep 0.4 
xdotool key alt+space # ウィンドウのキーボード操作を可能にする
xdotool sleep 0.4
xdotool key Down # 移動メニューのために下矢印を2回
xdotool sleep 0.2
xdotool key Down
xdotool sleep 0.2
xdotool key Return # 移動メニュー選択
xdotool sleep 0.1
xdotool key Left # ウィンドウを移動してVNCサブディスプレイ内に
xdotool sleep 0.1
xdotool key Left
xdotool sleep 0.1
xdotool key Left
xdotool sleep 0.1
xdotool key Left
xdotool key Return # 位置確定
wmctrl -ir  $window_id  -e 0,0,0,1920,1080 # VNCサブディスプレイ全体を覆う

```

# APPENDIX

試したスライドショーアプリ

- eog (Ubuntu標準の画像ビューワ)
- shotwell
- nomacs
- slide
- geeqie
- gthumb
- gwenview
- viewnior
- mirage
- ristretto



と試してristrettoになりました。
複数のjpgファイルを一つのPDFファイルに変換する

# やりたいこと
scannerなどで取り入れた明細の画像ファイル(jpg or JPEG or JPG)をPDFファイルにまとめたい
明細はディレクトリ毎に区分けしてあり、区分け毎にPDFファイルにまとめる
ディレクトリの中には日付ごとにまとめらている

``` sh
ls ~/scan/meisai/
   自動車保険明細
　　自動車保険明細/2017/
　　自動車保険明細/2018/
　　自動車保険明細/2019/
　　自動車保険明細/2020/
　　クレジッドカードA/2017/
　　クレジッドカードA/2018/
　　クレジッドカードA/2019/
　　クレジッドカードA/2020/
   医療明細2020_01_20.jpg
　　.DS_store
```
このようなディレクトリ構成のなかに複数の画像ファイルが存在している。
それを以下のようにまとめる

``` sh
ls ~/scan/meisai_pdf/
   自動車保険明細.pdf
　　クレジッドカードA.pdf
　　.DS_store
```
使用した画像ファイルは手動で削除する。

# 使用したもの
python3
import os
import img2pdf

# コード

``` py
import os
import img2pdf

EXP_DIR = "/Users/myname/scan/meisai"
OUT_DIR = "/Users/myname/scan/meisai_pdf"


def extract_image_file_list(folder_path):
    ret = []
    if os.path.isdir(folder_path) == False:
        return ret 
    for t in sorted(os.listdir(path=folder_path)):
        if os.path.isdir(folder_path + "/" + t):
            # 再起的にディレクトリを辿っていく
            ret += extract_image_file_list(folder_path + "/" + t)
        else:
            ret.append(folder_path + "/" + t)
    return ret

def image_to_pdf(pdf_name,image_list):
    convert_target = []
    for i in image_list:
        if i.endswith(".jpg") or i.endswith(".JPG") or i.endswith(".jpeg"):  
            convert_target.append(i)
    if len(convert_target) == 0:
        return
    with open(pdf_name + ".pdf", "wb") as f:
        f.write(img2pdf.convert(convert_target))

#os.chdir(EXP_DIR)  # ディレクトリに移動して相対パスで処理したい場合。
for path in os.listdir(path=EXP_DIR):
    print(path)
    files = extract_image_file_list(EXP_DIR + "/" + path)
    image_to_pdf(OUT_DIR + "/" + path,files)

```
実務未経験エンジニアはDockerを最低限勉強しておく。という提案

こんにちは。

おかげさまで先日めでたく駆け出しエンジニアとしてのスタートを切りました。
就職するまでの経緯については
[【実体験をもとに】30歳未経験から独学4ヶ月でバックエンドエンジニアとしてWeb系自社開発企業へ転職するまでのロードマップ](https://qiita.com/WATARU_GP01/items/7e534e08f9f583d15775)
こちらに記載してます。

さてさてそんな私が出勤初日から「勉強しておいてよかった〜」と思ったスキルがありまして、それが**Docker**です。

内容をYouTube動画でも投稿してるので是非見ください

https://youtu.be/j0jXUIF9KKk

これから駆け出しプログラマーとしての転職を目指す方々は是非学んでおいたほうが良いと感じたので、私自身の振り返りも含め紹介します。

併せて、「これちゃんと勉強しておけばよかった…」と後悔している技術も紹介します。

#なぜDcokerを勉強しておいて良かったか
###スタートで躓かなかった

入社初日の挨拶等が終わり、帰宅したあと会社からGitHubの招待がきました。

会社にジョインして最初にやることは（挨拶とか説明を抜きにすると）開発環境の構築というのはきっとどの企業でも同じでしょう。

昨今のモダンなWeb企業ではDockerでの環境構築が主流なので、例に漏れず招待されたGitHubの`READ ME`にもDockerでの構築を行う旨が記載されていました。

**入社して最初に打ったコマンドが`Git clone`、
そして次が`docker-compose build`です**

ここでもし私がDockerについて学んでなければ、**開発環境の構築をするための勉強からスタート**することになったでしょう。

「仕事をスタートすることができない」という状況は評価者からのイメージも良くないですし、自分自身のメンタルにもかなりのダメージだと思います（汗）


###どんな勉強をしておけばいいのか

Dockerは**ただ構築に使うだけ**なら簡単なものです。
コマンドを打つだけですからね。
しかしDockerの概念や構造をそれなりに学んでおかないと後々に困ることがあります。

いざエラーが発生した時にlogの確認ができなかったり。
DBの中身を確認したくてもどのコンテナにどう入れば良いか分からなかったり。

仕事をする上で必ず触れるポイントなので、入社前に学習しましょう。

とりあえず入社前は以下のような内容を学んでおけ良いのではないでしょうか？
- コンテナの概念
- Dockerfileの内容
- docker-compose.ymlの内容
- docker-composeコマンドの使い方
(build.up.down.runをとりあえず)
- dockerコンテナへの入り方(`docker exec -i -t コンテナID bash`)

ハードルが高く感じがちですが、ネット上に情報がたくさん落ちてますので参考にすれば初学者でもちゃんと扱えるようになります。
というか、Dockerfileやdocker-compose.ymlはテンプレがたくさん落ちてるのでとにかく構築してポートフォリオに導入してみるのが早道でしょう。

###コンテナの概念については文章で見るより説明を受けたほうが早い。
ネット上には図を使って説明している記事など多くあり情報ソースに事欠かないDockerですが、なかでもYoutubeで解説動画を出しているベテランエンジニアさんがいます。
私がヘビーローテションしていたオススメのYouTuberさんを紹介します。

### くろかわこうへい　さんの「目指せコンテナマスター！」シリーズ
[今から追いつくDocker講座！AWS ECSとFargateで目指せコンテナマスター！〜シリーズ1回目〜]
(https://www.youtube.com/watch?v=DS5HBTMG1RI&list=PLtpYHR4V8Mg-jbuk4yoXhXwJtreodnvzg)

[![IMAGE ALT TEXT HERE](http://img.youtube.com/vi/DS5HBTMG1RI/0.jpg)](http://www.youtube.com/watch?v=DS5HBTMG1RI)
※くろかわさんご本人の許可を得て掲載しています。

とても丁寧に説明してくださっているので、わざわざ他で調べる必要がないくらいです(๑❛౪❛๑)
まずは前半のコンテナについての概要をサックリと理解すると良いのではないでしょうか。

他にもAWSについての解説を出していらっしゃるので必見です。

個人的にはくろかわさんの喋り方がクセになるんですよね〜w


##SQL
ついでにSQLも勉強しておいてよかったな〜と思ったので紹介します。

入社してたった数日間ですが、一日中DBを触ってた日もありました。
難しいことができる必要は無いですが、基本的な動作は行えるようにしておいて良かったと思います。

基本的な動作って何？って言うと、以下のサイトに書いてある内容くらいは把握していれば良いのではないでしょうか。
https://yukun.info/category/sql/

SQLクエリを打って検索や追加を行えるって感じですね。

※ちなみに私はmigrationエラーするので、入社時もschema_migrationsを見に行ったりダブってるmigrationファイルをinsertしたりしてました。

#勉強しておけば良かったと後悔したこと
##rails consoleの使い方
未経験エンジニアがポートフォリオを作る過程で**デバッグ作業をしっかり行なう。**ということは少ないのではないでしょうか？

既存の商材にもバグは必ず発生しますし、その際には他人が作ったコードが実際にどう動作しているのか知らなければ直せません。

私はポートフォリオ作成において`rails console`をしっかりと使用していなかったので、メソッドの動作確認がスムーズにできませんでした。


##リファクタリング
環境構築が終わったら、既存のソースコードの内容を把握する作業が必要だと思います。

恥ずかしながら僕はまともにリファクタリングの勉強をしていなかったので、まさにちんぷんかんぷんでした…

scopeで式をメソッドとして定義したりviewを２ファイルに分割するなど、ちゃんと勉強や活用をしておかないとコードの把握に時間がかかりメンタルが・・・。

##mailer
「開発をしていれば100%mailerを使う機会がある。」ってなんで思いつかなかったんだろう…。
と後悔する限りです。

未経験者のポートフォリオには**ユーザー登録時にメールが届くくらいの機能は付けておくべきでしょう！**


#終わり
入社して１週間になりますが、正直「何もできなかった・・・」というのが感想です。
転職前は**転職を有利にするスキル**を身につけることに頭がいっぱいでしたが、実践に役立つスキルに対しアンテナを張って学ぶことも未経験エンジニアには大切なことだなと思いました。
Rails6 DBのupdate_atなどに格納された日にちをYYYY-MM-DDの形で取り出す

# 目的

- DBのcreated_atやupdate_atなどのカラムに格納された日にちの値を`YYYY-MM-DD HH:MM:SS`の形で取り出す方法をまとめる

# 困りごと

- 特定のidのupdate_atをそのまま使用しようとすると下記の様に出力されてしまう。

    ```ruby
    Tue, 04 Feb 2020 09:01:35 UTC +00:00
    ```

- `YYYY-MM-DD HH:MM:SS`の形で出力したい。

    ```ruby
    2020-02-04 09:01:35
    ```

# 結論

- 取得したupdate_atの値を`.to_s`で文字列に変換することで`YYYY-MM-DD HH:MM:SS`で得ることができる。

# 書き方の例

- Postモデルのpostsテーブルのidが1のレコードのupdate_atカラムに格納されている日にちデータを`YYYY-MM-DD HH:MM:SS`の形で変数に格納する方法を記載する。
- postsデーブルのidが1のレコードに格納されているデータはすでにDBに格納されているものとする。

    ```ruby
    #DBのpostsテーブルのidが1のレコード情報を変数@postに格納
    @post = Post.find_by(id: 1)
    #変数last_update_dateに先に取得したidが1のレコードのupdated_atをto_sメソッドにて文字列にしたものを格納する。
    last_update_date = @post.updated_at.to_s
    puts last_update_date
    ```
DynamoDBのテーブルをscanするPythonコード

# 概要
- boto3(AWS SDK for Python)でDynamoDBをスキャンするコードです
- きちんとscanするために `LastEvaluatedKey` を使ってループする必要があります
- 自分用スニペットです

# コード

```python
def get_records(table, **kwargs):
    while True:
        response = table.scan(**kwargs)
        for item in response['Items']:
            yield item
        if 'LastEvaluatedKey' not in response:
            break
        kwargs.update(ExclusiveStartKey=response['LastEvaluatedKey'])
```

# 使い方サンプル

## フルスキャン
```python
import boto3

dynamodb = boto3.resource('dynamodb')
records = get_records(
    dynamodb.Table('mytable')
)
for record in records:
    print(record)
```

## フルスキャン,Projection指定

```python
import boto3

dynamodb = boto3.resource('dynamodb')
records = get_records(
    dynamodb.Table('mytable'),
    ProjectionExpression='MyPartitionKey, MyTimestamp'
)
for record in records:
    print(record)
```

## Projection指定,Filter指定

```python
import boto3
from boto3.dynamodb.conditions import Key

dynamodb = boto3.resource('dynamodb')
records = get_records(
    dynamodb.Table('mytable'),
    ProjectionExpression='MyPartitionKey, MyTimestamp',
    FilterExpression=Key('MyTimestamp').begins_with('2020-02-23')
)
for record in records:
    print(record)
```

## Projection指定,Filter指定,Index指定

```python
import boto3
from boto3.dynamodb.conditions import Key

dynamodb = boto3.resource('dynamodb')
records = get_records(
    dynamodb.Table('mytable'),
    ProjectionExpression='MyPartitionKey, MyTimestamp',
    FilterExpression=Key('MyTimestamp').begins_with('2020-02-23'),
    IndexName='MyTimestamp-index'
)
for record in records:
    print(record)
```

# リンク
- https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb.html#DynamoDB.Table.scan
- https://boto3.amazonaws.com/v1/documentation/api/latest/_modules/boto3/dynamodb/conditions.html
- https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GettingStarted.Python.04.html
デプロイ時によく使うコマンド

前提
railsバージョン:5.2.4.1
レポジトリ名：freemarket
pemキー名：freemarket.pem
ユーザー名：ec2-user
Elastic IP:@12.345.678.910
***
#サーバー立ち上げ
```:ターミナル（ローカル）
cd .ssh/
ssh -i freemarket.pem ec2-user@12.345.678.910
cd /var/www/freemarket
```
#pemキー確認

```:ターミナル（ローカル）
cd
cd .ssh
ls
```

#デプロイ
```:ターミナル（ローカル）
bundle exec cap production deploy
```


#AWS アクセスキー、キーID確認

```:ターミナル（ローカル）
env | grep AWS_SECRET_ACCESS_KEY
env | grep AWS_ACCESS_KEY_ID
```

#sql
```:ターミナル（リモート）
状況確認
sudo service mysqld status
起動する
sudo service mysqld start
```
#unicorn
```:ターミナル（リモート）
状況確認
ps auxwww | grep unicorn
ログ
cat log/unicorn.stderr.log

less /var/www/freemarket/current/log/unicorn.stderr.log
または
cat /var/www/freemarket/current/log/unicorn.stderr.log
```
#nginx
```:ターミナル（リモート）
再起動
sudo service nginx restart

ログ
ホームディレクトリで
sudo less /var/log/nginx/error.log 
```

#capistrano
```:ターミナル（ローカル）
ログ
less log/capistrano.log
```

#vim
```:ターミナル（ローカル）
vim ~/.bash_profile

(credential.yml編集）
EDITOR='vim' rails credentials:edit

```

```:ターミナル（リモート）
vim ~/.zshrc

（環境変数を設定する場合）
sudo vim /etc/environment

（記述する場合）
i

（抜け出し方）
ESC
:wq

（強制的な上書き）
:w !sudo tee %
:q!

（読み取り専用から入って抜け出す時）
control + c
:q!
```
#エラー文
we're sorry, but something went wrongの内容を把握する方法

```:/config/environments/production.rb
config.consider_all_requests_local       = true ←デフォルトはfalse
Laravelでミニブログを作ろう #3

前回:https://qiita.com/customaddone/items/7563a3252e4debc05f1a
GitHub:https://github.com/customaddone/larablog

今回はdockerでterraformのコンテナを立てます

# Terraform概要
###Terraformとは
コードによりインフラの構築、変更を行うツール
AWS,GCPなどのクラウド上のインフラに手を加えることができる

### Terraformのメリット
GUIを通さずインフラに変更を加えられる（ブラウザ上からの操作によるミスを防げる）
一度書いたコードを使い回すことができる

# Dockerfile
まずdockerディレクトリ直下にTerraform用のディレクトリを追加して下さい

```dockerfile:docker/terraform/Dockerfile
FROM alpine:3.10

# 最新バージョン
ARG terraform_version="0.12.21"

# Terraformインストール用のコマンドを使えるようにwget unzip curlをインストール
# Terraformをインストールして解凍する
# ディレクトリを作成する
RUN apk update --no-cache \
    && apk add --no-cache \
        wget \
        unzip \
        curl \
    && wget https://releases.hashicorp.com/terraform/${terraform_version}/terraform_${terraform_version}_linux_amd64.zip \
    && unzip ./terraform_${terraform_version}_linux_amd64.zip -d /usr/local/bin/ \
    && rm -rf ./terraform_${terraform_version}_linux_amd64.zip \
    && mkdir terraform

WORKDIR /terraform

# いちいちterraformコマンドを打つのが面倒な場合
# CMD ["terraform"]
```

#docker-compose
```xml:docker-compose.yaml
terraform:
    build:
      context: .
      dockerfile: docker/terraform/Dockerfile
    volumes:
      - ./terraform:/terraform/ 
    environment:
      - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID}
      - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY}
      - AWS_REGION=${AWS_DEFAULT_REGION}
    tty: true
```
environmentでAWS_ACCESS_KEY_ID、AWS_SECRET_ACCESS_KEYを指定しますが
、直に書いたままgit pushするとGitHub上に反映され、キーの情報が全世界に向けて公開される（最悪赤の他人にキーを悪用されてMAX課金されてしまう）ので、環境変数を用います

```:.env
AWS_ACCESS_KEY_ID=xxxxxxxxxxxxxxx
AWS_SECRET_ACCESS_KEY=xxxxxxxxxxxxxxxxx
AWS_DEFAULT_REGION=xxxxxxxxxxxxxxx
```

docker-compose.yamlはデフォルトで.envの情報を読み込むので、.envに変数の値（キーの情報）を書くと、docker-compose.yamlでその値を使えます
.envはgitignoreに書いてあるので、git pushしてもGitHub上に反映されません

これでdocker-compose up -d した後、docker-compose exec terraform shしてterraformコンテナの中に入ることでterraformを利用できます

# Terraformの使用
試しにVPCを立ててみましょう

```
larablog
　　├── terraform
           ├── vpc # 追加
```

```terraform:terraform/vpc/aws_vpc.tf
# ファイルの末尾にtfをつけてください
# VPCは他のネットワークから論理的に切り離されたネットワークです
# EC2などのリソースを配置します
resource "aws_vpc" "example" {

  #vpcのipv4のアドレス範囲を設定します
  # 10.0.0.0〜10.0.255.255まで使えるよ
  cidr_block           = "10.0.0.0/16"

  # AWSのDNSサーバーによる名前解決を有効にする
  # route53が使えるようになる
  enable_dns_support   = true
  enable_dns_hostnames = true

  # タグをつけてコンソール上でわかりやすく
  tags = {
    Name = "larablog"
  }
}
```
vpcディレクトリに移動してterraform init, terraform applyするとvpcが作成されます。
が、作成と同時にvpcディレクトリにterraform.tfstate等のファイルができます（どでかい）。これらはgitignoreに書き込んでgit管理しないようしましょう

```:.gitignore
...

# Local .terraform directories
/terraform/**/.terraform/* # 追加

# .tfstate files
/terraform/**/*.tfstate # 追加
/terraform/**/*.tfstate.* # 追加

...
```

これでterraform init, terraform applyコマンドを打つとvpcが立ち上がります。AWSのコンソールで確認してみてください
[Maya MEL]選択したオブジェクトに対してコピースキンウエイトするだけ

バインドしたあとに、コピー先のオブジェクトを複数選択して
bodyCageの部分の名前を変更してコピー元のオブジェクトの名前を入力して実行

```
string $meshes[] = `ls -sl`;
for($mesh in $meshes)
{
	select -r "bodyCage" $mesh;
	copySkinWeights  -noMirror -surfaceAssociation closestPoint -influenceAssociation closestJoint;
}