Skip to content
Live at Cranleigh FC — grassroots football

PitchFit

Fixture data · schedule monitoring · pitch prediction · live allocation · admin

A suite of Python scripts and web apps that automates the journey from FA fixture data through to published pitch assignments — replacing manual WhatsApp coordination with a live, joined-up system that every stakeholder can trust.

Client
Cranleigh FC
Status
In active use
Scale
53 teams · 12 leagues · 33 pitches · 6 locations
Stack
Python · HTML/JS · Supabase · Railway
The problem

Fixture coordination at scale is harder than it looks

Cranleigh FC runs 53 teams across 12 different league competitions. Home fixtures are spread across 33 pitches at six locations — a mix of grass and 3G surfaces — with multiple games running simultaneously on busy match days, each needing the right pitch format (3v3, 5v5, 7v7, 9v9, 11v11), the right kick-off slot, and the right information reaching the right people in time.

Fixtures come from the FA's own system and change without warning. Postponements and reschedules arrive as emails at any hour. Previously, coordinators checked FA Full-Time manually and broadcast updates across multiple WhatsApp groups, with no guarantee information reached everyone or stayed current. PitchFit was built to replace that chain — automating the data flow from source to allocation, and giving every stakeholder a single live view they can trust.

The pipeline

Three Python scripts, one shared database

The backend is a set of focused Python scripts. Each does one thing and writes into a shared Supabase PostgreSQL database — the single source of truth that all three web apps read from.

Fixture
Extractor
FA Full-Time → Supabase (scheduled)
Fixture
Monitor
IMAP email → upsert
Pitch
Predictor
CP-SAT solver → CSVs
FastAPI
on Railway
HTTP wrapper for apps
Script 1 — pitchfit_extractor.py

Fixture Extractor

Reads fixture data from FA Full-Time for every active league code registered to the club, normalises the rows, and upserts them to Supabase using a stable MD5 fixture ID as the conflict key. Runs on a Windows Task Scheduler job as well as on demand, with each run logged to the same run-history table as the email Monitor. League codes and club configuration are read from the database, so adding a new team or league requires no code change.

Scheduled + on demand Config-driven MD5 conflict key
Script 2 — pitchfit_monitor.py

Fixture Monitor

Polls a dedicated IMAP inbox for FA Full-Time notification emails — the automated messages the FA sends when fixtures are postponed, rescheduled, or cancelled. When a relevant email arrives, the monitor identifies the affected fixture using the same canonical ID contract as the Extractor, and upserts the change to Supabase. Updates propagate immediately to the live Allocator view without any manual coordinator action, and every run is logged to the same run-history table as the Extractor.

IMAP polling Shared fixture ID contract Real-time propagation
Script 3 — pitchfit_predictor.py

Pitch Predictor

Reads live pitch, team and fixture data from Supabase and applies a CP-SAT constraint solver (Google OR-Tools) to assign each home fixture to a pitch and kick-off slot, writing three forecast CSVs: a per-fixture pitch forecast, a demand summary, and a format-level breakdown. A published FA kick-off time is honoured directly on any eligible pitch; the canonical 09:30/11:00/14:00 grid only fills in a default when no time has been published yet. Eligibility is data-driven per pitch — surface, adult-only, evening-lights and Open-Age-reserved flags — and existing training-session bookings are excluded before the solver runs, so forecasts never clash with a confirmed booking. A FastAPI wrapper on Railway exposes the same logic over HTTP so the Dashboard can trigger a fresh run without direct server access.

CP-SAT solver OR-Tools Training-conflict aware FastAPI on Railway
The apps

Three web apps, all reading the same database

The three web apps are self-contained HTML/JS files — no framework, no build pipeline, deployed directly to Netlify. They all read from and write to the same Supabase database, with access scoped by the club's Row Level Security policies.

pitchfit_allocator.html

Pitch Allocator

