Skip to content

Commit

Permalink
Expose overloaded API for evaluateMeasure
Browse files Browse the repository at this point in the history
- Implement evaluateMeasure API method in the FHIR Operator with Measure as parameter
  • Loading branch information
ndegwamartin committed Feb 12, 2024
1 parent e6439e3 commit 7097dfb
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.google.android.fhir.workflow;

import org.hl7.fhir.instance.model.api.IBaseBundle;
import org.hl7.fhir.r4.model.Measure;
import org.hl7.fhir.r4.model.MeasureReport;
import org.opencds.cqf.fhir.api.Repository;
import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions;
import org.opencds.cqf.fhir.cr.measure.common.MeasureEvalType;
import org.opencds.cqf.fhir.cr.measure.common.SubjectProvider;
import org.opencds.cqf.fhir.cr.measure.r4.R4MeasureProcessor;
import org.opencds.cqf.fhir.cr.measure.r4.R4RepositorySubjectProvider;
import org.opencds.cqf.fhir.utility.repository.FederatedRepository;
import org.opencds.cqf.fhir.utility.repository.InMemoryFhirRepository;

import java.lang.reflect.Field;
import java.util.List;
import java.util.stream.Collectors;

public class FhirEngineR4MeasureProcessor extends R4MeasureProcessor {
private static String FIELD_SUBJECT_PROVIDER="subjectProvider";
private Repository repository;

public FhirEngineR4MeasureProcessor(Repository repository, MeasureEvaluationOptions measureEvaluationOptions) {
super(repository, measureEvaluationOptions);
this.repository = repository;
}

public FhirEngineR4MeasureProcessor(Repository repository, MeasureEvaluationOptions measureEvaluationOptions, SubjectProvider subjectProvider) {
super(repository, measureEvaluationOptions, subjectProvider);
this.repository = repository;
}

@Override
public MeasureReport evaluateMeasure(Measure measure, String periodStart, String periodEnd, String reportType, List<String> subjectIds, IBaseBundle additionalData, MeasureEvalType evalType) {
var actualRepo = this.repository;
if (additionalData != null) {
actualRepo = new FederatedRepository(
this.repository, new InMemoryFhirRepository(this.repository.fhirContext(), additionalData));
}

SubjectProvider subjectProvider = getSubjectProvider();
var subjects = subjectProvider.getSubjects(actualRepo, evalType, subjectIds).collect(Collectors.toList());
return super.evaluateMeasure( measure, periodStart, periodEnd, reportType, subjects, additionalData, evalType) ;
}


/***
* We have two constructors that could result in different subject providers. So for this field we will use reflection
* @return [SubjectProvider] the SubjectProvider
*/
public SubjectProvider getSubjectProvider(){
SubjectProvider subjectProvider;
try {
Field field = this.getClass().getSuperclass().getDeclaredField(FIELD_SUBJECT_PROVIDER);
field.setAccessible(true);
subjectProvider = (SubjectProvider) field.get(this);
}catch (Exception e){
subjectProvider = null;
}
return subjectProvider;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,15 +31,16 @@ import org.hl7.fhir.instance.model.api.IBaseParameters
import org.hl7.fhir.instance.model.api.IBaseResource
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.hl7.fhir.r4.model.PlanDefinition
import org.hl7.fhir.r4.model.Reference
import org.opencds.cqf.fhir.cql.EvaluationSettings
import org.opencds.cqf.fhir.cql.LibraryEngine
import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions
import org.opencds.cqf.fhir.cr.measure.common.MeasureEvalType
import org.opencds.cqf.fhir.cr.measure.common.MeasureReportType
import org.opencds.cqf.fhir.cr.measure.r4.R4MeasureProcessor
import org.opencds.cqf.fhir.cr.plandefinition.r4.PlanDefinitionProcessor
import org.opencds.cqf.fhir.utility.monad.Eithers
import org.opencds.cqf.fhir.utility.repository.ProxyRepository
Expand Down Expand Up @@ -68,7 +69,8 @@ internal constructor(

private val libraryProcessor = LibraryEngine(repository, evaluationSettings)

private val measureProcessor = R4MeasureProcessor(repository, measureEvaluationOptions)
private val measureProcessor: FhirEngineR4MeasureProcessor =
FhirEngineR4MeasureProcessor(repository, measureEvaluationOptions)
private val planDefinitionProcessor = PlanDefinitionProcessor(repository, evaluationSettings)

/**
Expand Down Expand Up @@ -209,6 +211,55 @@ internal constructor(
return report
}

/**
* Generates a [MeasureReport] based on the provided inputs.
*
* NOTE: The API may internally result in a blocking IO operation. The user should call the API
* from a worker thread or it may throw [BlockingMainThreadException] exception.
*/
@WorkerThread
fun evaluateMeasure(
measure: Measure,
start: String,
end: String,
reportType: String,
subjectId: String? = null,
practitioner: String? = null,
additionalData: IBaseBundle? = null,
): MeasureReport {
val subject =
if (!practitioner.isNullOrBlank()) {
checkAndAddType(practitioner, "Practitioner")
} else if (!subjectId.isNullOrBlank()) {
checkAndAddType(subjectId, "Patient")
} else {
// List of null is required to run population-level measures
null
}
val evalType =
MeasureEvalType.fromCode(reportType)
.orElse(
if (!subjectId.isNullOrEmpty()) MeasureEvalType.SUBJECT else MeasureEvalType.POPULATION,
) as MeasureEvalType

val report =
measureProcessor.evaluateMeasure(
/* measure = */ measure,
/* periodStart = */ start,
/* periodEnd = */ end,
/* reportType = */ reportType,
/* subjectIds = */ if (subject.isNullOrEmpty()) listOf() else listOf(subject),
/* additionalData = */ additionalData,
/* evalType = */ evalType,
)

// add subject reference for non-individual reportTypes
if (report.type.name == MeasureReportType.SUMMARY.name && !subject.isNullOrBlank()) {
report.setSubject(Reference(subject))
}
return report
}

/**
* Generates a [CarePlan] based on the provided inputs.
*
Expand Down

0 comments on commit 7097dfb

Please sign in to comment.