OmniAICreator/Qiita-1.07M huggingface dataset:
GCPの公開IPレンジを調べるpythonコード

# はじめに
GCPリソースのIPレンジを知る必要があったので調べました

概ねここで議論されています
https://gist.github.com/n0531m/f3714f6ad6ef738a3b0a
ただ、シェル芸が読めなかったのでpythonで書き直しました

公式ページでの説明は下記になります
https://cloud.google.com/compute/docs/faq#find_ip_range

# 説明
`_cloud-netblocks.googleusercontent.com`のTXTレコードを取得すると、いくつかアドレスが返されます
それらのTXTレコードの中にCIDR表記のIPアドレスが入っています
注意点として、gistの議論にもあるのですが`_cloud-netblocks6.googleusercontent.com`と`_cloud-netblocks7.googleusercontent.com`は再帰的に`_cloud-netblocks1.googleusercontent.com`の中に入っているため(2019年9月現在)、再帰を解釈できるよう書く必要があります

# コード
```python
import re
import dns.resolver

global ip4, ip6
ip4 = set()
ip6 = set()


def search_dns(target):
    global ip4, ip6
    for dns_info in dns.resolver.query(target, 'TXT'):
        servers = re.findall(r'include:(\S*)', str(dns_info))
        if len(servers) > 0:
            for s in servers:
                print(f'searching domain: {s}')
                search_dns(s)

        ip4 |= set(re.findall(r'ip4:(\S*)', str(dns_info)))
        ip6 |= set(re.findall(r'ip6:(\S*)', str(dns_info)))


spf_gcp = '_cloud-netblocks.googleusercontent.com'
search_dns(spf_gcp)
print('ip4')
print('\n'.join(sorted(ip4)))
print('ip6')
print('\n'.join(sorted(ip6)))
```

```pyproject.toml
[tool.poetry]
name = "gcp_ip_range"
version = "0.1.0"
description = ""
authors = [""]

[tool.poetry.dependencies]
python = "^3.6"
dnspython = "^1.16"

[tool.poetry.dev-dependencies]

[build-system]
requires = ["poetry>=0.12"]
build-backend = "poetry.masonry.api"
```

# output IP range
(2019年9月時点)
変わってる可能性もあるので自分で動かしたほうが良さそうです

```output
searching domain: _cloud-netblocks1.googleusercontent.com
searching domain: _cloud-netblocks6.googleusercontent.com
searching domain: _cloud-netblocks7.googleusercontent.com
searching domain: _cloud-netblocks2.googleusercontent.com
searching domain: _cloud-netblocks3.googleusercontent.com
searching domain: _cloud-netblocks4.googleusercontent.com
searching domain: _cloud-netblocks5.googleusercontent.com
ip4
104.154.0.0/15
104.196.0.0/14
107.167.160.0/19
107.178.192.0/18
108.170.192.0/20
108.170.208.0/21
108.170.216.0/22
108.170.220.0/23
108.170.222.0/24
108.59.80.0/20
130.211.128.0/17
130.211.16.0/20
130.211.32.0/19
130.211.4.0/22
130.211.64.0/18
130.211.8.0/21
146.148.16.0/20
146.148.2.0/23
146.148.32.0/19
146.148.4.0/22
146.148.64.0/18
146.148.8.0/21
162.216.148.0/22
162.222.176.0/21
173.255.112.0/20
192.158.28.0/22
199.192.112.0/22
199.223.232.0/22
199.223.236.0/23
208.68.108.0/23
23.236.48.0/20
23.251.128.0/19
34.100.0.0/16
34.102.0.0/15
34.104.0.0/14
34.124.0.0/18
34.64.0.0/11
34.96.0.0/14
35.184.0.0/14
35.188.0.0/15
35.190.0.0/17
35.190.128.0/18
35.190.192.0/19
35.190.224.0/20
35.190.240.0/22
35.192.0.0/14
35.196.0.0/15
35.198.0.0/16
35.199.0.0/17
35.199.128.0/18
35.200.0.0/13
35.208.0.0/13
35.216.0.0/15
35.220.0.0/14
35.224.0.0/13
35.232.0.0/15
35.234.0.0/16
35.235.0.0/17
35.235.192.0/20
35.235.216.0/21
35.235.224.0/20
35.236.0.0/14
35.240.0.0/13
8.34.208.0/20
8.35.192.0/21
8.35.200.0/23
ip6
2600:1900::/35
```

# おまけ
下記によるとG SuiteのIPレンジも同じように取得できそうです
https://support.google.com/a/answer/60764

`_spf.google.com`から取るといけます(紛らわしいので出力は載せません)
cli-kintoneをAWS Lambdaで実行する


## やること
cli-kintoneをAWS Lambdaで実行。
kintoneのレコード全件をCSV形式で出力してS3へアップロードするまで。

## cli-kintoneとは
サイボウズが提供しているkintone公式のCLI（Command Line Interface）
kintone APIでは実現できないようなデータ全件取得などが提供されているのが特徴。
2019/09/18時点でv0.94だが問題なく動作するレベルだが本番環境への導入は慎重に！

## 環境
- ローカル（MAC）
- kintone
- cli-kintone v0.94
- AWS Service （Lambda, S3, IAM Role）
- Python 3.7

## 手順
### cli-kintoneをダウンロード
[公式cli-kintone](https://github.com/kintone/cli-kintone/releases)から最新版Linux実行ファイル`linux-x64.zip`をダウンロードする。

解凍するとbuild/linux-x64フォルダの中に実行ファイルが含まれているため、
フォルダなし版でzipを作り直す。

```bash
cd ~/Downloads
unzip linux-x64.zip
zip -jr cli-kintone.zip build/linux-x64/cli-kintone
```

### cli-kintoneをAWS lambdaにアップロード

Lambdaのコードエントリタイプで`.zipファイルをアップロード`を指定するとzipファイルをアップロードできるようになる。前項で作ったcli-kintone.zipファイルをアップロードする。

<img width="1312" alt="Lambda_Management_Console.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248805/5c421c7e-708b-5db7-a503-dec51382582e.jpeg">


### S3バケットの作成

S3に適当な名前でバケットを作成。
デフォルトのままの設定でOK！！

### IAMロールにS3に配置する権限を付与

AWS Lambdaに紐づいているIAMロールに対して以下の権限を付与する。

|Key|Description|
|---|---|
|サービス|S3|
|権限|PutObject|
|Bucket name|作成したバケット名|
|Object name|*|

<img width="1221" alt="IAM_Management_Console.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248805/5d6cd362-81bb-7436-68d1-a98eb6239023.jpeg">

### 環境変数を設定

|Key|Description|
|---|---|
|DOMAIN|kintoneのサブドメイン名|
|APP_ID|API ID|
|API_KEY|API毎に発行したAPIキー|
|BUCKET|s3バケット名（ARNではない)|
|FILE_NAME|s3に配置するファイル名|


### Let's コーディング！！

ファイル名を`lambda_function.py`で新規作成する。

```python
import json
import subprocess
import os
import boto3

# 環境変数の読み込み
DOMAIN = os.environ["DOMAIN"]
APP_ID = os.environ["APP_ID"]
API_KEY = os.environ["API_KEY"]

BUCKET = os.environ["BUCKET"]
FILE_NAME = os.environ["FILE_NAME"]

# S3オブジェクト操作
s3 = boto3.resource("s3")
bucket = s3.Bucket(BUCKET)

def lambda_handler(event, context):
    # 出力ファイル定義
    file_name = FILE_NAME
    output_file = f"/tmp/%s" % file_name
    
    # cli-kintoneを実行してkintoneのアプリ情報を一時ファイルに出力する
    with open(output_file, "w") as f:
        cmd = f"./cli-kintone -a %s -d %s -t %s" % (APP_ID, DOMAIN, API_KEY)
        res = subprocess.check_call(cmd.split(), stdout=f)
        
    # 作成した一時ファイルをS3へファイルへアップロード
    bucket.upload_file(output_file, file_name)

    # レスポンス
    return {
        'statusCode': 200,
        'body': json.dumps('OK')
    }
```

## テスト

ソースコードと適当なテストコードを保存して実行するとS3バケットにCSVファイルが出力される。

<img width="1323" alt="S3_Management_Console.jpg" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/248805/6e3c87d7-0f28-27aa-e1a8-563b7f30cdeb.jpeg">


## まとめ
以外と簡単にkintoneからCSVを出力してS3にアップロードできますね。

