Add more measure testing infrastructure, test cases for 104,105,108,124,125 (#2379)

This commit is contained in:
JP 2021-02-19 11:44:56 -07:00 committed by GitHub
parent 3eda19c65a
commit 804e023189
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 50971 additions and 32 deletions

View File

@ -73,10 +73,18 @@ public class BaseCqlDstu3Test extends BaseJpaDstu3Test implements CqlProviderTes
return count;
}
protected Bundle loadBundle(String theLocation) throws IOException {
protected Bundle parseBundle(String theLocation) throws IOException {
String json = stringFromResource(theLocation);
Bundle bundle = (Bundle) myFhirContext.newJsonParser().parseResource(json);
Bundle result = (Bundle) mySystemDao.transaction(null, bundle);
return result;
return bundle;
}
protected Bundle loadBundle(Bundle bundle) {
return (Bundle) mySystemDao.transaction(null, bundle);
}
protected Bundle loadBundle(String theLocation) throws IOException {
Bundle bundle = parseBundle(theLocation);
return loadBundle(bundle);
}
}

View File

@ -8,8 +8,11 @@ import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.api.dao.IFhirSystemDao;
import ca.uhn.fhir.jpa.subscription.match.config.SubscriptionProcessorConfig;
import ca.uhn.fhir.jpa.test.BaseJpaR4Test;
import ca.uhn.fhir.util.BundleUtil;
import org.apache.commons.io.FileUtils;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.MeasureReport;
import org.junit.jupiter.api.extension.ExtendWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@ -21,6 +24,7 @@ import org.springframework.test.context.junit.jupiter.SpringExtension;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.List;
@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = {CqlR4Config.class, TestCqlConfig.class, SubscriptionProcessorConfig.class})
@ -72,11 +76,18 @@ public class BaseCqlR4Test extends BaseJpaR4Test implements CqlProviderTestBase
return count;
}
protected Bundle loadBundle(String theLocation) throws IOException {
protected Bundle parseBundle(String theLocation) throws IOException {
String json = stringFromResource(theLocation);
Bundle bundle = (Bundle) myFhirContext.newJsonParser().parseResource(json);
Bundle result = (Bundle) mySystemDao.transaction(null, bundle);
return result;
return bundle;
}
protected Bundle loadBundle(Bundle bundle) {
return (Bundle) mySystemDao.transaction(null, bundle);
}
protected Bundle loadBundle(String theLocation) throws IOException {
Bundle bundle = parseBundle(theLocation);
return loadBundle(bundle);
}
}

View File

