-
Notifications
You must be signed in to change notification settings - Fork 23
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
#377 - added PlanDefinition refresh processing and plugged into IGRef…
…resh operation ... updated tests ... pretty print by default in IOUtils
- Loading branch information
Showing
15 changed files
with
700 additions
and
207 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
52 changes: 52 additions & 0 deletions
52
tooling/src/main/java/org/opencds/cqf/tooling/parameter/RefreshPlanDefinitionParameters.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
package org.opencds.cqf.tooling.parameter; | ||
|
||
import ca.uhn.fhir.context.FhirContext; | ||
import org.opencds.cqf.tooling.processor.IProcessorContext; | ||
import org.opencds.cqf.tooling.utilities.IOUtils; | ||
|
||
public class RefreshPlanDefinitionParameters { | ||
/* | ||
The ig ini file | ||
*/ | ||
public String ini; | ||
|
||
/* | ||
The canonical base URL of the ig | ||
*/ | ||
public String igCanonicalBase; | ||
|
||
/* | ||
The path to CQL library content | ||
*/ | ||
public String cqlContentPath; | ||
|
||
/* | ||
The fhirContext for the current process | ||
*/ | ||
public FhirContext fhirContext; | ||
|
||
/* | ||
The target encoding for the output | ||
*/ | ||
public IOUtils.Encoding encoding; | ||
|
||
/* | ||
Whether or not version is included in the name | ||
*/ | ||
public Boolean versioned; | ||
|
||
/* | ||
The path to the measure resource(s) | ||
*/ | ||
public String planDefinitionPath; | ||
|
||
/* | ||
An initialized processor context that can provide the IG context directly | ||
*/ | ||
public IProcessorContext parentContext; | ||
|
||
/* | ||
Directory target for writing output | ||
*/ | ||
public String planDefinitionOutputDirectory; | ||
} |
359 changes: 359 additions & 0 deletions
359
tooling/src/main/java/org/opencds/cqf/tooling/plandefinition/PlanDefinitionProcessor.java
Large diffs are not rendered by default.
Oops, something went wrong.
82 changes: 82 additions & 0 deletions
82
.../src/main/java/org/opencds/cqf/tooling/plandefinition/PlanDefinitionRefreshProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,82 @@ | ||
package org.opencds.cqf.tooling.plandefinition; | ||
|
||
import org.cqframework.cql.cql2elm.CqlTranslatorOptions; | ||
import org.cqframework.cql.cql2elm.LibraryManager; | ||
import org.cqframework.cql.cql2elm.model.CompiledLibrary; | ||
import org.cqframework.cql.elm.requirements.fhir.DataRequirementsProcessor; | ||
import org.hl7.fhir.r5.model.*; | ||
|
||
import java.util.Date; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Set; | ||
|
||
public class PlanDefinitionRefreshProcessor { | ||
|
||
public PlanDefinition refreshPlanDefinition(PlanDefinition planToUse, LibraryManager libraryManager, | ||
CompiledLibrary compiledLibrary, CqlTranslatorOptions options) { | ||
planToUse.setDate(new Date()); | ||
Set<String> expressions = new HashSet<>(); | ||
if (planToUse.hasAction()) { | ||
getExpressions(planToUse.getAction(), expressions); | ||
} | ||
DataRequirementsProcessor dqReqTrans = new DataRequirementsProcessor(); | ||
Library moduleDefinitionLibrary = dqReqTrans.gatherDataRequirements(libraryManager, compiledLibrary, | ||
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()) { | ||
if (extension.hasUrl() | ||
&& extension.getUrl().equals( | ||
"http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-directReferenceCode")) { | ||
continue; | ||
} | ||
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")); | ||
} | ||
|
||
} |
153 changes: 153 additions & 0 deletions
153
...ng/src/main/java/org/opencds/cqf/tooling/plandefinition/r4/R4PlanDefinitionProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
package org.opencds.cqf.tooling.plandefinition.r4; | ||
|
||
import org.hl7.fhir.convertors.advisors.impl.BaseAdvisor_40_50; | ||
import org.hl7.fhir.convertors.conv40_50.VersionConvertor_40_50; | ||
import org.hl7.fhir.r4.formats.FormatUtilities; | ||
import org.opencds.cqf.tooling.common.r4.CqfmSoftwareSystemHelper; | ||
import org.opencds.cqf.tooling.library.LibraryProcessor; | ||
import org.opencds.cqf.tooling.parameter.RefreshPlanDefinitionParameters; | ||
import org.opencds.cqf.tooling.plandefinition.PlanDefinitionProcessor; | ||
import org.opencds.cqf.tooling.processor.CDSHooksProcessor; | ||
import org.opencds.cqf.tooling.utilities.IOUtils; | ||
|
||
import java.io.File; | ||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.Objects; | ||
|
||
public class R4PlanDefinitionProcessor extends PlanDefinitionProcessor { | ||
|
||
private static CqfmSoftwareSystemHelper cqfmHelper; | ||
|
||
public R4PlanDefinitionProcessor(LibraryProcessor libraryProcessor, CDSHooksProcessor cdsHooksProcessor) { | ||
super(libraryProcessor, cdsHooksProcessor); | ||
} | ||
|
||
private String getPlanDefinitionPath(String planDefinitionPath) { | ||
File f = new File(planDefinitionPath); | ||
if (!f.exists() && f.getParentFile().isDirectory() && f.getParentFile().exists()) { | ||
return f.getParentFile().toString(); | ||
} | ||
return planDefinitionPath; | ||
} | ||
/* | ||
Refresh all PlanDefinition resources in the given planDefinitionPath | ||
If the path is not specified, or is not a known directory, process | ||
all known PlanDefinition resources overriding any currently existing files. | ||
*/ | ||
protected List<String> refreshPlanDefinitions(String planDefinitionPath, IOUtils.Encoding encoding) { | ||
return refreshPlanDefinitions(planDefinitionPath, null, encoding); | ||
} | ||
|
||
/* | ||
Refresh all PlanDefinition resources in the given planDefinitionPath | ||
If the path is not specified, or is not a known directory, process | ||
all known PlanDefinition resources | ||
*/ | ||
protected List<String> refreshPlanDefinitions(String planDefinitionPath, String planDefinitionOutputDirectory, IOUtils.Encoding encoding) { | ||
File file = planDefinitionPath != null ? new File(planDefinitionPath) : null; | ||
Map<String, String> fileMap = new HashMap<>(); | ||
List<org.hl7.fhir.r5.model.PlanDefinition> planDefinitions = new ArrayList<>(); | ||
|
||
if (file == null || !file.exists()) { | ||
for (String path : IOUtils.getPlanDefinitionPaths(this.fhirContext)) { | ||
loadPlanDefinition(fileMap, planDefinitions, new File(path)); | ||
} | ||
} | ||
else if (file.isDirectory()) { | ||
for (File libraryFile : Objects.requireNonNull(file.listFiles())) { | ||
if(IOUtils.isXMLOrJson(planDefinitionPath, libraryFile.getName())) { | ||
loadPlanDefinition(fileMap, planDefinitions, libraryFile); | ||
} | ||
} | ||
} | ||
else { | ||
loadPlanDefinition(fileMap, planDefinitions, file); | ||
} | ||
|
||
List<String> refreshedPlanDefinitionNames = new ArrayList<>(); | ||
List<org.hl7.fhir.r5.model.PlanDefinition> refreshedPlanDefinitions = super.refreshGeneratedContent(planDefinitions); | ||
VersionConvertor_40_50 versionConvertor = new VersionConvertor_40_50(new BaseAdvisor_40_50()); | ||
for (org.hl7.fhir.r5.model.PlanDefinition refreshedPlanDefinition : refreshedPlanDefinitions) { | ||
org.hl7.fhir.r4.model.PlanDefinition planDefinition = (org.hl7.fhir.r4.model.PlanDefinition) versionConvertor.convertResource(refreshedPlanDefinition); | ||
if (planDefinition.hasIdentifier() && !planDefinition.getIdentifier().isEmpty()) { | ||
this.getIdentifiers().addAll(planDefinition.getIdentifier()); | ||
} | ||
String filePath; | ||
IOUtils.Encoding fileEncoding; | ||
if (fileMap.containsKey(refreshedPlanDefinition.getId())) | ||
{ | ||
filePath = fileMap.get(refreshedPlanDefinition.getId()); | ||
fileEncoding = IOUtils.getEncoding(filePath); | ||
} else { | ||
filePath = getPlanDefinitionPath(planDefinitionPath); | ||
fileEncoding = encoding; | ||
} | ||
cqfmHelper.ensureCQFToolingExtensionAndDevice(planDefinition, fhirContext); | ||
// Issue 96 | ||
// Passing the includeVersion here to handle not using the version number in the filename | ||
if (new File(filePath).exists()) { | ||
// TODO: This prevents mangled names from being output | ||
// It would be nice for the tooling to generate library shells, we have enough information to, | ||
// but the tooling gets confused about the ID and the filename and what gets written is garbage | ||
String outputPath = filePath; | ||
if (planDefinitionOutputDirectory != null) { | ||
File planDefinitionDirectory = new File(planDefinitionOutputDirectory); | ||
if (!planDefinitionDirectory.exists()) { | ||
//TODO: add logger and log non existant directory for writing | ||
} else { | ||
outputPath = planDefinitionDirectory.getAbsolutePath(); | ||
} | ||
} | ||
IOUtils.writeResource(planDefinition, outputPath, fileEncoding, fhirContext, this.versioned); | ||
String refreshedPlanDefinitionName; | ||
if (this.versioned && refreshedPlanDefinition.getVersion() != null) { | ||
refreshedPlanDefinitionName = refreshedPlanDefinition.getName() + "-" + refreshedPlanDefinition.getVersion(); | ||
} else { | ||
refreshedPlanDefinitionName = refreshedPlanDefinition.getName(); | ||
} | ||
refreshedPlanDefinitionNames.add(refreshedPlanDefinitionName); | ||
} | ||
} | ||
|
||
return refreshedPlanDefinitionNames; | ||
} | ||
|
||
private void loadPlanDefinition(Map<String, String> fileMap, List<org.hl7.fhir.r5.model.PlanDefinition> planDefinitions, File planDefinitionFile) { | ||
try { | ||
org.hl7.fhir.r4.model.Resource resource = FormatUtilities.loadFile(planDefinitionFile.getAbsolutePath()); | ||
VersionConvertor_40_50 versionConvertor = new VersionConvertor_40_50(new BaseAdvisor_40_50()); | ||
org.hl7.fhir.r5.model.PlanDefinition planDefinition = (org.hl7.fhir.r5.model.PlanDefinition) versionConvertor.convertResource(resource); | ||
fileMap.put(planDefinition.getId(), planDefinitionFile.getAbsolutePath()); | ||
planDefinitions.add(planDefinition); | ||
} catch (Exception ex) { | ||
logMessage(String.format("Error reading PlanDefinition: %s. Error: %s", planDefinitionFile.getAbsolutePath(), ex.getMessage())); | ||
} | ||
} | ||
|
||
@Override | ||
public List<String> refreshPlanDefinitionContent(RefreshPlanDefinitionParameters params) { | ||
if (params.parentContext != null) { | ||
initialize(params.parentContext); | ||
} | ||
else { | ||
initializeFromIni(params.ini); | ||
} | ||
|
||
String planDefinitionPath = params.planDefinitionPath; | ||
String planDefinitionOutputDirectory = params.planDefinitionOutputDirectory; | ||
fhirContext = params.fhirContext; | ||
IOUtils.Encoding encoding = params.encoding; | ||
versioned = params.versioned; | ||
|
||
cqfmHelper = new CqfmSoftwareSystemHelper(rootDir); | ||
|
||
if (planDefinitionOutputDirectory != null) { | ||
return refreshPlanDefinitions(planDefinitionPath, planDefinitionOutputDirectory, encoding); | ||
} else { | ||
return refreshPlanDefinitions(planDefinitionPath, encoding); | ||
} | ||
} | ||
} |
11 changes: 11 additions & 0 deletions
11
...rc/main/java/org/opencds/cqf/tooling/plandefinition/stu3/STU3PlanDefinitionProcessor.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
package org.opencds.cqf.tooling.plandefinition.stu3; | ||
|
||
import org.opencds.cqf.tooling.library.LibraryProcessor; | ||
import org.opencds.cqf.tooling.plandefinition.PlanDefinitionProcessor; | ||
import org.opencds.cqf.tooling.processor.CDSHooksProcessor; | ||
|
||
public class STU3PlanDefinitionProcessor extends PlanDefinitionProcessor { | ||
public STU3PlanDefinitionProcessor(LibraryProcessor libraryProcessor, CDSHooksProcessor cdsHooksProcessor) { | ||
super(libraryProcessor, cdsHooksProcessor); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.