001package ca.uhn.fhir.cql.r4.evaluation;
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.helper.DateHelper;
025import ca.uhn.fhir.cql.common.helper.UsingHelper;
026import ca.uhn.fhir.cql.common.provider.EvaluationProviderFactory;
027import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider;
028import ca.uhn.fhir.cql.r4.helper.LibraryHelper;
029import ca.uhn.fhir.rest.api.server.RequestDetails;
030import org.apache.commons.lang3.tuple.Triple;
031import org.cqframework.cql.elm.execution.Library;
032import org.hl7.fhir.r4.model.Measure;
033import org.opencds.cqf.cql.engine.data.DataProvider;
034import org.opencds.cqf.cql.engine.debug.DebugMap;
035import org.opencds.cqf.cql.engine.execution.Context;
036import org.opencds.cqf.cql.engine.execution.LibraryLoader;
037import org.opencds.cqf.cql.engine.runtime.DateTime;
038import org.opencds.cqf.cql.engine.runtime.Interval;
039import org.opencds.cqf.cql.engine.terminology.TerminologyProvider;
040
041import java.util.Date;
042import java.util.List;
043
044public class MeasureEvaluationSeed {
045        private Measure measure;
046        private Context context;
047        private Interval measurementPeriod;
048        private final LibraryLoader libraryLoader;
049        private final LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider;
050        private final EvaluationProviderFactory providerFactory;
051        private DataProvider dataProvider;
052        private final LibraryHelper libraryHelper;
053
054        public MeasureEvaluationSeed(EvaluationProviderFactory providerFactory, LibraryLoader libraryLoader,
055                                                                                  LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider, LibraryHelper libraryHelper) {
056                this.providerFactory = providerFactory;
057                this.libraryLoader = libraryLoader;
058                this.libraryResourceProvider = libraryResourceProvider;
059                this.libraryHelper = libraryHelper;
060        }
061
062        public Measure getMeasure() {
063                return this.measure;
064        }
065
066        public Context getContext() {
067                return this.context;
068        }
069
070        public Interval getMeasurementPeriod() {
071                return this.measurementPeriod;
072        }
073
074        public DataProvider getDataProvider() {
075                return this.dataProvider;
076        }
077
078        public void setup(Measure measure, String periodStart, String periodEnd, String productLine, String source,
079                                                        String user, String pass, RequestDetails theRequestDetails) {
080                this.measure = measure;
081
082                this.libraryHelper.loadLibraries(measure, this.libraryLoader, this.libraryResourceProvider, theRequestDetails);
083
084                // resolve primary library
085                Library library = this.libraryHelper.resolvePrimaryLibrary(measure, libraryLoader, this.libraryResourceProvider, theRequestDetails);
086
087
088                // resolve execution context
089                context = new Context(library);
090                context.registerLibraryLoader(libraryLoader);
091
092                List<Triple<String, String, String>> usingDefs = UsingHelper.getUsingUrlAndVersion(library.getUsings());
093
094                if (usingDefs.size() > 1) {
095                        throw new IllegalArgumentException(Msg.code(1671) + "Evaluation of Measure using multiple Models is not supported at this time.");
096                }
097
098                // If there are no Usings, there is probably not any place the Terminology
099                // actually used so I think the assumption that at least one provider exists is
100                // ok.
101                TerminologyProvider terminologyProvider = null;
102                if (usingDefs.size() > 0) {
103                        // Creates a terminology provider based on the first using statement. This
104                        // assumes the terminology
105                        // server matches the FHIR version of the CQL.
106                        terminologyProvider = this.providerFactory.createTerminologyProvider(usingDefs.get(0).getLeft(),
107                                        usingDefs.get(0).getMiddle(), source, user, pass);
108                        context.registerTerminologyProvider(terminologyProvider);
109                }
110
111                for (Triple<String, String, String> def : usingDefs) {
112                        this.dataProvider = this.providerFactory.createDataProvider(def.getLeft(), def.getMiddle(),
113                                terminologyProvider, theRequestDetails);
114                        context.registerDataProvider(def.getRight(), dataProvider);
115                }
116
117                if (periodStart != null && periodEnd != null) {
118                        // resolve the measurement period
119                        measurementPeriod = new Interval(DateHelper.resolveRequestDate("periodStart", periodStart), true,
120                                DateHelper.resolveRequestDate("periodEnd", periodEnd), true);
121
122                        context.setParameter(null, "Measurement Period",
123                                new Interval(DateTime.fromJavaDate((Date) measurementPeriod.getStart()), true,
124                                        DateTime.fromJavaDate((Date) measurementPeriod.getEnd()), true));
125                }
126
127                if (productLine != null) {
128                        context.setParameter(null, "Product Line", productLine);
129                }
130
131                context.setExpressionCaching(true);
132
133                // This needs to be made configurable
134                DebugMap debugMap = new DebugMap();
135                debugMap.setIsLoggingEnabled(true);
136                context.setDebugMap(debugMap);
137        }
138}