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

journal-freebusy.mddocs/

Journal & FreeBusy

Journal entry management and free/busy scheduling functionality for calendar coordination, availability management, and personal journaling within CalDAV systems.

Capabilities

Journal Class

Represents CalDAV journal entries (VJOURNAL components) for personal notes, meeting minutes, and diary-style calendar entries.

class Journal(CalendarObjectResource):
    """
    Journal entry class for VJOURNAL components.
    
    Inherits all methods from CalendarObjectResource including:
    - load(), save(), delete()
    - copy(), move() 
    - change_uid()
    - expand() (for recurring journals)
    """

Usage Examples:

import caldav
from datetime import datetime, timezone
import uuid

# Get existing journals
calendar = principal.calendars()[0]
journals = calendar.journals()

if journals:
    journal = journals[0]
    
    # Get journal information
    component = journal.icalendar_component
    summary = component.get('SUMMARY', 'No title')
    description = component.get('DESCRIPTION', 'No content')
    dtstart = component.get('DTSTART')
    
    print(f"Journal: {summary}")
    print(f"Date: {dtstart}")
    print(f"Content: {description}")
    
    # Modify journal
    component['SUMMARY'] = 'Updated Journal Entry'
    component['DESCRIPTION'] = 'Updated content with new insights'
    journal.save()
    print("Journal updated successfully")

Journal Creation

Create new journal entries with comprehensive VJOURNAL support for documentation, meeting notes, and personal journaling.

# Journal creation through calendar.save_journal() - see calendar-management.md
# Additional journal-specific creation patterns:

def create_simple_journal(summary, content, date=None):
    """
    Helper function to create simple journal iCalendar data.
    
    Parameters:
    - summary: str, journal entry title
    - content: str, journal entry content/description
    - date: datetime, entry date (default: now)
    
    Returns:
    str: iCalendar data for the journal entry
    """

Usage Examples:

# Create a meeting minutes journal entry
def create_meeting_minutes(meeting_title, attendees, notes, meeting_date=None):
    if meeting_date is None:
        meeting_date = datetime.now(timezone.utc)
    
    journal_uid = f"{uuid.uuid4()}@example.com"
    
    attendee_list = ", ".join(attendees) if attendees else "No attendees recorded"
    full_content = f"""Meeting Attendees: {attendee_list}

Meeting Notes:
{notes}"""
    
    ical_data = f"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//My Journal App//My Journal App//EN
