COVID-19Hokkaidoデータ編③完全自動化（バリデーション・エラー検知）

## もくじ
[COVID-19Hokkaidoデータ編①スクレイピングなどによる初期データ作成](https://qiita.com/Kanahiro/items/b47984fbf77f4e632663)
[COVID-19Hokkaidoデータ編②オープンデータ化＋自動更新へ向けて](https://qiita.com/Kanahiro/items/8f0768708d2c47ae3206)
[COVID-19Hokkaidoデータ編③完全自動化](https://qiita.com/Kanahiro/items/782cb6d5497d3c3704ec)←この記事だよ！

## 完全自動化へ向けて
#### 現状
- GitHub Actionsにより、元データの読み込み・jsonファイルの生成は既に自動化されている
- GitHub Pagesにより、自動で生成されたjsonファイルのホスティングは実装されている

#### 課題
- 元データはオープンデータとなったものの、ファイルは各自治体担当者さんの手作業で作成されるため、入力値のバリデーションが必要
- データの不整合などでjsonファイルの生成を失敗した場合、何らかのアラートが必要

## バリデーション
Python内部でdictを生成し、json.dump()でjsonファイルに書き出しています。つまりdictのデータをバリデーションすべきと言えます。
では何をバリデーションすべきか。たとえばフロントで参照したkeyが存在しなかったら困ります、なのでkeyチェックが必要です。また、日付keyに何故か整数値が入っていたらこれまた困ります。なのでkeyごとに型チェックが必要です。

これら２点はフルスクラッチで実装しても良さそうですが、先人が発明した車輪を使わせてもらう事とします。「jsonschema」を利用します。

参考サイト：https://medium.com/veltra-engineering/python-json-schema-validation-6936238f107d

#### jsonschemaのインストール
```
pip install jsonschema
```

#### jsonschemaでバリデーション
事前に定義しておいた、JSONの構造・型に、与えられたdictが適合するかチェックします。
適合しなければエラーが出て、適合すれば何も出力されません。

使い方などは上記参考サイトで丁寧に解説されていますので、実装のみ紹介します。

```python
SCHEMAS = {
    "patients":{ スキーマ定義 },
    "contacts":{ スキーマ定義 }
    #〜割愛〜
}
```

というように、keyごとにスキーマ定義をSCHEMASとして定義します。

```python

def validate(self):
    for key in self.data:
        jsonschema.validate(self.data[key], SCHEMAS[key])
```

self.dataとSCHEMASはkeyが一致しています。self.data[key]は、そのままjsonに出力すべきdictです。
したがって、例えば入力ミスなどでkeyが欠損していたり、型が整合しない場合はエラーが発生して処理が中断します（<b>jsonは生成されない</b>）。
おかしなjsonは生成されず、最後に正常に出力されたjsonが残り続ける訳です。
（5と入力すべき箇所に五と入力されていれば引っかかりますが、6と入力されていたら素通りします。この手のヒューマンエラーはそもそも避けようがない気もしますが）

たとえばdateにはintegerが入るべき、と定義したにも関わらずデータがstringだった場合は、以下のようなエラーが出て処理が中断されます。

```

Traceback (most recent call last):
  File "main.py", line 231, in <module>
    dm.validate()
  File "main.py", line 90, in validate
    jsonschema.validate(self.data[key], SCHEMAS[key])
  File "/opt/hostedtoolcache/Python/3.8.2/x64/lib/python3.8/site-packages/jsonschema/validators.py", line 934, in validate
    raise error
jsonschema.exceptions.ValidationError: '2020-03-17T21:31:40.309090+09:00' is not of type 'integer'

Failed validating 'type' in schema['properties']['last_update']:
    {'default': '', 'type': 'integer'}

On instance['last_update']:
    '2020-03-17T21:31:40.309090+09:00'
##[error]Process completed with exit code 1.
```

## Slackにエラーをアラート

参考サイト：
[Qiita - SlackのWebhook URL取得手順](https://qiita.com/vmmhypervisor/items/18c99624a84df8b31008)
[Qiita - GitHub Actionsを定期実行して結果をSlackに通知する](https://qiita.com/DotaKobayashi/items/7f8c6275f410d22ed6bd)

理由問わず、データ生成に失敗した場合にSlackに通知するようにします。
上記サイトを参考に以下のとおりyamlを記述しました。

```yaml

name: Python application

on:
  schedule:
    - cron:  '0 * * * *'

jobs:
  build:

    runs-on: ubuntu-latest

    steps:
    - uses: actions/checkout@v2
    - name: Set up Python 3.8
      uses: actions/setup-python@v1
      with:
        python-version: 3.8
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install -r requirements.txt
    - name: Run script
      run: |
        python main.py
    - name: Slack Notification
      # ここから
      if: failure()
      uses: rtCamp/action-slack-notify@master
      env:
        SLACK_MESSAGE: 'Error occurred! Please check a log!'
        SLACK_TITLE: ':fire: Data Update Error :fire:'
        SLACK_USERNAME: covid19hokkaido_scraping
        SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
      # ここまで追加
    - name: deploy
      uses: peaceiris/actions-gh-pages@v3
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
        publish_dir: ./data
        publish_branch: gh-pages
```

Setting→SecretsにSLACK_WEBHOOKを追加しておく必要があります（さもなくばwebhookURLがオープンソースになってしまいます）。

Secretsに格納した値は暗号化されます。yamlからはsecrets.{ なまえ }で取得出来ます。
GITHUBから始まる{ なまえ }は使えません。

以上を実装すると、エラー発生時には以下のようにSlackに投稿されます。
<img width="647" alt="スクリーンショット 2020-03-17 22.04.29.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/73197/71faa50b-55bb-ce49-aba6-ad93a16d9855.png">


## 終わりに
以上で、データ取得→jsonファイル生成のバリデーション込みの自動化処理が完成しました。
ただ、現在はkeyチェックと型チェックのみなので、例えばあまりにもな異常値を検知する仕組みなど、バリデーションにはキリがありません。
またデータ生成は自動化されましたが、フロント側の非同期通信はデバッグ含めた実装中です。
今後も改善の余地がありますが、当初の半分手作業から比べると劇的に運用がラクになったと思います。以上で３回に渡った連載は終了となります、ありがとうございました。