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

Experimental changes for issues 600, 601, and 602. #603

Draft
wants to merge 4 commits into
base: master
Choose a base branch
from
Draft
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
10 changes: 10 additions & 0 deletions cqf-fhir-cr/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,16 @@
<artifactId>slf4j-test</artifactId>
<scope>test</scope>
</dependency>
<!--
Needed to support @Language annotation for JDK 17 multi-line strings and syntax highlighting.
This supports SQL, JSON, etc, but unfortunately, not CQL (yet).
-->
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>23.0.0</version>
<scope>test</scope>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
Expand Down Expand Up @@ -303,6 +305,8 @@ protected MeasureDef evaluate(
EvaluationResult result =
libraryEngine.getEvaluationResult(id, subjectId, null, null, null, null, zonedDateTime, context);

someEvaluationValidationStuff(measureDef, result);

evaluateSubject(measureDef, subjectTypePart, subjectIdPart, subjectSize, type, result);
}

Expand Down Expand Up @@ -565,6 +569,7 @@ protected void evaluateGroup(
int populationSize,
MeasureReportType reportType,
EvaluationResult evaluationResult) {

evaluateStratifiers(subjectId, groupDef.stratifiers(), evaluationResult);

var scoring = groupDef.measureScoring();
Expand All @@ -582,6 +587,29 @@ protected void evaluateGroup(
}
}

private void someEvaluationValidationStuff(MeasureDef measureDef, EvaluationResult evaluationResult) {
measureDef.groups().forEach(groupDef -> someEvaluationValidationStuff(groupDef, evaluationResult));
}

private void someEvaluationValidationStuff(GroupDef groupDef, EvaluationResult evaluationResult) {
final Map<String, ExpressionResult> expressionResults = evaluationResult.expressionResults;
final CodeDef groupDefPopulationBasis = groupDef.getPopulationBasis();
final List<StratifierDef> stratifiers = groupDef.stratifiers();
final List<Map<String, CriteriaResult>> criteriaResults =
stratifiers.stream().map(StratifierDef::getResults).collect(Collectors.toList());

// LUKETODO: 600 LEFT side of the comparison is the groupDefPopulationBasis
logger.info("expressionResults: {}, groupDefPopulationBasis: {}", expressionResults, groupDefPopulationBasis);

for (Map<String, CriteriaResult> criteriaResult : criteriaResults) {
logger.info("criteriaResult: {}", criteriaResult);

for (Entry<String, CriteriaResult> criteriaResultEntry : criteriaResult.entrySet()) {
logger.info("criteriaResultEntry: {}", criteriaResultEntry);
}
}
}

