Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add initial SystemischeTherapieMedicationStatementMapper #170

Merged
merged 8 commits into from
Nov 25, 2024
Merged
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,14 @@ public static class FhirSystems {
private String miiCsOnkoStrahlentherapieZielgebiet;
private String strahlentherapieProcedureId;
private String systemischeTherapieProcedureId;
private String systemischeTherapieMedicationStatementId;
private String miiCsOnkoSystemischeTherapieArt;
private String miiCsOnkoSeitenlokalisation;
private String miiCsTherapieGrundEnde;
private String conditionVerStatus;
private String icdo3MorphologieOid;
private String atcBfarm;
private String atcWho;
}

@Data
Expand All @@ -100,6 +103,7 @@ public static class FhirProfiles {
private String miiPrOnkoDiagnosePrimaertumor;
private String miiPrOnkoStrahlentherapie;
private String miiPrOnkoSystemischeTherapie;
private String miiPrMedicationStatement;
}

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

import com.google.common.hash.Hashing;
import de.basisdatensatz.obds.v3.SYSTTyp;
import de.basisdatensatz.obds.v3.SYSTTyp.Meldeanlass;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Objects;
import org.apache.commons.lang3.Validate;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Enumerations.ResourceType;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.MedicationStatement;
import org.hl7.fhir.r4.model.Period;
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;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

