CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-caldav

CalDAV (RFC4791) client library for Python with comprehensive calendar server interaction capabilities

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

calendar-management.mddocs/

Calendar Management

Comprehensive calendar operations including object retrieval, search functionality, synchronization, and calendar object management with full support for events, todos, and journals.

Capabilities

Calendar Object Retrieval

Retrieve different types of calendar objects (events, todos, journals, free/busy) from calendar collections with filtering and type-specific access methods.

class Calendar(DAVObject):
    def events(self):
        """
        Get all events in this calendar.
        
        Returns:
        list[Event]: List of Event objects
        """
    
    def todos(self):
        """
        Get all todos/tasks in this calendar.
        
        Returns:
        list[Todo]: List of Todo objects
        """
    
    def journals(self):
        """
        Get all journal entries in this calendar.
        
        Returns:
        list[Journal]: List of Journal objects
        """
    
    def freebusy(self):
        """
        Get all free/busy objects in this calendar.
        
        Returns:
        list[FreeBusy]: List of FreeBusy objects
        """
    
    def objects(self, load_objects=False, sort=None):
        """
        Get all calendar objects of all types.
        
        Parameters:
        - load_objects: bool, whether to load full object data
        - sort: str, sort order for objects
        
        Returns:
        list[CalendarObjectResource]: All objects in calendar
        """
    
    def objects_by_sync_token(self, sync_token=None, load_objects=True):
        """
        Retrieve objects using WebDAV synchronization for efficient updates.
        
        Parameters:
        - sync_token: str, synchronization token from previous sync
        - load_objects: bool, whether to load full object data (default True)
        
        Returns:
        tuple: (objects, new_sync_token) where objects is list of 
               CalendarObjectResource and new_sync_token is str
        """

Usage Examples:

import caldav

# Get calendar from principal
client = caldav.DAVClient(url="...", username="...", password="...")
principal = client.principal()
calendar = principal.calendars()[0]

# Get all events
all_events = calendar.events()
print(f"Found {len(all_events)} events")

# Get all todos
all_todos = calendar.todos()
for todo in all_todos:
    summary = todo.icalendar_component.get('SUMMARY', 'No title')
    print(f"Todo: {summary}")

# Note: No general objects() method available
# Use specific methods: calendar.events(), calendar.todos(), calendar.journals()

# Efficient synchronization
sync_token = None
while True:
    objects, new_sync_token = calendar.objects_by_sync_token(
        sync_token=sync_token,
        load_objects=True
    )
    
    if objects:
        print(f"Found {len(objects)} changed objects")
        # Process changed objects
        for obj in objects:
            print(f"Updated: {obj.id}")
    
    sync_token = new_sync_token
    break  # In real usage, would check for changes periodically

Calendar Object Creation

Create and save new calendar objects with support for different object types and creation options.

def save_event(self, ical, no_overwrite=False, no_create=False):
    """
    Save an event to this calendar.
    
    Parameters:
    - ical: str, iCalendar data (VCALENDAR format)
    - no_overwrite: bool, prevent overwriting existing events (default False)
    - no_create: bool, prevent creating new events (default False)
    
    Returns:
    Event: Saved Event object
    
    Raises:
    ConsistencyError: If no_overwrite=True and event exists
    NotFoundError: If no_create=True and event doesn't exist
    """

def save_todo(self, ical, no_overwrite=False, no_create=False):
    """
    Save a todo/task to this calendar.
    
    Parameters:
    - ical: str, iCalendar data (VCALENDAR format)
    - no_overwrite: bool, prevent overwriting existing todos
    - no_create: bool, prevent creating new todos
    
    Returns:
    Todo: Saved Todo object
    """

def save_journal(self, ical, no_overwrite=False, no_create=False):
    """
    Save a journal entry to this calendar.
    
    Parameters:
    - ical: str, iCalendar data (VCALENDAR format)
    - no_overwrite: bool, prevent overwriting existing journals
    - no_create: bool, prevent creating new journals
    
    Returns:
    Journal: Saved Journal object
    """

# Generic object creation
def save_object(self, objclass, ical=None, no_overwrite=False, no_create=False, **ical_data):
    """
    Save calendar object of any type.
    
    Parameters:
    - objclass: class, calendar object class (Event, Todo, Journal, FreeBusy)
    - ical: str, iCalendar data (alternative to **ical_data)
    - no_overwrite: bool, prevent overwriting existing objects
    - no_create: bool, prevent creating new objects
    - **ical_data: additional iCalendar properties
    
    Returns:
    CalendarObjectResource: Saved object
    """

