CQL hardening and Dstu3 unit tests (#2450)
* WIP for elm caching * Bug fixes and a working DSTU3 unit test * More tests, Address review feedback, add initial docs * Update to cql-evaluator 1.2.0 * Fixes for documentation
This commit is contained in:
parent
fc1cef24ae
commit
239d2496ea
|
@ -70,6 +70,9 @@ page.server_jpa_mdm.mdm_operations=MDM Operations
|
|||
page.server_jpa_mdm.mdm_details=MDM Technical Details
|
||||
page.server_jpa_mdm.mdm_expansion=MDM Search Expansion
|
||||
|
||||
section.server_jpa_cql.title=JPA Server: CQL
|
||||
page.server_jpa_cql.cql=CQL Getting Started
|
||||
|
||||
section.server_jpa_partitioning.title=JPA Server: Partitioning and Multitenancy
|
||||
page.server_jpa_partitioning.partitioning=Partitioning and Multitenancy
|
||||
page.server_jpa_partitioning.partition_interceptor_examples=Partition Interceptor Examples
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
# CQL Getting Started
|
||||
|
||||
## Introduction
|
||||
|
||||
Clinical Quality Language (CQL) is a high-level, domain-specific language focused on clinical quality and targeted at measure and decision support artifact authors. HAPI embeds a [CQL engine](https://github.com/DBCG/cql_engine) allowing the evaluation of clinical knowledge artifacts that use CQL to describe their logic.
|
||||
|
||||
A more detailed description of CQL is available at the [CQL Specification Implementation Guide](https://cql.hl7.org/)
|
||||
|
||||
The FHIR [Clinical Reasoning module](http://www.hl7.org/fhir/clinicalreasoning-module.html) defines a set of resources, profiles, operations, etc. that can be used to work with clinical knowledge within FHIR. HAPI provides implementation for some of those operations, described in more detail below.
|
||||
|
||||
## Working Example
|
||||
|
||||
A complete working example of HAPI CQL can be found in the [JPA Server Starter](/hapi-fhir/docs/server_jpa/get_started.html) project. You may wish to browse its source to see how it is set up.
|
||||
|
||||
## Overview
|
||||
|
||||
To get up and running with HAPI CQL, you can enable it using the `hapi.properties` file in the JPA Server Starter by setting `hapi.fhir.enable_cql` key to `true`. If you are running your own server follow the instructions below to [enable it in HAPI FHIR directly](#cql-settings).
|
||||
|
||||
Once you've enabled CQL processing, the next step is to load the appropriate knowledge artifact resources into your server.
|
||||
|
||||
## CQL Settings
|
||||
|
||||
There are two Spring beans available that add CQL processing to HAPI. You can enable CQL processing by importing the appropriate version for your server configuration.
|
||||
|
||||
* `ca.uhn.fhir.cql.config.CqlDstu3Config`
|
||||
* `ca.uhn.fhir.cql.config.CqlR4Config`
|
||||
|
||||
## Operations
|
||||
|
||||
HAPI provides implementations for some Measure operations for DSTU3 and R4
|
||||
|
||||
### $evaluate-measure
|
||||
|
||||
The [$evaluate-measure](http://hl7.org/fhir/measure-operation-evaluate-measure.html) operation allows the evaluation of a clinical quality measure. This operation is invoked on an instance of a Measure resource:
|
||||
|
||||
`http://base/Measure/measureId/$evaluate-measure?subject=124&periodStart=2014-01&periodend=2014-03`
|
||||
|
||||
The Measure will be evaluated, including any CQL that is referenced. The CQL evaluation requires that all the supporting knowledge artifacts for a given Measure be loaded on the HAPI server, including `Libaries` and `ValueSets`.
|
|
@ -22,14 +22,15 @@ package ca.uhn.fhir.cql.common.provider;
|
|||
|
||||
import org.cqframework.cql.cql2elm.FhirLibrarySourceProvider;
|
||||
import org.hl7.elm.r1.VersionedIdentifier;
|
||||
import org.opencds.cqf.cql.evaluator.cql2elm.content.LibraryContentType;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class LibrarySourceProvider<LibraryType, AttachmentType>
|
||||
implements org.cqframework.cql.cql2elm.LibrarySourceProvider {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LibrarySourceProvider.class);
|
||||
public class LibraryContentProvider<LibraryType, AttachmentType>
|
||||
implements org.opencds.cqf.cql.evaluator.cql2elm.content.LibraryContentProvider {
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(LibraryContentProvider.class);
|
||||
|
||||
private FhirLibrarySourceProvider innerProvider;
|
||||
private LibraryResolutionProvider<LibraryType> provider;
|
||||
|
@ -37,7 +38,7 @@ public class LibrarySourceProvider<LibraryType, AttachmentType>
|
|||
private Function<AttachmentType, String> getContentType;
|
||||
private Function<AttachmentType, byte[]> getContent;
|
||||
|
||||
public LibrarySourceProvider(LibraryResolutionProvider<LibraryType> provider,
|
||||
public LibraryContentProvider(LibraryResolutionProvider<LibraryType> provider,
|
||||
Function<LibraryType, Iterable<AttachmentType>> getAttachments,
|
||||
Function<AttachmentType, String> getContentType, Function<AttachmentType, byte[]> getContent) {
|
||||
|
||||
|
@ -50,7 +51,13 @@ public class LibrarySourceProvider<LibraryType, AttachmentType>
|
|||
}
|
||||
|
||||
@Override
|
||||
public InputStream getLibrarySource(VersionedIdentifier versionedIdentifier) {
|
||||
public InputStream getLibraryContent(VersionedIdentifier versionedIdentifier, LibraryContentType libraryContentType){
|
||||
|
||||
// TODO: Support loading ELM
|
||||
if (libraryContentType != LibraryContentType.CQL) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
LibraryType lib = this.provider.resolveLibraryByName(versionedIdentifier.getId(),
|
||||
versionedIdentifier.getVersion());
|
|
@ -27,6 +27,7 @@ import java.util.Map;
|
|||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.cqframework.cql.cql2elm.model.Model;
|
||||
import org.cqframework.cql.elm.execution.Library;
|
||||
import org.hl7.elm.r1.VersionedIdentifier;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
|
||||
|
@ -46,4 +47,9 @@ public abstract class BaseCqlConfig {
|
|||
Map<VersionedIdentifier, Model> globalModelCache() {
|
||||
return new ConcurrentHashMap<VersionedIdentifier, Model>();
|
||||
}
|
||||
|
||||
@Bean(name="globalLibraryCache")
|
||||
Map<org.cqframework.cql.elm.execution.VersionedIdentifier, Library> globalLibraryCache() {
|
||||
return new ConcurrentHashMap<org.cqframework.cql.elm.execution.VersionedIdentifier, Library>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,12 +26,20 @@ import ca.uhn.fhir.cql.common.provider.EvaluationProviderFactory;
|
|||
import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider;
|
||||
import ca.uhn.fhir.cql.dstu3.evaluation.ProviderFactory;
|
||||
import ca.uhn.fhir.cql.dstu3.helper.LibraryHelper;
|
||||
import ca.uhn.fhir.cql.dstu3.listener.ElmCacheResourceChangeListener;
|
||||
import ca.uhn.fhir.cql.dstu3.provider.JpaTerminologyProvider;
|
||||
import ca.uhn.fhir.cql.dstu3.provider.LibraryResolutionProviderImpl;
|
||||
import ca.uhn.fhir.cql.dstu3.provider.MeasureOperationsProvider;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvcDstu3;
|
||||
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
|
||||
import org.cqframework.cql.cql2elm.CqlTranslatorOptions;
|
||||
import org.cqframework.cql.cql2elm.model.Model;
|
||||
import org.cqframework.cql.elm.execution.Library;
|
||||
import org.hl7.elm.r1.VersionedIdentifier;
|
||||
import org.opencds.cqf.cql.engine.fhir.model.Dstu3FhirModelResolver;
|
||||
import org.opencds.cqf.cql.engine.model.ModelResolver;
|
||||
|
@ -60,7 +68,7 @@ public class CqlDstu3Config extends BaseCqlConfig {
|
|||
|
||||
@Lazy
|
||||
@Bean
|
||||
LibraryResolutionProvider libraryResolutionProvider() {
|
||||
LibraryResolutionProvider<org.hl7.fhir.dstu3.model.Library> libraryResolutionProvider() {
|
||||
return new LibraryResolutionProviderImpl();
|
||||
}
|
||||
|
||||
|
@ -70,13 +78,28 @@ public class CqlDstu3Config extends BaseCqlConfig {
|
|||
return new MeasureOperationsProvider();
|
||||
}
|
||||
|
||||
@Lazy
|
||||
@Bean
|
||||
public ModelResolver fhirModelResolver() {
|
||||
return new CachingModelResolverDecorator(new Dstu3FhirModelResolver());
|
||||
}
|
||||
|
||||
@Lazy
|
||||
@Bean
|
||||
public LibraryHelper libraryHelper(Map<VersionedIdentifier, Model> globalModelCache) {
|
||||
return new LibraryHelper(globalModelCache);
|
||||
public LibraryHelper libraryHelper(Map<VersionedIdentifier, Model> globalModelCache, Map<org.cqframework.cql.elm.execution.VersionedIdentifier, Library> globalLibraryCache, CqlTranslatorOptions cqlTranslatorOptions) {
|
||||
return new LibraryHelper(globalModelCache, globalLibraryCache, cqlTranslatorOptions);
|
||||
}
|
||||
|
||||
|
||||
@Bean
|
||||
public CqlTranslatorOptions cqlTranslatorOptions() {
|
||||
return CqlTranslatorOptions.defaultOptions().withCompatibilityLevel("1.3");
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ElmCacheResourceChangeListener elmCacheResourceChangeListener(IResourceChangeListenerRegistry resourceChangeListenerRegistry, IFhirResourceDao<org.hl7.fhir.dstu3.model.Library> libraryDao, Map<org.cqframework.cql.elm.execution.VersionedIdentifier, Library> globalLibraryCache) {
|
||||
ElmCacheResourceChangeListener listener = new ElmCacheResourceChangeListener(libraryDao, globalLibraryCache);
|
||||
resourceChangeListenerRegistry.registerResourceResourceChangeListener("Library", new SearchParameterMap(), listener, 1000);
|
||||
return listener;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,12 +27,20 @@ import ca.uhn.fhir.cql.common.provider.EvaluationProviderFactory;
|
|||
import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider;
|
||||
import ca.uhn.fhir.cql.r4.evaluation.ProviderFactory;
|
||||
import ca.uhn.fhir.cql.r4.helper.LibraryHelper;
|
||||
import ca.uhn.fhir.cql.r4.listener.ElmCacheResourceChangeListener;
|
||||
import ca.uhn.fhir.cql.r4.provider.JpaTerminologyProvider;
|
||||
import ca.uhn.fhir.cql.r4.provider.LibraryResolutionProviderImpl;
|
||||
import ca.uhn.fhir.cql.r4.provider.MeasureOperationsProvider;
|
||||
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.cache.IResourceChangeListenerRegistry;
|
||||
import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4;
|
||||
|
||||
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
|
||||
|
||||
import org.cqframework.cql.cql2elm.CqlTranslatorOptions;
|
||||
import org.cqframework.cql.cql2elm.model.Model;
|
||||
import org.cqframework.cql.elm.execution.Library;
|
||||
import org.hl7.elm.r1.VersionedIdentifier;
|
||||
import org.opencds.cqf.cql.engine.fhir.model.R4FhirModelResolver;
|
||||
import org.opencds.cqf.cql.engine.model.ModelResolver;
|
||||
|
@ -55,19 +63,21 @@ public class CqlR4Config extends BaseCqlConfig {
|
|||
|
||||
@Lazy
|
||||
@Bean
|
||||
TerminologyProvider terminologyProvider(ITermReadSvcR4 theITermReadSvc, DaoRegistry theDaoRegistry, IValidationSupport theValidationSupport) {
|
||||
TerminologyProvider terminologyProvider(ITermReadSvcR4 theITermReadSvc, DaoRegistry theDaoRegistry,
|
||||
IValidationSupport theValidationSupport) {
|
||||
return new JpaTerminologyProvider(theITermReadSvc, theDaoRegistry, theValidationSupport);
|
||||
}
|
||||
|
||||
@Lazy
|
||||
@Bean
|
||||
EvaluationProviderFactory evaluationProviderFactory(FhirContext theFhirContext, DaoRegistry theDaoRegistry, TerminologyProvider theLocalSystemTerminologyProvider, ModelResolver modelResolver) {
|
||||
EvaluationProviderFactory evaluationProviderFactory(FhirContext theFhirContext, DaoRegistry theDaoRegistry,
|
||||
TerminologyProvider theLocalSystemTerminologyProvider, ModelResolver modelResolver) {
|
||||
return new ProviderFactory(theFhirContext, theDaoRegistry, theLocalSystemTerminologyProvider, modelResolver);
|
||||
}
|
||||
|
||||
@Lazy
|
||||
@Bean
|
||||
LibraryResolutionProvider libraryResolutionProvider() {
|
||||
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResolutionProvider() {
|
||||
return new LibraryResolutionProviderImpl();
|
||||
}
|
||||
|
||||
|
@ -77,13 +87,30 @@ public class CqlR4Config extends BaseCqlConfig {
|
|||
return new MeasureOperationsProvider();
|
||||
}
|
||||
|
||||
@Lazy
|
||||
@Bean
|
||||
public ModelResolver fhirModelResolver() {
|
||||
return new CachingModelResolverDecorator(new R4FhirModelResolver());
|
||||
}
|
||||
|
||||
@Lazy
|
||||
@Bean
|
||||
public LibraryHelper libraryHelper(Map<VersionedIdentifier, Model> globalModelCache) {
|
||||
return new LibraryHelper(globalModelCache);
|
||||
public LibraryHelper libraryHelper(Map<VersionedIdentifier, Model> globalModelCache,
|
||||
Map<org.cqframework.cql.elm.execution.VersionedIdentifier, Library> globalLibraryCache,
|
||||
CqlTranslatorOptions cqlTranslatorOptions) {
|
||||
return new LibraryHelper(globalModelCache, globalLibraryCache, cqlTranslatorOptions);
|
||||
}
|
||||
|
||||
@Lazy
|
||||
@Bean
|
||||
public CqlTranslatorOptions cqlTranslatorOptions() {
|
||||
return CqlTranslatorOptions.defaultOptions();
|
||||
}
|
||||
|
||||
@Bean
|
||||
public ElmCacheResourceChangeListener elmCacheResourceChangeListener(IResourceChangeListenerRegistry resourceChangeListenerRegistry, IFhirResourceDao<org.hl7.fhir.r4.model.Library> libraryDao, Map<org.cqframework.cql.elm.execution.VersionedIdentifier, Library> globalLibraryCache) {
|
||||
ElmCacheResourceChangeListener listener = new ElmCacheResourceChangeListener(libraryDao, globalLibraryCache);
|
||||
resourceChangeListenerRegistry.registerResourceResourceChangeListener("Library", new SearchParameterMap(), listener, 1000);
|
||||
return listener;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import org.hl7.fhir.dstu3.model.Measure;
|
|||
import org.hl7.fhir.dstu3.model.MeasureReport;
|
||||
import org.hl7.fhir.dstu3.model.Observation;
|
||||
import org.hl7.fhir.dstu3.model.Patient;
|
||||
import org.hl7.fhir.dstu3.model.Quantity;
|
||||
import org.hl7.fhir.dstu3.model.Reference;
|
||||
import org.hl7.fhir.dstu3.model.Resource;
|
||||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
|
@ -677,7 +678,7 @@ public class MeasureEvaluation {
|
|||
.setValue(new StringType(sdeKey));
|
||||
obsExtension.addExtension(extExtPop);
|
||||
obs.addExtension(obsExtension);
|
||||
obs.setValue(new IntegerType(sdeAccumulatorValue));
|
||||
obs.setValue(new Quantity(sdeAccumulatorValue));
|
||||
if(!isSingle) {
|
||||
valueCoding.setCode(sdeAccumulatorKey);
|
||||
obsCodeableConcept.setCoding(Collections.singletonList(valueCoding));
|
||||
|
|
|
@ -20,11 +20,12 @@ package ca.uhn.fhir.cql.dstu3.helper;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.cql.common.evaluation.LibraryLoader;
|
||||
import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider;
|
||||
import ca.uhn.fhir.cql.common.provider.LibrarySourceProvider;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.cqframework.cql.cql2elm.LibraryManager;
|
||||
import ca.uhn.fhir.cql.common.provider.LibraryContentProvider;
|
||||
|
||||
import org.cqframework.cql.cql2elm.CqlTranslatorOptions;
|
||||
import org.cqframework.cql.cql2elm.ModelManager;
|
||||
import org.cqframework.cql.cql2elm.model.Model;
|
||||
import org.cqframework.cql.elm.execution.Library;
|
||||
|
@ -36,11 +37,13 @@ import org.hl7.fhir.dstu3.model.Reference;
|
|||
import org.hl7.fhir.dstu3.model.RelatedArtifact;
|
||||
import org.hl7.fhir.dstu3.model.Resource;
|
||||
import org.opencds.cqf.cql.evaluator.cql2elm.model.CacheAwareModelManager;
|
||||
import org.opencds.cqf.cql.evaluator.engine.execution.PrivateCachingLibraryLoaderDecorator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.opencds.cqf.cql.evaluator.engine.execution.CacheAwareLibraryLoaderDecorator;
|
||||
import org.opencds.cqf.cql.evaluator.engine.execution.TranslatingLibraryLoader;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -48,22 +51,24 @@ public class LibraryHelper {
|
|||
private static final Logger ourLog = LoggerFactory.getLogger(LibraryHelper.class);
|
||||
|
||||
private final Map<org.hl7.elm.r1.VersionedIdentifier, Model> modelCache;
|
||||
private Map<VersionedIdentifier, Library> libraryCache;
|
||||
private CqlTranslatorOptions translatorOptions;
|
||||
|
||||
public LibraryHelper(Map<org.hl7.elm.r1.VersionedIdentifier, Model> modelCache) {
|
||||
|
||||
public LibraryHelper(Map<org.hl7.elm.r1.VersionedIdentifier, Model> modelCache, Map<VersionedIdentifier, Library> libraryCache, CqlTranslatorOptions translatorOptions) {
|
||||
this.modelCache = modelCache;
|
||||
this.libraryCache = libraryCache;
|
||||
this.translatorOptions = translatorOptions;
|
||||
}
|
||||
|
||||
public org.opencds.cqf.cql.engine.execution.LibraryLoader createLibraryLoader(
|
||||
LibraryResolutionProvider<org.hl7.fhir.dstu3.model.Library> provider) {
|
||||
ModelManager modelManager = new CacheAwareModelManager(this.modelCache);
|
||||
LibraryManager libraryManager = new LibraryManager(modelManager);
|
||||
libraryManager.getLibrarySourceLoader().clearProviders();
|
||||
|
||||
libraryManager.getLibrarySourceLoader().registerProvider(
|
||||
new LibrarySourceProvider<org.hl7.fhir.dstu3.model.Library, Attachment>(
|
||||
provider, x -> x.getContent(), x -> x.getContentType(), x -> x.getData()));
|
||||
List<org.opencds.cqf.cql.evaluator.cql2elm.content.LibraryContentProvider> contentProviders = Collections.singletonList(new LibraryContentProvider<org.hl7.fhir.dstu3.model.Library, Attachment>(
|
||||
provider, x -> x.getContent(), x -> x.getContentType(), x -> x.getData()));
|
||||
|
||||
return new PrivateCachingLibraryLoaderDecorator(new LibraryLoader(libraryManager, modelManager));
|
||||
return new CacheAwareLibraryLoaderDecorator(new TranslatingLibraryLoader(modelManager, contentProviders, translatorOptions), libraryCache);
|
||||
}
|
||||
|
||||
public List<Library> loadLibraries(Measure measure,
|
||||
|
@ -115,11 +120,13 @@ public class LibraryHelper {
|
|||
if (artifact.hasType() && artifact.getType().equals(RelatedArtifact.RelatedArtifactType.DEPENDSON) && artifact.hasResource() && artifact.getResource().hasReference()) {
|
||||
if (artifact.getResource().getReferenceElement().getResourceType().equals("Library")) {
|
||||
org.hl7.fhir.dstu3.model.Library library = libraryResourceProvider.resolveLibraryById(artifact.getResource().getReferenceElement().getIdPart());
|
||||
|
||||
if (library != null && isLogicLibrary(library)) {
|
||||
libraries.add(
|
||||
libraryLoader.load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion()))
|
||||
);
|
||||
if (library != null) {
|
||||
if (isLogicLibrary(library)) {
|
||||
libraries.add(libraryLoader
|
||||
.load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion())));
|
||||
} else {
|
||||
ourLog.warn("Library {} not included as part of evaluation context. Only Libraries with the 'logic-library' type are included.", library.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
package ca.uhn.fhir.cql.dstu3.listener;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.cqframework.cql.elm.execution.Library;
|
||||
import org.cqframework.cql.elm.execution.VersionedIdentifier;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.cache.IResourceChangeEvent;
|
||||
import ca.uhn.fhir.jpa.cache.IResourceChangeListener;
|
||||
|
||||
public class ElmCacheResourceChangeListener implements IResourceChangeListener {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ElmCacheResourceChangeListener.class);
|
||||
|
||||
private IFhirResourceDao<org.hl7.fhir.dstu3.model.Library> libraryDao;
|
||||
private Map<VersionedIdentifier, Library> globalLibraryCache;
|
||||
|
||||
public ElmCacheResourceChangeListener(IFhirResourceDao<org.hl7.fhir.dstu3.model.Library> libraryDao, Map<VersionedIdentifier, Library> globalLibraryCache) {
|
||||
this.libraryDao = libraryDao;
|
||||
this.globalLibraryCache = globalLibraryCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleInit(Collection<IIdType> theResourceIds) {
|
||||
// Intentionally empty. Only cache ELM on eval request
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleChange(IResourceChangeEvent theResourceChangeEvent) {
|
||||
if (theResourceChangeEvent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.invalidateCacheByIds(theResourceChangeEvent.getDeletedResourceIds());
|
||||
this.invalidateCacheByIds(theResourceChangeEvent.getUpdatedResourceIds());
|
||||
}
|
||||
|
||||
private void invalidateCacheByIds(List<IIdType> theIds) {
|
||||
if (theIds == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (IIdType id : theIds) {
|
||||
this.invalidateCacheById(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void invalidateCacheById(IIdType theId) {
|
||||
if (!theId.getResourceType().equals("Library")) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
org.hl7.fhir.dstu3.model.Library library = this.libraryDao.read(theId);
|
||||
|
||||
this.globalLibraryCache.remove(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion()));
|
||||
}
|
||||
// This happens when a Library is deleted entirely so it's impossible to look up name and version.
|
||||
catch (Exception e) {
|
||||
// TODO: This needs to be smarter... the issue is that ELM is cached with library name and version as the key since
|
||||
// that's the access path the CQL engine uses, but change notifications occur with the resource Id, which is not
|
||||
// necessarily tied to the resource name. In any event, if a unknown resource is deleted, clear all libraries as a workaround.
|
||||
// One option is to maintain a cache with multiple indices.
|
||||
ourLog.debug("Failed to locate resource {} to look up name and version. Clearing all libraries from cache.", theId.getValueAsString());
|
||||
this.globalLibraryCache.clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -40,8 +40,6 @@ import org.hl7.fhir.dstu3.model.MeasureReport;
|
|||
import org.hl7.fhir.dstu3.model.StringType;
|
||||
import org.hl7.fhir.exceptions.FHIRException;
|
||||
import org.opencds.cqf.cql.engine.execution.LibraryLoader;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
|
@ -54,8 +52,6 @@ import org.springframework.stereotype.Component;
|
|||
*/
|
||||
@Component
|
||||
public class MeasureOperationsProvider {
|
||||
private static final Logger logger = LoggerFactory.getLogger(MeasureOperationsProvider.class);
|
||||
|
||||
@Autowired
|
||||
private LibraryResolutionProvider<Library> libraryResolutionProvider;
|
||||
@Autowired
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -20,10 +20,11 @@ package ca.uhn.fhir.cql.r4.helper;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.cql.common.evaluation.LibraryLoader;
|
||||
import ca.uhn.fhir.cql.common.provider.LibraryResolutionProvider;
|
||||
import ca.uhn.fhir.cql.common.provider.LibrarySourceProvider;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import ca.uhn.fhir.cql.common.provider.LibraryContentProvider;
|
||||
|
||||
import org.cqframework.cql.cql2elm.CqlTranslatorOptions;
|
||||
import org.cqframework.cql.cql2elm.LibraryManager;
|
||||
import org.cqframework.cql.cql2elm.ModelManager;
|
||||
import org.cqframework.cql.cql2elm.model.Model;
|
||||
|
@ -33,14 +34,16 @@ import org.hl7.fhir.r4.model.Attachment;
|
|||
import org.hl7.fhir.r4.model.CanonicalType;
|
||||
import org.hl7.fhir.r4.model.Coding;
|
||||
import org.hl7.fhir.r4.model.Measure;
|
||||
import org.hl7.fhir.r4.model.PlanDefinition;
|
||||
import org.hl7.fhir.r4.model.RelatedArtifact;
|
||||
import org.hl7.fhir.r4.model.Resource;
|
||||
import org.opencds.cqf.cql.evaluator.cql2elm.model.CacheAwareModelManager;
|
||||
import org.opencds.cqf.cql.evaluator.engine.execution.PrivateCachingLibraryLoaderDecorator;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
import org.opencds.cqf.cql.evaluator.engine.execution.CacheAwareLibraryLoaderDecorator;
|
||||
import org.opencds.cqf.cql.evaluator.engine.execution.TranslatingLibraryLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
|
@ -51,34 +54,43 @@ public class LibraryHelper {
|
|||
private static final Logger ourLog = LoggerFactory.getLogger(LibraryHelper.class);
|
||||
|
||||
private final Map<org.hl7.elm.r1.VersionedIdentifier, Model> modelCache;
|
||||
private Map<VersionedIdentifier, Library> libraryCache;
|
||||
private CqlTranslatorOptions translatorOptions;
|
||||
|
||||
public LibraryHelper(Map<org.hl7.elm.r1.VersionedIdentifier, Model> modelCache) {
|
||||
public LibraryHelper(Map<org.hl7.elm.r1.VersionedIdentifier, Model> modelCache,
|
||||
Map<VersionedIdentifier, Library> libraryCache, CqlTranslatorOptions translatorOptions) {
|
||||
this.modelCache = modelCache;
|
||||
this.libraryCache = libraryCache;
|
||||
this.translatorOptions = translatorOptions;
|
||||
}
|
||||
|
||||
public org.opencds.cqf.cql.engine.execution.LibraryLoader createLibraryLoader(LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> provider) {
|
||||
public org.opencds.cqf.cql.engine.execution.LibraryLoader createLibraryLoader(
|
||||
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> provider) {
|
||||
ModelManager modelManager = new CacheAwareModelManager(this.modelCache);
|
||||
LibraryManager libraryManager = new LibraryManager(modelManager);
|
||||
libraryManager.getLibrarySourceLoader().clearProviders();
|
||||
List<org.opencds.cqf.cql.evaluator.cql2elm.content.LibraryContentProvider> contentProviders = Collections
|
||||
.singletonList(new LibraryContentProvider<org.hl7.fhir.r4.model.Library, Attachment>(provider,
|
||||
x -> x.getContent(), x -> x.getContentType(), x -> x.getData()));
|
||||
|
||||
libraryManager.getLibrarySourceLoader().registerProvider(
|
||||
new LibrarySourceProvider<org.hl7.fhir.r4.model.Library, Attachment>(provider,
|
||||
x -> x.getContent(), x -> x.getContentType(), x -> x.getData()));
|
||||
|
||||
return new PrivateCachingLibraryLoaderDecorator(new LibraryLoader(libraryManager, modelManager));
|
||||
return new CacheAwareLibraryLoaderDecorator(
|
||||
new TranslatingLibraryLoader(modelManager, contentProviders, translatorOptions), libraryCache);
|
||||
}
|
||||
|
||||
public org.opencds.cqf.cql.engine.execution.LibraryLoader createLibraryLoader(org.cqframework.cql.cql2elm.LibrarySourceProvider provider) {
|
||||
public org.opencds.cqf.cql.engine.execution.LibraryLoader createLibraryLoader(
|
||||
org.cqframework.cql.cql2elm.LibrarySourceProvider provider) {
|
||||
ModelManager modelManager = new CacheAwareModelManager(this.modelCache);
|
||||
LibraryManager libraryManager = new LibraryManager(modelManager);
|
||||
libraryManager.getLibrarySourceLoader().clearProviders();
|
||||
|
||||
libraryManager.getLibrarySourceLoader().registerProvider(provider);
|
||||
|
||||
return new PrivateCachingLibraryLoaderDecorator(new LibraryLoader(libraryManager, modelManager));
|
||||
return new CacheAwareLibraryLoaderDecorator(new TranslatingLibraryLoader(modelManager, null, translatorOptions),
|
||||
libraryCache);
|
||||
}
|
||||
|
||||
public org.hl7.fhir.r4.model.Library resolveLibraryReference(LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider, String reference) {
|
||||
public org.hl7.fhir.r4.model.Library resolveLibraryReference(
|
||||
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider, String reference) {
|
||||
// Raw references to Library/libraryId or libraryId
|
||||
if (reference.startsWith("Library/") || !reference.contains("/")) {
|
||||
return libraryResourceProvider.resolveLibraryById(reference.replace("Library/", ""));
|
||||
|
@ -92,30 +104,21 @@ public class LibraryHelper {
|
|||
}
|
||||
|
||||
public List<org.cqframework.cql.elm.execution.Library> loadLibraries(Measure measure,
|
||||
org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader,
|
||||
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider) {
|
||||
org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader,
|
||||
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider) {
|
||||
List<org.cqframework.cql.elm.execution.Library> libraries = new ArrayList<org.cqframework.cql.elm.execution.Library>();
|
||||
|
||||
List<String> messages = new ArrayList<>();
|
||||
|
||||
// load libraries
|
||||
//TODO: if there's a bad measure argument, this blows up for an obscure error
|
||||
// TODO: if there's a bad measure argument, this blows up for an obscure error
|
||||
org.hl7.fhir.r4.model.Library primaryLibrary = null;
|
||||
|
||||
List<CanonicalType> measureLibraries = measure.getLibrary();
|
||||
if (measureLibraries.isEmpty()) {
|
||||
String message = "No libraries found on " + measure.getId() + ". Did you perhaps load a DSTU3 Measure onto an R4 server?";
|
||||
messages.add(message);
|
||||
ourLog.warn(message);
|
||||
}
|
||||
for (CanonicalType ref : measureLibraries) {
|
||||
for (CanonicalType ref : measure.getLibrary()) {
|
||||
// if library is contained in measure, load it into server
|
||||
String id = ref.getValue(); //CanonicalHelper.getId(ref);
|
||||
String id = ref.getValue(); // CanonicalHelper.getId(ref);
|
||||
if (id.startsWith("#")) {
|
||||
id = id.substring(1);
|
||||
for (Resource resource : measure.getContained()) {
|
||||
if (resource instanceof org.hl7.fhir.r4.model.Library
|
||||
&& resource.getIdElement().getIdPart().equals(id)) {
|
||||
&& resource.getIdElement().getIdPart().equals(id)) {
|
||||
libraryResourceProvider.update((org.hl7.fhir.r4.model.Library) resource);
|
||||
}
|
||||
}
|
||||
|
@ -127,34 +130,30 @@ public class LibraryHelper {
|
|||
primaryLibrary = library;
|
||||
}
|
||||
|
||||
|
||||
if (library != null) {
|
||||
if (isLogicLibrary(library)) {
|
||||
libraries.add(
|
||||
libraryLoader.load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion()))
|
||||
);
|
||||
} else {
|
||||
String message = "Skipping library " + library.getId() + " is not a logic library. Probably missing type.coding.system=\"http://terminology.hl7.org/CodeSystem/library-type\"";
|
||||
messages.add(message);
|
||||
ourLog.warn(message);
|
||||
}
|
||||
if (library != null && isLogicLibrary(library)) {
|
||||
libraries.add(libraryLoader
|
||||
.load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion())));
|
||||
}
|
||||
}
|
||||
|
||||
if (libraries.isEmpty()) {
|
||||
throw new IllegalArgumentException(String
|
||||
.format("Could not load library source for libraries referenced in %s:\n%s", measure.getId(), StringUtils.join("\n", messages)));
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Could not load library source for libraries referenced in %s.", measure.getId()));
|
||||
}
|
||||
|
||||
for (RelatedArtifact artifact : primaryLibrary.getRelatedArtifact()) {
|
||||
if (artifact.hasType() && artifact.getType().equals(RelatedArtifact.RelatedArtifactType.DEPENDSON) && artifact.hasResource()) {
|
||||
if (artifact.hasType() && artifact.getType().equals(RelatedArtifact.RelatedArtifactType.DEPENDSON)
|
||||
&& artifact.hasResource()) {
|
||||
org.hl7.fhir.r4.model.Library library = null;
|
||||
library = resolveLibraryReference(libraryResourceProvider, artifact.getResource());
|
||||
|
||||
if (library != null && isLogicLibrary(library)) {
|
||||
libraries.add(
|
||||
libraryLoader.load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion()))
|
||||
);
|
||||
if (library != null) {
|
||||
if (isLogicLibrary(library)) {
|
||||
libraries.add(libraryLoader
|
||||
.load(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion())));
|
||||
} else {
|
||||
ourLog.warn("Library {} not included as part of evaluation context. Only Libraries with the 'logic-library' type are included.", library.getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -168,12 +167,13 @@ public class LibraryHelper {
|
|||
}
|
||||
|
||||
if (!library.hasType()) {
|
||||
// If no type is specified, assume it is a logic library based on whether there is a CQL content element.
|
||||
// If no type is specified, assume it is a logic library based on whether there
|
||||
// is a CQL content element.
|
||||
if (library.hasContent()) {
|
||||
for (Attachment a : library.getContent()) {
|
||||
if (a.hasContentType() && (a.getContentType().equals("text/cql")
|
||||
|| a.getContentType().equals("application/elm+xml")
|
||||
|| a.getContentType().equals("application/elm+json"))) {
|
||||
if (a.hasContentType()
|
||||
&& (a.getContentType().equals("text/cql") || a.getContentType().equals("application/elm+xml")
|
||||
|| a.getContentType().equals("application/elm+json"))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -186,8 +186,8 @@ public class LibraryHelper {
|
|||
}
|
||||
|
||||
for (Coding c : library.getType().getCoding()) {
|
||||
if (c.hasSystem() && c.getSystem().equals("http://terminology.hl7.org/CodeSystem/library-type")
|
||||
&& c.hasCode() && c.getCode().equals("logic-library")) {
|
||||
if (c.hasSystem() && c.getSystem().equals("http://terminology.hl7.org/CodeSystem/library-type") && c.hasCode()
|
||||
&& c.getCode().equals("logic-library")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -195,27 +195,40 @@ public class LibraryHelper {
|
|||
return false;
|
||||
}
|
||||
|
||||
public Library resolveLibraryById(String libraryId,
|
||||
org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader,
|
||||
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider) {
|
||||
// Library library = null;
|
||||
public Library resolveLibraryById(String libraryId, org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader,
|
||||
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider) {
|
||||
|
||||
org.hl7.fhir.r4.model.Library fhirLibrary = libraryResourceProvider.resolveLibraryById(libraryId);
|
||||
return libraryLoader
|
||||
.load(new VersionedIdentifier().withId(fhirLibrary.getName()).withVersion(fhirLibrary.getVersion()));
|
||||
.load(new VersionedIdentifier().withId(fhirLibrary.getName()).withVersion(fhirLibrary.getVersion()));
|
||||
}
|
||||
|
||||
public Library resolvePrimaryLibrary(Measure measure,
|
||||
org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader,
|
||||
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider) {
|
||||
org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader,
|
||||
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider) {
|
||||
// default is the first library reference
|
||||
String id = CanonicalHelper.getId(measure.getLibrary().get(0));
|
||||
|
||||
Library library = resolveLibraryById(id, libraryLoader, libraryResourceProvider);
|
||||
|
||||
if (library == null) {
|
||||
throw new IllegalArgumentException(String.format("Could not resolve primary library for Measure/%s.",
|
||||
measure.getIdElement().getIdPart()));
|
||||
throw new IllegalArgumentException(
|
||||
String.format("Could not resolve primary library for Measure/%s.", measure.getIdElement().getIdPart()));
|
||||
}
|
||||
|
||||
return library;
|
||||
}
|
||||
|
||||
public Library resolvePrimaryLibrary(PlanDefinition planDefinition,
|
||||
org.opencds.cqf.cql.engine.execution.LibraryLoader libraryLoader,
|
||||
LibraryResolutionProvider<org.hl7.fhir.r4.model.Library> libraryResourceProvider) {
|
||||
String id = CanonicalHelper.getId(planDefinition.getLibrary().get(0));
|
||||
|
||||
Library library = resolveLibraryById(id, libraryLoader, libraryResourceProvider);
|
||||
|
||||
if (library == null) {
|
||||
throw new IllegalArgumentException(String.format("Could not resolve primary library for PlanDefinition/%s",
|
||||
planDefinition.getIdElement().getIdPart()));
|
||||
}
|
||||
|
||||
return library;
|
||||
|
|
|
@ -0,0 +1,72 @@
|
|||
package ca.uhn.fhir.cql.r4.listener;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.cqframework.cql.elm.execution.Library;
|
||||
import org.cqframework.cql.elm.execution.VersionedIdentifier;
|
||||
import org.hl7.fhir.instance.model.api.IIdType;
|
||||
|
||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
|
||||
import ca.uhn.fhir.jpa.cache.IResourceChangeEvent;
|
||||
import ca.uhn.fhir.jpa.cache.IResourceChangeListener;
|
||||
|
||||
public class ElmCacheResourceChangeListener implements IResourceChangeListener {
|
||||
|
||||
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(ElmCacheResourceChangeListener.class);
|
||||
|
||||
private IFhirResourceDao<org.hl7.fhir.r4.model.Library> libraryDao;
|
||||
private Map<VersionedIdentifier, Library> globalLibraryCache;
|
||||
|
||||
public ElmCacheResourceChangeListener(IFhirResourceDao<org.hl7.fhir.r4.model.Library> libraryDao, Map<VersionedIdentifier, Library> globalLibraryCache) {
|
||||
this.libraryDao = libraryDao;
|
||||
this.globalLibraryCache = globalLibraryCache;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleInit(Collection<IIdType> theResourceIds) {
|
||||
// Intentionally empty. Only cache ELM on eval request
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleChange(IResourceChangeEvent theResourceChangeEvent) {
|
||||
if (theResourceChangeEvent == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.invalidateCacheByIds(theResourceChangeEvent.getDeletedResourceIds());
|
||||
this.invalidateCacheByIds(theResourceChangeEvent.getUpdatedResourceIds());
|
||||
}
|
||||
|
||||
private void invalidateCacheByIds(List<IIdType> theIds) {
|
||||
if (theIds == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (IIdType id : theIds) {
|
||||
this.invalidateCacheById(id);
|
||||
}
|
||||
}
|
||||
|
||||
private void invalidateCacheById(IIdType theId) {
|
||||
if (!theId.getResourceType().equals("Library")) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
org.hl7.fhir.r4.model.Library library = this.libraryDao.read(theId);
|
||||
|
||||
this.globalLibraryCache.remove(new VersionedIdentifier().withId(library.getName()).withVersion(library.getVersion()));
|
||||
}
|
||||
// This happens when a Library is deleted entirely so it's impossible to look up name and version.
|
||||
catch (Exception e) {
|
||||
// TODO: This needs to be smarter... the issue is that ELM is cached with library name and version as the key since
|
||||
// that's the access path the CQL engine uses, but change notifications occur with the resource Id, which is not
|
||||
// necessarily tied to the resource name. In any event, if a unknown resource is deleted, clear all libraries as a workaround.
|
||||
// One option is to maintain a cache with multiple indices.
|
||||
ourLog.debug("Failed to locate resource {} to look up name and version. Clearing all libraries from cache.", theId.getValueAsString());
|
||||
this.globalLibraryCache.clear();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,15 +1,23 @@
|
|||
package ca.uhn.fhir.cql.config;
|
||||
|
||||
import ca.uhn.fhir.jpa.api.config.DaoConfig;
|
||||
import ca.uhn.fhir.jpa.subscription.channel.config.SubscriptionChannelConfig;
|
||||
import ca.uhn.fhir.jpa.subscription.submit.config.SubscriptionSubmitterConfig;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Import;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
|
||||
|
||||
|
||||
@Configuration
|
||||
@Import({SubscriptionSubmitterConfig.class, SubscriptionChannelConfig.class})
|
||||
public class TestCqlConfig {
|
||||
|
||||
@Bean
|
||||
public DaoConfig daoConfig() {
|
||||
DaoConfig daoConfig = new DaoConfig();
|
||||
daoConfig.setAllowExternalReferences(true);
|
||||
daoConfig.setEnforceReferentialIntegrityOnWrite(false);
|
||||
daoConfig.setEnforceReferenceTargetTypes(false);
|
||||
|
||||
return daoConfig;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ public class CqlMeasureEvaluationDstu3Test extends BaseCqlDstu3Test {
|
|||
assertNotNull("expected MeasureReport can not be null", expected);
|
||||
assertNotNull("actual MeasureReport can not be null", actual);
|
||||
|
||||
String errorLocator = String.format("Measure: %s, Subject: %s", expected.getMeasure(),
|
||||
String errorLocator = String.format("Measure: %s, Subject: %s", expected.getMeasure().getReference(),
|
||||
expected.getPatient().getReference());
|
||||
|
||||
assertEquals(expected.hasGroup(), actual.hasGroup(), errorLocator);
|
||||
|
@ -83,10 +83,10 @@ public class CqlMeasureEvaluationDstu3Test extends BaseCqlDstu3Test {
|
|||
|
||||
for (MeasureReportGroupComponent mrgcExpected : expected.getGroup()) {
|
||||
Optional<MeasureReportGroupComponent> mrgcActualOptional = actual.getGroup().stream()
|
||||
.filter(x -> x.getId().equals(mrgcExpected.getId())).findFirst();
|
||||
.filter(x -> x.getIdentifier() != null && x.getIdentifier().getValue().equals(mrgcExpected.getIdentifier().getValue())).findFirst();
|
||||
|
||||
errorLocator = String.format("Measure: %s, Subject: %s, Group: %s", expected.getMeasure(),
|
||||
expected.getPatient().getReference(), mrgcExpected.getId());
|
||||
errorLocator = String.format("Measure: %s, Subject: %s, Group: %s", expected.getMeasure().getReference(),
|
||||
expected.getPatient().getReference(), mrgcExpected.getIdentifier().getValue());
|
||||
assertTrue(errorLocator, mrgcActualOptional.isPresent());
|
||||
|
||||
MeasureReportGroupComponent mrgcActual = mrgcActualOptional.get();
|
||||
|
@ -94,7 +94,7 @@ public class CqlMeasureEvaluationDstu3Test extends BaseCqlDstu3Test {
|
|||
if (mrgcExpected.getMeasureScore() == null) {
|
||||
assertNull(mrgcActual.getMeasureScore(), errorLocator);
|
||||
} else {
|
||||
assertNotNull(mrgcActual.getMeasureScore());
|
||||
assertNotNull(errorLocator, mrgcActual.getMeasureScore());
|
||||
BigDecimal decimalExpected = mrgcExpected.getMeasureScore();
|
||||
BigDecimal decimalActual = mrgcActual.getMeasureScore();
|
||||
|
||||
|
@ -135,10 +135,13 @@ public class CqlMeasureEvaluationDstu3Test extends BaseCqlDstu3Test {
|
|||
return new DateTimeType(date).getValueAsString();
|
||||
}
|
||||
|
||||
// As of 2/11/2021, all the DSTU3 bundles in the Connectathon IG are out of date
|
||||
// and can't be posted
|
||||
// @Test
|
||||
// public void test_EXM117_83000() throws IOException {
|
||||
// this.testMeasureBundle("dstu3/connectathon/EXM117_FHIR3-8.3.000-bundle.json");
|
||||
// }
|
||||
@Test
|
||||
public void test_EXM124_FHIR3_72000() throws IOException {
|
||||
this.testMeasureBundle("dstu3/connectathon/EXM124-FHIR3-7.2.000-bundle.json");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void test_EXM104_FHIR3_81000() throws IOException {
|
||||
this.testMeasureBundle("dstu3/connectathon/EXM104-FHIR3-8.1.000-bundle.json");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -83,7 +83,7 @@ public class CqlMeasureEvaluationR4Test extends BaseCqlR4Test {
|
|||
|
||||
for (MeasureReportGroupComponent mrgcExpected : expected.getGroup()) {
|
||||
Optional<MeasureReportGroupComponent> mrgcActualOptional = actual.getGroup().stream()
|
||||
.filter(x -> x.getId().equals(mrgcExpected.getId())).findFirst();
|
||||
.filter(x -> x.getId() != null && x.getId().equals(mrgcExpected.getId())).findFirst();
|
||||
|
||||
errorLocator = String.format("Measure: %s, Subject: %s, Group: %s", expected.getMeasure(),
|
||||
expected.getSubject().getReference(), mrgcExpected.getId());
|
||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
4
pom.xml
4
pom.xml
|
@ -829,8 +829,8 @@
|
|||
<elastic_apm_version>1.13.0</elastic_apm_version>
|
||||
<!-- CQL Support -->
|
||||
<cql-engine.version>1.5.1</cql-engine.version>
|
||||
<cql-evaluator.version>1.1.0</cql-evaluator.version>
|
||||
<cqframework.version>1.5.1</cqframework.version>
|
||||
<cql-evaluator.version>1.2.0</cql-evaluator.version>
|
||||
<cqframework.version>1.5.2</cqframework.version>
|
||||
|
||||
<!-- Site properties -->
|
||||
<fontawesomeVersion>5.4.1</fontawesomeVersion>
|
||||
|
|
Loading…
Reference in New Issue