これでCloudWatchEventと組み合わせれば、CSV出力が定期的に実行できるようになります。
今回は何も触れていませんがDynamoDBと組み合わせれば、CSVで出力した内容をDynamoDBに連携することも実現できそうです。

cli-kintoneで実行するコマンドを変化させればkintoneからレコードを取得、登録も容易になります。
違うコマンド（オプション）は[公式](https://github.com/kintone/cli-kintone#usage)を確認してね！

良いkintone, AWSライフを！！！
Spring MVCがひとつ以上のテンプレートエンジン一緒に利用できる

# 例

この例は、Thymeleafテンプレートエンジンを使っているSpring MVCプロジェクトに、もうひとつFreeMarkerテンプレートエンジンを加え、FreeMarkerテンプレートエンジンを使う'hello world'ウェブページを作ること。

まずは[サンプルアプリケーション](https://qiita.com/saladlam/items/ce25e79dc2babcb453c3)のソースコードを取り、*pom.xml*にFreeMarkerテンプレートエンジンをプロジェクトに入れる。

```xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
```

FreeMarkerテンプレートを作成する。名前は*'resources/templates/public/freemarker_test.ftl'*。

```html
<html>
<head>
  <title>${title}</title>
</head>
<body>
  <div>${body}</div>
</body>
</html>
```

*info.saladlam.example.spring.noticeboard.controller.PublicController*コントローラに新しいメソッドを入れる。

```java
public class PublicController {
    // ...
    @GetMapping("/freemarker_test")

    public String freemarkerTest(Model model) {
        model.addAttribute("title", "FreeMarker test page");
        model.addAttribute("body", "Hello world!");

        return "public/freemarker_test";
    }
}
```

アプリケーションを起動し、ブラウザで[http://localhost:8080/freemarker_test](http://localhost:8080/freemarker_test)をアクセスする。すると'Hello world!'が見える。

# 何があった？

アプリケーションを起動すると、下の*org.springframework.web.servlet.ViewResolver* BeanはSpring Bootが作った。
- org.thymeleaf.spring5.view.ThymeleafViewResolver
- org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver

*org.springframework.web.servlet.DispatcherServlet*インスタンスが作られて、初期化する時、メソッド**initViewResolvers()**が呼ばれった。

```java
        if (this.detectAllViewResolvers) {
            // Find all ViewResolvers in the ApplicationContext, including ancestor contexts.
            Map<String, ViewResolver> matchingBeans =
                    BeanFactoryUtils.beansOfTypeIncludingAncestors(context, ViewResolver.class, true, false);
            if (!matchingBeans.isEmpty()) {
                this.viewResolvers = new ArrayList<>(matchingBeans.values());
                // We keep ViewResolvers in sorted order.
                AnnotationAwareOrderComparator.sort(this.viewResolvers);
            }
        }
        // ...
```

上のコードに示したのは、全ての*ViewResolver* Beanは*BeanFactory*からもらう。

*PublicController*クラスの**freemarkerTest()**メソッドが呼ばれ、文字列'public/freemarker_test'が返す。この文字列は*org.thymeleaf.spring5.view.ThymeleafViewResolver*で調べ、この名前のThymeleafテンプレートが定義されないため、nullが返した。そして*org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver*で調べ、*org.springframework.web.servlet.View*インタフェースのインスタンスが返した。
AWS Lightsail で Bitnami HTTPS Configuration Tool を 使った Let's Encrypt (自動更新まで)

# Bitnami HTTPS Configuration Tool

Let’s Encrypt での証明書の取得、HttpからHttpsへのリダイレクト、同じくnon-wwwからwwwドメインへのリダイレクト、cron での自動更新の設定までやってくれる、スムーズに使えればありがたいツール

**↓本来ならここにある↓**

`$ /opt/bitnami/bncert-tool`

なぜかない場合(2019-09-18 現在、Lightsail で WordPress を選ぶと入ってなかった)入れましょう。

## ツールをダウンロードする

Bitnami のサイトからツールをダウンロードして既定の場所に置く
詳しくは[リンク先](https://docs.bitnami.com/aws/how-to/understand-bncert/)参照

```
wget -O bncert-linux-x64.run https://downloads.bitnami.com/files/bncert/latest/bncert-linux-x64.run
sudo mkdir /opt/bitnami/bncert
sudo mv bncert-linux-x64.run /opt/bitnami/bncert/
sudo chmod +x /opt/bitnami/bncert/bncert-linux-x64.run
sudo ln -s /opt/bitnami/bncert/bncert-linux-x64.run /opt/bitnami/bncert-tool

```

# 実際の手順

## 事前準備

- 事前にドメイン取っておく
- www 有りと www 無しのどちらにもレコードを設定しておく
- もし別の証明書がある場合は事前にバックアップをとっておく(一応途中で聞いてくれるが手動のが安心)

### www サブドメインについて補足

本来どちらでもいいらしいが、ツールの自動構成上 www にリダイレクトされる(例: example.com → www.example.com)。
事前に変更できるかは、よく知らない。たぶん、設定書き換えればできると思う。

# ツールを起動する

`sudo /opt/bitnami/bncert-tool`

あとは指示に従って進めればおk。
おk じゃない人は続きどうぞ。

## もしかしたらつまづきそうなところ

### ドメイン入力

- 入力は www 有りでも www 無しでも大丈夫
- 上述の通り、最終的には www 有りにリダイレクトされる。その際、www 有りのサブドメインで名前解決できないとエラーになるので、予めどちらも設定しておくといい

### メールアドレス

きちんと入れましょう。受信できれば専用アドレスを作ればいいと思います。ただ、Gmail のエイリアスは弾かれました(ゴミ情報)

### 基本的に Yes

いえ、本来は「読んだ上で Yes」ですが、結局全部 Yes じゃないと使えません。~~熱心な人は~~Let's Encrypt の利用規約など読んでください。

# ログの確認

/tmp/bncert-yyyymmddHHMM.log というファイルに一部始終が~~書かれています~~と思っていましたが、スクリプトのログ読み慣れていないのでいまいち何やってるかわかりません m(\_ \_)m

とりあえず、証明書のパスや cron の設定などは出力されているので、バックアップや cron が実行されるかわからなくて不安な人は確認してみるといいと思います。

## cron に設定されるらしい

※備忘のため書いておきます※
詳しくは crontab を確認するといい。(説明によると毎月で設定される。時間はずらすと吉。ログ見るとスクリプトを実行した時間で毎月実行されるっぽい??)

(更新履歴)
2019-09-18:公開
GASのdoPost関数をcurlでテストする時リダイレクトが必要なら-Xオプションを使わない

# 結論

GASの「ウェブアプリケーションとして導入」を利用して、doPostの検証をcurlで行う時はリダイレクトが必要なら

```
$ curl -d "" -L <url>
```

のように指定する。

```
$ curl -X POST -d "" -L <url>
```

のように-Xオプションを指定すると405エラーになります。
-Xオプションのリダイレクト時の挙動が原因です。
その事については以下の記事にまとめましたのでぜひ御覧ください。

[curlで送ったPOSTリクエストがリダイレクトされる時-XPOSTを使うか否かで挙動が変わる (https://qiita.com/nkitgamesake/items/3907a677e09ad1a5b57b)](https://qiita.com/nkitgamesake/items/3907a677e09ad1a5b57b)

# 詳細

```javascript
function doPost(e){
  return ContentService.createTextOutput("Hello World");
}
```

上記のシンプルなHelloWorldコードをcurlを使ってテストします。
doPostのテストなので-Xオプションを使ってPOSTを指定します。

```
$ curl -X POST <end point url>
<HTML>
<HEAD>
<TITLE>Moved Temporarily</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Moved Temporarily</H1>
The document has moved <A HREF="...">here</A>.
</BODY>
</HTML>
```

Moved Temporarilyと言われてしまいました。
レスポンスヘッダを確認してみます。

```
curl -i -X POST <end point url>
HTTP/2 302
...
```

302が返ってきました。
curlはデフォルトではリダイレクトに対応しないので、-Lオプションをつけてリダイレクトを処理します。

```
curl -X POST -L <end point url>
<!DOCTYPE html>

...

<title>Google ドライブ -- ページが見つかりません</title>

...

<p class="errorMessage" style="padding-top: 50px">現在、ファイルを 開くことができません。</p><p>アドレスを確認して、もう一度試してください。</p>

...
```

Google ドライブ？ファイルを開くことができません？
何言ってんだこいつ🤔

余談ですがすぐにレスポンスヘッダとか確認すればいいのにぼくはそれをせず、結果沼の奥底へ沈んでいきました。
推測するな、計測せよは本当に金言だと思います。（パフォーマンスチューニングの言葉らしいですが）

レスポンスヘッダを確認してみます。

```
$ curl -i -X POST -L <end point url>
HTTP/2 302

...

HTTP/2 405
...
allow: HEAD, GET

...
```

405が返ってきていました。
これはターゲットリソースがメソッドをサポートしていない事を表しています。
allowヘッダが示しているように、HEADとGETのみ許可されているらしいです。

-vオプションを付けると動作の詳細を確認出来るので見てみました。

```
$ curl -v -X POST -L <end point url>
...

> POST /macros/echo?user_content_key=... HTTP/2

...
```

この行だけだと伝わりづらいかもしれませんが、リダイレクト先のリソースに対してPOSTメソッドでリクエストを送っていました。上記の通り許可されているのはHEADとGETのみなのでこれが問題だったみたいです。

一般的にWebブラウザは302が返ってきたらGETメソッドに切り替えてリダイレクトします。
GASもその仕様に合わせてリダイレクト先ではGETメソッドを受け付けているという事なんだと思います。

curlの-Lオプションでリダイレクトを許可すると、多くのブラウザと同じようにリダイレクト時にPOSTをGETに切り替えます。
しかし-Xオプションは全てのリクエストに対して反映されるのでリダイレクト先にもPOSTリクエストを送ってしまいます。
このあたりは上記の私の記事にまとめましたのでぜひ御覧ください。

解決方法は既に結論で申し上げたとおり、-Xオプションを使いません。
-dオプションはPOSTのデータを指定するためのオプションですが、このオプションを指定した時点でPOSTメソッドが指定されるため-Xオプションを使う必要がありません。
送るデータがない場合は例のように空にすればOKです。

```
$ curl -d "" -L <end point url>
Hello World
```

やったぜ😂
GCEインスタンスの権限設定について


##はじめに
GCEインスタンスからGCPサービスを操作するための権限設定方法について書きたいと思います。

##権限設定の方法
GCEインスタンスからGCPサービスを操作するための権限設定は、サービスアカウントをGCEインスタンスに付与することで設定します。AWSで言うところのEC2にIAMロールを付与するようなイメージです。

サービスアカウントに、ロールを付与して、ロールに各GCPのAPIを実行できるパーミッションが付与される形になります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78184/4112edcf-9e9b-dac4-25fa-e775017a4063.png)

GCPコンソールで、GCEを作成する画面にサービスアカウントの設定の下にAccess Scopeというものが表示されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78184/c17ab67f-70eb-d46f-ee98-998cd9b626a0.png)

Access Scopeは、デフォルトで作成されるGCE用のサービスアカウントで使用できるものです。
デフォルトで作成されているGCE用のサービスアカウント(Compute Engine default service account)は、レガシーなPrivilege RoleであるEditorロールが付与されており、強い権限を持っています。

>
注: roles/editor の役割には、ほとんどの GCP サービスでリソースを作成および削除する権限が含まれますが、一部のサービス（Cloud Source Repositories や Stackdriver など）ではそれらの権限が含まれません。
https://cloud.google.com/iam/docs/understanding-roles#role_types

そのため、Compute Engine default service accountの権限を制限するためにAccess Scopeが使用できます。カスタムサービスアカウントでは、Access Scopeの設定はできません。Privilege Roleとアクセススコープを使った権限設定は、レガシーな方法であり推奨されていません。

デフォルトで作成されているGCE用のサービスアカウント(Compute Engine default service account)は、Access Scopeが選択できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78184/037ee02f-d3b5-a866-4c56-8591f5b3b4c5.png)

自身で作成したサービスアカウントは、Access Scopeが選択できない。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78184/aae0ba94-820a-77d9-7482-ee732ad811df.png)

アクセススコープは、以下の3つから選択できます。
https://cloud.google.com/compute/docs/access/service-accounts?hl=ja#accesscopesiam

- Allow default access
    -  デフォルト値: Storage および Service Management に対する読み取り専用アクセス権、Stackdriver Logging および Monitoring に対する書き込みアクセス権、Service Control に対する読み取り / 書き込みアクセス権
- Allow full access to all Cloud APIs
    - GCEインスタンスから、プロジェクト内のリソースに対してすべてのAPIが実行可能になります
- Set access for each API
    - 任意のAPIに対して許可設定します 
    - IAM Permissionほど細かい制御はできません

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78184/df74464c-8a7a-6e61-19aa-74281e4dcabc.png)

##サービスアカウントの作り方
IAM & admin>Service accounts>Create service accounts
サービスアカウントに名前を記載する。(メールアドレスが作成される)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78184/8c3d0a20-c396-8a5e-1ae0-5a338ae8f604.png)

