Examples ¶
-
https://github.com/python-caldav/caldav/blob/master/examples/scheduling_examples.py
-
https://github.com/python-caldav/caldav/blob/master/tests/test_caldav.py
Check the examples folder, particularly basic examples.
There is also a scheduling examples for sending, receiving and replying to invites, though this is not very well-tested so far.
The test code also covers lots of stuff, though it’s not much optimized for readability (at least not as of 2020-05).
Tobias Brox is also working on a command line interface built around the caldav library.
Usage Examples ¶
basic_usage_examples ¶
1import sys
2from datetime import date
3from datetime import datetime
4
5## We'll try to use the local caldav library, not the system-installed
6sys.path.insert(0, "..")
7sys.path.insert(0, ".")
8
9import caldav
10
11## DO NOT name your file calendar.py or caldav.py! We've had several
12## issues filed, things break because the wrong files are imported.
13## It's not a bug with the caldav library per se.
14
15## CONFIGURATION. Edit here, or set up something in
16## tests/conf_private.py (see tests/conf_private.py.EXAMPLE).
17caldav_url = "https://calendar.example.com/dav"
18username = "somebody"
19password = "hunter2"
20
21## When using the caldav library, one should always start off with initiating a
22## DAVClient object, which should contain connection details and credentials.
23## Initiating the object does not cause any requests to the server, so this
24## will not break even if caldav url is set to example.com
25client = caldav.DAVClient(url=caldav_url, username=username, password=password)
26
27## For the convenience, if things are correctly set up in test config,
28## the code below may replace the client object with one that works.
29if "example.com" in caldav_url and password == "hunter2":
30 from tests.conf import client as client_
31
32 client = client_()
33
34## Typically the next step is to fetch a principal object.
35## This will cause communication with the server.
36my_principal = client.principal()
37
38## The principals calendars can be fetched like this:
39calendars = my_principal.calendars()
40if calendars:
41 ## Some calendar servers will include all calendars you have
42 ## access to in this list, and not only the calendars owned by
43 ## this principal.
44 print("your principal has %i calendars:" % len(calendars))
45 for c in calendars:
46 print(" Name: %-20s URL: %s" % (c.name, c.url))
47else:
48 print("your principal has no calendars")
49
50## Let's try to find or create a calendar ...
51try:
52 ## This will raise a NotFoundError if calendar does not exist
53 my_new_calendar = my_principal.calendar(name="Test calendar")
54 assert my_new_calendar
55 ## calendar did exist, probably it was made on an earlier run
56 ## of this script
57except caldav.error.NotFoundError:
58 ## Let's create a calendar
59 my_new_calendar = my_principal.make_calendar(name="Test calendar")
60
61## Let's add an event to our newly created calendar
62## (This usage pattern is new from v0.9.
63## Earlier save_event would only accept some ical data)
64my_event = my_new_calendar.save_event(
65 dtstart=datetime(2020, 5, 17, 8),
66 dtend=datetime(2020, 5, 18, 1),
67 summary="Do the needful",
68 rrule={"FREQ": "YEARLY"},
69)
70
71## Let's search for the newly added event.
72## (this may fail if the server doesn't support expand)
73print("Here is some icalendar data:")
74try:
75 events_fetched = my_new_calendar.date_search(
76 start=datetime(2021, 5, 16), end=datetime(2024, 1, 1), expand=True
77 )
78 print(events_fetched[0].data)
79except:
80 print("Your calendar server does apparently not support expanded search")
81 events_fetched = my_new_calendar.date_search(
82 start=datetime(2020, 5, 16), end=datetime(2024, 1, 1), expand=False
83 )
84 print(events_fetched[0].data)
85
86event = events_fetched[0]
87
88## To modify an event, it's best to use either the vobject or icalendar module for it.
89## The caldav library has always been supporting vobject out of the box, but icalendar is more popular.
90## event.instance will as of version 0.x yield a vobject instance, but this may change in future versions.
91## Both event.vobject_instance and event.icalendar_instance works from 0.7.
92event.vobject_instance.vevent.summary.value = "Norwegian national day celebratiuns"
93event.icalendar_instance.subcomponents[0][
94 "summary"
95] = event.icalendar_instance.subcomponents[0]["summary"].replace(
96 "celebratiuns", "celebrations"
97)
98event.save()
99
100## Please note that the proper way to save new icalendar data
101## to the calendar is calendar.save_event(ics_data),
102## while the proper way to update a calendar event is
103## event.save(). Doing calendar.save_event(event.data)
104## may break. See https://github.com/python-caldav/caldav/issues/153
105## for details.
106
107## It's possible to access objects such as calendars without going
108## through a Principal object if one knows the calendar URL
109the_same_calendar = client.calendar(url=my_new_calendar.url)
110
111## to get all events from the calendar, it's also possible to use the
112## events()-method. Recurring events will not be expanded.
113all_events = the_same_calendar.events()
114
115## It's also possible to use .objects.
116all_objects = the_same_calendar.objects()
117
118## since we have only added events (and neither todos nor journals), those
119## should be equal ... except, all_objects is an iterator and not a list.
120assert len(all_events) == len(list(all_objects))
121
122## Let's check that the summary got right
123assert all_events[0].vobject_instance.vevent.summary.value.startswith("Norwegian")
124assert all_events[0].vobject_instance.vevent.summary.value.endswith("celebrations")
125
126## This calendar should as a minimum support VEVENTs ... most likely
127## it also supports VTODOs and maybe even VJOURNALs. We can query the
128## server what it can accept:
129acceptable_component_types = my_new_calendar.get_supported_components()
130assert "VEVENT" in acceptable_component_types
131
132## Clean up - remove the new calendar
133my_new_calendar.delete()
134
135## Let's try with a task list. Some servers cannot combine events and todos in the same calendar.
136my_new_tasklist = my_principal.make_calendar(
137 name="Test tasklist", supported_calendar_component_set=["VTODO"]
138)
139
140## We'll add a task to the task list
141my_new_tasklist.add_todo(
142 ics="RRULE:FREQ=YEARLY",
143 summary="Deliver some data to the Tax authorities",
144 dtstart=date(2020, 4, 1),
145 due=date(2020, 5, 1),
146 categories=["family", "finance"],
147 status="NEEDS-ACTION",
148)
149
150## Fetch the tasks
151todos = my_new_tasklist.todos()
152assert len(todos) == 1
153assert "FREQ=YEARLY" in todos[0].data
154
155print("Here is some more icalendar data:")
156print(todos[0].data)
157
158## date_search also works on task lists, but one has to be explicit to get them
159todos_found = my_new_tasklist.date_search(
160 start=datetime(2021, 1, 1),
161 end=datetime(2024, 1, 1),
162 compfilter="VTODO",
163 expand=True,
164)
165if not todos_found:
166 print(
167 "Apparently your calendar server does not support searching for future instances of reoccurring tasks"
168 )
169else:
170 print("Here is even more icalendar data:")
171 print(todos_found[0].data)
172
173## Mark the task as completed
174todos[0].complete()
175
176## This is a yearly task. Completing it for one year should probably
177## spawn a new task recurrence instance for the next year. The RFC
178## says nothing about it, it seems like it's up to the clients weather
179## to implement such logic or not. I've implemented such logic in the
180## calendar-cli project, perhaps it should be moved into the caldav
181## library, but as for now ... completing the task will cause the task
182## list to be emptied.
183todos = my_new_tasklist.todos()
184assert len(todos) == 0
185
186## It's possible to fetch historic tasks too
187todos = my_new_tasklist.todos(include_completed=True)
188assert len(todos) == 1
189
190## and it's possible to delete tasks completely
191todos[0].delete()
192
193my_new_tasklist.delete()