Skip to content

Commit

Permalink
Reporter parameter for multi-measure processor (#459)
Browse files Browse the repository at this point in the history
* reporter parameter for multi-measure service

* constants moved to avoid checkstyle error

* swap role and practitioner constants

* more coverage

* spotless fix

* remove dependency on care-gaps properties
  • Loading branch information
Capt-Mac authored May 15, 2024
1 parent c0f8cd4 commit 4850d91
Show file tree
Hide file tree
Showing 8 changed files with 253 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,9 @@ private MeasureReportConstants() {}
public static final String COUNTRY_CODING_SYSTEM_CODE = "urn:iso:std:iso:3166";
public static final String US_COUNTRY_CODE = "US";
public static final String US_COUNTRY_DISPLAY = "United States of America";

public static final String RESOURCE_TYPE_PRACTITIONER = "Practitioner";
public static final String RESOURCE_TYPE_PRACTITIONER_ROLE = "PractitionerRole";
public static final String RESOURCE_TYPE_ORGANIZATION = "Organization";
public static final String RESOURCE_TYPE_LOCATION = "Location";
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Resource;
import org.opencds.cqf.fhir.api.Repository;
import org.opencds.cqf.fhir.cr.measure.CareGapsProperties;
import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions;
import org.opencds.cqf.fhir.cr.measure.common.MeasureEvalType;
import org.opencds.cqf.fhir.cr.measure.r4.utils.R4MeasureServiceUtils;
Expand All @@ -34,15 +33,13 @@ public class R4MultiMeasureService {
private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(R4MultiMeasureService.class);
private final Repository repository;
private final MeasureEvaluationOptions measureEvaluationOptions;
private final CareGapsProperties careGapsProperties;
private String serverBase;

public R4MultiMeasureService(
Repository repository,
MeasureEvaluationOptions measureEvaluationOptions,
CareGapsProperties careGapsProperties) {
Repository repository, MeasureEvaluationOptions measureEvaluationOptions, String serverBase) {
this.repository = repository;
this.measureEvaluationOptions = measureEvaluationOptions;
this.careGapsProperties = careGapsProperties;
this.serverBase = serverBase;
}

public Bundle evaluate(
Expand All @@ -57,7 +54,8 @@ public Bundle evaluate(
Bundle additionalData,
Parameters parameters,
String productLine,
String practitioner) {
String practitioner,
String reporter) {

var repo = Repositories.proxy(repository, true, dataEndpoint, contentEndpoint, terminologyEndpoint);

Expand Down Expand Up @@ -95,11 +93,16 @@ public Bundle evaluate(

// add subject reference for non-individual reportTypes
measureReport = r4MeasureServiceUtils.addSubjectReference(measureReport, practitioner, subjectId);

// add reporter if available
if (reporter != null && !reporter.isEmpty()) {
measureReport.setReporter(r4MeasureServiceUtils.getReporter(reporter));
}
// add id to measureReport
initializeReport(measureReport);

// add report to bundle
bundle.addEntry(getBundleEntry(careGapsProperties.getMyFhirBaseUrl(), measureReport));
bundle.addEntry(getBundleEntry(serverBase, measureReport));

// progress feedback
var measureUrl = measureReport.getMeasure();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_DEFINITION_DATE;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_URL;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.MEASUREREPORT_SUPPLEMENTALDATA_SEARCHPARAMETER_VERSION;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.RESOURCE_TYPE_LOCATION;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.RESOURCE_TYPE_ORGANIZATION;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.RESOURCE_TYPE_PRACTITIONER;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.RESOURCE_TYPE_PRACTITIONER_ROLE;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.US_COUNTRY_CODE;
import static org.opencds.cqf.fhir.cr.measure.constant.MeasureReportConstants.US_COUNTRY_DISPLAY;

Expand Down Expand Up @@ -123,4 +127,27 @@ public MeasureReport addSubjectReference(MeasureReport measureReport, String pra
}
return measureReport;
}

public Reference getReporter(String reporter) {
if (!reporter.isEmpty() && !reporter.contains("/")) {
throw new IllegalArgumentException(
"R4MultiMeasureService requires '[ResourceType]/[ResourceId]' format to set MeasureReport.reporter reference.");
}
Reference reference = null;
if (!reporter.isEmpty()) {
if (reporter.startsWith(RESOURCE_TYPE_PRACTITIONER_ROLE)) {
reference = new Reference(Ids.ensureIdType(reporter, RESOURCE_TYPE_PRACTITIONER_ROLE));
} else if (reporter.startsWith(RESOURCE_TYPE_PRACTITIONER)) {
reference = new Reference(Ids.ensureIdType(reporter, RESOURCE_TYPE_PRACTITIONER));
} else if (reporter.startsWith(RESOURCE_TYPE_ORGANIZATION)) {
reference = new Reference(Ids.ensureIdType(reporter, RESOURCE_TYPE_ORGANIZATION));
} else if (reporter.startsWith(RESOURCE_TYPE_LOCATION)) {
reference = new Reference(Ids.ensureIdType(reporter, RESOURCE_TYPE_LOCATION));
} else {
throw new IllegalArgumentException("MeasureReport.reporter does not accept ResourceType: " + reporter);
}
}

return reference;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@
import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings.SEARCH_FILTER_MODE;
import org.opencds.cqf.fhir.cql.engine.retrieve.RetrieveSettings.TERMINOLOGY_FILTER_MODE;
import org.opencds.cqf.fhir.cql.engine.terminology.TerminologySettings.VALUESET_EXPANSION_MODE;
import org.opencds.cqf.fhir.cr.measure.CareGapsProperties;
import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions;
import org.opencds.cqf.fhir.cr.measure.common.MeasureConstants;
import org.opencds.cqf.fhir.utility.monad.Either3;
Expand Down Expand Up @@ -89,7 +88,7 @@ public static MultiMeasure.Given given() {
public static class Given {
private Repository repository;
private MeasureEvaluationOptions evaluationOptions;
private CareGapsProperties careGapsProperties;
private String serverBase;

public Given() {
this.evaluationOptions = MeasureEvaluationOptions.defaultOptions();
Expand All @@ -104,11 +103,7 @@ public Given() {
.getTerminologySettings()
.setValuesetExpansionMode(VALUESET_EXPANSION_MODE.PERFORM_NAIVE_EXPANSION);

this.careGapsProperties = new CareGapsProperties();

this.careGapsProperties.setCareGapsReporter("alphora");
this.careGapsProperties.setCareGapsCompositionSectionAuthor("alphora-author");
this.careGapsProperties.setMyFhirBaseUrl("http://localhost");
this.serverBase = "http://localhost";
}

public MultiMeasure.Given repository(Repository repository) {
Expand All @@ -128,8 +123,8 @@ public MultiMeasure.Given evaluationOptions(MeasureEvaluationOptions evaluationO
return this;
}

public MultiMeasure.Given careGapsProperties(CareGapsProperties careGapsProperties) {
this.careGapsProperties = careGapsProperties;
public MultiMeasure.Given serverBase(String serverBase) {
this.serverBase = serverBase;
return this;
}

Expand All @@ -138,7 +133,7 @@ private R4MeasureProcessor buildProcessor() {
}

private R4MultiMeasureService buildMeasureService() {
return new R4MultiMeasureService(repository, evaluationOptions, careGapsProperties);
return new R4MultiMeasureService(repository, evaluationOptions, serverBase);
}

public MultiMeasure.When when() {
Expand Down Expand Up @@ -167,6 +162,7 @@ public static class When {
private Supplier<Bundle> operation;
private String practitioner;
private String productLine;
private String reporter;

public MultiMeasure.When measureId(String measureId) {
Either3<CanonicalType, IdType, Measure> eitherMeasure =
Expand Down Expand Up @@ -220,6 +216,11 @@ public MultiMeasure.When productLine(String productLine) {
return this;
}

public MultiMeasure.When reporter(String reporter) {
this.reporter = reporter;
return this;
}

public MultiMeasure.When evaluate() {
this.operation = () -> service.evaluate(
measureIds,
Expand All @@ -233,7 +234,8 @@ public MultiMeasure.When evaluate() {
additionalData,
parameters,
productLine,
practitioner);
practitioner,
reporter);
return this;
}

Expand Down Expand Up @@ -392,6 +394,12 @@ public SelectedMeasureReport hasReportType(String reportType) {
assertEquals(reportType, ref.getDisplay());
return this;
}

public SelectedMeasureReport hasReporter(String reporter) {
var ref = this.report().getReporter().getReference();
assertEquals(reporter, ref);
return this;
}
}

static class SelectedGroup extends MultiMeasure.Selected<MeasureReportGroupComponent, SelectedMeasureReport> {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package org.opencds.cqf.fhir.cr.measure.r4;

import static org.junit.Assert.assertThrows;

import org.junit.jupiter.api.Test;
import org.opencds.cqf.fhir.cr.measure.r4.MultiMeasure.Given;

Expand Down Expand Up @@ -510,7 +512,7 @@ void MultiMeasure_EightMeasures_SubjectList() {
}

@Test
void MultiMeasure_EightMeasures_Practitioner() {
void MultiMeasure_EightMeasures_PractitionerJustId() {
var when = GIVEN_REPO
.when()
.measureId("MinimalProportionNoBasisSingleGroup")
Expand Down Expand Up @@ -544,4 +546,180 @@ void MultiMeasure_EightMeasures_Practitioner() {
.population("numerator")
.hasCount(0);
}

@Test
void MultiMeasure_EightMeasures_Practitioner() {
var when = GIVEN_REPO
.when()
.measureId("MinimalProportionNoBasisSingleGroup")
.periodStart("2024-01-01")
.periodEnd("2024-12-31")
.reportType("population")
.practitioner("Practitioner/tester")
.evaluate();

when.then()
.hasMeasureReportCount(1)
.measureReport("http://example.com/Measure/MinimalProportionNoBasisSingleGroup")
.hasReportType("Summary")
.hasSubjectReference("Practitioner/tester")
.firstGroup()
.population("initial-population")
.hasCount(1)
.up()
.population("denominator")
.hasCount(0)
.up()
.population("denominator-exclusion")
.hasCount(0)
.up()
.population("denominator-exception")
.hasCount(1)
.up()
.population("numerator-exclusion")
.hasCount(0)
.up()
.population("numerator")
.hasCount(0);
}

@Test
void MultiMeasure_EightMeasures_ReporterPractitioner() {
var when = GIVEN_REPO
.when()
.measureId("MinimalProportionNoBasisSingleGroup")
.periodStart("2024-01-01")
.periodEnd("2024-12-31")
.reportType("population")
.practitioner("tester")
.reporter("Practitioner/empty")
.evaluate();

when.then()
.hasMeasureReportCount(1)
.measureReport("http://example.com/Measure/MinimalProportionNoBasisSingleGroup")
.hasReportType("Summary")
.hasSubjectReference("Practitioner/tester")
.hasReporter("Practitioner/empty");
}

@Test
void MultiMeasure_EightMeasures_ReporterPractitionerRole() {
var when = GIVEN_REPO
.when()
.measureId("MinimalProportionNoBasisSingleGroup")
.periodStart("2024-01-01")
.periodEnd("2024-12-31")
.reportType("population")
.practitioner("tester")
.reporter("PractitionerRole/test")
.evaluate();

when.then()
.hasMeasureReportCount(1)
.measureReport("http://example.com/Measure/MinimalProportionNoBasisSingleGroup")
.hasReportType("Summary")
.hasSubjectReference("Practitioner/tester")
.hasReporter("PractitionerRole/test");
}

@Test
void MultiMeasure_EightMeasures_ReporterLocation() {
var when = GIVEN_REPO
.when()
.measureId("MinimalProportionNoBasisSingleGroup")
.periodStart("2024-01-01")
.periodEnd("2024-12-31")
.reportType("population")
.practitioner("tester")
.reporter("Location/office")
.evaluate();

when.then()
.hasMeasureReportCount(1)
.measureReport("http://example.com/Measure/MinimalProportionNoBasisSingleGroup")
.hasReportType("Summary")
.hasSubjectReference("Practitioner/tester")
.hasReporter("Location/office");
}

@Test
void MultiMeasure_EightMeasures_ReporterOrganization() {
var when = GIVEN_REPO
.when()
.measureId("MinimalProportionNoBasisSingleGroup")
.periodStart("2024-01-01")
.periodEnd("2024-12-31")
.reportType("population")
.practitioner("tester")
.reporter("Organization/payer")
.evaluate();

when.then()
.hasMeasureReportCount(1)
.measureReport("http://example.com/Measure/MinimalProportionNoBasisSingleGroup")
.hasReportType("Summary")
.hasSubjectReference("Practitioner/tester")
.hasReporter("Organization/payer");
}

@Test
void MultiMeasure_EightMeasures_ReporterJustId() {
var when = GIVEN_REPO
.when()
.measureId("MinimalProportionNoBasisSingleGroup")
.periodStart("2024-01-01")
.periodEnd("2024-12-31")
.reportType("population")
.practitioner("tester")
.reporter("payer")
.evaluate();

assertThrows(IllegalArgumentException.class, () -> when.then()
.hasMeasureReportCount(1)
.measureReport("http://example.com/Measure/MinimalProportionNoBasisSingleGroup")
.hasReportType("Summary")
.hasSubjectReference("Practitioner/tester")
.hasReporter("Organization/payer"));
}

@Test
void MultiMeasure_EightMeasures_ReporterNull() {
var when = GIVEN_REPO
.when()
.measureId("MinimalProportionNoBasisSingleGroup")
.periodStart("2024-01-01")
.periodEnd("2024-12-31")
.reportType("population")
.practitioner("tester")
.reporter(null)
.evaluate();

when.then()
.hasMeasureReportCount(1)
.measureReport("http://example.com/Measure/MinimalProportionNoBasisSingleGroup")
.hasReportType("Summary")
.hasSubjectReference("Practitioner/tester")
.hasReporter(null);
}

@Test
void MultiMeasure_EightMeasures_ReporterNotAcceptedResource() {
var when = GIVEN_REPO
.when()
.measureId("MinimalProportionNoBasisSingleGroup")
.periodStart("2024-01-01")
.periodEnd("2024-12-31")
.reportType("population")
.practitioner("tester")
.reporter("Patient/male-2022")
.evaluate();

assertThrows(IllegalArgumentException.class, () -> when.then()
.hasMeasureReportCount(1)
.measureReport("http://example.com/Measure/MinimalProportionNoBasisSingleGroup")
.hasReportType("Summary")
.hasSubjectReference("Practitioner/tester")
.hasReporter("Patient/male-2022"));
}
}
Loading

0 comments on commit 4850d91

Please sign in to comment.