Roleを選択する
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78184/dd41fbf9-3fc9-0855-cb08-7ca72df3fc37.png)

作成するサービスアカウントの所有権とアクセス権限を付与したいユーザグループを指定する※オプション
https://cloud.google.com/iam/docs/granting-roles-to-service-accounts?&_ga=2.216597334.-1303976701.1565066087#granting_access_to_a_user_for_a_service_account
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/78184/e29af425-b6c9-4195-4420-a4fec2f33db2.png)

##その他
注意点は、サービスアカウント、アクセススコープの変更は、インスタンスを停止しないとできない。

##投稿内容は私個人の意見であり、所属企業・部門見解を代表するものではありません。
Named routeに引数を渡すサンプルコード

# はじめに

本記事は[Flutter Cookbook - Pass arguments to a named route](https://flutter.dev/docs/cookbook/navigation/navigate-with-argumentsFlutter Cookbook)と殆ど同じ内容です。

Cookbookに記載されているComplete exampleが`onGenerateRoute`関数を使用したものしかなかった為、`onGenerateRoute`関数を使用しない版のComlete exampleを記録しています。

# 実装の手順
1. 引数を定義したクラスを作成
2. 引数を抽出し、引数を処理するウィジェットを作成
3. 2で作成したウィジェットをルートテーブルに設定
4. 2で作成したウィジェットに引数を渡しつつ遷移

# Comlete example

```dart

import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Navigation with Arguments',
      home: HomeScreen(),
      // 3. 2で作成したWidgetをルートテーブルに設定
      routes: {
        ExtractArgumentsScreen.routeName: (_) => ExtractArgumentsScreen(),
      }
    );
  }
}

class HomeScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home Screen'),
      ),
      body: Center(
        child: RaisedButton(
          child: Text("Navigate to screen that extracts arguments"),
          onPressed: () {
            // 4. ExtractArgumentsScreenに遷移
            //    argumentsプロパティでScreenArgumentsオブジェクトを生成し、引数を提供
            Navigator.pushNamed(
              context,
              ExtractArgumentsScreen.routeName,
              arguments: ScreenArguments(
                'First argument',
                'Second argument.',
              )
            );
          },
        ),
      ),
    );
  }
}

// 2. 引数を抽出し、引数を処理するWidgetを作成
class ExtractArgumentsScreen extends StatelessWidget {
  static const routeName = '/extractArguments';

  @override
  Widget build(BuildContext context) {

    // `ModalRoute.of()`メソッドを使用して、
    // `ScreenArguments('First argument', 'Second argument.')の引数を取得
    final ScreenArguments args = ModalRoute.of(context).settings.arguments;

    return Scaffold(
      appBar: AppBar(
        title: Text(args.title),
      ),
      body: Center(
        child: Text(args.message),
      ),
    );
  }
}

// 1. 引数を定義したクラス
class ScreenArguments {
  final String title;
  final String message;

  ScreenArguments(this.title, this.message);
}
```
Homestead.yamlのsitesの役割の話（nginxの設定値になっている）

Homestead.yamlのsitesだが、Homesteadが起動するnginxのdocument_rootに使用される。

```yaml:Homestead.yaml
sites:
    - map: homestead.test
      to: /home/vagrant/code/hoge-web-api/public
```

上記の場合、

```conf:/etc/nginx/sites-enabled/homestead.test
server {
    listen 80;
    listen 443 ssl http2;
    server_name .homestead.test;
    root "/home/vagrant/code/hoge-web-api/public"; # この部分にsites.toの値が反映される
    以下略...
}
```

修正時、設定を反映させる（`/etc/nginx/sites-enabled/homestead.test`の修正版を出力する）ためには、

```
vagrant reload --provision
```

する必要がある。（`vagrant reload`だけだと修正されない）
PytorchのTensorをonehotにする

PytrochでVOCのマスク画像をonehotにしなければならなかったのですが、少し調べるのに時間がかかったのでここに記します。

for文を回せばできることはできますが、
tensorのままスマートにやりたかったので探しました。

#onehot化する
```python
def onehot(image_tensor, n_clsses):
    h, w = image_tensor.size()
    onehot = torch.LongTensor(n_classes, h, w).zero_()
    image_tensor = image_tensor.unsqueeze_(0)
    onehot = onehot.scatter_(0, image_tensor, 1)
    return onehot
