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

Reporter parameter for multi-measure processor #459

Merged
merged 7 commits into from
May 15, 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 @@ -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