Posts Tagged ‘ical’

Scheduling to-dos

Geez, it’s been a while since I last posted an original script here, so let’s fix that.

One of the things I miss about OmniFocus is its ability to hide tasks until a given date. The advantage of being able to do this is that you can add the task when it comes to you, but you won’t see it as part of your regular to-do list until the time is ripe. My favorite example is the recurring task of changing my furnace filter, which I do every 3-4 months. I liked how OmniFocus allowed me to enter this task but kept it out of my Next Actions list until a week or so before I was supposed to do it.

TaskPaper, my current to-do-list manager, doesn’t have this feature. TaskPaper’s greatest strength, and the reason I actually use it, is its simplicity, but this is one area where a little complexity would help. Since TaskPaper’s “database” of to-dos is just a simple text file, it’s easy to write scripts that extend its features (like this one), and last week I started planning a script that would automatically add scheduled tasks to my main to-do list. After a bit of thinking, though, I stopped.

Here’s the thing: I’ve never stuck with any particular task managing system, software- or paper-based, for more than a year at a time. As much as I like TaskPaper now, why should I assume I’ll still be using it a year from now? Why should I invest the time in writing a script that will be used for only a small percentage of my to-dos and may never be used after Christmas? I decided to go for a longer-lasting solution.

I’ve been using iCal and Mail continuously for over five years, ever since returning to the Mac from Linux. If I’m at my computer, they’re running. So the most robust system, it seemed, would be one that emails me a reminder when it’s time to add a task to my to-do list. Although this system won’t add the entry automatically, it will work no matter what task manager I use.

First, I created a new iCal calendar called tasks.

Every entry in this calendar will be an item that should go into my to-do list on the specified date. I keep this calendar unchecked, and therefore invisible, except when entering a scheduled task, because these aren’t part of what David Allen calls the “hard landscape,” and so, according to GTD orthodoxy, shouldn’t be in my calendar at all. In fact, I don’t consider the tasks entries as part of my calendar, per se—they’re merely using iCal as a convenient mechanism for running scripts on a schedule.

Here’s my entry for replacing the furnace filter.

It’s a recurring event that appears quarterly on the morning of the first day of the month. The summary, “Change furnace filter,” is the task to be added to my to-do list on that day. When the scheduled time arrives, it runs an AppleScript called Email Scheduled Task:

 1:  set rightNow to current date
 2:  set fiveMinutesAgo to rightNow - 5 * minutes
 3:  set fiveMinutesFromNow to rightNow + 5 * minutes
 4:  
 5:  tell application "iCal"
 6:    set theEvents to every event of calendar "tasks" whose start date comes after fiveMinutesAgo and start date comes before fiveMinutesFromNow
 7:    set theTask to the summary of item 1 of theEvents
 8:  end tell
 9:  
10:  tell application "Mail"
11:    set theMessage to make new outgoing message with properties {visible:true, subject:"Scheduled Task", content:theTask}
12:    tell theMessage
13:      make new to recipient at end of to recipients with properties {name:"Dr. Drang", address:"user@example.com"}
14:    end tell
15:    send theMessage
16:  end tell

The script does three things:

  1. It gets the current time and sets up a ten-minute window around it.
  2. It looks through the tasks calendar for entries within that window of time and gets the summary of the first one.
  3. It sends an email to me with that summary as the content and “Scheduled Task” as the subject.

A time window of some sort was needed because the script’s call to current date will almost certainly not be exactly equal to the entry’s start date. A window as wide as ten minutes is probably unnecessary, but I was feeling generous. As long as I don’t create entries with overlapping windows—a very easy restriction to satisfy—the theEvents list returned by Line 6 will have just one item, and therefore Line 7 will grab only the task that triggered the script.

(If you’re wondering why there isn’t an easier way to get the event that triggered the script, join the club. I couldn’t find one.)

I learned how to do the date math in Lines 2 and 3 from the AppleScript Language Guide and how to set up and send an email message from this MacTech article.

