Skip to content

Commit

Permalink
Merge pull request #313 from cqframework/RefreshOperationAndProcessor…
Browse files Browse the repository at this point in the history
…TestInfrastructure

Refresh Testing Infrastructure
  • Loading branch information
rob-reynolds authored Nov 12, 2021
2 parents cbad9e3 + 1881211 commit 9ba08f0
Show file tree
Hide file tree
Showing 820 changed files with 4,566 additions and 198,462 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@


public abstract class BaseCqfmSoftwareSystemHelper {
public static final String separator = System.getProperty("file.separator");
private String rootDir;
protected String getRootDir() {
return this.rootDir;
Expand All @@ -17,7 +18,7 @@ protected String getRootDir() {
private static String cqfToolingDeviceName = "cqf-tooling";
// private static String cqfToolingDeviceReferenceID = "#" + cqfToolingDeviceName;
private static String cqfmSoftwareSystemExtensionUrl = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem";
protected static String devicePath = "/input/resources/device";
protected static String devicePath = separator + "input" + separator + "resources" + separator + "device";

protected String getCqfToolingDeviceName() { return cqfToolingDeviceName; }
// protected String getCqfToolingDeviceReferenceID() { return cqfToolingDeviceReferenceID; }
Expand Down Expand Up @@ -52,6 +53,9 @@ protected Boolean getSystemIsValid(CqfmSoftwareSystem system) {
protected void EnsureDevicePath() {
try {
IOUtils.ensurePath(rootDir + devicePath);
if (!IOUtils.resourceDirectories.contains(rootDir + devicePath)) {
IOUtils.resourceDirectories.add(rootDir + devicePath);
}
}
catch (IOException ex) {
LogUtils.putException("EnsureDevicePath", ex.getMessage());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public <T extends DomainResource> void ensureSoftwareSystemExtensionAndDevice(T

/* Create the device if one doesn't already exist */
if (device == null) {
System.out.println("Creating Device");
device = createSoftwareSystemDevice(system);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.model.Measure;
import org.hl7.fhir.utilities.Utilities;
import org.opencds.cqf.tooling.library.LibraryProcessor;
import org.opencds.cqf.tooling.measure.r4.R4MeasureProcessor;
import org.opencds.cqf.tooling.measure.stu3.STU3MeasureProcessor;
Expand All @@ -21,10 +20,7 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

public class MeasureProcessor extends BaseProcessor {
public static final String ResourcePrefix = "measure-";
Expand All @@ -34,8 +30,11 @@ public static String getId(String baseId) {
return ResourcePrefix + baseId;
}
private Logger logger = LoggerFactory.getLogger(this.getClass());

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

public List<String> refreshIgMeasureContent(BaseProcessor parentContext, Encoding outputEncoding, String measureOutputDirectory, Boolean versioned, FhirContext fhirContext, String measureToRefreshPath) {

System.out.println("Refreshing measures...");

Expand All @@ -59,6 +58,7 @@ public List<String> refreshIgMeasureContent(BaseProcessor parentContext, Encodin
params.fhirContext = fhirContext;
params.encoding = outputEncoding;
params.versioned = versioned;
params.measureOutputDirectory = measureOutputDirectory;
return measureProcessor.refreshMeasureContent(params);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
public class R4MeasureProcessor extends MeasureProcessor {

private String measurePath;
private String measureOutputDirectory;
private IOUtils.Encoding encoding;
private static CqfmSoftwareSystemHelper cqfmHelper;

Expand All @@ -23,13 +24,21 @@ private String getMeasurePath(String measurePath) {
}
return measurePath;
}
/*
Refresh all measure resources in the given measurePath
If the path is not specified, or is not a known directory, process
all known measure resources overriding any currently existing files.
*/
protected List<String> refreshMeasures(String measurePath, IOUtils.Encoding encoding) {
return refreshMeasures(measurePath, null, encoding);
}

/*
Refresh all measure resources in the given measurePath
If the path is not specified, or is not a known directory, process
all known measure resources
*/
protected List<String> refreshMeasures(String measurePath, IOUtils.Encoding encoding) {
protected List<String> refreshMeasures(String measurePath, String measureOutputDirectory, IOUtils.Encoding encoding) {
File file = measurePath != null ? new File(measurePath) : null;
Map<String, String> fileMap = new HashMap<String, String>();
List<org.hl7.fhir.r5.model.Measure> measures = new ArrayList<>();
Expand Down Expand Up @@ -69,7 +78,16 @@ else if (file.isDirectory()) {
// 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
IOUtils.writeResource(measure, filePath, fileEncoding, fhirContext, this.versioned);
String outputPath = filePath;
if (measureOutputDirectory != null) {
File measureDirectory = new File(measureOutputDirectory);
if (!measureDirectory.exists()) {
//TODO: add logger and log non existant directory for writing
} else {
outputPath = measureDirectory.getAbsolutePath();
}
}
IOUtils.writeResource(measure, outputPath, fileEncoding, fhirContext, this.versioned);
String refreshedMeasureName;
if (this.versioned && refreshedMeasure.getVersion() != null) {
refreshedMeasureName = refreshedMeasure.getName() + "-" + refreshedMeasure.getVersion();
Expand Down Expand Up @@ -104,12 +122,17 @@ public List<String> refreshMeasureContent(RefreshMeasureParameters params) {
}

measurePath = params.measurePath;
measureOutputDirectory = params.measureOutputDirectory;
fhirContext = params.fhirContext;
encoding = params.encoding;
versioned = params.versioned;

R4MeasureProcessor.cqfmHelper = new CqfmSoftwareSystemHelper(rootDir);

return refreshMeasures(measurePath, encoding);
if (measureOutputDirectory != null) {
return refreshMeasures(measurePath, measureOutputDirectory, encoding);
} else {
return refreshMeasures(measurePath, encoding);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.opencds.cqf.tooling.measure.r4;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.parser.JsonParser;
import ca.uhn.fhir.parser.XmlParser;

Expand All @@ -16,20 +17,28 @@
import java.util.NoSuchElementException;
import java.util.Optional;

import com.google.common.base.Strings;

public class RefreshR4MeasureOperation extends RefreshGeneratedContentOperation {

private JsonParser jsonParser;
private XmlParser xmlParser;
private CqfmSoftwareSystemHelper cqfmHelper = new CqfmSoftwareSystemHelper();
private CqfmSoftwareSystemHelper cqfmHelper;

public RefreshR4MeasureOperation() {
super("src/main/resources/org/opencds/cqf/tooling/measure/output/r4", "-RefreshR4Measure", FhirContext.forR4());
super("src/main/resources/org/opencds/cqf/tooling/measure/output/r4", "-RefreshR4Measure", FhirContext.forCached(FhirVersionEnum.R4));
cqfmHelper = new CqfmSoftwareSystemHelper("src/main/resources/org/opencds/cqf/tooling/measure/output/r4");
jsonParser = (JsonParser)this.getFhirContext().newJsonParser();
xmlParser = (XmlParser)this.getFhirContext().newXmlParser();
}

public RefreshR4MeasureOperation(String pathToMeasures) {
super(FilenameUtils.getPath(pathToMeasures), "-RefreshR4Measure", FhirContext.forR4(), null, pathToMeasures);
super(pathToMeasures, "-RefreshR4Measure", FhirContext.forCached(FhirVersionEnum.R4), null, pathToMeasures);
if (!Strings.isNullOrEmpty(getOutputPath())) {
cqfmHelper = new CqfmSoftwareSystemHelper(getOutputPath());
} else {
cqfmHelper = new CqfmSoftwareSystemHelper();
}
jsonParser = (JsonParser)this.getFhirContext().newJsonParser();
xmlParser = (XmlParser)this.getFhirContext().newXmlParser();
xmlParser = (XmlParser)this.getFhirContext().newXmlParser();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package org.opencds.cqf.tooling.measure.stu3;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.parser.JsonParser;
import ca.uhn.fhir.parser.XmlParser;

Expand All @@ -16,21 +17,29 @@
import java.util.NoSuchElementException;
import java.util.Optional;

import com.google.common.base.Strings;

public class RefreshStu3MeasureOperation extends RefreshGeneratedContentOperation {

private JsonParser jsonParser;
private XmlParser xmlParser;
private CqfmSoftwareSystemHelper cqfmHelper = new CqfmSoftwareSystemHelper();
private CqfmSoftwareSystemHelper cqfmHelper;

//NOTE: Only consumed from OperationFactory - that call should come through a proper Operation that calls a processor.
public RefreshStu3MeasureOperation() {
super("src/main/resources/org/opencds/cqf/tooling/measure/output/stu3", "-RefreshStu3Measure", FhirContext.forDstu3());
super("src/main/resources/org/opencds/cqf/tooling/measure/output/stu3", "-RefreshStu3Measure", FhirContext.forCached(FhirVersionEnum.DSTU3));
cqfmHelper = new CqfmSoftwareSystemHelper("src/main/resources/org/opencds/cqf/tooling/measure/output/r4");
jsonParser = (JsonParser)this.getFhirContext().newJsonParser();
xmlParser = (XmlParser)this.getFhirContext().newXmlParser();
}

public RefreshStu3MeasureOperation(String pathToMeasures) {
super(FilenameUtils.getPath(pathToMeasures), "-RefreshStu3Measure", FhirContext.forDstu3(), null, pathToMeasures);
super(pathToMeasures, "-RefreshStu3Measure", FhirContext.forCached(FhirVersionEnum.DSTU3), null, pathToMeasures);
if (!Strings.isNullOrEmpty(getOutputPath())) {
cqfmHelper = new CqfmSoftwareSystemHelper(getOutputPath());
} else {
cqfmHelper = new CqfmSoftwareSystemHelper();
}
jsonParser = (JsonParser)this.getFhirContext().newJsonParser();
xmlParser = (XmlParser)this.getFhirContext().newXmlParser();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.opencds.cqf.tooling.operation;

import com.google.common.base.Strings;

import org.hl7.fhir.instance.model.api.IBaseResource;
import org.opencds.cqf.tooling.Operation;
import org.opencds.cqf.tooling.utilities.IOUtils;
Expand Down Expand Up @@ -61,7 +63,11 @@ public void execute(String[] args) {
}

public void output(IBaseResource resource, IOUtils.Encoding encoding) {
IOUtils.writeResource(resource, pathToMeasures, encoding, fhirContext);
if (Strings.isNullOrEmpty(getOutputPath())) {
IOUtils.writeResource(resource, pathToMeasures, encoding, fhirContext);
} else {
IOUtils.writeResource(resource, getOutputPath(), encoding, fhirContext);
}
}

public abstract void refreshGeneratedContent();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,6 @@ public class RefreshIGParameters {
public ArrayList<String> resourceDirs;
public Boolean conformant;
public String measureToRefreshPath;
public String libraryOutputPath;
public String measureOutputPath;
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,9 @@ The path to the measure resource(s)
An initialized processor context that can provide the IG context directly
*/
public IProcessorContext parentContext;

/*
Directory target for writing output
*/
public String measureOutputDirectory;
}
26 changes: 24 additions & 2 deletions src/main/java/org/opencds/cqf/tooling/processor/IGProcessor.java
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
package org.opencds.cqf.tooling.processor;

import static java.util.Objects.requireNonNull;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import com.google.common.base.Strings;

import org.apache.commons.io.FilenameUtils;
import org.hl7.fhir.utilities.Utilities;
import org.opencds.cqf.tooling.library.LibraryProcessor;
Expand All @@ -30,6 +34,14 @@ public IGProcessor(IGBundleProcessor igBundleProcessor, LibraryProcessor library
}
//mega ig method
public void publishIG(RefreshIGParameters params) {
requireNonNull(params.includeDependencies, "includeDependencies can not be null");
requireNonNull(params.includeELM, "includeELM can not be null");
requireNonNull(params.includePatientScenarios, "includePatientScenarios can not be null");
requireNonNull(params.includeTerminology, "includeTerminology can not be null");
requireNonNull(params.versioned, "versioned can not be null");
requireNonNull(params.resourceDirs, "resourceDirs can not be null");
requireNonNull(params.outputEncoding, "outputEncoding can not be null");
requireNonNull(params.ini, "ini can not be null");
if (params.ini != null) {
initializeFromIni(params.ini);
}
Expand Down Expand Up @@ -101,6 +113,8 @@ public void refreshIG(RefreshIGParameters params) {
Encoding encoding = params.outputEncoding;
// Boolean includeELM = params.includeELM;
// Boolean includeDependencies = params.includeDependencies;
String libraryOutputPath = params.libraryOutputPath;
String measureOutputPath = params.measureOutputPath;
Boolean includeTerminology = params.includeTerminology;
Boolean includePatientScenarios = params.includePatientScenarios;
Boolean versioned = params.versioned;
Expand All @@ -121,11 +135,19 @@ public void refreshIG(RefreshIGParameters params) {
IGProcessor.ensure(rootDir, includePatientScenarios, includeTerminology, IOUtils.resourceDirectories);

List<String> refreshedLibraryNames;
refreshedLibraryNames = libraryProcessor.refreshIgLibraryContent(this, encoding, versioned, fhirContext);
if (Strings.isNullOrEmpty(libraryOutputPath)) {
refreshedLibraryNames = libraryProcessor.refreshIgLibraryContent(this, encoding, versioned, fhirContext);
} else {
refreshedLibraryNames = libraryProcessor.refreshIgLibraryContent(this, encoding, libraryOutputPath, versioned, fhirContext);
}
refreshedResourcesNames.addAll(refreshedLibraryNames);

List<String> refreshedMeasureNames;
refreshedMeasureNames = measureProcessor.refreshIgMeasureContent(this, encoding, versioned, fhirContext, measureToRefreshPath);
if (Strings.isNullOrEmpty(measureOutputPath)) {
refreshedMeasureNames = measureProcessor.refreshIgMeasureContent(this, encoding, versioned, fhirContext, measureToRefreshPath);
} else {
refreshedMeasureNames = measureProcessor.refreshIgMeasureContent(this, encoding, measureOutputPath, versioned, fhirContext, measureToRefreshPath);
}
refreshedResourcesNames.addAll(refreshedMeasureNames);

if (refreshedResourcesNames.isEmpty()) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ public class RefreshIGArgumentProcessor {
public static final String[] FHIR_URI_OPTIONS = {"fs", "fhir-uri"};
public static final String[] MEASURE_TO_REFRESH_PATH = {"mtrp", "measure-to-refresh-path"};
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"};

@SuppressWarnings("unused")
public OptionParser build() {
Expand All @@ -43,13 +45,17 @@ public OptionParser build() {
OptionSpecBuilder igOutputEncodingBuilder = parser.acceptsAll(asList(IG_OUTPUT_ENCODING), "If omitted, output will be generated using JSON encoding.");
OptionSpecBuilder fhirUriBuilder = parser.acceptsAll(asList(FHIR_URI_OPTIONS),"If omitted the final bundle will not be loaded to a FHIR server.");
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");

OptionSpec<String> ini = iniBuilder.withRequiredArg().describedAs("Path to the IG ini file");
OptionSpec<String> rootDir = rootDirBuilder.withOptionalArg().describedAs("Root directory of the IG");
OptionSpec<String> igPath = igPathBuilder.withRequiredArg().describedAs("Path to the IG, relative to the root directory");
OptionSpec<String> resourcePath = resourcePathBuilder.withOptionalArg().describedAs("directory of resources");
OptionSpec<String> igOutputEncoding = igOutputEncodingBuilder.withOptionalArg().describedAs("desired output encoding for resources");
OptionSpec<String> measureToRefreshPath = measureToRefreshPathBuilder.withOptionalArg().describedAs("Path to Measure to refresh.");
OptionSpec<String> libraryOutputPath = libraryOutputPathBuilder.withOptionalArg().describedAs("path to the output directory for updated libraries");
OptionSpec<String> measureOutputPath = measureOutputPathBuilder.withOptionalArg().describedAs("path to the output directory for updated measures");

//TODO: FHIR user / password (and other auth options)
OptionSpec<String> fhirUri = fhirUriBuilder.withOptionalArg().describedAs("uri of fhir server");
Expand Down Expand Up @@ -92,6 +98,16 @@ public RefreshIGParameters parseAndConvert(String[] args) {
String fhirUri = (String)options.valueOf(FHIR_URI_OPTIONS[0]);
String measureToRefreshPath = (String)options.valueOf(MEASURE_TO_REFRESH_PATH[0]);

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

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

ArrayList<String> paths = new ArrayList<String>();
if (resourcePaths != null && !resourcePaths.isEmpty()) {
paths.addAll(resourcePaths);
Expand All @@ -110,6 +126,8 @@ public RefreshIGParameters parseAndConvert(String[] args) {
ip.resourceDirs = paths;
ip.fhirUri = fhirUri;
ip.measureToRefreshPath = measureToRefreshPath;
ip.libraryOutputPath = libraryOutputPath;
ip.measureOutputPath = measureOutputPath;

return ip;
}
Expand Down
6 changes: 6 additions & 0 deletions src/main/java/org/opencds/cqf/tooling/utilities/IOUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -898,6 +898,12 @@ public static HashSet<String> getDevicePaths(FhirContext fhirContext) {
}
return devicePaths;
}

// TODO: This should not be necessary this is awful... For now it is needed for passing tests in Travis
public static void clearDevicePaths() {
devicePaths = new HashSet<String>();
}

private static void setupDevicePaths(FhirContext fhirContext) {
HashMap<String, IBaseResource> resources = new LinkedHashMap<String, IBaseResource>();
for(String dir : resourceDirectories) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.opencds.cqf.tooling;

public interface CqfmSoftwareSystemTest {
public static final String separator = System.getProperty("file.separator");
public static final String cqfmSoftwareSystemExtensionUrl = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-softwaresystem";
}
Loading

0 comments on commit 9ba08f0

Please sign in to comment.