```

簡単にコードの解説をします。

最初に`torch.LongTensor(n_classes, h, w).zero_()`で
縦横のサイズはおなじで奥行きがクラス数の、0で埋められたTensorを作ります。

次に`image_tensor = image_tensor.unsqueeze_(0)`で先程作ったゼロTensorにサイズをあわせます。
ゼロTensorのサイズは_(n_classes, h, w)_なので、
入力する画像をサイズ_(h, w)_の0からクラス数までの整数をもったTensorとすると、
`unsqueeze_(0)`で_(1, h, w)_としています。

最後に`onehot.scatter_(0, image_tensor, 1)`でondhotにすることができます。
引数はscatter_(dim, index, src)となっており、
image_tensorをインデックスとして0を1に変換するということになります。

#使ってみる
実際に使ってみましょう

```python
import torch
import torch.nn as nn
import torch.nn.functional as F

n_classes = 4
w, h = 8, 8
image_tensor = torch.randint(0, n_classes, (w, h))
print(image_tensor)
# tensor([[0, 1, 3, 3, 3, 0, 3, 0],
#         [1, 3, 2, 1, 2, 3, 1, 3],
#         [1, 0, 2, 0, 1, 3, 3, 1],
#         [1, 0, 0, 1, 3, 1, 2, 0],
#         [0, 0, 0, 3, 3, 3, 1, 3],
#         [2, 3, 2, 3, 0, 1, 2, 0],
#         [1, 1, 2, 0, 1, 3, 0, 1],
#         [2, 0, 3, 0, 1, 1, 1, 0]])


def onehot(image_tensor, n_clsses):
    h, w = image_tensor.size()
    onehot = torch.LongTensor(n_classes, h, w).zero_()
    image_tensor = image_tensor.unsqueeze_(0)
    onehot = onehot.scatter_(0, image_tensor, 1)
    return onehot

onehot_tensor = onehot(image_tensor, n_classes)
print(onehot_tensor)
# tensor([[[1, 0, 0, 0, 0, 1, 0, 1],
#          [0, 0, 0, 0, 0, 0, 0, 0],
#          [0, 1, 0, 1, 0, 0, 0, 0],
#          [0, 1, 1, 0, 0, 0, 0, 1],
#          [1, 1, 1, 0, 0, 0, 0, 0],
#          [0, 0, 0, 0, 1, 0, 0, 1],
#          [0, 0, 0, 1, 0, 0, 1, 0],
#          [0, 1, 0, 1, 0, 0, 0, 1]],

#         [[0, 1, 0, 0, 0, 0, 0, 0],
#          [1, 0, 0, 1, 0, 0, 1, 0],
#          [1, 0, 0, 0, 1, 0, 0, 1],
#          [1, 0, 0, 1, 0, 1, 0, 0],
#          [0, 0, 0, 0, 0, 0, 1, 0],
#          [0, 0, 0, 0, 0, 1, 0, 0],
#          [1, 1, 0, 0, 1, 0, 0, 1],
#          [0, 0, 0, 0, 1, 1, 1, 0]],

#         [[0, 0, 0, 0, 0, 0, 0, 0],
#          [0, 0, 1, 0, 1, 0, 0, 0],
#          [0, 0, 1, 0, 0, 0, 0, 0],
#          [0, 0, 0, 0, 0, 0, 1, 0],
#          [0, 0, 0, 0, 0, 0, 0, 0],
#          [1, 0, 1, 0, 0, 0, 1, 0],
#          [0, 0, 1, 0, 0, 0, 0, 0],
#          [1, 0, 0, 0, 0, 0, 0, 0]],

#         [[0, 0, 1, 1, 1, 0, 1, 0],
#          [0, 1, 0, 0, 0, 1, 0, 1],
#          [0, 0, 0, 0, 0, 1, 1, 0],
#          [0, 0, 0, 0, 1, 0, 0, 0],
#          [0, 0, 0, 1, 1, 1, 0, 1],
#          [0, 1, 0, 1, 0, 0, 0, 0],
#          [0, 0, 0, 0, 0, 1, 0, 0],
#          [0, 0, 1, 0, 0, 0, 0, 0]]])
```
ちゃんとサイズを保ったままonehotになっているのがわかると思います。


#追記
より少ない行数でできる方法を@koshian2さんに教えていただきました。

```
img_tensor=torch.randint(0, 4, (8,8))
torch.eye(4)[img_tensor].permute([2,0,1])
```
Tensorはインデックス指定もできるんですね!
Golangで外部パッケージから呼び出す際に躓きがちなこと

Golangの初学者が、ハマりがちな外部パッケージでの構造体（関数等々）呼び出しに関して書いておきます！

# importするだけじゃ不十分

下記のコードでmodelに書いた構造体（user）を外部パッケージでは呼び出せません。

```model/user.go
package model

type user struct {
	name string
	age  int
}
```

```controller/user.go
package controller

import (
	"${PATH}/model"
)

var user model.user //undefined: model.user


```
//undefined: model.user
ってなります。


# 解決策

```model/user.go
package model

type User struct {
	name string
	age  int
}
```

```controller/user.go
package controller

import (
	"${PATH}/model"
)

var user model.User


```

参考：https://qiita.com/zurazurataicho/items/4a95e0daf0d960cfc2f7

参照したい構造体(や関数)を大文字にするだけで読み込める！

多分躓くと思うので書いておきます！
JavascriptでPDFを作成する

#javascriptでPDFを出力する

##利用ツール
pdfMake

##手順
###①pdfMakeをダウンロードしてくる
https://github.com/bpampuch/pdfmake
※上記からcloneしてきても、zipでダウンロードしてきてもOK

###②日本語化フォントを取得

https://github.com/naoa/pdfmake
から日本語化フォント取得

日本語化フォントのある場所は、buildフォルダ内にあります。

naoa様が日本語化のフォントを作成してくださっています。
http://blog.createfield.com/entry/2015/12/16/052221
ipagp.tff等のフォントを利用して作成してもいいのですが、個人的には
naoa様が作成しているフォントセットを利用することをお勧めします。

ipagp.tffなどのフォントセットは、フルで日本語フォントが入っているため、
容量も5MBとかなり大きいものとなっています。
クライアントサイドで何度も読み出すことを考えると、ちょっと重いなーと。。。

naoa様のフォントセットは、あまり利用回数が多くないものを除外し、2M強までシェイプアップしてくれています。
クライアント側で動かすことを考えるとかなり嬉しいシェイプアップ。
利用して、対応できなければフルのフォントセットを利用する。ぐらいでいいのではないでしょうか？

追記
上記のフォントセットを利用させて頂きましたが、要件で、日本語フォントの数字（１　２）などが文字化けしてしまう問題を受けて、
フルフォントセットを利用する方法を自社のエンジニアが記載してくれました。
→[pdfmakeの日本語対応](https://qiita.com/dq_noda_kotaro/items/eb56f7fecd9a2a318b8c "pdfmakeの日本語対応")

###③JSのソースにフォルダを作成

今回はこんな感じの構成にしました。

PJフォルダ
　└── js
　　└──pdfMake
　　　└──pdfmake.min.js
　　　└──vfs_fonts.js

　└─sample.html
　　　　　※ここにnaoa様のvfs_font.jsを置く



###④pdfMakeを読み込み

```sample.html

      <!-- OutPutPDF -->
      <script src="./js/pdfmake/pdfmake.min.js"></script>
      <script src="./js/pdfmake/vfs_fonts.js"></script>
```

###⑤PDF出力処理を記載

```sample.html
      <script>
          //日本語フォント読み込み
          pdfMake.fonts = {
            GenShin: {
            normal: 'GenShinGothic-Normal-Sub.ttf',
            bold: 'GenShinGothic-Normal-Sub.ttf',
            italics: 'GenShinGothic-Normal-Sub.ttf',
            bolditalics: 'GenShinGothic-Normal-Sub.ttf'
            }
          };
          //PDF作成処理
          var docDef = {
            content:[
              {text: "Hellow World", fontSize: 20}
            ],
            defaultStyle: {
              font: 'GenShin',
            },
          };
          pdfMake.createPdf(docDef).download("出力テスト.pdf");

      </script>
