Skip to content

Getting Started

This guide walks you through setting up RotaCC locally for development. You can choose between Docker Compose (quickest path) or a full local development setup (most control).

Prerequisites

Tool Version Notes
Python 3.12+ Required for local dev (django-unfold ≥ 0.103.0 needs 3.12; 3.13 matches CI/production)
PostgreSQL 15+ Required for local dev (or use SQLite)
Redis 7+ Required for Celery (or use Docker just for Redis)
Docker & Docker Compose Any recent Optional but recommended
uv Latest Install uv

The fastest way to get the full stack running — PostgreSQL, Redis, Celery worker, and the Django app, all containerized.

1. Clone the repository and configure environment:

git clone <repository-url>
cd rota-cc
cp .env.example .env

Edit .env and set at minimum:

SECRET_KEY=change-this-to-something-secure
DJANGO_SUPERUSER_USERNAME=admin
DJANGO_SUPERUSER_EMAIL=admin@example.com
DJANGO_SUPERUSER_PASSWORD=changeme

2. Build and start all services:

docker compose up --build

This starts four services: web (Gunicorn on port 8000), celery_worker (task processing with beat scheduler), postgres (15-alpine), and redis (7-alpine). The entrypoint script runs migrations and creates the superuser automatically.

3. Open the application:

4. Load sample data (optional):

docker compose exec web uv run python manage.py populate_dev_data

Useful commands for working with the Docker setup:

docker compose logs -f              # Stream logs from all services
docker compose logs -f web          # Stream logs from web only
docker compose down                 # Stop all services
docker compose down -v              # Stop services and delete volumes (resets DB)

Option 2: Local Development

This gives you the most control. The dev settings module (rota.settings.dev) uses SQLite and in-memory cache by default, so you do not need PostgreSQL or Redis for basic development.

1. Clone the repository and install dependencies:

git clone <repository-url>
cd rota-cc
uv sync

2. Configure environment:

cp .env.example .env

For local development with SQLite (no PostgreSQL needed), keep the defaults in .env and set:

DJANGO_SETTINGS_MODULE=rota.settings.dev

The dev settings module overrides the database to SQLite, uses in-memory cache instead of Redis, enables the Django Debug Toolbar, and disables SSL/CSRF security requirements.

3. Run migrations and create an admin user:

uv run python manage.py migrate
uv run python manage.py createsuperuser

4. Load sample data (optional):

uv run python manage.py populate_dev_data

5. Start the development server:

uv run python manage.py runserver

6. Start Celery (separate terminal):

For Celery you need Redis running. You can either install Redis locally or run just the Redis container:

# Option A: Run Redis via Docker
docker run -d --name redis -p 6379:6379 redis:7-alpine

# Option B: If Redis is installed locally
# No action needed

Then start the Celery worker:

uv run celery -A rota worker -l info

Note

Without Celery running, rota generation tasks will not execute. The rest of the app (viewing shifts, managing leave, etc.) works fine without it.

7. Open the application:

Using PostgreSQL locally

If you prefer to run against PostgreSQL instead of SQLite, set these in your .env:

DB_ENGINE=django.db.backends.postgresql
DB_NAME=rota_db
DB_USER=rota_user
DB_PASSWORD=rota_password
DB_HOST=localhost
DB_PORT=5432
REDIS_HOST=localhost
CELERY_BROKER_URL=redis://localhost:6379/0
CELERY_RESULT_BACKEND=redis://localhost:6379/0

You can still use DJANGO_SETTINGS_MODULE=rota.settings.dev alongside these database settings — the dev settings provide the debug toolbar and development-friendly defaults.

Running Tests

Tests use pytest with django-configurations. The development settings module (rota.settings.dev) is used for testing, configured automatically via pyproject.toml.

# Run all tests
uv run pytest

# Run with coverage report (generates htmlcov/)
uv run pytest --cov=. --cov-report=html

# Run tests for a specific app
uv run pytest config/tests/

# Run tests in parallel (uses pytest-xdist)
uv run pytest -n auto

# Run a specific test file
uv run pytest rota_generation/tests/test_algorithm.py

# Verbose output
uv run pytest -v

Code Quality

The project uses ruff for linting and formatting, and mypy for type checking.

# Lint (check for errors and style issues)
uv run ruff check .

# Auto-fix lint issues
uv run ruff check --fix .

# Format code
uv run ruff format .

# Check formatting without writing
uv run ruff format --check .

# Type checking
uv run mypy .

Environment Variables

All configuration is loaded from a .env file in the project root using python-decouple. Copy .env.example to .env to get started.

Core Settings

Variable Required Default Description
SECRET_KEY Yes (prod) Insecure fallback Django secret key
DEBUG No False Enable Django debug mode
DEPLOYMENT_ENV No PROD Environment banner: DEV, STAGING, or PROD
ALLOWED_HOSTS No localhost,127.0.0.1 Comma-separated allowed hosts
CSRF_TRUSTED_ORIGINS No http://localhost:8000 Comma-separated CSRF trusted origins
DJANGO_SETTINGS_MODULE No rota.settings.base Settings module to load

