Skip to content

Commit

Permalink
Implemented parameter support for measure evaluation (#363)
Browse files Browse the repository at this point in the history
* Implemented parameter support for measure evaluation - added tests

* resolving merge conflicts

* formatting fixes

* Remove inlined content

---------

Co-authored-by: JP <jonathan.percival@smilecdr.com>
Co-authored-by: Jonathan Percival <jonathan.i.percival@gmail.com>
  • Loading branch information
3 people committed Nov 3, 2023
1 parent e6347e1 commit ef48689
Show file tree
Hide file tree
Showing 50 changed files with 11,305 additions and 10 deletions.
Original file line number Diff line number Diff line change
@@ -1,15 +1,22 @@
package org.opencds.cqf.fhir.cr.measure.dstu3;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.hl7.elm.r1.VersionedIdentifier;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.Library;
import org.hl7.fhir.dstu3.model.Measure;
import org.hl7.fhir.dstu3.model.MeasureReport;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.opencds.cqf.cql.engine.fhir.model.Dstu3FhirModelResolver;
import org.opencds.cqf.cql.engine.runtime.DateTime;
import org.opencds.cqf.cql.engine.runtime.Interval;
import org.opencds.cqf.fhir.api.Repository;
Expand Down Expand Up @@ -45,9 +52,11 @@ public MeasureReport evaluateMeasure(
String periodEnd,
String reportType,
List<String> subjectIds,
IBaseBundle additionalData) {
IBaseBundle additionalData,
Parameters parameters) {
var measure = this.repository.read(Measure.class, measureId);
return this.evaluateMeasure(measure, periodStart, periodEnd, reportType, subjectIds, additionalData);
return this.evaluateMeasure(
measure, periodStart, periodEnd, reportType, subjectIds, additionalData, parameters);
}

// NOTE: Do not make a top-level function that takes a Measure resource. This ensures that
Expand All @@ -58,7 +67,8 @@ protected MeasureReport evaluateMeasure(
String periodEnd,
String reportType,
List<String> subjectIds,
IBaseBundle additionalData) {
IBaseBundle additionalData,
Parameters parameters) {

if (!measure.hasLibrary()) {
throw new IllegalArgumentException(
Expand All @@ -84,6 +94,21 @@ protected MeasureReport evaluateMeasure(

context.getState().init(lib.getLibrary());

if (parameters != null) {
Map<String, Object> paramMap = resolveParameterMap(parameters);
context.getState().setParameters(lib.getLibrary(), paramMap);
// Set parameters for included libraries
// Note: this may not be the optimal method (e.g. libraries with the same parameter name, but different
// values)
if (lib.getLibrary().getIncludes() != null) {
lib.getLibrary()
.getIncludes()
.getDef()
.forEach(includeDef -> paramMap.forEach((paramKey, paramValue) -> context.getState()
.setParameter(includeDef.getLocalIdentifier(), paramKey, paramValue)));
}
}

var actualRepo = this.repository;
if (additionalData != null) {
actualRepo = new FederatedRepository(
Expand Down Expand Up @@ -127,4 +152,32 @@ private Interval buildMeasurementPeriod(String periodStart, String periodEnd) {
DateTime.fromJavaDate(DateHelper.resolveRequestDate(periodEnd, false)),
true);
}

private Map<String, Object> resolveParameterMap(Parameters parameters) {
Map<String, Object> parameterMap = new HashMap<>();
Dstu3FhirModelResolver modelResolver = new Dstu3FhirModelResolver();
parameters.getParameter().forEach(param -> {
Object value;
if (param.hasResource()) {
value = param.getResource();
} else {
value = param.getValue();
if (value instanceof IPrimitiveType) {
// TODO: handle Code, CodeableConcept, Quantity, etc
// resolves Date/Time values
value = modelResolver.toJavaPrimitive(((IPrimitiveType<?>) value).getValue(), value);
}
}
if (parameterMap.containsKey(param.getName())) {
if (parameterMap.get(param.getName()) instanceof List) {
CollectionUtils.addIgnoreNull((List<?>) parameterMap.get(param.getName()), value);
} else {
parameterMap.put(param.getName(), Arrays.asList(parameterMap.get(param.getName()), value));
}
} else {
parameterMap.put(param.getName(), value);
}
});
return parameterMap;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import org.hl7.fhir.dstu3.model.Extension;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.MeasureReport;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.SearchParameter;
import org.hl7.fhir.dstu3.model.StringType;
import org.opencds.cqf.fhir.api.Repository;
Expand Down Expand Up @@ -102,6 +103,7 @@ public MeasureReport evaluateMeasure(
String theLastReceivedOn,
String theProductLine,
Bundle theAdditionalData,
Parameters parameters,
Endpoint theTerminologyEndpoint) {

ensureSupplementalDataElementSearchParameter();
Expand All @@ -114,7 +116,8 @@ public MeasureReport evaluateMeasure(
thePeriodEnd,
theReportType,
Collections.singletonList(theSubject),
theAdditionalData);
theAdditionalData,
parameters);

if (theProductLine != null) {
Extension ext = new Extension();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,7 @@ private List<MeasureReport> getReports(
thePeriodEnd,
theReportType,
subjects,
null,
null);

if (!report.hasGroup()) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
package org.opencds.cqf.fhir.cr.measure.r4;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.CanonicalType;
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.Parameters;
import org.opencds.cqf.cql.engine.fhir.model.R4FhirModelResolver;
import org.opencds.cqf.cql.engine.runtime.DateTime;
import org.opencds.cqf.cql.engine.runtime.Interval;
import org.opencds.cqf.fhir.api.Repository;
Expand Down Expand Up @@ -50,11 +57,12 @@ public MeasureReport evaluateMeasure(
String periodEnd,
String reportType,
List<String> subjectIds,
IBaseBundle additionalData) {
IBaseBundle additionalData,
Parameters parameters) {

var evalType = MeasureEvalType.fromCode(reportType)
.orElse(
subjectIds.get(0) == null || subjectIds == null || subjectIds.isEmpty()
subjectIds == null || subjectIds.isEmpty() || subjectIds.get(0) == null
? MeasureEvalType.POPULATION
: MeasureEvalType.SUBJECT);

Expand All @@ -66,7 +74,8 @@ public MeasureReport evaluateMeasure(
var subjects =
subjectProvider.getSubjects(actualRepo, evalType, subjectIds).collect(Collectors.toList());

return this.evaluateMeasure(measure, periodStart, periodEnd, reportType, subjects, additionalData, evalType);
return this.evaluateMeasure(
measure, periodStart, periodEnd, reportType, subjects, additionalData, parameters, evalType);
}

public MeasureReport evaluateMeasure(
Expand All @@ -76,9 +85,11 @@ public MeasureReport evaluateMeasure(
String reportType,
List<String> subjectIds,
IBaseBundle additionalData,
Parameters parameters,
MeasureEvalType evalType) {
var m = measure.fold(this::resolveByUrl, this::resolveById, Function.identity());
return this.evaluateMeasure(m, periodStart, periodEnd, reportType, subjectIds, additionalData, evalType);
return this.evaluateMeasure(
m, periodStart, periodEnd, reportType, subjectIds, additionalData, parameters, evalType);
}

protected MeasureReport evaluateMeasure(
Expand All @@ -88,6 +99,7 @@ protected MeasureReport evaluateMeasure(
String reportType,
List<String> subjectIds,
IBaseBundle additionalData,
Parameters parameters,
MeasureEvalType evalType) {

if (!measure.hasLibrary()) {
Expand All @@ -108,6 +120,21 @@ protected MeasureReport evaluateMeasure(

context.getState().init(lib.getLibrary());

if (parameters != null) {
Map<String, Object> paramMap = resolveParameterMap(parameters);
context.getState().setParameters(lib.getLibrary(), paramMap);
// Set parameters for included libraries
// Note: this may not be the optimal method (e.g. libraries with the same parameter name, but different
// values)
if (lib.getLibrary().getIncludes() != null) {
lib.getLibrary()
.getIncludes()
.getDef()
.forEach(includeDef -> paramMap.forEach((paramKey, paramValue) -> context.getState()
.setParameter(includeDef.getLocalIdentifier(), paramKey, paramValue)));
}
}

if (evalType == null) {
evalType = MeasureEvalType.fromCode(reportType)
.orElse(
Expand Down Expand Up @@ -155,4 +182,32 @@ private Interval buildMeasurementPeriod(String periodStart, String periodEnd) {
DateTime.fromJavaDate(DateHelper.resolveRequestDate(periodEnd, false)),
true);
}

private Map<String, Object> resolveParameterMap(Parameters parameters) {
Map<String, Object> parameterMap = new HashMap<>();
R4FhirModelResolver modelResolver = new R4FhirModelResolver();
parameters.getParameter().forEach(param -> {
Object value;
if (param.hasResource()) {
value = param.getResource();
} else {
value = param.getValue();
if (value instanceof IPrimitiveType) {
// TODO: handle Code, CodeableConcept, Quantity, etc
// resolves Date/Time values
value = modelResolver.toJavaPrimitive(((IPrimitiveType<?>) value).getValue(), value);
}
}
if (parameterMap.containsKey(param.getName())) {
if (parameterMap.get(param.getName()) instanceof List) {
CollectionUtils.addIgnoreNull((List<?>) parameterMap.get(param.getName()), value);
} else {
parameterMap.put(param.getName(), Arrays.asList(parameterMap.get(param.getName()), value));
}
} else {
parameterMap.put(param.getName(), value);
}
});
return parameterMap;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
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.Parameters;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.SearchParameter;
import org.hl7.fhir.r4.model.StringType;
Expand Down Expand Up @@ -55,6 +56,7 @@ public MeasureReport evaluate(
Endpoint terminologyEndpoint,
Endpoint dataEndpoint,
Bundle additionalData,
Parameters parameters,
String productLine,
String practitioner) {

Expand All @@ -73,7 +75,13 @@ public MeasureReport evaluate(
}

measureReport = processor.evaluateMeasure(
measure, periodStart, periodEnd, reportType, Collections.singletonList(subjectId), additionalData);
measure,
periodStart,
periodEnd,
reportType,
Collections.singletonList(subjectId),
additionalData,
parameters);

// add ProductLine after report is generated
addProductLineExtension(measureReport, productLine);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import org.hl7.fhir.dstu3.model.MeasureReport.MeasureReportGroupComponent;
import org.hl7.fhir.dstu3.model.MeasureReport.MeasureReportGroupPopulationComponent;
import org.hl7.fhir.dstu3.model.MeasureReport.MeasureReportGroupStratifierComponent;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.Reference;
import org.hl7.fhir.dstu3.model.Resource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
Expand Down Expand Up @@ -97,6 +98,7 @@ public static class When {
private String reportType;

private Bundle additionalData;
private Parameters parameters;

private Supplier<MeasureReport> operation;

Expand Down Expand Up @@ -130,14 +132,20 @@ public When additionalData(Bundle additionalData) {
return this;
}

public When parameters(Parameters parameters) {
this.parameters = parameters;
return this;
}

public When evaluate() {
this.operation = () -> processor.evaluateMeasure(
new IdType("Measure", measureId),
periodStart,
periodEnd,
reportType,
Collections.singletonList(this.subjectId),
additionalData);
additionalData,
parameters);
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
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.Parameters;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.Resource;
import org.opencds.cqf.fhir.api.Repository;
Expand Down Expand Up @@ -122,6 +123,8 @@ public static class When {
private String subject;
private String reportType;
private Bundle additionalData;
private Parameters parameters;

private Supplier<MeasureReport> operation;
private String practitioner;
private String productLine;
Expand Down Expand Up @@ -161,6 +164,11 @@ public When additionalData(Bundle additionalData) {
return this;
}

public When parameters(Parameters parameters) {
this.parameters = parameters;
return this;
}

public When practitoner(String practitioner) {
this.practitioner = practitioner;
return this;
Expand All @@ -183,6 +191,7 @@ public When evaluate() {
null,
null,
additionalData,
parameters,
productLine,
practitioner);
return this;
Expand Down
Loading

0 comments on commit ef48689

Please sign in to comment.