On the morning of July 1, the message “Change furnace filter” will be in my inbox when I get to work. I’ll select and copy the content text and paste it into TaskPaper (or whatever I’m using by then). Presumably, I’ll change the filter shortly thereafter and scratch it off my list. Repeat on October 1, January 1, etc.


Extending the Calendar Events bundle for TextMate

I’ve added a new command to my Calendar Events bundle for TextMate (described in this post and available in this GitHub repository). The new command saves the generated iCalendar document to a temporary file and opens that file in iCal (or, presumably, BusyCal, if that’s your default calendar program). When an iCalendar file is opened in iCal, it asks you which calendar you want to put the new events into:

Clicking the popup menu will let you choose any of your existing calendars and will also let you create a new one.

The new command is called “Add to calendar,” and it’s bound to the ⌃⌥⌘A keyboard shortcut.

So the process of adding events to your calendar is now:

  1. Open a new document in TextMate and press ⌃⌥⇧E to make it an Events document.
  2. Use the “ev” and/or “ad” snippets to add timed events or all-day events to the document in a simple pipe-separated format.
  3. Press ⌃⌥⌘C to create an iCalendar document with those events.
  4. Assuming the iCalendar document is correct, press ⌃⌥⌘A to add those events to an iCal calendar chosen via the window shown above.

Calendar events bundle for TextMate

I think most users would agree that entering new events in iCal has always been clumsy and the Snow Leopard update just made it clumsier. I mentioned in a post last week that BusyCal’s entry method was distinctly better, although overall, BusyCal wasn’t enough of an improvement to get me to switch. In the past, I’ve used TextMate to create files using a simple pipe-separated format for the events; these are converted through a TM command into iCalendar files, which can be dragged to iCal to add the events. I’ve now improved that command and made a TextMate bundle for generating calendar events.

You can get the bundle from its GitHub repository. You install it by either

Although it’s not necessary, it would help if you also install my fork of the iCalendar bundle. This adds just one feature to the standard iCalendar bundle: it recognizes the file type based on a first line of BEGIN:VCALENDAR. This makes saving the generated iCalendar file with the proper extension a little bit easier.

To use the Calendar Events bundle, open a new document and press ⌃⌥⇧E to change the document’s language to Event. You now have two tab-triggered snippets available to enter events. For timed events, type “ev” and tab to get

Event|01/07/10 9:00 am|1.00|Location|Notes

This is the pipe-separated format for defining an event. The five fields are:

  1. The event name, which is the top line in the usual iCal event bubble.
  2. The event’s starting time, which is both a date and time. There are three TextMate tab stops within this field: one for the month and day, one for the year, and one for the time. The date parts default to the current date, and the time defaults to 9:00 am.
  3. The event’s duration in hours, which defaults to 1.
  4. The event’s location.
  5. Any other notes you may want to describe the event.

The location and notes fields may be blank, but the pipe characters between them must be present.

For all-day events, type “ad” and tab to get

Event|01/07/10|all day|Location|Notes

There is no time associated with the start of the event and therefore only two tab stops in this field. The duration is set to “all day” and is skipped over when tabbing through the snippet. The other three fields are the same as before.

When you’ve entered all the events, invoke the “Make ICS” command, either by choosing it from the gear popup menu at the bottom of the window or by typing ⌃⌥⌘C. That will generate a new document with the events transformed into the iCalendar format. If you’ve made a formatting error in the Events document, it will (probably) be flagged as such in the iCalendar document. Here’s an example in which

Project conf call|1/7/10 9:00 am|1.00|office|Pull drawings from file
All day event|1/20/10|all day||
Wrong # of fields|8/23/10 9:00 am|1.00|Location
Project meeting|1/10/10 10am|2|Chicago|Bring drawings
Bad start time|1/40/10 9:00 am|1.00|home|
Personal day|1/30/10|all day||
Bad duration|1/12/10 9:00 am|bad|office|whatever
Basketball|1/15/10 7pm|2|Stadium|