protected Object evaluateDateOfCompliance(PopulationDef populationDef) {
var ref = Libraries.resolveExpressionRef(
populationDef.expression(), this.context.getState().getCurrentLibrary());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,12 @@ 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";
// LUKETODO: 602 get rid of this:
@Deprecated
public static final String EXT_TOTAL_DENOMINATOR_URL =
"http://hl7.org/fhir/us/davinci-deqm/StructureDefinition/extension-cqfm-denominator-membership";
// LUKETODO: 602 get rid of this:
@Deprecated
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
Expand Up @@ -346,11 +346,15 @@ protected void buildGroup(
}

if (groupDef.isBooleanBasis()) {
// LUKETODO: 602 why doesn't this consider exclusions?
// LUKETODO: 602 do we add this WITH or WITHOUT exclusions? my guess is WITHOUT
addExtension(
reportGroup, EXT_TOTAL_DENOMINATOR_URL, getReportPopulation(groupDef, TOTALDENOMINATOR), true);
addExtension(reportGroup, EXT_TOTAL_NUMERATOR_URL, getReportPopulation(groupDef, TOTALNUMERATOR), true);

} else {
// LUKETODO: 602 why doesn't this consider exclusions?
// LUKETODO: 602 do we add this WITH or WITHOUT exclusions? my guess is WITHOUT
addExtension(
reportGroup, EXT_TOTAL_DENOMINATOR_URL, getReportPopulation(groupDef, TOTALDENOMINATOR), false);
addExtension(
Expand All @@ -377,6 +381,8 @@ protected void addExtension(
group.addExtension().setUrl(extUrl).setValue(new StringType(Integer.toString(count)));
}

// LUKETODO: 602 is there such a thing as validation for group population basis?

/**
*
* Resource result --> Patient Key, Resource result --> can intersect on patient for Boolean basis, can't for Resource
Expand All @@ -390,6 +396,7 @@ protected void validateStratifierBasisType(Map<String, CriteriaResult> subjectVa
.filter(x -> x.rawValue() instanceof Resource)
.collect(Collectors.toList());
if (list.size() != subjectValues.values().size()) {
// LUKETODO: 602 this is the stratifier case
throw new IllegalArgumentException(
"stratifier expression criteria results must match the same type as population.");
}
Expand Down Expand Up @@ -497,6 +504,7 @@ protected void buildStratum(
}

// add totalDenominator and totalNumerator extensions
// LUKETODO: 602
buildStratumExtPopulation(groupDef, TOTALDENOMINATOR, subjectIds, stratum, EXT_TOTAL_DENOMINATOR_URL);
buildStratumExtPopulation(groupDef, TOTALNUMERATOR, subjectIds, stratum, EXT_TOTAL_NUMERATOR_URL);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,21 @@
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 java.util.stream.Collectors;
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;
import org.opencds.cqf.fhir.cr.measure.common.MeasureDef;
import org.opencds.cqf.fhir.cr.measure.common.MeasureScoring;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* <p>The R4 MeasureScorer takes population components from MeasureReport resources and scores each group population
Expand All @@ -36,6 +42,8 @@
*/
public class R4MeasureReportScorer extends BaseMeasureReportScorer<MeasureReport> {

private static final Logger logger = LoggerFactory.getLogger(R4MeasureReportScorer.class);

@Override
public void score(MeasureDef measureDef, MeasureReport measureReport) {
// Measure Def Check
Expand All @@ -48,6 +56,7 @@ public void score(MeasureDef measureDef, MeasureReport measureReport) {
}

for (MeasureReportGroupComponent mrgc : measureReport.getGroup()) {

scoreGroup(
getGroupMeasureScoring(mrgc, measureDef),
mrgc,
Expand Down Expand Up @@ -106,12 +115,70 @@ protected MeasureScoring getGroupMeasureScoring(MeasureReportGroupComponent mrgc

protected void scoreGroup(
MeasureScoring measureScoring, MeasureReportGroupComponent mrgc, boolean isIncreaseImprovementNotation) {
// LUKETODO: 602 should this be from the MeasureReportGroupComponent or the GroupDef?
/*
MeasureScorer should now look for Numerator/Denominator values to make the calculation instead of the extension values.
Any code that sets or maintains these populations can be removed
Testing classes that have assertions for these values should be deprecated
*/
final List<MeasureReportGroupPopulationComponent> populations = mrgc.getPopulation();

// LUKETODO: 602 I think we need to do the populations.stream()
final List<MeasureReportGroupPopulationComponent> numerators = populations.stream()
.filter(population -> "numerator"
.equals(population.getCode().getCodingFirstRep().getCode()))
.collect(Collectors.toList());

final List<MeasureReportGroupPopulationComponent> denominators = populations.stream()
.filter(population -> "numerator"
.equals(population.getCode().getCodingFirstRep().getCode()))
.collect(Collectors.toList());

final Integer populationNumeratorCount = populations.stream()
.filter(population -> "numerator"
.equals(population.getCode().getCodingFirstRep().getCode()))
.map(MeasureReportGroupPopulationComponent::getCount)
.findAny()
.orElse(0);

final Integer populationDenominatorCount = populations.stream()
.filter(population -> "denominator"
.equals(population.getCode().getCodingFirstRep().getCode()))
.map(MeasureReportGroupPopulationComponent::getCount)
.findAny()
.orElse(0);
logger.info(
"populationNumeratorCount: {}, populationDenominatorCount: {}",
populationNumeratorCount,
populationDenominatorCount);
System.out.println("populationNumeratorCount = " + populationNumeratorCount);
System.out.println("populationDenominatorCount = " + populationDenominatorCount);

final Integer extNumeratorCount = getGroupExtensionCount(mrgc, EXT_TOTAL_NUMERATOR_URL);
final Integer extDenominatorCount = getGroupExtensionCount(mrgc, EXT_TOTAL_DENOMINATOR_URL);

logger.info("extNumeratorCount: {}, extDenominatorCount: {}", extNumeratorCount, extDenominatorCount);
System.out.println("extNumeratorCount= " + populationNumeratorCount);
System.out.println("extDenominatorCount= " + extDenominatorCount);

// LUKETODO: 602
// if (!Objects.equals(extNumeratorCount, populationNumeratorCount)) {
// throw new IllegalStateException("numerator counts don't match: ext:" +
// extNumeratorCount + " != population:" + populationNumeratorCount);
// }
//
// if (!Objects.equals(extDenominatorCount, populationDenominatorCount)) {
// throw new IllegalStateException("denominator counts don't match: ext:" +
// extDenominatorCount + " != population:" + populationDenominatorCount);
// }
switch (measureScoring) {
case PROPORTION:
case RATIO:
Double score = this.calcProportionScore(
getGroupExtensionCount(mrgc, EXT_TOTAL_NUMERATOR_URL),
getGroupExtensionCount(mrgc, EXT_TOTAL_DENOMINATOR_URL));

// LUKETODO: 602: do we need a fix for the PRODUCTION WRITE????
// LUKETODO: 602: this is the actual fix for the READ
// Double score = this.calcProportionScore(extNumeratorCount, extDenominatorCount);
Double score = this.calcProportionScore(populationNumeratorCount, populationDenominatorCount);
if (score != null) {
if (isIncreaseImprovementNotation) {
mrgc.setMeasureScore(new Quantity(score));
Expand All @@ -124,6 +191,7 @@ protected void scoreGroup(
break;
}

// LUKETODO: 602: pass down populationNumerator and denominator to scoreStratifier() ???
for (MeasureReportGroupStratifierComponent stratifierComponent : mrgc.getStratifier()) {
scoreStratifier(measureScoring, stratifierComponent);
}
Expand All @@ -133,6 +201,29 @@ protected void scoreStratum(MeasureScoring measureScoring, StratifierGroupCompon
switch (measureScoring) {
case PROPORTION:
case RATIO:
// LUKETODO: 602: use this instead of the MeasureGroup Population????????
final List<StratifierGroupPopulationComponent> populations = stratum.getPopulation();

final Integer stratumPopulationNumeratorCount = populations.stream()
.filter(population -> "numerator"
.equals(population.getCode().getCodingFirstRep().getCode()))
.map(StratifierGroupPopulationComponent::getCount)
.findAny()
.orElse(0);

final Integer stratumPopulationDenominatorCount = populations.stream()
.filter(population -> "denominator"
.equals(population.getCode().getCodingFirstRep().getCode()))
.map(StratifierGroupPopulationComponent::getCount)
.findAny()
.orElse(0);
logger.info(
"stratumPopulationNumeratorCount: {}, stratumPopulationDenominatorCount: {}",
stratumPopulationNumeratorCount,
stratumPopulationDenominatorCount);
System.out.println("stratumPopulationNumeratorCount = " + stratumPopulationNumeratorCount);
System.out.println("stratumPopulationDenominatorCount = " + stratumPopulationDenominatorCount);

Double score = this.calcProportionScore(
getStratumPopulationCount(stratum, EXT_TOTAL_NUMERATOR_URL),
getStratumPopulationCount(stratum, EXT_TOTAL_DENOMINATOR_URL));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.Measure;
import org.hl7.fhir.r4.model.MeasureReport;
import org.hl7.fhir.r4.model.Period;
import org.hl7.fhir.r4.model.Resource;
import org.opencds.cqf.fhir.api.Repository;

Expand All @@ -19,16 +21,24 @@ public R4SubmitDataService(Repository repository) {
/**
* Save measure report and resources to the local repository
*
* @param id
* @param report
* @param resources
* @param measureId ???
* @param report The measure report being submitted
* @param resources The individual resources that make up the data-of-interest being submitted
* @return Bundle transaction result
*/
public Bundle submitData(IdType id, MeasureReport report, List<IBaseResource> resources) {
public Bundle submitData(IdType measureId, MeasureReport report, List<IBaseResource> resources) {
/*
* TODO - resource validation using $data-requirements operation (params are the provided id and
* TODO - resource validation using $data-requirements operation (params are the provided measureId and
* the measurement period from the MeasureReport)
*
*/

var measureFromDb = repository.read(Measure.class, measureId);
var measureReportPeriod = report.getPeriod();

validateResource(measureFromDb, measureReportPeriod);

// LUKETODO: 601
/*
* TODO - profile validation ... not sure how that would work ... (get StructureDefinition from
* URL or must it be stored in Ruler?)
*/
Expand All @@ -39,6 +49,7 @@ public Bundle submitData(IdType id, MeasureReport report, List<IBaseResource> re
if (resources != null) {
for (IBaseResource res : resources) {
// Unpack nested Bundles
// TODO: LD: replace with pattern variable once animal sniffer allows it
if (res instanceof Bundle) {
Bundle nestedBundle = (Bundle) res;
for (Bundle.BundleEntryComponent entry : nestedBundle.getEntry()) {
Expand All @@ -52,6 +63,11 @@ public Bundle submitData(IdType id, MeasureReport report, List<IBaseResource> re
return repository.transaction(transactionBundle);
}

private void validateResource(Measure measureFromDb, Period measureReportPeriod) {
// LUKETODO: 601 ???
// LUKETODO: 601 unit tests for any cases that fail validation
}

private Bundle.BundleEntryComponent createEntry(IBaseResource resource) {
return new Bundle.BundleEntryComponent()
.setResource((Resource) resource)
Expand Down
Loading
Loading