Database

Variable Required Default Description
DB_ENGINE No django.db.backends.sqlite3 Database backend (django.db.backends.postgresql or django.db.backends.sqlite3)
DB_NAME No rota_db Database name (or path for SQLite)
DB_USER No rota_user Database user (PostgreSQL only)
DB_PASSWORD No rota_password Database password (PostgreSQL only)
DB_HOST No localhost Database host (postgres in Docker)
DB_PORT No 5432 Database port

Redis and Celery

Variable Required Default Description
REDIS_HOST No localhost Redis host (redis in Docker)
REDIS_PORT No 6379 Redis port
REDIS_DB No 0 Redis database number
REDIS_USER No (empty) Redis username (for authenticated providers)
REDIS_PASS No (empty) Redis password (for authenticated providers)
CELERY_BROKER_URL No Built from Redis config Celery broker URL
CELERY_RESULT_BACKEND No Same as broker Celery result backend URL

Email

Variable Required Default Description
EMAIL_PROVIDER No smtp Email provider: smtp, mailgun, sendgrid, postmark, resend, or console
DEFAULT_FROM_EMAIL No noreply@rota.system Default sender address
EMAIL_HOST No localhost SMTP server hostname
EMAIL_PORT No 587 SMTP server port
EMAIL_HOST_USER No (empty) SMTP username
EMAIL_HOST_PASSWORD No (empty) SMTP password
EMAIL_USE_TLS No False Enable TLS for SMTP
MAILGUN_API_KEY No (empty) Mailgun API key
MAILGUN_SENDER_DOMAIN No (empty) Mailgun sender domain
SENDGRID_API_KEY No (empty) SendGrid API key
POSTMARK_SERVER_TOKEN No (empty) Postmark server token
RESEND_API_KEY No (empty) Resend API key
SITE_URL No http://localhost:8000 Base URL used in email links

Security (Production)

Variable Required Default Description
SECURE_SSL_REDIRECT No True Redirect HTTP to HTTPS
SECURE_HSTS_SECONDS No 31536000 HSTS max-age (1 year)
DJANGO_LOG_LEVEL No INFO Logging level: DEBUG, INFO, WARNING, ERROR, CRITICAL

Application

Variable Required Default Description
FIXED_PERIOD_WEEKS No 6 Number of weeks in the fixed period
CURRENT_DATE No Today's date Override current date (format: YYYY-MM-DD)

Superuser (Auto-Creation)

Variable Required Default Description
DJANGO_SUPERUSER_USERNAME No (empty) Auto-created superuser username
DJANGO_SUPERUSER_EMAIL No (empty) Auto-created superuser email
DJANGO_SUPERUSER_PASSWORD No (empty) Auto-created superuser password

Leave all three empty to skip auto-creation.

Superuser (debug view bootstrap)

Variable Required Default Description
GENERATE_SUPERADMIN_IF_NONE_WITH_PW No (empty) Password for the superuser the /debug/unfold/ debug view creates when none exists. Empty disables creation — the view then only explains how to create one. Explicit opt-in, independent of DEBUG. While set, superusers see a site-wide warning banner (frontend and Django admin) urging them to remove the variable and rotate the password

Management Commands

These are Django management commands you will use during development and operations.

Command Description
uv run python manage.py populate_dev_data Load sample data for development (clinicians, shifts, leave)
uv run python manage.py ensure_superuser Create superuser from env vars (idempotent)
uv run python manage.py create_clinician_user Create a clinician user interactively
uv run python manage.py run_celery Start Celery worker (wrapper command)
uv run python manage.py backup_data Export data as JSON backup
uv run python manage.py restore_data Restore from JSON backup
uv run python manage.py pg_dump_backup PostgreSQL-level dump backup
uv run python manage.py pg_restore_backup Restore from PostgreSQL dump
uv run python manage.py cleanup_old_backups Remove backups older than retention period
uv run python manage.py clear_audit_log Clear the audit trail

Settings Architecture

Django settings are split across three modules:

Module Purpose
rota.settings.base Shared configuration — database, Celery, email, installed apps, middleware
rota.settings.dev Development overrides — SQLite, LocMem cache, Debug Toolbar, verbose logging
rota.settings.prod Production overrides — PostgreSQL, Redis, Gunicorn, security hardening

To switch between them, set DJANGO_SETTINGS_MODULE in your .env:

# Development (SQLite, no external services needed)
DJANGO_SETTINGS_MODULE=rota.settings.dev

# Production config (used by Docker Compose)
DJANGO_SETTINGS_MODULE=rota.settings.prod

Next Steps

Once you have the application running: