From f2e28dba669550ece31645077a8b15d01d4200cd Mon Sep 17 00:00:00 2001 From: David Raeside Date: Thu, 9 Jan 2025 09:04:40 -0500 Subject: [PATCH 1/3] added new narrative generator utility method --- .../NarrativeGeneratorTemplateUtils.java | 34 ++++++++++++ .../NarrativeGeneratorTemplateUtilsTest.java | 55 +++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtils.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtils.java index adb173eb582f..23f6abd86ec4 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtils.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtils.java @@ -22,8 +22,12 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.context.FhirVersionEnum; import ca.uhn.fhir.util.BundleUtil; +import ca.uhn.fhir.util.TerserUtil; +import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.Pair; +import org.hl7.fhir.instance.model.api.IBase; import org.hl7.fhir.instance.model.api.IBaseBundle; +import org.hl7.fhir.instance.model.api.IBaseCoding; import org.hl7.fhir.instance.model.api.IBaseResource; import java.util.List; @@ -52,4 +56,34 @@ public boolean bundleHasEntriesWithResourceType(IBaseBundle theBaseBundle, Strin .filter(Objects::nonNull) .anyMatch(t -> ctx.getResourceType(t).equals(theResourceType)); } + + /** + * Returns if the bundle contains a resource that has a `code` property that contains a matching code system and code. + * + * @param theBundle the bundle to inspect + * @param theResourceType the resource type to look for + * @param theCodeSystem the code system to find + * @param theCode the code to find + * @return returns true if bundle has a resource that with matching code/code system + */ + public boolean bundleHasEntriesWithCode( + IBaseBundle theBundle, String theResourceType, String theCodeSystem, String theCode) { + FhirVersionEnum fhirVersionEnum = theBundle.getStructureFhirVersionEnum(); + FhirContext ctx = FhirContext.forCached(fhirVersionEnum); + + List> entryResources = BundleUtil.getBundleEntryUrlsAndResources(ctx, theBundle); + + return entryResources.stream() + .map(Pair::getValue) + .filter(Objects::nonNull) + .filter(t -> ctx.getResourceType(t).equals(theResourceType)) + .anyMatch(t -> { + List codeList = TerserUtil.getFieldByFhirPath(ctx, "code.coding", t); + return codeList.stream().anyMatch(m -> { + IBaseCoding coding = (IBaseCoding) m; + return StringUtils.equals(coding.getSystem(), theCodeSystem) + && StringUtils.equals(coding.getCode(), theCode); + }); + }); + } } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtilsTest.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtilsTest.java index ad71176a1f39..cf12f7828e43 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtilsTest.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtilsTest.java @@ -1,7 +1,10 @@ package ca.uhn.fhir.narrative2; import org.hl7.fhir.dstu3.model.Bundle; +import org.hl7.fhir.dstu3.model.CodeableConcept; +import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.Medication; +import org.hl7.fhir.dstu3.model.Observation; import org.hl7.fhir.dstu3.model.Patient; import org.junit.jupiter.api.Test; @@ -24,5 +27,57 @@ public void testBundleHasEntriesWithResourceType_False() { assertFalse(NarrativeGeneratorTemplateUtils.INSTANCE.bundleHasEntriesWithResourceType(bundle, "Patient")); } + @Test + public void testResourcesHaveCodeValue_isTrue() { + Bundle bundle = new Bundle(); + bundle.addEntry().setResource(new Observation().setCode( + new CodeableConcept( + new Coding("http://loinc.org", "123", "")))); + bundle.addEntry().setResource(new Observation().setCode( + new CodeableConcept( + new Coding("http://loinc.org", "456", "")))); + + assertTrue(NarrativeGeneratorTemplateUtils.INSTANCE + .bundleHasEntriesWithCode(bundle, "Observation", "http://loinc.org", "123")); + } + + @Test + public void testResourcesHaveCodeValue_isTrueWhenOneCodeMatches() { + Bundle bundle = new Bundle(); + bundle.addEntry().setResource(new Observation().setCode( + new CodeableConcept() + .addCoding(new Coding("http://loinc.org", "abc", "")) + .addCoding(new Coding("http://loinc.org", "123", "")) + )); + bundle.addEntry().setResource(new Observation().setCode( + new CodeableConcept( + new Coding("http://loinc.org", "456", "")))); + + assertTrue(NarrativeGeneratorTemplateUtils.INSTANCE + .bundleHasEntriesWithCode(bundle, "Observation", "http://loinc.org", "123")); + } + + @Test + public void testResourcesHaveCodeValue_isFalse() { + Bundle bundle = new Bundle(); + bundle.addEntry().setResource(new Observation().setCode( + new CodeableConcept( + new Coding("http://loinc.org", "123", "")))); + bundle.addEntry().setResource(new Observation().setCode( + new CodeableConcept( + new Coding("http://loinc.org", "456", "")))); + + assertFalse(NarrativeGeneratorTemplateUtils.INSTANCE + .bundleHasEntriesWithCode(bundle, "Observation", "http://loinc.org", "789")); + } + + @Test + public void testResourcesHaveCodeValue_isFalseWhenNoResourcePresent() { + Bundle bundle = new Bundle(); + bundle.addEntry().setResource(new Patient().setActive(true)); + bundle.addEntry().setResource(new Medication().setIsBrand(true)); + assertFalse(NarrativeGeneratorTemplateUtils.INSTANCE + .bundleHasEntriesWithCode(bundle, "Observation", "http://loinc.org", "789")); + } } From 7d3445287978ca5327c510d1a14ff6b84e4da5de Mon Sep 17 00:00:00 2001 From: David Raeside Date: Thu, 9 Jan 2025 13:43:51 -0500 Subject: [PATCH 2/3] added another helper method --- .../NarrativeGeneratorTemplateUtils.java | 53 +++++++++++++++---- .../NarrativeGeneratorTemplateUtilsTest.java | 29 ++++++++++ 2 files changed, 71 insertions(+), 11 deletions(-) diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtils.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtils.java index 23f6abd86ec4..9eb42ff098b9 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtils.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtils.java @@ -32,6 +32,7 @@ import java.util.List; import java.util.Objects; +import java.util.stream.Stream; /** * An instance of this class is added to the Thymeleaf context as a variable with @@ -58,7 +59,7 @@ public boolean bundleHasEntriesWithResourceType(IBaseBundle theBaseBundle, Strin } /** - * Returns if the bundle contains a resource that has a `code` property that contains a matching code system and code. + * Returns if the bundle contains an entry resource whose `code` property contains a matching code system and code. * * @param theBundle the bundle to inspect * @param theResourceType the resource type to look for @@ -71,19 +72,49 @@ public boolean bundleHasEntriesWithCode( FhirVersionEnum fhirVersionEnum = theBundle.getStructureFhirVersionEnum(); FhirContext ctx = FhirContext.forCached(fhirVersionEnum); - List> entryResources = BundleUtil.getBundleEntryUrlsAndResources(ctx, theBundle); + return getEntryResources(ctx, theBundle, theResourceType).anyMatch(t -> { + List codeList = TerserUtil.getFieldByFhirPath(ctx, "code.coding", t); + return codeList.stream().anyMatch(m -> { + IBaseCoding coding = (IBaseCoding) m; + return StringUtils.equals(coding.getSystem(), theCodeSystem) + && StringUtils.equals(coding.getCode(), theCode); + }); + }); + } + /** + * Gets a boolean indicating if at least one bundle entry resource's `code` property does NOT contain the + * code system/code specified. + * + * @param theBundle the bundle to inspect + * @param theResourceType the resource type to find + * @param theCodeSystem the code system to find + * @param theCode the code to find + * @return Returns true if one entry of resource type requested does not contain the specified code/system + */ + public boolean bundleHasEntriesWithoutCode( + IBaseBundle theBundle, String theResourceType, String theCodeSystem, String theCode) { + + FhirVersionEnum fhirVersionEnum = theBundle.getStructureFhirVersionEnum(); + FhirContext ctx = FhirContext.forCached(fhirVersionEnum); + + return getEntryResources(ctx, theBundle, theResourceType).anyMatch(t -> { + List codeList = TerserUtil.getFieldByFhirPath(ctx, "code.coding", t); + return codeList.stream().allMatch(m -> { + IBaseCoding coding = (IBaseCoding) m; + return !(StringUtils.equals(coding.getSystem(), theCodeSystem) + && StringUtils.equals(coding.getCode(), theCode)); + }); + }); + } + + private Stream getEntryResources( + FhirContext theContext, IBaseBundle theBundle, String theResourceType) { + List> entryResources = + BundleUtil.getBundleEntryUrlsAndResources(theContext, theBundle); return entryResources.stream() .map(Pair::getValue) .filter(Objects::nonNull) - .filter(t -> ctx.getResourceType(t).equals(theResourceType)) - .anyMatch(t -> { - List codeList = TerserUtil.getFieldByFhirPath(ctx, "code.coding", t); - return codeList.stream().anyMatch(m -> { - IBaseCoding coding = (IBaseCoding) m; - return StringUtils.equals(coding.getSystem(), theCodeSystem) - && StringUtils.equals(coding.getCode(), theCode); - }); - }); + .filter(t -> theContext.getResourceType(t).equals(theResourceType)); } } diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtilsTest.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtilsTest.java index cf12f7828e43..1a72a4514ae2 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtilsTest.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtilsTest.java @@ -80,4 +80,33 @@ public void testResourcesHaveCodeValue_isFalseWhenNoResourcePresent() { assertFalse(NarrativeGeneratorTemplateUtils.INSTANCE .bundleHasEntriesWithCode(bundle, "Observation", "http://loinc.org", "789")); } + + @Test + public void testResourcesDoNotHaveCodeValue_isTrue() { + Bundle bundle = new Bundle(); + bundle.addEntry().setResource(new Observation().setCode( + new CodeableConcept( + new Coding("http://loinc.org", "123", "")))); + bundle.addEntry().setResource(new Observation().setCode( + new CodeableConcept( + new Coding("http://loinc.org", "456", "")))); + + assertTrue(NarrativeGeneratorTemplateUtils.INSTANCE + .bundleHasEntriesWithoutCode(bundle, "Observation", "http://loinc.org", "456")); + } + + @Test + public void testResourcesDoNotHaveCodeValue_isFalse() { + Bundle bundle = new Bundle(); + bundle.addEntry().setResource(new Observation().setCode( + new CodeableConcept( + new Coding("http://loinc.org", "456", "")))); + bundle.addEntry().setResource(new Observation().setCode( + new CodeableConcept() + .addCoding(new Coding("http://loinc.org", "abc", "")) + .addCoding(new Coding("http://loinc.org", "456", "")) + )); + assertFalse(NarrativeGeneratorTemplateUtils.INSTANCE + .bundleHasEntriesWithoutCode(bundle, "Observation", "http://loinc.org", "456")); + } } From 478236cefabe2dada0218c39c78567edacbbb4df Mon Sep 17 00:00:00 2001 From: David Raeside Date: Thu, 9 Jan 2025 15:24:36 -0500 Subject: [PATCH 3/3] updated test --- .../fhir/narrative2/NarrativeGeneratorTemplateUtilsTest.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtilsTest.java b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtilsTest.java index 1a72a4514ae2..0898f7958bf6 100644 --- a/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtilsTest.java +++ b/hapi-fhir-structures-dstu3/src/test/java/ca/uhn/fhir/narrative2/NarrativeGeneratorTemplateUtilsTest.java @@ -6,6 +6,7 @@ import org.hl7.fhir.dstu3.model.Medication; import org.hl7.fhir.dstu3.model.Observation; import org.hl7.fhir.dstu3.model.Patient; +import org.hl7.fhir.dstu3.model.Procedure; import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.*; @@ -76,6 +77,7 @@ public void testResourcesHaveCodeValue_isFalseWhenNoResourcePresent() { Bundle bundle = new Bundle(); bundle.addEntry().setResource(new Patient().setActive(true)); bundle.addEntry().setResource(new Medication().setIsBrand(true)); + bundle.addEntry().setResource(new Procedure().setCode(new CodeableConcept(new Coding("http://loinc.org", "789", "")))); assertFalse(NarrativeGeneratorTemplateUtils.INSTANCE .bundleHasEntriesWithCode(bundle, "Observation", "http://loinc.org", "789"));