@ -0,0 +1,144 @@
package ca.uhn.fhir.cql.dstu3;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import org.hamcrest.Matchers;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.DateTimeType;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Measure;
import org.hl7.fhir.dstu3.model.MeasureReport;
import org.hl7.fhir.dstu3.model.MeasureReport.MeasureReportGroupComponent;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.cql.BaseCqlDstu3Test;
import ca.uhn.fhir.cql.dstu3.provider.MeasureOperationsProvider;
import ca.uhn.fhir.util.BundleUtil;
public class CqlMeasureEvaluationDstu3Test extends BaseCqlDstu3Test {
Logger ourLog = LoggerFactory.getLogger(CqlMeasureEvaluationDstu3Test.class);
@Autowired
MeasureOperationsProvider myMeasureOperationsProvider;
protected void testMeasureBundle(String theLocation) throws IOException {
Bundle bundle = parseBundle(theLocation);
loadBundle(bundle);
List<Measure> measures = BundleUtil.toListOfResourcesOfType(myFhirContext, bundle, Measure.class);
if (measures == null || measures.isEmpty()) {
throw new IllegalArgumentException(String.format("No measures found for Bundle %s", theLocation));
}
List<MeasureReport> reports = BundleUtil.toListOfResourcesOfType(myFhirContext, bundle, MeasureReport.class);
if (reports == null || reports.isEmpty()) {
throw new IllegalArgumentException(String.format("No measure reports found for Bundle %s", theLocation));
}
for (MeasureReport report : reports) {
testMeasureReport(report);
}
}
protected void testMeasureReport(MeasureReport expected) {
String measureId = this.getMeasureId(expected);
String patientId = this.getPatientId(expected);
String periodStart = this.getPeriodStart(expected);
String periodEnd = this.getPeriodEnd(expected);
this.ourLog.info("Measure: %s, Patient: %s, Start: %s, End: %s", measureId, patientId, periodStart, periodEnd);
MeasureReport actual = this.myMeasureOperationsProvider.evaluateMeasure(new IdType("Measure", measureId),
periodStart, periodEnd, null,
// TODO: These are all individual reports
"patient", patientId,
// TODO: Generalize these parameters into a Parameters resource
null, null, null, null, null, null);
compareMeasureReport(expected, actual);
}
protected void compareMeasureReport(MeasureReport expected, MeasureReport actual) {
assertNotNull("expected MeasureReport can not be null", expected);
assertNotNull("actual MeasureReport can not be null", actual);
String errorLocator = String.format("Measure: %s, Subject: %s", expected.getMeasure(),
expected.getPatient().getReference());
assertEquals(expected.hasGroup(), actual.hasGroup(), errorLocator);
assertEquals(expected.getGroup().size(), actual.getGroup().size(), errorLocator);
for (MeasureReportGroupComponent mrgcExpected : expected.getGroup()) {
Optional<MeasureReportGroupComponent> mrgcActualOptional = actual.getGroup().stream()
.filter(x -> x.getId().equals(mrgcExpected.getId())).findFirst();
errorLocator = String.format("Measure: %s, Subject: %s, Group: %s", expected.getMeasure(),
expected.getPatient().getReference(), mrgcExpected.getId());
assertTrue(errorLocator, mrgcActualOptional.isPresent());
MeasureReportGroupComponent mrgcActual = mrgcActualOptional.get();
if (mrgcExpected.getMeasureScore() == null) {
assertNull(mrgcActual.getMeasureScore(), errorLocator);
} else {
assertNotNull(mrgcActual.getMeasureScore());
BigDecimal decimalExpected = mrgcExpected.getMeasureScore();
BigDecimal decimalActual = mrgcActual.getMeasureScore();
assertThat(errorLocator, decimalActual, Matchers.comparesEqualTo(decimalExpected));
}
}
}
public String getPatientId(MeasureReport measureReport) {
String[] subjectRefParts = measureReport.getPatient().getReference().split("/");
String patientId = subjectRefParts[subjectRefParts.length - 1];
return patientId;
}
public String getMeasureId(MeasureReport measureReport) {
String[] measureRefParts = measureReport.getMeasure().getReference().split("/");
String measureId = measureRefParts[measureRefParts.length - 1];
return measureId;
}
public String getPeriodStart(MeasureReport measureReport) {
Date periodStart = measureReport.getPeriod().getStart();
if (periodStart != null) {
return toDateString(periodStart);
}
return null;
}
public String getPeriodEnd(MeasureReport measureReport) {
Date periodEnd = measureReport.getPeriod().getEnd();
if (periodEnd != null) {
return toDateString(periodEnd);
}
return null;
}
public String toDateString(Date date) {
return new DateTimeType(date).getValueAsString();
}
// As of 2/11/2021, all the DSTU3 bundles in the Connectathon IG are out of date
// and can't be posted
// @Test
// public void test_EXM117_83000() throws IOException {
// this.testMeasureBundle("dstu3/connectathon/EXM117_FHIR3-8.3.000-bundle.json");
// }
}

View File

