Skip to main content
The pipeline has a single entry point: python -m src.main. Three modes, controlled by flags.

Modes

Default — fetch from Outlook

  • Connects to Microsoft Graph via MSAL client credentials
  • Fetches unread emails matching Metaweave Forms: in the subject
  • Processes each one: Parse → Map → Write
  • Marks each email as read after successful processing
  • Commits the session per email; rolls back on error
  • Logs Done: X/N processed successfully at the end
This is what you’d schedule (cron, Cloud Scheduler, etc.).

Single-file — process a saved email

  • Skips the Outlook fetch entirely
  • Reads the body from a local file
  • Runs Parse → Map → Write
  • Useful for: testing, replay, debugging a specific submission
The file should contain the full email body — preferably with the BEGIN MW FORM DATA / END MW FORM DATA markers so the parser hits the encrypted path. Without markers, it falls back to text parsing (limited).

Schema setup — create tables

  • Calls Base.metadata.create_all(engine) on the configured database
  • Creates the 17 tables if missing; no-ops if they exist
  • Exits immediately — does not process any emails
Run this once per database. Idempotent.

Scheduled invocation

A typical cron entry (every 10 minutes):
For Cloud Scheduler / Lambda / Cloud Functions, the same idea — invoke the entry point on the cadence you want.

Log output

The pipeline logs at INFO level with timestamps. A successful run looks like:
A failing email is logged but doesn’t stop the run:
Failed emails are not marked as read by default — they’ll be re-attempted on the next run. Investigate by reading the body and replaying with --file.

What happens per email

Idempotency and replay

Re-running the same email is safe because:
  • The Vessel upsert keys on IMO (won’t duplicate)
  • The Voyage upsert keys on (vessel_id, voyage_number)
  • The Report uses delete-then-insert on (vessel_id, report_type, report_datetime_utc)
So the same email always produces the same final database state. This is also how corrections work — re-submit a corrected Noon and it overwrites cleanly. Mark-as-read is the only side effect on re-run — once marked, the default-mode fetcher skips it. To re-process, either:
  • Mark unread in Outlook manually
  • Save the body to a file and use --file

Failure modes

FailureWhat you’ll seeWhat to do
Bad AES paddingERROR Failed to process … Could not decrypt payloadSubmit was tampered with or used wrong key. Check the email body.
Subject regex mismatchEmail skipped silently (filter excludes it)Crew didn’t follow subject convention. Ask them to resubmit with the correct format.
DB connection refusedERROR could not connect to serverCheck CLOUD_SQL_INSTANCE_CONNECTION_NAME and service account permissions.
Outlook auth failsERROR AcquireTokenForClient … invalid_clientRotate AZURE_CLIENT_SECRET, check tenant ID.
Unique constraint violationIntegrityError on insertTwo emails for the same (vessel, type, datetime) processed in the same second. Re-run — second will replace first.

See also