Skip to content

Commit

Permalink
Merge pull request #762 from cqframework/fix-761-fhirpath-now-today
Browse files Browse the repository at this point in the history
#761: Fixed now(), today(), and timeOfDay() not resolving as FHIRPath…
  • Loading branch information
brynrhodes committed Jun 20, 2022
2 parents 2a71f51 + b46949c commit 7b59b96
Show file tree
Hide file tree
Showing 5 changed files with 144 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -176,15 +176,18 @@ else if (dateTimeConversion != null) {
return resolveTime(fun);
}

case "Now": {
case "Now":
case "now": {
return resolveNow(fun);
}

case "Today": {
case "Today":
case "today": {
return resolveToday(fun);
}

case "TimeOfDay": {
case "TimeOfDay":
case "timeOfDay": {
return resolveTimeOfDay(fun);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,8 @@ public Expression resolveMethod(Expression target, String functionName, cqlParse
case "matches": return builder.resolveFunction(null, "Matches", getParams(target, ctx));
case "memberOf": return builder.resolveFunction(null, "InValueSet", getParams(target, ctx));
case "not": return builder.resolveFunction(null, "Not", getParams(target, ctx));
case "now": return builder.resolveFunction(null, "Now", getParams(target, ctx));
//now could never resolve as a method because it has no arguments
//case "now": return builder.resolveFunction(null, "Now", getParams(target, ctx));
case "ofType": return createOfType(target, functionName, ctx);
case "power": return builder.resolveFunction(null, "Power", getParams(target, ctx));
case "repeat": return createRepeat(target, functionName, ctx);
Expand All @@ -478,12 +479,14 @@ public Expression resolveMethod(Expression target, String functionName, cqlParse
case "supersetOf": return builder.resolveFunction(null, "Includes", getParams(target, ctx));
case "tail": return builder.resolveFunction(null, "Tail", getParams(target, ctx));
case "take": return builder.resolveFunction(null, "Take", getParams(target, ctx));
case "timeOfDay": return builder.resolveFunction(null, "TimeOfDay", getParams(target, ctx));
//timeOfDay could never resolve as a method because it has no arguments
//case "timeOfDay": return builder.resolveFunction(null, "TimeOfDay", getParams(target, ctx));
case "toBoolean": return builder.resolveFunction(null, "ToBoolean", getParams(target, ctx));
case "toChars": return builder.resolveFunction(null, "ToChars", getParams(target, ctx));
case "toDate": return builder.resolveFunction(null, "ToDate", getParams(target, ctx));
case "toDateTime": return builder.resolveFunction(null, "ToDateTime", getParams(target, ctx));
case "today": return builder.resolveFunction(null, "Today", getParams(target, ctx));
//today could never resolve as a method because it has no arguments
//case "today": return builder.resolveFunction(null, "Today", getParams(target, ctx));
case "toDecimal": return builder.resolveFunction(null, "ToDecimal", getParams(target, ctx));
case "toInteger": return builder.resolveFunction(null, "ToInteger", getParams(target, ctx));
case "toQuantity": return builder.resolveFunction(null, "ToQuantity", getParams(target, ctx));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -496,6 +496,7 @@ public static TranslatedLibrary load(SystemModel systemModel, TypeBuilder tb) {
add(system, tb, new Operator("DurationBetween", new Signature(systemModel.getDate(), systemModel.getDate()), systemModel.getInteger()));
add(system, tb, new Operator("DurationBetween", new Signature(systemModel.getTime(), systemModel.getTime()), systemModel.getInteger()));
add(system, tb, new Operator("Now", new Signature(), systemModel.getDateTime()));
add(system, tb, new Operator("now", new Signature(), systemModel.getDateTime()));
add(system, tb, new Operator("SameAs", new Signature(systemModel.getDateTime(), systemModel.getDateTime()), systemModel.getBoolean()));
add(system, tb, new Operator("SameAs", new Signature(systemModel.getDate(), systemModel.getDate()), systemModel.getBoolean()));
add(system, tb, new Operator("SameAs", new Signature(systemModel.getTime(), systemModel.getTime()), systemModel.getBoolean()));
Expand All @@ -509,11 +510,13 @@ public static TranslatedLibrary load(SystemModel systemModel, TypeBuilder tb) {
add(system, tb, new Operator("Subtract", new Signature(systemModel.getDate(), systemModel.getQuantity()), systemModel.getDate()));
add(system, tb, new Operator("Subtract", new Signature(systemModel.getTime(), systemModel.getQuantity()), systemModel.getTime()));
add(system, tb, new Operator("Today", new Signature(), systemModel.getDate()));
add(system, tb, new Operator("today", new Signature(), systemModel.getDate()));
add(system, tb, new Operator("Time", new Signature(systemModel.getInteger()), systemModel.getTime()));
add(system, tb, new Operator("Time", new Signature(systemModel.getInteger(), systemModel.getInteger()), systemModel.getTime()));
add(system, tb, new Operator("Time", new Signature(systemModel.getInteger(), systemModel.getInteger(), systemModel.getInteger()), systemModel.getTime()));
add(system, tb, new Operator("Time", new Signature(systemModel.getInteger(), systemModel.getInteger(), systemModel.getInteger(), systemModel.getInteger()), systemModel.getTime()));
add(system, tb, new Operator("TimeOfDay", new Signature(), systemModel.getTime()));
add(system, tb, new Operator("timeOfDay", new Signature(), systemModel.getTime()));

// Interval Operators
// After<T>(interval<T>, interval<T>) : Boolean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -410,6 +410,114 @@ public void testFHIRWithoutNamespaces() throws IOException {
assertThat(includeDef.getVersion(), is("4.0.1"));
}

@Test
public void testFHIRPath() throws IOException {
CqlTranslator translator = TestUtils.runSemanticTest("fhir/r401/TestFHIRPath.cql", 0);
TranslatedLibrary library = translator.getTranslatedLibrary();
ExpressionDef expressionDef = library.resolveExpressionRef("TestNow");
assertThat(expressionDef, notNullValue());
assertThat(expressionDef.getExpression(), instanceOf(Now.class));
expressionDef = library.resolveExpressionRef("TestToday");
assertThat(expressionDef, notNullValue());
assertThat(expressionDef.getExpression(), instanceOf(Today.class));
expressionDef = library.resolveExpressionRef("TestTimeOfDay");
assertThat(expressionDef.getExpression(), instanceOf(TimeOfDay.class));
String xml = translator.toXml();
assertThat(xml, notNullValue());
/*
// Doesn't work because this literal adds carriage returns
assertThat(xml, is("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<library xmlns=\"urn:hl7-org:elm:r1\" xmlns:t=\"urn:hl7-org:elm-types:r1\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:fhir=\"http://hl7.org/fhir\" xmlns:qdm43=\"urn:healthit-gov:qdm:v4_3\" xmlns:qdm53=\"urn:healthit-gov:qdm:v5_3\" xmlns:a=\"urn:hl7-org:cql-annotations:r1\">\n" +
" <annotation translatorOptions=\"\" xsi:type=\"a:CqlToElmInfo\"/>\n" +
" <identifier id=\"TestFHIRPath\"/>\n" +
" <schemaIdentifier id=\"urn:hl7-org:elm\" version=\"r1\"/>\n" +
" <usings>\n" +
" <def localIdentifier=\"System\" uri=\"urn:hl7-org:elm-types:r1\"/>\n" +
" <def localIdentifier=\"FHIR\" uri=\"http://hl7.org/fhir\" version=\"4.0.1\"/>\n" +
" </usings>\n" +
" <includes>\n" +
" <def localIdentifier=\"FHIRHelpers\" path=\"FHIRHelpers\" version=\"4.0.1\"/>\n" +
" </includes>\n" +
" <contexts>\n" +
" <def name=\"Patient\"/>\n" +
" </contexts>\n" +
" <statements>\n" +
" <def name=\"Patient\" context=\"Patient\">\n" +
" <expression xsi:type=\"SingletonFrom\">\n" +
" <operand dataType=\"fhir:Patient\" templateId=\"http://hl7.org/fhir/StructureDefinition/Patient\" xsi:type=\"Retrieve\"/>\n" +
" </expression>\n" +
" </def>\n" +
" <def name=\"TestToday\" context=\"Patient\" accessLevel=\"Public\">\n" +
" <expression xsi:type=\"Today\"/>\n" +
" </def>\n" +
" <def name=\"TestNow\" context=\"Patient\" accessLevel=\"Public\">\n" +
" <expression xsi:type=\"Now\"/>\n" +
" </def>\n" +
" <def name=\"TestTimeOfDay\" context=\"Patient\" accessLevel=\"Public\">\n" +
" <expression xsi:type=\"TimeOfDay\"/>\n" +
" </def>\n" +
" <def name=\"Encounters\" context=\"Patient\" accessLevel=\"Public\">\n" +
" <expression dataType=\"fhir:Encounter\" templateId=\"http://hl7.org/fhir/StructureDefinition/Encounter\" xsi:type=\"Retrieve\"/>\n" +
" </def>\n" +
" <def name=\"TestTodayInWhere\" context=\"Patient\" accessLevel=\"Public\">\n" +
" <expression xsi:type=\"Query\">\n" +
" <source alias=\"$this\">\n" +
" <expression name=\"Encounters\" xsi:type=\"ExpressionRef\"/>\n" +
" </source>\n" +
" <where xsi:type=\"And\">\n" +
" <operand xsi:type=\"Equal\">\n" +
" <operand name=\"ToString\" libraryName=\"FHIRHelpers\" xsi:type=\"FunctionRef\">\n" +
" <operand path=\"status\" scope=\"$this\" xsi:type=\"Property\"/>\n" +
" </operand>\n" +
" <operand valueType=\"t:String\" value=\"in-progress\" xsi:type=\"Literal\"/>\n" +
" </operand>\n" +
" <operand xsi:type=\"LessOrEqual\">\n" +
" <operand name=\"ToDateTime\" libraryName=\"FHIRHelpers\" xsi:type=\"FunctionRef\">\n" +
" <operand path=\"end\" xsi:type=\"Property\">\n" +
" <source path=\"period\" scope=\"$this\" xsi:type=\"Property\"/>\n" +
" </operand>\n" +
" </operand>\n" +
" <operand xsi:type=\"ToDateTime\">\n" +
" <operand xsi:type=\"Subtract\">\n" +
" <operand xsi:type=\"Today\"/>\n" +
" <operand value=\"72\" unit=\"hours\" xsi:type=\"Quantity\"/>\n" +
" </operand>\n" +
" </operand>\n" +
" </operand>\n" +
" </where>\n" +
" </expression>\n" +
" </def>\n" +
" <def name=\"TestNowInWhere\" context=\"Patient\" accessLevel=\"Public\">\n" +
" <expression xsi:type=\"Query\">\n" +
" <source alias=\"$this\">\n" +
" <expression name=\"Encounters\" xsi:type=\"ExpressionRef\"/>\n" +
" </source>\n" +
" <where xsi:type=\"And\">\n" +
" <operand xsi:type=\"Equal\">\n" +
" <operand name=\"ToString\" libraryName=\"FHIRHelpers\" xsi:type=\"FunctionRef\">\n" +
" <operand path=\"status\" scope=\"$this\" xsi:type=\"Property\"/>\n" +
" </operand>\n" +
" <operand valueType=\"t:String\" value=\"in-progress\" xsi:type=\"Literal\"/>\n" +
" </operand>\n" +
" <operand xsi:type=\"LessOrEqual\">\n" +
" <operand name=\"ToDateTime\" libraryName=\"FHIRHelpers\" xsi:type=\"FunctionRef\">\n" +
" <operand path=\"end\" xsi:type=\"Property\">\n" +
" <source path=\"period\" scope=\"$this\" xsi:type=\"Property\"/>\n" +
" </operand>\n" +
" </operand>\n" +
" <operand xsi:type=\"Subtract\">\n" +
" <operand xsi:type=\"Now\"/>\n" +
" <operand value=\"72\" unit=\"hours\" xsi:type=\"Quantity\"/>\n" +
" </operand>\n" +
" </operand>\n" +
" </where>\n" +
" </expression>\n" +
" </def>\n" +
" </statements>\n" +
"</library>\n"));
*/
}

@Test
public void testFHIRPathLiteralStringEscapes() throws IOException {
CqlTranslator translator = TestUtils.runSemanticTest("fhir/r401/TestFHIRPathLiteralStringEscapes.cql", 0);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
library TestFHIRPath

using FHIR version '4.0.1'

include FHIRHelpers version '4.0.1'

context Patient

define TestToday: today()

define TestNow: now()

define TestTimeOfDay: timeOfDay()

define Encounters: [Encounter]

define TestTodayInWhere: Encounters.where(status = 'in-progress' and period.end <= today() - 72 hours)

define TestNowInWhere: Encounters.where(status = 'in-progress' and period.end <= now() - 72 hours)

define TestNow2: Patient.birthDate < date from now()

0 comments on commit 7b59b96

Please sign in to comment.