updating to latest hapi, fixing cr tests

This commit is contained in:
Justin McKelvy
2023-09-18 16:53:32 -06:00
parent 8566f7f8ed
commit 1bb6e9e610
8 changed files with 249 additions and 500 deletions

View File

@@ -6,14 +6,12 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import ca.uhn.fhir.cr.config.CrProperties;
import org.cqframework.cql.cql2elm.CqlCompilerException;
import org.cqframework.cql.cql2elm.CqlTranslator;
import org.cqframework.cql.cql2elm.CqlTranslatorOptions;
import org.cqframework.cql.cql2elm.LibraryBuilder;
import org.hl7.fhir.r4.model.Bundle;
import org.opencds.cqf.cql.evaluator.CqlOptions;
import org.opencds.cqf.cql.evaluator.engine.CqlEngineOptions;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@@ -30,7 +28,6 @@ import ca.uhn.fhir.rest.api.EncodingEnum;
@EnableConfigurationProperties
public class AppProperties {
//cql settings
private CqlEngineOptions cqlEngineOptions = CqlEngineOptions.defaultOptions();
private Boolean cql_use_embedded_libraries = true;
private Boolean cql_runtime_debug_logging_enabled = false;
private Boolean cql_runtime_enable_validation = false;
@@ -385,50 +382,6 @@ public class AppProperties {
this.cql_compiler_translator_format = cqlTranslatorFormat;
}
private CqlTranslatorOptions cqlTranslatorOptions = new CqlTranslatorOptions(
getCqlTranslatorFormat(),
cql_compiler_enable_date_range_optimization,
cql_compiler_enable_annotations,
cql_compiler_enable_locators,
cql_compiler_enable_results_type,
isCqlCompilerVerifyOnly(),
cql_compiler_enable_detailed_errors,
getCqlCompilerErrorSeverityLevel(),
cql_compiler_disable_list_traversal,
cql_compiler_disable_list_demotion,
cql_compiler_disable_list_promotion,
cql_compiler_enable_interval_demotion,
cql_compiler_enable_interval_promotion,
cql_compiler_disable_method_invocation,
cql_compiler_require_from_keyword,
isCqlCompilerValidateUnits(),
cql_compiler_disable_default_model_info_load,
getCqlCompilerSignatureLevel(),
getCqlCompilerCompatibilityLevel());
public CqlTranslatorOptions getCqlTranslatorOptions() {
return this.cqlTranslatorOptions;
}
public void setCqlTranslator(CqlTranslatorOptions translator) {
this.cqlTranslatorOptions = translator;
}
public CqlEngineOptions getCqlEngineOptions() {
return this.cqlEngineOptions;
}
public void setCqlEngineOptions(CqlEngineOptions engine) {
this.cqlEngineOptions = engine;
}
public CqlOptions getCqlOptions() {
CqlOptions cqlOptions = new CqlOptions();
cqlOptions.setUseEmbeddedLibraries(this.cql_use_embedded_libraries);
cqlOptions.setCqlEngineOptions(this.getCqlEngineOptions());
cqlOptions.setCqlTranslatorOptions(this.getCqlTranslatorOptions());
return cqlOptions;
}
public Boolean getCr_enabled() {
return cr_enabled;
@@ -448,16 +401,6 @@ public class AppProperties {
public void setCareGapsReporter(String theCareGapsReporter) {
this.caregaps_reporter = theCareGapsReporter;
}
public CrProperties.MeasureProperties getMeasureProperties(){
var measureProperties = new CrProperties.MeasureProperties();
var measureReportConfiguration = new CrProperties.MeasureProperties.MeasureReportConfiguration();
measureReportConfiguration.setCareGapsReporter(this.getCareGapsReporter());
measureReportConfiguration.setCareGapsCompositionSectionAuthor(this.getCareGapsSectionAuthor());
measureProperties.setMeasureReportConfiguration(measureReportConfiguration);
return measureProperties;
}
public Boolean getIps_enabled() {
return ips_enabled;
}

View File

@@ -1,103 +0,0 @@
package ca.uhn.fhir.jpa.starter.cr;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.cr.r4.measure.CareGapsOperationProvider;
import ca.uhn.fhir.cr.r4.measure.SubmitDataProvider;
import ca.uhn.fhir.cr.dstu3.activitydefinition.ActivityDefinitionOperationsProvider;
import ca.uhn.fhir.cr.dstu3.measure.MeasureOperationsProvider;
import ca.uhn.fhir.cr.dstu3.plandefinition.PlanDefinitionOperationsProvider;
import ca.uhn.fhir.cr.dstu3.questionnaire.QuestionnaireOperationsProvider;
import ca.uhn.fhir.cr.dstu3.questionnaireresponse.QuestionnaireResponseOperationsProvider;
public class CrOperationProviderFactory {
@Autowired
private FhirContext myFhirContext;
@Autowired
private ApplicationContext myApplicationContext;
public Object getMeasureOperationsProvider() {
switch (myFhirContext.getVersion().getVersion()) {
case DSTU3:
return myApplicationContext.getBean(MeasureOperationsProvider.class);
case R4:
return myApplicationContext.getBean(ca.uhn.fhir.cr.r4.measure.MeasureOperationsProvider.class);
default:
throw new ConfigurationException("Measure operations are not supported for FHIR version "
+ myFhirContext.getVersion().getVersion());
}
}
public Object getActivityDefinitionProvider() {
switch (myFhirContext.getVersion().getVersion()) {
case DSTU3:
return myApplicationContext.getBean(ActivityDefinitionOperationsProvider.class);
case R4:
return myApplicationContext
.getBean(ca.uhn.fhir.cr.r4.activitydefinition.ActivityDefinitionOperationsProvider.class);
default:
throw new ConfigurationException("ActivityDefinition operations are not supported for FHIR version "
+ myFhirContext.getVersion().getVersion());
}
}
public Object getPlanDefinitionProvider() {
switch (myFhirContext.getVersion().getVersion()) {
case DSTU3:
return myApplicationContext.getBean(PlanDefinitionOperationsProvider.class);
case R4:
return myApplicationContext
.getBean(ca.uhn.fhir.cr.r4.plandefinition.PlanDefinitionOperationsProvider.class);
default:
throw new ConfigurationException("PlanDefinition operations are not supported for FHIR version "
+ myFhirContext.getVersion().getVersion());
}
}
public Object getCareGapsProvider() {
switch (myFhirContext.getVersion().getVersion()) {
case R4:
return myApplicationContext.getBean(CareGapsOperationProvider.class);
default:
throw new ConfigurationException("PlanDefinition operations are not supported for FHIR version "
+ myFhirContext.getVersion().getVersion());
}
}
public Object getSubmitDataProvider() {
switch (myFhirContext.getVersion().getVersion()) {
case R4:
return myApplicationContext.getBean(SubmitDataProvider.class);
default:
throw new ConfigurationException("PlanDefinition operations are not supported for FHIR version "
+ myFhirContext.getVersion().getVersion());
}
}
public Object getQuestionnaireResponseOperationProvider() {
switch (myFhirContext.getVersion().getVersion()) {
case DSTU3:
return myApplicationContext.getBean(QuestionnaireResponseOperationsProvider.class);
case R4:
return myApplicationContext
.getBean(ca.uhn.fhir.cr.r4.questionnaireresponse.QuestionnaireResponseOperationsProvider.class);
default:
throw new ConfigurationException("PlanDefinition operations are not supported for FHIR version "
+ myFhirContext.getVersion().getVersion());
}
}
public Object getQuestionnaireOperationProvider() {
switch (myFhirContext.getVersion().getVersion()) {
case DSTU3:
return myApplicationContext.getBean(QuestionnaireOperationsProvider.class);
case R4:
return myApplicationContext
.getBean(ca.uhn.fhir.cr.r4.questionnaire.QuestionnaireOperationsProvider.class);
default:
throw new ConfigurationException("PlanDefinition operations are not supported for FHIR version "
+ myFhirContext.getVersion().getVersion());
}
}
}

View File

@@ -1,54 +0,0 @@
package ca.uhn.fhir.jpa.starter.cr;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.context.event.EventListener;
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
public class CrOperationProviderLoader {
private static final Logger myLogger = LoggerFactory.getLogger(CrOperationProviderLoader.class);
private final FhirContext myFhirContext;
private final ResourceProviderFactory myResourceProviderFactory;
private final CrOperationProviderFactory myCrProviderFactory;
private final PostInitProviderRegisterer myPostInitProviderRegister;
public CrOperationProviderLoader(FhirContext theFhirContext, ResourceProviderFactory theResourceProviderFactory,
CrOperationProviderFactory theCrProviderFactory, PostInitProviderRegisterer thePostInitProviderRegister) {
myFhirContext = theFhirContext;
myResourceProviderFactory = theResourceProviderFactory;
myCrProviderFactory = theCrProviderFactory;
myPostInitProviderRegister = thePostInitProviderRegister;
loadProvider();
}
public void loadProvider() {
switch (myFhirContext.getVersion().getVersion()) {
case DSTU3:
myLogger.info("Registering DSTU3 Clinical Reasoning Providers");
myResourceProviderFactory.addSupplier(myCrProviderFactory::getMeasureOperationsProvider);
myResourceProviderFactory.addSupplier(myCrProviderFactory::getActivityDefinitionProvider);
myResourceProviderFactory.addSupplier(myCrProviderFactory::getPlanDefinitionProvider);
myResourceProviderFactory.addSupplier(myCrProviderFactory::getQuestionnaireResponseOperationProvider);
myResourceProviderFactory.addSupplier(myCrProviderFactory::getQuestionnaireOperationProvider);
break;
case R4:
myLogger.info("Registering R4 Clinical Reasoning Providers");
myResourceProviderFactory.addSupplier(myCrProviderFactory::getMeasureOperationsProvider);
myResourceProviderFactory.addSupplier(myCrProviderFactory::getActivityDefinitionProvider);
myResourceProviderFactory.addSupplier(myCrProviderFactory::getPlanDefinitionProvider);
myResourceProviderFactory.addSupplier(myCrProviderFactory::getCareGapsProvider);
myResourceProviderFactory.addSupplier(myCrProviderFactory::getSubmitDataProvider);
myResourceProviderFactory.addSupplier(myCrProviderFactory::getQuestionnaireResponseOperationProvider);
myResourceProviderFactory.addSupplier(myCrProviderFactory::getQuestionnaireOperationProvider);
break;
default:
throw new ConfigurationException("Clinical Reasoning not supported for FHIR version "
+ myFhirContext.getVersion().getVersion());
}
}
}

View File

@@ -1,51 +0,0 @@
package ca.uhn.fhir.jpa.starter.cr;
import java.util.function.Supplier;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.provider.IResourceProviderFactoryObserver;
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
public class PostInitProviderRegisterer {
public PostInitProviderRegisterer(RestfulServer restfulServer,
ResourceProviderFactory resourceProviderFactory) {
resourceProviderFactory.attach(new Observer(restfulServer));
}
private class Observer implements IResourceProviderFactoryObserver {
private RestfulServer restfulServer;
public Observer(RestfulServer restfulServer) {
this.restfulServer = restfulServer;
}
public void update(Supplier<Object> theSupplier) {
if (theSupplier == null) {
return;
}
var provider = theSupplier.get();
if (provider == null) {
return;
}
this.restfulServer.registerProvider(provider);
}
public void remove(Supplier<Object> theSupplier) {
if (theSupplier == null) {
return;
}
var provider = theSupplier.get();
if (provider == null) {
return;
}
this.restfulServer.unregisterProvider(provider);
}
}
}

View File

@@ -1,123 +1,126 @@
package ca.uhn.fhir.jpa.starter.cr;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.cr.config.CrDstu3Config;
import ca.uhn.fhir.cr.config.CrProperties;
import ca.uhn.fhir.cr.dstu3.IActivityDefinitionProcessorFactory;
import ca.uhn.fhir.cr.dstu3.IPlanDefinitionProcessorFactory;
import ca.uhn.fhir.cr.dstu3.IQuestionnaireProcessorFactory;
import ca.uhn.fhir.cr.dstu3.IQuestionnaireResponseProcessorFactory;
import ca.uhn.fhir.cr.dstu3.activitydefinition.ActivityDefinitionOperationsProvider;
import ca.uhn.fhir.cr.dstu3.plandefinition.PlanDefinitionOperationsProvider;
import ca.uhn.fhir.cr.dstu3.questionnaire.QuestionnaireOperationsProvider;
import ca.uhn.fhir.cr.dstu3.questionnaireresponse.QuestionnaireResponseOperationsProvider;
import ca.uhn.fhir.cr.config.dstu3.CrDstu3Config;
import ca.uhn.fhir.jpa.starter.AppProperties;
import ca.uhn.fhir.jpa.starter.annotations.OnDSTU3Condition;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
import org.cqframework.cql.cql2elm.CqlTranslatorOptions;
import org.opencds.cqf.cql.evaluator.CqlOptions;
import org.opencds.cqf.cql.evaluator.activitydefinition.dstu3.ActivityDefinitionProcessor;
import org.opencds.cqf.cql.evaluator.library.EvaluationSettings;
import org.opencds.cqf.cql.evaluator.plandefinition.dstu3.PlanDefinitionProcessor;
import org.opencds.cqf.cql.evaluator.questionnaire.dstu3.QuestionnaireProcessor;
import org.opencds.cqf.cql.evaluator.questionnaireresponse.dstu3.QuestionnaireResponseProcessor;
import org.cqframework.cql.cql2elm.CqlCompilerOptions;
import org.cqframework.cql.cql2elm.model.CompiledLibrary;
import org.cqframework.cql.cql2elm.model.Model;
import org.hl7.cql.model.ModelIdentifier;
import org.hl7.elm.r1.VersionedIdentifier;
import org.opencds.cqf.cql.engine.execution.CqlEngine;
import org.opencds.cqf.fhir.cql.EvaluationSettings;
import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions;
import org.opencds.cqf.fhir.utility.ValidationProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.*;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
@Configuration
@Conditional({ OnDSTU3Condition.class, CrConfigCondition.class })
@Import({ CrDstu3Config.class })
public class StarterCrDstu3Config {
private static final Logger ourLogger = LoggerFactory.getLogger(StarterCrDstu3Config.class);
@Bean
public PostInitProviderRegisterer postInitProviderRegisterer(RestfulServer theRestfulServer,
ResourceProviderFactory theResourceProviderFactory) {
return new PostInitProviderRegisterer(theRestfulServer, theResourceProviderFactory);
}
@Bean
public CrOperationProviderFactory crOperationProviderFactory() {
return new CrOperationProviderFactory();
}
MeasureEvaluationOptions measureEvaluationOptions(EvaluationSettings theEvaluationSettings, Map<String, ValidationProfile> theValidationProfiles){
MeasureEvaluationOptions measureEvalOptions = new MeasureEvaluationOptions();
measureEvalOptions.setEvaluationSettings(theEvaluationSettings);
@Bean
public CrOperationProviderLoader crOperationProviderLoader(FhirContext theFhirContext,
ResourceProviderFactory theResourceProviderFactory,
CrOperationProviderFactory theCrOperationProviderFactory,
PostInitProviderRegisterer thePostInitProviderRegister) {
return new CrOperationProviderLoader(theFhirContext, theResourceProviderFactory, theCrOperationProviderFactory,
thePostInitProviderRegister);
}
@Bean
public QuestionnaireOperationsProvider myR4QuestionnaireOperationsProvider() {
return new QuestionnaireOperationsProvider();
}
@Bean
public PlanDefinitionOperationsProvider r4PlanDefinitionOperationsProvider() {
return new PlanDefinitionOperationsProvider();
}
@Bean
public QuestionnaireResponseOperationsProvider myR4QuestionnaireResponseOperationsProvider() {
return new QuestionnaireResponseOperationsProvider();
}
@Bean
public ActivityDefinitionOperationsProvider myR4ActivityDefinitionOperationsProvider() {
return new ActivityDefinitionOperationsProvider();
}
@Bean
IActivityDefinitionProcessorFactory myR4ActivityDefinitionProcessorFactory(
EvaluationSettings theEvaluationSettings) {
return r -> new ActivityDefinitionProcessor(r,
theEvaluationSettings);
}
@Bean
IQuestionnaireResponseProcessorFactory myR4QuestionnaireResponseProcessorFactory() {
return r -> new QuestionnaireResponseProcessor(r);
}
@Bean
IQuestionnaireProcessorFactory myR4QuestionnaireProcessorFactory() {
return r -> new QuestionnaireProcessor(r);
}
@Bean
IPlanDefinitionProcessorFactory myR4PlanDefinitionProcessorFactory(
EvaluationSettings theEvaluationSettings) {
return r -> new PlanDefinitionProcessor(r, theEvaluationSettings);
}
@Primary
@Bean
public CqlOptions cqlOptions(AppProperties theAppProperties) {
return theAppProperties.getCqlOptions();
}
@Primary
@Bean
public CrProperties.MeasureProperties measureProperties(AppProperties theAppProperties) {
return theAppProperties.getMeasureProperties();
}
@Primary
@Bean
public CqlTranslatorOptions cqlTranslatorOptions(FhirContext theFhirContext, AppProperties theAppProperties) {
CqlTranslatorOptions options = theAppProperties.getCqlTranslatorOptions();
if (theFhirContext.getVersion().getVersion().isOlderThan(FhirVersionEnum.R4)
&& (options.getCompatibilityLevel().equals("1.5") || options.getCompatibilityLevel().equals("1.4"))) {
ourLogger.warn("{} {} {}",
"This server is configured to use CQL version > 1.4 and FHIR version <= DSTU3.",
"Most available CQL content for DSTU3 and below is for CQL versions 1.3.",
"If your CQL content causes translation errors, try setting the CQL compatibility level to 1.3");
if(measureEvalOptions.isValidationEnabled()) {
measureEvalOptions.setValidationProfiles(theValidationProfiles);
}
return options;
return measureEvalOptions;
}
@Bean
public EvaluationSettings evaluationSettings(
AppProperties theAppProperties,
Map<VersionedIdentifier, CompiledLibrary> theGlobalLibraryCache,
Map<ModelIdentifier, Model> theGlobalModelCache) {
var evaluationSettings = EvaluationSettings.getDefault();
var cqlOptions = evaluationSettings.getCqlOptions();
var cqlEngineOptions = cqlOptions.getCqlEngineOptions();
Set<CqlEngine.Options> options = EnumSet.noneOf(CqlEngine.Options.class);
if (theAppProperties.isCqlRuntimeEnableExpressionCaching()) {
options.add(CqlEngine.Options.EnableExpressionCaching);
}
if (theAppProperties.isCqlRuntimeEnableValidation()) {
options.add(CqlEngine.Options.EnableValidation);
}
cqlEngineOptions.setOptions(options);
cqlOptions.setCqlEngineOptions(cqlEngineOptions);
var cqlCompilerOptions = new CqlCompilerOptions();
if (theAppProperties.isEnableDateRangeOptimization()
) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDateRangeOptimization);
}
if (theAppProperties.isEnableAnnotations()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableAnnotations);
}
if (theAppProperties.isEnableLocators()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableLocators);
}
if (theAppProperties.isEnableResultsType()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableResultTypes);
}
cqlCompilerOptions.setVerifyOnly(theAppProperties.isCqlCompilerVerifyOnly());
if (theAppProperties.isEnableDetailedErrors()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDetailedErrors);
}
cqlCompilerOptions.setErrorLevel(theAppProperties.getCqlCompilerErrorSeverityLevel());
if (theAppProperties.isDisableListTraversal()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListTraversal);
}
if (theAppProperties.isDisableListDemotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListDemotion);
}
if (theAppProperties.isDisableListPromotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListPromotion);
}
if (theAppProperties.isEnableIntervalDemotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalDemotion);
}
if (theAppProperties.isEnableIntervalPromotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalPromotion);
}
if (theAppProperties.isDisableMethodInvocation()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableMethodInvocation);
}
if (theAppProperties.isRequireFromKeyword()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.RequireFromKeyword);
}
cqlCompilerOptions.setValidateUnits(theAppProperties.isCqlCompilerValidateUnits());
if (theAppProperties.isDisableDefaultModelInfoLoad()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableDefaultModelInfoLoad);
}
cqlCompilerOptions.setSignatureLevel(theAppProperties.getCqlCompilerSignatureLevel());
cqlCompilerOptions.setCompatibilityLevel(theAppProperties.getCqlCompilerCompatibilityLevel());
cqlCompilerOptions.setAnalyzeDataRequirements(theAppProperties.isCqlCompilerAnalyzeDataRequirements());
cqlCompilerOptions.setCollapseDataRequirements(theAppProperties.isCqlCompilerCollapseDataRequirements());
cqlOptions.setCqlCompilerOptions(cqlCompilerOptions);
evaluationSettings.setLibraryCache(theGlobalLibraryCache);
evaluationSettings.setModelCache(theGlobalModelCache);
return evaluationSettings;
}
@Bean
public Map<VersionedIdentifier, CompiledLibrary> globalLibraryCache() {
return new ConcurrentHashMap<>();
}
@Bean
public Map<ModelIdentifier, Model> globalModelCache() {
return new ConcurrentHashMap<>();
}
}

