OmniAICreator/Qiita-1.07M huggingface dataset:
jQueryでアコーディオン

#jQueryでアコーディオンを作る

&lt;script&gt;タグの中と&lt;dl&gt;タグのところを丸ごとコピーして
貼り付けたら動くので、あとはタイトルとかを好きに変えたりメソッドチェーンでCSSを付け外しする処理を加える。その辺の完成形はググればいくらでも書いてあると思うので最低限これで動くというところを残しておく。
気が向いたら下に追記するかも。

```html
<!DOCTYPE html>
<html>
<head>
    <meta content="ja" charset="utf-8">
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <link href="style.css" type="text/css" rel="stylesheet">
<script>
   $(function(){
        var accordion = $('.accordion');
        $('.accordion > dd').hide().eq(0).show();           //ddを全て隠した後、一番上だけ表示
        $(">dt",accordion).click(function(){                //クリックした時
            if($(this).next('dd').css('display')=='none'){  //クリックしたdt直下のddが消えていたら
                $('>dd',accordion).slideUp("fast");         //全てのddを閉じて
                $(this).next("dd").slideDown("fast");       //クリックした直下のddを開く
            }
        })
   });
</script>

</head>
<body>
    <dl class="accordion">
        <dt>タイトルA</dt>
        <dd>データA</dd>
        <dt>タイトルB</dt>
        <dd>データB</dd>
        <dt>タイトルC</dt>
        <dd>データC</dd>
        <dt>タイトルD</dt>
        <dd>データD</dd>
    </dl>
</body>
</html>
```
【備忘録】centos7にOpenCV2系を入れようとして詰まったお話

## 経緯
Railsの環境に`ruby-opencv`入れるために`OpenCV2`入れようとしたら数日詰まってしまったんです。
ぶっちゃけ誰がcentos7にOpenCV2入れるんだよって感じですが、誰かの助けになれればいいかなって思ったり。

## どうでもいいから手順だけ欲しい人向け
```
sudo yum install epel-release
yum install build-essential cmake pkg-config
yum install libavcodec-dev libavformat-dev libswscale-dev libv4l-dev
yum install libxvidcore-dev libx264-dev
#### ぶっちゃけcmakeしか入らないと思う
wget -O opencv.zip https://downloads.sourceforge.net/project/opencvlibrary/opencv-unix/2.4.13/opencv-2.4.13.zip
#### 上記zipを解凍
cd oepncv-2.4.13
mkdir release
cd release
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local ..
make
sudo make install 
## OpenCVだけならここまで

sudo gem install ruby-opencv -- --with-opencv-dir=/usr/local/lib
```

## 上記を１個づつ説明（メモ）していくよ
```
yum install cmake
```
コンパイルするやつ。他は入らなかったからなにかわからんちん＾ｑ＾

```
wget -O opencv.zip https://downloads.sourceforge.net/project/opencvlibrary/opencv-unix/2.4.13/opencv-2.4.13.zip
```
なんかZipファイルをDLしてるよ。

```
cd oepncv-2.4.13
```
ここなんだけど。解凍しないとこのフォルダ出来ないよ。  
解凍してなくて詰まったよ。
私はsamba入れてたからGUIで解凍したけどコンソールからだと`unzip`かというコマンド使うらしいよ。知らんけど。
詳細は調べてね。

```
mkdir release
```
説明要らんでしょ。

```
cmake -D CMAKE_BUILD_TYPE=RELEASE -D CMAKE_INSTALL_PREFIX=/usr/local ..
make
```
上記２つはわからん。多分コンパイルやらしてる。

```
sudo make install
```
言わずもがな。インストール。


### 参考サイト
* https://qiita.com/tatsuo98se/items/952631e49c8112d4ef1b
* https://qiita.com/takewaka/items/a97e5a0a3831d6c849c3
c++で複数の数値データをcsvに出力する。part2

