.. _tut_localterminology: ConceptMap terminology ====================== .. note:: A whole tutorial referenced here is available in the following Postman collection: https://documenter.getpostman.com/view/1515623/2sBXcLex64 .. note:: If running against a public sandbox.open-fhir.com, don't forget to include Authorization header of type Basic. A more complex terminology mapping than described in :doc:`inlineterminology` can be provided with ConceptMaps. openFHIR Engine has a full terminology service built-in. That terminology service provides a `$translate` operation as per the FHIR IG of the terminology service (https://build.fhir.org/terminology-service.html). The ConceptMap below externalizes the same translation we defined inline in the previous step — between the openEHR body_weight archetype's local ``State of dress`` codes and the `Vital Signs with Qualifying Elements `_ IG's Body Weight Associated Situation value set — but now covering the whole value set in both directions (SNOMED CT plus the IG's Solor temporary code system), including an ``unmapped`` fallback. Add ConceptMap -------------- .. http:post:: http://localhost:8080/terminology/fhir/ConceptMap .. sourcecode:: http POST http://localhost:8080/terminology/fhir/ConceptMap HTTP/1.1 Content-Type: application/json Body: { "resourceType": "ConceptMap", "id": "e1a90f56-4773-44a6-8004-0ef178ce85a8", "url": "http://open-fhir.com/DemoConceptmap", "name": "StateOfDressToAssociatedSituation", "status": "active", "description": "Maps the openEHR body_weight archetype's local 'State of dress' codes to the codes of the Body Weight Associated Situation value set of the HL7 'Vital Signs with Qualifying Elements' IG (SNOMED CT and the Solor temporary code system), and back.", "group": [ { "source": "local", "target": "http://snomed.info/sct", "element": [ { "code": "at0013", "display": "Naked", "target": [ { "code": "248160001", "display": "Undressed", "equivalence": "equivalent" } ] }, { "code": "at0011", "display": "Lightly clothed/underwear", "target": [ { "code": "248160001", "display": "Undressed", "equivalence": "inexact", "comment": "The value set has no 'underwear only' concept; 'Undressed' is the closest code for interpreting a weight measurement." } ] }, { "code": "at0017", "display": "Nappy/diaper", "target": [ { "code": "248160001", "display": "Undressed", "equivalence": "inexact", "comment": "The value set has no 'diaper only' concept; 'Undressed' is the closest code for interpreting a weight measurement." } ] } ], "unmapped": { "mode": "fixed", "code": "248160001", "display": "Undressed" } }, { "source": "local", "target": "http://hl7.org/fhir/us/vitals/CodeSystem/SolorTemporaryCodeSystem", "element": [ { "code": "at0010", "display": "Fully clothed, including shoes", "target": [ { "code": "971000205103", "display": "Wearing street clothes with shoes (finding)", "equivalence": "equivalent" } ] } ] }, { "source": "http://snomed.info/sct", "target": "local", "element": [ { "code": "248160001", "display": "Undressed", "target": [ { "code": "at0013", "display": "Naked", "equivalence": "narrower", "comment": "Infants are weighed naked, so the reverse translation defaults to at0013." } ] } ], "unmapped": { "mode": "fixed", "code": "at0013", "display": "Naked" } }, { "source": "http://hl7.org/fhir/us/vitals/CodeSystem/SolorTemporaryCodeSystem", "target": "local", "element": [ { "code": "971000205103", "display": "Wearing street clothes with shoes (finding)", "target": [ { "code": "at0010", "display": "Fully clothed, including shoes", "equivalence": "equivalent" } ] }, { "code": "961000205106", "display": "Wearing street clothes, no shoes (finding)", "target": [ { "code": "at0010", "display": "Fully clothed, including shoes", "equivalence": "inexact", "comment": "The archetype has no 'street clothes without shoes' concept; at0010 is the closest available state." } ] } ] } ] } Reference ConceptMap in the model mapping ----------------------------------------- ConceptMap created above needs to be referenced from a specific model mapper so the engine picks it up when doing a translation from openEHR<>FHIR. To do this, we replace the ``inline`` terminology tag from the previous step with a reference to the ConceptMap: :: terminology: type: "local" # local when using the embedded terminology service, could also be 'inline' and 'remote' conceptmap: "http://open-fhir.com/DemoConceptmap" # concept map canonical URL as provided when creating it Full payload when updating a model mapper should look like: .. http:put:: http://localhost:8080/fc/model/(id of the model mapper) .. sourcecode:: http PUT http://localhost:8080/fc/model/(id) HTTP/1.1 Body: :: grammar: FHIRConnect/v0.0.1 type: model metadata: name: "OBSERVATION.body_weight.v2" version: 0.0.1a # version of this particular mapping spec: # schema specific to the FHIRConnect v0.0.1 engine system: FHIR version: R4 openEhrConfig: archetype: "openEHR-EHR-OBSERVATION.body_weight.v2" fhirConfig: structureDefinition: http://hl7.org/fhir/StructureDefinition/Observation preprocessor: hierarchy: with: fhir: "$resource" openehr: "$archetype/data[at0002]/events[at0003]" split: fhir: create: "resource" fhirCondition: targetRoot: "$resource" targetAttribute: "code.coding.code" operator: "one of" criteria: "29463-7" mappings: - name: "weight" with: fhir: "$resource.value" openehr: "$archetype/data[at0002]/events[at0003]/data[at0001]/items[at0004]" - name: "category" with: fhir: "$resource.category.coding" openehr: "$archetype" manual: - name: "vitalSignsCategory" fhir: - path: "code" value: "vital-signs" - path: "system" value: "http://terminology.hl7.org/CodeSystem/observation-category" - path: "display" value: "Vital Signs" - name: "time" with: fhir: "$resource.effective" openehr: "$archetype/data[at0002]/events[at0003]/time" - name: "status" unidirectional: "openehr->fhir" with: fhir: "$resource" openehr: "$archetype" manual: - name: "status" fhir: - path: "status" value: "final" - name: "profile" unidirectional: "openehr->fhir" with: fhir: "$resource" openehr: "$archetype" manual: - name: "profile" fhir: - path: "meta.profile" value: "http://hl7.org/fhir/us/vitals/StructureDefinition/body-weight" - name: "code" unidirectional: "openehr->fhir" with: fhir: "$resource.code.coding" openehr: "$archetype" manual: - name: "code" fhir: - path: "code" value: "29463-7" - path: "system" value: "http://loinc.org" - path: "display" value: "Body weight" - name: "comment" with: fhir: "$resource.note.text" openehr: "$archetype/data[at0002]/events[at0003]/data[at0001]/items[at0024]" - name: "state of dress" with: fhir: "$resource.extension" openehr: "$archetype/data[at0002]/events[at0003]/state[at0008]" fhirCondition: targetRoot: "$resource.extension" targetAttribute: "url" operator: "one of" criteria: "http://hl7.org/fhir/us/vitals/StructureDefinition/AssociatedSituationExt" terminology: type: "local" conceptmap: "http://open-fhir.com/DemoConceptmap" followedBy: mappings: - name: "value" with: fhir: "value" openehr: "items[at0009]" - name: "url" with: fhir: "$fhirRoot" openehr: "$archetype" manual: - name: "url" fhir: - path: "url" value: "http://hl7.org/fhir/us/vitals/StructureDefinition/AssociatedSituationExt" Testing the embedded terminology service ---------------------------------------- Use the same example of openEHR as in previous steps: .. http:post:: http://localhost:8080/$tofhir?templateId=Growth chart&patient=Patient/lina-weber .. sourcecode:: http POST http://localhost:8080/$tofhir?templateId=Growth%20chart&patient=Patient/lina-weber HTTP/1.1 Content-Type: application/fhir+json Body: :download:`growth_chart_flat.json ` Within the result, you should see the AssociatedSituation extension translated through the ConceptMap: :: "extension": [ { "url": "http://hl7.org/fhir/us/vitals/StructureDefinition/AssociatedSituationExt", "valueCodeableConcept": { "coding": [ { "system": "http://snomed.info/sct", "code": "248160001", "display": "Undressed" } ], "text": "Naked" } } ] Going in the other direction (``$toopenehr``), the reverse groups of the ConceptMap translate SNOMED CT ``248160001`` back to a properly openEHR-coded ``at0013`` in the resulting Composition: :: "growth_chart/body_weight/any_event:0/state_of_dress|code": "at0013", "growth_chart/body_weight/any_event:0/state_of_dress|terminology": "local" You can also test what happens when providing a code that's not within the ConceptMap. A fallback would be used with the "unmapped" entry of the ConceptMap, as per the ConceptMap's purpose.