Benoit Debled

Software Developer
Firmware Developer
SysAdmin
Entrepreneur

Enhance iCal Calendars

My university uses a web app called HyperPlanning to communicate students schedule.

Unfortunately, the title of every classes I have is full of useless information which make my schedule unreadable at a first glance! So, I’ve decided to do something about it, and I’ve created a small Python script that would rename each event into a more readable version of it.

So the principle is very simple : I’ve created a script on my server that download every hour the .ics file of the calendar I wish to enhance. Once downloaded, I look if an event’s title contains a specific string. If it does, then I replace the event’s title with a readable title. I put then the new .ics file to a web server.

This program is basically based on two libraries :

  • urllib3 to download the .ics from the school server
  • icalendar to parse and modify the iCal To reduce the ics file, I deleted any classes from the semester I was not in. To do that, it was easier to create a new calendar and append every classes of my current semester than deleting the classes from my old semester.

I also wanted to keep the information of whether the class would be a teaching class or an exercise session.

So here is the python code :


#!/usr/bin/python3
from icalendar import Calendar, Event
import os
import urllib3, io

#Documentation : http://icalendar.readthedocs.org/en/latest/index.html

classes = [
["S-PHYS-016",""],
["S-MATH-020",""],
["I-TCTS-003",""],
["S-INFO-075",""],
["W-COGE-018",""],
["I-INFO-008",""],
["S-INFO-020",""],
["I-MARO-011",""],
["Statistique multidimensionnell", "Stat. Multi."],
["Intelligence artificielle", "IA"],
["Simulation", "Simulation"],
["Compilation", "Compilation"],
["Bases de données II", "BDD II"],
["Sém. Informatique en gestion", "Séminaire"]
]

typ = ["Cours", "TP", "Exercices", "Crs/exerc"]

#Download ics file
urllib3.disable_warnings()
http = urllib3.PoolManager()
res = http.urlopen('GET',"http://www.domain.com/foo.ics?", preload_content=False)
i = io.BufferedReader(res, 2048)

#Create inCal
inCal = Calendar.from_ical(i.read())
i.close()

#Create outCal
outCal = Calendar()
outCal.add('version', '2.0')
outCal.add('prodid', 'Debled_Copyright')

#Do actions on events
for component in inCal.walk():
    if component.name == "VEVENT":
        event_ok = False
        summary = component.get('summary')
        cancelled = ""
        if "ANNULÉ" in component.get('uid'):
            cancelled = "[Annulé] - "
        for c in classes:
            if c[0] in summary:
                event_ok = True
                if c[1] != "":
                    component['summary'] = cancelled + c[1]
                    for t in typ:
                        if t in summary :
                            component['summary'] += " - " + t
                    outCal.add_component(component)
        if event_ok == False:
            outCal.add_component(component)

o = open(os.path.join('/path/to/your/web/home/folder/foo.ics'), 'wb')
o.write(outCal.to_ical())
o.close()

So, now that you can transform your ics file as you wish, this script needs to be run at a periodic time.

For that to happen, you need to have this #!/usr/bin/python3 as a first line of your python file. It basically says that if this script is ran, then, it should be interpreted by the interpreter the OS can find at the path /usr/bin/python3. This path might be different on your own server. To know what’s the path of your python3 interpreter, type which python3 in your bash.

Once this is done, you need to make your python file executable by running this command : chmod +x foo.py.

You can now execute your script by typing ./foo.py

To run this script every hour, just edit your crontab file : vim /etc/crontab and add this line : 0 * * * * root /path/to/your/script/foo.py

That’s it! You can now import an iCal, modify it, and publish the modified one on your website. So you can import your awesome new calendar to your device with the adress www.yourdomain.com/foo.ics for example!

Enjoy!