CalDAV (RFC4791) client library for Python with comprehensive calendar server interaction capabilities
—
Quality
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Comprehensive event management functionality including creation, modification, deletion, recurring event handling, and attendee management with full iCalendar support and CalDAV scheduling extensions.
Represents CalDAV events (VEVENT components) with specialized methods for event-specific operations and date/time handling.
class Event(CalendarObjectResource):
"""
Event class inherits all functionality from CalendarObjectResource.
Additional methods available for Event objects:
"""
# Event-specific alias
set_dtend = CalendarObjectResource.set_end # Alias for setting event end time
# Inherited methods from CalendarObjectResource for date/time handling:
def get_duration(self):
"""Get event duration (inherited from CalendarObjectResource)."""
def set_duration(self, duration, movable_attr="DTSTART"):
"""Set event duration (inherited from CalendarObjectResource)."""
def set_end(self, end, move_dtstart=False):
"""Set event end time (inherited from CalendarObjectResource)."""
# Access to event properties through icalendar_component property:
# event.icalendar_component['DTSTART'] - start time
# event.icalendar_component['DTEND'] - end time
# event.icalendar_component['SUMMARY'] - event title
# event.icalendar_component['DESCRIPTION'] - event descriptionUsage Examples:
import caldav
from datetime import datetime, timedelta
# Get an existing event
calendar = principal.calendars()[0]
events = calendar.events()
if events:
event = events[0]
# Get event timing information through icalendar component
component = event.icalendar_component
start_time = component.get('DTSTART')
end_time = component.get('DTEND')
duration = event.get_duration() # Available method
print(f"Event: {component.get('SUMMARY', 'No title')}")
print(f"Start: {start_time}")
print(f"End: {end_time}")
print(f"Duration: {duration}")
# Modify event timing
new_start = datetime(2025, 9, 20, 15, 0) # 3 PM
component['DTSTART'] = new_start
event.set_duration(timedelta(hours=2)) # 2 hour meeting
# Save changes
event.save()
print("Event updated successfully")Create new events with comprehensive iCalendar support including all standard properties and extensions.
# Event creation through calendar.save_event() - see calendar-management.md
# Additional event-specific creation patterns:
def create_simple_event(summary, start, end, description=None, location=None):
"""
Helper function to create simple event iCalendar data.
Parameters:
- summary: str, event title
- start: datetime, event start time
- end: datetime, event end time
- description: str, optional event description
- location: str, optional event location
Returns:
str: iCalendar data for the event
"""Usage Examples:
from datetime import datetime, timezone
import uuid
# Create a simple event manually
def create_meeting_event(title, start_time, duration_hours, description=None, location=None):
end_time = start_time + timedelta(hours=duration_hours)
event_uid = f"{uuid.uuid4()}@example.com"
ical_data = f"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//My Calendar App//My Calendar App//EN
BEGIN:VEVENT
UID:{event_uid}
DTSTART:{start_time.strftime('%Y%m%dT%H%M%SZ')}
DTEND:{end_time.strftime('%Y%m%dT%H%M%SZ')}
SUMMARY:{title}
"""
if description:
ical_data += f"DESCRIPTION:{description}\n"
if location:
ical_data += f"LOCATION:{location}\n"
ical_data += """DTSTAMP:{}Z
STATUS:CONFIRMED
END:VEVENT
END:VCALENDAR""".format(datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%S'))
return ical_data
# Create and save an event
meeting_start = datetime(2025, 9, 25, 10, 0, tzinfo=timezone.utc)
meeting_ical = create_meeting_event(
title="Project Review Meeting",
start_time=meeting_start,
duration_hours=1.5,
description="Review project progress and discuss next steps",
location="Conference Room B"
)
event = calendar.save_event(meeting_ical)
print(f"Created event: {event.id}")
# Create all-day event
allday_ical = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//My App//My App//EN
BEGIN:VEVENT
UID:holiday-001@example.com
DTSTART;VALUE=DATE:20250925
DTEND;VALUE=DATE:20250926
SUMMARY:Team Building Day
DESCRIPTION:Annual team building activities
END:VEVENT
END:VCALENDAR"""
allday_event = calendar.save_event(allday_ical)Handle recurring events with support for RRULE patterns, exception dates, and recurrence expansion.
def expand(self, start, end):
"""
Expand recurring event within date range.
Parameters:
- start: datetime, start of expansion range
- end: datetime, end of expansion range
Returns:
list[Event]: List of Event instances for each occurrence
"""Usage Examples:
# Create a recurring event
recurring_ical = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//My App//My App//EN
BEGIN:VEVENT
UID:weekly-standup@example.com
DTSTART:20250915T090000Z
DTEND:20250915T093000Z
SUMMARY:Weekly Team Standup
DESCRIPTION:Weekly team synchronization meeting
RRULE:FREQ=WEEKLY;BYDAY=MO;COUNT=10
END:VEVENT
END:VCALENDAR"""
recurring_event = calendar.save_event(recurring_ical)
# Expand recurring event for next month
from datetime import datetime, timedelta
start_date = datetime.now()
end_date = start_date + timedelta(days=30)
occurrences = recurring_event.expand(start_date, end_date)
print(f"Found {len(occurrences)} occurrences in the next month")
for occurrence in occurrences:
start_time = occurrence.get_dtstart()
print(f"Standup on: {start_time}")
# Create recurring event with exceptions
recurring_with_exception = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//My App//My App//EN
BEGIN:VEVENT
UID:daily-checkin@example.com
DTSTART:20251001T150000Z
DTEND:20251001T151500Z
SUMMARY:Daily Check-in
RRULE:FREQ=DAILY;BYDAY=MO,TU,WE,TH,FR;COUNT=20
EXDATE:20251015T150000Z,20251030T150000Z
END:VEVENT
END:VCALENDAR"""
daily_event = calendar.save_event(recurring_with_exception)Manage event attendees with support for CalDAV scheduling extensions including RSVP tracking and role management.
def add_attendee(self, attendee, rsvp=None, role=None, partstat=None, cutype=None):
"""
Add attendee to event.
Parameters:
- attendee: str, attendee email address or calendar user address
- rsvp: bool, whether RSVP is requested
- role: str, attendee role ('REQ-PARTICIPANT', 'OPT-PARTICIPANT', 'CHAIR')
- partstat: str, participation status ('NEEDS-ACTION', 'ACCEPTED', 'DECLINED', 'TENTATIVE')
- cutype: str, calendar user type ('INDIVIDUAL', 'GROUP', 'RESOURCE', 'ROOM')
"""
def remove_attendee(self, attendee):
"""
Remove attendee from event.
Parameters:
- attendee: str, attendee email address to remove
"""
def add_organizer(self):
"""Add organizer property to event based on current user."""
def remove_organizer(self):
"""Remove organizer property from event."""Usage Examples:
# Get an event and manage attendees
event = calendar.event_by_uid("meeting-001@example.com")
if event:
# Add required participant
event.add_attendee(
attendee="alice@example.com",
rsvp=True,
role="REQ-PARTICIPANT",
partstat="NEEDS-ACTION"
)
# Add optional participant
event.add_attendee(
attendee="bob@example.com",
rsvp=True,
role="OPT-PARTICIPANT",
partstat="NEEDS-ACTION"
)
# Add meeting room resource
event.add_attendee(
attendee="room-a@example.com",
cutype="ROOM",
role="NON-PARTICIPANT"
)
# Set organizer
event.add_organizer()
# Save the updated event
event.save()
print("Attendees added to event")
# Later, remove an attendee
event.remove_attendee("bob@example.com")
event.save()Modify existing events with proper change tracking and conflict resolution.
# Inherited from CalendarObjectResource
def save(self):
"""Save event changes to the server."""
def delete(self):
"""Delete event from the server."""
def copy(self, new_parent):
"""Copy event to different calendar."""
def move(self, new_parent):
"""Move event to different calendar."""
def change_uid(self, new_uid=None):
"""Change event UID (generates new UID if not provided)."""
def load(self):
"""Load/reload event data from the server."""
def get_relatives(self):
"""
Get related calendar objects.
Returns:
list[CalendarObjectResource]: Related objects
"""
def add_relation(self, related_object, reltype="PARENT"):
"""
Add relation to another calendar object.
Parameters:
- related_object: CalendarObjectResource, target object
- reltype: str, relation type ('PARENT', 'CHILD', 'SIBLING')
"""
def split_expanded(self):
"""
Split expanded recurrence into individual objects.
Returns:
list[Event]: Individual event instances
"""Usage Examples:
# Modify an existing event
event = calendar.event_by_uid("meeting-001@example.com")
if event:
# Access the iCalendar component for detailed modifications
component = event.icalendar_component
# Update basic properties
component['SUMMARY'] = 'Updated Meeting Title'
component['DESCRIPTION'] = 'Updated meeting description with new agenda'
component['LOCATION'] = 'New Conference Room'
# Update timing
event.set_dtstart(datetime(2025, 9, 25, 14, 30, tzinfo=timezone.utc))
event.set_duration(timedelta(hours=2))
# Add alarm/reminder
from icalendar import Alarm
alarm = Alarm()
alarm.add('TRIGGER', timedelta(minutes=-15)) # 15 minutes before
alarm.add('ACTION', 'DISPLAY')
alarm.add('DESCRIPTION', 'Meeting reminder')
component.add_component(alarm)
# Save changes
event.save()
print("Event updated successfully")
# Copy event to another calendar
other_calendar = principal.calendars()[1]
copied_event = event.copy(other_calendar)
print(f"Copied event to {other_calendar.name}: {copied_event.id}")
# Move event between calendars
moved_event = event.move(other_calendar)
print(f"Moved event to {other_calendar.name}")Advanced event querying with date ranges, text search, and property filtering.
# These methods are available on Calendar objects for event-specific searches
# See calendar-management.md for full details
# Common event search patterns:
def find_events_by_summary(calendar, summary_text):
"""Find events matching summary text."""
return calendar.search(
comp_filter="VEVENT",
text_match=summary_text
)
def find_events_by_location(calendar, location):
"""Find events at specific location."""
return calendar.search(
comp_filter="VEVENT",
prop_filter={"LOCATION": location}
)
def find_upcoming_events(calendar, days=7):
"""Find events in the next N days."""
from datetime import datetime, timedelta
start = datetime.now()
end = start + timedelta(days=days)
return calendar.date_search(start=start, end=end, compfilter="VEVENT")# Common event properties that can be accessed via icalendar_component
EVENT_PROPERTIES = {
"SUMMARY": "str", # Event title/summary
"DESCRIPTION": "str", # Event description
"DTSTART": "datetime", # Start date/time
"DTEND": "datetime", # End date/time
"DURATION": "timedelta", # Duration (alternative to DTEND)
"LOCATION": "str", # Event location
"STATUS": "str", # CONFIRMED, TENTATIVE, CANCELLED
"PRIORITY": "int", # Priority (0-9, 0=undefined, 1=highest, 9=lowest)
"CLASS": "str", # PUBLIC, PRIVATE, CONFIDENTIAL
"CATEGORIES": "list[str]", # Event categories/tags
"ORGANIZER": "vCalAddress", # Event organizer
"ATTENDEE": "list[vCalAddress]", # Event attendees
"RRULE": "str", # Recurrence rule
"EXDATE": "list[datetime]", # Exception dates
"RDATE": "list[datetime]", # Additional recurrence dates
}
# Event status values
EVENT_STATUS = {
"CONFIRMED": "Confirmed event",
"TENTATIVE": "Tentative/provisional event",
"CANCELLED": "Cancelled event"
}
# Event classification values
EVENT_CLASS = {
"PUBLIC": "Public event (default)",
"PRIVATE": "Private to organizer",
"CONFIDENTIAL": "Confidential access required"
}Install with Tessl CLI
npx tessl i tessl/pypi-caldav