def save_with_invites(self, ical, attendees, **attendeeoptions):
    """
    Save calendar object and send invitations to attendees.
    
    Parameters:
    - ical: str, iCalendar data
    - attendees: list[str], attendee email addresses
    - **attendeeoptions: additional attendee options
    
    Returns:
    CalendarObjectResource: Saved object with invitations sent
    """

# Convenient aliases
def add_event(self, ical, no_overwrite=False, no_create=False):
    """Alias for save_event."""

def add_todo(self, ical, no_overwrite=False, no_create=False):
    """Alias for save_todo."""

def add_journal(self, ical, no_overwrite=False, no_create=False):
    """Alias for save_journal."""

Usage Examples:

# Create a new event
event_ical = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//My App//My App//EN
BEGIN:VEVENT
UID:meeting-001@example.com
DTSTART:20250915T140000Z
DTEND:20250915T150000Z
SUMMARY:Team Meeting
DESCRIPTION:Weekly team sync meeting
LOCATION:Conference Room A
END:VEVENT
END:VCALENDAR"""

# Save the event
event = calendar.save_event(event_ical)
print(f"Created event with UID: {event.id}")

# Create a todo
todo_ical = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//My App//My App//EN
BEGIN:VTODO
UID:task-001@example.com
SUMMARY:Complete project documentation
DESCRIPTION:Finish writing the API documentation
DUE:20250920T170000Z
PRIORITY:1
STATUS:NEEDS-ACTION
END:VTODO
END:VCALENDAR"""

# Save with no overwrite to prevent accidents
todo = calendar.save_todo(todo_ical, no_overwrite=True)

# Create journal entry
journal_ical = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//My App//My App//EN
BEGIN:VJOURNAL
UID:journal-001@example.com
DTSTART:20250915T090000Z
SUMMARY:Daily standup notes
DESCRIPTION:Discussed sprint progress and blockers
END:VJOURNAL
END:VCALENDAR"""

journal = calendar.add_journal(journal_ical)

Object Lookup

Find specific calendar objects by URL or UID for direct access and manipulation.

def event_by_url(self, href):
    """
    Get event by its URL/href.
    
    Parameters:
    - href: str, event URL or href
    
    Returns:
    Event: Event object or None if not found
    """

def event_by_uid(self, uid):
    """
    Get event by its UID.
    
    Parameters:
    - uid: str, event UID
    
    Returns:
    Event: Event object or None if not found
    """

def todo_by_uid(self, uid):
    """
    Get todo by its UID.
    
    Parameters:
    - uid: str, todo UID
    
    Returns:
    Todo: Todo object or None if not found
    """

def journal_by_uid(self, uid):
    """
    Get journal entry by its UID.
    
    Parameters:
    - uid: str, journal UID
    
    Returns:
    Journal: Journal object or None if not found
    """

def object_by_uid(self, uid):
    """
    Get any calendar object by its UID.
    
    Parameters:
    - uid: str, object UID
    
    Returns:
    CalendarObjectResource: Object or None if not found
    """

Advanced Search

Comprehensive search functionality with support for XML queries, component filters, property filters, and date range searches.

def search(self, xml_query=None, comp_filter=None, prop_filter=None, 
           text_match=None, start=None, end=None, expand=False, verify_expand=True):
    """
    Advanced search for calendar objects with multiple filter options.
    
    Parameters:
    - xml_query: str, raw CalDAV XML query
    - comp_filter: str, component filter (e.g., "VEVENT", "VTODO")
    - prop_filter: dict, property filters {property: value}
    - text_match: str, text search in summaries and descriptions
    - start: datetime, start of date range
    - end: datetime, end of date range
    - expand: bool, expand recurring events within date range
    - verify_expand: bool, verify expanded events (default True)
    
    Returns:
    list[CalendarObjectResource]: Matching calendar objects
    """

def date_search(self, start, end=None, compfilter=None, expand=False, verify_expand=True):
    """
    Search for calendar objects within a date range.
    
    Parameters:
    - start: datetime, start of search range
    - end: datetime, end of search range (if None, searches from start forward)
    - compfilter: str, component type filter ("VEVENT", "VTODO", "VJOURNAL")
    - expand: bool, expand recurring events within range
    - verify_expand: bool, verify expanded recurring events
    
    Returns:
    list[CalendarObjectResource]: Objects within date range
    """

Usage Examples:

from datetime import datetime, timedelta

# Find events in the next week
start_date = datetime.now()
end_date = start_date + timedelta(days=7)

upcoming_events = calendar.date_search(
    start=start_date,
    end=end_date,
    compfilter="VEVENT",
    expand=True  # Expand recurring events
)

print(f"Found {len(upcoming_events)} upcoming events")
for event in upcoming_events:
    summary = event.icalendar_component.get('SUMMARY', 'No title')
    dtstart = event.icalendar_component.get('DTSTART').dt
    print(f"  {dtstart}: {summary}")

# Search for todos with text matching
urgent_todos = calendar.search(
    comp_filter="VTODO",
    text_match="urgent",
    prop_filter={"STATUS": "NEEDS-ACTION"}
)

# Complex search with XML query
xml_query = """
<C:calendar-query xmlns:C="urn:ietf:params:xml:ns:caldav">
  <D:prop xmlns:D="DAV:">
    <D:getetag/>
    <C:calendar-data/>
  </D:prop>
  <C:filter>
    <C:comp-filter name="VCALENDAR">
      <C:comp-filter name="VEVENT">
        <C:prop-filter name="SUMMARY">
          <C:text-match collation="i;ascii-casemap">meeting</C:text-match>
        </C:prop-filter>
      </C:comp-filter>
    </C:comp-filter>
  </C:filter>
