Healthy Route Implementation Guide
0.2.0 - ci-build

Healthy Route Implementation Guide, published by Savannah Informatics. This guide is not an authorized publication; it is the continuous build for version 0.2.0 built by the FHIR (HL7® FHIR® Standard) CI Build. This version is based on the current content of https://github.com/savannahghi/healthy-route-ig/ and changes regularly. See the Directory of published versions

StructureMap: Extract the vitals form

Official URL: https://fhir.optimalhealth.org/ig/healthy-route/StructureMap/HRExtractVitals Version: 0.2.0
Draft as of 2026-11-12 Computable Name: HRExtractVitals

/// url = 'https://fhir.optimalhealth.org/ig/healthy-route/StructureMap/HRExtractVitals'
/// version = '0.2.0'
/// name = 'HRExtractVitals'
/// status = 'draft'

uses "http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse" alias QR as source
uses "http://hl7.org/fhir/StructureDefinition/Bundle" alias Bundle as target
uses "http://hl7.org/fhir/StructureDefinition/Observation" alias Observation as target

group HRExtractVitals(source qr : QR, target bundle : Bundle) {
  qr -> bundle.type = 'transaction' "type";
  qr.item as g where (linkId = 'bp-reading-1') ->  bundle.entry as e,  e.resource = create('Observation') as obs then {
    qr -> obs then Reading(qr, g, obs, e) "reading";
    g.item as si where (linkId = 'bp-reading-1.8480-6') then {
      si.answer first as sa then {
        sa.value as sv -> obs then Systolic(qr, sv, obs) "systolic";
      };
    } "systolicItem";
    g.item as di where (linkId = 'bp-reading-1.8462-4') then {
      di.answer first as da then {
        da.value as dv -> obs then Diastolic(qr, dv, obs) "diastolic";
      };
    } "diastolicItem";
    g.item as ti where (linkId = 'bp-reading-1.time') then {
      ti.answer first as ta then {
        ta.value as tv -> obs.effective = tv "effective";
      };
    } "timeItem";
    qr -> obs.extension as x then {
      qr -> x.url = 'https://fhir.optimalhealth.org/ig/healthy-route/StructureDefinition/hr-reading-sequence' "url";
      qr -> x.value = 1 "value";
    } "sequence";
  } "reading1";
  qr.item as g where (linkId = 'bp-reading-2') ->  bundle.entry as e,  e.resource = create('Observation') as obs then {
    qr -> obs then Reading(qr, g, obs, e) "reading";
    g.item as si where (linkId = 'bp-reading-2.8480-6') then {
      si.answer first as sa then {
        sa.value as sv -> obs then Systolic(qr, sv, obs) "systolic";
      };
    } "systolicItem";
    g.item as di where (linkId = 'bp-reading-2.8462-4') then {
      di.answer first as da then {
        da.value as dv -> obs then Diastolic(qr, dv, obs) "diastolic";
      };
    } "diastolicItem";
    g.item as ti where (linkId = 'bp-reading-2.time') then {
      ti.answer first as ta then {
        ta.value as tv -> obs.effective = tv "effective";
      };
    } "timeItem";
    qr -> obs.extension as x then {
      qr -> x.url = 'https://fhir.optimalhealth.org/ig/healthy-route/StructureDefinition/hr-reading-sequence' "url";
      qr -> x.value = 2 "value";
    } "sequence";
    g.item as pi where (linkId = 'bp-reading-2.bp-position') then {
      pi.answer first as pa then {
        pa.value as pv -> obs.extension as x then {
          qr -> x.url = 'https://fhir.optimalhealth.org/ig/healthy-route/StructureDefinition/hr-reading-position' "url";
          pv ->  x.value = create('CodeableConcept') as concept,  concept.coding = pv "value";
        } "extension";
      };
    } "position";
  } "reading2";
  qr.item as g where (linkId = 'bp-reading-3') ->  bundle.entry as e,  e.resource = create('Observation') as obs then {
    qr -> obs then Reading(qr, g, obs, e) "reading";
    g.item as si where (linkId = 'bp-reading-3.8480-6') then {
      si.answer first as sa then {
        sa.value as sv -> obs then Systolic(qr, sv, obs) "systolic";
      };
    } "systolicItem";
    g.item as di where (linkId = 'bp-reading-3.8462-4') then {
      di.answer first as da then {
        da.value as dv -> obs then Diastolic(qr, dv, obs) "diastolic";
      };
    } "diastolicItem";
    g.item as ti where (linkId = 'bp-reading-3.time') then {
      ti.answer first as ta then {
        ta.value as tv -> obs.effective = tv "effective";
      };
    } "timeItem";
    qr -> obs.extension as x then {
      qr -> x.url = 'https://fhir.optimalhealth.org/ig/healthy-route/StructureDefinition/hr-reading-sequence' "url";
      qr -> x.value = 3 "value";
    } "sequence";
  } "reading3";
  qr.item as it where (linkId = 'retinal-capture-time') then {
    it.answer first as a ->  bundle.entry as e,  e.resource = create('Observation') as obs then {
      qr -> obs then Shared(qr, it, obs, e) "base";
      qr ->  obs.meta as m,  m.profile = 'https://fhir.optimalhealth.org/ig/healthy-route/StructureDefinition/hr-observation' "profile";
      qr -> obs.category = create('CodeableConcept') as cv then {
        qr -> cv.coding = create('Coding') as cd then {
          qr -> cd.system = 'http://terminology.hl7.org/CodeSystem/observation-category' "categorySystem";
          qr -> cd.code = 'procedure' "categoryCode";
          qr -> cd.display = 'Procedure' "categoryDisplay";
        } "categoryCoding";
      } "category";
      qr -> obs.code = create('CodeableConcept') as cv then {
        qr -> cv.coding = create('Coding') as cd then {
          qr -> cd.system = 'https://fhir.optimalhealth.org/ig/healthy-route/CodeSystem/hr-measurement' "codeSystem";
          qr -> cd.code = 'retinal-capture-time' "codeCode";
          qr -> cd.display = 'Time of retinal capture' "codeDisplay";
        } "codeCoding";
      } "code";
      a.value as v -> obs.effective = v "effective";
      a.value as v -> obs.value = v;
    } "obs";
  } "captureTime";
  qr -> evaluate(qr, id.replaceMatches('/_history/.*$', '').replaceMatches('^.*/', '')) as qid then {
    qr.item as g where (linkId = 'bp-mean') ->  bundle.entry as e,  e.resource = create('Observation') as obs then {
      qr -> obs then Reading(qr, g, obs, e) "reading";
      qr -> obs then Derived(qr, obs) "derivedCategory";
      g.item as si where (linkId = 'bp-mean.8480-6') then {
        si.answer first as sa then {
          sa.value as sv -> obs then Systolic(qr, sv, obs) "systolic";
        };
      } "systolicItem";
      g.item as di where (linkId = 'bp-mean.8462-4') then {
        di.answer first as da then {
          da.value as dv -> obs then Diastolic(qr, dv, obs) "diastolic";
        };
      } "diastolicItem";
      qr.item as r3 where (linkId = 'bp-reading-3') then {
        r3.item as ti where (linkId = 'bp-reading-3.time') then {
          ti.answer first as ta then {
            ta.value as tv -> obs.effective = tv "effective";
          };
        } "time";
      } "reading3";
      qr ->  obs.method = create('CodeableConcept') as m,  m.text = 'Mean of readings 2 and 3' "method";
      qr ->  obs.derivedFrom = create('Reference') as r,  r.reference = append('Observation/', qid, '-bp-reading-2') "reading2Ref";
      qr ->  obs.derivedFrom = create('Reference') as r,  r.reference = append('Observation/', qid, '-bp-reading-3') "reading3Ref";
    } "mean";
    qr.item as g where (linkId = 'bp-mean-all') ->  bundle.entry as e,  e.resource = create('Observation') as obs then {
      qr -> obs then Reading(qr, g, obs, e) "reading";
      qr -> obs then Derived(qr, obs) "derivedCategory";
      g.item as si where (linkId = 'bp-mean-all.8480-6') then {
        si.answer first as sa then {
          sa.value as sv -> obs then Systolic(qr, sv, obs) "systolic";
        };
      } "systolicItem";
      g.item as di where (linkId = 'bp-mean-all.8462-4') then {
        di.answer first as da then {
          da.value as dv -> obs then Diastolic(qr, dv, obs) "diastolic";
        };
      } "diastolicItem";
      qr.item as r3 where (linkId = 'bp-reading-3') then {
        r3.item as ti where (linkId = 'bp-reading-3.time') then {
          ti.answer first as ta then {
            ta.value as tv -> obs.effective = tv "effective";
          };
        } "time";
      } "reading3";
      qr ->  obs.method = create('CodeableConcept') as m,  m.text = 'Mean of readings 1, 2 and 3' "method";
      qr ->  obs.derivedFrom = create('Reference') as r,  r.reference = append('Observation/', qid, '-bp-reading-1') "reading1Ref";
      qr ->  obs.derivedFrom = create('Reference') as r,  r.reference = append('Observation/', qid, '-bp-reading-2') "reading2Ref";
      qr ->  obs.derivedFrom = create('Reference') as r,  r.reference = append('Observation/', qid, '-bp-reading-3') "reading3Ref";
    } "meanAll";
  } "derived";
}

