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 successfullyat the end
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
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
Scheduled invocation
A typical cron entry (every 10 minutes):Log output
The pipeline logs at INFO level with timestamps. A successful run looks like:--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)
- Mark unread in Outlook manually
- Save the body to a file and use
--file
Failure modes
| Failure | What you’ll see | What to do |
|---|---|---|
| Bad AES padding | ERROR Failed to process … Could not decrypt payload | Submit was tampered with or used wrong key. Check the email body. |
| Subject regex mismatch | Email skipped silently (filter excludes it) | Crew didn’t follow subject convention. Ask them to resubmit with the correct format. |
| DB connection refused | ERROR could not connect to server | Check CLOUD_SQL_INSTANCE_CONNECTION_NAME and service account permissions. |
| Outlook auth fails | ERROR AcquireTokenForClient … invalid_client | Rotate AZURE_CLIENT_SECRET, check tenant ID. |
| Unique constraint violation | IntegrityError on insert | Two emails for the same (vessel, type, datetime) processed in the same second. Re-run — second will replace first. |
See also
- ETL stages — internal stage details
- Configuration — env vars
- Data model — schema and CASCADE behaviour