From b0cd33d120aff7befb763e5f32f2d69e334ef48d Mon Sep 17 00:00:00 2001 From: Michael Holck Date: Tue, 28 Nov 2023 14:28:54 -0700 Subject: [PATCH] Fixes #455 - Create Group for test patients (#458) * Fixes #455 - Created a Group for all patients for a measure, supports both individual resources and bundles * Added some logic to check FHIR version, only implemented R4 for now but logic in place to handle others --------- Co-authored-by: Michael Holck Co-authored-by: JP --- .../tooling/processor/TestCaseProcessor.java | 91 ++++++++++++++++--- .../cqf/tooling/utilities/IOUtils.java | 13 ++- 2 files changed, 92 insertions(+), 12 deletions(-) diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/processor/TestCaseProcessor.java b/tooling/src/main/java/org/opencds/cqf/tooling/processor/TestCaseProcessor.java index 6460edf75..dd82b5a82 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/processor/TestCaseProcessor.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/processor/TestCaseProcessor.java @@ -1,5 +1,7 @@ package org.opencds.cqf.tooling.processor; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -7,8 +9,12 @@ import javax.annotation.Nullable; +import ca.uhn.fhir.context.FhirVersionEnum; +import ca.uhn.fhir.model.api.IFhirVersion; import org.apache.commons.io.FilenameUtils; +import org.hl7.fhir.Patient; import org.hl7.fhir.instance.model.api.IBaseResource; +import org.hl7.fhir.r4.model.*; import org.opencds.cqf.tooling.utilities.BundleUtils; import org.opencds.cqf.tooling.utilities.IOUtils; import org.opencds.cqf.tooling.utilities.LogUtils; @@ -30,29 +36,92 @@ public void refreshTestCases(String path, IOUtils.Encoding encoding, FhirContext { logger.info("Refreshing tests"); List resourceTypeTestGroups = IOUtils.getDirectoryPaths(path, false); + IFhirVersion version = fhirContext.getVersion(); for (String group : resourceTypeTestGroups) { List testArtifactPaths = IOUtils.getDirectoryPaths(group, false); for (String testArtifactPath : testArtifactPaths) { List testCasePaths = IOUtils.getDirectoryPaths(testArtifactPath, false); - for (String testCasePath : testCasePaths) { - try { - List paths = IOUtils.getFilePaths(testCasePath, true); - List resources = IOUtils.readResources(paths, fhirContext); - ensureIds(testCasePath, resources); - Object bundle = BundleUtils.bundleArtifacts(getId(FilenameUtils.getName(testCasePath)), resources, fhirContext, false); - IOUtils.writeBundle(bundle, testArtifactPath, encoding, fhirContext); - } catch (Exception e) { - LogUtils.putException(testCasePath, e); + + org.hl7.fhir.r4.model.Group testGroup = null; + + if (version.getVersion() == FhirVersionEnum.R4) { + testGroup = new org.hl7.fhir.r4.model.Group(); + testGroup.setActive(true); + testGroup.setType(Group.GroupType.PERSON); + testGroup.setActual(true); + } + + // For each test case we need to create a group + if (!testCasePaths.isEmpty()) { + String measureName = IOUtils.getMeasureTestDirectory(testCasePaths.get(0)); + if (testGroup != null) { + testGroup.setId(measureName); + + testGroup.addExtension("http://hl7.org/fhir/StructureDefinition/artifact-testArtifact", + new CanonicalType("http://ecqi.healthit.gov/ecqms/Measure/" + measureName)); } - finally { - LogUtils.warn(testCasePath); + + for (String testCasePath : testCasePaths) { + try { + List paths = IOUtils.getFilePaths(testCasePath, true); + List resources = IOUtils.readResources(paths, fhirContext); + ensureIds(testCasePath, resources); + + // Loop through resources and any that are patients need to be added to the test Group + // Handle individual resources when they exist + for (IBaseResource resource : resources) { + if ((resource.fhirType() == "Patient") && (version.getVersion() == FhirVersionEnum.R4)) { + org.hl7.fhir.r4.model.Patient patient = (org.hl7.fhir.r4.model.Patient) resource; + addPatientToGroupR4(testGroup, patient); + } + + // Handle bundled resources when that is how they are provided + if ((resource.fhirType() == "Bundle") && (version.getVersion() == FhirVersionEnum.R4)) { + org.hl7.fhir.r4.model.Bundle bundle = (org.hl7.fhir.r4.model.Bundle) resource; + ArrayList bundleResources = + BundleUtils.getR4ResourcesFromBundle(bundle); + for (IBaseResource bundleResource : bundleResources) { + if (bundleResource.fhirType() == "Patient") { + org.hl7.fhir.r4.model.Patient patient = (org.hl7.fhir.r4.model.Patient) bundleResource; + addPatientToGroupR4(testGroup, patient); + } + } + } + } + + Object bundle = BundleUtils.bundleArtifacts(getId(FilenameUtils.getName(testCasePath)), resources, fhirContext, false); + IOUtils.writeBundle(bundle, testArtifactPath, encoding, fhirContext); + } catch (Exception e) { + LogUtils.putException(testCasePath, e); + } finally { + LogUtils.warn(testCasePath); + } + } + + // Need to output the Group if it exists + if (testGroup != null) { + IOUtils.writeResource(testGroup, testArtifactPath, encoding, fhirContext, true, + "Group-" + measureName); } } } } } + private void addPatientToGroupR4(Group group, org.hl7.fhir.r4.model.Patient patient) { + IdType idType = patient.getIdElement(); + org.hl7.fhir.r4.model.Group.GroupMemberComponent member = group.addMember(); + org.hl7.fhir.r4.model.Reference patientRef = new Reference(); + patientRef.setReference("Patient/" + idType.getIdPart()); + + // Get name for display value + org.hl7.fhir.r4.model.HumanName name = patient.getName().get(0); + patientRef.setDisplay(name.getNameAsSingleString()); + + member.setEntity(patientRef); + } + public static List getTestCaseResources(String path, FhirContext fhirContext) { List resources = new ArrayList(); diff --git a/tooling/src/main/java/org/opencds/cqf/tooling/utilities/IOUtils.java b/tooling/src/main/java/org/opencds/cqf/tooling/utilities/IOUtils.java index 8ea634127..45f781acb 100644 --- a/tooling/src/main/java/org/opencds/cqf/tooling/utilities/IOUtils.java +++ b/tooling/src/main/java/org/opencds/cqf/tooling/utilities/IOUtils.java @@ -24,8 +24,10 @@ import java.util.Map; import java.util.NoSuchElementException; import java.util.Optional; +import java.util.regex.Matcher; import java.util.Set; import java.util.stream.Collectors; +import java.util.stream.StreamSupport; import ca.uhn.fhir.util.BundleBuilder; import com.google.gson.JsonObject; @@ -1035,6 +1037,15 @@ public static boolean isXMLOrJson(String fileDirPath, String libraryName){ return false; } + // Assumes the tests are structured as .../input/tests/measure/{MeasureName}/{TestName} and will extract + // the measure name + public static String getMeasureTestDirectory(String pathString) { + Path path = Paths.get(pathString); + String[] testDirs = StreamSupport.stream(path.spliterator(), false).map(Path::toString).toArray(String[]::new); + String testDir = testDirs[testDirs.length - 2]; + return testDir; + } + public static String concatFilePath(String basePath, String... pathsToAppend) { String filePath = basePath; for (String pathToAppend: pathsToAppend) { @@ -1042,4 +1053,4 @@ public static String concatFilePath(String basePath, String... pathsToAppend) { } return filePath; } -} \ No newline at end of file +}