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

fix not-applicable status for care-gaps #574

Merged
merged 2 commits into from
Oct 31, 2024
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
Original file line number Diff line number Diff line change
Expand Up @@ -23,34 +23,25 @@
*/
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 absence from 'Numerator' means criteria for care was met</li>
* <li>ex: 1/10 with improvementNotation "increase" means that the measureScore is 10%, therefore absence 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>
* <p>'prospective-gap' is a concept that represents a period of time where a 'care-gap' measure has opportunity to address recommended care in a specific window of time. This 'window of time' we call the 'Date of Compliance' to indicate a range of time that optimally represents when care is meant to be provided.</p>
*<br/>
* <p>If care has not been provided ('open-gap'), and the date (reportDate) of evaluating for the Measure is before or within the 'Date of Compliance' interval, then the Measure is considered a 'prospective-gap' for the subject evaluated.</p>
* Below table is a mapping of expected behavior of the care-gap-status to Measure Report score
* | Gap-Status | initial-population | denominator | denominator-exclusion | denominator-exception\*\* | numerator-exclusion | numerator | Improvement Notation\*\*\* |
* | ----------------- | ------------------ | ----------- | --------------------- | ------------------------- | ------------------- | --------- | -------------------------- |
* | not-applicable | FALSE | N/A | N/A | N/A | N/A | N/A | N/A |
* | closed-gap | TRUE | FALSE | FALSE | FALSE | FALSE | FALSE | N/A |
* | closed-gap | TRUE | FALSE | TRUE | FALSE | FALSE | FALSE | N/A |
* | closed-gap | TRUE | FALSE | FALSE | TRUE | FALSE | FALSE | N/A |
* | prospective-gap\* | TRUE | TRUE | FALSE | FALSE | FALSE | FALSE | N/A |
* | prospective-gap\* | TRUE | TRUE | FALSE | FALSE | TRUE | FALSE | N/A |
* | open-gap | TRUE | TRUE | FALSE | FALSE | TRUE | FALSE | increase |
* | open-gap | TRUE | TRUE | FALSE | FALSE | FALSE | FALSE | increase |
* | open-gap | TRUE | TRUE | FALSE | FALSE | FALSE | TRUE | decrease |
* | closed-gap | TRUE | TRUE | FALSE | FALSE | TRUE | FALSE | decrease |
* | closed-gap | TRUE | TRUE | FALSE | FALSE | FALSE | TRUE | increase |
* | closed-gap | TRUE | TRUE | FALSE | FALSE | FALSE | FALSE | decrease |
*
* <p></p>
* *`prospective-gap` status requires additional data points than just population-code values within a MeasureReport in order to determine if ‘prospective-gap’ or just ‘open-gap’.
* **denominator-exception: is only for ‘proportion’ scoring type.
* ***improvement Notation: is a Measure defined value that tells users how to view results of the Measure Report.
*/
public Map<String, CareGapsStatusCode> getGroupGapStatus(Measure measure, MeasureReport measureReport) {
Map<String, CareGapsStatusCode> groupStatus = new HashMap<>();
Expand All @@ -67,10 +58,18 @@ public Map<String, CareGapsStatusCode> getGroupGapStatus(Measure measure, Measur

private CareGapsStatusCode getGapStatus(
Measure measure, MeasureReportGroupComponent measureReportGroup, DateTimeType reportDate) {
Pair<String, Boolean> inInitialPopulation = new MutablePair<>("initial-population", false);
Pair<String, Boolean> inNumerator = new MutablePair<>("numerator", false);
Pair<String, Boolean> inDenominator = new MutablePair<>("denominator", false);
// get Numerator and Denominator membership
measureReportGroup.getPopulation().forEach(population -> {
if (population.hasCode()
&& population
.getCode()
.hasCoding(MEASUREREPORT_MEASURE_POPULATION_SYSTEM, inInitialPopulation.getKey())
&& population.getCount() == 1) {
inInitialPopulation.setValue(true);
}
if (population.hasCode()
&& population.getCode().hasCoding(MEASUREREPORT_MEASURE_POPULATION_SYSTEM, inNumerator.getKey())
&& population.getCount() == 1) {
Expand All @@ -83,6 +82,10 @@ private CareGapsStatusCode getGapStatus(
}
});

// is subject eligible for measure?
if (Boolean.FALSE.equals(inInitialPopulation.getValue())) {
return CareGapsStatusCode.NOT_APPLICABLE;
}
// default improvementNotation
boolean isPositive = true;

Expand All @@ -94,11 +97,6 @@ private CareGapsStatusCode getGapStatus(
.hasCoding(MEASUREREPORT_IMPROVEMENT_NOTATION_SYSTEM, 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 getOpenOrProspectiveStatus(measureReportGroup, reportDate);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ void exm125_careGaps_openGap() {

@Test
void exm125_careGaps_NA() {
// validates initial-population=false returns 'not-applicable' status
given.when()
.subject("Patient/neg-denom-EXM125")
.periodStart(LocalDate.of(2019, Month.JANUARY, 1).atStartOfDay().atZone(ZoneId.systemDefault()))
Expand Down Expand Up @@ -759,4 +760,27 @@ void noMeasureSpecified() {
assertTrue(e.getMessage().contains("no measure resolving parameter was specified"));
}
}
// MinimalProportionDenominatorExclusion
@Test
void InInitalPopulationAndDenominatorExclusion() {
// when subject is in initial population
// not in Denominator
// they should produce 'closed-gap'
GIVEN_REPO
.when()
.subject("Patient/female-1988")
.periodStart(LocalDate.of(2019, Month.JANUARY, 1).atStartOfDay().atZone(ZoneId.systemDefault()))
.periodEnd(LocalDate.of(2019, Month.DECEMBER, 31).atStartOfDay().atZone(ZoneId.systemDefault()))
.measureId("MinimalProportionDenominatorExclusion")
.measureIdentifier(null)
.measureUrl(null)
.status("closed-gap")
.getCareGapsReport()
.then()
.hasBundleCount(1)
.firstParameter()
.detectedIssueCount(1)
.detectedIssue()
.hasCareGapStatus("closed-gap");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
{
"id": "MinimalProportionDenominatorExclusion",
"resourceType": "Measure",
"url": "http://example.com/Measure/MinimalProportionDenominatorExclusion",
"library": [
"http://example.com/Library/MinimalProportionBooleanBasisSingleGroup"
],
"scoring": {
"coding": [
{
"system": "http://hl7.org/fhir/measure-scoring",
"code": "proportion"
}
]
},
"group": [
{
"population": [
{
"id": "initial-population",
"code": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/measure-population",
"code": "initial-population",
"display": "Initial Population"
}
]
},
"criteria": {
"language": "text/cql-identifier",
"expression": "always true"
}
},
{
"id": "denominator",
"code": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/measure-population",
"code": "denominator",
"display": "Denominator"
}
]
},
"criteria": {
"language": "text/cql-identifier",
"expression": "always false"
}
},
{
"id": "denominator-exclusion",
"code": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/measure-population",
"code": "denominator-exclusion",
"display": "Denominator-Exclusion"
}
]
},
"criteria": {
"language": "text/cql-identifier",
"expression": "always true"
}
},
{
"id": "denominator-exception",
"code": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/measure-population",
"code": "denominator-exception",
"display": "Denominator-Exception"
}
]
},
"criteria": {
"language": "text/cql-identifier",
"expression": "always false"
}
},
{
"id": "numerator-exclusion",
"code": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/measure-population",
"code": "numerator-exclusion",
"display": "Numerator-Exclusion"
}
]
},
"criteria": {
"language": "text/cql-identifier",
"expression": "always false"
}
},
{
"id": "numerator",
"code": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/measure-population",
"code": "numerator",
"display": "Numerator"
}
]
},
"criteria": {
"language": "text/cql-identifier",
"expression": "always false"
}
}
]
}
]
}