6.0 KiB
6.0 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_htmxmiddleware (request.htmx).
API
- Django Ninja (not DRF). Routers in
{app}/api.py, registered inrad/api.pyat/api/v1/. - Use
ModelSchemawith explicitMeta.fieldslists (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 contentAuthorMixin— M2M authors fieldCidUser,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) orLoginRequiredMixin(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=Trueto 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 usingwithkeyword. - 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 aFormHelper. Layout,Fieldset,Div,Row,Columnfromcrispy_forms.layoutfor structure.- Custom widgets in
generic/widgets.py— check before creating new widgets. - Autocomplete fields use
django-autocomplete-light(dal).
Testing
- Runner:
pytest(config inpytest.ini;--nomigrationsis on by default). - Files:
{app}/tests/test_*.pyor{app}/tests.py. - Fixtures: shared fixtures in
{app}/tests/conftest.py. Checkgeneric/tests/conftest.pyfor common fixtures (create_user,create_examination, etc.). - Do not use
TestCaseunless necessary — prefer plainpytestfunctions with@pytest.mark.django_db. - Run tests:
pytestorpytest {app}/tests/.
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 ofsettings.py).
Docker (Dev)
# Start dev environment
COMPOSE_ENV=dev docker compose -f docker/docker-compose.dev.yml up -d --build
# Run management commands inside the container
docker compose exec web python manage.py migrate
docker compose exec web python manage.py createsuperuser
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
pytest— all tests passpython manage.py makemigrations --check— no missing migrationspython manage.py check— no system check errors- No hardcoded secrets or debug prints