A mobile-first PWA (installable, no app store required) where coordinators assign each home fixture to a pitch and kick-off time, with an amber warning flagged on any fixture card whose format doesn't match its assigned pitch. PIN-gated write access via a Supabase RPC. Team managers access a public read-only view — no login required. Fixture changes from the Monitor and Extractor appear in real time via Supabase subscriptions.

Coordinators Team managers PWA PIN-gated writes
pitchfit_admin.html

Admin Panel

The club configuration interface — manages pitches, coordinators, team records, league codes, and training sessions. Also supports manual fixture entry for friendlies and non-FA competitions, and now hosts the fixture-pipeline Monitor tab, showing run history and alerts from both the Extractor and the email Monitor in one PIN-gated place. All data entered here flows directly into the Allocator and Predictor without any additional sync step.

Club administrator Training sessions Manual fixtures Pipeline monitor
pitchfit_dashboard.html

Forecast Dashboard

A fully public, read-only planning view for club management, the groundsman, and the parish council — no login for any part of it. Shows the Pitch Predictor's latest forecast across three tables plus a fixtures-and-training Calendar tab, each table exportable to CSV. Calls the Railway API to trigger a fresh predictor run on demand, then displays the updated results. Gives stakeholders a regularly refreshed forward view of expected ground usage without needing any direct database access.

Club management Groundsman Parish council CSV export Calls Railway API
Data architecture

Multi-tenant from the start

Every table is scoped to a club_id, making the schema naturally multi-tenant. Supabase Row Level Security policies enforce the boundary at the database level — no application-layer filtering required. The schema reflects the full fixture lifecycle from FA source through to pitch assignment, plus training sessions and league configuration.

clubs
One row per club — anchor for all other tables
fixtures
Home and away fixtures; stable MD5 fixture_id key
pitches
33 pitches across 6 locations; formats array, eligibility flags, overflow priority, cost
allocations
Coordinator assignments — fixture → pitch mapping
leagues
FA Full-Time league codes; drives extractor scope
teams
Team registry; links to league, format, age group
training_sessions
Recurring pitch bookings managed in admin
users
Coordinator records with PIN-based authentication
monitor_runs
Run history for both the Extractor and email Monitor
monitor_alerts
Deduplicated alerts surfaced in Admin's Monitor tab
Outcomes

What it replaced

PitchFit replaced a coordination process that ran almost entirely through WhatsApp — coordinators checking FA Full-Time manually, then posting updates across multiple group chats with no guarantee the right people saw them in time.

Faster fixture entry

The Extractor reduces fixture data entry to running a single script — a coordinator copies data from FA Full-Time in one step rather than transcribing fixtures manually across multiple teams.

Automatic change propagation

Postponements and reschedules update the moment an FA email arrives. No coordinator action needed — the Allocator view reflects the change immediately.

Rolling forward visibility

The Predictor and Dashboard give the groundsman and parish council a regularly refreshed view of expected pitch demand across the coming weeks and months.

Off WhatsApp

Team managers check the public Allocator view directly for pitch and kick-off information, reducing the volume of messages coordinators need to field.

Technical approach

Deliberately lightweight

The Python scripts use standard library HTTP alongside requests and pandas; no browser automation required. The Pitch Predictor uses Google OR-Tools' CP-SAT solver, exposed via a FastAPI endpoint deployed on Railway. The web apps are single HTML files with all CSS and JS inline — no framework, no build step, deployed straight to Netlify. The whole suite is version-controlled in one repository alongside a shared config.js that provides Supabase credentials to all three apps.

Python FastAPI OR-Tools / CP-SAT HTML / JS Supabase PostgreSQL Row Level Security FA Full-Time IMAP / email parsing Task Scheduler Progressive web app Railway Netlify

Interested in working together?

If you have a coordination or data problem that needs a practical, well-built solution, get in touch for an informal conversation.

hello@upmostltd.co.uk