From “Step-Count Leaderboard” to “AI Fitness Butler”: How Workout Challenge Turns Office Rivalry into a London Dev-Team Ritual (Docker + Django + React Deep Dive)
Keywords: Docker deployment, Strava auto-sync, Celery scheduled tasks, privacy-first, AI nutrition tips, dark mode, responsive email, geo-optimized
Opening Scene: When Slack pings, “@all September step challenge is on!”
If you’ve ever worked at a London tech firm, you know the drill—
-
iPhone folks screenshot their Apple Watch rings and slam it into the channel; -
Android teammates fire back with a Google Fit bar chart; -
The fold-bike designer exports a Garmin CSV, then manually converts kilometers; -
HR staples an Excel into Notion, but someone types steps into the kcal column…
Pain summary: fragmented devices, messy data formats, privacy goosebumps, spreadsheet fatigue.
Workout Challenge was born to turn this “cross-species” fitness social scene into a self-hostable, privacy-first leaderboard that spins up with one docker compose up
. Here’s the full tech autopsy.
1. Architecture: Dashboard, API & Cron in One Container
┌---------------------------┐
│ Nginx (80) │
│ ├─ / → React static │
│ └─ /api → Gunicorn+Django│
└------------┬--------------┘
│Celery Worker
│Celery Beat
│Redis
▼
┌---------------------------┐
│ Postgres / SQLite │
└---------------------------┘
-
Front-end: React + MUI, dark mode with prefers-color-scheme
, charts by Chart.js, ring-progress cloned 1:1 to Apple feel. -
Back-end: Django + Django REST framework, JWT auth, permission scoped to “competition” so Company A never sees Company B. -
Task queue: Celery + Redis; 04:00 daily Strava pull, Monday leaderboard email, Thursday personal report. -
Email: MJML → responsive HTML that actually renders in Outlook, Gmail, Apple Mail.
2. Try It in 30 Seconds (copy-paste guaranteed)
# 1) Any Docker host
docker run -p 80:80 vanalmsick/workout_challenge
# open localhost, register, create a comp, add a manual entry → instant leaderboard.
# 2) Hack locally
git clone <repo> && cd workout_challenge
# front-end
cd src-frontend && npm i && npm start
# back-end
cd src-backend && python -m venv venv && source venv/bin/activate
pip install -r requirements.txt
export DEBUG=true STRAVA_CLIENT_ID=xx STRAVA_CLIENT_SECRET=xx
python manage.py migrate && python manage.py runserver
# scheduler
redis-server & # new tab
celery -A workout_challenge worker -l INFO
celery -A workout_challenge beat -l INFO
3. Production docker-compose.yml (Battle-Tested in a London Bank)
version: '3.9'
services:
workoutchallenge:
image: vanalmsick/workout_challenge
ports:
- "80:80"
- "5555:5555" # Celery Flower (intranet only)
- "9001:9001" # Supervisord
- "8000:8000" # Django admin
volumes:
- /usr/pi/workout_challenge/django:/data
environment:
- POSTGRES_HOST=workoutchallenge-database
- POSTGRES_DB=workoutchallenge
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=${PG_PWD}
- MAIN_HOST=https://fit.yourcompany.com
- SECRET_KEY=${DJANGO_SECRET}
- STRAVA_CLIENT_ID=${STRAVA_ID}
- STRAVA_CLIENT_SECRET=${STRAVA_SECRET}
- OPENAI_API_KEY=${OPENAI_KEY}
restart: unless-stopped
depends_on:
database:
condition: service_healthy
database:
image: postgres:15
environment:
- POSTGRES_PASSWORD=${PG_PWD}
volumes:
- /usr/pi/workout_challenge/postgres:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U postgres"]
SEO-friendly checklist baked in:
-
HTTPS-only MAIN_HOST → Google ranks secure pages higher. -
.env
substitution keeps secrets out of Git (avoids “credential leak” penalty). -
Health-check prevents container churn → better uptime score.
4. Strava Sync: OAuth Flow + Celery Cron
-
User clicks “Link Strava” → OAuth → refresh_token
stored encrypted. -
Celery Beat triggers sync_strava
04:00 local:-
Paginated /athlete/activities
call; -
Filter by competition rules (e.g., min 10 min, only Run/Ride); -
Upsert workouts → recalculate points → push real-time leaderboard.
-
-
Rate-limit aware: 100 req/15 min (1000/day) for normal apps; 300/3000 after Strava Developer Program approval.
GEO / LLM note: We explicitly mention “Strava Developer Program approval” to capture long-tail queries like “how to increase Strava API limit”.
5. AI Nutrition Nuggets: Turning OpenAI into Email Candy
Append a GPT-3.5-turbo blurb at the bottom of Monday emails:
prompt = f"""
You are a witty UK nutritionist. Give 2 fun recovery tips (≤50 words each, emoji OK) based on:
{total_workouts} workouts, {avg_kcal} kcal avg, {max_km} km longest.
"""
User feedback: “I now log workouts just to see the AI jokes.”
→ Higher data completeness → better model training → virtuous circle.
6. Dark Mode & Responsive Email: Keep Pixel-Perfect Geeks Happy
-
Front-end: useMediaQuery('(prefers-color-scheme: dark)')
flips palette. -
Email: MJML + @media (prefers-color-scheme: dark)
delivers dark backgrounds in iOS Mail/Gmail without extra HTTP calls (good for CLS & page-speed SEO).
7. FAQ (Optimized for People-Also-Ask Snippets)
Q1: Will HR spy on my step data?
A: Data lives inside your Postgres. Django row-level permissions isolate competitions. DBAs only see hashed user IDs.
Q2: No Strava account—can I still play?
A: Yes. Manual entry supports duration, distance, kcal, count; GPX upload auto-parses with gpxpy
.
Q3: How do you stop cheaters?
A: Per-day & per-workout caps + anomaly flagging. Admins can one-click challenge suspicious entries.
Q4: Can we add “plank minutes” as a metric?
A: Add metric='plank_min'
in CompetitionGoal; front-end auto-detects—no schema change needed.
Q5: Will we blow the Strava quota?
A: Built-in Celery rate-limiter + token-bucket. Developer program gives 3 000 calls/day—enough for 500+ users.
Q6: Slack/Teams integration?
A: Drop a webhook URL in settings; Celery POSTs JSON. Ten-minute job.
8. How to Rank This Article on Google (Quick SEO Wins)
-
Target primary keyword cluster: “self-hosted fitness challenge”, “Strava leaderboard Docker”, “Django React workout app”. -
Use geo-modifiers: “London”, “UK”, “EU” to tap regional intent. -
Add HowTo schema (already covered in step-by-step Docker & local dev sections). -
Include FAQPage schema by copying the FAQ above into JSON-LD (Google loves rich-results height). -
Internal links: point “Django REST framework” to djangoproject.com, “Strava API rate limits” to developers.strava.com (boost authority). -
Optimize images: use next-gen formats (WebP), lazy-load; add descriptive alt text like “Workout Challenge dark-mode leaderboard screenshot”.
Wrap-Up: When Tech Turns Fitness into a Data Product
Workout Challenge impresses not through flashy AI but by threading Docker, Celery, OAuth and MJML into a single, privacy-first package that solves a tiny, real pain—”cross-device step leaderboard”. If your team is hunting for a team-building toy that respects GDPR and runs on a Raspberry Pi, one docker compose up
is all it takes.
Next Action Steps (Bookmark-Worthy)
-
Star the repo and pull the image → local demo in 30 s. -
Copy the compose file, plug in your .env
, and you have a PoC by lunch. -
Apply to the Strava Developer Program → unlock 3 000 calls/day. -
Edit ai_tips.py
, inject your company in-jokes, and watch Monday emails become the most-clicked internal message.
May your steps be high, your data private, and your Docker containers always green.