SRC:
git clone git@gitlab.cirg.washington.edu:cnics/mci.git
DOCKER DATABASE:
cp .env.example .env
docker-compose up -d mariadb
# Initialization scripts load `init/04-create-patients.sql` which
# populates the `patients` table from `uw_patients2` if it does not
# already exist
This repository includes a lightweight Docker configuration based on the setup used in the asbi-screening-app project. The compose file builds the React frontend and Flask backend for local development.
-
Copy
.env.exampleto.envand edit if necessary. For unified-domain deployments, leaveVITE_API_URL(API: Application Programming Interface) empty (same-origin) so the frontend calls/api/...on the same host. -
Build the Docker images:
docker-compose build
-
Start the stack:
docker-compose up
The application will be served on
https://cnics-validation.pm.ssingh20.dev.cirg.uw.edu/and the API (Application Programming Interface) under the same origin at/api/.... The compose file mountsapp/webroot/filesinto the backend container so instruction documents are available at/files/<name>.
Runtime configuration is provided via a .env file that you create by
copying .env.example. Docker Compose automatically loads this file when the
services are built or started. The template defines the following variables:
DB_ROOT_PASSWORD– password for the MariaDB root user.DB_NAME– name of the application's database.DB_USER– database user for the application.DB_PASSWORD– password forDB_USER.VITE_API_URL– base URL of the backend API (Application Programming Interface) consumed by the React frontend. For unified-domain deployments leave empty to use same-origin.FRONTEND_ORIGIN– allowed origin for CORS (Cross‑Origin Resource Sharing) requests to the backend.FHIR_SERVER– URL of the FHIR (Fast Healthcare Interoperability Resources) server used by the application.FILES_DIR– directory containing instruction files served by the backend.DOWNLOADS_DIR– writable directory where the backend saves generated/downloadable artifacts (e.g., uploaded scrubbed ZIPs). Defaults to a subdirectory underFILES_DIRif not set.EXTERNAL_DB_URL– optional SQLAlchemy URL for a secondary database.
Override these values in your copied .env file as needed.
See docs/development.md for instructions on running the application with Docker. For a full end‑to‑end overview of setup, auth, data, and file flows, read docs/WORKFLOW.md.
The Flask backend under flask_backend/ exposes REST endpoints that the React frontend fetches. Docker Compose runs a backend service alongside the web frontend service. The frontend reads the API base URL from the VITE_API_URL environment variable; if unset it uses same-origin.
See docs/separation_of_duties.md for details on the responsibilities of each component.
-### Available Endpoints
/api/tables/<name>– return rows from a database table./api/events– events with patient site information./api/events/need_packets– events awaiting packet uploads./api/events/for_review– events with packets ready for review./api/events/need_reupload– events requiring packet re-upload./api/events/status_summary– counts of events grouped by status.
The backend supports header-based authentication via an Apache/Ldap front-end that injects an X-Remote-User header. When this header is present, the app looks up the authenticated user in the users table by the login field and attaches a compact identity to the Flask request context. Role flags (admin, uploader, reviewer, third_reviewer) are enforced via decorators:
@requires_auth– required for all API endpoints; ifX-Remote-Useris present, the user must exist in the database or a 403 is returned.@requires_roles("role1", ...)– require all named roles (enforced only when header auth is in use).@requires_any_role("role1", ...)– require at least one of the named roles (enforced only when header auth is in use).
Current role protections applied:
- Admin only:
POST /api/events,GET /api/events/status_summary,POST /api/users - Reviewer/uploader/admin:
GET /api/events/need_packets,GET /api/events/need_reupload - Reviewer/admin:
GET /api/events/for_review
Frontend loads the current user via GET /api/auth/me and renders UI based on the returned flags.
Outstanding next steps:
- Confirm Apache is consistently sending
X-Remote-Userand decide on normalization (email vs. netid); ensureusers.loginvalues match. - Add/seed required users (e.g., Satinder) with
loginfilled and appropriate role flags. - Review and refine per-endpoint role requirements; extend decorators where needed.
- Decide whether to require header auth in all environments or keep the permissive dev/Keycloak fallback.
Run python scripts/generate_openapi.py to generate openapi.json describing
the backend API. A GitHub action updates this file on each push.
flask_backend/models2.py is a copy of models.py with all back_populates
arguments removed from the relationship() definitions. The current
application still imports models.py and does not use models2.py.
Removing back_populates turns these relationships into one‑way links. SQLA
chemy will no longer keep both sides of a relationship in sync automatically.
For example, appending a Criterias object to Events.criterias will not set
the corresponding Criterias.event attribute unless done manually. The new
file is included for future experimentation and has no effect on the running
code.
Two small scripts illustrate the difference:
scripts/demo_back_populates.py– usesflask_backend.models(withback_populates).scripts/demo_no_back_populates.py– usesflask_backend.models2(withoutback_populates).
Run them after your database is up (e.g., with Docker Compose). If running directly, ensure the project root is on PYTHONPATH or run via python -m from the repo root:
docker-compose up -d mariadb
export DB_USER=root
export DB_PASSWORD=${DB_ROOT_PASSWORD}
export DB_HOST=127.0.0.1
export DB_NAME=cnics
# Option 1: run scripts directly (they add the repo root to sys.path)
python3 scripts/demo_back_populates.py
python3 scripts/demo_no_back_populates.py
# Option 2: run as modules
python3 -m scripts.demo_back_populates
python3 -m scripts.demo_no_back_populates
To switch which models the backend uses globally, set `SQLA_MODELS`:
```bash
# Use standard models with back_populates (default)
export SQLA_MODELS=models
# Or use the alternate one-way models
export SQLA_MODELS=models2
The first script will show that the child object's `.event` is synchronized in-memory upon append, while the second script will not.
## File handling and storage
The backend uses two configurable directories for file operations:
- `FILES_DIR`: read-only documents served to the frontend (e.g., instructions under `/files/<name>`).
- `DOWNLOADS_DIR`: writable area for generated or uploaded artifacts (e.g., scrubbed ZIP files) served by `/api/events/download/<id>`.
You can configure these via environment variables. If `DOWNLOADS_DIR` is not set, it falls back to `FILES_DIR/downloads`.
For containerized deployments, choose one of the following:
1) Bind-mount (convenient for development, visible on the host):
```yaml
services:
backend:
environment:
FILES_DIR: /files
DOWNLOADS_DIR: /downloads
volumes:
- ./app/webroot/files:/files:ro # read-only docs
- ./downloads:/downloads # writable artifacts
- Named Docker volumes (isolated, easier lifecycle via
docker volume ls):
services:
backend:
environment:
FILES_DIR: /files
DOWNLOADS_DIR: /downloads
volumes:
- cnics-files:/files:ro
- cnics-downloads:/downloads
volumes:
cnics-files:
cnics-downloads:Notes:
- If files do not need to be accessed by external host processes (confirmed), named volumes are a good default for production.
- Ensure sufficient disk space on the VM hosting Docker; containers do not need their own disk allocation.
- Keep the upload/download locations configurable via env so staging/prod can use different mounts.