At the Forge - Dynamically Generated Calendars

Want to remind your Web site's users about upcoming events or get the whole company synced on a common calendar? Get started creating iCalendar files with Python.
Creating an iCalendar

The real solution, and one that makes life more interesting, is to create the iCalendar file dynamically when the user requests it. That is, our CGI program does not return the contents of an existing iCalendar file; instead, it creates an iCalendar file programmatically, returning it to the user's calendar client program.

At first glance, this might seem to be a simple task. After all, the iCalendar file format appears to be straightforward, so maybe we can code something together ourselves. But upon closer examination, we discover that creating an iCalendar file is easier said than done, particularly if we want to include recurring events.

Given the increasing popularity of the iCalendar standard and the plethora of open-source projects, I was surprised to discover the relative lack of attention that iCalendar has received from the biggest open-source programming communities. Part of my surprise was because iCalendar has been around for several years, is used by many companies and is supported by many calendar programs, from Novell's Evolution to Lotus Notes to Microsoft Outlook. This combination usually is a recipe for several different options, in several different programming languages.

I first looked at Perl, whose CPAN archive is renowned for its many modules, including many for Internet standards of various sorts. Although several Perl modules are available that parse iCalendar files, no up-to-date module exists for building them. Net::ICal::Libical was going to be a wrapper around the C-language libical library but was last released in a pre-alpha version, several years ago. Net::ICal was part of a project called ReefKnot, which also appears to have been abandoned.

Luckily, the Danish developer Max M (see the on-line Resources) recently decided to fill this gap and wrote a Python package that makes it easy to create an iCalendar file. I downloaded and installed the package on my computer without any trouble, and I found that it is quite straightforward to create a calendar with this package. Combined with our simple CGI program from before, we should be able to create and publish a calendar without any trouble.

Creating a Dynamic Calendar

I downloaded and installed the iCalendar package from the maxm.dk site. Unlike many modern Python packages, it doesn't install automatically. You must copy it manually to your system's site-packages directory, which on my Fedora Core 3 system is located at /usr/lib/python-2.3/site-packages.

As you can see in Listing 2, I was able to use this newly installed iCalendar package to create new objects of type Calendar and Event. The first thing I had to do was import the appropriate packages into the current namespace:

from iCalendar import Calendar, Event

The Calendar and Event modules inside of the iCalendar package correspond to the entire iCalendar file and one event in that file, respectively. We thus create a single instance of the Calendar object and one Event object for each event that we might want to create.

We then can create the calendar object:

cal = Calendar()
cal.add('prodid',
        '-//Python iCalendar 0.9.3//mxm.dk//')
cal.add('version', '2.0')

The second and third lines here, in which we invoke cal.add(), allow us to add identifying data to our iCalendar file. The first of these allows us to tell the client software which program generated the iCalendar file. This is useful for debugging; if we consistently get corrupt iCalendar files from a particular software package, we can contact the author or publisher and report a bug. The second line, in which we add a version identifier, indicates which version of the iCalendar specification we are following. RFC 2445 indicates that we should give this field a value of 2.0 if we are going to follow that specification.

Now that we have created a calendar, let's create an event and give it a summary line to be displayed in the calendar program of anyone subscribing to this iCalendar file:

event = Event()
event.add('summary', 'ATF deadline')

Every event, as we have already seen in the file we examined, has three date/time fields associated with it: the starting date and time, dtstart; the ending date and time, dtend; and an indication of when this entry was added to the calendar, dtstamp. The iCalendar standard uses a strange if useful format for its dates and times, but the Event object knows how to work with those if we give it a datetime object from the standard datetime Python package. So, we can say:

event.add('dtstart',
          datetime(2005,3,11,14,0,0,tzinfo=UTC()))
event.add('dtend',
          datetime(2005,3,11,16,0,0,tzinfo=UTC()))
event.add('dtstamp',
          datetime(2005,3,11,0,10,0,tzinfo=UTC()))

Notice that the above three lines used UTC as the time zone. When the iCalendar file is displayed inside of a client Calendar application, it is shown with the user's local time zone, as opposed to UTC.

Once we have created the event, we need to give it a unique ID. When I say unique, I mean that the ID should be truly unique, across all calendars and computers in the world. This sounds trickier than it actually is. You can use a number of different strategies, including using a combination of the creation timestamp, IP address of the computer on which the event was created and a large random number. I decided to create a simple UID, but if you are creating an application to be shared across multiple computers, you probably should think about what sort of UIDs you want to create and then standardize on them:

event['uid'] = 'ATF20050311A@lerner.co.il'

