Skip to content

Commit

Permalink
Merge pull request #31 from MeasureAuthoringTool/feature/MAT-7801-res…
Browse files Browse the repository at this point in the history
…ulttype-null-fix

MAT-7801: handle possible null expression result type when building expressionToReturnTypeMap
  • Loading branch information
nmorasb authored Jan 8, 2025
2 parents 57a4cb4 + 938c9dd commit 47c36db
Show file tree
Hide file tree
Showing 4 changed files with 143 additions and 5 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>gov.cms.madie</groupId>
<artifactId>madie-translator-commons</artifactId>
<version>2.0.2-SNAPSHOT</version>
<version>2.0.3-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<java.version>17</java.version>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -398,12 +398,13 @@ private void collectReturnTypeMap() {
this.allNamesToReturnTypeMap.put(libraryName + "-" + libraryVersion, new HashMap<>());

for (ExpressionDef expression : statements.getDef()) {
String resultType =
expression.getResultType() == null ? null : expression.getResultType().toString();
this.allNamesToReturnTypeMap
.get(libraryName + "-" + libraryVersion)
.put(expression.getName(), expression.getResultType().toString());
this.nameToReturnTypeMap.put(expression.getName(), expression.getResultType().toString());
this.expressionToReturnTypeMap.put(
expression.getName(), expression.getResultType().toString());
.put(expression.getName(), resultType);
this.nameToReturnTypeMap.put(expression.getName(), resultType);
this.expressionToReturnTypeMap.put(expression.getName(), resultType);
}

if (parameters != null) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
package gov.cms.madie.cql_elm_translator.utils.cql;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import lombok.extern.slf4j.Slf4j;
import org.cqframework.cql.cql2elm.CqlTranslator;
import org.cqframework.cql.cql2elm.model.CompiledLibrary;
import org.cqframework.cql.cql2elm.model.ResolvedIdentifierContext;
import org.hl7.cql.model.DataType;
import org.hl7.elm.r1.IncludeDef;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.Mock;
Expand All @@ -26,13 +37,16 @@
import org.hl7.elm.r1.ExpressionDef;

@ExtendWith(MockitoExtension.class)
@Slf4j
class CQLToolsTest {

@Mock private CqlTranslator cqlTranslator;
@Mock private CompiledLibrary compiledLibrary;
@Mock private Library library;
@Mock private ResolvedIdentifierContext element;
@Mock private Statements statements;
@Mock private Library.Includes includes;
@Mock private Map<String, CompiledLibrary> compiledLibraryMap;

VersionedIdentifier versionedIdentifier = new VersionedIdentifier();

Expand All @@ -59,4 +73,66 @@ void testGenerate() {
fail();
}
}

@Test
void testItHandlesNullExpressionResultType() {
versionedIdentifier.setId("local");
String cqlData = ResourceUtils.getData("/tooling_test_qicore6.cql");
doReturn(statements).when(library).getStatements();
doReturn(compiledLibrary).when(cqlTranslator).getTranslatedLibrary();
doReturn(library).when(compiledLibrary).getLibrary();
doReturn(versionedIdentifier).when(compiledLibrary).getIdentifier();
doReturn(element).when(compiledLibrary).resolve(any(String.class));
doReturn(includes).when(library).getIncludes();

ExpressionDef expression1 = mock(ExpressionDef.class);
DataType booleanDataType = mock(DataType.class);
doReturn(booleanDataType).when(expression1).getResultType();
doReturn("expression1").when(expression1).getName();
doReturn("System.Boolean").when(booleanDataType).toString();

ExpressionDef expression2 = mock(ExpressionDef.class);
doReturn("expression2").when(expression2).getName();
doReturn(null).when(expression2).getResultType();

List<ExpressionDef> expressionDefs = List.of(expression1, expression2);

doReturn(expressionDefs).when(statements).getDef();

Set<String> parentExpressions = new HashSet<>();
IncludeDef includeDef1 = mock(IncludeDef.class);
List<IncludeDef> includeDefs = List.of(includeDef1);
doReturn(includeDefs).when(includes).getDef();
doReturn("SupplementalDataElements").when(includeDef1).getPath();
CompiledLibrary sdeCompiledLib = mock(CompiledLibrary.class);
Library sdeLib = mock(Library.class);
doReturn(sdeLib).when(sdeCompiledLib).getLibrary();
VersionedIdentifier sdeLibVersionedIdentifier = new VersionedIdentifier();
sdeLibVersionedIdentifier.setId("SupplementalDataElements");
doReturn(sdeLibVersionedIdentifier).when(sdeCompiledLib).getIdentifier();

Library.Statements statements1 = mock(Library.Statements.class);
doReturn(statements1).when(sdeLib).getStatements();
Library.Parameters parameters1 = mock(Library.Parameters.class);
doReturn(parameters1).when(sdeLib).getParameters();

doReturn(sdeCompiledLib).when(compiledLibraryMap).get(anyString());

CQLTools cqlTools =
new CQLTools(cqlData, null, parentExpressions, cqlTranslator, compiledLibraryMap);
assertNotNull(cqlTools);
try {
cqlTools.generate();
Set<DefinitionContent> contents = cqlTools.getDefinitionContents();
assertNotNull(contents);
Map<String, String> expressionToReturnTypeMap = cqlTools.getExpressionToReturnTypeMap();
assertThat(expressionToReturnTypeMap, is(notNullValue()));
assertThat(expressionToReturnTypeMap.containsKey("expression1"), is(true));
assertThat(expressionToReturnTypeMap.containsKey("expression2"), is(true));
assertThat(expressionToReturnTypeMap.get("expression1"), is(equalTo("System.Boolean")));
assertThat(expressionToReturnTypeMap.get("expression2"), is(nullValue()));
} catch (IOException e) {
fail();
}
}
}
61 changes: 61 additions & 0 deletions src/test/resources/tooling_test_qicore6.cql
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
library QM61 version '0.0.000'

