Using Python to Parse iCalendar File

In the last learning phase I tried to use Examtime to create my study plan in a celandar. After the exam I wanted to check how much time I’ve used for each lecture to prepare. So I wrote a small Python script and exported an iCalendar from Examtime.

Examtime Calendar

##Parsing the .ical To calculate the used time I have to get all the calendar events and count their time together. In the iCal format the VEVENT Block marks an event subcomponent with properties like start time DTSTART and end time DTEND.

BEGIN:VCALENDAR
...
BEGIN:VEVENT
DTEND;TZID=Europe/Berlin;VALUE=DATE-TIME:20140113T123000
DTSTART;TZID=Europe/Berlin;VALUE=DATE-TIME:20140113T103000
DTSTAMP;VALUE=DATE-TIME:20131214T104201Z
UID:20140215121243023-3749894@examtime.com
DESCRIPTION:Asignatura: Math1I\nCategoría: exam\n
SUMMARY:P_An1I
END:VEVENT
...

The iCalendar library makes parsing a breeze. You can iterate over all components of a certain type by using the walk function.

from icalendar import Calendar

lectures = ['An1I', 'Math1I', 'Bsys1', 'CN1', 'EnglHTw', 'Prog1', 'ICTh']
file = open('examtime_export.ics', 'rb')
cal = Calendar.from_ical(file.read())
sessions = [(lecturize(e), calculate_time(e)) for e in cal.walk('vevent')]

I struggled at first with getting the time of the event but you simply have to call the dt attribute and you get a normal datetime object.

def calculate_time(event):
    start = event['DTSTART'].dt
    end = event['DTEND'].dt
    return end - start

The lecturize function searches for the lecture names in the event summary and then uses that lecture name as a key.

def lecturize(event):
    summary = str(event['SUMMARY'])
    return [lecture for lecture in lectures if lecture in summary]

Now we have the data in a usable format.

[('An1I', datetime.timedelta(0, 7200)),
 ('Prog1', datetime.timedelta(0, 9000)),
 ('EnglHTw', datetime.timedelta(0, 7200)),
 ...
]

##Calculating the used time We group the records by it’s lecture name and simply yield the sum of the timedeltas.

from itertools import groupby
from operator import itemgetter
from datetime import timedelta

def time_per_lecture(events):
    lecture_name = itemgetter(0)
    sorted_events = sorted(events, key=lecture_name)
    for key, group in groupby(sorted_events, lecture_name):
        yield (key, sum(map(itemgetter(1), group), timedelta()))

Now we have the used_time foreach lecture and the total_time and are done.

used_time = dict(time_per_lecture(events))
total_time = sum(used_time.values(), timedelta())

for lecture, time in used_time.items():
    print('{}\t{}h'.format(lecture, time.total_seconds() / 3600))

print('=============')
print('TOTAL\t{}h'.format(total_time.total_seconds() / 3600))

In my case that returned the time used while I was learning during December and January:

CN1	58.0h
An1I	63.5h
ICTh	49.5h
EnglHTw	16.0h
Bsys1	30.0h
Prog1	12.0h
Math1I	45.5h
=============
TOTAL	274.5h

I created a Gist with the code in case you’re interested.