Finally, we must give our event a priority, in the range of 0 through 9. An event with priority 5 is considered to be normal or average; urgent items get higher numbers and less-urgent items get lower ones:

event.add('priority', 5)

Once we have created our event, we attach it to the calendar object, which has been waiting for us to do something with it:

cal.add_component(event)

If we are so interested, we then could to add more events to the calendar. So long as each has a unique UID field, there won't be any problems.

Finally, we turn our Calendar object into an iCalendar file, using the as_string() method:

print cal.as_string()

Because print writes to standard output by default, and because CGI programs send their standard output back to the HTTP client, this has the effect of sending an iCalendar file back to whoever made the HTTP request. And because we have defined the MIME type to be of type text/calendar, the HTTP client knows to interpret this as a calendar and display it appropriately. If we look at the output ourselves, we see that it is indeed in iCalendar format:

BEGIN:VCALENDAR
PRODID:-//Python iCalendar 0.9.3//mxm.dk//
VERSION:2.0
BEGIN:VEVENT
DTEND:20050311T160000Z
DTSTAMP:20050311T001000Z
DTSTART:20050311T140000Z
PRIORITY:5
SUMMARY:ATF deadline
UID:ATF20050311A@lerner.co.il
END:VEVENT
END:VCALENDAR

Now, I must admit that this example is almost as contrived as the previous one. True, we have exploited the fact that we can generate a calendar dynamically, but this event was hard-coded into the program, making it impossible for a nonprogrammer to add, modify or delete the event. That said, we have taken an additional step toward the programmatic calculation of events and dates. The next step is to store the dates in a file or even in a relational database and to use our program to convert the information on the fly.

______________________

Comments

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.

The webpage for this module s

ewindisch's picture

The webpage for this module seems to have been moved to http://codespeak.net/icalendar/ where there are much more current releases available than reviewed here. Amongst other things, it now supports installation via the command "python setup.py install"

The old webpage still exists and makes no acknowledgement of the new site, thus I understand the author's confusion.

iCalendar/vCalendar

Anonymous's picture

I really liked the article, and I like where it's going. I'm a member of more than one non-profit org that would benefit greatly from a calendar system like the one proposed.

Unfortunately, MS Outlook, the calendar used by most of the members, does not seem to like iCalendar files generated by Mozilla. I don't know if MSO's iCalendar/vCalendar import works at all.

On MSO 2000, it just says it can't read the file. On MSO 2003, it displays the message

"This error can appear if you have attempted to save a recurring Lunar appointment in iCalendar format.
To avoid this error, set the appointment option to Gregorian instead of Lunar."

So I tried a file that had only 2 appointments, both non-recurring. Still got the same message. Configuring Mozilla to store universal time didn't help either.
MSO will not export an .ics/.vcs file, so it's difficult to see what it thinks is a good example.

I think this problem will somehow have to be resolved to make this idea really useful, since unfortunately I can't dictate to the other members what calendar app they should use. Any ideas?

Thanks,
--Jeff in Austin, TX

MSO exports icalendar files j

Anonymous's picture

MSO exports icalendar files just fine. Just send an appointment using internet format or drag and drop an appointment on an email after setting the default format to iCalendar. Then you can examine the format of a MSO iCalendar file.

White Paper
Linux Management with Red Hat Satellite: Measuring Business Impact and ROI

Linux has become a key foundation for supporting today's rapidly growing IT environments. Linux is being used to deploy business applications and databases, trading on its reputation as a low-cost operating environment. For many IT organizations, Linux is a mainstay for deploying Web servers and has evolved from handling basic file, print, and utility workloads to running mission-critical applications and databases, physically, virtually, and in the cloud. As Linux grows in importance in terms of value to the business, managing Linux environments to high standards of service quality — availability, security, and performance — becomes an essential requirement for business success.

Learn More

Sponsored by Red Hat

White Paper
Private PaaS for the Agile Enterprise

If you already use virtualized infrastructure, you are well on your way to leveraging the power of the cloud. Virtualization offers the promise of limitless resources, but how do you manage that scalability when your DevOps team doesn’t scale? In today’s hypercompetitive markets, fast results can make a difference between leading the pack vs. obsolescence. Organizations need more benefits from cloud computing than just raw resources. They need agility, flexibility, convenience, ROI, and control.

Stackato private Platform-as-a-Service technology from ActiveState extends your private cloud infrastructure by creating a private PaaS to provide on-demand availability, flexibility, control, and ultimately, faster time-to-market for your enterprise.

Learn More

Sponsored by ActiveState