SGHI FHIR Profile Implementation Guide
0.1.0 - ci-build

SGHI FHIR Profile Implementation Guide, published by Kathurima Kimathi. This guide is not an authorized publication; it is the continuous build for version 0.1.0 built by the FHIR (HL7® FHIR® Standard) CI Build. This version is based on the current content of https://github.com/savannahghi/sil_fhir_profile_ig/ and changes regularly. See the Directory of published versions

StructureMap: Vital Signs Extraction

Official URL: https://fhir.slade360.co.ke/fhir/StructureMap/VitalSignsExtract Version: 0.1.0
Active as of 2025-11-27 Computable Name: VitalSignsExtract

Extracts Observation resources from a QuestionnaireResponse to the Vital Signs questionnaire.

/// url = 'https://fhir.slade360.co.ke/fhir/StructureMap/VitalSignsExtract'
/// name = 'VitalSignsExtract'
/// title = 'Vital Signs Extraction'
/// status = 'active'

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 VitalSignsExtract(source qr : QR, target bundle : Bundle) {
  qr -> bundle.type = 'transaction' "setBundleType";
  qr.item as it where linkId = '834591352448' ->  bundle.entry as eH,  eH.resource = create('Observation') as oH then {
    qr -> oH.id then BuildBaseObs(qr, oH, eH) "baseH";
    qr -> oH.category = cc('http://terminology.hl7.org/CodeSystem/observation-category', 'vital-signs', 'Vital Signs') "catH";
    qr -> oH.code = cc('http://loinc.org', '8302-2', 'Body Height') "codeH";
    it then SetQuantityValueFromItem(it, oH, 'http://unitsofmeasure.org', 'cm', 'centimeter') "valH";
    it then SetNoteFromNested(it, oH) "noteH";
  } "heightObs"; // Height, LOINC 8302-2, cm
  qr.item as it where linkId = '612376851550' ->  bundle.entry as eW,  eW.resource = create('Observation') as oW then {
    qr -> oW.id then BuildBaseObs(qr, oW, eW) "baseW";
    qr -> oW.category = cc('http://terminology.hl7.org/CodeSystem/observation-category', 'vital-signs', 'Vital Signs') "catW";
    qr -> oW.code = cc('http://loinc.org', '29463-7', 'Body Weight') "codeW";
    it then SetQuantityValueFromItem(it, oW, 'http://unitsofmeasure.org', 'kg', 'kilogram') "valW";
    it then SetNoteFromNested(it, oW) "noteW";
  } "weightObs"; // Weight, LOINC 29463-7, kg
  qr.item as it where linkId = '118945747144' ->  bundle.entry as eP,  eP.resource = create('Observation') as oP then {
    qr -> oP.id then BuildBaseObs(qr, oP, eP) "baseP";
    qr -> oP.category = cc('http://terminology.hl7.org/CodeSystem/observation-category', 'vital-signs', 'Vital Signs') "catP";
    qr -> oP.code = cc('http://loinc.org', '8889-8', 'Heart rate by Pulse oximetry') "codeP";
    it then SetQuantityValueFromItem(it, oP, 'http://unitsofmeasure.org', '/min', 'per minute') "valP";
    it then SetNoteFromNested(it, oP) "noteP";
  } "pulseObs"; // Pulse rate, LOINC 8889-8, /min
  qr.item as it where linkId = '961601071435' ->  bundle.entry as eS,  eS.resource = create('Observation') as oS then {
    qr -> oS.id then BuildBaseObs(qr, oS, eS) "baseS";
    qr -> oS.category = cc('http://terminology.hl7.org/CodeSystem/observation-category', 'vital-signs', 'Vital Signs') "catS";
    qr -> oS.code = cc('http://loinc.org', '8480-6', 'Systolic blood pressure') "codeS";
    it then SetQuantityValueFromItem(it, oS, 'http://unitsofmeasure.org', 'mm[Hg]', 'millimeter of mercury') "valS";
    it then SetNoteFromNested(it, oS) "noteS";
  } "systolicObs"; // Systolic BP, LOINC 8480-6, mm[Hg]
  qr.item as it where linkId = '354414349687' ->  bundle.entry as eD,  eD.resource = create('Observation') as oD then {
    qr -> oD.id then BuildBaseObs(qr, oD, eD) "baseD";
    qr -> oD.category = cc('http://terminology.hl7.org/CodeSystem/observation-category', 'vital-signs', 'Vital Signs') "catD";
    qr -> oD.code = cc('http://loinc.org', '8462-4', 'Diastolic blood pressure') "codeD";
    it then SetQuantityValueFromItem(it, oD, 'http://unitsofmeasure.org', 'mm[Hg]', 'millimeter of mercury') "valD";
    it then SetNoteFromNested(it, oD) "noteD";
  } "diastolicObs"; // Diastolic BP, LOINC 8462-4, mm[Hg]
  qr.item as it where linkId = '608221161831' ->  bundle.entry as eT,  eT.resource = create('Observation') as oT then {
    qr -> oT.id then BuildBaseObs(qr, oT, eT) "baseT";
    qr -> oT.category = cc('http://terminology.hl7.org/CodeSystem/observation-category', 'vital-signs', 'Vital Signs') "catT";
    qr -> oT.code = cc('http://loinc.org', '8310-5', 'Body Temperature') "codeT";
    it then SetQuantityValueFromItem(it, oT, 'http://unitsofmeasure.org', 'Cel', 'degree Celsius') "valT";
    it then SetNoteFromNested(it, oT) "noteT";
  } "tempObs"; // Temperature, LOINC 8310-5, Cel
  qr.item as it where linkId = '851250344540' ->  bundle.entry as eO2,  eO2.resource = create('Observation') as oO2 then {
    qr -> oO2.id then BuildBaseObs(qr, oO2, eO2) "baseO2";
    qr -> oO2.category = cc('http://terminology.hl7.org/CodeSystem/observation-category', 'vital-signs', 'Vital Signs') "catO2";
    qr -> oO2.code = cc('http://loinc.org', '20564-1', 'Oxygen saturation in Blood') "codeO2";
    it then SetQuantityValueFromItem(it, oO2, 'http://unitsofmeasure.org', '%', 'percent') "valO2";
    it then SetNoteFromNested(it, oO2) "noteO2";
  } "spo2Obs"; // Oxygen saturation, LOINC 20564-1, %
  qr.item as it where linkId = '222626668435' ->  bundle.entry as eR,  eR.resource = create('Observation') as oR then {
    qr -> oR.id then BuildBaseObs(qr, oR, eR) "baseR";
    qr -> oR.category = cc('http://terminology.hl7.org/CodeSystem/observation-category', 'vital-signs', 'Vital Signs') "catR";
    qr -> oR.code = cc('http://loinc.org', '9279-1', 'Respiratory rate') "codeR";
    it then SetQuantityValueFromItem(it, oR, 'http://unitsofmeasure.org', '/min', 'per minute') "valR";
    it then SetNoteFromNested(it, oR) "noteR";
  } "respObs"; // Respiratory rate, LOINC 9279-1, /min
  qr.item as it where linkId = '943347546713' ->  bundle.entry as eM,  eM.resource = create('Observation') as oM then {
    qr -> oM.id then BuildBaseObs(qr, oM, eM) "baseMUAC";
    qr -> oM.category = cc('http://terminology.hl7.org/CodeSystem/observation-category', 'vital-signs', 'Vital Signs') "catMUAC";
    qr -> oM.code = cc('http://loinc.org', '9847-5', 'Circumference') "codeMUAC";
    it then SetQuantityValueFromItem(it, oM, 'http://unitsofmeasure.org', 'mm', 'millimeter') "valMUAC";
    it then SetNoteFromNested(it, oM) "noteMUAC";
  } "muacObs"; // MUAC, LOINC 9847-5, mm
  qr.item as it where linkId = '606919475524' ->  bundle.entry as eBMI,  eBMI.resource = create('Observation') as oBMI then {
    qr -> oBMI.id then BuildBaseObs(qr, oBMI, eBMI) "baseBMI";
    qr -> oBMI.category = cc('http://terminology.hl7.org/CodeSystem/observation-category', 'vital-signs', 'Vital Signs') "catBMI";
    qr -> oBMI.code = cc('http://loinc.org', '39156-5', 'Body mass index (BMI) [Ratio]') "codeBMI";
    it then SetStringValueFromItem(it, oBMI) "valBMI";
    it then SetNoteFromNested(it, oBMI) "noteBMI";
  } "bmiObs"; // BMI, LOINC 39156-5, as string if provided
}

