A Django-based web application for managing and participating in DataTalks.Club courses.
The platform supports course administration, homework and project submissions, peer review workflows, and course leaderboards. It also provides API access to course data.
The main features are:
- User authentication: registration and login.
- Course management: instructors can create and manage courses.
- Homework and projects: students can submit homework and projects.
- Peer reviews: students can evaluate project submissions from their peers.
- Leaderboard: course rankings based on submitted work and scores.
- API access: authenticated endpoints for course, homework, project, graduate, certificate, and OpenAPI data.
- Health check: a public endpoint for service monitoring.
The main directories are:
├── accounts/ # User accounts and authentication
├── api/ # OpenAPI schema and API tests
├── cadmin/ # Custom admin views
├── course_management/ # Django project settings and root configuration
├── courses/ # Course, homework, project, and review logic
├── data/ # Public and authenticated data API views
├── deploy/ # Deployment scripts
├── docs/ # Project documentation
├── notebooks/ # Analysis notebooks
├── templates/ # Shared Django templates
Install these tools before running the app:
- Python 3.13
- uv
- Docker and Docker Compose, for containerized local development
Install uv with the official installer:
curl -LsSf https://astral.sh/uv/install.sh | shInstall Python 3.13 and project dependencies:
uv python install 3.13
uv sync --devUse SQLite for local development:
export DATABASE_URL="sqlite:///db/db.sqlite3"Prepare the database:
make migrationsCreate an admin user:
make adminLoad sample data:
make dataRun the development server:
make runThe app will be available at:
http://localhost:8000
Useful direct Django commands:
uv run python manage.py makemigrations
uv run python manage.py migrate
uv run python manage.py createsuperuser
uv run python manage.py runserver 0.0.0.0:8000Run the Django test suite:
make testsDirect command:
uv run python manage.py test --timing --durations 30One-time scripts and temporary debug files should go in .tmp/, which is
ignored by git.
Start the app and PostgreSQL with Docker Compose:
docker-compose up --buildCompose starts:
web: the Django application on port 8000db: PostgreSQL 17ngrok: optional TCP tunnel for the database
The app will be available at:
http://localhost:8000
Build and run the application image without Compose:
docker build -t course_management .Then run the image:
DBDIR=`cygpath -w ${PWD}/db`
docker run -it --rm \
-p 8000:80 \
--name course_management \
-e DATABASE_URL="sqlite:////data/db.sqlite3" \
-e SITE_ID="${SITE_ID}" \
-v ${DBDIR}:/data \
course_managementOpen a shell in the running container:
docker exec -it course_management bashThe public health check endpoint returns service status and version:
GET /api/health/
Example:
curl http://localhost:8000/api/health/Response:
{
"status": "ok",
"version": "0.1.0"
}In local development, the version comes from the VERSION environment
variable and falls back to local-development-build-version-not-configured.
The platform can sync created users and course enrollments to Datamailer.
Set all required environment variables to enable the integration:
export DATAMAILER_URL="https://datamailer.dtcdev.click"
export DATAMAILER_API_KEY="<token>"
export DATAMAILER_CLIENT="dtc-courses"
export DATAMAILER_AUDIENCE="dtc-courses"
export DATAMAILER_FROM_EMAIL="courses"Optional settings:
export DATAMAILER_STRICT="0"
export DATAMAILER_SYNC_ON_USER_CREATE="1"With DATAMAILER_STRICT=0, Datamailer API failures are logged and don't
break signup or enrollment flows. Set DATAMAILER_STRICT=1 only when those
flows should fail on Datamailer errors.
PUBLIC_BASE_URL is used for links in delivered transactional email. Set it
when sending email from a local server so messages contain public HTTPS links
instead of localhost links.
export PUBLIC_BASE_URL="https://dev.courses.datatalks.club"Use the Compose override to run CMP with a local Datamailer in capture mode:
make datamailer_captureThe command expects the Datamailer repository at ../datamailer, starts
Datamailer on http://localhost:8001, seeds the local dtc-courses API key,
and points CMP at http://datamailer-web:8000 inside the Compose network.
CMP remains available at http://localhost:8000.
Datamailer captures rendered transactional and campaign messages instead of
sending real email. Look at captured subject, text, and HTML in Datamailer at
/api/testbed/runs. You can also use the Datamailer UI to review links,
metadata, and suppression details.
Deadline reminder emails are triggered by a short CMP management command:
uv run python manage.py send_deadline_remindersFor deployed environments, this command is run on a schedule as a one-off ECS
task. The recurring trigger (an EventBridge/CloudWatch rule targeting
ecs:RunTask with the send_deadline_reminders command override) and its
invocation role are provisioned via Terraform in DataTalksClub/infra-terraform,
alongside the CMP ECS service. CMP doesn't configure the schedule.
The scheduled task exits after reconciling Datamailer recipient lists and triggering Datamailer list sends. Datamailer handles per-recipient delivery asynchronously.
Datamailer transactional template keys are stable code-level constants in CMP. Don't configure one environment variable per template.
The CMP/Datamailer integration, including the conceptual design and API
reference, is documented in
docs/datamailer-integration.md.
Most /api endpoints require token authentication:
Authorization: Token <token>
Example:
TOKEN="TEST_TOKEN"
HOST="http://localhost:8000"
COURSE="fake-course"
HOMEWORK="hw1"
curl \
-H "Authorization: Token ${TOKEN}" \
"${HOST}/api/courses/${COURSE}/homeworks/${HOMEWORK}/submissions"Run make data to create sample data, including an authentication token.
See endpoints.md for API documentation, including:
- OpenAPI specification
- Public course criteria
- Public health check
- Public leaderboard data
- Homework data
- Project data
- Graduate data
- Certificate updates
- Course management API
OAuth is optional for local testing.
To configure Google OAuth locally:
- Go to the admin panel at
http://localhost:8000/admin. - Add a record to
Sites. - Use
localhost:8000for the display name and domain name. - Note the site ID, usually
2. - Add a record to
Social applications. - Use
GoogleDTCas the name andGoogleas the provider. - Ask for the local OAuth keys. Don't share them publicly.
- Attach the application to the localhost site.
Export the local site ID:
export SITE_ID=2Restart the app:
uv run python manage.py runserver 0.0.0.0:8000Then log out as admin and log in with Google.
Example SSH config for an RDS tunnel:
Host bastion-tunnel
HostName <IP>
User ubuntu
IdentityFile c:/Users/alexe/.ssh/<KEY>.pem
LocalForward 5433 dev-course-management-cluster.cluster-cpj5uw8ck6vb.eu-west-1.rds.amazonaws.com:5432
ServerAliveInterval 60
Connect to the bastion:
ssh bastion-tunnelConnect to PostgreSQL through the tunnel:
pgcli -h localhost -p 5433 -u pgusr -d coursemanagementWhen connecting for the first time, create the dev and prod databases:
CREATE DATABASE dev;
CREATE DATABASE prod;Open a Django shell against dev:
export DATABASE_URL="postgresql://pgusr:${DB_PASSWORD}@localhost:5433/dev"
export SECRET_KEY="${DJANGO_SECRET}"
uv run python manage.py shellOpen a Django shell against prod:
export DATABASE_URL="postgresql://pgusr:${DB_PASSWORD}@localhost:5433/prod"
export SECRET_KEY="${DJANGO_SECRET}"
uv run python manage.py shellFind a user by email:
from django.contrib.auth import get_user_model
User = get_user_model()
user = User.objects.get(email="test@gmail.com")