Skip to content

Commit

Permalink
feat: added dummy strahlentherapie mapper (#147)
Browse files Browse the repository at this point in the history
* feat: added dummy strahlentherapie mapper

* feat: add mapping for status

This also adds custom enum binding for ST_Type.Meldeanlass

* made dummy mapper profile-conformant

* added further Testpatient resources and snapshots

* drop timezone overrides since we're only having dates and not timestamps anyways

* chore: remove now obsolete test file

* validate that subject points to a patient resource

* re-order commented code

---------

Co-authored-by: Paul-Christian Volkmer <[email protected]>
  • Loading branch information
chgl and pcvolkmer authored Nov 6, 2024
1 parent 73adc4c commit 4c51e3c
Show file tree
Hide file tree
Showing 9 changed files with 448 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ public static class FhirExtensions {
private String sysTheraProto;
private String dataAbsentReason;
private String genderAmtlich;
private String miiExOnkoStrahlentherapieIntention;
private String miiExOnkoStrahlentherapieBestrahlung;
}

@Data
Expand Down Expand Up @@ -65,6 +67,11 @@ public static class FhirSystems {
private String gleasonScoreObservationId;
private String psaObservationId;
private String ucum;
private String miiCsOnkoIntention;
private String miiCsOnkoStrahlentherapieApplikationsart;
private String miiCsOnkoStrahlentherapieStrahlenart;
private String miiCsOnkoStrahlentherapieZielgebiet;
private String strahlentherapieProcedureId;
}

@Data
Expand All @@ -82,6 +89,7 @@ public static class FhirProfiles {
private String miiPatientPseudonymisiert;
private String deathObservation;
private String miiPrOnkoDiagnosePrimaertumor;
private String miiPrOnkoStrahlentherapie;
}

@Data
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package org.miracum.streams.ume.obdstofhir.mapper.mii;

import de.basisdatensatz.obds.v3.STTyp;
import java.util.Objects;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.DateTimeType;
import org.hl7.fhir.r4.model.Enumerations.ResourceType;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.Period;
import org.hl7.fhir.r4.model.Procedure;
import org.hl7.fhir.r4.model.Reference;
import org.miracum.streams.ume.obdstofhir.FhirProperties;
import org.miracum.streams.ume.obdstofhir.mapper.ObdsToFhirMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class StrahlentherapieMapper extends ObdsToFhirMapper {
private static final Logger LOG = LoggerFactory.getLogger(StrahlentherapieMapper.class);

public StrahlentherapieMapper(FhirProperties fhirProperties) {
super(fhirProperties);
}

public Procedure map(STTyp st, Reference subject) {
Objects.requireNonNull(st);
Objects.requireNonNull(subject);

Validate.notBlank(st.getSTID(), "Required ST_ID is unset");
Validate.isTrue(
Objects.equals(
subject.getReferenceElement().getResourceType(), ResourceType.PATIENT.toCode()),
"The subject reference should point to a Patient resource");

var procedure = new Procedure();
procedure.getMeta().addProfile(fhirProperties.getProfiles().getMiiPrOnkoStrahlentherapie());

// TODO: can we be sure that this ST-ID is globally unqiue across all STs? -
// if not we may instead need to construct the ID from the patient-id + others.
var identifier =
new Identifier()
.setSystem(fhirProperties.getSystems().getStrahlentherapieProcedureId())
.setValue(st.getSTID());
procedure.addIdentifier(identifier);
procedure.setId(computeResourceIdFromIdentifier(identifier));

// Status
if (st.getMeldeanlass() == STTyp.Meldeanlass.BEHANDLUNGSENDE) {
procedure.setStatus(Procedure.ProcedureStatus.COMPLETED);
} else {
procedure.setStatus(Procedure.ProcedureStatus.INPROGRESS);
}

procedure.setSubject(subject);

var dataAbsentExtension =
new Extension(
fhirProperties.getExtensions().getDataAbsentReason(), new CodeType("unknown"));
var dataAbsentCode = new CodeType();
dataAbsentCode.addExtension(dataAbsentExtension);

var code = new CodeableConcept();
code.addCoding().setSystem(fhirProperties.getSystems().getOps()).setCodeElement(dataAbsentCode);
procedure.setCode(code);

var category =
new CodeableConcept(
new Coding(
fhirProperties.getSystems().getSnomed(), "277132007", "Therapeutic procedure"));
procedure.setCategory(category);

var dataAbsentConcept = new CodeableConcept();
dataAbsentConcept.addExtension(dataAbsentExtension);

var performedStart = new DateTimeType();
performedStart.addExtension(dataAbsentExtension);
var performed = new Period().setStartElement(performedStart);
procedure.setPerformed(performed);

// for now, all required extensions are filled with sample values from
// https://simplifier.net/guide/mii-ig-modul-onkologie-2024-de/MIIIGModulOnkologie/TechnischeImplementierung/FHIR-Profile/Strahlentherapie/Strahlentherapie-Procedure.page.md?version=current

var intention = new CodeableConcept();
intention
.addCoding()
.setSystem(fhirProperties.getSystems().getMiiCsOnkoIntention())
.setCode("P");
procedure.addExtension(
fhirProperties.getExtensions().getMiiExOnkoStrahlentherapieIntention(), intention);

var bestrahlung =
new Extension(fhirProperties.getExtensions().getMiiExOnkoStrahlentherapieBestrahlung());

var applikationsart = new CodeableConcept();
applikationsart
.addCoding()
.setSystem(fhirProperties.getSystems().getMiiCsOnkoStrahlentherapieApplikationsart())
.setCode("P-ST");
bestrahlung.addExtension("Applikationsart", applikationsart);

var strahlenart = new CodeableConcept();
strahlenart
.addCoding()
.setSystem(fhirProperties.getSystems().getMiiCsOnkoStrahlentherapieStrahlenart())
.setCode("UH");
bestrahlung.addExtension("Strahlenart", strahlenart);

var zielgebiet = new CodeableConcept();
zielgebiet
.addCoding()
.setSystem(fhirProperties.getSystems().getMiiCsOnkoStrahlentherapieZielgebiet())
.setCode("3.4");
bestrahlung.addExtension("Zielgebiet", zielgebiet);

/* The extensions below are optional:
var dataAbsentQuantity = new Quantity();
dataAbsentQuantity.setSystem(fhirProperties.getSystems().getUcum());
dataAbsentQuantity.setCodeElement(dataAbsentCode);
var absentUnit = new StringType();
absentUnit.addExtension(dataAbsentExtension);
dataAbsentQuantity.setUnitElement(absentUnit);
bestrahlung.addExtension("Zielgebiet_Lateralitaet", dataAbsentConcept);
bestrahlung.addExtension("Gesamtdosis", dataAbsentQuantity);
bestrahlung.addExtension("Einzeldosis", dataAbsentQuantity);
bestrahlung.addExtension("Boost", dataAbsentConcept);
*/
procedure.addExtension(bestrahlung);

return procedure;
}
}
8 changes: 8 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ fhir:
sysTheraProto: "http://dktk.dkfz.de/fhir/StructureDefinition/onco-core-Extension-SystemischeTherapieProtokoll"
dataAbsentReason: "http://hl7.org/fhir/StructureDefinition/data-absent-reason"
genderAmtlich: "http://fhir.de/StructureDefinition/gender-amtlich-de"
mii-ex-onko-strahlentherapie-intention: "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/StructureDefinition/mii-ex-onko-strahlentherapie-intention"
mii-ex-onko-strahlentherapie-bestrahlung: "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/StructureDefinition/mii-ex-onko-strahlentherapie-bestrahlung"
systems:
patientId: "https://fhir.diz.uk-erlangen.de/identifiers/patient-id"
identifier-type: "http://terminology.hl7.org/CodeSystem/v2-0203"
Expand All @@ -22,6 +24,7 @@ fhir:
observationCategorySystem: "http://hl7.org/fhir/observation-category"
gleasonScoreObservationId: "https://fhir.diz.uk-erlangen.de/identifiers/obds/gleason-score-observation-id"
psaObservationId: "https://fhir.diz.uk-erlangen.de/identifiers/obds/psa-observation-id"
strahlentherapie-procedure-id: "https://bzkf.github.io/obds-to-fhir/identifiers/strahlentherapie-procedure-id"
loinc: "http://loinc.org"
idco3Morphologie: "urn:oid:2.16.840.1.113883.6.43.1"
gradingDktk: "http://dktk.dkfz.de/fhir/onco/core/CodeSystem/GradingCS"
Expand Down Expand Up @@ -51,6 +54,10 @@ fhir:
observation-value: "http://terminology.hl7.org/CodeSystem/v3-ObservationValue"
genderAmtlichDe: "http://fhir.de/CodeSystem/gender-amtlich-de"
ucum: "http://unitsofmeasure.org"
mii-cs-onko-intention: "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/CodeSystem/mii-cs-onko-intention"
mii-cs-onko-strahlentherapie-applikationsart: "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/CodeSystem/mii-cs-onko-strahlentherapie-applikationsart"
mii-cs-onko-strahlentherapie-strahlenart: "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/CodeSystem/mii-cs-onko-strahlentherapie-strahlenart"
mii-cs-onko-strahlentherapie-zielgebiet: "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/CodeSystem/mii-cs-onko-strahlentherapie-zielgebiet"
profiles:
histologie: "http://dktk.dkfz.de/fhir/StructureDefinition/onco-core-Observation-Histologie"
grading: "http://dktk.dkfz.de/fhir/StructureDefinition/onco-core-Observation-Grading"
Expand All @@ -66,6 +73,7 @@ fhir:
# MII
mii-patient-pseudonymisiert: "https://www.medizininformatik-initiative.de/fhir/core/modul-person/StructureDefinition/PatientPseudonymisiert"
mii-pr-onko-diagnose-primaertumor: https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/StructureDefinition/mii-pr-onko-diagnose-primaertumor
mii-pr-onko-strahlentherapie: "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/StructureDefinition/mii-pr-onko-strahlentherapie"
display:
histologyLoinc: "Histology and Behavior ICD-O-3 Cancer"
gradingLoinc: "Grade pathology value Cancer"
Expand Down
3 changes: 3 additions & 0 deletions src/main/resources/schema/oBDS_v3.0.3.bindings.xjb
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,8 @@
<jaxb:typesafeEnumMember value="9" name="DS_9" />
</jaxb:typesafeEnumClass>
</jaxb:bindings>
<jaxb:bindings node="//xs:complexType[@name='ST_Typ']//xs:element[@name='Meldeanlass']/xs:simpleType">
<jaxb:typesafeEnumClass name="Meldeanlass" />
</jaxb:bindings>
</jaxb:bindings>
</jaxb:bindings>
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import com.fasterxml.jackson.module.jakarta.xmlbind.JakartaXmlBindAnnotationModule;
import de.basisdatensatz.obds.v3.OBDS;
import java.io.IOException;
import java.util.TimeZone;
import org.approvaltests.Approvals;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
Expand All @@ -26,7 +25,6 @@ class PatientMapperTest {

@BeforeAll
static void beforeEach(@Autowired FhirProperties fhirProps) {
TimeZone.setDefault(TimeZone.getTimeZone("Europe/Berlin"));
sut = new PatientMapper(fhirProps);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.miracum.streams.ume.obdstofhir.mapper.mii;

import static org.assertj.core.api.Assertions.assertThat;

import ca.uhn.fhir.context.FhirContext;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.module.jakarta.xmlbind.JakartaXmlBindAnnotationModule;
import de.basisdatensatz.obds.v3.OBDS;
import java.io.IOException;
import org.approvaltests.Approvals;
import org.hl7.fhir.r4.model.Reference;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.miracum.streams.ume.obdstofhir.FhirProperties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest(classes = {FhirProperties.class})
@EnableConfigurationProperties
class StrahlentherapieMapperTest {
private static StrahlentherapieMapper sut;

@BeforeAll
static void beforeEach(@Autowired FhirProperties fhirProps) {
sut = new StrahlentherapieMapper(fhirProps);
}

@ParameterizedTest
@CsvSource({"Testpatient_1.xml", "Testpatient_2.xml", "Testpatient_3.xml"})
void map_withGivenObds_shouldCreateValidProcedure(String sourceFile) throws IOException {
final var resource = this.getClass().getClassLoader().getResource("obds3/" + sourceFile);
assertThat(resource).isNotNull();

final var xmlMapper =
XmlMapper.builder()
.defaultUseWrapper(false)
.addModule(new JakartaXmlBindAnnotationModule())
.addModule(new Jdk8Module())
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.build();

final var obds = xmlMapper.readValue(resource.openStream(), OBDS.class);

var obdsPatient = obds.getMengePatient().getPatient().getFirst();

var subject = new Reference("Patient/any");
var stMeldung =
obdsPatient.getMengeMeldung().getMeldung().stream()
.filter(m -> m.getST() != null)
.findFirst()
.get();
var procedure = sut.map(stMeldung.getST(), subject);

var fhirParser = FhirContext.forR4().newJsonParser().setPrettyPrint(true);
var fhirJson = fhirParser.encodeResourceToString(procedure);
Approvals.verify(
fhirJson, Approvals.NAMES.withParameters(sourceFile).forFile().withExtension(".fhir.json"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
{
"resourceType": "Procedure",
"id": "bfc5c7a0c00d1c00e9ab41a4507f8cfbbc87e13953eba89e14c3fe3a54325559",
"meta": {
"profile": [ "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/StructureDefinition/mii-pr-onko-strahlentherapie" ]
},
"extension": [ {
"url": "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/StructureDefinition/mii-ex-onko-strahlentherapie-intention",
"valueCodeableConcept": {
"coding": [ {
"system": "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/CodeSystem/mii-cs-onko-intention",
"code": "P"
} ]
}
}, {
"url": "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/StructureDefinition/mii-ex-onko-strahlentherapie-bestrahlung",
"extension": [ {
"url": "Applikationsart",
"valueCodeableConcept": {
"coding": [ {
"system": "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/CodeSystem/mii-cs-onko-strahlentherapie-applikationsart",
"code": "P-ST"
} ]
}
}, {
"url": "Strahlenart",
"valueCodeableConcept": {
"coding": [ {
"system": "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/CodeSystem/mii-cs-onko-strahlentherapie-strahlenart",
"code": "UH"
} ]
}
}, {
"url": "Zielgebiet",
"valueCodeableConcept": {
"coding": [ {
"system": "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/CodeSystem/mii-cs-onko-strahlentherapie-zielgebiet",
"code": "3.4"
} ]
}
} ]
} ],
"identifier": [ {
"system": "https://bzkf.github.io/obds-to-fhir/identifiers/strahlentherapie-procedure-id",
"value": "101_ST-2"
} ],
"status": "completed",
"category": {
"coding": [ {
"system": "http://snomed.info/sct",
"code": "277132007",
"display": "Therapeutic procedure"
} ]
},
"code": {
"coding": [ {
"system": "http://fhir.de/CodeSystem/bfarm/ops",
"_code": {
"extension": [ {
"url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason",
"valueCode": "unknown"
} ]
}
} ]
},
"subject": {
"reference": "Patient/any"
},
"performedPeriod": {
"_start": {
"extension": [ {
"url": "http://hl7.org/fhir/StructureDefinition/data-absent-reason",
"valueCode": "unknown"
} ]
}
}
}
Loading

0 comments on commit 4c51e3c

Please sign in to comment.