</C:calendar-query>"""

meetings = calendar.search(xml_query=xml_query)

# Look up specific objects
specific_event = calendar.event_by_uid("meeting-001@example.com")
if specific_event:
    print(f"Found event: {specific_event.icalendar_component['SUMMARY']}")

# Find any object type by UID
any_object = calendar.object_by_uid("some-uid@example.com")
if any_object:
    print(f"Object type: {any_object.__class__.__name__}")

Free/Busy Operations

Request and manage free/busy information for scheduling and availability coordination.

def freebusy_request(self, start, end, attendees=None):
    """
    Request free/busy information for a time range.
    
    Parameters:
    - start: datetime, start of time range
    - end: datetime, end of time range
    - attendees: list[str], attendee email addresses (optional)
    
    Returns:
    FreeBusy: Free/busy information object
    """

Calendar Properties

Access and manage calendar-specific properties and capabilities.

@property
def supported_calendar_component_set(self):
    """Get supported calendar component types for this calendar."""

def get_supported_components(self):
    """
    Get the set of supported component types.
    
    Returns:
    set[str]: Set of supported component types (VEVENT, VTODO, etc.)
    """

Usage Examples:

# Check what components this calendar supports
supported = calendar.get_supported_components()
print(f"Calendar supports: {', '.join(supported)}")

# Only create todos if supported
if 'VTODO' in supported:
    todo = calendar.save_todo(todo_ical)
else:
    print("This calendar doesn't support todos")

# Request free/busy for scheduling
from datetime import datetime, timedelta

meeting_start = datetime(2025, 9, 20, 14, 0)  # 2 PM
meeting_end = meeting_start + timedelta(hours=1)

freebusy_info = calendar.freebusy_request(
    start=meeting_start,
    end=meeting_end,
    attendees=["attendee1@example.com", "attendee2@example.com"]
)

# Check availability (implementation depends on server response format)
if freebusy_info:
    print("Free/busy information retrieved successfully")

Search Filter Examples

# Common component filters
COMPONENT_FILTERS = {
    "events": "VEVENT",
    "todos": "VTODO", 
    "tasks": "VTODO",  # alias
    "journals": "VJOURNAL",
    "freebusy": "VFREEBUSY"
}

# Common property filters
PROPERTY_FILTERS = {
    # Todo status filters
    "pending_todos": {"STATUS": "NEEDS-ACTION"},
    "completed_todos": {"STATUS": "COMPLETED"},
    "in_progress_todos": {"STATUS": "IN-PROCESS"},
    
    # Event status filters  
    "confirmed_events": {"STATUS": "CONFIRMED"},
    "tentative_events": {"STATUS": "TENTATIVE"},
    "cancelled_events": {"STATUS": "CANCELLED"},
    
    # Priority filters
    "high_priority": {"PRIORITY": "1"},
    "medium_priority": {"PRIORITY": "5"},
    "low_priority": {"PRIORITY": "9"}
}

Install with Tessl CLI

npx tessl i tessl/pypi-caldav

docs

calendar-management.md

client-auth.md

event-operations.md

index.md

journal-freebusy.md

principal-calendar.md

task-management.md

tile.json