diff --git a/src/main/groovy/projects/cxx/napkon/BER/observation.groovy b/src/main/groovy/projects/cxx/napkon/BER/observation.groovy new file mode 100644 index 00000000..b510414f --- /dev/null +++ b/src/main/groovy/projects/cxx/napkon/BER/observation.groovy @@ -0,0 +1,194 @@ +package projects.cxx + +import de.kairos.fhir.centraxx.metamodel.CatalogEntry +import de.kairos.fhir.centraxx.metamodel.IdContainer +import de.kairos.fhir.centraxx.metamodel.IdContainerType +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.SampleIdContainer +import de.kairos.fhir.centraxx.metamodel.Unity +import de.kairos.fhir.centraxx.metamodel.UsageEntry +import de.kairos.fhir.centraxx.metamodel.enums.LaborMappingType +import de.kairos.fhir.centraxx.metamodel.enums.SampleCategory +import org.hl7.fhir.r4.model.Observation + +import static de.kairos.fhir.centraxx.metamodel.LaborFindingLaborValue.LABOR_VALUE +import static de.kairos.fhir.centraxx.metamodel.RecordedValue.BOOLEAN_VALUE +import static de.kairos.fhir.centraxx.metamodel.RecordedValue.DATE_VALUE +import static de.kairos.fhir.centraxx.metamodel.RecordedValue.NUMERIC_VALUE +import static de.kairos.fhir.centraxx.metamodel.RecordedValue.STRING_VALUE +import static de.kairos.fhir.centraxx.metamodel.RecordedValue.TIME_VALUE +import static de.kairos.fhir.centraxx.metamodel.RecordedValue.CATALOG_ENTRY_VALUE +import static de.kairos.fhir.centraxx.metamodel.RootEntities.laborMapping +import static de.kairos.fhir.centraxx.metamodel.RootEntities.sample + +/** + * Represented by a CXX LaborMapping + * @author Jonas Küttner, Mike Wähnert + * @since v.1.8.0, CXX.v.3.8.1.1 + * + * Script to extract measurement results that contain only simple data types and single / multiple selections from value lists. + * Based on the assumption that the measurement profiles (LaborMethods), measurement parameters (LaborValues) and the associated value lists are + * defined with the same codes in both CXX instances. In this case, only one mapping to the oid of the value list in the target system is required + * for the import. + */ +observation { + if (!((LaborMappingType.SAMPLELABORMAPPING == context.source[laborMapping().mappingType()] as LaborMappingType) && (["DZHKFLAB"].contains(context.source[laborMapping().laborFinding().laborMethod().code()])))) { + return + } + + + final def extSampleId = context.source[laborMapping().sample().idContainer()]?.find { final def entry -> + "NAPKONProbenID" == entry[SampleIdContainer.ID_CONTAINER_TYPE]?.getAt(IdContainerType.CODE) + } + + + final SampleCategory category = context.source[sample().sampleCategory()] as SampleCategory + + + // Filter mastersamples of HEPFIX and its familly - in test was _BB + if ((category == SampleCategory.MASTER && !extSampleId) || (extSampleId && (extSampleId?.getAt(SampleIdContainer.PSN) as String).contains("_BB"))) { + return + } + + + String napkonId = extSampleId?.getAt(SampleIdContainer.PSN) as String + if (napkonId && napkonId.length() == 20) { + napkonId = napkonId.substring(0, 10) + } + + + // Filter HEPFIX mastersample childs + if (context.source[sample().sampleType().code()] == "HEPARINFIXATED") { + return + } + + + id = "Observation/" + context.source[laborMapping().id()] + + + status = Observation.ObservationStatus.UNKNOWN + + + code { + coding { + system = "urn:centraxx" + code = context.source[laborMapping().laborFinding().shortName()] as String + } + } + + + final def patIdContainer = context.source[laborMapping().relatedPatient().idContainer()]?.find { + "NAPKONPATID" == it[IdContainer.ID_CONTAINER_TYPE]?.getAt(IdContainerType.CODE) + } + + if (patIdContainer) { + subject { + identifier { + value = patIdContainer[IdContainer.PSN] + type { + coding { + system = "urn:centraxx" + code = "LIMSPSN" + // patIdContainer[IdContainer.ID_CONTAINER_TYPE]?.getAt(IdContainerType.CODE) as String + } + } + } + } + } + + if (context.source[laborMapping().sample()] != null) { + // Reference by identifier SampleId, because parent MasterSample might already exists in the target system + // The napkon sample id of BB is provided as sample id to DZHK. + if (extSampleId) { + specimen { + identifier { + type { + coding { + code = "SAMPLEID" + } + } + value = napkonId + } + } + } + } + + + effectiveDateTime = context.source[laborMapping().laborFinding().findingDate().date()] + + + method { + coding { + system = "urn:centraxx" + version = context.source[laborMapping().laborFinding().laborMethod().version()] + code = context.source[laborMapping().laborFinding().laborMethod().code()] as String + } + } + + + context.source[laborMapping().laborFinding().laborFindingLaborValues()].each { final def labFinLabVal -> + component { + code { + coding { + system = "urn:centraxx" + code = labFinLabVal[LABOR_VALUE][LaborValue.CODE] as String + } + } + if (labFinLabVal[NUMERIC_VALUE]) { + valueQuantity { + value = labFinLabVal[NUMERIC_VALUE] + unit = labFinLabVal[LABOR_VALUE]?.getAt(LaborValueNumeric.UNIT)?.getAt(Unity.CODE) as String + } + } + + if (labFinLabVal[STRING_VALUE]) { + valueString(labFinLabVal[STRING_VALUE] as String) + } + + if (labFinLabVal[STRING_VALUE]) { + valueQuantity { + value = labFinLabVal[STRING_VALUE] + } + } + + if (labFinLabVal[DATE_VALUE]) { + valueDateTime { + date = labFinLabVal[DATE_VALUE]?.getAt(PrecisionDate.DATE) + } + } + + if (labFinLabVal[TIME_VALUE]) { + valueTime(labFinLabVal[TIME_VALUE] as String) + } + + if (labFinLabVal[BOOLEAN_VALUE]) { + valueBoolean(labFinLabVal[BOOLEAN_VALUE] as Boolean) + } + + if (labFinLabVal[CATALOG_ENTRY_VALUE]) { + valueCodeableConcept { + labFinLabVal[LaborFindingLaborValue.CATALOG_ENTRY_VALUE].each { final def entry -> + coding { + system = "urn:centraxx:CodeSystem/UsageEntry-" + entry[CatalogEntry.ID] + code = entry[CatalogEntry.CODE] as String + } + } + } + } + if (labFinLabVal[LaborFindingLaborValue.MULTI_VALUE]) { + valueCodeableConcept { + labFinLabVal[LaborFindingLaborValue.MULTI_VALUE].each { final def entry -> + coding { + system = "urn:centraxx:CodeSystem/UsageEntry-" + entry[UsageEntry.ID] + code = entry[UsageEntry.CODE] as String + } + } + } + } + } + } +} + diff --git a/src/main/groovy/projects/cxx/napkon/BER/specimen.groovy b/src/main/groovy/projects/cxx/napkon/BER/specimen.groovy new file mode 100644 index 00000000..e1a45bfb --- /dev/null +++ b/src/main/groovy/projects/cxx/napkon/BER/specimen.groovy @@ -0,0 +1,568 @@ +package projects.cxx + +import de.kairos.centraxx.fhir.r4.utils.FhirUrls +import de.kairos.fhir.centraxx.metamodel.IdContainer +import de.kairos.fhir.centraxx.metamodel.IdContainerType +import de.kairos.fhir.centraxx.metamodel.SampleIdContainer +import de.kairos.fhir.centraxx.metamodel.enums.SampleCategory +import de.kairos.fhir.centraxx.metamodel.enums.SampleKind + +import static de.kairos.fhir.centraxx.metamodel.RootEntities.sample +/** + * Represented by a CXX AbstractSample + * @author Jonas Küttner, Mike Wähnert + * @since v.1.8.0, CXX.v.3.8.1.1 + * + * The mapping transforms specimen from the BB Charité system to the DZHK Greifswald system. + * + */ +specimen { + + // 1. Filter sample category + final SampleCategory category = context.source[sample().sampleCategory()] as SampleCategory + boolean containsCategory = [SampleCategory.DERIVED, SampleCategory.MASTER, SampleCategory.ALIQUOTGROUP].contains(category) + if (!containsCategory) { + return + } + + + // 2. Filter OrgUnit (NAPKON(-HAP), NAPKON-POP) + String[] list = ["NAPKON", "NAPKON-POP"] + boolean containsOrgUnit = list.contains(context.source[sample().organisationUnit().code()] as String) || list.contains(context.source[sample().parent().organisationUnit().code()] as String) + + if (!containsOrgUnit) { + return + } + + + // 3. get sample Ids + final def externalSampleIdContainer = context.source[sample().idContainer()]?.find { final def entry -> + "NAPKONProbenID" == entry[SampleIdContainer.ID_CONTAINER_TYPE]?.getAt(IdContainerType.CODE) + } + final def derivedSampleIdContainer = context.source[sample().idContainer()]?.find { final def entry -> + "SAMPLEID" == entry[SampleIdContainer.ID_CONTAINER_TYPE]?.getAt(IdContainerType.CODE) + } + + + // 4a Filter HEPFIX master samples (emtpy napkon id or suffix _BB) + if ((category == SampleCategory.MASTER && !externalSampleIdContainer) || (externalSampleIdContainer && (externalSampleIdContainer?.getAt(SampleIdContainer.PSN) as String).contains("_BB"))) { + return + } + + + // 4b Filter HEPFIX for derived and aliquotgroups + if (context.source[sample().sampleType().code()] == "HEPARINFIXATED") { + return + } + + + // 5. Get pooled samples and set appropriate napkon sample id (left part) + String napkonId = externalSampleIdContainer?.getAt(SampleIdContainer.PSN) as String + if (napkonId && napkonId.length() == 20) { + napkonId = napkonId.substring(0, 10) + } + + + // set the fhir resource id + id = "Specimen/" + context.source[sample().id()] + + + // 5: mapping of the ids + // the sample id of the BB is provided as external sampled id to DZHK. + // The external sample id of BB is provided as sample id to DZHK + if (externalSampleIdContainer) { // must be master + if (napkonId) { + identifier { + type { + coding { + system = "urn:centraxx" + code = "SAMPLEID" + } + } + value = napkonId + } + } + if (derivedSampleIdContainer) { + identifier { + type { + coding { + system = "urn:centraxx" + code = "EXTSAMPLEID" + } + } + value = derivedSampleIdContainer?.getAt(SampleIdContainer.PSN) + } + } + } else if (derivedSampleIdContainer) // must be a derived sample + { + identifier { + type { + coding { + system = "urn:centraxx" + code = "SAMPLEID" + } + } + value = derivedSampleIdContainer?.getAt(SampleIdContainer.PSN) + } + } + + + //6: setting the repositiondate + if (context.source[sample().repositionDate()]) { + extension { + url = FhirUrls.Extension.Sample.REPOSITION_DATE + valueDateTime = context.source[sample().repositionDate().date()] + } + } + +// TODO this is only valid for aliquots - check available temperature and handle case if no temperature exists + //7: Standard location path + if (category == SampleCategory.DERIVED) { + String locPath = context.source[sample().sampleLocation().locationPath()] as String + if (context.source[sample().sampleLocation().temperature()] == -175.0) + locPath = "N2 Tank -196°C" + else if (context.source[sample().sampleLocation().temperature()] == -80.0) + locPath = "Ultra-Tiefkühlschrank -80°C" + + extension { + url = FhirUrls.Extension.Sample.SAMPLE_LOCATION + extension { + url = FhirUrls.Extension.Sample.SAMPLE_LOCATION_PATH + valueString = "NUM --> Berlin CVK --> " + "Testlager"// später: locPath // (Tiefkühler -20°C, Ultra-Tiefkühlschrank -80°C) + } + } + } + + + // mapping of ous + String OE = "" + if (context.source[sample().organisationUnit().code()] == "NAPKON") + OE = "NUM_BER_HAP" // "NUM_Berlin" + else if (context.source[sample().organisationUnit().code()] == "NAPKON-POP") + OE = "NUM_BER_POP" + + + //4: Organization unit attached to sample + extension { + url = FhirUrls.Extension.Sample.ORGANIZATION_UNIT + valueReference { + // by identifier + identifier { + value = OE + } + } + } + + + //5: storage temperature attached to sample + /* + if (context.source[sample().sampleLocation().temperature()]) { + extension { + url = "https://fhir.bbmri.de/StructureDefinition/StorageTemperature" + valueCodeableConcept { + coding { + system = "https://fhir.bbmri.de/CodeSystem/StorageTemperature" + code = toDzhkTemp(context) + } + } + } + } +*/ + + + // Mapping of the derival date + if (context.source[sample().derivalDate()]) { + extension { + url = FhirUrls.Extension.Sample.DERIVAL_DATE + valueDateTime = context.source[sample().derivalDate().date()] + } + } + + + // Set availability of sample + status = context.source[sample().restAmount().amount()] > 0 ? "available" : "unavailable" + + + // sampletype + type { + coding { + system = "urn:centraxx" + code = toDzhkType(context.source[sample().sampleType().code()]) + } + } + + + final def patIdContainer = context.source[sample().patientContainer().idContainer()]?.find { + "NAPKONPATID" == it[IdContainer.ID_CONTAINER_TYPE]?.getAt(IdContainerType.CODE) + } + + + if (patIdContainer) { + subject { + identifier { + value = patIdContainer[IdContainer.PSN] + type { + coding { + system = "urn:centraxx" + code = "LIMSPSN" // patIdContainer[IdContainer.ID_CONTAINER_TYPE]?.getAt(IdContainerType.CODE) + } + } + } + } + } + + + if (context.source[sample().parent()] != null) { + parent { + // Reference by identifier SampleId, because parent MasterSample might already exists in the target system + final def extSampleIdParent = context.source[sample().parent().idContainer()]?.find { final def entry -> + "NAPKONProbenID" == entry[SampleIdContainer.ID_CONTAINER_TYPE]?.getAt(IdContainerType.CODE) + } + if (SampleCategory.MASTER == context.source[sample().parent().sampleCategory()] as SampleCategory && extSampleIdParent) { + identifier { + type { + coding { + code = "SAMPLEID" + } + } + value = (extSampleIdParent[SampleIdContainer.PSN] as String).length() == 20 ? (extSampleIdParent[SampleIdContainer.PSN] as String).substring(0, 10) : (extSampleIdParent[SampleIdContainer.PSN] as String) + } + } + else { + reference = "Specimen/" + context.source[sample().parent().id()] + } + } + } + + // Eingangsdatum + // TODO receivedtime partly available, choose that or samplingdate, but not in case of HEP + if (context.source[sample().sampleType().code()] != "Heparin") { + receivedTime { + date = context.source[sample().receiptDate().date()] ? context.source[sample().receiptDate().date()] : context.source[sample().samplingDate().date()] + } + } + + + // Entnahmedatum + collection { + collectedDateTime { + date = context.source[sample().samplingDate().date()] + quantity { + value = context.source[sample().initialAmount().amount()] as Number + unit = context.source[sample().initialAmount().unit()] + system = "urn:centraxx" + } + } + } + + // Probenbehälter + container { + if (context.source[sample().receptable()]) { + identifier { + value = toDzhkContainer(context.source[sample().receptable().code()]) + system = "urn:centraxx" + } + + // info: not mapped because it would overwrite the capacity in destination system + /*capacity { + value = context.source[sample().receptable().size()] + unit = context.source[sample().restAmount().unit()] + system = "urn:centraxx" + }*/ + } + + specimenQuantity { + value = context.source[sample().restAmount().amount()] as Number + unit = context.source[sample().restAmount().unit()] + system = "urn:centraxx" + } + } + + + // Probenkategorie + extension { + url = FhirUrls.Extension.SAMPLE_CATEGORY + valueCoding { + system = "urn:centraxx" + code = context.source[sample().sampleCategory()] + } + } + + +// SPREC Extensions + extension { + url = FhirUrls.Extension.SPREC + extension { + url = FhirUrls.Extension.Sprec.USE_SPREC + valueBoolean = context.source[sample().useSprec()] + } + + // + // SPREC TISSUE + // + if (SampleKind.TISSUE == context.source[sample().sampleKind()] as SampleKind) { + if (context.source[sample().sprecTissueCollectionType()]) { + extension { + url = FhirUrls.Extension.Sprec.SPREC_TISSUE_COLLECTION_TYPE + valueCoding { + system = "urn:centraxx" + code = context.source[sample().sprecTissueCollectionType().code()] + } + } + } + if (context.source[sample().warmIschTime()]) { + extension { + url = FhirUrls.Extension.Sprec.WARM_ISCH_TIME + valueCoding { + system = "urn:centraxx" + code = context.source[sample().warmIschTime().code()] + } + } + } + if (context.source[sample().warmIschTimeDate()]) { + extension { + url = FhirUrls.Extension.Sprec.WARM_ISCH_TIME_DATE + valueDateTime = context.source[sample().warmIschTimeDate().date()] + } + } + if (context.source[sample().coldIschTime()]) { + extension { + url = FhirUrls.Extension.Sprec.COLD_ISCH_TIME + valueCoding { + system = "urn:centraxx" + code = context.source[sample().coldIschTime().code()] + } + } + } + if (context.source[sample().coldIschTimeDate()]) { + extension { + url = FhirUrls.Extension.Sprec.COLD_ISCH_TIME_DATE + valueDateTime = context.source[sample().coldIschTimeDate().date()] + } + } + if (context.source[sample().stockType()]) { + extension { + url = FhirUrls.Extension.Sprec.STOCK_TYPE + valueCoding { + system = "urn:centraxx" + code = context.source[sample().stockType().code()] + } + } + } + if (context.source[sample().sprecFixationTime()]) { + extension { + url = FhirUrls.Extension.Sprec.SPREC_FIXATION_TIME + valueCoding { + system = "urn:centraxx" + code = context.source[sample().sprecFixationTime().code()] + } + } + } + if (context.source[sample().sprecFixationTimeDate()]) { + extension { + url = FhirUrls.Extension.Sprec.SPREC_FIXATION_TIME_DATE + valueDateTime = context.source[sample().sprecFixationTimeDate().date()] + } + } + } + + // + // SPREC LIQUID + // + if (SampleKind.LIQUID == context.source[sample().sampleKind()] as SampleKind) { + if (context.source[sample().sprecPrimarySampleContainer()]) { + extension { + url = FhirUrls.Extension.Sprec.SPREC_PRIMARY_SAMPLE_CONTAINER + valueCoding { + system = "urn:centraxx" + code = toDzhkPrimaryContainer(context.source[sample().sprecPrimarySampleContainer().code()] as String) + } + } + } + if (context.source[sample().sprecPreCentrifugationDelay()]) { + extension { + url = FhirUrls.Extension.Sprec.SPREC_PRE_CENTRIFUGATION_DELAY + valueCoding { + system = "urn:centraxx" + code = context.source[sample().sprecPreCentrifugationDelay().code()] + } + } + } + if (context.source[sample().sprecPreCentrifugationDelayDate()]) { + extension { + url = FhirUrls.Extension.Sprec.SPREC_PRE_CENTRIFUGATION_DELAY_DATE + valueDateTime = context.source[sample().sprecPreCentrifugationDelayDate().date()] + } + } + if (context.source[sample().sprecPostCentrifugationDelay()]) { + extension { + url = FhirUrls.Extension.Sprec.SPREC_POST_CENTRIFUGATION_DELAY + valueCoding { + system = "urn:centraxx" + code = context.source[sample().sprecPostCentrifugationDelay().code()] + } + } + } + if (context.source[sample().sprecPostCentrifugationDelayDate()]) { + extension { + url = FhirUrls.Extension.Sprec.SPREC_POST_CENTRIFUGATION_DELAY_DATE + valueDateTime = context.source[sample().sprecPostCentrifugationDelayDate().date()] + } + } + if (context.source[sample().stockProcessing()]) { + extension { + url = FhirUrls.Extension.Sprec.STOCK_PROCESSING + valueCoding { + system = "urn:centraxx" + code = toDzhkProcessing(context.source[sample().stockProcessing().code()] as String) + } + } + } + if (context.source[sample().stockProcessingDate()]) { + extension { + url = FhirUrls.Extension.Sprec.STOCK_PROCESSING_DATE + valueDateTime = context.source[sample().stockProcessingDate().date()] + } + } + if (context.source[sample().secondProcessing()]) { + extension { + url = FhirUrls.Extension.Sprec.SECOND_PROCESSING + valueCoding { + system = "urn:centraxx" + code = toDzhkProcessing(context.source[sample().secondProcessing().code()] as String) + } + } + } + if (context.source[sample().secondProcessingDate()]) { + extension { + url = FhirUrls.Extension.Sprec.SECOND_PROCESSING_DATE + valueDateTime = context.source[sample().secondProcessingDate().date()] + } + } + } + } +} + + +// Mappings of the SampleType-Codes + static String toDzhkType(final Object sourceType) { + switch (sourceType) { + case "ZB": + return "DHZB_ZB" + case "URN": + return "URN" + case "URINSEDI": + return "NUM_urins" + case "SER": + return "SER" + case "SAL": + return "NUM_speichel" + case "NASO-A": + return "NUM_nasen-rachenabstrich" + case "ORO-A": + return "NUM_rachenabstrich" + case "Heparin": + return "NUM_pbmc_hep" + case ["EP", "EPPL1"]: + return "EDTA" + case ["CP", "Citrat"]: + return "CIT" + case "BLDRNASTABIL": // case ["SPT", "SPT(ind)"]: + return "NUM_pax" + case "EDTA": + return "EDTAWB" + + + // CTU processed + case "HEPAP": + return "NUM_heppl" + case "PBMC-l": + return "NUM_PBMC_C" + + default: + return sourceType + } + } + + +// Mapping of the stockProcessing codes. + static String toDzhkProcessing(final String sourceProcessing) { + switch (sourceProcessing) { + case "1000g10minob": + return "NUM_BEGINN_ZENT" + case "1000g30smb": + return "NUM_BEGINN_ZENT" + case "2000 g 15 min 18°C mB": + return "NUM_RT15min2000g" + case "2000 g 15 min 18C": + return "NUM_RT15min2000g" + case "400g10minrt": + return "NUM_BEGINN_ZENT" + default: return sourceProcessing + } + } + + +// Mappings for container codes (Probenbehälter) + static String toDzhkContainer(final Object sourceType) { + switch (sourceType) { + case ["CITRATVACUTAINER", "EDTAVACU", "eSwab", "ET 1.5", "ET2ml", "ORG", "PAXgeneRNA", "SALIVETTE", "SER", "TempusBlutRNATube", "UR"]: + return "ORG" + case "LVL-2D-Cryo-0.5-A": + return "NUMCryoAliquot500" + case "LVL-2D-Cryo-2.0-A": + return "NUMCryoAliquot2000" + default: return sourceType + } + } + + +// Mapping for primary container (SPREC) + static String toDzhkPrimaryContainer(final String sourceContainer) { + switch (sourceContainer) { + + case "ESWAB": + return "ZZZ" + case "HEP": + return "HEP" + case "ORG": + return "ORG" + case "PAX": + return "PAX" + case "PED": + return "PED" + case "SALIVETTE": + return "ORG" + case "SCI": + return "SCI" + case "SST": + return "SST" + case "TEM": + return "TEM" + case "URIN": + return "ZZZ" + default: return sourceContainer + } + } + +// Mapping for temperature +// https://samply.github.io/bbmri-fhir-ig/Specimen-0-specimen.json.html +// https://samply.github.io/bbmri-fhir-ig/ValueSet-StorageTemperature.html + + static String toDzhkTemp(final ctx) { + final def temp = ctx.source[sample().sampleLocation().temperature()] + + if (null != temp) { + switch (temp) { + case { it >= 2.0 && it <= 10 }: + return "temperature2to10" + case { it <= -18.0 && it >= -35.0 }: + return "temperature-18to-35" + case { it <= -60.0 && it >= -85.0 }: + return "temperature-60to-85" + default: return "temperatureOther" + } + } + return null + } + diff --git a/src/main/groovy/projects/cxx/napkon/BER/specimen_pooled_null.groovy b/src/main/groovy/projects/cxx/napkon/BER/specimen_pooled_null.groovy new file mode 100644 index 00000000..7cfdab04 --- /dev/null +++ b/src/main/groovy/projects/cxx/napkon/BER/specimen_pooled_null.groovy @@ -0,0 +1,87 @@ +package projects.cxx + + +import de.kairos.fhir.centraxx.metamodel.IdContainerType +import de.kairos.fhir.centraxx.metamodel.SampleIdContainer +import de.kairos.fhir.centraxx.metamodel.enums.SampleCategory + +import static de.kairos.fhir.centraxx.metamodel.RootEntities.sample +/** + * Represented by a CXX AbstractSample + * @author Jonas Küttner, Mike Wähnert + * @since v.1.8.0, CXX.v.3.8.1.1 + * + * The mapping transforms pooled specimen (that are set to rest amount 0) from the BB Charité system to the DZHK Greifswald system. + * + */ +specimen { + + // 1. Filter sample category + final SampleCategory category = context.source[sample().sampleCategory()] as SampleCategory + boolean containsCategory = [SampleCategory.MASTER].contains(category) + if (!containsCategory) { + return + } + + + // 2. Filter OrgUnit (NAPKON(-HAP), NAPKON-POP) + String[] list = ["NAPKON", "NAPKON-POP"] + boolean containsOrgUnit = list.contains(context.source[sample().organisationUnit().code()] as String) + + if (!containsOrgUnit) { + return + } + + + // 3. get pooled samples and set appropriate napkon sample id containing 20 numbers + final def sampleIdContainer = context.source[sample().idContainer()]?.find { final def entry -> + "NAPKONProbenID" == entry[SampleIdContainer.ID_CONTAINER_TYPE]?.getAt(IdContainerType.CODE) + } + + + // 4. Filter mastersamples of HEPFIX + if (!sampleIdContainer || (sampleIdContainer?.getAt(SampleIdContainer.PSN) as String).contains("_BB")) { + return + } + + + // get Id + String napkonId = sampleIdContainer?.getAt(SampleIdContainer.PSN) as String + if (napkonId && napkonId.length() == 20) { + id = "Specimen/" + context.source[sample().id()] + "0" // to ref a diff sample than first part / sample before + + // ref sample id + identifier { + type { + coding { + system = "urn:centraxx" + code = "SAMPLEID" + } + } + value = napkonId.substring(10, 20) // the right part of the id + } + + + // change rest amount + container { + specimenQuantity { + value = 0 + unit = "ML" // can be UNKNOWN but is no option here --context.source[sample().restAmount().unit()] + system = "urn:centraxx" + } + } + + // change sample location -no! Cxx will do that for us? + /* + extension { + url = FhirUrls.Extension.Sample.SAMPLE_LOCATION + extension { + url = FhirUrls.Extension.Sample.SAMPLE_LOCATION_PATH + valueString = "" + } + } + */ + // change availabitlity + status = "unavailable" + } +} \ No newline at end of file