Skip to content

Commit

Permalink
#377: Added effective data requirement generation to PlanDefinition p…
Browse files Browse the repository at this point in the history
…rocessing ... added test
  • Loading branch information
c-schuler committed Jul 19, 2022
1 parent 622af65 commit ecf6568
Show file tree
Hide file tree
Showing 60 changed files with 12,036 additions and 11 deletions.
10 changes: 9 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -478,7 +478,7 @@
<arg>-Xlint:-options</arg>
<!-- This fixes a warning about Test annotations not being processed as part of compilation - They won't be since they are processed as part of testing-->
<arg>-Xlint:-processing</arg>
<arg>-Werror</arg>
<!-- <arg>-Werror</arg>-->
</compilerArgs>
</configuration>
</plugin>
Expand Down Expand Up @@ -682,6 +682,14 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import org.opencds.cqf.tooling.processor.CDSHooksProcessor;
import org.opencds.cqf.tooling.processor.IGBundleProcessor;
import org.opencds.cqf.tooling.processor.IGProcessor;
import org.opencds.cqf.tooling.processor.PlanDefinitionProcessor;
import org.opencds.cqf.tooling.plandefinition.PlanDefinitionProcessor;
import org.opencds.cqf.tooling.processor.argument.RefreshIGArgumentProcessor;
import org.opencds.cqf.tooling.questionnaire.QuestionnaireProcessor;

