Skip to content

Commit

Permalink
Merge pull request #299 from MeasureAuthoringTool/MAT-6664
Browse files Browse the repository at this point in the history
MAT-6664 QDM - If CQL has a retrieve without a filter ( for value set or direct reference code), display error message to user
  • Loading branch information
adongare authored Jan 8, 2025
2 parents c15a811 + 7ef543a commit 19f8655
Show file tree
Hide file tree
Showing 3 changed files with 126 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,18 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.cqframework.cql.cql2elm.CqlCompilerException;
import org.cqframework.cql.cql2elm.CqlTranslator;
import org.cqframework.cql.cql2elm.LibraryContentType;
import org.cqframework.cql.cql2elm.model.CompiledLibrary;
import org.cqframework.cql.elm.serializing.ElmLibraryWriterFactory;
import org.cqframework.cql.elm.tracking.TrackBack;
import org.hl7.elm.r1.CodeSystemRef;
import org.hl7.elm.r1.Library;
import org.hl7.elm.r1.Retrieve;
import org.hl7.elm.r1.VersionedIdentifier;
import org.springframework.stereotype.Service;

Expand Down Expand Up @@ -105,6 +109,7 @@ public void processForLibraryRulesExceptions(CqlTranslator cqlTranslator, String
new MadieCqlValidator().checkNoDuplicateIncludes(cqlTranslator, includes);
}
}
validateRetrieve(cqlTranslator);
}

public List<TranslatedLibrary> getTranslatedLibrariesForCql(String cql, String accessToken)
Expand Down Expand Up @@ -154,6 +159,33 @@ public TranslatedLibrary buildTranslatedLibrary(
}
}

/**
* This method validate retrieves for presence of value set or code filter. If one doesn't have
* it, create a CqlCompilerException and add it to the compiler exceptions
*
* @param cqlTranslator - an instance of CqlTranslator
*/
public void validateRetrieve(CqlTranslator cqlTranslator) {
List<Retrieve> retrieves = cqlTranslator.toRetrieves();
if (!CollectionUtils.isEmpty(cqlTranslator.toRetrieves())) {
List<CqlCompilerException> exceptions =
retrieves.stream()
.filter(
retrieve ->
retrieve.getCodes() == null || retrieve.getCodes() instanceof CodeSystemRef)
.map(
retrieve -> {
TrackBack trackable = retrieve.getTrackbacks().get(0);
return new CqlCompilerException(
"Retrieves must contain a code or value set filter", trackable);
})
.toList();
if (!CollectionUtils.isEmpty(exceptions)) {
cqlTranslator.getExceptions().addAll(exceptions);
}
}
}

private TranslatedLibrary buildTranslatedLibrary(Library library, String cql) throws IOException {
VersionedIdentifier identifier = library.getIdentifier();
String elmJson = convertToJson(library, LibraryContentType.JSON);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import gov.cms.madie.cql_elm_translator.utils.cql.cql_translator.TranslationResource;
import gov.cms.madie.models.dto.TranslatedLibrary;
import gov.cms.mat.cql.CqlTextParser;
import gov.cms.mat.cql.dto.CqlConversionPayload;
Expand All @@ -12,6 +13,7 @@
import gov.cms.madie.cql_elm_translator.exceptions.InternalServerException;
import gov.cms.madie.cql_elm_translator.service.CqlLibraryService;

import org.cqframework.cql.cql2elm.CqlTranslator;
import org.cqframework.cql.cql2elm.LibraryContentType;
import org.cqframework.cql.cql2elm.model.CompiledLibrary;
import org.hl7.elm.r1.Library;
Expand Down Expand Up @@ -266,4 +268,52 @@ void testGetTranslatedLibrariesForCqlIncludedLibraryNull() throws IOException {
.findFirst();
assertThat(matchingLib.get().getName(), is(equalTo("DataCriteriaRetrivalTest")));
}

@Test
void testValidateRetrieveWithNoCodeOrValueSetFoundShouldRaiseErrors() {
String cql = getData("/cql_retrieve.cql");
RequestData requestData =
RequestData.builder()
.cqlData(cql)
.showWarnings(false)
.annotations(false)
.locators(true)
.disableListDemotion(true)
.disableListPromotion(true)
.disableMethodInvocation(true)
.validateUnits(false)
.resultTypes(true)
.build();

CqlTranslator cqlTranslator =
TranslationResource.getInstance(true).buildTranslator(requestData);
assertThat(cqlTranslator.getExceptions().size(), is(equalTo(0)));
service.validateRetrieve(cqlTranslator);
assertThat(cqlTranslator.getExceptions().size(), is(equalTo(3)));
assertThat(
cqlTranslator.getExceptions().get(0).getMessage(),
is(equalTo("Retrieves must contain a code or value set filter")));
}

@Test
void testValidateRetrieveIfNoRetrieveFoundShouldNotRaiseErrors() {
RequestData requestData =
RequestData.builder()
.cqlData("")
.showWarnings(false)
.annotations(false)
.locators(true)
.disableListDemotion(true)
.disableListPromotion(true)
.disableMethodInvocation(true)
.validateUnits(false)
.resultTypes(true)
.build();

CqlTranslator cqlTranslator =
TranslationResource.getInstance(true).buildTranslator(requestData);
assertThat(cqlTranslator.getExceptions().size(), is(equalTo(0)));
service.validateRetrieve(cqlTranslator);
assertThat(cqlTranslator.getExceptions().size(), is(equalTo(0)));
}
}
44 changes: 44 additions & 0 deletions src/test/resources/cql_retrieve.cql
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
library TestRetriveValidation version '1.0.000'

using QDM version '5.6'

codesystem "LOINC": 'urn:oid:2.16.840.1.113883.6.1'
codesystem "SNOMEDCT": 'urn:oid:2.16.840.1.113883.6.96'


valueset "Office Visit": 'urn:oid:2.16.840.1.113883.3.464.1003.101.12.1001'
valueset "Annual Wellness Visit": 'urn:oid:2.16.840.1.113883.3.526.3.1240'

code "Acute Pharyngitis Code":
'363746003' from "SNOMEDCT" display 'Acute pharyngitis (disorder)'

parameter "Measurement Period" Interval<DateTime>

context Patient

define "Initial Population":
AgeInYearsAt(date from
end of "Measurement Period"
)in Interval[52, 74]

define "Get Encounter from valueset Retrieve":
["Encounter, Performed": "Office Visit"]

define "Get Encounter From Code Retrieve":
["Encounter, Performed": "Acute Pharyngitis Code"]

define "Get Encounter from Code Declaration":
["Encounter, Performed": code ~ "Acute Pharyngitis Code"]

// invalid
define "Get Encounter from CodeSystem Declaration":
["Encounter, Performed": code in "SNOMEDCT"]

// invalid
define "Get Diagnosis from CodeSystem Declaration":
["Diagnosis": "SNOMEDCT"]

// invalid
define "Retrieve with no filter":
["Encounter, Performed"] ValidEncounter
where ValidEncounter.relevantPeriod during "Measurement Period"

0 comments on commit 19f8655

Please sign in to comment.