Skip to content

Commit

Permalink
Merge pull request #340 from DBCG/data-requirements-use-translator
Browse files Browse the repository at this point in the history
Add DataRequrementsProcessor from cqframework elm
  • Loading branch information
Adam Stevenson authored Oct 26, 2021
2 parents a4bd9e1 + 49c5816 commit 4e36c3a
Show file tree
Hide file tree
Showing 8 changed files with 350 additions and 20 deletions.
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

0 comments on commit 4e36c3a

Please sign in to comment.