Skip to content

Commit

Permalink
Merge pull request #717 from MeasureAuthoringTool/MAT-7529
Browse files Browse the repository at this point in the history
MAT-7529 QI-Core Code Search/Apply - Apply Code to QI-Core Measures
  • Loading branch information
adongare authored Jan 2, 2025
2 parents 4fc00db + 13adcd2 commit 95b4ac9
Show file tree
Hide file tree
Showing 6 changed files with 113 additions and 28 deletions.
9 changes: 5 additions & 4 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
2 changes: 1 addition & 1 deletion src/components/__mocks__/bulkCreate.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const bulkCreate = (count: number) => {

const measureInput = {
measureName: "",
model: "",
model: Model.QDM_5_6,
cqlLibraryName: "",
} as Measure;

Expand Down
2 changes: 1 addition & 1 deletion src/components/editMeasure/editor/MeasureEditor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
75 changes: 68 additions & 7 deletions src/components/editMeasure/editor/codeApplier.test.ts
Original file line number Diff line number Diff line change
@@ -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 = {
Expand Down Expand Up @@ -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'`
Expand All @@ -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}'`
);
Expand All @@ -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.`
);
Expand All @@ -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(
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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."
);
});
});
});
51 changes: 37 additions & 14 deletions src/components/editMeasure/editor/codeApplier.ts
Original file line number Diff line number Diff line change
@@ -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;
}
Expand All @@ -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
);
}
});
Expand Down Expand Up @@ -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);
}

Expand Down

0 comments on commit 95b4ac9

Please sign in to comment.