All documentation Download (Markdown) Technical

analyse · analyse.pdhc.se

Technical manual


analyse.pdhc — technical documentation

Purpose

analyse.pdhc is the GROUP / population + federated-events half of the old
dashboard, extracted into its own FHIR-R5 microservice (docs/470_scoping.md).
It hosts:

It is gated on the ANALYSIS phase. The individual / point-of-care half
(nurse, /charts, patient single-view, CDR1 care-delivery reads) lives in
cd-assist (host dashboard.pdhc.se, unchanged) and is deliberately absent here.

Architecture

browser ──SSO login──▶ /auth/login → sso.pdhc → /auth/callback (session token)
                        │
gateway.pdhc ──svc-key──┤ before_request loader (app/auth.py)
monitor.pdhc ──svc-key──┤   • service-key   → service blob (service_source)
                        │   • AUTH_MODE=off  → dev-SU blob
                        │   • AUTH_MODE=sso  → re-validate bearer every request
                        ▼   • gate: has_analysis_access (SU OR prof+analysis)
        ┌───────────────────────────────────────────────┐
        │ researcher_api  (/api/cohort…)  researcher_required + @audit_read
        │ observations_search (/api/v1/observations)  service_source gated
        │ analyse_stats / canonical / openehr (/api/v1/…) service_source gated
        └───────────────────────────────────────────────┘
                        │  app/analyse/federation.fanout (ThreadPool)
                        ▼  X-Source-Service: analyse.pdhc + ANALYSE_PDHC_SERVICE_KEY
                    CDR2 … CDR6   (/api/v1/fhir/Observation, /api/v1/stats, …)

Auth (app/auth.py)

Web login (app/routes/auth.py, ported from dashboard.pdhc)

/auth/login → sso.pdhc (anti-CSRF state), /auth/callback validates the
returned token, admits only has_analysis_access, stores session['sso_token']
(re-validated each request by the loader), /auth/logout clears + revokes.
/ renders the researcher workspace shell (routes/views.py).

Read core (app/analyse/federation.py + aggregations.py)

Copied from dashboard.pdhc (source of truth per decision D5 — mirror any fix
across dashboard/cd-assist/analyse). CdrRegistry.from_config reads
CDR_ENDPOINTS; fanout calls every CDR concurrently with per-CDR timeout and
partial-result tolerance (mode = complete|degraded|error). Aggregators:
merge_histograms, merge_agp_bands, concat_series, lttb_downsample;
aggregations.compute_stats/compute_agp produce the cdr1-shaped Parameters the
mergers consume.

Cohort member sets pass through ips.pdhc's analysis-filter (purpose=research)
before persist/aggregate — ips owns the consent flags (D1 #404); the verdict is
never computed locally and fails closed (503) if ips is unreachable.
analyse calls it with ANALYSE_PDHC_SERVICE_KEY.

Data model (app/models)

JSONB is dialect-aware (real JSONB on Postgres, plain JSON on SQLite for the
hermetic test suite). Single alembic head 0001_initial.

Config (analyse_app/.env.example)

APP_PORT=9110, DB_PORT=9111 (127.0.0.1 only), COMPOSE_PROJECT_NAME=analyse_pdhc,
AUTH_MODE, SSO_*, GATEWAY_PDHC_SERVICE_KEY, MONITOR_PDHC_SERVICE_KEY,
ANALYSE_PDHC_SERVICE_KEY, IPS_BASE_URL, CDR_ENDPOINTS (comma-sep;
https://cdrN.pdhc.se or cdrN=http://127.0.0.1:PORT), CDR_FANOUT_TIMEOUT.

Run / test

Deploy / cutover

Server layout CLAUDE.md §7. Hard follow-ups: #540 gateway
ANALYSE_BASE_URL repoint, #541 CDR2–6 read-identity flip, #543 delete
the group half from dashboard(cd-assist). See readme.md.

Port Allocation

All ports bind to 127.0.0.1 (loopback only); external traffic arrives
via the reverse proxy.

Port Service
9110 Flask application (Gunicorn)
9111 PostgreSQL database