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}