Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add DataRequrementsProcessor from cqframework elm #340

Merged
merged 10 commits into from
Oct 26, 2021
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;

import javax.xml.bind.JAXBException;

import org.cqframework.cql.cql2elm.CqlTranslator;
import org.cqframework.cql.cql2elm.CqlTranslatorException;
import org.cqframework.cql.cql2elm.CqlTranslatorOptions;
import org.cqframework.cql.cql2elm.LibraryManager;
import org.cqframework.cql.cql2elm.ModelManager;
import org.cqframework.cql.elm.execution.Library;
Expand Down Expand Up @@ -47,16 +49,22 @@ public static CqlTranslator getTranslator(String cql, LibraryManager libraryMana
modelManager);
}

public static CqlTranslator getTranslator(InputStream cqlStream, LibraryManager libraryManager,
ModelManager modelManager) {
public static List<CqlTranslator.Options> generateDefaultTranslatorOptions() {
ArrayList<CqlTranslator.Options> options = new ArrayList<>();
options.add(CqlTranslator.Options.EnableAnnotations);
options.add(CqlTranslator.Options.EnableLocators);
options.add(CqlTranslator.Options.DisableListDemotion);
options.add(CqlTranslator.Options.DisableListPromotion);
options.add(CqlTranslator.Options.DisableMethodInvocation);
return options;
}

public static CqlTranslator getTranslator(InputStream cqlStream, LibraryManager libraryManager,
ModelManager modelManager) {

CqlTranslator translator;
try {
List<CqlTranslator.Options> options = generateDefaultTranslatorOptions();
translator = CqlTranslator.fromStream(cqlStream, modelManager, libraryManager,
options.toArray(new CqlTranslator.Options[options.size()]));
} catch (IOException e) {
Expand All @@ -81,4 +89,12 @@ public static Library translateLibrary(InputStream cqlStream, LibraryManager lib
public static Library translateLibrary(CqlTranslator translator) {
return readLibrary(new ByteArrayInputStream(translator.toXml().getBytes(StandardCharsets.UTF_8)));
}

public static CqlTranslatorOptions getTranslatorOptions() {
CqlTranslatorOptions cqlTranslatorOptions = new CqlTranslatorOptions();
cqlTranslatorOptions.getOptions().addAll(generateDefaultTranslatorOptions());
cqlTranslatorOptions.setAnalyzeDataRequirements(true);
cqlTranslatorOptions.setCollapseDataRequirements(true);
return cqlTranslatorOptions;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
package org.opencds.cqf.common.providers;

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.CanonicalType;
import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.Library;
import org.hl7.fhir.r5.model.Measure;
import org.hl7.fhir.r5.model.Meta;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

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

@Component
public class CommonDataRequirementsProvider {

private static final Logger logger = LoggerFactory.getLogger(CommonDataRequirementsProvider.class);

private static String EXTENSION_URL_PARAMETER = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-parameter";
private static String EXTENSION_URL_DATA_REQUIREMENT = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-dataRequirement";
private static String EXTENSION_URL_REFERENCE_CODE = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-directReferenceCode";
private static String EXTENSION_URL_LOGIC_DEFINITION = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-logicDefinition";
private static String EXTENSION_URL_COMPUTABLE_MEASURE = "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/computable-measure-cqfm";


public Measure createMeasure(Measure measureToUse, LibraryManager libraryManager, TranslatedLibrary translatedLibrary, CqlTranslatorOptions options) {

Library moduleDefinitionLibrary = getModuleDefinitionLibrary(measureToUse, libraryManager, translatedLibrary, options);

measureToUse.setDate(new Date());
setMeta(measureToUse, moduleDefinitionLibrary);
clearMeasureExtensions(measureToUse, EXTENSION_URL_PARAMETER);
clearMeasureExtensions(measureToUse, EXTENSION_URL_DATA_REQUIREMENT);
clearMeasureExtensions(measureToUse, EXTENSION_URL_REFERENCE_CODE);
clearMeasureExtensions(measureToUse, EXTENSION_URL_LOGIC_DEFINITION);
clearRelatedArtifacts(measureToUse);
setParameters(measureToUse, moduleDefinitionLibrary);
setDataRequirements(measureToUse, moduleDefinitionLibrary);
setDirectReferenceCode(measureToUse, moduleDefinitionLibrary);
setLogicDefinition(measureToUse, moduleDefinitionLibrary);
measureToUse.setRelatedArtifact(moduleDefinitionLibrary.getRelatedArtifact());

return measureToUse;
}

public Library getModuleDefinitionLibrary(org.hl7.fhir.r5.model.Measure measureToUse, LibraryManager libraryManager, TranslatedLibrary translatedLibrary, CqlTranslatorOptions options) {

Set<String> expressionList = getExpressions(measureToUse);
DataRequirementsProcessor dqReqTrans = new DataRequirementsProcessor();

Library library = dqReqTrans.gatherDataRequirements(libraryManager, translatedLibrary, options, expressionList, true);
return library;
}

private Set<String> getExpressions(Measure measureToUse) {
Set<String> expressionSet = new HashSet<>();
measureToUse.getSupplementalData().forEach(supData -> {
expressionSet.add(supData.getCriteria().getExpression());
});
measureToUse.getGroup().forEach(groupMember -> {
groupMember.getPopulation().forEach(population -> {
expressionSet.add(population.getCriteria().getExpression());
});
groupMember.getStratifier().forEach(stratifier -> {
expressionSet.add(stratifier.getCriteria().getExpression());
});
});
return expressionSet;
}

private void clearMeasureExtensions(Measure measure, String extensionUrl) {
List<Extension> extensionsToRemove = measure.getExtensionsByUrl(extensionUrl);
measure.getExtension().removeAll(extensionsToRemove);
}

private void clearRelatedArtifacts(Measure measure) {
measure.getRelatedArtifact().removeIf(r -> r.getType() == org.hl7.fhir.r5.model.RelatedArtifact.RelatedArtifactType.DEPENDSON);
}

private void setLogicDefinition(Measure measureToUse, org.hl7.fhir.r5.model.Library moduleDefinitionLibrary) {
moduleDefinitionLibrary.getExtension().forEach(extension -> {
if (extension.getUrl().equalsIgnoreCase(EXTENSION_URL_LOGIC_DEFINITION)) {
measureToUse.addExtension(extension);
}
});
}

private void setDirectReferenceCode(Measure measureToUse, org.hl7.fhir.r5.model.Library moduleDefinitionLibrary) {
moduleDefinitionLibrary.getExtension().forEach(extension -> {
if (extension.getUrl().equalsIgnoreCase(EXTENSION_URL_REFERENCE_CODE)) {
measureToUse.addExtension(extension);
}
});
}

private void setDataRequirements(Measure measureToUse, org.hl7.fhir.r5.model.Library moduleDefinitionLibrary) {
moduleDefinitionLibrary.getDataRequirement().forEach(dataRequirement -> {
Extension dataReqExtension = new Extension();
dataReqExtension.setUrl(EXTENSION_URL_DATA_REQUIREMENT);
dataReqExtension.setValue(dataRequirement);
measureToUse.addExtension(dataReqExtension);
});
}

private void setParameters(Measure measureToUse, org.hl7.fhir.r5.model.Library moduleDefinitionLibrary) {
Set<String> parameterName = new HashSet<>();
moduleDefinitionLibrary.getParameter().forEach(parameter -> {
if (!parameterName.contains(parameter.getName())) {
Extension parameterExtension = new Extension();
parameterExtension.setUrl(EXTENSION_URL_PARAMETER);
parameterExtension.setValue(parameter);
measureToUse.addExtension(parameterExtension);
parameterName.add(parameter.getName());
}
});
}

private void setMeta(Measure measureToUse, org.hl7.fhir.r5.model.Library moduleDefinitionLibrary) {
if (measureToUse.getMeta() == null) {
measureToUse.setMeta(new Meta());
}
boolean hasProfileMarker = false;
for (CanonicalType canonical : measureToUse.getMeta().getProfile()) {
if (EXTENSION_URL_COMPUTABLE_MEASURE.equals(canonical.getValue())) {
hasProfileMarker = true;
}
}
if (!hasProfileMarker) {
measureToUse.getMeta().addProfile(EXTENSION_URL_COMPUTABLE_MEASURE);
}
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.opencds.cqf.dstu3.helpers;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -89,6 +92,21 @@ protected Boolean translatorOptionsMatch(Library library) {
};
}

public static InputStream extractContentStream(org.hl7.fhir.dstu3.model.Library library) {
org.hl7.fhir.dstu3.model.Attachment cql = null;
for (org.hl7.fhir.dstu3.model.Attachment a : library.getContent()) {
if (a.getContentType().equals("text/cql")) {
cql = a;
break;
}
}

if (cql == null) {
return null;
}
return new ByteArrayInputStream(Base64.getDecoder().decode(cql.getDataElement().getValueAsString()));
}

// @Override
// public org.opencds.cqf.cql.engine.execution.LibraryLoader createLibraryLoader(
// org.cqframework.cql.cql2elm.LibrarySourceProvider provider) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
Expand All @@ -31,8 +32,10 @@
import org.apache.commons.lang3.tuple.Pair;
import org.apache.commons.lang3.tuple.Triple;
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.cql2elm.model.TranslatedLibrary;
import org.cqframework.cql.elm.execution.CodeDef;
import org.cqframework.cql.elm.execution.CodeSystemDef;
import org.cqframework.cql.elm.execution.ExpressionDef;
Expand All @@ -44,6 +47,7 @@
import org.cqframework.cql.tools.formatter.CqlFormatterVisitor;
import org.cqframework.cql.tools.formatter.CqlFormatterVisitor.FormatResult;
import org.hl7.elm.r1.ValueSetRef;
import org.hl7.fhir.convertors.VersionConvertor_30_50;
import org.hl7.fhir.dstu3.model.Attachment;
import org.hl7.fhir.dstu3.model.Base64BinaryType;
import org.hl7.fhir.dstu3.model.CodeableConcept;
Expand All @@ -62,6 +66,7 @@
import org.hl7.fhir.dstu3.model.StringType;
import org.hl7.fhir.dstu3.model.Type;
import org.opencds.cqf.common.helpers.TranslatorHelper;
import org.opencds.cqf.common.providers.CommonDataRequirementsProvider;
import org.opencds.cqf.cql.engine.execution.LibraryLoader;
import org.opencds.cqf.tooling.measure.stu3.CodeTerminologyRef;
import org.opencds.cqf.tooling.measure.stu3.CqfMeasure;
Expand All @@ -78,12 +83,43 @@
public class DataRequirementsProvider {

private LibraryHelper libraryHelper;
private CommonDataRequirementsProvider dataRequirementsProvider;

@Inject
public DataRequirementsProvider(LibraryHelper libraryHelper) {
public DataRequirementsProvider(CommonDataRequirementsProvider dataRequirementsProvider,
LibraryHelper libraryHelper) {
this.dataRequirementsProvider = dataRequirementsProvider;
this.libraryHelper = libraryHelper;
}


public org.hl7.fhir.dstu3.model.Library getModuleDefinitionLibrary(org.hl7.fhir.dstu3.model.Measure measure, LibraryManager libraryManager, TranslatedLibrary translatedLibrary, CqlTranslatorOptions options) {
org.hl7.fhir.r5.model.Measure measureR5 = (org.hl7.fhir.r5.model.Measure) VersionConvertor_30_50.convertResource(measure);

org.hl7.fhir.r5.model.Library libraryR5 = dataRequirementsProvider.getModuleDefinitionLibrary(measureR5, libraryManager, translatedLibrary, options);
org.hl7.fhir.dstu3.model.Library libraryDstu3 = (org.hl7.fhir.dstu3.model.Library) VersionConvertor_30_50.convertResource(libraryR5);
return libraryDstu3;
}

public org.hl7.fhir.dstu3.model.Library getLibraryFromMeasure(org.hl7.fhir.dstu3.model.Measure measure,
LibraryResolutionProvider<org.hl7.fhir.dstu3.model.Library> libraryResourceProvider) {
org.hl7.fhir.dstu3.model.Library primaryLibrary = null;
Iterator var6 = measure.getLibrary().iterator();

String id = null;
//use the first library
while (var6.hasNext() && id == null) {
Reference ref = (Reference)var6.next();
id = ref.getReferenceElement().getIdPart();
if (id.startsWith("#")) {
id = id.substring(1);
}
}
primaryLibrary = (org.hl7.fhir.dstu3.model.Library)libraryResourceProvider.resolveLibraryById(id);

return primaryLibrary;
}

// For creating the CQF measure we need to:
// 1. Find the Primary Library Resource
// 2. Load the Primary Library as ELM. This will recursively load the dependent
Expand Down Expand Up @@ -498,13 +534,6 @@ private String markdownToHtml(Parser parser, HtmlRenderer renderer, String markd
return renderer.render(document);
}

public org.hl7.fhir.dstu3.model.Library getDataRequirements(Measure measure,
LibraryResolutionProvider<org.hl7.fhir.dstu3.model.Library> libraryResourceProvider) {
Map<VersionedIdentifier, Pair<Library, org.hl7.fhir.dstu3.model.Library>> libraryMap = this
.createLibraryMap(measure, libraryResourceProvider);
return this.getDataRequirements(measure, libraryMap.values().stream().map(x -> x.getRight())
.filter(x -> x != null).collect(Collectors.toList()));
}

private org.hl7.fhir.dstu3.model.Library getDataRequirements(Measure measure,
Collection<org.hl7.fhir.dstu3.model.Library> libraries) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;

import org.cqframework.cql.cql2elm.CqlTranslator;
import org.cqframework.cql.cql2elm.LibraryManager;
import org.cqframework.cql.cql2elm.ModelManager;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.CodeableConcept;
Expand Down Expand Up @@ -40,6 +43,7 @@
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.opencds.cqf.common.config.HapiProperties;
import org.opencds.cqf.common.evaluation.EvaluationProviderFactory;
import org.opencds.cqf.common.helpers.TranslatorHelper;
import org.opencds.cqf.cql.engine.data.DataProvider;
import org.opencds.cqf.cql.engine.execution.LibraryLoader;
import org.opencds.cqf.dstu3.evaluation.MeasureEvaluation;
Expand Down Expand Up @@ -634,7 +638,23 @@ public org.hl7.fhir.dstu3.model.Library dataRequirements(@IdParam IdType theId,
@OperationParam(name = "endPeriod") String endPeriod) throws InternalErrorException, FHIRException {

Measure measure = this.measureResourceProvider.getDao().read(theId);
return this.dataRequirementsProvider.getDataRequirements(measure, this.libraryResolutionProvider);

ModelManager modelManager = libraryHelper.getModelManager();
LibraryManager libraryManager = libraryHelper.getLibraryManager(libraryResolutionProvider);

Library library = this.dataRequirementsProvider.getLibraryFromMeasure(measure, libraryResolutionProvider);
if (library == null) {
throw new RuntimeException("Could not load measure library.");
}

CqlTranslator translator = TranslatorHelper.getTranslator(
LibraryHelper.extractContentStream(library), libraryManager, modelManager);
if (translator.getErrors().size() > 0) {
throw new RuntimeException("Errors during library compilation.");
}

return this.dataRequirementsProvider.getModuleDefinitionLibrary(measure, libraryManager,
translator.getTranslatedLibrary(), TranslatorHelper.getTranslatorOptions());
}

@SuppressWarnings("unchecked")
Expand Down
18 changes: 18 additions & 0 deletions r4/src/main/java/org/opencds/cqf/r4/helpers/LibraryHelper.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package org.opencds.cqf.r4.helpers;

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.Base64;
import java.util.Collections;
import java.util.List;
import java.util.Map;
Expand Down Expand Up @@ -74,4 +77,19 @@ public org.opencds.cqf.cql.engine.execution.LibraryLoader createLibraryLoader(

return new CacheAwareLibraryLoaderDecorator(translatingLibraryLoader, libraryCache);
}

public static InputStream extractContentStream(org.hl7.fhir.r4.model.Library library) {
Attachment cql = null;
for (Attachment a : library.getContent()) {
if (a.getContentType().equals("text/cql")) {
cql = a;
break;
}
}

if (cql == null) {
return null;
}
return new ByteArrayInputStream(Base64.getDecoder().decode(cql.getDataElement().getValueAsString()));
}
}
Loading