GitHub Actions と papermill で家計簿を作る

## 概要

一年の計は何とやらということで、GitHub Actions の応用例として、家計簿の自動作成のようなことをやってみました。仕組みとしては以下のような感じ。

![kakeibo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63995/a244ef74-6c5b-3023-ae01-235595cebe0a.png)

1. カード会社のウェブサイトから手動で CSV をダウンロードしてくる
2. CSV を所定の場所に配置して commit & push
3. CSV が push されると GitHub Actions が以下を行う
  1. 解析スクリプトをインストールして実行する
  2. 生成された Jupyter Notebook 形式の家計簿(月次レポート?) をリポジトリに commit & push

解析スクリプトリポジトリ、データ保管用リポジトリの例はそれぞれ以下です。

- <https://github.com/pn11/kakeibo>
- <https://github.com/pn11/kakeibo-data-sample>

CSV を手動で落としてこなければならないためどう考えても Zaim や MoneyForward などを使った方が良いですが、GitHub Actions を使って何かやってみたかった。

## GitHub Actions の記述

GitHub Actions はデータ保管用のリポジトリに書きます。
[ソース](https://github.com/pn11/kakeibo-data-sample/blob/master/.github/workflows/create_report.yml) から一部抜粋: 

```yaml
    - name: Install dependencies
      run: |
        python -m pip install --upgrade pip
        pip install git+https://github.com/pn11/kakeibo.git
    - name: create report
      run: |
        for year in {2019..2019}; do for month in {10..10}; do papermill ~/.notebook-template/monthly_report.ipynb -p year $year -p month $month reports/report${year}${month}.ipynb; done; done
    - name: Commit files
      run: |
        git config --local user.email "action@github.com"
        git config --local user.name "GitHub Action"
        git add reports/*
        git commit -m "create report"
    - name: Push changes
      uses: ad-m/github-push-action@master
      with:
        github_token: ${{ secrets.GITHUB_TOKEN }}
```

各ステップについて簡単に説明すると、

- "Install dependencies" では、pip を使って解析スクリプトをインストールしています。
    - 解析スクリプトのリポジトリには `setup.py` が置いてあり、解析スクリプトと依存ライブラリがインストールされます。
    - 詳細は次節で説明します。
- "create report" では、papermill(後述) のコマンドラインを使って解析処理を実行しています。
    - 本当は変化のあった月に対してだけ実行するなどの処理をするべきですが、クレジットカード明細のファイル名と実際にカードを使用した月は一致しないため、シェルスクリプトでループを回して全期間実行する形になってしまっています (このサンプルでは 2019/10 のみ実行)。
- "Commit files" および "Push changes" では、リポジトリに対して行った変更をコミット、プッシュしています。
    - コミット時は、`user.email="action@github.com"`, `user.name="GitHub Action"` を設定しています。
    - プッシュは こちらのリポジトリ ([ad-m/github-push-action](https://github.com/ad-m/github-push-action)) のアクションを呼び出して使わせて頂いています。


## 解析スクリプト

解析スクリプトは Pandas を用いた CSV の読み込みライブラリと、[papermill](https://github.com/nteract/papermill) を用いた iPython Notebook からなります。

### CSV の読み込み

[pn11/kakeibo/kakeibo](https://github.com/pn11/kakeibo/tree/master/kakeibo) 以下にプログラムがあります。Pandas を用いて CSV を DataFrame にしているだけで特に共有すべき知見はありません。
強いて言えば、Shift-JIS の CSV に「(株)」が一文字になっている文字が入っていると読み込めず、自動で修正する方法も思いつかなかったため、手動で修正しています。
なお当然、クレジットカード会社によって CSV のフォーマットが異なるため、それぞれパーサを書く必要があります。

### papermill によるレポート作成

[papermill](https://papermill.readthedocs.io/) は iPython Notebook にパラメータを渡して実行できるようにするためのライブラリです。Netflix で使われていることが知られています[^1] [^2]。個人的には、脚注に挙げるポッドキャストで知りました [^3] [^4]。

[^1]: [Beyond Interactive: Notebook Innovation at Netflix - Netflix TechBlog - Medium](https://medium.com/netflix-techblog/notebook-innovation-591ee3221233)
[^2]: [Part 2: Scheduling Notebooks at Netflix - Netflix TechBlog - Medium](https://medium.com/netflix-techblog/scheduling-notebooks-348e6c14cfd6)
[^3]: [Episode #217 Notebooks vs data science-enabled scripts - [Talk Python To Me Podcast]](https://talkpython.fm/episodes/show/217/notebooks-vs-data-science-enabled-scripts)
[^4]: [Episode #226 Building Flask APIs for data scientists - [Talk Python To Me Podcast]](https://talkpython.fm/episodes/show/226/building-flask-apis-for-data-scientists)


今回の例では、[雛形となるノートブック](https://github.com/pn11/kakeibo/blob/master/notebook-template/monthly_report.ipynb)を作り、これを papermill でパラメータを変えて処理します。
このノートブックは [GitHub](https://github.com/pn11/kakeibo/blob/master/notebook-template/monthly_report.ipynb) や [nbviewer](https://nbviewer.jupyter.org/github/pn11/kakeibo/blob/master/notebook-template/monthly_report.ipynb) 上では何の変哲もないものに見えますが、[ソース](https://github.com/pn11/kakeibo/blob/master/notebook-template/monthly_report.ipynb?short_path=59ba70c)を見ると、2個目のセルに

```json
    "tags": [
     "parameters"
    ]
```

というタグが追加されており、Jupyter で開くと以下のように見えます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63995/ce8ef545-0fc9-60dd-b4a7-0a8a3bfc9c50.png)


これを、GitHub Actions の YAML に書いたように、papermill でパラメータを指定して処理すると、

```sh
papermill ~/.notebook-template/monthly_report.ipynb -p year 2019 -p month 10 reports/report201910.ipynb
```

以下のようなノートブックが出来上がります ([GitHub](https://github.com/pn11/kakeibo-data-sample/blob/master/reports/report201910.ipynb), [nbviewer](https://nbviewer.jupyter.org/github/pn11/kakeibo-data-sample/blob/master/reports/report201910.ipynb))。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63995/db4f146f-46ae-eb5a-0adc-77003fff1519.png)

出来上がったノートブックを見ると、 parameter タグを指定した 2番目のセルの下に新たなセルが以下のように自動で自動で挿入されていて、以降の処理がそのパラメータに従って処理されるようになっています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/63995/05b9fa08-cb54-b09d-5c0d-e4db2bceea3f.png)

### その他の依存ライブラリ

[setup.py](https://github.com/pn11/kakeibo/blob/master/setup.py) に書いた依存ライブラリについて簡単に触れます。

```python
    install_requires=[
        'jupyter',
        'papermill',
        'pandas',
        'matplotlib',
        'japanize_matplotlib',
        'mojimoji'
      ],
```

メジャーなものを除くと、

- [japanize_matplotlib](https://github.com/uehara1414/japanize-matplotlib)
    - import するだけで matplotlib で日本語が使えるようになる便利なライブラリです。
    - 開発者による Qiita 記事があります。 [pip install して import するだけで matplotlib を日本語表示対応させる - Qiita](https://qiita.com/uehara1414/items/6286590d2e1ffbf68f6c)
    - お世話になってますが、 matplotlib は正直卒業したい...
- [mojimoji](https://github.com/studio-ousia/mojimoji)
    - 全角←→半角の変換を行うためのライブラリです。
    - こちらも開発者による Qiita 記事があります。 [Pythonで半角・全角の変換を高速に行う - Qiita](https://qiita.com/ikuyamada/items/fea6c8f81e7cac7cf318)
    - 日本語のクレジングには欠かせないライブラリのひとつではないかと思います。

## まとめ

まとめです。

- GitHub Actions でリポジトリに commit&push するには [ad-m/github-push-action](https://github.com/ad-m/github-push-action) のようにやる
- [papermill](https://github.com/nteract/papermill) を使うと IPython Notebook にパラメータを渡して処理できる
- GitHub Actions で papermill を実行することでレポート作成ができる