Skip to content

Commit

Permalink
Eliminate total denominator and total numerator extensions as well as…
Browse files Browse the repository at this point in the history
… all related code in favour of denominator and numerator (#606)

* First checking:  Deprecate extensions and switch calculation to proper numerator and denominator populations, but don't delete the extensions yet.

* Cleanup from first round of total**** deletions.

* First round of deleting total denominator and numerator from R4MeasureScoringTypePopulations.

* Finish final cleanup.

* Remove TOTAL*** extensions from JSONs.
  • Loading branch information
lukedegruchy authored Dec 3, 2024
1 parent c8a3ea2 commit 332eb29
Show file tree
Hide file tree
Showing 15 changed files with 58 additions and 276 deletions.
2 changes: 0 additions & 2 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,6 @@
"SUBJECTLIST",
"SUPPLEMENTALDATA",
"testng",
"TOTALDENOMINATOR",
"TOTALNUMERATOR",
"unsignedint",
"Unvalidated",
"unversioned",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@
import static org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType.MEASUREPOPULATIONEXCLUSION;
import static org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType.NUMERATOR;
import static org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType.NUMERATOREXCLUSION;
import static org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType.TOTALDENOMINATOR;
import static org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType.TOTALNUMERATOR;

import java.time.LocalDateTime;
import java.time.OffsetDateTime;
Expand Down Expand Up @@ -434,8 +432,6 @@ protected void evaluateProportion(
// Evaluate Population Expressions
denominator = evaluatePopulationMembership(subjectType, subjectId, denominator, evaluationResult);
numerator = evaluatePopulationMembership(subjectType, subjectId, numerator, evaluationResult);
var totalDenominator = groupDef.getSingle(TOTALDENOMINATOR);
var totalNumerator = groupDef.getSingle(TOTALNUMERATOR);

// Evaluate Exclusions and Exception Populations
if (denominatorExclusion != null) {
Expand Down Expand Up @@ -471,8 +467,6 @@ protected void evaluateProportion(
denominator.getSubjects().removeAll(denominatorException.getSubjects());
denominator.getResources().removeAll(denominatorException.getResources());
}
totalDenominator.getSubjects().addAll(denominator.getSubjects());
totalNumerator.getSubjects().addAll(numerator.getSubjects());
} else {
// Remove Only Resource Exclusions
// * Multiple resources can be from one subject and represented in multiple populations
Expand All @@ -490,9 +484,6 @@ protected void evaluateProportion(
// Remove Resources in Denominator that are not in Numerator
denominator.getResources().removeAll(denominatorException.getResources());
}
// TODO: Evaluate validity of TotalDenominator & TotalDenominator
totalDenominator.getResources().addAll(denominator.getResources());
totalNumerator.getResources().addAll(numerator.getResources());
}
if (reportType.equals(MeasureReportType.INDIVIDUAL) && populationSize == 1 && dateOfCompliance != null) {
var doc = evaluateDateOfCompliance(dateOfCompliance);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,6 @@ public enum MeasurePopulationType {
"Measure Observation",
"Defines the individual observation to be performed for each patient or event in the measure population. Measure observations for each case in the population are aggregated to determine the overall measure score for the population"),

TOTALDENOMINATOR(
"total-denominator",
"Total Denominator",
"The calculated denominator value used to calculate the measure score from Denominator, Denominator-Exclusion, Denominator-Exception and Numerator values"),

TOTALNUMERATOR(
"total-numerator",
"Total Numerator",
"The calculated numerator value used to calculate the measure score from Numerator and Numerator-Exclusion values"),
DATEOFCOMPLIANCE(
"date-compliance",
"Date of Compliance",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,4 @@ private MeasureConstants() {}
public static final String FHIR_ALL_TYPES_SYSTEM_URL = "http://hl7.org/fhir/fhir-types";
public static final String POPULATION_BASIS_URL =
"http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis";
public static final String EXT_TOTAL_DENOMINATOR_URL =
"http://hl7.org/fhir/us/davinci-deqm/StructureDefinition/extension-cqfm-denominator-membership";
public static final String EXT_TOTAL_NUMERATOR_URL =
"http://hl7.org/fhir/us/davinci-deqm/StructureDefinition/extension-cqfm-numerator-membership";
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package org.opencds.cqf.fhir.cr.measure.dstu3;

import static org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType.TOTALDENOMINATOR;
import static org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType.TOTALNUMERATOR;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants.FHIR_ALL_TYPES_SYSTEM_URL;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.IMPROVEMENT_NOTATION_SYSTEM_DECREASE;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.IMPROVEMENT_NOTATION_SYSTEM_INCREASE;
Expand Down Expand Up @@ -76,18 +74,6 @@ public MeasureDef build(Measure measure) {
populations.add(new PopulationDef(
pop.getId(), conceptToConceptDef(pop.getCode()), populationType, pop.getCriteria()));
}
// total Denominator/Numerator Def Builder
// validate population is not in Def
if (checkPopulationForCode(populations, TOTALDENOMINATOR) == null) {
// add to definition
populations.add(new PopulationDef(
"totalDenominator", totalConceptDefCreator(TOTALDENOMINATOR), TOTALDENOMINATOR, null));
}
if (checkPopulationForCode(populations, TOTALNUMERATOR) == null) {
// add to definition
populations.add(new PopulationDef(
"totalNumerator", totalConceptDefCreator(TOTALNUMERATOR), TOTALNUMERATOR, null));
}

// Stratifiers
List<StratifierDef> stratifiers = new ArrayList<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,10 +134,7 @@ protected void buildGroup(
MeasureGroupComponent measureGroup,
MeasureReportGroupComponent reportGroup,
GroupDef groupDef) {
// groupDef contains populations/stratifier components not defined in measureGroup (TOTAL-NUMERATOR &
// TOTAL-DENOMINATOR), and will not be added to group populations.
// Subtracting '2' from groupDef to balance with Measure defined Groups
if (measureGroup.getPopulation().size() != (groupDef.populations().size() - 2)) {
if (measureGroup.getPopulation().size() != (groupDef.populations().size())) {
throw new IllegalArgumentException(
"The MeasureGroup has a different number of populations defined than the GroupDef");
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package org.opencds.cqf.fhir.cr.measure.r4;

import static org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType.DATEOFCOMPLIANCE;
import static org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType.TOTALDENOMINATOR;
import static org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType.TOTALNUMERATOR;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants.CQFM_CARE_GAP_DATE_OF_COMPLIANCE_EXT_URL;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants.CQFM_SCORING_EXT_URL;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants.FHIR_ALL_TYPES_SYSTEM_URL;
Expand Down Expand Up @@ -89,18 +87,7 @@ public MeasureDef build(Measure measure) {
populationType,
pop.getCriteria().getExpression()));
}
// total Denominator/Numerator Def Builder
// validate population is not in Def
if (checkPopulationForCode(populations, TOTALDENOMINATOR) == null) {
// add to definition
populations.add(new PopulationDef(
"totalDenominator", totalConceptDefCreator(TOTALDENOMINATOR), TOTALDENOMINATOR, null));
}
if (checkPopulationForCode(populations, TOTALNUMERATOR) == null) {
// add to definition
populations.add(new PopulationDef(
"totalNumerator", totalConceptDefCreator(TOTALNUMERATOR), TOTALNUMERATOR, null));
}

if (group.getExtensionByUrl(CQFM_CARE_GAP_DATE_OF_COMPLIANCE_EXT_URL) != null
&& checkPopulationForCode(populations, DATEOFCOMPLIANCE) == null) {
// add to definition
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
package org.opencds.cqf.fhir.cr.measure.r4;

import static org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType.DATEOFCOMPLIANCE;
import static org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType.TOTALDENOMINATOR;
import static org.opencds.cqf.fhir.cr.measure.common.MeasurePopulationType.TOTALNUMERATOR;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants.CQFM_CARE_GAP_DATE_OF_COMPLIANCE_EXT_URL;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants.EXT_CRITERIA_REFERENCE_URL;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants.EXT_SDE_REFERENCE_URL;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants.EXT_TOTAL_DENOMINATOR_URL;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants.EXT_TOTAL_NUMERATOR_URL;

import com.google.common.collect.Lists;
import java.util.ArrayList;
Expand Down Expand Up @@ -274,17 +270,14 @@ protected void buildGroup(
MeasureReportGroupComponent reportGroup,
GroupDef groupDef) {

// groupDef contains populations/stratifier components not defined in measureGroup (TOTAL-NUMERATOR &
// TOTAL-DENOMINATOR), and will not be added to group populations.
// Subtracting '2' from groupDef to balance with Measure defined Groups
var groupDefSizeDiff = 2;
var groupDefSizeDiff = 0;
if (groupDef.populations().stream()
.filter(x -> x.type().equals(MeasurePopulationType.DATEOFCOMPLIANCE))
.findFirst()
.orElse(null)
!= null) {
// dateOfNonCompliance is another population not calculated
groupDefSizeDiff = 3;
groupDefSizeDiff = 1;
}

if ((measureGroup.getPopulation().size()) != (groupDef.populations().size() - groupDefSizeDiff)) {
Expand Down Expand Up @@ -344,18 +337,6 @@ protected void buildGroup(
}
}
}

if (groupDef.isBooleanBasis()) {
addExtension(
reportGroup, EXT_TOTAL_DENOMINATOR_URL, getReportPopulation(groupDef, TOTALDENOMINATOR), true);
addExtension(reportGroup, EXT_TOTAL_NUMERATOR_URL, getReportPopulation(groupDef, TOTALNUMERATOR), true);

} else {
addExtension(
reportGroup, EXT_TOTAL_DENOMINATOR_URL, getReportPopulation(groupDef, TOTALDENOMINATOR), false);
addExtension(
reportGroup, EXT_TOTAL_NUMERATOR_URL, getReportPopulation(groupDef, TOTALNUMERATOR), false);
}
}
for (int i = 0; i < measureGroup.getStratifier().size(); i++) {
var groupStrat = measureGroup.getStratifier().get(i);
Expand All @@ -365,18 +346,6 @@ protected void buildGroup(
}
}

protected void addExtension(
MeasureReportGroupComponent group, String extUrl, PopulationDef populationDef, boolean useSubjects) {
int count;
if (useSubjects) {
count = populationDef.getSubjects().size();
} else {
count = populationDef.getResources().size();
}

group.addExtension().setUrl(extUrl).setValue(new StringType(Integer.toString(count)));
}

/**
*
* Resource result --> Patient Key, Resource result --> can intersect on patient for Boolean basis, can't for Resource
Expand Down Expand Up @@ -495,38 +464,6 @@ protected void buildStratum(
var stratumPopulation = stratum.addPopulation();
buildStratumPopulation(bc, stratumPopulation, subjectIds, mgpc);
}

// add totalDenominator and totalNumerator extensions
buildStratumExtPopulation(groupDef, TOTALDENOMINATOR, subjectIds, stratum, EXT_TOTAL_DENOMINATOR_URL);
buildStratumExtPopulation(groupDef, TOTALNUMERATOR, subjectIds, stratum, EXT_TOTAL_NUMERATOR_URL);
}

protected void buildStratumExtPopulation(
GroupDef groupDef,
MeasurePopulationType measurePopulationType,
List<String> subjectIds,
StratifierGroupComponent stratum,
String extUrl) {
Set<String> subjectPop;
var reportPopulation = getReportPopulation(groupDef, measurePopulationType);
assert reportPopulation != null;
if (groupDef.isBooleanBasis()) {
subjectPop = reportPopulation.getSubjects().stream()
.map(t -> ResourceType.Patient.toString().concat("/").concat(t))
.collect(Collectors.toSet());
} else {
subjectPop = reportPopulation.getResources().stream()
.filter(Resource.class::isInstance)
.map(Resource.class::cast)
.map(x -> x.getResourceType().toString().concat("/").concat(x.getIdPart()))
.collect(Collectors.toSet());
}
int count;

Set<String> intersection = new HashSet<>(subjectIds);
intersection.retainAll(subjectPop);
count = intersection.size();
stratum.addExtension().setUrl(extUrl).setValue(new StringType(Integer.toString(count)));
}

protected void buildStratumPopulation(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package org.opencds.cqf.fhir.cr.measure.r4;

import static org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants.EXT_TOTAL_DENOMINATOR_URL;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureConstants.EXT_TOTAL_NUMERATOR_URL;

import java.util.List;
import org.hl7.fhir.r4.model.MeasureReport;
import org.hl7.fhir.r4.model.MeasureReport.MeasureReportGroupComponent;
import org.hl7.fhir.r4.model.MeasureReport.MeasureReportGroupPopulationComponent;
import org.hl7.fhir.r4.model.MeasureReport.MeasureReportGroupStratifierComponent;
import org.hl7.fhir.r4.model.MeasureReport.StratifierGroupComponent;
import org.hl7.fhir.r4.model.MeasureReport.StratifierGroupPopulationComponent;
import org.hl7.fhir.r4.model.Quantity;
import org.opencds.cqf.fhir.cr.measure.common.BaseMeasureReportScorer;
import org.opencds.cqf.fhir.cr.measure.common.GroupDef;
Expand Down Expand Up @@ -36,6 +36,9 @@
*/
public class R4MeasureReportScorer extends BaseMeasureReportScorer<MeasureReport> {

private static final String NUMERATOR = "numerator";
private static final String DENOMINATOR = "denominator";

@Override
public void score(MeasureDef measureDef, MeasureReport measureReport) {
// Measure Def Check
Expand Down Expand Up @@ -106,12 +109,13 @@ protected MeasureScoring getGroupMeasureScoring(MeasureReportGroupComponent mrgc

protected void scoreGroup(
MeasureScoring measureScoring, MeasureReportGroupComponent mrgc, boolean isIncreaseImprovementNotation) {

switch (measureScoring) {
case PROPORTION:
case RATIO:
Double score = this.calcProportionScore(
getGroupExtensionCount(mrgc, EXT_TOTAL_NUMERATOR_URL),
getGroupExtensionCount(mrgc, EXT_TOTAL_DENOMINATOR_URL));
var score = calcProportionScore(
getCountFromGroupPopulation(mrgc.getPopulation(), NUMERATOR),
getCountFromGroupPopulation(mrgc.getPopulation(), DENOMINATOR));
if (score != null) {
if (isIncreaseImprovementNotation) {
mrgc.setMeasureScore(new Quantity(score));
Expand All @@ -133,9 +137,9 @@ protected void scoreStratum(MeasureScoring measureScoring, StratifierGroupCompon
switch (measureScoring) {
case PROPORTION:
case RATIO:
Double score = this.calcProportionScore(
getStratumPopulationCount(stratum, EXT_TOTAL_NUMERATOR_URL),
getStratumPopulationCount(stratum, EXT_TOTAL_DENOMINATOR_URL));
var score = calcProportionScore(
getCountFromStratifierPopulation(stratum.getPopulation(), NUMERATOR),
getCountFromStratifierPopulation(stratum.getPopulation(), DENOMINATOR));
if (score != null) {
stratum.setMeasureScore(new Quantity(score));
}
Expand All @@ -145,28 +149,30 @@ protected void scoreStratum(MeasureScoring measureScoring, StratifierGroupCompon
}
}

protected Integer getGroupExtensionCount(MeasureReportGroupComponent mrgc, String extUrl) {
var ext = mrgc.getExtension().stream()
.filter(x -> x.getUrl().equals(extUrl))
.findFirst();
return ext.map(extension -> Integer.valueOf(extension.getValue().toString()))
.orElse(null);
}

protected Integer getStratumPopulationCount(StratifierGroupComponent sgc, String extUrl) {
var pop = sgc.getExtension();
var ext =
pop.stream().filter(x -> x.getUrl().equals(extUrl)).findFirst().orElse(null);
if (ext != null) {
return Integer.valueOf(ext.getValue().toString());
}
return null;
}

protected void scoreStratifier(
MeasureScoring measureScoring, MeasureReportGroupStratifierComponent stratifierComponent) {
for (StratifierGroupComponent sgc : stratifierComponent.getStratum()) {
scoreStratum(measureScoring, sgc);
}
}

private int getCountFromGroupPopulation(
List<MeasureReportGroupPopulationComponent> populations, String populationName) {
return populations.stream()
.filter(population -> populationName.equals(
population.getCode().getCodingFirstRep().getCode()))
.map(MeasureReportGroupPopulationComponent::getCount)
.findAny()
.orElse(0);
}

private int getCountFromStratifierPopulation(
List<StratifierGroupPopulationComponent> populations, String populationName) {
return populations.stream()
.filter(population -> populationName.equals(
population.getCode().getCodingFirstRep().getCode()))
.map(StratifierGroupPopulationComponent::getCount)
.findAny()
.orElse(0);
}
}
Loading

0 comments on commit 332eb29

Please sign in to comment.