Skip to content

Commit

Permalink
Care-Gap Service Refactor (#522)
Browse files Browse the repository at this point in the history
* refactor care-gaps service

* javadoc added for new classes

* remove unused parameter values

* remove static set and formatting errors

* remove unused method

* test coverage for evalauted resources added to patient bundles

* comment out multiple rate scoringtypes check, for future implementation

* formatting correction

* code review edits

* error message edit

* map must be immutable for check-android-26-compliance validation
  • Loading branch information
Capt-Mac authored Sep 23, 2024
1 parent e97f034 commit a085dd5
Show file tree
Hide file tree
Showing 11 changed files with 830 additions and 633 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package org.opencds.cqf.fhir.cr.measure;

import static com.google.common.base.Preconditions.checkArgument;

import com.google.common.base.Strings;

public class CareGapsProperties {
private String careGapsReporter;
/**
Expand Down Expand Up @@ -37,4 +41,16 @@ public String getCareGapsCompositionSectionAuthor() {
public void setCareGapsCompositionSectionAuthor(String careGapsCompositionSectionAuthor) {
this.careGapsCompositionSectionAuthor = careGapsCompositionSectionAuthor;
}

public void validateRequiredProperties() {
checkArgument(
!Strings.isNullOrEmpty(getCareGapsReporter()),
"Setting care-gaps properties.care_gaps_reporter setting is required for the $care-gaps operation.");
checkArgument(
!Strings.isNullOrEmpty(getCareGapsCompositionSectionAuthor()),
"Setting care-gaps properties.care_gaps_composition_section_author is required for the $care-gaps operation.");
checkArgument(
!Strings.isNullOrEmpty(fhirBaseUrl),
"The fhirBaseUrl setting is required for the $care-gaps operation.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,6 @@ private CareGapsConstants() {
"http://hl7.org/fhir/us/davinci-deqm/StructureDefinition/extension-gapStatus";
public static final String CARE_GAPS_GAP_STATUS_SYSTEM =
"http://hl7.org/fhir/us/davinci-deqm/CodeSystem/gaps-status";
public static final String CARE_GAPS_REPORTER_KEY = "care_gaps_reporter";
public static final String CARE_GAPS_SECTION_AUTHOR_KEY = "care_gaps_composition_section_author";
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,5 @@ private MeasureReportConstants() {}
public static final String RESOURCE_TYPE_PRACTITIONER_ROLE = "PractitionerRole";
public static final String RESOURCE_TYPE_ORGANIZATION = "Organization";
public static final String RESOURCE_TYPE_LOCATION = "Location";
public static final String RESOURCE_TYPE_PATIENT = "Patient";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package org.opencds.cqf.fhir.cr.measure.r4;

import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_IMPROVEMENT_NOTATION_SYSTEM;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_MEASURE_POPULATION_SYSTEM;

import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.hl7.fhir.r4.model.Measure;
import org.hl7.fhir.r4.model.MeasureReport;
import org.opencds.cqf.fhir.cr.measure.enumeration.CareGapsStatusCode;

/**
* Care Gaps Status Evaluator houses the algorithm logic for which Care-Gap status is applicable to a Measure Report.
*/
public class R4CareGapStatusEvaluator {
/**
* <p>
* GapStatus is determined by interpreting a MeasureReport resource of Type Ratio or Proportion that contain the populations: Numerator & Denominator
* </p>
*<p>
* <ul>
* <li>'not-applicable': When a subject does not meet the criteria for the Measure scenario, whether by exclusion or exception criteria, or just by not meeting any required criteria, they will not show membership results in the 'Denominator'.</li>
* <li> subject is applicable (not a status): When a subject meets the criteria for a Measure they will have membership results in the 'Denominator', indicating they are of the appropriate criteria for the Measure scenario.</li>
* If in membership of 'Denominator', the subject will be assigned a 'closed-gap' or 'open-gap' status based on membership in 'Numerator' and the 'improvement notation'.
*</ul>
* </p>
* <p>
* Improvement Notation of Scoring Algorithm indicates whether the ratio of Numerator over Denominator populations represents a scenario to increase the Numerator to improve outcomes, or to decrease the Numerator count. If this value is not set on a Measure resource, then it is defaulted to 'Increase' under the IsPositive variable.
* </p>
* <ul>
* <li>ex: 1/10 with improvementNotation "decrease" means that the measureScore is 90%, therefore absense from 'Numerator' means criteria for care was met</li>
* <li>ex: 1/10 with improvementNotation "increase" means that the measureScore is 10%, therefore absense from 'Numerator' means criteria for care was NOT met.</li>
* </ul>
* <ul>
* <li>'open-gap': if in 'Denominator' & NOT in 'Numerator', where 'improvement notation' = increase. Then the subject is 'open-gap'</li>
* <li>'open-gap': if in 'Denominator' & in 'Numerator', where 'improvement notation' = decrease. Then the subject is 'open-gap'</li>
* <li>'closed-gap': if in 'Denominator' & NOT in 'Numerator', where 'improvement notation' = decrease. Then the subject is 'closed-gap'</li>
* <li>'closed-gap': if in 'Denominator' & in 'Numerator', where 'improvement notation' = increase. Then the subject is 'closed-gap'</li>
* </ul>
*/
public CareGapsStatusCode getGapStatus(Measure measure, MeasureReport measureReport) {
Pair<String, Boolean> inNumerator = new MutablePair<>("numerator", false);
Pair<String, Boolean> inDenominator = new MutablePair<>("denominator", false);
measureReport.getGroup().forEach(group -> group.getPopulation().forEach(population -> {
if (population.hasCode()
&& population.getCode().hasCoding(MEASUREREPORT_MEASURE_POPULATION_SYSTEM, inNumerator.getKey())
&& population.getCount() == 1) {
inNumerator.setValue(true);
}
if (population.hasCode()
&& population.getCode().hasCoding(MEASUREREPORT_MEASURE_POPULATION_SYSTEM, inDenominator.getKey())
&& population.getCount() == 1) {
inDenominator.setValue(true);
}
}));

// default improvementNotation
boolean isPositive = true;

// if value is present, set value from measure if populated
if (measure.hasImprovementNotation()) {
isPositive =
measure.getImprovementNotation().hasCoding(MEASUREREPORT_IMPROVEMENT_NOTATION_SYSTEM, "increase");
}

if (Boolean.FALSE.equals(inDenominator.getValue())) {
// patient is not in eligible population
return CareGapsStatusCode.NOT_APPLICABLE;
}

if (Boolean.TRUE.equals(inDenominator.getValue())
&& ((isPositive && !inNumerator.getValue()) || (!isPositive && inNumerator.getValue()))) {
return CareGapsStatusCode.OPEN_GAP;
}

return CareGapsStatusCode.CLOSED_GAP;
}
}
Loading

0 comments on commit a085dd5

Please # to comment.