2007-07-01
■ [CustomFeed] CustomFeed::GoogleCalendar

Googleカレンダーに限って言えばAtomを返すAPIを叩く方がより柔軟にデータの取得が出来ると思う。
つーか俺もプラグイン作る前に確認しようぜ。
iCal形式のファイルを取得し,イベントごとにエントリ化したフィードを生成する。
名前が”GoogleCalendar”となっているが,iCal形式であればおそらくGoogle Calendar以外でも取得・生成が可能だと思う。ただし確認はしていない。
Plagger/Plugin/CustomFeed/GoogleCalendar.pm
package Plagger::Plugin::CustomFeed::GoogleCalendar; use utf8; use strict; use base qw( Plagger::Plugin ); use Data::ICal; use DateTime::Format::ICal; use URI; use Plagger::Date; use Plagger::Util; sub register { my ($self, $context) = @_; $context->register_hook( $self, 'subscription.load' => \&load, ); } sub load { my ($self, $context) = @_; my $calendars = $self->conf->{calendar} or return; $calendars = [$calendars] unless ref $calendars; for my $calendar (@$calendars) { my $now = $calendar->{now} ? Plagger::Date->parse_dwim(delete $calendar->{now}) : Plagger::Date->now; my $feed = Plagger::Feed->new; $feed->aggregator( sub { $self->aggregate($context, $calendar, $now); }); $context->subscription->add($feed); } } sub aggregate { my ($self, $context, $config, $now) = @_; my $content = Plagger::Util::load_uri(URI->new($config->{url})) or return; my $calendar = Data::ICal->new(data => $content); unless ($calendar) { $context->error($calendar->error_message); return; } my $feed = Plagger::Feed->new; $feed->url($config->{url}); $feed->title($config->{title} || $self->unescape($calendar->property('x-wr-calname')->[0]->value)); $feed->description($config->{desc} || $self->unescape($calendar->property('x-wr-caldesc')->[0]->value)); my $events = $calendar->entries; my $timezone = shift @$events; for my $event (@$events) { my $data; for my $key (qw/ dtstart dtend uid summary description location /) { $data->{$key} = $event->property($key); $data->{$key} = $data->{$key}->[0]->value if $data->{$key}; # @ADHOC } my $allday = ($data->{dtstart} =~ /^\d{8}$/) && ($data->{dtend} =~ /^\d{8}$/); for my $key (qw/ dtstart dtend /) { $data->{$key} = DateTime::Format::ICal->parse_datetime($data->{$key}); } my $entry = Plagger::Entry->new; $entry->id($self->unescape($data->{uid})); $entry->title($self->unescape($data->{summary})); $entry->body($self->unescape($data->{description})); $entry->author($self->unescape($data->{location})); if (Plagger::Date->compare($now, $data->{dtstart}) < 0) { $entry->date($data->{dtstart}); $entry->add_tag('prep'); } else { $entry->date($allday ? $data->{dtend}->clone->subtract(days => 1) : $data->{dtend}); if (Plagger::Date->compare($now, $data->{dtend}) < 0) { $entry->add_tag('open'); } else { $entry->add_tag('close'); } } $feed->add_entry($entry); } $context->update->add($feed); } sub unescape { my ($self, $content) = @_; # to single line $content = join '', split /\r\n[ ]/, $content; my @unescaped = (); my $state = 0; for my $char (split //, $content) { if ($state == 1) { push @unescaped, ($char eq 'n' ? "\n" : $char); $state = 0; } elsif ($char eq "\\") { $state = 1; } else { push @unescaped, $char; } } join '', @unescaped; } 1;
エントリのauthorには,iCalで言うVEVENTのLOCATIONがセットされる。
開催前・開催中・開催終了のイベントのエントリには,それぞれprep,open,closeというタグが付与される。
config.calendar.yaml
この例では,取得した全てのイベントの中から,今週のイベントのみを残し,日付順に整列して,RSS形式で出力する。
plugins: - module: CustomFeed::GoogleCalendar config: calendar: - url: http://www.google.com/calendar/ical/hb3ceqibm3d7kbp99e61klnvpo%40group.calendar.google.com/public/basic.ics - module: Filter::Rule rule: - module: Expression expression: | use Plagger::Date; Plagger::Date->compare(Plagger::Date->today, $args->{entry}->date) <= 0 && Plagger::Date->compare($args->{entry}->date, Plagger::Date->today->add(days => 7)) < 0; - module: Filter::SortEntries config: expression: | use Plagger::Date; Plagger::Date->compare($a->date, $b->date); - module: Publish::Feed config: format: RSS dir: ./calendar filename: calendar.xml taguri_base: sweetpotato
Subscription::Configと同様,複数のカレンダーをconfigファイルに登録できる。その場合はひとつのカレンダーがひとつのフィードになる。
各カレンダーにはまた,上で述べた開催前・開催中・開催終了の「基準」となる日時を指定することもできる。指定しない場合は現在の日時が基準になる。
Filter::SortEntriesについては以下の記事を参照。
何ができるの?
例えば,カレンダーから今週のイベントのみをふるい分けて,フィードに出力して,そのフィードをJavaScriptで読み込んでブログの柱に表示する,とか。イベントの管理がカレンダーで一元管理できるようになるわけだ。
他には,カレンダーから今日の予定のみをふるい分けて,自分のメールアドレスに毎朝送るとか。リマインダー的な使い方。Google Calendarデフォルトのリマインダー機能よりもリッチな処理ができるはず。
でもPlaggerだからきっともっといろいろできるはずだよ!
補足
プラグイン名をCustomFeed::iCalにしなかった理由は,半分はGoogle Calendar以外のiCalでもちゃんとテストをすることが面倒だったことから。
もう半分は,iCalサポートは次のバージョンのPlaggerで既に予定されており,おそらくそうなるであろう名前と衝突するのを避けたかったから。これについてはmiyagawa氏の第9回XML開発者の日のスライド186枚目に:
Calendar Support
iCal parser & emitter
hCalendar microformats
.ics attached in emails
Sync::SyncML
no title
と書かれている。
http://nyarla.net/blog/plagger24
nyarla-netさんは比較にcmpを使ってらっしゃいますが,これは日付(date)での整列も可能ですか?
僕の作ったプラグインの場合、指定されたプロパティを単純に比較しているだけなので、そのあたり込み入ったことが出来ないと思います。
まあSweetPotatoさんのか僕のかどちらかをベースにしてくっつけてしまえば良いと思いますが。