OmniAICreator/Qiita-1.07M huggingface dataset:
FileMakerにおけるグローバル変数の管理：値セット編

# はじめに
## 対象読者
* 前回記事 [FileMakerにおけるグローバル変数の管理：宣言編](https://qiita.com/Hi_Noguchi/items/250e19ddefff58ef6529) を読まれた人
* 過去の自分

## 要旨
グローバル変数は便利だけれど無闇矢鱈に使うとバグの元になるので、適切に管理してあげると黒歴史を作らずに済むようになります。

では FMPA において、グローバル変数を適切に管理するにはどのようにしたらよいか？

FMPA はメタ的な内容を記述管理することが苦手なので、グローバル変数を扱う「スクリプト」そのもので管理しなくてはなりません。

前回の記事では「宣言すること」を主題としました。本記事では、「宣言したグローバル変数へ、いかにして値をセットするか」を主題とします。


# 前置き
前回記事を読んできてください。よろしくお願いいたします。


# 本題
## グローバル変数への値セットにおけるルール
さて、早速本題に入ります。

グローバル変数へ値をセットする際のルールを、以下のように決めておきます。

* 個別のスクリプト内で直に値をセットすることをやめる
* グローバル変数一つにつき、値をセットするためのスクリプトを一つ作成する

以下、それぞれについて解説します。

## 個別スクリプト内で直に値をセットしない理由
普通に開発していたら、グローバル変数への値セットは、個別スクリプト内で直におこなうものと思います。

ただ、そうしていると、開発規模が大きくなるにつれ、また、経年にともない、またまた、複数の開発者でまぎれることにもより、どのスクリプトでどんな値のセットのされ方をされているのか、把握しきれなくなっていきます。いくのですよ。

そのグローバル変数に入るべきでない値が入ってしまうというケースも起こり得るでしょう。その際、ヴァリデーション／サニタイズ処理が必要になったら、個別スクリプトの各々に、処理を書いていくのは、途方もないことです。

が、そんなときでも、グローバル変数一つにつき、値をセットするためのスクリプトを一つ作成しておく、という手法を採っておくことにより、ヴァリデーション／サニタイズ処理を書く箇所は一箇所で済みます。

個別スクリプトのコンテクストに依存する値については、引数として受け渡しすればよいことです。

## 値をセットするためのスクリプトを作成
というわけで、値をセットするためのスクリプトを作成します。以下のような感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37739/bf731fe4-fb29-f272-99d4-fa1f92ab70bb.png)

それぞれ中身を覗いてみます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37739/8432b506-d3b5-c730-df68-95cd8aa43200.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37739/7f22feeb-2795-d7c8-b9ae-074ffc3692ce.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37739/3a496733-d232-f68b-539f-cb289acdca5e.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37739/2713072b-f990-3b34-162c-414207d6b61b.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37739/917943c6-793a-b7db-48b8-1eece5a4f6b5.png)

といった感じです。
（コメント注記のところには FMPA の ver. や、転記元となるドキュメント URL を入れておく、というのもオススメです。仕様が変わることもありうるので）

値セットのためのスクリプトを分けておくことで、わざわざドキュメントを見に行かないと覚えていられないような注記をコメントに書いておいても、雑然とした具合になりません。

今回の例では、引数を受け取る処理もなければ、ヴァリデーション／サニタイズ処理も書かれていませんが、必要に応じて書けばよいでしょう。「例えばどんなの？」とか、もしリクエストあるようでしたら、追記します。


# おわりに
まとめとして。

* ファイルで使われるグローバル変数をすべて宣言し、一目瞭然の状態にしておく
* それぞれのグローバル変数に値をセットするためのスクリプトを、一つずつ用意する

この二つを守って開発していけば、あとはデータビューアを活用することにより、グローバル変数で地雷を踏んで吹き飛ぶ経験をすることは無くなることでしょう。

かといって、何でもかんでもグローバル変数にすれば良いわけではなく、ローカル変数でなくてグローバル変数でなければならない理由があるかどうか、を常に問い続けておくことをオススメします。

グローバル変数は、適切な用量・用法を守って、便利に使いましょう！

## 余談
くれぐれも、グローバルフィールドをグローバル変数代わりに使ってはなりません！　とだけ書き残しておきます。

追記 2019/12/09
こちらについて、理由を以下コメント欄にて回答しています。
LPIC 303 対策メモ記事

## 背景

## 暗号化

### X.509 証明書と公開鍵の基礎

##### X.509

X.509とはITU-T（国際通信連合（ITU）の電気通信標準化部門）の規格であり、公開鍵暗号方式に基づいて認証局によって発行される公開鍵証明書の標準形式などを定めています。
2018年現在はX.509v3が広く利用されています。

### 暗号化、署名および認証のX.509 証明書

### 暗号化ファイルシステム

##### openssl

|コマンド|機能|
|-------|---|
|ca|CA(認証局)の管理|
|dgst|メッセージダイジェストの計算|
|genrsa|RSA暗号化方式の秘密鍵を生成|
|rsa|RSA暗号方式の鍵の管理|
|X.509|X.509証明書の管理|
|c_client|SSL/TLSプロトコルを使用したサーバに接続|
|s_server|SSL/TLSプロトコルを使用してデータを受け取るサーバとして動作|
|ciphers|使用可能な暗号スイートを一覧表示|
|verify|X.509証明書の検証|

##### cryptsetup

|コマンド|機能|
|-------|---|
|open --type | マッピングとデバイスを指定して暗号化マッピングを作成|
|close/remove|暗号化マッピングを削除|
|resize|暗号化マッピングのサイズ変更|
|luksFormat|デバイスをLUKSパーティションとして初期化|
|luksOpen|デバイスとLUKSパーティション名を指定しLUKSパーティションを開く|
|luksClose|LUKSパーティションを閉じる|
|luksAddKey|LUKSパーティションにパスフレーズを追加|
|luksKillSlot/luksDelKey|LUKSパーティションに設定したパスフレーズを削除|
|luksDump|LUKSパーティションの状態表示|
|isLuks|デバイスがLUKSパーティションの場合0で偽なら0以外|

##### eCryptfs

|コマンド|機能|
|-------|---|
|ecryptfs-setup-private|暗号化ディレクトリのセットアップ|
|ecryptfs-migrate-home|ユーザホームディレクトリの暗号化|
|ecryptfs-mount-private|暗号化ディレクトリのマウント|
|ecryptfs-umount-private|暗号化ディレクトリのアンマウント|
|ecryptfs-unwarp-passphrase|パスフレーズの複合|

ディレクトリ構造

```
$HOME
　├ Private/ ... 復号されたデータを含むマウントポイント
　├ .ecryptfs/
　│　├ Private.mnt ... 暗号化ディレクトリのマウントポイントが書かれたファイル
　│　├ Private.sig ... 暗号化パスフレーズの署名ファイル
　│　├ wrapped-passphrase ... マウント用の暗号化パスフレーズ
　│　├ auto-mount ... 自動マウント用の空ファイル
　│　└ auto-umount ... 自動アンマウント用の空ファイル
　└ .Private/ ... 暗号化されたデータを含むディレクトリ
```

### DNS と暗号化

##### DNSSEC関連の主要コマンド

|コマンド|機能|
|-------|---|
|dnssec-keygen|DNSSECの要であるZSKとKSKを生成する|
|dnssec-signzone|ゾーンファイルへの署名を行う|
|dnssec-settime|鍵ファイルのメタデータである時間の表示・変更を行う|
|dnssec-dsformkey|鍵ファイルから上位サーバに登録するDSレコードを生成する|
|openssl|TLSAレコードなどの検証を行う|
|rndc|BINDの制御・設定ツール
|delv|BINDの検証・解析ツール|

## ホストセキュリティ

## アクセス制御

## ネットワークセキュリティ
AWS Cloud9環境でdocker-composeをできるようにする

## はじめに

とある事情でAWS Cloud9でローカル開発環境をdockerで構築しようとした時の備忘録。

今回は全てデフォルト設定でAmazonLinuxのインスタンスを立ち上げました。
OSバージョンはこれ（Amazon Linux2じゃないんだね...）

```
$ cat /etc/system-release
Amazon Linux AMI release 2018.03
```

ちなみにDockerはデフォルトでセットアップされてました。

```
$ docker -v
Docker version 18.09.9-ce, build 039a7df
```

Docker-composeは存在しない模様

```
$ docker-compose -v
bash: docker-compose: command not found
```

# 導入手順

