diff --git a/package-lock.json b/package-lock.json index b4b3c5cd..3ebf96c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8026,9 +8026,10 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", diff --git a/src/api/CalculationService.test.ts b/src/api/CalculationService.test.ts index ce3f7cdb..230577da 100644 --- a/src/api/CalculationService.test.ts +++ b/src/api/CalculationService.test.ts @@ -345,7 +345,7 @@ describe("CalculationService Tests", () => { populationValues: undefined, stratificationValues: undefined, }; - const output = calculationService.isGroupPass(groupPop); + const output = calculationService.isGroupPass(groupPop, {} as Group); expect(output).toEqual(true); }); @@ -357,7 +357,7 @@ describe("CalculationService Tests", () => { populationValues: [], stratificationValues: undefined, }; - const output = calculationService.isGroupPass(groupPop); + const output = calculationService.isGroupPass(groupPop, {} as Group); expect(output).toEqual(true); }); @@ -369,7 +369,7 @@ describe("CalculationService Tests", () => { populationValues: [], stratificationValues: [], }; - const output = calculationService.isGroupPass(groupPop); + const output = calculationService.isGroupPass(groupPop, {} as Group); expect(output).toEqual(true); }); @@ -389,7 +389,7 @@ describe("CalculationService Tests", () => { ], stratificationValues: [], }; - const output = calculationService.isGroupPass(groupPop); + const output = calculationService.isGroupPass(groupPop, {} as Group); expect(output).toEqual(true); }); @@ -423,7 +423,7 @@ describe("CalculationService Tests", () => { ], stratificationValues: [], }; - const output = calculationService.isGroupPass(groupPop); + const output = calculationService.isGroupPass(groupPop, {} as Group); expect(output).toEqual(true); }); @@ -457,7 +457,7 @@ describe("CalculationService Tests", () => { ], stratificationValues: [], }; - const output = calculationService.isGroupPass(groupPop); + const output = calculationService.isGroupPass(groupPop, {} as Group); expect(output).toEqual(false); }); @@ -492,7 +492,7 @@ describe("CalculationService Tests", () => { }, ], }; - const output = calculationService.isGroupPass(groupPop); + const output = calculationService.isGroupPass(groupPop, {} as Group); expect(output).toEqual(true); }); @@ -527,8 +527,155 @@ describe("CalculationService Tests", () => { }, ], }; - const output = calculationService.isGroupPass(groupPop); - expect(output).toEqual(false); + const output = calculationService.isGroupPass(groupPop, {} as Group); + expect(output).toEqual(true); + }); + + it("Should pass group with stratifications and filter out invalid population values", () => { + const group: Group = { + id: "6734f7e4af7d11385b114b91", + scoring: "Continuous Variable", + populations: [ + { + id: "69333bb0-0e65-41ac-9648-8badc3e70c6e", + name: PopulationType.INITIAL_POPULATION, + definition: "Initial Population", + description: "", + }, + { + id: "60ab5bfd-bd1a-4e53-8cd8-1cb4aae465dd", + name: PopulationType.MEASURE_POPULATION, + definition: "Qualifying Encounters", + description: "", + }, + { + id: "9a4b1c39-5a94-49b2-ae33-75ec80a5d7d6", + name: PopulationType.MEASURE_POPULATION_OBSERVATION, + definition: "", + description: "", + }, + ], + measureObservations: [ + { + id: "3a429f92-8eb6-456a-bfd4-658458d54885", + definition: "MeasureObservation", + + criteriaReference: "60ab5bfd-bd1a-4e53-8cd8-1cb4aae465dd", + aggregateMethod: "Maximum", + }, + ], + groupDescription: "", + improvementNotation: "", + rateAggregation: "", + measureGroupTypes: [MeasureGroupTypes.PROCESS], + scoringUnit: "", + stratifications: [ + { + id: "a9197a7f-78ca-484c-a84d-05531c1dd9e4", + description: "StratificationOne", + cqlDefinition: "Stratification 1", + associations: [PopulationType.INITIAL_POPULATION], + }, + { + id: "c88151f1-83dd-477e-afe7-631e17cec6ff", + description: "StratificationTwo", + cqlDefinition: "Stratification 2", + associations: [PopulationType.MEASURE_POPULATION], + }, + ], + populationBasis: "Encounter", + }; + const groupPop = { + groupId: "6734f7e4af7d11385b114b91", + scoring: "Continuous Variable", + populationBasis: "Encounter", + populationValues: [ + { + id: "69333bb0-0e65-41ac-9648-8badc3e70c6e", + criteriaReference: null, + name: PopulationType.INITIAL_POPULATION, + expected: "1", + actual: 1, + }, + { + id: "60ab5bfd-bd1a-4e53-8cd8-1cb4aae465dd", + criteriaReference: null, + name: PopulationType.MEASURE_POPULATION, + expected: "1", + actual: 1, + }, + { + id: "measurePopulationObservation0", + criteriaReference: "60ab5bfd-bd1a-4e53-8cd8-1cb4aae465dd", + name: PopulationType.MEASURE_POPULATION_OBSERVATION, + expected: "02", + actual: 2, + }, + ], + stratificationValues: [ + { + id: "a9197a7f-78ca-484c-a84d-05531c1dd9e4", + name: "Strata-1", + expected: null, + actual: null, + populationValues: [ + { + id: "69333bb0-0e65-41ac-9648-8badc3e70c6e", + criteriaReference: null, + name: PopulationType.INITIAL_POPULATION, + expected: "1", + actual: 1, + }, + { + id: "60ab5bfd-bd1a-4e53-8cd8-1cb4aae465dd", + criteriaReference: null, + name: PopulationType.MEASURE_POPULATION, + expected: "1", + actual: 1, + }, + { + id: "measurePopulationObservation0", + criteriaReference: "60ab5bfd-bd1a-4e53-8cd8-1cb4aae465dd", + name: PopulationType.MEASURE_POPULATION_OBSERVATION, + expected: "02", + actual: 0, + }, + ], + }, + { + id: "c88151f1-83dd-477e-afe7-631e17cec6ff", + name: "Strata-2", + expected: null, + actual: null, + populationValues: [ + { + id: "69333bb0-0e65-41ac-9648-8badc3e70c6e", + criteriaReference: null, + name: PopulationType.INITIAL_POPULATION, + expected: "0", + actual: 0, + }, + { + id: "60ab5bfd-bd1a-4e53-8cd8-1cb4aae465dd", + criteriaReference: null, + name: PopulationType.MEASURE_POPULATION, + expected: "0", + actual: 0, + }, + { + id: "measurePopulationObservation0", + criteriaReference: "60ab5bfd-bd1a-4e53-8cd8-1cb4aae465dd", + name: PopulationType.MEASURE_POPULATION_OBSERVATION, + expected: "02", + actual: 0, + }, + ], + }, + ], + }; + + const output = calculationService.isGroupPass(groupPop, group); + expect(output).toEqual(true); }); it("should fail group with incorrect measure observations for Ratio", () => { @@ -575,7 +722,7 @@ describe("CalculationService Tests", () => { ], stratificationValues: [], }; - const output = calculationService.isGroupPass(groupPop); + const output = calculationService.isGroupPass(groupPop, {} as Group); expect(output).toEqual(false); }); }); diff --git a/src/api/CalculationService.ts b/src/api/CalculationService.ts index bcdd20c4..10c217da 100644 --- a/src/api/CalculationService.ts +++ b/src/api/CalculationService.ts @@ -12,6 +12,7 @@ import { Population, PopulationExpectedValue, TestCase, + StratificationExpectedValue, } from "@madie/madie-models"; import { Bundle, ValueSet } from "fhir/r4"; import * as _ from "lodash"; @@ -290,7 +291,7 @@ export class CalculationService { return results; } - isGroupPass(groupPopulation: GroupPopulation) { + isGroupPass(groupPopulation: GroupPopulation, measureGroup: Group) { let groupPass = true; if (groupPopulation) { const patientBased = @@ -312,9 +313,24 @@ export class CalculationService { } // verify stratification & stratified populations passing if they exist if (groupPopulation.stratificationValues) { - return groupPopulation.stratificationValues.every((strata) => { + let validStratPopValues = []; + groupPopulation.stratificationValues.forEach( + (stratValues: StratificationExpectedValue) => { + stratValues.populationValues?.forEach((popValue) => { + const validPopValues = this.getValidStratPopulationValues( + measureGroup, + stratValues.id, + popValue + ); + if (validPopValues && validPopValues.length > 0) { + validStratPopValues.push(validPopValues); + } + }); + } + ); + return validStratPopValues.every((strata) => { // verify stratified populations passing - return strata.populationValues.every((population) => + return strata.every((population) => this.isValuePass( population.actual, population.expected, @@ -327,6 +343,24 @@ export class CalculationService { return groupPass; } + getValidStratPopulationValues( + measureGroup: Group, + stratId: string, + popValue: PopulationExpectedValue + ): PopulationExpectedValue[] { + let valiePopValue = []; + measureGroup?.stratifications?.forEach((strat) => { + if (strat.id === stratId) { + strat.associations?.forEach((association) => { + if (association === popValue.name) { + valiePopValue.push(popValue); + } + }); + } + }); + return valiePopValue; + } + processTestCaseResults( testCase: TestCase, measureGroups: Group[], @@ -463,7 +497,8 @@ export class CalculationService { }); }); // need to do work here. - allGroupsPass = allGroupsPass && this.isGroupPass(tcGroupPopulation); + allGroupsPass = + allGroupsPass && this.isGroupPass(tcGroupPopulation, measureGroup); } updatedTestCase.executionStatus = allGroupsPass ? ExecutionStatusType.PASS