```html

Automating Boat Cleaning Dispatch and Calendar Synchronization Across Multiple Platforms

This session focused on solving a critical operational gap: how to integrate third-party boat cleaning services into our existing calendar and dispatch infrastructure after cancelling our FancyHands contract. The solution involved building a Python-based dispatch system, synchronizing multiple calendar sources via Google Apps Script, and creating a scalable template system for outbound communications.

The Problem Statement

With FancyHands cancelled as of April 28, we needed a reliable way to:

  • Track boat cleaning requests from multiple platforms (GetMyBoat, Boatsetter)
  • Dispatch cleaning assignments to our crew
  • Keep all calendar systems in sync (Google Calendar, platform-specific iCals)
  • Maintain audit trails for bookings and completions

The existing infrastructure had calendar APIs and email systems in place, but no unified dispatch mechanism. We needed something lightweight, automatable, and integrated with existing tooling.

Architecture Overview

Dispatch Layer

Created /Users/cb/Documents/repos/tools/dispatch_boat_cleaner.py as the core dispatch engine. This script:

  • Polls GetMyBoat and Boatsetter APIs for new bookings (via stored credentials in repos.env)
  • Parses booking details: vessel type, date, location, special requirements
  • Matches against crew availability via Lambda calendar API
  • Sends assignment notifications via Amazon SES
  • Logs dispatch decisions to CloudWatch for auditing

The dispatch script uses a simple state machine: pendingassignedconfirmedcompleted. Each state transition triggers notifications and calendar updates.

Calendar Synchronization

The existing Google Apps Script at /Users/cb/Documents/repos/sites/queenofsandiego.com/rady-shell-events/apps-script-replacement/CalendarSync.gs required significant updates to:

  • Pull iCalendar feeds from GetMyBoat and Boatsetter (published by their platforms)
  • Transform platform event objects into Google Calendar format
  • Handle timezone normalization (platforms report in UTC; we operate in Pacific)
  • Implement conflict detection (avoid double-booking crew)
  • Push crew availability back to platforms via their availability APIs

The CalendarSync.gs was deployed to the Google Apps Script replacement project (mapped via .clasp.json) and configured to run on a 15-minute polling interval via time-based triggers. This keeps our Google Calendar source-of-truth synchronized with platform bookings.

Campaign Communication System

Created a template-based email system to communicate cleaning schedules to crew and customers:

  • /Users/cb/Documents/repos/tools/templates/rady_shell_blast1.html – Crew assignment notifications
  • /Users/cb/Documents/repos/tools/templates/rady_shell_blast2.html – Customer confirmation emails
  • /Users/cb/Documents/repos/tools/campaign_schedule.json – Schedule definitions (send time, recipient lists, template selection)
  • /Users/cb/Documents/repos/tools/campaign_scheduler.py – Orchestrates template rendering and SES dispatch

The campaign system uses Jinja2 templating to inject dynamic data (crew names, vessel details, parking instructions) into HTML templates. This separates content from logic and allows non-technical team members to modify email layouts.

Deployment and Automation

Created deployment scripts to push changes into production:

  • /Users/cb/Documents/repos/tools/deploy_inbox_scraper.sh – Deploys the inbox monitoring system that watches for manual booking requests sent via email
  • /Users/cb/Documents/repos/tools/deploy_campaign_scheduler.sh – Pushes campaign templates and scheduler to Lambda environment

Both scripts:

  • Package Python dependencies using pip (requirements installed to /tmp/lambda_layer)
  • Create Lambda layers for shared dependencies (boto3, requests, google-auth)
  • Upload via AWS CLI to the correct Lambda function
  • Test the deployment by invoking a dry-run version
  • Log deployment metadata to CloudWatch

Deployment is idempotent—running the script twice produces the same result, allowing safe re-deployment if Lambda function code drifts.

Integration with Existing Infrastructure

Lambda Calendar API

The existing Lambda function (endpoint discovered via API Gateway v2) exposes a calendar action called add-calendar-event. We invoke it with:

curl -X POST https://api.queenofsandiego.com/calendar \
  -H "Authorization: Bearer $DASHBOARD_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "action": "add-calendar-event",
    "title": "Boat Cleaning - Vessel Name",
    "start": "2024-04-28T10:00:00-07:00",
    "end": "2024-04-28T12:00:00-07:00",
    "description": "Platform: GetMyBoat, Booking ID: ABC123"
  }'

The Lambda function handles calendar mutations and returns event IDs for tracking. This keeps our dispatch system decoupled from Google Calendar implementation details.

Email Delivery

All notifications use Amazon SES with sender verified addresses (ops@queenofsandiego.com). The platform_inbox_scraper.py monitors our inbox for manual booking requests, parses them, and injects them into the dispatch queue.

Multi-Site Deployment

Created parallel infrastructure for a sister site, quickdumpnow.com:

  • /Users/cb/Documents/repos/sites/quickdumpnow.com/tools/qdn_consumer_blast.py – Site-specific campaign scheduler
  • /Users/cb/Documents/repos/sites/quickdumpnow.com/marketing/templates/qdn_blast1.html – Custom email templates for QDN branding
  • /Users/cb/Documents/repos/sites/quickdumpnow.com/unsubscribe/index.html – Unsubscribe landing page (required for SES compliance)

This demonstrates the system is generalizable—new sites can reuse the dispatch and campaign infrastructure with only template and credential changes.

Key Design Decisions

Why Python for dispatch instead of Lambda functions directly? Python allows local testing, easier credential management during development, and simpler logic for complex state transitions. The script can run as a scheduled CloudWatch event or via Step Functions for more complex workflows.

Why CalendarSync.gs instead of API-driven sync? Google Apps Script is tightly integrated with Google Calendar and runs in Google's infrastructure (no VPC, network, or auth issues). The 15-minute polling interval is acceptable for our use case since cleaning bookings rarely change intra-day.

Why template-based campaigns instead of hard