diff --git a/package-lock.json b/package-lock.json index cc58ddd5f..a4ac28a39 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,7 +19,7 @@ "@madie/madie-auth": "^0.0.1", "@madie/madie-design-system": "^1.2.41", "@madie/madie-editor": "^0.0.1", - "@madie/madie-models": "^1.4.23", + "@madie/madie-models": "^1.4.24", "@madie/madie-root": "^0.0.3", "@material-ui/core": "^4.12.4", "@mui/core": "^5.0.0-alpha.54", @@ -4654,9 +4654,10 @@ } }, "node_modules/@madie/madie-models": { - "version": "1.4.23", - "resolved": "https://registry.npmjs.org/@madie/madie-models/-/madie-models-1.4.23.tgz", - "integrity": "sha512-zGgYbtmRCNtjvpaNi+wyt1YAh+slL8zkcXpzCptY9H8q8o2PdvtYzgiUZGGhBL3OgtcLVDOPr3tK/0m7/Ud1ag==" + "version": "1.4.24", + "resolved": "https://registry.npmjs.org/@madie/madie-models/-/madie-models-1.4.24.tgz", + "integrity": "sha512-8VnBrF9lwaqOVssvOtrAZx+xoZWnPe+eDp4VLsmmwh24BbAEPZxvEXoR8zefORADlkWn4xMq6FKzD1c7oIUmIg==", + "license": "ISC" }, "node_modules/@madie/madie-root": { "version": "0.0.3", diff --git a/package.json b/package.json index 67f9bfd56..f8918383b 100644 --- a/package.json +++ b/package.json @@ -93,7 +93,7 @@ "@madie/madie-auth": "^0.0.1", "@madie/madie-design-system": "^1.2.41", "@madie/madie-editor": "^0.0.1", - "@madie/madie-models": "^1.4.23", + "@madie/madie-models": "^1.4.24", "@madie/madie-root": "^0.0.3", "@material-ui/core": "^4.12.4", "@mui/core": "^5.0.0-alpha.54", diff --git a/src/components/__mocks__/bulkCreate.tsx b/src/components/__mocks__/bulkCreate.tsx index 4e1044b5e..600dcc726 100644 --- a/src/components/__mocks__/bulkCreate.tsx +++ b/src/components/__mocks__/bulkCreate.tsx @@ -26,7 +26,7 @@ const bulkCreate = (count: number) => { const measureInput = { measureName: "", - model: "", + model: Model.QDM_5_6, cqlLibraryName: "", } as Measure; diff --git a/src/components/editMeasure/editor/MeasureEditor.tsx b/src/components/editMeasure/editor/MeasureEditor.tsx index 7ca6d32a3..5495ece91 100644 --- a/src/components/editMeasure/editor/MeasureEditor.tsx +++ b/src/components/editMeasure/editor/MeasureEditor.tsx @@ -511,7 +511,7 @@ const MeasureEditor = () => { }; const handleApplyCode = (code: Code) => { - const result = applyCode(editorVal, code); + const result = applyCode(editorVal, code, measure.model); if (result.status) { // if result status is true, we modified the CQL // let's store off the codeSystem/code/version diff --git a/src/components/editMeasure/editor/codeApplier.test.ts b/src/components/editMeasure/editor/codeApplier.test.ts index f48c28563..7eebb5abf 100644 --- a/src/components/editMeasure/editor/codeApplier.test.ts +++ b/src/components/editMeasure/editor/codeApplier.test.ts @@ -1,6 +1,6 @@ import applyCode from "./codeApplier"; import * as fs from "fs"; -import { Code } from "@madie/madie-models"; +import { Code, Model } from "@madie/madie-models"; import { CqlApplyActionResult } from "./CqlApplyActionResult"; const mockCode = { @@ -86,7 +86,7 @@ describe("applyCode test cases", () => { expect(cql).not.toContain("281302008"); const code = JSON.parse(codeJson); - let result: CqlApplyActionResult = applyCode(cql, code); + let result: CqlApplyActionResult = applyCode(cql, code, Model.QDM_5_6); expect(result.cql).toContain( `codesystem "SNOMEDCT": 'urn:oid:2.16.840.1.113883.6.96'` @@ -109,7 +109,7 @@ describe("applyCode test cases", () => { `codesystem "SNOMEDCT": 'urn:oid:2.16.840.1.113883.6.96'` ); expect(cql).not.toContain("281302008"); - let result = applyCode(cql, mockCode); + let result = applyCode(cql, mockCode, Model.QDM_5_6); expect(result.cql).toContain( `codesystem "${mockCode.codeSystem}:${mockCode.svsVersion}": 'urn:oid:${mockCode.codeSystemOid}' version 'urn:hl7:version:${mockCode.svsVersion}'` ); @@ -123,7 +123,7 @@ describe("applyCode test cases", () => { }); it("Should insert code if editor is blank", () => { - let result = applyCode("", mockCode); + let result = applyCode("", mockCode, Model.QDM_5_6); expect(result.message).toContain( `Code ${mockCode.name} has been successfully added to the CQL.` ); @@ -139,7 +139,7 @@ describe("applyCode test cases", () => { it("Should insert code if CQL has only library statement", () => { const libraryStatement = "library ABC version '0.0.000'"; - let result = applyCode(libraryStatement, mockCode); + let result = applyCode(libraryStatement, mockCode, Model.QDM_5_6); const cqlArray = result.cql.split("\n"); expect(cqlArray[0]).toEqual(libraryStatement); expect(cqlArray[1]).toEqual( @@ -154,7 +154,7 @@ describe("applyCode test cases", () => { const usingStatement = "using QDM version '5.6'"; const libraryStatement = "library ABC version '0.0.000'"; const cql = `${libraryStatement}\n${usingStatement}`; - let result = applyCode(cql, mockCode); + let result = applyCode(cql, mockCode, Model.QDM_5_6); const cqlArray = result.cql.split("\n"); expect(cqlArray[0]).toEqual(libraryStatement); expect(cqlArray[1]).toEqual(usingStatement); @@ -172,7 +172,7 @@ describe("applyCode test cases", () => { const codeSystemStatement = "codesystem \"CPT\": 'urn:oid:2.16.840.1.113883.6.12'"; const cql = `${libraryStatement}\n${usingStatement}\n${codeSystemStatement}`; - let result = applyCode(cql, mockCode); + let result = applyCode(cql, mockCode, Model.QDM_5_6); const cqlArray = result.cql.split("\n"); expect(cqlArray[0]).toEqual(libraryStatement); expect(cqlArray[1]).toEqual(usingStatement); @@ -184,4 +184,65 @@ describe("applyCode test cases", () => { `code "${mockCode.display} (${mockCode.suffix})": '${mockCode.name}' from "${mockCode.codeSystem}:${mockCode.svsVersion}" display '${mockCode.display}'` ); }); + + describe("Apply code to QICore CQL", () => { + const code = { + name: "24353-5", + display: + "Glucose tolerance 2 hours gestational panel - Urine and Serum or Plasma", + version: "2.76", + codeSystem: "LOINC", + codeSystemOid: "2.16.840.1.113883.6.1", + status: "NA", + svsVersion: "2.78", + fhirVersion: "2.78", + codeSystemUrl: "http://loinc.org", + versionIncluded: false, + }; + it("Should apply new code to QICore measure CQL", () => { + const cql = + "library MAT6197UCUMAnnotations version '0.0.000'\n" + + "using QICore version '4.1.1'"; + + let result: CqlApplyActionResult = applyCode( + cql, + code, + Model.QICORE_6_0_0 + ); + expect(result.cql).toContain("24353-5"); + expect(result.status).toEqual("success"); + expect(result.message).toEqual( + "Code 24353-5 has been successfully added to the CQL." + ); + + // include code system version + result = applyCode( + cql, + { ...code, versionIncluded: true }, + Model.QICORE_6_0_0 + ); + expect(result.cql).toContain("LOINC:2.78"); + expect(result.status).toEqual("success"); + expect(result.message).toEqual( + "Code 24353-5 has been successfully added to the CQL." + ); + }); + + it("Should update code for QICore measure CQL", () => { + const cql = + "library MAT6197UCUMAnnotations version '0.0.000'\n" + + "using QICore version '4.1.1'\n" + + "codesystem \"LOINC\": 'http://loinc.org'\n" + + "code \"Glucose tolerance 2 hours gestational panel - Urine and Serum or Plasma\": '24353-5' from \"LOINC:2.78\" display 'Glucose tolerance 2 hours gestational panel - Urine and Serum or Plasma'"; + + let result: CqlApplyActionResult = applyCode( + cql, + code, + Model.QICORE_6_0_0 + ); + expect(result.message).toEqual( + "Code 24353-5 has been updated successfully." + ); + }); + }); }); diff --git a/src/components/editMeasure/editor/codeApplier.ts b/src/components/editMeasure/editor/codeApplier.ts index 647536ed3..fe2b760f6 100644 --- a/src/components/editMeasure/editor/codeApplier.ts +++ b/src/components/editMeasure/editor/codeApplier.ts @@ -1,8 +1,8 @@ import { CqlAntlr, CqlResult } from "@madie/cql-antlr-parser/dist/src"; -import { Code } from "@madie/madie-models"; +import { Code, Model } from "@madie/madie-models"; import { CqlApplyActionResult } from "./CqlApplyActionResult"; -const findCodeSystem = (code, codeSystems) => { +const findCodeSystem = (code, codeSystems, measureModel) => { if (!code || !codeSystems) { return undefined; } @@ -14,17 +14,21 @@ const findCodeSystem = (code, codeSystems) => { const oldCodeSystemVersion = codeSystem.version ?.replace(/["']/g, "") ?.replace(/urn:hl7:version:/g, ""); - + let updatedOid = code.codeSystemUrl; + let updatedVersion = code.fhirVersion; + if (measureModel === Model.QDM_5_6) { + updatedOid = code.codeSystemOid; + updatedVersion = code.svsVersion; + } if (code.versionIncluded) { return ( oldCodeSystemName === `${code.codeSystem}:${code.svsVersion}` && - oldCodeSystemOid === code.codeSystemOid && - oldCodeSystemVersion === code.svsVersion + oldCodeSystemOid === updatedOid && + oldCodeSystemVersion === updatedVersion ); } else { return ( - oldCodeSystemName === code.codeSystem && - oldCodeSystemOid === code.codeSystemOid + oldCodeSystemName === code.codeSystem && oldCodeSystemOid === updatedOid ); } }); @@ -57,25 +61,44 @@ const createCodeDeclaration = (code: Code) => { return newCode; }; -const createCodeSystemDeclaration = (code: Code) => { +const createCodeSystemDeclaration = (code: Code, measureModel: Model) => { + let oid: string; + let codeSystemVersion: string; + let versionSuffix: string; + if (measureModel === Model.QDM_5_6) { + versionSuffix = code.svsVersion; + oid = `urn:oid:${code.codeSystemOid}`; + codeSystemVersion = `urn:hl7:version:${code.svsVersion}`; + } else { + versionSuffix = code.svsVersion; // TODO: confirm this + oid = code.codeSystemUrl; + codeSystemVersion = code.fhirVersion; + } if (code.versionIncluded) { - return `codesystem "${code.codeSystem}:${code.svsVersion}": 'urn:oid:${code.codeSystemOid}' version 'urn:hl7:version:${code.svsVersion}'`; + return `codesystem "${code.codeSystem}:${versionSuffix}": '${oid}' version '${codeSystemVersion}'`; } else { - return `codesystem "${code.codeSystem}": 'urn:oid:${code.codeSystemOid}'`; + return `codesystem "${code.codeSystem}": '${oid}'`; } }; -const applyCode = (cql: string, code: Code): CqlApplyActionResult => { +const applyCode = ( + cql: string, + code: Code, + measureModel?: Model +): CqlApplyActionResult => { const cqlArr: string[] = cql.split("\n"); // Parse CQL to get code and code systems const parseResults: CqlResult = new CqlAntlr(cql).parse(); - // Let's check if the code system is already in the CQL - const previousCodeSystem = findCodeSystem(code, parseResults.codeSystems); + const previousCodeSystem = findCodeSystem( + code, + parseResults.codeSystems, + measureModel + ); // Add code system to CQL if it does not exist if (!previousCodeSystem) { - let newCodeSystem = createCodeSystemDeclaration(code); + let newCodeSystem = createCodeSystemDeclaration(code, measureModel); cqlArr.splice(findCodeSystemInsertPoint(parseResults), 0, newCodeSystem); }