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

3.2 KiB
Raw Blame History

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

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.