Example 1: Clinical Review, Annotation & EHR Audit¶
A clinician's journey from a new patient through review, annotation, and a
versioned diagnosis — the workflow testing/persona_clinician.py simulates
and testing/full_verify.py's data/annot/audit sections exercise
programmatically. Demonstrates the versioned/audited amend-and-void model
described in Clinical-safety model.
1. Register the patient¶
register_subject(
external_id="JANE-1985",
demographics={"name": "Jane D.", "birthDate": "1985-04-02"},
actor="dr_smith",
)
# -> {"outcome": "created", "id": 1, "external_id": "JANE-1985", ...}
The clinician (or the agent on their behalf) invents a stable external_id
and supplies actor — there's no logged-in identity, so this must come from
the calling application.
2. Import her EEG recording¶
import_recording(
file_path="/data/jane_rest.fif",
subject_external_id="JANE-1985",
task="rest",
dataset_name="clinic",
session_id="jane",
)
# -> {"outcome": "imported", "session_id": "jane", "id": 1, "bids_path": "...", ...}
One call files the recording under the patient in BIDS and opens it in a
processing session (load_into_session=True by default) — no separate
load_neuro needed. Keep the returned id — it's the recording_id used
below.
3. Review and clean up¶
plot_raw(session_id="jane", duration=5.0)
# -> {"image": "data:image/png;base64,...", "kind": "raw_traces"}
filter_neuro(session_id="jane", l_freq=1.0, h_freq=40.0, notch_freqs=[50.0])
# -> {"status": "filtered", "highpass": 1.0, "lowpass": 40.0, ...}
detect_bad_channels(session_id="jane", z_threshold=3.0)
# -> {"flagged": [...], "marked_as_bad": true, "current_bads": [...]}
Plots return inline PNG data URIs the agent can display without a second round trip.
4. Annotate a finding¶
add_annotation(
recording_id=1, onset=5.0, label="suspected spike", actor="dr_smith",
duration=0.2, channels=["Oz"],
)
# -> {"outcome": "created", "logical_id": "<uuid>", "version": 1, ...}
Save logical_id — it's what you'll use to correct this annotation later.
update_annotation(
logical_id="<uuid>", actor="dr_smith",
label="sharp wave", note="reclassified on review",
)
# -> {"outcome": "updated", "version": 2, "label": "sharp wave", ...}
The original ("suspected spike", v1) is retained with status amended;
list_annotations now shows only the current label. This is the same
edit-preserves-history model as EHR records.
5. Record a diagnosis, then update it¶
add_ehr_record(
subject_external_id="JANE-1985", resource_type="Condition",
fhir={"resourceType": "Condition", "code": {"text": "focal epilepsy"}},
actor="dr_smith", note="clinical impression",
)
# -> {"outcome": "created", "logical_id": "<ehr-uuid>", "version": 1, ...}
amend_ehr_record(
logical_id="<ehr-uuid>",
fhir={"resourceType": "Condition", "code": {"text": "focal epilepsy, left temporal"}},
actor="dr_smith", note="localized after MRI",
)
# -> {"outcome": "amended", "version": 2, "status": "active", ...}
fhir is a raw FHIR resource dict — there's no schema validation on it, so
the calling agent is responsible for well-formed FHIR.
6. Read back the current state¶
get_subject(external_id="JANE-1985")
# -> {"subject": {...}, "ehr_records": [{"version": 2, "fhir": {...}, ...}]}
get_ehr_history(logical_id="<ehr-uuid>")
# -> {"logical_id": "<ehr-uuid>", "versions": [
# {"version": 2, "status": "active", ...},
# {"version": 1, "status": "amended", ...},
# ]}
get_subject hides superseded versions — the clinician sees the right "now"
state. get_ehr_history shows the full trail when you need it.
7. Who changed what, and when¶
get_audit_log(target_table="ehr_records")
# -> {"entries": [
# {"actor": "dr_smith", "action": "amend", "target_id": 1, ...},
# {"actor": "dr_smith", "action": "create", "target_id": 1, ...},
# ]}
A plain "who changed Jane's diagnosis" question maps directly to a real audit trail with actor, action, and before/after payloads — every mutation in steps 4–5 produced one of these rows automatically.
Full sequence at a glance¶
register_subject -> import_recording -> plot_raw -> filter_neuro ->
detect_bad_channels -> add_annotation -> update_annotation ->
add_ehr_record -> amend_ehr_record -> get_subject -> get_ehr_history ->
get_audit_log