View File

@@ -1,151 +1,148 @@
package ca.uhn.fhir.jpa.starter.cr;
import ca.uhn.fhir.IHapiBootOrder;
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.FhirVersionEnum;
import ca.uhn.fhir.cr.config.CrProperties;
import ca.uhn.fhir.cr.config.CrR4Config;
import ca.uhn.fhir.cr.r4.IActivityDefinitionProcessorFactory;
import ca.uhn.fhir.cr.r4.IPlanDefinitionProcessorFactory;
import ca.uhn.fhir.cr.r4.IQuestionnaireProcessorFactory;
import ca.uhn.fhir.cr.r4.IQuestionnaireResponseProcessorFactory;
import ca.uhn.fhir.cr.r4.activitydefinition.ActivityDefinitionOperationsProvider;
import ca.uhn.fhir.cr.r4.measure.CareGapsOperationProvider;
import ca.uhn.fhir.cr.r4.measure.CareGapsService;
import ca.uhn.fhir.cr.r4.plandefinition.PlanDefinitionOperationsProvider;
import ca.uhn.fhir.cr.r4.questionnaire.QuestionnaireOperationsProvider;
import ca.uhn.fhir.cr.r4.questionnaireresponse.QuestionnaireResponseOperationsProvider;
import ca.uhn.fhir.cr.common.CqlThreadFactory;
import ca.uhn.fhir.cr.config.ApplyOperationConfig;
import ca.uhn.fhir.cr.config.ExtractOperationConfig;
import ca.uhn.fhir.cr.config.PackageOperationConfig;
import ca.uhn.fhir.cr.config.PopulateOperationConfig;
import ca.uhn.fhir.cr.config.r4.CrR4Config;
import ca.uhn.fhir.jpa.starter.AppProperties;
import ca.uhn.fhir.jpa.starter.annotations.OnR4Condition;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.RestfulServer;
import ca.uhn.fhir.rest.server.provider.ResourceProviderFactory;
import org.apache.commons.lang3.StringUtils;
import org.cqframework.cql.cql2elm.CqlTranslatorOptions;
import org.opencds.cqf.cql.evaluator.CqlOptions;
import org.opencds.cqf.cql.evaluator.activitydefinition.r4.ActivityDefinitionProcessor;
import org.opencds.cqf.cql.evaluator.library.EvaluationSettings;
import org.opencds.cqf.cql.evaluator.plandefinition.r4.PlanDefinitionProcessor;
import org.opencds.cqf.cql.evaluator.questionnaire.r4.QuestionnaireProcessor;
import org.opencds.cqf.cql.evaluator.questionnaireresponse.r4.QuestionnaireResponseProcessor;
import org.cqframework.cql.cql2elm.CqlCompilerOptions;
import org.cqframework.cql.cql2elm.model.CompiledLibrary;
import org.cqframework.cql.cql2elm.model.Model;
import org.hl7.cql.model.ModelIdentifier;
import org.hl7.elm.r1.VersionedIdentifier;
import org.opencds.cqf.cql.engine.execution.CqlEngine;
import org.opencds.cqf.fhir.cql.EvaluationSettings;
import org.opencds.cqf.fhir.cr.measure.CareGapsProperties;
import org.opencds.cqf.fhir.cr.measure.MeasureEvaluationOptions;
import org.opencds.cqf.fhir.utility.ValidationProfile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.*;
import org.springframework.security.concurrent.DelegatingSecurityContextExecutorService;
import java.util.EnumSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.function.Function;
@Configuration
@Conditional({ OnR4Condition.class, CrConfigCondition.class })
@Import({ CrR4Config.class })
@Import({ CrR4Config.class,
ApplyOperationConfig.class,
ExtractOperationConfig.class,
PackageOperationConfig.class,
PopulateOperationConfig.class})
public class StarterCrR4Config {
private static final Logger ourLogger = LoggerFactory.getLogger(StarterCrR4Config.class);
@Bean
public PostInitProviderRegisterer postInitProviderRegisterer(RestfulServer theRestfulServer,
ResourceProviderFactory theResourceProviderFactory) {
return new PostInitProviderRegisterer(theRestfulServer, theResourceProviderFactory);
}
@Bean
public CrOperationProviderFactory crOperationProviderFactory() {
return new CrOperationProviderFactory();
}
@Bean
public CrOperationProviderLoader crOperationProviderLoader(FhirContext theFhirContext,
ResourceProviderFactory theResourceProviderFactory,
CrOperationProviderFactory theCrOperationProviderFactory,
PostInitProviderRegisterer thePostInitProviderRegister) {
return new CrOperationProviderLoader(theFhirContext, theResourceProviderFactory, theCrOperationProviderFactory,
thePostInitProviderRegister);
}
@Bean
public QuestionnaireOperationsProvider myR4QuestionnaireOperationsProvider() {
return new QuestionnaireOperationsProvider();
}
@Bean
public PlanDefinitionOperationsProvider r4PlanDefinitionOperationsProvider() {
return new PlanDefinitionOperationsProvider();
}
@Bean
public QuestionnaireResponseOperationsProvider myR4QuestionnaireResponseOperationsProvider() {
return new QuestionnaireResponseOperationsProvider();
}
@Bean
public ActivityDefinitionOperationsProvider myR4ActivityDefinitionOperationsProvider() {
return new ActivityDefinitionOperationsProvider();
}
@Bean
IActivityDefinitionProcessorFactory myR4ActivityDefinitionProcessorFactory(
EvaluationSettings theEvaluationSettings) {
return r -> new ActivityDefinitionProcessor(r,
theEvaluationSettings);
}
@Bean
IQuestionnaireResponseProcessorFactory myR4QuestionnaireResponseProcessorFactory() {
return r -> new QuestionnaireResponseProcessor(r);
}
@Bean
IQuestionnaireProcessorFactory myR4QuestionnaireProcessorFactory() {
return r -> new QuestionnaireProcessor(r);
}
@Bean
IPlanDefinitionProcessorFactory myR4PlanDefinitionProcessorFactory(
EvaluationSettings theEvaluationSettings) {
return r -> new PlanDefinitionProcessor(r, theEvaluationSettings);
}
@Primary
@Bean
public CqlOptions cqlOptions(AppProperties theAppProperties) {
return theAppProperties.getCqlOptions();
public ExecutorService cqlExecutor() {
CqlThreadFactory factory = new CqlThreadFactory();
ExecutorService executor = Executors.
newFixedThreadPool(2
, factory);
executor = new DelegatingSecurityContextExecutorService(executor);
return executor;
}
@Bean
CareGapsProperties careGapsProperties(AppProperties theAppProperties) {
var careGapsProperties = new CareGapsProperties();
careGapsProperties.setThreadedCareGapsEnabled(false);
careGapsProperties.setCareGapsReporter(theAppProperties.getCareGapsReporter());
careGapsProperties.setCareGapsCompositionSectionAuthor(theAppProperties.getCareGapsSectionAuthor());
return careGapsProperties;
}
@Primary
@Bean
public CrProperties.MeasureProperties measureProperties(AppProperties theAppProperties) {
return theAppProperties.getMeasureProperties();
}
@Primary
@Bean
CareGapsOperationProvider careGapsOperationProvider(Function<RequestDetails, CareGapsService> r4CareGapsServiceFactory, AppProperties theAppProperties) {
if(StringUtils.isBlank(theAppProperties.getCareGapsReporter()) || StringUtils.isBlank(theAppProperties.getCareGapsSectionAuthor())){
throw new RuntimeException("Configuration failed to register reporter or author properties for running care gaps functionality");
MeasureEvaluationOptions measureEvaluationOptions(EvaluationSettings theEvaluationSettings, Map<String, ValidationProfile> theValidationProfiles){
MeasureEvaluationOptions measureEvalOptions = new MeasureEvaluationOptions();
measureEvalOptions.setEvaluationSettings(theEvaluationSettings);
if(measureEvalOptions.isValidationEnabled()) {
measureEvalOptions.setValidationProfiles(theValidationProfiles);
}
Function<RequestDetails, CareGapsService> careGapsServiceFunction = r4CareGapsServiceFactory.andThen(careGapsService -> {
var measureReportConfiguration = new CrProperties.MeasureProperties.MeasureReportConfiguration();
measureReportConfiguration.setCareGapsReporter(theAppProperties.getCareGapsReporter());
measureReportConfiguration.setCareGapsCompositionSectionAuthor(theAppProperties.getCareGapsSectionAuthor());
careGapsService.getCrProperties().getMeasureProperties().setMeasureReportConfiguration(measureReportConfiguration);
return careGapsService;
});
CareGapsOperationProvider careGapsOperationProvider = new CareGapsOperationProvider(careGapsServiceFunction);
return careGapsOperationProvider;
return measureEvalOptions;
}
@Primary
@Bean
public CqlTranslatorOptions cqlTranslatorOptions(FhirContext theFhirContext, AppProperties theAppProperties) {
CqlTranslatorOptions options = theAppProperties.getCqlTranslatorOptions();
public EvaluationSettings evaluationSettings(
AppProperties theAppProperties,
Map<VersionedIdentifier, CompiledLibrary> theGlobalLibraryCache,
Map<ModelIdentifier, Model> theGlobalModelCache) {
var evaluationSettings = EvaluationSettings.getDefault();
var cqlOptions = evaluationSettings.getCqlOptions();
if (theFhirContext.getVersion().getVersion().isOlderThan(FhirVersionEnum.R4)
&& (options.getCompatibilityLevel().equals("1.5") || options.getCompatibilityLevel().equals("1.4"))) {
ourLogger.warn("{} {} {}",
"This server is configured to use CQL version > 1.4 and FHIR version <= DSTU3.",
"Most available CQL content for DSTU3 and below is for CQL versions 1.3.",
"If your CQL content causes translation errors, try setting the CQL compatibility level to 1.3");
var cqlEngineOptions = cqlOptions.getCqlEngineOptions();
Set<CqlEngine.Options> options = EnumSet.noneOf(CqlEngine.Options.class);
if (theAppProperties.isCqlRuntimeEnableExpressionCaching()) {
options.add(CqlEngine.Options.EnableExpressionCaching);
}
if (theAppProperties.isCqlRuntimeEnableValidation()) {
options.add(CqlEngine.Options.EnableValidation);
}
cqlEngineOptions.setOptions(options);
cqlOptions.setCqlEngineOptions(cqlEngineOptions);
return options;
var cqlCompilerOptions = new CqlCompilerOptions();
if (theAppProperties.isEnableDateRangeOptimization()
) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDateRangeOptimization);
}
if (theAppProperties.isEnableAnnotations()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableAnnotations);
}
if (theAppProperties.isEnableLocators()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableLocators);
}
if (theAppProperties.isEnableResultsType()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableResultTypes);
}
cqlCompilerOptions.setVerifyOnly(theAppProperties.isCqlCompilerVerifyOnly());
if (theAppProperties.isEnableDetailedErrors()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableDetailedErrors);
}
cqlCompilerOptions.setErrorLevel(theAppProperties.getCqlCompilerErrorSeverityLevel());
if (theAppProperties.isDisableListTraversal()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListTraversal);
}
if (theAppProperties.isDisableListDemotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListDemotion);
}
if (theAppProperties.isDisableListPromotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableListPromotion);
}
if (theAppProperties.isEnableIntervalDemotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalDemotion);
}
if (theAppProperties.isEnableIntervalPromotion()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.EnableIntervalPromotion);
}
if (theAppProperties.isDisableMethodInvocation()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableMethodInvocation);
}
if (theAppProperties.isRequireFromKeyword()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.RequireFromKeyword);
}
cqlCompilerOptions.setValidateUnits(theAppProperties.isCqlCompilerValidateUnits());
if (theAppProperties.isDisableDefaultModelInfoLoad()) {
cqlCompilerOptions.setOptions(CqlCompilerOptions.Options.DisableDefaultModelInfoLoad);
}
cqlCompilerOptions.setSignatureLevel(theAppProperties.getCqlCompilerSignatureLevel());
cqlCompilerOptions.setCompatibilityLevel(theAppProperties.getCqlCompilerCompatibilityLevel());
cqlCompilerOptions.setAnalyzeDataRequirements(theAppProperties.isCqlCompilerAnalyzeDataRequirements());
cqlCompilerOptions.setCollapseDataRequirements(theAppProperties.isCqlCompilerCollapseDataRequirements());
cqlOptions.setCqlCompilerOptions(cqlCompilerOptions);
evaluationSettings.setLibraryCache(theGlobalLibraryCache);
evaluationSettings.setModelCache(theGlobalModelCache);
return evaluationSettings;
}
}

