How to Import and Export Todos Using the iCalendar Service in Lifetrace
The iCalendar service converts internal Todo dictionaries into VTODO or VEVENT components for export and parses .ics strings into TodoCreate objects for import, handling UTC datetime normalization, status mapping, and recurrence rules through specialized helper methods.
The iCalendar service (lifetrace/services/icalendar_service.py) serves as the primary bridge between Lifetrace's internal task representation and standard iCalendar (ICS) formats used by external calendar applications. This implementation enables bidirectional data flow, allowing users to export their todo lists for use in third-party calendars and import tasks from existing .ics files. The service relies on a serialization layer in lifetrace/storage/todo_manager_ical.py to convert SQLAlchemy models into plain dictionaries before processing.
Exporting Todos to iCalendar Format
The export process transforms a list of Todo dictionaries into a valid iCalendar string through a multi-step pipeline that supports both task and event representations.
Building the Calendar Container
Every export begins with _build_calendar() (lines 55-60), which instantiates a base Calendar object populated with standard PRODID, version, and calendar scale fields. This container serves as the root element that will eventually hold all converted todo components.
Converting Todos to VTODO or VEVENT
The export_todos method (lines 97-106) iterates over the supplied dictionary list and determines component type based on the item_type field. For each entry, it dispatches to either _todo_to_vtodo (lines 67-106) or _todo_to_vevent (lines 109-143) depending on whether the item represents a task or a calendar event.
These conversion helpers selectively add optional fields—including summary, description, dates, status, priority, categories, and recurrence rules—only when the source dictionary contains non-null values. This approach ensures the resulting .ics file remains clean and free of empty properties.
Serializing to .ics String
After all components are added via cal.add_component(), the service calls cal.to_ical().decode("utf-8") to produce a UTF-8 encoded string suitable for file storage or HTTP transmission.
Importing Todos from iCalendar Files
The import workflow reverses the process, parsing raw iCalendar data into structured TodoCreate objects ready for database insertion.
Parsing the .ics Content
The import_todos method (lines 11-18) begins by invoking Calendar.from_ical(ics_content) to build an icalendar.Calendar object from the raw string. It then walks through all sub-components using cal.walk(), filtering specifically for VTODO and VEVENT types while ignoring other calendar elements.
Extracting and Normalizing Fields
For each valid component, the service extracts mandatory and optional fields:
- Summary: Required field used as the todo name
- UID and Description: Unique identifiers and detailed notes
- Date/Time fields:
dtstart,dtend,due, andcompletedare converted to UTC via_from_ical_dt(lines 29-33)
Mapping Status, Priority, and Categories
The service applies several normalization helpers to ensure data integrity:
- Percent complete:
_normalize_percent(lines 25-32) clamps values to the 0-100 range - Status mapping:
_status_from_ical(lines 45-52) converts iCalendar status strings to the internalTodoStatusenum, defaulting toCOMPLETEDwhen percent-complete equals 100 - Priority conversion:
_priority_from_ical(lines 52-66) maps numeric iCalendar priority values to the internalTodoPriorityenum - Categories: The code normalizes the component's
categoriesproperty—whether presented as a list, tuple, set, oricalendar.prop.vCategoryobject—into a standardized list of strings (lines 55-63) - Recurrence rules: The raw
rrulevalue is converted to its string representation, with the original value preserved if parsing fails (lines 65-71)
All extracted values are packaged into TodoCreate schema instances (lines 73-85) and returned as a list for bulk insertion via the Todo manager.
Code Example: Complete Import and Export Workflow
from lifetrace.services.icalendar_service import ICalendarService
from lifetrace.schemas.todo import TodoCreate
# Export a set of Todo dicts (as produced by TodoIcalMixin._todo_to_dict)
todos = [
{
"uid": "12345",
"name": "Buy groceries",
"summary": "Buy groceries",
"description": "Milk, Eggs, Bread",
"item_type": "VTODO",
"dtstart": None,
"due": "2026-03-10T12:00:00Z",
"status": "active",
"priority": "high",
},
]
ics_str = ICalendarService().export_todos(todos)
with open("mytodos.ics", "w", encoding="utf-8") as f:
f.write(ics_str)
# Import an .ics file back into TodoCreate objects
with open("mytodos.ics", "r", encoding="utf-8") as f:
raw_ics = f.read()
imported_todos: list[TodoCreate] = ICalendarService().import_todos(raw_ics)
# imported_todos can now be passed to the Todo manager for creation
Key Implementation Files
| File | Role | Key Components |
|---|---|---|
lifetrace/services/icalendar_service.py |
Core import/export logic, conversion helpers, status/priority mapping | export_todos (L97-L106), import_todos (L11-L18), _todo_to_vtodo (L67-L106), _todo_to_vevent (L109-L143) |
lifetrace/storage/todo_manager_ical.py |
Serializes SQLAlchemy Todo models to dictionaries consumable by the service | _todo_to_dict (L54-L122) |
lifetrace/schemas/todo.py |
Pydantic schema for newly imported todos | TodoCreate class definition |
lifetrace/util/time_utils.py |
UTC conversion utilities | ensure_utc, naive_as_utc, to_local |
lifetrace/util/logging_config.py |
Logging infrastructure | get_logger (line 13) |
Summary
- The iCalendar service provides bidirectional conversion between Lifetrace's internal Todo representation and standard ICS format through
export_todosandimport_todosmethods. - Export supports both
VTODOandVEVENTcomponents, selectively including only non-null fields to generate clean .ics output. - Import filters for VTODO and VEVENT components, normalizing dates to UTC, clamping percent-complete values, mapping status and priority enums, and handling complex category structures.
- The service depends on
TodoIcalMixinfor model serialization and returnsTodoCreateobjects ready for immediate database insertion. - All datetime handling utilizes centralized utilities in
lifetrace/util/time_utils.pyto ensure consistent timezone management.
Frequently Asked Questions
What iCalendar components does Lifetrace support for todo import and export?
The service specifically handles VTODO and VEVENT components. During export, the item_type field in the Todo dictionary determines which component type is generated. During import, the cal.walk() method filters out all other component types, ensuring only tasks and events are processed while ignoring calendar metadata like timezones or daylight saving rules.
How does the iCalendar service handle timezone conversion during import?
All datetime fields—including dtstart, dtend, due, and completed—are converted to UTC using the _from_ical_dt helper method (lines 29-33). This ensures consistent storage regardless of the timezone specified in the original .ics file. The service relies on utilities in lifetrace/util/time_utils.py to handle naive datetimes and timezone-aware conversions safely.
Can the service distinguish between calendar events and tasks during the export process?
Yes. The export_todos method checks the item_type field in each Todo dictionary to determine the appropriate component type. If item_type indicates a task, it calls _todo_to_vtodo (lines 67-106) to generate a VTODO component. For calendar events, it invokes _todo_to_vevent (lines 109-143) to create a VEVENT component, allowing the same export pipeline to handle both appointment-style events and task-style todos.
What happens to recurrence rules when importing todos from iCalendar files?
The service extracts the raw rrule property from each component and attempts to convert it to its string representation. If the recurrence rule parsing fails, the original value is preserved rather than discarded (lines 65-71). This ensures that complex recurrence patterns are maintained for storage and can be interpreted correctly when the todo is later exported or displayed in the Lifetrace interface.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →