もうちょっとicsをいじってみる。

PyPiでicalendarというのを見つけてちょっといじってみたんだけれど、icalendarは今ひとつドキュメントが足りてないのと、「テストコードがあるから使用方法はそっちを見てね」的な感じだけどカバーしきれていない感じなのとで、やっぱりicsをもうちょっといじってみることにしました。

さて、GoogleカレンダーのエクスポートをCSVにする。では浅瀬でパシャパシャやってただけなので、もうちょっと深いところまでいってみましょう。


まずデータを読み込みます。前回と同じデータですが、文字化けしている部分(どうもカレンダーを作成したアプリの不具合らしく、DESCRIPTIONのところのエンコードが壊れてた)はあらかじめ削除してあります。

>>> import ics
>>> with open('chiyo.ics', encoding='utf-8') as f:
...   cals = ics.Calendar(f.read())
>>> cals
<Calendar with 1663 events>
>>> cals.events[5]
<Event '岡山出張' begin:2008-12-10T00:00:00+00:00 end:2008-12-12T00:00:00+00:00>

with文を使うと、ブロックを抜けたときに自動的にファイルをclose()してくれるので便利です。
次に、それぞれのデータの値を見てみます。

>>> cals.events[5].name
'岡山出張'
>>> cals.events[5].begin
<Arrow [2008-12-10T00:00:00+00:00]>
>>> cals.events[5].end
<Arrow [2008-12-12T00:00:00+00:00]>
>>> cals.events[5].location
''
設定されていない空の項目は '' と空の値を返してくるようなので、同じメソッドで扱えそうです。
ところでここで、Arrow というキーワードが出てきました。前回は「日時が出てきたね、よしよし」と気にもとめなかったのですが、Arrow: better dates and times for Pythonという、Pythonのdatetimeモジュールをさらに改良したようなものらしいです。PyPiはこちら
Arrow を継承したオブジェクトは to() とか humanize() とかのファンクションが使えるようです。

>>> local=cals.events[5].begin.to('Asia/Tokyo')
>>> local
<Arrow [2008-12-10T09:00:00+09:00]>
>>> local.humanize()
'9 years ago'
>>> local.humanize(locale='ja_jp')
'9年前'
なかなかに多芸です。

ちなみに to() のパラメータはtimezone形式で、IANAのTime Zone Databaseで定義されています。

#code coordinates TZ comments
JP +353916+1394441 Asia/Tokyo


coordinatesというのは緯度経度のことです。日本標準時は東経135度の子午線がちょうど兵庫県を通っているため、協定世界時 (UTC) に135度分のオフセット (+0900) を加えたものになります。

それはさておき。
もうちょっと見てみましょう。日時のフォーマットは format() で変更できるようです。

>>> cals.events[5].begin.format('YYYY/MM/DD HH:mm:ss')
'2008/12/10 00:00:00'
>>> local.format('YYYY/MM/DD HH:mm:ss')
'2008/12/10 09:00:00'
>>> cals.events[5].begin.to('Asia/Tokyo').format('YYYY/MM/DD HH:mm:ss')
'2008/12/10 09:00:00'

これなら読みやすくなりそうです。
ということで試しに書いてみました。
for i in range(0, len(cals.events)):
    with cals.events[i] as ev:
        print('{0}, {1}, {2}, {3}, {4}'.format(
            ev.begin.to('Asia/Tokyo').format('YYYY/MM/DD HH:mm:ss'), 
            ev.end.to('Asia/Tokyo').format('YYYY/MM/DD HH:mm:ss'), 
            ev.summary, 
            ev.location, 
            ev.description, 
        ))
実行してみると、AttributeError: __enter__ というエラーが返ってきました。
>>> import ics
>>> dir(ics.Calendar.events)
['__class__', '__delattr__', '__delete__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__get__', '__getattribute__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__isabstractmethod__', '__le__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__set__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'deleter', 'fdel', 'fget', 'fset', 'getter', 'setter']
with文で使用するには __enter____exit__ が必要なので、events ではwith文は使えないようです。

結局、試行錯誤しながら以下のようにしてみました。

import ics
import re

with open('diary.ics', encoding = 'utf-8') as fin:
    cals = ics.Calendar(fin.read())

with open('output.csv', 'w', encoding = 'utf-8') as fout:
    fout.write('開始, 終了, 件名, 場所, 説明\n')
    for i in range(0, len(cals.events)):
        s = u'{0}, {1}, "{2}", "{3}", "{4}"'.format(
            cals.events[i].begin.to('Asia/Tokyo').format('YYYY/MM/DD HH:mm:ss'), 
            cals.events[i].end.to('Asia/Tokyo').format('YYYY/MM/DD HH:mm:ss'), 
            cals.events[i].name, 
            cals.events[i].location, 
            cals.events[i].description, 
        )
        if re.search('(東京)|(岡山)|(藤沢)', s):
            if not re.search('移動', s):
                fout.write(s + '\n')

これで「東京」「岡山」「藤沢」を含み、かつ「移動」を含まないものをファイルに書き出します。ファイルは with 文で扱っているので、ブロック終了時に自動的に close() されますから fout.close() はありません。

0 件のコメント:

コメントを投稿

Mercurial から Git へ引越し。

母艦(という言い方もあまりしなくなってきたみたいですが)を切り替えるのにあわせて、これまでのメイン PC でやってきた各種設定や Python スクリプトをコピーして使おうとしたのですが、環境が違うと手直しも必要になってきます。 これまではバージョン管理はメイン PC の、たとえ...