```

###最後に
手軽に導入できるので、
利用してみればいいかもです。
font-awesomeをrailsで使う

初投稿です。

#####Rails5でFont-awesomeのgemを入れてみたが、表示されるアイコンと表示されないアイコンがあったので、そもそもgemを使わない方法を調べてみた。

https://saruwakakun.com/html-css/basic/font-awesome
こちらの記事を参考にさせていただきました。

```
<link href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" rel="stylesheet">
```
こちらを、application.html.erbのheadタグ内に記述するだけです。

欲しいアイコンをfont-awesomeの公式サイトで探します。
（例）```<i class="fas fa-home"></i>```
上記を表示させたいviewに貼り付けて完成。

サイズを大きくしたい場合は、
（２倍のサイズ）```<i class="fas fa-home　fa-2x "></i>```
[Tips] TFRecordsのNoteSequenceの中身を確認する方法


備忘録も兼ねてTipsを共有します。掲題の通り、Tensorflowで楽曲データを取り扱うときのデータ形式である `NoteSequence` の中身を目視で確認する方法です。

## 動作環境

- [magenta](https://github.com/tensorflow/magenta)がインストール済。
    - `tensorflow/tensorflow:latest-devel` のDocker内で `pip install magenta` を実行した状態。
- Python 2.7
    - [Cloud Dataflow](https://cloud.google.com/dataflow/?hl=ja)をPythonから使うために2系にする必要があったので...。

## コード

```python
from magenta.music import note_sequence_io


tfrecord_path = '/path/to/note_sequence.tfrecord'
for sequence in note_sequence_io.note_sequence_record_iterator(tfrecord_path):
    print sequence.id
    print sequence.collection_name
    print sequence.filename
    print sequence.notes
```

## 出力結果

以下のような出力が得られます。 私は `楽器Noって含まれてるんだっけ？` 等の確認がしたくて見た感じです。

```
/id/midi/dir_name/xxxxxxxxxxxxxxxxxxxxxxx
dir_name
xxxxxxx.mid
[pitch: 75
velocity: 94
start_time: 74.5522065
end_time: 74.608176625
program: 12
, pitch: 87
velocity: 86
start_time: 29.552226
end_time: 29.88804675
instrument: 1
, pitch: 89
velocity: 93
start_time: 29.999987
end_time: 30.33580775
instrument: 1
, pitch: 91
...
]
```

## midi -> NoteSequence化

[magentaリポジトリのコンバートスクリプト](https://github.com/tensorflow/magenta/tree/master/magenta/scripts)で簡単にできます。
Macにおける、Puppeteer の文字列削除

##環境
パペッターのバージョンは、^1.19.0

##背景
文字列削除したかったら、Cmd + Aして削除しようと思っていたが、
Macだと色々面倒らしいです。「Command」が使えなかったり。

もちろんググりましたが、Macの人は文字列削除とかキーに関しては結構困っているっぽいです。僕も困りました(Command押せない。。。)

色々ググったらこの記事が出てきたのですが、
https://github.com/GoogleChrome/puppeteer/issues/1313

```
await input.click({ clickCount: 3 }); await page.keyboard.press("Backspace");
```

クリック三回して削除押せばなんとかなるで？みたいに書いてあったのですが、これは文字列が一行だったらの話で、2行以降あったらすべて削除できなくなります。

対象の文字列が一行なら上記で解決できますが、私の場合は勤怠入力の自動化がしたくて、複数行のコメントの入力をなんどもやり直ししたいので、上記の方法ではできなかった。

ここで、ゴリ押しですが、なんとか考えてみました。

## プログラム
page.focusとかpage.click試してみたのですが、なぜかうまく行かなかったので、フォーカスする目的で空の文字列を入力するようにして解決しました。

文字を入力するときに使う「page.type」は追記となるので、入力を始めると最終的にカーソルが一番下に来ることになるので、
Shift+上矢印して複数行選択して、最後にBackSpace押すという
ゴリ押しの解決です。
Windowsとか仮想環境でやればこんな変なことしなくてすむんですけどねぇ。。。

```javascript:hoge.js
    // テキストボックスにフォーカスする目的で、空の文字列を入力
    await page.type('textarea[name="txt"]', '');
    
    //入力しようよしている時点でカーソルが一番下にあるから
    await page.keyboard.down('Shift');
//—————————————————————————————
    //行数分でループさせるか、めっちゃ増やすか
    await page.keyboard.press('ArrowUp');
    await page.keyboard.press('ArrowUp');
    await page.keyboard.press('ArrowUp');
    await page.keyboard.press('ArrowUp');
//—————————————————————————————
    await page.keyboard.up('Shift');
    await page.keyboard.press("Backspace"); 

```
上記の処理、文字列の行数を取得してその分ループさせてもいいかもですね。

## 結果
ふぅ〜！消えた！

## 感想
このモジュール、1番好き。
自動で操作できるの楽しい
EC2上にNode.jsでWSSサーバをたてる

## はじめに
WebSocketの通信テスト(SSL)を簡単に実施したくて、EC2上にたててみました。

## サーバ側でしたこと
### EC2の設定
```
$ sudo su
# curl -sL https://rpm.nodesource.com/setup_12.x | sudo bash -
# sudo yum install nodejs
# node -v
v12.10.0
# npm -v
6.10.3
# npm install ws https http
```
* securitygroupはssh,http,https,TCP-8080をOPEN
* SSL用のサーバ証明書一式を/etc/ssl/certs/へコピー

### Route53の設定
* EC2インスタンスのIPアドレスをレコードセットに登録

<img width="301" alt="route53" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/89831/70902e50-c1a3-6ee3-a57d-2644ae06a5b0.png">


### EC2上に置くWSSサーバのコード
[websockets/ws](https://github.com/websockets/ws)のサンプルコードに一部追記しました。
今回clientはサーバから送られるのを受けとめることがメインです。

```wsServer.js

const fs = require('fs');
const https = require('https');
const WebSocket = require('ws');

const server = https.createServer({
  cert: fs.readFileSync('/etc/ssl/certs/サーバ証明書'),
  ca: fs.readFileSync('/etc/ssl/certs/中間証明書'),
  key: fs.readFileSync('/etc/ssl/certs/キーファイル')
});

const wss = new WebSocket.Server({ server });

wss.on('connection', function connection(ws,req) {
    //connect時
    const ip = req.connection.remoteAddress;
    console.log('---connect from ' + ip);
    ws.send('Hello from wssServer!');

    //receive message
    ws.on('message', function incoming(message) {
        console.log(message);
    });

    //標準入力で入力したメッセージを接続中のclientに送る
    process.stdin.on('data', function (chunk) {
      var sendmessage = chunk.toString();
      ws.send(sendmessage);
    });
});

//8080ポートでサーバ待ち受け
server.listen(8080);
```

* EC2インスタンス上でWSSサーバを起動させる。
    * 標準入力で入力した文字列がソケット接続中のclientに送られます
   
```
$ node wsServer.js
```

## クライアント側
### 簡易テストではクライアント側はwscatを使用すると便利！
[WebSocket の検証では wscat が便利だった](https://qiita.com/toshihirock/items/4dfaca29058c9e744529)を参考にさせていただきました！

```
$ npm install -g wscat

#HTTP
$ wscat -c ws://xxxx

#HTTPS
$ wscat -c wss://xxxx:8080
```

Route53で登録したドメインでエラーなく接続できればOK!だったのですが…

ここで正規の証明書を用いているのに`error: unable to verify the first certificate`とエラーになってしまった…。

### クライアント側に証明書が入ってないの？
* クライアント：Windows上でWSL(ubuntu)を使用

```
$ ls /usr/share/ca-certificates/mozilla/ | grep DigiCert
DigiCert_Assured_ID_Root_CA.crt
DigiCert_Assured_ID_Root_G2.crt
DigiCert_Assured_ID_Root_G3.crt
DigiCert_Global_Root_CA.crt
DigiCert_Global_Root_G2.crt
DigiCert_Global_Root_G3.crt
DigiCert_High_Assurance_EV_Root_CA.crt
DigiCert_Trusted_Root_G4.crt

