OmniAICreator/Qiita-1.07M huggingface dataset:
BokehのグラフをWebサイトにシュッと埋め込む

JupyterでこねたグラフをWebサイト上に表示させたいというケースは案外多いのではないでしょうか。[Bokeh](https://bokeh.pydata.org/en/latest/)を使えばインタラクティブで見栄えの良いグラフを簡単にHTMLに埋め込む事が可能です。

# データ
Bokeh公式の[categorical scatter](https://bokeh.pydata.org/en/latest/docs/user_guide/categorical.html#scatters)プロットの例を使用していきます。

```py
from bokeh.io import output_notebook, show
from bokeh.models import ColumnDataSource
from bokeh.plotting import figure
from bokeh.sampledata.commits import data
from bokeh.transform import jitter

# output_file("bars.html")
output_notebook() # Jupyter notebook上で動かす場合

DAYS = ['Sun', 'Sat', 'Fri', 'Thu', 'Wed', 'Tue', 'Mon']

source = ColumnDataSource(data)

p = figure(plot_width=700, plot_height=400, y_range=DAYS, x_axis_type='datetime', title="Commits by Time of Day (US/Central) 2012—2016")
p.circle(x='time', y=jitter('day', width=0.45, range=p.y_range),  source=source, alpha=0.3)
p.xaxis[0].formatter.days = ['%Hh']
p.x_range.range_padding = 0
p.ygrid.grid_line_color = None

show(p)
```

![ss.png](https://qiita-image-store.s3.amazonaws.com/0/234940/f110e1aa-77a6-3ede-b300-fd38a9b9c451.png)

このグラフは2012-2016年におけるGitHub上のBokehリポジトリのコミット時刻(曜日毎)
を表しているそうです。ジッター散布図になっていてとても見易いですね。

# 埋め込み

プロットしたfigureオブジェクトを[bokeh.embed.components](https://bokeh.pydata.org/en/latest/docs/user_guide/embed.html#components)に渡すと、埋め込み用のHTMLタグが取得できます。

```py
from bokeh.embed import components
script, div = components(p)
```

```html:tags
<!-- script -->
<script type="text/javascript">
  (function() {
    // 略
  })();
</script>

<!-- div -->
<div class="bk-root">
    <div class="bk-plotdiv" id="a13d4bde-f3a8-4777-ab92-44973aaea397"></div>
</div>
```

また、↑のタグは単体では動作しません。Bokehを動作させるためのランタイムが必要となります。適当なところでロードしておきましょう。

```html
<!-- Bokeh 0.12.13を使用する場合。他バージョンの場合は要バージョン番号書き換え -->
<link href="https://cdn.pydata.org/bokeh/release/bokeh-0.12.13.min.css" rel="stylesheet" type="text/css">
<link href="https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.13.min.css" rel="stylesheet" type="text/css">
<link href="https://cdn.pydata.org/bokeh/release/bokeh-tables-0.12.13.min.css" rel="stylesheet" type="text/css">
<script src="https://cdn.pydata.org/bokeh/release/bokeh-0.12.13.min.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-widgets-0.12.13.min.js"></script>
<script src="https://cdn.pydata.org/bokeh/release/bokeh-tables-0.12.13.min.js"></script>
```

実際にこれらのタグをHTMLに埋め込んでみたサンプルです。CodePen便利...

<p data-height="571" data-theme-id="0" data-slug-hash="NybmNP" data-default-tab="html,result" data-user="yoku001" data-embed-version="2" data-pen-title="bokeh-embed" class="codepen">See the Pen <a href="https://codepen.io/yoku001/pen/NybmNP/">bokeh-embed</a> by yoku (<a href="https://codepen.io/yoku001">@yoku001</a>) on <a href="https://codepen.io">CodePen</a>.</p>
<script async src="https://production-assets.codepen.io/assets/embed/ei.js"></script>

**　Codepen**

Jupyter上と変わらず操作できる事が分かるかと思います。



# 参考
Embedding Plots and Apps - Bokeh
https://bokeh.pydata.org/en/latest/docs/user_guide/embed.html

Qiitaで記事にCodePenが埋め込めるようになりました - Qiita
https://qiita.com/Qiita/items/edae7417214c8e957f54
ToDictionaryメソッドで「キーが重複した」エラーが出た時の対策について

# 問題説明
 LINQにおける[ToDictionaryメソッド](https://msdn.microsoft.com/ja-jp/library/bb549277(v=vs.110).aspx)では、**キーが重複するとArgumentExceptionが発生します**。

```sample1.cs
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
	using Idol = KeyValuePair<string, int>;
	class Program
	{
		static void Main(string[] args) {
			// サンプルデータ
			var list = new [] {
				new Idol("島村卯月", 17),
				new Idol("櫻井桃華", 12),
				new Idol("相原雪乃", 22),
				new Idol("安部菜々", 17),	//ウーサミン！
			};
			// 　例外がスローされました: 'System.ArgumentException' (mscorlib.dll の中)
			// 　型 'System.ArgumentException' のハンドルされていない例外が mscorlib.dll で発生しました
			// 　同一のキーを含む項目が既に追加されています。
			var dic = list.ToDictionary(idol => idol.Value, idol => idol.Key);
		}
	}
}
```

　つまり、全部LINQで片付けようとすると、「事前にキーが重複しないか調べる」という**面倒な手間**が発生するわけです。
　もちろん次のように書けば問題ありませんが、やはり冗長に見えますよね？

```sample2.cs
using System.Collections.Generic;

namespace ConsoleApp1
{
	using Idol = KeyValuePair<string, int>;
	class Program
	{
		static void Main(string[] args) {
			// サンプルデータ
			var list = new [] {
				new Idol("島村卯月", 17),
				new Idol("櫻井桃華", 12),
				new Idol("相原雪乃", 22),
				new Idol("安部菜々", 17),	//ウーサミン！
			};
			// 無難な追加方法
			// dicの中身はKeyValuePair<int, string>型で
			// [0] {[17, 安部菜々]}
			// [1] {[12, 櫻井桃華]}
			// [2] {[22, 相原雪乃]}
			var dic = new Dictionary<int, string>();
			foreach(var idol in list) {
				dic[idol.Value] = idol.Key;
			}
		}
	}
}
```

## 解決策
　[GroupByメソッド](https://msdn.microsoft.com/ja-jp/library/bb534304(v=vs.110).aspx)を使えば良いのでした。これでキー毎にグルーピングした後、それぞれについて[Lastメソッド](https://msdn.microsoft.com/ja-jp/library/bb358775(v=vs.110).aspx)を使えば、上記の`sample2.cs`と同じ結果が返ってきます。

```sample3.cs
using System;
using System.Collections.Generic;
using System.Linq;

namespace ConsoleApp1
{
	using Idol = KeyValuePair<string, int>;
	class Program
	{
		static void Main(string[] args) {
			// サンプルデータ
			var list = new [] {
				new Idol("島村卯月", 17),
				new Idol("櫻井桃華", 12),
				new Idol("相原雪乃", 22),
				new Idol("安部菜々", 17),	//ウーサミン！
			};
			// あくまでLINQによる解決策
			// dicの中身はKeyValuePair<int, string>型で
			// [0] {[17, 安部菜々]}
			// [1] {[12, 櫻井桃華]}
			// [2] {[22, 相原雪乃]}
			var dic = list.GroupBy(idol => idol.Value)
				.ToDictionary(idolGroup => idolGroup.Last(), idolGroup => idolGroup.Key);
		}
	}
}
```
ビルボード


ex.オブジェクトAをカメラの方に向かせたい場合
オブジェクトAに`Billboard.cs`を、`targetTrans`にカメラをアタッチ。

完全にカメラの方に向かせると表示がおかしくなる場合は、参照する座標の一部を切る。特にy座標が多い。

```csharp:Billboard.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Billboard : MonoBehaviour {

	[SerializeField] Transform targetTrans;
	[SerializeField] bool isTargetXValid = true;
	[SerializeField] bool isTargetYValid = true;
	[SerializeField] bool isTargetZValid = true;

	Transform objTrans;

	void Start () {
		objTrans = gameObject.transform;
	}
	
	void Update () {
		objTrans.LookAt(new Vector3(
			isTargetXValid ? targetTrans.position.x : objTrans.position.x,
			isTargetYValid ? targetTrans.position.y : objTrans.position.y,
			isTargetZValid ? targetTrans.position.z : objTrans.position.z
		));
	}
}
```
「見える化2.0」を考える - その1

#背景
IoTの本格的な各種現場への普及が始まる機運を見せているが、
まずは「見える化」してみようというプロジェクトが圧倒的に多いのでは無いか？

そもそも、見える化とはトヨタ用語（らしく）、PDCAサイクルを回す上で、現場が見えて無くてどうすんの？という所から始まっている（きっと）。

ここでいう「見える化」とは、ICTを使っての見える化という事になるわけだが、
一口に「ICTを活用した見える化」といっても、実際には80年代から既に始まっている枯れたシステムであり、ここ数十年にわたり脈々と「設備管理システムの一種や亜種」として根付いていると思われる。

#見える化2.0の要件
##前段
「見える化のVer2.0」とは、ICTによる見える化の進化版を指すことになるので、この要件を考える上で重要なのは、
`IoTテクノロジがどのように活用されているか？`
に尽きるのではないか。
しかし、テクノロジとしてはIoTが活用されていると言っても、その設計思想や出来た実装には大きな隔たりが出てきている。

「見える化2.0」に必要と考えられる要件を書き出してみたい。
##要件1：永続的なデータソース管理
事業ドメインや管理対象内の全てのモノが一意のIDで永続的にキチンと管理されている必要ある。
###残念な事例
####データの永続性の毀損
また、IoTのデータを保存する先も、RDBMSといった例も少なくなく、とある企業では
>エネルギーデータの生値保存は、72時間と決まっていて、72時間を過ぎると30分単位でAVEした値しか取得できなくなる。三連休に入る際は、泣く泣く休出して生値を吸い上げている

という笑えるに笑えない話がある。これは、大量に収集されるセンサデータをRDBMSに貯め込む故、データ量の削減とDB内の整理の為に「生値を削除」して、当面エネルギー管理に必要なAVEった値のみを永続保存しているからに他ならない。
####データの一意性の毀損
現場に設置されているセンサや計測機器が変わったからといって、測定対象が変わる訳ではないのに、同じデータソースとして扱えないという例をよく耳にする。
これでは
>折角データをKVS的なDBに突っ込んでいたとしても「残念極まりない」
としか言いようがない。

これはデータソースとしてのIDが、センサや計測起点で管理されいているため、データの一意性が失われてしまっている事を意味する。

###要件1を満たすために
これらの残念な事例やアルアルから考えるに、
・IoTデータの保存先として「RDBMSのからの脱却」
　　⇒　ソフトウェアの問題
・IoTデータの管理方法の改善
　　⇒　エンジニアリングの問題
は、避けて通れないと結論づけられる。


～　つづく　～
GravatarをWebページのfaviconに自動適用するやつ

# Gravatarとは
いろんなWebサービス(Qiitaもその1つ)で共通のアイコンを使えるようにするもので、
画像をアップロードすると簡単に取得できるようになっています。

# これを作った経緯
1. Apacheとかでfaviconをアイコンにしたい。手動で設定したらGravatarじゃなくていいじゃん?
1. いちいちlinkタグを書きたくない。多分メンテナンス性が下がっちゃう
1. gravatarに.htaccessとかで転送したらうまくいかない。(pngですし外部ですし)
1. よし、PHPで頑張って作ろう!

# やってること
- faviconはPHPに転送しちゃう。
- PHPはGravatarを見に行って、更新されてたら持ってきてicoに変換。
- アクセスにIf-Modified-Sinceがついてて、まだ変更がなかったら304返しておわり。
- 新しいアイコン/クライアントならアイコンのデータを送ってあげる

# 用意するもの
- Webサーバー(Apacheがおすすめ。というか他は動作確認してないだけ)
- PHPの実行環境(7.1でテスト済)
- Gravatarのアイコン
- ある程度の知識(コピペで終わるけどね)

# 手順
## アイコン画像生成ライブラリを入れる
[PHP ICO - The PHP ICO Generator](https://github.com/chrisbliss18/php-ico)
ドキュメントルート/lib/にphpファイルだけ入れる、でOK.

## ドキュメントルートにfavicon.phpを置いてコードを書く。
場所を変えたいなら以降は適宜書き換える必要あり。

```php:favicon.php
<?php 

$force=false;
$force=isset($_GET['force']);

if(file_exists($_SERVER['DOCUMENT_ROOT'] . "/favicon.ico")){//faviconあるかな?
    $found=true;
    $last_modified_num = filemtime($_SERVER['DOCUMENT_ROOT'] . "/favicon.ico");
    $last_modified=gmdate( "D, d M Y H:i:s T", $last_modified_num);
}else{
    $found=false;//無いのかよ
    $last_modified=gmdate( "D, d M Y H:i:s T", time());//とりあえず今の時間渡しとけばいいかな
}


// Gravatarを見に行って
$url = "https://secure.gravatar.com/avatar/[自分のid(メールアドレスのmd5)]?size=256";
$ch = curl_init ();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt ( $ch, CURLOPT_RETURNTRANSFER, true ); //これをしないと結果をブラウザへ送ってしまう

if($found && !$force){
    curl_setopt($ch, CURLOPT_HTTPHEADER,['If-Modified-Since: '.$last_modified]);
}
$r = curl_exec ($ch);
$info = curl_getinfo ($ch);
$status_code = $info['http_code'];

if(!$found||$force||$status_code==200){ //データが新しければ取得して保存!
    file_put_contents ("favicon.png", $r);
    require_once $_SERVER['DOCUMENT_ROOT'] . '/lib/class-php-ico.php';
    $ico_lib = new PHP_ICO();
    // 画像指定
    $ico_lib->add_image('favicon.png');
    // ファビコン生成/保存
    $ico_lib->save_ico('favicon.ico');
}

// さてブラウザへデータを返そうか

header("Last-Modified: {$last_modified}");

$if_modified_since = filter_input( INPUT_SERVER, 'HTTP_IF_MODIFIED_SINCE' );
if(!$found || $force || strtotime($if_modified_since)<$last_modified_num){ // ブラウザからの要求より新しいデータか?
    // header( "Content-Type: image/x-icon;" );
    header( "Content-Type: image/vnd.microsoft.icon;" );
    echo file_get_contents($_SERVER['DOCUMENT_ROOT'] . "/favicon.ico");
}else{
    header( 'HTTP', true, 304 );
    exit;
}
?>
```

## .htaccessの編集
あるなら追記、無いなら新規作成しましょう

```apache:.htaccess
RewriteEngine On
RewriteRule favicon\.ico$ favicon.php
```

## 権限の確認
ファイルを操作するときに権限がないと怒られるのでサーバーがいじれるようにする

以上。簡単でしょ?

# 追加説明
新しめのfaviconがもう置いてあるから更新されない!って言う人はfavicon.ico?forceにアクセスすると更新します。
セキュリティ的にマズそうな気がしたらコメントアウトしてください。

# ライセンス(?)
放棄します。こんなソースくれてやる!
自由に使って下さい。
秀丸エディタ > 設定 > ファイルの自動再読み込み (git stash dropなどによる変更時)

```txt:動作環境
Windows 10 Pro (64bit) Fall Creators Update適用済
秀丸エディタ Version 8.77
SmartGit v17.1.4 #11213
```

秀丸エディタ + SmartGitで作業をしている。

作業内容をgit stash dropで破棄したとき、秀丸の方ではgit stash drop前の情報のままであることに気づいた。

下記を設定する。

- その他 > 動作環境 > ファイル > 排他制御2
    - 他のソフトでファイルが書き換えられていないか確認
        - [x] ファイルのタイムスタンプを時々チェックする
        - ...
        - [x] 自動で再読み込みする (U)


### 追記 (2020-10-28) > IDEとの編集合戦の回避

この設定をすることで「IDEとの編集合戦」を回避できる。
（IDE上で編集した分が秀丸で反映されず、秀丸でさらに編集したときにIDEの編集が消える。）
コンポーネント設計について

CodeGridさんの記事をまとめてみました。
[いまどきのコンポーネント設計をめぐる座談会](https://app.codegrid.net/series/2017-component-talk)


## コンポーネントとは
Vue.js、React、Angularなど主にWebアプリケーション開発でページを構成する各部品のことを指す。

## コンポーネント感の相違って！？

- 見た目のコンポーネント
- データの流れに注目したコンポーネント

### 見た目のコンポーネントとは

デザインの見た目でコンポーネントを切ったもの

### データの流れに注目したコンポーネントとは

1. 大きなオブジェクトをひとつのコンポーネントにした場合、そのコンポーネントは全部のAPIを叩かないといけない。
2. 小さなコンポーネントを複数含むコンポーネント設計すれば、大きなコンポーネントで取得したデータを小さなコンポーネントに流してあればいい。そうすると小さなコンポーネントはもらったデータの処理だけをすればよくなる。
3. その全体のデータの流れを考えた上でコンポーネントを切ったもの

## コンポーネントを作るときに苦労したところ

- 見た目のコンポーネントに依存してつくったら、大きすぎてデバッグがしにくかった。
- でっかいテーブル（表）をひとつのコンポーネントにしたら、そのテーブルに流されていたデータが膨大になってしまったので、1列ずつデバッグできるように設計すべきだった。(変更するには大手術)


## 開発するときの注意点

### HTML/CSSが先か、JSが先か
 - jsを書くエンジニアがコンポーネントを切ってから、HTML/CSSの作業をした方が効率的なときがある。
 - jsとHTML/CSSどちらかの作業が終わってからの作業の場合、「cssを今から直すのはつらい」「jsのデータの持ち方を変えたい」などの出戻りが発生

### cssの書き方が従来とは違う!？
 - コンポーネントごとにスタイルを書いていく。
 - そこに書いたものはほかのコンポーネントに影響しないし、使い回しもしない。
 - 同じボタンであっても、コンポーネントが違えば、それはそれ。
 - 使われているかどうか判断できないものが増えていくのを避けるのが目的

#### CSS in JSを使う
CSS in JSとはJSでスタイルを当てること。
CSSをそのまま書いて読み込んでいると、どこに干渉するかわからないので使う。
そこにしかスタイルを当てないというのを実装でなんとか実現しようとしたのが、CSS in JS。
↓
フレームワークに依存するので2、3年後にはレガシーになって管理が難しくなる可能性あり

#### 従来の書きがやっぱり良い
外部cssファイルを読み込んでモジュールごとにスタイルして流用、継承した方が効率的。

#### 結論(？)
ページレイアウトのベースとなるスタイルは外部cssで書き、コンポーネント単体でのスタイルはコンポーネントごとで書いていく。

※プロジェクトによりけり(?)

### 作業者全員が共通認識を持つことが大事

- デザイナー、マークアップエンジニア、フロントエンドエンジニアの3者がコンポーネントの単位を理解していると開発が進む。
- コンポーネント思考っていうのをエンジニア側は持っている。
- かたや、デザイナーはビジュアルデザインとして物事を見ている。


### ワークショップを行うことで3者の認識のすり合わせができる。

コンポーネントの粒度をお互い意識するために3者でワークショップを行う。

例) 完成した画面を紙に出力して、ハサミを持って「コンポーネントってどこですかね？」と確認。そこで切った理由をお互いに説明し合う。
↓
エンジニアが、これは確かにこういう見た目だけど、この下の部分は同じデータカラムに入っていて、データベース上はここはセットだし、持ち運びやすさも考えると、これをセットにするほうがいいんですよっていう話をする。

するとデザイナーはそれを知らないだけだったので、じゃあ、それを元にコンポーネント再設計してみますねという話になって、前へ進む。

### UIはデザイナーだけの作業ではない。

- コンポーネント設計を推し進めるとUIはデザイナーだけの作業ではなく、エンジニアもUIを意識する必要が出てくる。
- UIはプロジェクトメンバー全員でつくっていくものである。

## 参考ページ

- [いまどきのコンポーネント設計をめぐる座談会](https://app.codegrid.net/series/2017-component-talk)
- [Reactと一緒に使う時のCSS in JSのライブラリ選定とか所感とか](https://qiita.com/inuscript/items/0cfdb1a0c1d172e27694)
領域拡張法をpythonで書いてみた

画像の領域分割方法には様々な手法がある
そのなかでも簡単なアルゴリズムである**領域拡張法**をライブラリ１行でポチー！
ではなく実際に書いてみることにした

# 領域分割方とは

>目的：
あらかじめ設定した画素値に関する条件を満足する，空間的
に連続する領域をひとかたまりの領域として認識すること．
方法：
① 画素値に関する条件を満足する画素をマニュアルにより決定し，
ひとつのラベルをつける（種子点：seed point）．
② 近傍の点で条件を満足する画素に同一のラベルをつける．
③ ②を繰り返す．
http://www.cfme.chiba-u.jp/~haneishi/class/7_segmentation.pdf

というわけで代表点をきめたらそれに近い画素値を持っている点の集合をジリジリ広がりながら探索していく方法らしい

# 実装結果
にゃんちゅうをinputとして用意した
![input.jpg](https://qiita-image-store.s3.amazonaws.com/0/236189/38179c34-774d-8324-55c1-f21e802f0f3d.jpeg)
これを領域分割した結果
![result.jpg](https://qiita-image-store.s3.amazonaws.com/0/236189/051ea743-fa02-37b5-2970-f8c4a627d49a.jpeg)

このようにある程度の制度で領域を見つけている


# 実装
jupyter notebook を用いたが、別にふつうにpythonファイルを作っても何も変わったことはしてない
画像のopenや書き出しについてはcv2を用いている

```python:ResionGrowing.py
#coding:utf-8
import numpy as np
import cv2


#画像の読み込み
test = cv2.imread("./Img/input.jpg", cv2.IMREAD_COLOR)#BGRなので気をつける
gray_test = cv2.imread("./Img/input.jpg",cv2.IMREAD_GRAYSCALE)
width = test.shape[0]
height = test.shape[1] 
frag = np.zeros(gray_test.shape)#領域分割フラグ

#画像の書き出し
cv2.imwrite('./Img/test.jpg', test)
cv2.imwrite('./Img/gray_test.jpg',gray_test)

#初期地点
start_position = [int(width/2),int(height/2)]
threshold = 127#分割領域の閾値
pix = gray_test[start_position[0]][start_position[1]]#グループ化させる領域の画素値

for y in range(0,int(height/2)):
    for x in range(0,int(width/2)):#上下
        r = gray_test[start_position[0]+x][start_position[1]+y]
        l = gray_test[start_position[0]-x][start_position[1]+y]
        r2 = gray_test[start_position[0]+x][start_position[1]-y]
        l2= gray_test[start_position[0]-x][start_position[1]-y]
        if ( max(pix,r) -min(pix,r) <= threshold):
            frag[start_position[0]+x][start_position[1]+y] = 1
        if ( max(pix,l) -min(pix,l) <= threshold):
            frag[start_position[0]-x][start_position[1]+y] = 1
        if ( max(pix,r2) -min(pix,r2) <= threshold):
            frag[start_position[0]+x][start_position[1]-y] = 1
        if ( max(pix,l2) -min(pix,l2) <= threshold):
            frag[start_position[0]-x][start_position[1]-y] = 1

frag *= 255
cv2.imwrite('./Img/result.jpg', frag)
```


# 実装のコメント
探索についてはスタート地点から上下に探索して、
そのあと横にスライド、また上下に探索みたいな方法で実装してみた

ちなみに終了端点のあたりはかなり適当で、領域は端っこまで続かないでしょもう、、、というやる気ないコードなのでその辺りを気にする方は終了条件をしっかりいれておくとよい(実際楽だと思うよ)

また、領域の閾値については0(黒)-255(白)の中間である１２７を用いているが、特に意味はない
ここを調整すると領域がぼんやりしたり、シャープになったりするのでいじってください
スプラトゥーンでbundlerを理解しながらubuntu16.04上のRails環境を整える

先日ubuntu16.04でRails環境を整える機会があったので参考までに。
これまでで何回も環境設定をしましたが初めてrails環境を整えたときは、どれがどれだか何がどんなことをしてくれるのかよくわからずふぎゃーとなったのでその辺も書いて当時の自分のような人に役立ったらうれしいです。個人的にはbundlerあたりが一番理解に苦しんだのでそこも丁寧にやっていきたいと思います。

#環境
ubuntu16.04 (Docker上)

# やること
1. rbenvをinstall
2. nodejsをinstall 
3. bundleでgem管理
4. railsをinstall

の4ステップです。

# rbenvをインストール
**rbenvの用途:** rbenvをインストールすることでrubyのバージョンを管理できる

```shell:rbenvをinstallしていく
$ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
// パフォーマンスをよくするためにoptionとして実行したらいいらしい(うまくいかなくても次に進んでよし)
$ cd ~/.rbenv && src/configure && make -C src
// パスを通していく
$ echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
// ここで一度環境変数を読み込ませる
$ exec $SHELL
// rbenvをinitする
$ ~/.rbenv/bin/rbenv init
$ echo 'eval "$(rbenv init -)"' >> ~/.bashrc
$ exec $SHELL
// これを実行することで各種rubyのバージョンを取得することができるようになる
$ git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
```

ここまででrbenvは無事入ったはずです。試してみましょう。

```shell:rbenvが無事入ったか確認
// rbenvでインストールできるrubyのバージョンを一覧表示する
$ rbenv install -l
1.8.5-p52
  1.8.5-p113
  1.8.5-p114
  1.8.5-p115
  1.8.5-p231
(以下略)
```

とずらずらとインストールできるバージョンが表示されてきたらOK。
今回は2.4.3をインストールしてみます。

ただインストールするために必要なライブラリが整ってない可能性があるのでそれらをまずインストールしていきます。

```shell:ruby2.4.3をインストールする
$ sudo apt update
$ sudo apt install -y wget bzip2 build-essential libffi-dev libsqlite3-dev tzdata libssl-dev libreadline-dev zlib1g-dev
$ rbenv install 2.4.3
```

無事入ったか確認してみます。

```shell:2.4.3が無事入ったかの確認
// rbenv内で管理できているrubyのバージョンを一覧表示
// 2.4.3の数字がいたらうまくインストールできてる
$ rbenv versions
2.4.3
// rails用のディレクトリを作る
$ mkdir rails && cd rails
// railsディレクトリ内でruby2.4.3環境を設定する
$ rbenv local 2.4.3
// 以下のように表示されたらOK
$ ruby -v
ruby 2.4.3p205 (2017-12-14 revision 61247) [x86_64-linux]
```

ここまででrbenvを用いてrubyのバージョンを管理できるようになりました。

# nodejsのインストール
**nodejsの用途** : Railsはnodejsが必要なのでインストールしていきます。

```shell:nodejsをinstallしていく
$ curl -sL https://deb.nodesource.com/setup_6.x | sudo -E bash -
$ sudo apt-get install nodejs
$ sudo apt-get install npm
```
以上でOKです。

# bundlerでgemを環境ごとに管理
**bundlerの用途**: rubyの場合主にgemというもので各種便利なパッケージをinstallしたりuninstallしたりします。bundlerとはさらにそのgemを管理するものです。
最初はそんなよく分からんもん使いたくない、環境整えるのが面倒になる、と感じますが使ったらすごく便利です。
スプラトゥーンに例えたらもう少し覚えやすいかもしれないです。


| Rails | スプラトゥーン |
|:-:|:-:|
|  プロジェクト | ギア  |
|  パッケージ |  ギアパワー |
| gem  |  スーパーサザエ |
|  bundler  | スパイキー |


とでも覚えておいてください。bundlerすごい。

```shell:bundlerのインストール
// rbenv環境内でbundlerをインストールするようにrbenv execであえて明示する
$ rbenv exec gem install bundler
$ bundle init
```

最後のコマンドを叩くといまいるディレクトリのなかでGemfileと言うファイルが作られます。このGemfileと言うものを使って、gemを管理していきます。
そのファイルのなかでは `#gem rails`　と書かれた一行があるはずなので、
`#gem rails` -> `gem rails`　と書き直します。

```shell:Railsをinstallする
// Gemfile内で管理されているgemをインストールしていく
$ bundle install --path=vendor/bundle
```

ここまでできたら後は適当なRailsプロジェクトを作成するだけです。

```shell:Railsプロジェクトsample_appを作る
$ bundle exec rails new test_app && cd test_app
// railsサーバーをたてる
$ bundle exec rails s
```

ブラウザで`https://localhost:3000`を検索してみて
![Screenshot from 2018-02-08 18-38-02.png](https://qiita-image-store.s3.amazonaws.com/0/192385/e1427637-eac8-bb81-fec8-1d7c75315949.png)

と出たらうまくいってます。お疲れさまでした!
ESLintでjavascript構文チェック！

## ESLintとは

- javascriptの構文をチェックするツールです。
- 人によって異なるjavascriptの書き方もチーム開発では揃っていないと気持ちが悪いし、バグのもとに…。
- 構文チェックツール「ESLint」を導入すれば、スタイルガイドに合わせたチーム開発ができる。

>1. コードスタイルの一貫性を保つ
 - スペース、インデント、ブレース（波括弧）の配置などのコードスタイルの問題をリンターで確認できる。
 - チームの同意を得たコーディングスタイルを設定ファイルに記述しておけば自動的に確認できる
2. 潜在的エラーや良くないパターンを見分けられる
 - リンターは、重複変数、到達不能コード、無効な正規表現の可能性があるエラーを発見するため、より高度な確認に使用できる。リンターの警告で、ランタイム前にエラーを修正できる
3. 品質を強化する
 - 人とは常に手抜きをしたくなるものなので、プロジェクトの特定のスタイルガイドに従うときはツールで強化することが大切だ。
 - 作成手順にリンティングツールが備わっている場合は、プロジェクトの開始を止めるか、未修整エラーがあるならリポジトリにコミットする
4. 時間を節約する
 - 上の3点から得られる主なメリットはリンターが開発中の手間を省くことだ。
 - ブレースについて同僚と見当違いの議論に貴重な時間を費やすことなく、初期段階で1～2個のバグを発見する


## ESLintを導入しよう
### npmでeslintをinstall

```
$ npm install --save-dev eslint
$ export PATH=$PATH:./node_modules/.bin
$ eslint -v
```

### eslintの初期設定

```
$ eslint --init

// 質問に対する回答を求められる
? How would you like to configure ESLint? Use a popular style guide
? Which style guide do you want to follow? Airbnb
? Do you use React? Yes
? What format do you want your config file to be in? JavaScript
```

``` javascript:package.json
{
  "devDependencies": {
    "eslint": "^4.15.0",
    "eslint-config-airbnb": "^16.1.0",
    "eslint-plugin-import": "^2.8.0",
    "eslint-plugin-jsx-a11y": "^6.0.3",
    "eslint-plugin-react": "^7.5.1",
  },
}
```

``` javascript:.eslintrc.js
module.exports = {
    "extends": "airbnb"
};
```

### Atomに導入
テキストエディタにlinterをインストール

- linter
- linter-eslint


## 導入結果

行の左の「赤い●」は、その行にjavascript構文エラーがありのマーク(jsは一応動く)

<img width="297" alt="01.png" src="https://qiita-image-store.s3.amazonaws.com/0/132855/d22dd10b-f64f-5c87-c065-69f4f091e4f4.png">


jsが動かない致命的なエラーの場合は、行の左に1点のみ「赤い●」が付く
<img width="273" alt="02.png" src="https://qiita-image-store.s3.amazonaws.com/0/132855/be2d7322-49ec-5f24-c89f-e23cb7bc352d.png">



## 参考サイト

- [JSプログラマーのイラッとする「クセ」はESLintを導入して対処しよう](https://www.webprofessional.jp/up-and-running-with-eslint-the-pluggable-javascript-linter/)
- [AtomにESLint導入した](https://qiita.com/pechefamille/items/40966a0c78846f4053c9)
S3に画像を再アップロードするバッチ処理

## 対象とする読者
railsを用いたプロダクトでgem 'carrierwave'によって画像をアップロードしている。
aws-cliを用いても画像アップロードが可能だが、Minimagickによるリサイズや圧縮プロセスを踏みたい。


## 実現したいこと
hoge_uploader.rbに任意のversionを追加した際にS3に再アップロードを行いたい。
rakeタスクで自動化したい。

## やったこと

```
$ rake:regenerate_images[任意のid,任意の数字]
```
上記コマンドを実行した際に指定のidで処理が始まり、find_eachによる一回あたりの取得件数を指定の件数に絞って再度アップロードし直す。

[carrierwave](https://github.com/carrierwaveuploader/carrierwave/wiki/How-to:-Recreate-and-reprocess-your-files-stored-on-fog)のWikiを参考にrakeタスクを作成。

```ruby:regenerate_images.rake
desc 'recreate_and_reprocess_files_stored_on_fog'
  task :item, [:id, :max_num] => :environment do |task, args|
    ItemImage.find_each(start: args[:id], batch_size: args[:max_num].to_i) do |item|
      p "id: #{item.id}"
      begin
        item.image.cache_stored_file!
        item.image.retrieve_from_cache!(item.image.cache_name)
        item.image.recreate_versions!
        item.save!
      rescue => e
        Rails.logger.error  "ERROR: YourModel: #{item.id} -> #{e.to_s} #{e.backtrace}"
      end
    end
  end
end
```

## 配慮した点
- rakeタスクが何らかの影響で落ちた場合、再度id=１から実行するのは面倒だったので、引数に指定出来るように実装した
- find_eachはorderやlimitが無視されるため、startやbatch_sizeといったオプションを使用

## 不明点
`batch_size: args[:max_num].to_i`
上記の `.to_i` が無いと下記のようなエラーを吐く。
`ArgumentError: comparison of Fixnum with String failed`

start: args[:id]単体の場合はエラーは発生せず、batch_size: args[:max_num]が記述されている場合はエラーになる。
to_iメソッド加えると大丈夫だった。


## 参考記事

[Rake タスクの作り方（引数を複数設定してみる）](https://qiita.com/yoshiokaCB/items/c97ba878469701c3d99b)
[APIdock>method>find_each](https://apidock.com/rails/ActiveRecord/Batches/find_each)
[[Ruby]Hashにmapで値を変換したHashを作成する]
(https://qiita.com/yukimura1227/items/5838c6e9eac9eda90030)
PhpStorm でショートカットからコメントアウトした時に"// hoge"みたいに1文字分スペースを入れる設定

PhpStorm でショートカット(cmd + `/`)からコメントアウトした時に `// hoge` みたいに1文字分スペースを入れる設定。

のメモ。

Open: Preferences -> Editor -> Code Stype -> PHP -> Code Generation tab.

"Line comment at first column" のチェックを外して、 "Add a space at comment start" にチェックを入れる。

※ Preferences から辿るより `Add a space at comment start` で文字列検索がおすすめ。
スクレイピングし特定の要素をファイルに書き出す



##やること
jsoupというライブラリを使ってスクレイピングし、特定の要素をファイルに書き出す。その際JSON形式で書き出す。（eclipseで動作）

##背景
サービスの連携先を調べていたら、幸運にも（？）たくさん連携先があるサービスに出会いました。これらのサービス名をコピーしてJSON形式`{"Name":"Service_name","Connectivity":"1"}`に入れていくのは大変だと思い作成。
今回扱うサービスは[Highriseの連携先](https://highrisehq.com/extras/)。

##使うライブラリ
**jsoup**というHTMLをスクレイピングができるライブラリを使う。
公式ページ:http://jsoup.org/
ダウンロードページ：http://jsoup.org/download

##Webページのどのタグに欲しい情報があるか確認
HTMLを見てみると、、
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178136/352538a0-fd76-cf83-0968-90845c5c6e64.png)
となり欲しい情報（サービスの名前）は**appクラスのh４タグのaタグ**の中であると確認できました。

##Eclipseにダウンロードした.jarファイルを追加
[package]を右クリック→[ビルドパス]→[ビルドパスの構成]をクリック
以下の画面が表示される。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178136/d9f1bfd4-53c8-cacb-0ede-713a6eb69de3.png)
[外部JARの追加]をクリックし、先ほどダウンロードしたjsoupの.jarファイルを選択
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178136/af3fb966-cbb4-69c0-a9a0-96f0ec976d9c.png)
その後[適用して閉じる]をクリック。これで追加完了。
"参照ライブラリー"というものができていてその中に追加された.jarファイルが入っていることを確認。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178136/fcc1a850-ccad-2d72-d7b7-e1a3a424fbf8.png)

##コードの作成
###jsoupをインポート
```Main.java
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
```
上記を記述。

###ファイル操作を書いていく
例外処理など忘れずに。

```Main.java
import java.io.FileWriter;
import java.io.IOException;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

public class Main {

　public static void main(String[] args) {
　　FileWriter fw = null;
  try {
    //ファイルの書き込み
  }catch(IOException e) {
　　　　　　　　　System.out.println("ファイル書き込みエラーです");
　　　　}finally {//ファイルを閉じる
　　　　　　　　if(fw != null) {
　　　　　　　　　　　　try {
　　　　　　　　　　　　if(fw != null) {
　　　　　　　　　　　　　　fw.close();
　　　　　　　　　　　　}
　　　　　　　　　　　　}catch(IOException e2) {}
　　　　　　　　}
　　　　　}
　}
}
```

###ファイルの書き込み部分にコードを書いていく
```Main.java
　　　　　　　　　　　　　　　　　　　　　　　　//ファイルを開く
			fw = new FileWriter("[書き込むファイルのパス]", true);
			//ファイルに書き込む
			Document document = Jsoup.connect("https://highrisehq.com/extras/").get();
			Elements elements = document.select(".app h4 a");
			for (Element element : elements) {
				String name = element.text();
				fw.write("{\"Name\":\"" + name + "\",\"Connectivity\":\"1\"}\n");
				System.out.println(name);
			}
			fw.flush();
```

`　Jsoup.connect("URL").get();`でWebサイトのHTMLを読み込み
`document.select("タグ");`で必要なHTMLタグを見つける。この場合、複数該当するのでelementsには該当するものが全部入っています。
`for`で１つづつ取り出します
`element.text();`で該当したHTMLのテキストを取得。（属性の場合はtextではなく`attr("属性名")`）
ファイルに書き込む際はJSON形式なので今回は`{"Name":"Service_name","Connectivity":"1"}`に合わせる。

こんな感じで書き出せました。めでたしめでたし。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/178136/c5ee98ad-6355-a1cb-9fa6-a12befe54f23.png)

※サイトの連携先一覧におすすめサービスがあり４つ重複がありました。（書き出したもののはじめ４つ）

##参考にさせていただいたもの
ありがとうございました。
jsoup使い方メモ:https://qiita.com/opengl-8080/items/d4864bbc335d1e99a2d7
Javaでスクレイピングをしよう！！:https://qiita.com/takahiroSakamoto/items/c2b269c07e15a04f5861
■[Java][Html Parser][jsoup]jqueryみたいに、htmlを操作できるJavaのライブラリ「jsoup」の使い方。:http://d.hatena.ne.jp/it-tech-dm/20110123/1295774869
Project Euler 25「1000桁のフィボナッチ数」

そのまんま。
デキる人はワンライナーでやっちゃいそうだけど思いつかなかった。

## Problem 25 「1000桁のフィボナッチ数」
フィボナッチ数列は以下の漸化式で定義される:
Fn = Fn-1 + Fn-2, ただし F1 = 1, F2 = 1.
最初の12項は以下である.
F1 = 1
F2 = 1
F3 = 2
F4 = 3
F5 = 5
F6 = 8
F7 = 13
F8 = 21
F9 = 34
F10 = 55
F11 = 89
F12 = 144
12番目の項, F12が3桁になる最初の項である.
1000桁になる最初の項の番号を答えよ.

```python
def hoge(num):
    a, b = 1, 1
    cnt = 2
    while b < 10 ** (num-1):
        cnt += 1
        a, b = b, a + b
    return cnt

print hoge(1000)
```
【PHP】file_get_contents関数を使わずにAPIと通信する(curlでPOST)

# file_get_contents関数がつかえない
APIと通信する処理を実装するのに`file_get_contents`関数を使ったらだめ（というか使えない）という規約にぶち当たった。
じゃあ何かほかに方法ないかな→[curlでPOST](https://qiita.com/_takwat/items/870ffec6ce4562f54c9d)できるらしい
これ使うしかない

# curlでPOSTする実装
例では、APIにPOSTするのは`name`項目という想定です。
（ちなみに想定しているAPIは自前スタブAPIです → [【PHP】とりあえずAPIのスタブをパパっと作る - Qiita](https://qiita.com/sola-msr/items/609677e6e57e7f126487)）

```post.php
/**
 * URLに対してPOST送信しAPIからの値を取得する
 *
 * @param string $url
 * @return array
 */
function sendPostToApiByCurlAndRetrieveJson($url)
{
    $ch = curl_init($url);

    /* POSTする値 */
    $postParams = [
        'name' => 'POST_TEST',
    ];

    $postOption = [
        CURLOPT_POST           => true,
        CURLOPT_TIMEOUT        => 10,
        CURLOPT_POSTFIELDS     => http_build_query($postParams),
        CURLOPT_RETURNTRANSFER => true,
    ];

    curl_setopt_array($ch, $postOption );

    $content = curl_exec($ch); 
    $err     = curl_errno($ch); 
    $errmsg  = curl_error($ch);
    $header  = curl_getinfo($ch);

    curl_close($ch); // リソース開放

    /* errorチェック */
    if ($err !== 0) {
        if (isset($errmsg)) {
            return ['error' => $errmsg];    
        }
        return [];
    }

    if ($header['http_code'] !== 200) {
        if (isset($errmsg)) {
            return ['error' => $errmsg];    
        }
        return [];
    }

    return json_decode($content, true);
}
```

# おわり
- `$postOptio`の設定がぁゃιぃ
- 関数名ながくない？ながいよね。ながいなぁ
- タイプヒンティング書いてないのはPHP7で動かせないからです
- Basic認証かかってるURLに対してPOSTする際は`https://user:pass@*******/api.php`みたいに書かないとエラーが帰ってくるので注意

# 参考
- [PHPのcurlでPOST - Qiita](https://qiita.com/_takwat/items/870ffec6ce4562f54c9d)
- [PHPでcURL POSTするときは、ちゃんとエンコードしましょう（しなくても動くからこそ！！） - Qiita](https://qiita.com/mochieer/items/ff140b5fd30deddf7ff8)
- [APIなどにfile_get_contents()を使うのはオススメしない理由と代替案 - Qiita](https://qiita.com/shinkuFencer/items/d7546c8cbf3bbe86dab8)
- [PHP: cURL 関数 - Manual](http://php.net/manual/ja/ref.curl.php)
android WebView サンプル

■インストール
android端末接続
　　Android_SDK_Manager
　　　　→　installer_r24.4.1-windows
　　→　android-studio-ide-version-windows

※問題点
Android SDK エミュレータで 「Open HAX device failed」エラーが発生した。
エラー情報：
13:36	Emulator: Open the vm device error:\\.\hax_vm00, ec:2
13:36	Emulator: Failed to open vm 0
13:36	Emulator: Failed to create HAX VM
13:36	Emulator: No accelerator found.
13:36	Emulator: failed to initialize HAX: Invalid argument

※対応方法
SDK　Managerで　HAXMのインストールは「not compatible with windows」といメッセージがあるため、ダウンロードできないそうです。
下記のページから、
https://software.intel.com/en-us/articles/intel-hardware-accelerated-execution-manager-intel-haxm
haxm-windows_v7_0_0.zip (7.0.0)をダウンロードし、インストールして該当問題を解消しました。

■サンプル

WebViewを使ってサンプルを作成する。
１．レイアウト作成
app→res→layout→activity_main.xml

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:padding="25dp">
    <WebView  xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/mainView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_weight="1"
        />
    </RelativeLayout>

app→mainfests→AndroidManifest.xmlに下記ソースを追加する。

    <uses-permission android:name="android.permission.INTERNET"/>
    <application>
       略
    </application>

２．WebViewを作成

app→java→leonbase.com.android→MainActivity

public class MainActivity extends AppCompatActivity {
    　　@Override
    　　protected void onCreate(Bundle savedInstanceState) {
        　　super.onCreate(savedInstanceState);
        　　setContentView(R.layout.activity_main);
        　　WebView  webView = (WebView)findViewById(R.id.mainView);
        　　webView.setWebViewClient(new WebViewClient());
        　　webView.loadUrl("www.google.co.jp");
        　　webView.getSettings().setJavaScriptEnabled(true);
    　　}
}
Cognos Analytics のSSO実装時に要注意な事もう一点

#内容
以前、CognosでのSSO(Single Sign On)実装に際して、こんな投稿をしてました。
**Cognos のTivoli Access Manager(TAM)経由のSSOでジャンクション作成時の-jオプションが必要**
https://qiita.com/shinyama/items/498c80213accdc7050bd

TAMに限らずSSO S/W経由でのCognosのSSO実装がうまく行かない際に、もう一点知っておいて欲しい事を投稿しておきます。

まず、SSOを実装する場合、SSO S/W経由でも、Windows統合認証でも、Cognos Analyticsの場合、オプションのGatewayを実装する事がIBMにサポートされている構成になります。
オプションのGatewayですが、通常Cognos Analyticsを導入すると、http://coghostname:9300/bi/ というアドレスでアクセス可能で、組み込みのWebsphere Liberty上で動作するので、IIS(Microsoft Internet Information Services)とか、IHS(IBM HTTP Server)のような、WEBサーバーは不要です。
しかしながらオプションのGatewayを構成すると、これらWEBサーバー経由でCognos Analyticsにアクセスでき、http://coghostname/ibmcognos/ みたいな、80番ポートでのCognos 10 BIの時のURLでアクセスが可能になります。

「サポートされている」というのは、実際にはオプションのGatewayを実装せず、http://coghostname:9300/bi/ の状態でも、SSO S/Wと組み合わせてのSSO実装はできるようです。

ここで注意点ですが、SSO S/Wの場合、
1). http://ssohostname/仮想path にアクセスして
2). 仮想path の値で、マッピングされたCognosの http://coghostname:9300/bi にフォワードされるという動きになります。

ここで、http://ssohostname/bi ⇒ http://coghostname:9300/bi はSSOが成り立つのですが、
http://ssohostname/bi2 ⇒ http://coghostname:9300/bi はSSOが成り立たない事があるようです。
つまり、SSO S/W側の仮想pathと、Cognos側のコンテキストルートを同じにしないといけないという事です。
※どんなSSO S/Wでも同じかは知りません。そういう事例があるという事です。SSOがうまく行かない時に、これに該当している可能性を考えて頂ければと。

ところが、Cognos Analyticsのhttp://coghostname:9300/bi の「/bi」の部分は変更できません。
しかしながら、オプションのGatewayで実装すれば、
http://coghostname/ibmcognos とか、http://coghostname/ibmcognos2に変更可能なので、SSO S/W側の仮想Pathに柔軟に対応できます。

そういう意味で、Cognos AnalyticsでSSOを実装する場合は、オプションのGatewayで実装しておくのが、やはり無難ですね。

あと、<CognosInstall>/cgi-bin/templates/ 以下に、cognos.confファイルのサンプルがあるので、是非ご活用下さい。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147246/6f9f3acd-2d00-5309-4233-766bec17dc1a.png)

以上です。
SSO実装ができない時に、ふとこの記事を思い出して頂ければ幸いです。

#参考情報
実際にCognos Analytics 11とISAM(TAM)で構成した時の事例を投稿していますので、うまく行かない場合はこちらもご参照下さい。
**Cognos Analytics と ISAM(TAM)でSSO実装する時に必要な設定**
https://qiita.com/shinyama/items/7877bc2240e27c7e0723
LINEのリッチメニューを使いこなして、チャットボットのUX向上を目指す

チャットボットを作ってみたは良いけど、
いざユーザに使ってもらったら、

「何に反応してくれるかが分からない」
「全然回答してくれない、ツカエナイ。。。」


このようなシーンに直面することは、有り得る話だと思います
なかなか作成者の思惑通りに使ってもらえないことって有りますよね


<b>それなら、ユーザの操作を誘導してしまいましょう</b>


LINEのリッチメニューを用いてユーザ操作を誘導し、
チャットボットのUX向上を図ってみました

##前提条件
・チャットボットを作成し、LINEアカウントと連携されていること
　今回、私はWatson conversationを用いたチャットボットを作成し、
　Node-REDでLINEアカウントと連携させたチャットボットを利用しました
　
　↓LINEBOT作成までは、コチラを参考に
　[IBM Watson Conversationを利用して、無料でコーディングせずにLINE BOTを作ってみた](https://qiita.com/mk57move/items/ce747e4cdae4203ab449)　

##では、設定していきましょう
###チャットボットの性質理解
まずは、チャットボットの性質を知ることから始めます
今回は単純に「じゃんけんをする」ということに特化したBOTを作成しました

ユーザから期待する入力は、
「グー」「チョキ」「パー」です
<img width="375" alt="IMG_5786.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/215347/ece4bf8f-6777-7124-1297-fbc0bdc5fef4.png">
※絶対にじゃんけんに負けてくれる接待BOTです


###リッチメニューの設定
では、リッチメニューの設定を行っていきます
LINE@ Managerを開き、左側の「リッチコンテンツ作成」を選択し、右側の「新規作成」を押下します
[LINE@ Manager](https://admin-official.line.me/)
<img width="960" alt="記事①.png" src="https://qiita-image-store.s3.amazonaws.com/0/215347/dc94271c-781d-a287-ef48-32f468641871.png">


設定をしていきます
<img width="962" alt="記事②.png" src="https://qiita-image-store.s3.amazonaws.com/0/215347/a628ac35-107f-2453-6feb-2ee61cbde655.png">

表示設定　　　　　　：反映する
表示期間　　　　　　：任意の期間を入れてください
タイトル　　　　　　：任意
トークルームメニュー：チャット画面の下部に表示される文言です
初期表示メニュー　　：初期表示するかしないかの選択です
テンプレート選択　　：今回は画像を作成したので「画像で作成」にしています

<img width="962" alt="記事③.png" src="https://qiita-image-store.s3.amazonaws.com/0/215347/7bb286c9-13a8-c645-9221-451e4afda668.png">
今回は画像タイプ２を選択しました
※画像のサイズは上図の通り、指定があるのでご注意ください
　今回は400×270の画像をペイントで作成しています

<img width="960" alt="記事④.png" src="https://qiita-image-store.s3.amazonaws.com/0/215347/8a9c63c4-fe56-0b58-91d4-5cc6cf02e90d.png">
用意した画像をアップロードし、各々のボタンをタップした時の動作を設定します。
例えば、グーの箇所をタップした場合は、テキストで「ぐー」と入力されるように設定しています。

最後に保存を押下して設定は完了です

※アップロードした画像
<img width="200" alt="無題a.png" src="https://qiita-image-store.s3.amazonaws.com/0/215347/aeb22e05-90aa-18a2-3fcf-f7f747b7759d.png">


##では、操作してみましょう
画面をタップするだけでじゃんけんが楽しめます

<img width="375" alt="IMG_5787.PNG" src="https://qiita-image-store.s3.amazonaws.com/0/215347/fcdebb77-c876-e668-67bd-48c4c0fd1fc1.png">


いかがでしたでしょうか？

BOTが期待する入力を、リッチメニューを活用することで、
作成者が意図する使い方をユーザに促すことができます

是非ともご活用ください
基本的なdestroyの使い方

post_path(@post)で、HTTPメソッドをDELETEに指定
（rails routesで見れるルートパスから確認）


```show.html.erb
<%= link_to "削除",post_path(@post) ,method: :delete, data:{confirm:"本当に削除していいですか？"} %>
```

コントローラーは、
①削除対象を見つける
②削除
③削除した後戻りたい場所を指定


```posts_controller.rb
def destroy
  @post = Post.find(params[:id])
  @post.destroy
  redirect_to root_path
end
```
基本的な1対多の関連付け

post一つにたくさんのcommentができるいわゆる1:nの関係を構築する。（postは完成している前提）
①commnetモデル作成

```コマンドライン
rails g model Comment name:string body:text post:references
```
最後のpost:referencesを忘れない！
するとmodel/comment.rbが生成され、以下の記述が。

```model/comment.rb
belongs_to :post
```
「commentはpostに属していますよ」ということ


②model/post.rbにを追記

```ruby:qiita.rb
has_many :comments, :dependent => :destroy
```
・こんどは「postはたくさんのcommentを持っていますよ」ということ（だから”comments”と複数形）

・:dependent => :destroyは「親であるpostが削除されたら、それに関連していたcommmentも削除します」ということ


③resources :postsにresources :commentsをネストさせる

```routes.rb
resources :posts do
  resources :comments
 end
```
httpリクエストでAzureVMを起動/割り当て解除してみる

案件でAzureを扱うことになり、WebアプリからAzureVMを起動することが必要になりそうということで、Azure初心者ながら調べてみました。
WebアプリからVMを起動する方法は少なくとも２通りありそうです。

- Azure Automation（Webhook + Runbook）
- Azure REST API

今回はAzure Automationでのやり方について記述したいと思います（REST APIはまだ調べてないのでまた後日に。。。）

Azure Automation の場合はAzureポータル上で作業するだけでいいので簡単に利用できます！
基本的な作業の流れは以下のとおりです。
AzureアカウントとAzureVMの作成は各自で準備をお願いします。

1. automationアカウント作成
2. VM起動用またはVM割り当て解除用のRunbookインポート
3. 作成したRunbookにWebhook追加

# 1.automationアカウント作成
Azureポータルの「その他のサービス」から「Automation アカウント」を選択します。
（一覧で見つからない場合は画像のようにフィルタで絞ってください）
![automationAccount.png](https://qiita-image-store.s3.amazonaws.com/0/203410/c1e9c8a6-7eb4-5afa-eb4c-f467d5914cfd.png)

「追加」を選択してアカウントを作成してください。
リソースグループは対象のVMと同じグループにしています。
![automationAccount_2.png](https://qiita-image-store.s3.amazonaws.com/0/203410/cf33002d-a92c-ebb3-6955-83f7ec737ca0.png)

# 2. VM起動用またはVM割り当て解除用のRunbookインポート
作成したautomationアカウントのページに移動してください。
ここからVM起動/割り当て解除用のRunbookを作成しますが、~~いちから作るのはめんどくさいので~~今回はすでに用意してあるRunbookをインポートします。
（一から作りたい方は[こちら](https://blogs.technet.microsoft.com/jpaztech/2017/09/04/8435/)を参考にしてください）

## 「Start Azure V2 VMs」「Stop Azure V2 VMs」のインポート
automationアカウントのページから「Runbookギャラリー」を選択し
「Start Azure V2 VMs」「Stop Azure V2 VMs」の２つのRunbookをインポートします。
名前のとおり、「Start Azure V2 VMs」がVM起動用Runbbok、「Stop Azure V2 VMs」がVM割り当て解除用Runbookになります。
![import.png](https://qiita-image-store.s3.amazonaws.com/0/203410/8cc97aee-55a1-b442-6acc-dd733146c0a5.png)

## 「Start Azure V2 VMs」「Stop Azure V2 VMs」の入力項目設定
インポートしたRunbookの入力パラメータを設定します。（「Start Azure V2 VMs」「Stop Azure V2 VMs」どちらとも同じパラメータを設定します）
オプション項目ですので設定しなくても動くと思いますが、サブスクリプション内のすべてのVMに対して実行されるようなので、特定のVMにのみ実行させたい場合はパラメータを設定する必要があります。

インポートしたRunbookのページに移動し、「編集」->「入力と出力」を選択してください。入力パラメータが３つあります。「ResouceGroupName」「VMName」 の既定値をカスタムに設定し、任意の文字列を入力してください。AzureConnectionAssetName は変更しなくて問題ありません。

- ResouceGroupName ：対象VMの存在するリソースグループ名
- VMName ：対象VM名

![inputparam.png](https://qiita-image-store.s3.amazonaws.com/0/203410/6f25963b-e733-70ed-130e-4471cb5d59f0.png)

値を変更後、「保存」を選択し、「テストウィンドウ」で正しく動作するか確認してください。
「開始」ボタンを押してテストを実行します
「Start Azure V2 VMs」の場合はコンソールに`<vmName> has been started.`と表示されれば成功です。

# 3. 作成したRunbookにWebhook追加
作成したRunbookの詳細ページから「Webhook」を選択してください。
その後「新しいWebhookを作成します」を選択します。
任意の名前を入力し、「OK」を押してください。
パラメータと実行設定は、既定値で問題なければそのまま「OK」を押します。
ここを変更することでwebhook毎に異なるパラメータで実行できるようです。

**「作成」ボタンを押す前にWebhookのURLは必ず保管してください。
作成後はURLを確認することができなくなります。**

![createWebhook.png](https://qiita-image-store.s3.amazonaws.com/0/203410/fed41bf1-3455-1ea2-cace-8a0b47a8d3fb.png)

Webhook作成後、取得したURLをPOSTで送信してみてください。
対象のVMが起動または割り当て解除したら無事成功です。
（実行まで少し時間がかかります）

# まとめ
ポータル上の作業で簡単に実装することができました。
VMの起動/割り当て解除程度ならREST APIを使わなくてもよさそうですね。
REST APIで実装する方法は後日調べてから追記したいと思います。
mysql utf8とutf8mb4 aws-rds

「utf8」とは1byte ~ 4byteで１文字が構成される文字コードである。

しかし、mysqlの「utf8」では4byte文字を扱うことが出来ず、4byte文字を扱う場合は「utf8mb4」を使用しなければならない。

この違いを認識していないと、4byte文字を扱ったときに、文字が「?」として処理されて正しく格納されない自体が発生する。

# RDS
RDSダッシュボードから「パラメーターグループ」を作成する。
以下のパラメーターを「utf8mb4」に設定する。

- character_set_client
- character_set_connection
- character_set_database
- character_set_results
- character_set_server

DBのインスタンスに関連付ける。
再起動する。

でけた
複数のlist内のアイテムを一括で置換したい時


```python3
print(AA, BB, CC)
>>> [0, 1, 2, 3, None, 5, 6, 7, 8, 9] ['q', 'w', 'e', None, 't', 'y'] [True, True, False, False, None]
```
という3つのリストがあるとする。
このときのNoneを'replace'に置換したい。

```python3
LL = [AA, BB, CC]
for L1 in LL:
    L1 = [x if x != None else 'replace' for x in L1]
print(LL)

>>> [[0, 1, 2, 3, None, 5, 6, 7, 8, 9], ['q', 'w', 'e', None, 't', 'y'], [True, True, False, False, None]]
```
置換されない。
そこで，こうする。

```python3
LL = [AA, BB, CC]
for i in range(len(LL)):
    LL[i] = [x if x != None else 'replace' for x in LL[i]]
print(LL)
>>> [[0, 1, 2, 3, 'replace', 5, 6, 7, 8, 9], ['q', 'w', 'e', 'replace', 't', 'y'], [True, True, False, False, 'replace']]
```
こうすると置換される。
最後にもとに戻してあげればよい。

```python3
AA, BB, CC = LL
print(AA, BB, CC)
>>> [0, 1, 2, 3, 'replace', 5, 6, 7, 8, 9] ['q', 'w', 'e', 'replace', 't', 'y'] [True, True, False, False, 'replace']
```
paper.cssを使ってブラウザだけで名刺を印刷する

##この記事について

CSSフレームワーク paper.css の活用法です。
名刺のみならず、ラベルシール等にも使えます。

paper.css は印刷帳票の出力を、ブラウザの印刷機能だけで完結するために作られたCSSフレークワークです。基本的な使い方については、作者の cognitom さんのエントリで解説されています。

paper.css
https://github.com/cognitom/paper-css

そろそろ真面目に、HTMLで帳票を描く話をしようか
https://qiita.com/cognitom/items/d39d5f19054c8c8fd592

業務に使えてとても助かっています。
ネットにあまり活用事例がないので微力ながらエントリを書いてみたいと思います。

##サンプルコード

サンプルコードとして名刺印刷用のアプリを作りました。

[画面イメージ]
![image.png](https://qiita-image-store.s3.amazonaws.com/0/217094/58549c7a-7332-7100-2cac-2dd555ddce01.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/217094/2ee2d758-33ff-9e8a-ca2e-0e99943d9e90.png)

サンプルアプリ
https://okoppe8.github.io/papercss_labelprint/

サンプルコード
https://github.com/okoppe8/papercss_labelprint

印刷帳票をHTMLで作るメリットは、CSS3より可能になった単位「mm」「cm」で正確にデザインが再現できることと、JavaScriptと連動できることです。サンプルコードでは手入力で名前を入れさせてますが、WebAPIと連動させて自動的に名前を取得させれば業務効率化につながります。

##開発手順

開発は以下の流れで進めました。

### HTML/CSS

**印刷範囲の大きさと同じ大きさのtableタグをページの中心に配置する**
     
今回はA-ONEの名刺印刷ラベル(A4)用とします。
名刺１枚のサイズが横91mm x 縦55mm で横2列縦5段です。ですので 横が182mm 縦が275㎜ となります。中心に配置する方法は、Top,Leftを50%に指定したうえで、marginで半分のサイズ分マイナスをつける方法をとりました。

```css
table.print {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 182mm;
    height: 275mm;
    margin: -137.5mm 0 0 -91mm;
    border: 1px solid;
    border-collapse: collapse;
}
```



**tableタグをセルで分割する**

同じ大きさの単票が並ぶデザインなら、セルの数を指定するだけで大きさが揃います。紙の真ん中に余白がある帳票だと色々工夫が必要ですね…。

**左上セルの中に印刷項目を配置。CSSで各項目の位置を親ボックスからの絶対位置指定(position: absolute;)で指定する**

名前とか住所とかをpタグで配置し適当なクラス名を付ける。
CSSで各クラスに対し0.1mm単位で左上からの位置を指定します。

### JavaScript
**左上セルの内容を全セルにコピーする処理を作成**

実行にあたってWEBサーバは不要です。工夫して応用してみてください。

## 注意点
印刷位置は紙の中心になっているはずですが、プリンタによっては印刷位置が数ミリずれることもあります。プリンタの印刷設定などで調整してください。

[知っておくと便利！プリンターごとのプリント位置の調整方法]
http://www.nakagawa-mfg.co.jp/info/406/
[備忘録]Unity で始める C#　途中　まとめ

1個めのゲームが完成したので軽くまとめをば。

**UIパーツの使い方**
・まずキャンパスを配置。
・キャンパスの子としてUIパーツを配置。
・RectTransformCOで位置やサイズを設定。

**ボタンの使い方**
・Unityエディタでボタンを配置。
・メソッドを書く。
・ボタンのOnClickに割り当てる。

**パブリック変数でスクリプトとGOを連携する**
・public修飾子付きのメンバー変数を宣言する。
・Unityエディターでスクリプトを割り当てたGoを選択する。
・インスペクターにGOやスプライトをDDする

**忘れがちなこと**
ボタンにOnclickをつけるときは、Scriptを書いて、それをProjectで空のGOにつけて、わかりやすいように名前を[・・・Manager]とかにしておいて、それをボタンのOnclickに付与する。**こうしておくと何が良いかというと、スクリプトから全体の動作をいじれるため便利であるということ。**
openvpn (PiVPN) で同じクライアントの設定ファイル (client.opvn) で複数から同時アクセスする方法

# raspberry piをVPNサーバーにすぐ出来るPiVPN

http://www.pivpn.io/
ルーターにVPN機能が無いときに便利。

設定後、`pivpn add` でクライアント設定ファイルが自動作成できてすぐに接続まで出来るが、同じクライアントファイルで同時に複数台から接続が出来ない。
（1人がwindows、Mac、iPhoneから使うとかそう言う意味で）

openvpnのサーバー側の設定を変えて、同じクライアント設定にて同時接続可能にする。

```/etc/openvpn/server.conf

...
...
duplicate-cn
...
```

`duplicate-cn`を追加するだけ
[備忘録]Unity で始める C# その6 仕掛けの設置

**public GameObject 変数名;**という風にパブリック変数を用意しておくことで、プログラムでメッセージを操作できるようにしておくと便利。
・**SetActiveメソッドにfalseを引数として渡すと、GOを非アクティブにできる**。
・**[復習]**

```:GetComponentメソッド
Componentの派生クラス.GetComponent<コンポーネントの型>()
```

```:全体的流れの1例
public class GameManager : MonoBehaviour {

	//定数定義：壁方向  数字では人間にわかりづらいのでわかり易い名前の定数を定義する。
	public const int WALL_FRONT = 1; //前
	public const int WALL_RIGHT = 2; //右
	public const int WALL_BACK  = 3; //後
	public const int WALL_LEFT  = 4; //左

	//定数定義　ボタンカラー
	public const int COLOR_GREEN = 0; //green
	public const int COLOR_RED = 1; //red 
	public const int COLOR_BLUE = 2; //blue
	public const int COLOR_WHITE = 3; //white


	public GameObject panelWalls; //壁全体

	public GameObject buttonHammer; //buttom hammer

	public GameObject imageHammerIcon; //icon hammer

	public GameObject buttonMessage; //ボタン：メッセージ
	public GameObject buttonMessageText; //メッセージテキスト

	public GameObject[] buttomLamp = new GameObject[3]; //buttom safebox 配列変数で管理

	public Sprite[] buttonPictue =new Sprite[4]; //buttom picture

	public Sprite hammmerPicture; //picture of hammmer


		
	private int wallNo;  //現在の向いている方向

	// Use this for initialization
	void Start () {
		wallNo = WALL_FRONT;  //スタートでは前を向く
		
	}
	


		//右ボタンを押した
	public void PushButtonRight(){
		wallNo++; //方向を１つ右に　int a= 5; int b = ++a; //aもbも6になる。
			//左の１つ右は前
		if(wallNo>WALL_LEFT){
			wallNo=WALL_FRONT;
			}
		DisplayWall(); //壁画面表示
		}

		//左ボタンを押した
	public void PushLeftButton(){
		wallNo--; //方向を１つ左に　int a= 5; int b = ++a; //aもbも6になる。
			//前の１つ左は後ろ
		if(wallNo<WALL_FRONT){
			wallNo=WALL_LEFT;
			}
		DisplayWall(); //壁画面表示
		}
	

		//向いている方向の壁を表示
	

	void DisplayWall(){
		switch (wallNo) {
		case WALL_FRONT:
			panelWalls.transform.localPosition = new Vector3 (0.0f, 0.0f, 0.0f);
			break;
		case WALL_RIGHT:
			panelWalls.transform.localPosition = new Vector3 (-1000.0f, 0.0f, 0.0f);
			break;
		case WALL_BACK:
			panelWalls.transform.localPosition = new Vector3 (-2000.0f, 0.0f, 0.0f);
			break;
		case WALL_LEFT:
			panelWalls.transform.localPosition = new Vector3 (-3000.0f, 0.0f, 0.0f);
			break;
		}
	}

	 //メッセージを表示
	void DisplayMessage(string mes){
		buttonMessage.SetActive (true);
		buttonMessageText.GetComponent<Text> ().text = mes;
	}

	public void PushButtonMemo(){
		DisplayMessage ("エッフェル塔と書いてある。");
	}

	//メッセージをタップ
	public void PushButtonMessage(){
		buttonMessage.SetActive (false);  //メッセージを消す
	}

		
	}

```

書き忘れていて躓いたこと
if文で条件式の=（いこーる）を一個しか書かなくて想定の動作をしなかったこと。
**＝1個は、変数に代入する場合で、＝2個は数学的ないこーるの意味。if文などで使う。**

**false**: if (doseHaveKey = false) {
**true**: if (doseHaveKey == false) {


・シーンを新しく作ったときは、ビルド設定に登録する必要がある。
ファイルメニューから「BuildSettings」「プロジェクト」ウィンドウの順。
・ボタンにonclick機能をつけるときは、スクリプトを作り、それを空のGOに付与して、ボタンOnclickにGOをつけて選択する。
Random Forestの属性重要度

#Random Forestの属性重要度
Random Forestを用いて機械学習を行った際，それぞれの属性が分類に対してどの程度重要であるかを知りたい場合，属性の重要度を表示することができます．

今回は，WEKAでRandom Forestを実行した際に重要度を表示する方法をまとめます．

1. Classifyタブを選択．
2. Classifierを変更．weka>classifiers>trees>RandomForestを選択．
3. 詳細設定からComputeAttributeInportanceをTrueに変更．

上記設定をして，実行すると下図のような結果が表示されます．

![スクリーンショット 2018-02-08 16.33.28.png](https://qiita-image-store.s3.amazonaws.com/0/102996/92c468da-961d-c8f6-ef0a-df824900498c.png)

左が重要度で，高いほど重要であることがわかります．右が対応する属性名です．

#注意
この重要度は，全ての属性を使用した場合に対しての結果なので，上位にあるからといって，必ず必要な属性であるとは限りません（多分）．
gcloud の Pub/Sub で受信したデータを Redis に登録する

次のサブスクライブでは、受信データを画面に表示しただけでしたが、受信データを Redis に登録するようにしました。
[gcloud の Pub/Sub を python3 で行う](https://qiita.com/ekzemplaro/items/09270045ff485c7e13f8)

```py3:iot_subscribe_redis.py
#! /usr/bin/python
# -*- coding: utf-8 -*-
#
#	iot_subscribe_redis.py
#
#						Feb/08/2018
# ------------------------------------------------------------------
import	sys	
import	json
import	redis
from google.cloud import pubsub
# ------------------------------------------------------------------
def to_redis_proc(json_str):
	sys.stderr.write("*** to_redis_proc *** start ***\n")
	rr = redis.Redis(host='localhost', port=6379, db=0)
	rec = json.loads(json_str)
	key = rec['data']['publish_time']
	sys.stderr.write("\tkey = " + str(key) + "\n")
	json_str_out = json.dumps(rec['data'])
	rr.set(key, json_str_out)
	sys.stderr.write("*** to_redis_proc *** end ***\n")
# ------------------------------------------------------------------
def callback(message):
	json_str = message.data.decode('utf8')
#
	to_redis_proc(json_str)
#
	message.ack()
#
# ------------------------------------------------------------------
sys.stderr.write("*** 開始 ***\n")
#
subscriber = pubsub.SubscriberClient()

sub_name = 'projects/my-project-sep-10-2017/subscriptions/subscription_1'

subscription = subscriber.subscribe(sub_name)

future = subscription.open(callback)

try:
	try:
		future.result()
	except Exception as ex:
		subscription.close()
		sys.stderr.write(str(ex) + "\n")
except KeyboardInterrupt:
	sys.stderr.write('*** interrupted! ***\n')
#
sys.stderr.write("*** 終了 ***\n")
# ------------------------------------------------------------------
```

実行スクリプト

```bash
export GOOGLE_APPLICATION_CREDENTIALS="./my-project-sep-10-2017.json"
./iot_subscribe_redis.py
```

次のようなデータが パブリッシュされる必要があります。

```json
{
  "data": {
    "id_raspberry": "tulip001",
    "sw0": 0,
    "sw1": 1,
    "sw2": 0,
    "sw3": 1,
    "tp0": 18.3,
    "tp1": 17.8,
    "vl0": 2.8,
    "vl1": 3.2,
    "publish_time": 20180208163531,
    "comment": "テスト　２月８日"
  }
}
```
Python で Java の CountDownLatch を実装してみる

[エンジニアの次のステップへの勉強法](https://qiita.com/newta/items/f4aff8cdd8706d5d08c5)を読んでいて、これなんだっけ？とふと思い、復習してみた。

そもそも CountDownLatch って何かは [Java のドキュメント](https://docs.oracle.com/javase/jp/8/docs/api/java/util/concurrent/CountDownLatch.html)を参照ください。

サンプルコード

```Python
#coding:utf-8

import threading

class CountDownLatch(object):
    def __init__(self, count):
        assert count > 0, "count は 1 以上の値を与えてください。入力値[{}]".format(count)
        self.count = count
        self.condition = threading.Condition()

    def count_down(self):
        self.condition.acquire()
        self.count -= 1
        if self.count == 0:
            self.condition.notifyAll()
        self.condition.release()

    def await(self):
        self.condition.acquire()
        while self.count > 0:
            self.condition.wait()
        self.condition.release()


def woker1(latch):
    latch.await()
    print("Finished!")

def woker2(latch, n):
    latch.count_down()
    print("Worker: {}".format(n))

if __name__ == "__main__":
    count = 5
    latch = CountDownLatch(count)

    # woker1 は woker2 のスレッドが全て完了してから実行される
    thread = threading.Thread(target=woker1, args=(latch,))
    thread.start()

    for i in range(count):
        thread = threading.Thread(target=woker2, args=(latch, i))
        thread.start()
```

実行結果

```
Worker: 0
Worker: 1
Worker: 2
Worker: 3
Worker: 4

Finished!
```