From 4b1c2d6bacdd588a3fe718b12438866efec5119e Mon Sep 17 00:00:00 2001 From: Justin McKelvy <60718638+Capt-Mac@users.noreply.github.com> Date: Tue, 11 Jun 2024 13:10:53 -0600 Subject: [PATCH] 466 bug fix for group level scoring def, and concurrent mod exception --- .../fhir/cr/measure/r4/R4CareGapsService.java | 39 ++---- .../fhir/cr/measure/r4/R4CareGapsTest.java | 33 +++++ ...ooleanBasisSingleGroupGroupScoringDef.json | 128 ++++++++++++++++++ .../tests/organization/alphora-author.json | 58 ++++++++ .../tests/organization/alphora.json | 58 ++++++++ 5 files changed, 292 insertions(+), 24 deletions(-) create mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionBooleanBasisSingleGroupGroupScoringDef.json create mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/tests/organization/alphora-author.json create mode 100644 cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/tests/organization/alphora.json diff --git a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsService.java b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsService.java index 04e5f2e14..cbf82379a 100644 --- a/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsService.java +++ b/cqf-fhir-cr/src/main/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsService.java @@ -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); } @@ -320,25 +315,18 @@ protected T addConfiguredResource(Class resourceClass, S protected List ensureMeasures(List 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( + "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 statuses, - List measures, - String organization) { + String periodStart, String periodEnd, Patient patient, List statuses, List measures) { // TODO: add organization to report, if it exists. Composition composition = getComposition(patient); List detectedIssues = new ArrayList<>(); @@ -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); @@ -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; } diff --git a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsTest.java b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsTest.java index 9aa11e202..35a2c0ff9 100644 --- a/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsTest.java +++ b/cqf-fhir-cr/src/test/java/org/opencds/cqf/fhir/cr/measure/r4/R4CareGapsTest.java @@ -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() { @@ -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 + } } diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionBooleanBasisSingleGroupGroupScoringDef.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionBooleanBasisSingleGroupGroupScoringDef.json new file mode 100644 index 000000000..76acc8f56 --- /dev/null +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/resources/measure/MinimalProportionBooleanBasisSingleGroupGroupScoringDef.json @@ -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" + } + } + ] + } + ] +} \ No newline at end of file diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/tests/organization/alphora-author.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/tests/organization/alphora-author.json new file mode 100644 index 000000000..aa64cb202 --- /dev/null +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/tests/organization/alphora-author.json @@ -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" + } + ] +} \ No newline at end of file diff --git a/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/tests/organization/alphora.json b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/tests/organization/alphora.json new file mode 100644 index 000000000..a2d23e769 --- /dev/null +++ b/cqf-fhir-cr/src/test/resources/org/opencds/cqf/fhir/cr/measure/r4/MinimalMeasureEvaluation/tests/organization/alphora.json @@ -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" + } + ] +} \ No newline at end of file