is transformed into

BEGIN:VCALENDAR
VERSION:2.0
BEGIN:VEVENT
DTSTART:20100107T090000
DTEND::20100107T100000
SUMMARY:Project conf call
LOCATION:office
DESCRIPTION:Pull drawings from file
END:VEVENT
BEGIN:VEVENT
DTSTART;VALUE=DATE:20100120
DTEND:;VALUE=DATE:20100121
SUMMARY:All day event
LOCATION:
DESCRIPTION:
END:VEVENT
!!!!!!!!!!!!!!!!!!!! This event has the wrong number of fields
BEGIN:VEVENT
DTSTART:20100110T100000
DTEND::20100110T120000
SUMMARY:Project meeting
LOCATION:Chicago
DESCRIPTION:Bring drawings
END:VEVENT
!!!!!!!!!!!!!!!!!!!! This event has a bad start time
BEGIN:VEVENT
DTSTART;VALUE=DATE:20100130
DTEND:;VALUE=DATE:20100131
SUMMARY:Personal day
LOCATION:
DESCRIPTION:
END:VEVENT
!!!!!!!!!!!!!!!!!!!! This event has a bad duration
BEGIN:VEVENT
DTSTART:20100115T190000
DTEND::20100115T210000
SUMMARY:Basketball
LOCATION:Stadium
DESCRIPTION:
END:VEVENT
END:VCALENDAR

On my system, the two documents look like this:

Because I have my iCalendar bundle installed, the generated document is automatically recognized as an iCalendar file and is syntax-highlighted appropriately. When I save it, the .ics extension will be present by default. If you don’t have my iCalendar bundle, TextMate won’t recognize it as an iCalendar file and you’ll have to change the extension from whatever your default is (probably .txt).

You’ll also notice that the pipe characters are colored in my Events document. That’s because I added an entry for tabular separators to the theme (IDLE) that I use.

This highlighting will also work for CSV and TSV files if you have the Tabular bundle installed. If you want special highlighting for the field separators, just add a line with a scope of punctuation.separator.tabular.field to the theme you use. The highlighting isn’t a requirement; the snippets and command will work without it.

The Make ICS command uses the parse function from Python’s datetime the dateutil module to interpret the date and time in the second field. This is a fairly forgiving function; you don’t need to include leading zeros, and as you can see from the example above, even something like “7pm” is interpreted correctly.

Update 1/8/10
Not sure what I was thinking when I wrote datetime in the previous paragraph. The module used is dateutil, which isn’t a standard Python module. On my iMac, it’s in a subdirectory of /System/Library/Frameworks/Python.framework, which suggests that it’s supplied by Apple and not something I installed and later forgot about. I’ll look into it and update here when I get the answer…

OK, I’m back, and I’m pretty sure now that dateutils has been included by Apple since Leopard. So if you’re running OS X 10.5.x or 10.6.x, the Make ICS command will work. If you’re running 10.4.x or earlier, you have three choices. In decreasing order of desirability, they are:

  1. Upgrade to Leopard or Snow Leopard, i.e., get with the program. Look, my home computer is an iBook that came with Jaguar, and it’s perfectly happy with Leopard—I’d have upgraded it to Snow Leopard if it weren’t Intel-only. There’s no reason for you to be that far behind.
  2. Download dateutils from Labix and install it according to their directions.
  3. Just don’t bother with the Calendar Events bundle at all.

I deliberately added errors to three lines in the Events document so you can see how they are handled. The position of the error messages in the iCalendar document will tell you which lines need to be fixed.

After saving the iCalendar document, you can drag it onto one of the calendar names in iCal to get all the events added to that calendar. iCal won’t accept a file with formatting errors, so if you forget to make the necessary corrections, iCal will tell you.

Is this faster than iCal’s normal entry method? Certainly not if you’re just adding one or two events. But if you need to add several events, especially when the events span several weeks or months, it’s much faster. I also find that I make fewer errors because I don’t get frustrated with all the keystrokes and/or mouse clicks iCal demands.


