001package ca.uhn.fhir.cql.dstu3.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.dstu3.evaluation.MeasureEvaluation;
027import ca.uhn.fhir.cql.dstu3.evaluation.MeasureEvaluationSeed;
028import ca.uhn.fhir.cql.dstu3.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.dstu3.model.Extension;
038import org.hl7.fhir.dstu3.model.IdType;
039import org.hl7.fhir.dstu3.model.Library;
040import org.hl7.fhir.dstu3.model.Measure;
041import org.hl7.fhir.dstu3.model.MeasureReport;
042import org.hl7.fhir.dstu3.model.StringType;
043import org.hl7.fhir.exceptions.FHIRException;
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 dstu3 $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/STU3/measure-operations.html#evaluate-measure">https://hl7.org/fhir/STU3/measure-operations.html#evaluate-measure</a>
054 */
055@Component
056public class MeasureOperationsProvider {
057        @Autowired
058        private LibraryResolutionProvider<Library> libraryResolutionProvider;
059        @Autowired
060        private DaoRegistry registry;
061        @Autowired
062        private IFhirResourceDao<Measure> myMeasureDao;
063        @Autowired
064        private EvaluationProviderFactory factory;
065        @Autowired
066        private LibraryHelper libraryHelper;
067
068        /*
069         *
070         * NOTE that the source, user, and pass parameters are not standard parameters
071         * for the FHIR $evaluate-measure operation
072         *
073         */
074        @Operation(name = ProviderConstants.CQL_EVALUATE_MEASURE, idempotent = true, type = Measure.class)
075        public MeasureReport evaluateMeasure(@IdParam IdType theId,
076                                                                                                         @OperationParam(name = "periodStart") String periodStart,
077                                                                                                         @OperationParam(name = "periodEnd") String periodEnd,
078                                                                                                         @OperationParam(name = "measure") String measureRef,
079                                                                                                         @OperationParam(name = "reportType") String reportType,
080                                                                                                         @OperationParam(name = "patient") String patientRef,
081                                                                                                         @OperationParam(name = "productLine") String productLine,
082                                                                                                         @OperationParam(name = "practitioner") String practitionerRef,
083                                                                                                         @OperationParam(name = "lastReceivedOn") String lastReceivedOn,
084                                                                                                         @OperationParam(name = "source") String source,
085                                                                                                         @OperationParam(name = "user") String user,
086                                                                                                         @OperationParam(name = "pass") String pass,
087                                                                                                         RequestDetails theRequestDetails) throws InternalErrorException, FHIRException {
088                LibraryLoader libraryLoader = this.libraryHelper.createLibraryLoader(this.libraryResolutionProvider);
089                MeasureEvaluationSeed seed = new MeasureEvaluationSeed(this.factory, libraryLoader,
090                        this.libraryResolutionProvider, this.libraryHelper);
091                Measure measure = myMeasureDao.read(theId, theRequestDetails);
092
093                if (measure == null) {
094                        throw new RuntimeException(Msg.code(1639) + "Could not find Measure/" + theId.getIdPart());
095                }
096
097                seed.setup(measure, periodStart, periodEnd, productLine, source, user, pass, theRequestDetails);
098
099                // resolve report type
100                MeasureEvaluation evaluator = new MeasureEvaluation(this.registry,
101                        seed.getMeasurementPeriod());
102                if (reportType != null) {
103                        switch (reportType) {
104                                case "patient":
105                                        return evaluator.evaluatePatientMeasure(seed.getMeasure(), seed.getContext(), patientRef, theRequestDetails);
106                                case "patient-list":
107                                        return evaluator.evaluatePatientListMeasure(seed.getMeasure(), seed.getContext(), practitionerRef, theRequestDetails);
108                                case "population":
109                                        return evaluator.evaluatePopulationMeasure(seed.getMeasure(), seed.getContext(), theRequestDetails);
110                                default:
111                                        throw new IllegalArgumentException(Msg.code(1640) + "Invalid report type: " + reportType);
112                        }
113                }
114
115                // default report type is patient
116                MeasureReport report = evaluator.evaluatePatientMeasure(seed.getMeasure(), seed.getContext(), patientRef, theRequestDetails);
117                if (productLine != null) {
118                        Extension ext = new Extension();
119                        ext.setUrl("http://hl7.org/fhir/us/cqframework/cqfmeasures/StructureDefinition/cqfm-productLine");
120                        ext.setValue(new StringType(productLine));
121                        report.addExtension(ext);
122                }
123
124                return report;
125        }
126}