# AGENTS.md — The Hope Pub Quiz Dashboard ## Overview Single-page Flask dashboard that reads `data.csv` (one row per quiz night) and renders summary statistics, a player cost table, and five Plotly charts via a Jinja2 template. No database — the CSV is the sole data store. ## Running the App ```bash uv sync # install deps from uv.lock / pyproject.toml PYTHONPATH=src python src/app.py # run from project root — resolves data.csv correctly ``` > **Path gotcha:** `app.py` reads `data.csv` with a bare relative path, so the CWD must be the **project root**. `PYTHONPATH=src` puts `src/` on the import path so local modules resolve. ## Data Shape (`data.csv`) - **One row = one quiz night** - Columns: `Date` (DD/MM/YYYY), `Absolute Position`, `Relative Position` (float 0–1, 0=1st/best, 1=last), `Number of Players`, `Number of Teams`, `Points on Scattergories`, then one binary column per player (1=attended, 0=absent). - Date parsing uses `dayfirst=True`; the DataFrame is sorted ascending by date on every load. ## Module Responsibilities | File | Role | |---|---| | `src/app.py` | Flask route, five Plotly chart builders, data loading | | `src/stats.py` | `generate_stats(df)` → `(stats_dict, highlights_list)` tuple | | `src/player_table.py` | `generate_player_table(df)` → flat list-of-lists; cost hard-coded at **£3/quiz** | | `src/constants.py` | Player names, regression features, colour scheme, `ordinal(n)` helper | | `src/templates/index.html` | Renders `highlights` list, `stats` dict, `player_table`, and `plots` dict | ## Key Conventions ### Adding/Removing a Player 1. Update `constants.PLAYER_NAME_COLUMNS` (ordered list — controls display order everywhere). 2. Update `constants.FEATURE_COLUMNS` (set — controls which columns feed the regression model). 3. Add the new binary column to `data.csv`. ### `generate_stats` Return Value Returns a **tuple** `(stats, highlights)`: - `highlights` — list of `{"label": str, "value": str, "detail": str}` dicts; rendered as 6 KPI cards. - `stats` — plain `dict` of human-readable `label: value` pairs; rendered as a secondary list. The `index()` route unpacks both: `stats, highlights = generate_stats(df)`. ### Plots Pipeline 1. Build a Plotly figure in `app.py` using `Relative Position` directly (or `Relative Position * 100` for percentile display), where **lower = better**. 2. Serialise: `json.dumps(fig, cls=plotly.utils.PlotlyJSONEncoder)`. 3. Store in the `plots` dict under a **snake_case key** (e.g. `"position_trend"`). 4. The template renders every entry automatically: `Plotly.newPlot("{{ key }}", ...)` — key is both the `