[公式ドキュメント](https://docs.docker.com/compose/install/)を参考に導入してみる

Docker Composeの現在の安定リリースバージョンをダウンロード

```
$ sudo curl -L "https://github.com/docker/compose/releases/download/1.25.0/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose
```

実行権限を付与する

```
$ sudo chmod +x /usr/local/bin/docker-compose
```

動作確認

```
$ docker-compose -v
docker-compose version 1.25.0, build 0a186604
```

以上です。
FileMakerにおけるグローバル変数の管理：宣言編

# はじめに
## 対象読者
* FileMaker Pro / ProAdvanced ( 以下 FMPA と略す ) でグローバル変数を扱っている人、もしくは、これから扱おうかなと思っている人
* 他プログラミング言語にあまり触れておらず「グローバル汚染」などと聞いてもピンとこない人
* 後編記事 [FileMakerにおけるグローバル変数の管理：値セット編](https://qiita.com/Hi_Noguchi/items/29e2aba2837085d10660) から飛んできた人
* 過去の私

## 要旨
グローバル変数は便利だけれど無闇矢鱈に使うとバグの元になるので、適切に管理してあげると黒歴史を作らずに済むようになります。

では FMPA において、グローバル変数を適切に管理するにはどのようにしたらよいか？

FMPA はメタ的な内容を記述管理することが苦手なので、グローバル変数を扱う「スクリプト」そのもので管理しなくてはなりません。

まず本記事では「宣言すること」を主題とします。


# 前置き
## そもそもグローバル変数とは
FMPA 以外でプログラミング言語に触れている人からしたら釈迦に説法な内容ですが……


変数には、大きくいって以下の二種があります。 FMPA での実装もこれにならっています。

* ローカル変数
* グローバル変数

両者の違いは、変数の値を保持できる範囲（空間）が狭いか、広いか、ということになります。

FMPA における両者の範囲は、以下の通りです。

* ローカル変数
    * 宣言されたスクリプト内のみにて。
    * スクリプトが終了すると、ローカル変数は名前も値も消えます。

* グローバル変数
    * 宣言されたスクリプトが存在する FMPA ファイル内のみにて。
    * ファイルが閉じられると、グローバル変数は名前も値も消えます。
    * 逆に言えば、ファイルが閉じられない限りは、一度宣言されたグローバル変数は、名前も値も保持され続けています。


## グローバル汚染という問題
グローバル変数が名前と値を保持し続ける範囲について、最後に述べましたが、この保持の広さこそが問題の根幹です。

「え、なんで？　ずっと保持し続けているんだから便利じゃん？」

という素朴な感想を抱いているかもしれません。が、他プログラミング言語においては、極力、使わないで済むようにとされます。何故でしょう？

ひと言でいえば、「同じ名前で衝突する可能性がある」からです。（何で衝突したらマズイのか、というところまでは掘り下げません）

「え、そんな初心者めいたことしないってばー」

今の開発に集中している真っ最中なら、そうかもしれません。しかし、以下のようなシチュエーションについて考えてみてください。

* 時間が経ってからの再開発時に
* 他の人との協同開発時に
* サードパーティ製のものを利用する際に
    * ~~もっとも、グローバル汚染するサードパーティ製のものなんてロクなものではｒｙ~~

しかも、グローバル変数がそもそも使われているのかどうかも定かでなく、どこのスクリプトで値セットされているのかも総ざらいで調査しないと判別しないとしたら……？

グローバル変数というのは、敵を倒すためにその瞬間は便利に役立つこともありますが、管理されていないと、自分をもまきこみかねない地雷となるわけです。

## それでも FMPA では便利なグローバル変数
「そんなに脅すなら、じゃあ、グローバル変数なんて使わないでおこう」

となっては、しかし、もったいないです。

他言語であれば、どうぞどうぞ使わないでおきましょう、と素直にうなずいても差し支えないこと多しです。が、こと FMPA においては使わずにいるのは、実装できる機能の幅が減り、パフォーマンスが低下することにもなります。

例えば FMPA において、グローバル変数がどのような場面で力を発揮するかというと、以下のものが代表的なところとして挙げられます。

* ユーザの情報 ( IP, MAC Address, OS, etc... ) を環境変数として保持しておく
* 何度も参照するレコードの値を格納しておくことで、レコードへのアクセス回数を減らしパフォーマンス向上
* マージ変数としてレイアウト上に格納された値を表示する

「なるほどわかった。じゃあやっぱり、グローバル変数は使おうじゃないか。で、どうすりゃいい？」

となってくれた方、お待たせしました。いよいよ本題です。


# 本題
## 宣言するだけのスクリプトを用意する
グローバル変数を宣言する、ただそれだけのスクリプトを用意します。

「え、それだけ？」

いや、次回の記事で「値セット編」を用意しますが。とりあえず今回は、それだけ。

最初に述べました通り FMPA には開発におけるメタ的なことを記述しておく機能がありません。あればよいのに。

なので、スクリプトにおいてのみ宣言／値セットすることができるグローバル変数は、やはりスクリプトで管理するしかないのです。

というわけで、用意したものが以下になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/37739/4f16bacd-9147-de30-a517-8cb7a72fe6a9.png)

分かりやすくグローバル変数を管理するためのスクリプトを格納しておくよ、というフォルダを一つ用意して、その中に declare.global_var というスクリプトを作成。

そこで、ひたすらグローバル変数の宣言だけしておきます。並び順はアルファベット昇順など、一見して分かりやすいものがよいかと。

あとは、このスクリプトを開きさえすれば、この FMP ファイルにおいて、どんなグローバル変数がどれだけ使われているか、というのが一目瞭然となります。

逆にいうと、これをしなければ、一目瞭然となりません。恐ろしいですね FMPA って。（ DDR 書き出して文字列検索するとか……いやいや）

なお、値が "" ということで空の設定になっていますが、これだとデータビューアには反映されません。だからといって、仮の値を突っ込むのはやめておきましょう。


# おわりに
前置きばかり長くなって本題が短くなりましたが……

とにかくまずは、このファイルで使われるグローバル変数はコレだ！！　という宣言を、必ずしておくようにしましょう。これだけで、将来にわたってたくさんの人たち／自分たちが救われることになるのは間違いなしです。~~なんで過去の自分はこれをやらなかっｒｙ~~

次の記事では、宣言したグローバル変数への値セットをどのようにすべきか、ということについて書きます。

⇒ 後編記事 [FileMakerにおけるグローバル変数の管理：値セット編](https://qiita.com/Hi_Noguchi/items/29e2aba2837085d10660)

## 余談
なお FMPA には、グローバル変数による汚染よりもさらに恐ろしい、グローバルフィールド汚染なるものがありますが……こちらについてはひとまず触れないでおきます……
InfluxDB 2.0 を使う(3)　Goのクライアントライブラリを使う

# はじめに

[InfluxDB 2.0](https://www.influxdata.com/products/influxdb-overview/influxdb-2-0/)で、[前々回](https://qiita.com/riverplus/items/5de5e830ddb622856209) と [前回](https://qiita.com/riverplus/items/20456e4188a4c0f5f62d) ではCLIで値の入出力を行いました。本記事ではGo言語クライアントライブラリ [influxdb-client-go](https://github.com/influxdata/influxdb-client-go) で同様の処理を行う手順を見ていきます。

# 環境

* OS: Debian 9.11
* InfluxDB: v2.0.0-alpha.20
* influxdb-client-go v0.1.4

# アクセストークンの作成

ライブラリからInfluxDBにアクセスするにはアクセストークンが必要です。アクセストークンはあらかじめサーバで作成します。アクセストークンの作成にはCLIを用います。

```
$ ./influx auth create --org my_organization  --write-buckets my_buckets --read-buckets my_buckets
```

これは指定したorganization（ユーザのグループ）に対し、値の登録と取得を許可するアクセストークンを作成するコマンドです。指定bucketへのread/writeを許可します。他のオプションは[こちら](https://v2.docs.influxdata.com/v2.0/reference/cli/influx/auth/create/)を参照して下さい。

結果は次のようになります。Tokenとある（bbbbbbbbbbbbbbbbに相当する部分）がアクセストークンです。

```
ID					Token				Status	UserID				Permissions
aaaaaaaaaaaaaaaa	bbbbbbbbbbbbbbbb	active	cccccccccccccccc	[read:orgs/dddddddddddddddd/buckets write:orgs/dddddddddddddddd/buckets]
```

# 値の登録と取得の例

サンプル値を登録し、同じ値を取得するコードを以下に示します。

```main.go
package main

import (
	"context"
	"fmt"
	"net/http"
	"time"

	influxdb "github.com/influxdata/influxdb-client-go"
)

func main() {
	// クライアントの初期化
	token := "bbbbbbbbbbbbbbbb"
	client := &http.Client{}
	influx, err := influxdb.New("http://localhost:9999", token, influxdb.WithHTTPClient(client))
	if err != nil {
		panic(err)
	}

	// 値の登録
	metrics := []influxdb.Metric{
		influxdb.NewRowMetric(
			map[string]interface{}{"temperature": 12.3, "humidity": 45.6},
			"my_measurement",
			map[string]string{"city": "tokyo"},
			time.Now(),
		),
	}
	if _, err := influx.Write(context.Background(), "my_bucket", "my_organization", metrics...); err != nil {
		panic(err)
	}

	// 値の取得
	flux := "from(bucket: \"my_bucket\") |> range(start:-24h)"
	result, err := influx.QueryCSV(context.Background(), flux, "my_organization")
	if err != nil {
		panic(err)
	}
	for i := 0; result.Next(); i++ {
		m := map[string]interface{}{}
		err := result.Unmarshal(m)
		if err != nil {
			panic(err)
		}
		fmt.Println(i, m["_measurement"], m["_time"], m["city"], m["_field"], m["_value"])
	}

	// クライアントの終了
	influx.Close()
}
```

値の登録には`Write`を用います。ここではタイムスタンプとして現在時刻を指定し、またタグにcityを追加しています。

値の取得には`QueryCSV`を用います。ここでは直近24時間のデータを取得するクエリをFluxで記し関数に渡しています。結果に対し、`result.Next`でfor文を回しつつ、`Unmarshal`でmapに変換するというのがイディオムのようです。

実行結果は以下の通りです。

```
$ go run main.go
0 my_measurement 2019-12-02 12:44:09.456475011 +0000 UTC tokyo temperature 12.3
1 my_measurement 2019-12-02 12:44:09.456475011 +0000 UTC tokyo humidity 45.6
```

# おわりに

InfluxDB 2.0のGo言語クライアントライブラリで値の登録と取得を行うサンプルを見ていきました。InfluxDB 2.0はまだアルファ版のため、今後ライブラリに大きな変更が入りうる点はご了承下さい。
Windows10にTensorFlowとKerasをインストールする方法（適宜更新）

# ディープラーニングの環境構築

## 目次

1. Anaconda（Miniconda）のインストール
2. CUDAのインストール
3. cuDNNのインストール
4. TensorFlowのインストール
5. Kerasのインストール

## 初めに

この記事はTensorFlowを公式のインストール方法に従ってインストールしていきます。
公式記事は[こちら](https://www.tensorflow.org/install/gpu)（2019年12月2日現在）

## Anaconda（Miniconda）のインストール

[miniconda](https://docs.conda.io/en/latest/miniconda.html)をインストール
そのあと、Anaconda Pronptで

```
conda create -n [name] python=3.7
```

を実行
[name]には自分が作成する仮想環境の名前を入力

```
conda activate [name]
```

で仮想環境を起動

## CUDAのインストール

[ここ](https://developer.nvidia.com/cuda-10.0-download-archive)からCuda10.0のダウンロード
※今回はTensorFlowの公式インストールガイドに合わせてCuda10.0にしています。

## cuDNNのインストール

[ここ](https://developer.nvidia.com/rdp/cudnn-download)からcuDNNのダウンロード
zipを展開し、得られたファイルをCUDAのインストール先のフォルダに対応させて入れる。
※基本的にCUDAのインストールフォルダは```C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA```あたりにあります。

## TensorFlowのインストール

Anaconda Pronptで

```
pip install tensorflow-gpu
```

バージョンを変更したいときは

```
pip install tensorflow-gpu==1.14
```

## Kerasのインストール

Anaconda Pronptで

```
pip install keras
```

以上



## 最後に

この記事は適宜更新していくと思います。
本番DBのデータを文字化けさせた話

実際にあったお話を供養させます。
何が原因だったのかを推測しながら読んでもらえるとポストモーテム冥利に尽きます。

## 経緯
### 混入
- 画像コメントの文字数の上限を50文字→100文字へ増やしたいという要望が来ました。
- 対応者は下記のようなDDLで対応することにしました。

```sql
-- テーブルはシャーディングされている
ALTER TABLE `table_1` CHANGE `image_comment` `image_comment` varchar(100) DEFAULT NULL;
ALTER TABLE `table_2` CHANGE `image_comment` `image_comment` varchar(100) DEFAULT NULL;
-- たくさんある… たくさん…
ALTER TABLE `table_100` CHANGE `image_comment` `image_comment` varchar(100) DEFAULT NULL;
```

- 私はDBAのような役回りを担っており、「テスト環境でも動作確認して、問題ない」と判断しました。
- そうしてアップデート当日を迎え、上記のDDLを流しました。
- 無事に画像コメントの文字数上限が増えたことを本番環境で確認し、家路についたのです。

### 発覚
- 翌朝、いつもどおり出社して仕事をし始める私。
- しばらくすると上記の対応をしたスタッフがやってきて言うのです。
- 「なんか画像のコメントが文字化けしてるみたいなんですよね…」
- 「……？！」

### 調査
- そんな馬鹿な… という思いを捨てきれず調査開始
- 文字化けが発生しているという *`table_60`* を確認し始める…
- 数分後、私は衝撃の事実に気付くのです。
  
「うわぁあ！！」と飛び上がって驚いたのは後にも先にもこのときくらいです。


## ここまで
- 対象のテーブルはシャーディングされている
  - 私が入社する前からたくさんあります
- 文字数を増やす対応をしただけなのに文字化けした…
  - VARCHAR 型で文字化けに関わるオプションといえば？
  - ちなみに変更したカラムの Before です

  - `image_comment varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,`

- テスト環境でも本番環境でも確認したのに…
  - その「確認」は漏れてない？


### 答えに入る前に

今年のアドベントカレンダーは[本番環境でやらかしちゃった人 Advent Calendar 2019](https://qiita.com/advent-calendar/2019/yarakashi-production) を要チェックしてます。

こういうやらかし系の共有は本当にありがたいです。

死屍累々のポストモーテム会…(ﾟＡﾟ;)ｺﾞｸﾘ



## 惨劇はなぜおこってしまったのか

```sql
-- 文字化けしてないテーブル
CREATE TABLE `table_1` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
  -- カラム省略
  `image_comment` varchar(100) DEFAULT NULL,
   PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci ROW_FORMAT=DYNAMIC;
```



``` sql
-- 文字化けしてるテーブル
CREATE TABLE `table_60` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT 'ID',
  -- カラム省略
  `image_comment` varchar(100) DEFAULT NULL,
   PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 COLLATE=latin1_swedish_ci ROW_FORMAT=DYNAMIC;
```



あ！やせいの`latin1`があらわれた！



何故か一部のテーブルだけ `CHARSET=utf8 COLLATE=utf8_unicode_ci`ではなかったのです！！



なんでかわかりませんが、昔からそうだったようです…



### つまりこうなります

元々入ってた文字データ(`CHARSET=utf8 COLLATE=utf8_unicode_ci`)が、カラムの型変更のタイミングで文字セットを指定しなかったため、テーブルのデフォルト`CHARSET=latin1 COLLATE=latin1_swedish_ci`になる。

- するとどうなる？
  - 文字が化ける
- するとどうなる？
  - メンテが始まる



## 二度と惨劇を起こさないためにどうしたのか
- シャーディングされているテーブルに齟齬がないかの確認
  - これは事件後に対応しました
  - あなたの現場のシャーディングは、大丈夫ですか？
- 全テーブルでのデバッグ
  - 登録済みのデータが無事か、全テーブルからいくつかサンプルでレコードを取ってくるようなデバッグをすればよかったです
- VATCHAR型, TEXT型の変更時には `CHARACTER SET utf8 COLLATE utf8_unicode_ci`あるいは`CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci`を入れる
  - これは今でもトラウマなのでチェックします

## ひとこと
ポストモーテムを書くのも読むのもトラブルシューティング力を高めるきっかけになるかと思います！
みなさんももっと恥を忍んでさらけ出しましょう！

### 参考リンク
- https://dev.mysql.com/doc/refman/5.6/ja/charset-column.html
- https://qiita.com/kazu56/items/6af85ffcf8d3954455ad
docker toolboxでマウントできずに苦しんだ話

Railsの開発環境をDockerで作成しようとしようとした際に、うまくホストのディレクトリがマウントできずに苦しんだ話。
最終的にはDocker ToolboxとVM VisualBoxを再インストールし直したらマウントできました。

メモとして残しておきます。

#導入環境
* Windows10 Home 64bit

# 1.何をやろうとしたか

Railsの開発環境をローカルで構築しようと、Dockerをインストールするところからスタート。
私のPCはWindows10 Homeだったため、Docker ToolBoxを使用。

[Windows環境にDocker Toolboxをインストールする](https://qiita.com/KIYS/items/8ac37f6757a6b7f84569)
[丁寧すぎるDocker-composeによるrails5 + MySQL on Dockerの環境構築(Docker for Mac)]
(https://qiita.com/azul915/items/5b7063cbc80192343fc0)

こちらの記事を参考に必要なファイルを用意し、rails newを実行しました。

(分かりやすくDocker初心者でも構築できるナイスな記事たちです。)

#2.実行結果

エラーは発生しませんでしたが、指定したディレクトリにうまくマウントできておらず、想定とは違う結果に。
本来の想定ではrails newを実行したフォルダ直下にファイルがずらっと並ぶはずでした。

#3.やったこと

いくつかの方法を試して見ました。

## (1)docker-compose.ymlのvolumesを変更
マウントが失敗している原因として真っ先に浮かんだのが、volumesの指定だったためパスの指定を絶対パスに変えてみました。
結果は駄目でした。いくつかのパターンを試してみましたが、どれもマウントできませんでした。

```docker-compose.yml
 web:
    build: .
    command: rails s -p 3000 -b '0.0.0.0'
    volumes:
      - #.:/app_name　修正前
      - C:\Users\ユーザー名\projects\ror:/app_name #修正後
```

## (2)boot2docker.isoを入れ直し、`docker-machine create`実行

調査中こちらの記事を見たため、実施。
[docker toolboxでホストのディレクトリがマウントできない場合の対処方法](https://qiita.com/imp954sti/items/5f6e12c8a412ab052f93)

結果は変わらずにマウントできませんでした。

## (3)Oracle VM VirtualBoxの共有フォルダ変更
Docker Toolboxはホストのディレクトリをマウントするとき、CドライブのUsers内のみ共有できるとの記載を見たので、確認してみました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/485956/f0d2242c-a079-d11d-c5f9-db520b609229.png)

確かに設定上はUsersフォルダになっています。
今回マウントしようとしていたフォルダもUsers下にありますが、念の為プロジェクトのパスを指定してみました。

これもマウントできない。。。

#4.初心に立ち返る

ここまでで私の心は疲れ果ててしまったので、全部まっさらにしてやり直すことにしました。

Docker ToolboxとVM VisualBoxを殺意を持ってアンインストールして、最初から再インストール。
使用するファイル(docker-compose.yml等)もすべて最初から作成し直し、半ば諦めながらrails newを実行。

![folder.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/485956/f55e3872-3bbc-c3cd-02b5-9d47f76b6e0c.png)


・・・

理由は全くわかりませんが、なぜかマウントできました。

#5.学んだこと
最後はあっけなく、また理由もわからないというしょうもない解決でしたが、学んだことも多かったです。

特に今回始めてDockerを使用したため、構築に必要なファイルの記述方法やコンテナの概念に詳しくなりました。
恐らく手順通りに実施できていれば、ここまで調査することもなかったと思うのでその点は収穫だと思います。

#まとめ
わけがわからなくなったときは、最初からやり直す。急がば回れ。
DjangoのTemplatesでの条件分岐に苦戦した。

### やりたいこと


Djangoで某サークルの機材予約をWebからできるようにしようとしていて、
過去の予約データを編集したり、削除できないようにするために
現在時刻より前の予定の編集ボタンや削除ボタンを非表示にしたい。

そのための条件分岐をtemplatesに記述しようとして苦戦。


```python:views.py
context = {
    'band_name' : band_name,
    'schedules' : Schedule.objects.all(),
    'target_date' : datetime.datetime.now()
}
return render(request, 'xxx.html', context)
```

```html:xxx.html
{% schedule in schedules %}
  {% if schedule.target_date > target_date %}
    ボタン表示
  {% endif %}
{% endfor %}
```

変数（今日の日付）とモデルをtemplatesに渡して
schedulesのループの中で日付で条件分岐をしてボタンを表示・非表示を制御したい

しかし、うまく条件分岐できない。
単純に
`{% if True %}`
ではうまく条件分岐ができていたので、viewで予め判定して、モデルのidと判定結果（Bool）を辞書として
templatesに渡して制御しようとした

```python:views.py
my_dic = {}
target_date = datetime.datetime.now()
for schedule in schedules:
  if target_date < schedule.target_date:
    my_dic[schedule.id] = True
  else:
    my_dic[schedule.id] = False 
context = {
    'schedules' : Schedule.objects.all(),
    'my_dic' : my_dic,
}
return render(request, 'xxx.html', context)
```

```html:xxx.html
{% schedule in schedules %}
  {% if my_dic[schedule.target_date] %}
    ボタン表示
  {% endif %}
{% endfor %}
```

しかし、これもうまくいかない。

templates内で辞書の展開は
`my_dic.id`
でidにキーを指定すれば展開ができるが、この「.id」の部分にschedulesのループ内のschedule.idを渡したいが
これもうまくいかない。
withタグを使って変数を代入してもうまくいかない。

`{% with target_id = schedule.id %}`
`{% if my_dic[schedule.target_id] %}`

これもうまくいかない。

`{% if my_dic.schedule.id %}`
`{% if my_dic[schedule.id] %}`
これらはもちろんダメだった。


###解決策

モデルに直接ニョキっとメソッドを生やした。

```python:models.py
class Schedule(models.Model):
  # カラムの定義 ・・・
  target_date = models.DateField(default=timezone.now, verbose_name=‘予約日‘)

  def is_available(self):
    current_day = timezone.datetime.today()
    return current_day < self.active_date
```

```html:xxx.html
{% schedule in schedules %}
  {% if schedule.is_available %}
    ボタン表示
  {% endif %}
{% endfor %}
```


Done.


このメソッドを使えばURLでの直接アクセスに対してもredirectできそう。

やりたいことに書いたようなコーディングでうまくいかないものなのか、、、
もちろんtemplatesでロジックはなるべく書かない方がよいが、
辞書の参照などのちょっとした操作はうまく働いて欲しかった。

他にもtemplates filterを自分で作ってしまうなどの解決策は見つけたが、
今のところはこのやり方が一番綺麗だと思う。

以上です。


P.S.
ちゃんゆーさん（@chanyou0311）にご教授頂きました。感謝です。
railsの`ActiveSupport::Callbacks`の各コールバックの実行順序

# railsの`ActiveSupport::Callbacks`の各コールバックの実行順序

## 結論

`before_*`, `around_*`はキューに積まれた新しい順番に、
`after_*`は積まれた順番の古い順に処理される

> Calls the before and around callbacks in the order they were set, yields
> the block (if given one), and then runs the after callbacks in reverse
> order.

See: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/callbacks.rb#L76-L78

## 実装
### キューを処理する処理
まずコールバックの実行自体は`ActiveSupport::Callbacks#run_callbacks`(https://github.com/rails/rails/blob/master/activesupport/lib/active_support/callbacks.rb#L96-L141) で処理されます。

その中の以下の処理で処理順を並び替えています。

```ruby
callbacks.compile

# See: https://github.com/rails/rails/blob/master/activesupport/lib/active_support/callbacks.rb#L103
```

```ruby
def compile
  @callbacks || @mutex.synchronize do
    final_sequence = CallbackSequence.new
    @callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback|
      callback.apply callback_sequence
    end
  end
end

# See: https://github.com/rails/rails/blob/d94263f3e76527f196cab2026cfa119ff26b6d9e/activesupport/lib/active_support/callbacks.rb#L562-L569
```

ポイントは`reverse`している所です。
処理するときは、積まれたキューを`reverse`して頭から処理していきます。

### キューを積む処理

キューを積む時は`set_callback`はが呼ばれます。
その中で、`prepend`と`append`どちらかが呼び出されます。

```
        def set_callback(name, *filter_list, &block)
          type, filters, options = normalize_callback_params(filter_list, block)

          self_chain = get_callbacks name
          mapped = filters.map do |filter|
            Callback.build(self_chain, filter, type, options)
          end

          __update_callbacks(name) do |target, chain|
            options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped)
            target.set_callbacks name, chain
          end
        end


# See: https://github.com/rails/rails/blob/d94263f3e76527f196cab2026cfa119ff26b6d9e/activesupport/lib/active_support/callbacks.rb#L673-L685
```

```
        def append(*callbacks)
          callbacks.each { |c| append_one(c) }
        end

        def prepend(*callbacks)
          callbacks.each { |c| prepend_one(c) }
        end

# See: https://github.com/rails/rails/blob/d94263f3e76527f196cab2026cfa119ff26b6d9e/activesupport/lib/active_support/callbacks.rb#L571-L577
```

この`skip_callback(name, *filter_list, &block)`を呼び出す時の第二引数`*filter_list`の値によって配列の順序を変更しています。

## 詳しく結論

### キューを積む時
`before_*`, `around_*`は`prepend` -> 先頭に追加
# 先頭から古い順

`after_*`は`append` -> 末尾に追加
# 先頭から新しい順

### キューを処理する時

`reverse`して先頭から処理していくので、
`before_*`, `around_*`はキューに積まれた新しい順番に、
`after_*`は積まれた順番の古い順に処理されます。
勉強記録　8日目 〜ポインタのconstとデータのconst〜 - Effective C++ 第3版 -

#　はじめに

Effective C++ 第3版の3項8ページから勉強していきます。
今回は、「ポインタのconstとデータのconst」についです。

# Effective C++ 第3版 - 3項　可能ならいつでもconstを使おう -

## ポインタのconstとデータのconst

### constを付ける利点

constを付ける利点は、特定のオブジェクトについて、「変更してはいけない」という意味的な制約をはっきりさせ、制約を破るとコンパイラが知らせてくれる点です。
また、constにより、他のプログラマにも、指定したオブジェクトの値を変更しないように知らせることできる。
そのため、オブジェクトの値を変更するつもりがないのであれば、constを付けた方が良い。

### constが使える範囲

constは、驚くほど広い範囲で使用されます。
クラス外では、グローバルな定数、あるいは、名前空間内での定数でconstを使える。
また、ファイル、関数、ブロックのstaticオブジェクトにも付けることができる。
クラス内では、static・非static双方のデータメンバに使える。
また、ポインタに使って、「ポインタそのものが変更不可」なのか、「ポインタの指すデータが変更不可」なのか、あるいは両方なのかを指定することができる。

以下にポインタにconstを付けた場合、「ポインタそのものが変更不可」なのか、「ポインタの指すデータが変更不可」なのかを示す。

```cpp
int *p1_x = &x;              // ポインタは非const
                             // データも非const
const int *p2_x = &x;        // ポインタは非const
                             // データはconst
int *const p3_x = &x;        //  ポインタはconst
                             // データは非const
const int *const p4_x = &x;  // ポインタはconst
                             // データもconst
```

上に、示す通り、constがアスタリスク(*)の右にあるのか左にあるかで、「ポインタが変更不可」なのか、「ポインタの指すデータが変更不可」なのかが区別できる。

以下に、勉強で使用したコードを示します。

### サンプルコード

```cpp
#include <iostream>

int main(int argc, char *argv[]) {
  std::cout << "3_const_tutorial.cpp" << std::endl;
  int x = 5;

  int *p1_x = &x;              // ポインタは非const
                               // データも非const
  const int *p2_x = &x;        // ポインタは非const
                               // データはconst
  int *const p3_x = &x;        //  ポインタはconst
                               // データは非const
  const int *const p4_x = &x;  // ポインタはconst
                               // データもconst

  int x2 = 3;
  *p1_x  = x2;
  p1_x   = &x2;

  // *p2_x = x2; // コンパイルエラー
  p2_x = &x2;

  *p3_x = x2;
  // p3_x  = &x2; // コンパイルエラー

  // *p4_x = x2;   // コンパイルエラー
  // p4_x  = &x2;  // コンパイルエラー
}
```

### 実行結果


## 参考文献

・https://www.amazon.co.jp/gp/product/4621066099/ref=dbs_a_def_rwt_hsch_vapi_taft_p1_i0
まだtimes導入検討で消耗してんの？

この記事は [NIJIBOX Advent Calendar2019](https://qiita.com/advent-calendar/2019/nijibox)の8日目の投稿です。


#はじめに
この記事では新人Frontendエンジニアの***JSONハードコーダー***がWEB業界で半年働いて得たTipsを紹介します。

これを読んでいる皆さんの中には、社内のコミュニケーションツールとして「Slack」を使ったことのある方が多いのではないでしょうか？そして分報文化を社内に取り入れた方が良いのかどうか？という議論もよく飛び交っていますよね。
そんな議論で消耗するくらいならさっさと導入しましょう。なぜならチームにとってデメリットを上回るメリットがあると思うからです。何より、分報文化は新人エンジニアを救済してくれると僕は思います。
今回はそんなSlackを用いた新人向けの**分報活用しようぜ！**ってお話をします。


##timesとは？
日報ならぬ、「分報」という文化があるのをご存知の人も多いかと思いますがtimesとは「分報を行うSlackのチャンネル」のことを指します。(以下、分報のチャンネルのことは「times」とします。)
1日に一度行う進捗報告ではなく、もっとラフな感じで、イメージとしては「社内版のTwitter」です。
僕はtimesを限りなく自由な場だと感じているので本当に自分のつぶやきの場という認識があってると思います。(弊社では毎日呟かないといけない！といった強制はないですし、timesを作ること自体任意です。)


このtimesの文化によるメリットは結構あります。
「新人の僕にとって嬉しい点」に絞って紹介したいと思います。
##メリット

>・個人的な作業メモを残すことができる
・Slack連携ツールを他人を気にせず試すことができる（自分のカレンダー予定を流したりtodo管理をSlack上でやったり）
・困っていることをつぶやくだけで解決することがある
・日報と比べ、リアルタイムにフィードバックを貰える
・誰がどんな業務をしているのかがわかる
・分報チャンネルが「自分専用の部屋」となることでチャットベースのコミュニケーションに慣れたり、心理的に発言のハードルが下がる
・それまでDMなどで行われる可能性があったやりとりをpublic channelで行うことができる（情報のオープン化）
・技術的知見を知ることができる
・趣味や興味関心ネタがわかるので「人となり」がわかる（コミュニケーションの促進）
>
>[分報を導入して3年経ったので振り返る](https://fortkle.hatenablog.com/entry/2018/12/06/222836)より引用

###意に反して困っていることやわからないことをつぶやいてしまう
新人エンジニアにとって、技術的にわからないことや、業務の進め方など多くの細かい問題が出てくると思うので、こういった問題に対してリアルタイムフィードバックがもらえるのはかなりのメリットになります。


わからないことを即時フィードバックしてもらい、かつ知見までもらっています。それだけでなく、業務的な指示をもらえるので作業が円滑に進みます。参考に僕のtimesでの先輩とのやりとりを貼ります。


![adokare01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528613/c7df8789-f0cb-9c69-574f-88e923e0f144.png)



###思わずコミュニケーションが促進されてしまう
コミュニケーションが円滑になれば、チーム内の人間がどういう人かもわかるし(仲良くもなれるよ！)、〇〇さんはこういった点に強みを持っている！とかもわかります。業務の話でいうなら、このタスクは誰が詳しいかとかもわかります。「このことは〇〇さんに聞いたら良いのか！timesで聞いてみよ！」となり、問題解決の時間は大幅に短縮できます。「これわかんないけど誰に聞けばいいんだろ....先輩には話しかけにくいしな...」で時間を消耗しなくて済みます。

###図らずもアウトプットの場ができてしまっていた
学びをアウトプットすることが一番の成長だと思います。
教えてもらったことやドキュメントを読んで得た知識を自分の言葉で書き出すことは学習したことの整理にもなります。
僕はアウトプットしたことが意外と自分の中に残っていますし、全容を覚えてなくてもtimesで書いたことさえ思い出せればすぐに見返せるし、調べ直す手間も省けますよね。
他にも、先輩エンジニアがtimesを見てくれて、この解釈はちょっと違うんじゃない？と行ったコメントをくれ、軌道修正してくれるのは、新人エンジニアにとってめちゃくちゃメリットです。

###先輩のtimesを見て時間を溶かしてしまう
先輩エンジニアのアウトプットは知見の宝庫です。見るだけで学びですし、今の自分では絶対知り得ないこと、理解できなかったことも学べます。知見です。

などなど多くて書ききれませんが

**つまり、良いことしかない！**

ということですね？では導入に参りましょう。

##timesの導入

めっちゃ簡単です。Slackを使ったことがある人なら今すぐはじめられます。
`times_{自分の名前やあだ名}`という自分専用の個室チャンネルを作ります。これをチーム全員分作れば終わりです。


##全員参加のチャンネルではダメなのか？

ここで、チームのみんなのいるgeneral的なチャンネルでやれば良いのでは？って思う方もいるかもしれませんが、個人チャンネルの方が自分の部屋感があるのでつぶやきやすいです。心理的ハードルが低いのは意外と重要です。
そもそもgeneralのような多くの人がいる場で自分のタスクのことを各々が書いたらカオスすぎてキャッチアップできなくなります。

##強制ではないとは言ったがあれは嘘だ
全員がtimesチャンネルを作ったとしても、各々のアウトプット習慣やチームの雰囲気によって、投稿頻度は変わってくる気がします。
僕が参画した現場でもtimesが盛り上がっているところもあれば、あまり使われていないところもありました。
僕的には是非とも導入した方が良い文化だと思います。最初の雰囲気が形成されるまではちょっと頑張ってアウトプットしましょう！アウトプットは慣れです。最初は戸惑うかもしれませんが、何も技術の話や、業務の話だけしかしてはダメという訳ではないです。気軽に今日の食べたご飯とかそんな感じのつぶやきからはじめて言っても良いと思います。



##ポエム
新人エンジニアは質問するの恥ずかしい＆こんなしょうもない質問して良いのだろうか...
という思考(僕もそうでしたw)になって、自分でずっと調べて結果、何も進捗が出ないという負のスパイラルに陥りがちだなって思います。


立ち止まるくらいならどんどん質問していった方が良いですし、
質問できるようになることがエンジニアとしての一歩のような気もします。

timesでアウトプットしていくことで、先輩エンジニアの方からの導きを受けられて、ちゃんとしたエンジニアとしての仕事の進め方や振る舞いを学べわからないがわからない状態を冷静に整理でき、1つ1つ地に足をつけた成長ができると思います。



**新人エンジニアの皆、今すぐtimesを作ろうな！！**
競プロ勉強会をやる際にやっていてよかったこと

この記事は[Competitive Programming (2) Advent Calendar 2019](https://adventar.org/calendars/3865)の3日目です。

人は弱い生き物です。競プロでもっと強くなりたいと思いつつも必ずしも精進し続けられるとは限りません。ということで、競プロ勉強会が立ち上がったりするわけです。

そこで、競プロ(AtCoder)勉強会に参加する上でやっていて良かったことを紹介したいと思います。

## :relaxed:参加者のAtCoder IDを返すAPIを作る

競技プログラミングをするくらいなので、**恐らく参加者はプログラミングの嗜みがあり**、勉強会をより良くしようとbotなりなんなりを作ることになると思います。

そこで便利なのが、参加者のIDを返すAPIを用意することです。進捗をつぶやくbotを作ったとして面倒なのが、参加者の情報の更新です。1個のアプリケーションだけなら1回の更新で済みますが、アプリケーションの数Nに対してO(N)の変更が必要になってしまいます。

APIを作るのに便利なサイトが以下です。
https://sheetdb.io/

GoogleのSpreadsheetと連携させるだけで、create, read, update, deleteなどのAPIが勝手に作成されます。

slackでメンションするために、slackのidやニックネームをカラムで持って使っていました（過去形）

## :sweat_smile:メンバーのsteak数をつぶやくbotを作る

AtCoder Promelemsで見れるcurrent steakを各メンバーの分呟くやつです。

「お、○○結構やってんな」みたいな感じでコミュニーケーションのきっかけになったり、続いているように見られたいという気持ちから忙しいときも頑張ってみようと思えたりして導入当初はいい感じでした。

しかし、一旦途切れるとまた稼ぎ直すのは中々大変で、時間が経つにつれて虚無になってしまったかなという印象があります。

**考えてみるとsteakというのは中々ストイックな指標で、モチベーションが様々な人が共通して追うには若干合わないかな**と思います。週末にまとめて問題を解いても1日続いた扱いになりますし。

ざっくり進捗を追うのであれば、一週間単位で解いた問題数orスコア合計とかで出せばもう少し続けるハードルが低くできたかもしれません。

## :smile: 今日の一問botを動かす

一瞬だけ[#chokudai今日の一問](https://twitter.com/hashtag/chokudai%E4%BB%8A%E6%97%A5%E3%81%AE%E4%B8%80%E5%95%8F?src=hashtag_click)というものがありましたね。

参加者のレベル帯の問題のうち、解いた人が少ない問題を毎日slackで通知するbotでメンバーの一人が作ってくれました。（例のAPIを使って）

とりあえず解くものが決まっていないときに手をつけられるし、共通の話題になるので1問用意されることのメリットは結構あると思います。

レベルが合わない問題がでると、解くモチベーションがないみたいな原理的な問題がどうしてもありますが。

## 以上です。

思ったより何もやっていなかったことに気づきました。

逆に、こんなことをしていたら盛り上がったみたいな話を聞きたいですね。
JavaScript 複数キーを対象とするソートの方法

# 概要
画面上テーブルで表示するようなデータを配列で持つ場合、以下のように オブジェクトを配列で持つようにする場合が結構あると思う。

```json
[
    {
        "id": 1,
        "group": 1,
        "name": "tom"
    },
    {
        "id": 2,
        "group": 1,
        "name": "tim"
    }
]
```
このような形のデータを画面処理でソートする際、
さらには複数キーでのソートを行いたい場合に実装した内容のメモ書きになります。

# 詳細
まずはベースとなるソート処理の関数
やってることとしては、keyを使って比較するオブジェクト（aとb）の値を取得し、判定するロジック。
後ろの引数で昇順、降順、データがない場合の表示順を制御できるようにもしてる。

```javascript
const defaultSortFunc = function(a, b, key, direction = 1, nullsFirst = 1) {
  if (a[key] == undefined && b[key] == undefined) return 0;
  if (a[key] == undefined) return nullsFirst * 1;
  if (b[key] == undefined) return nullsFirst * -1;
  if (a[key] > b[key]) return direction * 1;
  if (a[key] < b[key]) return direction * -1;
  return 0;
}
```
ソートキーが１つの場合は、sort処理内で関数を呼び出すだけでおしまい。

```javascript
const data = [
  {"id":1,"group":1,"name":"tom"},
  {"id":2,"group":1,"name":"tim"},
  {"id":3,"group":3,"name":"tomas"},
  {"id":4,"group":3,"name":"tanaka"},
  {"id":5,"group":2,"name":"takahashi"},
  {"id":6,"group":2,"name":"takada"}
]
data.sort((a, b) => defaultSortFunc(a, b, 'name'))
```
ソートキーが複数の場合は、以下のような処理になる。
対象キーでソートの判断がつかない場合のみ、次のキーでのソートを行っていくようなイメージ。

```javascript
const sortFunc = function(data, keys) {
  const _data = data.slice();
  _data.sort((a, b) => {
    let order = 0;
    keys.some(key => {
      order = defaultSortFunc(a, b, key);
      return !!order;
    });
    return order;
  });
  return _data;
}
```
上記の関数を呼び出してソートを行う場合は以下のような呼び出し方になる。
ソートのキーとなる項目を配列で渡してあげる。

```javascript
const data = [
  {"id":1,"group":1,"name":"tom"},
  {"id":2,"group":1,"name":"tim"},
  {"id":3,"group":3,"name":"tomas"},
  {"id":4,"group":3,"name":"tanaka"},
  {"id":5,"group":2,"name":"takahashi"},
  {"id":6,"group":2,"name":"takada"}
]
sortFunc(data, ['group','id'])
```

# まとめ
オブジェクトを配列で持つこの形はAPIのレスポンスなどでも結構多い形のイメージがあるので、
結構使いどころがある処理だと思います。

ソートの基本的な処理を関数にして呼び出しておく形にすると、
ソート処理で特殊な判断をしたい項目があったら関数を置き換えてソート処理をするなど、
応用がきくので個人的には便利だと思っています。
MutationObserver, IntersectionObserver, elementFromPointを使ってクソゲーを作る

[テックタッチアドベントカレンダー](https://qiita.com/advent-calendar/2019/techtouch)の3日目を担当する @92thunder です。
2日目は @smith-30 による [初めての ECS](https://qiita.com/smith-30/items/8261adb105507e77b4f6) でした。ちなみに自分は手元のDocker環境が動かなくなったのでバックエンドを触れなくなってしまいました。

# できたもの
<p class="codepen" data-height="265" data-theme-id="default" data-default-tab="js,result" data-user="92thunder" data-slug-hash="yLyLMap" style="height: 265px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Demon Play with IntersectionObserver , MutationObserver, elementFromPoint">
  <span>See the Pen <a href="https://codepen.io/92thunder/pen/yLyLMap">
  Demon Play with IntersectionObserver , MutationObserver, elementFromPoint</a> by Ryota Kunisada (<a href="https://codepen.io/92thunder">@92thunder</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
* MutationObserver, IntersectionObserver, elementFromPointを使って鬼ごっこゲームを作りました
* 素晴らしい名作ゲームが簡単に完成してしまいましたね
* 所要時間2時間

# Mutation Observer
DOMの変更を監視するためのAPI
オプションを設定することで子孫全部やstyleが変わった場合だけ反応するよう設定できる
https://developer.mozilla.org/ja/docs/Web/API/MutationObserver

subtreeオプションを有効にしてdocument.bodyなどに対して使うとページ読み込み時など動作が重くなってしまうので使いどころに気を付けたほうが良さそうです。
今回のゲームではPlayerとEnemyの要素が動くたびにスタイルが変わっていてその変更時に毎回elementFromPointによる衝突判定が行われているので敵を大量に増やすとパフォーマンスが残念なことになりそうです

```ts
// コールバックの設定とオブザーバインスタンスの設定
const observer = new MutationObserver(() => {
  console.log('DOMに変更がありました')
})

// document.bodyを対象に監視を開始する
observer.observe(document.body)

// 監視を止める
observe.disconnect()
```

# Document.elementFromPoint
左上を起点として、座標位置にある要素を返す
iframe中の要素だった場合はiframeを返すため、iframeを考慮した使い方が必要
https://developer.mozilla.org/ja/docs/Web/API/Document/elementFromPoint

今回のゲームではPlayerの上にEnemyが重なっているかの判定に使っていて、重なったら負けになるよう実装しています
テックタッチでは、これを使うと座標の上に何の要素が表示されているのかがわかるので、要素が隠れているかどうかなどの判定に使っています

```ts
// 100, 100の位置にある要素を返す
const element = document.elementFromPoint(100, 100)

// Node.containsと合わせて使うことがよくあります
const app = getElementById('app')
if (app.contains(element)) {
  // appに含まれているので無視
  return
}
```

# IntersectionObserver
対象の要素と祖先もしくは最上位のビューポートと交差したタイミングでコールバックを実行する
スクロールを使ったコンテンツの遅延読み込みなどに使う
オプションでrootを指定して交差判定に使う要素や交差の閾値を設定できる
IEはサポートされていない

IntersectionObserverを使って画面の範囲外に出たら負けになるよう実装しました
交差の閾値（要素が何％はみ出ているか）を設定できるので50%の0.5を設定しています
https://developer.mozilla.org/ja/docs/Web/API/IntersectionObserver

```ts
// コールバック設定
const observer = new IntersectionObserver(() => {
  console.log('交差しました')
})

// #targetの要素とビューポートの範囲外に出るor範囲内に入るたびにコールバック関数を実行
observer.observe(document.getElementById('target'))

// 監視を止める
observe.disconnect()
```

# 所感
* 3つとも今年初めて知ったAPIで普通の開発ではあまり使わなさそうだがとても便利
* [テックタッチ](https://techtouch.jp)は外部サービスにembedして機能するサービスのため、このような普段の開発では使わないような機能をふんだんに使って開発をしています

4日目は @terunuma による「[4Kモニタ環境で1年間Web開発してみた所感](https://qiita.com/terunuma/items/3eede74b6354da113d66)」です。
非同期処理（と、その周辺のこと）について調べたこと

Twitterにて、「エンジニアにとってAPIと非同期処理は要にして難関」というツイートをみたのでまず非同期処理について調べたことをど初心者ながらまとめたいと思います。


非同期処理の前に、非ではない***同期処理とは？***というところを書きます
## 同期処理
- プログラムが記述された通りに実行されるもの
- 1行のプログラムに時間がかかったら全体が重くなる
- その処理の1つに時間のかかる処理があると、その実行が完了するまで、次の行には進まない

#### なんでかとういうとJavaScriptはシングルスレッドで動いているから！
JavaScriptは一度に一つの仕事しかできない、私みたいなマルチタスク苦手マンらしいです


#非同期処理について

（例となるコードを書かずに日本語で私の理解している感じで書いてみます）

- コードは上から順番に実行されるので、途中に「5秒後にやる（完了する）」みたいなコードを書くと、その下のコードは5秒後に実行されてしまい、そうすると全体的に実行速度が遅くなってしまいます。

- しかし、callback関数のように、「5秒後にこれやっとって！私先行くわ！」とそのコードの実行が完了するのを待たず、すっ飛ばして次のコードを実行し、すっ飛ばされたものが実行完了したらそこのコードを実行させる、せっかち母ちゃんだと捉えています
（間違ってたら教えてください）



非同期処理について感覚的に理解したい方は私の記事なんか読まず[こちらのサイト](https://h5y1m141.github.io/step-up-javascript/doc/understanding_javascript/async_02.html)をお読みになった方が単純明快でわかりやすいと思いますので是非みてみてください



### 私の掴んだイメージ
（[このサイト](https://wa3.i-3-i.info/word1624.html)を読んで）

-  同期処理
    -  与えられた仕事を言われた通りにやる、（というかそれしかできない）

- 非同期処理
    - 言われなくても自分からできる仕事はないか探して実行する


なんかどっかで聞いたことのある響きですね




[Callbackについて参考にしたサイト](https://engineers.weddingpark.co.jp/?p=1087)

## Callback関数とは
[こちらのサイト](https://sbfl.net/blog/2019/02/08/javascript-callback-func/#i)（わかりやすかったです）によると、

- 別の関数に呼び出してもらうための関数
- 高階関数に渡すための関数
- 他の関数に引数として渡す関数のこと
- 「ある関数が呼び出された時、戻り値として、本来渡したい結果を返すのではなく、一度関数としては終了し（＝呼び出し元に戻る）、あとで『本来渡したかった値』を返せる状態になった時に、呼び出し元にその値を通知する」仕組み


ということです。




## コールバックの問題点

- 複雑な処理になると、途端にわかりにくくなるということ

▶︎読みやすく書くためには、***同期的に***書く！


- ネストが入れこいにならない
- 上から下に読んで実行フローが把握できる
- 非同期処理の結果得られた値を次の処理に引き回せる。
- try/catchで素直に例外を捕捉できる


同期的に書く代表的な方法は

- Promiseを使う方法
- Generatorを使う方法

がある

***


### Promiseについて
[こちらのサイト](https://engineers.weddingpark.co.jp/?p=1087)によると、Promiseは

- ES2015(ES6)から追加仕様として加わった

- 3つの状態(resolve, reject, done)を持つ


という特徴がある。

- promiseの基本的な考え方は、非同期的に動作する関数は、本来返したい戻り値の代わりに、『プロミスオブジェクト』という特別なオブジェクトを返しておき、（本来返したい）値を渡せる状態になったら、そのプロミスオブジェクトを通して、呼び出し元に戻す」というもの

- 非同期処理の成功や失敗を受け取り、処理を行うことができる
- 非同期処理の最終的な完了もしくは失敗を表すオブジェクト。
- JavaScriptやNode.jsにおいて、非同期処理のコールバック関数をエレガントに記述するための仕組み


### Promiseのメリット
- コールバック地獄を回避できる
- 時間のかかるAPIから取得した値を次の処理に渡せる
- 上から順に実行されるので可読性が上がる


また追加で調べたことがあれば、追記していきます！

[JavaScriptの「コールバック関数」とは一体なんなのか](https://sbfl.net/blog/2019/02/08/javascript-callback-func/#i)

[JavaScriptの非同期処理Promise・async awaitを学んでみた](https://engineers.weddingpark.co.jp/?p=1087)

[Promisen非同期処理について](https://qiita.com/fujisawaryohei/items/c51ced74af190e187f62)

[非同期処理ってどういうこと？JavaScriptで一から学ぶ](https://qiita.com/kiyodori/items/da434d169755cbb20447)
SRAM 23K256 > LSBが文字化けする > VccのラインにAnalog Discovery 2を接続すると文字化けが解消した

```txt:動作環境
Ubuntu 18.04 LTS
ボード: STM32F769 Discovery Kit (以下、STM32F769)
Zephyr 2.1.0-rc1
```

### 概要

- SRAM 23K256の読書きの実装をZephyr + STM32F769で実装していた
- 読書をした時に、LSBのビットが0になる
    - 例: 
        - 0x0F -> 0x0E
        - 0xF9 -> 0xF8

### 回路

- [表記] 23K256 <-> STM32F769 : 使用ケーブルの色
    - 1. CS <-100k-> CN13:8 (PJ3) : 白
    - 2. SO <-> CN9:5 (PB14) : オレンジ
    - 3. NC
    - 4. Vss <-> CN9:7 (GND) : 緑
    - 5. SI <-> CN9:4 (PB15) : 青
    - 6. SCK <-> CN9:6 (PA12) : 黄
    - 7. HOLD <-100k-> CN11:4 (3V3) : 赤
    - 8. Vcc <-> CN11:4 (3V3) : 赤

SCK, SO, SI, CSにAnalog Discovery 2のロジックモニタを接続し、モニタをしていた。

### Application note

- [AN1245: Recommended Usage of Microchip SPI Serial SRAM Devices](http://ww1.microchip.com/downloads/en/AppNotes/DS-01245A.pdf)

回路図において、HOLDとVccの間にdecoupling capacitorを付けている。
こちらの実装にはつけていなかった。

### 文字化けの解消

VccのラインをAnalog Discovery 2のロジックモニタに接続したところ、文字化けが解消した。
モニタで見る限りではVccは安定してるが、Application Noteにあるように、本来はdecoupling capacitorを入れる必要があるのだろうか?

下記はモニタした状況。

![Screenshot from 2019-12-02 21-25-52.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32870/408e15eb-367d-ebea-5b7e-a31762bb05cc.png)

VccのラインをAnalog Discovery 2から切断をすると、文字化けが再発した。

HOLDとVccの間にdecoupling capacitorをつける時と同じ効果が、VccにAnalog Discovery 2を接続することで発生したのだろうか？
Power Automate(旧Flow)を使ってTeamsへの情報連携を実践した

本投稿は、[Office365 AdventCalendar2019](https://adventar.org/calendars/3960) 2日目の投稿です。

# 何をしたか

Office365のPower Automate(旧Flow)を使って、Teamsへの情報連携を自動化しました。
連携したトリガーは、SharePointのアイテム編集。
もともと、Microsoft製品をほとんど使ったことがないので、備忘録兼ねて実施したポイントを残しておこうと思いました。

ちょうど、そのときにAdventCalendar2019の募集があったので登録。

※まだ慣れないので、Power Automateは以下Flowと記載しています。

## なぜ実施したか

人の手を使った情報共有を仕組み化したいと思いました。
一定のトリガーで、定形の通知を手打ちでメール送信することや、
特定のチャットツールにメッセージを投稿するなど、とにかく楽をできるところは楽にしたい。(してほしい)

もちろん、なんでもかんでも連携すれば良いというわけではなく、
あくまでも現場に馴染む仕組みにしたい。
そのためにも、まず仕組み化して改善する流れを作ることが大事だと思っています。
そこで、Flowを知りました。

## とにかくIntegration

個人的には、 [Integromat](https://www.integromat.com) や [Zapier](https://zapier.com/) などのインテグレーションサービスが大好き。
IFFTFとか、何かと連携したがり。業務外では、UI/UXがどんどん良くなるIntegromat一本。

## Flow

あるトリガーをきっかけに、指定したアクションを構築できます。
トリガーは、Office365のサービスを指定でき、アクションは、Office365はもちろんのこと、
外部サービスとの連携も可能(セキュリティポリシー等の設計は必要)です。

公式サイトは[コチラ](https://flow.microsoft.com/ja-jp/)。

## どうやったか

- Sandboxのチームとチャネルを作り、検証を行いました。
    - Flowのアクションはチームとチャネルを切り替えるだけなので、本番と検証の切り替えが楽。
- フローをチームで共有し、所有者以外もフローを編集できるようにしました。
    - チームフローの作り方は[コチラ](https://docs.microsoft.com/ja-jp/power-automate/create-team-flows)
    - 個人のフローを共有する手順なので、あとから所有者変更することができなかった。変更したい場合は、フローを複製して対応しました。(他の方法あれば知りたい)
- 判定ロジックを入れ、目的に合わせて通知先のTeamsチャネルを出し分けました。
    - 条件の追加方法は[コチラ](https://docs.microsoft.com/ja-jp/power-automate/add-condition)
    - 最近、プライベートチャネルが使えるようになったが、既存のチャネルをプライベートチャネルに変更できずちょっと残念でした。。。
- フローボットとして通知する際の固定文言を非表示にした。
    - フローボットの追加方法は [コチラ](https://docs.microsoft.com/ja-jp/power-automate/flows-teams)
    - フローボットのメッセージ文末に必ず所有者の連絡先が入ってしまいます。『'hogehoge used Microsoft Flow to automate this notification'』のような。
    不特定多数のチャネルに投稿することと、都合により所有者を出したくなかったのですが、
     [Forumでも削除できないという投稿あり](https://powerusers.microsoft.com/t5/Using-Flows/Hide-footer-used-Microsoft-Flow-to-automate-this-notification/td-p/348030)、諦めていた。
    - 検証中にメッセージ内にHTMLタグが出ることがあり、もしかして・・・と思い。
    メッセージはScriptで (JavaScriptのようでそうじゃない？) 記述できるので、文末に <!-- を入れて コメントにしたところ、非表示にできました。
    - 公式にはセキュリティ観点からも必要なメッセージなので消せないということなので、オススメはしないでおきます。

## 使ってみて

Flowだからこそ！というのは正直まだ見つけられていません。(個人的にはIntegromatのほうが使いやすい)
が、インテグレーションツールとして、外部サービス利用の制限があるような現場で、
且つOffice365を導入している場合は、必ず活用すべきサービスだと思う。

最初はとっつきにくいかもしれないですが、
試行錯誤するうちに現場に合った効率化が図れると確信しています。

## まとめ

もっとIntegrationを！
rspecのrequest specでうまくいかない時の原因調査のちょっとしたハック

# ユースケース

例えば、Rspecのrequest specを以下の様に書いていて、

```ruby:spec/request/user_spec.rb
it do
  get users_path

  expect(response.code).to eq 200
end
```

実行したら500で返ってきてちょっと調べても
???ってなった場合のハックです。

```shell
$ rspec spec/request/user_spec.rb

Failure/Error: expect(response.code).to eq 200
       
expected: 200
got: "500"

(compared using ==)
```

# 手順
## 1. リクエストの後に`binding.pry`する

```diff:spec/request/user_spec.rb
it do
  get users_path

+ bunding.pry
  expect(response.code).to eq 200
end
```

## 2. `response.body`を確認

`pry`コンソールが起動したら`response.body`を確認します。
以下の様にHTMLの文字列が返ってくると思います。

```ruby
$ response.body
=> "<!DOCTYPE html>\n<html>\n<head>\n    <title>NoMethodError at
```

## 3. `response.body`をファイル出力

そのままコンソールで以下を実行してhtmlファイルを作成します。

```ruby
$ File.write("tmp/debug.html", response.body)
$ exit # console抜けます
```

consoleを抜けて、ファイルが作成されているか確認します。

```shell
$ ls -la tmp/
debug.html
```

## 4. 作成したファイルをブラウザで表示
このHTMLファイルをブラウザで読み込むとrailsのエラーページが描画されるので多少わかりやすくなります。

```
$ open tmp/debug.html
```
LabBaseを紹介しながら、スカウト文作成補助機能を考える

# はじめに
株式会社POLという会社でエンジニアをやっている [@mejihabenatawa](https://twitter.com/mejihabenatawa) です。この記事は「POL Advent Calendar 2019」の2日目の記事です。POLが提供しているサービスであるLabBaseがどんなものなのかを紹介しながら、リスペクトしているLAPRASさんのスカウトメール補助機能をLabBaseでも考えてみたいと思います。

# LabBaseとは
まず、僕が所属する株式会社POLが提供するサービスであるLabBaseを手短に紹介させてください。

この[リンク](https://labbase.biz/)がわかりやすく説明されています。

LabBaseは、学生が自分のプロフィールや研究内容をサイトに載せて、企業がそのプロフィールを読んで興味を持った学生にスカウトをするという流れの就活サイトです。ダイレクトリクルーティングとか逆求人型と呼ばれています。同様なサービスはirootsさん、OfferBoxさん、などありますが、LabBaseは理系学生に特化しているところが他と違うところです。

ダイレクトリクルーティングサービスの特徴として、自社にマッチした学生に個別にアプローチすることができるという点がありますが、その分、人事の方の工数が増えてしまうというデメリットがあります。

また、通常のダイレクトリクルーティングサービスは返信率があまり高くないということもあるのですが、LabBaseでは平均返信率が30%近い(2019年12月現在)ということもあり、今回は「その人にあったスカウト文を考えて、送る」という部分の自動化にフォーカスして工数をへらすことを考えます。

# LAPRASさんのメール作成補助機能
さっそく、LAPRASさんのメール作成補助機能を見てみましょう。
こちらの[リンク](https://help.lapras.com/use-mail-assist)にまとまってますね。

以下のような手順のようです。
1. 言及する項目を選択する
2. メール作成補助の文章から文面を生成
3. メール内の{{proposed_content}}部分に挿入

# 良いスカウト文とは
いろんな形の良いスカウト文があると思いますが、[こちら](https://hr-tech-lab.lapras.com/knowhow/writing-scout-mail/)参考にします。
やっぱり、具体的に候補者のどの部分に注目したのかがわかると良いですよね。

今回は、
- 学生のどの部分に興味をもったのか
- どの部分が自社とマッチしているのか
が含まれた文章をよいスカウト文とします。

具体的にはこんな感じのスカウト文ですね。簡易版ということでご容赦ください。

<img width="904" alt="スクリーンショット 2019-12-02 21.01.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462106/21e6b0e0-6b0d-e0bc-b0b2-dc04e47bb107.png">

# LabBaseでメール作成補助機能をやるとしたら
LabBaseの学生のプロフィール項目はいろいろあるのですが、主な項目としては以下となります。

- 自己紹介
    - 自由記述
- 研究概要
    - 自由記述
- キーワード
    - 自由記述
- 希望職種
    - 選択式
    - 例）プログラマー、コンサルティング、マーケティング等
- 就職先に求めること
    - 選択式
    - 例）若いうちから裁量がもらえる、自分の専攻やスキルを活かせる等
- これからやってみたいこと
    - 自由記述

さっきあげた良いスカウト文を上記の項目の組み合わせから生成すればよさそう。

<img width="993" alt="スクリーンショット 2019-12-02 21.17.01.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/462106/40078e7d-85ee-e05b-0951-92620f6b8e92.png">

# 課題感
- 自己紹介、研究概要、これからやってみたいことのような項目は定形がないので、良い落とし込み方を考える。

# おわりに
今回は叩きで考えましたが、MVPとして、実装するのそんなに工数かからなそう。100%使えるスカウト文を生成するのは難しいが、この部分はこんな感じでどうですか？という提案用のテンプレートは作れそうなので、お客様にあててみて、フィードバックもらったら、よいと思いました！ということで実装しよう！

POLに興味を持った方、LabBaseに興味を持った方、お気軽にご連絡ください！
自然言語処理を始める準備

この記事では，自然言語処理をする時にはこういう作業をするんだよ，というアウトラインを紹介します．

# 形態素解析

形態素解析というのは，文章を単語に切り刻む作業です．品詞を調べたり，活用形を原形に戻します．例えば「身の丈に合った受験」というフレーズは，こう解析されます．
> 身の丈  名詞,一般,*,*,*,*,身の丈,ミノタケ,ミノタケ
> に      助詞,格助詞,一般,*,*,*,に,ニ,ニ
> 合っ    動詞,自立,*,*,五段・ワ行促音便,連用タ接続,合う,アッ,アッ
> た      助動詞,*,*,*,特殊・タ,基本形,た,タ,タ
> 受験    名詞,サ変接続,*,*,*,*,受験,ジュケン,ジュケン

形態素解析にはMeCabを使っています．
https://taku910.github.io/mecab/
これは広く使われていますが，「管理栄養士試験」を「管理/栄養/士/試験」にしてしまうなど必要以上に単語を分割するため，新語や専門用語には弱い面もあります．新語についてはmecab-ipadic-NEologdという頻繁に更新されている辞書が公開されており，MeCabの弱点を補うことができます．
https://github.com/neologd/mecab-ipadic-neologd
わたしはこれ以外に，解析対象の新語辞書をローカルに作成して，いろいろ追加して使っています．

精度の高さではJUMAN++も使ってみたいと思っています．
http://nlp.ist.i.kyoto-u.ac.jp/index.php?JUMAN++
「外国人参政権」をMeCabで処理すると，「外国/人参/政権」となりますが，JUMAN++だと「外国人/参政権」と解析します．機械学習で解析の腕を上げることができるのが特徴です．しかしJUMAN++をboostでbuildするところでエラーを解決できず今に至ります．このままMeCabを使うのかなあ．

# 前処理

HTMLタグがついているようなテキストの場合は，タグを除去する必要があります．それ以外にも形態素解析に掛ける前に前処理をしておかないと，頻出キーワードに①やⅰが登場したり，同じ意味の単語が別々に集計されたりしてしまいます．

## 顔文字

何気に鬼門です．顔文字研究は実は，数十年の歴史がある奥深いものです．興味のある人は，人工知能学会誌 Vol. 32 No. 3 (2017/05)で特集が組まれているので読んでみてください．
https://www.ai-gakkai.or.jp/vol32_no3/
わたしは顔文字辞書を適当に拾って，自分の辞書に付け加えてお茶を濁していますが，カバー率は高くありません．解析対象テキストによっては，顔文字と真っ向から取り組む必要があるかもしれません．

## 正規化

同じ意味の類似した単語が別々に集計されることを防ぐための処理です．
<例>
全角半角，大文字小文字(例: Qiita，QIITA，Ｑｉｉｔａ，ＱＩＩＴＡ)
記法を揃える(例: 高3,高３,高校三年生,高三,高三生)
略語を揃える(例: 広大，広島大，広島大学)

## 数字

数字の扱いについては，文書の種類にもよるかもしれません．通常の文脈であれば数値はすべて削除でもいいかもしれません．一方，スポーツの記録など，数字がキーワードになるものは，数字は数字として扱えた場合が良いでしょう．この場合に面倒なのは，MeCabは小数点の入った数字を，整数部・ピリオド・小数部と，それぞれ別の単語に区切ってしまいます．この場合，いっぺん形態素解析を実施した後に，数値を復元するような処理が必要になります．

## ストップワードの除去

ストップワードとは，「私」「です」のように，どこの文書にも顔を出すような単語のことです．わたしは，非自立語と，分析対象文書に登場頻度の高い単語を，ストップワードとして解析対象からはずしています．

## その他

化学式，数学や物理の公式，URL，商品コードや型番などは，字句解析対象外とした方がよいでしょう．例えばフェノール(C6H5OH)の化学式を単純にMeCabに与えるとこうなってしまいます．
```
C       名詞,一般,*,*,*,*,*
6       名詞,数,*,*,*,*,*
H       名詞,一般,*,*,*,*,*
5       名詞,数,*,*,*,*,*
OH      名詞,固有名詞,組織,*,*,*,*
```
これでは全然フェノールと認識できません．また，英文やプログラムコードを日本語と同等の手法で解析しても，意味のないことも多いでしょう．

# 文書のベクトル化
文書に含まれている単語を手掛かりに，文書をベクトル化します(Doc2Vec)．
似たような文書であれば，似たようなベクトルになるはずです．
Doc2Vecはgensimというライブラリに含まれています．
https://radimrehurek.com/gensim/

いったんベクトルにしてしまえば，似た文書をまとめることも可能です．
似た文書をまとめるには，後述するトピック分析という手法もあります．
トピック分析と，ベクトル化を併用する場合もあります．
このあたりもいろいろ試行錯誤したいところです．

# トピック分析
形態素解析済みの文書を自動的に分類し，指定したトピック数に分割するものです．
これまたgensimでできてしまいます．gensimおそるべし．

- LDA(Latent Dirichlet Allocation): 
文書群を与えると，指定したトピック群に話題を分割してくれます．問題はいくつに分割したらいいかがわかりにくい．perplexityとかcoherenceとか指標はありますが，使ってみてそれっぽい形になりませんでした．
Rのtopicmodelsパッケージを使ってperplexityとcoherenceを出してみました．
https://cran.r-project.org/web/packages/topicmodels/index.html
文書の量が多すぎたのか，グラフ一本描くのに二週間連続実行しても足りませんでした．
![coherence.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222398/a3fffdb1-2c82-24a5-acdb-f1215aa3fd18.png)
![perplexity.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/222398/407da8d8-8a67-0af6-3380-5fb9046de5f8.png)

- DTM(Dynamic Topic Model):
LDAを拡張し，SNS上の一定期間の話題を解析する場合のように，新しいニュースが飛び込んできて話題が遷移する時に使うモデルです．

- HDP(Hierarchical Dirichlet Process): 
LDAを拡張し，文書をいくつのトピックに分割すればいいかご託宣をくれるはず，なのですが，手持ちのデータセットを食わせたところ，トピックの半分以上が似たり寄ったりの内容で，妥当なトピック分けができていませんでした．

# BERT
Googleが昨年発表した自然言語処理モデルです。使ってみたいといいつつ，全然調べていません．これから勉強します．
テスターの評価基準を作ろう！

# この記事について
 - **対象：評価者**
 - **概要：テスター(テスト実行者/デバッガー)の評価基準について**
 - **読了までにかかる時間：3分**

# 基準策定の背景
### 背景その1：業務の多様化
テスターに求めるスキルは多様化しています。
聞くところによると昔はいわゆるモンキーテストがメインで
テスターはひたすらに時間をかけてテスト実行を行うことが主流だったそうです。

現代のテスト現場は昔に比べればおそらく洗練されており
テストケースなどを用いて計画的かつ効率的にテスト実行が進みます。

その業務の変化にともないテスターの業務も
テスト計画を立てたりテストケースを作ったりBTSと呼ばれるシステムにバグを登録したりと
テスト実行とその報告がメインだった昔よりもかなり多様化されています。

業務も多様化すれば評価基準も増えるのが自然の摂理で
煩雑化する基準を統一しようというのが一番の背景です。

### 背景その2：テスターの離脱防止
業務の多様化以外にも背景はあります。
現代は様々な仕事にあふれています。
加えて各自治体が定めている最低賃金の上昇にともない
どの仕事でも一定の収入を得ることが可能です。
(違ったらごめんなさい)

その結果としてテスターはテスト以外の仕事を常に選択肢として持っています。
なのでテスターが自身の評価が適切でない、と感じてしまうと
転職により離脱してしまうリスクが増えています。

そこで評価者/被評価者の双方が納得感のある適切な評価基準を作ろう
というのがもう一つの背景です。

### 背景その3：
ここまでは昔と今の違いが背景となってきました。
最後の背景は昔から変わらないテスターの特徴です

**「テスター(もしくはテスト関係者)は成果アピールが下手」**

これだけはいつまでも不変のようです。
だから会社側で基準を作り自己評価を促す、という背景もありますが
勘違いがないよう注意点を記載しておきます。

**※注意※
あくまで被評価者の自己評価とすり合わせる用の基準です
自分の成果を整理して評価者に説明するのも仕事の一つなので
「会社がこういう基準を作ってくれるのならちゃんと勝手に評価してくれる」
などの誤解はなきようよろしくお願いします**


# 定義した評価基準
### テスト周りの基準
まずは何はともあれテストの評価基準です。
我々はテストフェーズを軸に項目の洗い出しを行いました。

#### テスト計画
テスターが関わる機会の少ないフェーズですが
経験の長いテスターをアサインし初期からのバグの潰しこみを目論む場合や
テストマネージャーの業務を一部行う場合も発生しうるので対象のフェーズとしました。
評価基準としては「仕様レビュー」「テスト内容の定義」などを定義しました。

#### テスト設計
テストケース作成といったテスト環境構築がここに当たります。
ソフトウェアがどのようなプラットフォーム上にあるかにより検証観点が変わるなど
担当者の技量によりアウトプットの質が大きく変化するフェーズです。
評価基準としては「テストケースの作成」などを定義しました。

#### テスト実施
(読んで字のごとくなので割愛します)

#### テスト実行管理
テスト実施に一番近いフェーズで経験の長いテスターが担当する場合が多いです。
評価基準としては「テストの進捗管理」「不具合報告」などを定義しました。

#### テスト報告
名前だけだとテスト実行管理に近いように思いますが
日々の報告というよりはテスト全体の結果報告がこのフェーズに該当します。
評価基準としては「テスト結果のレポート」などを定義しました。


### テスト周り以外の基準
テスターの業務が多様化している影響でフェーズには分類されないものの
評価基準となる項目をテスト周り以外の基準として抽出しました。
具体的には高度な業務に関する基準として「品質面の改善提案」「費用処理」などを、
テスト実施の前提となる社会人スキルに関する基準として「関係者とのコミュニケーション」「勤怠」などを定義しました。


# 今後の展望
今はこのように基準を定義し今後の運用を予定しています。
今後の展望としては基準の精査も挙げられますがさらにその先の目標として下記を考えています。

**テスターのスキルアップ/キャリアアップにどうつなげるか**

法律により雇用期限が定められた昨今でどの会社も大きな課題になっていると思います。
その一端として表題の取り組みも各社で行われていると思いますので
意見があればぜひ頂きたいです。