No matter if a healthy close your rings competition with friends or a September steps challenge with work colleagues, this webapp enables you to compete with friends and co-workers across devices (Apple / Android / Garmin / etc.) using the metrics you want to use (km / minutes / kcal / # of times / etc.) respecting your privacy. Participants can either add their workouts and/or steps manually or link their free Strava account for automatic workout import.
Create your own competition or use a friend’s invitation link to join their competition, enter your workouts manually or link your Strava for automatic workout import, and enjoy the competition. You can customize each competition's activity goals individually. Participants earn 1 point for every 1% progress towards a goal. For example, if the goal is 100 minutes of exercise and you work out 50 minutes, you earn 50 points. Additionally, you can cap / floor the maximum / minimum points participants can earn per workout / day / week to e.g. ensure a healthy competition that is focused on consistency.
Features:
- Create your own competition or use a friend’s invitation link to join their competition
- Enter workouts manually or import them automatically via Strava (daily at 4 AM)
- Your personal dashboard shows workout stats and your workout streak
- The competition dashboards show friends’ workouts, leaderboards, and your progress towards the competition goals
- A weekly email on Mondays shows you your spot on the competition leaderboards
- An optional weekly email on Thursday shows your progress against your personal goals
- Fully responsive website and emails (mobile, tablet, desktop)
- Light and dark mode
Competition Goal Choices:
Create one or several goals for your competition.
- Metrics: time (minutes) / number of times (#) / distance (km) / calories (kcal) / kilojoules (kj)
- Period: per day / per week / per month / during the entire competition
- Limits: min / max per workout, min / max per day, min / max per week
docker run -p 80:80 -e ALLOW_ALL_HOSTS=true vanalmsick/workout_challenge
version: '3.9'
services:
workoutchallenge:
image: vanalmsick/workout_challenge
container_name: workoutchallenge
ports:
- "80:80"
- "5555:5555" # Celery Flower task monitoring - do not open to public - only for local network for debugging
- "9001:9001" # Supervisord process monitoring - do not open to public - only for local network for debugging
- "8000:8000" # Django admin space - do not open to public - only for local network for debugging
volumes:
- /usr/pi/workout_challenge/django:/workout_challenge/src-backend/data
environment:
- POSTGRES_HOST=workoutchallenge-database
- POSTGRES_DB=workoutchallenge
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
- MAIN_HOST=http://your-url.com
- HOSTS=http://your-url.com,http://localhost,http://127.0.0.1
- SECRET_KEY=<your_random_string_for_encryption>
- TIME_ZONE=Europe/London
- STRAVA_CLIENT_ID=000000
- STRAVA_CLIENT_SECRET=<secret_key>
- SENTRY_DSN=https://<PUBLIC_KEY>@<HOST>/<PROJECT_ID>
- EMAIL_HOST=smtp.gmail.com
- EMAIL_PORT=465
- EMAIL_HOST_USER=competition@yourdomain.com
- EMAIL_HOST_PASSWORD=password
- EMAIL_USE_SSL=True
- EMAIL_USE_TLS=False
- EMAIL_FROM=competition@yourdomain.com
- EMAIL_REPLY_TO=support@yourdomain.com
- OPENAI_API_KEY=<secret_key>
restart: unless-stopped
depends_on:
database:
condition: service_healthy
database:
image: postgres:15
container_name: workoutchallenge-database
environment:
- POSTGRES_DB=workoutchallenge
- POSTGRES_USER=postgres
- POSTGRES_PASSWORD=password
ports:
- "5432:5432"
volumes:
- /usr/pi/workout_challenge/postgres:/var/lib/postgresql/data
restart: unless-stopped
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U postgres" ]
interval: 5s
timeout: 5s
retries: 5
docker compose -f /path/to/docker-compose.yml up
| Variable | Default | Definition |
|---|---|---|
| MAIN_HOST | "http://localhost" | The main hosting url for the Django backend. |
| HOSTS | "http://localhost,http://127.0.0.1" | Comma seperated list of hosts for Django. This is used for ALLOWED_HOSTS, CORS_ALLOWED_ORIGINS, and CSRF_TRUSTED_ORIGINS. |
| SECRET_KEY | [a hard-coded string in code] | Django's SECRET_KEY for cryptographic signing. |
| TIME_ZONE | "Europe/London" | Timezone for Django and Celery |
| DEBUG | false | Django's DEBUG mode. If true, CORS_ALLOW_ALL_ORIGINS will also be true and the CACHE will use Local Memory Cache instead of Redis. |
| ALLOW_ALL_HOSTS | false | For quick testing to not having to set HOSTS. Do not set to true in production but set HOSTS. |
| POSTGRES_HOST | None | If set to None, Django will use SQLite as database (might cause database lock errors in production), else this is the host url to the Postgres database. |
| POSTGRES_DB | "postgres" | Database name in Postgres database |
| POSTGRES_USER | "postgres" | Database username in Postgres database |
| POSTGRES_PASSWORD | "" | Database password in Postgres database |
| SENTRY_DSN | None | If None no Sentry.io error capturing, else please provide the project url https://<PUBLIC_KEY>@/<PROJECT_ID> (In local development you might have to set REACT_APP_SENTRY_DSN instead) |
| STRAVA_CLIENT_ID | "1234321" | Strava API Client Id. Please see below how to get one. (In local development you might have to set REACT_APP_STRAVA_CLIENT_ID instead) |
| STRAVA_CLIENT_SECRET | "ReplaceWithClientSecret" | Strava API Client Secret. Please see below how to get one. |
| STRAVA_LIMIT_15MIN | 100 | Strava API Limit per 15min. 300 if part of developer program, else 100. |
| STRAVA_LIMIT_DAY | 1000 | Strava API Limit per day. 3000 if part of developer program, else 1000. |
| REACT_APP_BACKEND_URL | "" | Overwrite the url to the Django API used by React. This is intended for local development outside of the docker container - e.g. http://localhost:8000. |
| EMAIL_HOST | None | SMTP server host url to send out automated emails. |
| EMAIL_PORT | None | SMTP server port to send out automated emails. |
| EMAIL_HOST_USER | None | SMTP server username to send out automated emails. |
| EMAIL_HOST_PASSWORD | None | SMTP server password to send out automated emails. |
| EMAIL_USE_SSL | False | SMTP server - if SSL ise used for authentication. |
| EMAIL_USE_TLS | False | SMTP server - if TLS ise used for authentication. |
| EMAIL_FROM | None | Sender email address of automated emails. |
| EMAIL_REPLY_TO | None | Reply-To email address of automated emails. |
| OPENAI_API_KEY | None | OpenAI API key to generate workout / health / nutritional facts for the weekly update email. |
- Login to your Strava account strava.com/login
- Profile picture -> Settings
- "My API Application"
- Test the workout challenge (only works with your own Strava account)
- If you like the workout challenge, apply to the Strava developer program here to also allow other users to link their Strava
- Docker Container for deployment
- Supervisord to start all processes in docker container
- Nginx to run frontend (React) and proxy traffic to backend (Django) which is run by gunicorn
Frontend (src-frontend)
Backend (src-backend)
- Django (RestAPI) via gunicorn for production
- Redis as cache and for Celery
- Celery as task que for Django
- Celery Beat for task scheduling for Django
- Celery Flower UI to inspect task que and task status
- mjml Framework / app to write responsive email html (mjml.io)
working dir: /workout_challenge/src-backend
run Redis: redis-server
run Celery Worker: celery -A workout_challenge worker --loglevel INFO --without-mingle --without-gossip --events
run Celery Beat: celery -A workout_challenge beat --scheduler django_celery_beat.schedulers:DatabaseScheduler --loglevel INFO
run Celery Flower: celery -A workout_challenge flower
Note: For testing email celery tasks, please set the Email env variables. For testing Strava sync celery tasks, please set the Strava env variables. For celery beat, don't forget to set the timezone env variable.
working dir: /workout_challenge/src-backend
suggested env variables:
PYTHONUNBUFFERED=1
DJANGO_SETTINGS_MODULE=workout_challenge.settings
DEBUG=true
MAIN_HOST=http://localhost
HOSTS=http://localhost,http://127.0.0.1
TIME_ZONE=Europe/London
STRAVA_CLIENT_ID=000000
STRAVA_CLIENT_SECRET=<secret_key>
initial Django setup: python manage.py makemigrations && python manage.py migrate
run Django: python manage.py runserver
working dir: /workout_challenge/src-frontend
suggested env variables:
REACT_APP_BACKEND_URL=http://localhost:8000
run React: npm start
- Add help text to "Link Strava Button" if Strava linkage is not set up
- Add explanation text for how Steps are converted
- Add tickbox for runs/walks if they count towards the total daily steps
- Group finished competitions in archive button
- Remove total daily steps from weekly email
- Add competition finish email
- Send competition start email if user joins after competition already started
- Add help text if users try joining competitions using non-invite link
- Add option for competition admin to remove users
- Improve README texts
- Improve repo link preview picture
- Add Garmin API support
- Add Futbit API suport
- Add friends page
- Add more personal statistics
- Add medals and achievements
- Add UI hint if old version of workout challenge is used


