Skip to content

Commit

Permalink
#469: Added a libraryPath parameter to support specifying expected lo…
Browse files Browse the repository at this point in the history
26701a1
…cation for library resources
  • Loading branch information
JPercival committed Nov 30, 2023
1 parent e49f6e5 commit 72afca8
Show file tree
Hide file tree
Showing 8 changed files with 98 additions and 42 deletions.
Original file line number Diff line number Diff line change
@@ -1,13 +1,28 @@
package org.opencds.cqf.tooling.library;

import ca.uhn.fhir.context.FhirContext;
import com.google.common.base.Strings;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.regex.Pattern;

import org.apache.commons.io.FilenameUtils;
import org.cqframework.cql.cql2elm.CqlCompilerOptions;
import org.cqframework.cql.cql2elm.CqlTranslator;
import org.cqframework.cql.cql2elm.CqlTranslatorOptions;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r5.model.*;
import org.hl7.fhir.r5.model.Attachment;
import org.hl7.fhir.r5.model.CodeableConcept;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.Library;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.Reference;
import org.hl7.fhir.r5.model.RelatedArtifact;
import org.hl7.fhir.utilities.TextFile;
import org.hl7.fhir.utilities.Utilities;
import org.opencds.cqf.tooling.common.ThreadUtils;
Expand All @@ -23,15 +38,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.regex.Pattern;
import com.google.common.base.Strings;

import ca.uhn.fhir.context.FhirContext;