BEGIN:VJOURNAL
UID:{journal_uid}
DTSTART:{meeting_date.strftime('%Y%m%dT%H%M%SZ')}
SUMMARY:{meeting_title} - Meeting Minutes
DESCRIPTION:{full_content}
CATEGORIES:meeting-minutes
DTSTAMP:{datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ')}
END:VJOURNAL
END:VCALENDAR"""
    
    return ical_data

# Create and save meeting minutes
meeting_date = datetime(2025, 9, 15, 14, 0, tzinfo=timezone.utc)
minutes_ical = create_meeting_minutes(
    meeting_title="Weekly Team Sync",
    attendees=["alice@example.com", "bob@example.com", "charlie@example.com"],
    notes="""
    - Reviewed sprint progress: 80% complete
    - Discussed upcoming deadlines for Q4
    - Alice raised concerns about resource allocation
    - Action items:
      * Bob to follow up on server migration
      * Charlie to update documentation
      * Schedule architecture review for next week
    """,
    meeting_date=meeting_date
)

journal = calendar.save_journal(minutes_ical)
print(f"Created meeting minutes: {journal.id}")

# Create personal journal entry
personal_ical = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//My App//My App//EN
BEGIN:VJOURNAL
UID:daily-reflection-20250915@example.com
DTSTART:20250915T200000Z
SUMMARY:Daily Reflection - September 15, 2025
DESCRIPTION:Today was productive. Made good progress on the CalDAV integration project. The documentation is coming along well and the team is aligned on the technical approach. Tomorrow I'll focus on testing the authentication flows.
CATEGORIES:personal,reflection
END:VJOURNAL
END:VCALENDAR"""

personal_journal = calendar.save_journal(personal_ical)

# Create project log entry
project_log_ical = """BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//My App//My App//EN
BEGIN:VJOURNAL
UID:project-log-001@example.com
DTSTART:20250915T170000Z
SUMMARY:CalDAV Integration - Development Log
DESCRIPTION:Completed implementation of event creation and modification. All unit tests passing. Next steps: implement recurring event support and add error handling for edge cases. Estimated completion: end of week.
CATEGORIES:project-log,development
END:VJOURNAL
END:VCALENDAR"""

project_journal = calendar.save_journal(project_log_ical)

Journal Queries

Search and filter journal entries by date, category, content, and other properties.

# Journal search patterns using calendar.search() - see calendar-management.md

def find_journals_by_category(calendar, category):
    """Find journal entries with specific category."""
    journals = calendar.search(comp_filter="VJOURNAL")
    matching = []
    
    for journal in journals:
        categories = journal.icalendar_component.get('CATEGORIES', [])
        if isinstance(categories, str):
            categories = [categories]
        if category.lower() in [cat.lower() for cat in categories]:
            matching.append(journal)
    
    return matching

def find_journals_by_date_range(calendar, start_date, end_date):
    """Find journal entries within date range."""
    return calendar.date_search(
        start=start_date,
        end=end_date,
        compfilter="VJOURNAL"
    )

def find_journals_by_text(calendar, search_text):
    """Find journal entries containing specific text."""
    return calendar.search(
        comp_filter="VJOURNAL",
        text_match=search_text
    )

Usage Examples:

from datetime import datetime, timedelta

# Find meeting minutes from last week
last_week = datetime.now() - timedelta(days=7)
this_week = datetime.now()

recent_journals = find_journals_by_date_range(calendar, last_week, this_week)
print(f"Journal entries from last week: {len(recent_journals)}")

# Find all meeting minutes
meeting_journals = find_journals_by_category(calendar, "meeting-minutes")
print(f"Meeting minutes entries: {len(meeting_journals)}")

# Search for specific project mentions
project_journals = find_journals_by_text(calendar, "CalDAV integration")
print(f"Project-related journals: {len(project_journals)}")

# List all journal categories
all_journals = calendar.journals()
categories = set()
for journal in all_journals:
    journal_categories = journal.icalendar_component.get('CATEGORIES', [])
    if isinstance(journal_categories, str):
        journal_categories = [journal_categories]
    categories.update(journal_categories)

print(f"Journal categories: {', '.join(sorted(categories))}")

FreeBusy Class

Represents CalDAV free/busy objects (VFREEBUSY components) for availability coordination and scheduling support.

class FreeBusy(CalendarObjectResource):
    """
    Free/busy information class for VFREEBUSY components.
    
    Inherits all methods from CalendarObjectResource including:
    - load(), save(), delete()
    - copy(), move()
    - change_uid()
    """

Usage Examples:

# Get existing free/busy objects
freebusy_objects = calendar.freebusy()

if freebusy_objects:
    fb = freebusy_objects[0]
    
    # Get free/busy information
    component = fb.icalendar_component
    dtstart = component.get('DTSTART')
    dtend = component.get('DTEND')
    organizer = component.get('ORGANIZER')
    
    print(f"Free/busy period: {dtstart} to {dtend}")
    print(f"Organizer: {organizer}")
    
    # Get free/busy periods
    freebusy_periods = component.get('FREEBUSY', [])
    if not isinstance(freebusy_periods, list):
        freebusy_periods = [freebusy_periods]
    
    for period in freebusy_periods:
        print(f"Busy period: {period}")

Free/Busy Requests

Request free/busy information for scheduling coordination and meeting planning.

# Free/busy requests are typically made through Principal or Calendar objects
# See principal-calendar.md and calendar-management.md for details

def request_freebusy_info(principal, start_time, end_time, attendees):
    """
    Request free/busy information for attendees.
    
    Parameters:
    - principal: Principal object
    - start_time: datetime, start of time range
    - end_time: datetime, end of time range
    - attendees: list[str], attendee email addresses
    
    Returns:
    FreeBusy: Free/busy information object
    """
    return principal.freebusy_request(start_time, end_time, attendees)

Usage Examples:

from datetime import datetime, timedelta, timezone

# Request free/busy information for meeting planning
meeting_start = datetime(2025, 9, 20, 14, 0, tzinfo=timezone.utc)  # 2 PM UTC
meeting_end = meeting_start + timedelta(hours=2)  # 2 hour meeting

attendees = [
    "alice@example.com",
    "bob@example.com", 
    "charlie@example.com"
]

# Request through principal
freebusy_info = principal.freebusy_request(
    start=meeting_start,
    end=meeting_end,
    attendees=attendees
)

if freebusy_info:
    print("Free/busy information retrieved")
    
    # Analyze the response (format depends on server implementation)
    component = freebusy_info.icalendar_component
    organizer = component.get('ORGANIZER', 'Unknown')
    
    # Check for busy periods
    busy_periods = component.get('FREEBUSY', [])
    if not isinstance(busy_periods, list):
        busy_periods = [busy_periods]
    
    print(f"Found {len(busy_periods)} busy periods")
    for i, period in enumerate(busy_periods):
        print(f"  Busy period {i+1}: {period}")

# Request through calendar
calendar_freebusy = calendar.freebusy_request(
    start=meeting_start,
    end=meeting_end,
    attendees=attendees
)

Free/Busy Creation

Create free/busy objects for publishing availability information.

def create_freebusy_object(start_time, end_time, busy_periods=None, organizer=None):
    """
    Helper function to create free/busy iCalendar data.
    
    Parameters:
    - start_time: datetime, start of free/busy period
    - end_time: datetime, end of free/busy period
    - busy_periods: list[tuple], list of (start, end) busy time tuples
    - organizer: str, organizer email address
    
    Returns:
    str: iCalendar data for the free/busy object
    """

Usage Examples:

# Create a free/busy object manually
def create_weekly_freebusy(week_start, busy_times, organizer_email):
    week_end = week_start + timedelta(days=7)
    fb_uid = f"freebusy-{week_start.strftime('%Y%m%d')}@example.com"
    
    ical_data = f"""BEGIN:VCALENDAR
VERSION:2.0
PRODID:-//My App//My App//EN
BEGIN:VFREEBUSY
UID:{fb_uid}
DTSTART:{week_start.strftime('%Y%m%dT%H%M%SZ')}
DTEND:{week_end.strftime('%Y%m%dT%H%M%SZ')}
ORGANIZER:mailto:{organizer_email}
DTSTAMP:{datetime.now(timezone.utc).strftime('%Y%m%dT%H%M%SZ')}
"""
    
    # Add busy periods
    for start, end in busy_times:
        ical_data += f"FREEBUSY;FBTYPE=BUSY:{start.strftime('%Y%m%dT%H%M%SZ')}/{end.strftime('%Y%m%dT%H%M%SZ')}\n"
    
    ical_data += """END:VFREEBUSY
END:VCALENDAR"""
    
    return ical_data

# Create free/busy for current week
week_start = datetime.now(timezone.utc).replace(hour=0, minute=0, second=0, microsecond=0)
# Subtract days to get to Monday
week_start -= timedelta(days=week_start.weekday())

busy_times = [
    (week_start + timedelta(days=0, hours=9), week_start + timedelta(days=0, hours=17)),   # Monday 9-5
    (week_start + timedelta(days=1, hours=9), week_start + timedelta(days=1, hours=17)),   # Tuesday 9-5
    (week_start + timedelta(days=2, hours=9), week_start + timedelta(days=2, hours=12)),   # Wednesday 9-12
    (week_start + timedelta(days=3, hours=14), week_start + timedelta(days=3, hours=17)),  # Thursday 2-5
    (week_start + timedelta(days=4, hours=9), week_start + timedelta(days=4, hours=15)),   # Friday 9-3
]

freebusy_ical = create_weekly_freebusy(
    week_start=week_start,
    busy_times=busy_times,
    organizer_email="user@example.com"
)

# Note: Free/busy objects are typically created by the server automatically
# Manual creation is mainly for testing or special use cases
print("Free/busy object created (for testing purposes)")

Journal Properties

# Common journal properties that can be accessed via icalendar_component
JOURNAL_PROPERTIES = {
    "SUMMARY": "str",           # Journal entry title
    "DESCRIPTION": "str",       # Journal entry content
    "DTSTART": "datetime",      # Entry date/time
    "CATEGORIES": "list[str]",  # Entry categories/tags
    "CLASS": "str",             # PUBLIC, PRIVATE, CONFIDENTIAL
    "STATUS": "str",            # DRAFT, FINAL, CANCELLED
    "ORGANIZER": "vCalAddress", # Entry creator
    "ATTENDEE": "list[vCalAddress]", # Associated attendees (for meeting minutes)
}

# Common journal categories
JOURNAL_CATEGORIES = [
    "meeting-minutes",
    "personal", 
    "reflection",
    "project-log",
    "development",
    "notes",
    "diary",
    "daily-standup",
    "retrospective"
]

FreeBusy Properties

# Common free/busy properties that can be accessed via icalendar_component  
FREEBUSY_PROPERTIES = {
    "DTSTART": "datetime",          # Start of free/busy period
    "DTEND": "datetime",            # End of free/busy period
    "ORGANIZER": "vCalAddress",     # Free/busy publisher
    "ATTENDEE": "list[vCalAddress]", # Attendees the free/busy applies to
    "FREEBUSY": "list[period]",     # Free/busy time periods
    "URL": "str",                   # URL for free/busy information
}

# Free/busy types (FBTYPE parameter)
FREEBUSY_TYPES = {
    "FREE": "Time is free",
    "BUSY": "Time is busy (default)",
    "BUSY-UNAVAILABLE": "Time is busy and unavailable",
    "BUSY-TENTATIVE": "Time is tentatively busy"
}

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