Welcome

Tourney

A March Madness pool where you draft individual college basketball players and coaches instead of picking which teams win — so your score depends on how the players you chose actually perform, not just who advances. This page is the front door: what the pool is, and a real, at-a-glance look at what it's built from. Everything below links out to a much deeper page on that specific piece.

01What makes this different from a normal bracket pool

A standard bracket pool asks you to predict game winners — get the Final Four right and you win, regardless of how any individual player does. This pool works differently: you draft 22 individual players and coaches across five groups (seeded 1–4, 5–8, 9–12, 13–16, and head coaches), and you earn points based on their actual performance in real games — a player's real scoring stats, a coach's real team wins. That means two people who agree on every single game winner can still land on completely different scores, because they drafted different players from those same teams. It rewards knowing the players, not just the bracket.

02How it's actually built — the whole system, at a glance

This isn't a spreadsheet with a form bolted on. It's a real, live web application: a hosted server, a real database, live data pulled continuously from ESPN, two different AI models generating daily commentary, and a separate analytics pipeline exploring what predicts a good draft pick. The two diagrams below show every real piece — the same names used throughout the rest of this site, not simplified stand-ins. One name worth flagging up front so it doesn't read as a typo: "the Spark" below is real, private hardware — a physical machine on the developer's own network — not the Apache Spark data-processing framework.

The core path: a player's browser to a live standing

flowchart LR
    BROWSER["Browser
player or admin"] --> APP subgraph AWS["AWS EC2 — one server"] APP["Flask web app
(Tourney)"] DB[("MySQL database
teams · players · picks
entries · scores · settings")] APP <--> DB end APP -->|"pulls the tournament field,
rosters, and box scores"| ESPN["ESPN public APIs"] DB -.->|"offline copy for analysis,
never wired into live scoring"| WH[("Analytics warehouse
DuckDB")] style AWS fill:transparent,stroke:#c65a2e,stroke-width:2px

The AI path: daily commentary, two ways

flowchart LR
    APP2["Flask web app
(Tourney)"] -->|"hosted foundation model,
pay per token"| CLAUDE["Claude
via the Anthropic API"] APP2 -->|"private Tailscale network"| SPARK["The Spark
gpt-oss:20b, served by Ollama"] APP2 -->|"account emails"| SES["Amazon SES"] style SPARK fill:transparent,stroke:#c65a2e,stroke-width:2px

Both diagrams show real components with their real names — nothing here is a simplified fiction. The table below is the click-through map: every named piece links to the page that covers it in full engineering depth.

ComponentWhat it isRead more
Flask web app, MySQL, AWS EC2The one-server hosting setup: how it's packaged, deployed, and kept runningHow It's Built →
ESPN public APIsThe real, live source for the tournament field, rosters, and every scorePlayer Experience →
Claude & the SparkThe same daily-commentary task, run through a hosted model and a self-hosted open-weight model, side by sideAI: Foundation vs. Open-Weight →
Amazon SESSends account emails such as password resetsPlayer Experience →
Analytics warehouse (DuckDB)A separate, offline copy of the data used to study what predicts a good draft pick — deliberately kept apart from live scoringData Warehouse →
How to Play
  1. Register — create an account with your name, email, and cell number.
  2. Submit an entry — pick players from each seed group and four coaches. You can submit multiple entries (brackets) if allowed.
  3. Set your tiebreaker — guess the total combined score in the championship game. Closest without going over wins ties.
  4. Edit before lock — entries can be changed any time until the admin locks the pool.
  5. Watch the standings — once the tournament starts, scores and standings update live.
Scoring Rules

Each entry has 22 picks spread across 5 groups:

Group Picks Scoring
Seeds 1–4 6Actual game points
Seeds 5–8 5Actual game points
Seeds 9–12 4Actual game points
Seeds 13–163Actual game points
Coaches 410 pts per team win

Rounds:

Rd 1 — Round of 64 Rd 2 — Round of 32 16 — Sweet 16 Quarter — Elite 8 Semi — Final Four Final — Championship
How It's Built — Full Map

This pool is a real system built end to end — live ESPN data, a working CI/CD pipeline, a self-hosted AI model on a private network, a from-scratch analytics warehouse. Each card below is a 90-second read on one part of it, with the engineering detail underneath for anyone who wants to verify a claim against the code.

How It's Built →

Hosting, containers, CI/CD, how ESPN scores become standings, the visual bracket, known tradeoffs.

Player Experience →

The smart entry form, live game links, accounts, bracket comparisons, historical simulation, the two APIs, email.

AI: Foundation vs. Open-Weight →

Claude vs. a self-hosted model on a private Tailscale network — the same task, run both ways, compared.

Admin Tour →

What an admin can actually do — settings, data loading, user management, the daily-update eval.

Database Design →

Every table, what it's for, and how it's kept accurate against the running schema.

Data Warehouse →

A separate DuckDB warehouse for predictive analysis, kept apart from the live database on purpose.

Security →

Auth, credential handling, and honestly-tracked gaps.

Pages Guide →

A tour of every page in the site, public and admin.

How This Gets Built Safely →

The real Architect → Developer → Qcoder → Architect → CI/CD pipeline every change goes through, with a checkable pointer for each stage.

Other Features
  • Live Scores — in-progress and upcoming games appear on every standings page, refreshing every 30 seconds.
  • Upsets Tracker — automatically detects and lists every upset (higher seed beats lower seed), with affected bracket count.
  • Compare — pick any two entries head-to-head to see shared picks, unique picks, and current score difference.
  • Prediction Page — filter and sort entries by projected final score.
  • Tiebreaker — used only if two entries are tied; closest guess for championship total (without going over) wins.
  • Backup & Restore — admin can download a full JSON snapshot of all users, entries, and picks at any time. If needed, the site can be fully restored from that file, with users prompted to reset their passwords on next login.
Built With
Python / Flask MySQL Bootstrap 5 HTMX Docker Amazon EC2 AWS SES (email) ESPN API (scores) Claude (Anthropic) Ollama (self-hosted) DuckDB (analysis warehouse) Git GitHub Actions