「札幌市家庭ごみ収集日カレンダー」をGoogleカレンダーに取り込む

# はじめに

「[札幌市ICT活用プラットフォーム DATA-SMART CITY SAPPORO](https://data.pf-sapporo.jp)」ってご存知ですか?

>札幌市とさっぽろ産業振興財団が共同運営する「札幌市ICT活用プラットフォーム DATA-SMART CITY SAPPORO」は、データを活用してイノベーションを創出するための共通基盤として、官民が保有するオープンデータやビッグデータの収集・管理・提供を行っています。また、本サイト掲載データはCKAN標準APIでの活用も可能となっています。

ということなのですが、ここで「[札幌市家庭ごみ収集日カレンダー](https://ckan.pf-sapporo.jp/dataset/garbage_collection_calendar)」というものが公開されています。
これをGoogleカレンダーに取り込めるようにしてみよう、というのが今回の内容です。

# データの確認

さっそくですが、実際に公開されているデータを見てみましょう。

```text:札幌市家庭ごみ収集日カレンダー（2017年10月1日～2019年10月8日）
日付,曜日,中央区1,中央区2,中央区3,中央区4,中央区5,中央区6,豊平区1,豊平区2,豊平区3,豊平区4,清田区1,清田区2,北区1,北区2,北区3,北区4,北区5,北区6,東区1,東区2,東区3,東区4,東区5,東区6,白石区1,白石区2,白石区3,白石区4,厚別区1,厚別区2,厚別区3,厚別区4,南区1,南区2,南区3,南区4,南区5,南区6,南区7,西区1,西区2,西区3,西区4,手稲区1,手稲区2,手稲区3
2017-10-01,日,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
2017-10-02,月,1,1,1,10,9,8,10,8,9,1,1,1,1,1,1,10,8,9,1,1,1,10,8,9,9,2,8,1,1,1,1,1,1,1,1,11,9,9,8,1,1,1,9,8,11,8
2017-10-03,火,8,10,9,1,1,1,1,1,1,10,9,8,9,10,8,1,1,1,10,8,9,1,1,1,1,1,1,8,9,9,8,2,9,11,8,1,1,1,1,9,11,8,1,1,1,1
…
```

```text:札幌市家庭ごみ収集日カレンダー（2019年10月1日～2020年9月30日）
日付,曜日,中央区1,中央区2,中央区3,中央区4,中央区5,中央区6,豊平区1,豊平区2,豊平区3,豊平区4,清田区1,清田区2,北区1,北区2,北区3,北区4,北区5,北区6,東区1,東区2,東区3,東区4,東区5,東区6,白石区1,白石区2,白石区3,白石区4,厚別区1,厚別区2,厚別区3,厚別区4,南区1,南区2,南区3,南区4,南区5,南区6,南区7,西区1,西区2,西区3,西区4,手稲区1,手稲区2,手稲区3
2019-10-1,火,8,10,9,1,1,1,1,1,1,10,9,8,9,10,8,1,1,1,10,8,9,1,1,1,1,1,1,8,9,9,8,11,9,2,8,1,1,1,1,9,2,8,1,1,1,1
2019-10-2,水,10,9,8,9,8,10,9,10,8,8,10,9,10,8,9,9,10,8,9,10,8,9,10,8,11,8,9,11,8,11,11,9,8,9,2,8,8,2,9,8,9,2,2,9,8,2
…
```

1行目が各列の項目名、2行目以降が日毎の地域ごとのゴミ種別をIDで示したものになります。

1行目の3列目以降の「中央区1」「中央区2」…は[こちら](http://www.city.sapporo.jp/seiso/kaisyu/index.html)で記載されている地域番号ですね。

2行目以降のゴミ種別IDは「ごみ種別・番号対応表」としてまとめられている番号です。

```text:ごみ種別・番号対応表
記号,ごみ種
1,燃やせるごみ
2,燃やせないごみ
8,びん・缶・ペット
9,容器プラ
10,雑がみ
11,枝・葉・草
空欄,収集なし
```

# 実装方針

まず今回はPythonで、というのを決めた上で、確認したデータをもとに、以下の方針で実装することにしました。

## 入力
- 札幌市家庭ごみ収集日カレンダーのCSV
 - CSV読みは楽をするために[pandas](https://pandas.pydata.org)を利用することにします。明らかにオーバースペックですが:sweat_smile:
- 地域番号
 - 地域番号はコマンドラインオプションで入力。
- ゴミ種別・番号対応表はソース内に直接抱えることにします

## 出力
- iCalendar形式で標準出力
 - googleカレンダーに取り込むにはこの形式が定番ですね。
 - [icalendar](https://github.com/collective/icalendar)というライブラリを利用することにします。私のicalendar記事だと必ず使っているライブラリです。

# 実装内容

以下の通り。[github](https://github.com/masaminh/sapporo_gc_calendar)にもおいてあります。

```python:sapporo_gc_calendar.py
"""札幌市家庭ごみ収集日カレンダーのical化.

札幌市家庭ごみ収集日カレンダーをicalにする.
元ファイルは以下を参照.
https://ckan.pf-sapporo.jp/dataset/garbage_collection_calendar
"""
import sys
from argparse import ArgumentParser, FileType

import dateutil.parser
import pandas
from icalendar import Calendar, Event, vDate


def main():
    """メイン関数."""
    p = ArgumentParser(description='札幌市家庭ゴミ収集日カレンダーCSVをical化する')
    p.add_argument('-a', '--area', help='地域名')
    p.add_argument(
        'input',
        type=FileType(encoding='utf-8'), help='札幌市家庭ゴミ収集日カレンダーCSV')
    args = p.parse_args()

    df = pandas.read_csv(args.input, dtype='object')

    if args.area is None:
        print('入力可能な地域名:', file=sys.stderr)
        for area in df.columns[2:]:
            print(' ' + area, file=sys.stderr)

        return

    if args.area not in df.columns[2:]:
        print('地域名が見つかりません.', file=sys.stderr)
        return

    gctypes = {
        '1': '燃やせるゴミ',
        '2': '燃やせないゴミ',
        '8': 'びん・缶・ペット',
        '9': '容器プラ',
        '10': '雑がみ',
        '11': '枝・葉・草'
    }

    ical = Calendar()

    for datestr, gctype in zip(df['日付'], df[args.area]):
        if gctype in gctypes:
            dt = dateutil.parser.parse(datestr)
            event = Event()
            event.add('SUMMARY', gctypes[gctype])
            event.add('DTSTART', vDate(dt))
            event.add('DTEND', vDate(dt))
            event.add('TRANSP', 'TRANSPARENT')
            ical.add_component(event)

    print(ical.to_ical().decode('utf-8'))


if __name__ == '__main__':
    main()
```

大したことはやっていませんが、1点だけハマった点。

- 札幌市家庭ごみ収集日カレンダー（2017年10月1日～2019年10月8日）の日付
 - 「2017-10-01」となっており、ISO 8601形式(YYYY-MM-DD形式)になっている
- 札幌市家庭ごみ収集日カレンダー（2019年10月1日～2020年9月30日）の日付
 - 「2019-10-1」となっており微妙にISO 8601からは外れてる…

ということで、2019年10月1日～2020年9月30日の方のファイルは、date.fromisoformat()を直接使えません。
このため、[python-dateutil](https://dateutil.readthedocs.io/en/stable/)のdateutil.parser.parse()を使用するようにしています。

# おわりに

今回のツールを使って生成したicalendar形式ファイルをgoogleカレンダーにインポートすればおしまい。
googleカレンダーに入れてしまえば、色々通知にも使えますので、便利に使えますね。