diff --git a/.vscode-test.js b/.vscode-test.js index b5724e1..9bda656 100644 --- a/.vscode-test.js +++ b/.vscode-test.js @@ -2,6 +2,6 @@ const { defineConfig } = require('@vscode/test-cli'); module.exports = defineConfig({ label: 'Tests (Empty Folder)', - files: './dist/test/suite/runTests.js', + files: './dist/test/runTests.js', launchArgs: ['tests/empty', '--new-window', '--disable-extensions'], }); diff --git a/.vscode/launch.json b/.vscode/launch.json index 1e13298..6dccbf9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -21,7 +21,7 @@ "args": [ "${workspaceFolder}/tests/empty", "--extensionDevelopmentPath=${workspaceFolder}", - "--extensionTestsPath=${workspaceFolder}/dist/test/suite/index", + "--extensionTestsPath=${workspaceFolder}/dist/test/index", "--disable-extensions", "--new-window" ], diff --git a/package.json b/package.json index 028699c..d21f5df 100644 --- a/package.json +++ b/package.json @@ -156,7 +156,7 @@ "vscode:prepublish": "npm run compile", "compile": "tsc -p ./", "watch": "tsc -watch -p ./", - "pretest": "npm run clean && npm run compile && cp -r src/test/suite/resources dist/test/suite/resources", + "pretest": "npm run clean && npm run compile && cp -r src/test/resources dist/test/resources", "clean": "rm -rf ./dist", "clean-all": "npm run clean && rm -rf ./jars", "test": "c8 --check-coverage --lines 80 --functions 80 --branches 80 vscode-test" diff --git a/src/test/suite/buildParameters.test.ts b/src/test/buildParameters.test.ts similarity index 94% rename from src/test/suite/buildParameters.test.ts rename to src/test/buildParameters.test.ts index 0e1a126..3d9206f 100644 --- a/src/test/suite/buildParameters.test.ts +++ b/src/test/buildParameters.test.ts @@ -2,18 +2,15 @@ import * as assert from 'assert'; import path from 'path'; import { ExtensionContext, extensions, window, workspace } from 'vscode'; import { URI } from 'vscode-uri'; -import { buildParameters } from '../../buildParameters'; -import { ConnectionManager } from '../../connectionManager'; +import { buildParameters } from '../buildParameters'; +import { ConnectionManager } from '../connectionManager'; -const libraryUrl = path.resolve(__dirname, '../suite/resources/simple-test-ig/input/cql'); +const libraryUrl = path.resolve(__dirname, './resources/simple-test-ig/input/cql'); const terminologyUrl = path.resolve( __dirname, - '../suite/resources/simple-test-ig/input/vocabulary/valueset', -); -const modelUrl = path.resolve( - __dirname, - '../suite/resources/simple-test-ig/input/tests/Test/simple-test', + './resources/simple-test-ig/input/vocabulary/valueset', ); +const modelUrl = path.resolve(__dirname, './resources/simple-test-ig/input/tests/Test/simple-test'); const remoteUrl = 'http://localhost:8000'; async function showSpecificFile(filePath: string) { const document = await workspace.openTextDocument(filePath); @@ -21,7 +18,7 @@ async function showSpecificFile(filePath: string) { } suite('buildParameters - Public API Testing', () => { - const testWorkspacePath = path.resolve(__dirname, '../suite/resources/simple-test-ig'); + const testWorkspacePath = path.resolve(__dirname, './resources/simple-test-ig'); const testFilePath = path.join(testWorkspacePath, 'input/cql/Test.cql'); let connectionManager = ConnectionManager.getManager(); diff --git a/src/test/suite/connectionManager.test.ts b/src/test/connectionManager.test.ts similarity index 97% rename from src/test/suite/connectionManager.test.ts rename to src/test/connectionManager.test.ts index 5a20940..e891617 100644 --- a/src/test/suite/connectionManager.test.ts +++ b/src/test/connectionManager.test.ts @@ -1,6 +1,6 @@ import * as assert from 'assert'; import * as vscode from 'vscode'; -import { ConnectionManager, Context } from '../../connectionManager'; +import { ConnectionManager, Context } from '../connectionManager'; suite('Connection Manager Test', () => { test('Connection Manager -- should be present', async function () { diff --git a/src/test/suite/executeCQL.test.ts b/src/test/executeCQL.test.ts similarity index 98% rename from src/test/suite/executeCQL.test.ts rename to src/test/executeCQL.test.ts index d3fb2c9..8dd8a3b 100644 --- a/src/test/suite/executeCQL.test.ts +++ b/src/test/executeCQL.test.ts @@ -5,8 +5,8 @@ import { commands, Uri, window } from 'vscode'; import { existsSync } from 'fs'; import { createFileSync, removeSync } from 'fs-extra'; import { join } from 'path'; -import { Commands } from '../../commands'; -import { executeCQL } from '../../executeCql'; +import { Commands } from '../commands'; +import { executeCQL } from '../executeCql'; const OUT_FILE_PATH = join(__dirname, 'out.txt'); const TERMINOLOGY_FILE_PATH = join(__dirname, 'terminology.txt'); diff --git a/src/test/suite/extension.test.ts b/src/test/extension.test.ts similarity index 100% rename from src/test/suite/extension.test.ts rename to src/test/extension.test.ts diff --git a/src/test/suite/normalizeCqlExecution.test.ts b/src/test/normalizeCqlExecution.test.ts similarity index 93% rename from src/test/suite/normalizeCqlExecution.test.ts rename to src/test/normalizeCqlExecution.test.ts index e6b8320..afa959b 100644 --- a/src/test/suite/normalizeCqlExecution.test.ts +++ b/src/test/normalizeCqlExecution.test.ts @@ -3,9 +3,9 @@ import path from 'path'; import * as sinon from 'sinon'; import { window } from 'vscode'; import { URI } from 'vscode-uri'; -import * as buildParametersModule from '../../buildParameters'; -import * as executeCQLModule from '../../executeCql'; -import { normalizeCqlExecution } from '../../normalizeCqlExecution'; +import * as buildParametersModule from '../buildParameters'; +import * as executeCQLModule from '../executeCql'; +import { normalizeCqlExecution } from '../normalizeCqlExecution'; suite('normalizeCqlExecution tests', () => { let stubShowErrorMessage: sinon.SinonStub; @@ -13,7 +13,7 @@ suite('normalizeCqlExecution tests', () => { let stubBuildParameters: sinon.SinonStub; let stubExecuteCQL: sinon.SinonStub; let stubActiveTextEditor: sinon.SinonStub; - const testWorkspacePath = path.resolve(__dirname, '../suite/resources/simple-test-ig'); + const testWorkspacePath = path.resolve(__dirname, '../resources/simple-test-ig'); const testFilePath = path.join(testWorkspacePath, 'input/cql/Test.cql'); const testFileUri = URI.file(testFilePath); diff --git a/src/test/suite/resources/simple-test-ig/bundles/Test-bundle.json b/src/test/resources/simple-test-ig/bundles/Test-bundle.json similarity index 100% rename from src/test/suite/resources/simple-test-ig/bundles/Test-bundle.json rename to src/test/resources/simple-test-ig/bundles/Test-bundle.json diff --git a/src/test/suite/resources/simple-test-ig/ig.ini b/src/test/resources/simple-test-ig/ig.ini similarity index 100% rename from src/test/suite/resources/simple-test-ig/ig.ini rename to src/test/resources/simple-test-ig/ig.ini diff --git a/src/test/suite/resources/simple-test-ig/input/cql/Test.cql b/src/test/resources/simple-test-ig/input/cql/Test.cql similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/cql/Test.cql rename to src/test/resources/simple-test-ig/input/cql/Test.cql diff --git a/src/test/suite/resources/simple-test-ig/input/cql/TestDSTU3FHIR.cql b/src/test/resources/simple-test-ig/input/cql/TestDSTU3FHIR.cql similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/cql/TestDSTU3FHIR.cql rename to src/test/resources/simple-test-ig/input/cql/TestDSTU3FHIR.cql diff --git a/src/test/suite/resources/simple-test-ig/input/cql/TestEmpty.cql b/src/test/resources/simple-test-ig/input/cql/TestEmpty.cql similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/cql/TestEmpty.cql rename to src/test/resources/simple-test-ig/input/cql/TestEmpty.cql diff --git a/src/test/suite/resources/simple-test-ig/input/cql/TestFhirMissing.cql b/src/test/resources/simple-test-ig/input/cql/TestFhirMissing.cql similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/cql/TestFhirMissing.cql rename to src/test/resources/simple-test-ig/input/cql/TestFhirMissing.cql diff --git a/src/test/suite/resources/simple-test-ig/input/resources/library/library-FHIRHelpers.json b/src/test/resources/simple-test-ig/input/resources/library/library-FHIRHelpers.json similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/resources/library/library-FHIRHelpers.json rename to src/test/resources/simple-test-ig/input/resources/library/library-FHIRHelpers.json diff --git a/src/test/suite/resources/simple-test-ig/input/resources/library/library-Test.json b/src/test/resources/simple-test-ig/input/resources/library/library-Test.json similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/resources/library/library-Test.json rename to src/test/resources/simple-test-ig/input/resources/library/library-Test.json diff --git a/src/test/suite/resources/simple-test-ig/input/resources/measure/measure-Test.json b/src/test/resources/simple-test-ig/input/resources/measure/measure-Test.json similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/resources/measure/measure-Test.json rename to src/test/resources/simple-test-ig/input/resources/measure/measure-Test.json diff --git a/src/test/suite/resources/simple-test-ig/input/simple-test-ig.xml b/src/test/resources/simple-test-ig/input/simple-test-ig.xml similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/simple-test-ig.xml rename to src/test/resources/simple-test-ig/input/simple-test-ig.xml diff --git a/src/test/suite/resources/simple-test-ig/input/tests/Test/simple-test/Encounter/simple-test-encounter.json b/src/test/resources/simple-test-ig/input/tests/Test/simple-test/Encounter/simple-test-encounter.json similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/tests/Test/simple-test/Encounter/simple-test-encounter.json rename to src/test/resources/simple-test-ig/input/tests/Test/simple-test/Encounter/simple-test-encounter.json diff --git a/src/test/suite/resources/simple-test-ig/input/tests/Test/simple-test/Patient/simple-test.json b/src/test/resources/simple-test-ig/input/tests/Test/simple-test/Patient/simple-test.json similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/tests/Test/simple-test/Patient/simple-test.json rename to src/test/resources/simple-test-ig/input/tests/Test/simple-test/Patient/simple-test.json diff --git a/src/test/suite/resources/simple-test-ig/input/tests/TestDSTU3FHIR/simple-test/Encounter/simple-test-encounter.json b/src/test/resources/simple-test-ig/input/tests/TestDSTU3FHIR/simple-test/Encounter/simple-test-encounter.json similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/tests/TestDSTU3FHIR/simple-test/Encounter/simple-test-encounter.json rename to src/test/resources/simple-test-ig/input/tests/TestDSTU3FHIR/simple-test/Encounter/simple-test-encounter.json diff --git a/src/test/suite/resources/simple-test-ig/input/tests/TestDSTU3FHIR/simple-test/Patient/simple-test.json b/src/test/resources/simple-test-ig/input/tests/TestDSTU3FHIR/simple-test/Patient/simple-test.json similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/tests/TestDSTU3FHIR/simple-test/Patient/simple-test.json rename to src/test/resources/simple-test-ig/input/tests/TestDSTU3FHIR/simple-test/Patient/simple-test.json diff --git a/src/test/suite/resources/simple-test-ig/input/tests/TestEmpty/simple-test/Encounter/simple-test-encounter.json b/src/test/resources/simple-test-ig/input/tests/TestEmpty/simple-test/Encounter/simple-test-encounter.json similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/tests/TestEmpty/simple-test/Encounter/simple-test-encounter.json rename to src/test/resources/simple-test-ig/input/tests/TestEmpty/simple-test/Encounter/simple-test-encounter.json diff --git a/src/test/suite/resources/simple-test-ig/input/tests/TestEmpty/simple-test/Patient/simple-test.json b/src/test/resources/simple-test-ig/input/tests/TestEmpty/simple-test/Patient/simple-test.json similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/tests/TestEmpty/simple-test/Patient/simple-test.json rename to src/test/resources/simple-test-ig/input/tests/TestEmpty/simple-test/Patient/simple-test.json diff --git a/src/test/suite/resources/simple-test-ig/input/tests/TestFhirMissing/simple-test/Encounter/simple-test-encounter.json b/src/test/resources/simple-test-ig/input/tests/TestFhirMissing/simple-test/Encounter/simple-test-encounter.json similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/tests/TestFhirMissing/simple-test/Encounter/simple-test-encounter.json rename to src/test/resources/simple-test-ig/input/tests/TestFhirMissing/simple-test/Encounter/simple-test-encounter.json diff --git a/src/test/suite/resources/simple-test-ig/input/tests/TestFhirMissing/simple-test/Patient/simple-test.json b/src/test/resources/simple-test-ig/input/tests/TestFhirMissing/simple-test/Patient/simple-test.json similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/tests/TestFhirMissing/simple-test/Patient/simple-test.json rename to src/test/resources/simple-test-ig/input/tests/TestFhirMissing/simple-test/Patient/simple-test.json diff --git a/src/test/suite/resources/simple-test-ig/input/tests/results/Test.txt b/src/test/resources/simple-test-ig/input/tests/results/Test.txt similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/tests/results/Test.txt rename to src/test/resources/simple-test-ig/input/tests/results/Test.txt diff --git a/src/test/suite/resources/simple-test-ig/input/vocabulary/valueset/valueset-DischargeDisposition.json b/src/test/resources/simple-test-ig/input/vocabulary/valueset/valueset-DischargeDisposition.json similarity index 100% rename from src/test/suite/resources/simple-test-ig/input/vocabulary/valueset/valueset-DischargeDisposition.json rename to src/test/resources/simple-test-ig/input/vocabulary/valueset/valueset-DischargeDisposition.json diff --git a/src/test/suite/runTests.ts b/src/test/runTests.ts similarity index 100% rename from src/test/suite/runTests.ts rename to src/test/runTests.ts diff --git a/src/test/suite/resources/KALM.postman_collection.json b/src/test/suite/resources/KALM.postman_collection.json deleted file mode 100644 index f1b5b96..0000000 --- a/src/test/suite/resources/KALM.postman_collection.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "info": { - "_postman_id": "b9f97516-ca99-45c4-b977-2322267c6a15", - "name": "KALM", - "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "7449271" - }, - "item": [ - { - "name": "simple-test-ig", - "item": [ - { - "name": "Test bundle (data included)", - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "file", - "file": { - "src": "/Users/joshuareynolds/Documents/simple-test-ig/bundles/Test-bundle.json" - } - }, - "url": { - "raw": "{{localserver}}", - "host": [ - "{{localserver}}" - ] - } - }, - "response": [] - }, - { - "name": "Patient simple-test", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{localserver}}/Patient/simple-test", - "host": [ - "{{localserver}}" - ], - "path": [ - "Patient", - "simple-test" - ] - } - }, - "response": [] - }, - { - "name": "simple-test-encounter", - "request": { - "method": "GET", - "header": [], - "url": { - "raw": "{{localserver}}/Encounter/simple-test-encounter", - "host": [ - "{{localserver}}" - ], - "path": [ - "Encounter", - "simple-test-encounter" - ] - } - }, - "response": [] - } - ] - } - ], - "variable": [ - { - "key": "localserver", - "value": "http://localhost:8000" - } - ] -} \ No newline at end of file diff --git a/src/test/suite/resources/simple-test-ig/_refresh.sh b/src/test/suite/resources/simple-test-ig/_refresh.sh deleted file mode 100755 index 4e2554d..0000000 --- a/src/test/suite/resources/simple-test-ig/_refresh.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash -#DO NOT EDIT WITH WINDOWS -tooling_jar=tooling-cli-3.3.0.jar -input_cache_path=./input-cache -ig_ini_path=$PWD/ig.ini - -set -e -echo Checking internet connection... -#wget -q --spider tx.fhir.org - -if [ $? -eq 0 ]; then - echo "Online" - fsoption="" -else - echo "Offline" - fsoption="" -fi - -echo "$fsoption" - -tooling=$input_cache_path/$tooling_jar -if test -f "$tooling"; then - java -jar $tooling -RefreshIG -ini="$ig_ini_path" -d -t $fsoption -else - tooling=../$tooling_jar - echo $tooling - if test -f "$tooling"; then - java -jar $tooling -RefreshIG -ini="$ig_ini_path" -d -t $fsoption - else - echo IG Refresh NOT FOUND in input-cache or parent folder. Please run _updateCQFTooling. Aborting... - fi -fi diff --git a/src/test/suite/resources/simple-test-ig/_updateCQFTooling.sh b/src/test/suite/resources/simple-test-ig/_updateCQFTooling.sh deleted file mode 100755 index a870ee8..0000000 --- a/src/test/suite/resources/simple-test-ig/_updateCQFTooling.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash -#DO NOT EDIT WITH WINDOWS -#exit 1 - -r=releases -g=org.opencds.cqf -a=tooling-cli -v=3.3.0 - -dlurl='https://oss.sonatype.org/service/local/artifact/maven/redirect?r='${r}'&g='${g}'&a='${a}'&v='${v}'' - -echo ${dlurl} - -input_cache_path=./input-cache/ -tooling_jar=tooling-cli-3.3.0.jar - -set -e -if ! type "curl" > /dev/null; then - echo "ERROR: Script needs curl to download latest IG Tooling. Please install curl." - exit 1 -fi - -tooling="$input_cache_path$tooling_jar" -if test -f "$tooling"; then - echo "IG Tooling FOUND in input-cache" - jarlocation="$tooling" - jarlocationname="Input Cache" - upgrade=true -else - tooling="../$tooling_jar" - upgrade=true - if test -f "$tooling"; then - echo "IG Tooling FOUND in parent folder" - jarlocation="$tooling" - jarlocationname="Parent Folder" - upgrade=true - else - echo IG Tooling NOT FOUND in input-cache or parent folder... - jarlocation="$input_cache_path$tooling_jar" - jarlocationname="Input Cache" - upgrade=false - fi -fi - - #echo Will place tooling jar here: $input_cache_path$tooling_jar - echo Will place tooling jar here: $jarlocation - -echo "Downloading most recent tooling to $jarlocationname - it's ~110 MB, so this may take a bit" -# wget "https://oss.sonatype.org/service/local/repositories/snapshots/content/org/opencds/cqf/tooling/1.0-SNAPSHOT/tooling-1.0-20200107.163002-6-jar-with-dependencies.jar" -O "$jarlocation" -curl $dlurl -L -o "$jarlocation" --create-dirs -echo "Download complete." diff --git a/src/test/suite/resources/simple-test-ig/input/resources/device/cqf-tooling.json b/src/test/suite/resources/simple-test-ig/input/resources/device/cqf-tooling.json deleted file mode 100644 index 68e0279..0000000 --- a/src/test/suite/resources/simple-test-ig/input/resources/device/cqf-tooling.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "resourceType": "Device", - "id": "cqf-tooling", - "meta": { - "profile": [ "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/device-softwaresystem-cqfm" ] - }, - "manufacturer": "CQFramework", - "deviceName": [ { - "name": "cqf-tooling", - "type": "manufacturer-name" - } ], - "type": { - "coding": [ { - "system": "http://hl7.org/fhir/us/cqfmeasures/CodeSystem/software-system-type", - "code": "tooling" - } ] - }, - "version": [ { - "value": "3.3.0" - } ] -} \ No newline at end of file diff --git a/tests/types-and-values/TypesAndValues.md b/tests/types-and-values/TypesAndValues.md deleted file mode 100644 index 116f774..0000000 --- a/tests/types-and-values/TypesAndValues.md +++ /dev/null @@ -1,238 +0,0 @@ -# Types and Values in Clinical Quality Language - -## Definitions - -A CQL library is a set of _definitions_: - -```cql -library SimplifiedOutpatientEncounters version '3.0.000' - -using FHIR version '4.0.1' - -include FHIRHelpers version '4.1.000' called FHIRHelpers -include MATGlobalCommonFunctionsFHIR4 version '7.0.000' called Global - -valueset "Office Visit": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001' - -parameter "Measurement Period" Interval - -context Patient - -define "Qualifying Encounters": - [Encounter: "Office Visit"] ValidEncounter - where ValidEncounter.status = 'finished' - and Global."Normalize Interval"(ValidEncounter.period) during day of "Measurement Period" -``` - -Definitions can be _declarations_ or _statements_ - -### Declarations - -_declarations_ establish things like terminology and parameters: - -```cql -library SimplifiedOutpatientEncounters version '3.0.000' - -using FHIR version '4.0.1' - -include FHIRHelpers version '4.1.000' called FHIRHelpers -include MATGlobalCommonFunctionsFHIR4 version '7.0.000' called Global - -valueset "Office Visit": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001' - -parameter "Measurement Period" Interval - -context Patient -``` - -### Statements - -_statements_ define _expressions_ and _functions_ available in the library: - -```cql -define "Qualifying Encounters": - [Encounter: "Office Visit"] ValidEncounter - where ValidEncounter.status = 'finished' - and Global."Normalize Interval"(ValidEncounter.period) during day of "Measurement Period" -``` - -## Expressions - -- Every valid _expression_ can be _evaluated_ and either returns a _result_, or produces an _error_. -- Every _expression_ has a _type_ that can be determined at author-time (i.e. by analysis, as opposed to having to evaluate the expression). -- Every _result_ is either a _value_ or _null_ -- Every _value_ is of some type, and the result of a successful evaluation of an expression is guaranteed to be of the _type_ determined at author-time. - -## Types - -CQL has 5 type _categories_: - -1. Simple - Simple values such as strings, numbers, and dates -2. Structured - Values with named elements such as tuples and classes -3. Interval - Ranges of values -4. List - Ordered lists of values -5. Choice - Type defined as a set of possible types for the value - -### Simple Types - -Simple types are single values like the Integer 5, or the String 'Hello' - -[System-Defined Types](https://cql.hl7.org/03-developersguide.html#system-defined-types) - -| Type | Example Values | -| -------- | ----------------------------------------- | -| Boolean | `true`, `false` | -| Integer | `16`, `-28` | -| Decimal | `100.015` | -| String | `pending`, `active`, `complete` | -| Date | `@2014-01-25` | -| DateTime | `@2014-01-25T`,`@2014-01-25T14:30:14.559` | -| Time | `@T12:00:00.0`,`@T14:30:14.559` | - -### Structured Types - -Structured types are values that contain _named elements_ (sometimes called attributes or properties). - -Structured types can be _tuple types_ (meaning they don't have a name, they are just a set of named elements): - -```cql -define Info: Tuple { name: 'Patrick', birthDate: @2014-01-01 } -``` - -or they can be _classes_, which are _named tuple types_ - -```cql -// NOTE: Simplified for display purposes -define PatientExpression: Patient { name: 'Patrick', birthDate: @2014-01-01 } -``` - -CQL also defines several structured types to facilitate representation and manipulation of clinical information: Code, Concept, Quantity and Ratio. - -As per the CQL Reference Appendix B, the Quantity type represents quantities with a specified unit within CQL. The unit must be a valid UCUM unit or CQL temporal keyword. UCUM units in CQL use the case-sensitive (c/s) form. When a quantity value has no unit specified, operations are performed with the default UCUM unit ('1'). The value element of a Quantity must be present. https://cql.hl7.org/09-b-cqlreference.html#quantity - -### Interval Types - -Interval types support ranges from a low value to a high value: - -```cql -define IntegerInterval: Interval[3, 5) -define DateTimeInterval: Interval[@2014-01-01T00:00:00.0, @2015-01-01T00:00:00.0) -``` - -### List Types - -11. List types support collections of values. - -```cql -define IntegerList: { 1, 2, 3, 4, 5 } -define StringList: { 'a', 'b', 'c' } -``` - -### Choice Types - -Choice types are used to indicate that a value may be one of any of a choice of types. For example, the Patient.deceased element in the FHIR model can be a Boolean, or a DateTime. This means that the value of the element at run-time (i.e. when the expression is being evaluated) can be either of those types. - -```cql -parameter X Choice - -define TestXAsInteger: X = 1 -define TestXAsDecimal: X = 1.0 -define TestXAsString: X = 'ABC' -define TestXAsQuantity: X = 20 'mg' -define TestXAsBoolean: X = true -``` - -Choice types are often used in data models such as QDM and FHIR, especially when the type of data being modeled may be represented in multiple ways. For example, Observation values or Physical Exam, Performed values. - -## Casting and Conversion - -CQL supports operations for _casting_ and _converting_ - -Converting involves changing a value from one type to another: - -```cql -// Explicit Conversions: https://cql.hl7.org/03-developersguide.html#explicit-conversion -define ConvertValues: convert '1' to Integer -define ConvertUnits: convert 5 'mg' to 'g' -define InvalidConvert: convert 'Foo' to Integer // Results in null -``` - -Conversions can be _implicit_ and _explicit_ Implicit conversion occurs when mixing types in operations, such as addition: - -```cql -// Implicit Conversions: https://cql.hl7.org/03-developersguide.html#implicit-conversions -define ImplicitConversion: 2 + 2.0 // Evaluates as Decimal addition, the integer input is implicitly converted to a decimal -``` - -Casting involves treating a value at author-time as a more specific type that the value is expected to be at run-time: - -```cql -define Procedures: [Procedure] -define ImagingProcedures: Procedures P where P is ImagingProcedure return P as ImagingProcedure -define ImagingProceduresOrError: Procedures P return cast P as ImagingProcedure -``` - -CQL supports type testing with the `is` operator, and type casting with the `as` and `cast`...`as` operators. - -- `is` will return true if the value at runtime is of the given type. -- `as` will return the value if it is of the given type, otherwise `null` -- `cast`...`as` will return the value if it is of the given type, otherwise an error will be raised - -## Casting with Choice Types - -In particular, casting can be used with choice types to treat values that may be represented as different types in different ways within the logic: - -```cql -/* -@description: Returns the value, interpreted as an Integer -@comments: If the input is a Quantity, the result is the same as invoking `ToInteger(value as Quantity)`. -If the input is an Integer, the result is the same as invoking `value as Integer` -*/ -define function ToInteger(value Choice): - if value is Quantity then - ToInteger(value as Quantity) - else - value as Integer -``` - -## Choice Types Revisited - -It is important to understand the following statement regarding the potential impact on data capture: - -> The semantics for casting will result in a null if the run-time value of the element is not of the appropriate type. - -Thus, even if the query expression casts a broader net, the result will be constrained to data specified by the ‘as type’. For example: - -- String data would not be returned if constrained ‘as Integer’ or ‘as Quantity’. -- Integer data would not be returned if constrained ‘as Quantity’. -- Quantity data would not be returned if constrained ‘as Integer’. - -Code systems, such as LOINC, may permit data in more than one type as specified within LOINC 2.73 code definition as Property, eg Num, and Scale, eg Qn where a Property category of Num indicates the result is a count that begins with a number. If Scale is Quantitative (Qn) that indicates the result of the test is a numeric value that relates to a continuous scale reported either as an integer, a ratio, a real number, or a range. The test result value may optionally contain a relational operator from the set [<=,<,>,>=]. Valid values for a quantitative test are of the form "7", "-7", "7.4", "-7.4", "7.8912", "0.125", "<10", ">12000", 1-10, 1:256 -Per QDM, LOINC and QRDA I specification, a numerical result could potentially be submitted as integer, decimal number or quantity. For example, data submitted as a quantity for evaluation by the eCQM as Integer, or as lab results submitted as string for evaluation as Quantity or Quantity.value, would not be detected without further logic within the measure or receiving system. - -Takeaways: - -- When evaluating a measure’s intent, one should assess the limits on data capture implied by use of ‘as type’ expression that further constrains the query expression. -- When evaluating data for submission, one should assess the data format vs schema vs eCQM specification to prevent unintended limits on data capture. -- To broaden data capture, potentially CQL logic could convert some data from one type to another. - -For example, appropriate data in Quantity.value where Quantity.unit = '1' might be converted to Integer by a CQL function: - -```cql -/* -@description: Returns the value of the given Quantity, interpreted as an Integer if it is safe to do so. -@comments: A Quantity value can be safely interpreted as an integer if it has no decimal component (i.e. zeros after the decimal), -and it has the default UCUM unit of '1' -*/ -define function ToInteger(value Quantity): - case - when Abs(value.value - Truncate(value.value)) > 0.00000001 then - Message(null, true, 'ToInteger.InvalidArgument', ErrorSeverity, 'The quantity has a non-zero decimal component and cannot be safely interpreted as an Integer') - when value.unit != '1' then - Message(null, true, 'ToInteger.InvalidUnit', ErrorSeverity, 'The quantity has non-default units specified and cannot be safely interpreted as an Integer') - else - Truncate(value.value) - end -``` - -> Hat tip Peter Muir for the guidance related to data retrieval, submission, and validation diff --git a/tests/types-and-values/_Agenda.md b/tests/types-and-values/_Agenda.md deleted file mode 100644 index a319135..0000000 --- a/tests/types-and-values/_Agenda.md +++ /dev/null @@ -1 +0,0 @@ -1. Deep Dive on Types, Values and Choices diff --git a/tests/types-and-values/ig.ini b/tests/types-and-values/ig.ini deleted file mode 100644 index 87d350b..0000000 --- a/tests/types-and-values/ig.ini +++ /dev/null @@ -1,4 +0,0 @@ -[IG] -ig = input/typesandvalues.xml -template = cqf.fhir.template#current -usage-stats-opt-out = false diff --git a/tests/types-and-values/input/cql/CQMCommonFHIR4.cql b/tests/types-and-values/input/cql/CQMCommonFHIR4.cql deleted file mode 100644 index e6d2d5e..0000000 --- a/tests/types-and-values/input/cql/CQMCommonFHIR4.cql +++ /dev/null @@ -1,337 +0,0 @@ -/* -@update: BTR 2020-03-31 -> -Incremented version to 5.0.000 -Updated FHIR version to 4.0.1 -Changed timezone keyword to timezoneoffset for use with CQL 1.4 -Removed Normalize Onset in favor of more general Normalize Interval -Updated CodeSystems for ConditionVerificationStatusCodes and RoleCodes - -@update: BTR 2021-05-13 -> -Added ActiveCondition Codes and Inactive Condition Codes value sets -Added function documentation throughout -Fixed EDVisit not using Last -Updated prevalence period to use an inclusive boundary if the condition is active -Added HasStart, HasEnd, Earliest, and Latest functions -Removed ToDate and Age calculation functions - -@update: BTR 2021-06-25 -> -Added GetBaseExtension overloads for Element - -@update: BTR 2022-05-24 -> -Update for 2022 AU content -Refactor to use CQF.FHIRCommon -*/ -library CQMCommonFHIR4 version '7.0.000' - -using FHIR version '4.0.1' - -include fhir.cqf.common.FHIRHelpers version '4.0.1' called FHIRHelpers -include fhir.cqf.common.FHIRCommon version '4.0.1' called FHIRCommon - -valueset "Emergency Department Visit": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.117.1.7.1.292' -valueset "Encounter Inpatient": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.666.5.307' -valueset "Intensive Care Unit": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1029.206' -valueset "Observation Services": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1111.143' -valueset "Outpatient Surgery Service": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1110.38' -valueset "Present on Admission or Clinically Undetermined": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113762.1.4.1147.197' - -parameter "Measurement Period" Interval - default Interval[@2022-01-01T00:00:00.0, @2023-01-01T00:00:00.0) - -context Patient - -define "Inpatient Encounter": - [Encounter: "Encounter Inpatient"] EncounterInpatient - where EncounterInpatient.status = 'finished' - and "LengthInDays"(EncounterInpatient.period) <= 120 - and EncounterInpatient.period ends during day of "Measurement Period" - -/* -@description: Returns an interval of date values extracted from the input interval of date-time values -@comment: This function returns an interval constructed using the `date from` extractor on the start -and end values of the input date-time interval. Note that using a precision specifier such as `day of` -as part of a timing phrase is preferred to communicate intent to perform day-level comparison, as well -as for general readability. -*/ -define function "ToDateInterval"(period Interval): - Interval[date from start of period, date from end of period] - -/* -@description: Calculates the difference in calendar days between the start and end of the given interval. -*/ -define function "LengthInDays"(Value Interval ): - difference in days between start of Value and end of Value - -/* -@description: Returns the most recent emergency department visit, if any, that occurs 1 hour or less prior to the given encounter. -*/ -define function "ED Visit"(TheEncounter FHIR.Encounter ): - Last( - [Encounter: "Emergency Department Visit"] EDVisit - where EDVisit.status = 'finished' - and EDVisit.period ends 1 hour or less on or before start of FHIRHelpers.ToInterval(TheEncounter.period) - sort by end of period - ) - -/* -@description: Hospitalization returns the total interval for admission to discharge for the given encounter, or for the admission of any immediately prior emergency department visit to the discharge of the given encounter. -*/ -define function "Hospitalization"(TheEncounter FHIR.Encounter ): - ( "ED Visit"(TheEncounter) ) X - return - if X is null then TheEncounter.period - else Interval[start of FHIRHelpers.ToInterval(X.period), end of FHIRHelpers.ToInterval(TheEncounter.period)] - -/* -@description: Returns list of all locations within an encounter, including locations for immediately prior ED visit. -*/ -define function "Hospitalization Locations"(TheEncounter FHIR.Encounter ): - ( "ED Visit"(TheEncounter) ) EDEncounter - return - if EDEncounter is null then TheEncounter.location - else flatten { EDEncounter.location, TheEncounter.location } - -/* -@description: Returns the length of stay in days (i.e. the number of days between admission and discharge) for the given encounter, or from the admission of any immediately prior emergency department visit to the discharge of the encounter -*/ -define function "Hospitalization Length of Stay"(TheEncounter FHIR.Encounter ): - LengthInDays("Hospitalization"(TheEncounter)) - -/* -@description: Returns admission time for an encounter or for immediately prior emergency department visit. -*/ -define function "Hospital Admission Time"(TheEncounter FHIR.Encounter ): - start of "Hospitalization"(TheEncounter) - -/* -@description: Hospital Discharge Time returns the discharge time for an encounter -*/ -define function "Hospital Discharge Time"(TheEncounter FHIR.Encounter ): - end of FHIRHelpers.ToInterval(TheEncounter.period) - -/* -@description: Returns earliest arrival time for an encounter including any prior ED visit. -*/ -define function "Hospital Arrival Time"(TheEncounter FHIR.Encounter ): - start of FHIRHelpers.ToInterval(First( - ( "Hospitalization Locations"(TheEncounter) ) HospitalLocation - sort by start of FHIRHelpers.ToInterval(period) - ).period) - -/* -@description: Returns the latest departure time for encounter including any prior ED visit. -*/ -define function "Hospital Departure Time"(TheEncounter FHIR.Encounter): - end of FHIRHelpers.ToInterval(Last( - ( "Hospitalization Locations"(TheEncounter) ) HospitalLocation - sort by start of FHIRHelpers.ToInterval(period) - ).period) - -define function "Emergency Department Arrival Time"(TheEncounter FHIR.Encounter): - start of FHIRHelpers.ToInterval(( - singleton from ( - ( "Hospitalization Locations"(TheEncounter) ) HospitalLocation - where GetLocation(HospitalLocation.location).type in "Emergency Department Visit" - ) - ).period) - -/* -@description: Hospitalization with Observation and Outpatient Surgery Service returns the total interval from the start of any immediately prior emergency department visit, outpatient surgery visit or observation visit to the discharge of the given encounter. -*/ -define function "HospitalizationWithObservationAndOutpatientSurgeryService"(TheEncounter "Encounter" ): - TheEncounter Visit - let ObsVisit: Last([Encounter: "Observation Services"] LastObs - where LastObs.status = 'finished' - and LastObs.period ends 1 hour or less on or before start of Visit.period - sort by end of period - ), - VisitStart: Coalesce(start of ObsVisit.period, start of Visit.period), - EDVisit: Last([Encounter: "Emergency Department Visit"] LastED - where LastED.status = 'finished' - and LastED.period ends 1 hour or less on or before VisitStart - sort by end of period - ), - VisitStartWithED: Coalesce(start of EDVisit.period, VisitStart), - OutpatientSurgeryVisit: Last([Encounter: "Outpatient Surgery Service"] LastSurgeryOP - where LastSurgeryOP.period ends 1 hour or less on or before VisitStartWithED - sort by end of period - ) - return Interval[Coalesce(start of OutpatientSurgeryVisit.period, VisitStartWithED), end of Visit.period] - -/* -@description: Hospitalization with Observation returns the total interval from the start of any immediately prior emergency department visit through the observation visit to the discharge of the given encounter -*/ -define function "HospitalizationWithObservation"(TheEncounter FHIR.Encounter ): - TheEncounter Visit - let ObsVisit: Last([Encounter: "Observation Services"] LastObs - where LastObs.status = 'finished' - and LastObs.period ends 1 hour or less on or before start of Visit.period - sort by end of period - ), - VisitStart: Coalesce(start of ObsVisit.period, start of Visit.period), - EDVisit: Last([Encounter: "Emergency Department Visit"] LastED - where LastED.status = 'finished' - and LastED.period ends 1 hour or less on or before VisitStart - sort by end of period - ) - return Interval[Coalesce(start of EDVisit.period, VisitStart), end of Visit.period] - -/* -@description: Hospitalization with Observation Length of Stay returns the length in days from the start of any immediately prior emergency department visit through the observation visit to the discharge of the given encounter -*/ -define function "HospitalizationWithObservationLengthofStay"(TheEncounter "Encounter" ): - "LengthInDays"("HospitalizationWithObservation"(TheEncounter)) - -/* -@description: First Inpatient Intensive Care Unit returns the first intensive care unit for the given encounter, without considering any immediately prior emergency department visit. -*/ -define function "FirstInpatientIntensiveCareUnit"(Encounter FHIR.Encounter ): - First((Encounter.location)HospitalLocation - where GetLocation(HospitalLocation.location).type in "Intensive Care Unit" - and HospitalLocation.period during Encounter.period - sort by start of period - ) - -/* -@description: Normalizes the input argument to an interval representation. -@comment: The input can be provided as a dateTime, Period, Timing, instant, string, Age, or Range. -The intent of this function is to provide a clear and concise mechanism to treat single -elements that have multiple possible representations as intervals so that logic doesn't have to account -for the variability. More complex calculations (such as medication request period or dispense period -calculation) need specific guidance and consideration. That guidance may make use of this function, but -the focus of this function is on single element calculations where the semantics are unambiguous. -If the input is a dateTime, the result a DateTime Interval beginning and ending on that dateTime. -If the input is a Period, the result is a DateTime Interval. -If the input is a Timing, an error is raised indicating a single interval cannot be computed from a Timing. -If the input is an instant, the result is a DateTime Interval beginning and ending on that instant. -If the input is a string, an error is raised indicating a single interval cannot be computed from a string. -If the input is an Age, the result is a DateTime Interval beginning when the patient was the given Age, -and ending immediately prior to when the patient was the given Age plus one year. -If the input is a Range, the result is a DateTime Interval beginning when the patient was the Age given -by the low end of the Range, and ending immediately prior to when the patient was the Age given by the -high end of the Range plus one year. -*/ -define function "Normalize Interval"(choice Choice ): - FHIRCommon.ToInterval(choice) - -/* -@description: Returns an interval representing the abatement of the given condition, if an -abatement element is present, null otherwise. -@comment: This function uses the semantics of Normalize Interval to interpret the abatement -element. -*/ -define function "Normalize Abatement"(condition Condition ): - FHIRCommon.ToAbatementInterval(condition) - -/* -@description: Returns an interval representing the period during which the condition was prevalent (i.e. onset to abatement) -@comment: If the condition is "active", then abatement being unknown -would indicate the condition is ongoing, and the ending boundary of the prevalence -period is inclusive, otherwise, the abatement is considered unknown and the ending boundary -of the prevalence period is exclusive. -Note that when using this function it should be noted that many clinical systems -do not actually capture abatement, so care should be taken when using this function -to meet clinical intent. -*/ -define function "Prevalence Period"(condition Condition ): - FHIRCommon.ToPrevalenceInterval(condition) - -/* -@description: Returns the tail of the given uri (i.e. everything after the last slash in the URI). -*/ -define function "GetId"(uri String ): - Last(Split(uri, '/')) - -/* -@description: Returns the Condition resources referenced by the diagnosis element of the Encounter -*/ -define function "EncounterDiagnosis"(Encounter Encounter ): - Encounter.diagnosis D - return singleton from ([Condition] C where C.id = "GetId"(D.condition.reference)) - -/* -@description: Returns the Condition resource for the given reference -*/ -define function "GetCondition"(reference Reference): - singleton from ([Condition] C where C.id = "GetId"(reference.reference)) - -/* -@description: Returns the condition that is specified as the principal diagnosis for the encounter -*/ -define function "PrincipalDiagnosis"(TheEncounter Encounter ): - (singleton from (TheEncounter.diagnosis D where D.rank = 1)) PD - return singleton from ([Condition] C where C.id = "GetId"(PD.condition.reference)) - -/* -@description: Returns the Location resource specified by the given reference -*/ -define function "GetLocation"(reference Reference ): - singleton from ( - [Location] L where L.id = GetId(reference.reference) - ) - -/* -@description: Returns the medication code for the given MedicationRequest -*/ -define function "GetMedicationCode"(request MedicationRequest ): - if request.medication is CodeableConcept then - request.medication as CodeableConcept - else - (singleton from ([Medication] M where M.id = GetId((request.medication as Reference).reference))).code - -/* -@description: Given an interval, return true if the interval has a starting boundary specified (i.e. the start of the interval is not null and not the minimum DateTime value) -*/ -define function "HasStart"(period Interval ): - not ( start of period is null - or start of period = minimum DateTime - ) - -/* -@description: Given an interval, return true if the interval has an ending boundary specified (i.e. the end of the interval is not null and not the maximum DateTime value) -*/ -define function "HasEnd"(period Interval ): - not ( - end of period is null - or end of period = maximum DateTime - ) - -/* -@description: Given an interval, return the ending point if the interval has an ending boundary specified, otherwise, return the starting point -*/ -define function "Latest"(choice Choice ): - ("Normalize Interval"(choice)) period - return - if (HasEnd(period)) then end of period - else start of period - -/* -@description: Given an interval, return the starting point if the interval has a starting boundary specified, otherwise, return the ending point -*/ -define function "Earliest"(choice Choice ): - ("Normalize Interval"(choice)) period - return - if (HasStart(period)) then start of period - else end of period - -/* -@description: Creates a list of integers from 1 to how many days are in the interval. Note, this wont create an index for -the final day if it is less than 24 hours. This also includes the first 24 hour period. -*/ -define function "Interval To Day Numbers"(Period Interval): - ( expand { Interval[1, duration in days between start of Period and end of Period]} ) DayNumber - return end of DayNumber - -/* -@description: Creates a list of 24 hour long intervals in an interval paired with the index (1 indexed) to which 24 hour interval it is. -Note that the result will include intervals that are closed at the beginning and open at the end -*/ -define function "Days In Period"(Period Interval): - ( "Interval To Day Numbers"(Period)) DayIndex - let startPeriod: start of Period + (24 hours * (DayIndex - 1)), - endPeriod: if (hours between startPeriod and end of Period < 24) then startPeriod - else start of Period + (24 hours * DayIndex) - return Tuple { - dayIndex: DayIndex, - dayPeriod: Interval[startPeriod, endPeriod) - } diff --git a/tests/types-and-values/input/cql/TestConvert.cql b/tests/types-and-values/input/cql/TestConvert.cql deleted file mode 100644 index 7eb5a91..0000000 --- a/tests/types-and-values/input/cql/TestConvert.cql +++ /dev/null @@ -1,50 +0,0 @@ -library TestConvert - -/* -@description: Determines whether failed conversions will result in a run-time error or a null result with a warning -@comments: With this parameter set to 'Error', failed conversions will result in a runtime error. -With this parameter set to 'Warning' failed conversions will result in a Warning and the function returning 'null'. -*/ -parameter ErrorSeverity String default 'Warning' - -/* -@description: Returns the value of the given Quantity, interpreted as an Integer if it is safe to do so. -@comments: A Quantity value can be safely interpreted as an integer if it has no decimal component (i.e. zeros after the decimal), -and it has the default UCUM unit of '1' -*/ -define function ToInteger(value Quantity): - case - when Abs(value.value - Truncate(value.value)) > 0.00000001 then - Message(null, true, 'ToInteger.InvalidArgument', ErrorSeverity, 'The quantity has a non-zero decimal component and cannot be safely interpreted as an Integer') - when value.unit != '1' then // Add support for UCUM Annotations - Message(null, true, 'ToInteger.InvalidUnit', ErrorSeverity, 'The quantity has non-default units specified and cannot be safely interpreted as an Integer') - else - Truncate(value.value) - end - -/* -@description: Returns the value, interpreted as an Integer -@comments: If the input is a Quantity, the result is the same as invoking `ToInteger(value as Quantity)`. -If the input is an Integer, the result is the same as invoking `value as Integer` -*/ -define function ToInteger(value Choice): - if value is Quantity then - ToInteger(value as Quantity) - else - value as Integer - -define TestToInteger1: - ToInteger(1.0 '1') = 1 - -define TestToInteger2: - ToInteger(-1.0 '1') = -1 - -define TestToIntegerFail: - ToInteger(1.0 'g') is null - -define TestToIntegerFail2: - ToInteger(1.1 '1') is null - -define TestToIntegerFail3: - ToInteger(-1.1 '1') is null - diff --git a/tests/types-and-values/input/cql/TypesAndValues.cql b/tests/types-and-values/input/cql/TypesAndValues.cql deleted file mode 100644 index 8936c6e..0000000 --- a/tests/types-and-values/input/cql/TypesAndValues.cql +++ /dev/null @@ -1,72 +0,0 @@ -library TypesAndValues version '3.0.000' - -using FHIR version '4.0.1' - -include fhir.cqf.common.FHIRHelpers version '4.0.1' called FHIRHelpers -include fhir.cqf.common.FHIRCommon version '4.0.1' called FHIRCommon - -valueset "Office Visit": 'http://cts.nlm.nih.gov/fhir/ValueSet/2.16.840.1.113883.3.464.1003.101.12.1001' - -parameter "Measurement Period" Interval -parameter X Choice -parameter ErrorSeverity String default 'Warning' - -context Patient - -define "Qualifying Encounters": - [Encounter: "Office Visit"] ValidEncounter - where ValidEncounter.status = 'finished' - and FHIRCommon.ToInterval(ValidEncounter.period) during day of "Measurement Period" - - -define Info: Tuple { name: 'Patrick', birthDate: @2014-01-01 } -define PatientExpression: Patient { name: { FHIR.HumanName { given: { FHIR.string { value: 'Patrick' } } } }, birthDate: FHIR.date { value: @2014-01-01 } } - -define IntegerInterval: Interval[3, 5) -define DateTimeInterval: Interval[@2014-01-01T00:00:00.0, @2015-01-01T00:00:00.0) - -define IntegerList: { 1, 2, 3, 4, 5 } -define StringList: { 'a', 'b', 'c' } - -define TestXAsInteger: X = 1 -define TestXAsDecimal: X = 1.0 -define TestXAsString: X = 'ABC' -define TestXAsQuantity: X = 20 'mg' -// Author-time error because Boolean is not one of the possible types for the parameter X -//define TestXAsBoolean: X = true - - -// Explicit Conversions: https://cql.hl7.org/03-developersguide.html#explicit-conversion -define ConvertValues: convert '1' to Integer -define ConvertUnits: convert 5 'mg' to 'g' -define InvalidConvert: convert 'Foo' to Integer // Results in null - -// Implicit Conversions: https://cql.hl7.org/03-developersguide.html#implicit-conversions -define ImplicitConversion: 2 + 2.0 // Evaluates as Decimal addition, the integer input is implicitly converted to a decimal - -/* -@description: Returns the value of the given Quantity, interpreted as an Integer if it is safe to do so. -@comments: A Quantity value can be safely interpreted as an integer if it has no decimal component (i.e. zeros after the decimal), -and it has the default UCUM unit of '1' -*/ -define function ToInteger(value Quantity): - case - when Abs(value.value - Truncate(value.value)) > 0.00000001 then - Message(null, true, 'ToInteger.InvalidArgument', ErrorSeverity, 'The quantity has a non-zero decimal component and cannot be safely interpreted as an Integer') - when value.unit != '1' then - Message(null, true, 'ToInteger.InvalidUnit', ErrorSeverity, 'The quantity has non-default units specified and cannot be safely interpreted as an Integer') - else - Truncate(value.value) - end - -/* -@description: Returns the value, interpreted as an Integer -@comments: If the input is a Quantity, the result is the same as invoking `ToInteger(value as Quantity)`. -If the input is an Integer, the result is the same as invoking `value as Integer` -*/ -define function ToInteger(value Choice): - if value is Quantity then - ToInteger(value as Quantity) - else - value as Integer - diff --git a/tests/types-and-values/input/tests/library/QuestionnaireExtraction/example/Patient-example.json b/tests/types-and-values/input/tests/library/QuestionnaireExtraction/example/Patient-example.json deleted file mode 100644 index 8d15772..0000000 --- a/tests/types-and-values/input/tests/library/QuestionnaireExtraction/example/Patient-example.json +++ /dev/null @@ -1,256 +0,0 @@ -{ - "resourceType": "Patient", - "id": "example", - "meta": { - "profile": ["http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-patient"] - }, - "text": { - "status": "generated", - "div": "

Generated Narrative with Details

id: example

meta:

identifier: Medical record number = 12345 (USUAL)

active: true

name: Peter James Chalmers (OFFICIAL), Jim Chalmers , Peter James Windsor (MAIDEN)

telecom: ph: (03) 5555 6473(WORK), ph: (03) 3410 5613(MOBILE), ph: (03) 5555 8834(OLD)

gender: male

birthDate: 1974-12-25

deceased: false

address: 534 Erewhon St PeasantVille, Utah 84414(HOME)

Contacts

-RelationshipNameTelecomAddressGenderPeriod
*Next-of-Kin (Details : {http://terminology.hl7.org/CodeSystem/v2-0131 code 'N' = 'Next-of-Kin)Bénédicte du Marché ph: +33 (237) 998327534 Erewhon St PleasantVille VT 3999 (HOME)femaleJan 1, 2012 12:00:00 AM --> (ongoing)

managingOrganization: Generated Summary: id: example; ??; active; Organizational team; name: Health Level Seven International; ph: (+1) 734-677-7777, fax: (+1) 734-677-6622, hq@HL7.org

" - }, - "extension": [ - { - "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-race", - "extension": [ - { - "url": "ombCategory", - "valueCoding": { - "system": "urn:oid:2.16.840.1.113883.6.238", - "code": "2106-3", - "display": "White" - } - }, - { - "url": "ombCategory", - "valueCoding": { - "system": "urn:oid:2.16.840.1.113883.6.238", - "code": "1002-5", - "display": "American Indian or Alaska Native" - } - }, - { - "url": "ombCategory", - "valueCoding": { - "system": "urn:oid:2.16.840.1.113883.6.238", - "code": "2028-9", - "display": "Asian" - } - }, - { - "url": "detailed", - "valueCoding": { - "system": "urn:oid:2.16.840.1.113883.6.238", - "code": "1586-7", - "display": "Shoshone" - } - }, - { - "url": "detailed", - "valueCoding": { - "system": "urn:oid:2.16.840.1.113883.6.238", - "code": "2036-2", - "display": "Filipino" - } - }, - { - "url": "detailed", - "valueCoding": { - "system": "urn:oid:2.16.840.1.113883.6.238", - "code": "1735-0", - "display": "Alaska Native" - } - }, - { - "url": "text", - "valueString": "Mixed" - } - ] - }, - { - "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-ethnicity", - "extension": [ - { - "url": "ombCategory", - "valueCoding": { - "system": "urn:oid:2.16.840.1.113883.6.238", - "code": "2135-2", - "display": "Hispanic or Latino" - } - }, - { - "url": "detailed", - "valueCoding": { - "system": "urn:oid:2.16.840.1.113883.6.238", - "code": "2184-0", - "display": "Dominican" - } - }, - { - "url": "detailed", - "valueCoding": { - "system": "urn:oid:2.16.840.1.113883.6.238", - "code": "2148-5", - "display": "Mexican" - } - }, - { - "url": "text", - "valueString": "Hispanic or Latino" - } - ] - }, - { - "url": "http://hl7.org/fhir/us/core/StructureDefinition/us-core-birthsex", - "valueCode": "M" - } - ], - "identifier": [ - { - "use": "usual", - "type": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0203", - "code": "MR" - } - ] - }, - "system": "urn:oid:1.2.36.146.595.217.0.1", - "value": "12345", - "period": { - "start": "2001-05-06" - }, - "assigner": { - "display": "Acme Healthcare" - } - } - ], - "active": true, - "name": [ - { - "use": "official", - "family": "Chalmers", - "given": ["Peter", "James"] - }, - { - "use": "usual", - "family": "Chalmers", - "given": ["Jim"] - }, - { - "use": "maiden", - "family": "Windsor", - "given": ["Peter", "James"], - "period": { - "end": "2002" - } - } - ], - "telecom": [ - { - "system": "phone", - "value": "(03) 5555 6473", - "use": "work", - "rank": 1 - }, - { - "system": "phone", - "value": "(03) 3410 5613", - "use": "mobile", - "rank": 2 - }, - { - "system": "phone", - "value": "(03) 5555 8834", - "use": "old", - "period": { - "end": "2014" - } - } - ], - "gender": "male", - "birthDate": "1974-12-25", - "_birthDate": { - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/patient-birthTime", - "valueDateTime": "1974-12-25T14:35:45-05:00" - } - ] - }, - "deceasedBoolean": false, - "address": [ - { - "use": "home", - "type": "both", - "text": "534 Erewhon St PeasantVille, Utah 84414", - "line": ["534 Erewhon St"], - "city": "PleasantVille", - "district": "Rainbow", - "state": "UT", - "postalCode": "84414", - "period": { - "start": "1974-12-25" - } - } - ], - "maritalStatus": { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v3-MaritalStatus", - "code": "M" - } - ] - }, - "contact": [ - { - "relationship": [ - { - "coding": [ - { - "system": "http://terminology.hl7.org/CodeSystem/v2-0131", - "code": "N" - } - ] - } - ], - "name": { - "family": "du Marché", - "_family": { - "extension": [ - { - "url": "http://hl7.org/fhir/StructureDefinition/humanname-own-prefix", - "valueString": "VV" - } - ] - }, - "given": ["Bénédicte"] - }, - "telecom": [ - { - "system": "phone", - "value": "+33 (237) 998327" - } - ], - "address": { - "use": "home", - "type": "both", - "line": ["534 Erewhon St"], - "city": "PleasantVille", - "district": "Rainbow", - "state": "VT", - "postalCode": "3999", - "period": { - "start": "1974-12-25" - } - }, - "gender": "female", - "period": { - "start": "2012" - } - } - ], - "managingOrganization": { - "reference": "Organization/example" - } -} diff --git a/tests/types-and-values/input/tests/library/QuestionnaireExtraction/example/QuestionnaireResponse-phq-9-questionnaireresponse.json b/tests/types-and-values/input/tests/library/QuestionnaireExtraction/example/QuestionnaireResponse-phq-9-questionnaireresponse.json deleted file mode 100644 index b86b137..0000000 --- a/tests/types-and-values/input/tests/library/QuestionnaireExtraction/example/QuestionnaireResponse-phq-9-questionnaireresponse.json +++ /dev/null @@ -1,138 +0,0 @@ -{ - "resourceType": "QuestionnaireResponse", - "id": "phq-9-questionnaireresponse", - "questionnaire": "http://somewhere.org/fhir/uv/mycontentig/Questionnaire/phq-9-questionnaire", - "status": "completed", - "subject": { - "reference": "Patient/example" - }, - "authored": "2021-09-13T16:29:00-07:00", - "item": [ - { - "linkId": "LittleInterest", - "text": "Little interest or pleasure in doing things", - "answer": [ - { - "valueCoding": { - "system": "http://loinc.org", - "code": "LA6568-5", - "display": "Not at all" - } - } - ] - }, - { - "linkId": "FeelingDown", - "text": "Feeling down, depressed, or hopeless", - "answer": [ - { - "valueCoding": { - "system": "http://loinc.org", - "code": "LA6569-3", - "display": "Several days" - } - } - ] - }, - { - "linkId": "TroubleSleeping", - "text": "Trouble falling or staying asleep", - "answer": [ - { - "valueCoding": { - "system": "http://loinc.org", - "code": "LA6569-3", - "display": "Several days" - } - } - ] - }, - { - "linkId": "FeelingTired", - "text": "Feeling tired or having little energy", - "answer": [ - { - "valueCoding": { - "system": "http://loinc.org", - "code": "LA6569-3", - "display": "Several days" - } - } - ] - }, - { - "linkId": "BadAppetite", - "text": "Poor appetite or overeating", - "answer": [ - { - "valueCoding": { - "system": "http://loinc.org", - "code": "LA6568-5", - "display": "Not at all" - } - } - ] - }, - { - "linkId": "FeelingBadAboutSelf", - "text": "Feeling bad about yourself - or that you are a failure or have let yourself or your family down", - "answer": [ - { - "valueCoding": { - "system": "http://loinc.org", - "code": "LA6568-5", - "display": "Not at all" - } - } - ] - }, - { - "linkId": "TroubleConcentrating", - "text": "Trouble concentrating on things, such as reading the newspaper or watching television", - "answer": [ - { - "valueCoding": { - "system": "http://loinc.org", - "code": "LA6568-5", - "display": "Not at all" - } - } - ] - }, - { - "linkId": "MovingSpeaking", - "text": "Moving or speaking so slowly that other people could have noticed. Or the opposite - being so fidgety or restless that you have been moving around a lot more than usual", - "answer": [ - { - "valueCoding": { - "system": "http://loinc.org", - "code": "LA6568-5", - "display": "Not at all" - } - } - ] - }, - { - "linkId": "TotalScore", - "text": "Total score", - "answer": [ - { - "valueInteger": 3 - } - ] - }, - { - "linkId": "Difficulty", - "text": "If you checked off any problems, how difficult have these problems made it for you to do your work, take care of things at home, or get along with other people", - "answer": [ - { - "valueCoding": { - "system": "http://loinc.org", - "code": "LA6568-5", - "display": "Not at all" - } - } - ] - } - ] -} diff --git a/tests/types-and-values/input/tests/results/TypesAndValues.txt b/tests/types-and-values/input/tests/results/TypesAndValues.txt deleted file mode 100644 index e69de29..0000000 diff --git a/tests/types-and-values/input/typesandvalues.xml b/tests/types-and-values/input/typesandvalues.xml deleted file mode 100644 index 14116e0..0000000 --- a/tests/types-and-values/input/typesandvalues.xml +++ /dev/null @@ -1,128 +0,0 @@ - - - - - - - - - <status value="draft"/> - <experimental value="true"/> - <publisher value="Alphora"/> - <contact> - <telecom> - <system value="url"/> - <value value="http://alphora.com"/> - </telecom> - </contact> - <description value="A brief description of what MyContentIG is about (probably the same text as in your readme)"/> - <jurisdiction> - <coding> - <system value="http://unstats.un.org/unsd/methods/m49/m49.htm"/> - <code value="001"/> - </coding> - </jurisdiction> - <packageId value="fhir.cqf.typesandvalues"/> - <license value="Apache-2.0"/> - <fhirVersion value="4.0.1"/> - <dependsOn id="cqf"> - <uri value="http://fhir.org/guides/cqf/common/ImplementationGuide/fhir.cqf.common"/> - <packageId value="fhir.cqf.common"/> - <version value="4.0.1"/> - </dependsOn> - <definition> - <!-- You don't need to define any groupings. The IGPublisher will define them for you. You only need to do so if your IG is 'special' and it's - inappropriate to use the defaults. Feel free to provide feedback about the defaults... --> - <!-- - <resource> - <reference> - <reference value="Library/dependency-example"/> - </reference> - <name value="Dependency library example"/> - <description value="A simple example showing use of a dependency library from an implementation guide dependency"/> - </resource> - --> - <page> - <!-- The root will always be toc.html - the template will force it if you don't do it --> - <nameUrl value="toc.html"/> - <title value="Table of Contents"/> - <generation value="html"/> - <page> - <nameUrl value="index.html"/> - <title value="Types and Values Home Page"/> - <generation value="html"/> - </page> - <page> - <nameUrl value="background.html"/> - <title value="Background"/> - <generation value="html"/> - </page> - <page> - <nameUrl value="spec.html"/> - <title value="Detailed Specification"/> - <generation value="markdown"/> - </page> - <page> - <nameUrl value="downloads.html"/> - <title value="Useful Downloads"/> - <generation value="html"/> - </page> - <page> - <nameUrl value="license.html"/> - <title value="License"/> - <generation value="markdown"/> - </page> - <page> - <nameUrl value="changes.html"/> - <title value="IG Change History"/> - <generation value="html"/> - </page> - </page> - <!-- copyright year is a mandatory parameter --> - <parameter> - <code value="copyrightyear"/> - <value value="2019+"/> - </parameter> - <!-- releaselabel should be the ballot status for HL7-published IGs. --> - <parameter> - <code value="releaselabel"/> - <value value="CI Build"/> - </parameter> - <parameter> - <code value="find-other-resources"/> - <value value="true"/> - </parameter> - <!-- This parameter indicates to the publisher that it should translate and package CQL libraries. See the example library resources for more --> - <!-- This is disabled for now because the CQF Tooling has more up-to-date CQL processing than the publisher. Run refresh to update IG resources - <parameter> - <code value="path-binary"/> - <value value="input/cql"/> - </parameter> - --> - <parameter> - <code value="path-liquid"/> - <value value="templates/liquid"/> - </parameter> - <parameter> - <code value="path-suppressed-warnings"/> - <value value="input/ignoreWarnings.txt"/> - </parameter> -<!-- Uncomment one or more of these if you want to limit which syntaxes are supported or want to disable the display of mappings - <parameter> - <code value="excludexml"/> - <value value="true"/> - </parameter> - <parameter> - <code value="excludejson"/> - <value value="true"/> - </parameter> - <parameter> - <code value="excludettl"/> - <value value="true"/> - </parameter> - <parameter> - <code value="excludemap"/> - <value value="true"/> - </parameter>--> - </definition> -</ImplementationGuide> diff --git a/tests/types-and-values/input/vocabulary/valueset/README.md b/tests/types-and-values/input/vocabulary/valueset/README.md deleted file mode 100644 index 17710d7..0000000 --- a/tests/types-and-values/input/vocabulary/valueset/README.md +++ /dev/null @@ -1 +0,0 @@ -This is the repository of vocabulary used by the evaluator. ValueSet resources can be in this folder, or any sub-folder, recursively. Resources can be in individual files in JSON or XML format, as well as in bundle files in either format.