Neural EXpert Unified System for Clinical Data Management
Live: https://nexuscdm.online | Repo: github.com/hitakshiA/nexus-cdm
A full-stack clinical data management platform built for the Novartis NEST 2.0 Hackathon (Problem Statement 1). NEXUS CDM transforms raw clinical trial data from 203 Excel files across 23 studies into an AI-powered, role-aware dashboard with real-time analytics, regulatory-grade audit trails, and multi-agent decision support.
Regulatory Compliance: ICH E6(R3) RBQM, 21 CFR Part 11, ALCOA+ Data Integrity
# 1. Clone the repository
git clone https://github.com/hitakshiA/nexus-cdm.git
cd nexus-cdm
# 2. Configure environment
cp .env.example .env
# Edit .env — add your GROQ_API_KEY (free at https://console.groq.com)
# 3. Copy clinical study data (23 studies, 203 Excel files)
cp -r /path/to/QC_Anonymized_Study_Files/* data/studies/
# 4. One command to launch everything
docker compose up --build
# This automatically: builds containers, runs migrations, seeds users, ingests all 23 studies
# 5. Open the app
open http://localhost:3000Note: The Groq API key is free — sign up at console.groq.com and create an API key. The AI agents work with the free tier. Without a key, all features except AI chat will work normally.
Login with any demo user (password: nexus2025!):
| User | Role | |
|---|---|---|
| Sarah Chen | CTT Lead | sarah.chen@nexus.dev |
| Michael Torres | CRA | michael.torres@nexus.dev |
| Jennifer Kim | Data Manager | jennifer.kim@nexus.dev |
| Dr. Robert Singh | Medical Monitor | robert.singh@nexus.dev |
| Lisa Park | Site Coordinator | lisa.park@nexus.dev |
| James Wright | Regulatory | james.wright@nexus.dev |
Each role sees a different dashboard experience — different navigation, different data visibility, and different action capabilities.
graph TB
subgraph Frontend ["Frontend (Port 3000)"]
WEB["Next.js 16<br/>React 19 + TypeScript<br/>Tailwind CSS 4 + Recharts<br/>Zustand + shadcn/ui"]
end
subgraph Backend ["Backend (Port 8000)"]
API["FastAPI<br/>64 REST endpoints + 1 WebSocket<br/>6-role RBAC + JWT Auth<br/>21 CFR Part 11 Audit"]
AGENTS["AI Agent System<br/>3 Specialists × 21 Tools<br/>LLM Routing + SSE Streaming"]
end
subgraph Data ["Data Layer"]
PG["PostgreSQL 16<br/>24 tables · 9 enums<br/>1.3M+ CRF records"]
REDIS["Redis 7<br/>64MB LRU Cache<br/>AOF Persistence"]
end
subgraph External ["External"]
LLM["Groq LLM API<br/>openai/gpt-oss-120b"]
EXCEL["203 Excel Files<br/>23 Clinical Studies"]
end
WEB -->|"HTTP / SSE / WebSocket"| API
API --> PG
API --> REDIS
API --> AGENTS
AGENTS -->|"Tool Calls (Live DB)"| PG
AGENTS -->|"LLM Inference"| LLM
EXCEL -->|"11 Parsers"| API
sequenceDiagram
participant U as User (Browser)
participant W as Next.js Frontend
participant A as FastAPI Backend
participant R as RBAC Middleware
participant D as PostgreSQL
participant AI as AI Agents
U->>W: Login (email + password)
W->>A: POST /auth/login
A->>D: Verify credentials
A-->>W: JWT access + refresh tokens
W-->>U: Role-specific dashboard
U->>W: Navigate to study
W->>A: GET /studies/:id/overview
A->>R: Check JWT + permissions
R->>D: Load user + verify role
D-->>A: Study data
A-->>W: Filtered response (role-aware)
W-->>U: Render dashboard
U->>W: Ask AI agent question
W->>A: POST /agents/chat/stream
A->>AI: Route to specialist agents
AI->>D: Execute 21 tools (live queries)
AI-->>A: Synthesized response
A-->>W: SSE stream (tokens)
W-->>U: Streaming AI response
graph LR
USER["User Query"] --> ROUTER["LLM Router<br/>Intent Classification"]
ROUTER --> DS["Data Steward<br/>9 Tools"]
ROUTER --> MM["Medical Monitor<br/>5 Tools"]
ROUTER --> REG["Regulatory<br/>7 Tools"]
DS --> TOOLS_D["query_status · missing_pages<br/>dqi_status · visit_compliance<br/>site_performance · patient_record<br/>crf_status · clean_patients · cra_perf"]
MM --> TOOLS_M["sae_reconciliation · coding_completion<br/>safety_signals · protocol_deviations<br/>prioritized_review"]
REG --> TOOLS_R["db_lock_readiness · audit_trail<br/>regulatory_metrics · compliance_report<br/>statistical_readiness · cdisc · site_dqi"]
TOOLS_D --> DB[(PostgreSQL)]
TOOLS_M --> DB
TOOLS_R --> DB
DS --> SYNTH["Result Synthesis<br/>Safety > Compliance > Data"]
MM --> SYNTH
REG --> SYNTH
SYNTH --> STREAM["SSE Stream<br/>to Frontend"]
erDiagram
USERS ||--o{ USER_ASSIGNMENTS : has
USERS ||--o{ COMMENTS : writes
USERS ||--o{ ACTION_ITEMS : creates
USERS ||--o{ AGENT_SESSIONS : starts
USERS ||--o{ AUDIT_LOG : generates
STUDIES ||--o{ SITES : contains
STUDIES ||--o{ PATIENTS : enrolls
STUDIES ||--o{ QUERIES : tracks
STUDIES ||--o{ SAE_RECORDS : monitors
STUDIES ||--o{ DQI_SNAPSHOTS : scores
STUDIES ||--o{ KRI_BREACHES : detects
STUDIES ||--o{ ALERTS : triggers
SITES ||--o{ PATIENTS : manages
SITES ||--o{ CRF_STATUS : tracks
PATIENTS ||--o{ VISITS : schedules
PATIENTS ||--o{ CRF_STATUS : has
PATIENTS ||--o{ CODING_RECORDS : codes
PATIENTS ||--o{ MISSING_PAGES : flags
USERS {
uuid id PK
string email UK
string full_name
enum role "6 roles"
boolean is_active
}
STUDIES {
uuid id PK
string name UK
string phase
string therapeutic_area
string status
}
SITES {
uuid id PK
uuid study_id FK
string site_number
string country
}
PATIENTS {
uuid id PK
uuid study_id FK
uuid site_id FK
string patient_number
boolean is_clean
int open_queries
int missing_visits
}
DQI_SNAPSHOTS {
uuid id PK
uuid study_id FK
decimal dqi_score
string status
jsonb component_breakdown "9 components"
}
AUDIT_LOG {
bigint id PK
timestamp timestamp
uuid user_id FK
string action
string resource_type
jsonb old_value
jsonb new_value
inet ip_address
text reason_for_change
}
graph TD
REQ["HTTP Request"] --> JWT["JWT Verification<br/>middleware/auth.py"]
JWT -->|Invalid| R401["401 Unauthorized"]
JWT -->|Valid| RBAC["Permission Check<br/>middleware/rbac.py"]
RBAC -->|Denied| R403["403 Forbidden"]
RBAC -->|Allowed| HANDLER["Route Handler"]
HANDLER --> AUDIT["Audit Log<br/>middleware/audit.py"]
AUDIT --> DB[(PostgreSQL)]
HANDLER --> RESP["Response"]
subgraph "Role Matrix (6 x 12)"
CTT["CTT_LEAD<br/>Full access + admin"]
CRA["CRA<br/>Assigned studies only"]
DM["DATA_MANAGER<br/>Data quality focus"]
MMON["MEDICAL_MONITOR<br/>Safety oversight"]
SC["SITE_COORDINATOR<br/>Query resolution only"]
REGUL["REGULATORY<br/>Compliance + audit"]
end
sequenceDiagram
participant DC as docker compose up
participant DB as PostgreSQL
participant RD as Redis
participant API as FastAPI
participant WEB as Next.js
DC->>DB: Start container
DC->>RD: Start container
DB-->>DC: Healthcheck: pg_isready
RD-->>DC: Healthcheck: redis-cli ping
DC->>API: Start container
API->>DB: Wait for connection
API->>API: Run Alembic migrations
API->>API: Check if first run
alt First Run (empty DB)
API->>API: Seed 6 demo users
API->>API: Ingest 203 Excel files (23 studies)
API->>API: Restart with 2 workers
end
API-->>DC: Healthcheck: /health
DC->>WEB: Start container
WEB-->>DC: Healthcheck: wget
Note over DC,WEB: All services ready at localhost:3000
Tech Stack:
| Layer | Technology |
|---|---|
| Frontend | Next.js 16, React 19, TypeScript, Tailwind CSS 4, Recharts, Zustand, shadcn/ui |
| Backend | FastAPI, Python 3.12, SQLAlchemy 2.0 (async), Alembic, Pydantic v2 |
| Database | PostgreSQL 16, Redis 7 |
| AI | 3 specialist agents, 21 tools, LLM routing, SSE streaming |
| Auth | JWT (HS256), bcrypt, 6-role RBAC with 12 granular permissions |
| Deployment | Docker Compose (4 services), nginx reverse proxy, Let's Encrypt SSL |
| Testing | pytest (114 tests), Vitest (129 tests), Playwright (23 E2E tests) |
NEXUS CDM implements a 6-role RBAC system enforced on both frontend (UI gating) and backend (HTTP 403). Each role has a distinct dashboard experience.
| Permission | CTT Lead | CRA | Data Manager | Medical Monitor | Site Coordinator | Regulatory |
|---|---|---|---|---|---|---|
| View all studies | Y | - | Y | Y | - | Y |
| View all sites | Y | - | Y | Y | - | Y |
| View patient data | - | Y | Y | Y | Y | - |
| View safety data | Y | Y | - | Y | Y | Y |
| Generate queries | - | Y | Y | - | - | - |
| Resolve queries | - | - | - | - | Y | - |
| Acknowledge alerts | Y | Y | Y | Y | - | Y |
| Generate reports | Y | Y | Y | Y | - | Y |
| Export data (CSV) | Y | Y | Y | Y | - | Y |
| Modify thresholds | Y | - | - | - | - | - |
| Manage users | Y | - | - | - | - | - |
| View audit trail | Y | - | Y | Y | - | Y |
What each role sees:
- CTT Lead (Sarah Chen): Full portfolio overview, management controls, all studies, admin access, data management, threshold configuration
- CRA (Michael Torres): Assigned studies/sites, query generation, monitoring checklists, CRA performance metrics
- Data Manager (Jennifer Kim): All studies, clean patient rates, data quality focus, query generation, audit trail
- Medical Monitor (Dr. Robert Singh): Safety-focused dashboard, SAE reconciliation, safety signals, protocol deviations
- Site Coordinator (Lisa Park): Assigned site only, query resolution queue, no exports, no audit trail, no admin
- Regulatory (James Wright): Compliance-focused view, submission readiness, audit trail, no patient-level data
- 11 specialized Excel parsers for 9 file types across 203 files
- Handles merged headers, variable columns, fuzzy sheet name matching
- Orchestrated pipeline: EDC metrics first (creates FK hierarchy), then remaining files
- Tracks ingestion status per file in
data_ingestionstable
- Full study CRUD: create, delete, and manage studies from the browser
- Drag-and-drop file upload with smart file-type detection from filename
- Per-file type selector with 9 clinical data types (EDC Metrics, Visit Tracker, Missing Lab, SAE Dashboard, Inactivated, Missing Pages, EDRR, MedDRA, WHODrug)
- Expected format preview showing required sheet names and columns per file type
- Upload validation with detailed error reporting
- Study cards showing patient/site counts, DQI badges, and ingestion progress (X/9 file types)
- Ingestion log with study, file, type, status, row count, and timestamp
9-component weighted formula per ICH E6(R3):
| Component | Weight | Priority |
|---|---|---|
| SAE Reconciliation | 18% | Critical |
| Missing Visits | 15% | High |
| Open Queries | 15% | High |
| CRF Completion | 12% | Medium |
| Lab Integrity | 12% | Medium |
| Protocol Deviations | 10% | Medium |
| Coding Completion | 8% | Low |
| Signature Compliance | 5% | Low |
| SDV Completion | 5% | Low |
Status thresholds: Excellent (>=95), Good (>=85), Needs Attention (>=70), At Risk (<70)
DQI calculation uses bulk aggregate SQL queries for performance — all 23 studies (58K patients) calculate in under 5 seconds.
8 KRIs with Quality Tolerance Limits:
- Query Aging (warn 15d, critical 30d)
- Query Rate (warn 5%, critical 10%)
- Missing Page Rate (warn 2%, critical 5%)
- SAE Reconciliation (warn 1 pending, critical 48h)
- Site DQI (warn <85, critical <70)
- Enrollment Rate (warn <80%, critical <60%)
- Uncoded Terms (warn 5, critical 20)
- Protocol Deviation Rate (warn 10%, critical 20%)
Strict binary gate (not a score) — all 8 criteria must be met:
- Zero missing visits
- Zero missing CRF pages
- Zero open queries
- Zero uncoded terms
- Zero unreconciled SAEs
- All CRFs SDV verified
- All CRFs signed by PI
- Zero non-conformant CRFs
Returns per-patient blocker list for actionable remediation.
GO / CONDITIONAL_GO / NO_GO assessment per study with 6 blocker categories:
- Queries open >30 days (CRITICAL)
- Unreconciled SAEs (CRITICAL)
- Total open queries (HIGH)
- Uncoded terms (HIGH)
- Missing CRF pages (HIGH)
- Unsigned CRFs (HIGH)
3 specialist agents with 21 database-connected tools:
| Agent | Tools | Expertise |
|---|---|---|
| Data Steward | 9 | ALCOA+, query management, CRF lifecycle, DQI, visit compliance |
| Medical Monitor | 5 | SAE reconciliation (48h ICH E6), coding, safety signals |
| Regulatory | 7 | DB lock readiness, 21 CFR Part 11 audit, CDISC, statistical readiness |
Flow: User query --> LLM routing --> Parallel agent dispatch --> Tool execution (live DB queries) --> Result synthesis --> SSE streaming response
- WebSocket-connected notification bell
- KRI breach scanning across all studies
- Severity-based alerting (CRITICAL/HIGH/MEDIUM/LOW)
- Lifecycle management: created --> acknowledged --> resolved
- Threaded comments with @mentions on any study/site/patient
- Action items with lifecycle (open --> in_progress --> resolved)
- Audit-logged per 21 CFR Part 11
7 downloadable reports (permission-gated):
- Patient DQI scores with 9-component breakdown
- Site DQI scores with risk classification
- Study DQI with patient summary
- KRI breaches with severity and thresholds
- Clean patient status with blocker details
- Database lock readiness per study
- Query aging with bucket classification
| Route | Page | Description |
|---|---|---|
/ |
Portfolio | Role-specific landing page, study table, KPIs, DQI comparison, health score |
/analytics |
Analytics | Query aging charts, protocol deviation breakdown, patient scatter plots |
/agents |
AI Agents | Multi-agent chat with session history, SSE streaming, tool visualization |
/alerts |
Alerts | Severity-filtered alert table, scan trigger, acknowledge actions |
/cra |
CRA Performance | Per-CRA query resolution metrics with progress bars |
/predictions |
Predictions | DB lock readiness, clean patients, statistical/CDISC readiness, CSV exports |
/audit |
Audit Trail | Filterable audit log, expandable detail rows, CSV export |
/data-management |
Data Management | Study CRUD, file upload with type selection, ingestion log, format validation |
/admin/ingest |
Admin | Data seeding, file upload, ingestion report |
/studies/[id] |
Study Overview | 8 KPI cards, patient table, safety summary |
/studies/[id]/dqi |
DQI | Calculate button, study/site/patient DQI tables, component bar chart |
/studies/[id]/kri |
KRI | Scan button, 8 KRI definitions, breaches table |
/studies/[id]/safety |
Safety | SAE reconciliation, coding completion, outlier sites |
/studies/[id]/sites |
Sites | All sites table with country filter, high-risk sites |
/studies/[id]/visits |
Visits | Compliance KPIs, donut chart, overdue visits table |
/studies/[id]/crf |
CRF | Per-site CRF lifecycle, stacked bar chart, signature compliance |
64 endpoints (63 REST + 1 WebSocket). All endpoints except auth and health require JWT.
| Method | Path | Description |
|---|---|---|
| POST | /auth/register |
Register new user |
| POST | /auth/login |
Login, returns JWT access + refresh tokens |
| POST | /auth/refresh |
Refresh access token |
| GET | /auth/me |
Get current authenticated user |
| Method | Path | Description |
|---|---|---|
| GET | /studies |
List all studies |
| GET | /studies/:id |
Get study detail |
| GET | /studies/:id/overview |
Aggregate study statistics |
| GET | /studies/:id/sites |
List sites with patient counts (filter by country) |
| GET | /studies/:id/sites/high-risk |
Sites with DQI below threshold |
| GET | /studies/:id/patients |
List patients (filter: is_clean, site_id, status) |
| GET | /studies/:id/queries |
List data queries |
| GET | /studies/:id/queries/aging |
Query aging report (4 time buckets) |
| GET | /safety/sae/:id |
SAE reconciliation status |
| GET | /safety/coding/:id |
MedDRA + WHODrug coding completion |
| GET | /safety/signals/:id |
Site-level SAE outlier detection |
| GET | /studies/:id/visit-compliance |
Visit completion statistics |
| GET | /studies/:id/overdue-visits |
Overdue visit list |
| GET | /studies/:id/crf-status |
CRF SDV/freeze/lock/sign per site |
| GET | /studies/:id/signature-compliance |
PI signature compliance |
| GET | /studies/:id/protocol-deviations |
Protocol deviations |
| GET | /studies/:id/protocol-deviations/summary |
PD summary by type/status |
| Method | Path | Description | RBAC |
|---|---|---|---|
| POST | /dqi/:studyId/calculate |
Trigger DQI calculation | JWT |
| GET | /dqi/:studyId |
DQI snapshots (study + site + patient) | JWT |
| GET | /dqi/portfolio |
All studies ranked by DQI | JWT |
| GET | /kri/definitions |
8 KRI definitions + thresholds | JWT |
| GET | /kri/:studyId/breaches |
Stored KRI breaches | JWT |
| POST | /kri/:studyId/scan |
Trigger KRI scan | JWT |
| GET | /compliance/db-lock/:studyId |
GO/CONDITIONAL_GO/NO_GO | JWT |
| GET | /compliance/clean-patients/:studyId |
Clean patient summary | JWT |
| POST | /compliance/clean-patients/:studyId/evaluate |
Full evaluation | JWT |
| GET | /compliance/statistical-readiness/:studyId |
CSR readiness | JWT |
| GET | /compliance/audit/:studyId |
Audit summary | JWT |
| GET | /compliance/cdisc/:studyId |
CDISC compliance | JWT |
| GET | /cra-performance/:studyId |
CRA query resolution metrics | JWT |
| Method | Path | Description | RBAC |
|---|---|---|---|
| GET | /alerts |
List alerts (filter: severity, category, study) | JWT |
| PATCH | /alerts/:id/acknowledge |
Acknowledge alert | acknowledge_alerts |
| POST | /alerts/scan |
Scan all studies for breaches | JWT |
| Method | Path | Description | RBAC |
|---|---|---|---|
| GET | /audit-trail |
Paginated audit log (filter: user, resource, date) | view_audit_trail |
| GET | /audit-trail/export |
CSV export (21 CFR Part 11 ready) | view_audit_trail |
| Method | Path | Description | RBAC |
|---|---|---|---|
| GET | /export/patient-dqi |
Patient DQI + component breakdown | export_data |
| GET | /export/site-dqi |
Site DQI + risk classification | export_data |
| GET | /export/study-dqi |
Study DQI + patient summary | export_data |
| GET | /export/kri-breaches |
KRI breaches + severity | export_data |
| GET | /export/clean-patients |
Clean patient status + blockers | export_data |
| GET | /export/db-lock-readiness |
DB lock GO/NO-GO per study | export_data |
| GET | /export/query-aging |
All queries + aging buckets | export_data |
| Method | Path | Description |
|---|---|---|
| POST | /comments |
Create comment (audit-logged) |
| GET | /comments |
List comments (filter: study, site, patient) |
| PATCH | /comments/:id |
Edit own comment (audit-logged) |
| POST | /action-items |
Create action item (audit-logged) |
| GET | /action-items |
List action items (filterable) |
| PATCH | /action-items/:id |
Update status (audit-logged) |
| Method | Path | Description |
|---|---|---|
| POST | /agents/chat |
Multi-agent AI chat (full orchestration) |
| POST | /agents/chat/stream |
SSE streaming multi-agent chat |
| GET | /agents/sessions |
List chat sessions |
| GET | /agents/sessions/:id |
Get session with message history |
| Method | Path | Description |
|---|---|---|
| POST | /seed/users |
Create 6 demo users (idempotent) |
| POST | /ingest/seed |
Ingest all 23 studies (203 files) |
| POST | /ingest/upload |
Upload single Excel file |
| POST | /ingest/upload-multi |
Upload multiple Excel files at once |
| POST | /ingest/create-study |
Create a new empty study |
| DELETE | /ingest/study/:id |
Delete study and all related data (cascade) |
| GET | /ingest/status |
Ingestion history grouped by study |
| GET | /ingest/file-types |
9 expected file types with descriptions |
| GET | /users |
List users (for @mention resolution) |
| GET | /health |
Health check |
| WS | /ws/alerts |
Real-time alert WebSocket |
24 tables across 13 domain modules:
| Domain | Tables | Purpose |
|---|---|---|
| Users | users, user_assignments |
Auth, RBAC (6 roles) |
| Clinical | studies, sites, patients |
Trial structure hierarchy |
| Quality | queries, missing_pages, lab_issues |
Data quality tracking |
| Safety | sae_records, coding_records |
SAE + MedDRA/WHODrug coding |
| CRF | visits, crf_status |
SDV, freeze, lock, sign lifecycle |
| Protocol | protocol_deviations, inactivated_records |
Protocol compliance |
| Third Party | edrr_issues |
External data reconciliation |
| Analytics | dqi_snapshots, kri_breaches |
DQI scores, KRI breach records |
| Alerts | alerts |
Severity + category alerting |
| Collaboration | comments, action_items |
Threaded comments, task tracking |
| Audit | audit_log |
21 CFR Part 11 append-only log |
| Agents | agent_sessions, agent_messages |
AI agent interactions |
| Ingestion | data_ingestions |
File ingestion tracking |
9 PostgreSQL enum types: userrole, querystatus, querytype, saesource, codingtype, visitstatus, alertseverity, alertcategory, actionitemstatus
Ingested data volume (23 studies, 203 Excel files):
| Table | Rows | Source |
|---|---|---|
| studies | 23 | EDC Subject Level Metrics |
| sites | 3,401 | EDC Subject Level Metrics |
| patients | 57,974 | EDC Subject Level Metrics |
| queries | 13,129 | EDC Query Report |
| missing_pages | 6,116 | Global Missing Pages Report |
| lab_issues | 20,443 | Missing Lab Name and Ranges |
| sae_records | 26,870 | SAE Dashboard (DM + Safety) |
| coding_records | 808,581 | MedDRA + WHODrug Coding |
| visits | 335,652 | EDC SV + Visit Projection Tracker |
| crf_status | 1,361,001 | PI Signature, SDV, Freeze, Lock |
| protocol_deviations | 10,750 | EDC Protocol Deviation |
| inactivated_records | 31,285 | Inactivated Forms Report |
| edrr_issues | 175 | Compiled EDRR |
| Standard | Implementation |
|---|---|
| ICH E6(R3) | Risk-Based Quality Management with 8 KRIs, Quality Tolerance Limits, 9-component DQI formula, proportionate SDV |
| 21 CFR Part 11 | Append-only audit log (BigInt PK, no UPDATE/DELETE), electronic signatures, reason-for-change capture, IP tracking, restricted access via RBAC |
| ALCOA+ | Attributable (user_id on every action), Legible (structured JSON), Contemporaneous (server-side timestamps), Original (immutable audit log), Accurate (validated at ingestion), Complete (no soft-deletes), Consistent (referential integrity), Enduring (PostgreSQL WAL), Available (API + CSV export) |
| Clean Patient | Strict binary gate: 8 criteria with zero tolerance, per-patient blocker list for remediation |
| Database Lock | GO/CONDITIONAL_GO/NO_GO with 6 blocker categories, supports soft-to-hard lock workflow |
docker compose exec api pytest tests/ -v| Module | Tests | Coverage |
|---|---|---|
test_health.py |
9 | Health check, DB connectivity, 24 tables, enums, FKs |
test_auth.py |
13 | Seed, register, login, JWT refresh, RBAC guards, audit |
test_ingestion.py |
20 | 23 studies ingestion, row counts, FK integrity |
test_crud.py |
28 | All CRUD endpoints, collaboration, audit export |
test_analytics.py |
14 | DQI scoring, KRI breaches, alerts, DB lock, clean patient |
test_agents.py |
11 | Agent routing, safety rules, tool calls, SSE, sessions |
test_exports.py |
8 | All 7 CSV exports + auth enforcement |
test_new_endpoints.py |
10 | High-risk sites, compliance extras, CRA, WebSocket |
cd apps/web && npx vitest runCovers: API client, auth tokens, WebSocket, SSE, Zustand stores, UI components, all page rendering.
cd apps/web && npx playwright test9 spec files: login, portfolio, study detail, DQI, agents, alerts, collaboration, compliance, audit.
Total: 266 tests (114 backend + 129 unit + 23 E2E)
4 services with health-check dependency chain:
docker-compose.yml
├── db PostgreSQL 16-alpine port 5432 shared_buffers=128MB
├── redis Redis 7-alpine port 6379 maxmemory=64mb, AOF
├── api Python 3.12 / FastAPI port 8000 2 uvicorn workers
└── web Node 22 / Next.js 16 port 3000 standalone build
Startup order: db (healthy) --> redis (healthy) --> api (healthy) --> web
The API entrypoint waits for PostgreSQL, runs Alembic migrations, then starts uvicorn. On first run it auto-seeds 6 demo users and ingests all 23 studies.
Production deployment (nexuscdm.online):
- nginx reverse proxy with SSL termination
- Let's Encrypt certificates (auto-renew via certbot)
/api/*stripped and proxied to FastAPI (port 8000)/ws/*proxied with WebSocket upgrade headers/proxied to Next.js (port 3000)
nexus-cdm/
├── docker-compose.yml # 4-service orchestration
├── .env.example # Environment template
├── docker/
│ ├── Dockerfile.api # Python 3.12 + FastAPI
│ ├── Dockerfile.web # Node 22 multi-stage build
│ └── entrypoint.sh # Migration runner + uvicorn
├── data/
│ └── studies/ # 23 study folders (203 Excel files)
├── apps/
│ ├── api/
│ │ ├── requirements.txt # 40 Python packages
│ │ ├── alembic.ini
│ │ ├── alembic/versions/
│ │ │ └── 001_initial_schema.py # 24 tables, 9 enums
│ │ ├── src/
│ │ │ ├── main.py # FastAPI app, 23 routers
│ │ │ ├── config.py # Pydantic Settings
│ │ │ ├── database.py # Async SQLAlchemy 2.0
│ │ │ ├── core/
│ │ │ │ ├── constants.py # DQI weights, KRI thresholds
│ │ │ │ └── roles.py # 6x12 RBAC permission matrix
│ │ │ ├── models/ # 13 modules, 24 tables
│ │ │ ├── schemas/ # 10 Pydantic schema files
│ │ │ ├── api/ # 22 route modules, 64 endpoints
│ │ │ ├── middleware/
│ │ │ │ ├── auth.py # JWT verification
│ │ │ │ ├── rbac.py # require_role(), require_permission()
│ │ │ │ └── audit.py # 21 CFR Part 11 logging
│ │ │ ├── services/
│ │ │ │ ├── dqi_service.py # 9-component bulk DQI calculation
│ │ │ │ ├── kri_service.py # 8 KRI breach detection
│ │ │ │ ├── clean_patient_service.py
│ │ │ │ ├── alert_service.py
│ │ │ │ └── parsers/ # 11 Excel file parsers
│ │ │ └── agents/
│ │ │ ├── orchestrator.py # Parallel agent dispatch
│ │ │ ├── router.py # LLM-based intent routing
│ │ │ └── tools/ # 21 tools (data, safety, compliance)
│ │ └── tests/ # 8 modules, 114 tests
│ └── web/
│ ├── package.json # Next.js 16, React 19, Tailwind 4
│ ├── next.config.ts # Standalone + API rewrites
│ └── src/
│ ├── app/
│ │ ├── (auth)/login/ # Login with demo user selector
│ │ └── (dashboard)/ # 18 protected routes
│ ├── components/
│ │ ├── ui/ # 25 shadcn/ui components
│ │ ├── layout/ # Sidebar, header, notifications
│ │ ├── dashboard/ # KPI cards, charts, study components
│ │ ├── ai/ # Chat panel, streaming, tool cards
│ │ └── collaboration/ # Comments, action items panels
│ ├── lib/
│ │ ├── api.ts # HTTP client with JWT auto-refresh
│ │ ├── permissions.ts # usePermissions() hook, RBAC matrix
│ │ ├── websocket.ts # WebSocket with reconnect backoff
│ │ └── sse.ts # SSE streaming for AI agents
│ ├── stores/ # Zustand: auth, study, alert, agent
│ └── types/api.ts # TypeScript types for 64 endpoints
| Variable | Description | Default |
|---|---|---|
POSTGRES_USER |
Database username | nexus |
POSTGRES_PASSWORD |
Database password | nexus_secret |
POSTGRES_DB |
Database name | nexus_cdm |
DB_PORT |
PostgreSQL port | 5432 |
REDIS_URL |
Redis connection URL | redis://redis:6379/0 |
API_PORT |
FastAPI port | 8000 |
SECRET_KEY |
JWT signing secret | (change in production) |
ACCESS_TOKEN_EXPIRE_MINUTES |
JWT access token TTL | 30 |
REFRESH_TOKEN_EXPIRE_DAYS |
JWT refresh token TTL | 7 |
GROQ_API_KEY |
LLM API key (Groq) | (required for AI agents) |
GROQ_MODEL |
LLM model identifier | openai/gpt-oss-120b |
STUDY_DATA_PATH |
Path to study Excel files | /app/data/studies |
cd apps/api
pip install -r requirements.txt
alembic upgrade head
uvicorn src.main:app --reload --port 8000cd apps/web
npm install
npm run dev # http://localhost:3000# Backend
docker compose exec api pytest tests/ -v
# Frontend unit
cd apps/web && npx vitest run
# Frontend E2E (requires backend running)
cd apps/web && npx playwright testdocker compose build --no-cache web api && docker compose up -d