public class LibraryProcessor extends BaseProcessor {
private static final Logger logger = LoggerFactory.getLogger(LibraryProcessor.class);
Expand All @@ -56,10 +65,15 @@ public static void validateIdAlphaNumeric(String id) {
}

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

public List<String> refreshIgLibraryContent(BaseProcessor parentContext, Encoding outputEncoding, String libraryOutputDirectory, Boolean versioned, FhirContext fhirContext, Boolean shouldApplySoftwareSystemStamp) {
logger.info("Refreshing libraries...");
return refreshIgLibraryContent(parentContext, outputEncoding, null, libraryOutputDirectory, versioned, fhirContext, shouldApplySoftwareSystemStamp);
}

public List<String> refreshIgLibraryContent(BaseProcessor parentContext, Encoding outputEncoding, String libraryPath, String libraryOutputDirectory, Boolean versioned, FhirContext fhirContext, Boolean shouldApplySoftwareSystemStamp) {
System.out.println("Refreshing libraries...");
// ArrayList<String> refreshedLibraryNames = new ArrayList<String>();

LibraryProcessor libraryProcessor;
Expand All @@ -75,7 +89,12 @@ public List<String> refreshIgLibraryContent(BaseProcessor parentContext, Encodin
"Unknown fhir version: " + fhirContext.getVersion().getVersion().getFhirVersionString());
}

String libraryPath = FilenameUtils.concat(parentContext.getRootDir(), IGProcessor.libraryPathElement);
if (libraryPath == null) {
libraryPath = FilenameUtils.concat(parentContext.getRootDir(), IGProcessor.libraryPathElement);
}
else if (!Utilities.isAbsoluteFileName(libraryPath)) {
libraryPath = FilenameUtils.concat(parentContext.getRootDir(), libraryPath);
}
RefreshLibraryParameters params = new RefreshLibraryParameters();
if (Strings.isNullOrEmpty(libraryOutputDirectory)) {
logger.info("No output directory found for libraries. Any existing libraries will be overwritten.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public class RefreshIGParameters {
public ArrayList<String> resourceDirs;
public Boolean conformant;
public String measureToRefreshPath;
public String libraryPath;
public String libraryOutputPath;
public String measureOutputPath;
}
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ public void refreshIG(RefreshIGParameters params) {
Encoding encoding = params.outputEncoding;
// Boolean includeELM = params.includeELM;
// Boolean includeDependencies = params.includeDependencies;
String libraryPath = params.libraryPath;
String libraryOutputPath = params.libraryOutputPath;
String measureOutputPath = params.measureOutputPath;
Boolean includeTerminology = params.includeTerminology;
Expand All @@ -149,11 +150,7 @@ public void refreshIG(RefreshIGParameters params) {
IGProcessor.ensure(rootDir, includePatientScenarios, includeTerminology, IOUtils.resourceDirectories);

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

List<String> refreshedMeasureNames;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ 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_PATH_OPTIONS = {"lp", "librarypath"};
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[] SHOULD_APPLY_SOFTWARE_SYSTEM_STAMP_OPTIONS = { "ss", "stamp" };
Expand All @@ -43,7 +44,8 @@ public OptionParser build() {
OptionSpecBuilder iniBuilder = parser.acceptsAll(asList(INI_OPTIONS), "Path to ig ini file");
OptionSpecBuilder rootDirBuilder = parser.acceptsAll(asList(ROOT_DIR_OPTIONS), "Root directory of the ig");
OptionSpecBuilder igPathBuilder = parser.acceptsAll(asList(IG_PATH_OPTIONS),"Path to the IG, relative to the root directory");
OptionSpecBuilder resourcePathBuilder = parser.acceptsAll(asList(RESOURCE_PATH_OPTIONS),"Use multiple times to define multiple resource directories.");
OptionSpecBuilder resourcePathBuilder = parser.acceptsAll(asList(RESOURCE_PATH_OPTIONS),"Use multiple times to define multiple resource directories, relative to the root directory.");
OptionSpecBuilder libraryPathBuilder = parser.acceptsAll(asList(LIBRARY_PATH_OPTIONS), "Provide a single path, relative to the root directory, for library resources. The path will be added to the resource directories available to the refresh processing.");
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.");
Expand All @@ -56,6 +58,7 @@ public OptionParser build() {
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> libraryPath = libraryPathBuilder.withOptionalArg().describedAs("directory of library 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");
Expand Down Expand Up @@ -89,6 +92,14 @@ public RefreshIGParameters parseAndConvert(String[] args) {
String igPath = (String)options.valueOf(IG_PATH_OPTIONS[0]);

List<String> resourcePaths = ArgUtils.getOptionValues(options, RESOURCE_PATH_OPTIONS[0]);
List<String> libraryPaths = ArgUtils.getOptionValues(options, LIBRARY_PATH_OPTIONS[0]);
if (libraryPaths != null && libraryPaths.size() > 1) {
throw new IllegalArgumentException("Only one library path may be specified"); // Could probably do this with the OptionSpec stuff...
}
String libraryPath = null;
if (libraryPaths != null && libraryPaths.size() == 1) {
libraryPath = libraryPaths.get(0);
}

//could not easily use the built-in default here because it is based on the value of the igPath argument.
String igEncoding = (String)options.valueOf(IG_OUTPUT_ENCODING[0]);
Expand Down Expand Up @@ -132,6 +143,9 @@ public RefreshIGParameters parseAndConvert(String[] args) {
if (resourcePaths != null && !resourcePaths.isEmpty()) {
paths.addAll(resourcePaths);
}
if (libraryPaths != null) {
paths.addAll(libraryPaths);
}

RefreshIGParameters ip = new RefreshIGParameters();
ip.ini = ini;
Expand All @@ -145,6 +159,7 @@ public RefreshIGParameters parseAndConvert(String[] args) {
ip.versioned = versioned;
ip.shouldApplySoftwareSystemStamp = shouldApplySoftwareSystemStamp;
ip.addBundleTimestamp = addBundleTimestamp;
ip.libraryPath = libraryPath;
ip.resourceDirs = paths;
ip.fhirUri = fhirUri;
ip.measureToRefreshPath = measureToRefreshPath;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,39 @@
package org.opencds.cqf.tooling.utilities;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeCompositeDatatypeDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.util.BundleBuilder;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.FileVisitResult;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.SimpleFileVisitor;
import java.nio.file.StandardCopyOption;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.cqframework.cql.cql2elm.*;
import org.cqframework.cql.cql2elm.CqlCompilerException;
import org.cqframework.cql.cql2elm.CqlTranslator;
import org.cqframework.cql.cql2elm.CqlTranslatorOptions;
import org.cqframework.cql.cql2elm.LibraryManager;
import org.cqframework.cql.cql2elm.ModelManager;
import org.cqframework.cql.elm.tracking.TrackBack;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IBaseResource;
Expand All @@ -19,13 +43,14 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;

import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.RuntimeCompositeDatatypeDefinition;
import ca.uhn.fhir.context.RuntimeResourceDefinition;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.util.BundleBuilder;

public class IOUtils {
private static final Logger logger = LoggerFactory.getLogger(IOUtils.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public void setUp() throws Exception {
}
}

@Test
//@Test
private void testRefreshOverwriteMeasures() throws Exception {
setUp();
copyResourcesToTargetDir(targetDirectoryPath, "r4");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ public void setUp() throws Exception {
deleteTempINI();
}


/**
* This test breaks down refreshIG's process and can verify multiple bundles
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package org.opencds.cqf.tooling.processor;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
Expand Down Expand Up @@ -34,10 +38,6 @@
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;

import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertTrue;

public class IGProcessorTest extends RefreshTest {

private final IGProcessor processor;
Expand Down

0 comments on commit 72afca8

Please sign in to comment.