OmniAICreator/Qiita-1.07M huggingface dataset:
「TIL」を使い始めて1ヶ月経過したので振り返る

#はじめに

予想外にも好評（?）だった「[Githubのリポジトリ「TIL」を使って小さなアウトプットを習慣化する](https://qiita.com/sitmk/items/239335b4ed0c3c797add0)」で紹介した、TIL（Today I Learned）を試してみて1ヶ月経過したので、振り返ってみる。

#アウトプットを振り返る
先に結論を言うと、1ヶ月、つまり30日連続アウトプットには至らなかったものの、効果は有ったと思うので、<b>今後もTILを使ったアウトプットを継続していきたい</b>と考えます。

この記事では、この1ヶ月を定量面と定性面の2つの観点で、順に振り返って行きます。

##定量面
実績とContributions Calendarで振り返ってみる。

###実績

|日数|草を生やした日数|コミット数|アウトプット継続率
|:--:|:--:|:--:|:--:|
|30|20|114|66.6%|

この数字を良いと捉えるか悪いと捉えるかは個人差があると思いますが、個人的には<b>1ヶ月のうちの半分以上は、アウトプットをしていた</b>ことになるので良かったかなと言う印象。

- 草を生やした日数＝アウトプットした日数
- コミット数は自分のリポジトリに対してなので、特に数字に意味はありませんが一応。


###Contributions Calendar

<img src="https://qiita-image-store.s3.amazonaws.com/0/310353/e6a7a40b-3f0b-e04c-a544-adbae85a8ec3.png" width=95%> 

土日は必ずアウトプット出来ていたものの、平日は出来る日とそうでない日があったことが分かる。

#追記（2019/5/６）
GW最終日時点での進捗。４月の月初を除き毎日継続できている。

Contribution数/dayは減少傾向にあるが、何か覚えたらTILに書くようにしている。
また、後で見返したり、新たな気付きなどがあれば都度内容を更新してブラッシュアップしている。

本記事にも書いてありますが、自作ライブラリとしてとても使いやすいのでおすすめです。

<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/310353/bb52ecf1-9c0e-938e-4a51-1bb8c5c615fe.png" width=85%>


#定性面
[YWT](https://hisa-magazine.net/blog/manabutikara/ywt/)という振り返りの手法を使って振り返ってみる。

##Y：やったこと

###1.Ruby入門書とドットインストールを1周した
TILを始めるタイミングで、興味のあったRubyを学ぼうと思い、まずは入門書（たのしいRuby）とドットインストール（Ruby入門）を1周しました。

<img src="https://qiita-image-store.s3.amazonaws.com/0/310353/3a00747e-b6c7-b016-30c9-bf8db3d32a8b.png" width=95%>

###2.学んだ内容をVisual Studio CodeにTILとしてまとめた

Visual Studio Codeを使って、プログラムを動かしながら、TILとしてまとめてGithubにpushしていました。

<img src="https://qiita-image-store.s3.amazonaws.com/0/310353/2d3704e8-4511-3cca-d961-4c8234fe6b84.png" width=85%>


##W：分かったこと

###1.草を生やすことの功罪

Contributions Calendarに草が生えていく光景は嬉しく、日々の学習も捗るので<b>アウトプットは楽しい</b>と思いました。副次的効果かもしれませんが、結果的にモチベーション維持に繋がっていたことは確かです。

一方で、草が生えていくことによる弊害もありました。それは、<b>「草を生やすためにインプットしなければ」と言う感情に駆られてしまった</b>ことです（恒常的ではありません）。

草を生やす行為は決して目的では有りません。別の目的があり、アウトプットのツールとしてGithubを使う姿が本来あるべき姿で、草を優先してしまったことは反省点でも有ります。

###2.後々読み返す時に役立つ
勉強する分野や内容ごとにディレクトリを作れるので、後から見返した時に見やすく、<b>自作ライブラリとして使える</b>ことが分かりました。

<img src="https://qiita-image-store.s3.amazonaws.com/0/310353/5731241f-a7f4-62dd-e6fb-74caa8232fcb.png" width=85%>

###3.知識が定着しやすい
これは書くことのほどでもないですが、インプット→アウトプットを繰り返すことで、知識が定着しやすいことが分かりました。
読み物を読むだけだと、数日後には忘れていることも多いですが、手を動かしてアウトプットを続けたことで、日が経っても覚えて居られたことが多かったです。

###4.使い易い
筆者の場合、ローカルの使い慣れたエディタ(Visual Studio Code）で書いて、最後にpushするだけでした。
その為、使い易さという観点では、TILは使い難さを全く感じることはなく、<b>ストレスなくアウトプット可能</b>とであることが分かりました。

###5.毎日アウトプットを続けることの難しさ

ここで言う難しさは、ネタが無いという意味での難しさでは無く、時間的余裕が無いという意味です。

冒頭にもある通り、この1ヶ月間、アウトプットを毎日続ける、つまりContributions Calendarに毎日草を生やすには至りませんでした。

繁忙期だったり夜は送別会などの飲み会で時間を確保できないことも有り、場合によっては日付が変わってから帰宅することもありました。とは言え、この手の問題は朝早起きすることで解決出来るのですが、何れにせよ睡魔に負けてしまった自分の意思の弱さがあったことは事実です。

##T：次にやること

向こう1ヶ月のアクションプランとしては、以下を目標にしたいと思います。

- 低レイヤ技術を学習すること
- たとえ夜忙してくても、朝早起きしてインプット&アウトプットすること
- 但し、決して草を生やす為のアウトプットはしないこと
- Go言語をマスターすること

#所感
この記事を書いてみて、TILの良さもそうですが、それ以上に<b>振り返ることの重要性を認識</b>したような気がします。

闇雲にただひたすら走るよりも、途中で自分の状況を振り返ることで、改善や更なる成長に繋げることが出来ると思うので、（Qiitaに書くかは別として）定期的に振り返りを行いたい思います。
[JavaScript]Firebase Cloud Functionsにおける関数ごとのファイル分割テンプレート

## やること

FirebaseのCloud Functionsで関数ごとにファイルを分割して管理しやすくしたい．
すでにJSだけでなく様々な言語でも提案されてきた内容ですが，JSで使い回せるテンプレートをまとめたいと思います．

## どうするか

論よりコードです．

### ディレクトリ図
```
functions/
　├ index.js
　├ src/
　│　└ hoge-function.js
  ...
```

### コード
```javascript:index.js
const admin = require('firebase-admin');
admin.initializeApp();

const functions = {
  // Write function references
  hogeFunction: './src/hoge-function',
};
  
loadFunctions = (funcs) => {
  for (let name in funcs) {
    if (!process.env.FUNCTION_NAME || process.env.FUNCTION_NAME === name) {
      exports[name] = require(funcs[name]);
    }
  }
};
  
loadFunctions(functions);

```

```javascript:src/hoge-function.js
const functions = require('firebase-functions');
const admin = require('firebase-admin');

module.exports = functions.https.onRequest((req, res) => {
  if (req.body.message === undefined) {
    // This is an error case, as "message" is required
    res.status(400).send('No message defined!');
  } else {
    // Everything is ok
    console.log(req.body.message);
    res.status(200).end();
  }
})
```

index.jsからhoge-function.jsを呼び出すという一般的な構造です．
ここにfirebaseのモジュールへの参照を追加しています．

関数を追加する場合は，srcフォルダ内にファイルを追加し，hoge-function.jsと同様のフォーマットで関数を記述します．その後，index.jsのfunctionsに参照を追加します．

### 注意点
admin.initializeApp関数は複数ファイルで呼び出すとエラーが発生するため，大元のindex.jsでのみ呼び出します．
関数を記述するファイルでは呼び出さないようにしてください．

## 参考
- [Firebase Functionsで関数ごとにファイルを分割し高速化とメンテナンス性向上も目指す - GAミント至上主義](https://uyamazak.hatenablog.com/entry/2018/10/22/113000)

ほとんどの内容を参考にさせていただきました．ありがとうございます．

## さいごに
料金に注意しつつ，Cloud Functionsを堪能しよう！
Docker入門 CentOSとNginxでWebサーバー構築

## 目的

+ DockerでWebサーバを構築する
+ コンテナOS: centos7
+ webサーバー: Nginx

## Docker

+ コンテナ型の仮想環境構築プラットフォーム
+ VirtualBoxなど仮想マシンに比べて軽量で高速
+ Dockerfileによるコード共有で同一環境の作成が容易

公式ドキュメント
https://docs.docker.com/

公式ドキュメントの日本語化プロジェクト
http://docs.docker.jp/index.html

## Dockerfile作成

```:Dockerfile
FROM centos:7
LABEL maintainer "My Name <mail@foo.bar>"
COPY ./nginx/nginx.repo /etc/yum.repos.d/nginx.repo
RUN yum install -y nginx
CMD ["nginx", "-g", "daemon off;"]
```

ここでの記述がなにをしているか確認

### FROM

+ ベースイメージを指定 以降の命令のベースとなる
+ `FROM イメージ` `FROM イメージ:タグ` のように記述する
+ Dockerfileはコメント以外では必ず `FROM` から開始する
+ `FROM` は複数指定可能

### LABEL

+ メタデータの記述
+ 本例ではmaintainer(Author)を記述した
+ 任意項目のため必要な分だけ記述する

### COPY

+ ローカルのファイルをコンテナにコピーする

centosにnginxのリポジトリが登録されてない場合に`No package nginx available`エラーが発現しインストールできないため、本例3行目の記述でリポジトリを追加する

ローカルにリポジトリ追加記述ファイルを作っておく

```:nginx/nginx.repo
[nginx]
name=nginx repo
baseurl=http://nginx.org/packages/centos/7/$basearch/
gpgcheck=0
enabled=1
```

### RUN

+ コマンドを実行する
+ イメージのビルド(docker build)時に実行される
+ シェル形式・exec形式での記述が可能
+ 本例ではcentos7にnginxをyum installする

```
RUN yum install -y nginx
```

### CMD

+ コマンドを実行する
+ buildされたイメージからコンテナを作成(docker run)するときに実行される
+ 推奨フォーマットはexec形式 `CMD ["実行バイナリ", "パラメータ１", "パラメータ２"]`
+ シェル形式でも記述可能
+ 本例ではnginxサーバを起動する

```
CMD ["nginx", "-g", "daemon off;"]
```

## Dockerイメージ作成

上記で作成したDockerfileをビルドしイメージを作成する

```
$ docker build -t centos-nginx:1.0 .
```

オプションを確認する

### -t
作成するイメージ名:タグを指定する
`-t イメージ名:タグ` または `--tag=イメージ名:タグ` のように記述


## Dockerコンテナ起動

上記で作成したDockerイメージからコンテナを起動する

```
$ docker run -d -p 8000:80 centos-nginx:1.0
```

オプションを確認する

### -d
バックグラウンド実行用オプション
`-d` または `--detach`

### -p
コンテナのポートを指定するオプション
`-p ポート番号` または `--publish=ポート番号` のように記述


本例ではコンテナ起動後にブラウザで `http://localhost:8000` にアクセスし、Nginxの起動を確認する
`Welcome to nginx!` 画面が出ていれば正常


## まとめ

docker入門編としてwebサーバを構築するところまでやってみた
学習コストが高い分自由度が高く、使いこなせればおもしろそう
nginx.confなどの細かい設定やdockerの細かいオプションなども勉強していきたい
次はnginxを利用したAPIスタブなどを作ってみようと思う
background-attachment: fixedについて

#簡単にいうとこういうことだ！

スクロールした時に、背景画像が一緒に動くか、固定されているか

#ちょっと細かく

background-attachment-scroll（初期値）
背景画像を固定せず、画面に合わせてスクロールする。
background-attachment-fixed
背景画像を固定します。画面がスクロールしても、背景画像はスクロールしない。

#どんな時に使うか

背景に悩んだら使いましょーーー


#使用例(img背景と、白背景を交互に使う)

####結果

スクロールした時に、**背景が一緒に動く**のと、**動かない背景**がいて、立体的(?)に見える

####前提

- セクションが５つある（Contact usとかAbout usとかトップページとか）
- img背景が２つまたは３つ以上ある（今回は３つの場合）


####手順

- 最初のセクションにimg背景を入れる

 `code`
.section1 {
    background: url(../img/○○.png);
    background-size: cover;
    background-repeat: no-repeat;
    background-attachment: fixed;
}


- 次のセクションに白背景を入れる

.section2 {
    background-color: white;
}

- セクションごとにそれを交互にやる
英語の勉強について

ここ数か月で英語の勉強をしているので、諸々メモ。最近は技術書よりも英語の本読んでる比率が多い。エンジニアでも英語勉強しなきゃ、でも。。。という人の指針になれば。。。

#英語勉強の目的
ニュース英語のような多少難しい文章をスラスラ読めるようになりたいと思ったから。もちろん技術書も

#著者英語スペック
高校の時は英語結構得意だった。理系なのに英語を武器に乗り切った。
高２の秋にはセンター模試とかで8割コンスタントに取れていた。その後大学、社会人とTOEICのためにちょろっと勉強したくらいで本格的に英語に触れるのは５年ぶり。ちなみにリスニング、スピーキングはダメダメの受験英語一辺倒にはよくいるタイプ

#勉強の方針
読むための英語習得ということで、英文解釈、単語増強にステータス全振り。教材だが、資格試験向けはテクニック習得に特化しがちなので却下。その他学びなおし系の英語もあるが、自分のレベルに適しているものがなさそうなのでこちらも却下。というわけで扱うレベルの幅が広い大学受験英語向けの本中心にした。amazonやbookoffとかで安価に手に入るし。

#勉強履歴と感想とか
使った本を時系列で書いていきます。個人の感想なのであまりあてにしないでください。amazonリンク張ってるので詳細はそちらを参照。
###[基礎英文問題精講](https://www.amazon.co.jp/%E5%9F%BA%E7%A4%8E%E8%8B%B1%E6%96%87%E5%95%8F%E9%A1%8C%E7%B2%BE%E8%AC%9B-%EF%BC%93%E8%A8%82%E7%89%88-%E4%B8%AD%E5%8E%9F-%E9%81%93%E5%96%9C/dp/4010329610)
所要期間60日。難しかったがいいリハビリになった。例題だけ解いた。あーこんな構文あったなとか思い出せたが同時にもう少しレベル下げたほうがいいかなと思ったので、１週回して次へ。問題自体は2章までやった。所要期間は1例題/1日で60日程度。3章は総合問題的な感じだったので、知識習得目的なら2章まででよしと思う。本のレベルは基礎と言いながらセンターを超えているので、注意。

###[英文読解入門基本はここだ!](https://www.amazon.co.jp/%E8%8B%B1%E6%96%87%E8%AA%AD%E8%A7%A3%E5%85%A5%E9%96%80%E5%9F%BA%E6%9C%AC%E3%81%AF%E3%81%93%E3%81%93%E3%81%A0-%E2%80%95%E4%BB%A3%E3%80%85%E6%9C%A8%E3%82%BC%E3%83%9F%E6%96%B9%E5%BC%8F-%E6%94%B9%E8%A8%82%E7%89%88-%E8%A5%BF-%E3%81%8D%E3%82%87%E3%81%86%E3%81%98/dp/4896808371/ref=sr_1_1?s=books&ie=UTF8&qid=1552813159&sr=1-1&keywords=%E8%8B%B1%E6%96%87%E8%AA%AD%E8%A7%A3%E5%85%A5%E9%96%80)
所要期間4日。レベルを下げました。内容のほとんどは覚えていて少々安心したが、逆にこのレベルで100%わかっていないとたぶんまともに英語読めないと思う。そういう意味では基礎を思い出させてくれたので良書。英文を構造をとらえて読むことを丁寧に叩き込んでくれる。本のレベルは高1～2くらいでこの本だけではセンター試験含めて大学受験には太刀打ちできない。

###[ポレポレ英文読解プロセス50](https://www.amazon.co.jp/%E3%83%9D%E3%83%AC%E3%83%9D%E3%83%AC%E8%8B%B1%E6%96%87%E8%AA%AD%E8%A7%A3%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B950%E2%80%95%E4%BB%A3%E3%80%85%E6%9C%A8%E3%82%BC%E3%83%9F%E6%96%B9%E5%BC%8F-%E8%A5%BF-%E3%81%8D%E3%82%87%E3%81%86%E3%81%98/dp/4896803388/ref=sr_1_1?s=books&ie=UTF8&qid=1552814755&sr=1-1&keywords=%E3%83%9D%E3%83%AC%E3%83%9D%E3%83%AC%E8%8B%B1%E6%96%87%E8%AA%AD%E8%A7%A3%E3%83%97%E3%83%AD%E3%82%BB%E3%82%B950)
現在取り組み中。ムズイ。くそムズイ。でも上記2冊に取り組んだおかげでついていける。この本のおかげかわからんが、英文解釈という行為が楽しくなってきた。難しめの構文を全部捉えられた時にはある種の快感を覚えるようになった。正直ニュース英語とかこのレベル求められんの？って思うけどまぁそこは気にしない。本のレベルは難関大受験レベルらしい。

###[Duo 3.0](https://www.amazon.co.jp/DUO-3-0-%E9%88%B4%E6%9C%A8-%E9%99%BD%E4%B8%80/dp/4900790052/ref=sr_1_1?s=books&ie=UTF8&qid=1552814976&sr=1-1&keywords=%E8%8B%B1%E5%8D%98%E8%AA%9E+duo)
唐突に英単語。基礎英文問題精講後半くらいから取り組みだした。現在取り組み中。文で単語を覚えるスタンス。文のインパクトが強く、単語が覚えやすい。このレベルの単語が完全に抜けていることが分かったので自分のレベルにマッチしている。本のレベルは難関大受験レベル。

#総論
もはやQiitaに書くようなものではなかったですが、Qiita以外のブログサービスやってないのでQiitaに書きました。自分はTOEICで高得点とったとかそういうのではないですが、誰かの役に立てば幸いです。上記終わったらニュース英語とか技術書とか読んでみたいですね。それと並行して単語力をたくさん身に着けたいです。ニュース英語は何度か読んだことありますが、単語がわけわからんでしたね。あれを日常的に読んでいるネイティブの単語力半端ねえなと本気で思いますわ。ポレポレ終わったらduo繰り返しともう一冊レベル高めの単語の本やろうかと思ってます。
あと、今回一つわかったのが、巷によく出ている学びなおし系の英語書籍よりも受験参考書のほうがずっと質が高そうだということです。受験英語不要論はずいぶん昔からありますが、文法、単語、構文把握などをしっかり身につけるという意味では不要なんてことはないと思います。読み書きができないものは聞く話すもできませんし。。。
なっとく！アルゴリズム(p164)ダイクストラ法をSwiftで書く

## はじめに
なっとく！アルゴリズム(p164)ダイクストラ法をSwiftで書いてみたのでメモ

## サンプルコード

```Swift

//辞書の中に辞書を入れておく
//ノードの準備
var graph: [String:[String:Int]] = [
    "start" : [ "a": 6 , "b": 2 ],
    "a":["fin":1],
    "b":["fin":5,"a":3],
    "fin":[:]
]

//コストのハッシューテーブル準備
var infinity = Int.max
var costs: [String: Int] = ["a": 6,"b":2,"fin":infinity]

//[親]ハッシュテーブル
var parents:[String:String?] = ["a":"start","b":"start","fin": nil]

//処理したノードをすべて追跡するための配列
var processed:[String] = []


//一番コストの小さいノードを見つける関数
func findLowestCostNode(costs:[String: Int]) -> String? {
    var lowestCost =  Int.max
    var lowestCostNode:String? = nil
    //各ノードをループで処理 
    //キーのみ取り出す
    for node in costs.keys {
        var cost = costs[node]!
        // costsの中でこれまで最もコストが低く、まだ処理されていないノードの場合
        if cost < lowestCost && processed.firstIndex(of: node) == nil {
            //新しくlowestNodeのノードとして設定
            
            lowestCost = cost
            lowestCostNode = node
        }
    }
    return lowestCostNode
    
}

//ダイクストラ法で最短距離を探す
func DijkstraAlgorithm(){
    //まだ処理していないノードのうち最もコストの低いノードを探す
    var node = findLowestCostNode(costs:costs)!
    //nodeは"b"がはいる

    //このWhileループが終了するのは、すべてのノードの処理が完了した場合
    while graph[node]!.isEmpty != true { 
    //while node == "fin" || node == "b" { 
        
        var cost = costs[node]!
        //costには2がはいる
        ////
        //nodeにaがはいると

        guard let neighbors = graph[node] else {
            return 
        }
        //neighborsには　"b":["fin":5,"a":3],
    
        for n in neighbors.keys {
            //nとしてはいるのは"fin"か"a"
            var newCost = cost + neighbors[n]!
            //nにaが入った場合　costは2 neighbors[a]は3　合計 5
            //nにfinが入った場合　costは2 neighbors[fin]は5　合計 7
        
            //移動するほうがコストが低い場合は
            if costs[n]! > newCost {
            //nにaが入った場合　costs[a]は6 newCost5  if分内にはいる
            //nにfinが入った場合 costs[fin]はinfinity newCost 7は　if分内にはいる
            
                //このノードのコストを更新
                costs[n] = newCost
                parents[n] = node
                //nにaが入った場合　costs[a]は5 parents[n]は b
                //nにfinが入った場合 costs[fin]は7 parents[n]は b
            }
        }
        //どちらにしても"b"は検索されたのでもう検索されないようにする
        processed.append(node)
        node = findLowestCostNode(costs:costs)!
    }
    print("node",node)
    print("答えになるcosts",costs)
    print("答えになるparents",parents)

}

DijkstraAlgorithm()

```

### いちおうPaizaでも動く
https://paiza.io/projects/jX5DFQlyQmT0Ny52eGCB1g
Mac の良いところ（10年使い続けてみて感じたこと）

Mac と Windows を使い続けて10年ぐらいの IT オタクが、Mac の良いところを書き連ねます。

# Windows と比較した Mac の良いところ

1. 買うときにモデルや構成を悩まなくて良い
2. 使い続けても遅くならない
3. 満足感がある

## 1. 買うときにモデルや構成を悩まなくて良い
ノートブックを買うときは、モデルやシリーズと呼ばれる「製品カテゴリ」とCPUやMemoryなどの「システム構成」を決める必要があります。Appleの場合、モデルの選択肢は以下の3つだけです。

* 無印
* Air
* Pro
 
しかし、某メーカの場合は、以下のの6つのシリーズから選ぶ必要があります。しかも、シリーズ名からは、どんなノートブックか分からないというトラップ付きです。

* LH
* AH（ハイスペック）
* AH（ベーシック）
* UH
* SH
* QH

またシステム構成も、Appleの場合、以下に示す5つの選択肢を選ぶだけで良いです.

* CPU
* メモリ
* SSDの容量
* カラー
* 保証

しかし、某メーカの場合は、以下に示す14つの選択肢を選ぶ必要があります。

* OS
* メモリ
* ストレージの種類と容量
* 光学ドライブ
* マウス
* バッテリー容量
* カラー
* オフィスの有無
* 保証 x2
* オプションサービス x4

これだけで某メーカでPCを選ぶ際は、だいぶ萎えます。しかも、某メーカのシリーズには、更に上位のカテゴリとして個人用/法人用というカテゴリがあります。今回比較したのは個人用のカテゴリでしたが、法人用のカテゴリは更に選択肢が多岐/難解/複雑になり、組み合わせ問題も発生します...。このシステム構成最適化問題も一種の巡回サラリー問題ですね...。

## 2. 使い続けても遅くならない
Windows は、使い始めの最初はサクサク動いてくれるが、そのうちもっさりしてくる。
Mac は、いつまでもサクサクが続く。とても不思議。

## 3. 満足感がある
液晶なり、キーボードなり、外観なりで満足感がある。あと、いつでも「ドヤァ」できる。

# Windows と比較した Mac の悪いところ
1. Windows のアプリが使えない

## 1. Windows のアプリが使えない
Windows のアプリが使えないのは、かなり辛い。以下のような工夫が必要。

- クラウドサービス / SaaS を使う
- Mac 上に仮想マシンをインストールする

## 

# その他
昔の Mac を使っている人は、「Mac はすぐ壊れる」と言う。しかし、アルミボディの Macbook は物理的に丈夫だし、OS は今や無償でアップグレードできる 。それに、頑丈だったというメーカーは既に滅んでしまっている（もしくは、滅びつつある）。
サーバは「壊れたら放置して、別のを使う」時代になったのだから、パソコンもそれに倣いましょう。それでも、壊れるのが怖いのと言うならば、TOUGHなんとかという軍用モデルをオススメします。
kubernetesのeventを過信してはいけない（戒め）

こんにちは、event大好きマンです。
<a href="https://qiita.com/masahata/items/607f7477d046bed06c51">こんなツール</a>を使ったりして快適イベント監視生活を送っています。
しかしこのeventというもの、完全に信頼するには少し足りなかったようです。

## eventは省略される

livenessのfailによりコンテナが再起動しているはずなのにeventには見当たらない、ということがたまにありました。
調べてみたところ、eventが大量発生した場合は一部省略されるようになっているようです。

## どういうことなの

tl;dr
https://github.com/kubernetes/client-go/blob/kubernetes-1.13.4/tools/record/events_cache.go#L44-L49

kubernetesの各コンポーネントはeventのハンドリングにこの辺りの仕組み
https://github.com/kubernetes/client-go/blob/kubernetes-1.13.4/tools/record
を使っていますが、これには大量に出されたeventを適度にフィルタする仕組みが入っています。
そしてそのフィルタの基準がtl;drで貼ったリンクの部分、定数で決められています。

- 各コンポーネントはeventハンドリング用のthread（便宜上Recorderと呼ぶ）を作る
- Recorderは25回eventを送ることができ、それを超えるとコンポーネント側でeventを発生させてもRecorderでフィルタされてしまう
- 5分毎に1回ぶん回復する
- 別々のeventでもRecorderを共有していればフィルタの対象になる

といったような感じになってます。

## どうすればいいの

そういうものなのでeventを扱う時は頭に入れておきましょう。
使いやすい入れ物


この入れ子構造が使いやすそう。たぶん

```
{
器１　：　GlobalDictionary  (key , vector)
器２　：　Vector (Dictionary)
器３　：　Dictionary (key , value)  <-- このvalueに値を入れていく

器１の中に器２が入り、器２中には器３が入る
器１のkeyはsymbolをセットする

呼び出し方は
	GlobalDictionary.contain[key]でvectorを特定し、そのvectorの要素をforループで検索
	Dictionary.contain[key]で目的の値を取得ｓるう
}


using elsystem;			//datetime
using tsdata.common;    //TokenList
using tsdata.marketdata;//QuotesProvider
using elsystem.xml;     //xml
using elsystem.collections; //Vector,GlobalDictionary,Dictionary,Stack,Queue;

var:GlobalDictionary gdic(null);


method void AnalysisTechnique_inisialized( elsystem.Object sender, elsystem.InitializedEventArgs args ) 
var:string r;
begin
	gdic = GlobalDictionary.Create(false,"MyGlobalDictionary");
	
	setGdic("8798-TS" , "description" , "アドクリ");
	setGdic("8798-TS" , "last" , "1050" );
	r = getGdic("8798-TS" , "description");
	
	print(r);
	print(getGdic("8798-TS" , "last"));
	print(getGdic("8798-TS" , "alllast"));
	print(getGdic("7203-TS" , "description"));
	print(gdic.Count);
end;

method void setGdic(string strsymbol , string key , string value)
var:bool isContainsSymbol ,bool isContainsKey , Vector vec , Dictionary dic ,int n;
begin
	//初期値
	isContainsSymbol = gdic.Contains(strsymbol);	//GlobalDictionary : symbol既存データ有無フラグ
	isContainsKey = false;	//Dictionary : key既存データ有無フラグ
	

	if (isContainsSymbol = false ) then begin
		//GlobalDictionary : symbol既存データなし
		//すべて新規登録
		vec = Vector.Create();
		dic = Dictionary.Create();
		
		dic.Add(key,value);
		vec.push_back(dic);
		gdic.Add(strsymbol , vec);
	end;
	
	if(isContainsSymbol = true ) then begin
		//GlobalDictionary : symbol既存データあり
		//Dictionary : key既存データあり
		//この場合は既存Dictionary-key-valueを更新する。
		//そして、Dictionary : key既存データ有無フラグをtrueにセットする
		vec = gdic.Items[strsymbol] astype Vector;
		for n = 0 to vec.Count-1 begin
			dic = vec.Items[n] astype Dictionary;
			if dic.Contains(key) then begin
				//
				dic.Items[key] = value;
				isContainsKey = true;
			end;
		end;		
	end;
	
	if (isContainsSymbol = true and isContainsKey = false) then begin
		//symbol既存データなし
		//Dictionary : key既存データ有
		//Dictionaryのみ新規addしてVectorに追加
		vec = gdic.Items[strsymbol] astype Vector;
		dic = Dictionary.Create();
		dic.Add(key,value);
		vec.push_back(dic);
	end;
end;

method string getGdic(string strsymbol , string key)
var:string ret , int n , Vector vec , Dictionary dic;
begin
	//初期値
	ret = "";
	
	if (gdic.Contains(strsymbol)) then begin
		vec = gdic.Items[strsymbol] astype Vector;
		for n = 0 to vec.Count-1 begin
			dic = vec.Items[n] astype Dictionary;
			if(dic.Contains(key)) then ret = dic.Items[key].tostring();
		end;
	end
	else begin
		//なにもしない ret=""を返す	
	end;
	
	return ret;
end;

```
Windowsのカレンダー機能がイケテル！

## Windowsのカレンダー機能の設定方法について
![WS000985.JPG](https://qiita-image-store.s3.amazonaws.com/0/138660/c3f8e6f0-39da-298a-4105-3802ffa7d4ed.jpeg)

## メールボックスの同期設定の変更
![WS000986.JPG](https://qiita-image-store.s3.amazonaws.com/0/138660/64e300d1-a0ee-b319-02cf-8d3fb5d3ed25.jpeg)

## 通常「メール」や「アドレス帳」にもチェックしてあるのですが、カレンダー機能のみ使いたいのでオフにします。
![WS000987.JPG](https://qiita-image-store.s3.amazonaws.com/0/138660/ca3b02ab-9227-e700-955c-0a9284789476.jpeg)

## このような形で連携してくれます。いい感じに通知してくれます！
![WS000988.JPG](https://qiita-image-store.s3.amazonaws.com/0/138660/ce2efe62-0c2a-edbb-d802-87f3627bd49b.jpeg)
Ruby_FizzBuzzワンライナー

弟がFizzBuzzやったと聞いて前に書いてみたらできなかった。
やっと書けた。

```
puts [*1..15].map{|x| (x % 15).zero? ? :FizzBuzz : (x % 3).zero? ? :Fizz : (x % 5).zero? ? :Buzz :x}

#=begin..=endでコメントアウト
=begin
1
2
Fizz
4
Buzz
Fizz
7
8
Fizz
Buzz
11
Fizz
13
14
FizzBuzz
=end
```
# 参考

参考にしたWebサイトや技術書などを列挙する

- [Ruby - Ruby =begin =endとbegin endの違い](｜teratail
https://teratail.com/questions/1543
"")

# 次にやること

Rubyを封印してJavaでFizzBuzz
デザイナージェネレーションコード部分


```


//自動生成コード部（プログラムコピーしたときに忘れがちなのでじかに記述しておく。
method override void InitializeComponent()
begin
   self.initialized += AnalysisTechnique_initialized;
   //あと、uninitializedとかworkspaceSavingとか
end;
//END

```


それと、ハンドラの部分

```
method void AnalysisTechnique_initialized( elsystem.Object sender, elsystem.InitializedEventArgs args ) 
begin
    //hogehoge
end;

```
CDK(Chemical Development Toolkit)を使ってみる

# はじめに
Javaから化合物をハンドリング可能なオープンソースであるCDK(ChemicalDevelopmentToolkit)の最新版を使ってみた。ここではソースを書いてビルド、実行するまでを記載する。

# 環境
- Java 11
- IntelliJ IDEA (Community版)
- CDK2.2

# 準備
[CDKのサイト](https://cdk.github.io/)からたどれるGitHubよりcdk2.2のjarファイルをダウンロードし、プロジェクトのライブラリに追加しておく。

# ソース
プロジェクトを作成後、以下のソース(CountHeavyAtoms.javaで作成)をビルドして実行すればOKだ。
CDK2.2になってかなりAPIも変わっているため、CDKのサイトのJavaDocを参考にしてほしい。


```java
import java.io.*;
import org.openscience.cdk.io.iterator.IteratingSDFReader;
import org.openscience.cdk.interfaces.IAtomContainer;
import org.openscience.cdk.DefaultChemObjectBuilder;
import org.openscience.cdk.interfaces.IAtom;

public class CountHeavyAtoms {
    public static void main(String[] args) {
        FileReader sdfile = null;

        try {
            /* CDK does not automatically understand gzipped files */
            sdfile = new FileReader(new File("solubility.train.sdf"));
        } catch (FileNotFoundException e) {
            System.err.println("benzodiazepine.sdf not found");
            System.exit(1);
        }

        IteratingSDFReader mdliter = new IteratingSDFReader(sdfile,
                DefaultChemObjectBuilder.getInstance());

        while (mdliter.hasNext()) {
            IAtomContainer mol = (IAtomContainer) mdliter.next();
            int numHeavies = 0;
            for (IAtom atom : mol.atoms()) {
                if (atom.getAtomicNumber() > 1) {
                    numHeavies++;
                }
            }
            System.out.println(numHeavies);
        }
    }
}
```

今後は記述子計算なども試したい。
TouchDesignerのオペレータをPythonで繋ぐ

# この記事で伝えたいこと

TouchDesignerのオペレータの繋がりをPythonで自動制御したいとき，以下の3つを覚えておけばなんとかなります．

* オペレータ`to`の入力としてオペレータ`from`を繋ぎたいときは，`to.inputConnectors[0].connect(from)`とする．
* 同様に，オペレータ`from`の出力としてオペレータ`to`を繋ぎたいときは，`from.outputConnectors[0].connect(to)`とする．
* 2つのオペレータの接続を切りたいときは，`me.outputConnectors[0].disconnect()`のように，`disconnect()`を呼ぶ．

# 具体例

かわいい猫の画像をネットで漁っていると，とても癒されますよね．ということで，Twitterで"cat"と検索をかけたときにヒットする画像を取得して，スライドショー表示させるものを作ります．

![cat.gif](https://qiita-image-store.s3.amazonaws.com/0/212951/ddb01510-5cff-115d-9409-602b782ac7e1.gif)

プログラムの全体図は次の通りです．

<img width="1437" alt="cat.png" src="https://qiita-image-store.s3.amazonaws.com/0/212951/5761d9ae-eb08-80fb-76a2-bd455f217949.png">


左半分の赤色の部分で画像のURLを取得し，それを`File`プロパティに指定したMovie File In TOPを，Replicator COMPで生成します．そしてコピー結果を全てSwitch TOPに繋ぎ，その`Index`プロパティをLFO CHOPで操作しています．

DATに画像のURLを収納し，その各成分を順に見ることで各画像を表示するMovie File In TOPを生成する方法は，TouchDesigner使いなら一度は通る道：[Visual Thinking with TouchDesigner - プロが選ぶリアルタイムレンダリング&プロトタイピングの極意](http://www.bnn.co.jp/books/8842/)の3章14節「Replicator COMPで動的に複製する」に記載されたものと同一なので，ここでは詳細は省きます．

今問題にしたいのは，複製された各Movie File In TOPをSwitch TOPに繋ぐことです．ここを本記事の最初に述べたコードで解決します．Replicator COMPには，コールバック用のText DATがくっついています．このPythonコードを以下のように書き換えます．

``` replicator1_callbacks.py
# me - this DAT
# 
# comp - the replicator component which is cooking
# allOps - a list of all replicants, created or existing
# newOps - the subset that were just created
# template - table DAT specifying the replicator attributes
# master - the master operator
#

def onReplicate(comp, allOps, newOps, template, master):
    switch = op('switch1')
    lfo = op('lfo1')

    # switchオペレータのIndexが，1から画像の総数まで，1秒ごとにインクリメントされるように設定
    lfo.par.frequency = 1.0 / len(newOps)
    lfo.par.offset = 1
    lfo.par.amp = len(newOps)

    for c in newOps:
        c.outputConnectors[0].connect(switch)
        #c.display = True
        #c.render = True
        #c.par.display = 1
        #c.par.clone = comp.par.master
        pass

    return
```

ここでは複製された各オペレータの出力としてSwitch TOPを繋ぎたいので，`c.outputConnectors[0].connect(switch)`としました．

せっかくノードベースでプログラミングできるのにPythonで繋がりを制御する場面はそうそうない気もしますが，例えばこのようにReplicator COMPと組み合わせることで，手間を減らすことが可能です．具体例では取り扱わなかった接続を切るコードも含め，是非役立ててみてください．
[WIP]正規表現tips

# 正規表現とは

「文字列の集合を一つの文字列で表現する方法の一つ」である。（[wikipedia](https://ja.wikipedia.org/wiki/%E6%AD%A3%E8%A6%8F%E8%A1%A8%E7%8F%BE) より）

正規表現には様々な種類があるが、基本正規表現と拡張正規表現は[IEEE](https://jp.ieee.org/)と[The Open Group](https://ja.wikipedia.org/wiki/The_Open_Group) が制定するUNIXの標準規格POSIXの中で定義されている。

[9. Regular Expressions](http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap09.html)
ローカルでCoderのサーバ立てるdocker-compose

## Coderがすごいらしい

https://coder.com/

 - vscodeがそのままブラウザ上で動くらしい
 - 試しに利用してみたいけど登録が面倒、ＯＳＳで公開されている（！）のでローカルで試すことに
 - dockerFileは公式で提供されているけどdocker-composeが見つからなかったので書くことに

## 書いた
```yml:docker-compose.yml
version: '3'

services:
  coder-server:
    image: codercom/code-server
    volumes:
    - ./project:/root/project
    ports: ["8443:8443"]
    entrypoint: "code-server --allow-http --no-auth" 
```

（公式のQuickStartにあった `docker run -t -p 127.0.0.1:8443:8443 -v "${PWD}:/root/project" codercom/code-server code-server --allow-http --no-auth` をyamlにしただけ）

## 見えた

`docker-compose up` して `192.168.99.100:8443` （自分はdocker-toolboxを利用しているので docker for win, docker for macの人はたぶん `localhost:8443` ）


<img width="823" alt="coder.png" src="https://qiita-image-store.s3.amazonaws.com/0/200942/5952dfd2-c056-6d0f-bea4-41dddeffefd9.png">

- 内蔵terminalで見るとvolumesで指定したディレクトリ直下がワークスペースになってる

- クラウド上とかで運用するならssh周りの設定はしたい
詳細はこの辺り https://github.com/codercom/code-server/blob/master/doc/security/ssl.md 
鍵を生成して↓のようにyml追記（entrypointは差し替え）すれば動きそう（未実施）

```yml
volumes:
- ./keys:/root/keys
entrypoint: "code-server --cert /root/keys/fullchain.cer --cert-key /root/keys/fullchain.key" 
```


## 感想

- リモートで利用するならば公式のサービス利用すればいい気はする
- ローカルで何か用途あるかな？ 一コンテナで開発環境を閉じさせるとかは出来そう
画像を切り取って必要な部分だけ使う方法

##１.表示する画像の大きさを指定する
まず切り取った後に表示したい大きさを指定します

```
.img1
  width: 438px
  height: 223px
```
##2.object-fitプロパティでcoverを指定し、object-positionプロパティの値を設定する
```
.img1
  width: 438px
  height: 223px
  object-fit: cover
  object-position: 0% 0%
```
今回のobject-positionの値の場合指定した範囲に画像の最頂部から表示する形になります

```
  object-position: 100% 100%
```

この場合は、最下部から指定する形になります。指定したい部分がそれ以外の部分の場合、値を徐々に調整する必要があります。地道に頑張りましょう。

```
  object-position: 50% 50%
```

この場ど真ん中になります。

##object-fit: coverを使うメリット
これは画像の縦横比が変わらないことです。
golang 並列処理でリバースプロキシ

# Goの勉強録

とりわけ忙しい訳ではないのですが、今回の内容は紆余曲折しつつ結構ヘビーだったので勉強録の更新が遅れてしまいました。

過去の勉強録でだいたいGo言語でWebアプリケーションを作るのに最低限必要な技術は揃ったので、途中の成果物としてチャットアプリケーションを作っています。その中で色々試行錯誤していた中、結構つまづいたリバースプロキシの並列処理での実装などを勉強したのでそれをまとめていきたいと思います。
将来的には Angular + Go + Docker で一つのアプリケーションを作っていきたいと思っています。

### 過去の勉強録
* [Go言語の環境構築 Hello Worldまで](https://qiita.com/lkeix_/items/161d448bea2547ae2d8b)
* [go言語 静的ページの作成](https://qiita.com/lkeix_/items/a5005efed487bba7c36e)
* [AngularJSのhttp通信からのpostリクエストをgo側で取得](https://qiita.com/lkeix_/items/8f859415cc87a1e9ca07)
* [go言語でhtmlから取得したデータをjson形式で保存する](https://qiita.com/lkeix_/items/e9078f7f49803d77e10f)


## 雑談
Angular + GoとGoogleに溺愛開発環境の中にDockerで運用するかと頑張っていました。Dockerいいですよね。。
コマンド一つで仮想マシン立ち上げられるし、設定ファイルに書き込むだけで色々できる。。なんでこんな便利なツールを知っておきながら勉強しなかったんやと後悔しました。まだまだDocker初心者ですがDockerの記事もまとめていこうかなと思います。

## 本題
今回の本題のgolangの特徴である並列処理を使ってリバースプロキシを実装しようというものです。
リバースプロキシについて[こちら](https://wa3.i-3-i.info/word1755.html)の記事のがわかりやすかったです。
今回の実装は9000、9001ポートでサーバを立てて9000に来たリクエストを9001に投げるという実装です。

では実装のコードをみていきましょう。

```go:main.go
package main

import (
	"fmt"
	"log"
	"net/http"
	"net/http/httputil"
)

func main() {
	serve()
}

func serve() {
	// ポート 9000 と 9001 を開くチャンネルの作成
	hosting9000 := make(chan bool)
	hosting9001 := make(chan bool)

	// ポート 9000 に来たリクエストを 9001 に投げる処理の定義
	go func() {
		director := func(request *http.Request) {
			fmt.Println("Launch Reverse Proxy")
			request.URL.Scheme = "http"
			request.URL.Host = ":9000"
		}
		rp := &httputil.ReverseProxy{Director: director}
		server := http.Server{
			Addr:    ":9001",
			Handler: rp,
		}
		if err := server.ListenAndServe(); err != nil {
			log.Fatal(err.Error())
		}
		hosting9000 <- true
	}()

	// ポート 9001 でホスティングの処理の定義
	go func() {
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
			fmt.Fprintf(w, "Hello Go")
			fmt.Println("Launch on 9001")
		})
		http.ListenAndServe(":9001", nil)
		hosting9001 <- true
	}()

	// チャンネルの呼び出し
	<-hosting9001
	<-hosting9000
}

```

`go run main.go`で`localhost:9000`にアクセスするとlogに Launch Reverse Proxy が出力され、 Launch on 9001 が出力されます。

## まとめ
リバースプロキシを基本情報で知っていたけどどのような実装の仕方か分からなかったが、なんとなく実装の仕方がわかりました。

もっといい実装方法など指摘や質問を歓迎しています！

# 参考
https://kido0617.github.io/go/2016-08-10-reverse-proxy/
https://qiita.com/suin/items/82ecb6f63ff4104d4f5d
Structure visualization of Tensorflow Lite model files (.tflite) 


# Environment

- Ubuntu16.04 x86_64
- Tensorflow v1.11.0 (Bazel 0.17.2)
- Tensorflow v1.12.0+ (Bazel 0.19.2)
- Tensorflow v2.0.0-alpha0 (Bazel 0.19.2)

# Procedure

```console:Advance_preparation
$ sudo apt instal -y libc-ares-dev
$ cd ~
$ git clone https://github.com/PINTO0309/Bazel_bin.git
```

```console:Tensorflow_v1.11.0+Bazel_0.17.2
$ cd ~
$ Bazel_bin/0.17.2/Ubuntu1604_x86_64/install.sh
$ git clone -b v1.11.0 https://github.com/tensorflow/tensorflow.git
$ cd ~/tensorflow
$ git checkout v1.11.0

$ sudo bazel clean
$ sudo bazel run tensorflow/contrib/lite/tools:visualize -- ~/weights.tflite model_viz.html
```
```console:model_viz.html
~/.cache/bazel/_bazel_root/xxxx/execroot/org_tensorflow/bazel-out/k8-opt/bin/tensorflow/contrib/lite/tools/visualize.runfiles/org_tensorflow/model_viz.html
```

```console:Tensorflow_v1.12.0+Bazel_0.19.2
$ cd ~
$ Bazel_bin/0.19.2/Ubuntu1604_x86_64/install.sh
$ git clone -b v1.12.0 https://github.com/tensorflow/tensorflow.git
$ cd ~/tensorflow
$ git checkout -b v1.12.0

$ sudo bazel clean
$ sudo bazel run tensorflow/contrib/lite/tools:visualize -- ~/weights.tflite model_viz.html
```

```console:Tensorflow_v2.0.0-alpha0+Bazel_0.19.2
$ cd ~
$ Bazel_bin/0.19.2/Ubuntu1604_x86_64/install.sh
$ git clone -b v2.0.0-alpha0 https://github.com/tensorflow/tensorflow.git
$ cd ~/tensorflow
$ git checkout v2.0.0-alpha0

$ sudo bazel clean
$ sudo bazel run tensorflow/lite/tools:visualize -- ~/weights.tflite model_viz.html
```
```console:model_viz.html
~/.cache/bazel/_bazel_root/xxxx/execroot/org_tensorflow/bazel-out/k8-opt/bin/tensorflow/lite/tools/visualize.runfiles/org_tensorflow/model_viz.html
```

![screencapture-file-home-b920405-cache-bazel-_bazel_root-1b482cb833d8dba716c683473b9bb6cb-execroot-org_tensorflow-bazel-out-k8-opt-bin-tensorflow-contrib-lite-tools-visualize-runfiles-org_tensorflow-model_viz-html-2019-03-17-14_31_15.png](https://qiita-image-store.s3.amazonaws.com/0/194769/2f10f168-0c78-f075-3b60-49a4b2ea7a3f.png)
PythonでWebアプリケーション（2）

今回は，Pythonを使ってWebアプリケーションを作るためにあたって，HTTPとPythonでWebサーバを作るについて話したいと思います．

## 実行環境
OS：MasOS High Sierra
MacBook Pro (13-inch, 2017, Four Thunderbolt 3 Ports)
プロセッサ：3.5 GHz Intel Core i7
メモリ：16 GB 2133 MHz LPDDR3

Python 3.6.4
Chromeバージョン: 71.0.3578.98（Official Build） （64 ビット）

## HTTPについて
WebアプリケーションではUIや結果を表示する部分と，データを受け取って処理を行う部分が分かれています．この様なアプリケーションをクライアント・サーバ型アプリケーションと呼びます．

クライアントとは，特定の機能を専門に受け持つアプリケーションを指します．Webアプリの場合は，HTMLで書かれたUIなどを表示するWebブラウザがクライアントに相当します．

サーバには，処理を実行するプログラムを置きます．ユーザが実際に操作をするのはクライアントになります．クライアントとサーバの間では通信が行われることになります．
アプリケーションが動くときに通信をするかしないかが，スタンドアロン型のアプリケーションとWebアプリのようなクライアント・サーバ型アプリケーションの決定的な違いです．

サーバは，UIに入力された内容を通信で受け取ります．必要があれば，処理の結果を通信でクライアントに戻します．つまり，通信には2つの方向があるわけです．

## リクエストとレスポンス
クライアントからサーバに向かう通信をリクエスト（要求）と呼びます．
逆に，サーバからクライアントへの通信をレスポンス（応答）と呼びます．
クライアントから処理を要求し，サーバが処理結果を応答として返します．

では，`https://www.python.org/about/`の様なURLの例を取って，URL文字列の意味について簡単に解説します．
`http`はスキーム，`www.python.org`はホスト，`about`はパスと呼ばれます．
URLは，大まかに分けて前半と後半に分かれます．
前半はスキーム，ホストと呼ばれる要素から構成します．スキームはどのような手法を使ってリクエストを送るかを指定する部分です．ホストはリクエストを送る先がインターネット上のどこにあるかを指定する部分です．
後半部分はパス，Pythonで作られているアプリケーションの一覧が書かれたHTMLを送るよう，パスを指定します．

## PythonでWebサーバを作る
Webアプリケーションを実際に動かすには，クライアントとなるWebブラウザと，サーバとなるWebサーバが必要です．
Webブラウザは，皆さんが日常的に利用しているはずです．Webサーバは，読者の多くが使っていないと思いますので，別途用意する必要があります．
Pythonに含まれる標準ライブラリを使ってWebサーバを用意します．Webサーバを走らせるためには，普段使っているパソコンを使います．Pythonをインストールしたパソコンが1台あれば，Webアプリケーションを作ることができるわけです．

サーバを用意する？難しい！と想う人がいますかもしれないが，実際，http.serverというモジュールを使って簡単なWebサーバを起動することができます．方法はとても簡単で，たった1行のコマンドを実行するだけです．
`python3 -m http.server 8000`
Webサーバを起動した後は，Webサーバの稼働状態がメッセージとして表示されます．

```
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
localhost - - [25/Dec/2018 15:19:58] code 404, message File not found
```

Webブラウザを使って「http://127.0.0.1:8000/」というURLにアクセスします．状況によってWebブラウザに表示される内容は違います．

127.0.0.1というのはループバックアドレスと呼ばれるIPアドレスです．Webブラウザと同じマシンを指しています．コロン(:)の後ろはポート番号です．ブラウザでアクセスしているURLは，「Webブラウザと同じマシンのポート8000番で通信を受け付けているWebサーバにアクセスする」という意味を持っています．
Webサーバを止めるには，LinuxやMac OS XではCtrl+Cを押します．Windowsの場合は本来Ctrl+Zを押します．終了できない場合があるので，その時ウィンドウを閉じてください．

http.serverモジュールを使ったWebサーバでは，起動したときのカレントフォルダ(ディレクトリ)にあるファイルを見せるような動きをします．先ほどWebブラウザに表示されていたファイルのリストは，Webサーバを起動したフォルダ(ディレクトリ)にあったファイルだったわけです．このように，Webサーバがファイルを扱う起点となるフォルダのことをドキュメントルートと呼ぶことがあります．

ドキュメントルートに相当する場所にindex.htmlという名前のHTMLファイルを置くと，Webブラウザにはそのファイルが表示されます．

![image.png](https://qiita-image-store.s3.amazonaws.com/0/179248/ad9d0509-3e7d-2905-9202-f3e5864213f3.png)

```simpleserver.py
import http.server

server_address = ("", 8000)
handler_class = http.server.SimpleHTTPRequestHandler #ハンドラを設定
simple_server = http.server.HTTPServer(server_address, handler_class)
simple_server.serve_forever()
```

```index.html
<html><body>
Python is awesome !
</body></html>
```
ファイルを設置したら，Webサーバを起動します．(コマンドは以下)
`$ python3 simpleserver.py`

すると，Webサーバが立ち上がります．
この状態で，Webブラウザを使って[http://127.0.0.1:8000/](http://127.0.0.1:8000/)という，先ほどと同じURLにアクセスします．すると，index.htmlの内容が表示されるはずです．

今回の「PythonでWebアプリケーション（2）」は以上で終わります．
読んでいただいてありがとうございます．

次回予告：簡単なプログラムを動かしたいと思います．
PWA: Workbox Cache Expiration 和訳

# Workbox Cache Expiration

参照元ページ: https://developers.google.com/web/tools/workbox/modules/workbox-cache-expiration

## Workbox Cache Expirationとは？

どれだけ数のファイルをキャッシュさせるか、もしくはキャッシュ期間に制限をかけたくなることはよくあります。Workboxでは`workbox-cache-expiration`プラグインを使うことで、キャッシュされるエントリー数もしくは長期間キャッシュされたエントリーを削除することができます。

## キャッシュエントリー数の制限

キャッシュに保存されるエントリー数を制限するために、`maxEntries`の設定を使用できます。

```js

workbox.routing.registerRoute(
  new RegExp('/images/'),
  new workbox.strategies.CacheFirst({
    cacheName: 'image-cache',
    plugins: [
      new workbox.expiration.Plugin({
        maxEntries: 20,
      }),
    ],
  })
);
```

これにより[プラグイン](https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.expiration.Plugin)がこのルーティングに追加されます。
キャッシュされたレスポンスが使用されたり、新しいリクエストがキャッシュに追加されると、プラグインは設定を見に行ってキャッシュエントリー数が設定されている値を超えないように調整します。もし超えてしまう場合は、**一番古いエントリーから削除されていきます**。


## キャッシュ期間の制限 
あるリクエストがどれくらいの期間キャッシュされるかは`maxAgeSeconds`の設定で秒数で指定します。

```js

workbox.routing.registerRoute(
  /\/images\//,
  new workbox.strategies.CacheFirst({
    cacheName: 'image-cache',
    plugins: [
      new workbox.expiration.Plugin({
        maxAgeSeconds: 24 * 60 * 60,
      })
    ]
  })
)
```

各リクエストもしくはキャッシュの更新後にプラグインはエントリーをチェック&削除します。

ここで注意すべきことは、

- IndexedDBを開くのは時間がかかるため、リクエストが使用されるまで期限は有効になりません。つまり、期限が切れたリクエストは一度使われることがありますが、その後は期限切れとして扱われます。
- これを軽減するために、プラグインはキャッシュされたレスポンスの "Date"ヘッダをチェックします。もし日付が解析できるのであれば、IndexedDBルックアップを必要としないため、これに基づいて期限が切れます。

## 応用編

[CacheExpiration](https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.expiration.CacheExpiration)クラスを使えば、他のWorkboxモジュールと切り離してExpirationロジックを使うことができます。

キャッシュに制限をかける場合、コントロールしたいキャッシュに対して`CacheExpiration`のインスタンスを作成します。

```js

const cacheName = 'my-cache';
const expirationManager = new workbox.expiration.CacheExpiration(
  cacheName,
  {
    maxAgeSeconds: 24 * 60 * 60,
    maxEntries: 20,
  }
);
```

キャッシュエントリーを更新したい場合、"age"を更新するために`updateTimestamp()`メソッドを呼ぶ必要があります。

```js

await openCache.put(
  request,
  response
);

await expirationManager.updateTimestamp(request.url)
```

そのあとエントリーを保存期間切れにしたい時は、`expireEntries()`メソッドを呼びます。`expireEntries()`メソッドは`maxAgeSeconds`と`maxEntries`の設定を元にエントリーを保存期間切れにします。
参考: https://developers.google.com/web/tools/workbox/reference-docs/v2.1.3/module-workbox-cache-expiration.CacheExpiration

```js

await expirationManager.expireEntries();
```
Mysql ERROR 1396 (HY000): Operation DROP USER failed for


#エラーメッセージ
```
ERROR 1396 (HY000): Operation DROP USER failed for
```

#解決策

```sql
drop user 'ユーザー名'; --既存のユーザー名
```
#なぜ起こったか
**作成しようとしたユーザーが既に存在するため**
#既存のユーザーを確認するには

```sql
SELECT User, Host FROM mysql.user;
```

#参考ページ
https://bugs.mysql.com/bug.php?id=28331
>Description:
When using CREATE USER to create a user account when it already exists, the error message is very unclear.
>>CREATE USERを使用して、すでに存在するユーザーアカウントを作成するときに、エラーメッセージが非常に不明瞭になる

>How to repeat:
mysql> create user 'world';
Query OK, 0 rows affected (0.00 sec)

>mysql> create user 'world';
ERROR 1396 (HY000): Operation CREATE USER failed for 'world'@'%'

>Suggested fix:
mysql> create user 'world';
Query OK, 0 rows affected (0.00 sec)

>mysql> create user 'world';
ERROR 1396 (HY000): Operation CREATE USER failed for 'world'@'%', user already exists

#参考ページ２
**MySQLで、ユーザーの一覧を確認するコマンド**
http://www-creators.com/archives/309


---
推奨される修正
と書いてあるから、修正されているのかと思った。
しかし、私のコマンドプロンプトには、適応していないようだ。
emblukをwin10へインストール

# embulkインストール
[github](https://github.com/embulk/embulk)のreadme.md通りにすればすんなりインストールできる。
インストールすると「embulk.bat」ができるのでパスを通しておこう。

# pluginのインストール
bundleを使えば、仮想環境みたいにpulginを管理できるみたい。

## 新規にbundle作成

```
>embulk mkbundle bundle
```
Gemfileを編集する。

```
source 'https://rubygems.org/'

# No versions are specified for 'embulk' to use the gem embedded in embulk.jar.
# Note that prerelease versions (e.g. "0.9.0.beta") do not match the statement.
# Specify the exact prerelease version (like '= 0.9.0.beta') for prereleases.
gem 'embulk'
gem 'embulk-output-command'    #追加
gem 'embulk-output-postgresql' #追加
gem 'embulk-filter-to_csv'     #追加
```

## pulginインストール実行

```
>cd bundle
>embulk bundle install --path=vendor/bundle
```

## pluginのリスト
```
>embulk bundke list
2019-03-17 17:32:05.660 +0900: Embulk v0.9.16
Gems included by the bundle:
  * bundler (1.16.0)
  * embulk (0.9.16)
  * embulk-filter-to_csv (0.0.1)
  * embulk-output-command (0.1.4)
  * embulk-output-postgresql (0.8.2)
  * liquid (4.0.3)
  * msgpack (1.1.0)
```

## pluginのupdate

```
>embulk bndle update
2019-03-17 17:34:03.220 +0900: Embulk v0.9.16
Fetching gem metadata from https://rubygems.org/..............
Fetching gem metadata from https://rubygems.org/.
Resolving dependencies...
Using bundler 1.16.0
Using liquid 4.0.3
Using msgpack 1.1.0 (java)
Using embulk 0.9.16 (java)
Using embulk-filter-to_csv 0.0.1
Using embulk-output-command 0.1.4
Using embulk-output-postgresql 0.8.2
Bundle updated!
```
Androidの自動バックアップ機能の罠

# Androidの自動バックアップ機能の罠

Androidのバックアップ機能についての知見をまとめておきます。

記事内で使用する用語は以下の前提で記載します（[公式ドキュメントの表][8]）

* 自動バックアップ：Android Auto Backup
* Key/Valueバックアップ：Key/Value Backup (Android Backup Service)

### ▼ 自動バックアップ機能の有効・無効について

自動バックアップ機能は、ドキュメントにあるようにallowBackupがtrueで有効になります。

```xml:AndroidManifest.xml
<manifest ... >
    ...
    <application android:allowBackup="true" ... >
        ...
    </application>
</manifest>
```

しかし、ここに罠があります。
[allowBackupのドキュメント][2]には、デフォルトで有効になっているとの記載があります。
ということは、特に意識せずにアプリを開発していた場合、意図せず自動バックアップが有効になっていることになります。

### ▼ Applicationクラスのサブクラスが呼ばれない

自動バックアップによるデータ復元時は、[Back up user data with Auto BackupのImplement BackupAgent][3]、[Test backup and restoreのApp methods not called][4]に記載があるようにApplicationクラスを継承したサブクラスを使用している場合、サブクラス側で実行されず、基本のApplicationクラスがインスタンス化されて実行されるようです。
ここで、復元後にアプリを起動したときも、サブクラス側がインスタンス化されない問題が発生しました。
Application#onCreateをオーバーライドして、そこが実行される前提で実装しており、クラッシュするなどの問題が発生しました。

```xml:AndroidManifest.xml
<manifest ... >
    ...
    <application android:name=".SubApplication" ... >
        ...
    </application>
</manifest>
```

### ▼ 自動バックアップ時のデータで復元されてしまう

1. 自動バックアップが意図せず有効でリリースしてしまう
2. リリースして、かなり経過後、自動バックアップを無効に変更
3. Key/Valueバックアップ実装版をリリース

3をリリース後、自動バックアップによるデータ復元が動作しているようなクラッシュログがレポートされました。
実際に動作検証してみたところ、自動バックアップが有効になっていたときにバックアップデータが作成されていると、自動バックアップによる復元が動作することがわかりました。
[android:fullBackupOnly][6]の設定によりKey/Valueバックアップではなく、自動バックアップを優先させる方法もありますが、こちらは無効の状態でした。

自動バックアップで復元してほしくないファイルは、[android:fullBackupContent][7]の設定で対応できました。

```xml:my_backup_rules.xml
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
    <exclude domain="file" path="."/>
    <exclude domain="database" path="."/>
    <exclude domain="sharedpref" path="."/>
    <exclude domain="external" path="."/>
    <exclude domain="root" path="."/>
</full-backup-content>
```

```xml:AndroidManifest.xml
<manifest ... >
    ...
    <application android:fullBackupContent="@xml/my_backup_rules" ... >
        ...
    </application>
</manifest>
```

# 参考文献

[allowBackup が true のときに Application Class が呼ばれないことがあって困った話][5]


[1]:https://developer.android.com/guide/topics/data/autobackup#EnablingAutoBackup
[2]:https://developer.android.com/guide/topics/manifest/application-element.html?hl=ja#allowbackup
[3]:https://developer.android.com/guide/topics/data/autobackup#ImplementingBackupAgent
[4]:https://developer.android.com/guide/topics/data/testingbackup
[5]:https://qiita.com/notona/items/126f0a10707931a735fc
[6]:https://developer.android.com/guide/topics/manifest/application-element#fullBackupOnly
[7]:https://developer.android.com/guide/topics/manifest/application-element#fullBackupContent
[8]:https://developer.android.com/guide/topics/data/backup.html#Choosing
NeuralNetworkConsoleでの初期設定


# NeuralNetworkConsoleを試してみる
数年前のリリース時の記事を見たときには「触ってみたい！」と思ったのに、
そのまますっかり忘れていたのだが、先日以下で改めていろいろと紹介してもらったので触ってみることに。

+ 2019年3月期AITCオープンラボ「身近になったAI開発シリーズ第2回」
+ ～Neural Network Consoleのご紹介～
+ https://aitc-openlab.connpass.com/event/120303/


## 初期設定
+ 起動そのものは簡単
+ チュートリアルを実行して学習結果がでるところまではあっさりと完了。

<img width="529" alt="2019-03-17_14h07_10.png" src="https://qiita-image-store.s3.amazonaws.com/0/106639/3c9907fb-d79a-ce55-1f4d-213b4ce34260.png">


## GPUモードも試してみる
+ 今週気が付いたのだが、自分の端末にGeForceが入っていたのでGPUも使えるのかどうかも確認してみることにする。
+ オープンラボでも「安くてもGPUを使った方がいい！」って言ってたしね。
+ マニュアルに書いてある通り、設定でEngineのProcessorをGPUに変更し、チュートリアルを実行してみるとエラー

<img width="769" alt="2019-03-17_14h01_45.png" src="https://qiita-image-store.s3.amazonaws.com/0/106639/c227c570-0aa0-d302-47e5-c439923c6bdb.png">


```
Failed status == CUDNN_STATUS_SUCCESS: NOT_INITIALIZED
```

<img width="535" alt="2019-03-17_14h09_08.png" src="https://qiita-image-store.s3.amazonaws.com/0/106639/7f3c2f7e-7b40-3038-2b7c-37d73254719f.png">

+ たぶんGPUが有効になっていないんだろうな。なにぶん購入してから一年ぐらい経ってるのに今更GPUが入っているのに気が付いたぐらいだし。
+ 初めて気が付くNVIDIAコントロールパネル
+ ここでプログラム設定ができるっぽいので設定をかえてみることに。

<img width="767" alt="2019-03-17_14h19_36.png" src="https://qiita-image-store.s3.amazonaws.com/0/106639/e5df418e-1e77-17c6-4842-b7181023adf6.png">

+ が、変わらず。
+ GeForceExperienceを設定していなかったので設定してみる

<img width="597" alt="2019-03-17_14h43_55.png" src="https://qiita-image-store.s3.amazonaws.com/0/106639/f9af85ec-41e4-1d7e-0f04-70ee9c8c5201.png">

+ CUDA Toolkit をインストール
+ 現状の最新の10.1
    + 途中でアダプタも入れる？って聞かれるのでOKOK。もう入っている気もするけど。。
    + マシンの再起動も必要とのこと。
+ cudnnも必要？

<img width="340" alt="2019-03-17_14h36_17.png" src="https://qiita-image-store.s3.amazonaws.com/0/106639/cbcc8768-d661-12d5-031b-b5e47b8a240e.png">

+ 最新のドライバにしろみたいなこともマニュアルに書いてあるのでアップデートしてみる
    + https://developer.nvidia.com/cudnn

+ が、変わらず・・・
<img width="553" alt="2019-03-17_15h47_33.png" src="https://qiita-image-store.s3.amazonaws.com/0/106639/4bc5c2aa-52c7-1671-5f44-1f7e1f04d062.png">

+ ふと気が付くとNVIDIAコントロールパネルが消えてしまっている・・・
    + もう一回Experoenceをインストール
    + この中でクリーンインストールを選択。
    + このインストール後に試してみたら上手くいった。なぜだ・・・
<img width="406" alt="2019-03-17_16h27_26.png" src="https://qiita-image-store.s3.amazonaws.com/0/106639/e1edd03c-e4d6-c51e-5a35-1b4112ef2a48.png">
    + 原因がまったくわからないが、最新にしたのがよかったのか？

## まとめ
とりあえずGPUでも使えるようになったということでいいのかな。
本当はもう少しチュートリアルをいろいろとやってみようかと思ったのだが、ここまでが長かったのでいったん終了。
Python 内部クラスについて

## 目次
+ 内部クラス
+ ソースを動かしてみる
+ まとめ

## 内部クラス
ソースだとこんなの

```test.py
import sys

class Outer:
    def __init__(self):
        print("create Outer Class")
        print(self.Inner)
        # self.Innerのメモリサイズを確認
        print(sys.getsizeof(self.Inner))

    class Inner:
        def __init__(self):
            print("create Inner Class")

outer = Outer()

```

内部クラス＝クラスの中のクラスのこと
英語だとinner class とかnested classって言うよ〜

## ソースを動かしてみる

```console
$ python test.py 
create Outer Class
<class '__main__.Outer.Inner'>
1056
```

Innerクラスはインスタンス化されてないので```print("create Inner Class")```は実行されていません。
しかし、self.Innerはしっかり存在して、メモリサイズも出てきています。

## まとめ
内部クラスのメリットとして以下が考えられます。

+ ソース上で依存関係を表せる
+ 外側クラスのインスタンス化を強制できる

逆にデメリットとして

+ 余計なメモリを消費している

機械学習などで大量のデータを扱うクラスに関しては、リソースの節約のために内部クラスを使用しないのがベターな気がしました。
ご意見、ご指摘は大歓迎です。むしろ他の人はどう考えているのか知りたいです。よろしくお願いします。。
複数のメモリースキームに対応したPimplの進化版イディオム「朧式」の提案

# 朧式
朧式（おぼろしき）は、複数のメモリースキームへの対応を可能にした、Pimplの進化版です。

以下の特徴があります。

* **Pimplより、更に記述が面倒**
* 公開classから、private要素を完全に除去
* ヒープを扱うunique_ptrやshared_ptrの他、**独自アロケータ用のポインタと併用が可能**


# リポジトリ
[朧式（おぼろしき） Unlicense](https://github.com/EmuraDaisuke/Idiom.OboroShiki)


# 概要
朧式は、公開classと実装classの相互reinterpret_castが全てです。

~~~cpp
実装class* Cast(公開class* p){ return reinterpret_cast<実装class*>(p); }    // 内向きに使うcast
公開class* Cast(実装class* p){ return reinterpret_cast<公開class*>(p); }    // 外向きに使うcast
~~~


# 複数のメモリースキームへの対応例
## local_ptr
以下の特徴があります。

* スタック上にインスタンスを生成する
* unique_ptrと同様、唯一の所有権を持つ
* ブレースを抜ける際にインスタンスを破棄する
* 関数を抜ける際にスタックを開放する
* **ループ内で使うと、スタックを大量に消費する可能性があるので、注意が必要(対策可能)**

## 使い方
公開classに以下のメソッドを追加します。

~~~cpp
static std::size_t local_size();    // 実装classの実体サイズを返す
void local_init([引数...]);         // コンストラクタのラッパー
~~~

インスタンスを生成します。

~~~cpp
auto p = make_local(型[, 引数...]);
~~~

## スタックオーバーフローへの対応
対策なし（ループ回数が増えると動作しない）

~~~cpp
for (int n = 回数; n; --n){
  auto p = make_local(型);
}
~~~

対策済み（ループ回数が増えても問題ない）

~~~cpp
for (int n = 回数; n; --n){
  []{
    auto p = make_local(型);
  }();
}
~~~


# ビルド＆テスト
検証を行った環境は以下のとおりです。

* Windows 10 Pro 64bit  
* Core i7-8700 3.20GHz  

## **Msvc**
Microsoft(R) C/C++ Optimizing Compiler Version 19.15.26732.1 for x64
Microsoft (R) Incremental Linker Version 14.15.26732.1

~~~sh
cl Main.cpp Pimpl.cpp OboroShiki.cpp -Ox -EHsc -openmp -Fe:TestMsvc.exe
TestMsvc.exe
~~~

## **clang++**
clang version 7.0.0 (tags/RELEASE_700/final)
Target: x86_64-w64-windows-gnu

~~~sh
clang++ Main.cpp Pimpl.cpp OboroShiki.cpp -O3 -o TestClang++.exe
TestClang++.exe
~~~

## **g++**
gcc version 8.2.0 (Rev3, Built by MSYS2 project)
Target: x86_64-w64-mingw32

~~~sh
g++ Main.cpp Pimpl.cpp OboroShiki.cpp -O3 -fopenmp -o TestG++.exe
TestG++.exe
~~~


# ベンチマーク
単位は秒で、数値が低いほど高速です。

## **Msvc**
|100,000,000回|Pimpl.unique_ptr|朧式.unique_ptr|朧式.local_ptr|
|:---|---:|---:|---:|
|生成と破棄(Normal)|3.77094619|3.77549581|**0.60779183**|
|生成と破棄(OpenMP)|0.66776221|0.67680080|**0.07440678**|
|メソッドの呼び出し|0.09252870|0.09257201|0.09184603|

## **clang++**
|100,000,000回|Pimpl.unique_ptr|朧式.unique_ptr|朧式.local_ptr|
|:---|---:|---:|---:|
|生成と破棄(Normal)|3.85479482|3.84132531|**0.48662753**|
|生成と破棄(OpenMP)|N/A|N/A|N/A|
|メソッドの呼び出し|0.09045938|0.09064752|0.09042264|

## **g++**
|100,000,000回|Pimpl.unique_ptr|朧式.unique_ptr|朧式.local_ptr|
|:---|---:|---:|---:|
|生成と破棄(Normal)|3.87667382|3.89724941|**0.46185907**|
|生成と破棄(OpenMP)|0.71910911|0.72185421|**0.05114993**|
|メソッドの呼び出し|0.09052177|0.09079541|0.09055738|

## 所感
同じunique_ptrであれば、Pimplと朧式で、差がないことが分かります。

「生成と破棄」のNormalとOpenMPを比較すると、unique_ptrでは5～6倍の高速化に対し、local_ptrでは8～9倍と、差が開いていることが分かります。
これは、メモリーの確保と解放に排他制御が必要なunique_ptrに対し、local_ptrでは排他制御が不要な為、より効率良く並列化できていることを示しています。

「メソッドの呼び出し」では、特に目立った環境差はなく、安定した結果となりました。


# 余談
如何だったでしょうか？

イディオムよりも、local_ptrの紹介になってる気がしなくもないですが、これは両輪のようなものです。
複数メモリースキームの運用であれば、メリットがあることを示せたと思います。

単一メモリースキームの運用では、手間ばかり掛かって「僅かばかりの体裁の良さ」しか得られない悲しみ。
reinterpret_castに抵抗感のある方もいらっしゃるでしょう。

評価の難しいイディオムだと思いますが、適所での活用が良いでしょう。
GitHub repository で管理している画像を自動で圧縮 - ImgBot の紹介

## ImgBot

- [ImgBot - Optimize your images on GitHub][ImgBot HP]

> A GitHub app that optimizes your images

ということで ImgBot とは **GitHub repository で管理している画像を監視して、最適化が可能であれば圧縮などを施した Pull request を自動的に作成してくれる GitHub App** です。

Hugo や Jekyll などの静的サイトジェネレーターと相性がいいと思います。テーマを画像圧縮に対応させたり、自分でその処理のためのスクリプトを書いたりするのが面倒な時に便利でしょう。

Public Repository であれば無料で導入できます。



## 導入方法

- [ImgBot HP]

上記リンク先の `Install now` をクリックして GitHub と連携すればすぐに利用可能です。すべてのリポジトリに適応するか、指定のリポジトリに限定するかを選択できます。

最適化可能な画像があれば自動的に Pull request を作成してくれるので、ユーザーはそれをマージするだけです。簡単ですね。

例えば以下のような Pull request が作成されます。16% の圧縮が可能だと教えてくれています。

- [[ImgBot] Optimize images by imgbot · Pull Request #6 · peaceiris/emoji-ime-dictionary](https://github.com/peaceiris/emoji-ime-dictionary/pull/6)

![imgbot.jpg](https://qiita-image-store.s3.amazonaws.com/0/285675/21a7e43c-a009-cff6-53d5-2125b088fb38.jpeg)



## ボットの詳細設定

- [ImgBot - Docs][ImgBot Docs]

`.imgbotconfig` を置くことで ImgBot の詳細な設定が可能です。

```json
{
    "schedule": "daily",
    "ignoredFiles": [
        "*.jpg",
        "image1.png",
        "public/special_images/*",
    ],
    "aggressiveCompression": "true"
}
```

### 監視時間間隔

`schedule` に `daily` `weekly` `monthly` などの値を設定して。ImgBot を毎日巡回させるのか、週一なのか、月一なのかを設定できます。

### 監視対象外画像

`ignoredFiles` に監視したくない画像を設定可能です。

例えば「オリジナル画像を残しつつ、圧縮した画像も別で保存したい」なんて使い方をしたいこともあるでしょう。

そのような場合は `images/org` と `images/min` に画像を保存しておき、`ignoredFiles` に `images/org` を指定しておけば良いでしょう。

`**/test_images/**` や `path/to/prefix*` のような指定も可能です。

### もっと圧縮したい

`"aggressiveCompression": true` と指定すれば lossless ではない積極的な圧縮も可能になります。デフォルトは `false` です。



[ImgBot HP]: https://imgbot.net/
[ImgBot Docs]: https://imgbot.net/docs/