Files
pub-quiz/AGENTS.md
John Gatward a69ef51825 init
2026-03-19 21:07:16 +00:00

46 lines
3.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# AGENTS.md — Pub Quiz Dashboard
## Overview
Single-page Flask dashboard that reads `data.csv` (one row per quiz night) and renders statistics, a player table, and Plotly charts via a Jinja2 template. No database — the CSV is the sole data store.
## Running the App
```bash
uv sync # install dependencies (uses uv.lock / pyproject.toml)
cd src && python app.py # start Flask dev server at http://127.0.0.1:5000
```
> **Critical:** `data.csv` is read with a bare relative path (`"data.csv"`), so the app **must** be launched from the `src/` directory, where Flask's CWD resolves to the project root via the relative path `../data.csv` — actually, `data.csv` sits at project root and `src/` is the CWD, so Flask resolves it as `../data.csv` would fail. The app reads `data.csv` directly, so run from `src/` only after confirming the path resolves (currently works because Flask's CWD is `src/` and `data.csv` is read as a sibling — verify if moving files).
## Data Shape (`data.csv`)
- **One row = one quiz night**
- Columns: `Date` (DD/MM/YYYY), `Absolute Position`, `Relative Position` (float 01, where 0=1st, 1=last), `Number of Players`, `Number of Teams`, `Points on Scattergories`, then one binary column per player (1=attended, 0=absent).
- Date parsing is always `dayfirst=True`; the DataFrame is sorted by date on load.
## Module Responsibilities
| File | Role |
|---|---|
| `src/app.py` | Flask routes, all Plotly figure generation, data loading |
| `src/stats.py` | Summary statistics dict (streaks, averages) passed to template as `stats` |
| `src/player_table.py` | List-of-lists table `[header, ...rows..., footer]`; cost fixed at **£3/quiz** per player |
| `src/constants.py` | Single source of truth for player names, regression features, and colour scheme |
| `src/templates/index.html` | Renders `stats` dict, `player_table` list, and `plots` dict of JSON-serialised Plotly figures |
## Key Conventions
### Adding/Removing a Player
1. Update `constants.PLAYER_NAME_COLUMNS` (ordered list — controls display order).
2. Update `constants.FEATURE_COLUMNS` (set — controls regression inputs).
3. Add the new column to `data.csv` with `0`/`1` values.
### Plots Pipeline
Figures are built with Plotly in `app.py`, serialised to JSON with `plotly.utils.PlotlyJSONEncoder`, stored in a `plots` dict keyed by a snake_case name, and rendered client-side via `Plotly.newPlot("{{ key }}", figure.data, figure.layout)` in the template. The dict key becomes both the HTML `id` and the JS variable target — keep keys unique and valid as HTML ids.
### `player_table` Structure
A flat list-of-lists: index `[0]` = header row, `[1:-1]` = data rows, `[-1]` = footer row. The template iterates `player_table[1:]` for `<tbody>`, so the footer is rendered as a regular row — style it in CSS if distinction is needed.
### `Relative Position` Semantics
Lower is better (0 = first place). The trendline in `generate_relative_position_over_time` extrapolates towards `target_value = 0.08` (≈top 8%). Adjust this constant to change the goal projection.
## Frontend
No build step. Tailwind CSS and Plotly are loaded from CDN in `index.html`. All styling is inline Tailwind utility classes.