using QICore version '6.0.0'

include QICoreCommon version '3.0.000' called QICoreCommon
include FHIRHelpers version '4.4.000' called FHIRHelpers
include SupplementalDataElements version '4.3.000' called SDE
include Hospice version '7.0.000' called Hospice
include Status version '2.0.000' called Status

codesystem "LOINC": 'http://loinc.org'
codesystem "SNOMEDCT": 'http://snomed.info/sct'

valueset "Clinical Oral Evaluation": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.125.12.1003'
valueset "Dental Caries": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.125.12.1004'
valueset "Discharged to Health Care Facility for Hospice Care": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.207'
valueset "Discharged to Home for Hospice Care": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.209'
valueset "Encounter Inpatient": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.666.5.307'

code "Discharge to healthcare facility for hospice care (procedure)": '428371000124100' from "SNOMEDCT" display 'Discharge to healthcare facility for hospice care (procedure)'
code "Discharge to home for hospice care (procedure)": '428361000124107' from "SNOMEDCT" display 'Discharge to home for hospice care (procedure)'
code "Hospice care [Minimum Data Set]": '45755-6' from "LOINC" display 'Hospice care [Minimum Data Set]'
code "Yes (qualifier value)": '373066001' from "SNOMEDCT" display 'Yes (qualifier value)'

parameter "Measurement Period" Interval<DateTime>

context Patient


define "Initial Population":
AgeInYearsAt(date from start of "Measurement Period")in Interval[1, 20]
and exists ( "Qualifying Encounters" )

define "Qualifying Encounters":
(([Encounter: "Clinical Oral Evaluation"]).isEncounterPerformed()) ValidEncounter
where ValidEncounter.period.toInterval() during day of "Measurement Period"

define "Denominator":
"Initial Population"

define "Denominator Exclusions":
Hospice."Has Hospice Services"

define "Numerator":
exists ["QICore Condition Problems Health Concerns": "Dental Caries"] DentalCaries
where DentalCaries.prevalenceInterval() overlaps "Measurement Period"

define "SDE Ethnicity":
SDE."SDE Ethnicity"

define "SDE Payer":
SDE."SDE Payer"

define "SDE Race":
SDE."SDE Race"

define "SDE Sex":
SDE."SDE Sex"

define "SDE Eth":
SDE."SDE Eth"

0 comments on commit 47c36db

Please sign in to comment.