// The category a value worked out on this form carries beside its FHIR one.
group Derived(source qr : QR, target obs : Observation) {
  qr -> obs.category = create('CodeableConcept') as cv then {
    qr -> cv.coding = create('Coding') as cd then {
      qr -> cd.system = 'https://fhir.optimalhealth.org/ig/healthy-route/CodeSystem/hr-observation-category' "derivedSystem";
      qr -> cd.code = 'derived' "derivedCode";
      qr -> cd.display = 'Derived' "derivedDisplay";
    } "derivedCoding";
  } "category";
}

// One reading on the core blood pressure profile: the panel code, the monitor
// as a component, and the shared identity. The group item's linkId names it.
group Reading(source qr : QR, source g, target obs : Observation, target e) {
  qr -> obs then Shared(qr, g, obs, e) "base";
  qr ->  obs.meta as m,  m.profile = 'https://fhir.optimalhealth.org/ig/healthy-route/StructureDefinition/hr-blood-pressure' "profile";
  qr -> obs.category = create('CodeableConcept') as cv then {
    qr -> cv.coding = create('Coding') as cd then {
      qr -> cd.system = 'http://terminology.hl7.org/CodeSystem/observation-category' "categorySystem";
      qr -> cd.code = 'vital-signs' "categoryCode";
      qr -> cd.display = 'Vital Signs' "categoryDisplay";
    } "categoryCoding";
  } "category";
  qr -> obs.code = create('CodeableConcept') as cv then {
    qr -> cv.coding = create('Coding') as cd then {
      qr -> cd.system = 'http://loinc.org' "codeSystem";
      qr -> cd.code = '85354-9' "codeCode";
      qr -> cd.display = 'Blood pressure panel with all children optional' "codeDisplay";
    } "codeCoding";
  } "code";
  qr.item as d where (linkId = 'bp-device') then {
    d.answer first as da then {
      da.value as dv -> obs.extension as x then {
        qr -> x.url = 'https://fhir.optimalhealth.org/ig/healthy-route/StructureDefinition/hr-reading-device' "url";
        dv ->  x.value = create('CodeableConcept') as concept,  concept.coding = dv "value";
      } "extension";
    };
  } "device";
}