$ cat /etc/ca-certificates.conf | grep DigiCert_Global
mozilla/DigiCert_Global_Root_CA.crt
mozilla/DigiCert_Global_Root_G2.crt
mozilla/DigiCert_Global_Root_G3.crt
```

あれ？使いたい証明書ちゃんとクライアント側にいるやん...
と、先輩の手も借りて調査したところ、サーバ側で中間証明書を指定して起動しているつもりが、大元のルート証明書を指定して起動していたためエラーが出ているだけでした…なんと初歩的ミスorz
修正して起動しなおして無事に通ることを確認。

Node.js簡単万歳！めでたしめでたし:grin:
[Laravel5.7]storageに保存した画像を指定してダウンロードする

## storageに保存した画像を指定してダウンロードする

### ルーティング

```web.php
Route::get('/download', 'PhotoController@download');
```
### コントローラー

```PhotoController.php
<?php

namespace App\Http\Controllers;
/* 以下略 */

    public function post(Request $request)
    {
        $post = new Photo();
        $post->title = $request->title; 
        //*画像の保存先　storage/app/public/ファイル名
        $post->file = $request->file->store('public'); 
        $post->file = str_replace('public/', '', $post->file);
        $post->user_id = Auth::user()->id;
        $post->save();

        return redirect('/');

    }
}
```


```Controller.php
    public function download()
    {

        $headers = [
            'Content-Type' => 'application/octet-stream',
        ];
        return response()->download(storage_path('public/ディレクトリのファイル名'), $headers);
    }
```
###フロントサイド
```blade.php
<a class="list--download list--icon" href="/download">
```
click. . . ダウンロード出来ました。
WindowsからMacへ移行した人へ贈る小技

Windowsのエクスプローラーでの動作とMacOSのFinderでの対応表。
Macの使い始めてEnter押したら名前変更になったことに面食らった諸兄に捧げます。

| Windows | MacOS | 動作 |
|:-:|:-:|:-:|
| Enter | Command⌘ + ↓ | 子フォルダに移動 <br> or <br> アプリケーションを開く |
| BackSpace | Command⌘ + ↑ | 親フォルダに移動 |
| F2 | Enter | 名前の変更 |
情報科学特論2 レポート課題 GitLab CI

<img width="200px" src="https://qiita-image-store.s3.amazonaws.com/0/155486/4202d3bb-3786-f7e1-fb94-7125439d6cd0.png" alt="https://gitlab.com/uploads/-/system/project/avatar/250833/runner_logo.png">

<a name="「gitlab-ci」とは"></a>
## 「GitLab CI」とは
GitLab CIはCI（Continuous Integration、継続的インテグレーション）ツールの一つです．
私は以前インターンシップでGitLabCIを用いたLaravelアプリケーションのCIツールを自作したことがあるのでこちらについて記載させていただきます． 
<a name="「gitlab-runner」とは"></a>
## 「GitLab Runner」とは

#### 「GitLabサーバー」と「GitLab Runner」の役割
GitLabを用いて継続的インテグレーションを行うためには、「GitLabサーバー」とは別にジョブを実行するための「GitLab Runner」というサーバーが必要．
####GitLabサーバー（CIサーバー、Coordinator）
ジョブ管理者．GitLabのプロジェクトで管理しているブランチへのプッシュやマージリクエストをトリガーとして、GitLab Runnerを呼び出す．
#### GitLab Runner  
実行者．サーバー内の環境でシェルを実行したり、一時的にDockerコンテナを生成してジョブを実行する．

<img width="790" alt="000001-12.png" src="https://qiita-image-store.s3.amazonaws.com/0/155486/664d820d-4295-3c10-30cd-32e4e70421a9.png">

<a name="「executor」とは"></a>
### GitLab Runnerの実行方式「Executor」とは

GitLab Runnerには「Executor」と呼ばれるジョブの実行方式がある．

#### Executorの種類

- **Docker（GitLab推奨）**  
Runnerとは別に一時的に生成したDockerコンテナの環境内でジョブを実行する．ジョブを実行する度にコンテナを新しく生成するため，実行するジョブの冪等性が保証される．
- **Shell（GitLab非推奨）**  
Runnerの環境内でジョブを実行する．ジョブを実行する際にRunnerの環境をクリーンアップしないため、実行するジョブの冪等性が保証されない．またShellによるRunnerの実行はGitLabサーバー上の他プロジェクトのソースコードを取得することができてしまうため、セキュリティに問題があり，GitLabはこの実行方式を非推奨としている． 
- **その他のExecutor**  
    - Docker Machine and Docker Machine SSH (auto-scaling)
    - Parallels
    - VirtualBox
    - SSH
    - Kubernetes

# 研究に用いるアーキテクチャ例
<img width="655" alt="Screen Shot 2019-09-18 at 15.45.34.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/180040/798644b4-5a0f-a0c7-4cbd-eb1a52a2f2ff.png">



# 効果
講義でも説明があったが，継続的インテグレーション (以下CI) とは，アプリ開発時の品質向上・納期短縮を目的とした習慣のこと．機械学習の研究成果においても，CIを行うことで以下の利点があると考えられます．
・バグの修正が容易
・共有がしやすい
・モデルの管理なども自動化することで管理品質の向上

# 問題
講義でもおっしゃられた通り，CIツールの管理は非常に難しく専門家が出来てしまう場合が多い．
特に機械学習のチームは流動性が高いということなので，標準化して協力してCIツールを使う体制が望まれると感じました．
keras2onnxでKeyErrorが出たときの対応

https://github.com/onnx/keras-onnx/tree/master/keras2onnx

keras2onnxを使ってkerasからonnxを作ろうとしたときにエラーがでたので対応策をまとめました。kerasでもtf.kerasでも同じエラーがでましたが、同じ対応でエラーが無くなりました。

それぞれのバージョンはこうです。

```
>>> import tensorflow
>>> tensorflow.__version__
'1.14.0'
>>> import keras
Using TensorFlow backend.
>>> keras.__version__
'2.2.5'
>>> import keras2onnx
>>> keras2onnx.__version__
'1.5.1'
```

## エラーメッセージ
この2つのどちらかなら対応可能

```
  File "C:\home\app\anaconda3\lib\site-packages\keras2onnx\ke2onnx\dense.py", line 39, in convert_keras_dense
    apply_activation_function = activation_map[operator.raw_operator.activation]
KeyError: <function softmax at 0x0000018F347C1598>
```

```
    File "C:\home\app\anaconda3\lib\site-packages\keras2onnx\ke2onnx\dense.py", line 39, in convert_keras_dense
    apply_activation_function = activation_map[operator.raw_operator.activation]
KeyError: <function relu at 0x00000156FB5B0730>
```

## 問題の箇所
GoogleのチュートリアルのソースコードをMLPにしたいのでLayerを追加してonnxにしようとしました。

```python
model = tf.keras.models.Sequential([
  tf.keras.layers.Flatten(input_shape=(28, 28)),
  tf.keras.layers.Dense(512, activation=tf.nn.relu),
  tf.keras.layers.Dropout(0.2),
  tf.keras.layers.Dense(10, activation=tf.nn.softmax)
])
```

これをkeras2onnx.convert_kerasに渡した所、activation=tf.nn.reluとactivation=tf.nn.softmaxが変換できなかったようで、エラーになっていました。
下のように2行に分けて書けばエラーが無くなりました。

```python
    keras.layers.Dense(512),
    keras.layers.Activation('relu'),
    keras.layers.Dropout(0.2),
    keras.layers.Dense(10),
    keras.layers.Activation('softmax')
```

最終的に上手く変換できたコードがこちらです。

```python
import tensorflow as tf
import keras
import keras2onnx
import os
import onnx

# https://www.tensorflow.org/tutorials
# PyCharmでも設定
#os.environ['TF_KERAS'] = "1"

mnist = tf.keras.datasets.mnist

(x_train, y_train),(x_test, y_test) = mnist.load_data()
x_train, x_test = x_train / 255.0, x_test / 255.0

model = keras.models.Sequential([
    keras.layers.Flatten(input_shape=(28, 28)),
#  tf.keras.layers.Dense(512, activation=tf.nn.relu),
#  tf.keras.layers.Dense(512, activation=tf.nn.relu),
    keras.layers.Dense(512),
    keras.layers.Activation('relu'),
    keras.layers.Dense(512),
    keras.layers.Activation('relu'),
    keras.layers.Dropout(0.2),
    #keras.layers.Dense(10, activation=tf.nn.softmax)
    keras.layers.Dense(10),
    keras.layers.Activation('softmax')
])
model.compile(optimizer='adam',
              loss='sparse_categorical_crossentropy',
              metrics=['accuracy'])

#model.fit(x_train, y_train, epochs=1)
#model.evaluate(x_test, y_test)

