Skip to content
Blog Β» Creating a Google Calendar sync with Claude code

Creating a Google Calendar sync with Claude code

After iterating with Claude.ai I opted for executing this generated Claude Code instruction to see if a google calendar sync could be built. This prompting session took about an hour. The Claude code is running now. I did have to pay for $100 Claude code premium seat so this will be evaluated to see if it is worthwhile.

This solution is targeted to reduce a projected $120 / yr subscription cost for something like reclaim.ai

🎯 Phase 1: Local Development First

  • JSON-based configuration – easy to share and backup
  • Docker Compose for simple setup
  • File-based storage – no database complexity
  • Portable configs between team members
  • Git repo for version control and collaboration

πŸ“ Project Structure

calendar-sync/
β”œβ”€β”€ docker-compose.yml           # Complete local environment
β”œβ”€β”€ Dockerfile                   # App container
β”œβ”€β”€ requirements.txt             # Python dependencies
β”œβ”€β”€ README.md                   # Setup instructions
β”œβ”€β”€ .env.example                # Environment template
β”œβ”€β”€ .gitignore                  # Ignore secrets and data
β”‚
β”œβ”€β”€ app/
β”‚   β”œβ”€β”€ main.py                 # FastAPI web app
β”‚   β”œβ”€β”€ sync_engine.py          # Core sync logic
β”‚   β”œβ”€β”€ scheduler.py            # Background sync scheduler
β”‚   β”œβ”€β”€ google_auth.py          # Google OAuth handling
β”‚   └── models.py              # Data models
β”‚
β”œβ”€β”€ config/
β”‚   β”œβ”€β”€ user_config.json       # User's calendar connections
β”‚   β”œβ”€β”€ sync_rules.json        # Sync rule definitions
β”‚   └── settings.json          # App settings
β”‚
β”œβ”€β”€ data/
β”‚   β”œβ”€β”€ credentials/            # Google OAuth tokens (gitignored)
β”‚   β”œβ”€β”€ logs/                   # Sync operation logs
β”‚   └── sync_history.json      # Record of past syncs
β”‚
└── web/
    β”œβ”€β”€ index.html             # Simple web interface
    β”œβ”€β”€ style.css              # Basic styling
    └── app.js                 # Frontend JavaScript

πŸ”§ Configuration Files (Portable JSON)

config/settings.json – App Settings

{
  "app": {
    "name": "Calendar Sync",
    "version": "1.0.0",
    "host": "0.0.0.0",
    "port": 8000
  },
  "sync": {
    "interval_minutes": 30,
    "window_days_ahead": 30,
    "window_days_behind": 7,
    "max_retries": 3,
    "timeout_seconds": 300
  },
  "google_api": {
    "scopes": [
      "https://www.googleapis.com/auth/calendar"
    ],
    "credentials_file": "data/credentials/google_credentials.json"
  },
  "logging": {
    "level": "INFO",
    "file": "data/logs/calendar-sync.log",
    "max_size_mb": 10,
    "backup_count": 5
  }
}

config/user_config.json – User’s Connected Calendars

{
  "user_id": "john_doe",
  "google_accounts": [
    {
      "email": "john@gmail.com",
      "nickname": "Personal",
      "calendars": [
        {
          "id": "john@gmail.com",
          "name": "Personal Calendar",
          "access": "read_write",
          "color": "#4285F4"
        }
      ],
      "credentials_file": "data/credentials/john_personal.json"
    },
    {
      "email": "john@company.com", 
      "nickname": "Work",
      "calendars": [
        {
          "id": "john@company.com",
          "name": "Work Calendar",
          "access": "read_write",
          "color": "#34A853"
        }
      ],
      "credentials_file": "data/credentials/john_work.json"
    }
  ],
  "created_at": "2024-11-17T10:00:00Z",
  "last_updated": "2024-11-17T10:00:00Z"
}

config/sync_rules.json – Sync Rules Configuration