Expand Down Expand Up @@ -37,7 +37,7 @@ public void execute(String[] args) {
PlanDefinitionProcessor planDefinitionProcessor = new PlanDefinitionProcessor(libraryProcessor, cdsHooksProcessor);
QuestionnaireProcessor questionnaireProcessor = new QuestionnaireProcessor(libraryProcessor);
IGBundleProcessor igBundleProcessor = new IGBundleProcessor(measureProcessor, planDefinitionProcessor, questionnaireProcessor);
IGProcessor processor = new IGProcessor(igBundleProcessor, libraryProcessor, measureProcessor);
IGProcessor processor = new IGProcessor(igBundleProcessor, libraryProcessor, measureProcessor, planDefinitionProcessor);
processor.publishIG(params);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@ public class RefreshIGParameters {
public String measureToRefreshPath;
public String libraryOutputPath;
public String measureOutputPath;
public String plandefinitionOutputPath;
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package org.opencds.cqf.tooling.processor;
package org.opencds.cqf.tooling.plandefinition;

import ca.uhn.fhir.context.FhirContext;
import org.apache.commons.io.FilenameUtils;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.utilities.Utilities;
import org.opencds.cqf.tooling.library.LibraryProcessor;
import org.opencds.cqf.tooling.processor.*;
import org.opencds.cqf.tooling.utilities.*;
import org.opencds.cqf.tooling.utilities.IOUtils.Encoding;
import org.slf4j.Logger;
Expand All @@ -14,7 +14,6 @@
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

public class PlanDefinitionProcessor {
public static final String ResourcePrefix = "plandefinition-";
Expand All @@ -28,6 +27,10 @@ public PlanDefinitionProcessor(LibraryProcessor libraryProcessor, CDSHooksProces
this.cdsHooksProcessor = cdsHooksProcessor;
}

public List<String> refreshIGPlandefinitionContent(BaseProcessor parentContext, Encoding outputEncoding, String plandefinitionOutputDirectory, Boolean versioned, FhirContext fhirContext) {
return null;
}

public void bundlePlanDefinitions(ArrayList<String> refreshedLibraryNames, String igPath, List<String> binaryPaths, Boolean includeDependencies,
Boolean includeTerminology, Boolean includePatientScenarios, Boolean includeVersion,
FhirContext fhirContext, String fhirUri, Encoding encoding) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package org.opencds.cqf.tooling.plandefinition;

import org.cqframework.cql.cql2elm.CqlTranslatorOptions;
import org.cqframework.cql.cql2elm.LibraryManager;
import org.cqframework.cql.cql2elm.model.TranslatedLibrary;
import org.cqframework.cql.elm.requirements.fhir.DataRequirementsProcessor;
import org.hl7.fhir.r5.model.*;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

public class PlanDefinitionRefreshProcessor {

public PlanDefinition refreshPlanDefinition(PlanDefinition planToUse, LibraryManager libraryManager, TranslatedLibrary translatedLibrary, CqlTranslatorOptions options) {
Set<String> expressions = new HashSet<>();
if (planToUse.hasAction()) {
getExpressions(planToUse.getAction(), expressions);
}
DataRequirementsProcessor dqReqTrans = new DataRequirementsProcessor();
Library moduleDefinitionLibrary = dqReqTrans.gatherDataRequirements(libraryManager, translatedLibrary, options, expressions, true);

planToUse.getExtension().removeAll(planToUse.getExtensionsByUrl("http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-parameter"));
planToUse.getExtension().removeAll(planToUse.getExtensionsByUrl("http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-dataRequirement"));
planToUse.getExtension().removeAll(planToUse.getExtensionsByUrl("http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-directReferenceCode"));
planToUse.getExtension().removeAll(planToUse.getExtensionsByUrl("http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-logicDefinition"));
planToUse.getExtension().removeAll(planToUse.getExtensionsByUrl("http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-effectiveDataRequirements"));

for (Extension extension : moduleDefinitionLibrary.getExtension()) {
planToUse.addExtension(extension);
}

planToUse.getContained().removeIf(resource -> resource.getId().equalsIgnoreCase("effective-data-requirements"));
// planToUse.addContained(moduleDefinitionLibrary.setId("effective-data-requirements"));
// planToUse.addExtension().setUrl("http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-effectiveDataRequirements").setValue(new Reference("#effective-data-requirements")).setId("effective-data-requirements");
return planToUse;
}

private void getExpressions(List<PlanDefinition.PlanDefinitionActionComponent> actions, Set<String> expressions) {
for (PlanDefinition.PlanDefinitionActionComponent action : actions) {
if (action.hasCondition()) {
for (PlanDefinition.PlanDefinitionActionConditionComponent condition : action.getCondition()) {
if (condition.hasKind() && condition.getKind() == Enumerations.ActionConditionKind.APPLICABILITY
&& condition.hasExpression() && isExpressionIdentifier(condition.getExpression())) {
expressions.add(condition.getExpression().getExpression());
}
}
}
if (action.hasDynamicValue()) {
for (PlanDefinition.PlanDefinitionActionDynamicValueComponent dynamicValue : action.getDynamicValue()) {
if (dynamicValue.hasExpression() && isExpressionIdentifier(dynamicValue.getExpression())) {
expressions.add(dynamicValue.getExpression().getExpression());
}
}
}
if (action.hasAction()) {
getExpressions(action.getAction(), expressions);
}
}
}

private boolean isExpressionIdentifier(Expression expression) {
return expression.hasLanguage() && expression.hasExpression() && (expression.getLanguage().equalsIgnoreCase("text/cql.identifier") || expression.getLanguage().equalsIgnoreCase("text/cql"));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.List;

import org.opencds.cqf.tooling.measure.MeasureProcessor;
import org.opencds.cqf.tooling.plandefinition.PlanDefinitionProcessor;
import org.opencds.cqf.tooling.questionnaire.QuestionnaireProcessor;
import org.opencds.cqf.tooling.utilities.IOUtils.Encoding;

Expand Down
14 changes: 13 additions & 1 deletion src/main/java/org/opencds/cqf/tooling/processor/IGProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.opencds.cqf.tooling.library.LibraryProcessor;
import org.opencds.cqf.tooling.measure.MeasureProcessor;
import org.opencds.cqf.tooling.parameter.RefreshIGParameters;
import org.opencds.cqf.tooling.plandefinition.PlanDefinitionProcessor;
import org.opencds.cqf.tooling.utilities.IGUtils;
import org.opencds.cqf.tooling.utilities.IOUtils;
import org.opencds.cqf.tooling.utilities.IOUtils.Encoding;
Expand All @@ -26,11 +27,13 @@ public class IGProcessor extends BaseProcessor {
protected IGBundleProcessor igBundleProcessor;
protected LibraryProcessor libraryProcessor;
protected MeasureProcessor measureProcessor;
protected PlanDefinitionProcessor planDefinitionProcessor;

public IGProcessor(IGBundleProcessor igBundleProcessor, LibraryProcessor libraryProcessor, MeasureProcessor measureProcessor) {
public IGProcessor(IGBundleProcessor igBundleProcessor, LibraryProcessor libraryProcessor, MeasureProcessor measureProcessor, PlanDefinitionProcessor planDefinitionProcessor) {
this.igBundleProcessor = igBundleProcessor;
this.libraryProcessor = libraryProcessor;
this.measureProcessor = measureProcessor;
this.planDefinitionProcessor = planDefinitionProcessor;
}
//mega ig method
public void publishIG(RefreshIGParameters params) {
Expand Down Expand Up @@ -123,6 +126,7 @@ public void refreshIG(RefreshIGParameters params) {
// Boolean includeDependencies = params.includeDependencies;
String libraryOutputPath = params.libraryOutputPath;
String measureOutputPath = params.measureOutputPath;
String planDefinitionOutputPath = params.plandefinitionOutputPath;
Boolean includeTerminology = params.includeTerminology;
Boolean includePatientScenarios = params.includePatientScenarios;
Boolean versioned = params.versioned;
Expand Down Expand Up @@ -158,6 +162,14 @@ public void refreshIG(RefreshIGParameters params) {
}
refreshedResourcesNames.addAll(refreshedMeasureNames);

List<String> refreshPlandefinitionNames;
if (Strings.isNullOrEmpty(planDefinitionOutputPath)) {
refreshPlandefinitionNames = planDefinitionProcessor.refreshIGPlandefinitionContent(this, encoding, planDefinitionOutputPath, versioned, fhirContext);
} else {
refreshPlandefinitionNames = planDefinitionProcessor.refreshIGPlandefinitionContent(this, encoding, null, versioned, fhirContext);
}
refreshedResourcesNames.addAll(refreshPlandefinitionNames);

if (refreshedResourcesNames.isEmpty()) {
LogUtils.info("No resources successfully refreshed.");
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ public class RefreshIGArgumentProcessor {
public static final String[] RESOURCE_PATH_OPTIONS = {"rp", "resourcepath"};
public static final String[] LIBRARY_OUTPUT_PATH_OPTIONS = {"libraryOutput", "libraryOutputPath", "lop"};
public static final String[] MEASURE_OUTPUT_PATH_OPTIONS = {"measureOutput", "measureOutputPath", "mop"};
public static final String[] PLANDEFINITION_OUTPUT_PATH_OPTIONS = {"plandefinitionOutput", "plandefinitionOutputPath", "pop"};
public static final String[] SHOULD_APPLY_SOFTWARE_SYSTEM_STAMP_OPTIONS = { "ss", "stamp" };

@SuppressWarnings("unused")
Expand All @@ -48,6 +49,7 @@ public OptionParser build() {
OptionSpecBuilder measureToRefreshPathBuilder = parser.acceptsAll(asList(MEASURE_TO_REFRESH_PATH), "Path to Measure to refresh.");
OptionSpecBuilder libraryOutputPathBuilder = parser.acceptsAll(asList(LIBRARY_OUTPUT_PATH_OPTIONS),"If omitted, the libraries will overwrite any existing libraries");
OptionSpecBuilder measureOutputPathBuilder = parser.acceptsAll(asList(MEASURE_OUTPUT_PATH_OPTIONS),"If omitted, the measures will overwrite any existing measures");
OptionSpecBuilder plandefinitionOutputPathBuilder = parser.acceptsAll(asList(PLANDEFINITION_OUTPUT_PATH_OPTIONS),"If omitted, the plandefinitions will overwrite any existing measures");
OptionSpecBuilder shouldApplySoftwareSystemStampBuilder = parser.acceptsAll(asList(SHOULD_APPLY_SOFTWARE_SYSTEM_STAMP_OPTIONS),"Indicates whether refreshed Measure and Library resources should be stamped with the 'cqf-tooling' stamp via the cqfm-softwaresystem Extension.");

OptionSpec<String> ini = iniBuilder.withRequiredArg().describedAs("Path to the IG ini file");
Expand Down Expand Up @@ -111,6 +113,11 @@ public RefreshIGParameters parseAndConvert(String[] args) {
measureOutputPath = "";
}

String plandefinitionOutputPath = (String)options.valueOf(PLANDEFINITION_OUTPUT_PATH_OPTIONS[0]);
if (plandefinitionOutputPath == null) {
plandefinitionOutputPath = "";
}

Boolean shouldApplySoftwareSystemStamp = true;
String shouldApplySoftwareSystemStampValue = (String)options.valueOf(SHOULD_APPLY_SOFTWARE_SYSTEM_STAMP_OPTIONS[0]);

Expand Down Expand Up @@ -139,6 +146,7 @@ public RefreshIGParameters parseAndConvert(String[] args) {
ip.measureToRefreshPath = measureToRefreshPath;
ip.libraryOutputPath = libraryOutputPath;
ip.measureOutputPath = measureOutputPath;
ip.plandefinitionOutputPath = plandefinitionOutputPath;

return ip;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
import org.opencds.cqf.tooling.processor.CDSHooksProcessor;
import org.opencds.cqf.tooling.processor.IGBundleProcessor;
import org.opencds.cqf.tooling.processor.IGProcessor;
import org.opencds.cqf.tooling.processor.PlanDefinitionProcessor;
import org.opencds.cqf.tooling.plandefinition.PlanDefinitionProcessor;
import org.opencds.cqf.tooling.processor.argument.RefreshIGArgumentProcessor;
import org.opencds.cqf.tooling.questionnaire.QuestionnaireProcessor;
import org.opencds.cqf.tooling.utilities.IOUtils;
Expand Down Expand Up @@ -276,7 +276,7 @@ public void testParamsMissingINI() {
PlanDefinitionProcessor planDefinitionProcessor = new PlanDefinitionProcessor(libraryProcessor, cdsHooksProcessor);
QuestionnaireProcessor questionnaireProcessor = new QuestionnaireProcessor(libraryProcessor);
IGBundleProcessor igBundleProcessor = new IGBundleProcessor(measureProcessor, planDefinitionProcessor, questionnaireProcessor);
IGProcessor processor = new IGProcessor(igBundleProcessor, libraryProcessor, measureProcessor);
IGProcessor processor = new IGProcessor(igBundleProcessor, libraryProcessor, measureProcessor, planDefinitionProcessor);

//override ini to be null
params.ini = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
package org.opencds.cqf.tooling.plandefinition.r4;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.parser.JsonParser;
import ca.uhn.fhir.parser.LenientErrorHandler;
import ca.uhn.fhir.parser.XmlParser;
import org.cqframework.cql.cql2elm.*;
import org.cqframework.cql.cql2elm.model.TranslatedLibrary;
import org.hl7.elm.r1.VersionedIdentifier;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.model.PlanDefinition;
import org.opencds.cqf.tooling.plandefinition.PlanDefinitionRefreshProcessor;
import org.opencds.cqf.tooling.utilities.CanonicalUtils;
import org.opencds.cqf.tooling.utilities.IOUtils;
import org.testng.annotations.Test;

import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class PlanDefinitionRefreshProcessorTest {

public static final String separator = System.getProperty("file.separator");

@Test
public void refreshPlanDefinition() {
String libraryDirectoryPath = "/Users/christopherschuler/Documents/workspace/alphora/cqf-tooling/src/test/resources/org/opencds/cqf/tooling/r4/input/resources/library";
String plandefinitionDirectoryPath = "/Users/christopherschuler/Documents/workspace/alphora/cqf-tooling/src/test/resources/org/opencds/cqf/tooling/r4/input/resources/plandefinition";

List<IBaseResource> resources = IOUtils.readResources(
Arrays.asList(
plandefinitionDirectoryPath + separator + "plandefinition-OpioidCDSREC01.xml",
plandefinitionDirectoryPath + separator + "plandefinition-OpioidCDSREC02.xml",
plandefinitionDirectoryPath + separator + "plandefinition-OpioidCDSREC03.xml",
plandefinitionDirectoryPath + separator + "plandefinition-OpioidCDSREC04.xml",
plandefinitionDirectoryPath + separator + "plandefinition-OpioidCDSREC04PatientView.xml",
plandefinitionDirectoryPath + separator + "plandefinition-OpioidCDSREC05.xml",
plandefinitionDirectoryPath + separator + "plandefinition-OpioidCDSREC06.xml",
plandefinitionDirectoryPath + separator + "plandefinition-OpioidCDSREC07.xml",
plandefinitionDirectoryPath + separator + "plandefinition-OpioidCDSREC08.xml",
plandefinitionDirectoryPath + separator + "plandefinition-OpioidCDSREC08OrderSign.xml",
plandefinitionDirectoryPath + separator + "plandefinition-OpioidCDSREC09.xml",
plandefinitionDirectoryPath + separator + "plandefinition-OpioidCDSREC10.xml",
plandefinitionDirectoryPath + separator + "plandefinition-OpioidCDSREC10OrderSign.xml",
plandefinitionDirectoryPath + separator + "plandefinition-OpioidCDSREC10PatientView.xml",
plandefinitionDirectoryPath + separator + "plandefinition-OpioidCDSREC11.xml",
plandefinitionDirectoryPath + separator + "plandefinition-OpioidCDSREC11PatientView.xml",
plandefinitionDirectoryPath + separator + "plandefinition-OpioidCDSREC12PatientView.xml"

), FhirContext.forR5()
);

LibrarySourceProvider sourceProvider = new DefaultLibrarySourceProvider(Path.of(libraryDirectoryPath));
LibraryManager libraryManager = new LibraryManager(new ModelManager());
libraryManager.getLibrarySourceLoader().registerProvider(sourceProvider);

CqlTranslatorOptions options = CqlTranslatorOptions.defaultOptions();
VersionedIdentifier identifier;
TranslatedLibrary translatedLibrary;
PlanDefinitionRefreshProcessor refreshProcessor = new PlanDefinitionRefreshProcessor();
for(IBaseResource plan : resources) {
PlanDefinition planDef = (PlanDefinition) plan;
identifier = new VersionedIdentifier().withId(CanonicalUtils.getId(planDef.getLibrary().get(0).getValue()));
translatedLibrary = libraryManager.resolveLibrary(identifier, options, new ArrayList<>());
PlanDefinition refreshedPlan = refreshProcessor.refreshPlanDefinition(planDef, libraryManager, translatedLibrary, options);
IOUtils.writeResource(refreshedPlan, "/Users/christopherschuler/Documents/workspace/scratch", IOUtils.Encoding.JSON, FhirContext.forR5());
}


// String resource = new XmlParser(FhirContext.forR5(), new LenientErrorHandler()).setPrettyPrint(true).encodeResourceToString(refreshedPlan);
// String resource = new JsonParser(FhirContext.forR5(), new LenientErrorHandler()).setPrettyPrint(true).encodeResourceToString(refreshedPlan);

String s = "";
}

}
Loading

0 comments on commit ecf6568

Please sign in to comment.