Coordinates a weekly pick-up basketball game: players mark themselves in/out, the organizer splits the "in" players into light vs. dark teams, then copies a ready-made text block to paste into an email they send to the players themselves.
This is a local prototype built with the Python standard library only — no installs, no build step.
cd pickup-hoops
python3 seed.py # create sample players + the upcoming Friday game (prints magic links)
python3 app.py # start the server at http://localhost:8000Open http://localhost:8000 for a dev index of everyone's magic links.
- Players mark In / Out for the upcoming game — either from the shared landing
board (a button next to their name) or their personal page (
/p/<token>). Once teams are locked they can see their own team. - The organizer opens
/admin/<token>to:- schedule a game (pick any date — usually Friday, occasionally Thursday),
- add players to the roster,
- Suggest teams (random balanced split of the "in" players), then drag players between Unassigned / Light / Dark to tweak,
- Lock teams, then Copy for email — a formatted text block (each team's names and email addresses) to paste into an email the organizer sends manually.
The app runs locally as an always-on process backed by SQLite, and as Vercel
serverless functions backed by Postgres — db.py picks the backend automatically
based on whether a database URL is present in the environment.
-
Provision Postgres (Vercel Postgres, Neon, or Supabase). Use the pooled connection string (serverless opens many short-lived connections).
-
Import the repo into Vercel (New Project → import
pickup-hoops). -
Set environment variables in the Vercel project:
Variable Required Purpose DATABASE_URL✅ Postgres connection string (or POSTGRES_URL)SETUP_KEYoptional Re-open /setupafter the first organizer exists -
Deploy, then visit
https://<your-app>/setup. On a fresh database (no organizer yet) this page is open so you can create your first organizer and get its private/admin/…link — bookmark it. Once an organizer exists,/setuplocks; setSETUP_KEYand use/setup?key=<SETUP_KEY>if you need it again (you can also add more organizers from the dashboard's roster).
How it maps to Vercel: api/index.py exposes the request handler as handler
(invoked per request — no serve_forever). vercel.json explicitly builds it with
the @vercel/python runtime (bundling the root *.py modules) and routes every
path to it. requirements.txt installs the Postgres driver.
Prefer the original always-on design? A process host (Render/Railway/Fly) runs the local SQLite version nearly unchanged — set the
PORTenv var and a start command ofpython3 app.py.
- Auth: magic-link tokens are unguessable but never expire and live in the URL — fine for a casual group; add expiry/rotation if you need more. The shared landing board lets anyone toggle any player's status (by design); the organizer admin link is kept off the public page in production.
| File | Purpose |
|---|---|
app.py |
Request handler + routing (works as a local server and on Vercel) |
db.py |
Backend-agnostic storage (Postgres in prod, SQLite locally) |
services.py |
Team suggestion + the copy-paste email summary |
templates.py |
Server-rendered HTML (incl. drag-and-drop + copy button) |
seed.py |
Sample data (local dev) |
api/index.py |
Vercel serverless entry point |
vercel.json |
Routes all paths to the function |
requirements.txt |
Postgres driver (used only when a DB URL is set) |