JPATerminologyProvider tests and bug fixes (#2346)

* Add initial jpaTerminologyProvider tests, add EXM130 to test suite
This commit is contained in:
JP 2021-02-05 06:22:02 -07:00 committed by GitHub
parent f3c0ecd54d
commit af5c45531a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 3868 additions and 143 deletions

View File

@ -30,7 +30,6 @@ 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.rp.dstu3.ValueSetResourceProvider;
import ca.uhn.fhir.jpa.term.api.ITermReadSvcDstu3;
import org.opencds.cqf.cql.engine.model.ModelResolver;
@ -39,6 +38,7 @@ import java.util.Map;
import org.cqframework.cql.cql2elm.model.Model;
import org.hl7.elm.r1.VersionedIdentifier;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.opencds.cqf.cql.engine.fhir.model.Dstu3FhirModelResolver;
import org.opencds.cqf.cql.engine.terminology.TerminologyProvider;
import org.opencds.cqf.cql.evaluator.engine.model.CachingModelResolverDecorator;
@ -50,14 +50,16 @@ import org.springframework.context.annotation.Lazy;
public class CqlDstu3Config extends BaseCqlConfig {
@Lazy
@Bean
TerminologyProvider terminologyProvider(ITermReadSvcDstu3 theITermReadSvc,
ValueSetResourceProvider theValueSetResourceProvider, IValidationSupport theValidationSupport) {
return new JpaTerminologyProvider(theITermReadSvc, theValueSetResourceProvider, theValidationSupport);
TerminologyProvider terminologyProvider(ITermReadSvcDstu3 theITermReadSvc, DaoRegistry daoRegistry,
IValidationSupport theValidationSupport) {
return new JpaTerminologyProvider(theITermReadSvc, daoRegistry.getResourceDao(ValueSet.class),
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);
}
@ -66,7 +68,7 @@ public class CqlDstu3Config extends BaseCqlConfig {
LibraryResolutionProvider libraryResolutionProvider() {
return new LibraryResolutionProviderImpl();
}
@Lazy
@Bean
public MeasureOperationsProvider measureOperationsProvider() {
@ -74,7 +76,7 @@ public class CqlDstu3Config extends BaseCqlConfig {
}
@Bean
public ModelResolver fhirModelResolver () {
public ModelResolver fhirModelResolver() {
return new CachingModelResolverDecorator(new Dstu3FhirModelResolver());
}

View File

@ -31,7 +31,6 @@ import ca.uhn.fhir.cql.r4.helper.LibraryHelper;
import ca.uhn.fhir.cql.r4.provider.JpaTerminologyProvider;
import ca.uhn.fhir.cql.r4.provider.MeasureOperationsProvider;
import ca.uhn.fhir.jpa.api.dao.DaoRegistry;
import ca.uhn.fhir.jpa.rp.r4.ValueSetResourceProvider;
import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4;
import org.opencds.cqf.cql.engine.model.ModelResolver;
@ -40,6 +39,7 @@ import java.util.Map;
import org.cqframework.cql.cql2elm.model.Model;
import org.hl7.elm.r1.VersionedIdentifier;
import org.hl7.fhir.r4.model.ValueSet;
import org.opencds.cqf.cql.engine.fhir.model.R4FhirModelResolver;
import org.opencds.cqf.cql.engine.terminology.TerminologyProvider;
import org.opencds.cqf.cql.evaluator.engine.model.CachingModelResolverDecorator;
@ -58,13 +58,15 @@ public class CqlR4Config extends BaseCqlConfig {
@Lazy
@Bean
TerminologyProvider terminologyProvider(ITermReadSvcR4 theITermReadSvc, ValueSetResourceProvider theValueSetResourceProvider, IValidationSupport theValidationSupport) {
return new JpaTerminologyProvider(theITermReadSvc,theValueSetResourceProvider, theValidationSupport);
TerminologyProvider terminologyProvider(ITermReadSvcR4 theITermReadSvc, DaoRegistry daoRegistry,
IValidationSupport theValidationSupport) {
return new JpaTerminologyProvider(theITermReadSvc, daoRegistry.getResourceDao(ValueSet.class), 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);
}
@ -81,7 +83,7 @@ public class CqlR4Config extends BaseCqlConfig {
}
@Bean
public ModelResolver fhirModelResolver () {
public ModelResolver fhirModelResolver() {
return new CachingModelResolverDecorator(new R4FhirModelResolver());
}

View File

@ -20,12 +20,10 @@ package ca.uhn.fhir.cql.dstu3.provider;
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.jpa.rp.dstu3.ValueSetResourceProvider;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.term.api.ITermReadSvcDstu3;
@ -34,7 +32,6 @@ import ca.uhn.fhir.rest.param.UriParam;
import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException;
import org.hl7.fhir.dstu3.model.IdType;
import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetExpansionContainsComponent;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.opencds.cqf.cql.engine.runtime.Code;
import org.opencds.cqf.cql.engine.terminology.CodeSystemInfo;
@ -53,14 +50,14 @@ import java.util.List;
public class JpaTerminologyProvider implements TerminologyProvider {
private ITermReadSvcDstu3 terminologySvc;
private ValueSetResourceProvider valueSetResourceProvider;
private IFhirResourceDao<ValueSet> valueSetDao;
private final IValidationSupport validationSupport;
@Autowired
public JpaTerminologyProvider(ITermReadSvcDstu3 terminologySvc,
ValueSetResourceProvider valueSetResourceProvider, IValidationSupport validationSupport) {
IFhirResourceDao<ValueSet> valueSetDao, IValidationSupport validationSupport) {
this.terminologySvc = terminologySvc;
this.valueSetResourceProvider = valueSetResourceProvider;
this.valueSetDao = valueSetDao;
this.validationSupport = validationSupport;
}
@ -78,9 +75,8 @@ public class JpaTerminologyProvider implements TerminologyProvider {
@Override
public Iterable<Code> expand(ValueSetInfo valueSet) throws ResourceNotFoundException {
List<Code> codes = new ArrayList<>();
boolean needsExpand = false;
ValueSet vs = null;
// This could possibly be refactored into a single call to the underlying HAPI Terminology service. Need to think through that..,
ValueSet vs;
if (valueSet.getId().startsWith("http://") || valueSet.getId().startsWith("https://")) {
if (valueSet.getVersion() != null
|| (valueSet.getCodeSystems() != null && valueSet.getCodeSystems().size() > 0)) {
@ -90,7 +86,8 @@ public class JpaTerminologyProvider implements TerminologyProvider {
valueSet.getId()));
}
}
IBundleProvider bundleProvider = valueSetResourceProvider.getDao()
IBundleProvider bundleProvider = this.valueSetDao
.search(new SearchParameterMap().add(ValueSet.SP_URL, new UriParam(valueSet.getId())));
List<IBaseResource> valueSets = bundleProvider.getResources(0, bundleProvider.size());
if (valueSets.isEmpty()) {
@ -101,41 +98,37 @@ public class JpaTerminologyProvider implements TerminologyProvider {
throw new IllegalArgumentException("Found more than 1 ValueSet with url: " + valueSet.getId());
}
} else {
vs = valueSetResourceProvider.getDao().read(new IdType(valueSet.getId()));
}
if (vs != null) {
if (vs.hasCompose()) {
if (vs.getCompose().hasInclude()) {
for (ValueSet.ConceptSetComponent include : vs.getCompose().getInclude()) {
if (include.hasValueSet() || include.hasFilter()) {
needsExpand = true;
break;
}
for (ValueSet.ConceptReferenceComponent concept : include.getConcept()) {
if (concept.hasCode()) {
codes.add(new Code().withCode(concept.getCode()).withSystem(include.getSystem()));
}
}
}
if (!needsExpand) {
return codes;
}
}
}
if (vs.hasExpansion() && vs.getExpansion().hasContains()) {
for (ValueSetExpansionContainsComponent vsecc : vs.getExpansion().getContains()) {
codes.add(new Code().withCode(vsecc.getCode()).withSystem(vsecc.getSystem()));
}
return codes;
vs = this.valueSetDao.read(new IdType(valueSet.getId()));
if (vs == null) {
throw new IllegalArgumentException(String.format("Could not resolve value set %s.", valueSet.getId()));
}
}
org.hl7.fhir.r4.model.ValueSet expansion = terminologySvc
.expandValueSet(new ValueSetExpansionOptions().setCount(Integer.MAX_VALUE), valueSet.getId(), null);
expansion.getExpansion().getContains()
.forEach(concept -> codes.add(new Code().withCode(concept.getCode()).withSystem(concept.getSystem())));
// Attempt to expand the ValueSet if it's not already expanded.
if (!(vs.hasExpansion() && vs.getExpansion().hasContains())) {
vs = (ValueSet)this.terminologySvc.expandValueSet(
new ValueSetExpansionOptions().setCount(Integer.MAX_VALUE).setFailOnMissingCodeSystem(false), vs);
}
List<Code> codes = new ArrayList<>();
// If expansion was successful, use the codes.
if (vs.hasExpansion() && vs.getExpansion().hasContains()) {
for (ValueSet.ValueSetExpansionContainsComponent vsecc : vs.getExpansion().getContains()) {
codes.add(new Code().withCode(vsecc.getCode()).withSystem(vsecc.getSystem()));
}
}
// If not, best-effort based on codes. Should probably make this configurable to match the behavior of the
// underlying terminology service implementation
else if (vs.hasCompose() && vs.getCompose().hasInclude()) {
for (ValueSet.ConceptSetComponent include : vs.getCompose().getInclude()) {
for (ValueSet.ConceptReferenceComponent concept : include.getConcept()) {
if (concept.hasCode()) {
codes.add(new Code().withCode(concept.getCode()).withSystem(include.getSystem()));
}
}
}
}
return codes;
}

View File

@ -20,12 +20,11 @@ package ca.uhn.fhir.cql.r4.provider;
* #L%
*/
import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.context.support.IValidationSupport;
import ca.uhn.fhir.context.support.IValidationSupport.LookupCodeResult;
import ca.uhn.fhir.context.support.ValidationSupportContext;
import ca.uhn.fhir.context.support.ValueSetExpansionOptions;
import ca.uhn.fhir.jpa.rp.r4.ValueSetResourceProvider;
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDao;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.term.api.ITermReadSvcR4;
import ca.uhn.fhir.rest.api.server.IBundleProvider;
@ -48,102 +47,98 @@ import java.util.List;
@Component
public class JpaTerminologyProvider implements TerminologyProvider {
private ITermReadSvcR4 terminologySvc;
private ValueSetResourceProvider valueSetResourceProvider;
private final IValidationSupport validationSupport;
private ITermReadSvcR4 terminologySvc;
private IFhirResourceDao<ValueSet> valueSetDao;
private final IValidationSupport validationSupport;
@Autowired
public JpaTerminologyProvider(ITermReadSvcR4 terminologySvc,
ValueSetResourceProvider valueSetResourceProvider, IValidationSupport validationSupport) {
this.terminologySvc = terminologySvc;
this.valueSetResourceProvider = valueSetResourceProvider;
this.validationSupport = validationSupport;
}
@Autowired
public JpaTerminologyProvider(ITermReadSvcR4 terminologySvc, IFhirResourceDao<ValueSet> valueSetDao,
IValidationSupport validationSupport) {
this.terminologySvc = terminologySvc;
this.valueSetDao = valueSetDao;
this.validationSupport = validationSupport;
}
@Override
public boolean in(Code code, ValueSetInfo valueSet) throws ResourceNotFoundException {
for (Code c : expand(valueSet)) {
if (c == null)
continue;
if (c.getCode().equals(code.getCode()) && c.getSystem().equals(code.getSystem())) {
return true;
}
}
return false;
}
@Override
public boolean in(Code code, ValueSetInfo valueSet) throws ResourceNotFoundException {
for (Code c : expand(valueSet)) {
if (c == null)
continue;
if (c.getCode().equals(code.getCode()) && c.getSystem().equals(code.getSystem())) {
return true;
}
}
return false;
}
@Override
public Iterable<Code> expand(ValueSetInfo valueSet) throws ResourceNotFoundException {
List<Code> codes = new ArrayList<>();
boolean needsExpand = false;
ValueSet vs;
if (valueSet.getId().startsWith("http://") || valueSet.getId().startsWith("https://")) {
if (valueSet.getVersion() != null
|| (valueSet.getCodeSystems() != null && valueSet.getCodeSystems().size() > 0)) {
if (!(valueSet.getCodeSystems().size() == 1 && valueSet.getCodeSystems().get(0).getVersion() == null)) {
throw new UnsupportedOperationException(String.format(
"Could not expand value set %s; version and code system bindings are not supported at this time.",
valueSet.getId()));
}
}
IBundleProvider bundleProvider = valueSetResourceProvider.getDao()
.search(new SearchParameterMap().add(ValueSet.SP_URL, new UriParam(valueSet.getId())));
List<IBaseResource> valueSets = bundleProvider.getResources(0, bundleProvider.size());
if (valueSets.isEmpty()) {
throw new IllegalArgumentException(String.format("Could not resolve value set %s.", valueSet.getId()));
} else if (valueSets.size() == 1) {
vs = (ValueSet) valueSets.get(0);
} else {
throw new IllegalArgumentException("Found more than 1 ValueSet with url: " + valueSet.getId());
}
} else {
vs = valueSetResourceProvider.getDao().read(new IdType(valueSet.getId()));
}
if (vs != null) {
if (vs.hasCompose()) {
if (vs.getCompose().hasInclude()) {
for (ValueSet.ConceptSetComponent include : vs.getCompose().getInclude()) {
if (include.hasValueSet() || include.hasFilter()) {
needsExpand = true;
break;
}
for (ValueSet.ConceptReferenceComponent concept : include.getConcept()) {
if (concept.hasCode()) {
codes.add(new Code().withCode(concept.getCode()).withSystem(include.getSystem()));
}
}
}
if (!needsExpand) {
return codes;
}
}
}
@Override
public Iterable<Code> expand(ValueSetInfo valueSet) throws ResourceNotFoundException {
// This could possibly be refactored into a single call to the underlying HAPI Terminology service. Need to think through that..,
ValueSet vs;
if (valueSet.getId().startsWith("http://") || valueSet.getId().startsWith("https://")) {
if (valueSet.getVersion() != null
|| (valueSet.getCodeSystems() != null && valueSet.getCodeSystems().size() > 0)) {
if (!(valueSet.getCodeSystems().size() == 1 && valueSet.getCodeSystems().get(0).getVersion() == null)) {
throw new UnsupportedOperationException(String.format(
"Could not expand value set %s; version and code system bindings are not supported at this time.",
valueSet.getId()));
}
}
if (vs.hasExpansion() && vs.getExpansion().hasContains()) {
for (ValueSetExpansionContainsComponent vsecc : vs.getExpansion().getContains()) {
codes.add(new Code().withCode(vsecc.getCode()).withSystem(vsecc.getSystem()));
}
IBundleProvider bundleProvider = this.valueSetDao
.search(new SearchParameterMap().add(ValueSet.SP_URL, new UriParam(valueSet.getId())));
List<IBaseResource> valueSets = bundleProvider.getResources(0, bundleProvider.size());
if (valueSets.isEmpty()) {
throw new IllegalArgumentException(String.format("Could not resolve value set %s.", valueSet.getId()));
} else if (valueSets.size() == 1) {
vs = (ValueSet) valueSets.get(0);
} else {
throw new IllegalArgumentException("Found more than 1 ValueSet with url: " + valueSet.getId());
}
} else {
vs = this.valueSetDao.read(new IdType(valueSet.getId()));
if (vs == null) {
throw new IllegalArgumentException(String.format("Could not resolve value set %s.", valueSet.getId()));
}
}
return codes;
}
// Attempt to expand the ValueSet if it's not already expanded.
if (!(vs.hasExpansion() && vs.getExpansion().hasContains())) {
vs = (ValueSet)this.terminologySvc.expandValueSet(
new ValueSetExpansionOptions().setCount(Integer.MAX_VALUE).setFailOnMissingCodeSystem(false), vs);
}
}
List<Code> codes = new ArrayList<>();
ValueSet expansion = terminologySvc
.expandValueSet(new ValueSetExpansionOptions().setCount(Integer.MAX_VALUE), valueSet.getId(), null);
expansion.getExpansion().getContains()
.forEach(concept -> codes.add(new Code().withCode(concept.getCode()).withSystem(concept.getSystem())));
// If expansion was successful, use the codes.
if (vs.hasExpansion() && vs.getExpansion().hasContains()) {
for (ValueSetExpansionContainsComponent vsecc : vs.getExpansion().getContains()) {
codes.add(new Code().withCode(vsecc.getCode()).withSystem(vsecc.getSystem()));
}
}
// If not, best-effort based on codes. Should probably make this configurable to match the behavior of the
// underlying terminology service implementation
else if (vs.hasCompose() && vs.getCompose().hasInclude()) {
for (ValueSet.ConceptSetComponent include : vs.getCompose().getInclude()) {
for (ValueSet.ConceptReferenceComponent concept : include.getConcept()) {
if (concept.hasCode()) {
codes.add(new Code().withCode(concept.getCode()).withSystem(include.getSystem()));
}
}
}
}
return codes;
}
return codes;
}
@Override
public Code lookup(Code code, CodeSystemInfo codeSystem) throws ResourceNotFoundException {
LookupCodeResult cs = terminologySvc.lookupCode(new ValidationSupportContext(validationSupport), codeSystem.getId(), code.getCode());
@Override
public Code lookup(Code code, CodeSystemInfo codeSystem) throws ResourceNotFoundException {
LookupCodeResult cs = terminologySvc.lookupCode(new ValidationSupportContext(validationSupport),
codeSystem.getId(), code.getCode());
code.setDisplay(cs.getCodeDisplay());
code.setSystem(codeSystem.getId());
code.setDisplay(cs.getCodeDisplay());
code.setSystem(codeSystem.getId());
return code;
}
return code;
}
}

View File

@ -4,6 +4,10 @@ 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})

View File

@ -0,0 +1,51 @@
package ca.uhn.fhir.cql.dstu3.provider;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.opencds.cqf.cql.engine.runtime.Code;
import org.opencds.cqf.cql.engine.terminology.TerminologyProvider;
import org.opencds.cqf.cql.engine.terminology.ValueSetInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.cql.BaseCqlDstu3Test;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
public class JpaTerminologyProviderTest extends BaseCqlDstu3Test {
private static final Logger ourLog = LoggerFactory.getLogger(JpaTerminologyProviderTest.class);
@Autowired
protected TerminologyProvider jpaTerminologyProvider;
@Autowired
protected DaoConfig daoConfig;
@BeforeEach
public void before() throws IOException {
// Load executable (i.e. "pre-expanded") value set
loadResource("dstu3/provider/test-executable-value-set.json");
}
@Test
public void testTerminologyProviderExpand() {
ValueSetInfo valueSetInfo = new ValueSetInfo().withId("http://test.com/fhir/ValueSet/test-executable-value-set");
Iterable<Code> codeIterable = this.jpaTerminologyProvider.expand(valueSetInfo);
assertNotNull(codeIterable);
List<Code> codes = new ArrayList<>();
codeIterable.forEach(codes::add);
assertThat(codes, hasSize(2));
}
}

View File

@ -0,0 +1,54 @@
package ca.uhn.fhir.cql.r4;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.jupiter.api.Assertions.assertEquals;
import java.io.IOException;
import java.math.BigDecimal;
import org.hl7.fhir.r4.model.IdType;
import org.hl7.fhir.r4.model.MeasureReport;
import org.hl7.fhir.r4.model.Quantity;
import org.hl7.fhir.r4.model.MeasureReport.MeasureReportGroupPopulationComponent;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.cql.BaseCqlR4Test;
import ca.uhn.fhir.cql.common.provider.CqlProviderTestBase;
import ca.uhn.fhir.cql.r4.provider.MeasureOperationsProvider;
public class CqlMeasureEvaluationR4Test extends BaseCqlR4Test implements CqlProviderTestBase {
private static final IdType measureId = new IdType("Measure", "measure-EXM130-7.3.000");
private static final String periodStart = "2019-01-01";
private static final String periodEnd = "2019-12-31";
@Autowired
MeasureOperationsProvider myMeasureOperationsProvider;
public void loadBundles() throws IOException {
loadBundle("r4/connectathon/EXM130-7.3.000-bundle.json");
}
@Test
public void testExm130PatientNumerator() throws IOException {
loadBundles();
MeasureReport report = myMeasureOperationsProvider.evaluateMeasure(measureId, periodStart, periodEnd, null, "patient",
"numer-EXM130", null, null, null, null, null, null);
// Assert it worked
assertThat(report.getGroup(), hasSize(1));
assertEquals(new BigDecimal("1.0"), report.getGroupFirstRep().getMeasureScore().getValue());
}
@Test
public void testExm130PatientDenominator() throws IOException {
loadBundles();
MeasureReport report = myMeasureOperationsProvider.evaluateMeasure(measureId, periodStart, periodEnd, null, "patient",
"denom-EXM130", null, null, null, null, null, null);
// Assert it worked
assertThat(report.getGroup(), hasSize(1));
assertEquals(new BigDecimal("0.0"), report.getGroupFirstRep().getMeasureScore().getValue());
}
}

View File

@ -0,0 +1,51 @@
package ca.uhn.fhir.cql.r4.provider;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.hasSize;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.opencds.cqf.cql.engine.runtime.Code;
import org.opencds.cqf.cql.engine.terminology.TerminologyProvider;
import org.opencds.cqf.cql.engine.terminology.ValueSetInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import ca.uhn.fhir.cql.BaseCqlR4Test;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
public class JpaTerminologyProviderTest extends BaseCqlR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(JpaTerminologyProviderTest.class);
@Autowired
protected TerminologyProvider jpaTerminologyProvider;
@Autowired
protected DaoConfig daoConfig;
@BeforeEach
public void before() throws IOException {
// Load executable (i.e. "pre-expanded") value set
loadResource("r4/provider/test-executable-value-set.json");
}
@Test
public void testTerminologyProviderExpand() {
ValueSetInfo valueSetInfo = new ValueSetInfo().withId("http://test.com/fhir/ValueSet/test-executable-value-set");
Iterable<Code> codeIterable = this.jpaTerminologyProvider.expand(valueSetInfo);
assertNotNull(codeIterable);
List<Code> codes = new ArrayList<>();
codeIterable.forEach(codes::add);
assertThat(codes, hasSize(2));
}
}

View File

@ -0,0 +1,39 @@
{
"resourceType": "ValueSet",
"id": "test-executable-value-set",
"meta" : {
"profile" : [
"http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-executablevalueset"
]
},
"url": "http://test.com/fhir/ValueSet/test-executable-value-set",
"extension" : [
{
"url" : "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability",
"valueCode" : "executable"
},
{
"url" : "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeRepresentationLevel",
"valueCode" : "executable"
},
{
"url" : "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-usageWarning",
"valueString" : "This value set contains a point-in-time expansion enumerating the codes that meet the value set intent. As new versions of the code systems used by the value set are released, the contents of this expansion will need to be updated to incorporate newly defined codes that meet the value set intent. Before, and periodically during production use, the value set expansion contents SHOULD be updated. The value set expansion specifies the timestamp when the expansion was produced, SHOULD contain the parameters used for the expansion, and SHALL contain the codes that are obtained by evaluating the value set definition. If this is ONLY an executable value set, a distributable definition of the value set must be obtained to compute the updated expansion."
}
],
"expansion": {
"timestamp" : "2020-03-26T17:39:09-06:00",
"contains" : [
{
"system" : "http://test.com/codesystem/test",
"code" : "1234",
"display" : "1234"
},
{
"system" : "http://test.com/codesystem/test",
"code" : "ABCD",
"display" : "ABCD"
}
]
}
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,39 @@
{
"resourceType": "ValueSet",
"id": "test-executable-value-set",
"meta" : {
"profile" : [
"http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-executablevalueset"
]
},
"url": "http://test.com/fhir/ValueSet/test-executable-value-set",
"extension" : [
{
"url" : "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeCapability",
"valueCode" : "executable"
},
{
"url" : "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-knowledgeRepresentationLevel",
"valueCode" : "executable"
},
{
"url" : "http://hl7.org/fhir/uv/cpg/StructureDefinition/cpg-usageWarning",
"valueString" : "This value set contains a point-in-time expansion enumerating the codes that meet the value set intent. As new versions of the code systems used by the value set are released, the contents of this expansion will need to be updated to incorporate newly defined codes that meet the value set intent. Before, and periodically during production use, the value set expansion contents SHOULD be updated. The value set expansion specifies the timestamp when the expansion was produced, SHOULD contain the parameters used for the expansion, and SHALL contain the codes that are obtained by evaluating the value set definition. If this is ONLY an executable value set, a distributable definition of the value set must be obtained to compute the updated expansion."
}
],
"expansion": {
"timestamp" : "2020-03-26T17:39:09-06:00",
"contains" : [
{
"system" : "http://test.com/codesystem/test",
"code" : "1234",
"display" : "1234"
},
{
"system" : "http://test.com/codesystem/test",
"code" : "ABCD",
"display" : "ABCD"
}
]
}
}