From fefd07613742ee9b8d9f4464d2d6a6cbc3ab46a8 Mon Sep 17 00:00:00 2001 From: davidmscholz Date: Thu, 1 Aug 2024 09:44:13 +0200 Subject: [PATCH 1/2] add export script for siop MetPredict --- .../dktk/v2/ExportResourceMappingConfig.json | 5 + .../projects/dktk/v2/siopObservation.groovy | 212 ++++++++++++++++++ 2 files changed, 217 insertions(+) create mode 100644 src/main/groovy/projects/dktk/v2/siopObservation.groovy diff --git a/src/main/groovy/projects/dktk/v2/ExportResourceMappingConfig.json b/src/main/groovy/projects/dktk/v2/ExportResourceMappingConfig.json index d83e578a..6051b7e0 100644 --- a/src/main/groovy/projects/dktk/v2/ExportResourceMappingConfig.json +++ b/src/main/groovy/projects/dktk/v2/ExportResourceMappingConfig.json @@ -110,6 +110,11 @@ "selectFromCxxEntity": "DIAGNOSIS", "transformByTemplate": "todUrsache", "exportToFhirResource": "Observation" + }, + { + "selectFromCxxEntity": "LABOR_MAPPING", + "transformByTemplate": "siopObservation", + "exportToFhirResource": "Observation" } ] } \ No newline at end of file diff --git a/src/main/groovy/projects/dktk/v2/siopObservation.groovy b/src/main/groovy/projects/dktk/v2/siopObservation.groovy new file mode 100644 index 00000000..dea94c5b --- /dev/null +++ b/src/main/groovy/projects/dktk/v2/siopObservation.groovy @@ -0,0 +1,212 @@ +package projects.dktk.v2 + +import ca.uhn.fhir.model.api.TemporalPrecisionEnum +import de.kairos.centraxx.fhir.r4.utils.FhirUrls +import de.kairos.fhir.centraxx.metamodel.AbstractCatalog +import de.kairos.fhir.centraxx.metamodel.CatalogEntry +import de.kairos.fhir.centraxx.metamodel.CrfTemplateField +import de.kairos.fhir.centraxx.metamodel.IcdEntry +import de.kairos.fhir.centraxx.metamodel.LaborFindingLaborValue +import de.kairos.fhir.centraxx.metamodel.LaborValue +import de.kairos.fhir.centraxx.metamodel.LaborValueNumeric +import de.kairos.fhir.centraxx.metamodel.PrecisionDate +import de.kairos.fhir.centraxx.metamodel.ValueReference +import de.kairos.fhir.centraxx.metamodel.enums.LaborValueDType +import org.hl7.fhir.r4.model.Observation + +import static de.kairos.fhir.centraxx.metamodel.AbstractCode.CODE +import static de.kairos.fhir.centraxx.metamodel.AbstractCodeName.NAME_MULTILINGUAL_ENTRIES +import static de.kairos.fhir.centraxx.metamodel.AbstractIdContainer.ID_CONTAINER_TYPE +import static de.kairos.fhir.centraxx.metamodel.AbstractIdContainer.PSN +import static de.kairos.fhir.centraxx.metamodel.MultilingualEntry.LANG +import static de.kairos.fhir.centraxx.metamodel.MultilingualEntry.VALUE +import static de.kairos.fhir.centraxx.metamodel.RootEntities.laborMapping + +/** + * Represented by a CXX LaborMapping + * @author Mike Wähnert (adapted by David Scholz) + * @since kairos-fhir-dsl.v.1.12.0, CXX.v.3.18.1.19, CXX.v.3.18.2 + * The first code of each component represents the LaborValue.Code in CXX. Further codes could be representations in LOINC, SNOMED-CT etc. + * LaborValueIdContainer in CXX are just an export example, but not intended to be imported by CXX FHIR API yet. + */ +observation { + + if (!context.source[laborMapping().laborFinding().laborMethod().code()].startsWith("PROFILE_SIOP")) { + return + } + + id = "Observation/" + context.source[laborMapping().laborFinding().id()] + + status = Observation.ObservationStatus.UNKNOWN + + code { + coding { + system = FhirUrls.System.Finding.LABOR_FINDING_SHORTNAME + code = context.source[laborMapping().laborFinding().shortName()] as String + } + } + + effectiveDateTime { + date = context.source[laborMapping().laborFinding().findingDate().date()] + precision = TemporalPrecisionEnum.DAY.name() + } + + subject { + reference = "Patient/" + context.source[laborMapping().relatedPatient().id()] + } + + method { + coding { + system = FhirUrls.System.LaborMethod.BASE_URL + version = context.source[laborMapping().laborFinding().laborMethod().version()] + code = context.source[laborMapping().laborFinding().laborMethod().code()] as String + } + } + + context.source[laborMapping().laborFinding().laborFindingLaborValues()].each { final lflv -> + + final def laborValue = lflv[LaborFindingLaborValue.LABOR_VALUE] != null + ? lflv[LaborFindingLaborValue.LABOR_VALUE] // before CXX.v.2022.3.0 + : lflv[LaborFindingLaborValue.CRF_TEMPLATE_FIELD][CrfTemplateField.LABOR_VALUE] // from CXX.v.2022.3.0 + + final String laborValueCode = laborValue?.getAt(CODE) as String + final String laborValueDisplay = laborValue?.getAt(NAME_MULTILINGUAL_ENTRIES)?.find { final mle -> mle[LANG] == "en" }?.getAt(VALUE) as String + + component { + code { + coding { + system = FhirUrls.System.LaborValue.BASE_URL + code = laborValueCode + display = laborValueDisplay + } + laborValue?.getAt(LaborValue.IDCONTAINERS)?.each { final idContainer -> + coding { + system = idContainer[ID_CONTAINER_TYPE]?.getAt(CODE) + code = idContainer[PSN] as String + } + } + } + + if (isNumeric(laborValue)) { + valueQuantity { + value = lflv[LaborFindingLaborValue.NUMERIC_VALUE] + unit = laborValue?.getAt(LaborValueNumeric.UNIT)?.getAt(CODE) as String + } + } else if (isBoolean(laborValue)) { + valueBoolean(lflv[LaborFindingLaborValue.BOOLEAN_VALUE] as Boolean) + } else if (isDate(laborValue)) { + valueDateTime { + date = lflv[LaborFindingLaborValue.DATE_VALUE]?.getAt(PrecisionDate.DATE) + } + } else if (isTime(laborValue)) { + valueTime(lflv[LaborFindingLaborValue.TIME_VALUE] as String) + } else if (isString(laborValue)) { + valueString(lflv[LaborFindingLaborValue.STRING_VALUE] as String) + } else if (isEnumeration(laborValue)) { + valueCodeableConcept { + lflv[LaborFindingLaborValue.MULTI_VALUE].each { final entry -> + coding { + system = "urn:centraxx:CodeSystem/UsageEntry" + code = entry[CODE] as String + } + } + lflv[LaborFindingLaborValue.CATALOG_ENTRY_VALUE].each { final entry -> + coding { + system = "urn:centraxx:CodeSystem/ValueList-" + entry[CatalogEntry.CATALOG]?.getAt(AbstractCatalog.ID) + code = entry[CODE] as String + } + } + } + } else if (isOptionGroup(laborValue)) { + valueCodeableConcept { + lflv[LaborFindingLaborValue.MULTI_VALUE].each { final entry -> + coding { + system = "urn:centraxx:CodeSystem/UsageEntry" + code = entry[CODE] as String + } + } + lflv[LaborFindingLaborValue.CATALOG_ENTRY_VALUE].each { final entry -> + coding { + system = "urn:centraxx:CodeSystem/ValueList-" + entry[CatalogEntry.CATALOG]?.getAt(AbstractCatalog.ID) + code = entry[CODE] as String + } + } + } + } else if (isCatalog(laborValue)) { + valueCodeableConcept { + lflv[LaborFindingLaborValue.CATALOG_ENTRY_VALUE].each { final entry -> + coding { + system = "urn:centraxx:CodeSystem/ValueList-" + entry[CatalogEntry.CATALOG]?.getAt(AbstractCatalog.ID) + code = entry[CODE] as String + } + } + lflv[LaborFindingLaborValue.ICD_ENTRY_VALUE].each { final entry -> + coding { + system = "urn:centraxx:CodeSystem/IcdCatalog-" + entry[IcdEntry.CATALOGUE]?.getAt(AbstractCatalog.ID) + code = entry[CODE] as String + } + } + // example for master data catalog entries of blood group + lflv[LaborFindingLaborValue.MULTI_VALUE_REFERENCES].each { final entry -> + final def bloodGroup = entry[ValueReference.BLOOD_GROUP_VALUE] + if (bloodGroup != null) { + coding { + system = FhirUrls.System.Patient.BloodGroup.BASE_URL + code = bloodGroup?.getAt(CODE) as String + } + } + + // example for master data catalog entries of attending doctor + final def attendingDoctor = entry[ValueReference.ATTENDING_DOCTOR_VALUE] + if (attendingDoctor != null) { + coding { + system = FhirUrls.System.AttendingDoctor.BASE_URL + // CXX uses the reference embedded in a coding to support multi selects + code = "Practitioner/" + attendingDoctor?.getAt(AbstractCatalog.ID) as String + } + } + } + } + } else { + final String msg = laborValue?.getAt(LaborValue.D_TYPE) + " not implemented yet." + System.out.println(msg) + } + } + } +} + +static boolean isDTypeOf(final Object laborValue, final List types) { + return types.contains(laborValue?.getAt(LaborValue.D_TYPE) as LaborValueDType) +} + +static boolean isBoolean(final Object laborValue) { + return isDTypeOf(laborValue, [LaborValueDType.BOOLEAN]) +} + +static boolean isNumeric(final Object laborValue) { + return isDTypeOf(laborValue, [LaborValueDType.INTEGER, LaborValueDType.DECIMAL, LaborValueDType.SLIDER]) +} + +static boolean isDate(final Object laborValue) { + return isDTypeOf(laborValue, [LaborValueDType.DATE, LaborValueDType.LONGDATE]) +} + +static boolean isTime(final Object laborValue) { + return isDTypeOf(laborValue, [LaborValueDType.TIME]) +} + +static boolean isEnumeration(final Object laborValue) { + return isDTypeOf(laborValue, [LaborValueDType.ENUMERATION]) +} + +static boolean isString(final Object laborValue) { + return isDTypeOf(laborValue, [LaborValueDType.STRING, LaborValueDType.LONGSTRING]) +} + +static boolean isCatalog(final Object laborValue) { + return isDTypeOf(laborValue, [LaborValueDType.CATALOG]) +} + +static boolean isOptionGroup(final Object laborValue) { + return isDTypeOf(laborValue, [LaborValueDType.OPTIONGROUP]) +} From 9bde465c791d41bdfe28623d8d7000e8e9515f90 Mon Sep 17 00:00:00 2001 From: davidmscholz Date: Wed, 14 Aug 2024 09:28:20 +0200 Subject: [PATCH 2/2] add cast to string --- src/main/groovy/projects/dktk/v2/siopObservation.groovy | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/groovy/projects/dktk/v2/siopObservation.groovy b/src/main/groovy/projects/dktk/v2/siopObservation.groovy index dea94c5b..35be278a 100644 --- a/src/main/groovy/projects/dktk/v2/siopObservation.groovy +++ b/src/main/groovy/projects/dktk/v2/siopObservation.groovy @@ -31,8 +31,9 @@ import static de.kairos.fhir.centraxx.metamodel.RootEntities.laborMapping */ observation { - if (!context.source[laborMapping().laborFinding().laborMethod().code()].startsWith("PROFILE_SIOP")) { - return + def laborMethodCode = context.source[laborMapping().laborFinding().laborMethod().code()] + if (laborMethodCode == null || !(laborMethodCode as String).startsWith("PROFILE_SIOP")) { + return } id = "Observation/" + context.source[laborMapping().laborFinding().id()]