From 6af1f3f0a7ab70657f226ed8de8b33037287f239 Mon Sep 17 00:00:00 2001 From: Joel Biskie Date: Mon, 30 Sep 2024 14:50:53 -0500 Subject: [PATCH 01/14] Roll up commits from previous branch --- .../custom/MapLocalObservationCodes.java | 8 + .../custom/RemoveAccessionNumber.java | 59 ++++++ .../custom/RemoveAccessionNumberTest.groovy | 193 ++++++++++++++++++ 3 files changed, 260 insertions(+) create mode 100644 etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveAccessionNumber.java create mode 100644 etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveAccessionNumberTest.groovy diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/MapLocalObservationCodes.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/MapLocalObservationCodes.java index da9a09f74..81d531cba 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/MapLocalObservationCodes.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/MapLocalObservationCodes.java @@ -60,6 +60,8 @@ public void transform(FhirResource resource, Map args) { } } + // @todo This will be moved to HapiHelper and turned into a more generic function for searching + // inside CWE coding objects private Boolean hasLocalCodeInAlternateCoding(Coding coding) { if (!HapiHelper.hasCodingExtensionWithUrl(coding, HapiHelper.EXTENSION_CWE_CODING)) { return false; @@ -103,6 +105,12 @@ private Coding getMappedCoding(IdentifierCode identifierCode) { return mappedCoding; } + /** + * Initializes the local-to-LOINC/PLT hash map, customized for CDPH and UCSD. Currently, the + * mapping is hardcoded for simplicity. If expanded to support additional entities, the + * implementation may be updated to allow dynamic configuration via + * transformation_definitions.json or a database-driven mapping. + */ private void initMap() { this.codingMap = new HashMap<>(); // ALD diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveAccessionNumber.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveAccessionNumber.java new file mode 100644 index 000000000..780fd0302 --- /dev/null +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveAccessionNumber.java @@ -0,0 +1,59 @@ +package gov.hhs.cdc.trustedintermediary.etor.ruleengine.transformation.custom; + +import gov.hhs.cdc.trustedintermediary.etor.ruleengine.FhirResource; +import gov.hhs.cdc.trustedintermediary.etor.ruleengine.transformation.CustomFhirTransformation; +import gov.hhs.cdc.trustedintermediary.external.hapi.HapiHelper; +import java.util.HashSet; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Coding; +import org.hl7.fhir.r4.model.Observation; +import org.hl7.fhir.r4.model.Resource; + +public class RemoveAccessionNumber implements CustomFhirTransformation { + public static final String CODE_NAME = "code"; + public static final String CODING_SYSTEM_NAME = "codingSystemExtension"; + public static final String CODING_NAME = "codingExtension"; + + @Override + public void transform(FhirResource resource, Map args) { + var bundle = (Bundle) resource.getUnderlyingResource(); + Set resourcesToRemove = new HashSet<>(); + + for (Bundle.BundleEntryComponent entry : bundle.getEntry()) { + Resource resourceEntry = entry.getResource(); + + if (resourceEntry instanceof Observation observation) { + processObservation(observation, resourcesToRemove, args); + } + } + + bundle.getEntry().removeIf(entry -> resourcesToRemove.contains(entry.getResource())); + } + + private void processObservation( + Observation observation, Set resourcesToRemove, Map args) { + for (Coding coding : observation.getCode().getCoding()) { + if (isMatchingCode(coding, args)) { + resourcesToRemove.add(observation); + break; // No need to continue once a match is found + } + } + } + + // TODO: Need to refactor this to handle missing extensions, etc. and determine if there's a way + // to generalize it along with HapiHelper.hasLocalCodeInAlternateCoding + private Boolean isMatchingCode(Coding coding, Map args) { + return Objects.equals(coding.getCode(), args.get(CODE_NAME)) + && coding.getExtensionByUrl(HapiHelper.EXTENSION_CODING_SYSTEM) + .getValue() + .toString() + .equals(args.get(CODING_SYSTEM_NAME)) + && coding.getExtensionByUrl(HapiHelper.EXTENSION_CWE_CODING) + .getValue() + .toString() + .equals(args.get(CODING_NAME)); + } +} diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveAccessionNumberTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveAccessionNumberTest.groovy new file mode 100644 index 000000000..066c9dd91 --- /dev/null +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveAccessionNumberTest.groovy @@ -0,0 +1,193 @@ +package gov.hhs.cdc.trustedintermediary.etor.ruleengine.transformation.custom + +import gov.hhs.cdc.trustedintermediary.ExamplesHelper +import gov.hhs.cdc.trustedintermediary.context.TestApplicationContext +import gov.hhs.cdc.trustedintermediary.external.hapi.HapiFhirHelper +import gov.hhs.cdc.trustedintermediary.external.hapi.HapiFhirResource +import gov.hhs.cdc.trustedintermediary.external.hapi.HapiHelper +import org.hl7.fhir.r4.model.Bundle +import org.hl7.fhir.r4.model.Coding +import org.hl7.fhir.r4.model.Observation +import org.hl7.fhir.r4.model.StringType +import spock.lang.Specification + +class RemoveAccessionNumberTest extends Specification { + def transformClass + + def setup() { + TestApplicationContext.reset() + TestApplicationContext.init() + TestApplicationContext.injectRegisteredImplementations() + + transformClass = new RemoveAccessionNumber() + } + + def "When an observation has the desired coding, it should be removed"() { + given: + def bundle = HapiFhirHelper.createMessageBundle(messageTypeCode: 'ORU_R01') + def observation = new Observation() + addCodingToObservation(observation, code, codingSystemExt, codingExt) + bundle.addEntry(new Bundle.BundleEntryComponent().setResource(observation)) + + def args = getArgs(code, codingSystemExt, codingExt) + + expect: + HapiHelper.resourcesInBundle(bundle, Observation.class).count() == 1 + + when: + transformClass.transform(new HapiFhirResource(bundle), args) + + then: + HapiHelper.resourcesInBundle(bundle, Observation.class).count() == 0 + + where: + code | codingSystemExt | codingExt + "99717-5" | "L" | "alt-coding" + "my_code" | "MY_SYS" | "coding" + } + + def "When an observation with >1 coding has the desired coding, it should be removed"() { + given: + final String MATCHING_CODE = "99717-5" + final String MATCHING_CODING_SYSTEM_EXT = "L" + final String MATCHING_CODING_EXT = "alt-coding" + + def bundle = HapiFhirHelper.createMessageBundle(messageTypeCode: 'ORU_R01') + def observation = new Observation() + + addCodingToObservation(observation, "ANOTHER_CODE", "ANOTHER_SYSTEM", "coding") + addCodingToObservation(observation, MATCHING_CODE, MATCHING_CODING_SYSTEM_EXT, MATCHING_CODING_EXT) + bundle.addEntry(new Bundle.BundleEntryComponent().setResource(observation)) + + def args = getArgs(MATCHING_CODE, MATCHING_CODING_SYSTEM_EXT, MATCHING_CODING_EXT) + + expect: + HapiHelper.resourcesInBundle(bundle, Observation.class).count() == 1 + + when: + transformClass.transform(new HapiFhirResource(bundle), args) + + then: + HapiHelper.resourcesInBundle(bundle, Observation.class).count() == 0 + } + + def "When an observation has coding that's only a partial match, it should NOT be removed"() { + given: + final String MATCHING_CODE = "99717-5" + final String MATCHING_CODING_SYSTEM_EXT = "L" + final String MATCHING_CODING_EXT = "alt-coding" + + def bundle = HapiFhirHelper.createMessageBundle(messageTypeCode: 'ORU_R01') + def observation = new Observation() + addCodingToObservation(observation, code, codingSystemExt, codingExt) + bundle.addEntry(new Bundle.BundleEntryComponent().setResource(observation)) + + def args = getArgs(MATCHING_CODE, MATCHING_CODING_SYSTEM_EXT, MATCHING_CODING_EXT) + + expect: + HapiHelper.resourcesInBundle(bundle, Observation.class).count() == 1 + + when: + transformClass.transform(new HapiFhirResource(bundle), args) + + then: + HapiHelper.resourcesInBundle(bundle, Observation.class).count() == 1 + + where: + code | codingSystemExt | codingExt + "11111-1" | "L" | "alt-coding" + "99717-5" | "DIFFERENT_SYS" | "alt-coding" + "99717-5" | "L" | "coding" + } + + def "When an observation has no identifier OBX-3, it should NOT be removed"() { + given: + def bundle = HapiFhirHelper.createMessageBundle(messageTypeCode: 'ORU_R01') + + // Add an observation with an observation value and a status, but no observation identifier + def observation = new Observation() + observation.status = Observation.ObservationStatus.FINAL + def valueCoding = new Coding() + valueCoding.code = "123456" + observation.valueCodeableConcept.coding.add(valueCoding) + bundle.addEntry(new Bundle.BundleEntryComponent().setResource(observation)) + + def args = getArgs("55555-5", "LN", "coding") + + expect: + HapiHelper.resourcesInBundle(bundle, Observation.class).count() == 1 + + when: + transformClass.transform(new HapiFhirResource(bundle), args) + + then: + HapiHelper.resourcesInBundle(bundle, Observation.class).count() == 1 + } + + def "When there is >1 matching observation, all matching observations should be removed"() { + given: + final String MATCHING_CODE = "99717-5" + final String MATCHING_CODING_SYSTEM_EXT = "L" + final String MATCHING_CODING_EXT = "alt-coding" + + def bundle = HapiFhirHelper.createMessageBundle(messageTypeCode: 'ORU_R01') + + def observation1 = new Observation() + def observation2 = new Observation() + + addCodingToObservation(observation1, MATCHING_CODE, MATCHING_CODING_SYSTEM_EXT, MATCHING_CODING_EXT) + addCodingToObservation(observation2, MATCHING_CODE, MATCHING_CODING_SYSTEM_EXT, MATCHING_CODING_EXT) + + bundle.addEntry(new Bundle.BundleEntryComponent().setResource(observation1)) + bundle.addEntry(new Bundle.BundleEntryComponent().setResource(observation2)) + + def args = getArgs(MATCHING_CODE, MATCHING_CODING_SYSTEM_EXT, MATCHING_CODING_EXT) + + expect: + HapiHelper.resourcesInBundle(bundle, Observation.class).count() == 2 + + when: + transformClass.transform(new HapiFhirResource(bundle), args) + + then: + HapiHelper.resourcesInBundle(bundle, Observation.class).count() == 0 + } + + def "When message has multiple observations with only 1 matching, only 1 is removed"() { + given: + final String MATCHING_CODE = "99717-5" + final String MATCHING_CODING_SYSTEM_EXT = "L" + final String MATCHING_CODING_EXT = "alt-coding" + + final String FHIR_ORU_PATH = "../CA/020_CA_ORU_R01_CDPH_OBX_to_LOINC_1_hl7_translation.fhir" + def fhirResource = ExamplesHelper.getExampleFhirResource(FHIR_ORU_PATH) + def bundle = fhirResource.getUnderlyingResource() as Bundle + + def args = getArgs(MATCHING_CODE, MATCHING_CODING_SYSTEM_EXT, MATCHING_CODING_EXT) + + expect: + HapiHelper.resourcesInBundle(bundle, Observation.class).count() == 114 + + when: + transformClass.transform(new HapiFhirResource(bundle), args) + + then: + HapiHelper.resourcesInBundle(bundle, Observation.class).count() == 113 + } + + void addCodingToObservation(Observation observation, String code, String codingSystemExtension, String codingExtension) { + def coding = new Coding() + + coding.code = code + coding.addExtension(HapiHelper.EXTENSION_CODING_SYSTEM, new StringType(codingSystemExtension)) + coding.addExtension(HapiHelper.EXTENSION_CWE_CODING, new StringType(codingExtension)) + observation.code.addCoding(coding) + } + + Map getArgs(String code, String codingSystem, String coding) { + return [ + "code" : code, + "codingSystemExtension" : codingSystem, + codingExtension : coding] + } +} From 7302cb03b87db9c411437866c1f4c7eace0fbd02 Mon Sep 17 00:00:00 2001 From: Luis Pabon Date: Mon, 30 Sep 2024 17:01:51 -0400 Subject: [PATCH 02/14] Rename new transformation to be more generic --- ...emoveAccessionNumber.java => RemoveObservationByCode.java} | 2 +- .../transformation/custom/RemoveAccessionNumberTest.groovy | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) rename etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/{RemoveAccessionNumber.java => RemoveObservationByCode.java} (97%) diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveAccessionNumber.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java similarity index 97% rename from etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveAccessionNumber.java rename to etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java index 780fd0302..2f790f31d 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveAccessionNumber.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java @@ -12,7 +12,7 @@ import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Resource; -public class RemoveAccessionNumber implements CustomFhirTransformation { +public class RemoveObservationByCode implements CustomFhirTransformation { public static final String CODE_NAME = "code"; public static final String CODING_SYSTEM_NAME = "codingSystemExtension"; public static final String CODING_NAME = "codingExtension"; diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveAccessionNumberTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveAccessionNumberTest.groovy index 066c9dd91..d06e9bf94 100644 --- a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveAccessionNumberTest.groovy +++ b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveAccessionNumberTest.groovy @@ -11,7 +11,7 @@ import org.hl7.fhir.r4.model.Observation import org.hl7.fhir.r4.model.StringType import spock.lang.Specification -class RemoveAccessionNumberTest extends Specification { +class RemoveObservationByCodeTest extends Specification { def transformClass def setup() { @@ -19,7 +19,7 @@ class RemoveAccessionNumberTest extends Specification { TestApplicationContext.init() TestApplicationContext.injectRegisteredImplementations() - transformClass = new RemoveAccessionNumber() + transformClass = new RemoveObservationByCode() } def "When an observation has the desired coding, it should be removed"() { From 838f615b951d7636a536a4964537879789b517c1 Mon Sep 17 00:00:00 2001 From: Luis Pabon Date: Mon, 30 Sep 2024 17:02:11 -0400 Subject: [PATCH 03/14] Update transformation_definitions.json Add new transformation rules --- .../resources/transformation_definitions.json | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/etor/src/main/resources/transformation_definitions.json b/etor/src/main/resources/transformation_definitions.json index 0daf8a52d..c47a34834 100644 --- a/etor/src/main/resources/transformation_definitions.json +++ b/etor/src/main/resources/transformation_definitions.json @@ -216,6 +216,25 @@ "args": {} } ] + }, + { + "name": "ucsdOruRemoveObservationByAccessionNumber", + "description": "Remove Observations when their OBX03.4 value is '99717-5' and OBX-3.6 is 'L'", + "message": "", + "conditions": [ + "Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.where(extension.value = 'HD.1').value in ('R797' | 'R508')", + "Bundle.entry.resource.ofType(MessageHeader).event.code = 'R01'" + ], + "rules": [ + { + "name": "RemoveObservationByCode", + "args": { + "code": "99717-5", + "codingSystemExtension": "L", + "codingExtension": "alt-coding" + } + } + ] } ] } From 332a1ff3a12e69af22859f49ab91aa4412a820b1 Mon Sep 17 00:00:00 2001 From: jorge Lopez Date: Mon, 30 Sep 2024 23:56:01 -0700 Subject: [PATCH 04/14] Map for args --- .../transformation/custom/RemoveObservationByCode.java | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java index 2f790f31d..a6fba3824 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java @@ -18,7 +18,7 @@ public class RemoveObservationByCode implements CustomFhirTransformation { public static final String CODING_NAME = "codingExtension"; @Override - public void transform(FhirResource resource, Map args) { + public void transform(FhirResource resource, Map args) { var bundle = (Bundle) resource.getUnderlyingResource(); Set resourcesToRemove = new HashSet<>(); @@ -34,7 +34,7 @@ public void transform(FhirResource resource, Map args) { } private void processObservation( - Observation observation, Set resourcesToRemove, Map args) { + Observation observation, Set resourcesToRemove, Map args) { for (Coding coding : observation.getCode().getCoding()) { if (isMatchingCode(coding, args)) { resourcesToRemove.add(observation); @@ -45,7 +45,8 @@ private void processObservation( // TODO: Need to refactor this to handle missing extensions, etc. and determine if there's a way // to generalize it along with HapiHelper.hasLocalCodeInAlternateCoding - private Boolean isMatchingCode(Coding coding, Map args) { + private Boolean isMatchingCode(Coding coding, Map args) { + // Let it fail if args.get() is not a string return Objects.equals(coding.getCode(), args.get(CODE_NAME)) && coding.getExtensionByUrl(HapiHelper.EXTENSION_CODING_SYSTEM) .getValue() From 4f7cd925cc84dc4ca343169023972594ff6c1e7c Mon Sep 17 00:00:00 2001 From: Luis Pabon Date: Tue, 1 Oct 2024 12:07:39 -0400 Subject: [PATCH 05/14] Update HapiHelperTest.groovy Added new test case to improve coverage --- .../external/hapi/HapiHelperTest.groovy | 24 ++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelperTest.groovy b/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelperTest.groovy index e7a6d8dc9..a4f8d9494 100644 --- a/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelperTest.groovy +++ b/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelperTest.groovy @@ -1,6 +1,5 @@ package gov.hhs.cdc.trustedintermediary.external.hapi -import java.util.stream.Stream import org.hl7.fhir.r4.model.Bundle import org.hl7.fhir.r4.model.Coding import org.hl7.fhir.r4.model.Extension @@ -909,4 +908,27 @@ class HapiHelperTest extends Specification { "L" || HapiHelper.LOCAL_CODE_URL "PLT" || null } + + def "check getPractitioner gets the correct resource"() { + given: + def bundle = new Bundle() + def dr = HapiFhirHelper.createDiagnosticReport(bundle) + def sr = HapiFhirHelper.createBasedOnServiceRequest(dr) + + def role = HapiFhirHelper.createPractitionerRole() + Reference requesterReference = HapiFhirHelper.createPractitionerRoleReference(role) + sr.setRequester(requesterReference) + + def practitioner = new Practitioner() + practitioner.setId(UUID.randomUUID().toString()) + + String organizationId = practitioner.getId() + Reference organizationReference = new Reference("Practitioner/" + organizationId) + organizationReference.setResource(practitioner) + role.setPractitioner(organizationReference) + + expect: + def pr = HapiHelper.getPractitioner(role) + pr.id == practitioner.id + } } From f869e92a78057d50781ded7ae160986f70f134cc Mon Sep 17 00:00:00 2001 From: Luis Pabon Date: Tue, 1 Oct 2024 12:10:09 -0400 Subject: [PATCH 06/14] Convert hasLocalCodeInAlternateCoding into a more reusable HapiHelper method --- .../custom/MapLocalObservationCodes.java | 23 +--------------- .../custom/RemoveObservationByCode.java | 2 +- .../external/hapi/HapiHelper.java | 26 ++++++++++++++++++- 3 files changed, 27 insertions(+), 24 deletions(-) diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/MapLocalObservationCodes.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/MapLocalObservationCodes.java index 81d531cba..b5f7e3f23 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/MapLocalObservationCodes.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/MapLocalObservationCodes.java @@ -8,7 +8,6 @@ import gov.hhs.cdc.trustedintermediary.wrappers.Logger; import java.util.HashMap; import java.util.Map; -import java.util.Objects; import org.hl7.fhir.r4.model.Bundle; import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Observation; @@ -43,7 +42,7 @@ public void transform(FhirResource resource, Map args) { } var coding = codingList.get(0); - if (!hasLocalCodeInAlternateCoding(coding)) { + if (!HapiHelper.hasDefinedAlternateCoding(coding, HapiHelper.LOCAL_CODE_URL)) { continue; } @@ -60,26 +59,6 @@ public void transform(FhirResource resource, Map args) { } } - // @todo This will be moved to HapiHelper and turned into a more generic function for searching - // inside CWE coding objects - private Boolean hasLocalCodeInAlternateCoding(Coding coding) { - if (!HapiHelper.hasCodingExtensionWithUrl(coding, HapiHelper.EXTENSION_CWE_CODING)) { - return false; - } - - if (!HapiHelper.hasCodingSystem(coding)) { - return false; - } - - var cwe = - HapiHelper.getCodingExtensionByUrl(coding, HapiHelper.EXTENSION_CWE_CODING) - .getValue() - .toString(); - var codingSystem = HapiHelper.getCodingSystem(coding); - - return Objects.equals(cwe, "alt-coding") && HapiHelper.LOCAL_CODE_URL.equals(codingSystem); - } - private void logUnmappedLocalCode(Bundle bundle, Coding coding) { var msh41Identifier = HapiHelper.getMSH4_1Identifier(bundle); var msh41Value = msh41Identifier != null ? msh41Identifier.getValue() : null; diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java index 2f790f31d..71d275315 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java @@ -44,7 +44,7 @@ private void processObservation( } // TODO: Need to refactor this to handle missing extensions, etc. and determine if there's a way - // to generalize it along with HapiHelper.hasLocalCodeInAlternateCoding + // to generalize it private Boolean isMatchingCode(Coding coding, Map args) { return Objects.equals(coding.getCode(), args.get(CODE_NAME)) && coding.getExtensionByUrl(HapiHelper.EXTENSION_CODING_SYSTEM) diff --git a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelper.java b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelper.java index 8614567c5..b9592129c 100644 --- a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelper.java +++ b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelper.java @@ -1,6 +1,7 @@ package gov.hhs.cdc.trustedintermediary.external.hapi; import java.util.List; +import java.util.Objects; import java.util.stream.Collectors; import java.util.stream.Stream; import org.hl7.fhir.r4.model.Bundle; @@ -88,7 +89,7 @@ private HapiHelper() {} */ public static Stream resourcesInBundle( Bundle bundle, Class resourceType) { - if (bundle == null || bundle.getEntry() == null) { + if (bundle == null || bundle.getEntry().isEmpty()) { return Stream.empty(); } return bundle.getEntry().stream() @@ -681,4 +682,27 @@ public static String urlForCodeType(String code) { default -> HapiHelper.LOCAL_CODE_URL; }; } + + /** + * Check if a given Coding resource has alternate coding, and if it has a value of the expected + * type. + * + * @param coding the resource to check. Expected to be converted from an HL7 CWE format field. + * @param codingSystem Name of coding system to look for (e.g. Local code, LOINC...) + * @return True if the Coding is formatted correctly and has the expected code type, else false + */ + public static Boolean hasDefinedAlternateCoding(Coding coding, String codingSystem) { + if (!HapiHelper.hasCodingSystem(coding) + || !HapiHelper.hasCodingExtensionWithUrl(coding, HapiHelper.EXTENSION_CWE_CODING)) { + return false; + } + + var cwe = + HapiHelper.getCodingExtensionByUrl(coding, HapiHelper.EXTENSION_CWE_CODING) + .getValue() + .toString(); + + return Objects.equals(cwe, HapiHelper.EXTENSION_ALT_CODING) + && HapiHelper.getCodingSystem(coding).equals(codingSystem); + } } From d3c0efc9a7ec4b2df6f4c43575b96c67cf53605a Mon Sep 17 00:00:00 2001 From: Luis Pabon Date: Tue, 1 Oct 2024 12:13:53 -0400 Subject: [PATCH 07/14] Update RemoveObservationByCode.java Remove part of comment that's not needed anymore in this branch --- .../transformation/custom/RemoveObservationByCode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java index a6fba3824..cd80cce9a 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java @@ -44,7 +44,7 @@ private void processObservation( } // TODO: Need to refactor this to handle missing extensions, etc. and determine if there's a way - // to generalize it along with HapiHelper.hasLocalCodeInAlternateCoding + // to generalize it private Boolean isMatchingCode(Coding coding, Map args) { // Let it fail if args.get() is not a string return Objects.equals(coding.getCode(), args.get(CODE_NAME)) From b0bb587014a77bbc8bbe6ce43662a8f3b9eb3e0c Mon Sep 17 00:00:00 2001 From: Luis Pabon Date: Tue, 1 Oct 2024 17:28:11 -0400 Subject: [PATCH 08/14] Update RemoveObservationByCode.java Dummy commit to nudge github actions --- .../transformation/custom/RemoveObservationByCode.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java index cd80cce9a..bcf05b1e7 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java @@ -43,8 +43,8 @@ private void processObservation( } } - // TODO: Need to refactor this to handle missing extensions, etc. and determine if there's a way - // to generalize it + // TODO: Need to refactor this to handle missing extensions, etc. and determine if we can + // generalize it private Boolean isMatchingCode(Coding coding, Map args) { // Let it fail if args.get() is not a string return Objects.equals(coding.getCode(), args.get(CODE_NAME)) From fc0fbae764406d8dc82e88d664588e0400ffd60d Mon Sep 17 00:00:00 2001 From: Joel Biskie Date: Wed, 2 Oct 2024 08:45:57 -0500 Subject: [PATCH 09/14] Minor cleanup --- .../transformation/custom/RemoveObservationByCode.java | 6 +++--- etor/src/main/resources/transformation_definitions.json | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java index bcf05b1e7..161c54d1d 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java @@ -26,19 +26,19 @@ public void transform(FhirResource resource, Map args) { Resource resourceEntry = entry.getResource(); if (resourceEntry instanceof Observation observation) { - processObservation(observation, resourcesToRemove, args); + removeMatchingObservation(observation, resourcesToRemove, args); } } bundle.getEntry().removeIf(entry -> resourcesToRemove.contains(entry.getResource())); } - private void processObservation( + private void removeMatchingObservation( Observation observation, Set resourcesToRemove, Map args) { for (Coding coding : observation.getCode().getCoding()) { if (isMatchingCode(coding, args)) { resourcesToRemove.add(observation); - break; // No need to continue once a match is found + break; } } } diff --git a/etor/src/main/resources/transformation_definitions.json b/etor/src/main/resources/transformation_definitions.json index c47a34834..0e57d92ce 100644 --- a/etor/src/main/resources/transformation_definitions.json +++ b/etor/src/main/resources/transformation_definitions.json @@ -218,8 +218,8 @@ ] }, { - "name": "ucsdOruRemoveObservationByAccessionNumber", - "description": "Remove Observations when their OBX03.4 value is '99717-5' and OBX-3.6 is 'L'", + "name": "ucsdOruRemoveAccessionNumberObservation", + "description": "Remove Observations when their OBX-3.4 value is '99717-5' and OBX-3.6 is 'L'", "message": "", "conditions": [ "Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.where(extension.value = 'HD.1').value in ('R797' | 'R508')", From 6d793d817de379f9850b518d23d21a8fff47047f Mon Sep 17 00:00:00 2001 From: Joel Biskie Date: Wed, 2 Oct 2024 09:29:17 -0500 Subject: [PATCH 10/14] Refactor common matching methods in Coding transforms --- .../custom/MapLocalObservationCodes.java | 3 +- .../custom/RemoveObservationByCode.java | 36 +++--------- ...ovy => RemoveObservationByCodeTest.groovy} | 0 .../external/hapi/HapiHelper.java | 55 +++++++++++++++---- 4 files changed, 53 insertions(+), 41 deletions(-) rename etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/{RemoveAccessionNumberTest.groovy => RemoveObservationByCodeTest.groovy} (100%) diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/MapLocalObservationCodes.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/MapLocalObservationCodes.java index e28c7baaf..4749f3779 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/MapLocalObservationCodes.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/MapLocalObservationCodes.java @@ -42,7 +42,8 @@ public void transform(FhirResource resource, Map args) { } var coding = codingList.get(0); - if (!HapiHelper.hasDefinedAlternateCoding(coding, HapiHelper.LOCAL_CODE_URL)) { + if (!HapiHelper.hasDefinedCoding( + coding, HapiHelper.EXTENSION_ALT_CODING, HapiHelper.LOCAL_CODE)) { continue; } diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java index 161c54d1d..dbbd7a88e 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java @@ -5,10 +5,8 @@ import gov.hhs.cdc.trustedintermediary.external.hapi.HapiHelper; import java.util.HashSet; import java.util.Map; -import java.util.Objects; import java.util.Set; import org.hl7.fhir.r4.model.Bundle; -import org.hl7.fhir.r4.model.Coding; import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Resource; @@ -26,35 +24,17 @@ public void transform(FhirResource resource, Map args) { Resource resourceEntry = entry.getResource(); if (resourceEntry instanceof Observation observation) { - removeMatchingObservation(observation, resourcesToRemove, args); - } - } - bundle.getEntry().removeIf(entry -> resourcesToRemove.contains(entry.getResource())); - } - - private void removeMatchingObservation( - Observation observation, Set resourcesToRemove, Map args) { - for (Coding coding : observation.getCode().getCoding()) { - if (isMatchingCode(coding, args)) { - resourcesToRemove.add(observation); - break; + if (HapiHelper.hasMatchingCoding( + observation, + args.get(CODE_NAME).toString(), + args.get(CODING_NAME).toString(), + args.get(CODING_SYSTEM_NAME).toString())) { + resourcesToRemove.add(observation); + } } } - } - // TODO: Need to refactor this to handle missing extensions, etc. and determine if we can - // generalize it - private Boolean isMatchingCode(Coding coding, Map args) { - // Let it fail if args.get() is not a string - return Objects.equals(coding.getCode(), args.get(CODE_NAME)) - && coding.getExtensionByUrl(HapiHelper.EXTENSION_CODING_SYSTEM) - .getValue() - .toString() - .equals(args.get(CODING_SYSTEM_NAME)) - && coding.getExtensionByUrl(HapiHelper.EXTENSION_CWE_CODING) - .getValue() - .toString() - .equals(args.get(CODING_NAME)); + bundle.getEntry().removeIf(entry -> resourcesToRemove.contains(entry.getResource())); } } diff --git a/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveAccessionNumberTest.groovy b/etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCodeTest.groovy similarity index 100% rename from etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveAccessionNumberTest.groovy rename to etor/src/test/groovy/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCodeTest.groovy diff --git a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelper.java b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelper.java index b9592129c..53bd782ca 100644 --- a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelper.java +++ b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelper.java @@ -13,6 +13,7 @@ import org.hl7.fhir.r4.model.Identifier; import org.hl7.fhir.r4.model.MessageHeader; import org.hl7.fhir.r4.model.Meta; +import org.hl7.fhir.r4.model.Observation; import org.hl7.fhir.r4.model.Organization; import org.hl7.fhir.r4.model.Patient; import org.hl7.fhir.r4.model.Practitioner; @@ -684,25 +685,55 @@ public static String urlForCodeType(String code) { } /** - * Check if a given Coding resource has alternate coding, and if it has a value of the expected - * type. + * Check if a given Coding resource has a coding extension and coding system extension with the + * specified type. * * @param coding the resource to check. Expected to be converted from an HL7 CWE format field. - * @param codingSystem Name of coding system to look for (e.g. Local code, LOINC...) + * @param codingExt Name of coding extension (e.g. "coding", "alt-coding") + * @param codingSystemExt Name of coding system to look for (e.g. Local code "L", LOINC "LN"...) * @return True if the Coding is formatted correctly and has the expected code type, else false */ - public static Boolean hasDefinedAlternateCoding(Coding coding, String codingSystem) { - if (!HapiHelper.hasCodingSystem(coding) - || !HapiHelper.hasCodingExtensionWithUrl(coding, HapiHelper.EXTENSION_CWE_CODING)) { + public static Boolean hasDefinedCoding( + Coding coding, String codingExt, String codingSystemExt) { + var codingExtMatch = + hasMatchingCodingExtension(coding, HapiHelper.EXTENSION_CWE_CODING, codingExt); + var codingSystemExtMatch = + hasMatchingCodingExtension( + coding, HapiHelper.EXTENSION_CODING_SYSTEM, codingSystemExt); + return codingExtMatch && codingSystemExtMatch; + } + + private static Boolean hasMatchingCodingExtension( + Coding coding, String extensionUrl, String valueToMatch) { + if (!HapiHelper.hasCodingExtensionWithUrl(coding, extensionUrl)) { return false; } - var cwe = - HapiHelper.getCodingExtensionByUrl(coding, HapiHelper.EXTENSION_CWE_CODING) - .getValue() - .toString(); + var extensionValue = + HapiHelper.getCodingExtensionByUrl(coding, extensionUrl).getValue().toString(); + return Objects.equals(valueToMatch, extensionValue); + } - return Objects.equals(cwe, HapiHelper.EXTENSION_ALT_CODING) - && HapiHelper.getCodingSystem(coding).equals(codingSystem); + /** + * Check if an observation has a Coding resource with the given code, coding, and coding system + * + * @param codeToMatch The code to look for. + * @param codingExtToMatch Name of coding extension (e.g. "coding", "alt-coding") + * @param codingSystemToMatch Name of coding system to look for (e.g. Local code "L", LOINC + * "LN"...) + * @return True if the Coding is present in the observation, else false + */ + public static Boolean hasMatchingCoding( + Observation observation, + String codeToMatch, + String codingExtToMatch, + String codingSystemToMatch) { + for (Coding coding : observation.getCode().getCoding()) { + if (Objects.equals(coding.getCode(), codeToMatch) + && hasDefinedCoding(coding, codingExtToMatch, codingSystemToMatch)) { + return true; + } + } + return false; } } From cb9004333fa15eeb07d9a3c34b0a40bc91a90f8c Mon Sep 17 00:00:00 2001 From: Joel Biskie Date: Wed, 2 Oct 2024 10:24:10 -0500 Subject: [PATCH 11/14] Fix code smell --- .../cdc/trustedintermediary/external/hapi/HapiHelper.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelper.java b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelper.java index 53bd782ca..8ff7cb02f 100644 --- a/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelper.java +++ b/shared/src/main/java/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelper.java @@ -693,7 +693,7 @@ public static String urlForCodeType(String code) { * @param codingSystemExt Name of coding system to look for (e.g. Local code "L", LOINC "LN"...) * @return True if the Coding is formatted correctly and has the expected code type, else false */ - public static Boolean hasDefinedCoding( + public static boolean hasDefinedCoding( Coding coding, String codingExt, String codingSystemExt) { var codingExtMatch = hasMatchingCodingExtension(coding, HapiHelper.EXTENSION_CWE_CODING, codingExt); @@ -703,7 +703,7 @@ public static Boolean hasDefinedCoding( return codingExtMatch && codingSystemExtMatch; } - private static Boolean hasMatchingCodingExtension( + private static boolean hasMatchingCodingExtension( Coding coding, String extensionUrl, String valueToMatch) { if (!HapiHelper.hasCodingExtensionWithUrl(coding, extensionUrl)) { return false; @@ -723,7 +723,7 @@ private static Boolean hasMatchingCodingExtension( * "LN"...) * @return True if the Coding is present in the observation, else false */ - public static Boolean hasMatchingCoding( + public static boolean hasMatchingCoding( Observation observation, String codeToMatch, String codingExtToMatch, From 67d730ff3df1ffcdaba2580e667749ff2cd36650 Mon Sep 17 00:00:00 2001 From: Joel Biskie Date: Wed, 2 Oct 2024 11:45:00 -0500 Subject: [PATCH 12/14] Add test for hasDefinedCoding --- .../external/hapi/HapiHelperTest.groovy | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelperTest.groovy b/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelperTest.groovy index a4f8d9494..b8ff46c6b 100644 --- a/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelperTest.groovy +++ b/shared/src/test/groovy/gov/hhs/cdc/trustedintermediary/external/hapi/HapiHelperTest.groovy @@ -931,4 +931,24 @@ class HapiHelperTest extends Specification { def pr = HapiHelper.getPractitioner(role) pr.id == practitioner.id } + + def "hasDefinedCoding returns the correct result"() { + given: + def coding = new Coding() + coding.code = "SOME_CODE" + coding.addExtension(HapiHelper.EXTENSION_CWE_CODING, new StringType("coding")) + coding.addExtension(HapiHelper.EXTENSION_CODING_SYSTEM, new StringType("L")) + + when: + def actualResult = HapiHelper.hasDefinedCoding(coding, codingExt, codingSystemExt) + + then: + actualResult == expectedResult + + where: + codingExt | codingSystemExt || expectedResult + "coding" | "L" || true + "alt-coding" | "L" || false + "coding" | "LN" || false + } } From b3e64d3ced4a272e5ec90094e323c20091cd0d18 Mon Sep 17 00:00:00 2001 From: Joel Biskie Date: Wed, 2 Oct 2024 12:05:43 -0500 Subject: [PATCH 13/14] Remove nested if Co-authored-by: @jorg3lopez Co-authored-by: @tjohnson7021 --- .../custom/RemoveObservationByCode.java | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java index dbbd7a88e..c3cb989ce 100644 --- a/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java +++ b/etor/src/main/java/gov/hhs/cdc/trustedintermediary/etor/ruleengine/transformation/custom/RemoveObservationByCode.java @@ -23,15 +23,16 @@ public void transform(FhirResource resource, Map args) { for (Bundle.BundleEntryComponent entry : bundle.getEntry()) { Resource resourceEntry = entry.getResource(); - if (resourceEntry instanceof Observation observation) { + if (!(resourceEntry instanceof Observation observation)) { + continue; + } - if (HapiHelper.hasMatchingCoding( - observation, - args.get(CODE_NAME).toString(), - args.get(CODING_NAME).toString(), - args.get(CODING_SYSTEM_NAME).toString())) { - resourcesToRemove.add(observation); - } + if (HapiHelper.hasMatchingCoding( + observation, + args.get(CODE_NAME).toString(), + args.get(CODING_NAME).toString(), + args.get(CODING_SYSTEM_NAME).toString())) { + resourcesToRemove.add(resourceEntry); } } From 209e5a8945ad891fa5e2db8bd4cba0f9a1bf68f8 Mon Sep 17 00:00:00 2001 From: Luis Pabon Date: Wed, 2 Oct 2024 14:46:39 -0400 Subject: [PATCH 14/14] Update transformation_definitions.json Added to new transformation description --- etor/src/main/resources/transformation_definitions.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/etor/src/main/resources/transformation_definitions.json b/etor/src/main/resources/transformation_definitions.json index 0e57d92ce..427cf0138 100644 --- a/etor/src/main/resources/transformation_definitions.json +++ b/etor/src/main/resources/transformation_definitions.json @@ -219,7 +219,7 @@ }, { "name": "ucsdOruRemoveAccessionNumberObservation", - "description": "Remove Observations when their OBX-3.4 value is '99717-5' and OBX-3.6 is 'L'", + "description": "Remove Observations for UCSD ORUs when their OBX-3.4 value is '99717-5' and OBX-3.6 is 'L'", "message": "", "conditions": [ "Bundle.entry.resource.ofType(MessageHeader).destination.receiver.resolve().identifier.where(extension.value = 'HD.1').value in ('R797' | 'R508')",