もうちょっと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 件のコメント:

コメントを投稿

Windowsでシンボリックリンクを試してみる。

きっかけは、1つのファイルを別の名前で起動したら違う動きになるようなスクリプトを書く、でした。  busybox なんかでは、同じ実行形式ファイルの名前を、lsにすればlsと同じ、cpとすればcpと同じ動作をするようにしてますが、Pythonスクリプトでそれと同じように argv...