group BuildBaseObs(source qr : QR, target obs : Observation, target entry) {
  qr -> entry.request as req then {
    qr -> req.method = 'POST' "reqMethod";
    qr -> req.url = 'Observation' "reqUrl";
  } "entryRequest";
  qr -> obs.id = uuid() then SetFullUrl(obs, entry) "setIdAndFullUrl";
  qr -> obs.status = 'final' "setStatus";
  qr.subject as s -> obs.subject = create('Reference') as r then {
    s.reference as ref -> r.reference = ref "subRef";
    s.display as disp -> r.display = disp "subDisp";
  } "setSubject";
  qr.authored as t -> obs.effective = t "setEffective";
  qr.authored as t -> obs.issued = t "setIssued";
  qr -> obs.derivedFrom = create('Reference') as dr then {
    qr.id as id -> dr.reference = append('QuestionnaireResponse/', id) "derRef";
  } "setDerivedFrom";
}

group SetQuantityValueFromItem(source it : Element, target obs : Observation, source sys : string, source ucode : string, source utext : string) {
  it.answer as a -> obs.value = create('Quantity') as q then {
    a.value as v -> q.value = v "qVal";
    sys -> q.system = sys "qSys";
    ucode -> q.code = ucode "qCode";
    utext -> q.unit = utext "qUnit";
  } "setQuantity";
}

group SetStringValueFromItem(source it : Element, target obs : Observation) {
  it.answer as a -> obs.value = a.value "setString";
}

group SetNoteFromNested(source it : Element, target obs : Observation) {
  it.answer as a then {
    a.item as ni then {
      ni.answer as na where na.value.exists() -> obs.note = create('Annotation') as an then {
        na.value as txt -> an.text = txt "noteTxt";
      } "addNote";
    } "scanNested";
  } "ifNested";
}

group SetFullUrl(source obs : Observation, target entry) {
  obs.id as id -> entry.fullUrl = append('https://fhir.slade360.co.ke/fhir/Observation/', id) "assignFullUrl";
}