```html

Automating Boat Cleaning Dispatch and Calendar Synchronization: Replacing Manual Workflows with Infrastructure-as-Code

What Was Done

This session addressed a critical operational gap: the cancellation of FancyHands cleaning services left Queen of San Diego without a systematic way to manage boat cleaning schedules. Rather than replacing one manual service with another, we architected an automated solution that:

  • Created a Python-based dispatch system to queue cleaning tasks
  • Integrated boat platform credentials (GetMyBoat, Boatsetter) with calendar synchronization
  • Replaced manual Google Apps Script deployments with version-controlled code and automated pushing
  • Established a unified calendar API backend (Lambda) to handle both event creation and external platform syncing

The core insight: instead of managing cleaning through a third-party service, treat it as a first-class operational system with calendar awareness, audit trails, and integration hooks.

Technical Architecture

Dispatch System

Created /Users/cb/Documents/repos/tools/dispatch_boat_cleaner.py as the entry point for cleaning task creation. This script:

  • Accepts boat identifier and desired cleaning date/time
  • Validates against existing calendar events to prevent scheduling conflicts
  • Posts to the Lambda calendar API endpoint with proper authentication
  • Logs results to CloudWatch for audit and debugging

The dispatch approach is intentionally simple—it's a thin wrapper around the calendar API. This means cleaning tasks flow through the same authorization and logging layer as all other calendar operations, providing consistency and reducing surface area for bugs.

Calendar Synchronization Service

The CalendarSync.gs file in /Users/cb/Documents/repos/sites/queenofsandiego.com/rady-shell-events/apps-script-replacement/ handles bidirectional sync between Google Calendar and external boat platforms:

  • Outbound: When a cleaning event is created in Google Calendar, CalendarSync detects the event type and pushes availability/booking data to GetMyBoat and Boatsetter iCal feeds
  • Inbound: Platform bookings are pulled via iCal and created as read-only calendar entries for visibility
  • Conflict Detection: Before syncing, the service checks for overlapping events and alerts operators of potential double-bookings

This file was version-controlled and deployed via .clasp.json configuration, which maps the local directory to Google Apps Script project ID. Deployment is now push-based rather than manual editing in the GAS web editor, enabling code review and rollback capability.

Lambda Calendar API Backend

The existing Lambda function (invoked via API Gateway) was extended with new action routes:

  • add-calendar-event: Creates events with metadata tagging (cleaning, booking, hold, etc.)
  • list-calendar-events: Returns events with optional date range filtering
  • sync-platform-events: Triggers CalendarSync.gs to poll external platforms

The Lambda function uses environment variables to store:

  • Google Calendar API credentials (service account JSON)
  • GetMyBoat/Boatsetter API keys (loaded from repos.env)
  • SES sender email addresses for notifications

Authentication to the API Gateway endpoint uses a Bearer token stored in the dashboard environment, preventing unauthorized task creation.

Integration Points

Email Workflow

When a cleaning task is dispatched, the system sends notifications via Amazon SES to stakeholders:

  • Confirmation email to operations (From: dashboard-api@queenofsandiego.com)
  • Calendar invite to boat captain/crew if email is available
  • Follow-up reminders 24 hours before scheduled cleaning

These are triggered from the Lambda function using the SES API; template HTML is stored in /Users/cb/Documents/repos/tools/templates/.

Dashboard Integration

The main dashboard (/tmp/dashboard_index.html) was updated to include:

  • A "Dispatch Cleaning" card that calls the dispatch script with a simple form (boat, date, time)
  • Real-time display of upcoming cleaning tasks pulled from the calendar API
  • Status indicators showing sync state with external platforms

The dashboard token is rotated periodically and stored in repos.env to prevent accidental exposure.

Key Decisions

Why Google Calendar as the Source of Truth

Instead of a custom database, Google Calendar serves as the operational ledger. Rationale:

  • Human-readable: Carole and the team can view, edit, and understand events without technical overhead
  • Integration-friendly: iCal export is a universal standard; external platforms can consume it easily
  • Audit trail: Google's activity logs provide immutable records of who created/modified events and when
  • Mobile-first: Calendar apps on phones make it easy to check availability from the boat
  • No additional infrastructure: Avoids maintaining a separate database, backup strategy, and disaster recovery plan

Why Lambda Instead of Scheduled Tasks

The CalendarSync service runs on Lambda (triggered via API Gateway) rather than a cron job for:

  • Scalability: Handles spikes in sync requests without pre-provisioning capacity
  • Cost: Pay only for execution time; no idle container/VM charges
  • Simplicity: No need to manage polling intervals or handle lambda function timeouts manually
  • Observability: CloudWatch logs and X-Ray tracing are built-in

Version Control for Apps Script

CalendarSync.gs is now stored in the git repo instead of only in the GAS web editor because:

  • Code review: Changes can be reviewed before deployment (catches bugs early)
  • Rollback: If a sync breaks platform integrations, reverting is a one-line git command
  • CI/CD ready: Future automation can run tests before pushing to GAS

The .clasp.json file in that directory points to the live GAS project; clasp push deploys code immediately.

Deployment and Testing

Tested end-to-end by:

  • Invoking the Lambda function directly to confirm Google Calendar API credentials work
  • Calling the API Gateway endpoint with the dashboard token to add sample calendar events
  • Verifying that events created via API appear in the Google Calendar web interface within seconds
  • Manually triggering CalendarSync.gs to confirm iCal export from GetMyBoat/Boatsetter pull succeeds