diff --git a/README.md b/README.md index 7dd7ad9..6ad7333 100644 --- a/README.md +++ b/README.md @@ -4,31 +4,37 @@ caldav2google

-A Python utility to synchronize events from a CalDAV calendar to Google Calendar, maintaining a local state to track changes. +A Python utility to synchronize events from a CalDAV calendar to Google Calendar, maintaining a local state to track changes. ## Features - One-way synchronization from CalDAV to Google Calendar -- Intelligent change detection: - - Adds new events - - Updates modified events - - Removes deleted events -- Local state management to track synchronized events +- Intelligent change detection using event UIDs and modification timestamps: + - Adds new events not present in Google Calendar + - Updates modified events based on last-modified timestamp + - Removes events deleted from CalDAV source +- Local state management via JSON file to track synchronized events - Detailed logging of synchronization activities -- Rate limiting to prevent API throttling -- Error handling with failed events tracking +- Built-in rate limiting (0.5s delay between API calls) to prevent Google Calendar API throttling +- Comprehensive error tracking with failed events reporting +- UTC timezone handling for consistent event timing ## Prerequisites ### 1. Python Environment -- Python 3.10 or higher +- Python 3.9 or higher - Poetry for dependency management +- Required Python packages (automatically installed via Poetry): + - `caldav` (^1.4.0) - For CalDAV server interaction + - `icalendar` (^6.1.0) - For iCalendar format parsing + - `google-api-python-client` (^2.154.0) - For Google Calendar API + - `python-dotenv` (^1.0.1) - For environment variable management ### 2. CalDAV Server Details You'll need: - CalDAV server URL - Username - Password -- Calendar name to synchronize +- Calendar name to synchronize (case-insensitive matching) ### 3. Google Calendar Setup @@ -58,7 +64,7 @@ To interact with the Google Calendar API, follow these steps: poetry install ``` -3. Create `.env` file: +3. Create `.env` file (copy from env.example): ```env CALDAV_URL=https://your-caldav-server.com CALDAV_USERNAME=your_username @@ -82,23 +88,23 @@ To interact with the Google Calendar API, follow these steps: On first run: - Browser opens for Google OAuth authentication - Grant requested calendar permissions -- Token is saved locally for future use +- Token is saved as `token.pickle` for future use ## Project Structure ``` src/ -├── auth_google.py # Google Calendar authentication -├── caldav_client.py # CalDAV server interactions -├── main.py # Main synchronization script -├── sync_logic.py # Core synchronization logic -pyproject.toml # Project configuration -env.example # Environment variables example file -.env # Environment variables -README.md # README instructions -credentials.json # Google OAuth credentials -token.pickle # Stored Google token -calendar_sync.json # Local sync state +├── auth_google.py # Google Calendar authentication & calendar search +├── caldav_client.py # CalDAV server connections & event fetching +├── main.py # Main synchronization orchestration +├── sync_logic.py # Core synchronization & event comparison logic +pyproject.toml # Poetry project & tool configuration +env.example # Environment variables template +.env # Active environment variables +README.md # Project documentation +credentials.json # Google OAuth credentials +token.pickle # Stored Google authentication token +calendar_sync.json # Local synchronization state ``` ## Configuration Files @@ -116,15 +122,21 @@ GOOGLE_CALENDAR_NAME=CalDAV Events ``` ### calendar_sync.json -Automatically maintained by the script to track synchronization state. +Automatically maintained JSON file tracking: +- Event UIDs +- Event summaries +- Start and end times +- Last modification timestamps +- Google Calendar event IDs ## Error Handling The script provides robust error handling: -- Failed events are tracked and reported -- Rate limiting prevents API throttling -- Detailed error messages for troubleshooting -- Synchronization state preserved on failure +- Failed events are tracked in memory during sync +- Detailed error messages for both CalDAV and Google Calendar operations +- Rate limiting prevents Google Calendar API throttling +- Synchronization state preserved even on partial failures +- Automatic token refresh for expired Google credentials ## Troubleshooting @@ -134,27 +146,31 @@ The script provides robust error handling: - Error: `Invalid client credentials` - Verify `credentials.json` is correctly downloaded and placed - Ensure OAuth consent screen is configured + - Check that Calendar API is enabled in Google Cloud Console - Error: `Token has been expired or revoked` - Delete `token.pickle` - - Re-run script to authenticate + - Re-run script to trigger new authentication flow #### CalDAV - Error: `Could not connect to server` - Check URL format and accessibility - Verify network connectivity - Confirm server SSL certificate if using HTTPS + - Validate username and password ### Synchronization Issues - Error: `Calendar not found` - - Verify calendar names in `.env` + - Verify calendar names in `.env` (case-insensitive matching supported) - Check calendar visibility/permissions + - Ensure calendar exists on both servers - Error: `Failed to add/update events` - - Check event data formatting + - Check event data formatting (especially date/time formats) - Verify calendar write permissions - Review API quotas and limits + - Check for required event fields ## Development @@ -172,10 +188,11 @@ poetry run pre-commit install 5. Submit pull request Please: -- Follow existing code style +- Follow existing code style (enforced by Ruff) - Add tests for new features - Update documentation - Run pre-commit hooks +- Maintain type hints and docstrings ## License @@ -188,3 +205,4 @@ Built with: - [google-api-python-client](https://github.com/googleapis/google-api-python-client) - Google Calendar API - [python-dotenv](https://pypi.org/project/python-dotenv/) - Environment management - [Poetry](https://python-poetry.org/) - Dependency management +- [Ruff](https://github.com/astral-sh/ruff) - Python linter