View File

@@ -18,10 +18,8 @@ import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.web.server.LocalServerPort;
import org.springframework.context.ApplicationContext;
import org.springframework.test.context.junit.jupiter.SpringExtension;
@@ -33,10 +31,11 @@ import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import static ca.uhn.fhir.util.TestUtil.waitForSize;
import static java.lang.Thread.sleep;
import static org.awaitility.Awaitility.await;
import static org.junit.jupiter.api.Assertions.*;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.opencds.cqf.fhir.utility.r4.Parameters.parameters;
import static org.opencds.cqf.fhir.utility.r4.Parameters.stringPart;
@ExtendWith(SpringExtension.class)
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
@@ -113,7 +112,7 @@ class ExampleServerR4IT implements IServerSupport{
Parameters.ParametersParameterComponent component = response.get(0);
assertTrue(component.getResource() instanceof MeasureReport);
MeasureReport report = (MeasureReport) component.getResource();
assertEquals(measureUrl, report.getMeasure());
assertEquals(measureUrl + "|0.0.003", report.getMeasure());
}
private org.hl7.fhir.r4.model.Bundle loadBundle(String theLocation, FhirContext theCtx, IGenericClient theClient) throws IOException {
@@ -123,6 +122,21 @@ class ExampleServerR4IT implements IServerSupport{
return result;
}
public Parameters runCqlExecution(Parameters parameters){
var results = ourClient.operation().onServer()
.named("$cql")
.withParameters(parameters)
.execute();
return results;
}
@Test
void testSimpleDateCqlExecutionProvider() {
Parameters params = parameters(stringPart("expression", "Interval[Today() - 2 years, Today())"));
Parameters results = runCqlExecution(params);
assertTrue(results.getParameter("return").getValue() instanceof Period);
}
private IBaseResource loadRec(String theLocation, FhirContext theCtx, IGenericClient theClient) throws IOException {
String json = stringFromResource(theLocation);
List<IBaseResource> resList = new ArrayList<>();
@@ -260,10 +274,10 @@ class ExampleServerR4IT implements IServerSupport{
Parameters params = new Parameters();
params.addParameter().setName("periodStart").setValue(new DateType(periodStartValid));
params.addParameter().setName("subject").setValue(new DateType(periodEndValid));
params.addParameter().setName("status").setValue(new StringType(subjectPatientValid));
params.addParameter().setName("measureId").setValue(new StringType(statusValid));
params.addParameter().setName("").setValue(new StringType(measureIdValid));
params.addParameter().setName("periodEnd").setValue(new DateType(periodEndValid));
params.addParameter().setName("subject").setValue(new StringType(subjectPatientValid));
params.addParameter().setName("status").setValue(new StringType(statusValid));
params.addParameter().setName("measureId").setValue(new IdType(measureIdValid));
assertDoesNotThrow(() -> {