Skip to content

Commit

Permalink
Br fixes 2023 07 10 (cqframework#1177)
Browse files Browse the repository at this point in the history
* cqframework#1156: Added test cases for CMS143 and CMS149 to validate data requirements output

* cqframework#1147: Fixed invalid let ref during data requirements inference

* cqframework#1146: Added test for MedicationRequest to ensure expected ELM output

* cqframework#1109: Fixed newlines being stripped incorrectly in mixed single- and multi-line comments
  • Loading branch information
brynrhodes authored Jul 11, 2023
1 parent 5ad1db1 commit 761b314
Show file tree
Hide file tree
Showing 23 changed files with 4,754 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -576,25 +576,32 @@ private Narrative buildNarrative(Chunk chunk) {
else {
String chunkContent = tokenStream.getText(chunk.getInterval());
if (chunk.isHeaderChunk()) {
chunkContent = chunkContent.trim();
chunkContent = stripLeading(chunkContent);
}
chunkContent = normalizeWhitespace(chunkContent);
chunkContent = makeSeparationBetweenCommentAndDefinition(chunkContent);
narrative.getContent().add(chunkContent);
}

return narrative;
}

private String normalizeWhitespace(String input) {
return input.replace("\r\n", "\n");
// TODO: Should just use String.stripLeading() but that is only available in 11+
private String stripLeading(String s) {
int index = 0;
while (index < s.length()) {
if (!Character.isWhitespace(s.charAt(index))) {
break;
}
index++;
}
if (index == s.length()) {
return "";
}
return s.substring(index);
}

private String makeSeparationBetweenCommentAndDefinition(String input) {
if(StringUtils.startsWith(input, "//") || StringUtils.endsWith(input, "*/")) {
return input + "\n";
}
return input;
private String normalizeWhitespace(String input) {
return input.replace("\r\n", "\n");
}

private boolean hasChunks(Narrative narrative) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,22 @@

import org.cqframework.cql.cql2elm.model.CompiledLibrary;
import org.hl7.cql_annotations.r1.Annotation;
import org.hl7.cql_annotations.r1.CqlToElmBase;
import org.hl7.cql_annotations.r1.Narrative;
import org.hl7.cql_annotations.r1.Tag;
import org.hl7.elm.r1.ExpressionDef;
import org.hl7.elm.r1.FunctionDef;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;

import javax.xml.bind.JAXBElement;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertTrue;

public class CommentTests {
@BeforeClass
Expand All @@ -25,6 +31,57 @@ public void testComments() throws IOException {
CqlTranslator translator = TestUtils.runSemanticTest("TestComments.cql", 0, CqlTranslatorOptions.Options.EnableAnnotations);
CompiledLibrary library = translator.getTranslatedLibrary();
assertThat(library.getLibrary().getAnnotation(), notNullValue());

Map<String, ExpressionDef> defs = new HashMap<>();

if (library.getLibrary().getStatements() != null) {
for (ExpressionDef def : library.getLibrary().getStatements().getDef()) {
defs.put(def.getName(), def);
}
}

// Validate that boolIpp has appropriate comment value
// Comment should be: "/* Multi-line works fine */\n// Single-line comment does not work\n"
ExpressionDef def = defs.get("boolIpp");
assertThat(def, notNullValue());
assertThat(def.getAnnotation(), notNullValue());
assertThat(def.getAnnotation().size(), is(1));
assertThat(def.getAnnotation().get(0), instanceOf(Annotation.class));
Annotation a = (Annotation)def.getAnnotation().get(0);
assertThat(a.getS().getContent(), notNullValue());
assertThat(a.getS().getContent().size(), is(2));
assertThat(a.getS().getContent().get(0), instanceOf(JAXBElement.class));
JAXBElement e = (JAXBElement)a.getS().getContent().get(0);
assertThat(e, notNullValue());
assertThat(e.getValue(), instanceOf(Narrative.class));
Narrative n = (Narrative)e.getValue();
assertThat(n.getContent(), notNullValue());
assertThat(n.getContent().size(), is(4));
assertThat(n.getContent().get(0), instanceOf(String.class));
String s = (String)n.getContent().get(0);
assertThat(s, is("/* Multi-line works fine */\n// Single-line comment does not work\n"));


// Validate that singleLineCommentTest has appropriate comment value
// Comment should be: "// Unmixed single-line comment works\n"
def = defs.get("singleLineCommentTest");
assertThat(def, notNullValue());
assertThat(def.getAnnotation(), notNullValue());
assertThat(def.getAnnotation().size(), is(1));
assertThat(def.getAnnotation().get(0), instanceOf(Annotation.class));
a = (Annotation)def.getAnnotation().get(0);
assertThat(a.getS().getContent(), notNullValue());
assertThat(a.getS().getContent().size(), is(2));
assertThat(a.getS().getContent().get(0), instanceOf(JAXBElement.class));
e = (JAXBElement)a.getS().getContent().get(0);
assertThat(e, notNullValue());
assertThat(e.getValue(), instanceOf(Narrative.class));
n = (Narrative)e.getValue();
assertThat(n.getContent(), notNullValue());
assertThat(n.getContent().size(), is(4));
assertThat(n.getContent().get(0), instanceOf(String.class));
s = (String)n.getContent().get(0);
assertThat(s, is("// Unmixed single-line comment works\n"));
}

@Test
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,33 @@ public void testPapTestWithResults() throws IOException {
assertThat(p.getScope(), equalTo("PapTest"));
}

@Test
public void TestMedicationRequest() throws IOException {
CqlTranslator translator = TestUtils.runSemanticTest("qicore/v411/TestMedicationRequest.cql", 0);
Library library = translator.toELM();
Map<String, ExpressionDef> defs = new HashMap<>();

if (library.getStatements() != null) {
for (ExpressionDef def : library.getStatements().getDef()) {
defs.put(def.getName(), def);
}
}

ExpressionDef def = defs.get("Antithrombotic Therapy at Discharge");
assertThat(def, notNullValue());
assertThat(def.getExpression(), instanceOf(Query.class));
Query q = (Query)def.getExpression();
assertThat(q.getSource().size(), is(1));
assertThat(q.getSource().get(0).getExpression(), instanceOf(Retrieve.class));
Retrieve r = (Retrieve)q.getSource().get(0).getExpression();
assertThat(r.getTemplateId(), is("http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-medicationrequest"));
assertThat(r.getCodeProperty(), is("medication"));
assertThat(r.getCodeComparator(), is("in"));
assertThat(r.getCodes(), instanceOf(ValueSetRef.class));
ValueSetRef vsr = (ValueSetRef)r.getCodes();
assertThat(vsr.getName(), is("Antithrombotic Therapy"));
}

@Test
public void TestChoiceUnion() throws IOException {
CqlTranslator translator = TestUtils.runSemanticTest("qicore/v411/TestChoiceUnion.cql", 0);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,11 @@ to ensure overlapping intervals do not contribute multiple times to the result#
define function CumulativeDuration(Intervals List<Interval<DateTime>>):
Sum((collapse Intervals) X return all duration in days of X)

/* Multi-line works fine */
// Single-line comment does not work
define "boolIpp":
exists ["Encounter"] E where E.period.start during MeasurementPeriod

// Unmixed single-line comment works
define "singleLineCommentTest":
exists ["Encounter"] E where E.period.start during "MeasurementPeriod"
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
library TestMedicationRequest

using QICore version '4.1.1'

include FHIRHelpers version '4.0.1'

valueset "Antithrombotic Therapy": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1110.62'

context Patient

define "Antithrombotic Therapy at Discharge":
["MedicationRequest": medication in "Antithrombotic Therapy"] Antithrombotic
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ public ElmQueryLetContext resolveLet(String letName) {
ElmQueryLetContext letContext = null;
for (ElmQueryContext queryContext : queryStack) {
letContext = queryContext.resolveLet(letName);
if (letName != null) {
if (letContext != null) {
break;
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1661,6 +1661,7 @@ private void assertEqualToExpectedModuleDefinitionLibrary(org.hl7.fhir.r5.model.
IParser parser = context.newJsonParser();
org.hl7.fhir.r5.model.Library expectedModuleDefinitionLibrary = (org.hl7.fhir.r5.model.Library)parser.parseResource(DataRequirementsProcessorTest.class.getResourceAsStream(pathToExpectedModuleDefinitionLibrary));
assertNotNull(expectedModuleDefinitionLibrary);
//outputModuleDefinitionLibrary(actualModuleDefinitionLibrary);
actualModuleDefinitionLibrary.setDate(null);
expectedModuleDefinitionLibrary.setDate(null);
assertTrue(actualModuleDefinitionLibrary.equalsDeep(expectedModuleDefinitionLibrary));
Expand Down Expand Up @@ -1690,6 +1691,42 @@ public void TestWithDependencies() throws IOException {
//outputModuleDefinitionLibrary(moduleDefinitionLibrary);
}

@Test
public void TestCMS645() throws IOException {
CqlTranslatorOptions translatorOptions = getTranslatorOptions();
translatorOptions.setAnalyzeDataRequirements(false);
CqlTranslator translator = setupDataRequirementsAnalysis("CMS645/CMS645Test.cql", translatorOptions);
org.hl7.fhir.r5.model.Library moduleDefinitionLibrary = getModuleDefinitionLibrary(translator, translatorOptions, new HashMap<String, Object>(), ZonedDateTime.of(2023, 1, 16, 0, 0, 0, 0, ZoneId.of("UTC")));
assertNotNull(moduleDefinitionLibrary);
assertEqualToExpectedModuleDefinitionLibrary(moduleDefinitionLibrary, "CMS645/CMS645-ModuleDefinitionLibrary.json");

//outputModuleDefinitionLibrary(moduleDefinitionLibrary);
}

@Test
public void TestCMS143() throws IOException {
CqlTranslatorOptions translatorOptions = getTranslatorOptions();
translatorOptions.setAnalyzeDataRequirements(false);
CqlTranslator translator = setupDataRequirementsAnalysis("CMS143/cql/POAGOpticNerveEvaluationFHIR-0.0.003.cql", translatorOptions);
org.hl7.fhir.r5.model.Library moduleDefinitionLibrary = getModuleDefinitionLibrary(translator, translatorOptions, new HashMap<String, Object>(), ZonedDateTime.of(2023, 1, 16, 0, 0, 0, 0, ZoneId.of("UTC")));
assertNotNull(moduleDefinitionLibrary);
assertEqualToExpectedModuleDefinitionLibrary(moduleDefinitionLibrary, "CMS143/resources/Library-EffectiveDataRequirements.json");

//outputModuleDefinitionLibrary(moduleDefinitionLibrary);
}

@Test
public void TestCMS149() throws IOException {
CqlTranslatorOptions translatorOptions = getTranslatorOptions();
translatorOptions.setAnalyzeDataRequirements(false);
CqlTranslator translator = setupDataRequirementsAnalysis("CMS149/cql/DementiaCognitiveAssessmentFHIR-0.0.003.cql", translatorOptions);
org.hl7.fhir.r5.model.Library moduleDefinitionLibrary = getModuleDefinitionLibrary(translator, translatorOptions, new HashMap<String, Object>(), ZonedDateTime.of(2023, 1, 16, 0, 0, 0, 0, ZoneId.of("UTC")));
assertNotNull(moduleDefinitionLibrary);
assertEqualToExpectedModuleDefinitionLibrary(moduleDefinitionLibrary, "CMS149/resources/Library-EffectiveDataRequirements.json");

//outputModuleDefinitionLibrary(moduleDefinitionLibrary);
}

@Test
public void TestDataRequirementsProcessorWithPertinence() {
CqlTranslatorOptions cqlTranslatorOptions = new CqlTranslatorOptions();
Expand Down
Loading

0 comments on commit 761b314

Please sign in to comment.