From a8f50378a6bc44e69e98153e5bec06931565d229 Mon Sep 17 00:00:00 2001 From: Chris Moesel Date: Fri, 27 Apr 2018 19:46:13 -0400 Subject: [PATCH 1/5] Support for StartsWith/EndsWith --- src/elm/string.coffee | 16 + test/elm/string/data.coffee | 730 ++++++++++++++++++++++++++++++++++++ test/elm/string/data.cql | 16 + test/elm/string/test.coffee | 40 ++ 4 files changed, 802 insertions(+) diff --git a/src/elm/string.coffee b/src/elm/string.coffee index 8e372c62c..5ae7aa96c 100644 --- a/src/elm/string.coffee +++ b/src/elm/string.coffee @@ -80,3 +80,19 @@ module.exports.Substring = class Substring extends Expression stringToSub.substr(startIndex, length) else stringToSub.substr(startIndex) + +module.exports.StartsWith = class StartsWith extends Expression + constructor: (json) -> + super + + exec: (ctx) -> + args = @execArgs ctx + if (args.some (x) -> not x?) then null else args[0].slice(0, args[1].length) == args[1] + +module.exports.EndsWith = class EndsWith extends Expression + constructor: (json) -> + super + + exec: (ctx) -> + args = @execArgs ctx + if (args.some (x) -> not x?) then null else args[1] is '' or args[0].slice(-args[1].length) == args[1] \ No newline at end of file diff --git a/test/elm/string/data.coffee b/test/elm/string/data.coffee index f9434ee8c..e073a4c0b 100644 --- a/test/elm/string/data.coffee +++ b/test/elm/string/data.coffee @@ -2637,3 +2637,733 @@ module.exports['Substring'] = { } } +### StartsWith +library TestSnippet version '1' +using QUICK +context Patient +define FooBarStartsWithFoo: StartsWith('FooBar', 'Foo') +define FooBarStartsWithBar: StartsWith('FooBar', 'Bar') +define FooBarStartsWithBlank: StartsWith('FooBar', '') +define BlankStartsWithFoo: StartsWith('', 'Foo') +define StartsWithNull: StartsWith('FooBar', null as String) +define NullStartsWith: StartsWith(null as String, 'Foo') +### + +module.exports['StartsWith'] = { + "library" : { + "identifier" : { + "id" : "TestSnippet", + "version" : "1" + }, + "schemaIdentifier" : { + "id" : "urn:hl7-org:elm", + "version" : "r1" + }, + "usings" : { + "def" : [ { + "localIdentifier" : "System", + "uri" : "urn:hl7-org:elm-types:r1" + }, { + "localId" : "1", + "localIdentifier" : "QUICK", + "uri" : "http://hl7.org/fhir" + } ] + }, + "statements" : { + "def" : [ { + "name" : "Patient", + "context" : "Patient", + "expression" : { + "type" : "SingletonFrom", + "operand" : { + "dataType" : "{http://hl7.org/fhir}Patient", + "templateId" : "patient-qicore-qicore-patient", + "type" : "Retrieve" + } + } + }, { + "localId" : "5", + "name" : "FooBarStartsWithFoo", + "context" : "Patient", + "accessLevel" : "Public", + "annotation" : [ { + "type" : "Annotation", + "s" : { + "r" : "5", + "s" : [ { + "value" : [ "define ","FooBarStartsWithFoo",": " ] + }, { + "r" : "4", + "s" : [ { + "value" : [ "StartsWith","(" ] + }, { + "r" : "2", + "s" : [ { + "value" : [ "'FooBar'" ] + } ] + }, { + "value" : [ ", " ] + }, { + "r" : "3", + "s" : [ { + "value" : [ "'Foo'" ] + } ] + }, { + "value" : [ ")" ] + } ] + } ] + } + } ], + "expression" : { + "localId" : "4", + "type" : "StartsWith", + "operand" : [ { + "localId" : "2", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "FooBar", + "type" : "Literal" + }, { + "localId" : "3", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "Foo", + "type" : "Literal" + } ] + } + }, { + "localId" : "9", + "name" : "FooBarStartsWithBar", + "context" : "Patient", + "accessLevel" : "Public", + "annotation" : [ { + "type" : "Annotation", + "s" : { + "r" : "9", + "s" : [ { + "value" : [ "define ","FooBarStartsWithBar",": " ] + }, { + "r" : "8", + "s" : [ { + "value" : [ "StartsWith","(" ] + }, { + "r" : "6", + "s" : [ { + "value" : [ "'FooBar'" ] + } ] + }, { + "value" : [ ", " ] + }, { + "r" : "7", + "s" : [ { + "value" : [ "'Bar'" ] + } ] + }, { + "value" : [ ")" ] + } ] + } ] + } + } ], + "expression" : { + "localId" : "8", + "type" : "StartsWith", + "operand" : [ { + "localId" : "6", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "FooBar", + "type" : "Literal" + }, { + "localId" : "7", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "Bar", + "type" : "Literal" + } ] + } + }, { + "localId" : "13", + "name" : "FooBarStartsWithBlank", + "context" : "Patient", + "accessLevel" : "Public", + "annotation" : [ { + "type" : "Annotation", + "s" : { + "r" : "13", + "s" : [ { + "value" : [ "define ","FooBarStartsWithBlank",": " ] + }, { + "r" : "12", + "s" : [ { + "value" : [ "StartsWith","(" ] + }, { + "r" : "10", + "s" : [ { + "value" : [ "'FooBar'" ] + } ] + }, { + "value" : [ ", " ] + }, { + "r" : "11", + "s" : [ { + "value" : [ "''" ] + } ] + }, { + "value" : [ ")" ] + } ] + } ] + } + } ], + "expression" : { + "localId" : "12", + "type" : "StartsWith", + "operand" : [ { + "localId" : "10", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "FooBar", + "type" : "Literal" + }, { + "localId" : "11", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "", + "type" : "Literal" + } ] + } + }, { + "localId" : "17", + "name" : "BlankStartsWithFoo", + "context" : "Patient", + "accessLevel" : "Public", + "annotation" : [ { + "type" : "Annotation", + "s" : { + "r" : "17", + "s" : [ { + "value" : [ "define ","BlankStartsWithFoo",": " ] + }, { + "r" : "16", + "s" : [ { + "value" : [ "StartsWith","(" ] + }, { + "r" : "14", + "s" : [ { + "value" : [ "''" ] + } ] + }, { + "value" : [ ", " ] + }, { + "r" : "15", + "s" : [ { + "value" : [ "'Foo'" ] + } ] + }, { + "value" : [ ")" ] + } ] + } ] + } + } ], + "expression" : { + "localId" : "16", + "type" : "StartsWith", + "operand" : [ { + "localId" : "14", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "", + "type" : "Literal" + }, { + "localId" : "15", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "Foo", + "type" : "Literal" + } ] + } + }, { + "localId" : "23", + "name" : "StartsWithNull", + "context" : "Patient", + "accessLevel" : "Public", + "annotation" : [ { + "type" : "Annotation", + "s" : { + "r" : "23", + "s" : [ { + "value" : [ "define ","StartsWithNull",": " ] + }, { + "r" : "22", + "s" : [ { + "value" : [ "StartsWith","(" ] + }, { + "r" : "18", + "s" : [ { + "value" : [ "'FooBar'" ] + } ] + }, { + "value" : [ ", " ] + }, { + "r" : "21", + "s" : [ { + "r" : "19", + "value" : [ "null"," as " ] + }, { + "r" : "20", + "s" : [ { + "value" : [ "String" ] + } ] + } ] + }, { + "value" : [ ")" ] + } ] + } ] + } + } ], + "expression" : { + "localId" : "22", + "type" : "StartsWith", + "operand" : [ { + "localId" : "18", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "FooBar", + "type" : "Literal" + }, { + "localId" : "21", + "strict" : false, + "type" : "As", + "operand" : { + "localId" : "19", + "type" : "Null" + }, + "asTypeSpecifier" : { + "localId" : "20", + "name" : "{urn:hl7-org:elm-types:r1}String", + "type" : "NamedTypeSpecifier" + } + } ] + } + }, { + "localId" : "29", + "name" : "NullStartsWith", + "context" : "Patient", + "accessLevel" : "Public", + "annotation" : [ { + "type" : "Annotation", + "s" : { + "r" : "29", + "s" : [ { + "value" : [ "define ","NullStartsWith",": " ] + }, { + "r" : "28", + "s" : [ { + "value" : [ "StartsWith","(" ] + }, { + "r" : "26", + "s" : [ { + "r" : "24", + "value" : [ "null"," as " ] + }, { + "r" : "25", + "s" : [ { + "value" : [ "String" ] + } ] + } ] + }, { + "value" : [ ", " ] + }, { + "r" : "27", + "s" : [ { + "value" : [ "'Foo'" ] + } ] + }, { + "value" : [ ")" ] + } ] + } ] + } + } ], + "expression" : { + "localId" : "28", + "type" : "StartsWith", + "operand" : [ { + "localId" : "26", + "strict" : false, + "type" : "As", + "operand" : { + "localId" : "24", + "type" : "Null" + }, + "asTypeSpecifier" : { + "localId" : "25", + "name" : "{urn:hl7-org:elm-types:r1}String", + "type" : "NamedTypeSpecifier" + } + }, { + "localId" : "27", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "Foo", + "type" : "Literal" + } ] + } + } ] + } + } +} + +### EndsWith +library TestSnippet version '1' +using QUICK +context Patient +define FooBarEndsWithBar: EndsWith('FooBar', 'Bar') +define FooBarEndsWithFoo: EndsWith('FooBar', 'Foo') +define FooBarEndsWithBlank: EndsWith('FooBar', '') +define BlankEndsWithFoo: EndsWith('', 'Foo') +define EndsWithNull: EndsWith('FooBar', null as String) +define NullEndsWith: EndsWith(null as String, 'Foo') +### + +module.exports['EndsWith'] = { + "library" : { + "identifier" : { + "id" : "TestSnippet", + "version" : "1" + }, + "schemaIdentifier" : { + "id" : "urn:hl7-org:elm", + "version" : "r1" + }, + "usings" : { + "def" : [ { + "localIdentifier" : "System", + "uri" : "urn:hl7-org:elm-types:r1" + }, { + "localId" : "1", + "localIdentifier" : "QUICK", + "uri" : "http://hl7.org/fhir" + } ] + }, + "statements" : { + "def" : [ { + "name" : "Patient", + "context" : "Patient", + "expression" : { + "type" : "SingletonFrom", + "operand" : { + "dataType" : "{http://hl7.org/fhir}Patient", + "templateId" : "patient-qicore-qicore-patient", + "type" : "Retrieve" + } + } + }, { + "localId" : "5", + "name" : "FooBarEndsWithBar", + "context" : "Patient", + "accessLevel" : "Public", + "annotation" : [ { + "type" : "Annotation", + "s" : { + "r" : "5", + "s" : [ { + "value" : [ "define ","FooBarEndsWithBar",": " ] + }, { + "r" : "4", + "s" : [ { + "value" : [ "EndsWith","(" ] + }, { + "r" : "2", + "s" : [ { + "value" : [ "'FooBar'" ] + } ] + }, { + "value" : [ ", " ] + }, { + "r" : "3", + "s" : [ { + "value" : [ "'Bar'" ] + } ] + }, { + "value" : [ ")" ] + } ] + } ] + } + } ], + "expression" : { + "localId" : "4", + "type" : "EndsWith", + "operand" : [ { + "localId" : "2", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "FooBar", + "type" : "Literal" + }, { + "localId" : "3", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "Bar", + "type" : "Literal" + } ] + } + }, { + "localId" : "9", + "name" : "FooBarEndsWithFoo", + "context" : "Patient", + "accessLevel" : "Public", + "annotation" : [ { + "type" : "Annotation", + "s" : { + "r" : "9", + "s" : [ { + "value" : [ "define ","FooBarEndsWithFoo",": " ] + }, { + "r" : "8", + "s" : [ { + "value" : [ "EndsWith","(" ] + }, { + "r" : "6", + "s" : [ { + "value" : [ "'FooBar'" ] + } ] + }, { + "value" : [ ", " ] + }, { + "r" : "7", + "s" : [ { + "value" : [ "'Foo'" ] + } ] + }, { + "value" : [ ")" ] + } ] + } ] + } + } ], + "expression" : { + "localId" : "8", + "type" : "EndsWith", + "operand" : [ { + "localId" : "6", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "FooBar", + "type" : "Literal" + }, { + "localId" : "7", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "Foo", + "type" : "Literal" + } ] + } + }, { + "localId" : "13", + "name" : "FooBarEndsWithBlank", + "context" : "Patient", + "accessLevel" : "Public", + "annotation" : [ { + "type" : "Annotation", + "s" : { + "r" : "13", + "s" : [ { + "value" : [ "define ","FooBarEndsWithBlank",": " ] + }, { + "r" : "12", + "s" : [ { + "value" : [ "EndsWith","(" ] + }, { + "r" : "10", + "s" : [ { + "value" : [ "'FooBar'" ] + } ] + }, { + "value" : [ ", " ] + }, { + "r" : "11", + "s" : [ { + "value" : [ "''" ] + } ] + }, { + "value" : [ ")" ] + } ] + } ] + } + } ], + "expression" : { + "localId" : "12", + "type" : "EndsWith", + "operand" : [ { + "localId" : "10", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "FooBar", + "type" : "Literal" + }, { + "localId" : "11", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "", + "type" : "Literal" + } ] + } + }, { + "localId" : "17", + "name" : "BlankEndsWithFoo", + "context" : "Patient", + "accessLevel" : "Public", + "annotation" : [ { + "type" : "Annotation", + "s" : { + "r" : "17", + "s" : [ { + "value" : [ "define ","BlankEndsWithFoo",": " ] + }, { + "r" : "16", + "s" : [ { + "value" : [ "EndsWith","(" ] + }, { + "r" : "14", + "s" : [ { + "value" : [ "''" ] + } ] + }, { + "value" : [ ", " ] + }, { + "r" : "15", + "s" : [ { + "value" : [ "'Foo'" ] + } ] + }, { + "value" : [ ")" ] + } ] + } ] + } + } ], + "expression" : { + "localId" : "16", + "type" : "EndsWith", + "operand" : [ { + "localId" : "14", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "", + "type" : "Literal" + }, { + "localId" : "15", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "Foo", + "type" : "Literal" + } ] + } + }, { + "localId" : "23", + "name" : "EndsWithNull", + "context" : "Patient", + "accessLevel" : "Public", + "annotation" : [ { + "type" : "Annotation", + "s" : { + "r" : "23", + "s" : [ { + "value" : [ "define ","EndsWithNull",": " ] + }, { + "r" : "22", + "s" : [ { + "value" : [ "EndsWith","(" ] + }, { + "r" : "18", + "s" : [ { + "value" : [ "'FooBar'" ] + } ] + }, { + "value" : [ ", " ] + }, { + "r" : "21", + "s" : [ { + "r" : "19", + "value" : [ "null"," as " ] + }, { + "r" : "20", + "s" : [ { + "value" : [ "String" ] + } ] + } ] + }, { + "value" : [ ")" ] + } ] + } ] + } + } ], + "expression" : { + "localId" : "22", + "type" : "EndsWith", + "operand" : [ { + "localId" : "18", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "FooBar", + "type" : "Literal" + }, { + "localId" : "21", + "strict" : false, + "type" : "As", + "operand" : { + "localId" : "19", + "type" : "Null" + }, + "asTypeSpecifier" : { + "localId" : "20", + "name" : "{urn:hl7-org:elm-types:r1}String", + "type" : "NamedTypeSpecifier" + } + } ] + } + }, { + "localId" : "29", + "name" : "NullEndsWith", + "context" : "Patient", + "accessLevel" : "Public", + "annotation" : [ { + "type" : "Annotation", + "s" : { + "r" : "29", + "s" : [ { + "value" : [ "define ","NullEndsWith",": " ] + }, { + "r" : "28", + "s" : [ { + "value" : [ "EndsWith","(" ] + }, { + "r" : "26", + "s" : [ { + "r" : "24", + "value" : [ "null"," as " ] + }, { + "r" : "25", + "s" : [ { + "value" : [ "String" ] + } ] + } ] + }, { + "value" : [ ", " ] + }, { + "r" : "27", + "s" : [ { + "value" : [ "'Foo'" ] + } ] + }, { + "value" : [ ")" ] + } ] + } ] + } + } ], + "expression" : { + "localId" : "28", + "type" : "EndsWith", + "operand" : [ { + "localId" : "26", + "strict" : false, + "type" : "As", + "operand" : { + "localId" : "24", + "type" : "Null" + }, + "asTypeSpecifier" : { + "localId" : "25", + "name" : "{urn:hl7-org:elm-types:r1}String", + "type" : "NamedTypeSpecifier" + } + }, { + "localId" : "27", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "Foo", + "type" : "Literal" + } ] + } + } ] + } + } +} + diff --git a/test/elm/string/data.cql b/test/elm/string/data.cql index 2d3eb6415..f36159cb8 100644 --- a/test/elm/string/data.cql +++ b/test/elm/string/data.cql @@ -58,3 +58,19 @@ define TooMuchLength: Substring('HelloWorld', 7, 25) define NegativeLength: Substring('HelloWorld', 7, -1) define NullString: Substring(null, 5) define NullStart: Substring('HelloWorld', null) + +// @Test: StartsWith +define FooBarStartsWithFoo: StartsWith('FooBar', 'Foo') +define FooBarStartsWithBar: StartsWith('FooBar', 'Bar') +define FooBarStartsWithBlank: StartsWith('FooBar', '') +define BlankStartsWithFoo: StartsWith('', 'Foo') +define StartsWithNull: StartsWith('FooBar', null as String) +define NullStartsWith: StartsWith(null as String, 'Foo') + +// @Test: EndsWith +define FooBarEndsWithBar: EndsWith('FooBar', 'Bar') +define FooBarEndsWithFoo: EndsWith('FooBar', 'Foo') +define FooBarEndsWithBlank: EndsWith('FooBar', '') +define BlankEndsWithFoo: EndsWith('', 'Foo') +define EndsWithNull: EndsWith('FooBar', null as String) +define NullEndsWith: EndsWith(null as String, 'Foo') \ No newline at end of file diff --git a/test/elm/string/test.coffee b/test/elm/string/test.coffee index 8f1b87fa8..e9093fc79 100644 --- a/test/elm/string/test.coffee +++ b/test/elm/string/test.coffee @@ -185,3 +185,43 @@ describe 'Substring', -> it 'should return null when start is null', -> should(@nullStart.exec(@ctx)).be.null + +describe 'StartsWith', -> + @beforeEach -> + setup @, data + + it 'should be true when it does start with', -> + @fooBarStartsWithFoo.exec(@ctx).should.be.true() + + it 'should be false when it does not start with', -> + @fooBarStartsWithBar.exec(@ctx).should.be.false() + + it 'should be true for starts with blank', -> + @fooBarStartsWithBlank.exec(@ctx).should.be.true() + + it 'should be false for blank starts with', -> + @blankStartsWithFoo.exec(@ctx).should.be.false() + + it 'should be null when either arg is null', -> + should(@startsWithNull.exec(@ctx)).be.null() + should(@nullStartsWith.exec(@ctx)).be.null() + +describe 'EndsWith', -> + @beforeEach -> + setup @, data + + it 'should be true when it does end with', -> + @fooBarEndsWithBar.exec(@ctx).should.be.true() + + it 'should be false when it does not end with', -> + @fooBarEndsWithFoo.exec(@ctx).should.be.false() + + it 'should be true for ends with blank', -> + @fooBarEndsWithBlank.exec(@ctx).should.be.true() + + it 'should be false for blank ends with', -> + @blankEndsWithFoo.exec(@ctx).should.be.false() + + it 'should be null when either arg is null', -> + should(@endsWithNull.exec(@ctx)).be.null() + should(@nullEndsWith.exec(@ctx)).be.null() From 1cee07ff8078069e1c60535438cd9bf75b158c22 Mon Sep 17 00:00:00 2001 From: Chris Moesel Date: Mon, 30 Apr 2018 16:22:10 -0400 Subject: [PATCH 2/5] Update cql4browsers --- src/example/browser/cql4browsers.js | 48 ++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/src/example/browser/cql4browsers.js b/src/example/browser/cql4browsers.js index d0e33f86d..b3a6b7a67 100644 --- a/src/example/browser/cql4browsers.js +++ b/src/example/browser/cql4browsers.js @@ -6495,7 +6495,7 @@ },{"./builder":14,"./expression":20}],35:[function(require,module,exports){ // Generated by CoffeeScript 1.12.7 (function() { - var Combine, Concatenate, Expression, Lower, PositionOf, Split, Substring, Upper, build, + var Combine, Concatenate, EndsWith, Expression, Lower, PositionOf, Split, StartsWith, Substring, Upper, build, extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; }, hasProp = {}.hasOwnProperty; @@ -6672,6 +6672,52 @@ })(Expression); + module.exports.StartsWith = StartsWith = (function(superClass) { + extend(StartsWith, superClass); + + function StartsWith(json) { + StartsWith.__super__.constructor.apply(this, arguments); + } + + StartsWith.prototype.exec = function(ctx) { + var args; + args = this.execArgs(ctx); + if (args.some(function(x) { + return x == null; + })) { + return null; + } else { + return args[0].slice(0, args[1].length) === args[1]; + } + }; + + return StartsWith; + + })(Expression); + + module.exports.EndsWith = EndsWith = (function(superClass) { + extend(EndsWith, superClass); + + function EndsWith(json) { + EndsWith.__super__.constructor.apply(this, arguments); + } + + EndsWith.prototype.exec = function(ctx) { + var args; + args = this.execArgs(ctx); + if (args.some(function(x) { + return x == null; + })) { + return null; + } else { + return args[1] === '' || args[0].slice(-args[1].length) === args[1]; + } + }; + + return EndsWith; + + })(Expression); + }).call(this); From ab65b9073b0179a3ced2323dfbecba60161024cb Mon Sep 17 00:00:00 2001 From: Chris Moesel Date: Mon, 30 Apr 2018 16:53:08 -0400 Subject: [PATCH 3/5] Add startsWith/endsWith tests for normal null --- test/elm/string/data.coffee | 293 ++++++++++++++++++++++++++++++------ test/elm/string/data.cql | 12 +- test/elm/string/test.coffee | 4 + 3 files changed, 256 insertions(+), 53 deletions(-) diff --git a/test/elm/string/data.coffee b/test/elm/string/data.coffee index e073a4c0b..933f46f26 100644 --- a/test/elm/string/data.coffee +++ b/test/elm/string/data.coffee @@ -2645,8 +2645,10 @@ define FooBarStartsWithFoo: StartsWith('FooBar', 'Foo') define FooBarStartsWithBar: StartsWith('FooBar', 'Bar') define FooBarStartsWithBlank: StartsWith('FooBar', '') define BlankStartsWithFoo: StartsWith('', 'Foo') -define StartsWithNull: StartsWith('FooBar', null as String) -define NullStartsWith: StartsWith(null as String, 'Foo') +define StartsWithNull: StartsWith('FooBar', null) +define StartsWithNullAsString: StartsWith('FooBar', null as String) +define NullStartsWith: StartsWith(null, 'Foo') +define NullAsStringStartsWith: StartsWith(null as String, 'Foo') ### module.exports['StartsWith'] = { @@ -2874,18 +2876,18 @@ module.exports['StartsWith'] = { } ] } }, { - "localId" : "23", + "localId" : "21", "name" : "StartsWithNull", "context" : "Patient", "accessLevel" : "Public", "annotation" : [ { "type" : "Annotation", "s" : { - "r" : "23", + "r" : "21", "s" : [ { "value" : [ "define ","StartsWithNull",": " ] }, { - "r" : "22", + "r" : "20", "s" : [ { "value" : [ "StartsWith","(" ] }, { @@ -2893,15 +2895,58 @@ module.exports['StartsWith'] = { "s" : [ { "value" : [ "'FooBar'" ] } ] + }, { + "value" : [ ", ","null",")" ] + } ] + } ] + } + } ], + "expression" : { + "localId" : "20", + "type" : "StartsWith", + "operand" : [ { + "localId" : "18", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "FooBar", + "type" : "Literal" + }, { + "asType" : "{urn:hl7-org:elm-types:r1}String", + "type" : "As", + "operand" : { + "localId" : "19", + "type" : "Null" + } + } ] + } + }, { + "localId" : "27", + "name" : "StartsWithNullAsString", + "context" : "Patient", + "accessLevel" : "Public", + "annotation" : [ { + "type" : "Annotation", + "s" : { + "r" : "27", + "s" : [ { + "value" : [ "define ","StartsWithNullAsString",": " ] + }, { + "r" : "26", + "s" : [ { + "value" : [ "StartsWith","(" ] + }, { + "r" : "22", + "s" : [ { + "value" : [ "'FooBar'" ] + } ] }, { "value" : [ ", " ] }, { - "r" : "21", + "r" : "25", "s" : [ { - "r" : "19", + "r" : "23", "value" : [ "null"," as " ] }, { - "r" : "20", + "r" : "24", "s" : [ { "value" : [ "String" ] } ] @@ -2913,50 +2958,93 @@ module.exports['StartsWith'] = { } } ], "expression" : { - "localId" : "22", + "localId" : "26", "type" : "StartsWith", "operand" : [ { - "localId" : "18", + "localId" : "22", "valueType" : "{urn:hl7-org:elm-types:r1}String", "value" : "FooBar", "type" : "Literal" }, { - "localId" : "21", + "localId" : "25", "strict" : false, "type" : "As", "operand" : { - "localId" : "19", + "localId" : "23", "type" : "Null" }, "asTypeSpecifier" : { - "localId" : "20", + "localId" : "24", "name" : "{urn:hl7-org:elm-types:r1}String", "type" : "NamedTypeSpecifier" } } ] } }, { - "localId" : "29", + "localId" : "31", "name" : "NullStartsWith", "context" : "Patient", "accessLevel" : "Public", "annotation" : [ { "type" : "Annotation", "s" : { - "r" : "29", + "r" : "31", "s" : [ { "value" : [ "define ","NullStartsWith",": " ] }, { - "r" : "28", + "r" : "30", + "s" : [ { + "value" : [ "StartsWith","(","null",", " ] + }, { + "r" : "29", + "s" : [ { + "value" : [ "'Foo'" ] + } ] + }, { + "value" : [ ")" ] + } ] + } ] + } + } ], + "expression" : { + "localId" : "30", + "type" : "StartsWith", + "operand" : [ { + "asType" : "{urn:hl7-org:elm-types:r1}String", + "type" : "As", + "operand" : { + "localId" : "28", + "type" : "Null" + } + }, { + "localId" : "29", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "Foo", + "type" : "Literal" + } ] + } + }, { + "localId" : "37", + "name" : "NullAsStringStartsWith", + "context" : "Patient", + "accessLevel" : "Public", + "annotation" : [ { + "type" : "Annotation", + "s" : { + "r" : "37", + "s" : [ { + "value" : [ "define ","NullAsStringStartsWith",": " ] + }, { + "r" : "36", "s" : [ { "value" : [ "StartsWith","(" ] }, { - "r" : "26", + "r" : "34", "s" : [ { - "r" : "24", + "r" : "32", "value" : [ "null"," as " ] }, { - "r" : "25", + "r" : "33", "s" : [ { "value" : [ "String" ] } ] @@ -2964,7 +3052,7 @@ module.exports['StartsWith'] = { }, { "value" : [ ", " ] }, { - "r" : "27", + "r" : "35", "s" : [ { "value" : [ "'Foo'" ] } ] @@ -2975,23 +3063,23 @@ module.exports['StartsWith'] = { } } ], "expression" : { - "localId" : "28", + "localId" : "36", "type" : "StartsWith", "operand" : [ { - "localId" : "26", + "localId" : "34", "strict" : false, "type" : "As", "operand" : { - "localId" : "24", + "localId" : "32", "type" : "Null" }, "asTypeSpecifier" : { - "localId" : "25", + "localId" : "33", "name" : "{urn:hl7-org:elm-types:r1}String", "type" : "NamedTypeSpecifier" } }, { - "localId" : "27", + "localId" : "35", "valueType" : "{urn:hl7-org:elm-types:r1}String", "value" : "Foo", "type" : "Literal" @@ -3010,8 +3098,10 @@ define FooBarEndsWithBar: EndsWith('FooBar', 'Bar') define FooBarEndsWithFoo: EndsWith('FooBar', 'Foo') define FooBarEndsWithBlank: EndsWith('FooBar', '') define BlankEndsWithFoo: EndsWith('', 'Foo') -define EndsWithNull: EndsWith('FooBar', null as String) +define EndsWithNull: EndsWith('FooBar', null) +define EndsWithNullAsString: EndsWith('FooBar', null as String) define NullEndsWith: EndsWith(null as String, 'Foo') +define NullAsStringEndsWith: EndsWith(null as String, 'Foo') ### module.exports['EndsWith'] = { @@ -3239,18 +3329,18 @@ module.exports['EndsWith'] = { } ] } }, { - "localId" : "23", + "localId" : "21", "name" : "EndsWithNull", "context" : "Patient", "accessLevel" : "Public", "annotation" : [ { "type" : "Annotation", "s" : { - "r" : "23", + "r" : "21", "s" : [ { "value" : [ "define ","EndsWithNull",": " ] }, { - "r" : "22", + "r" : "20", "s" : [ { "value" : [ "EndsWith","(" ] }, { @@ -3258,15 +3348,58 @@ module.exports['EndsWith'] = { "s" : [ { "value" : [ "'FooBar'" ] } ] + }, { + "value" : [ ", ","null",")" ] + } ] + } ] + } + } ], + "expression" : { + "localId" : "20", + "type" : "EndsWith", + "operand" : [ { + "localId" : "18", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "FooBar", + "type" : "Literal" + }, { + "asType" : "{urn:hl7-org:elm-types:r1}String", + "type" : "As", + "operand" : { + "localId" : "19", + "type" : "Null" + } + } ] + } + }, { + "localId" : "27", + "name" : "EndsWithNullAsString", + "context" : "Patient", + "accessLevel" : "Public", + "annotation" : [ { + "type" : "Annotation", + "s" : { + "r" : "27", + "s" : [ { + "value" : [ "define ","EndsWithNullAsString",": " ] + }, { + "r" : "26", + "s" : [ { + "value" : [ "EndsWith","(" ] + }, { + "r" : "22", + "s" : [ { + "value" : [ "'FooBar'" ] + } ] }, { "value" : [ ", " ] }, { - "r" : "21", + "r" : "25", "s" : [ { - "r" : "19", + "r" : "23", "value" : [ "null"," as " ] }, { - "r" : "20", + "r" : "24", "s" : [ { "value" : [ "String" ] } ] @@ -3278,50 +3411,50 @@ module.exports['EndsWith'] = { } } ], "expression" : { - "localId" : "22", + "localId" : "26", "type" : "EndsWith", "operand" : [ { - "localId" : "18", + "localId" : "22", "valueType" : "{urn:hl7-org:elm-types:r1}String", "value" : "FooBar", "type" : "Literal" }, { - "localId" : "21", + "localId" : "25", "strict" : false, "type" : "As", "operand" : { - "localId" : "19", + "localId" : "23", "type" : "Null" }, "asTypeSpecifier" : { - "localId" : "20", + "localId" : "24", "name" : "{urn:hl7-org:elm-types:r1}String", "type" : "NamedTypeSpecifier" } } ] } }, { - "localId" : "29", + "localId" : "33", "name" : "NullEndsWith", "context" : "Patient", "accessLevel" : "Public", "annotation" : [ { "type" : "Annotation", "s" : { - "r" : "29", + "r" : "33", "s" : [ { "value" : [ "define ","NullEndsWith",": " ] }, { - "r" : "28", + "r" : "32", "s" : [ { "value" : [ "EndsWith","(" ] }, { - "r" : "26", + "r" : "30", "s" : [ { - "r" : "24", + "r" : "28", "value" : [ "null"," as " ] }, { - "r" : "25", + "r" : "29", "s" : [ { "value" : [ "String" ] } ] @@ -3329,7 +3462,7 @@ module.exports['EndsWith'] = { }, { "value" : [ ", " ] }, { - "r" : "27", + "r" : "31", "s" : [ { "value" : [ "'Foo'" ] } ] @@ -3340,23 +3473,85 @@ module.exports['EndsWith'] = { } } ], "expression" : { - "localId" : "28", + "localId" : "32", "type" : "EndsWith", "operand" : [ { - "localId" : "26", + "localId" : "30", "strict" : false, "type" : "As", "operand" : { - "localId" : "24", + "localId" : "28", "type" : "Null" }, "asTypeSpecifier" : { - "localId" : "25", + "localId" : "29", "name" : "{urn:hl7-org:elm-types:r1}String", "type" : "NamedTypeSpecifier" } }, { - "localId" : "27", + "localId" : "31", + "valueType" : "{urn:hl7-org:elm-types:r1}String", + "value" : "Foo", + "type" : "Literal" + } ] + } + }, { + "localId" : "39", + "name" : "NullAsStringEndsWith", + "context" : "Patient", + "accessLevel" : "Public", + "annotation" : [ { + "type" : "Annotation", + "s" : { + "r" : "39", + "s" : [ { + "value" : [ "define ","NullAsStringEndsWith",": " ] + }, { + "r" : "38", + "s" : [ { + "value" : [ "EndsWith","(" ] + }, { + "r" : "36", + "s" : [ { + "r" : "34", + "value" : [ "null"," as " ] + }, { + "r" : "35", + "s" : [ { + "value" : [ "String" ] + } ] + } ] + }, { + "value" : [ ", " ] + }, { + "r" : "37", + "s" : [ { + "value" : [ "'Foo'" ] + } ] + }, { + "value" : [ ")" ] + } ] + } ] + } + } ], + "expression" : { + "localId" : "38", + "type" : "EndsWith", + "operand" : [ { + "localId" : "36", + "strict" : false, + "type" : "As", + "operand" : { + "localId" : "34", + "type" : "Null" + }, + "asTypeSpecifier" : { + "localId" : "35", + "name" : "{urn:hl7-org:elm-types:r1}String", + "type" : "NamedTypeSpecifier" + } + }, { + "localId" : "37", "valueType" : "{urn:hl7-org:elm-types:r1}String", "value" : "Foo", "type" : "Literal" diff --git a/test/elm/string/data.cql b/test/elm/string/data.cql index f36159cb8..774b41073 100644 --- a/test/elm/string/data.cql +++ b/test/elm/string/data.cql @@ -64,13 +64,17 @@ define FooBarStartsWithFoo: StartsWith('FooBar', 'Foo') define FooBarStartsWithBar: StartsWith('FooBar', 'Bar') define FooBarStartsWithBlank: StartsWith('FooBar', '') define BlankStartsWithFoo: StartsWith('', 'Foo') -define StartsWithNull: StartsWith('FooBar', null as String) -define NullStartsWith: StartsWith(null as String, 'Foo') +define StartsWithNull: StartsWith('FooBar', null) +define StartsWithNullAsString: StartsWith('FooBar', null as String) +define NullStartsWith: StartsWith(null, 'Foo') +define NullAsStringStartsWith: StartsWith(null as String, 'Foo') // @Test: EndsWith define FooBarEndsWithBar: EndsWith('FooBar', 'Bar') define FooBarEndsWithFoo: EndsWith('FooBar', 'Foo') define FooBarEndsWithBlank: EndsWith('FooBar', '') define BlankEndsWithFoo: EndsWith('', 'Foo') -define EndsWithNull: EndsWith('FooBar', null as String) -define NullEndsWith: EndsWith(null as String, 'Foo') \ No newline at end of file +define EndsWithNull: EndsWith('FooBar', null) +define EndsWithNullAsString: EndsWith('FooBar', null as String) +define NullEndsWith: EndsWith(null as String, 'Foo') +define NullAsStringEndsWith: EndsWith(null as String, 'Foo') \ No newline at end of file diff --git a/test/elm/string/test.coffee b/test/elm/string/test.coffee index e9093fc79..e41985897 100644 --- a/test/elm/string/test.coffee +++ b/test/elm/string/test.coffee @@ -204,7 +204,9 @@ describe 'StartsWith', -> it 'should be null when either arg is null', -> should(@startsWithNull.exec(@ctx)).be.null() + should(@startsWithNullAsString.exec(@ctx)).be.null() should(@nullStartsWith.exec(@ctx)).be.null() + should(@nullAsStringStartsWith.exec(@ctx)).be.null() describe 'EndsWith', -> @beforeEach -> @@ -224,4 +226,6 @@ describe 'EndsWith', -> it 'should be null when either arg is null', -> should(@endsWithNull.exec(@ctx)).be.null() + should(@endsWithNullAsString.exec(@ctx)).be.null() should(@nullEndsWith.exec(@ctx)).be.null() + should(@nullAsStringEndsWith.exec(@ctx)).be.null() From 5116e7520608ea60a15c7b24b63c4722a581a9ed Mon Sep 17 00:00:00 2001 From: Chris Moesel Date: Thu, 3 May 2018 11:17:37 -0400 Subject: [PATCH 4/5] v1.2.1 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index d88d3ca0d..349c4a176 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cql-execution", - "version": "1.2.0", + "version": "1.2.1", "description": "An execution framework for the Clinical Quality Language (CQL)", "keywords": [ "CQL", From 1ca7045ea117fd66130bf63db5a526129168c5f6 Mon Sep 17 00:00:00 2001 From: Chris Moesel Date: Thu, 3 May 2018 12:27:49 -0400 Subject: [PATCH 5/5] Prefer getId() as data source API for getting string-based ids The CQL execution engine requires that data models expose an id over the API and it expects that this id is a string. Currently this is only used when building out the results object (in which the patient id is used as a key) -- but it is important. Problems arise when a data model defines an id property that is not a string. This is the case for the FHIR data models (in which id is an object, in order to support extensions). Then the required string-based id conflicts with the data-model defined object id. To get around this, we change the API to expect a getId() function that should return the ID as a string. To support backwards compatibility (i.e., not break Bonnie and other apps), if the getId() function isn't found, we fall back to id(). --- src/example/browser/cql4browsers.js | 5 +++-- src/runtime/results.coffee | 6 +++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/example/browser/cql4browsers.js b/src/example/browser/cql4browsers.js index b3a6b7a67..c9e6910a8 100644 --- a/src/example/browser/cql4browsers.js +++ b/src/example/browser/cql4browsers.js @@ -43831,8 +43831,9 @@ } Results.prototype.recordPatientResult = function(patient_ctx, resultName, result) { - var base, patientId; - patientId = patient_ctx.patient.id(); + var base, p, patientId; + p = patient_ctx.patient; + patientId = typeof p.getId === 'function' ? p.getId() : p.id(); if ((base = this.patientResults)[patientId] == null) { base[patientId] = {}; } diff --git a/src/runtime/results.coffee b/src/runtime/results.coffee index e44fe4032..b2a542d9e 100644 --- a/src/runtime/results.coffee +++ b/src/runtime/results.coffee @@ -5,7 +5,11 @@ module.exports.Results = class Results @localIdPatientResultsMap = {} recordPatientResult: (patient_ctx, resultName, result) -> - patientId = patient_ctx.patient.id() + p = patient_ctx.patient + # NOTE: From now on prefer getId() over id() because some data models may have an id property + # that is not a string (e.g., FHIR) -- so reserve getId() for the API (and expect a string + # representation) but leave id() for data-model specific formats. + patientId = if typeof p.getId == 'function' then p.getId() else p.id() @patientResults[patientId] ?= {} @patientResults[patientId][resultName] = result @localIdPatientResultsMap[patientId] = patient_ctx.getAllLocalIds()