dp.cx blog

Posted on

Filed under perl, calendar, google, exchange, and agenda

Yesterday, I got tired of not having a simple way of seeing an agenda for the upcoming days of all my calendars. Between work using hosted Exchange, using a joint iCloud calendar, and still using Google Calendar quite often, getting all of the information in one place is a pain.

I tried starting out with Google Calendar. Unfortunately, I can't add iCloud shared calendars because the iCloud servers have a robots.txt that disallow everything to everyone, and Google Calendar for some reason respects that.

iCloud doesn't let you subscribe to third party calendars. So it's out as a combined solution.

Hosted Exchange makes subscribing to third party calendars so difficult as to be impossible.

So like any good programmer, I decided I needed to write my own.

#!/usr/bin/perl use strict;use warnings; use Data::Dumper;use HTML::Entities;use iCal::Parser;use LWP::UserAgent::Cached;use Text::Table; my $cache_dir = '/tmp/lwp-cache';my @calendars = qw|http://www.google.com:80/finance/events?q=NYSE:KO&output=ical|; my $now = DateTime->now( time_zone => 'America/New_York' );my $tb = Text::Table->new({ }, { align => "right" }); my $parser = iCal::Parser->new();foreach my $calendar_url (@calendars) {    $parser->parse_strings(get($calendar_url));} my $combined = $parser->calendar; my @events_list;my $events = $combined->{events}; my %summaries;my %summaries_seen; foreach my $year (keys %{$events}) {    foreach my $month (keys %{$events->{$year}}) {        foreach my $day (keys %{$events->{$year}{$month}}) {            foreach my $uuid (keys %{$events->{$year}{$month}{$day}}) {                 my $event = $events->{$year}{$month}{$day}{$uuid};                 my $summary = $event->{SUMMARY};                my $start = $event->{DTSTART};                my $end = $event->{DTEND};                 my $delta = $start->delta_days(DateTime->today());                next if (DateTime->compare($start, DateTime->today()) == -1);                next if ($delta->in_units("days") > 14);                 $summaries{$summary}++;                push(@events_list, [ $summary, $start, $end ]);            }        }    }} foreach my $event (sort { DateTime->compare($a->[1], $b->[1]) } @events_list) {    next if ($summaries_seen{$event->[0]});    my $summary = $event->[0];    if ($summaries{$summary} > 1) {        $summary .= ' (multiple)';    }     my $delta = $event->[1]->subtract_datetime($now);    my ($days, $hours, $minutes) = $delta->in_units('days', 'hours', 'minutes');     my $delta_text = 'd';    if ($days == 0) {        $delta = $delta->in_units('hours');        $delta_text = 'h';    } else {        $delta = $delta->in_units('days');    }     $tb->load([ decode_entities($summary), $delta . $delta_text ]);    $summaries_seen{$event->[0]}++;}print $tb; sub get {    my ($url) = @_;     mkdir($cache_dir) unless -d $cache_dir;    my $ua = new LWP::UserAgent::Cached(cache_dir => $cache_dir);    $ua->show_progress(1);    my $response = $ua->get($url);     if ($response->is_success()) {        return $response->content();    } else {        warn $response->status_line;    }}

Above is the source for the script. Simply, it fetches the calendar URLs provided in @calendars, parses them, and ignores anything from the past, and anything more than 14 days out. Then it generates a nice text table showing the events happening.

If you find this useful, please let me know.