group Systolic(source qr : QR, source sv, target obs : Observation) {
  sv -> obs.component as comp then {
    qr -> comp.code = create('CodeableConcept') as cv then {
      qr -> cv.coding = create('Coding') as cd then {
        qr -> cd.system = 'http://loinc.org' "codeSystem";
        qr -> cd.code = '8480-6' "codeCode";
        qr -> cd.display = 'Systolic blood pressure' "codeDisplay";
      } "codeCoding";
    } "code";
    sv -> comp.value = create('Quantity') as q then {
      sv -> q.value = sv "value";
      qr -> q.system = 'http://unitsofmeasure.org' "system";
      qr -> q.code = 'mm[Hg]' "unitCode";
      qr -> q.unit = 'mmHg' "unit";
    } "quantity";
  } "component";
}

group Diastolic(source qr : QR, source dv, target obs : Observation) {
  dv -> obs.component as comp then {
    qr -> comp.code = create('CodeableConcept') as cv then {
      qr -> cv.coding = create('Coding') as cd then {
        qr -> cd.system = 'http://loinc.org' "codeSystem";
        qr -> cd.code = '8462-4' "codeCode";
        qr -> cd.display = 'Diastolic blood pressure' "codeDisplay";
      } "codeCoding";
    } "code";
    dv -> comp.value = create('Quantity') as q then {
      dv -> q.value = dv "value";
      qr -> q.system = 'http://unitsofmeasure.org' "system";
      qr -> q.code = 'mm[Hg]' "unitCode";
      qr -> q.unit = 'mmHg' "unit";
    } "quantity";
  } "component";
}

// Identity, tags, tenant, subject, encounter and performer. The profile is set
// here for a measurement and overridden by Reading for a blood pressure.
group Shared(source qr : QR, source it, target obs : Observation, target e) {
  qr -> evaluate(qr, id.replaceMatches('/_history/.*$', '').replaceMatches('^.*/', '')) as qid then {
    it.linkId as k ->  append(qid, '-', k) as idText,  obs.id = cast(idText, 'id') "id";
    it.linkId as k -> e.fullUrl = append('Observation/', qid, '-', k) "fullUrl";
    it.linkId as k -> e.request as rq then {
      qr -> rq.method = 'PUT' "method";
      qr -> rq.url = append('Observation/', qid, '-', k) "url";
    } "request";
  } "ids";
  qr.meta as qm then {
    qm.tag as t ->  obs.meta as m,  m.tag = t;
  } "tags";
  qr.extension as x where (url = 'https://fhir.optimalhealth.org/ig/healthy-route/StructureDefinition/hr-tenant') -> obs.extension = x "tenant";
  qr -> obs.status = 'final' "status";
  qr.subject as s -> obs.subject = s;
  qr.encounter as en -> obs.encounter = en;
  qr.author as au -> obs.performer = au "performer";
}