@ -1,54 +1,208 @@
package ca.uhn.fhir.cql.r4;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import org.hamcrest.Matchers;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.DateTimeType;
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.MeasureReport.MeasureReportGroupComponent;
import org.hl7.fhir.r4.model.Quantity;
import org.hl7.fhir.r4.model.MeasureReport.MeasureReportGroupPopulationComponent;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.cql.BaseCqlR4Test;
import ca.uhn.fhir.cql.common.provider.CqlProviderTestBase;
import ca.uhn.fhir.cql.r4.provider.MeasureOperationsProvider;
import ca.uhn.fhir.util.BundleUtil;
public class CqlMeasureEvaluationR4Test extends BaseCqlR4Test implements CqlProviderTestBase {
private static final IdType measureId = new IdType("Measure", "measure-EXM130-7.3.000");
private static final String periodStart = "2019-01-01";
private static final String periodEnd = "2019-12-31";
public class CqlMeasureEvaluationR4Test extends BaseCqlR4Test {
Logger ourLog = LoggerFactory.getLogger(CqlMeasureEvaluationR4Test.class);
@Autowired
MeasureOperationsProvider myMeasureOperationsProvider;
public void loadBundles() throws IOException {
loadBundle("r4/connectathon/EXM130-7.3.000-bundle.json");
protected void testMeasureBundle(String theLocation) throws IOException {
Bundle bundle = parseBundle(theLocation);
loadBundle(bundle);
List<Measure> measures = BundleUtil.toListOfResourcesOfType(myFhirContext, bundle, Measure.class);
if (measures == null || measures.isEmpty()) {
throw new IllegalArgumentException(String.format("No measures found for Bundle %s", theLocation));
}
List<MeasureReport> reports = BundleUtil.toListOfResourcesOfType(myFhirContext, bundle, MeasureReport.class);
if (reports == null || reports.isEmpty()) {
throw new IllegalArgumentException(String.format("No measure reports found for Bundle %s", theLocation));
}
for (MeasureReport report : reports) {
testMeasureReport(report);
}
}
protected void testMeasureReport(MeasureReport expected) {
String measureId = this.getMeasureId(expected);
String patientId = this.getPatientId(expected);
String periodStart = this.getPeriodStart(expected);
String periodEnd = this.getPeriodEnd(expected);
this.ourLog.info("Measure: %s, Patient: %s, Start: %s, End: %s", measureId, patientId, periodStart, periodEnd);
MeasureReport actual = this.myMeasureOperationsProvider.evaluateMeasure(new IdType("Measure", measureId),
periodStart, periodEnd, null,
// TODO: These are all individual reports
"patient", patientId,
// TODO: Generalize these parameters into a Parameters resource
null, null, null, null, null, null);
compareMeasureReport(expected, actual);
}
protected void compareMeasureReport(MeasureReport expected, MeasureReport actual) {
assertNotNull("expected MeasureReport can not be null", expected);
assertNotNull("actual MeasureReport can not be null", actual);
String errorLocator = String.format("Measure: %s, Subject: %s", expected.getMeasure(),
expected.getSubject().getReference());
assertEquals(expected.hasGroup(), actual.hasGroup(), errorLocator);
assertEquals(expected.getGroup().size(), actual.getGroup().size(), errorLocator);
for (MeasureReportGroupComponent mrgcExpected : expected.getGroup()) {
Optional<MeasureReportGroupComponent> mrgcActualOptional = actual.getGroup().stream()
.filter(x -> x.getId().equals(mrgcExpected.getId())).findFirst();
errorLocator = String.format("Measure: %s, Subject: %s, Group: %s", expected.getMeasure(),
expected.getSubject().getReference(), mrgcExpected.getId());
assertTrue(errorLocator, mrgcActualOptional.isPresent());
MeasureReportGroupComponent mrgcActual = mrgcActualOptional.get();
if (mrgcExpected.getMeasureScore() == null) {
assertNull(mrgcActual.getMeasureScore(), errorLocator);
} else {
assertNotNull(mrgcActual.getMeasureScore());
Quantity quantityExpected = mrgcExpected.getMeasureScore();
Quantity quantityActual = mrgcActual.getMeasureScore();
assertThat(errorLocator, quantityActual.getValue(), Matchers.comparesEqualTo(quantityExpected.getValue()));
}
}
}
// TODO: In R4 the Subject will not necessarily be a Patient.
public String getPatientId(MeasureReport measureReport) {
String[] subjectRefParts = measureReport.getSubject().getReference().split("/");
String patientId = subjectRefParts[subjectRefParts.length - 1];
return patientId;
}
public String getMeasureId(MeasureReport measureReport) {
String[] measureRefParts = measureReport.getMeasure().split("/");
String measureId = measureRefParts[measureRefParts.length - 1];
return measureId;
}
public String getPeriodStart(MeasureReport measureReport) {
Date periodStart = measureReport.getPeriod().getStart();
if (periodStart != null) {
return toDateString(periodStart);
}
return null;
}
public String getPeriodEnd(MeasureReport measureReport) {
Date periodEnd = measureReport.getPeriod().getEnd();
if (periodEnd != null) {
return toDateString(periodEnd);
}
return null;
}
public String toDateString(Date date) {
return new DateTimeType(date).getValueAsString();
}
// @Test - No test results in this bundle yet
// public void test_EXM74_102000() throws IOException {
// this.testMeasureBundle("r4/connectathon/EXM74-10.2.000-bundle.json");
// }
@Test
public void test_EXM105_82000() throws IOException {
this.testMeasureBundle("r4/connectathon/EXM105-8.2.000-bundle.json");
}
@Test
public void testExm130PatientNumerator() throws IOException {
loadBundles();
MeasureReport report = myMeasureOperationsProvider.evaluateMeasure(measureId, periodStart, periodEnd, null, "patient",
"numer-EXM130", null, null, null, null, null, null);
// Assert it worked
assertThat(report.getGroup(), hasSize(1));
assertEquals(new BigDecimal("1.0"), report.getGroupFirstRep().getMeasureScore().getValue());
public void test_EXM108_83000() throws IOException {
this.testMeasureBundle("r4/connectathon/EXM108-8.3.000-bundle.json");
}
// @Test - No test results in this bundle yet
// public void test_EXM111_91000() throws IOException {
// this.testMeasureBundle("r4/connectathon/EXM111-9.1.000-bundle.json");
// }
// @Test - The test data for the denominator exclusion appears to be invalid
// public void test_EXM124_82000() throws IOException {
// this.testMeasureBundle("r4/connectathon/EXM124-8.2.000-bundle.json");
// }
@Test
public void test_EXM124_90000() throws IOException {
this.testMeasureBundle("r4/connectathon/EXM124-9.0.000-bundle.json");
}
@Test
public void testExm130PatientDenominator() throws IOException {
loadBundles();
MeasureReport report = myMeasureOperationsProvider.evaluateMeasure(measureId, periodStart, periodEnd, null, "patient",
"denom-EXM130", null, null, null, null, null, null);
// Assert it worked
assertThat(report.getGroup(), hasSize(1));
assertEquals(new BigDecimal("0.0"), report.getGroupFirstRep().getMeasureScore().getValue());
public void test_EXM125_73000() throws IOException {
this.testMeasureBundle("r4/connectathon/EXM125-7.3.000-bundle.json");
}
@Test
public void test_EXM130_73000() throws IOException {
this.testMeasureBundle("r4/connectathon/EXM130-7.3.000-bundle.json");
}
// @Test - No test results in this bundle yet
// public void test_EXM149_92000() throws IOException {
// this.testMeasureBundle("r4/connectathon/EXM149-9.2.000-bundle.json");
// }
// @Test - Missing Adult outpatient encounters Library
// public void test_EXM153_92000() throws IOException {
// this.testMeasureBundle("r4/connectathon/EXM153-9.2.000-bundle.json");
// }
// @Test - Missing Encounter data for Numerator test
// public void test_EXM347_43000() throws IOException {
// this.testMeasureBundle("r4/connectathon/EXM347-4.3.000-bundle.json");
// }
// @Test - No test results in this bundle yet
// public void test_EXM349_210000() throws IOException {
// this.testMeasureBundle("r4/connectathon/EXM349-2.10.000-bundle.json");
// }
// @Test - No test results in this bundle yet
// public void test_EXM506_22000() throws IOException {
// this.testMeasureBundle("r4/connectathon/EXM506-2.2.000-bundle.json");
// }
// @Test - No test results in this bundle yet
// public void test_EXM529_10000() throws IOException {
// this.testMeasureBundle("r4/connectathon/EXM529-1.0.000-bundle.json");
// }
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long