{
  "sync_rules": [
    {
      "id": "personal_to_work",
      "name": "Personal β†’ Work",
      "enabled": true,
      "source": {
        "calendar_id": "john@gmail.com",
        "account_email": "john@gmail.com"
      },
      "targets": [
        {
          "calendar_id": "john@company.com",
          "account_email": "john@company.com"
        }
      ],
      "sync_event_template": {
        "title": "Busy (Personal)",
        "description": "Auto-synced from personal calendar",
        "visibility": "private",
        "show_as": "busy"
      },
      "filters": {
        "exclude_all_day": true,
        "exclude_declined": true,
        "exclude_tentative": false,
        "exclude_keywords": ["test", "maybe"],
        "only_show_as_busy": true
      }
    },
    {
      "id": "work_to_personal", 
      "name": "Work β†’ Personal",
      "enabled": true,
      "source": {
        "calendar_id": "john@company.com",
        "account_email": "john@company.com"
      },
      "targets": [
        {
          "calendar_id": "john@gmail.com", 
          "account_email": "john@gmail.com"
        }
      ],
      "sync_event_template": {
        "title": "Busy (Work)",
        "description": "Work commitment",
        "visibility": "private",
        "show_as": "busy"
      },
      "filters": {
        "exclude_all_day": true,
        "exclude_declined": true,
        "exclude_tentative": true,
        "exclude_keywords": ["lunch", "optional"],
        "only_show_as_busy": true
      }
    }
  ],
  "global_settings": {
    "sync_marker": "[AUTO-SYNC]",
    "conflict_resolution": "skip",
    "cleanup_old_events": true,
    "max_events_per_sync": 100
  }
}

data/sync_history.json – Operation History

{
  "sync_operations": [
    {
      "id": "sync_20241117_100000",
      "timestamp": "2024-11-17T10:00:00Z",
      "duration_seconds": 12.5,
      "rules_processed": [
        {
          "rule_id": "personal_to_work",
          "events_read": 15,
          "events_created": 8,
          "events_updated": 2,
          "events_skipped": 5,
          "errors": []
        }
      ],
      "total_events_processed": 15,
      "total_events_synced": 10,
      "status": "success"
    }
  ],
  "last_sync": "2024-11-17T10:00:00Z",
  "total_syncs": 47,
  "success_rate": 0.98
}

🐳 Docker Compose Setup

docker-compose.yml

version: '3.8'

services:
  calendar-sync:
    build: .
    container_name: calendar-sync-app
    ports:
      - "8000:8000"
    volumes:
      # Config files (editable)
      - ./config:/app/config
      # Data directory (credentials, logs, history)
      - ./data:/app/data
      # Web assets
      - ./web:/app/web
    environment:
      - PYTHONPATH=/app
      - CONFIG_DIR=/app/config
      - DATA_DIR=/app/data
    restart: unless-stopped
    command: >
      sh -c "
        python -m app.main &
        python -m app.scheduler
      "

  # Optional: Simple file browser for config management
  config-manager:
    image: filebrowser/filebrowser
    container_name: calendar-sync-files
    ports:
      - "8001:80"
    volumes:
      - ./config:/srv/config
      - ./data/logs:/srv/logs:ro
    environment:
      - FB_BASEURL=/files
    profiles: ["tools"]  # Optional service

volumes:
  calendar_data:
    driver: local

Dockerfile

FROM python:3.11-slim

WORKDIR /app

# Install dependencies
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt

# Copy application code
COPY app/ ./app/
COPY web/ ./web/

# Create directories for config and data
RUN mkdir -p /app/config /app/data/credentials /app/data/logs

# Set proper permissions
RUN chmod +x /app

EXPOSE 8000

CMD ["python", "-m", "app.main"]

πŸ“‹ Quick Start Instructions

1. Initial Setup (5 minutes)

# Clone the repository
git clone https://github.com/your-team/calendar-sync.git
cd calendar-sync

# Copy environment template
cp .env.example .env

# Create initial config directories
mkdir -p data/credentials data/logs
mkdir -p config

