OmniAICreator/Qiita-1.07M huggingface dataset:
Goでzstd形式のディレクトリ圧縮をやってみた

# やりたいこと
Goでzstdを用いたディレクトリ圧縮をしたい。

# zstdとは

http://facebook.github.io/zstd/

facebookがOSS化した圧縮アルゴリズム。
他の圧縮アルゴリズムよりも圧縮展開速度・圧縮効率に優れている。らしい。

# Goで使えんの？

ラッパーライブラリがありました。

リポジトリ: https://github.com/DataDog/zstd

godoc: https://godoc.org/github.com/DataDog/zstd

今回は上記を使用しますが、これ以外にもあるみたいです。

# 使ってみました

### サンプルリポジトリ

https://github.com/Raita876/zstd-sample

tar, zstdパッケージを使用してsampleディレクトリをアーカイブ、圧縮しています。

### ディレクトリ構成

```
$ tree .
.
├── Makefile
├── README.md
├── go.mod
├── go.sum
├── main.go
└── sample //圧縮対象のディレクトリ
    ├── bcat.txt
    ├── go
    │   └── go.txt
    └── hello.txt

2 directories, 8 files
```

### コマンド

圧縮
sampleディレクトリを圧縮してsample.zstdを生成

```
$ go run main.go compress
sample
sample/bcat.txt
sample/go
sample/go/go.txt
sample/hello.txt
$ ls sample.zstd
sample.zstd
```

解凍
sample.zstdを解凍（outputディレクトリ配下を解凍先に指定）

```
$ go run main.go decompress
output/sample/bcat.txt
output/sample/go/go.txt
output/sample/hello.txt
$ tree output
output
└── sample
    ├── bcat.txt
    ├── go
    │   └── go.txt
    └── hello.txt

2 directories, 3 files
```

# まとめ
細かい説明は端折っちゃいましたが、とりあえずGoでできました。

shellスクリプト等でもzstdでの圧縮はできますが、Goで書けるとプログラミング言語の恩恵を受けることができるので、選択肢の一つとして持っておくといいなと感じました。

# 参考

https://medium.com/@skdomino/taring-untaring-files-in-go-6b07cf56bc07
DockerでJupyterLab(Python)環境を構築

# 実現できたこと
Dockerで、以下の様な機械学習用のJupyterLab環境が作成できました。
DockerイメージビルドからJupyterLab起動までの流れをまとめました

- Pythonのバージョン切り替えが容易にできた。 (Python3 ⇄ Python2)
- JupyterLabの起動が簡単[^1]
- 環境が不要になったらすぐ削除可能。
- MacとWindows上で、簡単に環境構築可能。

#### 実際に構築した環境

- macOS Catalina (MacBook Pro(15-inch, Late2016))
- Windows 10

# Dockerイメージ
本ブログは以下に移動しましたので、以下の記事にアクセスをお願いします。

**[docker-composeでデータ分析や機械学習のためのJupyterLab環境を手軽に構築する方法 - SE技術メモ](https://bit.ly/3vjDkDy)**
Ruby 破壊的メソッド 非破壊的メソッド

#破壊的メソッド、非破壊的メソッドって？？

##1.破壊的メソッド
オブジェスト自体を変更するメソッドである。
それ以降、オブジェクトは、変更された状態が続く。

##2.非破壊的メソッド
オブジェクト自体を変更した場合の値を返すメソッド
以降のプログラム上では、値は元のまま

##3.例
例として、
小文字→大文字の破壊的メソッドのupcase!
小文字→大文字の非破壊的メソッドのupcase

~~~Ruby
str = "abc"
str.upcase!
puts str
~~~

実行結果

~~~
ABC
~~~

~~~Ruby
str = "abc"
puts str.upcase
puts str
~~~

実行結果

~~~
ABC
abc
~~~
IPsecについて

## IPsecについて
- ネットワーク層で暗号化や認証を行うプロトコル。
- IPv6では標準プロトコルとして採用されている。
- マルチキャスト伝送はできないので、GREを検討する必要がある。
- 以下のプロトコルから成り立つ。
  - AH(Authentication Header)
  - ESP(Encapsulated Security Payload)
  - IKE(Internet Key Exchange)
- AHは認証のみ、ESPは認証と暗号化の情報が入る。

## SA(Security Association)
- IKEによって確立されるコネクションのこと。
- ISAKMP SAが1つ、IPsec SAが2つ。
- SAの確立に先立ち認証方式を決める。(事前共有鍵、公開鍵暗号、デジタル署名) 

## IPsecの通信
### トランスポートモード
- ホスト間でのIPsec-VPN
- ホストにVPNソフトをインストール必要がある。
- IPヘッダは暗号化されない。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149611/6f56f396-755b-4547-e36e-150ea7ca892d.png)
https://www.infraexpert.com/study/ipsec6.html

### トンネルモード
- ルータ間でのIPsec-VPN
- IPヘッダは暗号化される。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/149611/a186186b-974e-f200-5bf9-7a612916d08e.png)
https://www.infraexpert.com/study/ipsec6.html

