Files
Ross a573ed78a4 Enhance Supervisor Dashboard and Trainee Feedback Features
- Redesigned the supervisor overview page to include a header card with supervisor details and trainee statistics.
- Implemented a tabbed interface for viewing trainees and their recent attempts.
- Improved the trainee detail page with a profile card and a table for displaying attempt history.
- Added a new view for supervisors to provide feedback on case collections, including outstanding feedback items and previous conversations.
- Updated URL routing to support new feedback functionality.
- Enhanced backend logic to ensure proper sharing permissions for exam attempts and case collections.
- Added comprehensive tests for supervisor access to trainee data and feedback functionalities.
2026-06-15 12:28:00 +01:00

7.8 KiB

RAD Project — Agent Guidelines

Django-based radiology education platform. Multi-app, HTMX-driven, PostgreSQL + Redis + Celery stack.

Architecture

Apps

generic · anatomy · physics · rapids · shorts · longs · sbas · wally · atlas · rcr · rota · oef

  • generic/ — shared base models, views, forms, mixins, decorators, widgets. Always check here before adding new base classes or utilities.
  • atlas/ — CaseCollection-based learning (DICOM cases, findings, display sets, differentials)
  • rad/ — project config (settings.py, urls.py, celery.py, root API)

Request/Response Pattern

  • HTMX-first: if request.htmx: returns a partial, else returns a full page.
  • Full pages extend base.html. HTMX partials live in {app}/templates/{app}/partials/.
  • Partials must be standalone fragments — no {% extends %}.
  • HTMX detection via django_htmx middleware (request.htmx).

HTMX input

  • When using htmx input for autoupdating search/other triggers triggers do not use changed in the hx-trigger. Using input is sufficient. e.g. instead of hx-trigger="input changed delay:300ms, focus" use hx-trigger="input delay:300ms, focus"

API

  • Django Ninja (not DRF). Routers in {app}/api.py, registered in rad/api.py at /api/v1/.
  • Use ModelSchema with explicit Meta.fields lists (avoid "__all__" on public endpoints).

Models

  • Auto-timestamp: use created_at = models.DateTimeField(auto_now_add=True) — match the convention already on the model you are editing.
  • Primary keys: default Django AutoField (integer). Do not switch to UUID without discussion.
  • Audit trail: wrap models that need history with @reversion.register().
  • Base classes in generic/models.py:
    • QuestionBase, ExamBase, UserAnswerBase — exam content
    • AuthorMixin — M2M authors field
    • CidUser, CidUserGroup — cohort-based access
  • After any model change: run python manage.py makemigrations && python manage.py migrate.

Views

  • Mix of FBVs and CBVs. Prefer the pattern already used in the app you are editing.
  • Login: @login_required (FBV) or LoginRequiredMixin (CBV).
  • Permission decorators in generic/decorators.py: @user_is_cid_user_manager, @check_user_in_group.
  • Key CBV mixins in generic/views.py: CidManagerRequiredMixin, AuthorRequiredMixin, CheckCanEditMixin.
  • Do not expose can_edit=True to candidate-facing views. Management and candidate views are intentionally separate.
  • When editing views please update docstrings to include their scope and functionality.

