Add optional Google Sheets export for the masterplan
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.
This commit is contained in:
@@ -79,6 +79,10 @@ gitignored, so your secret never gets committed. An already‑exported
|
||||
> The package raises at import time if no key is found, so `GOOGLE_MAPS_API_KEY`
|
||||
> must be set (even to a dummy value) just to import `tatami.traveltimes`.
|
||||
|
||||
Optionally, also set `GOOGLE_SHEETS_CREDENTIALS_FILE` and
|
||||
`GOOGLE_SHEETS_SPREADSHEET_ID` in `.env` to export the plan to a shared
|
||||
Google Sheet — see [Sharing the plan with participants](#sharing-the-plan-with-participants).
|
||||
|
||||
## Input: the participant CSV
|
||||
|
||||
A **tab‑separated** file (the default working file is `test-config.csv` in the
|
||||
@@ -153,14 +157,63 @@ keyed by UUID, so resolve names/addresses by looking participants up by `uuid`.
|
||||
> Note: only a group's `main_member` address is used for all travel calculations;
|
||||
> other members are assumed to join at the main member's home.
|
||||
|
||||
## Sharing the plan with participants
|
||||
|
||||
`get_masterplan`'s dicts are great for code, but participants need something
|
||||
readable. `compute_masterplan_groups` (the same computation, returning live
|
||||
`Group`/`Participant` objects instead of dicts) feeds an optional Google
|
||||
Sheets export — the same kind of shared spreadsheet organizers have used in
|
||||
previous years, just generated automatically instead of by hand.
|
||||
|
||||
Set up once:
|
||||
|
||||
1. Create a Google Cloud service account and enable the **Google Sheets API**
|
||||
for its project.
|
||||
2. Download the service account's JSON key and point
|
||||
`GOOGLE_SHEETS_CREDENTIALS_FILE` at it (in `.env`).
|
||||
3. Create a blank Google Sheet, share it with the service account's
|
||||
`client_email` (from the JSON key) as **Editor**, and set
|
||||
`GOOGLE_SHEETS_SPREADSHEET_ID` to that sheet's ID (in `.env`).
|
||||
|
||||
With both set, running `uv run python -m tatami.tatami_masterplan` populates
|
||||
that spreadsheet with an **Overview** tab (every participant, their group,
|
||||
course, address, phone, allergies — followed by a Meal Times table, a Support
|
||||
Contacts table, and a free-text Info block, see below) and one tab per group
|
||||
(their own course, route with addresses and fixed course times, and the
|
||||
guest list — with allergies — for the course they host). Reruns are
|
||||
idempotent: tabs are cleared and rewritten, and stale tabs from a previous
|
||||
run are deleted.
|
||||
|
||||
Tatami never contacts participants directly — sharing the sheet's link is
|
||||
still up to the organizer, exactly as before.
|
||||
|
||||
The three extra Overview sections are plain configuration, passed straight
|
||||
through to the sheet with no logic in between — edit these in
|
||||
`tatami_masterplan.py`:
|
||||
|
||||
```python
|
||||
COURSE_TIMES = {
|
||||
"starter": "18:30", "main": "20:00", "dessert": "22:00", "after_party": "23:30",
|
||||
} # -> "Meal Times" table
|
||||
ORGANIZER_CONTACTS = [("Lars (Organizer)", "0151-23456789")] # -> "Support Contacts" table
|
||||
INFO_TEXT = "Welcome to the running dinner! ..." # -> "Info" block (one row per line)
|
||||
```
|
||||
|
||||
`organizer_contacts` and `info_text` are optional (`None`/empty skips that
|
||||
section); `course_times` is also reused for each group's own route table.
|
||||
|
||||
If neither variable is set, this step is skipped entirely and Tatami just
|
||||
prints the plan, as before.
|
||||
|
||||
## What you can tweak
|
||||
|
||||
All knobs currently live in the source. The most useful ones:
|
||||
|
||||
| What | Where | Default | Effect |
|
||||
|------|-------|---------|--------|
|
||||
| **After‑party address** | `tatami_masterplan.py:230` (`__main__`) | a Karlsruhe address | Where everyone ends the night; also influences host ranking. |
|
||||
| **Travel mode** | `tatami_masterplan.py:233` (`mode="BICYCLE"`) | `BICYCLE` | Any Routes API `travelMode`: `BICYCLE`, `DRIVE`, `WALK`, `TWO_WHEELER`, `TRANSIT`. |
|
||||
| **After‑party address** | `tatami_masterplan.py:251` (`__main__`) | a Karlsruhe address | Where everyone ends the night; also influences host ranking. |
|
||||
| **Travel mode** | `tatami_masterplan.py:254` (`mode="BICYCLE"`) | `BICYCLE` | Any Routes API `travelMode`: `BICYCLE`, `DRIVE`, `WALK`, `TWO_WHEELER`, `TRANSIT`. |
|
||||
| **Course start times** | `tatami_masterplan.py` (`COURSE_TIMES`) | `18:30` / `20:00` / `22:00` / `23:30` | Fixed slot times written into the Google Sheet export; the dinner runs on a synchronized schedule, not travel-derived timing. |
|
||||
| **Group sizing** | `tatami_masterplan.py:24` (`len(participants) / 6`) | 1 group per ~6 people | The divisor sets how many participants form one "course‑triple". Larger → fewer, bigger groups. |
|
||||
| **Kitchen‑size penalty** | `classes.py:19` (`minutes=3 * (10 - kitchen_size)`) | 3 min per point | Travel‑time‑equivalent penalty for small kitchens. Raise the `3` to push hosting toward big kitchens. |
|
||||
| **Annealing schedule** | `tatami_masterplan.py:48–50` | `T=1000`, `cooling=0.99`, `iters=10000` | Optimization quality vs. runtime. More iterations / slower cooling → better routes, slower. |
|
||||
@@ -240,6 +293,7 @@ uv run pytest -m e2e # opt-in live test that calls the real Routes API
|
||||
src/tatami/
|
||||
classes.py # Participant and Group domain model
|
||||
traveltimes.py # Google Routes API wrapper + matrix helpers
|
||||
sheets_export.py # optional Google Sheets export for participants
|
||||
tatami_masterplan.py # pipeline: load → fetch → group → optimize → assign
|
||||
tests/ # pytest suite (offline + opt-in live e2e)
|
||||
running_dinner/ # legacy standalone prototype — NOT used by the package
|
||||
|
||||
Reference in New Issue
Block a user