Website of Daniel A. Mayer

Splitting a VCALENDAR Into Single VEVENTs

Recently, I had to upload a backup of my tasks and events to my CalDAV/CardDAV Server. The backup was one VCALENDAR containing all the events (VEVENT) and tasks (VTODO). However, the event and todo items are stored on the server individually and, moreover, the calendar server does not support the upload of a VCALENDAR containing multiple entries.

To solve this I wrote a quick Python script which splits a VCALENDAR containing multiple VEVENTs/VTODOs into multiple VCALENDARs containing only a single event or task.; (I’m sure this can be done in a one-line awk script as well)

(split_vcalendar.py) download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
"""
Splits a VCALENDAR file (a collection of VEVENT and VTODO objects) 
in its parts, i.e., individual files containing a single VEVENT/VTODO.

The files are named based on the UID of the VEVENT/VTODO object and each 
VEVENT/VTODO is wrapped in the same VCALENDAR meta data as the original file.
"""

PREFIX = "out/"

def extractUID(vitem):
    """
    Extracts the UID attribute fron VEVENT/VTODO
    """
    for x in vitem:
        if x[0:3] == "UID":
            return x.split(":")[1]


def writeToFile(entry, pre, post):
    uid = extractUID(entry)
    # File name is UID of event. strip the two newline characters
    output = open(PREFIX + uid[:-2] + '.ics', 'w')
    output.write(''.join(pre))
    output.write(''.join(entry))
    output.write(''.join(post))
    output.close()

def main():
    # open VCALENDAR file 
    vcal = open('backup.ics', 'r').readlines()

    # Preamble
    pre = []
    # Postamble
    post = []
    # current entry
    entry = []
    # list of all VEVENTS/VTODOs
    vitems = []

    for line in vcal:

        # Relevant blocks start with "BEGIN:VEVENT" OR "BEGIN:VTODO"
        if line.startswith("BEGIN:VEVENT") or \
           line.startswith("BEGIN:VTODO"):

            # everything before the first VEVENT/VTODO was preamble
            if not pre:
                pre = entry
                entry = []

        entry.append(line)

        # When block is complete, store to list of vitems and start new entry
        if line.startswith("END:VEVENT") or  \
           line.startswith("END:VTODO"):

            # Add item to list of vitems
            vitems.append(entry)
            entry = []

    # everything after the last entry is postamble
    post = entry

    # output all events and todos
    for x in vitems:
        writeToFile(x, pre, post)

if __name__ == "__main__":
    main()

The resulting files can easily be uploaded to your caldav server using, e.g., the following curl command:

1
find . -exec curl -L -vv -k -T {} -u <user>:<password> https://<server>:8443/calendars/users/<user>/calendar/$(`basename {}`) \;

Comments