Simple Text 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.