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

466 bug fix for group level scoring def, and concurrent mod exception #479

Merged
merged 1 commit into from
Jun 11, 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 @@ -155,12 +155,7 @@ public Parameters getCareGapsReport(
Parameters result = initializeResult();
patients.forEach(patient -> {
Parameters.ParametersParameterComponent patientReports = patientReports(
periodStart.getValueAsString(),
periodEnd.getValueAsString(),
patient,
statuses,
measures,
organization);
periodStart.getValueAsString(), periodEnd.getValueAsString(), patient, statuses, measures);
if (patientReports != null) {
result.addParameter(patientReports);
}
Expand Down Expand Up @@ -320,25 +315,18 @@ protected <T extends Resource> T addConfiguredResource(Class<T> resourceClass, S

protected List<Measure> ensureMeasures(List<Measure> measures) {
measures.forEach(measure -> {
if (!measure.hasScoring()) {
ourLog.info("Measure does not specify a scoring so skipping: {}.", measure.getId());
measures.remove(measure);
}
if (!measure.hasImprovementNotation()) {
ourLog.info("Measure does not specify an improvement notation so skipping: {}.", measure.getId());
measures.remove(measure);
ourLog.info(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thought: This is the type of validation we should push upstream. Then we can give specific feedback in an OperationOutcome about issues with the Measure. In general, we don't want to be lenient with these type of requirements. In this particular case, the improvement notation flips the meaning of the Measure. Good means bad, bad means good.

"Measure '{}' does not specify an improvement notation, defaulting to: '{}'.",
measure.getId(),
"increase");
}
});
return measures;
}

protected Parameters.ParametersParameterComponent patientReports(
String periodStart,
String periodEnd,
Patient patient,
List<String> statuses,
List<Measure> measures,
String organization) {
String periodStart, String periodEnd, Patient patient, List<String> statuses, List<Measure> measures) {
// TODO: add organization to report, if it exists.
Composition composition = getComposition(patient);
List<DetectedIssue> detectedIssues = new ArrayList<>();
Expand Down Expand Up @@ -417,8 +405,6 @@ protected void initializeReport(MeasureReport measureReport) {
}
Reference reporter = new Reference().setReference(careGapsProperties.getCareGapsReporter());
// TODO: figure out what this extension is for
// reporter.addExtension(new
// Extension().setUrl(CARE_GAPS_MEASUREREPORT_REPORTER_EXTENSION));
measureReport.setReporter(reporter);
if (measureReport.hasMeta()) {
measureReport.getMeta().addProfile(CARE_GAPS_REPORT_PROFILE);
Expand Down Expand Up @@ -465,16 +451,21 @@ protected CareGapsStatusCode getGapStatus(Measure measure, MeasureReport measure
inDenominator.setValue(true);
}
}));
// default improvementNotation
boolean isPositive = true;

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

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

if (inDenominator.getValue()
if (Boolean.TRUE.equals(inDenominator.getValue())
&& ((isPositive && !inNumerator.getValue()) || (!isPositive && inNumerator.getValue()))) {
return CareGapsStatusCode.OPEN_GAP;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@

class R4CareGapsTest {
private static final Given given = CareGaps.given().repositoryFor("BreastCancerScreeningFHIR");
private static final Given GIVEN_REPO = CareGaps.given().repositoryFor("MinimalMeasureEvaluation");

@Test
void exm125_careGaps_closedGap() {
Expand Down Expand Up @@ -407,4 +408,36 @@ void exm125_careGaps_error_notPersonOrGroup() {
.then()
.hasBundleCount(0); // no results
}

// Issue #466 concurrent modification exception when improvementNotation is not populated on measure.
@Test
void ProportionBooleanBasisSingleGroup_Subject_noImprovementNotation() {
GIVEN_REPO
.when()
.subject("Patient/female-1988") // invalid
.periodStart("2019-01-01")
.periodEnd("2019-12-31")
.measureIds("MinimalProportionBooleanBasisSingleGroup")
.statuses("closed-gap")
.statuses("open-gap")
.getCareGapsReport()
.then()
.hasBundleCount(1); // no results
}

// Issue #466 unable to process group level scoring definition
@Test
void ProportionBooleanBasisSingleGroup_Subject_groupScoringDef() {
GIVEN_REPO
.when()
.subject("Patient/female-1988") // invalid
.periodStart("2019-01-01")
.periodEnd("2019-12-31")
.measureIds("MinimalProportionBooleanBasisSingleGroupGroupScoringDef")
.statuses("closed-gap")
.statuses("open-gap")
.getCareGapsReport()
.then()
.hasBundleCount(1); // no results
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
{
"id": "MinimalProportionBooleanBasisSingleGroupGroupScoringDef",
"resourceType": "Measure",
"url": "http://example.com/Measure/MinimalProportionBooleanBasisSingleGroupGroupScoringDef",
"library": [
"http://example.com/Library/MinimalProportionBooleanBasisSingleGroup"
],
"extension": [ {
"url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-populationBasis",
"valueCode": "boolean"
} ],
"group": [
{
"extension": [
{
"url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-scoring",
"valueCodeableConcept": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/measure-scoring",
"code": "proportion",
"display": "Proportion"
}
]
}
}
],
"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": "Initial Population"
}
},
{
"id": "denominator",
"code": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/measure-population",
"code": "denominator",
"display": "Denominator"
}
]
},
"criteria": {
"language": "text/cql-identifier",
"expression": "Denominator"
}
},
{
"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": "Denominator Exclusion"
}
},
{
"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": "Denominator Exception"
}
},
{
"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": "Numerator Exclusion"
}
},
{
"id": "numerator",
"code": {
"coding": [
{
"system": "http://terminology.hl7.org/CodeSystem/measure-population",
"code": "numerator",
"display": "Numerator"
}
]
},
"criteria": {
"language": "text/cql-identifier",
"expression": "Numerator"
}
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"resourceType": "Organization",
"id": "alphora-author",
"meta": {
"profile": [
"http://hl7.org/fhir/us/davinci-deqm/StructureDefinition/organization-deqm"
]
},
"identifier": [
{
"system": "urn:oid:2.16.840.1.113883.4.4",
"value": "123456790",
"use": "official",
"type": {
"coding": [
{
"code": "TAX",
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
"display": "Tax ID number"
}
]
},
"assigner": {
"display": "www.irs.gov"
}
}
],
"active": true,
"type": [
{
"coding": [
{
"code": "prov",
"system": "http://terminology.hl7.org/CodeSystem/organization-type",
"display": "Healthcare Provider"
}
]
}
],
"name": "Alphora",
"telecom": [
{
"system": "phone",
"value": "(+1) 401-555-1212"
}
],
"address": [
{
"line": [
"73 Lakewood Street"
],
"city": "Warwick",
"state": "RI",
"postalCode": "02886",
"country": "USA"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"resourceType": "Organization",
"id": "alphora",
"meta": {
"profile": [
"http://hl7.org/fhir/us/davinci-deqm/StructureDefinition/organization-deqm"
]
},
"identifier": [
{
"system": "urn:oid:2.16.840.1.113883.4.4",
"value": "123456789",
"use": "official",
"type": {
"coding": [
{
"code": "TAX",
"system": "http://terminology.hl7.org/CodeSystem/v2-0203",
"display": "Tax ID number"
}
]
},
"assigner": {
"display": "www.irs.gov"
}
}
],
"active": true,
"type": [
{
"coding": [
{
"code": "prov",
"system": "http://terminology.hl7.org/CodeSystem/organization-type",
"display": "Healthcare Provider"
}
]
}
],
"name": "Alphora",
"telecom": [
{
"system": "phone",
"value": "(+1) 401-555-1212"
}
],
"address": [
{
"line": [
"73 Lakewood Street"
],
"city": "Warwick",
"state": "RI",
"postalCode": "02886",
"country": "USA"
}
]
}