Why I won’t be switching to BusyCal

After reading Wolf Rentzsch’s review of BusyCal (which I was led to my this Daring Fireball link), I decided to download it and give it quick test run. My initial impression is that its improvements over iCal aren’t enough to justify a switch.

Let me start by saying that BusyCal’s greatest strength—the ease with which it syncs with Google Calendar and with other BusyCals on your local network—are of little value to me. Apart from some very basic information, I don’t share calendars with co-workers (who run Windows, anyway), and my wife uses a paper calendar. Keeping my iPhone up-to-date is about all the syncing I need, so I’m not exactly a prime target for BusyCal.

But one thing in Rentzsch’s review caught my eye:

Simple text box for entering times. You can actually type out “4:23p” into a new event’s start time instead of navigating iCal’s awkward segregated time field.

Entering events in iCal has always been a pain because the hours and minutes are in different fields, forcing you to tab from one to the other. Simpler time entry would be a significant improvement.

What I learned from playing with BusyCal is that time entry is even better than Rentzsch’s description. Yes, you can type “4:23p” directly, but you can also type just “423p” and get the same result. And for an appointment at 3:00 PM, you can get by with typing just “3p.” This is so good it makes the multi-step procedure in iCal seem criminal.

Another nice thing about BusyCal is how it displays holidays in month view—with gray italic text down at the bottom of the day box.

Just like a nicely printed paper calendar. And you can designate any set of events as holidays, so family birthdays and anniversaries will get the same treatment as Christmas and New Year’s Day.

BusyCal also has preferences for showing weather forecasts and the phases of the moon. I particularly liked the way the full moon on New Year’s Eve was displayed in blue.

But there are some downsides. I agree with Rentzsch that weekends are kind of crappy looking. I don’t mind the red date numerals so much, but I really dislike the gray background; it makes reading the text on those days a bit harder and it diminishes the distinctiveness of the blue-gray “Today” background.

I was also disappointed to learn that, like iCal, BusyCal has only one preference setting for the starting day of the week. When I’m looking at a monthly view, I like seeing Sunday as the first day of the week, but when I’m looking at a weekly view, I prefer to start with Monday. This is the way most, if not all, paper calendars are laid out, and I can’t understand why this wouldn’t be an option in any professionally made calendar software. If BusyCal had that option, they might have made the sale.

I say “might” rather than “would” because it’s not clear to me how well BusyCal syncs with the iPhone. According to the BusyCal Configuration Guide, the syncing would have to be done through either MobileMe or Google Calendars. I have no interest in buying a MobileMe account, and my experiments with Google Sync in the past have been disasters. So I have to say that the lack of a direct sync with the iPhone is another mark against BusyCal.1

Update 1/3/10
I’m now pretty sure BusyCal will sync with the iPhone when the phone is docked. I added a test event via BusyCal, closed it, and launched iCal. The test event appeared in iCal, which suggests BusyCal is saving to iCal’s files, which will be synced when the iPhone is docked.

Because I’m not at my office computer (the one I sync my phone to), I can’t take the last step and see if the test event added in BusyCal gets put on the iPhone. I’ll give it a try tomorrow.

This test is more to satisfy my curiosity than anything else. Whatever the outcome, I won’t be switching to this version of BusyCal.

Update 1/4/10
OK, now I’ve run a sync test with my iPhone, and it’s clear that:

  1. BusyCal reads from and writes to the same calendar data files as iCal.
  2. Syncing my iPhone via the USB dock and iTunes works perfectly.

My syncing concerns in the paragraph above were, therefore, unfounded, and I’ve struck them out.

So it looks like I’ll be sticking with iCal and wearing out my tab key as I enter events. Too bad. There’s room for a high-end Mac calendar application.


  1. I recognize that it isn’t BusyCal’s fault that it can’t sync directly to a docked iPhone; Apple keeps that syncing option to itself. But that recognition doesn’t make BusyCal’s syncing with my iPhone any easier.