OmniAICreator/Qiita-1.07M huggingface dataset:
iPhoneとAIでマスクしている人を判別する


現在コロナウィルスが猛威を奮っております。ITパワーで何かできないものでしょうか。

# 開発環境
- XCode iOS Swift
- Vision Framework  
iOSで顔の検出ができます。
- CoreML Framework  
iOSで学習モデルを扱います。
- Turi Create  
MACで学習モデルを作成します。

# 画像判別のまとめ

機械学習にはモデルが必要です。Inception v3という公開モデルには1000個のカテゴリーが入っています。マウスであったり、コンピューターキーボードであったり、PCモニターの前に座った人をバーバーと判別したりします。
結果は、全てのカテゴリーを足して100になるように出ます。ABCDEの５つある場合、A80、B10、C10、D0、E0、のように「強いて言えばどれか」という結果になります。
なので、特定のモノを判別させたければ、違うモノを判別する必要があります。

顔検出と顔判別は以前の記事にあります。
[iPhoneで顔認識 - Qiita](https://qiita.com/koji4104/items/7afc8c8fba6a942f4145)

# 学習モデルの作成
今回はマスクするしないの２つだけです。結果はMask80%、None20%のようになります。MaskフォルダとNoneフォルダに顔写真をいっぱい入れて、pythonを実行して学習モデルを作成します。
<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/d9a8f039-f705-3c61-7d09-4ea4842fa7cf.png' width=70%>

```python:model.py
import turicreate as tc
import os

data = tc.image_analysis.load_images('train', with_path=True, recursive=True)
data['label'] = data['path'].apply(lambda path: os.path.basename(os.path.dirname(path)))
data.save('Chicken.sframe')

train_data, test_data = data.random_split(0.9)
model = tc.image_classifier.create(train_data, target='label', max_iterations=100)

predictions = model.predict(test_data)
metrics = model.evaluate(test_data)
print('metrics=' + str(metrics['accuracy']))

model.save('Chicken.model')
model.export_coreml('Chicken.mlmodel')
```

# 実験
1. マスクは顔検出できず（ちっきしょー）
元々のアプリは顔検出 → 画像切り抜き → 顔の判別です。使えなくて残念です。
<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/60f36302-b804-7feb-847f-ebd426cf2054.png' width=70%>

1. 全画面を判別するよう修正しました。
日本人限定、女性限定、正面限定、白マスク限定（試行錯誤しました）
一応下のサンプルはフリー素材使っています。
<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/cf283af8-5b97-4fad-6ab9-c77ee625c119.png' width=40%> <img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/ac0ca94c-0e05-7519-bffe-ee308c8bf4f9.png' width=40%>
<img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/ebffa11c-46fd-5192-a21e-525d1c0d776f.png' width=40%> <img src='https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/253995/6ce2f426-4c27-72b4-49d2-01b7a863782f.png' width=40%>

1. 結果
マスク判定が99なのがおかしいです。不明なものはすべてマスクになっている模様。作業の途中で悟りました。もっと他にいい方法があるはず。

# ソース
動かすにはmodelを作成する必要があります。
<https://github.com/koji4104/VisionCoreML>

# まとめ
iPhoneで画像の機械学習を行うアプリは、いろいろ作り直して今回で３つ目です。判別しやすい画像や、トレーニング画像の集め方など、少しづつコツが掴めたと思います。
今回あまりうまくいきませんでしたけど、仮にマスクの判別がうまくいったとして、それからどうするかも課題です。アプリ側で一般的なCMSと連携できる機能があればいいかなと思います。
DQ10のコインボス撃破した日をSplunkで表示してみた

# はじめに
DQ10ネタをくれた知人から「コインボスを初めて倒した日を知りたい」と言われたので「コインボスってなーに？」って返したら、「アトラスとかバズズとか」など懐かしい単語が出てきたのでやってみた。

# やってみた
コインボスのリストを貰ったのでルックアップで取り込んみ、ボス名でログ(日記)と突き合わせて日付を出せばいいだろう・・・と思ったのだが、「コインボスを倒していない」場合があるようなので、ログ→ルックアップの順で連結すると、倒していないボスが抜けてしまうことになる。なので、逆にルックアップ→ログの順に連結することにした。

```
| inputlookup dq10_coinboss
| join boss type=left [search index="XXX" host="YYY" msg="*をはじめて倒した！" | rex field=msg "^(?P<party>.*)と(?P<boss>.*)をはじめて倒した！" 
| replace "べリアル" with "ベリアル" in boss
| eval date2 = _time | table boss, date2]
| eval date1=strptime('date', "%Y/%m/%d")
| eval difftime=if(date2!="", round((date2-date1)/60/60/24,0), round((now()-date1)/60/60/24,0))
| eval date2 = strftime('date2', "%Y/%m/%d")
| fields boss,date,date2,difftime
| sort - date
| rename boss AS "ボス名", date AS "実装日", date2 AS "初撃破", difftime AS "間隔"
```

### ルックアップ→ログ連結
コインボスリストは「date(実装日),boss(ボス名)」のCSVファイルだったので、joinコマンドでbossを渡してログを検索。このとき「リストにあるけどログにない(倒していない)」場合も残したいので、type=leftを指定した。

### 日記から抽出
joinでつないだ先は[]で囲ってsearchからスタート。
msgが「をはじめて倒した！」で終わるものだけ一旦抽出し、rexコマンドでboss名をさらに抽出。その時の_timeをdate2に代入して撃破日を記録。

### 実装日から何日後に倒したか
コインボスとやらはたまに追加される代物らしく、追加されてもすぐ倒せるものではないらしい。ということで撃破日-実装日＝間隔を求めると、どれくらい経ってから倒したのがわかるのでちょっと面白いのではないかと。

実装日(date)をstrptimeでUNIX通算秒に変換してdate1に代入。
date1がNULL＝まだ倒していない場合を考慮し、if文で分岐させて倒していればdate2-date1を、倒していなければnow()-date1を求め、さらに60/60/24してround()で丸め処理をして日数に変換した。

### 整形
最後に実装日でソートし、ボス名、実装日、撃破日、間隔にリネームして完成！

# ベリアルを倒していない？
出来たー、と思ったのだが、キャラ(数キャラ分日記を入れた)によっては３番目に実装された「ベリアル(DQ2に出てきた黄色いやつですな)」が「倒していない」ことになるのだ。知人に聞いたら「倒していないわけない」と言うし、全部そうなら何かミスってると思うのだが、キャラによって出たり出なかったりというのはどういうことだ？

確かに日記を「ベリアル」でサーチすると、あるキャラだけ倒したログが見つからない。

ところが何となく「リアル」でサーチしたら、あるキャラもベリアルを倒したログが出てくるのだ。

え？

まさか？















***「ベリアル」ではなくて「べリアル」になっていることが判明！！***

字面だけ見ていると何言ってるのかよくわからないと思うが、「ベ(カタカナ)」ではなく「べ(ひらがな)」なのである。確認のため、本元のHPの日記の該当箇所をコピペしてよーく見比べてみても、やっぱり「ベ(カタカナ)」ではなく「べ(ひらがな)」なのである。つまり元のデータが悪いのだｗｗｗ
※ちなみに全体を見直しみたら「キャロルべリアル」なる敵も「べ(ひらがな)」であった・・・

仕方ないので、replaceコマンドで「べリアル」は「ベリアル」に置換するようにした。これもデータクレンジングの１つなのか・・・。

# 完成
![qiita_20200216.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/552413/bb1c97f1-6642-61b2-7eed-645d71283db8.png)

# 気づきとまとめ
・inputlookup・・・ルックアップからデータを読み込む。
・join・・・サーチとサーチを特定のフィールドで繋ぐ。typeはinner,left,outer。
・replace・・・フィールドの値を置換する。値A with 値B in フィールド名。
・rename・・・フィールド名を変換する。フィールド名A AS フィールド名B。
・round(A,B)・・・数値Aを桁数Bで四捨五入する。B=0が１の位、正数が小数点第ｎ位を意味するので、負数が１の位より上の位になる(-1は10の位)
・strftime(sec,"%Y/%m/%d")・・・UNIX通算秒(sec)を指定した時間形式の文字列に変換する
・_timeをrenameするとUNIX通算秒で表示されてしまうのでstrftime()で変換すること
【Swift5】UnitテストでArray(配列)のMatchableを作った時の備忘録

#はじめに
Cuckooを使ったUnitテストで、
Array(配列)のMatchableを作成した際の備忘録を残しておきます。
なお、誤った記述等ございましたら、コメントをいただけますと幸いです。

#実際に使用した用途・目的
Bluetoothクラスの実装で、
[discoverCharacteristics(_:for:)メソッド
](https://developer.apple.com/documentation/corebluetooth/cbperipheral/1518797-discovercharacteristics)のテストコードを書く際に、このメソッドの引数である[[CBUUID]?](https://developer.apple.com/documentation/corebluetooth/cbuuid)をMatchableに適合する必要がありました。

CBUUIDは、Bluetoothで使用する特殊な型です。
CBUUIDの使い所については[【Swift5】Bluetoothクラス実装の備忘録](https://qiita.com/haru15komekome/items/f5791d322995a3fd7452)にも記載しておりますので、宜しければご覧ください。

#エラー内容
```
Argument type '[CBUUID]' does not conform to expected type 'OptionalMatchable'
```

#実際にテストした内容

[discoverCharacteristics(_:for:)メソッド
](https://developer.apple.com/documentation/corebluetooth/cbperipheral/1518797-discovercharacteristics)を呼んだ際に、引数のCBUUIDに適切な値がセットされて呼ばれているのかをテストします。

```.swift
_ = XCTContext.runActivity(named: "Peripheral.discoverCharacteristics is called.", block: { _ in
    let uuidArray: [CBUUID] = [CBUUID(string: Const.Bluetooth.kUUID01),
                               CBUUID(string: Const.Bluetooth.kUUID02),
                               CBUUID(string: Const.Bluetooth.kUUID03)]
    verify(self.peripheral, times(1)).discoverCharacteristics(uuidArray, for: any())
})
```



#解決方法・コード

エラーの解消には以下の2つのextensionが必要でした。

```MatchableExtensions.swift
extension Array: OptionalMatchable where Element == CBUUID {
    public var optionalMatcher: ParameterMatcher<[CBUUID]?> {
        return equal(to: self)
    }
}

extension CBUUID: OptionalMatchable {
    public var optionalMatcher: ParameterMatcher<CBUUID?> {
        return equal(to: self)
    }
}

```
今回は、[CBUUID?](https://developer.apple.com/documentation/corebluetooth/cbuuid)の配列に適合する必要があったので、Arrayのextensionと、CBUUIDのextensionの２つが必要でした。

#まとめ
Cuckooを使っているからといって特殊な対応が必要というわけではなく、
Arrayのextensionが作れれば問題なし！といった感じでした。

応用も、基礎知識を持っていれば対応できるのですね・・・！


#参考
- [[Swift]Array<Element>型](https://qiita.com/makopy_inside/items/71f385f67ca215da5c47)
- [Cuckooを使ったiOSアプリのユニットテスト](https://qiita.com/Yaruki00/items/97f47cf71045c8e9bfe3)
- [Swift3.0 で Array の Element が特定の型のときだけ extension したい](https://qiita.com/kuroyam/items/628911051f1ee823d300)
ブラウザでランダムなQRコードを生成するツールを作った話

# サマリ
ブラウザで動作するランダムQRコード発行ツールを作成しました。
<img width="800" alt="screenShot.png" src="https://tetunori.github.io/RandomQRCodeGenerator/images/screenShot.png">

# 成果物
[こちら](https://tetunori.github.io/RandomQRCodeGenerator/)にアクセスするだけで使えます。[ソースコード](https://github.com/tetunori/RandomQRCodeGenerator)もOSSでどうぞ。

# 開発モチベーション
[先日作成したQRコードのReader](https://github.com/tetunori/HTML5WebcamTester)のパフォーマンステストをしたいなと思い、自動でかつ、いろいろなサイズでQRコードが発行されるツールが欲しくなりました。テスト結果も上々でした↓。
<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">QRコードをランダムに自動生成するツール作ってiPhone 11 Proで確かめてみたけど、かなり良いパフォーマンスで読み込めている事がわかった。 <a href="https://t.co/4kUXCE866U">pic.twitter.com/4kUXCE866U</a></p>&mdash; Tetsunori NAKAYAMA | 中山 哲法 (@tetunori_lego) <a href="https://twitter.com/tetunori_lego/status/1229010547221204992?ref_src=twsrc%5Etfw">February 16, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

# 使い方
1. [ツール](https://tetunori.github.io/RandomQRCodeGenerator/)にアクセスするだけで、OKです。すぐにQRコードが発行されると思います。
2. 下記の設定が可能です。
    3. リフレッシュレート( 0.2 Hz - 10 Hz)
    4. Min/Max version ( Version 1 - Version 40. QRコードのサイズの事です) 
    5. エラー訂正レベル (L/M/Q/H)

# SWのポイント
## QRコード読み取りライブラリ
[こちらの記事](https://qiita.com/dojyorin/items/78c22be5411ad68cdff9#writer
)を参考に、[node-qrcode @oldair](https://github.com/soldair/node-qrcode
)を使用することにしました。
使い方もすごく簡単です。

```HTML:index.html
<script src="https://cdn.jsdelivr.net/npm/qrcode@latest/build/qrcode.min.js"></script>
<canvas id="QRCode"></canvas>
```

```Javascript:index.js
const QRCodeCanvas = document.getElementById( "QRCode" );

// generateRandomData() はランダムにデータ生成する自前関数
QRCode.toCanvas( QRCodeCanvas, generateRandomData(), {
 margin: 2,
 scale: 3,
 errorCorrectionLevel: getErrCorrectionLevel()
} );
```

とするだけで、canvasへQRコードを描画してくれます。

# 所感と考察
- 今回の目的であるパフォーマンス測定には十分使えたので大変良かった。また、これにより読み取りツール側のバグらしき挙動も見つけた。
  - 特大サイズ(Level19以上)になってくると読めない、特定のデータサイズ(特に各レベルのデータ最大境界値付近)で生成したQRが読めないなど。
- 図らずも、Generative artのようなものが産まれた
   - <blockquote class="twitter-tweet"><p lang="ja" dir="ltr">QR codeのテスター作ってたら、図らずもgenerative artみたいになった。 <a href="https://t.co/hITIEAaZwY">pic.twitter.com/hITIEAaZwY</a></p>&mdash; Tetsunori NAKAYAMA | 中山 哲法 (@tetunori_lego) <a href="https://twitter.com/tetunori_lego/status/1229024677311307776?ref_src=twsrc%5Etfw">February 16, 2020</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
- 今回のQRコード生成をきっかけにいくつかのアイディアを思いついたので、時間を見つけて検証していきたい。
python3　外部モジュールのインストール方法について

pythonには多くのモジュールが提供されている。
標準配布パッケージとしてインストールされたもの以外の外部モジュールについては、別途インストールが必要となる。

pipコマンドを利用してインストール、アップデート、アンインストールが可能

#インストール方法
コマンドプロンプトにて下記を実行

```
pip install module_name (外部モジュール名)
```


参考にしたサイト
https://note.nkmk.me/python-pip-usage/
gdspyを使って、PythonではじめてのGDSIIファイルを作る

#2020/11/25注
[gdspy](https://github.com/heitzmann/gdspy/)の作者[@heitzmann](https://github.com/heitzmann/)が現在新プロジェクト[gdstk](https://github.com/heitzmann/gdstk/)に注力しているため、筆者もgdstkに乗り換えた。gdspyに比べgdstkは爆速なので、よろしければ次の記事[gdstkでPython最速のGDSII layout制作環境を作る](https://qiita.com/isourou/items/e33e814dee2b4446b77f)を参照されたい。

#はじめに
 本稿は、[PythonでGDSII formatを扱う](https://qiita.com/isourou/items/5ae84a020da65338d19b)の続きである。gdspyライブラリがインストールできたことを前提に、はじめてのGDSファイルを作ってみる。
 まず、原稿執筆時点（2020年2月16日）でgdspyの最新バージョンは1.5.2である。1.5から設計思想が**だいぶ変わった**らしく、以前のバージョンで作ったスクリプトは**そのままでは動かない**。gdspyのオフィシャルにある[スクリプト](https://gdspy.readthedocs.io/en/stable/gettingstarted.html#first-gdsii)ですら現時点で正しく動かないので、取り急ぎ動くようにしたバージョンのみ下記に挙げる。

`import gdspy`

`# The GDSII file is called a library, which contains multiple cells.`
`lib = gdspy.GdsLibrary()`

`# Geometry must be placed in cells.`
`cell = lib.new_cell('FIRST')`

`# Create the geometry (a single rectangle) and add it to the cell.`
`rect = gdspy.Rectangle((0, 0), (2, 1))`
`cell.add(rect)`

`# Save the library in a file called 'first.gds'.`
`lib.write_gds('first.gds')`

`# Optionally, save an image of the cell as SVG.`
`cell.write_svg('first.svg')`

`# Display all cells using the internal viewer.`
`gdspy.LayoutViewer(lib)` <- ここ1行だけ[オフィシャル](https://gdspy.readthedocs.io/en/stable/gettingstarted.html#first-gdsii)から変えた

#GDSファイルの構成
GDSファイル（GDS IIフォーマット）の詳細については、参考サイトを見てほしいが[^1][^2]、大まかにいうと
ライブラリ
    ├セル
        ├ジオメトリ
という構成になっている。ライブラリが、1つのGDSファイル全体を指す。ジオメトリは個々の図形（多角形、円形、etc, etc,...）であり、セルはジオメトリをまとめたものである。
　通常、GDSはTopという名前のCellを持ち、これがライブラリ全体を記述するCellとなる。Topセルにはライブラリか、他のセル（サブセルという）への参照（リファレンス）が含まれる。つまりセルには階層構造が許されているので、適切に利用することができればファイルサイズの効率化につながる。、

#gdspy 1.4 と1.5以降で何が変わったか
1.4までは、取り扱うGDSのライブラリをグローバルなクラスとして取り扱っていたのだが[^1]、1.5以降は明確にローカルなクラス `lib = gdspy.GdsLibrary()` として取り扱われるようになった。それに伴い、オフィシャルのドキュメント含めそのままでは動かないコードが多々ある。当然、サブルーチン(関数)へ渡す時も変更が必要だし、CellReferenceなどの使い方が大きく変わっている。
　今後、gdspy2.0へのアップデートが予定されており、変更点はさらに増大すると思われる。詳しくは[GitHub](https://github.com/heitzmann/gdspy)でソースを見てほしい~~(それを言ったらおしまいである)~~。

#Python/GdspyでGDSファイルを取り扱う上での注意
前節で、 `lib = gdspy.GdsLibrary()`はローカルなクラスとして取り扱われると述べたが、原稿執筆時点では問題も多い。まず、gdspy.GdsLibrary には、コンストラクタはあってもデストラクタがないようである。
つまり、forループ中で`lib = gdspy.GdsLibrary()`　を複数回実行した場合、以前の履歴が残っているためエラーが出る。このエラーを回避するには、Spyderで実行している場合、Pythonスクリプトの中でgdspy.GdsLibrary()の実行は1回のみにするとともに、スクリプトを実行するたびに"コンソール"->”カーネルの再起動”をするべきである[^4]。

[^1]: http://bluefish.orz.hm/sdoc/gds2.html
[^2]: https://gdspy.readthedocs.io/en/stable/gettingstarted.html#gdsii-library
[^4]: これは私のPythonのプログラミングスキルがタコなせいかもしれないが判別できない。識者のご意見を求む。
node.js(express)のパーシャルとは何なのか

#パーシャルとは

レンプレート内から更に読み込んで使われる、テンプレート内の小さな部品を”パーシャル”と呼びます。
node.jsのテンプレートはejsなどがあります。

#パーシャルの例
```html
<tr>
    <th><%= key %></th><td><%= val %></td>
</tr>
```
これはテーブル内の一部の例で
これをテンプレートで読み込んで使います。

#パーシャルの使い方

テンプレート内で下のように書いて読み込みます。

```html
<%- incluede('パーシャルのファイル名',{受け渡す値}) %>
```


実際の例

```html
<%- incluede('data_item',{key:key, val:data[key]) %>
```
Python の win32gui を使ってアクティブウインドウの記録を取るスクリプトを作ってみた

ソフトウェアの開発をしている会社だと、どの作業を何分やったかを日報に入れろと言われることが多いのではないでしょうか？
そんな事言われても忙しいときは記録取るのが難しいので、「アクティブウインドウの記録を取ればだいたい何をしていたのかわかるのでは？」という前提でそれをするためのスクリプトを作ってみました。使えると嬉しい人が他にもいそうなので、公開しておきます。
タイトルバーに編集中のファイル名が出る種類のエディタをお使いのプログラマでしたらこのスクリプトでだいたい何をしていたか後で振り返ることができます。

## 出来上がったスクリプト

以下にあります：
https://github.com/aikige/homeBinWin/blob/master/dumpForegroundWindow.py

### 基本的なコンセプト

1. [pywin32](https://github.com/mhammond/pywin32) に含まれている `win32gui.GetForegroundWindow` および、`win32gui.GetWindowText` を使って現在フォアグラウンドで動作しているウインドウのタイトルを取得します。
2. 取得したものをタイムスタンプ付きでダンプします。
3. 上記の処理を定期実行（間隔は引数で変更可能）します。
4. ログは日付ごとのファイルに記録されます。とりあえず現在はファイルと標準出力の両方にログが出力されるようにしてあります。

### スクリプトを動かすための前準備

pywin32がまだ入っていない場合はインストールしてください。

```
pip install pywin32
```

### 動作確認した環境

```
Python 3.8.1
pywin32 Version 227
```
※win32guiは、pywin32に含まれているようです。

## 参考にしたもの

* [Python win32gui.GetForegroundWindow() Examples](https://www.programcreek.com/python/example/81370/win32gui.GetForegroundWindow)

## 関連する記事

https://qiita.com/aikige/items/140c51ec87a1b67996b6

## 余談

ダンプされたファイルを Vim などで開くとデコードエラーが出ていたので、原因を解析しました。

Windows 10 の場合、タイトルバーは UTF-8 の範囲の文字が利用されているようですが、UTF-8 には CP932 の範囲外の文字があるため、Vim の内部エンコーディングを CP932 にすると、それが原因でデコードエラーになる事がわかりました。

https://qiita.com/aikige/items/12ffa2574199cc740a44

対策として、取得したタイトルから、CP932 の範囲外の文字を削除する処理をスクリプトに追加しました。

https://qiita.com/aikige/items/1bc3b33a41dd2317cba5

動作確認に使ったVim: `VIM - Vi IMproved 8.2 (2019 Dec 12, compiled Dec 12 2019 13:30:17)`
【多分無意味】Bashでpriority-queue

# はじめに
過去に「Bashでアルゴリズムの課題解くのって面白そうじゃね？」と思い取り組んだのですが**特に面白くもないコードが出来上がったので供養します**．
コードは個人的に見やすいように書いたので改善の余地アリアリです．

# 想定問題
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ALDS1_9_C
このpriority-queue問題を想定してコーディングします．提出はしていません(Bashで提出する方法を知らないだけ)．

# input

```bash
#とりあえずグローバル変数
S=() #priority-queueの配列
h=0  #priority-queueの要素数

#
#ここに諸々の関数を書く
#

#main的な
while : #無限ループ
do
    read -a op #入力1行を配列としてopに格納する
    case ${op[0]} in
        "insert")
            insert ${op[1]};; #op[1]を引数としてinsert関数を呼び出す
        "extract")
            extract;; #extract関数を呼び出す
        "end")
            exit;; #終了
    esac
done
```

# parent,left,right

```bash
function parent(){ #親ノードの番号
    local i=$1 #引数．
    echo $((i/2))
}
function left(){ #子ノード(左)の番号
    local i=$1
    echo $((2*i))
}
function right(){ #子ノード(右)の番号
    local i=$1
    echo $((2*i+1))
}
```

# maxHeapify

```bash
function maxHeapify(){
    local i=$1 #引数
    local l=`left $i` 
    local r=`right $i`
    local largest #自身と子で最も大きい要素のノード番号
    if((i>=1))
    then 
        #largestの取得
        if((l<=h&&S[l]>S[i]))
        then largest=$l
        else largest=$i
        fi
        if((r<=h&&S[r]>S[largest]))
        then largest=$r
        fi
        if((largest!=i))
        then
            #S[largest]とS[i]をswap
            local tmp=${S[i]}
            S[$i]=${S[largest]}
            S[$largest]=$tmp
            #移動後のノードについて再帰
            maxHeapify $largest
        fi
    fi
}
```

# insert

```bash
function insert(){
    local x=$1
    h=$((h+1))
    S[$h]=$x
    local i=$h
    while((i>=1))
    do #必要に応じてmaxHeapify
        local p=`parent $i`
        if((S[i]<S[p]))
        then break
        fi
        i=$p
        maxHeapify $i
    done
}
```

# extract

```bash
function extract(){
    echo ${S[1]} #出力
    S[1]=${S[h]}
    h=$((h-1))
    maxHeapify 1
}
```
xreaでpythonからMeCabを使うまで

[xrea](https://www.xrea.com/)は、無料枠でもSSHが使える破格のレンタルサーバーです。

そんなxreaですが、形態素解析ツールの[MeCab](https://taku910.github.io/mecab/)が初めから使えます。

## TL;DR

- 辞書は`/usr/lib64/mecab/dic/ipadic/`を指定する
- 対話モードでテストするときはサーバーの`locale`を適当に設定する

## MeCabを使ってみる

サーバーをレンタルして入ったら、そのまま`mecab`を使うことができます。

```
[username@sxxxx ~]$ mecab
xreaでMeCabを使いたい
xrea    ̾,ͭ̾,ȿ,*,*,*,*
でM     ,,*,*,*,*,*
eCab    ̾,ͭ̾,ȿ,*,*,*,*
を      ,,*,*,*,*,*
        ̾,,*,*,*,*,,ӥ,ӥ
いたい  ,,*,*,*,*,*
EOS
```

このように文字コードの問題でまともに動作しません。
xreaがデフォルトで利用している辞書は、EUC-JPでビルドされているため、utf8のものを使うようにします。
`/usr/lib64/mecab/dic/ipadic/`にutf8でビルドされたIPA辞書があるため、それを使います。

```
[username@sxxxx ~]$ mecab -d /usr/lib64/mecab/dic/ipadic/
xreaでMeCabを使いたい
xrea    名詞,一般,*,*,*,*,*
で      助詞,格助詞,一般,*,*,*,で,デ,デ
MeCab   名詞,一般,*,*,*,*,*
を      助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
使い    動詞,自立,*,*,五段・ワ行促音便,連用形,使う,ツカイ,ツカイ
たい    助動詞,*,*,*,特殊・タイ,基本形,たい,タイ,タイ
EOS
```

よさげです。

## pythonからMeCabを使う

ここでは、python3.6でMeCabを利用することを目標とします。
とはいっても、基本的には`mecab-python3`をインストールするだけです。

```
[username@sxxxx ~]$ python3.6 -m pip install mecab-python3 --user
```

最後に対話モードで動作をテストしましょう。
ここで1つ注意点があります。そのまま日本語の文字列を形態素解析しようとすると`'ascii' codec can't decode ~`のようなエラーが表示されるはずです。これは、[pythonがサーバーの環境変数を読んで、ASCIIコードで日本語をデコードしようとするからです](https://qiita.com/cclef/items/dc8692d8b5a0c8c7812c)。
そのため、環境変数を適当に設定します。

```
[username@sxxxx ~]$ export LANG=ja_JP.utf8
```

以上で、`mecab-python3`の動作をチェックすることができます。

```python
>>> import MeCab
>>> mecab = MeCab.Tagger ('-d /usr/lib64/mecab/dic/ipadic/')
>>> print(mecab.parse('xreaでMeCabを使いたい'))
xrea    名詞,一般,*,*,*,*,*
で      助詞,格助詞,一般,*,*,*,で,デ,デ
MeCab   名詞,一般,*,*,*,*,*
を      助詞,格助詞,一般,*,*,*,を,ヲ,ヲ
使い    動詞,自立,*,*,五段・ワ行促音便,連用形,使う,ツカイ,ツカイ
たい    助動詞,*,*,*,特殊・タイ,基本形,たい,タイ,タイ
EOS
```

## 参考
[Python3環境で日本語を使うとUnicodeEncodeErrorが出る問題について](https://qiita.com/cclef/items/dc8692d8b5a0c8c7812c)
Github Actionsを使うなら今は素直にホストランナー上で動かそう

Github Actionsが普通に使うことが出来るようになってきたので、利用ユーザーが増えてると思います。

ここでとりあえず間違えないようにメモ程度にタイトルのことを書いておきます。

# Github Actionsを使うなら素直にホストランナー上でそのまま動かそう

これ、CircleCIとかでCIを回してた人とかにありがちだと思うのですができるだけ実行時間を減らすためにAlpine Linuxで作ったイメージとかで動かす場合があると思います。

このAlpine Linuxを利用した状態でのGithub Actionsに移すのはあまり良くないでしょう。（今は

主な理由としては **動かないActionがあったりする** ためです。

しかもこれ、公式のActionでも動かないものがあったりします。

https://github.com/actions/setup-node/issues/109
これはsetup-nodeのactionのissueですが、alpine linux上で実行するとうまく動作してくれません。

このような状況にあるため、基本的にはホストランナーで動かしましょう。

# 素直に作るために

基本的なものは https://github.com/actions に揃ってます。
言語系のインストールで存在しないものがあるときはsetup-言語名でactionを調べると良いのがあったりするので、それを利用しましょう。(特に私が利用しているsetup-phpはサンプルが豊富に載ってあるので使いやすい)

この手のちゃんと作られたやつは最初からバイナリとして存在しているものを設置したりとかするので比較的ステップ実行が早かったりします。良き。
.NET Standard のバージョンとサポートされた.NET実装の話

# 前置き
今更感のある話ですが、.NET Standardのバージョンについて誤解している人もいるようなので、小ネタとして書いてみます。

# nugetパッケージの依存関係
.NET Coreでプログラムを作る時、nugetから多種多様なライブラリを参照するのが普通です。
その際、自分の作っている環境にnugetパッケージが対応しているか、依存関係を見て判断すると思います。
例えば依存関係に以下のような記載があった場合、何の.NET実装のどのバージョンがサポートされているのでしょうか。
> **.NETStandard,Version=v2.0**
> NETStandard.Library (>=2.0.1)

# 依存関係を読む
Microsoftの.NET Standardのドキュメントを見てみましょう。
[.NET Standard](https://docs.microsoft.com/ja-jp/dotnet/standard/net-standard)
2020年2月時点では以下の表が記載されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/150005/fe5521a0-3be4-e48b-b2a2-0fab14473abf.png)
前段で例示したバージョン表記は.NET Standard 2.0。
.NET Coreの場合は2.0がサポートされていることになります。
この場合、.NET Core2.xで作られたプログラムでは使用できると見ることができます。
では、より新しいバージョンである.NET Core 3.xで作ったプログラムでは使えないのでしょうか？

# .NET Standardのドキュメントを見直す
結論から先に言ってしまうと、.NET Core 3.xでも普通に使うことができます。
先ほどの.NET Standardのドキュメントをもう一度見てみます。
[.NET 実装のサポート](https://docs.microsoft.com/ja-jp/dotnet/standard/net-standard#net-implementation-support)のセクションの中に以下のような記載があります。
>次の表は、.NET Standard の各バージョンをサポートするプラットフォームの**最小**バージョンを一覧表示します。 つまり、一覧に示されたプラットフォームの以降のバージョンでは、対応する .NET Standard バージョンもサポートされます。 たとえば、.NET Core 2.2 では、.NET Standard 2.0 以前がサポートされます。

言い回しが分かりにくいですが、簡単に言うと.NET Standardの新しいバージョンは古いバージョン全てを含んでいます。
上記の例の様に **.NETStandard,Version=v2.0** と記載されている場合、.NET Coreでは 2.x系と3.x系で作ったプログラムで使用することができます。
つまり.NET Standard v2.1に対応していないnugetパッケージでも、.NET Core 3.xのプログラムで使用することが可能です。

# 依存関係に複数の.NET Standardのバージョンの記載がある場合
nugetパッケージの中には複数の.NET Standardに対応している様に記載されている物があります。
一見不思議ですが、これは異常なものではありません。
またまた .NET Standardのドキュメントを見てみます。
[対象にする .NET Standard バージョン](https://docs.microsoft.com/ja-jp/dotnet/standard/net-standard#which-net-standard-version-to-target)のセクションの中に、以下の記載があります。
>ただし、下位バージョンの .NET Standard を対象にしている場合、さまざまな依存関係のサポートが導入されます。 お使いのプロジェクトが .NET Standard 1.x を対象としている場合、.NET Standard 2.0 "も" 対象にすることをお勧めします。 これにより、.NET Standard 2.0 と互換性のあるフレームワーク上で実行されるライブラリのユーザーでは、依存関係グラフが簡素化され、ダウンロードが必要になるパッケージ数が減少します。

上位バージョンもターゲットに入れておくと依存ライブラリが減り、無駄が少なくなるので複数のバージョンが指定されているのです。

# 何が言いたかったか
使いたいnugetパッケージが .NET Standard 2.1 に対応していないからと言って、.NET Core 3.xでプログラムを作ることを諦める必要はありません。どんどん最新の.NET Coreでプログラムを作りましょう。

~~.NET Frameworkしか対応していないライブラリがある？自分で移植するか、さもなくば諦めてください。
もしそれがMicrosoft謹製のライブラリならばMicrosoftにissueを投げましょう。~~
Herokuにデプロイしたら画像が突然取得できなくなった

##Herokuにデプロイした直後に画像が消える
開発環境：cloud9
言語：PHP7.2
フレームワーク：Laravel 5.5

ローカルサーバーでLaravelのプロジェクトを起動していた時はアプリ内で画像は問題なく表示されていました。ちなみにこの時から画像はAWSのS3に保存して取得するようにしていました。アプリの最低限の機能ができたのでherokuへデプロイしてみましたが、画像が一切読み込めなくなりました。おまけに、表示されるエラーが"something went wrong."だけなので困りました。

##Herokuのログを確認
とりあえず不具合の原因を探るためにhrokuのログを確認してみました。

```
$ heroku config:set APP_LOG=errorlog
$ heroku logs
```

ログを見ると、S3の関連でエラーが出ていることが分かりました。

##HerokuにS3用の環境設定をしていなかった

初心者あるあるかもしれませんが、非常に初歩的なミスで、HerokuのconfigファイルにS3の設定を書き込んでいませんでした。というわけで、configファイルにS3の設定を書き込みました。

```
heroku config:set AWS_ACCESS_KEY_ID=自分のアクセスキーID
heroku config:set AWS_SECRET_ACCESS_KEY=S3へのシークレットアクセスキー
heroku config:set S3_BUCKET_NAME=S3バケットの名前
heroku config:set AWS_DEFAULT_REGION=AWSのリージョン（バージニア北部ならus-east-1）
```
これで問題なく画像が表示されるようになりました。
SSL証明書発行ではcfsslを使おう（opensslめんどくさい）

# SSL証明書
自社や顧客の公開系サーバのインフラ周りを担当すると、SSL 証明書購入がたまに発生して、openssl でどうするんだっけ？みたいによくなります？なりません？
同じことを思ってる人が多いのかわからないですけど、cloudflare に提供していただいている [cfssl](https://github.com/cloudflare/cfssl) コマンドで簡単にCSR、秘密鍵を作成できます。

## cfssl の使い方

### cfssl のインストール方法
cfssl のインストール方法は次のサイトをご参照ください。

* [cfssl cloudflare の Github](https://github.com/cloudflare/cfssl)

### CSR 情報 JSON ファイルの作成
openssl などで CSR ファイルを作成するときに入力する情報を、JSON ファイルとして作成します。JSONファイルを管理しておけばいいのは嬉しいです。（opensslは interactive なので・・・ファイルで持てたかもよく知らない。）

```csr-info.json
{
     "CN":"example.jp"、
     "key": {
       "algo": "rsa",
       "size": 2048
            },
   "names": [
     {
      "C": "JP",
      "L": "Bunkyo",
      "O": "company,Inc",
      "OU": "",
      "ST": "Tokyo"
     }
   ]
}
```

### CSR（Certificate Signing Request）ファイルと秘密鍵の作成
cfssl genkey コマンドを使うと、CSR ファイルと秘密鍵が JSON で出力されます。
CSR ファイルとは、訳したまんま証明書の署名要求ファイルです。
`cfssl genkey csr-info.json`


この JSON を CSR ファイルと秘密鍵 pem ファイルとして出力するには、cfssljson コマンドを使います。
```cfssljson -bare <ファイルのプレフィックス> <CSR・秘密鍵情報の JSON>```

よく見るのは次のようにパイプして実行する方法です。
```cfssl genkey <json> | cfssljson -bare <ファイルのプレフィックス> <json>```

実際に実行すると次のようになります。
![qiita_2020-02-16_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237667/bbaa4b5a-58a3-9729-ee50-87df959d375b.png)

ちなみに、example.csr の中はこんな感じです。
![qiita_2020-02-16_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/237667/c9abda2b-1b85-8cb3-0187-088f1d4f58b9.png)

秘密鍵も作成されますので、作成するフォルダの権限設定は気をつけてください。

## SSL証明書発行

### SSL証明書購入
例えば、[GMO グローバルサイン](https://jp.globalsign.com/service/ssl/)や [SSL ストア](https://www.ssl-store.jp/support/program/buy/)、[ValueSSL](https://valuessl.net/?gclid=EAIaIQobChMI0bCY6dfV5wIV2LWWCh28jQZlEAAYBCAAEgIV1fD_BwE)などで、SSL証明書を購入する場合、作成したCSRファイルを使って申請します。購入方法は購入するサイトをご確認ください。

証明書が発行されましたら、WEBサーバに設定してください。（方法はサービスによるため省略）


## 終わりに
結構端折って簡単に書きました。
私は SSL 証明書購入では、あまり混乱しなくなりそうです。（基本は GCP の無料の証明書を使いますけど・・・）
オレオレ証明書も作成できますが、非推奨のため記載はやめました。

## おまけ
[SSL サーバ証明書と認証局の選び方](https://www.digicert.co.jp/welcome/pdf/wp_sid_ca-selection.pdf)
JSなし）サムネにホバーするとメイン画像が切り替わるやつ

## サムネにホバーするとメイン部分が切り替わるあれ
名前なんて言うんでしょうね。
<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="css,result" data-user="sphenisc" data-slug-hash="GRJZmVy" 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="GRJZmVy">
  <span>See the Pen <a href="https://codepen.io/sphenisc/pen/GRJZmVy">
  GRJZmVy</a> by sphenisc (<a href="https://codepen.io/sphenisc">@sphenisc</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

## サムネにホバーするとメイン画像が切り替わるやつ
<p class="codepen" data-height="265" data-theme-id="dark" data-default-tab="css,result" data-user="sphenisc" data-slug-hash="abONwbd" 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="abONwbd">
  <span>See the Pen <a href="https://codepen.io/sphenisc/pen/abONwbd">
  abONwbd</a> by sphenisc (<a href="https://codepen.io/sphenisc">@sphenisc</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>
見た目だけだと分かりづらいですがplaceholder.comから画像を読み込んでいます。
サムネ用の縮小画像と拡大用を別々にできるようにしています。

～仕組み～

* 6つのliを500×500の大きさで同じ位置に重ねて置く
* 最初のli内のDivだけ表示、他は透明(opacity:0)にしておく
* サムネ(span)にホバーするとそのliのDivだけ表示する
RPA自動化の方法について

## どうやって操作する？

アプリケーション操作を自動化できるのが、RPAの基本的な機能だと思っている。

RPA製品と対象のアプリケーションには相性のようなものがあって、簡単に自動化できる場合もあれば、そうでない場合もある。それは、概ねどういう方法で自動化できるかによってくる。

RPA製品でアプリケーションを操作する方法としては以下のものがある。基本的には検討するべき優先順位の順序で並べている。

1. アプリケーション連携
2. UIオブジェクト認識[^1]
3. キーボード入力
4. OCR
5. 画像
6. 位置座標

優先順位の基準は、安定すること。
安定していれば開発も楽なことが多い（試行錯誤しなくて済むし、後からうまく動かないことが判明して直さなきゃいけなくなるようなことも少ない）。
そして動作も早いことが多い（例外的に、位置座標は不安定だけど動作は高速）。

実際には境界が曖昧なものもあるし、状況次第で番号の大きいものを使った方がいいこともある。
また、1つのアプリケーションの操作でも、複数の方法を組み合わせて使うことが多い。

#### アプリケーション連携

大体のRPA製品で、Excelとの連携が可能。こういう場合、内部的なプログラムで、対象のアプリケーションと直接的に連携していることが多く、誤動作が少ない。
そういうものが使えるならまずそれを使う。

ただ、Excelの操作機能がありますよ、というように機能紹介に書かれていたとしても、実際には機能が限られる。例えば、セルの読み書きなどは普通は大抵の製品で可能だけど、フォントを変えたりまではできない。
その場合はそれ以外の方法を使うことになる。

#### UIオブジェクト認識

製品によっては、アプリケーションの内部構造を分析した上で、どこをクリックするのか、どの部分のテキストを読み取るか、などをRPAに教えることができたりする。[^2]
こういうことができると非常に開発効率が高く、誤作動も少なく、しかも早い。
でもアプリケーションによって対応していたりしていなかったりするので、これができるかどうかをまず確認したい。

また、やや技術的な話になるが、内部的にどのようにオブジェクトを特定しているか把握しておくとベター。
Automation Anywhereは木構造内の位置関係を上から順に辿っている。この場合構造の位置関係が変更になると動作しなくなるため要注意。
一方、UiPathは、セレクターと呼ばれるXML形式のコードでオブジェクトを指定しており、編集することもできる。順序が変わっても動くことが多い。

UiPathはこのあたりの技術に長けている、と個人的には判断している。

#### キーボード入力

この辺りから地道になってくる。
例えば、ショートカットキーを使う、矢印キーでカーソルを動かす、Enterキーで確定するなど、マウスを使わずに操作を進めていく方法。
オブジェクト認識ができない場合、マウスを使うよりこちらほうが安定する。

ただし、カーソルを矢印で動かす場合などは、カーソル初期位置が操作のたびに変わるようなことが無いかは要注意。

#### OCR

画面上に指定の文字があるかを判断し、その文字のあった場所をクリックするなどして操作する方法。
例えば、CitrixやVMWareなどのVDI製品やリモートデスクトップ接続の接続先のアプリケーションを自動化したい場合などは、こうした方法に頼ることになる。

UIオブジェクトも認識せず、キー入力も受け付けない場合、OCRが選択肢に入る。

ただし、以下の点に注意したい。
まず、精度を確認すること。
読み取り対象の文字が英数のみの場合、かなりの精度で認識できる。
日本語で精度の高いOCRエンジンを使うには、有償のものを使う必要があることが多い。

また、例えば「Submit」と書かれたボタンをクリックする場合、他の箇所に「Submit」の文字が現れないかもチェックする。現れる場合は画面の上半分から探すなど、範囲を限定できるかを考える。

認識に時間がかかる点にも注意が必要。

クラウドサービスを利用する場合、開発環境はもちろん、本番環境のネットワークもサービスに接続可能であることを確認したい。（プロキシなどでブロックされてないよね？ということ）

#### 画像

予め画像をRPAに登録しておき、マッチした画像で対象の位置座標を指定してクリックなどを行う方法。

注意点としては、解像度や文字の大きさが変わると動作しなくなることが多い。
開発環境と本番環境が違う環境の場合、この辺りの設定は合わせる必要がある。設定を合わせたつもりでも環境が変わると動かなくなることもよくあるので、必ず確認すること。

#### 位置座標

クリックするマウスの位置座標をRPAに登録する。ウィンドウ内の座標だったり、相対的な座標を指定できる場合もある。

ウィンドウの大きさによって位置が変わってしまう場合には使えなかったり、なかなか安定しない。最終手段。


[^1]: 画面に表示される、アプリケーションのボタンやテキストボックスなど、ユーザーが操作する部品のことをUIオブジェクトとここでは呼ぶことにする
[^2]: 技術的にはCOMやらWebDriverやらというものを使っているっぽいけど、よく知りません。
Trying to get property of non-object　とは

Object(入力欄)に何も入力しないとこのエラーが出ました。

端的には、「そのObjectには、textという(publicに触れる)プロパティはないよ」というエラーメッセージのようです。
つまり、そのObjectにはtext(文字)が何も入ってないですよ、と私なりに解釈しました。

「存在しないObject(オブジェクト)のプロパティを呼び出そうとしている」というのがエラーの直訳ですが、私の解釈で合っていますか？

お答えいただければ嬉しいです。
RPAにおけるモジュール化（共通部品の作成）

# モジュール化とは？
ここでいうモジュール化とは、同じ処理があったときに、共通の処理を1つのコードで表現し、複数の箇所から利用できるようにすること。
一般的なプログラミングだと、関数を作ったりする。RPAの場合は（製品によるけど）ワークフローを別のファイルに切り出したりすることで実現できる。

モジュール以外の別の選択肢としては、次の方法が考えられる（他にもあるかも）：

- コピペして増やす
- ループで実装する（構造上ループで実装するのは無理な場合もあると思う）

# モジュール化のメリット・デメリット

## メリット
- ワークフローの論理構造がシンプルになり、どこで何をやっているかを理解しやすい
    - ループや分岐が増えるとどんどん複雑になる。複雑になると人間の脳では把握できなくなる
- 改修箇所の特定が容易
    - 引数の増減や引数の型の変更にともなって、呼び出し元の変更が必要になることもある。そのような場合にも、モジュールの名前（関数名やファイル名）で検索するなどすれば、比較的容易に変更箇所を特定できる
    - 改修箇所が分散していると、直すのももちろん探すのが大変。
- 記述量が減り、視認性が向上
  - 製品によるが、RPAは（通常のプログラム以上に）記述量が増えると視認性が悪化しがち。
- 改修箇所が少なくて済む
    - N箇所にコピペした場合、その処理が変更になったり不具合が見つかった場合、N回同じ作業をする必要がある。
        - これが一般的なプログラミングなら、テキストエディタを使って一括置換できる（＝1度の作業で済む）場合もあるが、RPA製品ではそういうことはできない[^2]
        - RPA開発では、特定の条件でしか再現しない不具合に遭遇することが多い。そのため、当初は想定していなかった不具合に見舞われ、事後的に手を打つことが多いように思う。[^3]　このような場合に逐一改修していくのは本当に辛い。
- 再利用しやすい
  - コピペする場合は、ワークフローのどこからどこまでをコピーすればいいのか考えてからコピーする必要があるが、その必要がない
  - ブラックボックス化できる。中でどんな変数を使ってて、途中でどんな処理をしているのかを知らなくても使える。
  - （ブラックボックス化と似てるけど）どんな変数を引数として与えればいいか、返り値として何をもらうのかが明確になる。[^1]
- テストしやすい
    - うまくモジュール化すれば、その部分だけテストすることができる。
        - 小さく試したほうが開発がスムーズに進むことが多い
            - 不具合に早めに気付ける
            - 不具合箇所の特定がしやすい
            - テストの実行時間を短縮できる

## デメリット
- 設計の検討が必要
    - メリットを最大化し、デメリットを最小化するにはどうすればいいか、色々と検討する必要がある
- 改修の影響が複数箇所におよぶ
    - 改修した後ちゃんと動くかのテストも、呼び出し箇所それぞれで行う必要がある
        - ということは、どこでその共通部品が使われているか（依存関係）を知る必要がある。検索などですぐ分かる場合もあるだろうし、製品側で依存関係を自動で管理・可視化してくれる場合もある。そういうのがない場合、予めドキュメントで管理しておく必要がある。
- 改修の影響が予測できない場合がある
    - 共通部品XをAとBの２箇所で使っていたとする。あるとき共通部品Xを改修すると、AではうまくいくがなぜかBではうまくいかない、みたいなことが起こりうる。

# 共通部品の作り方について

## 利用範囲をどこまで広げるか

共通部品を作成したとして、それをどの範囲で利用するかはよく考える必要がある。

例えば、以下の選択肢が考えられ、下にいくほど利用範囲が広い。

1. 一手順の中で同じ処理が複数回現れるので、その部分をモジュール化し、手順内で利用する
2. まとまった業務のグループの異なる手順の中に同じ処理が複数含まれるので、その部分をモジュール化し、各手順を自動化するワークフローの中で使用する
3. 異なる業務で同じ処理が複数含まれるので、その部分をモジュール化し、様々な業務から呼び出せるようにする

利用範囲が広くなるほど改修や開発にかける時間を省ける。一方で影響範囲が大きくなり、検証が大変になったりする。

判断基準としては、次のような観点があると思う。以下の場合は利用範囲を広げるメリットが大きく、デメリットが少ない。

* 品質を保証しやすい処理である
    * 画面操作やファイル操作など、外部とのやりとりを伴わない処理のほうが安定しやすい
       * 例えば、計算や文字列操作を行う処理とか、外部のコードやスクリプトを呼び出す処理
    * 画面操作を行う処理でも、UIオブジェクトを正確に認識でき、エラー率が少ない処理
* 再利用する可能性が高い
* 処理内容が変更になる可能性が高い
* その機能を呼び出すのに必要な前提条件や引数がシンプルに表現でき、使う側で誤解することが少ない


## モジュール単位のコピペ

再利用可能な部品を作ったところで、異なるファイルとしてコピペして再利用するということも考えられる。
結局コピペかよ！と突っ込まれそうだけど、一定の再利用性を確保した上で、改修時の影響範囲を限定できるため、実務上は効果がある。

例えばAさんが自分用に作ったワークフローの部品を、Bさんでも使えるように渡す、というような場合。AさんはBさんに渡した後も好き勝手に改修したいし、Bさんも自分用にカスタマイズしたいことがある。
ファイルを分けていれば、お互いに気兼ねすることなく編集できる。




[^1]: ただ、これは製品によるかもしれない。少なくともUiPathはワークフローを別ファイルに切り出した際、内部で使用する変数とは別に引数を明示的に指定できる。Automation Anywhereの通常のタスクでは引数と変数の区別がない。同様のことをするにはMetabotを使う必要がある。
[^2]: UiPathの場合、（裏技的に）ワークフローファイルをテキストエディタで開いて置換することはできる。ただ、間違いなくサポート対象外。他の製品はよく知らない。
[^3]: RPAあるあるだと思うけど、例えば検索ボタンをクリックして、検索結果が返ってくるまでちょっと待ってから次の処理に進みたい。開発環境だと3秒だけ待てばよかったけど、実運用上は10秒くらい待った方がいい・・・とか。
TigerGraphを徹底検証してみた


グラフDB(GraphDB)の仲間、TigerGraphが気になって検証してみました。

https://www.tigergraph.com/
https://docs.tigergraph.com/

同タイプのDBランキングではトップ10にも入っていないですが、何人かに「良いかも」と言われ、かつ、Neo4jユーザー会や社内チャネルでもキーワードとして登場したので、実態が知りたくなりました。

同タイプトップのNeo4jと比較するつもりはなく、淡々とTigerGraphを使ってみることにします。

以下、検証結果をまとめたレポートです。

[![thumbnail](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/69832/6a9a80e0-423a-e0c0-f9e9-95cdf8db2340.png)](https://docs.google.com/document/d/1qFXDCuoQHcU-Vd3e0zYDterIFx2-plYQhcnqA1ZRXHE/)

* 概要
* インストール
* DBMS管理(gadmin)
* シンプルクエリ(GSQL 101)
* パラメータクエリ（GSQL101）
* パターンマッチ(GSQL102)
* GraphStudio(Web UI)
LINE WORKSのAPIを呼び出すためのPythonライブラリを作成しました

こんばんは、@0yanです。
LINE WORKSのトークBotを使うことが多いので、昨年9月にLINE WORKSのAPIを呼び出すためのPythonライブラリを作成しました・・・といってもREADME.mdをきちんと書いておらず、他の人からしたら使いようのないゴミライブラリ。
今回、README.mdをきちんと書いてバグ取りして改めてリリースしました（日本のビジネスチャットなので日本語で書きました）。

- [PyPI - lineworks 0.1.0](https://pypi.org/project/lineworks/)
- [GitHub - 0yan/lineworks](https://github.com/0yan/lineworks)

# 使い方
1. `TalkBotApi`クラスのインスタンス生成
2. `TalkBotApi`クラスのメソッド使用

の2ステップでトークBotにメッセージ送信させたりすることができます。

# 使用例
```python
from lineworks import TalkBotApi

api_id = "your api id."
server_api_consumer_key = "your server api consumer key"
server_id = "your server id."
private_key = "your private key."
domain_id = "your domain id."
bot_no = "your bot number."

talk_bot = TalkBotApi(api_id, server_api_consumer_key, server_id, private_key, domain_id, "your bot no.(option)")

# メッセージ送信（Text）
talk_bot.send_text_message(send_text="こんにちは", account_id="test1@example.com")
```

詳しくはGitHubのREADMEに記載しました。
ご活用頂けますと幸いです。
宜しくお願い致します。
Raspberry Pi セットアップ覚書

# はじめに

実家が農家なんですが、害獣（イノシシ、シカ、ヌートリアetc）に悩まされているとのこと。
何とかテクノロジーが解決できないか、とのことなので、とりあえずマイコンでどこまでできるかを知るためにRaspberry Piをポチりました。



# 購入物

とりあえず予算を2万と決めてあれこれ買いました。

### [Raspberry Pi 3 MODEL B+](https://www.switch-science.com/catalog/3920/)
今回のメイン機
![IMG_4675.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193460/1cefe321-c3e9-4cc4-2b82-3caf64cf5e57.jpeg)


### [Raspberry Pi Zero WH](https://www.switch-science.com/catalog/3646/)
安くて色々使えそうなので~~送料を無料にするために~~2台購入
![IMG_4676.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193460/18248814-5857-af77-5653-c174aaed63c3.jpeg)

### [5V/3.0A USB Micro-Bコネクタ出力](https://www.switch-science.com/catalog/2801/)
あまり詳しくないんだけど、3.0A出力のmicroUSBって普通なくない？
多分、Zero HWはパワーはそんなにいらないと思いRaspberry Pi 3 MODEL B+用に1個だけ購入
![IMG_4677.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193460/8a5ed091-72d8-af11-15f9-18ba11707df1.jpeg)

### [SanDisk microSDXC ULTRA 128GB](https://www.amazon.co.jp/dp/B07HHD7C7T/ref=cm_sw_r_tw_dp_U_x_WulsEbGTBTDJC)
動画とか画像撮りそうなので、大きめのマイクロSDも購入。
4GBぐらいのmicroSDならたくさんあるので、大容量はとりあえず1枚だけ購入。
![IMG_4678.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193460/c32924d4-7f47-fe84-7bf3-65cac46f6316.jpeg)

### [Raspberry Pi カメラモジュール V2 ソニーIMX219PQ](https://www.amazon.co.jp/dp/B07W7JY4XF/ref=cm_sw_r_tw_dp_U_x_MjlsEbKJTDB0D)
赤外線カメラ。害獣は夜行性なので、夜間撮影できるように購入。
![IMG_4679.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/193460/3c47e465-d9d1-7824-6fe7-12144b862c0e.jpeg)

### [赤外線反射モーションボディセンサーモジュールボード](https://www.amazon.co.jp/dp/B081JXXQFW/ref=cm_sw_r_tw_dp_U_x_CqlsEbVTVA35P)
[ここ](https://qiita.com/isann_tech/items/778a8fc71a5c57bff72d)を参考に、赤外線センサーでも遊んでみる予定。
害獣対策に使うかわからないけど。とりあえず購入。

### [ジャンパワイヤ](https://www.amazon.co.jp/dp/B00P9BVKOK/ref=cm_sw_r_tw_dp_U_x_0olsEbZ9BFB9K)
センサ取り付け用。

# Raspberry Pi セットアップ

## OSインストール

### OSを公式よりダウンロード
とりあえずNOOBSではなくRaspbianをチョイス。
CUIおじさんなので[公式](https://www.raspberrypi.org/downloads/raspbian/)よりRaspbian Buster Liteをインストール。

### win32diskimagerを使ってSDにOSを書き込み
特記すべきことはないよ。

## パワーオンまで

### 挿す
microSD
↓
HDMI
↓
USBキーボード
↓
電源（microUSB）

### 電源ON
何やらbootっぽい動きしたので成功。

## 初期設定

### wi-fi拾う
```sudo raspi-config```
でNetwork Optionsから選択すれば、拾えるようになる。
ちなみに、```/etc/wpa_supplicant/wpa_supplicant.conf```にwifiのpassが平で保存されているような気がしますが、気のせいでしょうか？気のせいですか。すいませんでした。

### 日本語キーボードに対応する
[こうする](https://qiita.com/sukinasaki/items/426068d6e87169fa3d88)

### デフォルトユーザ変更する
とりあえずpiっていうユーザだとセキュリティ不安なのでadminというユーザを作って使う

### 追加したユーザでsudoできるようにする
[こうする](https://suin.io/473)

### SSHできるようにする
```sudo raspi-config```でsshをenableする

## カメラで遊んでみる

### 撮影
```raspistill -o /tmp/image.jpg```

### ストリーミング再生

[こうする](https://qiita.com/MuAuan/items/b3abd4cff2f39aa1f2d0)
node.js(express)のbody-parserを理解する

#bodyParserとは、
HTML(ejs)のformのinputに入力された値を受け取れるようにするものです。

例↓

```html
<form action="/" method="post">
    <p>
      <input type="text" name="message">
      <input type="submit" value="送信">
    </p>
</form>
```

この例の場合は、

```html

<input type="text" name="message">
```

の値を取得します。

#inputの値をどう取得するの？

inputにnameを指定して、javascriptで

```javascript

req.body.[inputのnameに指定した値]
```

というように書く事でinputの値を受け取れるようになります。

上の例のnameにmessageを指定した例ですと、下のように書けば値を受け取れます。

```javascript

req.body.message
```

body-parserをインストールしなければ、上のコードを書いてもエラーになります。

#body-parserを使った簡単なアプリの例

html(ejs)でフォームを作成します。

```index.html
<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta http-equiv="content-type" content="text/html" charset="UTF-8">
        <title><%=title %></title>
        <link type="text/css" href="./style.css" rel="stylesheet">
    </head>

    <body>
        <head>
            <h1><%=title %></h1>
        </head>
        <div role="main">
            <p><%-content %></p>
            <form action="/" method="post">
                <p><input type="text" name="message">
                   <input type="submit" value="送信"></p>
            </form>
        </div>
    </body>
</html>
```

このフォームを送信したら(input type="submitのボタンを押したら)、actionへ移動します。

```index.js
var express = require('express');
var ejs = require("ejs");

var app = express();
app.engine('ejs', ejs.renderFile);
app.use(express.static('public'));

var bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({extended: false}));

// ※トップページ
app.get('/', (req, res) => {
    var msg = 'This is Express Page!<br>' + '※メッセージを書いて送信して下さい。';
    res.render('index.ejs',
    {
        title: 'Index',
        content: msg,
    });
});

// ※POST送信の処理
app.post('/', (req, res) => {
    var msg = 'This is Posted Page!<br>' + 'あなたは「<b>' + req.body.message + '</b>」と送信しました。';
    res.render('index.ejs',
        {
            title: 'Posted',
            content: msg,
        });
});

var server = app.listen(3000,() => {
    console.log('Server is runnning!');
})
```

javascriptでpost送信されたあと、
body-parserを使ってreq.body.messageで値を受け取り、
それを表示させています。

作ったアプリ↓

<img width="1440" alt="スクリーンショット 2020-02-16 21.37.22.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/558434/269fe406-2cf5-a1e0-972c-af966e2983a2.png">

”ありがとう”という値を送信しました。
それを受け取っていることが分かります。


#終わりに
参考にした本↓
https://www.amazon.co.jp/Node-js%E8%B6%85%E5%85%A5%E9%96%80-%E7%AC%AC2%E7%89%88-%E6%8E%8C%E7%94%B0-%E6%B4%A5%E8%80%B6%E4%B9%83/dp/4798055220/ref=asc_df_4798055220/?tag=jpgo-22&linkCode=df0&hvadid=295723231663&hvpos=1o1&hvnetw=g&hvrand=2502203059216170870&hvpone=&hvptwo=&hvqmt=&hvdev=c&hvdvcmdl=&hvlocint=&hvlocphy=1009243&hvtargid=pla-526606012765&psc=1&th=1&psc=1

の212ページ
アロー関数の思わずぴえんしちゃった記法

## はじめに
社内SEからJavaScriptを主に使用するWeb系企業を転職をして一ヶ月。
会社で使うわけではないですが、Reactを勉強する上でJavaScript（ES６）の記法を知らないと
頭がぐちゃぐちゃになる気がしたので、ちょっとだけ記事としてまとめたいと思います。

## 意味がわからなかった記法
こいつです。

:cry:アロー関数:cry:

「そんなこともわからないのなら辞めてしまえ」という声が聞こえてきそう。。。
基本的なことはわかっているのですが、ちょっと複雑にされると

「？？？？？？？？？？？」

となるのです。

## アロー関数
基本は簡単です。

```js
const addFunc = function(a, b) {
    return a + b;
}

const result = addFunc(1, 2);
console.log(result);
```

上記が今までの記法。

ES６だとアロー関数が使用できます。

```js
const addFunc = (a, b) => {
    return a + b;
}

const result = addFunc(1, 2);
console.log(result);
```

returnを書くのが面倒な人のために、

```js
const addFunc = (a, b) => (
    a + b
)

const result = addFunc(1, 2);
console.log(result);
```

戻り値がオブジェクトの場合は、

```js
const addFunc = (a, b) => ({
    result: a + b
})

const result = addFunc(1, 2);
console.log(result);
```

ここまでは簡単。

お次。

```js
const addFunc = a => b => {
    return a + b
}
```

ぴえん:cry:
矢印２つある:cry:
ぴえん:cry:

【コメントより】
これはアロー関数で高階関数（カリー化）を定義しています。

```js
const addFunc = a => b => {
    return a + b
}

const addTest = addFunc(1);

const result = addTest(2)
console.log(result);
```

これは、aという引数を持たせた状態でaddTestに関数を代入します。
う〜ん、使いどころがイマイチわからない。

恐らく、exportするファイル内で知ることのできる値と知ることのできない値を引数に使用したい場合に使うのかな？
上記の例でいうと、

「aは今わかるけど、bはstore内の値を使いたいから今は知らんなぁ。。。export先でならわかるけどそっちではaがわからんなぁ。。。」

みたいな状況で使うのかなと思っています。
詳しい人教えてください。

## 終わり
次は分割代入についてまとめたいです。
以上！
ご指摘等あればよろしくお願いいたします。
ぴえん:cry:

## ちなみに
ぴえん:cry: の正式な絵文字をQiitaでも使いたい:cry:
PythonでGDSII formatを扱う

#2020/11/25注
[gdspy](https://github.com/heitzmann/gdspy/)の作者[@heitzmann](https://github.com/heitzmann/)が現在新プロジェクト[gdstk](https://github.com/heitzmann/gdstk/)に注力しているため、筆者もgdstkに乗り換えた。gdspyに比べgdstkは爆速なので、よろしければ次の記事[gdstkでPython最速のGDSII layout制作環境を作る](https://qiita.com/isourou/items/e33e814dee2b4446b77f)を参照されたい。

# はじめに
　GDSII（以下GDS）はICやMEMSのレイアウト用CADフォーマットとして知られている。筆者はMEMSやメタサーフェス製作のためにGDSを利用している。GDSは[LayoutEditor](https://layouteditor.com/)や[KLayout](https://www.klayout.de/)などのマスク用CADソフトで直接作成することもできるし~~（お金持ちはCadenceを使うw）~~、AutoCADやSolidworks等で扱えるdxfファイルから変換することもできるが、数式を用いて記述できるパターンに対してはプログラミング言語の適用が便利である。
　筆者も永らく[JAXA三田先生のページ](http://mnems.eng.isas.jaxa.jp/perl_gds/)を参考にPerlでGDSを描いてきたのだが、近年はCADのファイルサイズが肥大化し、Perlでは限界を感じることが多くなった[^1]。そのため、2019年以降はPythonでGDSを作成している。ここではPythonでGDSファイルを作成するためのノウハウを記述していこうと思う。

# PythonでGDSIIを扱うためのライブラリの選定
PythonでGDSIIフォーマットを取り扱うためのライブラリはいくつか提案されているが、筆者のおススメはgdspyである。知られているライブラリを列挙すると、
- [gdspy](https://gdspy.readthedocs.io/en/stable/index.html)
- [gdsCAD](https://pythonhosted.org/gdsCAD/)
- [python-gdsii](https://pythonhosted.org/python-gdsii/)
等がある。このうち、執筆時点（2020年2月16日）でアップデートが頻繁に行われているのは[gdspy](https://gdspy.readthedocs.io/en/stable/index.html)であり、機能的にも充分に感じているので、本稿では主にこちらを取り挙げる。

# Windows 10 上のAnaconda環境へのgdspyのインストール
ここではWindowsマシン上でとりあえずgdspyでgdsファイルを作成するまでの環境設定について述べる。
1. Anaconda 3 のインストール
https://www.python.jp/install/centos/anaconda/install.html
を見ながらインストールする。

2. Anaconda 3  のアップデート（適宜実行する）
Windows -> cmd.exe　（コマンドプロンプト）から
`> conda update --all`

3. gds用環境の設定
conda環境とpip環境が混在していると[なんだかうまくいかないらしい](https://qiita.com/Daisuke__/items/99de0098ec5668070f75)ので、念のためAnaconda 3 に、新しいenvironmentを作る。gdsiiなどの名前にしておく。
 3-1. gdspyに必要なモジュールをインストールしておく。
      numpy
      cython
      pip
      spyder

4. gdspyの導入
下記URLにある
https://qiita.com/mckeeeen/items/d4cbe4a16a102157f40c
"Anaconda環境にpipでパッケージをインストールする"を実行し、gdspyをインストールする。

Windows -> cmd.exe　（コマンドプロンプト）から、下記を実行する。
(> の行がコマンド入力を示す)

`>conda info -e`
`# conda environments:`
`#`
`base                  *  C:\Users\k_iwa\Anaconda3`

`> source activate base` <-ここの名前(base)は3.で導入したgdspy用environmentにする。

5.　gdspyのインストール

`> pip install gdspy`

C++コンパイラ環境（例えば[Build Tools for Visual Studio 2019](https://visualstudio.microsoft.com/ja/downloads/)）や、cythonなどのライブラリのインストールができていないと、gdspyのインストールができてもきちんと正常動作しない。ダメなら`pip uninstall gdspy`でアンインストールしてからライブラリを整えてインストールしなおす。

6.　Anaconda 3からSpyderなどのPython IDE環境を利用してgdspyを利用する。ドキュメントは[ここ](https://gdspy.readthedocs.io/en/stable/)。
gdspy 1.5 (20, Dec, 2019)からだいぶ記法が変わった。[今後詳述](https://qiita.com/isourou/items/9d8d9220063e839f3e56)する。

[^1]: どうも600MBを超えるとPerlではエラーが出てGDSを出力できなかった。なぜかは結局よくわからなかった。
unityでとりあえずプレイヤーを動かすスクリプト

#始めに
フライトゲームやFPSでプレイヤーを動かすスクリプトを書きました。<br>プレイヤーの動かし方が分からない人は必見です！
(これはmacで開発することが前提となっています)
#下準備
1. unityのnewを選択
<img width="1440" alt="スクリーンショット 2020-02-16 20.28.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581846/6da2dfd4-cf5c-a21c-b0fd-df3e2b39215f.png">
2. Project nameを決めてCreate projectを押す<img width="1440" alt="スクリーンショット 2020-02-16 20.31.50.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581846/fc688811-0f32-6f30-a02c-19505016ae74.png">
3. Create -> 3D Object -> Planeをクリック、地面を作ります。
<img width="1440" alt="スクリーンショット 2020-02-16 20.45.42.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581846/fd497ef1-d402-e6f7-9251-dab7ecf17223.png">
4. Create -> 3D Object -> Cube、今回はこれを動かします。
<img width="1440" alt="スクリーンショット 2020-02-16 20.46.41.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581846/81d6e5f7-5a2f-aa40-0e6c-3ffd64a0f2d7.png">
5. Cube -> Add Componentをクリック'Rigidbody'と検索Rigidbodyをクリックして追加する
<img width="786" alt="スクリーンショット 2020-02-16 20.54.18.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581846/e73ad4ff-a939-174a-0d02-a8c6f5dca777.png">
6. Main CameraをドラクアンドドロップでCubeに入れる<br><img width="228" alt="スクリーンショット 2020-02-16 20.59.28.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581846/97a89357-ee1a-b1dc-2643-f85b085c2f8e.png">
7. ProjectのCreateからC#Scriptを選択してクリック<br>
<img width="229" alt="スクリーンショット 2020-02-16 21.03.09.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581846/44f287b5-a756-c2fe-c649-ea723ebff8f7.png">
8. デリートキーを押し、'Player_controller'と入力<br><img width="229" alt="スクリーンショット 2020-02-16 21.44.32.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581846/177b9826-dcd9-a37b-6536-487e6b449041.png">

9. 作成したスクリプトをダブルクリックで開く
<img width="1440" alt="スクリーンショット 2020-02-16 21.13.27.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/581846/8a2e6a1c-9cfe-e79e-82fc-e10cd56b62e8.png">
これで下準備はOKです。

#スクリプト
```C#
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Player_controller : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetKey(KeyCode.UpArrow))
        {
            transform.position += new Vector3(0,0,0.1f);
        }
        if (Input.GetKey(KeyCode.DownArrow))
        {
            transform.position += new Vector3(0,0,-0.1f);
        }
        if (Input.GetKey(KeyCode.RightArrow))
        {
            transform.position += new Vector3(0.1f,0,0);
        }
        if (Input.GetKey(KeyCode.LeftArrow))
        {
            transform.position += new Vector3(-0.1f,0,0);
        }
    }

}
```
開いたプログラムの内容を全て削除して上のスクリプトをコピペしてコマンドSで保存。<br>unityに戻りこのスクリプトをCubeにドラクアンドドロップをする。<br>これで上の三角のボタンを押せば終了です。
もし、Cubeにスクリプトを入れられなかったらスクリプトを右クリック -> Remameを押して'Player_controller'と入れてみてください！

#最後に
最後まで見てくださってありがとうございます。<br>Qiitaは始めたばかりなので間違っていたら遠慮なく指摘してください！よろしくお願いします。
FlutterのWebViewからBasic認証ページにアクセスする

# 概要
FlutterアプリのWebViewからBasic認証ページの接続テストをする際に少しハマったのでメモ。
WebViewパッケージは[flutter_webview_plugin](https://pub.dev/packages/flutter_webview_plugin)を使用。

```pubspec.yaml
dependencies:
  flutter_webview_plugin: ^0.3.10+1
```

```
$ flutter pub get
```

ローカルサーバー立てて`localhost`にBasic認証接続してみた。
[Node.js + Express + passport.jsでローカルサーバーにBasic認証ページを立てる - Qiita](https://qiita.com/unsoluble_sugar/items/255e6b12339b6920fb17)

# サンプルアプリ

ヘッダー情報にAuthorizationを指定することでBasic認証が通せる。ユーザー名とパスワードを連結した上でbase64Encodeが必要な点に注意。

```main.dart
import 'package:flutter/material.dart';
import 'package:flutter_webview_plugin/flutter_webview_plugin.dart';
import 'dart:convert';
import 'dart:io';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
       routes: {
        "/": (_) => new WebviewScaffold(
          url: getLocalhostUrl(),
          headers: {
            'Authorization':'Basic ' + base64Encode(utf8.encode('user:pass')) 
          },
          appBar: new AppBar(
            title: new Text("Basic authentication"),
          ),
        ),
      },
    );
  }

  String getLocalhostUrl() {
    if (Platform.isIOS) {
      return "http://127.0.0.1:3000/";
    } else if (Platform.isAndroid) {
      return "http://10.0.2.2:3000/";
    } else {
      return "http://localhost:3000/";
    }
  }
}
```
iOS SimulatorとAndoroid Emulatorで動作確認できました。

![スクリーンショット 2020-02-16 18.32.11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/6380/d8bc8c91-2a68-e6b0-34fe-01e573bccba0.png)

# ハマった点
接続先が`localhost`、および`http`であることが原因で以下３点ハマった。

- iOS Simulatorで認証ページが真っ白になる
- Andoroid Emulatorで`ERR_CREARTEXT_NOT_PERMITTED`が出る
- Andoroid Emulatorで`ERR_CONNECTION_REFUSED`が出る

## 解決法
それぞれ記事書いたので、同様の現象に悩まれている方はご参照ください。

- [iOS SimulatorでFlutter WebViewのlocalhost参照でページが真っ白になる件 - Qiita](https://qiita.com/unsoluble_sugar/items/f9612c113b0621a54e2e)
- [Android Emulatorでnet::ERR_CLEARTEXT_NOT_PERMITTEDが出た時の対処法 - Qiita](https://qiita.com/unsoluble_sugar/items/90db09e2083ffc5a33fb)
- [Android Emulatorでnet::ERR_CONNECTION_REFUSEDが出た時の対処法 - Qiita](https://qiita.com/unsoluble_sugar/items/f39bb5afae60f51ea7a4)
rubocopをbundlerでインストールするときにrequire: falseにする理由

## 本記事の対象者
「rubocopを導入するときにrequire: falseにしてたけど、理由がよく分からない」

そんな方に向けてこの記事を書いています。

## rubocopの導入方法
rubocopの公式ドキュメントでは、以下のコマンドを実行する

```
$ gem install rubocop
```
もしくは、Gemfileに以下を記載して、bundlerで導入してくださいと書いてあります。

```
$ gem 'rubocop', require: false
```
この記事では、後者について記載していきます。

## bundlerの特徴
rubocopを導入するうえで、覚えておきたいbundlerの特徴は、
「bundlerでは、Gemfileに書いたgemをまとめて自動でrequireする仕組みになっている」ということです。

## rubocopはどこで使うの？
rubocopは、ソースコードが規約に沿っているか確認するために、ターミナル等でコマンドを実行します。

## 結論
rubocopは、ターミナル等で使用するため、bundlerによってアプリ側に自動で読み込む必要がない。よって、`require: false`にする。

## 参考
主に以下を参考にさせていただきました。ありがとうございます！

- 【Rails】RuboCopの基本的な使用方法と出力の見方
https://qiita.com/terufumi1122/items/ad55bf8713c0df053f58
- Home - RuboCop: The Ruby Linter that Serves and Protects
https://rubocop.readthedocs.io/en/latest/