Lets organizers automatically populate a shared Google Sheet (the same kind
they previously built by hand) with an Overview tab and one tab per group,
instead of printing dicts. Group tabs are named after all their members
(not a single host) and show each group's route, course times, and guests
with allergies. The Overview tab also gets configurable Meal Times, Support
Contacts, and Info sections, passed through as plain data from
tatami_masterplan.py. Export is fully opt-in via GOOGLE_SHEETS_CREDENTIALS_FILE
and GOOGLE_SHEETS_SPREADSHEET_ID; without them, behavior is unchanged.
- test_e2e_api.py: minimal 2-address (4-element) call to the real Google
Routes API; reads the real key from env/.env and skips on a placeholder.
- Marked `e2e` and deselected by default via addopts; run with
`pytest -m e2e`.
Route building / optimization (tatami_masterplan.py):
- fast_total_time was permutation-invariant: it applied get_courses to
group *values* instead of slots and ignored the permutation, so every
ordering scored identically and the annealing optimized nothing. It now
maps each rotation slot to its assigned group via the permutation.
- Replaced the broken next_permutation/simulated_annealing (enumerated n!
orderings per iteration, fed unnormalized Boltzmann weights to
np.random.choice -> ValueError, and returned the last random sample) with
a standard neighbor-swap annealer that tracks and returns the best
solution and handles <2 slots.
- Convert the reduced Timedelta matrix to float seconds before annealing
(np.exp can't operate on Timedelta).
Group building (tatami_masterplan.py):
- assign_courses set each group's hosts (sorted by course) before all
courses were assigned, so hosts whose course was still None got
mis-ordered. Assign all courses first, then wire up hosts.
- get_masterplan no longer mutates the caller's participant list.
classes.py:
- Narrow casts on distance-matrix lookups to satisfy the mypy gate
(pre-existing failures).
Tests:
- Add pytest suite (74 tests) covering the rotation topology, route cost
and optimization, the domain model, the masterplan pipeline, and the
Routes API wrapper (HTTP mocked). The cost cross-check caught the
assign_courses ordering bug above.