Reviving an old calendar script

Sometimes things work out better than you had any reason to hope. My older son’s swim season just started, which means it’s time to enter his meet schedule into my calendar. Given that all the team parents have smartphones, and all the teachers and administrators have laptops with coordinated calendar software, you’d think we’d be sent a link to a swim-schedule.ics file that would automatically populate our calendars. You’d think that, at least, if you’d never dealt with school IT staff before. These are people who think they’re doing us a big favor by putting the schedule up as a table on a web page.

When I was using TextMate, I had my own calendar events bundle that would, among other things, convert a Markdown table-like description of events into an .ics file for importing into iCal. I described the first iteration of it here and the later, bundled version here. The input is a series of lines that look like this:

Swim meet|12/07/2013 05:30 PM|3.00|Evanston Township HS|vs. ETHS
Swim meet|12/08/2013 01:00 PM|3.00|NCHS|vs. Hinsdale Central
Swim meet|12/10/2013 05:00 PM|3.00|NCHS|vs. W. Aurora/Glenbard Coop

where the pipe-separated fields are the event name, start date and time, duration in hours, location, and notes. The advantage of doing it this way is that entering a schedule in this form usually takes less time than entering it into iCal or my phone one event at a time. Even better, when the schedule is presented as a table in a web page or (shudder) a Word document, it can be copied, pasted into a plain text file, and massaged into the pipe-separated format with just a few find-and-replaces.

Since leaving TextMate last year, I haven’t had this bundle available to me. During last year’s sports seasons, I just did without a calendar because I thought redoing the commands for BBEdit would be difficult. I was wrong.

What I’d forgotten was that TextMate commands typically use standard input and standard output, just like a BBEdit Text Filter. All I had to do was copy the code from the bundle plist file on GitHub

Make ics

paste it into a new document, and save it in BBEdit’s Text Filters folder. I didn’t have to make a single edit to the script:

python:
 1:  #!/usr/bin/env python
 2:  import sys
 3:  from dateutil.parser import parse
 4:  from dateutil.relativedelta import *
 5:  
 6:  print '''BEGIN:VCALENDAR
 7:  VERSION:2.0'''
 8:  
 9:  for line in sys.stdin:
10:    # Assign the fields; skip if there aren't 5 fields.
11:    try:
12:      event, sdtime, dur, place, desc = line.strip().split('|')
13:    except:
14:      print '!' * 20 + ' This event has the wrong number of fields'
15:      continue
16:    # Parse the start time; skip if it's not formatted correctly.
17:    try:
18:      start = parse(sdtime)
19:    except:
20:      print '!' * 20 + ' This event has a bad start time'
21:      continue
22:    # Distinguish between timed and all day events; skip if the duration is bad.
23:    if dur == 'all day':
24:      end = start + relativedelta(days=+1)
25:      dtstart = ';VALUE=DATE:' + start.strftime('%Y%m%d')
26:      dtend   = ';VALUE=DATE:' +   end.strftime('%Y%m%d')
27:    else:
28:      try:
29:        end = start + relativedelta(hours=+float(dur))
30:        dtstart = start.strftime('%Y%m%dT%H%M00')
31:        dtend   = end.strftime('%Y%m%dT%H%M00')
32:      except:
33:        print '!' * 20 + ' This event has a bad duration'
34:        continue
35:    
36:    print '''BEGIN:VEVENT
37:  DTSTART:%s
38:  DTEND:%s
39:  SUMMARY:%s
40:  LOCATION:%s
41:  DESCRIPTION:%s
42:  END:VEVENT''' % (dtstart, dtend, event, place, desc)
43:  
44:  print 'END:VCALENDAR'

I could look at how easy this was and think what a dummy I was for not doing it sooner. I prefer, in my Panglossian way, to look forward to the time I’ll be saving from now on.