Live demo: isartab.nuh.me
Try it out — create an event and play around. (Don't use the demo for a real event)
New to this kind of thing? If you're not a programmer, follow noobtutorial.md instead — it walks you through deployment step by step with no jargon.
A small web app for the Munich Debating Club to run debate evenings: participants register from their phones, the tabmaster proposes rooms (with an OR-Tools CP-SAT solver), drags people around, locks slots, and publishes — participants see their room appear on their phone.
No accounts, no emails. Each event has a code; each admin gets a secret link; each browser identifies itself with a local token.
If you have Docker installed, this is the easiest way:
docker build -t debate-allocator .
docker run --rm -p 8000:8000 debate-allocatorOpen http://localhost:8000 and click "Create event". Done.
The database is in-memory only — nothing is written to disk, and all
events are wiped when the app restarts. This is intentional, for data
protection. There is no data/ folder and no volume to mount.
You need Python 3.12 (ortools doesn't ship wheels for newer versions yet). Then:
python3.12 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload --port 8000This repo ships with a Dockerfile and a /healthz endpoint, so any
Docker host works. For Coolify specifically:
- New Resource → Dockerfile pointing at this repo.
- Port:
8000. - Environment variable: set
BASE_URLto your public HTTPS URL (e.g.https://debate.example.org, no trailing slash). This is what appears in share links and QR codes.
No persistent volume is needed: the database lives in memory only and is wiped on every restart/redeploy (by design, for data protection).
That's it. After the first deploy, hit BASE_URL/healthz — you should
see {"ok": true}.
See coolify.md for screenshots/extra notes.
All optional — the app runs with defaults if none are set. Copy
.env.example to .env for local use, or set them in your
host's dashboard (e.g. Coolify). The real .env is git-ignored.
| Variable | Default | What it does |
|---|---|---|
BASE_URL |
http://localhost:8000 |
Public base URL (no trailing slash). Used in share links and QR codes. |
IMPRINT_TEXT |
(empty) | Free-form Impressum block shown verbatim on /legal. Multi-line; quote it. Empty → placeholder. |
LOG_REGISTRATIONS |
true |
Print every accepted registration to stdout so history can be reconstructed from logs (DB is in-memory). Set false/0/no/off to disable. Logs personal data — keep retention short. |
| Path | What it is |
|---|---|
/ |
Landing — create a new event |
/register?event={code} |
Participant form |
/waiting?event={code} |
Live waiting page |
/rooms?event={code} |
Public room view (after publish) |
/admin/{admin_token} |
Tabmaster panel |
/legal |
Impressum & privacy (uses IMPRINT_TEXT) |
/healthz |
Health check |
- Event code (9 digits) — public, in URLs.
- Admin token (32-char hex) — secret. Whoever has the admin link controls the event. Lose it and the event is unrecoverable.
- Browser token — stored in the participant's
localStorage, so the same browser keeps identity across reloads.
# Smoke tests (build the Docker image automatically on first run):
bash tests/smoke_api.sh
bash tests/smoke_admin.sh
# Concurrency regression test — guards against the DB-lock outage where
# the whole server wedges under simultaneous requests (see app/db.py).
docker run --rm -v "$(pwd)":/repo -w /repo \
-e PYTHONPATH=/repo \
--entrypoint python debate-allocator tests/smoke_concurrency.py
# …or directly, if you have a local venv with the deps installed:
.venv/bin/python tests/smoke_concurrency.py
# Solver test (needs ortools — easiest via the Docker image):
docker run --rm -v "$(pwd)":/repo -w /repo \
-e PYTHONPATH=/repo \
--entrypoint python debate-allocator tests/test_solver.pyapp/ FastAPI app (routes, DB, solver, templates, static)
tests/ Smoke + solver tests
legacy/ Original solver + participant generator (kept for reference)
Dockerfile Single-stage python:3.12-slim
coolify.md Coolify deployment notes
architecture.md design doc