## 参考
[IPsecとは - ネットワークエンジニアとして]
(https://www.infraexpert.com/study/study10.html)
[Rails]「×××_path」ってどう編集すればいいの？

#経緯

オリジナルアプリ作成に伴いログイン機能を実装しました。

その際、ログイン後にアクセスしたいページに遷移することができず、かなり時間をとられたので備忘録的に書き残します。

#該当箇所


```rb:controrllers/application_controller.rb

class Users::SessionsController 
  def after_sign_in_path_for(resource)
    XXXX_path #ご自身のpath
  end
```

#詰まった理由

・「×××_path」が何を表すのか分からなかった。

・「×××」に何を入れればいいか分からなかった。


#分かったこと

・「×××_path」が何を表すのか分からなかった。
→　**サインインした後に表示したいパス（URL）**



・「×××」に何を入れればいいか分からなかった。
→　**rails routesした際のPATHを入力**
（※今回は赤線部分「bustersコントローラー」の「indexアクション」に紐づくViewをログイン後に表示したいので、赤戦部分のPATHを入力）

![login1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561934/2bc831a0-2e8b-52a9-9ae1-537aa7476039.png)


#解決策

```rb:controrllers/application_controller.rb

class Users::SessionsController 
  def after_sign_in_path_for(resource)
    busters_path #ご自身のpath
  end
```
【jQuery】ボタンを押したら文字の表示・削除を実行するtoggle

```html:filename.html
<button id="toggle">トグルする</button>
<p id="text">ここがtoggleされます</p>
```

```css:filename.css
#text {
  display: none
}
```

```javascript:filename.js
$("#toggle").click(function() {
  if($("#text").css("display")== "none") {
    $("#text").fadeIn("slow");
  } else {
    $("#text").fadeOut("slow");
  }
})
```
IntelliJ IDE で TypeScript+Reactの開発環境準備

# 概要
VSCodeの情報が溢れているのにIntelliJ IDE 系列のTypeScript+Reactの情報がなかったので試してみました。
IDEの違いやバージョンで小さな差異はあるかもしれないですが、大体こんな感じで出来ると思います。


## 必要なプラグイン
Prettier
File Watchers

## 設定
Preferences -> Tools -> File Wathers

「+」 アイコンからPrettierを選択します

以下のようにダイアログが出てくるので File Type -> TypeScriptにします。

<img width="768" alt="prettier_dialog.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74504/e90d8075-3e44-e100-c75b-346bb34c8b95.png">

同じ要領であと二つ追加します。 
File Type -> TypeScript JSX
File Type -> JSON

この辺りは必要なものを適宜やっていきます。



## プロジェクトの作成と実行

File -> New Project -> Static Web -> React APP
Reactアプリのシンプルなテンプレートを作ってくれます。

次にRun(npm start)で実行。
規定のブラウザが開いて下記のURLが立ち上がります。
http://localhost:3000/


<img width="647" alt="react_start_page.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74504/788613f6-1673-d664-5add-f83eeb638c1a.png">


Reactを使っている人なら同じみの画面が出てきます。


## ファイルを書き換えて保存してみる
何か変更があるとFile Wathersが反応してPretterで整形してくれます。

試しに src/App.tsxを書き換えてみます。

```
Edit <code>src/App.tsx</code> and save to reload.
```

```
Edit <code>src/App.tsx</code> and save to reload.AAAA


```

末尾にAAAAと改行を二つ付け加えてファイルを保存します。
トランスパイルが終わると自動で整形して無駄な改行二つが無くなります。

ブラウザ画面では次のように出てきます。

<img width="664" alt="react_start_page_AAAA.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/74504/905ff471-1b2d-2944-8b1b-75691a684553.png">


末尾にAAAAが付きましたね。

トランスパイルはIDEのほうでソースに変更があれば自動で行われてブラウザに同期されて反映されます。
所謂、ホットリロードされます。


## VSCodeと比べてのメリット

+ PretterやESLintの設定を自動で **よしなに** してくれる。
+ TypeScriptとReactに集中できる

## VSCodeと比べてのデメリット
+ Pretterの整形がちょっと遅い。VSCodeだと一瞬でおわっったのが0.5秒くらいかかります。
ただ、VSCodeの場合整形後にESLintのエラー波線が一瞬入るので気持ちの問題の気もします。
+ PretterやESLint+webpackの知識が覚えられない。
+ 多くの人がVSCodeを使って開発している（自分の観測範囲内で）

## 感想
設定関連のメリットがそのままデメリットに繋がりますね。
ただ、PretterやESLintの設定はバージョンアップで微妙に変わったりするので、この部分をクリアできるのは嬉しかったりします。
webpackは覚えた方が良いと推奨している方も多いので (自分の中の仮想の人間)TypeScriptとReactがそれなりに使えるようになったら、設定周りを勉強するというのもありかなと思います。

たぶん、PretterやESLintの設定ファイルをおけば自分で変更できると思うのですが調査してないです。

不便を感じるまでIntelliJ使ってみます。

最後にVSCodeじゃなくてIntelliJのIDEを選択したのは金を払っているから勿体無いなぁという動機です。
enumerate

```py:1
for player in ['勇者', '戦士', '魔法使い']:
    print(player)
```

```:1の実行結果
勇者
戦士
魔法使い
```

これにインデックスも一緒に出力したい場合、

```py:indexも
i = 0
for player in ['勇者', '戦士', '魔法使い']:
    print(i, player)
    i += 1
```

```:indexもの実行結果
0 勇者
1 戦士
2 魔法使い
```

ここでenumerate関数を使えば、
もっと簡単に書ける

```py:enumerate
for i, player in enumerate(['勇者', '戦士', '魔法使い']):
    print(i, player)
```

```:enumerateの実行結果
0 勇者
1 戦士
2 魔法使い
```
HTML タグの入れ子構造について

HTMLのタグの入れ子のルールについて学んだんで書いてみました。

#入れ子のルールは何で決まっているのか？
時々なるのですが、「このタグってこの中のタグに入れてもいいんだっけ？」
こういった経験皆さんも一度はあると思います。

ではどのようにして決まっているのかというと
HTML５にはコンテンツモデルとカテゴリーという概念が存在します。

##カテゴリーとは？

html5では全ての要素が６つのカテゴリーに属している
以下の
**フローコンテンツ
セクショニングコンテンツ
ヘッディングコンテンツ
フレージングコンテンツ
エンベデッドコンテンツ
インタラクティブコンテンツ**
である

##コンテンツモデルとは？
その要素にはどのカテゴリーのコンテンツを入れていいか決めてるルールの事です
例えば下記の例はコンテンツモデルに違反しています。
pタグが含んでいいコンテンツはフレージングコンテンツのカテゴリーのみです。
hタグはヘッディングコンテンツなのでルール違反となります。

```html
<p>
  <h1>ヘッドライン</h1>
</p>
```
正しいコンテンツモデル
imgタグはフレージングコンテンツなので正しいコンテンツモデルと言えます。

```html
<p>
  <img src="img.png">
</p>
```

こう言った概念を知るのって大事だと思ったので投稿させてもらいました。
しかし、じゃあどれがどのコンテンツでどのカテゴリーなの？ってなると思います。

以下の記事を参考に見たので読んでみてください。
参考
[【一覧表あり】HTML5でのタグの入れ子のルールを徹底まとめ](https://webliker.info/46840/#toc_1)

上の記事で内容理解できたら良いツールを見つけたので紹介しときます
[HTML5 子要素・親要素対応](https://yoshikawaweb.com/element/)
【SpringBoot】とりあえず使ってみたい

IT業界に飛び込んで気づけばもうすぐ4年。
恥ずかしながら自分でSpringBootでHello Worldも表示できなかったので、これを機に勉強しながら実践してみました。

初めての記事なのに、とてつもない二番煎じ感がしますがご容赦を。。

ソースは[ここ](https://github.com/okaponta/first-repo/tree/master/demo)にあがってます。
→リンク切れしていたら[プルリク](https://github.com/okaponta/first-repo/pull/1)見てください。

## eclipseのインストール

こちらは少し割愛します。
[eclipse公式サイト](https://www.eclipse.org/downloads/)からexeファイルをダウンロードしてダブルクリックしてぽちぽちすれば入ります。

- 途中で証明書をApproveするか、と聞かれるのでクリックしないと一生終わりません。
- どの種類のeclipseいれますか？というのは基本的に一番上を選んでおけば間違いないと思います。
- 自分はあまり日本語化版eclipseは使いたくないのでPleiades使ってないです。
 - Pleiades使ったバージョンは他記事のが正直分かりやすいかと思います。

## eclipseのプラグインをインストール

 - Help > Eclipse Marketplace > 検索で[STS]と入力。
 - Spring Tools 4を選択してInstall。
  - その後Confirmを押下、License Agreementsに合意してFinishを押下。
  - まあまあ待ちます。はやいですが、休憩します。
  - 再起動を要求されるので、おとなしく再起動します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561891/3c3b1ddf-2820-5b87-5192-9666a39d7594.png)

## プロジェクトを作成する

 - File > New > Project を選択
   - Spring Boot > Spring Starter Project を選択。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561891/d2ea2c17-4aa2-e506-3852-84c551a79e67.png)

- 最初なので、デフォルトのままいきます。
    - 以下の画面でNext

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561891/e2f195a8-9e2d-bd2c-b38e-69abe5af62c1.png)

- 以下の画面で、「web」と入力。
    - Spring Webを選択し、Finishを押下。
    - またしばらく待ちます。またですが、休憩します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561891/aaeaf658-3868-8c99-95c9-8b2085e5c6d3.png)


## コントローラの追加

- 先ほど作成されたパッケージ「com.example.demo」を右クリック、New > Classをクリック。
- クラス名は「HelloWorldController」で自分は作成しました。
 - クラスに`@RestController`のアノテーションをつける。
 - `@RequestMapping(value="/")`のアノテーションをつけてメソッドを作成。
 - 以下のコードでアノテーションのインポート(Ctrl + Shift + Oとかでできます)すると、コンパイルが通るはずです。

```java
package com.example.demo;

@RestController
public class HelloWorldController {
	
	@RequestMapping(value="/")
	private String helloWorld() {
		return "Hello World!!";
	}
}
```

## 実行!!

 - DemoApplicationのクラスの上で右クリック
    - Run as > Spring Boot Appを押下
 - http://localhost:8080/ にアクセス。
   - こんな感じで表示されるはずです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561891/e6655d8c-8b24-e631-fc9d-0a677a7154e5.png)

## 感想

- 超簡単
    - 前phpとapache使ってちょっと遊びがてらやったことあったような気がしますが、数倍手間取りました。
- 自分で書いた分量は本当にこれだけ。すご。
    - https://github.com/okaponta/first-repo/pull/1/commits/fe2efedeec9d8ecdb6c516902a3720e3fe35e6ad
Go言語においてstruct定義をすることなくJSONを扱うときのTips

go言語においてJSONをあつかう際には、構造体を定義してmappingするのが一般的です。
ほとんどの場合この方法で良いのですが、たまにStructを定義したくない、できない状況があるかもしれません。
struct定義することなくJSONを扱う方法について記載していきます。

jsonを楽に使うパッケージ：[github.com/aimof/jaason](https://github.com/aimof/jaason)を作っている時に得た知見をまとめました。

## はじめに：`json.Unmarshal()`の基本

JSONをjson.Unmarshal()でunmarshalした際には、以下の型になります。

```
bool, for JSON booleans
float64, for JSON numbers
string, for JSON strings
[]interface{}, for JSON arrays
map[string]interface{}, for JSON objects
nil for JSON null
```

ご存知の通り、JSONには、booleans, numbers, strings, arrays, objects, nullの６つの型しかありません。
([jsonパッケージの解説](https://golang.org/pkg/encoding/json/#Unmarshal)より)

## Arrayから中身を取り出す方法

interface{}から取り出すのが若干面倒くさいのですが、以下に例を上げておきます。

```go
// Unmarshalしたinterface{}をarray、取り出したい要素のindexをindexとすると
var value interface{}
if index >= 0 && index < len(array.([]interface{})) {
	value = array.([]interface{})[index]
}
```

これで、value変数に`interface{}`として、値が入りました。
値をinterface{}以外の型に変換するときは、

```go
s := value.(string)
```

みたいにすればokです。

### Objectから値を取り出す。

```go
// Unmarshalしたinterface{}をobject、取り出したい要素のkeyをkeyとすると
value, ok := object.(map[string]interface{})[key]
if !ok {
	// 適当なハンドリング
}
```


valueを使う際は同様です。

## 注意

interface{}から型を取り出すときは、必ず型チェックしましょう。
いともたやすくpanicします。

この記事は、[私のブログ](https://blog.aimof.net/go-json)と同時に投稿しています。
input と readlines

```py:input
line = input().rstrip()
print(line)
```

```:入力
勇者は荒野を歩いていた。
```

```:inputの実行結果
勇者は荒野を歩いていた。
```

```py:readlines
import sys
lines = sys.stdin.readlines()
print(lines)
```

```:入力
勇者は荒野を歩いていた。
スライムが現れた。
勇者はスライムと戦った。
```

```:readlinesの実行結果
['勇者は荒野を歩いていた。\n', 'スライムが現れた。\n', '勇者はスライムと戦った。']
```

改行が出力されてしまうので、

```py:readlines改
import sys
lines = sys.stdin.readlines()
print(lines.rstrip())
```

```:入力
勇者は荒野を歩いていた。
スライムが現れた。
勇者はスライムと戦った。
```

```:readlines改の実行結果
['勇者は荒野を歩いていた。', 'スライムが現れた。', '勇者はスライムと戦った。']
```

for文と組み合わせて、
リストの中身を出力させるには

```py:readlinesとfor
import sys
for line in sys.stdin.readlines():
    print(line.rstrip())
```

```:入力
勇者は荒野を歩いていた。
スライムが現れた。
勇者はスライムと戦った。
```

```:readlinesとforの実行結果
勇者は荒野を歩いていた。
スライムが現れた。
勇者はスライムと戦った。
```
RISC-Vエミュレータ上でリアルタイムOSを動かす(QEMU + NuttX)

QEMU上でNuttXというリアルタイムOSを動かすまでの手順について紹介します。

# NuttXとは
[NuttX](https://nuttx.org/) とは、POSIXライクなインターフェイスを持つリアルタイムOSです。
小さなLinux(Tiny Linux)のようなOSです。
[ドローンのプロジェクト](https://github.com/PX4)やSonyのオーディオ製品[^1][^2][^3]などに使われています。

[^1]: https://openiotelcna2017.sched.com/event/9OOs/developing-audio-products-with-cortex-m3nuttxc11-masayuki-ishikawa-sony
[^2]: https://elciotna18.sched.com/event/DYML/smp-and-networking-support-on-nuttxlc823450-masayuki-ishikawa-koichi-okamoto-sony-video-sound-products-inc
[^3]: https://osseu19.sched.com/event/UtJ0/nuttx-for-embedded-linux-developers-masayuki-ishikawa-sony

# 準備
[README](https://bitbucket.org/nuttx/nuttx/src/master/boards/risc-v/fe310/hifive1-revb/README-qemu.txt)を参考にしました。
2020年1月時点での手順です。WSL+Ubuntu 18.04上で確認しました。
ビルドなどで足りないパッケージがあれば、必要に応じて`apt install` などで追加してください。

## QEMUのインストール
```zsh
$ git clone https://github.com/qemu/qemu
$ cd qemu/
$ ./configure --target-list=riscv32-softmmu
$ make
$ sudo make install
```

## NuttXのソースコード取得
```zsh
$ mkdir nuttx
$ cd nuttx/
$ git clone https://bitbucket.org/nuttx/nuttx.git
$ git clone https://bitbucket.org/nuttx/apps.git
```

## kconfig-frontends のインストール
```zsh
$ git clone https://bitbucket.org/nuttx/tools.git
$ cd tools/kconfig-frontends/
$ ./configure
$ make
$ sudo make install
$ sudo /sbin/ldconfig
```

## toolchainのビルド
時間がかかります

```zsh
$ git clone --recursive https://github.com/riscv/riscv-gnu-toolchain
$ cd riscv-gnu-toolchain/
$ ./configure --prefix=/opt/riscv --enable-multilib
$ sudo make
$ export PATH=$PATH:/opt/riscv/bin
```

## NuttXのビルド
nuttxやappsをgit cloneしたディレクトリから、

```zsh
$ cd nuttx/
$ ./tools/configure.sh hifive1-revb:nsh
$ make menuconfig
$ make
```

`make menuconfig` では『FE310 Chip Selection』で『FE310 QEMU』を選んでSave。
![fe310_qemu.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561901/3b946fdc-40b9-4ab8-07bd-4370e8b9cb7b.png)

# 起動と終了

起動

```zsh
$ qemu-system-riscv32 -nographic -machine sifive_e -kernel ./nuttx
```

![nsh_riscv.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561901/cc6662fa-e8fc-3f95-b83a-41f77ebda718.png)

終了
Ctrl+aのあとにx
パッケージインストールしたRocket.ChatをDocker Containersへ移行してみる

# 目的
「大人の事情でSlackが使えない」という話をよく聞きます、うちの会社も例外ではなく同じ悩みを抱えておりRocket.Chatを使っています。OSSで使えるSlackっぽいチャットツールでは、当時は人気だった？（GitHubリポジトリのスター数が多かった）という印象を持っていた。

他人が構築してくれていた、仕事でコミューニケーションツールとして使っているRocket.Chatのバージョンが古かったのでバージョンアップしようと考えたけど、ついでになので他と環境を統一する目的を含めDocker Containersへ移行してみた。ちなみに移行元も移行先も同じサーバ。たぶん誰の役にも立たない情報だと思うけど自分のメモのため。

# 前提

・Rocket.Chat
　社員（約５０名）のコミュニケーションツール。
　バージョンアップ前：0.70.0
　バージョンアップ後：2.2.0

・OS
　Amazon Linux 4.14.104-95.84.amzn2.x86_64

・その他
　Docker and Docker-Compose はインストール済み。
　バージョンアップ前のRocket.Chatは[公式：Manual Installation](https://rocket.chat/docs/installation/manual-installation/)を参考にインストール＆起動済み。
　バージョンアップ後のRocket.Chatは[公式：Docker Containers](https://rocket.chat/docs/installation/docker-containers/)を参考にインストール＆起動済み。
　バージョンアップ前とバージョンアップ後のRocket.Chatは異なるポート番号（10443と3000
とか）で起動している。

# バージョンアップ前サービスの停止
サービスの停止と自動起動の停止

```
sudo systemctl disable mongod && sudo systemctl stop mongod
sudo systemctl disable rocketchat && sudo systemctl stop rocketchat
```

# mongoDB のバックアップとリストア

docker-compose.ymlから、バックアップ先のディレクトリ（mongoDBのdockerがマウントしているディレクトリ）を確認しておく。
今回の環境では「＜dockerホーム＞/data/dump/」としています。

#### バックアップ先の確認

```docker-compose.yml（抜粋）
  mongo:
    image: mongo:4.0
    restart: unless-stopped
    volumes:
     - ./data/db:/data/db
     - ./data/dump:/dump　　👈今回はココです
```

#### バックアップ

バージョンアップ前サーバ（ローカル）でmongodumpコマンドを使い、出力先のディレクトリと対象となるDB名を指定してバックアップ。

```
sudo mkdir ＜dockerホーム＞/data/dump/mongodump 
sudo mongodump -o ＜dockerホーム＞/data/dump/mongodump -d rocketchat
```

#### mongoDBのDockerコンテナの中に入る
コンテナ内でbashを起動して作業を行う
-i　：Keep STDIN open even if not attached
指定したコマンドを実行した後、コンテナの標準入力をDockerホストの標準入力と接続。

-t　：Allocate a pseudo-TTY
コンテナ内で擬似TTYを割り当てて、Dockerホストの標準出力と接続。

```
cd ＜dockerホーム＞
sudo docker exec -it rocketchat_mongo_1 /bin/bash
```

#### リストア
リストアコマンドmongorestoreでリストア対象のDB名とバックアップデータがあるディレクトリを指定する。今回の環境ではリストア先に既にDBがあるため「--drop」オプションを付け、データベースを削除する。

```
mongorestore -d rocketchat /dump/mongodump/rocketchat/ --drop
exit
```

# バージョンアップ後サービスの再起動

```
sudo docker-compose down
sudo docker-compose up -d
sudo docker-compose ps
sudo docker-compose logs
```

# 結果

バージョンアップ後サービスの初回アクセス時にセットアップウィザードが出てきた時はちょっと焦ったけど適当にスキップして終了した。
データ移行は無事に完了したっぽい。
gqlgen + Prisma Lift + gormで出勤管理を作ってみた

# サンプル
https://github.com/inadati/sample01-AttendManager.services

# コンテナ構成

### DB
- PostgreSQL

### cron処理
- busybox

### cronで叩く用graphql api(非公開api)
- gqlgen
- gorm

### 公開用graphql api
- gqlgen 
- Prisma(Liftのみ使用)
- gorm

### クライアントアプリ
- nuxt
- Apollo Client

### その他
本番公開にhttps-portalを使用(別コンテナ)

# graphQLサーバーを立てる上での躓きポイントをまとめる
※下記は今後、別記事にしてリンクを貼るようにします。

### Hasuraによって作れるapiはいかなる場合もweb公開するしかないという話
PrismaはORMを使い、内部的にSQLを叩くことでDBを読み書きしているので
Prismaが自動生成するapiを直接、web公開する以外にも
prismaのORMを使い、別途公開用のgraphqlサーバーを立てることが可能です。
ですが、hasuraはORMではないので、hasuraが提供するgraphql apiは、http通信を通して叩くしか
DBの読み書きをする方法がありません。
つまり、hasuraではPrismaのように別途、公開用のgraphqlサーバーを立てるという事はできないという事です。

これについては、僕にもう少し
ネットワークの仕組みについて理解があれば躓く事はなかったのですが
[こちらの記事](https://qiita.com/chimame/items/f0a8b992cfd67443a7b5#%E8%A8%80%E3%81%84%E3%81%9F%E3%81%84%E3%81%93%E3%81%A8)の
>Prismaと違ってHasura GraphQL Engineはクライアントから直接呼ばれることも可能だが、凝った実装しようとするとやはりClient専用のGraphQL Serverは必要

という情報からhasuraでもPrismaのように別途、公開用のgraphqlサーバーを立てられるのだと
誤解してしまっていました。

その結果、hasuraに対してapolloクライアントを使ってgraphql apiを叩いてやる為の
[graphQLサーバーフレームワークを作るという試み](https://qiita.com/Inadati/items/2c58482cfe7845d9c681)を一度やってみたのですが
完全に思惑はハズレました。

なにぶん、僕自身まだまだフロントエンドの方が長く
サーバー側の常識が十分に分かっていなかった為に起こった躓きです。

[前の記事](https://qiita.com/Inadati/items/2c58482cfe7845d9c681)では、
2019年末時点でgraphqlサーバーを立てる上でのオススメのプログラミング言語は
goよりもnode.jsが良いと言う旨の記事を書きましたが
前言撤回
gqlgen + Prisma(Lift) + gormがかなりイイ感じで開発が楽そうです。
特に、prismaのORMは非常に使いづらかったのですが
gormでresolverを書くのは、かなり楽でした。

# 今回のその他の躓き
大したことでは無いのですが
postgresのコンテナがDBの内容を出力する
今回のサンプルでいうところの「postgres/data」ディレクトリ内の一部のディレクトリが空の場合
git pushすると消えてしまって本番環境でpostgresコンテナが起動しないという事態が発生しました。
gitは空のディレクトリはcommitしてくれないので空のディレクトリで必要なディレクトリは
.gitkeepを入れときましょう。
とただそれだけの話なんですが
割と小一時間頭を抱えたので書いておきます。

# まとめ
今回のサンプル、まだ若干必要な修正があるものの
ようやく、graphqlを活用したサンプルアプリを公開することができました。
次は、画像アップロードなど研究しようと思います。

しかし、
gqlgenにしろ
prismaのliftにしろ
gormにしろ最高です。
Python用にdlibをインストール（Windows）


地味につまづいたので、画像多めで説明します。
不要な工程もあるかもしれませんが、色々試したなかで間違いない方法をご紹介いたします。
徐々に無駄を省く編集をしたいと思います。

# 0. 前提
環境は以下の通りです。
・Windows 10 Home 64 bit

# 1. Pythonのインストール
以下よりダウンロードできます。
https://www.python.org/downloads/windows/

今回は「Python3.8.1 64bit」をインストール
![スクリーンショット (6)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406190/e8034c98-9e4f-8d60-f80f-559118ba12f3.jpeg)

インストール画面では、「Add Python 3.8 to PATH」にチェックを入れておいてください。
（パスを通す手間が省けますよっ）
![スクリーンショット (5)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406190/b4352e48-1664-f362-b515-a3104b25e2a0.jpeg)




インストールが終われば、以下のコマンドをコマンドプロンプトに入力。

```
python -V
```

以下のように、バージョンが表示されれば無事インストール完了です。

```
Python 3.8.1
```

反応がない or インストールされていないと表示される場合は、コマンドプロンプトの再起動をおすすめします！
それでも、ダメならPCの再起動をしてから試してみてください！

# 2. CMakeのインストール
以下より、Visual Studio 2019 Communityの実行ファイルをダウンロードして、インストール開始
https://visualstudio.microsoft.com/ja/downloads/

後に出てくるこの画面では、「C++によるデスクトップ開発」を選択してください。
「Winsows用C++CMakeツール」がチェックされていなければ選択。
![スクリーンショット (23)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406190/ca138a86-b16e-8bdc-2381-ed6fb7dacc9a.jpeg)

インストール完了後は、Visual Studio 2019を起動。
「コードなしで続行」を選択。
![スクリーンショット (25)_LI.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406190/f1e3fcfd-fd8a-db48-20a9-f9b229308b2b.jpeg)

「ツール > コマンドライン > 開発者用コマンドプロンプト」よりコマンドプロンプトを起動
![スクリーンショット (26).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/406190/64c72fbf-3b79-28f8-a6d7-0690f4d8e593.png)

起動したコマンドプロンプトに以下のように入力。

```
cmake -version
```

以下のように、バージョンが表示されれば無事インストール完了です。

```
cmake version 3.15.19101501-MSVC_2
```

反応がない or インストールされていないと表示される場合は、PCの再起動をおすすめします！

# 3. dlibのインストール
先ほど起動したコマンドプロンプトに以下のように入力。

```
pip install dlib
```

インストールが完了すれば、無事dlibが使えるはずです！
ダメな場合、コマンドプロンプト or PCの再起動をおすすめします！
canvasのレスポンシブ対応

canvasのレスポンシブ対応に関する備忘録

ちなみにclassはハイフン、idはキャメルケース。CSSではclassのみ利用し、jsではidのみを利用する。という規則になっています。

```html
<div class="canvas-parent" id="canvasParent">
    <canvas id="canvas"></canvas>
</div>
```

こんな感じで、canvas要素に対し親となるdivを定義し、div要素に対しでサイズを指定します。

```css
.canvas-parent {
    width: 80%;
    height: 50vh;
}
```

jsでcanvasのサイズをcanvasParentのサイズに合わせるための処理を書いていきます。

```javascript
let canvasParent = document.getElementById("canvasParent");
let canvas = document.getElementById("canvas");

let resize = () => {
    canvas.width = canvasParent.clientWidth;
    canvas.height = canvasParent.clientHeight;
};

setInterval(() => resize(), 0);
```

canvasとcanvasParentを取得し、canvasのwidth|heightにcanvasParentのwidth|heightを代入するだけの手続きresizeを作ります。

その後、setIntervalを用いてresizeを呼び出し続けます。

これにより、常にcanvasのサイズがcanvasParentの、view portや%で指定された曖昧なサイズに適宜修正されていきます。

これだけでは、0msecでcanvasのサイズを修正していくのでリソースを無駄に使っているように見えたり、canvasはサイズが変わるとcanvas内のオブジェクトをクリアするという性質に対応できなくなってしまいます。

ですので、少し修正しましょう。

```javascript
const FRAMERATE = 60;
let canvasParent = document.getElementById("canvasParent");
let canvas = document.getElementById("canvas");

let resize = () => {
    canvas.width = canvasParent.clientWidth;
    canvas.height = canvasParent.clientHeight;
};

let draw = () => {
    let context = canvas.getContext("2d");
    context.fillRect(100, 100, 80, 80);
};

let world = () => {
    resize();
    draw();
};

setInterval(() => world(), 1000/FRAMERATE);
```

80x80の正方形を描画するだけの手続きdrawを作成し、resizeとdrawを呼び出すworldも作成します。

setIntervalでは、このworldを呼び出すようにします。

また、intervalの時間は1000/FRAMERATEとなっています。FRAMERATEには60が入っていますが、こう書く事でcanvasが60FPSで動作するようになります。

このsetIntervalの1行`setInterval(() => world(), 1000/FRAMERATE);`が重要なポイントです。

これにより、ただ描画しろと命令され"画像"を画面上に表示するだけだったcanvasが、一気に動画やゲーム画面として扱えるエリアに進化します。
Facebookのアカウント凍結について

#Facebookの利用規約通り、アカウントを開設したが、アカウントが突然停止されて復帰できません。
よくある下記の様な行為はもちろんおこなっていません。。。
・Facebook利用規約に従っていないコンテンツを投稿している
・偽名を使用している
・他の人になりすましている
・Facebookのコミュニティ規定に違反して、Facebookで許可されていない行為を続けている
・嫌がらせ、広告、宣伝、その他の許可されていない行為を目的として、他の利用者に接触している



「アカウント停止が間違いだと思われる場合は、こちらのフォームを使用して、審査のリクエストを依頼してください。」
のフォームに複数回、きちんとした個人情報を送信しているが一向に改善されません。

どのような事が考えられますでしょうか？
ご存知の方いらっしゃいましたら改善方法を教えていただけますと大変助かります。
宜しくお願い致します。
Patch textures(mesh colors)のメモ

CyHair で有名な Cem Yuksel 先生のところから

http://www.cemyuksel.com/research/meshcolors/

## 概要

ptex のような感じですが, より既存パイプラインに乗せやすい & 効率化 & HW 化しやすい感じの texture mapping 手法です.
初出論文(Mesh colors)では, ptex と比べて, meshcolors では triangle 対応が利点とありますが, ptex も実装のほうは triangle 対応しているので, この点についてはあまり advantage は無くなったと思います.

## 手法の発展

Mesh colors : 初出 http://www.cemyuksel.com/research/meshcolors/meshcolors_techreport.pdf
Mesh Color Textures : 既存の 2D texture に mesh color をコンバートすることで, でより効率的な HW 化(ただし HW 化は完全ではない. たとえば anisotropic filtering 未対応
Patch Textures: Hardware Implementation of Mesh Colors : Mesh colors の完全な HW 対応(aniso filtering 対応) http://www.cemyuksel.com/research/meshcolors/patchtextures_hpg2019.pdf

## 実装

face ごとに texture を持つため, 隣接 face との補間をどうするかが
特に GPU(rasterizer)ですとこの辺りが面倒です
 
### edge, 隣接 face を求める

入力形状が subdivision surface(quad mesh)でしたら, OpenSubdiv などで求めることができますが, 任意 polygon の場合は half-edge を計算するなどして edge, 隣接 face のデータ構造を求める必要があります.

単に隣接関係だけでよければ, `std::map<std::pair<int, int>>` でやってしまうのが一番楽かも?ですが,

meshcolors などに使える half-edge ライブラリを作りました.

https://github.com/syoyo/tinymeshutils

ただ, 隣接関係を求めるのは half-edge を walk する必要がありちょっと面倒です.

既存のものを使うのであれば, libigl, OpenMesh など形状処理ライブラリはいくつかありますが, pmp-library

http://www.pmp-library.org/

が組み込みが楽かと思います.
ただ, pmp-library や libigl は三角形限定とかだったりします(tinymeshutils は三角形や四角形, 多角形混在にも使えます)


### offline raytrace レンダラでの実装

特段大きな変更はありません.
ptex のように, 隣接 face の情報をみて, filtering します. 

### bindless texture

bindless texture でやってしまうというのが一番楽ですが, HW に制約がでます(最新 NVIDIA/AMD GPU では対応しているかと思いますが). また, Vulkan では bindless texture に対応する機能が標準機能としてはまだありません https://community.khronos.org/t/bindless-textures/103941/13

### SSBO

GPU の場合, SSBO(全体を 1D/2D array として扱う)でやるのが楽でしょうか.
tinymeshutils では, index ベースで half-edge を作っているので, SSBO 化はやりやすいと思います.

## 問題点

* `mesh colors` という名称がわかりづらいし検索性も低い(`patch textures` も同様)
pandasのエラー対処法(備忘録)

##1. はじめに
本記事は備忘録であり、人に見せようと思って書いているものではありません。そのため、分かりにくい表現やニッチな内容が含まれているかもしれません。

##2. mergeができない
```python:merge.py
import pandas as pd
import numpy as np

a = np.range(9).reshape(3,3)
b = np.range(9).reshape(3,3)

df_a = pd.DataFrame(a, columns=['A', 'B', 'C'])
df_b = pd.DataFrame(b, columns=[['A', 'B', 'D']])

print(df_a)
print(df_b)
```
とすると

```python:merge_output.py
   A  B  C
0  0  1  2
1  3  4  5
2  6  7  8
   A  B  D
0  0  1  2
1  3  4  5
2  6  7  8
```
のように出力される。
このdf_aとdf_bをmergeしたい。

```python:merge.py
df_c = pd.merge(df_a, df_b, how='left')
```

すると、下のようなエラーメッセージが出てくる。

```plaintext
pandas.errors.MergeError: No common columns to perform merge on. Merge options: left_on=None, right_on=None, left_index=False, right_index=False
```

これは、共通するカラムがないのでmergeができませんと言っている。
df_aとdf_bでカラム'A','B'が共通しているはず。

カラムのデータタイプをそれぞれ調べる。

```python:check_df_column.py
print(type(df_a.column))
print(type(df_b.column))
```

すると

```plaintext:output
<class 'pandas.core.indexes.base.Index'>
<class 'pandas.core.indexes.multi.MultiIndex'>
```
となっており、columnの型が異なっていたことが分かる。
これは、DataFrameを作る時に、
df_bのカラムはcolumn=[['A', 'B', 'D']]としていたため。
これをdf_aと同じようにする。

```python:merge.py
import pandas as pd
import numpy as np

a = np.range(9).reshape(3,3)
b = np.range(9).reshape(3,3)

df_a = pd.DataFrame(a, columns=['A', 'B', 'C'])
df_b = pd.DataFrame(b, columns=['A', 'B', 'D'])

df_c = pd.merge(df_a, df_b, how='left')
print(df_c)
```

```plaintext:output
   A  B  C  D
0  0  1  2  2
1  3  4  5  5
2  6  7  8  8
```
となり、修正することができた。
技術系書籍の新刊情報の入手先サイト一覧

# はじめに

コンピュータ・ITなどの技術系書籍の新刊情報を知るためのサイト一覧を備忘録として整理します。
※ 随時更新する予定です。

# 新刊情報の入手先サイト一覧


|サイト名称 |URL  |
|---|---|
|コンピュータ・IT : 7日以内 - Amazon|[https://www.amazon.co.jp/s?rh=n:466298,p_n_publication_date:2285539051](https://www.amazon.co.jp/s?rh=n:466298,p_n_publication_date:2285539051)|
|コンピュータ・IT : 30日以内 - Amazon|[https://www.amazon.co.jp/s?rh=n:466298,p_n_publication_date:2285541051](https://www.amazon.co.jp/s?rh=n:466298,p_n_publication_date:2285541051)|
|コンピュータ・IT : 90日以内 - Amazon|[https://www.amazon.co.jp/s?rh=n:466298,p_n_publication_date:82839051](https://www.amazon.co.jp/s?rh=n:466298,p_n_publication_date:82839051)|
|書籍一覧新刊｜技術評論社  |[https://gihyo.jp/book/list](https://gihyo.jp/book/list)|
|オライリー・ジャパン新刊・近刊|[https://www.oreilly.co.jp/catalog/soon.html](https://www.oreilly.co.jp/catalog/soon.html)|
|書籍一覧 - インプレスブックス|[https://book.impress.co.jp/booklist/](https://book.impress.co.jp/booklist/)|
|近日刊行書籍 - 翔泳社の本|[https://www.shoeisha.co.jp/book/upcoming](https://www.shoeisha.co.jp/book/upcoming)|
|これからでる予定の新刊 - 秀和システム|[https://www.shuwasystem.co.jp/smp/news/n27546.html](https://www.shuwasystem.co.jp/smp/news/n27546.html)|
|新刊 - 秀和システム|[https://www.shuwasystem.co.jp/smp/search/new.html](https://www.shuwasystem.co.jp/smp/search/new.html)|
データベースに変更を加える一連の流れ

#データベースにカラムを追加する方法
1.マイグレーションファイルを作成する
`rails g migration ファイル名`
2.マイグレーションファイルを編集する
chageメソッド内に編集内容を記述する
`「add_column :テーブル名, :カラム名, :データ型」`
3.データベースに反映させる
`rails db:migrate`
Pod として稼働する WAS Liberty をリモート・デバッグする

Kubernetes クラスタ内で稼働する WAS Liberty をリモート・デバッグする方法を簡単にまとめてみました。

## デバッグ・オプションを設定をして、WAS Liberty を起動する
最初は、WAS Liberty 側の準備です。何も準備がいらないと楽ですが、さすがにそうは行きません。。。
以下の JVM オプションを指定して、WAS Liberty を起動します。デバッグ用の一時的な変更なので、環境変数 `JVM_ARGS` で指定するのが良いと思います。
 
```
-Dwas.debug.mode=true
-Dcom.ibm.websphere.ras.inject.at.transform=true
-Dsun.reflect.noInflation=true
-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=7777
```

デフォルトの suspend=y のままだと、デバッガーから接続されるまで WAS Liberty が起動しません。ここでは、suspend=n を指定することで、デバッガーからの接続を待たないようにしています。
address=7777 はリモート・デバッグ用のポート番号の指定です。

- 参照： [Liberty 環境のカスタマイズ](https://www.ibm.com/support/knowledgecenter/ja/SSEQTP_liberty/com.ibm.websphere.wlp.doc/ae/twlp_admin_customvars.html)

## デバッガーから WAS Liberty に接続しデバッグする
デバッグ・オプションを指定した WAS Liberty の Pod が起動したら、デバッガーで WAS Liberty に接続します。
**ここらの操作は、デバッガーを起動するマシン(PC)で行います。**

まず、`kubectl get pod -o wide` を実行し、pod の IP アドレスを確認します。
以下の例の場合は、Pod 'infra-test-debug-liberty-dfc9464cf-zdktk' の IP アドレスは 10.1.60.121 になっています。

```
C:\>kubectl get pod -o wide
NAME                                       READY   STATUS             RESTARTS   AGE   IP            NODE            NOMINATED NODE   READINESS GATES
infra-test-debug-liberty-dfc9464cf-zdktk   1/1     Running            0          8d    10.1.60.121   192.168.124.125   <none>           <none>
```

次に、`kubectl port-forward pod/<Pod名> <ローカルport>:<Podのport>` を実行し、ポート・フォワーディングを開始します。
以下は、localhost のポート 8888 を Pod 'infra-test-debug-liberty-dfc9464cf-zdktk' のポート 7777 にポート・フォワーディングする場合の例です。

```
C:\>kubectl port-forward pod/infra-test-debug-liberty-dfc9464cf-zdktk 8888:7777
Forwarding from [::1]:8888 -> 7777
Forwarding from 127.0.0.1:8888 -> 7777
```

準備が整ったので、デバッガーを起動して WAS Liberty に接続します。Eclipse のデバッガーの場合は、以下の手順で接続できます。
1. Eclipse の `Debug` パースペクティブを開き、メニューから `Run -> Debug Configurations...` を選択します。
2. ダイアログの左側のリストから `Remote Java Application` を選び、ダイアログのツール・バーの左端にある`新規作成`を選び、下表の情報を入力します。
3. ダイアログの `Apply` ボタンを押し、`Debug` ボタンを押して、接続します。
4. デバッガーが Pod のデバッグポートに接続し、デバッグ可能な状態になります。

|項目|入力内容|
|:------------|:------------------------------------------|
|name|構成の名前|
|project|デバッグするアプリのソースが入っているプロジェクト|
|connection type|Standard (Socket Attach)|
|host|**localhost**|
|port|Pod のデバッグポートにフォワーディングしている localhost のポート (この例の場合は 8888 )|
 
デバッグを終了する場合は、`Debug` ウィンドウで該当のデバッグ構成を選び、ポップアップから `Disconnect` を選択します。
EC2 で Jenkins 構築する時にやったこと (Railsアプリケーション)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/308256/46abe8ab-c102-37eb-3e01-9fc91199df3a.png)

# はじめに
JenkinsにてRuby on Railsアプリケーションのテスト、デプロイの自動化を実施しました。
そのときに勉強になったことをまとめたいと思います。

ここでは、EC2 上に Jenkins 環境を構築するときに起こったエラーと対処を列挙しました。
綺麗にまとめることはできていませんが、URL先の記事の内容を読めば解決できるかと思います。
少しでも参考になればと思い載せさせていただきます。

#### 関連リンク
関連リンクを下記に載せておくので、必要であれば参考にしてください。。

* Jenkins ふんわり概要
    * https://qiita.com/syukan3/items/9f43931988841c2fe343
* Jenkins + Backlog + ElasticBeanstalk 連携
    * https://qiita.com/syukan3/items/dd9c8bd0219043343c82

## 環境


* java + Jenkins
    * https://qiita.com/thirota/items/dc1dc558e5439fb0b0b6
* git
* rbenv
* ruby （cのコンパイラを入れる。パスを通す。rbenv globa 2.6.3実行。）
	L gcc-c++入れる。gem ’scss’ があったから。
※こういう環境構築作業がめんどくさい・・・。
* sudo yum install -y mysql-devel（mysql）
* rmagick
ERROR: Can't install RMagick 4.0.0. Can't find ImageMagick with pkg-config
    * brewをインストールhttps://qiita.com/ryokurosu/items/9898d320f7e56143b1bf
    * インストール後に出てくるコードを実行して、パス通すのとgccを入れる。
    * brew install imagemagick@6 -> gccエラーが出る。 -> パスが通ってなかっただけかな？
（https://qiita.com/seyself/items/65aad4ef2f88747efaa7）
    * brew install pkg-config -> うまくいく。
* node.js のインストール
    * https://qiita.com/azusanakano/items/771dc9919f347de061d7
* MySQLサーバーをインストール
    * https://qiita.com/toshihirock/items/c5933d3628c7d4e81ffa
* MySQLが落ちる -> Swap領域の確保
    * https://qiita.com/madaran0805/items/ae0532a7436e1c684e72
* MySQLのpassword変更
    * https://webkaru.net/mysql/mysql-root-password/
* MySQLの中でDB作成
    * mysql >   create database PIMENT_test;
* RSpecのテスト書き直し
    * DBが空でもちゃんと動くように、FactoryBotからデータを生成するようにリファクタリング
* MySQLのencodingを変更
    * https://babiy3104.hateblo.jp/entry/2014/02/13/000219
    * http://neos21.hatenablog.com/entry/2018/09/12/080000
* Jenkins のビルド設定にて、rails db:drop / rails db:create / rails db:seedを追加。
    * （どちらにせよtest環境でseedを追加してくれなかったから、RSpecがDBなくてもテストできるように書き換えた。）



## まとめ
インフラエンジニアはこういう環境構築をしたりするのかなーと想像しながら取り組みました。
綺麗にまとまっておらずエラーを列挙しただけとなりますが、このような記事でも助けられたことがあるので軌跡として残しておきました。

（過去に取り組んだため記憶が曖昧だったため・・ご了承ください。。）

---
### 会社紹介

**株式会社 Mosaica**  
最先端テクノロジーで社会課題を解決し、持続可能な未来を創造する IT カンパニー。
AI ソリューション、クラウド統合、DX 推進、経営コンサルティングなど包括的なサービスでビジネス変革を支援しています。  

詳しくは [公式サイト](https://mosaica.co.jp/) までお気軽にご相談ください。
公式サイト： https://mosaica.co.jp/
Vue.jsのv-forでの並び順を逆順にした際の現象と注意点と対策

#はじめに
現在vue.jsにてtodoアプリを作成しており、v-forの順序を逆順にした際に起きた現象と注意点と対策を備忘録としてあげたいと思います。

###### htmlの部分
```App.vue
  computed: {
    // todoを逆順にしておく
    reversTodos() {
      return this.todos.slice().reverse();
    }
  }
```

```App.vue
<transition-group name="slide" tag="ul" class="todo-list">
  <li v-for="(todo, index) in reversTodos" :key="todo.id">
    <p class="data">Posted date：{{todo.today}}</p>
    <p class="deadline" :class="{hurry: todo.isDone}">dead-line：{{todo.deadline}}</p>

    <div class="change-btns">
      <span class="edit" @click="editForm(index)">edit</span>
      <span class="delete" @click="deleteItem(index)">delete</span>
    </div>
    <p class="title">
      <label class="text">
        <input type="checkbox" class="check" v-model="todo.isDone" />
        <span :class="{done: todo.isDone}">{{todo.title}}</span>
      </label>
    </p>
    <p class="content">{{todo.content}}</p>
  </li>
</transition-group>
```
その後に削除処理を実施。

```App.vue
  methods: {
    // todoの削除
    deleteItem(index) {
      confirm("delete ok?") ? this.todos.splice(index, 1) : true;
    }
  }
```
上記のように表示をさせて削除処理を進めていると問題が起きました。
リストの要素を削除すると別の要素が削除されてしまいました。
落ち着いて考えると分かるのですがcomputedで定義したreversTodosとdataのtodosとで順番が異なってしまったことが原因です。

#解決案①
```App.vue
<transition-group name="slide" tag="ul" class="todo-list">
                       //indexの引数を使わない
  <li v-for="todo in reversTodos" :key="todo.id">
    <p class="data">Posted date：{{todo.today}}</p>
    <p class="deadline" :class="{hurry: todo.isDone}">dead-line：{{todo.deadline}}</p>

    <div class="change-btns">
                       //indexでなくtodoを引数に入れる
      <span class="edit" @click="editForm(todo)">edit</span>
      <span class="delete" @click="deleteItem(todo)">delete</span>
    </div>
    <p class="title">
      <label class="text">
        <input type="checkbox" class="check" v-model="todo.isDone" />
        <span :class="{done: todo.isDone}">{{todo.title}}</span>
      </label>
    </p>
    <p class="content">{{todo.content}}</p>
  </li>
</transition-group>
```
```App.vue
  methods: {
    // todoの削除
    deleteItem(todo) {
      var index = this.todos.indexOf(todo)
      confirm("delete ok?") ? this.todos.splice(index, 1) : true;
    }
  }
```

あくまでもtodosの配列の順番をindexに入れるようにし、削除するようにしました。


#解決案②
todoを作成する段階でpush()でなくunshift()を使う。

```App.vue
    addItem() {
      if (this.newTitle == "" || this.newContent == "" || this.selected == "") {
        window.alert("Some items have not been entered.");
        return false;
      }
      var today = "";
      var item = {
        id: this.todos.length,
        today: this.calcDay(today),
        title: this.newTitle,
        content: this.newContent,
        selected: this.selected,
        deadline: this.calcDay(this.selected),
        isDone: false,
      }
　　　　//unshift（）に変更しました。
      this.todos.unshift(item);
      this.newTitle = this.newContent = this.selected = "";
    },
```

ただ逆にするだけであればこちらの方がシンプルに出来ますね。computedで値を定義する必要もなくなりましたし、他の編集機能を実施する際にもわざわざindexをとる必要もありません。


#最後に
まだ僕も学習を始めたばかりでよくわかっていないかもですが、同じくvue.js初学者の参考になればいいと思います。
うっとおしいビープ音を何とかしたい一心で、initファイルの設定をOSで条件分岐する

- [Windows 10 WSL (Windows Subsystem for Linux) 上のUbuntuからemacsを起動する](https://qiita.com/liamst19/items/6b96c50850e6a302b93f)
- [Windows側のブラウザでWSL emacsに表示されたurlリンクを開く](https://qiita.com/liamst19/items/0b0ec5679f1e629ae27b)

WSLからLinux版emacsに移行してから、ピーンピーンとベルが鳴ってうっとおしくなった。MSYS2のNTEmacsではサイレント設定できてたはずなのに、と検索したら、WIn版では`set-message-beep`だったのが、linux版では`visible-bell`という設定なのだそう。何かの理由でまたWin版に戻るかもしれないので、条件の付け方を調べてみた。

[Elisp: Determine OS, Emacs Version, Machine Host Name (Xah Emacs)](http://ergoemacs.org/emacs/elisp_determine_OS_version.html)より：

```elisp
;; Turn off the annoying beeping sound
(cond
 ((string-equal system-type "windows-nt") ; Windows
  (progn
    (set-message-beep 'silent)))
 ((string-equal system-type "gnu/linux") ; Linux
  (progn
    (setq visible-bell t))))
```

シェルの指定とか、他にもWin版でしか使えない設定がちらばってるので、色んな環境でemacsを使うのに、これなら一つのinitファイルをコピーして使えば良かったり、結構便利そうで、もしかしてこれはもっと前から知って使っておくべきだった、常識的な情報だったのかなと思ってみたりも。
[memo] Numbers/OpenOffice/GoogleスプレッドシートでWBSのためのナンバリングの数式

# [memo] Numbers/OpenOffice/GoogleスプレッドシートでWBSのためのナンバリングの数式
- Webアプリ制作のための仕事の見える化をしたくって、WBS(WorkBreakDown Structure)をMacで最早で作りました。MacOSにデフォルトで入っているNumbersで表を作りました。
- 面倒なのはWBSのナンバリングですが、空欄かどうかを判定して自動に採番する1行数式です。その紹介です。番号は各担当と進捗話するなど会話に重要なので必須要件でした。
- 前提としてWBSの構造は最初の3列が大項目、中項目、小項目の数字で次の3列が大項目、中項目、小項目の項目の中身になっています。空行はなしで、空欄は上の項目に同じと言う意味とします。
- 初項(初期値)は1,1,1とします（0,0,0でも構いません)。
- 大項目の数式です。A22にいるとして、大項目の欄(D22)が空欄でなければインクリメントします。

```{Numbers}
A22=IF(NOT(ISBLANK(D22)),A21+1,A21)
```

- 中項目の数式です。B22にいるとして、E22の欄が空欄で大項目(A21とA22)に変化がなければインクリメントし、大項目に変化があれば1にクリアします。それだけの数式です。`AND`は引数にとるとか、等しいかの判定は`=`だったりマニュアル斜め読みしました。C欄はB欄のコピペです。

```{Numbers}
B22=IF(AND((A21=A22),NOT(ISBLANK(E22))),IF(AND((A21=A22),ISBLANK(E22)),B21,B21+1),IF(NOT(A21=A22),1,B21))
```
- その例です。CSVをTyporaで貼り付けてmakedownで書いてみました（例、部分）。


| 大   | 中   | 小   | **大項目** | **中項目**               | **小項目**                           |
| ---- | ---- | ---- | ---------- | ------------------------ | -------------------------------------------------- |
| 1    | 1    | 1    | ユーザ対応 | サービス規約             | 利用規約の策定                                     |
| 1    | 1    | 2    |            |                          | プライバシーポリシーの策定                         |
| 1    | 2    | 1    |            | ユーザの認知向上         | XXX                      |
| 1    | 2    | 2    |            |                          | 告知メール                                   |
| 1    | 2    | 3    |            |                          | Facebookでの告知                                   |
| 1    | 2    | 4    |            |                          | twitterでの告知                                    |
| 1    | 2    | 5    |            |                          | 学会での告知                                       |
| 1    | 2    | 6    |            |                          | 国際会議での告知                                   |
| 1    | 3    | 1    |            | すぐ謝れる準備をしておく | サービスダウンの検知                               |
| 1    | 3    | 2    |            |                          | サービスダウンの告知(twitter, Foocebook, GitHub!） |
| 1    | 3    | 3    |            |                          | ダウン時のDNSのCNAMEの変更                         |
| 2    | 1    | 1    | インフラ   | ドメイン名取得           | www.xxx.com                                        |
| 2    | 2    | 1    |            | VPSレンタル              | XXX                                                |
- 何かのお役に立てれば嬉しいです。

### PS
- Numbers -> excelで保存 -> googleスプレッドシートで読み込んだら数式はそのまま保存されていました。googleスプレッドシートでも大丈夫そうです。表題を変更しました。ということでopenofficeでもいけました。excelは持っていないのでわかりませんが多分同じ文法なのでしょう。
キューとPythonでの実装モジュール「deque」

幅優先探索を実装する時にキューと言うデータ構造を使用するのが良いそうなので、
今回はキューについてを調べて見た。


# キューとは

>キュー（英: queue）、あるいは待ち行列はコンピュータの基本的なデータ構造の一つ。データを先入れ先出しのリスト構造で保持するものである。キューからデータを取り出すときには、先に入れられたデータから順に取り出される。キューにデータを入れることをエンキュー(enqueue)、取り出すことをデキュー(dequeue)という。(Wikipediaより)

図にすると以下のような構造を持つデータ構造である。

<img width="297" alt="queue_example.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325786/5012a2bc-c4fe-7309-c28c-a3da97cc6841.png">

イメージとしては筒のようなもの。  
キューの中にキューの後ろからデータを入れる事ができ（エンキュー）、  
キューの先頭からデータを取り出す事ができる（デキュー）。  
構造上取り出せるデータはキューに入れた順となる。  
エンキュー、デキューを下図に示す。  

<img width="370" alt="queue_enqueue.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325786/13e401a5-76ac-fec4-c866-652eac7d6f68.png">

<img width="359" alt="queue_dequeue.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/325786/34f94dba-59cd-7e78-c233-9edd07a16272.png">

# Pythonでの実装方法

Pythonにはキューを実装するには、 **collections** モジュールの **deque** 型を利用する。  
このdeque型だが、キューに加えスタックの機能も持ったようなデータ構造であり、使い方次第ではスタックとしても使用できる。  
今回はキューとして利用することを前提に説明する。

## キューの作成

dequeをインポートしてキューのオブジェクトを作成する。

```python
>>> from collections import deque
>>> 
>>> a=deque()
>>> 
```

## キューへの要素の追加

dequeに要素を追加するには**append**()関数で行う。  
append関数により、要素がキューの右側から追加される。  
本来のキューの使い方ではないが、左から追加するには**appendleft**()関数を使う。  

```python
>>> a
deque([])
>>> 
>>> a.append(1)
>>> a
deque([1])
>>> 
>>> a.append(2)
>>> a
deque([1, 2])
>>> 
>>> a.appendleft(3)
>>> a
deque([3, 1, 2])
>>> 
```

### キューに別リストの要素を一挙に追加

キューに別のリストにある要素を一挙に追加したい時は**extend**関数を使う。  
キューの左から追加したい時は**extendleft**関数を使う。（リストの左の要素から順にキューに追加されていく。）  

```python
>>> a
deque([1])
>>> 
>>> b=[2,3,4]
>>> 
>>> a.extend(b)
>>> 
>>> a
deque([1, 2, 3, 4])
>>> 
>>> a.extendleft(b)
>>> 
>>> a
deque([4, 3, 2, 1, 2, 3, 4])
>>> 
```

## キューから要素を取り出す・削除

dequeから要素を取り出すときは**pop**関数を使う。  
pop関数により、dequeの右側から要素を一つ削除し、その要素が返される。  
dequeの左側から要素を取り出したい時は**popleft**関数を使う。

また、dequeから特定の要素を削除したい時は**remove**関数を使う。  
deque.remove(x) でdeque内に最初に現れるxを削除する。

```python
>>> a
deque([3, 1, 2])
>>> 
>>> a.pop()
2
>>> a
deque([3, 1])
>>> 
>>> a.popleft()
3
>>> a
deque([1])
>>> 
>>> 
>>> a.append(2)
>>> a.append(2)
>>> a.append(3)
>>> 
>>> 
>>> a
deque([1, 2, 2, 3])
>>> 
>>> a.remove(2)
>>> a
deque([1, 2, 3])
>>> 
```

### キューから要素を全削除する

キューから要素を全て削除するには**clear**関数を使う。

```python
>>> a
deque([1, 2, 3])
>>> 
>>> a.clear()
>>> 
>>> a
deque([])
>>> 
```

## キューの要素の順を逆にする

キュー内の要素の順番を逆にするには**reverse**関数を使う。

```python
>>> a
deque([1, 2, 3, 4])
>>> 
>>> a.reverse()
>>> 
>>> a
deque([4, 3, 2, 1])
>>> 
```

-----------

今後活用していきたい。
Pythonによる AI・機械学習について学んだ内容(4)

#はじめに
この本を使って勉強しています
[Pythonによる AI・機械学習・深層学習アプリのつくり方](https://www.socym.co.jp/book/1164)

#2-2 アヤメの分類
機械学習でよくあるアヤメの分類をしてみる。
ダウンロードは、次のURLから CSV ファイルを取得する。
https://github.com/pandas-dev/pandas/blob/master/pandas/tests/data/iris.csv
「Raw」ボタンを押してブラウザの保存機能を使って保存する。
次のような構成になっている。

| 列 | カラム名 | カラムの意味 | 値の例 |
|:-----------------|------------------:|:------------------:|:------------------:|
| 1|SepalLength|がく片の長さ|5.1|
| 2|SepalWidth|がく片の幅|3.5|
| 3|PetalLength|花びらの長さ|1.4|
| 4|PetalWidth|花びらの幅|0.2|
| 5|Name|アヤメの品種|Iris-setosa|

| アヤメの品種 |
|:-----------------|
| Iris-Setosa|
| Iris-Versicolor|
| Iris-Virginica|

![Iris_name.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/561197/21557f6d-ac96-d29b-bcc5-d499a88f77bc.png)

## サイトから直接ダウンロードする方法
ブラウザで保存するのではなく Python で直接ダウンロードすることもできる。

```
import urllib.request as req
import pandas as pd

# ファイルをダウンロードする
url = "https://raw.githubusercontent.com/pandas-dev/pandas/master/pandas/tests/data/iris.csv" # 先ほどのURLではない
savefile = "iris.csv"
req.urlretrieve(url, savefile)

# ダウンロードしたファイルの内容を表示する
csv = pd.read_csv(savefile, encoding="utf-8")
csv
```
150行のデータが表示される。

## ゴールを決定する
がく片や花びらの長さと幅から、アヤメの品種を分類することをゴールとする。
次の順番で機械学習プログラムを実装する。

1. アヤメデータとしてダウンロードした iris.csv を読み込む
2. アヤメデータをがく片や花びらの長さと幅の情報とアヤメの品種情報（ラベル部分）に分離する
3. 全データのうち、80%を学習用データに、残り20%をテストデータに分離する
4. 学習データを使って学習させ、テスト用データを与えた場合に正しく分類しているか評価する

##　プログラムを実装する

```python:iris.py
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.svm import SVC
from sklearn.metrics import accuracy_score

# アヤメデータの読み込み 
iris_data = pd.read_csv("iris.csv", encoding="utf-8")

# アヤメデータをラベルと入力データに分離する
y = iris_data.loc[:, "Name"]
x = iris_data.loc[:,["SepalLength", "SepalWidth", "PetalLength", "PetalWidth"]]

# 学習用とテスト用に分離する
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.2, train_size = 0.8, shuffle = True)

# 学習する
clf = SVC()
clf.fit(x_train, y_train)

# 評価する
y_pred = clf.predict(x_test)
print("正解率：", accuracy_score(y_test, y_pred))
```

```
正解率： 0.9333333333333333
/usr/local/lib/python3.6/dist-packages/sklearn/svm/base.py:193: FutureWarning: The default value of gamma will change from 'auto' to 'scale' in version 0.22 to account better for unscaled features. Set gamma explicitly to 'auto' or 'scale' to avoid this warning.
  "avoid this warning.", FutureWarning)
```

なにやら警告が出る。
FutureWarning があるのは、将来 SVCのgamma は、'auto' から 'scale'　になるよと言っている。

```
clf = SVC(gamma = "scale")
```
と書けば警告が消える。
さらに、

```
# 学習用とテスト用に分離する
x_train, x_test, y_train, y_test = train_test_split(x, y, test_size = 0.2, train_size = 0.8, shuffle = True)
```
の部分だが、今後は


[scikit-learn でトレーニングデータとテストデータを作成する](https://pythondatascience.plavox.info/scikit-learn/%E3%83%88%E3%83%AC%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0%E3%83%87%E3%83%BC%E3%82%BF%E3%81%A8%E3%83%86%E3%82%B9%E3%83%88%E3%83%87%E3%83%BC%E3%82%BF)

を参考に　stratify　オプションを記述したほうがよいだろう。
Selenium で要素のテキストが取得できないときの対処法

# スクレイピングによくつかうSeleniumで、うまく要素のテキストが取得できないときの対策方法

ハマりやすいポイント
- クラス名にスペースが含まれている
- クラス名はあっているのにテキストが取得できない

今回は、この２つのポイントについて対処法をまとめます。
##　クラス名にスペースが含まれている場合
JavaScriptであれば、スペースが含まれていても

```javascript
document.getElementByClassName("class name").innerText;
```
のように、そのままクラス名を記述するだけで、
指定したクラス名のテキスト要素は簡単に取得できます。
それをSeleniumで代替する場合は、

```Java
WebDriver.findElement(By.cssSelector("div[class='class name']")).getText().toString();
```
のようにBy.cssSelectorを使って、
`WebDriver.findElement(By.cssSelector("{タグ名}[class='{クラス名}']"))`で指定するとうまくテキスト要素が取得できます。


## 次にクラス名は合っているのにテキストが取得できない場合
Seleniumで取得できる要素のタグの種類が明確には調べていませんが、
主に`div`,`a`,`table`,`tr`,`td`に限られていて
`span`タグや`b`タグその他の要素を特定するにはXpathで要素を直接指定するか、
取得したい要素の親要素で`div`タグの要素のクラス名を指定して、取得が可能になります。


```html
<div class="div_class_name">
    <b class="b_class_name">
        <span class="span_class_name">取得したいテキスト</span>
    </b>
</div>
```
この場合、`div`タグのクラス名 "`div_class_name`"を利用してテキストの取得が可能です。

Xpathを用いる場合は、
Chromeの開発ツールを使って、要素にマウスがフォーカスしている状態で
右クリック→ [Copy] → [Copy XPath]
でXPathが取得できるので、

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/531595/01844216-48de-c01d-31d7-4e37a338aac2.png)

```java
WebDriver.findElement(By.xpath("{xpath}")).getText();
```

でテキストを取得できます。

以上です。


それと記事のテーマとは逸れますが、
JavaScriptとSeleniumではスクロール処理が微妙に食い違うところがあるので、
その点も注意が必要ですね。

JavaScriptではうまく取得できてもSeleniumだとたまに期待通りに動作しないときがあるので、
最悪、Javaコードの中にJavaScriptコードを埋め込んで稼働させることも可能なので、
それさえ知っておけば大丈夫です！
初git commit時にエディタが開かない（Pathエラー）解決方法。【VSCodeとGitHubの設定】



問題点：GitHubにて　初git commitを実行し、メッセージ入力エディターが開かなかった。

------------------
環境:  MacBookPro/ vscode バージョン: 1.41.1


- ①git config --global core.editor 'code --wait'　は設定済み


- ②git commit -m "" では問題なく進める。
 git commit -v では同じエラーが返る



エラー内容↓

```
$ git commit
hint: Waiting for your editor to close the file... code --wait: code: command not found
error: There was a problem with the editor 'code --wait'.
Please supply the message using either -m or -F option.
```

**code: command not foundと出たため、**

####vscodeのPathが原因と思われる。　
####vscodeを開いて設定を行う。####

コマンドパレット（　⌘ + ⇧ + p　）　にて　
Shell Command:...を入力、インストール。
（画像赤枠内を実行。）


<img width="1095" alt="スクリーンショット 2020-01-04 21.44.24.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/547910/b42201f1-9a8c-40c4-ae76-41a1e2abfdc1.png">


以上で解決。
Catalina/Chromeで明朝体となってしまったサイトをゴシックで表示する方法

macOS Catalina になってから Chrome で `sans-serif` 指定でも明朝体が表示されるようになってしまった問題の対処法です。

Stylus というカスタムCSSを指定できる拡張機能を追加し、`Write new style` をクリックして以下のCSSをペーストし、`Save` をクリックします。

[Stylus - Chrome ウェブストア](https://chrome.google.com/webstore/detail/stylus/clngdbkpkpeebahjckkjfobafhncgmne?hl=ja)
https://chrome.google.com/webstore/detail/stylus/clngdbkpkpeebahjckkjfobafhncgmne?hl=ja

```css
@font-face {
  font-family: sans-serif;
  src: local("Helvetica");
  font-weight: bold;
}
```

`sans-serif` の部分にに嫌いなフォントを指定すればそれらを置き換えることもできます。


Chrome Canary (デベロッパー向けビルド、不安定) ではもう修正されているようなのでそちらを使うこともできます。
https://www.google.com/intl/ja/chrome/canary/

# 参考
https://chaika.hatenablog.com/entry/2019/10/29/083000
https://applech2.com/archives/20191027-google-chrome-fixed-hiragino-kaku-gothic-pro-pron-issues.html
(もうすぐChrome Stableでも修正されそうなのでもっと早く書けばよかった...)
【AWS】Amazon SNSでSMSが届かなかった時の対応

# Amazon SNS
Amazon SNSの `テキストメッセージング(SMS) > テキストメッセージの発行` でSMSの発行ができます。

![スクリーンショット 2020-01-05 12.08.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251648/50626197-578e-246a-b015-f2ba62183ee6.png)


テキストメッセージの発行ボタンを押すと、SMSの発行内容を入力するページへ遷移します
↓

![スクリーンショット 2020-01-05 12.14.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251648/502a2c0c-9ea5-c2f3-6c50-d0154a008538.png)


## SMSが届かないパターン
電話番号の欄にSMS送信先の電話番号を入力していきますが、普段使っているような
「**090xxxxxxxx**」や「**080xxxxxxxx**」で入力すると**送信はされるがメッセージは届かない**。

電話番号の欄では**E.164番号**と呼ばれる**国際公衆電気通信番号**の形式で入力する必要があるらしい。詳細はJPNICのページで↓
https://www.nic.ad.jp/ja/basics/terms/E164.html

日本の番号の場合は、「**+81**」であるので、
「+8190xxxxxxxx」や「+8180xxxxxxxx」と入力する。

## SMSが届いた
電話番号は私の携帯番号のE.164です。

![スクリーンショット 2020-01-05 12.02.23.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251648/9d770642-16a6-a075-b323-7d52bc8a8fb4.png)

<img width="375" alt="IMG_4075.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/251648/11d9f11e-887f-7560-cd3e-683811c1e9f1.png">


hogehogeから「test AWS」という内容のSMSが届きました。