001package ca.uhn.fhir.cql.r4.provider;
002
003/*-
004 * #%L
005 * HAPI FHIR JPA Server - Clinical Quality Language
006 * %%
007 * Copyright (C) 2014 - 2022 Smile CDR, Inc.
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import ca.uhn.fhir.i18n.Msg;
024import ca.uhn.fhir.cql.common.provider.EvaluationProviderFactory;
025import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider;
026import ca.uhn.fhir.cql.r4.evaluation.MeasureEvaluation;
027import ca.uhn.fhir.cql.r4.evaluation.MeasureEvaluationSeed;
028import ca.uhn.fhir.cql.r4.helper.LibraryHelper;
029import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
030import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
031import ca.uhn.fhir.rest.annotation.IdParam;
032import ca.uhn.fhir.rest.annotation.Operation;
033import ca.uhn.fhir.rest.annotation.OperationParam;
034import ca.uhn.fhir.rest.api.server.RequestDetails;
035import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
036import ca.uhn.fhir.rest.server.provider.ProviderConstants;
037import org.hl7.fhir.exceptions.FHIRException;
038import org.hl7.fhir.r4.model.Extension;
039import org.hl7.fhir.r4.model.IdType;
040import org.hl7.fhir.r4.model.Library;
041import org.hl7.fhir.r4.model.Measure;
042import org.hl7.fhir.r4.model.MeasureReport;
043import org.hl7.fhir.r4.model.StringType;
044import org.opencds.cqf.cql.engine.execution.LibraryLoader;
045import org.springframework.beans.factory.annotation.Autowired;
046import org.springframework.stereotype.Component;
047
048/**
049 * This class implements the r4 $evaluate-measure operation defined in the FHIR Clinical Reasoning module.
050 * Changes should comply with the specification in as far as is possible, and questions about Measure or CQL evaluation can be directed to the original authors.
051 * @author Jonathan Percival
052 * @author Bryn Rhodes
053 * @see <a href="https://hl7.org/fhir/measure-operation-evaluate-measure.html">https://hl7.org/fhir/measure-operation-evaluate-measure.html</a>
054 */
055@Component
056public class MeasureOperationsProvider {
057        @Autowired
058        private LibraryResolutionProvider<Library> libraryResolutionProvider;
059        @Autowired
060        private IFhirResourceDao<Measure> myMeasureDao;
061        @Autowired
062        private DaoRegistry registry;
063        @Autowired
064        private EvaluationProviderFactory factory;
065        @Autowired
066        private LibraryHelper libraryHelper;
067
068
069        /*
070         *
071         * NOTE that the source, user, and pass parameters are not standard parameters
072         * for the FHIR $evaluate-measure operation
073         *
074         */
075        @Operation(name = ProviderConstants.CQL_EVALUATE_MEASURE, idempotent = true, type = Measure.class)
076        public MeasureReport evaluateMeasure(@IdParam IdType theId,
077                                                                                                         @OperationParam(name = "periodStart") String periodStart,
078                                                                                                         @OperationParam(name = "periodEnd") String periodEnd,
079                                                                                                         @OperationParam(name = "measure") String measureRef,
080                                                                                                         @OperationParam(name = "reportType") String reportType,
081                                                                                                         @OperationParam(name = "patient") String patientRef,
082                                                                                                         @OperationParam(name = "productLine") String productLine,
083                                                                                                         @OperationParam(name = "practitioner") String practitionerRef,
084                                                                                                         @OperationParam(name = "lastReceivedOn") String lastReceivedOn,
085                                                                                                         @OperationParam(name = "source") String source,
086                                                                                                         @OperationParam(name = "user") String user,
087                                                                                                         @OperationParam(name = "pass") String pass,
088                                                                                                         RequestDetails theRequestDetails) throws InternalErrorException, FHIRException {
089                LibraryLoader libraryLoader = this.libraryHelper.createLibraryLoader(this.libraryResolutionProvider);
090
091                MeasureEvaluationSeed seed = new MeasureEvaluationSeed(this.factory, libraryLoader,
092                        this.libraryResolutionProvider, this.libraryHelper);
093                Measure measure = myMeasureDao.read(theId, theRequestDetails);
094
095                if (measure == null) {
096                        throw new RuntimeException(Msg.code(1663) + "Could not find Measure/" + theId.getIdPart());
097                }
098
099                seed.setup(measure, periodStart, periodEnd, productLine, source, user, pass, theRequestDetails);
100
101                // resolve report type
102                MeasureEvaluation evaluator = new MeasureEvaluation(seed.getDataProvider(), this.registry,
103                        seed.getMeasurementPeriod());
104                if (reportType != null) {
105                        switch (reportType) {
106                                case "patient":
107                                        return evaluator.evaluatePatientMeasure(seed.getMeasure(), seed.getContext(), patientRef, theRequestDetails);
108                                case "patient-list":
109                                        return evaluator.evaluateSubjectListMeasure(seed.getMeasure(), seed.getContext(), practitionerRef, theRequestDetails);
110                                case "population":
111                                        return evaluator.evaluatePopulationMeasure(seed.getMeasure(), seed.getContext(), theRequestDetails);
112                                default:
113                                        throw new IllegalArgumentException(Msg.code(1664) + "Invalid report type: " + reportType);
114                        }
115                }
116
117                // default report type is patient
118                MeasureReport report = evaluator.evaluatePatientMeasure(seed.getMeasure(), seed.getContext(), patientRef, theRequestDetails);
119                if (productLine != null) {
120                        Extension ext = new Extension();
121                        ext.setUrl("http://hl7.org/fhir/us/cqframework/cqfmeasures/StructureDefinition/cqfm-productLine");
122                        ext.setValue(new StringType(productLine));
123                        report.addExtension(ext);
124                }
125
126                return report;
127        }
128}