onnx_model = keras2onnx.convert_keras(model, model.name)
onnx.save_model(onnx_model, "onnx/keras_mlp.onnx")
```
Nuxt.jsと認証 ~tokenの扱い~

最初はaxiosで取得したtokenをstateで持たせてページ遷移の際に毎回APIを叩く処理を考えたのですが
stateはページ遷移の際消えるので考えていた処理ができませんでした。

調べてみると王道の手法はtokenをセッションブラウザのクッキーに保存しページ遷移時にstateに変更を加えるという方法でした。

クッキーを処理するために下記moduleを入れます。

```
npm install js-cookie --save
npm install cookieparser --save
```

続いてstoreの処理


```store/index.js
const cookieparser = require('cookieparser')


export const state = () => ({
  auth: false,
})

//mutationsはstateに反映する処理を書く
//発火させるのはまた別の箇所で記述
export const mutations = {
  setAuth(state, auth) {
      state.auth = auth
  },
}


export const actions = {
  nuxtServerInit({commit}, {req}) {
    let auth = null
    if (req.headers.cookie) {
      const parsed = cookieparser.parse(req.headers.cookie)
      try {
        auth = JSON.parse(parsed.auth)
      } catch (err) {
        // 有効なクッキーが見つからない場合
      }
    }
    commit('setAuth', auth)
    //↑ここで発火（stateに反映させてる）
  }
```

View側の処理

```pages/login.vue
<template>
  <main>
    <div id="registrationForm" class="form-wrapper">
      <form>
        <div class="form-item">
          <label>メール</label>
          <input v-model="username" type="email" name="email"></input>
        </div>
        <div class="form-item">
          <label>パスワード</label>
          <input v-model="password" type="password" name="password" ></input>
        </div>
        <div class="button-panel">
          <button class="button" title="ログイン" v-on:click="Login" type="button" value="ログイン">ログイン</button>
        </div>
      </form>
    </div>
  </main>
</template>


<script>
  const Cookie = require('js-cookie')
  export default {
    middleware: 'notAuthenticated',
    data() {
      return {
        username: "",
        password: "",
        token: ""
      };
    },
    created() {
    },
    methods: {
      async Login() {
        setTimeout(() => {
          const postData = {
            scope: "*",
            grant_type: "password",
            client_id: "5",
            client_secret: process.env.CLIENTSECRET,
            username: this.username,
            password: this.password
          };
          this.$axios.post(process.env.AUTH, postData).then((res) => {
            const auth = res.data
            this.$store.commit("setAuth", {auth});
            //↑ここでstate変更
            Cookie.set('auth', auth)
            //↑ここでCookieにtoken保存
            this.$router.push("/admin");
          })
        }, 1000)
      }
    }
  };
</script>
```



### nuxtServerInit　アクション
公式から引用

>nuxtServerInit というアクションがストア内に定義されているときは、Nuxt.js はそれをコンテキストとともに呼び出します（ただしサーバーサイドに限ります）。サーバーサイドからクライアントサイドに直接渡したいデータがあるときに便利です。

つまりnuxtServerInit関数を使用すれば、アプリケーションがロードされた際にセッションブラウザのクッキーをVuexのstoreに渡すことができます。

この方法であれば認証かけたいページで毎回APIを立たなくても
Middleware等で下記処理を書けばよい

```
export default function ({ store, redirect }) {
  // ユーザが認証されて場合
  if (store.state.auth) {
    return redirect('/admin')
  }
}
```

```
export default function ({ store, redirect }) {
  // ユーザが認証されていない場合
  if (!store.state.auth) {
    return redirect('/login')
  }
}
```

### 参考記事
- [Nuxt.jsでlocalStorageを用いる際のTipsとハマったこと](https://qiita.com/sunecosuri/items/3544fb101cabd310acc3)
- [外部認証API (JWT)](https://ja.nuxtjs.org/examples/auth-external-jwt/)
localhost  wget出来ない(ローカルサーバーで出来ない)

wget http://localhost.exemple
でエラーになる。

phpでfile_get_contents("localhost.exemple");
でも取得できない時

# php.ini　の設定確認
/etc/php.ini
allow_url_fopen = true

#/etc/hosts の設定確認
127.0.0.1      localhost.localdomain localhost localhost.exemple

#/etc/httpd/conf/httpd.conf の設定確認
Listen 127.0.0.1:80
が追加されているか。
→見落としがちListenは複数指定できます。
k3dでPrivateRepositoryを使う

## 公式ドキュメント
https://github.com/rancher/k3d/blob/master/docs/examples.md

- LinuxとMacとで、ホームディレクトリのパスは読み換える

## 環境
- Mac

## やること

Docker上でPrivateRegistryを起動して、k3dでのPodデプロイ時にPrivateRegistryからPullする

## PrivateRegisty用のボリューム作成

```
docker volume create local_registry
```

```
❯ docker volume ls | grep local_registrylocal
local_registry
```

## PrivateRegistryコンテナ起動
- レジストリ名は`registry.local`
- ポートは`5000` コンテナ側もローカル側も

```
docker container run -d --name registry.local -v local_registry:/var/lib/registry --restart always -p 5000:5000 registry:2
```

## ローカルにコンフィグファイル置き場を作成

```
mkdir -p ~/.k3d
```

## k3dコンフィグをローカルに作成

```
vim ~/.k3d/config.toml.tmpl
```

```~/.k3d/config.toml.tmpl
# Original section: no changes
[plugins.opt]
path = "{{ .NodeConfig.Containerd.Opt }}"
[plugins.cri]
stream_server_address = "{{ .NodeConfig.AgentConfig.NodeName }}"
stream_server_port = "10010"
{{- if .IsRunningInUserNS }}
disable_cgroup = true
disable_apparmor = true
restrict_oom_score_adj = true
{{ end -}}
{{- if .NodeConfig.AgentConfig.PauseImage }}
sandbox_image = "{{ .NodeConfig.AgentConfig.PauseImage }}"
{{ end -}}
{{- if not .NodeConfig.NoFlannel }}
  [plugins.cri.cni]
    bin_dir = "{{ .NodeConfig.AgentConfig.CNIBinDir }}"
    conf_dir = "{{ .NodeConfig.AgentConfig.CNIConfDir }}"
{{ end -}}

# Added section: additional registries and the endpoints
[plugins.cri.registry.mirrors]
  [plugins.cri.registry.mirrors."registry.local:5000"]
    endpoint = ["http://registry.local:5000"]
```

## k3d起動

```
CLUSTER_NAME=k3s-default
k3d create \
    --name ${CLUSTER_NAME} \
    --wait 0 \
    --auto-restart \
    --volume ~/.k3d/config.toml.tmpl:/var/lib/rancher/k3s/agent/etc/containerd/config.toml.tmpl
```

## kubectlコマンドをk3sを使用するための環境変数をセット

```
export KUBECONFIG="$(k3d get-kubeconfig --name='k3s-default')"
```

## k3dとDockerとNetworkを連結

```
docker network connect k3d-k3s-default registry.local
```

## hostsへPrivateRegistry名を定義

```
❯ tail -n 1 /etc/hosts
127.0.0.1 registry.local
```

## イメージがPushできるか確認

```
docker pull nginx:latest
docker tag nginx:latest registry.local:5000/nginx:latest
docker push registry.local:5000/nginx:latest
```

## PrivateRegistryからPullしてくるDeployment

```
cat <<EOF | kubectl apply -f -
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-test-registry
  labels:
    app: nginx-test-registry
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx-test-registry
  template:
    metadata:
      labels:
        app: nginx-test-registry
    spec:
      containers:
      - name: nginx-test-registry
        image: registry.local:5000/nginx:latest
        ports:
        - containerPort: 80
EOF
```

## Pullできたか確認

```
~ ❯❯❯ kubectl get pod
NAME                                   READY   STATUS    RESTARTS   AGE
nginx-test-registry-7f6c7b79fb-mhxpm   1/1     Running   0          5m36s
```
社会的利益のための2,602のAIの使用、およびそれらから学んだこと

(來源：Google Blog)

過去数年間、Google AIの中核研究とエンジニアリングを、洪水の予測、クジラの保護、famineの予測など、社会にプラスの影響を与えるプロジェクトに適用してきました。人工知能は大きな社会的、人道的、環境的な問題に対処する信じられないほどの可能性を秘めていますが、この可能性を達成するためには、これらの分野ですでに進歩している組織がアクセスできる必要があります。したがって、2018年10月に開始されたGoogle AIインパクトチャレンジは、世界中の組織に、AIを使用して社会的課題に対処する方法についてのアイデアを提出するための公募でした。

![Google_AI_ImpactChallenge_Report_Charts_Gl.max-1000x1000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448511/0bc48612-e2a8-a036-4808-7c0f423ccbf6.png)


「人工知能による社会的利益の促進」は、AIを使用して大きな問題に対処するさまざまな組織に光を当てます。また、社会的利益のためにAIを使用することに関連する機会と課題に関するいくつかの傾向を特定します。学んだことの一部を次に示します。詳細についてはレポートをご覧ください。


AIはグローバルに関連しています

6大陸と119か国から2,602件の応募があり、教育から環境に至るまで、幅広い問題分野に取り組むプロジェクトがありました。一部の応募者はAIの経験がありましたが、非営利組織の55％と非営利の社会的企業の40％がAIの以前の経験を報告していませんでした。

![微信截图_20190918141352.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/448511/5a4db898-071d-e633-9cb4-92e259fcfafd.png)


同様のプロジェクトは共有リソースの恩恵を受けることができます

すべてのアプリケーションを確認したところ、多くの人が同じ問題に取り組んでおり、同じアプローチを使用していることさえわかりました。たとえば、AIを使用して農業害虫を特定および管理することを提案する30以上のアプリケーションを受け取りました。レポートには、一般的なプロジェクトの提出物のリストが含まれています。これにより、人々は、同様の問題を解決するために働いている他の人々と協力し、リソースを共有することができます。



社会的利益のためにAIを使用するために専門家である必要はありません

新しい機械学習ライブラリや、TensorflowやML Kitなどの他のオープンソースツールがAIの実装に必要な技術的専門知識を減らすため、AIはよりアクセスしやすくなっています。組織はAIの深いバックグラウンドを持つ人を必要としなくなり、ゼロから始める必要はありません。すべてのセクターおよび組織の種類にわたって、提出物の70％以上が既存のAIフレームワークを使用して提案された課題に取り組んでいます。


成功するプロジェクトは、技術的能力とセクターの専門知識を兼ね備えています

プロジェクトを最初から最後まで成功裏に設計および実装するために、社会部門とAIの技術的な専門知識の両方を持つ組織はほとんどありませんでした。最も包括的なアプリケーションは、深いセクターの専門知識を持つ非営利団体と、技術的な経験を持つ学術機関またはテクノロジー企業との間にパートナーシップを確立しました。


MLだけが答えではありません

一部の問題は、AIの代替方法を使用することで対処できます。その結果、実行がより速く、より簡単に、より安価になります。たとえば、いくつかの組織は、機械学習を使用して、十分なサービスを受けていない集団を法的知識とツールに一致させることを提案しました。 AIは役立つかもしれませんが、適切に設計されたWebサイトを通じて同様の結果を達成できます。 AIが大きな問題の解決に与える影響を見てきましたが、より単純なアプローチも除外すべきではありません。


社会的利益のためのAIを取り巻く世界的な勢いが増しており、多くの組織がすでにさまざまな社会的課題に対処するためにAIを使用しています。より多くの社会部門組織がAIの可能性を認識しているため、私たち全員がより良い世界のために彼らの仕事を支援する上で果たすべき役割を持っています。


[Google Coral Edge TPU代理店Gravitylink!](https://store.gravitylink.com/global)


これらのデバイスはまだ正式に発表されていないようで、最初にHacksterによって発見されました。これらは新しいGoogle Coralブランド（それ自体はまだ「ベータ版」）の下で導入されており、149.99ドルで販売されている開発ボード、74.99ドルで販売されているUSBアクセラレータ、および24.99ドルで入手可能な5メガピクセルのカメラを含みます。 。

開発ボードとアクセラレータはどちらもGoogleのEdge TPUチップを搭載しています。これは、汗をかくことなくAIモデルを実行するように設計された、あなたの指の爪ほどの大きさのASICプロセッサです。その間、カメラは開発ボードのためのアドオンとしてあります。
Docker Community Edition を Windows 10 Home Edition 上の Windows Subsystem for Linux (WSL) 1 環境に導入する手順

# これは何?

Windows Subsystem for Linux (WSL) 1 の環境に Docker Community Edition (以降 docker と表記) をインストールして使用するまでの手順です。

Windows 10 Home Edition  バージョン 1903 (OS ビルド 18362.356) の環境を前提としています。

# 手順の前に結論
Windows 10 Home Edition と WSL 1 の組み合わせで docker を使うのはおすすめしません。サポート切れの docker を使うことになります。

おとなしく Windows 10 Pro のライセンスを購入して Docker Toolbox on Windows を使いましょう。あるいは WSL 2 が利用できるようになる Windows 10 20H1 を待ちましょう。今すぐ WSL 2 を試すなら Windows Insider Program に加入して Fast Ring に入る方法もあります。

# 手順
### 1. Microsoft Store から Ubuntu 18.04 (LTS) をインストール
デフォルトではタスクバーに Microsoft Store がいるので、そこから起動しても OK です。
### 2. Ubuntu 18.04 を起動
初回起動時はユーザーの作成を求められるので適当に設定します。
### 3. 次の一連のコマンドを実行
    sudo apt-get update && \
    sudo apt-get upgrade -y && \
    sudo apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common && \
    curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - && \
    sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu xenial stable" && \
    sudo apt-get update && \
    sudo apt-get install -y docker-ce=17.12.1~ce-0~ubuntu containerd.io

上記でやっていること

- インストール済み環境のアップデート
- Docker Community Edition のインストールに必要な一連のコマンドの実行
    - [https://docs.docker.com/install/linux/docker-ce/ubuntu/](https://docs.docker.com/install/linux/docker-ce/ubuntu/) からコピペしてきたものです。将来的に環境が変わるとスクリプトの内容も変わる可能性があります。
- `docker-ce` の WSL 1 環境で動作する（しているように見える）上限バージョン (17 系) のインストール
    - 18 系以降は WSL 1 系ではどうやら動作しないようなので、本手順では 17 系を使用します。
    - 17 系は `bionic` 向けのパッケージ一覧はないので意図的に `xenial` から取得して使用しています。
    - 17 系のパッケージがいつまでリポジトリに存在するのか分からないので、手元に 上記パッケージの .deb ファイルをダウンロードしておくと安心かもしれません。

### 4.  docker に対するコマンド実行権限を指定したデフォルトのログインユーザーに与える
特に設定していなければ デフォルトのログインユーザー == WSL 初回起動時に作成したユーザーです。

    sudo usermod -aG docker デフォルトのログインユーザー名

### 5. docker 起動用スクリプト (PowerShell) を作成し適当な場所に保存
ファイル名はなんでもいいですが `StartupDocker.ps1` としておきます。

    $DistributionName = "Ubuntu-18.04"
    $DockerStartupCommands = (
      "--distribution", $DistributionName,
      "--user", "root",
      "--", "cgroupfs-mount ; service docker start"
    )
    Start-Process -FilePath "wsl.exe" -ArgumentList $DockerStartupCommands -PassThru -Verb "RunAs" -WindowStyle Hidden

### 6. スタートアップフォルダーに (5) の PowerShell スクリプトを起動する bat を配置
`Win + R` キーの `ファイル名を指定して実行` から `shell:startup` と入力し、現在サインインしているユーザーのスタートアップフォルダーを開きます。
フォルダーを開いたら `StartupDocker.bat` ファイルを作成し、次の内容とします。

    @echo off
    powershell -ExecutionPolicy ByPass -File "StartupDocker.ps1 への絶対パス"

### 7. Windows を再起動、またはサインインし直す
スタートアップスクリプトを起動したいだけなので、この手順を省略して `StartupDocker.ps1` ファイルを右クリックしてコンテキストメニューから `PowerShell で実行` を選ぶか、手順 (6) で作成したバッチファイルを叩く方法でも構いません。

### 8. docker デーモンプロセスの起動確認 
サインインした後タスクマネージャーを開いて、詳細タブ内に `dockerd` と `docker-containerd` プロセスが存在することを確認します。

### 9. Ubunu-18.04 を起動
スタートメニューから起動、もしくは `Win + R` キーの `ファイル名を指定して実行` から `wsl` としてシェルを起動します。

### 10. シェル上で `docker run hello-world` を実行
シェル上で次のコマンドを実行します。

    docker run hello-world

ここまでの設定が問題なくできていれば、 Hello from Docker! から始まるメッセージが表示されるはずです。