Skip to content

Commit

Permalink
feat: add initial SystemischeTherapieMedicationStatementMapper (#170)
Browse files Browse the repository at this point in the history
* feat: add initial SystemischeTherapieMedicationStatementMapper

* add medication and bundle type

* add missing subject, status and effective

* change property "mii-pr-medication-statement"

Co-authored-by: chgl <[email protected]>

* set "partOf"

* extract and change substance id generation from plain name

* Revert "change property "mii-pr-medication-statement""

This reverts commit 71b87df.

* use custom system for medical statement id

---------

Co-authored-by: chgl <[email protected]>
  • Loading branch information
pcvolkmer and chgl authored Nov 25, 2024
1 parent d41f569 commit 5ce8b80
Show file tree
Hide file tree
Showing 7 changed files with 304 additions and 0 deletions.
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();
bundle.setType(Bundle.BundleType.TRANSACTION);

for (var substanz : syst.getMengeSubstanz().getSubstanz()) {
var systMedicationStatement = new MedicationStatement();
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"
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"
}
} ]
}

0 comments on commit 5ce8b80

Please sign in to comment.