From a64c226172d6a21914da8723ac792abbef5ec55d Mon Sep 17 00:00:00 2001 From: Elsa Date: Mon, 5 Aug 2024 11:05:56 -0400 Subject: [PATCH 1/9] appliesTo functionality in the detailed results --- src/calculation/DetailedResultsBuilder.ts | 17 +++- test/unit/DetailedResultsBuilder.test.ts | 94 +++++++++++++++++++++++ 2 files changed, 110 insertions(+), 1 deletion(-) diff --git a/src/calculation/DetailedResultsBuilder.ts b/src/calculation/DetailedResultsBuilder.ts index 5b70067a..87b92ebd 100644 --- a/src/calculation/DetailedResultsBuilder.ts +++ b/src/calculation/DetailedResultsBuilder.ts @@ -304,7 +304,22 @@ export function createPatientPopulationValues( populationGroup.stratifier.forEach(strata => { if (strata.criteria?.expression) { const value = patientResults[strata.criteria?.expression]; - const result = isStatementValueTruthy(value); + + // if the cqfm-appliesTo extension is present, then we want to consider the result of that + // population in our stratifier result + const appliesToExtension = strata.extension?.find( + e => e.url === 'http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-appliesTo' + ); + + let popValue = true; + if (appliesToExtension) { + const popCode = appliesToExtension.valueCodeableConcept?.coding?.[0].code; + if (popCode) { + popValue = patientResults[popCode]; + } + } + const result = isStatementValueTruthy(value && popValue); + stratifierResults?.push({ strataCode: strata.code?.text ?? strata.id ?? `strata-${strataIndex++}`, result, diff --git a/test/unit/DetailedResultsBuilder.test.ts b/test/unit/DetailedResultsBuilder.test.ts index 380b36ad..bb10115d 100644 --- a/test/unit/DetailedResultsBuilder.test.ts +++ b/test/unit/DetailedResultsBuilder.test.ts @@ -1111,6 +1111,100 @@ describe('DetailedResultsBuilder', () => { }); }); + describe('Patient Population Values', () => { + test('should take population result into consideration when appliesTo extension exists', () => { + const populationGroup: fhir4.MeasureGroup = { + stratifier: [ + { + id: 'example-strata-id', + extension: [ + { + url: 'http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-appliesTo', + valueCodeableConcept: { + coding: [ + { + system: 'http://terminology.hl7.org/CodeSystem/measure-population', + code: 'initial-population', + display: 'Initial Population' + } + ] + } + } + ], + criteria: { + expression: 'strat1', + language: 'text/cql' + } + } + ] + }; + + const patientResults: StatementResults = { + 'Initial Population': false, + Denominator: false, + 'Denominator Exclusion': false, + Numerator: false, + 'Numerator Exclusion': false, + strata1: true + }; + + const { stratifierResults } = DetailedResultsBuilder.createPatientPopulationValues( + populationGroup, + patientResults + ); + + expect(stratifierResults).toBeDefined(); + expect(stratifierResults).toHaveLength(1); + expect(stratifierResults).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + strataId: 'example-strata-id', + result: false + }) + ]) + ); + }); + + test('does not take any population result into consideration when appliesTo extension does not exist', () => { + const populationGroupNoExtension: fhir4.MeasureGroup = { + stratifier: [ + { + id: 'example-strata-id-2', + criteria: { + expression: 'strat2', + language: 'text/cql' + } + } + ] + }; + + const patientResults: StatementResults = { + 'Initial Population': false, + Denominator: false, + 'Denominator Exclusion': false, + Numerator: false, + 'Numerator Exclusion': false, + strat2: true + }; + + const { stratifierResults } = DetailedResultsBuilder.createPatientPopulationValues( + populationGroupNoExtension, + patientResults + ); + + expect(stratifierResults).toBeDefined(); + expect(stratifierResults).toHaveLength(1); + expect(stratifierResults).toEqual( + expect.arrayContaining([ + expect.objectContaining({ + strataId: 'example-strata-id-2', + result: true + }) + ]) + ); + }); + }); + describe('ELM JSON Function', () => { test('should properly generate episode-based ELM JSON given name and parameter', () => { const exampleFunctionName = 'exampleFunction'; From cd80e3581caed851af7bcf34f5d579cee14421b3 Mon Sep 17 00:00:00 2001 From: Elsa Date: Mon, 5 Aug 2024 14:24:54 -0400 Subject: [PATCH 2/9] Add additional attribute appliesResult to StratifierResults --- src/calculation/DetailedResultsBuilder.ts | 4 +++- src/types/Calculator.ts | 6 ++++++ test/unit/DetailedResultsBuilder.test.ts | 8 +++++--- 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/src/calculation/DetailedResultsBuilder.ts b/src/calculation/DetailedResultsBuilder.ts index 87b92ebd..37db82ed 100644 --- a/src/calculation/DetailedResultsBuilder.ts +++ b/src/calculation/DetailedResultsBuilder.ts @@ -318,11 +318,13 @@ export function createPatientPopulationValues( popValue = patientResults[popCode]; } } - const result = isStatementValueTruthy(value && popValue); + const result = isStatementValueTruthy(value); + const appliesResult = isStatementValueTruthy(value && popValue); stratifierResults?.push({ strataCode: strata.code?.text ?? strata.id ?? `strata-${strataIndex++}`, result, + appliesResult, ...(strata.id ? { strataId: strata.id } : {}) }); } diff --git a/src/types/Calculator.ts b/src/types/Calculator.ts index 8b007950..ee015efd 100644 --- a/src/types/Calculator.ts +++ b/src/types/Calculator.ts @@ -231,6 +231,12 @@ export interface StratifierResult { * True if patient or episode is in stratifier. False if not. */ result: boolean; + /** + * True if patient or episode is in stratifier AND the population + * result it appliesTo is true. False if not. Only implemented for + * patient based measures currently. + */ + appliesResult?: boolean; strataId?: string; } diff --git a/test/unit/DetailedResultsBuilder.test.ts b/test/unit/DetailedResultsBuilder.test.ts index bb10115d..236c65d4 100644 --- a/test/unit/DetailedResultsBuilder.test.ts +++ b/test/unit/DetailedResultsBuilder.test.ts @@ -1145,7 +1145,7 @@ describe('DetailedResultsBuilder', () => { 'Denominator Exclusion': false, Numerator: false, 'Numerator Exclusion': false, - strata1: true + strat1: true }; const { stratifierResults } = DetailedResultsBuilder.createPatientPopulationValues( @@ -1159,7 +1159,8 @@ describe('DetailedResultsBuilder', () => { expect.arrayContaining([ expect.objectContaining({ strataId: 'example-strata-id', - result: false + result: true, + appliesResult: false }) ]) ); @@ -1198,7 +1199,8 @@ describe('DetailedResultsBuilder', () => { expect.arrayContaining([ expect.objectContaining({ strataId: 'example-strata-id-2', - result: true + result: true, + appliesResult: true }) ]) ); From 0f941760f0d7953805e8f36502092babce472076 Mon Sep 17 00:00:00 2001 From: Elsa Date: Wed, 7 Aug 2024 11:29:42 -0400 Subject: [PATCH 3/9] Pulled out handling stratifier results --- src/calculation/DetailedResultsBuilder.ts | 58 ++++++++++----- test/unit/DetailedResultsBuilder.test.ts | 88 ++++++++++++++--------- 2 files changed, 95 insertions(+), 51 deletions(-) diff --git a/src/calculation/DetailedResultsBuilder.ts b/src/calculation/DetailedResultsBuilder.ts index 37db82ed..b23e22ca 100644 --- a/src/calculation/DetailedResultsBuilder.ts +++ b/src/calculation/DetailedResultsBuilder.ts @@ -33,6 +33,9 @@ export function createPopulationValues( populationResults = popAndStratResults.populationResults; stratifierResults = popAndStratResults.stratifierResults; populationResults = handlePopulationValues(populationResults, populationGroup, measureScoringCode); + if (stratifierResults) { + stratifierResults = handleStratificationValues(populationGroup, populationResults, stratifierResults); + } } else { // episode of care based measure // collect results per episode @@ -45,6 +48,9 @@ export function createPopulationValues( const popAndStratResults = createPatientPopulationValues(populationGroup, patientResults); populationResults = popAndStratResults.populationResults; stratifierResults = popAndStratResults.stratifierResults; + if (stratifierResults) { + stratifierResults = handleStratificationValues(populationGroup, populationResults, stratifierResults); + } } else { populationResults = []; stratifierResults = []; @@ -97,6 +103,9 @@ export function createPopulationValues( } }); }); + if (stratifierResults) { + stratifierResults = handleStratificationValues(populationGroup, populationResults, stratifierResults); + } } } const detailedResult: DetailedPopulationGroupResult = { @@ -304,27 +313,11 @@ export function createPatientPopulationValues( populationGroup.stratifier.forEach(strata => { if (strata.criteria?.expression) { const value = patientResults[strata.criteria?.expression]; - - // if the cqfm-appliesTo extension is present, then we want to consider the result of that - // population in our stratifier result - const appliesToExtension = strata.extension?.find( - e => e.url === 'http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-appliesTo' - ); - - let popValue = true; - if (appliesToExtension) { - const popCode = appliesToExtension.valueCodeableConcept?.coding?.[0].code; - if (popCode) { - popValue = patientResults[popCode]; - } - } const result = isStatementValueTruthy(value); - const appliesResult = isStatementValueTruthy(value && popValue); stratifierResults?.push({ strataCode: strata.code?.text ?? strata.id ?? `strata-${strataIndex++}`, result, - appliesResult, ...(strata.id ? { strataId: strata.id } : {}) }); } @@ -337,6 +330,39 @@ export function createPatientPopulationValues( }; } +export function handleStratificationValues( + populationGroup: fhir4.MeasureGroup, + populationResults: PopulationResult[], + stratifierResults: StratifierResult[] +): StratifierResult[] { + if (populationGroup.stratifier) { + populationGroup.stratifier.forEach(strata => { + if (strata.criteria?.expression) { + const strataResult = stratifierResults.find(strataRes => strataRes.strataId === strata.id); + + if (strataResult) { + // if the cqfm-appliesTo extension is present, then we want to consider the result of that + // population in our stratifier result + const appliesToExtension = strata.extension?.find( + e => e.url === 'http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-appliesTo' + ); + + let popValue = true; + if (appliesToExtension) { + const popCode = appliesToExtension.valueCodeableConcept?.coding?.[0].code; + if (popCode) { + popValue = populationResults.find(pr => pr.populationType === popCode)?.result ?? true; + } + } + console.log(popValue); + strataResult.appliesResult = isStatementValueTruthy(popValue && strataResult.result); + } + } + }); + } + return stratifierResults; +} + function isStatementValueTruthy(value: any): boolean { if (Array.isArray(value) && value.length > 0) { return true; diff --git a/test/unit/DetailedResultsBuilder.test.ts b/test/unit/DetailedResultsBuilder.test.ts index 236c65d4..383e175d 100644 --- a/test/unit/DetailedResultsBuilder.test.ts +++ b/test/unit/DetailedResultsBuilder.test.ts @@ -3,7 +3,7 @@ import * as DetailedResultsBuilder from '../../src/calculation/DetailedResultsBu import { getJSONFixture } from './helpers/testHelpers'; import { MeasureScoreType, PopulationType } from '../../src/types/Enums'; import { StatementResults } from '../../src/types/CQLTypes'; -import { PopulationResult, EpisodeResults } from '../../src/types/Calculator'; +import { PopulationResult, EpisodeResults, StratifierResult } from '../../src/types/Calculator'; import { ELMExpressionRef, ELMQuery, ELMTuple, ELMFunctionRef } from '../../src/types/ELMTypes'; type MeasureWithGroup = fhir4.Measure & { @@ -1111,8 +1111,8 @@ describe('DetailedResultsBuilder', () => { }); }); - describe('Patient Population Values', () => { - test('should take population result into consideration when appliesTo extension exists', () => { + describe('handleStratificationValues', () => { + test('it should take population result into consider when appliesTo extension exists', () => { const populationGroup: fhir4.MeasureGroup = { stratifier: [ { @@ -1139,23 +1139,32 @@ describe('DetailedResultsBuilder', () => { ] }; - const patientResults: StatementResults = { - 'Initial Population': false, - Denominator: false, - 'Denominator Exclusion': false, - Numerator: false, - 'Numerator Exclusion': false, - strat1: true - }; + const populationResults: PopulationResult[] = [ + { + populationType: PopulationType.IPP, + criteriaExpression: 'Initial Population', + result: false, + populationId: 'exampleId' + } + ]; + + const stratifierResults: StratifierResult[] = [ + { + strataCode: 'example-strata-id', + result: true, + strataId: 'example-strata-id' + } + ]; - const { stratifierResults } = DetailedResultsBuilder.createPatientPopulationValues( + const newStratifierResults = DetailedResultsBuilder.handleStratificationValues( populationGroup, - patientResults + populationResults, + stratifierResults ); - expect(stratifierResults).toBeDefined(); - expect(stratifierResults).toHaveLength(1); - expect(stratifierResults).toEqual( + expect(newStratifierResults).toBeDefined(); + expect(newStratifierResults).toHaveLength(1); + expect(newStratifierResults).toEqual( expect.arrayContaining([ expect.objectContaining({ strataId: 'example-strata-id', @@ -1166,39 +1175,48 @@ describe('DetailedResultsBuilder', () => { ); }); - test('does not take any population result into consideration when appliesTo extension does not exist', () => { - const populationGroupNoExtension: fhir4.MeasureGroup = { + test('it should not take population result into consideration when appliesTo extension does not exist', () => { + const populationGroup: fhir4.MeasureGroup = { stratifier: [ { - id: 'example-strata-id-2', + id: 'example-strata-id', criteria: { - expression: 'strat2', + expression: 'strat1', language: 'text/cql' } } ] }; - const patientResults: StatementResults = { - 'Initial Population': false, - Denominator: false, - 'Denominator Exclusion': false, - Numerator: false, - 'Numerator Exclusion': false, - strat2: true - }; + const populationResults: PopulationResult[] = [ + { + populationType: PopulationType.IPP, + criteriaExpression: 'Initial Population', + result: false, + populationId: 'exampleId' + } + ]; - const { stratifierResults } = DetailedResultsBuilder.createPatientPopulationValues( - populationGroupNoExtension, - patientResults + const stratifierResults: StratifierResult[] = [ + { + strataCode: 'example-strata-id', + result: true, + strataId: 'example-strata-id' + } + ]; + + const newStratifierResults = DetailedResultsBuilder.handleStratificationValues( + populationGroup, + populationResults, + stratifierResults ); - expect(stratifierResults).toBeDefined(); - expect(stratifierResults).toHaveLength(1); - expect(stratifierResults).toEqual( + expect(newStratifierResults).toBeDefined(); + expect(newStratifierResults).toHaveLength(1); + expect(newStratifierResults).toEqual( expect.arrayContaining([ expect.objectContaining({ - strataId: 'example-strata-id-2', + strataId: 'example-strata-id', result: true, appliesResult: true }) From 638cfa5e7a711f59c3409c7918c474002c544d33 Mon Sep 17 00:00:00 2001 From: Elsa Date: Thu, 8 Aug 2024 09:57:42 -0400 Subject: [PATCH 4/9] Remove console.logs, unnecessary function --- src/calculation/DetailedResultsBuilder.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/calculation/DetailedResultsBuilder.ts b/src/calculation/DetailedResultsBuilder.ts index b23e22ca..bbd2c20f 100644 --- a/src/calculation/DetailedResultsBuilder.ts +++ b/src/calculation/DetailedResultsBuilder.ts @@ -354,8 +354,7 @@ export function handleStratificationValues( popValue = populationResults.find(pr => pr.populationType === popCode)?.result ?? true; } } - console.log(popValue); - strataResult.appliesResult = isStatementValueTruthy(popValue && strataResult.result); + strataResult.appliesResult = popValue && strataResult.result; } } }); From 8bccbb3872313e372afc844aef9cecd3e604c279 Mon Sep 17 00:00:00 2001 From: Elsa Date: Thu, 8 Aug 2024 10:17:05 -0400 Subject: [PATCH 5/9] Go through stratifierResults instead of group stratifiers --- src/calculation/DetailedResultsBuilder.ts | 33 +++++++++++------------ 1 file changed, 16 insertions(+), 17 deletions(-) diff --git a/src/calculation/DetailedResultsBuilder.ts b/src/calculation/DetailedResultsBuilder.ts index bbd2c20f..31b350a0 100644 --- a/src/calculation/DetailedResultsBuilder.ts +++ b/src/calculation/DetailedResultsBuilder.ts @@ -336,26 +336,25 @@ export function handleStratificationValues( stratifierResults: StratifierResult[] ): StratifierResult[] { if (populationGroup.stratifier) { - populationGroup.stratifier.forEach(strata => { - if (strata.criteria?.expression) { - const strataResult = stratifierResults.find(strataRes => strataRes.strataId === strata.id); - - if (strataResult) { - // if the cqfm-appliesTo extension is present, then we want to consider the result of that - // population in our stratifier result - const appliesToExtension = strata.extension?.find( - e => e.url === 'http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-appliesTo' - ); + stratifierResults.forEach(stratRes => { + const strata = + populationGroup.stratifier?.find(s => s.id === stratRes.strataCode) || + populationGroup.stratifier?.find(s => s.code && s.code.text === stratRes.strataCode); + if (strata) { + // if the cqfm-appliesTo extension is present, then we want to consider the result of that + // population in our stratifier result + const appliesToExtension = strata.extension?.find( + e => e.url === 'http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-appliesTo' + ); - let popValue = true; - if (appliesToExtension) { - const popCode = appliesToExtension.valueCodeableConcept?.coding?.[0].code; - if (popCode) { - popValue = populationResults.find(pr => pr.populationType === popCode)?.result ?? true; - } + let popValue = true; + if (appliesToExtension) { + const popCode = appliesToExtension.valueCodeableConcept?.coding?.[0].code; + if (popCode) { + popValue = populationResults.find(pr => pr.populationType === popCode)?.result ?? true; } - strataResult.appliesResult = popValue && strataResult.result; } + stratRes.appliesResult = popValue && stratRes.result; } }); } From e5e62a6b4a91608cb7dfcde098b4c1d71ca4167f Mon Sep 17 00:00:00 2001 From: Elsa Date: Thu, 8 Aug 2024 10:21:21 -0400 Subject: [PATCH 6/9] handleStratifierValues after individual episode results --- src/calculation/DetailedResultsBuilder.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/calculation/DetailedResultsBuilder.ts b/src/calculation/DetailedResultsBuilder.ts index 31b350a0..f1886c11 100644 --- a/src/calculation/DetailedResultsBuilder.ts +++ b/src/calculation/DetailedResultsBuilder.ts @@ -482,6 +482,9 @@ export function createEpisodePopulationValues( populationGroup, measureScoringCode ); + if (episodeResults.stratifierResults) { + handleStratificationValues(populationGroup, episodeResults.populationResults, episodeResults.stratifierResults); + } }); // TODO: Remove any episode that don't fall in any populations or stratifications after the above code From c726a368fa8132f39a3bbb0370f418e2eb3654ca Mon Sep 17 00:00:00 2001 From: Elsa Date: Thu, 8 Aug 2024 14:01:28 -0400 Subject: [PATCH 7/9] Add all populations to stratum.population in measure report --- README.md | 4 ++++ src/calculation/DetailedResultsBuilder.ts | 5 ++++- src/calculation/MeasureReportBuilder.ts | 20 +++----------------- src/types/Calculator.ts | 2 +- test/unit/DetailedResultsBuilder.test.ts | 2 ++ test/unit/MeasureReportBuilder.test.ts | 14 +++++++++++--- 6 files changed, 25 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 4f9ef05f..304b5dd4 100644 --- a/README.md +++ b/README.md @@ -520,6 +520,10 @@ fqm-execution reports -m /path/to/measure/bundle.json -p /path/to/patient1/bundl # Guides and Concepts +## Stratification + +For Measures with + ## Measures with Observation Functions For [Continuous Variable Measures](https://build.fhir.org/ig/HL7/cqf-measures/measure-conformance.html#continuous-variable-measure) and [Ratio Measures](https://build.fhir.org/ig/HL7/cqf-measures/measure-conformance.html#ratio-measures), some of the measure populations can have an associated "measure observation", which is a CQL function that will return some result based on some information present on the data during calculation. In the case of these measures, `fqm-execution` will include an `observations` property on the `populationResult` object for each measure observation population. This `observations` property diff --git a/src/calculation/DetailedResultsBuilder.ts b/src/calculation/DetailedResultsBuilder.ts index f1886c11..fa2c94c6 100644 --- a/src/calculation/DetailedResultsBuilder.ts +++ b/src/calculation/DetailedResultsBuilder.ts @@ -98,6 +98,7 @@ export function createPopulationValues( stratifierResults?.push({ result: strat.result, strataCode: strat.strataCode, + appliesResult: strat.result, ...(strat.strataId ? { strataId: strat.strataId } : {}) }); } @@ -318,6 +319,7 @@ export function createPatientPopulationValues( stratifierResults?.push({ strataCode: strata.code?.text ?? strata.id ?? `strata-${strataIndex++}`, result, + appliesResult: result, ...(strata.id ? { strataId: strata.id } : {}) }); } @@ -565,7 +567,8 @@ function createOrSetValueOfEpisodes( newEpisodeResults.stratifierResults?.push({ ...(strataId ? { strataId } : {}), strataCode: newStrataCode, - result: newStrataCode === strataCode ? true : false + result: newStrataCode === strataCode ? true : false, + appliesResult: newStrataCode === strataCode ? true : false }); }); } diff --git a/src/calculation/MeasureReportBuilder.ts b/src/calculation/MeasureReportBuilder.ts index 514ff695..29338f77 100644 --- a/src/calculation/MeasureReportBuilder.ts +++ b/src/calculation/MeasureReportBuilder.ts @@ -140,23 +140,9 @@ export default class MeasureReportBuilder exten const strat = {}; // use existing populations, but reduce count as appropriate // Deep copy population with matching attributes but different interface - // if a stratifier has a cqfm-appliesTo extension, then we only want to - // include that population. If none is specified, the stratification applies - // to all populations in a group - const appliesToExtension = s.extension?.find( - e => e.url === 'http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-appliesTo' + strat.population = ( + JSON.parse(JSON.stringify(group.population)) ); - if (appliesToExtension) { - const popCode = appliesToExtension.valueCodeableConcept?.coding?.[0].code; - const matchingPop = group.population?.find(p => p.code?.coding?.[0].code === popCode); - strat.population = ( - JSON.parse(JSON.stringify([matchingPop])) - ); - } else { - strat.population = ( - JSON.parse(JSON.stringify(group.population)) - ); - } reportStratifier.stratum = [strat]; group.stratifier?.push(reportStratifier); @@ -278,7 +264,7 @@ export default class MeasureReportBuilder exten if (group.stratifier) { groupResults.stratifierResults?.forEach(stratResults => { // only add to results if this patient is in the strata - if (stratResults.result) { + if (stratResults.appliesResult) { // the strataCode has the potential to be a couple of things, either s.code[0].text (previous measures) // or s.id (newer measures) const strata: MeasureReportGroupStratifier | undefined = diff --git a/src/types/Calculator.ts b/src/types/Calculator.ts index ee015efd..dd48a91a 100644 --- a/src/types/Calculator.ts +++ b/src/types/Calculator.ts @@ -236,7 +236,7 @@ export interface StratifierResult { * result it appliesTo is true. False if not. Only implemented for * patient based measures currently. */ - appliesResult?: boolean; + appliesResult: boolean; strataId?: string; } diff --git a/test/unit/DetailedResultsBuilder.test.ts b/test/unit/DetailedResultsBuilder.test.ts index 383e175d..819a6dd5 100644 --- a/test/unit/DetailedResultsBuilder.test.ts +++ b/test/unit/DetailedResultsBuilder.test.ts @@ -1152,6 +1152,7 @@ describe('DetailedResultsBuilder', () => { { strataCode: 'example-strata-id', result: true, + appliesResult: true, strataId: 'example-strata-id' } ]; @@ -1201,6 +1202,7 @@ describe('DetailedResultsBuilder', () => { { strataCode: 'example-strata-id', result: true, + appliesResult: true, strataId: 'example-strata-id' } ]; diff --git a/test/unit/MeasureReportBuilder.test.ts b/test/unit/MeasureReportBuilder.test.ts index 043ed595..43fcf310 100644 --- a/test/unit/MeasureReportBuilder.test.ts +++ b/test/unit/MeasureReportBuilder.test.ts @@ -265,21 +265,25 @@ const propWithStratExecutionResults: ExecutionResult { result!.stratifierResults!.forEach(sr => { const stratifierResult = group.stratifier?.find(s => s.id === sr.strataId); expect(stratifierResult).toBeDefined(); - expect(stratifierResult!.stratum?.[0].population?.length).toEqual(1); + expect(stratifierResult!.stratum?.[0].population?.length).toEqual(4); expect(stratifierResult!.stratum?.[0].measureScore?.value).toEqual(0); }); }); @@ -441,11 +445,13 @@ describe('MeasureReportBuilder Static', () => { { strataCode: '93f5f1c7-8638-40a4-a596-8b5831599209', result: false, + appliesResult: false, strataId: '93f5f1c7-8638-40a4-a596-8b5831599209' }, { strataCode: '5baf37c7-8887-4576-837e-ea20a8938282', result: false, + appliesResult: false, strataId: '5baf37c7-8887-4576-837e-ea20a8938282' } ] @@ -462,11 +468,13 @@ describe('MeasureReportBuilder Static', () => { { strataCode: '125b3d95-2d00-455f-8a6e-d53614a2a50e', result: false, + appliesResult: false, strataId: '125b3d95-2d00-455f-8a6e-d53614a2a50e' }, { strataCode: 'c06647b9-e134-4189-858d-80cee23c0f8d', result: false, + appliesResult: false, strataId: 'c06647b9-e134-4189-858d-80cee23c0f8d' } ] @@ -475,10 +483,10 @@ describe('MeasureReportBuilder Static', () => { }); }); - test('should generate a summary MeasureReport whose stratifierResults only contain one population in the stratum', () => { + test('should generate a summary MeasureReport whose stratifierResults only contain all populations', () => { const { report } = builder; expect(report).toBeDefined(); - expect(report.group?.[0].stratifier?.[0].stratum?.[0].population?.length).toEqual(1); + expect(report.group?.[0].stratifier?.[0].stratum?.[0].population?.length).toEqual(4); }); }); From 9f1aef3a86b4edc9e2f9b5bf9a7ce5eb6066f7e1 Mon Sep 17 00:00:00 2001 From: Elsa Date: Thu, 8 Aug 2024 15:10:39 -0400 Subject: [PATCH 8/9] Add stratifier info to readme --- README.md | 40 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 304b5dd4..f12f44b1 100644 --- a/README.md +++ b/README.md @@ -522,7 +522,45 @@ fqm-execution reports -m /path/to/measure/bundle.json -p /path/to/patient1/bundl ## Stratification -For Measures with +The results for each stratifier on a Measure (if they exist) are reported on the DetailedResults array for each group. The StratifierResult object contains two result attributes: `result` and `appliesResult`. `result` is simply the raw result of the stratifier and `appliesResult` is the same unless that stratifier contains a [cqfm-appliesTo extension](https://hl7.org/fhir/us/cqfmeasures/STU4/StructureDefinition-cqfm-appliesTo.html). In the case that a stratifier applies to a specified population, the `appliesResult` is the result of the stratifier result AND the result of the specified population. The following is an example of what the DetailedResults would look like for a Measure whose single stratifier has a result of `true` but appliesTo the numerator population that has a result of `false`. + +```typescript +[ + { + patientId: 'patient-1', + detailedResults: [ + { + groupId: 'group-1', + populationResults: [ + { + populationType: 'initial-population', + criteriaExpression: 'Initial Population', + result: false + }, + { + populationType: 'denominator', + criteriaExpression: 'Denominator', + result: false + }, + { + populationType: 'numerator', + criteriaExpression: 'Numerator', + result: false + } + ], + stratifierResults: [ + { + strataCode: 'strata-1', + result: true, + appliesResult: false, + strataId: 'strata-1' + } + ] + } + ] + } +]; +``` ## Measures with Observation Functions From 8fd71a30be2553d5968747cad7d62b340f098a6f Mon Sep 17 00:00:00 2001 From: Elsa Perelli Date: Fri, 9 Aug 2024 08:58:04 -0400 Subject: [PATCH 9/9] Update test/unit/MeasureReportBuilder.test.ts Co-authored-by: lmd59 --- test/unit/MeasureReportBuilder.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/MeasureReportBuilder.test.ts b/test/unit/MeasureReportBuilder.test.ts index 43fcf310..80543c14 100644 --- a/test/unit/MeasureReportBuilder.test.ts +++ b/test/unit/MeasureReportBuilder.test.ts @@ -483,7 +483,7 @@ describe('MeasureReportBuilder Static', () => { }); }); - test('should generate a summary MeasureReport whose stratifierResults only contain all populations', () => { + test('should generate a summary MeasureReport whose stratifierResults contain all populations', () => { const { report } = builder; expect(report).toBeDefined(); expect(report.group?.[0].stratifier?.[0].stratum?.[0].population?.length).toEqual(4);