[前回の記事](https://qiita.com/embetan/items/dfef7c77d8b5f9374bc7)では、列数が２つのデータを扱ったため、std::pairを使っていたが、今回は列数が３つのデータ以上のデータを扱うために、tupleを使ってデータを取得する。

```output.cpp

#include <iostream>
#include <fstream>
#include <vector>
#include <tuple>

int main()
{
	//出力したいファイル名を指定する
	std::ofstream outputfile("test.csv");

	std::vector<std::tuple<int, int, int>>data;
	//出力したいデータを作る
	data.emplace_back(1, 3, 4);
	data.emplace_back(2, 3, 5);
	data.emplace_back(7, 2, 8);
	//データの出力
	for (auto d : data) {
		outputfile << std::get<0>(d);
		outputfile << ',';
		outputfile << std::get<1>(d);
		outputfile << ',';
		outputfile << std::get<2>(d);
		outputfile << '\n';
	}
	//ファイルの出力
	outputfile.close();
}
```
[Vue.js]tableの中にコンポーネントが描画されない

tableを作製し、tbodyの一部をコンポーネントで描画しようとしたが、
tableの外で描画されてしまった時の対処法。

###外に描画される書き方。
```vue

<div id="app">
 <thead>
  <td>ブログコメント</td>
 </thaed>
 <tbody>
  <blog-comment>
  </blog-comment>
 </tbody>
</div>

//BlogCommentテンプレート
<tr>
 <td>
 {{ data }}
 </td>
</tr>
```

このような書き方をしてしまうとVueが動く前に`<tbody>`のなかの`<tr>`ではない要素に関しては、弾かれてしまいます。なので以下のように修正します。

###修正コード
```vue

<div id="app">
 <thead>
  <td>苗字</td>
 </thaed>
 <tbody>
  <tr is=blog-comment>　　
　</tr>
 </tbody>
</div>

//BlogCommentテンプレート
<tr>
 <td>
 {{ data }}
 </td>
</tr>
```

この記事にたどり着くまで結構時間かかってしまったので、お役に立てれば幸いです。

>https://vuejs.org/v2/guide/components.html#DOM-Template-Parsing-Caveats
gunicornを動かしたい(ubuntu+nginx+django+gunicorn)

gunicornを使ってみたい。
サーバ周りの知識が足りないため、公式サイトやQiitaやブログの記事を参考に進めてみるも、なかなか上手くいかなかった。
なんとかvagrantでgunicornを設定して表示できるようになったので、メモ。


「gunicornというアプケーションサーバ（wsgiサーバ）を、ubuntu 18.04のsystemdのunitで自動で起動して動作するようにしたい。」


以下のサイトが参考になった。（今回はDBの設定は飛ばした）
https://www.digitalocean.com/community/tutorials/how-to-set-up-django-with-postgres-nginx-and-gunicorn-on-ubuntu-18-04

※以下の部分は、エラーがでた。

```
$ gunicorn --bind 0.0.0.0:8000 myproject.wsgi
↓
$ cd .. && gunicorn --bind 0.0.0.0:8000 myproject.wsgi 
```

参考：
https://codeday.me/jp/qa/20190301/343639.html

上記を参考に、gunicorn.seviceファイルのWorkingDirectoryのパスはdjangoのsettings.pyのあるディレクトリに設定する。




時間ができたら、手順をまとめたい。。

---

さらに知識を深めるためのメモ

Linux女子部 systemd徹底入門
https://www.slideshare.net/enakai/linux-27872553

systemd サービスユニット覚書
https://qiita.com/ch7821/items/369090459769c603bb6b

Systemd入門(1) - Unitの概念を理解する
http://enakai00.hatenablog.com/entry/20130914/1379146157

いまさらだけどsystemdに入門した
http://yukiyan.hatenablog.jp/entry/2015/10/18/190639

調べなきゃ寝れない！と調べたら余計に寝れなくなったソケットの話
https://qiita.com/kuni-nakaji/items/d11219e4ad7c74ece748

---
わかりやすかったけど今の理解力では、うまくいかなかった。。。


Deploying Gunicorn systemd
http://docs.gunicorn.org/en/stable/deploy.html#systemd

nginxとgunicornでDjangoの本番環境を構築する(2018年9月最新版)
https://qiita.com/takehiro_t59/items/bf4af2da35a4281f403f
Yubikey を Linux で使用する方法ーFirefox 編ー

Yubikey を使用して、多要素認証する方法、
1. about:config をアドレスバーに入れる、
2. u2f と検索する、
3. security.webauth.u2f と言う項目を false から true に変更する。

以上。


参考URL
https://support.yubico.com/support/solutions/articles/15000017511-enabling-u2f-support-in-mozilla-firefox
Python学習

# Pythonの基礎・応用

## リスト型・辞書型

* 五十音を一文字ずつリスト型へ展開してみよう

```python
"""
あいうえお
かきくけこ
さしすせそ
たちつてと
なにぬねの
はひふへほ
まみむめも
や　ゆ　よ
らりるれろ
わ　　　を
ん
"""
list = ['あ','い','う','え','お'...]
```

* 五十音を'あ'からインデックスで引き当てられるように辞書型を作成してみよう

```python
list = ['あ','い','う','え','お'...]
dict = {1:'あ',2:'い',3:'う',4:'え',5:'お'}
```

* 五十音からインデックスを引き当てられるように辞書型を作成してみよう

```python
list = ['あ','い','う','え','お'...]
dict = {'あ':1,'い':2,'う':3,'え':4,'お':5}
```

※社内学習向けに記載した記事を全体公開
Firebaseの落とし穴　カスタムパラメータ編

# Firebase で穴という穴に入った話

こんにちはFirebase男ことiOSエンジニア木村知史です。
現在会社にて主にFirebase Analyticsの実装を担当しています。

最近Firebaseがかなり更新されていますよね。
記事を探しても古いものがかなり多く苦労しました。いや、まだ苦労(not setで)しています。

この記事は単純に自分がかなり長く引っかかった問題について書きます。
この問題で他の人が自分と同じように時間を無駄にしないようにと思います。

さて本題です。

## カスタムパラメーターの値は"UTF-8"で100バイトまでです！！！:boy_tone1:

シンプルにこれです。

以下のFirebaseの公式ドキュメントによると100文字と書いてあります。

<img width="706" alt="スクリーンショット 2019-10-07 22.56.54.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/270095/33da3bf2-a925-d787-1ac2-6f725b4d3a64.png">


それだけです。これに引っかからないようにしましょう。
iOSでString.length()で文字数をカウントすると100文字以上になりエラーログも出ず、
DebugViewでもエラーとして表示されず、ただパラメーターが出てこない現象が起きます。

iOSでutf-8でのカウント

```Swift:test.swift
str.utf8.count > 100
```

[公式Firebaseドキュメント]
(https://support.google.com/firebase/answer/9237506?hl=ja)
https://support.google.com/firebase/answer/9237506?hl=ja
32bit ラップトップPCをLubuntu 18.04 LTSで再利用

生き返れ生き返れ...

備忘録的に詰まったところや、要点のみ書いていく

# 環境
NEC LaVie Light BL300/T
＼日本電気／

# OSインストール
## ディストロ
とりあえず軽いLinux + デスクトップ環境ということで、
Lubuntu 18.04 LTS i386 (32bit)
を使用。

`Ubuntu` をベースにした軽量ディストロ。
デスクトップ環境は `GTK+` ツールキットを使用する `LXDE` 。
同じく軽量級Ubuntuライクなものに、交野市にも導入された `Xubuntu` があるが、RAMの要求がその半分以下というおっそろしいもの。

## iso用意とか
日本のミラーがJAISTにあったのでそこから落とす。
USBに焼き込み、そっちからインスコ。
起動したら、特に余計な設定を加えずに標準でインストール。

# 初期設定
ここからインストール完了後の設定。
GUIで行っていく。

## ネットワーク設定
Ethernetは接続して使えていたが、Wifiがつながらない。
Lubuntuのデスクトップ環境は(18.04当時は)LXDEを使っていて、
ネットワーク設定も有ることには有るが、調子が悪い。

似たようなもんだろ(ﾊﾅﾎｼﾞ)とUbuntu18.04の設定を調べていると、
`netplan` 使おうね、という話があったので、やってみることに。
参考にさせていただいた[翻訳+解説がこちら](https://www.komee.org/entry/2018/06/12/181400)。
で、[こちらが公式](https://netplan.io/)。

まずwifiドングルの名前を確認。
GUIで行っていく、とか言った矢先にターミナル。熱い手のひら返しである。
好きです、ターミナル。

`ifconfig` が使えなくて焦った。入ってないらしい。
このため `ip` で代用。

```shell
$ ip addr
```

wifiドングルっぽいものの名前を控えておいて、
`/etc/netplan/` 内に任意の名前でyaml形式のファイルを作成。
netplanはこのディレクトリ以下のyamlを読んで設定できるようだ。
編集の際には `sudo` 忘れずに。

```yaml:/etc/netplan/01-network-manager-all.yaml
network:
  versions: 2
  renderer: networkd
  wifis:
    wls2s0: # ここにドングル名。
      dhcp4: true
      access-points: 
        "SSID-is-here":
          password: "password_is_here"
```

SSIDとパスワードは適宜。
レンダラーは最初はnetwork-manager経由で使っていたので `NetworkManager` としていたのだがつながらず、
`networkd` でつながった。

最後に設定を反映。

```shell
$ sudo netplan apply
```

## 言語
言語設定は、
`スタート` > :gear: `設定` > `言語サポート`
から。
入ってすぐの設定をインストールし、いつもの `ibus-mozc` も入れておく。

```shell
$ sudo apt install ibus-mozc
```

## キーボード
WinのJISキーボードなのだが、Caps Lockの位置にCtrlを持ってきたい。
Xmodmapを使って設定する。

CapsLockと左Ctrlを入れ替えるので、それぞれのキーコード、名称を確認。

```shell
$ xev
```

これを起動するとウィンドウがでてくるので、そこで押したキーの情報がプロンプトに流れる。
今回、CapsLockは 66: `Eisu_toggle` 、
左Ctrlは 37: `Control_L` となっていた。

以下で特殊キーのグループを調べておく。
キーバインド時に使うものは特殊キーとしてグループ名が割り当てられている。
一旦解除(というかremove)してからつけ直す必要がある。

```shell
$ xmodmap
```

まず特殊キーはグループからremoveし、キーコードを変更してからもう一度特殊キーのグループに設定する。
ファイル自体は `~/.Xmodmap` に置いておくことにする。

```config:~/.Xmodmap
remove lock = Eisu_toggle
remove control = Control_L

keycode 37 = Eisu_toggle
keycode 66 = Control_L

add lock = Eisu_toggle
add control = Control_L
```

以下で設定読み込み

```shell
$ xmodmap ~/.Xmodmap
```

## 開発用の諸々
### shell関係
このPCはうちの相棒が使うものなので要らないが、私がいじるときに使う文だけ入れておく。

```shell
$ sudo apt install git fish neovim
```

あと、dotfilesも入れておく

```shell
$ git clone https://github.com/oratake/dotfiles.git 
```


# 今後の展望

- キーボード配列の確認  
ラップトップでよくあるfnキー押しながら輝度変えるのがうまくいかないので、その調整をしてみたい
Sequelizeで外部キーのみで構成されたテーブルを行ロックして更新する際の注意

対象テーブルのModelのfindOneのオプションにlock:transaction.LOCK.UPDATEを指定してロックを行い、取得したModelインスタンスのsaveを使用して更新するのがロックを使った更新の一般的な方法と思っている。

```javascript
  const model = require('../models/model');
  let upModel = model.findOne({
    lock: transaction.LOCK.UPDATE,
    where: {
      attr1: 42
    }
  });
  upModel.save({
    attr2: null
  });
```
外部キーのみで構成されたテーブルの外部キーを更新する場合、この方法では更新が出来なかった。

取得したModelインスタンスは使用せず、Modelのupdateを呼び出しoptionのwhereを同じ条件で更新することで対応できた。

```javascript
  const model = require('../models/model');
  let upModel = model.findOne({
    lock: transaction.LOCK.UPDATE,
    where: {
      attr1: 42
    }
  });
  model.update({
    attr2: null
  }, {
    where: {
      attr1: 42
    }
  });
```
＃正式な方法かどうかは怪しい。。。
VirtualBox+VagrantでCentOS6.5の環境をあっという間に作る

# はじめに
最近、機械学習の勉強ばかりでくたびれたので
気分転換にWebクローラーのプログラミングを勉強

# 補足
Virtual+VagrantのくだりはQiita内でびっくりするほど見かけたので
誰かに教えたいわけではなく、あくまで自分の環境を作り直すときに
最速で行えるように自分向けの内容に特化。

# 参考書籍
「JS+Node.jsによるWebクローラー/ネットエージェント開発テクニック」
https://www.amazon.co.jp/JS-Node-js%E3%81%AB%E3%82%88%E3%82%8BWeb%E3%82%AF%E3%83%AD%E3%83%BC%E3%83%A9%E3%83%BC-%E3%83%8D%E3%83%83%E3%83%88%E3%82%A8%E3%83%BC%E3%82%B8%E3%82%A7%E3%83%B3%E3%83%88%E9%96%8B%E7%99%BA%E3%83%86%E3%82%AF%E3%83%8B%E3%83%83%E3%82%AF-%E3%82%AF%E3%82%B8%E3%83%A9%E9%A3%9B%E8%A1%8C%E6%9C%BA/dp/4883379930

# 基礎知識：Vagrantって何？
ちょっと雑い説明だけど、仮想イメージの入手～初期セットアップをワンタッチで実行してくれるツール。
通常だと、、、
 1. 公式サイトから仮想イメージ（isoなど）をダウンロード
 2. VirtualBoxへインポート
 3. 仮想ホストの設定(CPU割り当て、メモリ、ネットワーク設定）
 4. 仮想イメージの起動
 5. 初期設定の入力（IPアドレス、IPアドレス設定、HOST名の設定、パッケージのインストールetc...）
 6. 仮想ホストへのログイン
…となんだかんだで早くても30分そこそこかかっちゃうけど

これが手を動かすところだけでいうと…
 1.Vagrantサイトから仮想イメージのセットアップコマンド確認
 2.コマンドプロンプト上でセットアップコマンド実行
 3.仮想ホストのログイン
で完結する。
この後の手順だとこの書き方だと説明が難しいので工程分けてるが
手を動かす箇所がほとんどないのが魅力。
その分、制約があって…
　・Vagrantのサイトで登録されている仮想イメージのみ利用可
　・初期ログインアカウントとか固定なので、気になるなら後で直す必要ある
といったところがあるが、
　「とりあえずなんか本で書いてあるプログラムがパパっと動くかどうか確かめたい」：
というニーズであればまさに最高に使い勝手がいい。

ちなみに、「とりあえずpythonプログラム動かしたい」とかであれば
自分の中では今でも「Google Colab最高！」だけど
Google Colabだとリソース制限あるしなぁ、、、AWSとかGCPとかだとお金かかるから
自前の実行環境使うか…ってときには結構重宝する。
そんなわけでVagrantの存在感が目下急上昇してるわけです。

# 手順
1. VirtualBoxのインストール
2. Vagrantのインストール
3. 仮想イメージのダウンロード by Vagrant
4. 仮想ホストのセットアップ by Vagrant
5. 仮想ホストの起動 by Vagrant
6. 仮想ホストへのログイン by Teraterm

# 1.VirtualBoxのインストール
詳細は割愛。↓↓のURLいってダウンロード。
https://www.oracle.com/technetwork/jp/server-storage/virtualbox/downloads/index.html
OK,OKでインストールすればよし。

# Vagrantのインストール
https://www.vagrantup.com/downloads.html

赤丸部をクリックしてダウンロード。
実行ファイルをダブルクリックしてインストール。
![コメント 2019-10-07 222649.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/97e2866d-120c-0797-bd2c-ad2264378d54.png)

# 3. 仮想イメージのダウンロード by Vagrant
[VagrantCloud](https://app.vagrantup.com/boxes/search)に行って
自分の欲しい仮想イメージを探したらよい。

今回のケースだとCentOS6.5が欲しかったので
検索窓にcentos6.5　記入
provider：Virtualbox　を選択して
検索アイコンクリック。したときの状態をキャプチャ。
![コメント 2019-10-07 222649.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/baf074d3-557e-22e9-d38d-cf9d4ee82d44.png)

で、一番上の行をクリックした画面が↓↓
[New]タブをクリックすると、この後セットアップに使うコマンドが書いてるのでメモっとく。
![コメント 2019-10-07 222649_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/26fcbef1-0ac7-3da0-1531-7b0b4775c171.png)

ちなみに[Vagrantfile]タブをクリックすると、vagrantのconfigファイルにセットアップ内容をそのまま記入するときの内容のめもがある。
![コメント 2019-10-07 222649_02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/416263/0f39ce1e-e09d-6ac2-2ab0-f541058c9f0a.png)

# 4.仮想ホストのセットアップ by Vagrant
コマンドプロンプトを起動して
さっきメモしたコマンドを実行

```:cmd
vagrant init nrel/CentOS-6.5-x86_64
vagrant up
```
これを実行すると後はVagrantが自動で仮想ホストのダウンロードとセットアップをしてくれるので
それまでしばらく待つ。

# 5. 仮想ホストの起動 by Vagrant
起動もコマンドプロンプトから行う

```:cmd
vagrant up
```

これでOK。

# 6. 仮想ホストへのログイン by Teraterm
デフォルトは共通っぽい。teraterm起動して
IPアドレス：127.0.0.1
サービス：SSH
ポート：2222
ID/PASS：vagrant / vagrant

これでプロンプトが出たら成功。NATネットワークになっているので
この仮想ホストからインターネット接続も可能。ナイスだ。

～おしまい～
SinatraにBundler導入〜起動まで

SinatraにBundlerを導入し、起動までやってみたので、
備忘録として残します。

# はじめに
`Gemとは`
Rubyで用意されている機能で、誰かが作ってくれたソースコードのパッケージを自分のプログラムにうまく取り込むための仕組み（ライブラリ）。
gemを利用することで0から開発をしなくても手軽に便利な機能を実装することが可能です。

`Gemfileとは`
必要なgemとそれらのバージョンの一覧が記載されたファイル。

`Gemfile.lockとは`
Gemfile.lockはGemfileを元に実際に取得してきた各gemとそれらのバージョンが記載されているファイル。

`Bundlerとは`
Rubyで用意されている機能で、Gemを管理する仕組み。
たくさんのGemをGemfileを元にワンアクションでインストールしてくれる。

# Bundlerのインストール

まずは以下のコマンドでBundlerをインストールします。

```
$ gem install bundler
```

# Gemfile作成
以下のコードを実行し、Gemfileを作成します。

```
$ bundle init
```
Gemfileに以下のコードを追加します。

```ruby:Gemfile
gem 'sinatra'
gem 'sinatra-reloader'
```

コード追加後のGemfileは以下のようになりました。

```ruby:Gemfile
# frozen_string_literal: true

source "https://rubygems.org"

git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }

gem 'sinatra'
gem 'sinatra-reloader'
```

# Rubyファイルにコードを追加
BundlerをSinatraで使うために、Rubyファイル（app.rbなど）に以下のコードを追加します。
参照：[How to use Bundler with Sinatra](https://bundler.io/v2.0/guides/sinatra.html)

```ruby:app.rb
require 'rubygems'
require 'bundler'

Bundler.require
```

# Gemのインストール

```
$ bundle install

# 特定のフォルダにひとまとめにインストールする場合
$ bundle install --path vendor/bundle
```

`bundle install`を実行すると、Gemfile.lockを元にgemをインストールします。
ただし、Gemfile.lockが存在しない場合、Gemfileを元にgemをinstallした後、Gemfile.lockを作成します。


# サーバー起動

```
$ bundle exec ruby app.rb
```

# 参照
https://forest-valley17.hatenablog.com/entry/2018/09/08/120108
20190803_Java&k8s on Azure祭りに行ってきた話

随分間が空いてしまいましたが。（まとめるのを忘れていました）

# 20190803 Java & k8s on Azureまつり


## 午前中の部
[Java活用から学ぶコンテナの利用シーン](https://www.slideshare.net/tyoshio2002/java-on-azure-with-docker-container)

* コンテナは歴史がある
 - Dockerよりもk8sになってきている。

* microserviceは何のために？
  - 競合他社に負けないために
  - 受け入れられるものと受け入れられないもの
     - 改善の繰り返し
  - 影響の極小化
  - カナリアリリース

* Windows Server 2016 TP3からDockerが使える。

* Dev/Ops
  - コンテナを触るなら、両方の知識が必要

* コンテナ
  - 出来るだけ小さいイメージを作るように心がけるべき。

* open-jdk
  - Azul Systemsのzuluが使える。

* dockerだけだと機能が足りない
  - 冗長構成には不向き（複雑な構成になると表現が難しい）

* dockerのセキュリティ
   - 脆弱性
      - officialのイメージを使う。
      - Aqua Security/Twistlock
        - 脆弱性がチェックできる模様

* docker-composeは不要になるか？
   - 複雑なシステム(サービスが複数あるようなものとか)だとdocker-composeだと面倒臭くなる。
     - なので、簡単なものであればdocker-composeでも良いがk8sを押している。

* 本番運用時は動いている環境は考慮しなければいけないのか？
   - しないといけない。
     - コンテナが全てのリソースを食い潰さないように設計しないといけない。


* なんでもかんでもコンテナに向いているわけではない
   - スケールアウトするようなものには向いている。
   - スケールアップが必要なものは向いていない。
      - 所詮、間借りしているだけだから、直接触った方がよい。

* Azureの場合
  - Web App
     - サービスが少ない場合はk8sよりもおすすめな模様。(ランニングコストが安いとのこと。)
  - Container Instance
     - Web Appは一度動かしたら動かしっぱなしで課金されてしまう。
     - Container Instanceは動いている時だけ課金される違いがある。

## 午後の部
[ハンズオンの資料](https://github.com/yoshioterada/k8s-Azure-Container-Service-AKS--on-Azure)
[k8s](https://www.slideshare.net/tyoshio2002/japan-container-day-2018)

* k8sのインストール方法
  - GUI
  - azコマンド
  - リソースグループのテンプレートを使う

* k8s
  - poはpodの略。
    - コンテナを起動しているわけではない。
       - なぜそうなっているかというと、依存関係が強いものをまとめられるようにしているとのこと。（パフォーマンス重視とのこと）
    - 落ちたらIPが変わる。

  - yamlファイルのlabels属性でフィルタリングできる？
    - podの操作ができる。

  - p.52のapplyは-fが必要
  - p.58で実行するのは12-Ingress.yaml


* コンテナの注意点
  - latestタグは使わない。
  - タグにはbuild-idをつける。
  - 小さいイメージの作成を心がける。
     - openjdkでもalpineのやつが一番軽い。
  - ユーザーのパーミッションを設定する。
  - DockerHubのイメージはむやみに信じない。
     - 使う場合はセキュリティを確認して！
  - cacheが効くような書き方でdockerfileを作る。

* k8s
  - ReplicationConroller -> Replica Set -> Deployment
  - リソース使用量：きちんと指定する。（設定しないと全部使い切っちゃう)
     - kubectl top pod で見れる。
  - Selector/Labelの理解が重要
     - 安全に切り替えができる
  - ServiceにLBを使わない
     - 外部に公開するのは基本的にIngress
  - 困った時は
     - describe, log, exec, get events
  - 情報は英語を見ましょう。
     - 本番環境に適用するならなおさら。（お試しなら日本語の情報を参考にしてもいいけど。）
  - 全てのk8sの機能を利用する必要はない
     - 変化が早い
  - 複雑な構成を作らない。
     - 運用構成を変えればできるようなものは無理してk8sでやらない。
  - k8sはポータビリティがある？
     - ある点とない点がある。
        - LBもPVも実装依存
  - どのバージョンを利用しているか？
    - バージョン毎の差異が大きい（設定など)
  - 大規模k8sクラスタを構成しない(ノードを多く作らない)
    - 多くても20〜30ノードに留めておくのがよい（再構築の際に時間がかかる）
  - k8sのバージョンアップは新規クラスタ構築が推奨とのこと。
  - PVは極力使わない。
  - DBはマネージドサービスを使うのがよい。
     - PVとの絡みがあるから?
express-validatorを使用した際の空Objectの判定

express-validatorがチェックで使用しているvalidator.jsにはObjectが空か({}か)のチェック処理はない。
このため、custom定義を行い、自力でチェック処理を実装する必要がある。その際のメモ。

```javascript:空Objectの判定

let check = [param("param1", "error message").custom((value, { req, location, path }) => {
  if (typeof value !== 'object') return false;  // 型がObjectでなければエラー
  return Object.keys(value).length !== 0;       // keyの一覧(配列)を取得、長さが0であればエラー
})];
```

チェック自体はよくあるやり方だと思うが、忘れないようにメモメモ。。。
2019-10-07 Link > LPC > (ユーザが用意) 2017-07-27 Quick Start Guide – Using MCUXpresso SDK with PINs&CLOCKs Config Tools

```txt:動作環境
OS: Ubuntu 18.04 LTS
基板: LPCXpresso54628
IDE: MCUXpresso IDE v11.0.1 [Build 2563] [2019-09-01]
```

Quick Start Guide – Using MCUXpresso SDK with PINs&CLOCKs Config Tools
https://community.nxp.com/docs/DOC-335064

ユーザが顧客から要請を受けて、QuickStartガイドを用意。

これは本来はNXPによる仕事のはずだが、残念ながらNXPによるQuickStartガイドはない？

入門者には厳しい環境。260ページの資料、100ページの資料、169ページの資料...を読んでも疑問点の解消には時間がかかる。
express-validatorでワイルドカードを使用する際の注意

express-validatorでワイルドカードを使用したチェックを実装時に気付いた挙動をメモ。

##ワイルドカードを指定する箇所そのもののチェックはできない

```javascript:param.*の対象はプロパティ名ではない
let check = [query("param.*", "error message").isString()];  // チェックの定義
// ↓チェック対象オブジェクト
queryObject = {
  param: {
    id: "abc",   // param.*とした場合、idは対象外(何故か判定は行われる)
    name: "efg"  // param.*とした場合、nameも対象外(何故か判定は行われる)
  }
};
```
custom判定を定義してデバッグしてみると、判定処理自体は行われるのがわかるが、falseを返却しようが、Errorをスローしようが、スルーされる。

```javascript:判定処理自体は行われる
let check = [query("param.*", "error message").custom(value => {
  if (value === "abc") return false;                     // チェックは行われるが無視される
  if (value === "efg") throw new Error("error thrown");  // チェックは行われるが無視される
})];  // チェックの定義
// ↓チェック対象オブジェクト
queryObject = {
  param: {
    id: "abc",   // param.*とした場合、idは対象外(何故か判定は行われる)
    name: "efg"  // param.*とした場合、nameも対象外(何故か判定は行われる)
  }
};
```

##ワイルドカードをクエリパラメータに対して「param.\*」のように使用した場合、クエリパラメータparamの数によって挙動が変わる
クエリパラメータparamが単一の場合、paramの文字1文字づつがチェック対象となる。

```javascript:paramが単一の場合
let check = [query("param.*", "error message").isString()];  // チェックの定義
// ↓チェック対象オブジェクト
queryObject = {
  param: "abc" //["a","b","c"]のように1文字ずつチェックする
};
```

クエリパラメータparamが複数の場合、各paramの値がチェック対象となる。

```javascript:paramが複数の場合
let check = [query("param.*", "error message").isString()];  // チェックの定義
// ↓チェック対象オブジェクト
queryObject = {
  param: ["abc", "def", "ghi"]  // "abc"→"def"→"ghi"を順にチェック
};
```
ドキュメントの例を見る限り、「param.\*.id」というような使い方が正しい使い方で、「param.*」とするのは非推奨と思われる。。。
（恐らくワイルドカードの対象が、プロパティ名ではなく添え字(0,1,2…)であるから、と考える。）

```javascript:推奨されている指定方法とオブジェクトの関係
let check = [query("param.*.id", "error message").isString(), query("param.*.name", "error message").isString()];  // チェックの定義
// ↓チェック対象オブジェクト
queryObject = {
  param: [{
    id: "abc",   // param.*.idと指定でparamの中のidを順にチェック
    name: "efg"  // param.*.nameと指定でparamの中のidを順にチェック
  },{
    id: "hij",   // param.*.idと指定でparamの中のidを順にチェック
    name: "klm"  // param.*.nameと指定でparamの中のidを順にチェック
  }]
};
```
【2019年10月版】VS CodeのRemote ContainerでRustをさっさと始めよう！

# とにかくさっさとRustを始めたい

VS Code の Remote Container 機能を使ってとにかくさっさとRustを始めたいという方に、以下の docker-compose.yaml をご用意いたしました！

```yaml:docker-compose.yaml
version: '3'
services:
    rust:
        image: rust:1.38
        volumes:
          - .:/opt/rustapp
        environment:
          - USER=`${USER}`
        working_dir: /opt/rustapp
        stdin_open: true
```

`rust:1.38` は現時点での最新バージョンです。[Docker HubのRustページ](https://hub.docker.com/_/rust)から配布されているお好みのバージョンを指定してください。

`/opt/rustapp` は適当です。こちらもプロジェクト、もしくはバイナリの名前に合わせて調整してください。

`${USER}` は `Cargo` 実行する際に環境変数`USER`がないと怒られたので設定しています。。。~~(バージョンの依存とかあるのでしょうか？なくて良ければなくていいけど・・・)~~

こちらを Rust プロジェクトのフォルダに置いてください。

## Docker Compose でコンテナの起動

続いて以下のコマンドで上記のコンテナを起動します。

```
$ cd プロジェクフォルダ
$ docker-compose up -d
```

あら簡単。

## VS Code で開く

VS Code を起動し Remote Explorer の`Dev Containers`タブから `プロジェクトフォルダと同じ名前のコンテナ` を探して `connect to container` しましょう。

コンテナの中に接続された状態で VS Code の新規ウィンドウが立ち上がると思います。

`Open Folder` にて、上記`docker-compose.yaml`の`volumes`で指定したフォルダ(今回の場合は`/opt/rustapp`)を開いてください。

## ターミナルから Cargo の実行

VS Code 上で新規のターミナルを開き、以下のコマンドでRustプロジェクトを開始します。

```
$ cargo init
     Created binary (application) package
```

`Cargo.toml`ファイルと`src`フォルダが無事に生成されたと思います。

`src`フォルダにはサンプル(？)の`Hello World`を出力するコードが既にありますので以下のコマンドでビルド＆実行してみましょう！

```
$ cargo install --path .
  Installing rustapp v0.1.0 (/opt/rustapp)
   Compiling rustapp v0.1.0 (/opt/rustapp)
    Finished release [optimized] target(s) in 0.90s
  Installing /usr/local/cargo/bin/rustapp
   Installed package `rustapp v0.1.0 (/opt/rustapp)` (executable `rustapp`)

$ rustapp
Hello, world!
```

`cargo install` でフォルダ名と同じ名前のバイナリをビルドし`/usr/local/cargo/bin/` 以下にインストールしてくれます。

そしてこの時点でもうコマンドとして使えるようになっているのです！
今回の場合のバイナリである`rustapp`とコマンドを叩くと `Hello, World!`と応答を返してくれます。

あら簡単！

## ここからが大変なんだけどね・・・

Rust、コーディングのハードルはそこそこ高いので新規プロジェクト始めるのくらいは簡単にしておきたいですね〜、ということで！

以上でした。
パスワード保護されたExcelブックを開かずにセル値を取得したい

パスワードを設定したExcelブックを、開かずにセル値を取得したいと考えて、ExcuteExcel4Macroのメソッドを記述したマクロを実行させると、
パスワードを聞いてくるダイアログ画面が出てしまう。

そこで、ダイアログ画面が開いたときにパスワードをセット、あるいはダイアログ画面表示させるにパスワードを渡すスクリプト、どうやって書いたらいいでしょうか？

ちなみに、パスワードを設定していないExcelブックでは、望み通りExcuteExcel4Macroのメソッドを記述したマクロはセル値を取得し、別シートにコピーできました。
パスワードはシート毎に異なるものを配布してあり、その数は120ブックくらいあります。
コマンドプロンプト　txt内のリストにあるファイル名を削除する。

メモメモ

>@echo off
for /f %%i in (list.txt) do del %%i
exit


txt内に記述されたリストと同名のファイルを削除します。#list.txt
同フォルダ内のファイルを削除します。

オプションを使用
/f トークンを代入する
Reactのclassでlodashのdebounceやthrottleを使う

Reactで `lodash` を使って、連続処理を制御したいときに、意外とトリッキーだと感じたのでメモしておく。

```jsx
class LodashSample extends React.Component {
  constructor(props) {
    super(props);
  }
  onInputChange = _.debounce((value) => {
    // Implement Logic here
    console.log('debounce!!');
  }, 300)
  throttleMethod = _.throttle((value) => {
    // Implement here
    console.log('throttle!!');
  }, 300)
}
```

こんな感じに書くと綺麗に書ける。

最初は下記のような感じで書いてしまった。

``` jsx
  onInputChange(value) {
    _.debounce((value) => {
      console.log('debounce!!');
    }, 300);
  }
```

こうすると、 `debounce` の中のfunctionが呼び出されない。
というのも、 `debounce` は、メソッドを返すので、メソッドをCallする部分がないからそりゃ呼ばれないという話。

ちょっと癖があってミスったりするけど意味が理解できるとしっくりくる。
Python3 リスト・タプル・辞書・集合

#リスト型
```python:定義
# []で囲う
x = [1, 2, 3, 4, 5]
y = ["a", "b", "c", "d"]
```
```python:再代入
x = [1, 2, 3, 4, 5]
y = ["a", "b", "c", "d"]

# 変数名[index番号] = 代入したい値
x[0] = 100
>>> [100, 2, 3, 4, 5]
y[3] = "xyz"
>>> ['a', 'b', 'c', 'xyz']
```
```python:追加
x = [1, 2, 3, 4, 5]
y = ["a", "b", "c", "d"]

# 変数.append(追加したい値)
x.append(100)
>>> [1, 2, 3, 4, 5, 100]
y.append("xyz")
>>> ['a', 'b', 'c', 'd', 'xyz']
```
```python:摘出
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# インデックス番号0と摘出
x[0]
>>> 1

# インデックス番号0から数えて5番目までを摘出
x[0:5]
>>> [1, 2, 3, 4, 5]

# 2個飛ばしで摘出
x[::2]
>>> [1, 3, 5, 7, 9]

# 逆から摘出
x[::-1]
>>> [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]
```
```python:削除
x = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

# 指定したインデックス番号が消える
x.pop[0]
>>> [2, 3, 4, 5, 6, 7, 8, 9, 10]
```
```python:コピー
x = [1, 2, 3, 4, 5]

# 再代入しても変数yは変わらない
y = x.copy()
x[0] = 100
print(y)
>>> [1, 2, 3, 4, 5]
```
```python:他データ型からのlist型に
# 変数 = list(他のデータ型)
x = list("dog")
>>> ['d', 'o', 'g']
```




#タプル型
```python:定義
# ()で囲う
x = (1, 2, 3, 4, 5)
y = ("a", "b", "c", "d")

# ()で囲わない
x = 1, 2, 3, 4, 5
y = "a", "b", "c", "d"
```
```python:摘出
x = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

# インデックス番号0と摘出
x[0]
>>> 1

# インデックス番号0から数えて5番目までを摘出
x[0:5]
>>> (1, 2, 3, 4, 5)

# 2個飛ばしで摘出
x[::2]
>>> (1, 3, 5, 7, 9)

# 逆から摘出
x[::-1]
>>> (10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
```
```python:一度に複数の変数に代入
# 定義
x = (1, 2, 3, 4, 5)

# 代入
a, b, c, d, e = x

a
>>> 1
e
>>> 5

# 代入したい変数の数がタプルの値の数と違うとエラーになる
```
```python:できないこと
# 再代入
x[0] = 100

# 追加
x.append(100)
```
```python:気をつける事
# 値が1つでカンマが無いとtuple型ではなくなる
x = (1)
>>> <class'int'>

y = "a"
>>> <class'str'>


# 値が1つでもカンマがあるとtuple型
x = (1,)
>>> <class'tuple'>

y = ("a",)
>>> <class'tuple'>
```



#辞書型
```python:定義
# {key : value}
fruits = {"りんご": 80, "バナナ": 100}

fruits
>>> {'りんご': 80, 'バナナ': 100}

# keyを使って値を摘出
fruits["りんご"]
>>> 80
```
```python:再代入
fruits = {"りんご": 80, "バナナ": 100}

# 値を変えたいkeyをしていするだけ
fruits["りんご"] = 120
{'りんご': 120, 'バナナ': 100}
>>> 120
```
```python:追加
fruits = {'りんご': 120, 'バナナ': 100}

# 変数[key] = value
fruits["レモン"] = 80
fruits
>>> {'りんご': 120, 'バナナ': 100, 'レモン': 80}
```
```python:削除
fruits = {'りんご': 120, 'バナナ': 100}

# 指定した key : value を削除
fruits.pop("りんご")
>>> {'バナナ': 100}
```


#集合型
```python:定義
# {}で囲う
x = {1, 2, 3, 4, 5}

x
>>> {1, 2, 3, 4, 5}
```
```python:特徴
x = {1, 1, 1, 2, 3, 4, 5}
y = {1, 2, 2, 3}

# 値が重複しない
x
>>> {1, 2, 3, 4, 5}
y
>>> {1, 2, 3}

# xとy両方に含まれる値が消える
x - y
>>> {4, 5}

# xとy両方に含まれる値が残る
x & y
>>> {1, 2, 3}

# xかyどちらかに含まれる値が残る
x | y
>>> {1, 2, 3, 4, 5}

# xかyに含まれていて重複しない値が残る
x ^ y
>>> {4, 5}
```
githubでnewrepositoryする際のメモ

## gitの設定

```git init```でgitを入れる。

## githubの設定

* new repositoryを作成（このときにreadmeを作っちゃうとcommitがついちゃうので作らない）
* レポジトリのurlを取得
* ```git remote add origin https://github.com/xxx/xxx.git```
* git add .
* git commit -m "firstcommit"
* git push origin master
Android使ってて必要になったこと②

#ライフサイクル
①起動
onCreate → onStart → onResume

②起動中にアラームが鳴る。アラームを消し、アプリに戻る。
<起動> → onPause → アラーム → onResume

③起動中にアラーム(メモリを食う)が鳴る。アラームを消し、アプリに戻る。
<起動> → onPause → アラーム → onCreate → onStart → onResume

④起動中に電話(電話アプリに全画面が奪われている)。その後、アプリに戻る。
<起動> → onPause → onStop → 電話 → onRestart → onStart → onResume

⑤起動中に自動で他のアプリ(メモリを食う)が立ち上がった。しばらくして戻った。
<起動> → onPause → onStop → アプリ → onCreate → onStart → onResume

⑥起動中に戻るボタンでホームに戻り、再度アプリを起動した。
<起動> → onPause → onStop → onDestroy → ホーム → onCreate → onStart → onResume

#実行されるタイミング
####onStart
・onCreate後に実行される
・onStop後にonRestartを経て前面に表示

####onStop
・onPauseの後にそのアプリが見えなくなった時に実行される

####onPause
・onResumeの後に他のアプリが立ち上がった時に実行される

####onResume
・onStartの後に実行される
・onPauseの後に問題なくアプリに戻った場合に実行される

####onRestart
・onStopの後にアプリを再度立ち上げた場合に実行される
※メモリが他アプリに大量に使われた場合はonCreateから実行される

####onCreate
・最初に実行される
・動作が一時中断されて他アプリにメモリを大量に使用された後に再起動した場合に実行される

####onDestroy
・終了ボタンが押されると実行される
How to Turn an App Idea Into a Product

Mobile App development is a process that requires constant modification and evolution whether its Android or iPhone app development. Few times, your original product or app development idea may transform into a solution for a particular problem— and it is especially vulnerable during the different stages of planning.
<h2>Process for Turn an App Idea Into a Product</h2>
1. Dealing With Negative Pushback
2. Feature Set With UX/UI
3. Mobile Limitations
4.Questions To Ask For Your MVP 
5. Hypothesis Testing 
6. Practical Steps For A New Feature Set
7. Start marketing it to targeted groups.

<p>Again, it boils down the above-mentioned points to implement step-by-step in the process of an App Idea Into a Product. Right from the color scheme, look and feel of the app, logo, design, and typography, everything should be a true reflection of what product stands for.</p>
<p>With the right product research on a target market and designing product purely on those grounds will require the product to deliver just what is expected and in fact, surpass the current industry standards to stand out amongst the rest.</p>
<p>The product idea to development will involve a range of feature but nowhere will it deviate from the final goal of developing a mobile app that you defined initially.</p>

Related Article:  <a href="https://www.hiddenbrains.co.uk/blog/nurture-app-idea-to-reality-app-product.html">How to Nurture an App Idea into Lucrative App Product?</a>
express-validatorでoneOfを使用する際の注意点

express-validatorでチェック処理実装時に出くわした事象をメモ。

配列に対しては問題なく動作する。

```javascript:配列
["a","b","c"]
```

以下のようなオブジェクトに対してoneOfを指定すると、正常に動作する。

```javascript:oneOfが動作するオブジェクト
  "obj": {
	"0": {
      "key1": "value1"
	},
	"1": {
      "key2": "value2"
	}
  }
```

添え字が配列のように連番で付与されている場合、配列の体をなしていると見なされると思われる。
以下のようなオブジェクトに対してはoneOfは正しく動作しない様子。

```javascript:oneOfが動作しないオブジェクト
  "obj": {
	"0": {
      "key1": "value1"
	}
  }
```

上記のような、配列の体をなしていないオブジェクト(？)の場合、エラーが発生するような値を送信しても、無視されてしまう。
APIが予期せぬ動作を起こす可能性があるため、オブジェクトのチェック処理にはoneOfは使用すべきではないかと。。
（ドキュメントにもArrayに対して使ってねというような内容が書かれているみたいなので、オブジェクトに対して使用するのは元々非推奨なのだと思う。。。）
ニコニコ動画の広告をスキップするChrome拡張を作ってみた

##はじめに
普段ニコニコ動画を流しながら作業していますが、ストレスを感じさせる動画広告が増えてきたように思います。
「胡散臭い話し方をするVtuberアバターが紹介する胡散臭い化粧品」の広告を1,000回見ても絶対に買わないので、
【動画広告を自動でスキップするChrome拡張機能】を自作しました。



##成果物の紹介
動画広告再生後、1秒以内に自動でスキップしてくれるようになりました。
※動画は知人の許諾のうえで載せています
![2019-10-07-21-35-09.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365854/4e453df5-c50f-361d-f750-afff6129a194.gif)


##ニコニコ動画の広告表示の仕組み
拡張機能作成にあたり、ニコニコ動画の動画広告がどのような仕組みかを確認しました。
![無題.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365854/d69593b0-fa45-13cc-2d1a-a5164b1bc310.jpeg)

どうやら再生画面(PlayerContainer)内に

- 通常動画再生用コンテナ(VideoPlayer)
- 右から左に流れるコメント再生用コンテナ(CommentRenderer)
- 動画広告再生用コンテナ(VideoAdContainer)

などがあり、再生ボタンが押されると動画広告再生用コンテナ内にある動画リンクを読み込む → 広告終了後に同コンテナの`style`が`display: none`に変更され、通常動画とコメントが再生される仕組みのようです。

そこで今回は、VideoAdContainer内にあるvideoのリンクを書き換えることで動画広告を終了させる方法で作成しました。

##スキップ機能の作成(JavaScript)
まずはソースコードをご覧ください。

```js:nicoadskipper.js
adSkip = setInterval(skip, 100);

function skip() {
    const elem1 = document.getElementsByTagName('video');
        if (elem1[1] == null){
        } else if(elem1[1].src !=""){
            elem1[1].src="";
            clearInterval(adSkip);
        }
}
```
`document.getElementsByTagName('video')`でページ内のvideoタグリストを取得しています。
再生ボタンを押すまではvideoタグが1つ(通常再生video)のみ存在し、再生ボタンを押すとvideoタグが合計3つになります。
videoタグの2つ目が動画広告に関するもののため、elem1[1]のリンクを`src=""`に書き換えます。
再生ボタンを押すまではelem[1]は存在しないため、nullだった場合も含め条件分岐式でまとめています。

この処理を`setInterval`を用いて100msごとに実行するのですが、このままでは通常動画再生中も延々と処理が続くため、
~~`clearInterval`を用いて300ms経過すると処理が停止するようにしました。(なお300msである必要性は特にありません)~~
(10/11追記: 300ms経過するとスキップ機能がはたらかない事象が確認されたため、`src=""`に処理後すぐに`clearInterval`するよう修正しました。そもそもsrc=""を繰り返すメリットもありませんしね。尚この場合`elem1[1]==null`である間延々と処理が続きますので、MutationObserverで監視する方式にすべきであることを併記しておきます)

これで【videoタグ 2つ目のsrcに動画広告リンクが記載されているとき、srcを消すことを100ms毎に行なう】機能の完成です。

##自分のChromeに拡張機能を読み込む
JavaScriptファイルだけではChrome拡張機能として使えないため、manifest.jsonやアイコン画像も準備します。
ここでは割愛させていただきます。
![2.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/365854/e2f68983-e561-bed4-c711-9d921bf294f0.jpeg)
無事にChrome拡張機能が追加されました。

##注意事項
YoutubeのAdBlockなどと同様、この拡張機能は運営会社(株式会社ドワンゴ)および動画広告者に損失を与えうるものです。
そのためChromeストアへの公開および不特定多数への配布は行いません。

ここで記載したコードを使用してのChromeストア公開はご遠慮ください。著作権を放棄したわけではありません。
また本機能を使用したことによる一切の責任を負いかねます。ご了承ください。

※ただし、個人利用で試してみたいという方がいましたら[Twitterアカウント(@gmnoir)](https://twitter.com/gmnoir) までご連絡ください。
当方の想定した用途かを確認のうえで、個別にファイルをお送りします。


##最後に##
JavaScriptを書くのが初めてなこともあり、まずい書き方があるかもしれません。(特に`setInterval / clearInterval`)
コメントでご指導いただけますと大変ありがたいです。


ニコニコ動画が、なるべくUXを損なわない方法で収益化できることを、ユーザーとして願っています。


##参考記事##
[課金なんて必要ない？YouTubeの広告自動スキップ＆バナー自動削除のChrome拡張作ってみた]
(https://qiita.com/HirosuguTakeshita/items/fd1da5f6b727fbe611e4)
[manifest.jsonの書き方]
(https://qiita.com/aoisa/items/5cb8cf2707b2aa884bbb)
moment.jsを使ったUTCと日本時間の相互変換

moment.jsで簡単に時間を扱えるのでUTCと日本時間(JTC)の相互の変換ができないか調べた際のメモ。
前提として、以下の全てのパターンでlocaleが日本になっていること。

##日本時間の取得

これは簡単。

```javascript:moment.jsで日本時間取得
moment("2019-02-21 09:00").format();  //"2019-02-21T09:00:00+09:00"
```


##UTCの取得

これも調べてみると簡単。

```javascript:moment.jsでUTC取得
moment.utc("2019-02-21 09:00").format();  //"2019-02-21T09:00:00Z"
```


##日本時間からUTCへの変換
まずは日本時間からUTCへの変換から。

```javascript:moment.jsで日本時間からUTCへの変換
moment("2019-02-21 09:00").utc().format();  //"2019-02-21T00:00:00Z"
```

##UTCから日本時間への変換
調べるのに時間がかかってしまいやっと見つけたのが以下の方法。

```javascript:moment.jsでUTCから日本時間への変換
moment.utc("2019-02-21 09:00").local().format();  //"2019-02-21T18:00:00+09:00"
```

moment.jsを使うと時間の操作が簡単になるのでおススメ。
Xcode11アップデート後にRealmのエラーでアプリ起動できなくなった際の対応

## 概要

「Xcode11にアプデしたらアプリ起動しなくなりました」

と取引先から連絡が入ったので急ぎ対応しました。
（アップデートはまだしないでおこうと思ったのだが。。）
Xcodeの更新をいつもサボり気味でしたので、これを機に備忘録として対応内容をまとめます。

## 環境

* iOS 12.4
* Xcode 11.0
* Swift4

## 不具合内容

これまでXcode10.3で動いていたアプリを、Xcodeのアップデートだけした後にビルドし直して起動しました。
ビルドは成功しましたが以下のエラーが。。

```
Terminating app due to uncaught exception 'RLMException', reason: 'Primary key property 'identity' does not exist on object 'RealmSwiftPermissionUser''
```

ん？RealmのPrimary keyがない？
いや、、ちゃんとあるはずだけど。。。

## 対応

[CocoaPods](https://cocoapods.org/)で入れていた[Realm](https://realm.io/jp/)が3.11系と古めだったので、最新バージョン3.19.0に更新したら直りました！
細かい原因はわからないままですが、マイグレーションかなにかで落ちたのかな？

Podfile書き換えてインストールし直すという方法が上がっていたのでこちらで対応しました。
（SwiftPackageManagerで導入している方は下記サイトを参考）

```:Podfile
pod 'Realm', git: 'https://github.com/realm/realm-cocoa.git', branch: 'master', submodules: true
pod 'RealmSwift', git: 'https://github.com/realm/realm-cocoa.git', branch: 'master', submodules: true
```

```sh
pod install
```

ちなみに、普通にコマンドでアップデートだけでも大丈夫でした。
ただ他ライブラリも全部更新されてしまうので要注意です。。

```sh
pod update
```

## 参考

* [Support Xcode 11 things · Issue #6163 · realm/realm-cocoa](https://github.com/realm/realm-cocoa/issues/6163)
初心者によるプログラミング学習ログ119日目

# 100日チャレンジの119日目

Twitterの100日チャレンジ#タグ、#100DaysOfCode実施中です。
すでに100日超えましたが、継続。

100日チャレンジは、ぱぺまぺの中ではプログラミングに限らず継続学習のために使っています。

119日目は、

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">おはようございます<br>119日目<br><br>メンターさんの課題のみ。<br>コードレビューで指摘を受けたところを修正したり。<br>・命名規則に沿って修正。<br>・宣言したけどつかってないところは削除<br>・同じような処理はfor〜ifの外へ<a href="https://twitter.com/hashtag/%E6%97%A9%E8%B5%B7%E3%81%8D%E3%83%81%E3%83%A3%E3%83%AC%E3%83%B3%E3%82%B8?src=hash&amp;ref_src=twsrc%5Etfw">#早起きチャレンジ</a><a href="https://twitter.com/hashtag/100DaysOfCode?src=hash&amp;ref_src=twsrc%5Etfw">#100DaysOfCode</a><a href="https://twitter.com/hashtag/%E9%A7%86%E3%81%91%E5%87%BA%E3%81%97%E3%82%A8%E3%83%B3%E3%82%B8%E3%83%8B%E3%82%A2%E3%81%A8%E3%81%A4%E3%81%AA%E3%81%8C%E3%82%8A%E3%81%9F%E3%81%84?src=hash&amp;ref_src=twsrc%5Etfw">#駆け出しエンジニアとつながりたい</a></p>&mdash; ぱぺまぺ@webエンジニアを目指したい社畜 (@yudapinokio) <a href="https://twitter.com/yudapinokio/status/1179869310438608896?ref_src=twsrc%5Etfw">October 3, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
Jerseyって？参考実装？

# Jerseyとは

## 定義
Java で
①REST アプリケーション
を開発するための
②JAX-RS
の
③リファレンス実装

## ①REST アプリケーション
RESTはWebサービスの設計原則。
Webサービスの設計モデルを実現しているのがREST アプリケーション。

RESTについては、[REST入門 基礎知識 - Qiita](https://qiita.com/TakahiRoyte/items/949f4e88caecb02119aa)

## ②JAX-RS(Java API for RESTful Web Services)
Webフレームワーク（色々使える機能がある）。
例えば、
アノテーションを使用してHTTPリクエスト・レスポンスをJavaコードにマッピングする
など
同じ動きができる比較対象としては、「Servlet」などがある。

## ③リファレンス実装 
参考実装
JAX-RSが持っている機能を実装すること目的として作られた方法。


## まとめ
Jerseyとは、
JavaでRESTの設計原則に基づいたWebアプリケーションを開発するためのWebフレームワークの機能を実現する方法の１つ

ラーメンで例えるなら、
醤油(EE)ラーメン(java)に野菜マシマシの選択肢(JAX-RS)があって、野菜のレシピが「Jersey」と今は理解。

## ＋α
・Webサービスの設計原則の種類
・RESTを実現するなら、JavaのFlameworkで「Spring」を選択すればいいのでは？EEに拘る理由があるの？
・JAX-RSの参考実装は他にもある。選択の基準は何になるの？
【Swift】ジェネリクス

#ジェネリクスとは？
ジェネリクスとは型をパラメータとして受け取り、汎用的で型安全性が高いコードを実現するための機能です。
まあ実際に見てみないと分からないと思うので、早速サンプルコードをみていきましょう。

```
func Equal<T : Equatable>(_ x: T, _ y: T) -> Bool {
  return x == y
}

//どの型でも値が等しいか判別できる。
Equal("abc", "abc") //true
Equal(1.05, 1.03)//false
Equal(true, true)//true
```
上記のコードを見ると、定義したEqual関数で複数の型に対して機能していることが確認できます。
これはT : Equatableという記述に秘密があります。この文字列は型引数というもので、引数の型を定義するものになっています。
今回で言うとTはEquatable型を設定されているため、そのTが引数として設定されたxとyもEquatable型ということになります。Equatable型にはString型やInt型など様々な型が準拠しているため、あらゆる型への==での評価を可能としました。

##定義方法
```
//通常関数
func 関数名(引数名: 型） -> 戻り値の型 {
}

//ジェネリック関数
func 関数名<型引数>(引数名:　型引数) -> 戻り値の型 {
}
```
先ほどのコードと同じように、上記の記述でジェネリック関数が定義できます。
ちなみにジェネリック型というのは<型引数>のことを指し、またOptional<Wrapped>型や配列のArray<Element>型などの<>内にプレースホルダー型（型引数の別名）を持つ型もジェネリック型ということになります。

##特殊化
ジェネリック関数を呼び出したり、ジェネリック型を持つ型や構造体などをインスタンス化する際には型を確定させる必要があります。
このようにジェネリクスを利用して汎用的にしているものを、具体的に型を確定させることを特殊化と言います。

```
struct Container<Content> {
  let content: Content
}

//型引数がStringに設定
let stringContainer = Container<String: "dog">

//型引数を型推論
let intContainer = Container(content: 3.14)
```
特殊化の方法は上記のように二つあり、**型引数を明示する方法**と**型推論を利用する方法**があります。
前者は引数の前に指定し、後者は引数を指定するだけでいいです。

##型制約
型制約とはその名の通りジェネリック型に制約をつけることです。この記事でも先ほど使用しました。

```
func 関数名<型引数 : プロトコル名やスーパークラス名>(引数) {
}
```
定義方法は簡単で、上記のように型引数の後に:(コロン)プロトコル名やスーパークラス名を記述すればそれに準じた制約が可能になります。

###連想型のスーパークラスやプロトコルに対する制約
通常の型制約で、あるプロトコルやスーパークラスに準じた型を定義したのちに、さらに他のプロトコルなどで制約したいときはwhere文を使い下記のように定義することができます。

```
func 関数名<型引数 :　プロトコル>(引数) -> 戻り値の型
  were 連想型 :プロトコルやスーパークラス {
}
```
下記ではこれのサンプルコードを記載します。

```
func sorted<U : Collection>(_ argument: T) -> [T.Element]
   where T.Element : Comparable {
  return argument.sorted()
}

sorted([5,4,1]) //[1,4,5]
```
上記のコードのようにCollectionプロトコルとComparableプロトコルで制約することで比較可能な列挙が実現できました。

##Any型との差異

```
//ジェネリック型
func something<T>(_ argument: T) -> T {
  return argument
}
let String = something("Test") //String型
let Double = something(3.14) //Double型

//Any型
func anything(_ argument: Any) -> Any {
  return argument
}

let anyInt = anything(3)//Any型
let anyString = anything("cat")//Any型
```
ジェネリック型について考えると、Any型との差異がわかりづらいと思ったのでここに違いを記しておきます。
ジェネリック型は特殊化ができるため、関数実行時やインスタンス化時に型が確定し、型安全性が保証されます。
その一方Any型は全ての値がAny型として定義されてしまし、型安全性が保証されません。
そのため汎用的なコードを求める場合は、特別な理由がない限りジェネリック型を利用しましょう。
GitHub プライベートリポジトリ で 草を 生やそう

## 〜宣伝〜

:::note info
個人開発でエンジニア専門マッチングサービスを開発しましたので、是非未経験からエンジニア目指している人！現役エンジニアで教えたい人！使ってみてください！

β版リリース記念キャンペーン中です！

10名様限定、抽選でお好きな技術本1冊プレゼント！
🎉当選者にはメッセージ差し上げます(送付の際に住所はお聞きしません)
詳しくはこちらから↓

https://x.com/dokupro01/status/1796837336145436846
:::
https://dokupro.dev/

## 目的

- プライベートリポジトリにコミットしたとしても草(Contributions)を生やす方法を知る。

## 実施方法

1. GitHubにログインする。
  1. ログイン画面は[こちら](https://github.com/login)
  1. 必要情報を入力してログインする。  
  ![WS000042.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/06f1596e-f36a-898f-8b61-60d1d8fda05c.jpeg)
1. Contributionsが見れるページまで移動する。
  1. 下記画像の赤枠の部分が表示されていることを確認する。
1. プライベートリポジトリの更新もContributionsに記載するようにする。
  1. 下記画像の「Contribution settings」と書かれた部分をクリックする。
  ![WS000043.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/b9799b4a-a2d8-a3b1-24cb-4137d87cb4b7.jpeg)
  1. 「Private contribution～」をクリックして設定をオンにする。
  ![WS000046.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/306417/3951b109-ea17-053e-09b0-b9597ec51463.jpeg)
  1. 「Visitors will now see your・・・」のコメントが出ればOK！これでプライベートリポジトリに更新を加えても草が生えるようになった。
Flutterのプラグインにプルリクエストを送ってみた

知っている人には当たり前の手順だと思いますが、個人用にメモ。

## 経緯

Dart+Flutterで開発しています。欲しい機能を99.99%理想通りに実装してくれているのに、残りの0.01%が残念な感じのパッケージがありました。
感謝の気持ちも込めて、改善案を提案することにしました。

自分でソースコピペして修正するよりも、相手を英語とソースで説得してマージさせられれば、バージョンが上がったときとかに楽かもという下心もあります。

話がずれますけど、英語なんかGoogle翻訳でいいって言っている人って、こういう発想あるんですかね。

## GitHubのドキュメントを読んでみた

エンジニアたるもの原典を読んで調査するのは当たり前...ということで、公式ページのプルリクエストの項目を読んでみました。

以下意訳

>読み取り許可があれば誰でもプルリクエストが送れますが、書き込み権限がないとブランチは作れません。プルリクエストのためのレポジトリを作りたいのに書き込み権限がない場合は、まずレポジトリをフォークします。

フォークって何だっけと思ったのでこれも確認

>GitHubに登録している個人・団体であれば、だれでもレポジトリをフォーク出来ます。フォークはレポジトリのコピーのようなものですが、以下の2点が異なります。
1)オリジナルのレポジトリに対しプルリクエストを送ることで、変更を提案することができます。
2)オリジナルのレポジトリの変更を反映させることができます。

## 実際にやってみた

プルリクエストを送りたいリポジトリを開くと、右上に「fork」とあるのでクリック
![fork.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227939/2b5915a4-b5c7-ac48-0f23-1213812ed08d.jpeg)

修正～pushしてから、forkした自分のリポジトリを開く

「pull request」というリンクがあるので、そのページを開く
![pullrequest.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227939/9674685f-97c7-011e-42d9-0c1e1ed668a1.jpeg)

「Create pull request」というボタンを押すとコメント入力欄が開くのでそこに入力
![sendpullrequest.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/227939/d7c24ebe-ec3b-39c3-4fd5-bfcea5eb0f9c.jpeg)

---

以上、おしまい。マージしてもらえるといいなー

**2019/10/10追記**
マージされました、やったね！！

## 参考資料
[GitHubのhelp](https://help.github.com/en/articles/creating-a-pull-request)
Pythonにおいてjson内でコメントアウトを使用した備忘録

基本的にjsonファイル内ではコメントアウトを使用できない。そんな中、コメントアウトを使用したくなったためその備忘録をここに残す。

# コメントアウト用のキーを使用する

読み込んだ先でそのままコメントアウトしてくれればよいのでは？ということで、こんな感じ。

```python
{
    "example": {
        "aaaa": aaaa,
        "#": "comment out",
        "bbbb": bbbb,
        "#": ["comment out line 1st",
        "comment out line 2nd"]
    },
}
```

ここを、C++なら`#`ではなく`//`を使用したりすればおｋ。

# 重複キーを使用する

同じキーが登場した場合、最後の定義されたものが採用される仕組みを利用して。

```python
{
    "example": {
        "cccc": "plz set cccc value",
        "cccc": cccc,
        "dddd": "plz set dddd value",
        "dddd": dddd
    },
}
```

# 終わりに

突貫工事的に思いついたのはこれくらい。
毎回指定して読み込むなら使用しないキーを作成するもありかなと思ったけど、jsonを使用するときには機械学習のパラメータを一括で読み込んだりって感じにまとめて使用することが多いため今回は「読み込むんだけど影響しない」という方針になりました。

もっとベターなスマートな方法があったら教えてください。

ではまた。残念無念また来週。
Ubuntu で Mailcatcher

今回は仮想のUbuntu上でMailcatcherを動かす。

## TL;DR

```
$ sudo apt install -y ruby ruby-dev build-essential libsqlite3-dev
$ sudo gem install mailcatcher
$ sudo mailcatcher --ip=0.0.0.0
```

## （当然）Rubyだけではだめなのである

Mailcatcher のインストールには色々なパッケージが必要であって、ただ `sudo apt install -y ruby` するだけでは入ってくれないのである。
当然エラーが吐かれるのであるが、我はそのエラーを見ても何が不足しているのかがわからないクソ雑魚なめくじであるからして、[こちらの記事](https://www.techrunnr.com/how-to-install-mail-catcher-in-ubuntu/) にお世話になったのである。

- [how to install mail catcher in ubuntu » Techrunnr](https://www.techrunnr.com/how-to-install-mail-catcher-in-ubuntu/)

この記事によれば必要なパッケージはこれらなのであるが、

- ruby
- ruby-dev
- libsqlite3-dev
- build-essential
- software-properties-common

`software-properties-common` は入れなくても正常に導入できた。（ひとつずつ入れながらトライしてみた） しかして、こうなるわけである。

```
$ sudo apt install -y ruby ruby-dev build-essential libsqlite3-dev
$ sudo gem install mailcatcher
```

## 起動する

起動はマニュアル通りにやれば大丈夫である。

```
$ sudo mailcatcher --ip=0.0.0.0
```

外部からMailcatcherのWebメーラーにアクセスしたいので、IPを `0.0.0.0` に指定しておく。これで、例えば仮想のIPが `192.168.56.101` などであった場合はブラウザで `http://192.168.56.101:1080` にアクセスすればキャッチされたメールを読むことができる。


## PHPから使う

PHPからMailcatcherを使うためには `php.ini` の `sendmail_path` に記述しなければいけない。ちなみにUbuntuのphp.iniはPHPのバージョン・環境毎に置き場所が異なる。例えばPHP7.2のCLI/Apacheモジュールのそれぞれのphp.iniの置き場所は以下の様になっている。

- CLI => /etc/php/7.2/cli/php.ini
- Apache => /etc/php/7.2/apache2/php.ini

今回はCLIからテスト送信も行ったので、上記の両方に次の記述を追加した。

```
sendmail_path = /usr/bin/env catchmail
```

## おしまい

エラーログ見て不足パッケージにアタリがつけられる能力が欲しい。
電子メールのセキュリティ対策について簡単にまとめてみた

# はじめに
電子メールのセキュリティについてまとめてみた

# プロトコル
* SMTP/pop3
  - 認証なし
* SMTP AUTH
  - 送信者のユーザー認証あり(ユーザー名とパスワード)
* POP3
  - パスワードを平文で送る。POP３サーバーPCがメールを受信したらメールは消える。
* APOP
  - パスワードを暗号化。あまり普及せず
* POP3S
  - POP３に対するSSL対応
* IMAP4
  - サーバー側でメールを保存

# メール暗号化の3つのフロー
* 端末からメールサーバー
  - smtps/starttls
  - 主にsmtps？
* メールサーバーから相手のメールサーバー
  - smtps/startls
  - 主にstarttls？
* 相手のメールサーバーから相手の端末まで
  - pops

https://ischool.co.jp/2015-11-18/#toc1

# starttls
STARTTLSは、サーバー間の通信（MTA間の通信）を暗号化します。
暗号化の方式は、SSL/TLS と一緒です。

# telnet
```
telnet smtpサーバー ポート番号
```

# 電子メールのシステム構成
DMZに中継メールサーバがあり内部ネットワークに内部メールサーバーがある。外部から直接内部ネットワークのメールサーバーにアクセスさせないように中継させる

# 電子メールのヘッダ
Recievedの by が経由されたメールサーバー。最後にtoに届く

# 電子メールのセキュリティ
* メール全体
  - S/MIME,SMTPS
* 認証メール
  - SMTP AUTH
* 送信元の認証
  - SPF
* スパムメール
  - OP25B
* 受信のパスワード暗号化
  - POP3S

# SPF
受信メールサーバーが電子メールの送信元ドメインが詐称されていないかを検査するための仕組み

### ドメイン保持者
* ドメインの保持者は自分のドメインでなりすましメールを送られるのが嫌
* なりすましを防ぐための技術の一つで、 DNSを利用するのが特徴。自分のドメインにSPFレコードを設定
* SPFレコードには、 そのドメイン名を送信元としてメールを送ってもよいサーバのIPアドレス等を記述
  - SPFレコードのIPを自分のメールサーバーのIPだったり、そもそもメールサーバーを持ってなかったら
  - IPアドレスを設定して自分以外は送れないようにする`-all`は拒否。

### 受信サーバー
* SPFに対応したメール受信サーバは、 メールの受信時にそのメールの送信元となっているドメインのSPFレコードを、 DNSで問い合わせます。 送信元のサーバがSPFレコード中で許可されていない場合は、 送信ドメインの詐称が行われたと判断して、 受信を拒否するなどの処理を行います。

https://www.nic.ad.jp/ja/basics/terms/spf.html

# OP25B
ISP管理下の動的IPアドレスからの電子メールは管理外へのネットワークへのSMTPを禁止する。
同じISPのメールサーバへの送信ならOK。

## サブミッションポート 587番
* 自分のISP以外のメールサーバーに送る場合は587番ポートを使用して必ず認証を強制すること。
pythonでreturnをする時に条件によって戻り値を変える 

面白いreturnの記述を見たのでサンプルをつくってみた。

```python:python
#!/bin/python3

def evenodd(n):
    return (['even','odd'][n%2]) # nが2で割り切れる場合'even'が、割り切れない場合'odd'が返される

if __name__ == '__main__':
    n = int(input())
    print(evenodd(n))
```

```python:python
以下と同じ。
def evenodd(n):
    arr = ['even','odd']
    index = n%2
    return arr[index]
```
pythonで画像処理  〜黒板の写真を撮って文字以外を透過させた画像を作る〜

#目標とモチベーション
大学で講義ノートをipadのノートアプリでとっている私のような者が最終的に行き着くのはこんなノートです
<img width=50% alt="IMG_0812.PNG" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403716/7b0fefbf-cf43-f109-9036-d0e650cda478.png" >

そう、黒板の写真を撮ってノートに貼っつけるわけです。
このやり方はとても楽な代わりに不恰好なノートになってしまいます…
そこでpythonistaでpython動かして黒板に書かれた文字だけ残した透過画像にしてやれば楽で、なおかつそこそこ綺麗なノートになるはず…と思ったのでやってみました。

#画像の処理

```sample.py
import photos
from PIL import Image
from PIL import ImageOps
```

今回はすべての処理をpythonistaで行いたかったのでpythonistaでも動く上記のモジュール等をimportします

```sample.py
img=photos.capture_image()
mono=img.convert("L")
mono=ImageOps.invert(mono)
```

photos.capture_image()でpythonistaからカメラを起動し黒板の写真を撮ります。
撮った写真はimgにPIL.Imageオブジェクトとして渡されます。

convert()では画像のモードを変更することができます。今回はグレースケールにしたかったので上記のようにしました。

次に、最終的に黒板の文字は黒にしたいのでネガポジ反転しておきます。
ここまでで以下のように画像が処理されてきました。

![IMG_9C6D09310ECD-1.jpeg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403716/7c9e3218-a907-513f-62cb-0c1045888c64.jpeg)
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403716/67d3175e-b403-1dd3-a4a1-cb23dba31329.png)
↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403716/99a1dfe7-6792-22f6-437d-785f6af2b475.png)

そしてここからバイナリ値で0と255の2値に落とし込みます

```sample.py
i =155
mono=mono.point(lambda x: 0 if x < i else x) 
mono=mono.point(lambda x: 255 if x >= i else x)
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403716/5841a58f-e6b7-77e4-5f39-3d88d1dc0ea6.png)
元の画質のせいで若干潰れているところがありますが結構綺麗になりましたね。

さてここから白い部分を透けさせていきます

Image.putalpha()は与える引数によって透過率を設定することができます。
0を与えると100%透過、255を与えると0%透過（不透過）となります。
また、引数に透過させたい画像と同サイズで8bitグレースケールのImageオブジェクトを指定することもでき、この場合も透過率は先ほどと同様です。

今、先ほどの画像の白い部分を100%透過させたいので、これをネガポジ反転した画像を引数として与えます

```sample.py
temp=mono.copy()
temp=ImageOps.invert(temp)
mono.putalpha(temp)
mono.show()
```

最終的にこんな感じになりました
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403716/d297047e-6780-7da9-343d-e26603798129.png)
少しわかりにくいですがしっかり透けています
ノートに貼り付けてみましょう
<img width=50% alt="IMG_93EF67348226-1.jpeg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/403716/f2e4344a-6766-9aa0-e297-6669779586ed.jpeg">

いい感じです。これでノートを書くのに夢中になって話を聞けない！
なんてことは無くなります

なお有志の手によって2値に落とし込むときの基準値をuiで微調整して多種多様な教授の筆圧に対応できるようになった完成品がこちらに置いてあります
https://github.com/Keisuke168/auto_boading

#補足
私の場合はiPhoneのpythonistaにこのスクリプトを入れておき、ショートカットから動かしました。
import clipboardして一番最後に

```hosoku.py
clipboard.set_image(mono)
```

これをつけておけばクリップボードにコピーまでできるのであとはipad上でペーストするだけです
フォントファイルをコマンドラインでサブセット化する方法

ウェブにアップロードするフォントファイルをできるだけ小さくするためのサブセット化の方法です。
コマンドラインから実行する方法についてまとめました。

## 用意するもの

NodeJS と Python を使いました。
(NodeJS から Python のライブラリを使っているみたいなので、 Python だけでもできそうです。)

* NodeJS (v12.6.0)
* Python (3.6.8)

## サブセット化対象の文字をチェック

サブセット化するべき文字の文字コード(16進数)を調べます。

### Python (3.6) でやる場合

```python
hex(ord('0'))  # => '0x30'
hex(ord('9'))  # => '0x39'
```

### Ruby (2.5.1) でやる場合

```ruby
'0'.ord.to_s(16)  # => "30"
'9'.ord.to_s(16)  # => "39"
```

## ツールをインストールする。

### Python のツール

```sh
$ pip install fonttools brotli
```

### NodeJS のツール

```sh
$ npm install -g glyphhanger
```

## サブセット化

```sh
$ glyphhanger --whitelist="U+30-39" --subset something.ttf --formats=woff2
```

`--whitelist` でサブセット化対象の文字を指定します。 `U+XX` という表記で指定します。
ここでは woff2 のフォーマットにしています。

結果として `something-subset.woff2` が出力されます。

`--formats` には カンマ区切りで複数フォーマットを指定できます。(例: `--formats=woff,woff2`)

## 関連ページ

* glyphhanger
    * https://github.com/filamentgroup/glyphhanger
PHP7.3からヒアドキュメントのインデントのズレが解消される件

ヒアドキュメントは使う箇所によってはソースの可読性をあげられますが
PHP7.2までは、終了マーカーまでをインデントなしで定義しないとダメでした。
最初PHPでこれを見た時に気持ち悪いなと思ってました。

が、PHP7.3からようやくこの問題が解消されるようです。

```php

<?php 
class foo { 
    public $bar = <<< EOT
barbar
hogehoge
EOT; 
}
```

今まではこう書く必要があったのが、、、

```php
<?php 
class foo { 
    public $bar = <<< EOT
    barbar
    hogehoge
    EOT; 
}
```
と記述できるようになります。
可読性が上がりますね！


１つ注意なのが
> タブもサポートされていますが、終了マーカーのインデントと本文のインデント（終了マーカーまで）に関してタブとスペースを混在させることはできません。これらのいずれの場合でも、ParseErrorがスローされます：

とのことなので、既存コードとの互換性という点ではバージョンアップする際は要注意ですね。