-
Notifications
You must be signed in to change notification settings - Fork 20
3. Add new mapping
After seting up and running the FHIR-Bridge (as explained here) a mapping can be added by following the steps explained beyond. All steps are explained following the example of implementing a mapping for body temperature.
Each change to the FHIR bridge should have a ticket created, explaining the change. Create a new feature branch with
ticket number like: feature/123_mapping_body_temperature
, where 123
stands for the issue number
# optional: make a new checkout
#git clone https://github.com/ehrbase/fhir-bridge.git
git clone [email protected]:ehrbase/fhir-bridge.git
# default:
cd fhir-bridge
git checkout -b feature/123_mapping_body_temperature
# At the first push:
git push -u origin feature/123_mapping_body_temperature
# For later pushes:
git push
# start docker, if it is not already running (analog to readme)
cd fhir-bridge/docker
docker-compose -f docker-compose-light.yml up
# build everything initially
cd fhir-bridge # bzw. 'cd ..' , wenn der vorherige Befehl ausgeführt wurde
mvn clean install
- visit http://88.198.146.13/ckm/projects/1246.152.26/resourcecentre
- download the
Körpertemperatur.opt
by clicking on it - add the file to the directory
fhir-bridge/src/main/resources/opt
- Optional: Check the OPT in Pablos Tool: toolkit.cabolabs.com
- leave the fhir-bridge folder
- clone the following project with
git clone https://github.com/ehrbase/openEHR_SDK
- enter the directory and execute
mvn clean install
- execute
java -jar YOUR_OPENEHR-SDK_PATH/generator/target/generator-VERSION.jar -opt YOUR_FHIR-BRIDGE_PATH/src/main/resources/opt/Körpertemperatur.opt -out YOUR_FHIR-BRIDGE_PATH/src/main/java/ -package org.ehrbase.fhirbridge.ehr.opt
This serializes Java classes for the Body temp Composition. - Make class
fhir-bridge/src/main/java/org/ehrbase/fhirbridge/ehr/opt/YOUR-COMP/YOUR-COMP-Composition
implement the Composition interface. Troubleshooting: In case IntelliJ can not resolve basic imports anymore, e.g. java.xxxx, close the project. Delete the ".idea" folder and open project again.
- visit https://simplifier.net/ForschungsnetzCovid-19/~resources?fhirVersion=R4&sortBy=RankScore_desc
- search for Body Temperature (Profile)
- click on the Profile and Download a Snapshot as XML
- move the Downloaded XML into the directory
fhir-bridge/src/main/resources/profiles
Note: If your Profile includes extensions, don't forget to download and add them, too.
To later test the implemented mapping, an example of the profile also needs to be provided
- visit https://simplifier.net/ForschungsnetzCovid-19/~resources?fhirVersion=R4&sortBy=RankScore_desc
- search for Body Temperature (Example)
- click on the Example and Download it as JSON
- open the JSON and search for the value of the field
¨resourceType¨
, in this case it is an Observation - move it to
fhir-bridge/src/test/resources/Observation
- rename it to
create-body-temp.json
- open the file and replace the JSON object
subject
with the following lines:
"subject": {
"identifier": {
"system": "urn:ietf:rfc:4122",
"value": "{{patientId}}"
}
},
Note: Step 7 depends on the resource type, e.g. for Patient it's just the identifier part that needs to be included.
To add a mapping to the fhir-bridge, the routing for the input has to be defined. Since the basic functionality is already provided only two classes need to be edited.
- open
fhir-bridge/src/main/java/org/ehrbase/fhir-bridge/fhir/common/Profile.java
- add the line
BODY_TEMP(Observation.class, "https://www.netzwerk-universitaetsmedizin.de/fhir/StructureDefinition/body-temperature"),
to the enum class e.g.:
public enum Profile {
PATIENT(Patient.class, "https://www.netzwerk-universitaetsmedizin.de/fhir/StructureDefinition/Patient"),
BODY_TEMP(Observation.class, "https://www.netzwerk-universitaetsmedizin.de/fhir/StructureDefinition/body-temperature"),
BODY_WEIGHT(Observation.class, "https://www.netzwerk-universitaetsmedizin.de/fhir/StructureDefinition/body-weight");
The values entered here derive from your fhir resource you want to map. In case of our example, body temperature is a Observation. This may vary depending on the resourceType of your fhir resource! The URL contained as the second parameter is found in your fhir JSON example within the JSON object meta e.g.:
"meta": {
"profile": [
"https://www.netzwerk-universitaetsmedizin.de/fhir/StructureDefinition/body-temperature"
]
},
- open
fhir-bridge/src/main/org/ehrbase/fhirbridge/config/ConversionConfiguration
- identify the appropriate "register" method for your converter
- add
conversionService.registerConverter(Profile.BODY_HEIGHT, new BodyHeightCompositionConverter());
to the method.
Hereby camel can route the input of an BodyHeight to the BodyHeightCompositionConverter where the mapping logic is to be contained.
- create a package for your conversion in
fhir-bridge/src/main/java/org/ehrbase/fhirbridge/ehr/converter/specific
- create the class BodyHeightCompositionConverter, consider using
Alt
+Enter
while your cursor is in IntelliJ on the red highlighted converter class name, you just wrote) - this class needs to implement the
YOUR_FHIR_TYPE_ToCompositionConverter
interface using theKoerpergroesseComposition
Example:
public class BodyHeightCompositionConverter extends ObservationToCompositionConverter<KoerpergroesseComposition> {
- the CompositionConverter interface has one method that needs to be set for each mapping that is done: the
convertInternal
method. It is part of a process to map a openEHR composition. TheconvertInternal
method is used to set the mapping of specific entries. Many fields are identical within the mappings. As an example startTimeValue is a field of each observation mapping and therefore doesn't need to be mapped every time. Instead it gets mapped in the abstract class, contained in the generic package. These abstract classes call the specificconvertInternal
method as part of the process. In our example, the methodconvertInternal
would look something like that:
@Override
public KoerpergroesseComposition convertInternal(@NonNull Observation resource) {
KoerpergroesseComposition composition = new KoerpergroesseComposition();
composition.setGroesseLaenge(new GroesseLaengeObservationConverter().convert(resource));
return composition;
}
- Each composition and each entry entity can contain other entry entries. Each of these entries has specific and generic mappings. In order to avoid duplicating generic mappings, we create new classes for each entry entity inheriting generic converters.
- In our case the composition has an GroesseLaengeObservation entry. To convert this observation we create a new class
YOUR_MAPPING_Converter
, e.g.GroesseLaengeObservationConverter
. - this class needs to implement the
YOUR_EHR_TYPE_Converter
interface using the openEHR observationGroesseLaengeObservation
. There are different converters provided for specific combinations, if not provided use the default EntryEntityConverter. In our case we convert an Fhir Observation to an openEHR observation and therefore use the ObservationToObservationConverter. Example:
public class GroesseLaengeObservationConverter extends ObservationToObservationConverter<GroesseLaengeObservation> {
- the Converter interface has one methods that needs to be set for each mapping that is done: the
convertInternal
method. It maps fhir resource into the openEHR equivalent. In our example, the method would look something like that:
@Override
protected GroesseLaengeObservation convertInternal(Observation resource) {
GroesseLaengeObservation groesseLaengeObservation = new GroesseLaengeObservation();
groesseLaengeObservation.setGroesseLaengeUnits(resource.getValueQuantity().getCode());
groesseLaengeObservation.setGroesseLaengeMagnitude(resource.getValueQuantity().getValue().doubleValue());
return groesseLaengeObservation;
}
- download the following graphic
- open draw.io
- import the graphic (file -> open from -> device)
- Go to Simplifier and open the resource you want to map (not the example !) e.g. body temp
- compare the fields with the ones of the template, to do so open this link, search your template and click "View template"
- add this value mappings to the grapic (see examples, also in the left top corner is a legend)
(Final file can be added to the Wiki under "Fertige Dokus")
To see all fields of the template you can use better designer instead of ckm. Therefore, you have to:
- Open the "Gecco Core Project" in ckm
- Export the project as zip
- Sign in with github-Account (https://tools.openehr.org/designer/#/)[https://tools.openehr.org/designer/#/]
- Create a new repository
- Import the zip into the repositry ("import", "upload", wait, "close")
In the next step the mapping needs to be implemented in your new Converter, in our case GroesseLangeObservationConverter / TemperatureObservationConverter.
To run what you implemented:
- Be sure, docker is running
(base) birgit@birgit-Latitude-7390:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
308cf2833064 ehrbase/ehrbase:next "/bin/sh -c ./docker…" 2 minutes ago Up 2 minutes 0.0.0.0:8080->8080/tcp docker_ehrbase_1
71abf5d575ae ehrbase/ehrbase-postgres:latest "docker-entrypoint.s…" 3 minutes ago Up 2 minutes 0.0.0.0:5432->5432/tcp docker_ehrbase-db_1
- Start your local fhir-bridge by clicking 'Maven' in the upper right corner of IntelliJ. Then navigate to 'FHIR Bridge | Plugins | spring-boot ' und start the 'spring-boot:run' command
- Run the Junit tests of interest. If you just implemented your mapping you probably want to continue reading first ;-)
Troubleshooting for Windows user in IntelliJ: If "command line command is to long" error occurs. IntelliJ will already propose how to fix it (first option). If not: Run, Edit Configurations, testclass-name (e.g. ObservationIT). If you don't see the "Shorten command line". Click on arrow next to Modify options on right side and activate "Shorten command line". Now select "@argfile ...". Now it should run.
Afterwards POST the example fhir json to the fhir-bridge ({base_url/fhir/Observation}). Within the log of the fhir-bridge server, the composition version uid is returned. Copy this uid and send an request to ehrbase to return this composition. Retrieve related composition from ehrbase via an AQL query, i.e.
POST {{ehrbase_url}}/query/aql
Content-Type: application/json
Authorization: Basic bXl1c2VyOm15UGFzc3dvcmQ0MzI=
# body/payload
{
"q": "SELECT c FROM EHR e [ehr_id/value='{{ehr_id}}'] CONTAINS COMPOSITION c"
}
#or
{
"q": "SELECT c FROM EHR e CONTAINS COMPOSITION c ORDER BY c/context/start_time DESC"
}
Check if the composition contains all the values as intended.
- open Postman
- import
config/postman/fhir-bridge.postman_collection.json
- run
Ehrbase -> Create EHR
- run
Patient -> Create Patient
- then post your composition
Tests shall cover the following cases:
- In
fhir-bridge/src/test/java/org/ehrbase/fhirbridge/fhir
create a new testfile for your mapping within the proper package, e.g.observation
. The new class itself must end withIT
, e.g.observation/BodyTempIT.java
. - Make the class
extends AbstractMappingTestSetupIT
- Add the constructor,the executeMappingUnprocessableEntityException()-method and the getJavers()-method.
- In the getJavers()-method: add every openEHR Observation, Composition, Element etc. included in your mapping as a ValueObject.
- The Composition has to include a location string, used to ignore the location when comparing object (example).
- Implementation hint: When running the mapping that uses javers, the class that is not comparable will be thrown in the error stack, copy this class name and add it to the getJavers()-method.
- In
test/resources/yourPackage
create a package for your mapping - In order to provide an object that the mapping test can be validated against, the output of your current one is to be used. It is crucial that you manually check this Composition and ensure its validity (to display your latest mapping, go to src/main/resources/application.yml and set debug to true, further more a specific path for the mapping output needs to be specified. It is recommended to use the default path:
src/main/resources/mapping-output.json
).
- Copy this json and add it to the package you just created. Suggested name prefix:
paragon-
- Write the mapping test using this file for the specific fhir input, do the same for all other valid testfiles.
Example:
@Test
void mappingNormalFinding() throws IOException {
DiagnosticReport diagnosticReport = (DiagnosticReport) super.testFileLoader.loadResource("create-radiology-report-normal-finding.json");
RadiologischerBefundConverter radiologischerBefundConverter = new RadiologischerBefundConverter();
GECCORadiologischerBefundComposition mappedGeccoRadiologischerBefundComposition = radiologischerBefundConverter.toComposition(diagnosticReport);
Diff diff = compareCompositions(getJavers(), "paragon-radiology-report-normal-finding.json", mappedGeccoRadiologischerBefundComposition);
assertEquals(0, diff.getChanges().size());
}
- Add one more test that map a resource and send it to the ehrbase (entire workflow).
@Test
void createNormalFinding() throws IOException {
create("create-radiology-report-normal-finding.json");
}
- If there are some exceptions you added that are not covered yet, add tests for those.