# Copy example configs
cp config.examples/* config/

2. Google OAuth Setup

# 1. Go to Google Cloud Console
# 2. Create new project or select existing
# 3. Enable Google Calendar API
# 4. Create OAuth 2.0 credentials (Desktop application)
# 5. Download credentials.json to data/credentials/

3. Run Locally

# Start the application
docker-compose up -d

# Check if running
curl http://localhost:8000

# View logs
docker-compose logs -f

# Access web interface
open http://localhost:8000

4. Configuration Setup

# Edit your calendar connections
nano config/user_config.json

# Setup sync rules
nano config/sync_rules.json

# Adjust settings
nano config/settings.json

# Restart to apply changes
docker-compose restart

πŸ”„ Usage Workflow

For Each Team Member:

  1. Clone repo: git clone ...
  2. Setup Google OAuth: Download their credentials
  3. Edit user_config.json: Add their calendar details
  4. Edit sync_rules.json: Configure their sync preferences
  5. Run: docker-compose up -d
  6. Verify: Check web interface at localhost:8000

Config Sharing Between Team:

# Share base configuration (without credentials)
git add config/settings.json
git add config/sync_rules.json.example
git commit -m "Update sync configuration template"

# Each person customizes locally
cp config/sync_rules.json.example config/sync_rules.json
# Edit with personal calendar details

🌐 Simple Web Interface

web/index.html (Simple & Clean)

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Calendar Sync</title>
    <script src="https://cdn.tailwindcss.com"></script>
</head>
<body class="bg-gray-100 min-h-screen py-8">
    <div class="max-w-4xl mx-auto px-4">
        <h1 class="text-3xl font-bold text-gray-900 mb-8">Calendar Sync Dashboard</h1>
        
        <!-- Status Overview -->
        <div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
            <div class="bg-white p-6 rounded-lg shadow">
                <h3 class="text-lg font-semibold text-gray-900 mb-2">Sync Status</h3>
                <div id="sync-status" class="text-sm text-gray-600">Loading...</div>
            </div>
            <div class="bg-white p-6 rounded-lg shadow">
                <h3 class="text-lg font-semibold text-gray-900 mb-2">Connected Calendars</h3>
                <div id="calendar-count" class="text-2xl font-bold text-blue-600">-</div>
            </div>
            <div class="bg-white p-6 rounded-lg shadow">
                <h3 class="text-lg font-semibold text-gray-900 mb-2">Active Sync Rules</h3>
                <div id="rules-count" class="text-2xl font-bold text-green-600">-</div>
            </div>
        </div>

        <!-- Actions -->
        <div class="bg-white p-6 rounded-lg shadow mb-8">
            <h2 class="text-xl font-semibold text-gray-900 mb-4">Actions</h2>
            <div class="space-x-4">
                <button id="manual-sync" class="bg-blue-500 hover:bg-blue-600 text-white px-6 py-2 rounded">
                    πŸ”„ Run Sync Now
                </button>
                <button id="reload-config" class="bg-gray-500 hover:bg-gray-600 text-white px-6 py-2 rounded">
                    βš™οΈ Reload Config
                </button>
            </div>
        </div>

        <!-- Sync History -->
        <div class="bg-white p-6 rounded-lg shadow">
            <h2 class="text-xl font-semibold text-gray-900 mb-4">Recent Sync Operations</h2>
            <div id="sync-history" class="space-y-2">
                <p class="text-gray-600">Loading sync history...</p>
            </div>
        </div>
    </div>

    <script src="app.js"></script>
</body>
</html>

πŸš€ Development Roadmap

Phase 1: Local MVP (This Weekend)

  • βœ… JSON-based configuration system
  • βœ… Docker Compose environment
  • βœ… Basic calendar sync engine
  • βœ… Simple web dashboard
  • βœ… Manual sync trigger

Phase 2: Enhanced Local (Next Weekend)

  • βœ… Automated scheduling (every 30 minutes)
  • βœ… Better error handling and logging
  • βœ… Config validation
  • βœ… Sync history tracking
  • βœ… Team documentation

Phase 3: Server Deployment (Later)

  • πŸ”„ Multi-user support
  • πŸ”„ Web-based config editor
  • πŸ”„ User authentication
  • πŸ”„ Azure Container Apps deployment
  • πŸ”„ Shared team dashboard

πŸ“Š Benefits of This Approach

βœ… Immediate Benefits

  • 5-minute setup per team member
  • No server costs during development
  • Complete data privacy (everything local)
  • Easy to debug and modify
  • Version controlled configurations

βœ… Team Collaboration

  • Shared repo for code updates
  • Template configs for easy setup
  • Documentation in the repo
  • Issue tracking via GitHub
  • Easy deployment when ready

βœ… Future Growth

  • Proven architecture ready for server deployment
  • Portable configs can move to database later
  • Team can scale from local to hosted
  • Investment protected – code translates directly

This gives you a working calendar sync solution in hours, not days, while building the foundation for a more sophisticated server-based system later!

Leave a Reply

Your email address will not be published. Required fields are marked *