Templates

  • Inheritance: all full pages use {% extends "base.html" %}.
  • Partials path: {app}/templates/{app}/partials/_fragment_name.html (prefix with _).
  • Template tags: always load {% load static %}, {% load django_htmx %}, {% load crispy_forms_tags %} as needed at the top of the file.
  • Bootstrap 5 (dark theme). Use existing utility classes; do not add inline styles.
  • To include a partial: {% include 'app/partials/_fragment.html' %}. Pass context explicitly when using with keyword.
  • Multi line comment NEVER use {# ... #} style comments for multiline comments as it will render into the html, it is designed for single line comments only. Use {% comment "Optional note: You can add a label here if you want" %} This is a multiline comment. None of this text or any {% tags %} inside here will be rendered or visible in the browser's "View Source". {% endcomment %} Instead

Forms

  • Crispy Forms with crispy_bootstrap5. All forms should use a FormHelper.
  • Layout, Fieldset, Div, Row, Column from crispy_forms.layout for structure.
  • Custom widgets in generic/widgets.py — check before creating new widgets.
  • Autocomplete fields use django-autocomplete-light (dal).

Testing

  • Runner: pytest (config in pytest.ini; --nomigrations is on by default).
  • Files: {app}/tests/test_*.py or {app}/tests.py.
  • Fixtures: shared fixtures in {app}/tests/conftest.py. Check generic/tests/conftest.py for common fixtures (create_user, create_examination, etc.).
  • Do not use TestCase unless necessary — prefer plain pytest functions with @pytest.mark.django_db.
  • Run tests: pytest or pytest {app}/tests/.

Exceptions

  • Avoid unnecessary try: except blocks.
  • We should not generate empty excepty blocks, if there is an error or bug this should be surfaced (even if just to the logs).

Celery / Tasks

  • Tasks autodiscovered from {app}/tasks.py.
  • Broker and result backend: Redis at redis://redis:6379 (Docker service name).
  • Import the shared Celery app: from rad.celery import app as celery_app.

Static & Media

  • Static: python manage.py collectstatic --noinput. App static dirs at {app}/static/.
  • Media: custom HashedFilenameFileSystemStorage. Do not assume filenames are original.
  • DICOM thumbnails via easy_thumbnails + helpers.images.pil_dicom_image.

Settings & Secrets

  • Never hardcode secrets. Use settings_local.py (gitignored) or environment variables.
  • Env vars: SECRET_KEY, DB_NAME/USER/PASSWORD/HOST/PORT, MEMCACHE_HOST, DEBUG.
  • Local overrides: rad/settings_local.py (imported at the bottom of settings.py).

Docker (Dev)

Start Dev Environment

Start the development server with local configurations:

COMPOSE_ENV=dev docker compose -f docker/docker-compose.local.yml -f docker/docker-compose.dev.yml up -d --build

Run Django Management Commands inside the Container

Since PostgreSQL and Redis run in docker, run all database/management commands within the web container:

# Run database migrations
COMPOSE_ENV=dev docker compose -f docker/docker-compose.local.yml -f docker/docker-compose.dev.yml exec web python manage.py migrate

# Generate new migrations
COMPOSE_ENV=dev docker compose -f docker/docker-compose.local.yml -f docker/docker-compose.dev.yml exec web python manage.py makemigrations

# Check if migrations are missing (useful for CI/pre-commit)
COMPOSE_ENV=dev docker compose -f docker/docker-compose.local.yml -f docker/docker-compose.dev.yml exec web python manage.py makemigrations --check

# Run system checks
COMPOSE_ENV=dev docker compose -f docker/docker-compose.local.yml -f docker/docker-compose.dev.yml exec web python manage.py check

# Create a Django superuser
COMPOSE_ENV=dev docker compose -f docker/docker-compose.local.yml -f docker/docker-compose.dev.yml exec web python manage.py createsuperuser

Runserver and service commands

To restart the web server/runserver or tail the container output:

# Restart the runserver/web service container
COMPOSE_ENV=dev docker compose -f docker/docker-compose.local.yml -f docker/docker-compose.dev.yml restart web

# View container logs (interactive)
COMPOSE_ENV=dev docker compose -f docker/docker-compose.local.yml -f docker/docker-compose.dev.yml logs -f web

Dev server runs on http://localhost:8080 (nginx → gunicorn/runserver).

Key Conventions

Convention Detail
HTMX partial detection if request.htmx: return render(...)
Partial naming _fragment_name.html with leading underscore
Permission checks Decorators/mixins from generic/; never inline if user.is_staff in templates
Model timestamps created_at (auto_now_add) preferred
API schemas Django Ninja ModelSchema; explicit field lists
Audit trail @reversion.register() on audited models
Logging loguru (from loguru import logger)
Candidate vs management views Keep separate; can_edit must not leak to candidate views

Before Opening a PR

  1. pytest — all tests pass
  2. python manage.py makemigrations --check — no missing migrations
  3. python manage.py check — no system check errors
  4. No hardcoded secrets or debug prints