FHIR IG analytics| Package | silfhirprofileig |
| Resource Type | StructureMap |
| Id | StructureMap-ExtractOpticalPrescription.json |
| FHIR Version | R5 |
| Source | https://build.fhir.org/ig/savannahghi/sil_fhir_profile_ig/StructureMap-ExtractOpticalPrescription.html |
| URL | https://fhir.slade360.co.ke/fhir/StructureMap/ExtractOpticalPrescription |
| Version | 0.1.0 |
| Status | active |
| Date | 2026-10-06T18:48:22+00:00 |
| Name | ExtractOpticalPrescription |
| Title | Optical Prescription Extraction |
| Description | The optical prescription out as one VisionPrescription plus one Observation per pupillary distance and visual acuity the prescriber wrote. VisionPrescription is the resource R5 defines for exactly this -- an authorization for the provision of glasses and/or contact lenses -- and it takes the powers: sphere, cylinder, axis and the reading addition for each eye, and the contact lens power, back curve, diameter, brand, colour and wear period. It has no element for pupillary distance or visual acuity, and LOINC names both on a glasses lens prescription per eye at distance and near, so those become LOINC-coded Observations rather than notes. Lens type and coating, which VisionPrescription also cannot code, go into lensSpecification.note, which R5 defines as notes for special requirements such as coatings and lens materials. |
No resources found
No resources found
Note: links and images are rebased to the (stated) source
Generated Narrative: StructureMap ExtractOpticalPrescription
/// url = 'https://fhir.slade360.co.ke/fhir/StructureMap/ExtractOpticalPrescription' /// name = 'ExtractOpticalPrescription' /// title = 'Optical Prescription Extraction' /// status = 'active' /// description = 'The optical prescription out as one VisionPrescription plus one Observation per pupillary distance and visual acuity the prescriber wrote. VisionPrescription is the resource R5 defines for exactly this -- an authorization for the provision of glasses and/or contact lenses -- and it takes the powers: sphere, cylinder, axis and the reading addition for each eye, and the contact lens power, back curve, diameter, brand, colour and wear period. It has no element for pupillary distance or visual acuity, and LOINC names both on a glasses lens prescription per eye at distance and near, so those become LOINC-coded Observations rather than notes. Lens type and coating, which VisionPrescription also cannot code, go into lensSpecification.note, which R5 defines as notes for special requirements such as coatings and lens materials.' 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/VisionPrescription" alias VisionPrescription as target uses "http://hl7.org/fhir/StructureDefinition/Observation" alias Observation as target group ExtractOpticalPrescription(source qr : QR, target bundle : Bundle) { qr -> bundle.type = 'transaction' "setBundleType"; // The prescription itself, created only when every element VisionPrescription // makes 1..1 can be filled from the response -- not bent to fit: // lensSpecification at least one lens power. The patterns are every power // item on the form -- the eyewear distance and near rows, // the reading addition and the contact lens rows -- and // deliberately not the pupillary distance or the visual // acuity, which are measurements and extract on their own // below, nor the transposed rows, which are the same // prescription computed the other way round. // patient the response's subject, when it is a Patient. // dateWritten, the response's authored. The form does not ask for a // created date; the client stamps it. // prescriber the response's author (else source) when it is a // Practitioner or a PractitionerRole. The form does not // ask who; the client stamps the practitioner recording // it. // A response short of any of these extracts no VisionPrescription. That is // the rule held rather than worked round: the alternative is a prescription // stored without a prescriber, or with one invented. The pupillary distances // and visual acuities still extract, because they need none of it. qr where ((repeat(item).where(linkId.matches('^(eyewear|contact-lens)/(right|left)/((distance|near)/)?(sph|cyl|add|power|base-curve|diameter)$')).answer.value.ofType(decimal).exists() or repeat(item).where(linkId.matches('^(eyewear|contact-lens)/(right|left)/((distance|near)/)?axis$')).answer.value.ofType(integer).exists()) and subject.where(reference.matches('(^|/)Patient/[^/]+$')).exists() and authored.exists() and (author.where(reference.matches('(^|/)(Practitioner|PractitionerRole)/[^/]+$')).exists() or source.where(reference.matches('(^|/)(Practitioner|PractitionerRole)/[^/]+$')).exists())) -> bundle.entry as entry, entry.resource = create('VisionPrescription') as vp then { qr -> vp, entry then BuildVisionPrescriptionBase(qr, vp, entry) "base"; qr -> vp then SetPrescriber(qr, vp) "prescriber"; qr.item as ew where (linkId = 'eyewear') then SpectacleLenses(qr, ew, vp) "eyewear"; qr.item as cl where (linkId = 'contact-lens') then ContactLenses(qr, cl, vp) "contactLens"; } "prescription"; // Pupillary distance and visual acuity, one Observation each. These extract // whether or not there is a prescription: a PD taken for a frame fitting is // worth keeping on its own. qr.item as ew where (linkId = 'eyewear') then EyewearMeasurements(qr, ew, bundle) "measurements"; } // ───────────────────────────────────────────────────────────────────────────── // The prescription // ───────────────────────────────────────────────────────────────────────────── group BuildVisionPrescriptionBase(source qr : QR, target vp : VisionPrescription, target entry) { qr -> vp then SetResponseProvenance(qr, vp) "responseProvenance"; qr -> vp.id = uuid() then SetVisionPrescriptionFullUrl(vp, entry) "idAndFullUrl"; qr -> entry.request as request then { qr -> request.method = 'POST' "requestMethod"; qr -> request.url = 'VisionPrescription' "requestUrl"; } "entryRequest"; // `active`: a prescription recorded on this form is one somebody is about to // dispense against. fm-status has no `completed`. qr -> vp.status = 'active' "status"; qr.subject as s where ((reference.exists() and (reference.startsWith('Patient/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Patient')))) -> vp.patient = s "patient"; qr.encounter as e where ((reference.exists() and (reference.startsWith('Encounter/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Encounter')))) -> vp.encounter = e; // Both dates, 1..1 each, are the response's `authored`. The form does not ask // for a prescription date: the client stamps the moment it was recorded, and // that is when this prescription was written and when this record was made. qr.authored as t -> vp.dateWritten = t "dateWritten"; qr.authored as t -> vp.created = t "created"; } // The prescriber is 1..1, and the form does not ask for one. It is the // practitioner who recorded the prescription, which the client stamps on the // response as `author`; `source` is taken when there is no author. Either is // copied across only when it points at a Practitioner or a PractitionerRole -- // the two types VisionPrescription.prescriber allows -- because `author` may // also name a Device, a Patient or an Organization. // Nothing is guessed when neither qualifies: the rule that creates the // prescription is guarded on one of them, so this group always finds one. group SetPrescriber(source qr : QR, target vp : VisionPrescription) { qr.author as a where ((reference.matches('(^|/)(Practitioner|PractitionerRole)/[^/]+$')) and ((reference.exists() and (reference.startsWith('Practitioner/') or reference.startsWith('PractitionerRole/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Practitioner') or ('/' + type).endsWith('/PractitionerRole'))))) -> vp.prescriber = a "prescriberFromAuthor"; qr where (author.where(((reference.exists() and (reference.startsWith('Practitioner/') or reference.startsWith('PractitionerRole/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Practitioner') or ('/' + type).endsWith('/PractitionerRole'))))).exists().not()) then { qr.source as s where ((reference.matches('(^|/)(Practitioner|PractitionerRole)/[^/]+$')) and ((reference.exists() and (reference.startsWith('Practitioner/') or reference.startsWith('PractitionerRole/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Practitioner') or ('/' + type).endsWith('/PractitionerRole'))))) -> vp.prescriber = s "prescriberFromSource"; } "sourceFallback"; } // ── Spectacle lenses ───────────────────────────────────────────────────────── // Up to two lens specifications per eye, one per row the prescriber filled in. // The distance specification carries the distance vision (DV) row and the // reading addition. That is the shape R5 itself uses for a spectacle // prescription -- one specification per eye with the distance powers and // `add` -- so a consumer that reads only that one still gets a complete // multifocal prescription. // The near specification carries the near vision (NV) row, when one was // written. It is not derived from DV + ADD and it is not dropped when an ADD is // present: the prescriber wrote it, and for reading glasses it is the whole // prescription. The two cannot be told apart by any element R5 gives a lens // specification, so `product` carries HL7's `lens` beside the local // `spectacle-lens-distance` or `spectacle-lens-near` -- the same product at a // finer grain, which the example-strength binding allows. // linkIds are matched by suffix inside an eye, so the two eyes share one set // of groups. The eye code is set by the caller, which is the only place that // knows which eye it is in. group SpectacleLenses(source qr : QR, source ew, target vp : VisionPrescription) { ew.item as eye where (linkId = 'eyewear/right') then { eye where (item.where(linkId.endsWith('/distance')).item.where(linkId.endsWith('/sph') or linkId.endsWith('/cyl')).answer.value.ofType(decimal).exists() or item.where(linkId.endsWith('/distance')).item.where(linkId.endsWith('/axis')).answer.value.ofType(integer).exists() or item.where(linkId.endsWith('/add')).answer.value.ofType(decimal).exists()) -> vp.lensSpecification as spec then { eye -> spec then DistanceSpectacleLens(qr, ew, eye, spec) "lens"; eye -> spec.eye = 'right' "eyeCode"; } "rightDistance"; eye where (item.where(linkId.endsWith('/near')).item.where(linkId.endsWith('/sph') or linkId.endsWith('/cyl')).answer.value.ofType(decimal).exists() or item.where(linkId.endsWith('/near')).item.where(linkId.endsWith('/axis')).answer.value.ofType(integer).exists()) -> vp.lensSpecification as spec then { eye -> spec then NearSpectacleLens(qr, ew, eye, spec) "lens"; eye -> spec.eye = 'right' "eyeCode"; } "rightNear"; } "right"; ew.item as eye where (linkId = 'eyewear/left') then { eye where (item.where(linkId.endsWith('/distance')).item.where(linkId.endsWith('/sph') or linkId.endsWith('/cyl')).answer.value.ofType(decimal).exists() or item.where(linkId.endsWith('/distance')).item.where(linkId.endsWith('/axis')).answer.value.ofType(integer).exists() or item.where(linkId.endsWith('/add')).answer.value.ofType(decimal).exists()) -> vp.lensSpecification as spec then { eye -> spec then DistanceSpectacleLens(qr, ew, eye, spec) "lens"; eye -> spec.eye = 'left' "eyeCode"; } "leftDistance"; eye where (item.where(linkId.endsWith('/near')).item.where(linkId.endsWith('/sph') or linkId.endsWith('/cyl')).answer.value.ofType(decimal).exists() or item.where(linkId.endsWith('/near')).item.where(linkId.endsWith('/axis')).answer.value.ofType(integer).exists()) -> vp.lensSpecification as spec then { eye -> spec then NearSpectacleLens(qr, ew, eye, spec) "lens"; eye -> spec.eye = 'left' "eyeCode"; } "leftNear"; } "left"; } group DistanceSpectacleLens(source qr : QR, source ew, source eye, target spec) { eye -> spec.product as product then { eye -> product.coding as coding then { eye -> coding.system = 'http://terminology.hl7.org/CodeSystem/ex-visionprescriptionproduct' "hl7System"; eye -> coding.code = 'lens' "hl7Code"; eye -> coding.display = 'Lens' "hl7Display"; } "hl7Coding"; eye -> product.coding as coding then { eye -> coding.system = 'https://fhir.slade360.co.ke/fhir/CodeSystem/concept-codesystem' "rowSystem"; eye -> coding.code = 'spectacle-lens-distance' "rowCode"; eye -> coding.display = 'Spectacle lens for distance vision' "rowDisplay"; } "rowCoding"; eye -> product.text = 'Spectacle lens — distance vision (DV)' "productText"; } "product"; eye.item as row where (linkId.endsWith('/distance')) then { row -> spec then LensPowers(row, spec) "powers"; } "distanceRow"; eye.item as it where (linkId.endsWith('/add') and answer.value.ofType(decimal).exists()) then { it.answer first as a then { a.value as v -> spec.add = v "addValue"; } "addAnswer"; } "addition"; ew -> spec then SpectacleLensNotes(qr, ew, spec) "notes"; } group NearSpectacleLens(source qr : QR, source ew, source eye, target spec) { eye -> spec.product as product then { eye -> product.coding as coding then { eye -> coding.system = 'http://terminology.hl7.org/CodeSystem/ex-visionprescriptionproduct' "hl7System"; eye -> coding.code = 'lens' "hl7Code"; eye -> coding.display = 'Lens' "hl7Display"; } "hl7Coding"; eye -> product.coding as coding then { eye -> coding.system = 'https://fhir.slade360.co.ke/fhir/CodeSystem/concept-codesystem' "rowSystem"; eye -> coding.code = 'spectacle-lens-near' "rowCode"; eye -> coding.display = 'Spectacle lens for near vision' "rowDisplay"; } "rowCoding"; eye -> product.text = 'Spectacle lens — near vision (NV)' "productText"; } "product"; eye.item as row where (linkId.endsWith('/near')) then { row -> spec then LensPowers(row, spec) "powers"; } "nearRow"; ew -> spec then SpectacleLensNotes(qr, ew, spec) "notes"; } // Sphere, cylinder and axis off one row. Typed guards, because a rule that // assigns an answer of the wrong type to a decimal element does not skip -- it // fails the whole extraction. group LensPowers(source row, target spec) { row.item as it where (linkId.endsWith('/sph') and answer.value.ofType(decimal).exists()) then { it.answer first as a then { a.value as v -> spec.sphere = v "sphereValue"; } "sphereAnswer"; } "sphere"; row.item as it where (linkId.endsWith('/cyl') and answer.value.ofType(decimal).exists()) then { it.answer first as a then { a.value as v -> spec.cylinder = v "cylinderValue"; } "cylinderAnswer"; } "cylinder"; row.item as it where (linkId.endsWith('/axis') and answer.value.ofType(integer).exists()) then { it.answer first as a then { a.value as v -> spec.axis = v "axisValue"; } "axisAnswer"; } "axis"; } // Lens type and coating, as the words the prescriber ticked. VisionPrescription // has no coded element for either; `note` is where R5 says they go ("notes for // special requirements such as coatings and lens materials"), and the codes // themselves stay on the response. Both go on every spectacle specification, // because the paper ticks them once for the pair. group SpectacleLensNotes(source qr : QR, source ew, target spec) { ew.item as it where ((linkId = 'eyewear/lens-types') and answer.value.ofType(Coding).exists()) -> spec.note as note then { it -> note.text = evaluate(it, 'Lens type: ' & answer.value.ofType(Coding).select(iif(display.exists(), display, code)).join(', ')) "lensTypeText"; } "lensTypes"; ew.item as it where ((linkId = 'eyewear/lens-coating') and answer.value.ofType(Coding).exists()) -> spec.note as note then { it -> note.text = evaluate(it, 'Lens coating: ' & answer.value.ofType(Coding).select(iif(display.exists(), display, code)).join(', ')) "lensCoatingText"; } "lensCoating"; qr -> spec then PrescriptionNotes(qr, spec) "prescriptionNotes"; } // The free-text prescription notes. VisionPrescription has no note of its own, // only one per lens specification, so the notes go on each specification -- // repeated rather than put on whichever happens to be first, which a consumer // reading one eye would miss. group PrescriptionNotes(source qr : QR, target spec) { qr.item as it where ((linkId = 'notes') and answer.value.ofType(string).exists()) -> spec.note as note then { it.answer first as a then { a.value as v -> note.text = v "noteText"; } "noteAnswer"; } "notes"; } // ── Contact lenses ─────────────────────────────────────────────────────────── // One specification per eye with anything written on it. The contact lens // power goes to `power`, R5's "contact lens power", which is what its own // contact lens example uses; `sphere` stays the spectacle lens power. Brand, // colour and the replacement schedule are asked once for the pair and copied // onto both eyes, because R5 holds them per specification. group ContactLenses(source qr : QR, source cl, target vp : VisionPrescription) { cl.item as eye where ((linkId = 'contact-lens/right') and (item.answer.value.ofType(decimal).exists() or item.answer.value.ofType(integer).exists())) -> vp.lensSpecification as spec then { eye -> spec then ContactLens(qr, cl, eye, spec) "lens"; eye -> spec.eye = 'right' "eyeCode"; } "right"; cl.item as eye where ((linkId = 'contact-lens/left') and (item.answer.value.ofType(decimal).exists() or item.answer.value.ofType(integer).exists())) -> vp.lensSpecification as spec then { eye -> spec then ContactLens(qr, cl, eye, spec) "lens"; eye -> spec.eye = 'left' "eyeCode"; } "left"; } group ContactLens(source qr : QR, source cl, source eye, target spec) { eye -> spec.product as product then { eye -> product.coding as coding then { eye -> coding.system = 'http://terminology.hl7.org/CodeSystem/ex-visionprescriptionproduct' "hl7System"; eye -> coding.code = 'contact' "hl7Code"; eye -> coding.display = 'Contact Lens' "hl7Display"; } "hl7Coding"; eye -> product.text = 'Contact lens' "productText"; } "product"; eye.item as it where (linkId.endsWith('/power') and answer.value.ofType(decimal).exists()) then { it.answer first as a then { a.value as v -> spec.power = v "powerValue"; } "powerAnswer"; } "power"; eye.item as it where (linkId.endsWith('/cyl') and answer.value.ofType(decimal).exists()) then { it.answer first as a then { a.value as v -> spec.cylinder = v "cylinderValue"; } "cylinderAnswer"; } "cylinder"; eye.item as it where (linkId.endsWith('/axis') and answer.value.ofType(integer).exists()) then { it.answer first as a then { a.value as v -> spec.axis = v "axisValue"; } "axisAnswer"; } "axis"; eye.item as it where (linkId.endsWith('/add') and answer.value.ofType(decimal).exists()) then { it.answer first as a then { a.value as v -> spec.add = v "addValue"; } "addAnswer"; } "addition"; eye.item as it where (linkId.endsWith('/base-curve') and answer.value.ofType(decimal).exists()) then { it.answer first as a then { a.value as v -> spec.backCurve = v "backCurveValue"; } "backCurveAnswer"; } "backCurve"; eye.item as it where (linkId.endsWith('/diameter') and answer.value.ofType(decimal).exists()) then { it.answer first as a then { a.value as v -> spec.diameter = v "diameterValue"; } "diameterAnswer"; } "diameter"; cl.item as it where ((linkId = 'contact-lens/brand') and answer.value.ofType(string).exists()) then { it.answer first as a then { a.value as v -> spec.brand = v "brandValue"; } "brandAnswer"; } "brand"; cl.item as it where ((linkId = 'contact-lens/colour') and answer.value.ofType(string).exists()) then { it.answer first as a then { a.value as v -> spec.color = v "colourValue"; } "colourAnswer"; } "colour"; // The replacement schedule is R5's `duration`, "the recommended maximum wear // period for the lens", as a UCUM Quantity. One rule per answer, each guarded // on its own code, so an answer this map does not know writes no duration // rather than an empty Quantity. cl.item as it where ((linkId = 'contact-lens/replacement') and (answer.value.ofType(Coding).code = 'contact-lens-replace-daily')) -> spec.duration as d then { it -> d.value = 1 "value"; it -> d.unit = 'day' "unit"; it -> d.system = 'http://unitsofmeasure.org' "system"; it -> d.code = 'd' "code"; } "daily"; cl.item as it where ((linkId = 'contact-lens/replacement') and (answer.value.ofType(Coding).code = 'contact-lens-replace-two-weekly')) -> spec.duration as d then { it -> d.value = 2 "value"; it -> d.unit = 'weeks' "unit"; it -> d.system = 'http://unitsofmeasure.org' "system"; it -> d.code = 'wk' "code"; } "twoWeekly"; cl.item as it where ((linkId = 'contact-lens/replacement') and (answer.value.ofType(Coding).code = 'contact-lens-replace-monthly')) -> spec.duration as d then { it -> d.value = 1 "value"; it -> d.unit = 'month' "unit"; it -> d.system = 'http://unitsofmeasure.org' "system"; it -> d.code = 'mo' "code"; } "monthly"; cl.item as it where ((linkId = 'contact-lens/replacement') and (answer.value.ofType(Coding).code = 'contact-lens-replace-quarterly')) -> spec.duration as d then { it -> d.value = 3 "value"; it -> d.unit = 'months' "unit"; it -> d.system = 'http://unitsofmeasure.org' "system"; it -> d.code = 'mo' "code"; } "quarterly"; cl.item as it where ((linkId = 'contact-lens/replacement') and (answer.value.ofType(Coding).code = 'contact-lens-replace-yearly')) -> spec.duration as d then { it -> d.value = 1 "value"; it -> d.unit = 'year' "unit"; it -> d.system = 'http://unitsofmeasure.org' "system"; it -> d.code = 'a' "code"; } "yearly"; qr -> spec then PrescriptionNotes(qr, spec) "prescriptionNotes"; } // ───────────────────────────────────────────────────────────────────────────── // Pupillary distance and visual acuity // Neither has a home in VisionPrescription, and LOINC names both exactly, per // eye, on a glasses lens prescription -- so each is its own Observation under // its own code rather than prose in a note. The codes were looked up, not // assumed; each display below is LOINC's own: // distance (DV) near (NV) // R-PD 28840-7 28841-5 mm // L-PD 28857-1 55985-6 mm // R-VA 28831-6 28839-9 as written // L-VA 28847-2 28856-3 as written // The PD without a qualifier is LOINC's distance PD; near is the one it // qualifies. Visual acuity is kept as the string the prescriber wrote -- 6/6, // 6/9, N6, CF -- because the notations mix Snellen fractions, N-point print // sizes and counting fingers, and forcing them into a Ratio would lose the ones // that are not fractions. // Nothing on VisionPrescription can point at these. They are tied to it by the // encounter, by `derivedFrom` naming the same response, and by the same // provenance tag. // ───────────────────────────────────────────────────────────────────────────── group EyewearMeasurements(source qr : QR, source ew, target bundle : Bundle) { ew.item as eye where (linkId = 'eyewear/right') then { eye.item as row where (linkId = 'eyewear/right/distance') then { row.item as it where ((linkId = 'eyewear/right/distance/pd') and answer.value.ofType(decimal).exists()) -> bundle.entry as entry, entry.resource = create('Observation') as obs then { qr -> obs, entry then BuildObservationBase(qr, obs, entry) "base"; it -> obs.code as code then { it -> code.coding as coding then { it -> coding.system = 'http://loinc.org' "system"; it -> coding.code = '28840-7' "code"; it -> coding.display = 'Right glasses lens prescription Pupillary distance.monocular' "display"; } "coding"; it -> code.text = 'Right eye pupillary distance, distance vision (R-PD, DV)' "text"; } "code"; it -> obs then SetMillimetres(it, obs) "value"; } "rightDistancePd"; row.item as it where ((linkId = 'eyewear/right/distance/va') and answer.value.ofType(string).exists()) -> bundle.entry as entry, entry.resource = create('Observation') as obs then { qr -> obs, entry then BuildObservationBase(qr, obs, entry) "base"; it -> obs.code as code then { it -> code.coding as coding then { it -> coding.system = 'http://loinc.org' "system"; it -> coding.code = '28831-6' "code"; it -> coding.display = 'Right glasses lens prescription Visual acuity far' "display"; } "coding"; it -> code.text = 'Right eye visual acuity, distance vision (R-VA, DV)' "text"; } "code"; it -> obs then SetAcuity(it, obs) "value"; } "rightDistanceVa"; } "rightDistance"; eye.item as row where (linkId = 'eyewear/right/near') then { row.item as it where ((linkId = 'eyewear/right/near/pd') and answer.value.ofType(decimal).exists()) -> bundle.entry as entry, entry.resource = create('Observation') as obs then { qr -> obs, entry then BuildObservationBase(qr, obs, entry) "base"; it -> obs.code as code then { it -> code.coding as coding then { it -> coding.system = 'http://loinc.org' "system"; it -> coding.code = '28841-5' "code"; it -> coding.display = 'Right glasses lens prescription Pupillary distance.monocular near' "display"; } "coding"; it -> code.text = 'Right eye pupillary distance, near vision (R-PD, NV)' "text"; } "code"; it -> obs then SetMillimetres(it, obs) "value"; } "rightNearPd"; row.item as it where ((linkId = 'eyewear/right/near/va') and answer.value.ofType(string).exists()) -> bundle.entry as entry, entry.resource = create('Observation') as obs then { qr -> obs, entry then BuildObservationBase(qr, obs, entry) "base"; it -> obs.code as code then { it -> code.coding as coding then { it -> coding.system = 'http://loinc.org' "system"; it -> coding.code = '28839-9' "code"; it -> coding.display = 'Right glasses lens prescription Visual acuity near' "display"; } "coding"; it -> code.text = 'Right eye visual acuity, near vision (R-VA, NV)' "text"; } "code"; it -> obs then SetAcuity(it, obs) "value"; } "rightNearVa"; } "rightNear"; } "right"; ew.item as eye where (linkId = 'eyewear/left') then { eye.item as row where (linkId = 'eyewear/left/distance') then { row.item as it where ((linkId = 'eyewear/left/distance/pd') and answer.value.ofType(decimal).exists()) -> bundle.entry as entry, entry.resource = create('Observation') as obs then { qr -> obs, entry then BuildObservationBase(qr, obs, entry) "base"; it -> obs.code as code then { it -> code.coding as coding then { it -> coding.system = 'http://loinc.org' "system"; it -> coding.code = '28857-1' "code"; it -> coding.display = 'Left glasses lens prescription Pupillary distance.monocular' "display"; } "coding"; it -> code.text = 'Left eye pupillary distance, distance vision (L-PD, DV)' "text"; } "code"; it -> obs then SetMillimetres(it, obs) "value"; } "leftDistancePd"; row.item as it where ((linkId = 'eyewear/left/distance/va') and answer.value.ofType(string).exists()) -> bundle.entry as entry, entry.resource = create('Observation') as obs then { qr -> obs, entry then BuildObservationBase(qr, obs, entry) "base"; it -> obs.code as code then { it -> code.coding as coding then { it -> coding.system = 'http://loinc.org' "system"; it -> coding.code = '28847-2' "code"; it -> coding.display = 'Left glasses lens prescription Visual acuity far' "display"; } "coding"; it -> code.text = 'Left eye visual acuity, distance vision (L-VA, DV)' "text"; } "code"; it -> obs then SetAcuity(it, obs) "value"; } "leftDistanceVa"; } "leftDistance"; eye.item as row where (linkId = 'eyewear/left/near') then { row.item as it where ((linkId = 'eyewear/left/near/pd') and answer.value.ofType(decimal).exists()) -> bundle.entry as entry, entry.resource = create('Observation') as obs then { qr -> obs, entry then BuildObservationBase(qr, obs, entry) "base"; it -> obs.code as code then { it -> code.coding as coding then { it -> coding.system = 'http://loinc.org' "system"; it -> coding.code = '55985-6' "code"; it -> coding.display = 'Left glasses lens prescription Pupillary distance.monocular near' "display"; } "coding"; it -> code.text = 'Left eye pupillary distance, near vision (L-PD, NV)' "text"; } "code"; it -> obs then SetMillimetres(it, obs) "value"; } "leftNearPd"; row.item as it where ((linkId = 'eyewear/left/near/va') and answer.value.ofType(string).exists()) -> bundle.entry as entry, entry.resource = create('Observation') as obs then { qr -> obs, entry then BuildObservationBase(qr, obs, entry) "base"; it -> obs.code as code then { it -> code.coding as coding then { it -> coding.system = 'http://loinc.org' "system"; it -> coding.code = '28856-3' "code"; it -> coding.display = 'Left glasses lens prescription Visual acuity near' "display"; } "coding"; it -> code.text = 'Left eye visual acuity, near vision (L-VA, NV)' "text"; } "code"; it -> obs then SetAcuity(it, obs) "value"; } "leftNearVa"; } "leftNear"; } "left"; } // `Observation.value[x]` has no decimal choice, so the PD is built as a UCUM // Quantity. The unit is fixed by the form. group SetMillimetres(source it, target obs : Observation) { it.answer first as a then { a.value as v -> obs.value = create('Quantity') as q then { v -> q.value = v "quantityValue"; v -> q.unit = 'mm' "quantityUnit"; v -> q.system = 'http://unitsofmeasure.org' "quantitySystem"; v -> q.code = 'mm' "quantityCode"; } "quantity"; }; } group SetAcuity(source it, target obs : Observation) { it.answer first as a then { a.value as v -> obs.value = v "acuityValue"; }; } // ───────────────────────────────────────────────────────────────────────────── // Shared scaffolding. Repeated in each map rather than imported: the transform // engine resolves `imports` against whatever StructureMaps happen to be on the // server, and a form that stops extracting because a shared map was not // uploaded is a worse failure than a duplicated group. // ───────────────────────────────────────────────────────────────────────────── group BuildObservationBase(source qr : QR, target obs : Observation, target entry) { qr -> obs then SetResponseProvenance(qr, obs) "responseProvenance"; qr -> obs.id = uuid() then SetObservationFullUrl(obs, entry) "idAndFullUrl"; qr -> entry.request as request then { qr -> request.method = 'POST' "requestMethod"; qr -> request.url = 'Observation' "requestUrl"; } "entryRequest"; qr -> obs.status = 'final' "status"; qr -> obs.category as cat then { qr -> cat.coding as coding then { qr -> coding.system = 'http://terminology.hl7.org/CodeSystem/observation-category' "categorySystem"; qr -> coding.code = 'exam' "categoryCode"; qr -> coding.display = 'Exam' "categoryDisplay"; } "categoryCoding"; } "category"; qr.subject as s where ((reference.exists() and (reference.startsWith('Patient/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Patient')))) -> obs.subject = s; qr.encounter as e where ((reference.exists() and (reference.startsWith('Encounter/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Encounter')))) -> obs.encounter = e; // Measured when the prescription was written, which is the response's // `authored` -- the form does not ask for a date. qr.authored as t -> obs.effective = t "effective"; // Measured by the prescriber, chosen the same way as on the prescription. qr.author as a where ((reference.matches('(^|/)(Practitioner|PractitionerRole)/[^/]+$')) and ((reference.exists() and (reference.startsWith('Organization/') or reference.startsWith('Patient/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Organization') or ('/' + type).endsWith('/Patient'))))) -> obs.performer = a "performerFromAuthor"; qr where (author.where(((reference.exists() and (reference.startsWith('Organization/') or reference.startsWith('Patient/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Organization') or ('/' + type).endsWith('/Patient'))))).exists().not()) then { qr.source as s where ((reference.matches('(^|/)(Practitioner|PractitionerRole)/[^/]+$')) and ((reference.exists() and (reference.startsWith('Organization/') or reference.startsWith('Patient/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Organization') or ('/' + type).endsWith('/Patient'))))) -> obs.performer = s "performerFromSource"; } "performerSourceFallback"; // A stored response's id already reads "QuestionnaireResponse/<id>", so it is // a relative reference as it stands. The hasValue() guard is what stops an // unsaved response -- one posted straight to $extract rather than saved first // -- from writing "QuestionnaireResponse/null" into the record. qr.id as qid where ($this.hasValue()) -> obs.derivedFrom = create('Reference') as ref, ref.reference = qid "linkQuestionnaireResponse"; } group SetObservationFullUrl(source obs : Observation, target entry) { obs.id as id -> entry.fullUrl = append('https://fhir.slade360.co.ke/fhir/Observation/', id) "assignFullUrl"; } group SetVisionPrescriptionFullUrl(source vp : VisionPrescription, target entry) { vp.id as id -> entry.fullUrl = append('https://fhir.slade360.co.ke/fhir/VisionPrescription/', id) "assignFullUrl"; } // ── Provenance and tenancy ─────────────────────────────────────────────────── // Two tags on everything this map creates. The first says which form produced // it -- the form's document type, not the questionnaire's id, because every // environment mints its own ids and there is one set of maps. The second copies // the tenant tags off the response, because nothing else on the path puts them // there. group SetResponseProvenance(source qr : QR, target r) { qr -> r.meta as m then { qr -> m.tag as t then { qr -> t.system = 'http://slade360edi.com/questionnaire-provenance' "provenanceSystem"; qr -> t.code = 'optical-prescription' "provenanceCode"; qr -> t.display = 'Optical prescription' "provenanceDisplay"; } "provenanceTag"; } "provenanceMeta"; // `meta` is 0..1, so this lands on the same meta the rule above created // rather than a second one. qr.meta as qm then { qm.tag as qt -> r.meta as m, m.tag = qt "copyTag"; } "tenantTags"; }
{
"resourceType": "StructureMap",
"id": "ExtractOpticalPrescription",
"text": {
"status": "generated",
"div": "<!-- snip (see above) -->"
},
"url": "https://fhir.slade360.co.ke/fhir/StructureMap/ExtractOpticalPrescription",
"version": "0.1.0",
"name": "ExtractOpticalPrescription",
"title": "Optical Prescription Extraction",
"status": "active",
"date": "2026-10-06T18:48:22+00:00",
"publisher": "Kathurima Kimathi",
"contact": [
{
"name": "Kathurima Kimathi",
"telecom": [
{
"system": "url",
"value": "https://www.linkedin.com/in/kathurima-kimathi/"
},
{
"system": "email",
"value": "kathurimakimathi415@gmail.com"
}
]
},
{
"name": "Oscar John",
"telecom": [
{
"system": "email",
"value": "oscarjohnotieno@gmail.com",
"use": "work"
}
]
},
{
"name": "Kennedy Omondi",
"telecom": [
{
"system": "email",
"value": "kennankole@gmail.com",
"use": "work"
}
]
}
],
"description": "The optical prescription out as one VisionPrescription plus one Observation per pupillary distance and visual acuity the prescriber wrote. VisionPrescription is the resource R5 defines for exactly this -- an authorization for the provision of glasses and/or contact lenses -- and it takes the powers: sphere, cylinder, axis and the reading addition for each eye, and the contact lens power, back curve, diameter, brand, colour and wear period. It has no element for pupillary distance or visual acuity, and LOINC names both on a glasses lens prescription per eye at distance and near, so those become LOINC-coded Observations rather than notes. Lens type and coating, which VisionPrescription also cannot code, go into lensSpecification.note, which R5 defines as notes for special requirements such as coatings and lens materials.",
"structure": [
{
"url": "http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse",
"mode": "source",
"alias": "QR"
},
{
"url": "http://hl7.org/fhir/StructureDefinition/Bundle",
"mode": "target",
"alias": "Bundle"
},
{
"url": "http://hl7.org/fhir/StructureDefinition/VisionPrescription",
"mode": "target",
"alias": "VisionPrescription"
},
{
"url": "http://hl7.org/fhir/StructureDefinition/Observation",
"mode": "target",
"alias": "Observation"
}
],
"group": [
{
"name": "ExtractOpticalPrescription",
"input": [
{
"name": "qr",
"type": "QR",
"mode": "source"
},
{
"name": "bundle",
"type": "Bundle",
"mode": "target"
}
],
"rule": [
{
"name": "setBundleType",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "bundle",
"element": "type",
"transform": "copy",
"parameter": [
{
"valueString": "transaction"
}
]
}
]
},
{
"name": "prescription",
"source": [
{
"context": "qr",
"condition": "(repeat(item).where(linkId.matches('^(eyewear|contact-lens)/(right|left)/((distance|near)/)?(sph|cyl|add|power|base-curve|diameter)$')).answer.value.ofType(decimal).exists() or repeat(item).where(linkId.matches('^(eyewear|contact-lens)/(right|left)/((distance|near)/)?axis$')).answer.value.ofType(integer).exists()) and subject.where(reference.matches('(^|/)Patient/[^/]+$')).exists() and authored.exists() and (author.where(reference.matches('(^|/)(Practitioner|PractitionerRole)/[^/]+$')).exists() or source.where(reference.matches('(^|/)(Practitioner|PractitionerRole)/[^/]+$')).exists())"
}
],
"target": [
{
"context": "bundle",
"element": "entry",
"variable": "entry"
},
{
"context": "entry",
"element": "resource",
"variable": "vp",
"transform": "create",
"parameter": [
{
"valueString": "VisionPrescription"
}
]
}
],
"rule": [
{
"name": "base",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "vp"
},
{
"context": "entry"
}
],
"dependent": [
{
"name": "BuildVisionPrescriptionBase",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "vp"
},
{
"valueId": "entry"
}
]
}
]
},
{
"name": "prescriber",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "vp"
}
],
"dependent": [
{
"name": "SetPrescriber",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "vp"
}
]
}
]
},
{
"name": "eyewear",
"source": [
{
"context": "qr",
"element": "item",
"variable": "ew",
"condition": "linkId = 'eyewear'"
}
],
"dependent": [
{
"name": "SpectacleLenses",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "ew"
},
{
"valueId": "vp"
}
]
}
]
},
{
"name": "contactLens",
"source": [
{
"context": "qr",
"element": "item",
"variable": "cl",
"condition": "linkId = 'contact-lens'"
}
],
"dependent": [
{
"name": "ContactLenses",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "cl"
},
{
"valueId": "vp"
}
]
}
]
}
],
"documentation": "The prescription itself, created only when every element VisionPrescription\r\nmakes 1..1 can be filled from the response -- not bent to fit:\r\nlensSpecification at least one lens power. The patterns are every power\r\nitem on the form -- the eyewear distance and near rows,\r\nthe reading addition and the contact lens rows -- and\r\ndeliberately not the pupillary distance or the visual\r\nacuity, which are measurements and extract on their own\r\nbelow, nor the transposed rows, which are the same\r\nprescription computed the other way round.\r\npatient the response's subject, when it is a Patient.\r\ndateWritten, the response's authored. The form does not ask for a\r\ncreated date; the client stamps it.\r\nprescriber the response's author (else source) when it is a\r\nPractitioner or a PractitionerRole. The form does not\r\nask who; the client stamps the practitioner recording\r\nit.\r\nA response short of any of these extracts no VisionPrescription. That is\r\nthe rule held rather than worked round: the alternative is a prescription\r\nstored without a prescriber, or with one invented. The pupillary distances\r\nand visual acuities still extract, because they need none of it."
},
{
"name": "measurements",
"source": [
{
"context": "qr",
"element": "item",
"variable": "ew",
"condition": "linkId = 'eyewear'"
}
],
"dependent": [
{
"name": "EyewearMeasurements",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "ew"
},
{
"valueId": "bundle"
}
]
}
],
"documentation": "Pupillary distance and visual acuity, one Observation each. These extract\r\nwhether or not there is a prescription: a PD taken for a frame fitting is\r\nworth keeping on its own."
}
]
},
{
"name": "BuildVisionPrescriptionBase",
"documentation": "─────────────────────────────────────────────────────────────────────────────\r\nThe prescription\r\n─────────────────────────────────────────────────────────────────────────────",
"input": [
{
"name": "qr",
"type": "QR",
"mode": "source"
},
{
"name": "vp",
"type": "VisionPrescription",
"mode": "target"
},
{
"name": "entry",
"mode": "target"
}
],
"rule": [
{
"name": "responseProvenance",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "vp"
}
],
"dependent": [
{
"name": "SetResponseProvenance",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "vp"
}
]
}
]
},
{
"name": "idAndFullUrl",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "vp",
"element": "id",
"transform": "uuid"
}
],
"dependent": [
{
"name": "SetVisionPrescriptionFullUrl",
"parameter": [
{
"valueId": "vp"
},
{
"valueId": "entry"
}
]
}
]
},
{
"name": "entryRequest",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "entry",
"element": "request",
"variable": "request"
}
],
"rule": [
{
"name": "requestMethod",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "request",
"element": "method",
"transform": "copy",
"parameter": [
{
"valueString": "POST"
}
]
}
]
},
{
"name": "requestUrl",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "request",
"element": "url",
"transform": "copy",
"parameter": [
{
"valueString": "VisionPrescription"
}
]
}
]
}
]
},
{
"name": "status",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "vp",
"element": "status",
"transform": "copy",
"parameter": [
{
"valueString": "active"
}
]
}
],
"documentation": "`active`: a prescription recorded on this form is one somebody is about to\r\ndispense against. fm-status has no `completed`."
},
{
"name": "patient",
"source": [
{
"context": "qr",
"element": "subject",
"variable": "s",
"condition": "(reference.exists() and (reference.startsWith('Patient/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Patient')))"
}
],
"target": [
{
"context": "vp",
"element": "patient",
"transform": "copy",
"parameter": [
{
"valueId": "s"
}
]
}
]
},
{
"name": "encounter",
"source": [
{
"context": "qr",
"element": "encounter",
"variable": "e",
"condition": "(reference.exists() and (reference.startsWith('Encounter/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Encounter')))"
}
],
"target": [
{
"context": "vp",
"element": "encounter",
"transform": "copy",
"parameter": [
{
"valueId": "e"
}
]
}
]
},
{
"name": "dateWritten",
"source": [
{
"context": "qr",
"element": "authored",
"variable": "t"
}
],
"target": [
{
"context": "vp",
"element": "dateWritten",
"transform": "copy",
"parameter": [
{
"valueId": "t"
}
]
}
],
"documentation": "Both dates, 1..1 each, are the response's `authored`. The form does not ask\r\nfor a prescription date: the client stamps the moment it was recorded, and\r\nthat is when this prescription was written and when this record was made."
},
{
"name": "created",
"source": [
{
"context": "qr",
"element": "authored",
"variable": "t"
}
],
"target": [
{
"context": "vp",
"element": "created",
"transform": "copy",
"parameter": [
{
"valueId": "t"
}
]
}
]
}
]
},
{
"name": "SetPrescriber",
"documentation": "The prescriber is 1..1, and the form does not ask for one. It is the\r\npractitioner who recorded the prescription, which the client stamps on the\r\nresponse as `author`; `source` is taken when there is no author. Either is\r\ncopied across only when it points at a Practitioner or a PractitionerRole --\r\nthe two types VisionPrescription.prescriber allows -- because `author` may\r\nalso name a Device, a Patient or an Organization.\r\nNothing is guessed when neither qualifies: the rule that creates the\r\nprescription is guarded on one of them, so this group always finds one.",
"input": [
{
"name": "qr",
"type": "QR",
"mode": "source"
},
{
"name": "vp",
"type": "VisionPrescription",
"mode": "target"
}
],
"rule": [
{
"name": "prescriberFromAuthor",
"source": [
{
"context": "qr",
"element": "author",
"variable": "a",
"condition": "(reference.matches('(^|/)(Practitioner|PractitionerRole)/[^/]+$')) and ((reference.exists() and (reference.startsWith('Practitioner/') or reference.startsWith('PractitionerRole/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Practitioner') or ('/' + type).endsWith('/PractitionerRole'))))"
}
],
"target": [
{
"context": "vp",
"element": "prescriber",
"transform": "copy",
"parameter": [
{
"valueId": "a"
}
]
}
]
},
{
"name": "sourceFallback",
"source": [
{
"context": "qr",
"condition": "author.where(((reference.exists() and (reference.startsWith('Practitioner/') or reference.startsWith('PractitionerRole/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Practitioner') or ('/' + type).endsWith('/PractitionerRole'))))).exists().not()"
}
],
"rule": [
{
"name": "prescriberFromSource",
"source": [
{
"context": "qr",
"element": "source",
"variable": "s",
"condition": "(reference.matches('(^|/)(Practitioner|PractitionerRole)/[^/]+$')) and ((reference.exists() and (reference.startsWith('Practitioner/') or reference.startsWith('PractitionerRole/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Practitioner') or ('/' + type).endsWith('/PractitionerRole'))))"
}
],
"target": [
{
"context": "vp",
"element": "prescriber",
"transform": "copy",
"parameter": [
{
"valueId": "s"
}
]
}
]
}
]
}
]
},
{
"name": "SpectacleLenses",
"documentation": "── Spectacle lenses ─────────────────────────────────────────────────────────\r\nUp to two lens specifications per eye, one per row the prescriber filled in.\r\nThe distance specification carries the distance vision (DV) row and the\r\nreading addition. That is the shape R5 itself uses for a spectacle\r\nprescription -- one specification per eye with the distance powers and\r\n`add` -- so a consumer that reads only that one still gets a complete\r\nmultifocal prescription.\r\nThe near specification carries the near vision (NV) row, when one was\r\nwritten. It is not derived from DV + ADD and it is not dropped when an ADD is\r\npresent: the prescriber wrote it, and for reading glasses it is the whole\r\nprescription. The two cannot be told apart by any element R5 gives a lens\r\nspecification, so `product` carries HL7's `lens` beside the local\r\n`spectacle-lens-distance` or `spectacle-lens-near` -- the same product at a\r\nfiner grain, which the example-strength binding allows.\r\nlinkIds are matched by suffix inside an eye, so the two eyes share one set\r\nof groups. The eye code is set by the caller, which is the only place that\r\nknows which eye it is in.",
"input": [
{
"name": "qr",
"type": "QR",
"mode": "source"
},
{
"name": "ew",
"mode": "source"
},
{
"name": "vp",
"type": "VisionPrescription",
"mode": "target"
}
],
"rule": [
{
"name": "right",
"source": [
{
"context": "ew",
"element": "item",
"variable": "eye",
"condition": "linkId = 'eyewear/right'"
}
],
"rule": [
{
"name": "rightDistance",
"source": [
{
"context": "eye",
"condition": "item.where(linkId.endsWith('/distance')).item.where(linkId.endsWith('/sph') or linkId.endsWith('/cyl')).answer.value.ofType(decimal).exists() or item.where(linkId.endsWith('/distance')).item.where(linkId.endsWith('/axis')).answer.value.ofType(integer).exists() or item.where(linkId.endsWith('/add')).answer.value.ofType(decimal).exists()"
}
],
"target": [
{
"context": "vp",
"element": "lensSpecification",
"variable": "spec"
}
],
"rule": [
{
"name": "lens",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "spec"
}
],
"dependent": [
{
"name": "DistanceSpectacleLens",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "ew"
},
{
"valueId": "eye"
},
{
"valueId": "spec"
}
]
}
]
},
{
"name": "eyeCode",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "spec",
"element": "eye",
"transform": "copy",
"parameter": [
{
"valueString": "right"
}
]
}
]
}
]
},
{
"name": "rightNear",
"source": [
{
"context": "eye",
"condition": "item.where(linkId.endsWith('/near')).item.where(linkId.endsWith('/sph') or linkId.endsWith('/cyl')).answer.value.ofType(decimal).exists() or item.where(linkId.endsWith('/near')).item.where(linkId.endsWith('/axis')).answer.value.ofType(integer).exists()"
}
],
"target": [
{
"context": "vp",
"element": "lensSpecification",
"variable": "spec"
}
],
"rule": [
{
"name": "lens",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "spec"
}
],
"dependent": [
{
"name": "NearSpectacleLens",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "ew"
},
{
"valueId": "eye"
},
{
"valueId": "spec"
}
]
}
]
},
{
"name": "eyeCode",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "spec",
"element": "eye",
"transform": "copy",
"parameter": [
{
"valueString": "right"
}
]
}
]
}
]
}
]
},
{
"name": "left",
"source": [
{
"context": "ew",
"element": "item",
"variable": "eye",
"condition": "linkId = 'eyewear/left'"
}
],
"rule": [
{
"name": "leftDistance",
"source": [
{
"context": "eye",
"condition": "item.where(linkId.endsWith('/distance')).item.where(linkId.endsWith('/sph') or linkId.endsWith('/cyl')).answer.value.ofType(decimal).exists() or item.where(linkId.endsWith('/distance')).item.where(linkId.endsWith('/axis')).answer.value.ofType(integer).exists() or item.where(linkId.endsWith('/add')).answer.value.ofType(decimal).exists()"
}
],
"target": [
{
"context": "vp",
"element": "lensSpecification",
"variable": "spec"
}
],
"rule": [
{
"name": "lens",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "spec"
}
],
"dependent": [
{
"name": "DistanceSpectacleLens",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "ew"
},
{
"valueId": "eye"
},
{
"valueId": "spec"
}
]
}
]
},
{
"name": "eyeCode",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "spec",
"element": "eye",
"transform": "copy",
"parameter": [
{
"valueString": "left"
}
]
}
]
}
]
},
{
"name": "leftNear",
"source": [
{
"context": "eye",
"condition": "item.where(linkId.endsWith('/near')).item.where(linkId.endsWith('/sph') or linkId.endsWith('/cyl')).answer.value.ofType(decimal).exists() or item.where(linkId.endsWith('/near')).item.where(linkId.endsWith('/axis')).answer.value.ofType(integer).exists()"
}
],
"target": [
{
"context": "vp",
"element": "lensSpecification",
"variable": "spec"
}
],
"rule": [
{
"name": "lens",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "spec"
}
],
"dependent": [
{
"name": "NearSpectacleLens",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "ew"
},
{
"valueId": "eye"
},
{
"valueId": "spec"
}
]
}
]
},
{
"name": "eyeCode",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "spec",
"element": "eye",
"transform": "copy",
"parameter": [
{
"valueString": "left"
}
]
}
]
}
]
}
]
}
]
},
{
"name": "DistanceSpectacleLens",
"input": [
{
"name": "qr",
"type": "QR",
"mode": "source"
},
{
"name": "ew",
"mode": "source"
},
{
"name": "eye",
"mode": "source"
},
{
"name": "spec",
"mode": "target"
}
],
"rule": [
{
"name": "product",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "spec",
"element": "product",
"variable": "product"
}
],
"rule": [
{
"name": "hl7Coding",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "product",
"element": "coding",
"variable": "coding"
}
],
"rule": [
{
"name": "hl7System",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "coding",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://terminology.hl7.org/CodeSystem/ex-visionprescriptionproduct"
}
]
}
]
},
{
"name": "hl7Code",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "coding",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "lens"
}
]
}
]
},
{
"name": "hl7Display",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "coding",
"element": "display",
"transform": "copy",
"parameter": [
{
"valueString": "Lens"
}
]
}
]
}
]
},
{
"name": "rowCoding",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "product",
"element": "coding",
"variable": "coding"
}
],
"rule": [
{
"name": "rowSystem",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "coding",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "https://fhir.slade360.co.ke/fhir/CodeSystem/concept-codesystem"
}
]
}
]
},
{
"name": "rowCode",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "coding",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "spectacle-lens-distance"
}
]
}
]
},
{
"name": "rowDisplay",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "coding",
"element": "display",
"transform": "copy",
"parameter": [
{
"valueString": "Spectacle lens for distance vision"
}
]
}
]
}
]
},
{
"name": "productText",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "product",
"element": "text",
"transform": "copy",
"parameter": [
{
"valueString": "Spectacle lens — distance vision (DV)"
}
]
}
]
}
]
},
{
"name": "distanceRow",
"source": [
{
"context": "eye",
"element": "item",
"variable": "row",
"condition": "linkId.endsWith('/distance')"
}
],
"rule": [
{
"name": "powers",
"source": [
{
"context": "row"
}
],
"target": [
{
"context": "spec"
}
],
"dependent": [
{
"name": "LensPowers",
"parameter": [
{
"valueId": "row"
},
{
"valueId": "spec"
}
]
}
]
}
]
},
{
"name": "addition",
"source": [
{
"context": "eye",
"element": "item",
"variable": "it",
"condition": "linkId.endsWith('/add') and answer.value.ofType(decimal).exists()"
}
],
"rule": [
{
"name": "addAnswer",
"source": [
{
"context": "it",
"element": "answer",
"listMode": "first",
"variable": "a"
}
],
"rule": [
{
"name": "addValue",
"source": [
{
"context": "a",
"element": "value",
"variable": "v"
}
],
"target": [
{
"context": "spec",
"element": "add",
"transform": "copy",
"parameter": [
{
"valueId": "v"
}
]
}
]
}
]
}
]
},
{
"name": "notes",
"source": [
{
"context": "ew"
}
],
"target": [
{
"context": "spec"
}
],
"dependent": [
{
"name": "SpectacleLensNotes",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "ew"
},
{
"valueId": "spec"
}
]
}
]
}
]
},
{
"name": "NearSpectacleLens",
"input": [
{
"name": "qr",
"type": "QR",
"mode": "source"
},
{
"name": "ew",
"mode": "source"
},
{
"name": "eye",
"mode": "source"
},
{
"name": "spec",
"mode": "target"
}
],
"rule": [
{
"name": "product",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "spec",
"element": "product",
"variable": "product"
}
],
"rule": [
{
"name": "hl7Coding",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "product",
"element": "coding",
"variable": "coding"
}
],
"rule": [
{
"name": "hl7System",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "coding",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://terminology.hl7.org/CodeSystem/ex-visionprescriptionproduct"
}
]
}
]
},
{
"name": "hl7Code",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "coding",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "lens"
}
]
}
]
},
{
"name": "hl7Display",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "coding",
"element": "display",
"transform": "copy",
"parameter": [
{
"valueString": "Lens"
}
]
}
]
}
]
},
{
"name": "rowCoding",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "product",
"element": "coding",
"variable": "coding"
}
],
"rule": [
{
"name": "rowSystem",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "coding",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "https://fhir.slade360.co.ke/fhir/CodeSystem/concept-codesystem"
}
]
}
]
},
{
"name": "rowCode",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "coding",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "spectacle-lens-near"
}
]
}
]
},
{
"name": "rowDisplay",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "coding",
"element": "display",
"transform": "copy",
"parameter": [
{
"valueString": "Spectacle lens for near vision"
}
]
}
]
}
]
},
{
"name": "productText",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "product",
"element": "text",
"transform": "copy",
"parameter": [
{
"valueString": "Spectacle lens — near vision (NV)"
}
]
}
]
}
]
},
{
"name": "nearRow",
"source": [
{
"context": "eye",
"element": "item",
"variable": "row",
"condition": "linkId.endsWith('/near')"
}
],
"rule": [
{
"name": "powers",
"source": [
{
"context": "row"
}
],
"target": [
{
"context": "spec"
}
],
"dependent": [
{
"name": "LensPowers",
"parameter": [
{
"valueId": "row"
},
{
"valueId": "spec"
}
]
}
]
}
]
},
{
"name": "notes",
"source": [
{
"context": "ew"
}
],
"target": [
{
"context": "spec"
}
],
"dependent": [
{
"name": "SpectacleLensNotes",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "ew"
},
{
"valueId": "spec"
}
]
}
]
}
]
},
{
"name": "LensPowers",
"documentation": "Sphere, cylinder and axis off one row. Typed guards, because a rule that\r\nassigns an answer of the wrong type to a decimal element does not skip -- it\r\nfails the whole extraction.",
"input": [
{
"name": "row",
"mode": "source"
},
{
"name": "spec",
"mode": "target"
}
],
"rule": [
{
"name": "sphere",
"source": [
{
"context": "row",
"element": "item",
"variable": "it",
"condition": "linkId.endsWith('/sph') and answer.value.ofType(decimal).exists()"
}
],
"rule": [
{
"name": "sphereAnswer",
"source": [
{
"context": "it",
"element": "answer",
"listMode": "first",
"variable": "a"
}
],
"rule": [
{
"name": "sphereValue",
"source": [
{
"context": "a",
"element": "value",
"variable": "v"
}
],
"target": [
{
"context": "spec",
"element": "sphere",
"transform": "copy",
"parameter": [
{
"valueId": "v"
}
]
}
]
}
]
}
]
},
{
"name": "cylinder",
"source": [
{
"context": "row",
"element": "item",
"variable": "it",
"condition": "linkId.endsWith('/cyl') and answer.value.ofType(decimal).exists()"
}
],
"rule": [
{
"name": "cylinderAnswer",
"source": [
{
"context": "it",
"element": "answer",
"listMode": "first",
"variable": "a"
}
],
"rule": [
{
"name": "cylinderValue",
"source": [
{
"context": "a",
"element": "value",
"variable": "v"
}
],
"target": [
{
"context": "spec",
"element": "cylinder",
"transform": "copy",
"parameter": [
{
"valueId": "v"
}
]
}
]
}
]
}
]
},
{
"name": "axis",
"source": [
{
"context": "row",
"element": "item",
"variable": "it",
"condition": "linkId.endsWith('/axis') and answer.value.ofType(integer).exists()"
}
],
"rule": [
{
"name": "axisAnswer",
"source": [
{
"context": "it",
"element": "answer",
"listMode": "first",
"variable": "a"
}
],
"rule": [
{
"name": "axisValue",
"source": [
{
"context": "a",
"element": "value",
"variable": "v"
}
],
"target": [
{
"context": "spec",
"element": "axis",
"transform": "copy",
"parameter": [
{
"valueId": "v"
}
]
}
]
}
]
}
]
}
]
},
{
"name": "SpectacleLensNotes",
"documentation": "Lens type and coating, as the words the prescriber ticked. VisionPrescription\r\nhas no coded element for either; `note` is where R5 says they go (\"notes for\r\nspecial requirements such as coatings and lens materials\"), and the codes\r\nthemselves stay on the response. Both go on every spectacle specification,\r\nbecause the paper ticks them once for the pair.",
"input": [
{
"name": "qr",
"type": "QR",
"mode": "source"
},
{
"name": "ew",
"mode": "source"
},
{
"name": "spec",
"mode": "target"
}
],
"rule": [
{
"name": "lensTypes",
"source": [
{
"context": "ew",
"element": "item",
"variable": "it",
"condition": "(linkId = 'eyewear/lens-types') and answer.value.ofType(Coding).exists()"
}
],
"target": [
{
"context": "spec",
"element": "note",
"variable": "note"
}
],
"rule": [
{
"name": "lensTypeText",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "note",
"element": "text",
"transform": "evaluate",
"parameter": [
{
"valueId": "it"
},
{
"valueString": "'Lens type: ' & answer.value.ofType(Coding).select(iif(display.exists(), display, code)).join(', ')"
}
]
}
]
}
]
},
{
"name": "lensCoating",
"source": [
{
"context": "ew",
"element": "item",
"variable": "it",
"condition": "(linkId = 'eyewear/lens-coating') and answer.value.ofType(Coding).exists()"
}
],
"target": [
{
"context": "spec",
"element": "note",
"variable": "note"
}
],
"rule": [
{
"name": "lensCoatingText",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "note",
"element": "text",
"transform": "evaluate",
"parameter": [
{
"valueId": "it"
},
{
"valueString": "'Lens coating: ' & answer.value.ofType(Coding).select(iif(display.exists(), display, code)).join(', ')"
}
]
}
]
}
]
},
{
"name": "prescriptionNotes",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "spec"
}
],
"dependent": [
{
"name": "PrescriptionNotes",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "spec"
}
]
}
]
}
]
},
{
"name": "PrescriptionNotes",
"documentation": "The free-text prescription notes. VisionPrescription has no note of its own,\r\nonly one per lens specification, so the notes go on each specification --\r\nrepeated rather than put on whichever happens to be first, which a consumer\r\nreading one eye would miss.",
"input": [
{
"name": "qr",
"type": "QR",
"mode": "source"
},
{
"name": "spec",
"mode": "target"
}
],
"rule": [
{
"name": "notes",
"source": [
{
"context": "qr",
"element": "item",
"variable": "it",
"condition": "(linkId = 'notes') and answer.value.ofType(string).exists()"
}
],
"target": [
{
"context": "spec",
"element": "note",
"variable": "note"
}
],
"rule": [
{
"name": "noteAnswer",
"source": [
{
"context": "it",
"element": "answer",
"listMode": "first",
"variable": "a"
}
],
"rule": [
{
"name": "noteText",
"source": [
{
"context": "a",
"element": "value",
"variable": "v"
}
],
"target": [
{
"context": "note",
"element": "text",
"transform": "copy",
"parameter": [
{
"valueId": "v"
}
]
}
]
}
]
}
]
}
]
},
{
"name": "ContactLenses",
"documentation": "── Contact lenses ───────────────────────────────────────────────────────────\r\nOne specification per eye with anything written on it. The contact lens\r\npower goes to `power`, R5's \"contact lens power\", which is what its own\r\ncontact lens example uses; `sphere` stays the spectacle lens power. Brand,\r\ncolour and the replacement schedule are asked once for the pair and copied\r\nonto both eyes, because R5 holds them per specification.",
"input": [
{
"name": "qr",
"type": "QR",
"mode": "source"
},
{
"name": "cl",
"mode": "source"
},
{
"name": "vp",
"type": "VisionPrescription",
"mode": "target"
}
],
"rule": [
{
"name": "right",
"source": [
{
"context": "cl",
"element": "item",
"variable": "eye",
"condition": "(linkId = 'contact-lens/right') and (item.answer.value.ofType(decimal).exists() or item.answer.value.ofType(integer).exists())"
}
],
"target": [
{
"context": "vp",
"element": "lensSpecification",
"variable": "spec"
}
],
"rule": [
{
"name": "lens",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "spec"
}
],
"dependent": [
{
"name": "ContactLens",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "cl"
},
{
"valueId": "eye"
},
{
"valueId": "spec"
}
]
}
]
},
{
"name": "eyeCode",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "spec",
"element": "eye",
"transform": "copy",
"parameter": [
{
"valueString": "right"
}
]
}
]
}
]
},
{
"name": "left",
"source": [
{
"context": "cl",
"element": "item",
"variable": "eye",
"condition": "(linkId = 'contact-lens/left') and (item.answer.value.ofType(decimal).exists() or item.answer.value.ofType(integer).exists())"
}
],
"target": [
{
"context": "vp",
"element": "lensSpecification",
"variable": "spec"
}
],
"rule": [
{
"name": "lens",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "spec"
}
],
"dependent": [
{
"name": "ContactLens",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "cl"
},
{
"valueId": "eye"
},
{
"valueId": "spec"
}
]
}
]
},
{
"name": "eyeCode",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "spec",
"element": "eye",
"transform": "copy",
"parameter": [
{
"valueString": "left"
}
]
}
]
}
]
}
]
},
{
"name": "ContactLens",
"input": [
{
"name": "qr",
"type": "QR",
"mode": "source"
},
{
"name": "cl",
"mode": "source"
},
{
"name": "eye",
"mode": "source"
},
{
"name": "spec",
"mode": "target"
}
],
"rule": [
{
"name": "product",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "spec",
"element": "product",
"variable": "product"
}
],
"rule": [
{
"name": "hl7Coding",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "product",
"element": "coding",
"variable": "coding"
}
],
"rule": [
{
"name": "hl7System",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "coding",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://terminology.hl7.org/CodeSystem/ex-visionprescriptionproduct"
}
]
}
]
},
{
"name": "hl7Code",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "coding",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "contact"
}
]
}
]
},
{
"name": "hl7Display",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "coding",
"element": "display",
"transform": "copy",
"parameter": [
{
"valueString": "Contact Lens"
}
]
}
]
}
]
},
{
"name": "productText",
"source": [
{
"context": "eye"
}
],
"target": [
{
"context": "product",
"element": "text",
"transform": "copy",
"parameter": [
{
"valueString": "Contact lens"
}
]
}
]
}
]
},
{
"name": "power",
"source": [
{
"context": "eye",
"element": "item",
"variable": "it",
"condition": "linkId.endsWith('/power') and answer.value.ofType(decimal).exists()"
}
],
"rule": [
{
"name": "powerAnswer",
"source": [
{
"context": "it",
"element": "answer",
"listMode": "first",
"variable": "a"
}
],
"rule": [
{
"name": "powerValue",
"source": [
{
"context": "a",
"element": "value",
"variable": "v"
}
],
"target": [
{
"context": "spec",
"element": "power",
"transform": "copy",
"parameter": [
{
"valueId": "v"
}
]
}
]
}
]
}
]
},
{
"name": "cylinder",
"source": [
{
"context": "eye",
"element": "item",
"variable": "it",
"condition": "linkId.endsWith('/cyl') and answer.value.ofType(decimal).exists()"
}
],
"rule": [
{
"name": "cylinderAnswer",
"source": [
{
"context": "it",
"element": "answer",
"listMode": "first",
"variable": "a"
}
],
"rule": [
{
"name": "cylinderValue",
"source": [
{
"context": "a",
"element": "value",
"variable": "v"
}
],
"target": [
{
"context": "spec",
"element": "cylinder",
"transform": "copy",
"parameter": [
{
"valueId": "v"
}
]
}
]
}
]
}
]
},
{
"name": "axis",
"source": [
{
"context": "eye",
"element": "item",
"variable": "it",
"condition": "linkId.endsWith('/axis') and answer.value.ofType(integer).exists()"
}
],
"rule": [
{
"name": "axisAnswer",
"source": [
{
"context": "it",
"element": "answer",
"listMode": "first",
"variable": "a"
}
],
"rule": [
{
"name": "axisValue",
"source": [
{
"context": "a",
"element": "value",
"variable": "v"
}
],
"target": [
{
"context": "spec",
"element": "axis",
"transform": "copy",
"parameter": [
{
"valueId": "v"
}
]
}
]
}
]
}
]
},
{
"name": "addition",
"source": [
{
"context": "eye",
"element": "item",
"variable": "it",
"condition": "linkId.endsWith('/add') and answer.value.ofType(decimal).exists()"
}
],
"rule": [
{
"name": "addAnswer",
"source": [
{
"context": "it",
"element": "answer",
"listMode": "first",
"variable": "a"
}
],
"rule": [
{
"name": "addValue",
"source": [
{
"context": "a",
"element": "value",
"variable": "v"
}
],
"target": [
{
"context": "spec",
"element": "add",
"transform": "copy",
"parameter": [
{
"valueId": "v"
}
]
}
]
}
]
}
]
},
{
"name": "backCurve",
"source": [
{
"context": "eye",
"element": "item",
"variable": "it",
"condition": "linkId.endsWith('/base-curve') and answer.value.ofType(decimal).exists()"
}
],
"rule": [
{
"name": "backCurveAnswer",
"source": [
{
"context": "it",
"element": "answer",
"listMode": "first",
"variable": "a"
}
],
"rule": [
{
"name": "backCurveValue",
"source": [
{
"context": "a",
"element": "value",
"variable": "v"
}
],
"target": [
{
"context": "spec",
"element": "backCurve",
"transform": "copy",
"parameter": [
{
"valueId": "v"
}
]
}
]
}
]
}
]
},
{
"name": "diameter",
"source": [
{
"context": "eye",
"element": "item",
"variable": "it",
"condition": "linkId.endsWith('/diameter') and answer.value.ofType(decimal).exists()"
}
],
"rule": [
{
"name": "diameterAnswer",
"source": [
{
"context": "it",
"element": "answer",
"listMode": "first",
"variable": "a"
}
],
"rule": [
{
"name": "diameterValue",
"source": [
{
"context": "a",
"element": "value",
"variable": "v"
}
],
"target": [
{
"context": "spec",
"element": "diameter",
"transform": "copy",
"parameter": [
{
"valueId": "v"
}
]
}
]
}
]
}
]
},
{
"name": "brand",
"source": [
{
"context": "cl",
"element": "item",
"variable": "it",
"condition": "(linkId = 'contact-lens/brand') and answer.value.ofType(string).exists()"
}
],
"rule": [
{
"name": "brandAnswer",
"source": [
{
"context": "it",
"element": "answer",
"listMode": "first",
"variable": "a"
}
],
"rule": [
{
"name": "brandValue",
"source": [
{
"context": "a",
"element": "value",
"variable": "v"
}
],
"target": [
{
"context": "spec",
"element": "brand",
"transform": "copy",
"parameter": [
{
"valueId": "v"
}
]
}
]
}
]
}
]
},
{
"name": "colour",
"source": [
{
"context": "cl",
"element": "item",
"variable": "it",
"condition": "(linkId = 'contact-lens/colour') and answer.value.ofType(string).exists()"
}
],
"rule": [
{
"name": "colourAnswer",
"source": [
{
"context": "it",
"element": "answer",
"listMode": "first",
"variable": "a"
}
],
"rule": [
{
"name": "colourValue",
"source": [
{
"context": "a",
"element": "value",
"variable": "v"
}
],
"target": [
{
"context": "spec",
"element": "color",
"transform": "copy",
"parameter": [
{
"valueId": "v"
}
]
}
]
}
]
}
]
},
{
"name": "daily",
"source": [
{
"context": "cl",
"element": "item",
"variable": "it",
"condition": "(linkId = 'contact-lens/replacement') and (answer.value.ofType(Coding).code = 'contact-lens-replace-daily')"
}
],
"target": [
{
"context": "spec",
"element": "duration",
"variable": "d"
}
],
"rule": [
{
"name": "value",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "value",
"transform": "copy",
"parameter": [
{
"valueInteger": 1
}
]
}
]
},
{
"name": "unit",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "unit",
"transform": "copy",
"parameter": [
{
"valueString": "day"
}
]
}
]
},
{
"name": "system",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://unitsofmeasure.org"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "d"
}
]
}
]
}
],
"documentation": "The replacement schedule is R5's `duration`, \"the recommended maximum wear\r\nperiod for the lens\", as a UCUM Quantity. One rule per answer, each guarded\r\non its own code, so an answer this map does not know writes no duration\r\nrather than an empty Quantity."
},
{
"name": "twoWeekly",
"source": [
{
"context": "cl",
"element": "item",
"variable": "it",
"condition": "(linkId = 'contact-lens/replacement') and (answer.value.ofType(Coding).code = 'contact-lens-replace-two-weekly')"
}
],
"target": [
{
"context": "spec",
"element": "duration",
"variable": "d"
}
],
"rule": [
{
"name": "value",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "value",
"transform": "copy",
"parameter": [
{
"valueInteger": 2
}
]
}
]
},
{
"name": "unit",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "unit",
"transform": "copy",
"parameter": [
{
"valueString": "weeks"
}
]
}
]
},
{
"name": "system",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://unitsofmeasure.org"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "wk"
}
]
}
]
}
]
},
{
"name": "monthly",
"source": [
{
"context": "cl",
"element": "item",
"variable": "it",
"condition": "(linkId = 'contact-lens/replacement') and (answer.value.ofType(Coding).code = 'contact-lens-replace-monthly')"
}
],
"target": [
{
"context": "spec",
"element": "duration",
"variable": "d"
}
],
"rule": [
{
"name": "value",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "value",
"transform": "copy",
"parameter": [
{
"valueInteger": 1
}
]
}
]
},
{
"name": "unit",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "unit",
"transform": "copy",
"parameter": [
{
"valueString": "month"
}
]
}
]
},
{
"name": "system",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://unitsofmeasure.org"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "mo"
}
]
}
]
}
]
},
{
"name": "quarterly",
"source": [
{
"context": "cl",
"element": "item",
"variable": "it",
"condition": "(linkId = 'contact-lens/replacement') and (answer.value.ofType(Coding).code = 'contact-lens-replace-quarterly')"
}
],
"target": [
{
"context": "spec",
"element": "duration",
"variable": "d"
}
],
"rule": [
{
"name": "value",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "value",
"transform": "copy",
"parameter": [
{
"valueInteger": 3
}
]
}
]
},
{
"name": "unit",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "unit",
"transform": "copy",
"parameter": [
{
"valueString": "months"
}
]
}
]
},
{
"name": "system",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://unitsofmeasure.org"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "mo"
}
]
}
]
}
]
},
{
"name": "yearly",
"source": [
{
"context": "cl",
"element": "item",
"variable": "it",
"condition": "(linkId = 'contact-lens/replacement') and (answer.value.ofType(Coding).code = 'contact-lens-replace-yearly')"
}
],
"target": [
{
"context": "spec",
"element": "duration",
"variable": "d"
}
],
"rule": [
{
"name": "value",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "value",
"transform": "copy",
"parameter": [
{
"valueInteger": 1
}
]
}
]
},
{
"name": "unit",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "unit",
"transform": "copy",
"parameter": [
{
"valueString": "year"
}
]
}
]
},
{
"name": "system",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://unitsofmeasure.org"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "d",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "a"
}
]
}
]
}
]
},
{
"name": "prescriptionNotes",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "spec"
}
],
"dependent": [
{
"name": "PrescriptionNotes",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "spec"
}
]
}
]
}
]
},
{
"name": "EyewearMeasurements",
"documentation": "─────────────────────────────────────────────────────────────────────────────\r\nPupillary distance and visual acuity\r\nNeither has a home in VisionPrescription, and LOINC names both exactly, per\r\neye, on a glasses lens prescription -- so each is its own Observation under\r\nits own code rather than prose in a note. The codes were looked up, not\r\nassumed; each display below is LOINC's own:\r\ndistance (DV) near (NV)\r\nR-PD 28840-7 28841-5 mm\r\nL-PD 28857-1 55985-6 mm\r\nR-VA 28831-6 28839-9 as written\r\nL-VA 28847-2 28856-3 as written\r\nThe PD without a qualifier is LOINC's distance PD; near is the one it\r\nqualifies. Visual acuity is kept as the string the prescriber wrote -- 6/6,\r\n6/9, N6, CF -- because the notations mix Snellen fractions, N-point print\r\nsizes and counting fingers, and forcing them into a Ratio would lose the ones\r\nthat are not fractions.\r\nNothing on VisionPrescription can point at these. They are tied to it by the\r\nencounter, by `derivedFrom` naming the same response, and by the same\r\nprovenance tag.\r\n─────────────────────────────────────────────────────────────────────────────",
"input": [
{
"name": "qr",
"type": "QR",
"mode": "source"
},
{
"name": "ew",
"mode": "source"
},
{
"name": "bundle",
"type": "Bundle",
"mode": "target"
}
],
"rule": [
{
"name": "right",
"source": [
{
"context": "ew",
"element": "item",
"variable": "eye",
"condition": "linkId = 'eyewear/right'"
}
],
"rule": [
{
"name": "rightDistance",
"source": [
{
"context": "eye",
"element": "item",
"variable": "row",
"condition": "linkId = 'eyewear/right/distance'"
}
],
"rule": [
{
"name": "rightDistancePd",
"source": [
{
"context": "row",
"element": "item",
"variable": "it",
"condition": "(linkId = 'eyewear/right/distance/pd') and answer.value.ofType(decimal).exists()"
}
],
"target": [
{
"context": "bundle",
"element": "entry",
"variable": "entry"
},
{
"context": "entry",
"element": "resource",
"variable": "obs",
"transform": "create",
"parameter": [
{
"valueString": "Observation"
}
]
}
],
"rule": [
{
"name": "base",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "obs"
},
{
"context": "entry"
}
],
"dependent": [
{
"name": "BuildObservationBase",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "obs"
},
{
"valueId": "entry"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "obs",
"element": "code",
"variable": "code"
}
],
"rule": [
{
"name": "coding",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "code",
"element": "coding",
"variable": "coding"
}
],
"rule": [
{
"name": "system",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://loinc.org"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "28840-7"
}
]
}
]
},
{
"name": "display",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "display",
"transform": "copy",
"parameter": [
{
"valueString": "Right glasses lens prescription Pupillary distance.monocular"
}
]
}
]
}
]
},
{
"name": "text",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "code",
"element": "text",
"transform": "copy",
"parameter": [
{
"valueString": "Right eye pupillary distance, distance vision (R-PD, DV)"
}
]
}
]
}
]
},
{
"name": "value",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "obs"
}
],
"dependent": [
{
"name": "SetMillimetres",
"parameter": [
{
"valueId": "it"
},
{
"valueId": "obs"
}
]
}
]
}
]
},
{
"name": "rightDistanceVa",
"source": [
{
"context": "row",
"element": "item",
"variable": "it",
"condition": "(linkId = 'eyewear/right/distance/va') and answer.value.ofType(string).exists()"
}
],
"target": [
{
"context": "bundle",
"element": "entry",
"variable": "entry"
},
{
"context": "entry",
"element": "resource",
"variable": "obs",
"transform": "create",
"parameter": [
{
"valueString": "Observation"
}
]
}
],
"rule": [
{
"name": "base",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "obs"
},
{
"context": "entry"
}
],
"dependent": [
{
"name": "BuildObservationBase",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "obs"
},
{
"valueId": "entry"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "obs",
"element": "code",
"variable": "code"
}
],
"rule": [
{
"name": "coding",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "code",
"element": "coding",
"variable": "coding"
}
],
"rule": [
{
"name": "system",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://loinc.org"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "28831-6"
}
]
}
]
},
{
"name": "display",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "display",
"transform": "copy",
"parameter": [
{
"valueString": "Right glasses lens prescription Visual acuity far"
}
]
}
]
}
]
},
{
"name": "text",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "code",
"element": "text",
"transform": "copy",
"parameter": [
{
"valueString": "Right eye visual acuity, distance vision (R-VA, DV)"
}
]
}
]
}
]
},
{
"name": "value",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "obs"
}
],
"dependent": [
{
"name": "SetAcuity",
"parameter": [
{
"valueId": "it"
},
{
"valueId": "obs"
}
]
}
]
}
]
}
]
},
{
"name": "rightNear",
"source": [
{
"context": "eye",
"element": "item",
"variable": "row",
"condition": "linkId = 'eyewear/right/near'"
}
],
"rule": [
{
"name": "rightNearPd",
"source": [
{
"context": "row",
"element": "item",
"variable": "it",
"condition": "(linkId = 'eyewear/right/near/pd') and answer.value.ofType(decimal).exists()"
}
],
"target": [
{
"context": "bundle",
"element": "entry",
"variable": "entry"
},
{
"context": "entry",
"element": "resource",
"variable": "obs",
"transform": "create",
"parameter": [
{
"valueString": "Observation"
}
]
}
],
"rule": [
{
"name": "base",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "obs"
},
{
"context": "entry"
}
],
"dependent": [
{
"name": "BuildObservationBase",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "obs"
},
{
"valueId": "entry"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "obs",
"element": "code",
"variable": "code"
}
],
"rule": [
{
"name": "coding",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "code",
"element": "coding",
"variable": "coding"
}
],
"rule": [
{
"name": "system",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://loinc.org"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "28841-5"
}
]
}
]
},
{
"name": "display",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "display",
"transform": "copy",
"parameter": [
{
"valueString": "Right glasses lens prescription Pupillary distance.monocular near"
}
]
}
]
}
]
},
{
"name": "text",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "code",
"element": "text",
"transform": "copy",
"parameter": [
{
"valueString": "Right eye pupillary distance, near vision (R-PD, NV)"
}
]
}
]
}
]
},
{
"name": "value",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "obs"
}
],
"dependent": [
{
"name": "SetMillimetres",
"parameter": [
{
"valueId": "it"
},
{
"valueId": "obs"
}
]
}
]
}
]
},
{
"name": "rightNearVa",
"source": [
{
"context": "row",
"element": "item",
"variable": "it",
"condition": "(linkId = 'eyewear/right/near/va') and answer.value.ofType(string).exists()"
}
],
"target": [
{
"context": "bundle",
"element": "entry",
"variable": "entry"
},
{
"context": "entry",
"element": "resource",
"variable": "obs",
"transform": "create",
"parameter": [
{
"valueString": "Observation"
}
]
}
],
"rule": [
{
"name": "base",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "obs"
},
{
"context": "entry"
}
],
"dependent": [
{
"name": "BuildObservationBase",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "obs"
},
{
"valueId": "entry"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "obs",
"element": "code",
"variable": "code"
}
],
"rule": [
{
"name": "coding",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "code",
"element": "coding",
"variable": "coding"
}
],
"rule": [
{
"name": "system",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://loinc.org"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "28839-9"
}
]
}
]
},
{
"name": "display",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "display",
"transform": "copy",
"parameter": [
{
"valueString": "Right glasses lens prescription Visual acuity near"
}
]
}
]
}
]
},
{
"name": "text",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "code",
"element": "text",
"transform": "copy",
"parameter": [
{
"valueString": "Right eye visual acuity, near vision (R-VA, NV)"
}
]
}
]
}
]
},
{
"name": "value",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "obs"
}
],
"dependent": [
{
"name": "SetAcuity",
"parameter": [
{
"valueId": "it"
},
{
"valueId": "obs"
}
]
}
]
}
]
}
]
}
]
},
{
"name": "left",
"source": [
{
"context": "ew",
"element": "item",
"variable": "eye",
"condition": "linkId = 'eyewear/left'"
}
],
"rule": [
{
"name": "leftDistance",
"source": [
{
"context": "eye",
"element": "item",
"variable": "row",
"condition": "linkId = 'eyewear/left/distance'"
}
],
"rule": [
{
"name": "leftDistancePd",
"source": [
{
"context": "row",
"element": "item",
"variable": "it",
"condition": "(linkId = 'eyewear/left/distance/pd') and answer.value.ofType(decimal).exists()"
}
],
"target": [
{
"context": "bundle",
"element": "entry",
"variable": "entry"
},
{
"context": "entry",
"element": "resource",
"variable": "obs",
"transform": "create",
"parameter": [
{
"valueString": "Observation"
}
]
}
],
"rule": [
{
"name": "base",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "obs"
},
{
"context": "entry"
}
],
"dependent": [
{
"name": "BuildObservationBase",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "obs"
},
{
"valueId": "entry"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "obs",
"element": "code",
"variable": "code"
}
],
"rule": [
{
"name": "coding",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "code",
"element": "coding",
"variable": "coding"
}
],
"rule": [
{
"name": "system",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://loinc.org"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "28857-1"
}
]
}
]
},
{
"name": "display",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "display",
"transform": "copy",
"parameter": [
{
"valueString": "Left glasses lens prescription Pupillary distance.monocular"
}
]
}
]
}
]
},
{
"name": "text",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "code",
"element": "text",
"transform": "copy",
"parameter": [
{
"valueString": "Left eye pupillary distance, distance vision (L-PD, DV)"
}
]
}
]
}
]
},
{
"name": "value",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "obs"
}
],
"dependent": [
{
"name": "SetMillimetres",
"parameter": [
{
"valueId": "it"
},
{
"valueId": "obs"
}
]
}
]
}
]
},
{
"name": "leftDistanceVa",
"source": [
{
"context": "row",
"element": "item",
"variable": "it",
"condition": "(linkId = 'eyewear/left/distance/va') and answer.value.ofType(string).exists()"
}
],
"target": [
{
"context": "bundle",
"element": "entry",
"variable": "entry"
},
{
"context": "entry",
"element": "resource",
"variable": "obs",
"transform": "create",
"parameter": [
{
"valueString": "Observation"
}
]
}
],
"rule": [
{
"name": "base",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "obs"
},
{
"context": "entry"
}
],
"dependent": [
{
"name": "BuildObservationBase",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "obs"
},
{
"valueId": "entry"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "obs",
"element": "code",
"variable": "code"
}
],
"rule": [
{
"name": "coding",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "code",
"element": "coding",
"variable": "coding"
}
],
"rule": [
{
"name": "system",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://loinc.org"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "28847-2"
}
]
}
]
},
{
"name": "display",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "display",
"transform": "copy",
"parameter": [
{
"valueString": "Left glasses lens prescription Visual acuity far"
}
]
}
]
}
]
},
{
"name": "text",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "code",
"element": "text",
"transform": "copy",
"parameter": [
{
"valueString": "Left eye visual acuity, distance vision (L-VA, DV)"
}
]
}
]
}
]
},
{
"name": "value",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "obs"
}
],
"dependent": [
{
"name": "SetAcuity",
"parameter": [
{
"valueId": "it"
},
{
"valueId": "obs"
}
]
}
]
}
]
}
]
},
{
"name": "leftNear",
"source": [
{
"context": "eye",
"element": "item",
"variable": "row",
"condition": "linkId = 'eyewear/left/near'"
}
],
"rule": [
{
"name": "leftNearPd",
"source": [
{
"context": "row",
"element": "item",
"variable": "it",
"condition": "(linkId = 'eyewear/left/near/pd') and answer.value.ofType(decimal).exists()"
}
],
"target": [
{
"context": "bundle",
"element": "entry",
"variable": "entry"
},
{
"context": "entry",
"element": "resource",
"variable": "obs",
"transform": "create",
"parameter": [
{
"valueString": "Observation"
}
]
}
],
"rule": [
{
"name": "base",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "obs"
},
{
"context": "entry"
}
],
"dependent": [
{
"name": "BuildObservationBase",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "obs"
},
{
"valueId": "entry"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "obs",
"element": "code",
"variable": "code"
}
],
"rule": [
{
"name": "coding",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "code",
"element": "coding",
"variable": "coding"
}
],
"rule": [
{
"name": "system",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://loinc.org"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "55985-6"
}
]
}
]
},
{
"name": "display",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "display",
"transform": "copy",
"parameter": [
{
"valueString": "Left glasses lens prescription Pupillary distance.monocular near"
}
]
}
]
}
]
},
{
"name": "text",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "code",
"element": "text",
"transform": "copy",
"parameter": [
{
"valueString": "Left eye pupillary distance, near vision (L-PD, NV)"
}
]
}
]
}
]
},
{
"name": "value",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "obs"
}
],
"dependent": [
{
"name": "SetMillimetres",
"parameter": [
{
"valueId": "it"
},
{
"valueId": "obs"
}
]
}
]
}
]
},
{
"name": "leftNearVa",
"source": [
{
"context": "row",
"element": "item",
"variable": "it",
"condition": "(linkId = 'eyewear/left/near/va') and answer.value.ofType(string).exists()"
}
],
"target": [
{
"context": "bundle",
"element": "entry",
"variable": "entry"
},
{
"context": "entry",
"element": "resource",
"variable": "obs",
"transform": "create",
"parameter": [
{
"valueString": "Observation"
}
]
}
],
"rule": [
{
"name": "base",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "obs"
},
{
"context": "entry"
}
],
"dependent": [
{
"name": "BuildObservationBase",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "obs"
},
{
"valueId": "entry"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "obs",
"element": "code",
"variable": "code"
}
],
"rule": [
{
"name": "coding",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "code",
"element": "coding",
"variable": "coding"
}
],
"rule": [
{
"name": "system",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://loinc.org"
}
]
}
]
},
{
"name": "code",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "28856-3"
}
]
}
]
},
{
"name": "display",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "coding",
"element": "display",
"transform": "copy",
"parameter": [
{
"valueString": "Left glasses lens prescription Visual acuity near"
}
]
}
]
}
]
},
{
"name": "text",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "code",
"element": "text",
"transform": "copy",
"parameter": [
{
"valueString": "Left eye visual acuity, near vision (L-VA, NV)"
}
]
}
]
}
]
},
{
"name": "value",
"source": [
{
"context": "it"
}
],
"target": [
{
"context": "obs"
}
],
"dependent": [
{
"name": "SetAcuity",
"parameter": [
{
"valueId": "it"
},
{
"valueId": "obs"
}
]
}
]
}
]
}
]
}
]
}
]
},
{
"name": "SetMillimetres",
"documentation": "`Observation.value[x]` has no decimal choice, so the PD is built as a UCUM\r\nQuantity. The unit is fixed by the form.",
"input": [
{
"name": "it",
"mode": "source"
},
{
"name": "obs",
"type": "Observation",
"mode": "target"
}
],
"rule": [
{
"name": "answer",
"source": [
{
"context": "it",
"element": "answer",
"listMode": "first",
"variable": "a"
}
],
"rule": [
{
"name": "quantity",
"source": [
{
"context": "a",
"element": "value",
"variable": "v"
}
],
"target": [
{
"context": "obs",
"element": "value",
"variable": "q",
"transform": "create",
"parameter": [
{
"valueString": "Quantity"
}
]
}
],
"rule": [
{
"name": "quantityValue",
"source": [
{
"context": "v"
}
],
"target": [
{
"context": "q",
"element": "value",
"transform": "copy",
"parameter": [
{
"valueId": "v"
}
]
}
]
},
{
"name": "quantityUnit",
"source": [
{
"context": "v"
}
],
"target": [
{
"context": "q",
"element": "unit",
"transform": "copy",
"parameter": [
{
"valueString": "mm"
}
]
}
]
},
{
"name": "quantitySystem",
"source": [
{
"context": "v"
}
],
"target": [
{
"context": "q",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://unitsofmeasure.org"
}
]
}
]
},
{
"name": "quantityCode",
"source": [
{
"context": "v"
}
],
"target": [
{
"context": "q",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "mm"
}
]
}
]
}
]
}
]
}
]
},
{
"name": "SetAcuity",
"input": [
{
"name": "it",
"mode": "source"
},
{
"name": "obs",
"type": "Observation",
"mode": "target"
}
],
"rule": [
{
"name": "answer",
"source": [
{
"context": "it",
"element": "answer",
"listMode": "first",
"variable": "a"
}
],
"rule": [
{
"name": "acuityValue",
"source": [
{
"context": "a",
"element": "value",
"variable": "v"
}
],
"target": [
{
"context": "obs",
"element": "value",
"transform": "copy",
"parameter": [
{
"valueId": "v"
}
]
}
]
}
]
}
]
},
{
"name": "BuildObservationBase",
"documentation": "─────────────────────────────────────────────────────────────────────────────\r\nShared scaffolding. Repeated in each map rather than imported: the transform\r\nengine resolves `imports` against whatever StructureMaps happen to be on the\r\nserver, and a form that stops extracting because a shared map was not\r\nuploaded is a worse failure than a duplicated group.\r\n─────────────────────────────────────────────────────────────────────────────",
"input": [
{
"name": "qr",
"type": "QR",
"mode": "source"
},
{
"name": "obs",
"type": "Observation",
"mode": "target"
},
{
"name": "entry",
"mode": "target"
}
],
"rule": [
{
"name": "responseProvenance",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "obs"
}
],
"dependent": [
{
"name": "SetResponseProvenance",
"parameter": [
{
"valueId": "qr"
},
{
"valueId": "obs"
}
]
}
]
},
{
"name": "idAndFullUrl",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "obs",
"element": "id",
"transform": "uuid"
}
],
"dependent": [
{
"name": "SetObservationFullUrl",
"parameter": [
{
"valueId": "obs"
},
{
"valueId": "entry"
}
]
}
]
},
{
"name": "entryRequest",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "entry",
"element": "request",
"variable": "request"
}
],
"rule": [
{
"name": "requestMethod",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "request",
"element": "method",
"transform": "copy",
"parameter": [
{
"valueString": "POST"
}
]
}
]
},
{
"name": "requestUrl",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "request",
"element": "url",
"transform": "copy",
"parameter": [
{
"valueString": "Observation"
}
]
}
]
}
]
},
{
"name": "status",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "obs",
"element": "status",
"transform": "copy",
"parameter": [
{
"valueString": "final"
}
]
}
]
},
{
"name": "category",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "obs",
"element": "category",
"variable": "cat"
}
],
"rule": [
{
"name": "categoryCoding",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "cat",
"element": "coding",
"variable": "coding"
}
],
"rule": [
{
"name": "categorySystem",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "coding",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://terminology.hl7.org/CodeSystem/observation-category"
}
]
}
]
},
{
"name": "categoryCode",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "coding",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "exam"
}
]
}
]
},
{
"name": "categoryDisplay",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "coding",
"element": "display",
"transform": "copy",
"parameter": [
{
"valueString": "Exam"
}
]
}
]
}
]
}
]
},
{
"name": "subject",
"source": [
{
"context": "qr",
"element": "subject",
"variable": "s",
"condition": "(reference.exists() and (reference.startsWith('Patient/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Patient')))"
}
],
"target": [
{
"context": "obs",
"element": "subject",
"transform": "copy",
"parameter": [
{
"valueId": "s"
}
]
}
]
},
{
"name": "encounter",
"source": [
{
"context": "qr",
"element": "encounter",
"variable": "e",
"condition": "(reference.exists() and (reference.startsWith('Encounter/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Encounter')))"
}
],
"target": [
{
"context": "obs",
"element": "encounter",
"transform": "copy",
"parameter": [
{
"valueId": "e"
}
]
}
]
},
{
"name": "effective",
"source": [
{
"context": "qr",
"element": "authored",
"variable": "t"
}
],
"target": [
{
"context": "obs",
"element": "effective",
"transform": "copy",
"parameter": [
{
"valueId": "t"
}
]
}
],
"documentation": "Measured when the prescription was written, which is the response's\r\n`authored` -- the form does not ask for a date."
},
{
"name": "performerFromAuthor",
"source": [
{
"context": "qr",
"element": "author",
"variable": "a",
"condition": "(reference.matches('(^|/)(Practitioner|PractitionerRole)/[^/]+$')) and ((reference.exists() and (reference.startsWith('Organization/') or reference.startsWith('Patient/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Organization') or ('/' + type).endsWith('/Patient'))))"
}
],
"target": [
{
"context": "obs",
"element": "performer",
"transform": "copy",
"parameter": [
{
"valueId": "a"
}
]
}
],
"documentation": "Measured by the prescriber, chosen the same way as on the prescription."
},
{
"name": "performerSourceFallback",
"source": [
{
"context": "qr",
"condition": "author.where(((reference.exists() and (reference.startsWith('Organization/') or reference.startsWith('Patient/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Organization') or ('/' + type).endsWith('/Patient'))))).exists().not()"
}
],
"rule": [
{
"name": "performerFromSource",
"source": [
{
"context": "qr",
"element": "source",
"variable": "s",
"condition": "(reference.matches('(^|/)(Practitioner|PractitionerRole)/[^/]+$')) and ((reference.exists() and (reference.startsWith('Organization/') or reference.startsWith('Patient/'))) or (reference.empty() and (type.empty() or ('/' + type).endsWith('/Organization') or ('/' + type).endsWith('/Patient'))))"
}
],
"target": [
{
"context": "obs",
"element": "performer",
"transform": "copy",
"parameter": [
{
"valueId": "s"
}
]
}
]
}
]
},
{
"name": "linkQuestionnaireResponse",
"source": [
{
"context": "qr",
"element": "id",
"variable": "qid",
"condition": "$this.hasValue()"
}
],
"target": [
{
"context": "obs",
"element": "derivedFrom",
"variable": "ref",
"transform": "create",
"parameter": [
{
"valueString": "Reference"
}
]
},
{
"context": "ref",
"element": "reference",
"transform": "copy",
"parameter": [
{
"valueId": "qid"
}
]
}
],
"documentation": "A stored response's id already reads \"QuestionnaireResponse/<id>\", so it is\r\na relative reference as it stands. The hasValue() guard is what stops an\r\nunsaved response -- one posted straight to $extract rather than saved first\r\n-- from writing \"QuestionnaireResponse/null\" into the record."
}
]
},
{
"name": "SetObservationFullUrl",
"input": [
{
"name": "obs",
"type": "Observation",
"mode": "source"
},
{
"name": "entry",
"mode": "target"
}
],
"rule": [
{
"name": "assignFullUrl",
"source": [
{
"context": "obs",
"element": "id",
"variable": "id"
}
],
"target": [
{
"context": "entry",
"element": "fullUrl",
"transform": "append",
"parameter": [
{
"valueString": "https://fhir.slade360.co.ke/fhir/Observation/"
},
{
"valueId": "id"
}
]
}
]
}
]
},
{
"name": "SetVisionPrescriptionFullUrl",
"input": [
{
"name": "vp",
"type": "VisionPrescription",
"mode": "source"
},
{
"name": "entry",
"mode": "target"
}
],
"rule": [
{
"name": "assignFullUrl",
"source": [
{
"context": "vp",
"element": "id",
"variable": "id"
}
],
"target": [
{
"context": "entry",
"element": "fullUrl",
"transform": "append",
"parameter": [
{
"valueString": "https://fhir.slade360.co.ke/fhir/VisionPrescription/"
},
{
"valueId": "id"
}
]
}
]
}
]
},
{
"name": "SetResponseProvenance",
"documentation": "── Provenance and tenancy ───────────────────────────────────────────────────\r\nTwo tags on everything this map creates. The first says which form produced\r\nit -- the form's document type, not the questionnaire's id, because every\r\nenvironment mints its own ids and there is one set of maps. The second copies\r\nthe tenant tags off the response, because nothing else on the path puts them\r\nthere.",
"input": [
{
"name": "qr",
"type": "QR",
"mode": "source"
},
{
"name": "r",
"mode": "target"
}
],
"rule": [
{
"name": "provenanceMeta",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "r",
"element": "meta",
"variable": "m"
}
],
"rule": [
{
"name": "provenanceTag",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "m",
"element": "tag",
"variable": "t"
}
],
"rule": [
{
"name": "provenanceSystem",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "t",
"element": "system",
"transform": "copy",
"parameter": [
{
"valueString": "http://slade360edi.com/questionnaire-provenance"
}
]
}
]
},
{
"name": "provenanceCode",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "t",
"element": "code",
"transform": "copy",
"parameter": [
{
"valueString": "optical-prescription"
}
]
}
]
},
{
"name": "provenanceDisplay",
"source": [
{
"context": "qr"
}
],
"target": [
{
"context": "t",
"element": "display",
"transform": "copy",
"parameter": [
{
"valueString": "Optical prescription"
}
]
}
]
}
]
}
]
},
{
"name": "tenantTags",
"source": [
{
"context": "qr",
"element": "meta",
"variable": "qm"
}
],
"rule": [
{
"name": "copyTag",
"source": [
{
"context": "qm",
"element": "tag",
"variable": "qt"
}
],
"target": [
{
"context": "r",
"element": "meta",
"variable": "m"
},
{
"context": "m",
"element": "tag",
"transform": "copy",
"parameter": [
{
"valueId": "qt"
}
]
}
]
}
],
"documentation": "`meta` is 0..1, so this lands on the same meta the rule above created\r\nrather than a second one."
}
]
}
]
}