@Service
public class SystemischeTherapieMedicationStatementMapper extends ObdsToFhirMapper {

private static final Logger LOG =
LoggerFactory.getLogger(SystemischeTherapieProcedureMapper.class);

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

public Bundle map(SYSTTyp syst, Reference patient, Reference procedure) {
Objects.requireNonNull(syst, "Systemtherapie must not be null");
Objects.requireNonNull(patient, "Reference to Patient must not be null");
Objects.requireNonNull(procedure, "Reference to Procedure must not be null");

Validate.notBlank(syst.getSYSTID(), "Required SYST_ID is unset");
Validate.isTrue(
Objects.equals(
patient.getReferenceElement().getResourceType(), ResourceType.PATIENT.toCode()),
"The subject reference should point to a Patient resource");
Validate.isTrue(
Objects.equals(
procedure.getReferenceElement().getResourceType(), ResourceType.PROCEDURE.toCode()),
"The subject reference should point to a Procedure resource");

var bundle = new Bundle();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Da wollte ich am Mittwoch nochmal drüber diskutieren ob Bundle als Rückgabewert oder List<> nützlicher ist - in #159 haben wir das auch. Aber soll jetzt nicht blocken

bundle.setType(Bundle.BundleType.TRANSACTION);

for (var substanz : syst.getMengeSubstanz().getSubstanz()) {
var systMedicationStatement = new MedicationStatement();
pcvolkmer marked this conversation as resolved.
Show resolved Hide resolved
systMedicationStatement
.getMeta()
.addProfile(fhirProperties.getProfiles().getMiiPrMedicationStatement());

if ((null != substanz.getATC() && StringUtils.hasText(substanz.getATC().getCode()))
|| StringUtils.hasText(substanz.getBezeichnung())) {
var substanzId = "";
if (null != substanz.getATC() && StringUtils.hasText(substanz.getATC().getCode())) {
substanzId = substanz.getATC().getCode();
var atcCoding =
new Coding(
fhirProperties.getSystems().getAtcBfarm(), substanz.getATC().getCode(), "");
systMedicationStatement.setMedication(new CodeableConcept(atcCoding));
} else {
substanzId = createSubstanzIdFromPlain(substanz.getBezeichnung());
systMedicationStatement.setMedication(
new CodeableConcept().setText(substanz.getBezeichnung()));
}

// TODO: can we be sure that this SYST-ID is globally unqiue across all SYSTs?
// if not we may instead need to construct the ID from the patient-id + others.
var identifier =
new Identifier()
.setSystem(
fhirProperties.getSystems().getSystemischeTherapieMedicationStatementId())
.setValue(String.format("%s_%s", syst.getSYSTID(), substanzId));
systMedicationStatement.addIdentifier(identifier);
systMedicationStatement.setId(computeResourceIdFromIdentifier(identifier));

// Status / Effective
var meldeanlass = syst.getMeldeanlass();
var period = new Period();
if (meldeanlass == Meldeanlass.BEHANDLUNGSENDE) {
systMedicationStatement.setStatus(
MedicationStatement.MedicationStatementStatus.COMPLETED);
convertObdsDatumToDateTimeType(syst.getBeginn())
.ifPresent(start -> period.setStart(start.getValue(), start.getPrecision()));
convertObdsDatumToDateTimeType(syst.getEnde())
.ifPresent(end -> period.setEnd(end.getValue(), end.getPrecision()));
} else {
systMedicationStatement.setStatus(MedicationStatement.MedicationStatementStatus.ACTIVE);
convertObdsDatumToDateTimeType(syst.getBeginn())
.ifPresent(start -> period.setStart(start.getValue(), start.getPrecision()));
}
systMedicationStatement.setEffective(period);

// Subject
systMedicationStatement.setSubject(patient);

// Part of
systMedicationStatement.setPartOf(List.of(procedure));

bundle = addResourceAsEntryInBundle(bundle, systMedicationStatement);
}
}

return bundle;
}

private String createSubstanzIdFromPlain(String plainName) {
Validate.notBlank(plainName, "Required substance name is unset");
return String.format(
"%s_%s",
plainName.replaceAll("[^A-Za-z0-9]+", ""),
Hashing.sha256().hashString(plainName, StandardCharsets.UTF_8).toString().substring(0, 4));
}
}
4 changes: 4 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ fhir:
psaObservationId: "${fhir.default.baseSystemUrl}/obds-to-fhir/identifiers/psa-observation-id"
strahlentherapie-procedure-id: "${fhir.default.baseSystemUrl}/obds-to-fhir/identifiers/strahlentherapie-procedure-id"
systemische-therapie-procedure-id: "${fhir.default.baseSystemUrl}/obds-to-fhir/identifiers/systemische-therapie-procedure-id"
systemische-therapie-medication-statement-id: "${fhir.default.baseSystemUrl}/obds-to-fhir/identifiers/systemische-therapie-medication-statement-id"
loinc: "http://loinc.org"
icdo3Morphologie: "http://terminology.hl7.org/CodeSystem/icd-o-3"
icdo3MorphologieOid: "urn:oid:2.16.840.1.113883.6.43.1"
Expand Down Expand Up @@ -69,6 +70,8 @@ fhir:
mii-cs-onko-seitenlokalisation: "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/CodeSystem/mii-cs-onko-seitenlokalisation"
mii-cs-onko-systemische-therapie-art: "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/CodeSystem/mii-cs-onko-therapie-typ"
mii-cs-therapie-grund-ende: "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/CodeSystem/mii-cs-therapie-grund-ende"
atcBfarm: "http://fhir.de/CodeSystem/bfarm/atc"
atcWho: "http://www.whocc.no/atc"
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 @@ -86,6 +89,7 @@ fhir:
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"
mii-pr-onko-systemische-therapie: "https://www.medizininformatik-initiative.de/fhir/ext/modul-onko/StructureDefinition/mii-pr-onko-systemische-therapie"
mii-pr-medication-statement: "https://www.medizininformatik-initiative.de/fhir/core/modul-medikation/StructureDefinition/MedicationStatement"
chgl marked this conversation as resolved.
Show resolved Hide resolved
display:
histologyLoinc: "Histology and Behavior ICD-O-3 Cancer"
gradingLoinc: "Grade pathology value Cancer"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
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 SystemischeTherapieMedicationStatementMapperTest {
private static SystemischeTherapieMedicationStatementMapper sut;

@BeforeAll
static void beforeAll(@Autowired FhirProperties fhirProps) {
sut = new SystemischeTherapieMedicationStatementMapper(fhirProps);
}

@ParameterizedTest
@CsvSource({"Testpatient_1.xml", "Testpatient_2.xml", "Testpatient_3.xml"})
void map_withGivenObds_shouldCreateValidMedicationStatement(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 patient = new Reference("Patient/any");
var procedure = new Reference("Procedure/any");
var systMeldung =
obdsPatient.getMengeMeldung().getMeldung().stream()
.filter(m -> m.getSYST() != null)
.findFirst()
.get();
var bundle = sut.map(systMeldung.getSYST(), patient, procedure);

var fhirParser = FhirContext.forR4().newJsonParser().setPrettyPrint(true);
var fhirJson = fhirParser.encodeResourceToString(bundle);

Approvals.verify(
fhirJson, Approvals.NAMES.withParameters(sourceFile).forFile().withExtension(".fhir.json"));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"resourceType": "Bundle",
"type": "transaction",
"entry": [ {
"fullUrl": "MedicationStatement/050a031e5a9043c28c565c6b60550c3d34cd059bc2f24c195cdb807f099cd978",
"resource": {
"resourceType": "MedicationStatement",
"id": "050a031e5a9043c28c565c6b60550c3d34cd059bc2f24c195cdb807f099cd978",
"meta": {
"profile": [ "https://www.medizininformatik-initiative.de/fhir/core/modul-medikation/StructureDefinition/MedicationStatement" ]
},
"identifier": [ {
"system": "https://bzkf.github.io/obds-to-fhir/identifiers/systemische-therapie-medication-statement-id",
"value": "101_IN-1_L01FF05"
} ],
"partOf": [ {
"reference": "Procedure/any"
} ],
"status": "completed",
"medicationCodeableConcept": {
"coding": [ {
"system": "http://fhir.de/CodeSystem/bfarm/atc",
"code": "L01FF05"
} ]
},
"subject": {
"reference": "Patient/any"
},
"effectivePeriod": {
"start": "2020-05",
"end": "2020-10-15"
}
},
"request": {
"method": "PUT",
"url": "MedicationStatement/050a031e5a9043c28c565c6b60550c3d34cd059bc2f24c195cdb807f099cd978"
}
} ]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"resourceType": "Bundle",
"type": "transaction",
"entry": [ {
"fullUrl": "MedicationStatement/e4b35c4756c9668e9c2b92f281667382020c401482e0a674566a695cd20a4cfa",
"resource": {
"resourceType": "MedicationStatement",
"id": "e4b35c4756c9668e9c2b92f281667382020c401482e0a674566a695cd20a4cfa",
"meta": {
"profile": [ "https://www.medizininformatik-initiative.de/fhir/core/modul-medikation/StructureDefinition/MedicationStatement" ]
},
"identifier": [ {
"system": "https://bzkf.github.io/obds-to-fhir/identifiers/systemische-therapie-medication-statement-id",
"value": "11_1_Innere.1_Bicalutamid_fa96"
} ],
"partOf": [ {
"reference": "Procedure/any"
} ],
"status": "active",
"medicationCodeableConcept": {
"text": "Bicalutamid"
},
"subject": {
"reference": "Patient/any"
},
"effectivePeriod": {
"start": "2021-11-08"
}
},
"request": {
"method": "PUT",
"url": "MedicationStatement/e4b35c4756c9668e9c2b92f281667382020c401482e0a674566a695cd20a4cfa"
}
} ]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"resourceType": "Bundle",
"type": "transaction",
"entry": [ {
"fullUrl": "MedicationStatement/da9d78a5d066bba6acd32a9a903c8663ecc1421cb373dcb51566b6d23dd30df7",
"resource": {
"resourceType": "MedicationStatement",
"id": "da9d78a5d066bba6acd32a9a903c8663ecc1421cb373dcb51566b6d23dd30df7",
"meta": {
"profile": [ "https://www.medizininformatik-initiative.de/fhir/core/modul-medikation/StructureDefinition/MedicationStatement" ]
},
"identifier": [ {
"system": "https://bzkf.github.io/obds-to-fhir/identifiers/systemische-therapie-medication-statement-id",
"value": "12_1_Innere.1_Paclitaxel_a0a4"
} ],
"partOf": [ {
"reference": "Procedure/any"
} ],
"status": "active",
"medicationCodeableConcept": {
"text": "Paclitaxel"
},
"subject": {
"reference": "Patient/any"
},
"effectivePeriod": {
"start": "2018-03-17"
}
},
"request": {
"method": "PUT",
"url": "MedicationStatement/da9d78a5d066bba6acd32a9a903c8663ecc1421cb373dcb51566b6d23dd30df7"
}
} ]
}
Loading