Find the best time to hang out at the gym.
Build the image:
docker build -t boulderbat .Run the container:
docker run -p 8000:8000 -v boulderbat-data:/data boulderbatThe frontend is available at http://localhost:8000. The named volume boulderbat-data persists the database across restarts.
Backend:
cd backend
uv sync
uv run fastapi dev main.py(fastapi dev enables auto-reload. Use fastapi run main.py for the production server.)
Frontend (dev server with HMR):
cd frontend
npm install
npm run devThe frontend dev server proxies /live and /history to the backend at http://localhost:8000.
To build the frontend for production (served by the backend):
cd frontend && npm run buildBy default the database is written to data/boulderbat.db. Override with the DB_PATH environment variable.
To seed a local database with the public production history, run:
cd backend
uv run python -m app.import_production_historyThe importer downloads the complete archive from https://boulderbat.flofriday.dev, adds any missing readings to the local database, and is safe to run again. Use --dry-run to inspect the download without writing, or --start 2026-06-01T00:00:00Z and --end 2026-06-02T00:00:00Z to import a narrower UTC range.
Interactive docs are available locally at http://localhost:8000/docs and in production at boulderbat.flofriday.dev/docs.
Returns the most recent capacity reading for each of the 8 locations.
curl http://localhost:8000/live[
{
"id": 260,
"title": "Linz",
"url": "https://boulderbar.net/standorte/linz/",
"capacity": 25,
"recorded_at": "2026-04-28T12:00:00Z"
},
...
]Returns all readings within a time range. Both start and end are required ISO 8601 timestamps. Add location_id to filter to a single location.
# All locations, last 24 hours
curl "http://localhost:8000/history?start=2026-04-27T00:00:00Z&end=2026-04-28T00:00:00Z"
# Single location
curl "http://localhost:8000/history?start=2026-04-27T00:00:00Z&end=2026-04-28T00:00:00Z&location_id=260"Returns average capacity for each local weekday and hour from 08:00 to midnight, using the latest eight local calendar weeks (including the current week). It also returns the number of weeks represented. location_id is required; timestamps are grouped in the Europe/Vienna timezone.
curl "http://localhost:8000/typical-week?location_id=262"| ID | Location |
|---|---|
| 260 | Linz |
| 261 | Salzburg |
| 262 | Hannovergasse |
| 263 | Hauptbahnhof |
| 264 | Seestadt |
| 265 | Wienerberg |
| 284 | St. Pölten |
| 1001 | Blockfabrik |