Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

MAT-7529 QI-Core Code Search/Apply - Apply Code to QI-Core Measures #717

Merged
merged 5 commits into from
Jan 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading