Skip to content

Latest commit

 

History

History
107 lines (79 loc) · 7.94 KB

File metadata and controls

107 lines (79 loc) · 7.94 KB

FHIR R4 Export

See Frozen contracts and Trust verification for verifying an EvidenceBundle.v0.1 (including integrity, schema, hashchain, and invariant trace) with labtrust verify-bundle --bundle <dir>.

The FHIR R4 exporter converts Receipt.v0.1 (from an evidence bundle or receipts directory) into valid HL7 FHIR R4 JSON. The repo targets valid FHIR R4 with deterministic resource IDs and missing data represented using the standard data-absent-reason extension and Observation.dataAbsentReason where appropriate.

  • Bundle: type = collection; contains Specimen (when present), Observation(s), and DiagnosticReport.
  • When specimen is missing: No Specimen resource is emitted; Observation.specimen and DiagnosticReport.specimen are set to a Reference object containing only the data-absent-reason extension (valueCode: unknown).
  • When Observation has no numeric value: value[x] is omitted; Observation.dataAbsentReason is populated with the HL7 data-absent-reason code system and code unknown.
  • IDs: All resource ids are deterministic (specimen_id or content-addressed hash; result_id or index-based); in-bundle references resolve. Placeholder ids such as id="placeholder" or Specimen/placeholder are never emitted.

No external FHIR libraries are required; output is pure JSON with lightweight structural validation.

CLI

labtrust export-fhir --receipts <dir> --out <dir> [--filename fhir_bundle.json]
  • --receipts: Directory containing receipt files (e.g. EvidenceBundle.v0.1/ with receipt_*.v0.1.json) or any folder with receipt_*.v0.1.json. If manifest.json is present, partner_id and policy_fingerprint are read and added to the bundle meta.
  • --out: Output directory; the FHIR bundle JSON is written here.
  • --filename: Output filename (default: fhir_bundle.json).

Mapping rules

Receipt field FHIR resource / path Notes
specimen_id Specimen.id, Specimen.identifier id = specimen_id when present; otherwise deterministic content-addressed hash of receipt. identifier system urn:labtrust:specimen, value = same id
accession_ids[0] Specimen.accessionIdentifier.value First accession only
timestamps.received / timestamps.accepted Specimen.receivedTime Converted to FHIR dateTime (UTC ISO 8601); if only integer timestamp, also in extension received-timestamp
result_id Observation.id, DiagnosticReport.id One Observation and one DiagnosticReport per result receipt
panel_id Observation.code.coding system urn:labtrust:test, code and display = panel_id (or result_id if no panel_id)
device_ids[0] Observation.extension url http://labtrust.org/fhir/StructureDefinition/device-identifier, valueIdentifier (no Device resource in bundle)
reason_codes (CRIT / HIGH / LOW) Observation.interpretation CRIT → v3-ObservationInterpretation code CR (Critical); HIGH → H; LOW → L
timestamps.result_generated / released Observation.issued, DiagnosticReport.effectiveDateTime FHIR dateTime
decision DiagnosticReport.status RELEASED → final; HELD → partial; REJECTED → entered-in-error; BLOCKED → registered
(specimen link) DiagnosticReport.specimen When specimen receipts exist: reference to first Specimen in bundle (#Specimen/<id>). When none exist: Reference with only data-absent-reason extension (no Specimen resource).

Status assumptions (DiagnosticReport)

  • final: Receipt decision = RELEASED (result released to care).
  • partial: Receipt decision = HELD (result held, not yet final).
  • entered-in-error: Receipt decision = REJECTED (specimen/result rejected).
  • registered: Receipt decision = BLOCKED or other (registered, not yet final).

Partner overlay hooks

  • partner_id: Included in Bundle.meta.tag with system http://labtrust.org/fhir/partner and code = partner_id (from manifest or optional override).
  • policy_fingerprint: Included in Bundle.meta.extension with url http://labtrust.org/fhir/StructureDefinition/policy-fingerprint and valueString = policy_fingerprint (from manifest).

Validation

  • Structural checks: Bundle has resourceType "Bundle", type "collection", and entry[] with fullUrl and resource. Each resource has resourceType and id. References that use reference resolve within the bundle (same-bundle references use #ResourceType/id). Specimen may be represented by a Reference with only the data-absent-reason extension (no reference), in which case no resolution is required. No resource id or fullUrl may contain "placeholder".
  • Determinism: Same receipts directory (same file order and content) produces identical bundle JSON (canonical key ordering).
  • Export contract schema: policy/schemas/fhir_bundle_export.v0.1.schema.json describes the minimal export contract (required keys, entry structure). This is not full FHIR profile validation.

Terminology validation (optional): The CLI command labtrust validate-fhir --bundle <path> --terminology <value_set_json> [--strict] checks coded elements (e.g. Observation.code, Observation.interpretation) in a FHIR bundle against a value set. Use this when you need codes to conform to a specific terminology beyond the minimal export contract. See Coordination benchmark card – Full FHIR or terminology validation.

When specimen is missing (data-absent-reason)

When the receipts contain only result receipts (no specimen receipts), no Specimen resource is emitted. Observation.specimen and DiagnosticReport.specimen are set to a Reference object that contains only the HL7 data-absent-reason extension (no reference field, no Specimen in bundle):

{
  "extension": [
    {
      "url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason",
      "valueCode": "unknown"
    }
  ]
}

When Observation value is missing (dataAbsentReason)

Receipts omit numeric lab values. The exporter leaves out value[x] and sets Observation.dataAbsentReason:

{
  "dataAbsentReason": {
    "coding": [
      {
        "system": "http://terminology.hl7.org/CodeSystem/data-absent-reason",
        "code": "unknown"
      }
    ]
  }
}

Placeholder text and placeholder IDs are omitted. Optional context may be added via Observation.note with explicit wording when needed.

Limitations

  • Numeric result value: Receipts omit lab values; Observation uses dataAbsentReason as above. Real value/unit mapping requires extending the receipt or a separate value feed.
  • One Observation per result: Each result receipt becomes one Observation and one DiagnosticReport. Multiple observations per report (e.g. panel with many analytes) would require multiple result receipts or a different mapping.
  • Specimen–result linking: When specimen receipts exist, the first Specimen in the bundle is referenced by all Observations and DiagnosticReports. When none exist, specimen is represented via data-absent-reason extension only. Explicit specimen–result linking requires specimen_id on the result receipt.
  • Device resource: Device is represented as an Observation extension (identifier); a standalone Device resource is omitted from the bundle.
  • FHIR validation scope: The minimal export contract and reference resolution are checked; full FHIR profile or terminology validation is a separate optional step (validate-fhir).

End-to-end

  1. Run: labtrust reproduce --profile minimal --out runs/my_repro
  2. Export receipts: labtrust export-receipts --run runs/my_repro/taska/logs/cond_0/episodes.jsonl --out runs/my_repro/taska/cond_0_export
  3. Export FHIR: labtrust export-fhir --receipts runs/my_repro/taska/cond_0_export/EvidenceBundle.v0.1 --out runs/my_repro/taska/cond_0_fhir

All tests (including export-receipts and export-fhir) should be green.