Fix handling of filters on valueset expansions

This commit is contained in:
James 2017-02-10 11:05:59 -05:00
parent 12e047b931
commit f1828d1ca8
5 changed files with 771 additions and 572 deletions

View File

@ -10,7 +10,7 @@ package ca.uhn.fhir.jpa.dao.dstu3;
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at * You may obtain a copy of the License at
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, * distributed under the License is distributed on an "AS IS" BASIS,
@ -33,6 +33,7 @@ import org.hl7.fhir.dstu3.model.CodeSystem;
import org.hl7.fhir.dstu3.model.CodeableConcept; import org.hl7.fhir.dstu3.model.CodeableConcept;
import org.hl7.fhir.dstu3.model.Coding; import org.hl7.fhir.dstu3.model.Coding;
import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus; import org.hl7.fhir.dstu3.model.Enumerations.PublicationStatus;
import org.hl7.fhir.dstu3.model.IntegerType;
import org.hl7.fhir.dstu3.model.UriType; import org.hl7.fhir.dstu3.model.UriType;
import org.hl7.fhir.dstu3.model.ValueSet; import org.hl7.fhir.dstu3.model.ValueSet;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
@ -82,12 +83,12 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
return retVal; return retVal;
// ValueSetExpansionComponent expansion = outcome.getValueset().getExpansion(); // ValueSetExpansionComponent expansion = outcome.getValueset().getExpansion();
// //
// ValueSet retVal = new ValueSet(); // ValueSet retVal = new ValueSet();
// retVal.getMeta().setLastUpdated(new Date()); // retVal.getMeta().setLastUpdated(new Date());
// retVal.setExpansion(expansion); // retVal.setExpansion(expansion);
// return retVal; // return retVal;
} }
private void validateIncludes(String name, List<ConceptSetComponent> listToValidate) { private void validateIncludes(String name, List<ConceptSetComponent> listToValidate) {
@ -137,11 +138,11 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
public ValueSet expand(ValueSet source, String theFilter) { public ValueSet expand(ValueSet source, String theFilter) {
ValueSet toExpand = new ValueSet(); ValueSet toExpand = new ValueSet();
// for (UriType next : source.getCompose().getInclude()) { // for (UriType next : source.getCompose().getInclude()) {
// ConceptSetComponent include = toExpand.getCompose().addInclude(); // ConceptSetComponent include = toExpand.getCompose().addInclude();
// include.setSystem(next.getValue()); // include.setSystem(next.getValue());
// addFilterIfPresent(theFilter, include); // addFilterIfPresent(theFilter, include);
// } // }
for (ConceptSetComponent next : source.getCompose().getInclude()) { for (ConceptSetComponent next : source.getCompose().getInclude()) {
toExpand.getCompose().addInclude(next); toExpand.getCompose().addInclude(next);
@ -155,18 +156,41 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
toExpand.getCompose().getExclude().addAll(source.getCompose().getExclude()); toExpand.getCompose().getExclude().addAll(source.getCompose().getExclude());
ValueSet retVal = doExpand(toExpand); ValueSet retVal = doExpand(toExpand);
if (isNotBlank(theFilter)) {
applyFilter(retVal.getExpansion().getTotalElement(), retVal.getExpansion().getContains(), theFilter);
}
return retVal; return retVal;
} }
private void applyFilter(IntegerType theTotalElement, List<ValueSetExpansionContainsComponent> theContains, String theFilter) {
for (int idx = 0; idx < theContains.size(); idx++) {
ValueSetExpansionContainsComponent next = theContains.get(idx);
if (isBlank(next.getDisplay()) || !org.apache.commons.lang3.StringUtils.containsIgnoreCase(next.getDisplay(), theFilter)) {
theContains.remove(idx);
idx--;
if (theTotalElement.getValue() != null) {
theTotalElement.setValue(theTotalElement.getValue() - 1);
}
}
applyFilter(theTotalElement, next.getContains(), theFilter);
}
}
private void addFilterIfPresent(String theFilter, ConceptSetComponent include) { private void addFilterIfPresent(String theFilter, ConceptSetComponent include) {
if (isNotBlank(theFilter)) { if (ElementUtil.isEmpty(include.getConcept())) {
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue(theFilter); if (isNotBlank(theFilter)) {
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue(theFilter);
}
} }
} }
@Override @Override
public ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode, IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, Coding theCoding, public ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCode(IPrimitiveType<String> theValueSetIdentifier, IIdType theId, IPrimitiveType<String> theCode,
IPrimitiveType<String> theSystem, IPrimitiveType<String> theDisplay, Coding theCoding,
CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) { CodeableConcept theCodeableConcept, RequestDetails theRequestDetails) {
List<IIdType> valueSetIds = Collections.emptyList(); List<IIdType> valueSetIds = Collections.emptyList();
@ -226,7 +250,8 @@ public class FhirResourceDaoValueSetDstu3 extends FhirResourceDaoDstu3<ValueSet>
return thePrimitive != null ? thePrimitive.getValue() : null; return thePrimitive != null ? thePrimitive.getValue() : null;
} }
private ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCodeIsInContains(List<ValueSetExpansionContainsComponent> contains, String theSystem, String theCode, Coding theCoding, CodeableConcept theCodeableConcept) { private ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult validateCodeIsInContains(List<ValueSetExpansionContainsComponent> contains, String theSystem, String theCode,
Coding theCoding, CodeableConcept theCodeableConcept) {
for (ValueSetExpansionContainsComponent nextCode : contains) { for (ValueSetExpansionContainsComponent nextCode : contains) {
ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult result = validateCodeIsInContains(nextCode.getContains(), theSystem, theCode, theCoding, theCodeableConcept); ca.uhn.fhir.jpa.dao.IFhirResourceDaoValueSet.ValidateCodeResult result = validateCodeIsInContains(nextCode.getContains(), theSystem, theCode, theCoding, theCodeableConcept);
if (result != null) { if (result != null) {

View File

@ -16,6 +16,7 @@ import org.hl7.fhir.dstu3.model.AllergyIntolerance.AllergyIntoleranceCategory;
import org.hl7.fhir.dstu3.model.AllergyIntolerance.AllergyIntoleranceClinicalStatus; import org.hl7.fhir.dstu3.model.AllergyIntolerance.AllergyIntoleranceClinicalStatus;
import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode; import org.hl7.fhir.dstu3.model.CodeSystem.CodeSystemContentMode;
import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent; import org.hl7.fhir.dstu3.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptReferenceComponent;
import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.dstu3.model.ValueSet.ConceptSetComponent;
import org.hl7.fhir.dstu3.model.ValueSet.FilterOperator; import org.hl7.fhir.dstu3.model.ValueSet.FilterOperator;
import org.hl7.fhir.dstu3.model.ValueSet.ValueSetComposeComponent; import org.hl7.fhir.dstu3.model.ValueSet.ValueSetComposeComponent;
@ -53,6 +54,9 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
public static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system"; public static final String URL_MY_CODE_SYSTEM = "http://example.com/my_code_system";
public static final String URL_MY_VALUE_SET = "http://example.com/my_value_set"; public static final String URL_MY_VALUE_SET = "http://example.com/my_value_set";
@Autowired
private IHapiTerminologySvc myHapiTerminologySvc;
@After @After
public void after() { public void after() {
myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize()); myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize());
@ -116,6 +120,37 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
createLocalVs(codeSystem); createLocalVs(codeSystem);
} }
private CodeSystem createExternalCsDogs() {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(URL_MY_CODE_SYSTEM);
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong());
TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table);
cs.setResourceVersionId(table.getVersion());
TermConcept hello = new TermConcept(cs, "hello").setDisplay("Hello");
cs.getConcepts().add(hello);
TermConcept goodbye = new TermConcept(cs, "goodbye").setDisplay("Goodbye");
cs.getConcepts().add(goodbye);
TermConcept dogs = new TermConcept(cs, "dogs").setDisplay("Dogs");
cs.getConcepts().add(dogs);
TermConcept labrador = new TermConcept(cs, "labrador").setDisplay("Labrador");
dogs.addChild(labrador, RelationshipTypeEnum.ISA);
TermConcept beagle = new TermConcept(cs, "beagle").setDisplay("Beagle");
dogs.addChild(beagle, RelationshipTypeEnum.ISA);
myTermSvc.storeNewCodeSystemVersion(table.getId(), URL_MY_CODE_SYSTEM, cs);
return codeSystem;
}
private void createLocalCsAndVs() { private void createLocalCsAndVs() {
//@formatter:off //@formatter:off
CodeSystem codeSystem = new CodeSystem(); CodeSystem codeSystem = new CodeSystem();
@ -144,6 +179,22 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
myValueSetDao.create(valueSet, mySrd); myValueSetDao.create(valueSet, mySrd);
} }
private void logAndValidateValueSet(ValueSet theResult) {
IParser parser = myFhirCtx.newXmlParser().setPrettyPrint(true);
String encoded = parser.encodeResourceToString(theResult);
ourLog.info(encoded);
FhirValidator validator = myFhirCtx.newValidator();
validator.setValidateAgainstStandardSchema(true);
validator.setValidateAgainstStandardSchematron(true);
ValidationResult result = validator.validateWithResult(theResult);
if (!result.isSuccessful()) {
ourLog.info(parser.encodeResourceToString(result.toOperationOutcome()));
fail(parser.encodeResourceToString(result.toOperationOutcome()));
}
}
@Test @Test
public void testCodeSystemCreateDuplicateFails() { public void testCodeSystemCreateDuplicateFails() {
CodeSystem codeSystem = new CodeSystem(); CodeSystem codeSystem = new CodeSystem();
@ -162,28 +213,6 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
} }
} }
@Test
public void testLookupSnomed() {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl("http://snomed.info/sct");
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong());
TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table);
cs.setResourceVersionId(table.getVersion());
TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A");
cs.getConcepts().add(parentA);
myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct", cs);
StringType code = new StringType("ParentA");
StringType system = new StringType("http://snomed.info/sct");
LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd);
assertEquals(true, outcome.isFound());
}
@Test @Test
public void testCodeSystemWithDefinedCodes() { public void testCodeSystemWithDefinedCodes() {
//@formatter:off //@formatter:off
@ -207,170 +236,6 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
} }
@Test
public void testExpandWithExcludeInExternalValueSet() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
ConceptSetComponent exclude = vs.getCompose().addExclude();
exclude.setSystem(URL_MY_CODE_SYSTEM);
exclude.addConcept().setCode("childAA");
exclude.addConcept().setCode("childAAA");
ValueSet result = myValueSetDao.expand(vs, null);
logAndValidateValueSet(result);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentA", "ParentB", "childAB", "childAAB", "ParentC", "childBA", "childCA"));
}
private void logAndValidateValueSet(ValueSet theResult) {
IParser parser = myFhirCtx.newXmlParser().setPrettyPrint(true);
String encoded = parser.encodeResourceToString(theResult);
ourLog.info(encoded);
FhirValidator validator = myFhirCtx.newValidator();
validator.setValidateAgainstStandardSchema(true);
validator.setValidateAgainstStandardSchematron(true);
ValidationResult result = validator.validateWithResult(theResult);
if (!result.isSuccessful()) {
ourLog.info(parser.encodeResourceToString(result.toOperationOutcome()));
fail(parser.encodeResourceToString(result.toOperationOutcome()));
}
}
@Test
public void testExpandWithInvalidExclude() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
/*
* No system set on exclude
*/
ConceptSetComponent exclude = vs.getCompose().addExclude();
exclude.addConcept().setCode("childAA");
exclude.addConcept().setCode("childAAA");
try {
myValueSetDao.expand(vs, null);
fail();
} catch (InvalidRequestException e) {
assertEquals("ValueSet contains exclude criteria with no system defined", e.getMessage());
}
}
@Test
public void testExpandWithNoResultsInLocalValueSet1() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("ZZZZ");
try {
myValueSetDao.expand(vs, null);
fail();
} catch (InvalidRequestException e) {
assertEquals("Unable to find code 'ZZZZ' in code system http://example.com/my_code_system", e.getMessage());
}
}
@Test
public void testReindex() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("ZZZZ");
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(null);
myTermSvc.saveDeferred();
mySystemDao.performReindexingPass(null);
myTermSvc.saveDeferred();
// Again
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(null);
myTermSvc.saveDeferred();
mySystemDao.performReindexingPass(null);
myTermSvc.saveDeferred();
}
@Test
public void testExpandWithNoResultsInLocalValueSet2() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM + "AA");
include.addConcept().setCode("A");
try {
myValueSetDao.expand(vs, null);
fail();
} catch (InvalidRequestException e) {
assertEquals("unable to find code system http://example.com/my_code_systemAA", e.getMessage());
}
}
@Test
public void testExpandWithIsAInExternalValueSet() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addFilter().setOp(FilterOperator.ISA).setValue("childAA").setProperty("concept");
ValueSet result = myValueSetDao.expand(vs, null);
logAndValidateValueSet(result);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("childAAA", "childAAB"));
}
@Autowired
private IHapiTerminologySvc myHapiTerminologySvc;
@Test
public void testExpandWithIsAInExternalValueSetReindex() {
BaseHapiTerminologySvc.setForceSaveDeferredAlwaysForUnitTest(true);
createExternalCsAndLocalVs();
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(100);
mySystemDao.performReindexingPass(100);
myHapiTerminologySvc.saveDeferred();
myHapiTerminologySvc.saveDeferred();
myHapiTerminologySvc.saveDeferred();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addFilter().setOp(FilterOperator.ISA).setValue("childAA").setProperty("concept");
ValueSet result = myValueSetDao.expand(vs, null);
logAndValidateValueSet(result);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("childAAA", "childAAB"));
}
@Test @Test
public void testExpandInvalid() { public void testExpandInvalid() {
createExternalCsAndLocalVs(); createExternalCsAndLocalVs();
@ -390,43 +255,110 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
} }
@Test @Test
public void testExpandWithSystemAndCodesInExternalValueSet() { public void testExpandWithCodesAndDisplayFilterBlank() {
createExternalCsAndLocalVs(); CodeSystem codeSystem = createExternalCsDogs();
ValueSet vs = new ValueSet(); ValueSet valueSet = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude(); valueSet.setUrl(URL_MY_VALUE_SET);
include.setSystem(URL_MY_CODE_SYSTEM); valueSet.getCompose()
include.addConcept().setCode("ParentA"); .addInclude()
include.addConcept().setCode("childAA"); .setSystem(codeSystem.getUrl())
include.addConcept().setCode("childAAA"); .addConcept(new ConceptReferenceComponent().setCode("hello"))
.addConcept(new ConceptReferenceComponent().setCode("goodbye"));
valueSet.getCompose()
.addInclude()
.setSystem(codeSystem.getUrl())
.addFilter()
.setProperty("concept")
.setOp(FilterOperator.ISA)
.setValue("dogs");
myValueSetDao.create(valueSet, mySrd);
ValueSet result = myValueSetDao.expand(vs, null); ValueSet result = myValueSetDao.expand(valueSet, "");
logAndValidateValueSet(result); logAndValidateValueSet(result);
assertEquals(4, result.getExpansion().getTotal());
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains()); ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAA")); assertThat(codes, containsInAnyOrder("hello", "goodbye", "labrador", "beagle"));
int idx = codes.indexOf("childAA");
assertEquals("childAA", result.getExpansion().getContains().get(idx).getCode());
assertEquals("Child AA", result.getExpansion().getContains().get(idx).getDisplay());
assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(idx).getSystem());
} }
@Test @Test
public void testExpandWithSystemAndFilterInExternalValueSet() { public void testExpandWithCodesAndDisplayFilterPartialOnFilter() {
createExternalCsAndLocalVs(); CodeSystem codeSystem = createExternalCsDogs();
ValueSet vs = new ValueSet(); ValueSet valueSet = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude(); valueSet.setUrl(URL_MY_VALUE_SET);
include.setSystem(URL_MY_CODE_SYSTEM); valueSet.getCompose()
.addInclude()
.setSystem(codeSystem.getUrl())
.addConcept(new ConceptReferenceComponent().setCode("hello"))
.addConcept(new ConceptReferenceComponent().setCode("goodbye"));
valueSet.getCompose()
.addInclude()
.setSystem(codeSystem.getUrl())
.addFilter()
.setProperty("concept")
.setOp(FilterOperator.ISA)
.setValue("dogs");
myValueSetDao.create(valueSet, mySrd);
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("Parent B"); ValueSet result = myValueSetDao.expand(valueSet, "lab");
ValueSet result = myValueSetDao.expand(vs, null);
logAndValidateValueSet(result); logAndValidateValueSet(result);
assertEquals(1, result.getExpansion().getTotal());
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains()); ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentB")); assertThat(codes, containsInAnyOrder("labrador"));
}
@Test
public void testExpandWithCodesAndDisplayFilterPartialOnCodes() {
CodeSystem codeSystem = createExternalCsDogs();
ValueSet valueSet = new ValueSet();
valueSet.setUrl(URL_MY_VALUE_SET);
valueSet.getCompose()
.addInclude()
.setSystem(codeSystem.getUrl())
.addConcept(new ConceptReferenceComponent().setCode("hello"))
.addConcept(new ConceptReferenceComponent().setCode("goodbye"));
valueSet.getCompose()
.addInclude()
.setSystem(codeSystem.getUrl())
.addFilter()
.setProperty("concept")
.setOp(FilterOperator.ISA)
.setValue("dogs");
myValueSetDao.create(valueSet, mySrd);
ValueSet result = myValueSetDao.expand(valueSet, "hel");
logAndValidateValueSet(result);
assertEquals(1, result.getExpansion().getTotal());
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("hello"));
}
@Test
public void testExpandWithCodesAndDisplayFilterPartialOnExpansion() {
CodeSystem codeSystem = createExternalCsDogs();
ValueSet valueSet = new ValueSet();
valueSet.setUrl(URL_MY_VALUE_SET);
valueSet.getCompose().addInclude().setSystem(codeSystem.getUrl());
myValueSetDao.create(valueSet, mySrd);
ValueSet result = myValueSetDao.expand(valueSet, "lab");
logAndValidateValueSet(result);
assertEquals(1, result.getExpansion().getTotal());
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("labrador"));
} }
@ -463,6 +395,127 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
} }
@Test
public void testExpandWithExcludeInExternalValueSet() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
ConceptSetComponent exclude = vs.getCompose().addExclude();
exclude.setSystem(URL_MY_CODE_SYSTEM);
exclude.addConcept().setCode("childAA");
exclude.addConcept().setCode("childAAA");
ValueSet result = myValueSetDao.expand(vs, null);
logAndValidateValueSet(result);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentA", "ParentB", "childAB", "childAAB", "ParentC", "childBA", "childCA"));
}
@Test
public void testExpandWithInvalidExclude() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
/*
* No system set on exclude
*/
ConceptSetComponent exclude = vs.getCompose().addExclude();
exclude.addConcept().setCode("childAA");
exclude.addConcept().setCode("childAAA");
try {
myValueSetDao.expand(vs, null);
fail();
} catch (InvalidRequestException e) {
assertEquals("ValueSet contains exclude criteria with no system defined", e.getMessage());
}
}
@Test
public void testExpandWithIsAInExternalValueSet() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addFilter().setOp(FilterOperator.ISA).setValue("childAA").setProperty("concept");
ValueSet result = myValueSetDao.expand(vs, null);
logAndValidateValueSet(result);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("childAAA", "childAAB"));
}
@Test
public void testExpandWithIsAInExternalValueSetReindex() {
BaseHapiTerminologySvc.setForceSaveDeferredAlwaysForUnitTest(true);
createExternalCsAndLocalVs();
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(100);
mySystemDao.performReindexingPass(100);
myHapiTerminologySvc.saveDeferred();
myHapiTerminologySvc.saveDeferred();
myHapiTerminologySvc.saveDeferred();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addFilter().setOp(FilterOperator.ISA).setValue("childAA").setProperty("concept");
ValueSet result = myValueSetDao.expand(vs, null);
logAndValidateValueSet(result);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("childAAA", "childAAB"));
}
@Test
public void testExpandWithNoResultsInLocalValueSet1() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("ZZZZ");
try {
myValueSetDao.expand(vs, null);
fail();
} catch (InvalidRequestException e) {
assertEquals("Unable to find code 'ZZZZ' in code system http://example.com/my_code_system", e.getMessage());
}
}
@Test
public void testExpandWithNoResultsInLocalValueSet2() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM + "AA");
include.addConcept().setCode("A");
try {
myValueSetDao.expand(vs, null);
fail();
} catch (InvalidRequestException e) {
assertEquals("unable to find code system http://example.com/my_code_systemAA", e.getMessage());
}
}
@Test @Test
public void testExpandWithSystemAndCodesAndFilterKeywordInLocalValueSet() { public void testExpandWithSystemAndCodesAndFilterKeywordInLocalValueSet() {
createLocalCsAndVs(); createLocalCsAndVs();
@ -493,6 +546,29 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
// //
} }
@Test
public void testExpandWithSystemAndCodesInExternalValueSet() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("ParentA");
include.addConcept().setCode("childAA");
include.addConcept().setCode("childAAA");
ValueSet result = myValueSetDao.expand(vs, null);
logAndValidateValueSet(result);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentA", "childAA", "childAAA"));
int idx = codes.indexOf("childAA");
assertEquals("childAA", result.getExpansion().getContains().get(idx).getCode());
assertEquals("Child AA", result.getExpansion().getContains().get(idx).getDisplay());
assertEquals(URL_MY_CODE_SYSTEM, result.getExpansion().getContains().get(idx).getSystem());
}
@Test @Test
public void testExpandWithSystemAndCodesInLocalValueSet() { public void testExpandWithSystemAndCodesInLocalValueSet() {
createLocalCsAndVs(); createLocalCsAndVs();
@ -520,6 +596,43 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
// //
} }
@Test
public void testExpandWithSystemAndDisplayFilterBlank() {
CodeSystem codeSystem = createExternalCsDogs();
ValueSet valueSet = new ValueSet();
valueSet.setUrl(URL_MY_VALUE_SET);
valueSet.getCompose()
.addInclude()
.setSystem(codeSystem.getUrl());
ValueSet result = myValueSetDao.expand(valueSet, "");
logAndValidateValueSet(result);
assertEquals(5, result.getExpansion().getTotal());
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("hello", "goodbye", "dogs", "labrador", "beagle"));
}
@Test
public void testExpandWithSystemAndFilterInExternalValueSet() {
createExternalCsAndLocalVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addFilter().setProperty("display").setOp(FilterOperator.EQUAL).setValue("Parent B");
ValueSet result = myValueSetDao.expand(vs, null);
logAndValidateValueSet(result);
ArrayList<String> codes = toCodesContains(result.getExpansion().getContains());
assertThat(codes, containsInAnyOrder("ParentB"));
}
@Test @Test
public void testIndexingIsDeferredForLargeCodeSystems() { public void testIndexingIsDeferredForLargeCodeSystems() {
myDaoConfig.setDeferIndexingForCodesystemsOfSize(1); myDaoConfig.setDeferIndexingForCodesystemsOfSize(1);
@ -560,6 +673,28 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
assertThat(encoded, containsStringIgnoringCase("<code value=\"childAAB\"/>")); assertThat(encoded, containsStringIgnoringCase("<code value=\"childAAB\"/>"));
} }
@Test
public void testLookupSnomed() {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl("http://snomed.info/sct");
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
ResourceTable table = myResourceTableDao.findOne(id.getIdPartAsLong());
TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table);
cs.setResourceVersionId(table.getVersion());
TermConcept parentA = new TermConcept(cs, "ParentA").setDisplay("Parent A");
cs.getConcepts().add(parentA);
myTermSvc.storeNewCodeSystemVersion(table.getId(), "http://snomed.info/sct", cs);
StringType code = new StringType("ParentA");
StringType system = new StringType("http://snomed.info/sct");
LookupCodeResult outcome = myCodeSystemDao.lookupCode(code, system, null, mySrd);
assertEquals(true, outcome.isFound());
}
/** /**
* Can't currently abort costly * Can't currently abort costly
*/ */
@ -594,6 +729,30 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
} }
} }
@Test
public void testReindex() {
createLocalCsAndVs();
ValueSet vs = new ValueSet();
ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(URL_MY_CODE_SYSTEM);
include.addConcept().setCode("ZZZZ");
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(null);
myTermSvc.saveDeferred();
mySystemDao.performReindexingPass(null);
myTermSvc.saveDeferred();
// Again
mySystemDao.markAllResourcesForReindexing();
mySystemDao.performReindexingPass(null);
myTermSvc.saveDeferred();
mySystemDao.performReindexingPass(null);
myTermSvc.saveDeferred();
}
@Test @Test
public void testSearchCodeAboveLocalCodesystem() { public void testSearchCodeAboveLocalCodesystem() {
createLocalCsAndVs(); createLocalCsAndVs();
@ -636,107 +795,6 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
} }
@Test
public void testSearchCodeBelowLocalCodesystem() {
createLocalCsAndVs();
Observation obsAA = new Observation();
obsAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("AA");
IIdType idAA = myObservationDao.create(obsAA, mySrd).getId().toUnqualifiedVersionless();
Observation obsBA = new Observation();
obsBA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("BA");
IIdType idBA = myObservationDao.create(obsBA, mySrd).getId().toUnqualifiedVersionless();
Observation obsCA = new Observation();
obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA");
IIdType idCA = myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "A").setModifier(TokenParamModifier.BELOW));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idAA.getValue()));
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "AAA").setModifier(TokenParamModifier.BELOW));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
}
@Test
public void testSearchCodeInBuiltInValueSet() {
AllergyIntolerance ai1 = new AllergyIntolerance();
ai1.setClinicalStatus(AllergyIntoleranceClinicalStatus.ACTIVE);
String id1 = myAllergyIntoleranceDao.create(ai1, mySrd).getId().toUnqualifiedVersionless().getValue();
AllergyIntolerance ai2 = new AllergyIntolerance();
ai2.setClinicalStatus(AllergyIntoleranceClinicalStatus.RESOLVED);
String id2 = myAllergyIntoleranceDao.create(ai2, mySrd).getId().toUnqualifiedVersionless().getValue();
AllergyIntolerance ai3 = new AllergyIntolerance();
ai3.setClinicalStatus(AllergyIntoleranceClinicalStatus.INACTIVE);
String id3 = myAllergyIntoleranceDao.create(ai3, mySrd).getId().toUnqualifiedVersionless().getValue();
SearchParameterMap params;
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-clinical-status").setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), containsInAnyOrder(id1, id2, id3));
// No codes in this one
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-criticality").setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), empty());
// Invalid VS
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/FOO").setModifier(TokenParamModifier.IN));
try {
myAllergyIntoleranceDao.search(params);
} catch (InvalidRequestException e) {
assertEquals("Unable to find imported value set http://hl7.org/fhir/ValueSet/FOO", e.getMessage());
}
}
/**
* Todo: not yet implemented
*/
@Test
@Ignore
public void testSearchCodeNotInBuiltInValueSet() {
AllergyIntolerance ai1 = new AllergyIntolerance();
ai1.setClinicalStatus(AllergyIntoleranceClinicalStatus.ACTIVE);
String id1 = myAllergyIntoleranceDao.create(ai1, mySrd).getId().toUnqualifiedVersionless().getValue();
AllergyIntolerance ai2 = new AllergyIntolerance();
ai2.setClinicalStatus(AllergyIntoleranceClinicalStatus.RESOLVED);
String id2 = myAllergyIntoleranceDao.create(ai2, mySrd).getId().toUnqualifiedVersionless().getValue();
AllergyIntolerance ai3 = new AllergyIntolerance();
ai3.setClinicalStatus(AllergyIntoleranceClinicalStatus.INACTIVE);
String id3 = myAllergyIntoleranceDao.create(ai3, mySrd).getId().toUnqualifiedVersionless().getValue();
SearchParameterMap params;
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-status").setModifier(TokenParamModifier.NOT_IN));
assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), empty());
// No codes in this one
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-criticality").setModifier(TokenParamModifier.NOT_IN));
assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), containsInAnyOrder(id1, id2, id3));
// Invalid VS
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/FOO").setModifier(TokenParamModifier.NOT_IN));
try {
myAllergyIntoleranceDao.search(params);
} catch (InvalidRequestException e) {
assertEquals("Unable to find imported value set http://hl7.org/fhir/ValueSet/FOO", e.getMessage());
}
}
@Test @Test
public void testSearchCodeBelowBuiltInCodesystem() { public void testSearchCodeBelowBuiltInCodesystem() {
AllergyIntolerance ai1 = new AllergyIntolerance(); AllergyIntolerance ai1 = new AllergyIntolerance();
@ -824,6 +882,68 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
} }
@Test
public void testSearchCodeBelowLocalCodesystem() {
createLocalCsAndVs();
Observation obsAA = new Observation();
obsAA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("AA");
IIdType idAA = myObservationDao.create(obsAA, mySrd).getId().toUnqualifiedVersionless();
Observation obsBA = new Observation();
obsBA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("BA");
IIdType idBA = myObservationDao.create(obsBA, mySrd).getId().toUnqualifiedVersionless();
Observation obsCA = new Observation();
obsCA.getCode().addCoding().setSystem(URL_MY_CODE_SYSTEM).setCode("CA");
IIdType idCA = myObservationDao.create(obsCA, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "A").setModifier(TokenParamModifier.BELOW));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), containsInAnyOrder(idAA.getValue()));
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(URL_MY_CODE_SYSTEM, "AAA").setModifier(TokenParamModifier.BELOW));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
}
@Test
public void testSearchCodeInBuiltInValueSet() {
AllergyIntolerance ai1 = new AllergyIntolerance();
ai1.setClinicalStatus(AllergyIntoleranceClinicalStatus.ACTIVE);
String id1 = myAllergyIntoleranceDao.create(ai1, mySrd).getId().toUnqualifiedVersionless().getValue();
AllergyIntolerance ai2 = new AllergyIntolerance();
ai2.setClinicalStatus(AllergyIntoleranceClinicalStatus.RESOLVED);
String id2 = myAllergyIntoleranceDao.create(ai2, mySrd).getId().toUnqualifiedVersionless().getValue();
AllergyIntolerance ai3 = new AllergyIntolerance();
ai3.setClinicalStatus(AllergyIntoleranceClinicalStatus.INACTIVE);
String id3 = myAllergyIntoleranceDao.create(ai3, mySrd).getId().toUnqualifiedVersionless().getValue();
SearchParameterMap params;
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-clinical-status").setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), containsInAnyOrder(id1, id2, id3));
// No codes in this one
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-criticality").setModifier(TokenParamModifier.IN));
assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), empty());
// Invalid VS
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/FOO").setModifier(TokenParamModifier.IN));
try {
myAllergyIntoleranceDao.search(params);
} catch (InvalidRequestException e) {
assertEquals("Unable to find imported value set http://hl7.org/fhir/ValueSet/FOO", e.getMessage());
}
}
@Test @Test
public void testSearchCodeInEmptyValueSet() { public void testSearchCodeInEmptyValueSet() {
ValueSet valueSet = new ValueSet(); ValueSet valueSet = new ValueSet();
@ -848,40 +968,6 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
ourLog.info("testSearchCodeInEmptyValueSet done"); ourLog.info("testSearchCodeInEmptyValueSet done");
} }
@Test
public void testSearchCodeInValueSetThatImportsInvalidCodeSystem() {
ValueSet valueSet = new ValueSet();
valueSet.getCompose().addInclude().addValueSet("http://non_existant_VS");
valueSet.setUrl(URL_MY_VALUE_SET);
IIdType vsid = myValueSetDao.create(valueSet, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params;
ourLog.info("testSearchCodeInEmptyValueSet without status");
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
try {
myObservationDao.search(params);
} catch(InvalidRequestException e) {
assertEquals("Unable to expand imported value set: Unable to find imported value set http://non_existant_VS", e.getMessage());
}
// Now let's update
valueSet = new ValueSet();
valueSet.setId(vsid);
valueSet.getCompose().addInclude().setSystem("http://hl7.org/fhir/v3/MaritalStatus").addConcept().setCode("A");
valueSet.setUrl(URL_MY_VALUE_SET);
myValueSetDao.update(valueSet, mySrd).getId().toUnqualifiedVersionless();
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
params.add(Observation.SP_STATUS, new TokenParam(null, "final"));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
}
@Test @Test
public void testSearchCodeInExternalCodesystem() { public void testSearchCodeInExternalCodesystem() {
createExternalCsAndLocalVs(); createExternalCsAndLocalVs();
@ -941,6 +1027,7 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
assertThat(toUnqualifiedVersionlessIdValues(myAuditEventDao.search(params)), empty()); assertThat(toUnqualifiedVersionlessIdValues(myAuditEventDao.search(params)), empty());
} }
@Test @Test
public void testSearchCodeInLocalCodesystem() { public void testSearchCodeInLocalCodesystem() {
createLocalCsAndVs(); createLocalCsAndVs();
@ -963,6 +1050,78 @@ public class FhirResourceDaoDstu3TerminologyTest extends BaseJpaDstu3Test {
} }
@Test
public void testSearchCodeInValueSetThatImportsInvalidCodeSystem() {
ValueSet valueSet = new ValueSet();
valueSet.getCompose().addInclude().addValueSet("http://non_existant_VS");
valueSet.setUrl(URL_MY_VALUE_SET);
IIdType vsid = myValueSetDao.create(valueSet, mySrd).getId().toUnqualifiedVersionless();
SearchParameterMap params;
ourLog.info("testSearchCodeInEmptyValueSet without status");
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
try {
myObservationDao.search(params);
} catch(InvalidRequestException e) {
assertEquals("Unable to expand imported value set: Unable to find imported value set http://non_existant_VS", e.getMessage());
}
// Now let's update
valueSet = new ValueSet();
valueSet.setId(vsid);
valueSet.getCompose().addInclude().setSystem("http://hl7.org/fhir/v3/MaritalStatus").addConcept().setCode("A");
valueSet.setUrl(URL_MY_VALUE_SET);
myValueSetDao.update(valueSet, mySrd).getId().toUnqualifiedVersionless();
params = new SearchParameterMap();
params.add(Observation.SP_CODE, new TokenParam(null, URL_MY_VALUE_SET).setModifier(TokenParamModifier.IN));
params.add(Observation.SP_STATUS, new TokenParam(null, "final"));
assertThat(toUnqualifiedVersionlessIdValues(myObservationDao.search(params)), empty());
}
/**
* Todo: not yet implemented
*/
@Test
@Ignore
public void testSearchCodeNotInBuiltInValueSet() {
AllergyIntolerance ai1 = new AllergyIntolerance();
ai1.setClinicalStatus(AllergyIntoleranceClinicalStatus.ACTIVE);
String id1 = myAllergyIntoleranceDao.create(ai1, mySrd).getId().toUnqualifiedVersionless().getValue();
AllergyIntolerance ai2 = new AllergyIntolerance();
ai2.setClinicalStatus(AllergyIntoleranceClinicalStatus.RESOLVED);
String id2 = myAllergyIntoleranceDao.create(ai2, mySrd).getId().toUnqualifiedVersionless().getValue();
AllergyIntolerance ai3 = new AllergyIntolerance();
ai3.setClinicalStatus(AllergyIntoleranceClinicalStatus.INACTIVE);
String id3 = myAllergyIntoleranceDao.create(ai3, mySrd).getId().toUnqualifiedVersionless().getValue();
SearchParameterMap params;
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-status").setModifier(TokenParamModifier.NOT_IN));
assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), empty());
// No codes in this one
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/allergy-intolerance-criticality").setModifier(TokenParamModifier.NOT_IN));
assertThat(toUnqualifiedVersionlessIdValues(myAllergyIntoleranceDao.search(params)), containsInAnyOrder(id1, id2, id3));
// Invalid VS
params = new SearchParameterMap();
params.add(AllergyIntolerance.SP_CLINICAL_STATUS, new TokenParam(null, "http://hl7.org/fhir/ValueSet/FOO").setModifier(TokenParamModifier.NOT_IN));
try {
myAllergyIntoleranceDao.search(params);
} catch (InvalidRequestException e) {
assertEquals("Unable to find imported value set http://hl7.org/fhir/ValueSet/FOO", e.getMessage());
}
}
private ArrayList<String> toCodesContains(List<ValueSetExpansionContainsComponent> theContains) { private ArrayList<String> toCodesContains(List<ValueSetExpansionContainsComponent> theContains) {
ArrayList<String> retVal = new ArrayList<String>(); ArrayList<String> retVal = new ArrayList<String>();
for (ValueSetExpansionContainsComponent next : theContains) { for (ValueSetExpansionContainsComponent next : theContains) {

View File

@ -0,0 +1,5 @@
[*.java]
charset = utf-8
indent_style = space
indent_size = 2

View File

@ -6,33 +6,33 @@ import java.io.FileNotFoundException;
import java.io.IOException; import java.io.IOException;
/* /*
Copyright (c) 2011+, HL7, Inc * Copyright (c) 2011+, HL7, Inc
All rights reserved. * All rights reserved.
*
Redistribution and use in source and binary forms, with or without modification, * Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met: * are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this * Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer. * list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice, * Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation * this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution. * and/or other materials provided with the distribution.
* Neither the name of HL7 nor the names of its contributors may be used to * Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific * endorse or promote products derived from this software without specific
prior written permission. * prior written permission.
*
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE. * POSSIBILITY OF SUCH DAMAGE.
*
*/ */
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
@ -72,66 +72,67 @@ import org.hl7.fhir.utilities.Utilities;
public class ValueSetExpanderSimple implements ValueSetExpander { public class ValueSetExpanderSimple implements ValueSetExpander {
private List<ValueSetExpansionContainsComponent> codes = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>(); private List<ValueSetExpansionContainsComponent> codes = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
private List<ValueSetExpansionContainsComponent> roots = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>(); private List<ValueSetExpansionContainsComponent> roots = new ArrayList<ValueSet.ValueSetExpansionContainsComponent>();
private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>(); private Map<String, ValueSetExpansionContainsComponent> map = new HashMap<String, ValueSet.ValueSetExpansionContainsComponent>();
private IWorkerContext context; private IWorkerContext context;
private boolean canBeHeirarchy = true; private boolean canBeHeirarchy = true;
private Set<String> excludeKeys = new HashSet<String>(); private Set<String> excludeKeys = new HashSet<String>();
private Set<String> excludeSystems = new HashSet<String>(); private Set<String> excludeSystems = new HashSet<String>();
private ValueSetExpanderFactory factory; private ValueSetExpanderFactory factory;
private ValueSet focus; private ValueSet focus;
private int maxExpansionSize = 500; private int maxExpansionSize = 500;
private int total; private int total;
public ValueSetExpanderSimple(IWorkerContext context, ValueSetExpanderFactory factory) { public ValueSetExpanderSimple(IWorkerContext context, ValueSetExpanderFactory factory) {
super(); super();
this.context = context; this.context = context;
this.factory = factory; this.factory = factory;
} }
public void setMaxExpansionSize(int theMaxExpansionSize) { public void setMaxExpansionSize(int theMaxExpansionSize) {
maxExpansionSize = theMaxExpansionSize; maxExpansionSize = theMaxExpansionSize;
} }
private ValueSetExpansionContainsComponent addCode(String system, String code, String display, ValueSetExpansionContainsComponent parent, List<ConceptDefinitionDesignationComponent> designations,
private ValueSetExpansionContainsComponent addCode(String system, String code, String display, ValueSetExpansionContainsComponent parent, List<ConceptDefinitionDesignationComponent> designations, ExpansionProfile profile, boolean isAbstract, boolean inactive, List<ValueSet> filters) { ExpansionProfile profile, boolean isAbstract, boolean inactive, List<ValueSet> filters) {
if (filters != null && !filters.isEmpty() && !filterContainsCode(filters, system, code)) if (filters != null && !filters.isEmpty() && !filterContainsCode(filters, system, code))
return null; return null;
ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent(); ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent();
n.setSystem(system); n.setSystem(system);
n.setCode(code); n.setCode(code);
if (isAbstract) if (isAbstract)
n.setAbstract(true); n.setAbstract(true);
if (inactive) if (inactive)
n.setInactive(true); n.setInactive(true);
if (profile.getIncludeDesignations() && designations != null) { if (profile.getIncludeDesignations() && designations != null) {
for (ConceptDefinitionDesignationComponent t : designations) { for (ConceptDefinitionDesignationComponent t : designations) {
ToolingExtensions.addLanguageTranslation(n, t.getLanguage(), t.getValue()); ToolingExtensions.addLanguageTranslation(n, t.getLanguage(), t.getValue());
} }
} }
ConceptDefinitionDesignationComponent t = profile.hasLanguage() ? getMatchingLang(designations, profile.getLanguage()) : null; ConceptDefinitionDesignationComponent t = profile.hasLanguage() ? getMatchingLang(designations, profile.getLanguage()) : null;
if (t == null) if (t == null)
n.setDisplay(display); n.setDisplay(display);
else else
n.setDisplay(t.getValue()); n.setDisplay(t.getValue());
String s = key(n); String s = key(n);
if (map.containsKey(s) || excludeKeys.contains(s)) { if (map.containsKey(s) || excludeKeys.contains(s)) {
canBeHeirarchy = false; canBeHeirarchy = false;
} else { } else {
codes.add(n); codes.add(n);
map.put(s, n); map.put(s, n);
} total++;
if (canBeHeirarchy && parent != null) { }
parent.getContains().add(n); if (canBeHeirarchy && parent != null) {
} else { parent.getContains().add(n);
roots.add(n); } else {
} roots.add(n);
return n; }
} return n;
}
private boolean filterContainsCode(List<ValueSet> filters, String system, String code) { private boolean filterContainsCode(List<ValueSet> filters, String system, String code) {
for (ValueSet vse : filters) for (ValueSet vse : filters)
@ -160,93 +161,92 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
return null; return null;
} }
private void addCodeAndDescendents(CodeSystem cs, String system, ConceptDefinitionComponent def, ValueSetExpansionContainsComponent parent, ExpansionProfile profile, List<ValueSet> filters) throws FHIRException { private void addCodeAndDescendents(CodeSystem cs, String system, ConceptDefinitionComponent def, ValueSetExpansionContainsComponent parent, ExpansionProfile profile, List<ValueSet> filters)
if (!CodeSystemUtilities.isDeprecated(cs, def)) { throws FHIRException {
ValueSetExpansionContainsComponent np = null; if (!CodeSystemUtilities.isDeprecated(cs, def)) {
ValueSetExpansionContainsComponent np = null;
boolean abs = CodeSystemUtilities.isNotSelectable(cs, def); boolean abs = CodeSystemUtilities.isNotSelectable(cs, def);
boolean inc = CodeSystemUtilities.isInactive(cs, def); boolean inc = CodeSystemUtilities.isInactive(cs, def);
if (canBeHeirarchy || !abs) if (canBeHeirarchy || !abs)
np = addCode(system, def.getCode(), def.getDisplay(), parent, def.getDesignation(), profile, abs, inc, filters); np = addCode(system, def.getCode(), def.getDisplay(), parent, def.getDesignation(), profile, abs, inc, filters);
for (ConceptDefinitionComponent c : def.getConcept()) for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(cs, system, c, np, profile, filters); addCodeAndDescendents(cs, system, c, np, profile, filters);
} else } else
for (ConceptDefinitionComponent c : def.getConcept()) for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(cs, system, c, null, profile, filters); addCodeAndDescendents(cs, system, c, null, profile, filters);
} }
private void addCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile, List<ValueSet> filters) throws ETooCostly { private void addCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile, List<ValueSet> filters) throws ETooCostly {
if (expand.getContains().size() > maxExpansionSize) if (expand.getContains().size() > maxExpansionSize)
throw new ETooCostly("Too many codes to display (>" + Integer.toString(expand.getContains().size()) + ")"); throw new ETooCostly("Too many codes to display (>" + Integer.toString(expand.getContains().size()) + ")");
for (ValueSetExpansionParameterComponent p : expand.getParameter()) { for (ValueSetExpansionParameterComponent p : expand.getParameter()) {
if (!existsInParams(params, p.getName(), p.getValue())) if (!existsInParams(params, p.getName(), p.getValue()))
params.add(p); params.add(p);
} }
copyImportContains(expand.getContains(), null, profile, filters); copyImportContains(expand.getContains(), null, profile, filters);
}
total = expand.getTotal();
}
private void excludeCode(String theSystem, String theCode) { private void excludeCode(String theSystem, String theCode) {
ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent(); ValueSetExpansionContainsComponent n = new ValueSet.ValueSetExpansionContainsComponent();
n.setSystem(theSystem); n.setSystem(theSystem);
n.setCode(theCode); n.setCode(theCode);
String s = key(n); String s = key(n);
excludeKeys.add(s); excludeKeys.add(s);
} }
private void excludeCodes(ConceptSetComponent exc, List<ValueSetExpansionParameterComponent> params) throws TerminologyServiceException { private void excludeCodes(ConceptSetComponent exc, List<ValueSetExpansionParameterComponent> params) throws TerminologyServiceException {
if (exc.hasSystem() && exc.getConcept().size() == 0 && exc.getFilter().size() == 0) { if (exc.hasSystem() && exc.getConcept().size() == 0 && exc.getFilter().size() == 0) {
excludeSystems.add(exc.getSystem()); excludeSystems.add(exc.getSystem());
} }
if (exc.hasValueSet()) if (exc.hasValueSet())
throw new Error("Processing Value set references in exclude is not yet done"); throw new Error("Processing Value set references in exclude is not yet done");
// importValueSet(imp.getValue(), params, profile); // importValueSet(imp.getValue(), params, profile);
CodeSystem cs = context.fetchCodeSystem(exc.getSystem()); CodeSystem cs = context.fetchCodeSystem(exc.getSystem());
if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(exc.getSystem())) { if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(exc.getSystem())) {
excludeCodes(context.expandVS(exc, false), params); excludeCodes(context.expandVS(exc, false), params);
return; return;
} }
for (ConceptReferenceComponent c : exc.getConcept()) { for (ConceptReferenceComponent c : exc.getConcept()) {
excludeCode(exc.getSystem(), c.getCode()); excludeCode(exc.getSystem(), c.getCode());
} }
if (exc.getFilter().size() > 0)
throw new NotImplementedException("not done yet");
}
private void excludeCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params) { if (exc.getFilter().size() > 0)
for (ValueSetExpansionContainsComponent c : expand.getContains()) { throw new NotImplementedException("not done yet");
excludeCode(c.getSystem(), c.getCode()); }
}
}
private boolean existsInParams(List<ValueSetExpansionParameterComponent> params, String name, Type value) { private void excludeCodes(ValueSetExpansionComponent expand, List<ValueSetExpansionParameterComponent> params) {
for (ValueSetExpansionParameterComponent p : params) { for (ValueSetExpansionContainsComponent c : expand.getContains()) {
if (p.getName().equals(name) && PrimitiveType.compareDeep(p.getValue(), value, false)) excludeCode(c.getSystem(), c.getCode());
return true; }
} }
return false;
}
@Override private boolean existsInParams(List<ValueSetExpansionParameterComponent> params, String name, Type value) {
for (ValueSetExpansionParameterComponent p : params) {
if (p.getName().equals(name) && PrimitiveType.compareDeep(p.getValue(), value, false))
return true;
}
return false;
}
@Override
public ValueSetExpansionOutcome expand(ValueSet source, ExpansionProfile profile) { public ValueSetExpansionOutcome expand(ValueSet source, ExpansionProfile profile) {
if (profile == null) if (profile == null)
profile = makeDefaultExpansion(); profile = makeDefaultExpansion();
try { try {
focus = source.copy(); focus = source.copy();
focus.setExpansion(new ValueSet.ValueSetExpansionComponent()); focus.setExpansion(new ValueSet.ValueSetExpansionComponent());
focus.getExpansion().setTimestampElement(DateTimeType.now()); focus.getExpansion().setTimestampElement(DateTimeType.now());
focus.getExpansion().setIdentifier(Factory.createUUID()); focus.getExpansion().setIdentifier(Factory.createUUID());
if (!profile.getUrl().startsWith("urn:uuid:")) if (!profile.getUrl().startsWith("urn:uuid:"))
focus.getExpansion().addParameter().setName("profile").setValue(new UriType(profile.getUrl())); focus.getExpansion().addParameter().setName("profile").setValue(new UriType(profile.getUrl()));
if (source.hasCompose()) if (source.hasCompose())
handleCompose(source.getCompose(), focus.getExpansion().getParameter(), profile); handleCompose(source.getCompose(), focus.getExpansion().getParameter(), profile);
if (canBeHeirarchy) { if (canBeHeirarchy) {
@ -254,38 +254,38 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
focus.getExpansion().getContains().add(c); focus.getExpansion().getContains().add(c);
} }
} else { } else {
for (ValueSetExpansionContainsComponent c : codes) { for (ValueSetExpansionContainsComponent c : codes) {
if (map.containsKey(key(c)) && !c.getAbstract()) { // we may have added abstract codes earlier while we still thought it might be heirarchical, but later we gave up, so now ignore them if (map.containsKey(key(c)) && !c.getAbstract()) { // we may have added abstract codes earlier while we still thought it might be heirarchical, but later we gave up, so now ignore them
focus.getExpansion().getContains().add(c); focus.getExpansion().getContains().add(c);
c.getContains().clear(); // make sure any heirarchy is wiped c.getContains().clear(); // make sure any heirarchy is wiped
} }
} }
} }
if (total > 0) { if (total > 0) {
focus.getExpansion().setTotal(total); focus.getExpansion().setTotal(total);
} }
return new ValueSetExpansionOutcome(focus); return new ValueSetExpansionOutcome(focus);
} catch (RuntimeException e) { } catch (RuntimeException e) {
// TODO: we should put something more specific instead of just Exception below, since // TODO: we should put something more specific instead of just Exception below, since
// it swallows bugs.. what would be expected to be caught there? // it swallows bugs.. what would be expected to be caught there?
throw e; throw e;
} catch (NoTerminologyServiceException e) { } catch (NoTerminologyServiceException e) {
// well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set // well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set
// that might fail too, but it might not, later. // that might fail too, but it might not, later.
return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage(), TerminologyServiceErrorClass.NOSERVICE); return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage(), TerminologyServiceErrorClass.NOSERVICE);
} catch (Exception e) { } catch (Exception e) {
// well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set // well, we couldn't expand, so we'll return an interface to a checker that can check membership of the set
// that might fail too, but it might not, later. // that might fail too, but it might not, later.
return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage(), TerminologyServiceErrorClass.UNKNOWN); return new ValueSetExpansionOutcome(new ValueSetCheckerSimple(source, factory, context), e.getMessage(), TerminologyServiceErrorClass.UNKNOWN);
} }
} }
private ExpansionProfile makeDefaultExpansion() { private ExpansionProfile makeDefaultExpansion() {
ExpansionProfile res = new ExpansionProfile(); ExpansionProfile res = new ExpansionProfile();
res.setUrl("urn:uuid:"+UUID.randomUUID().toString().toLowerCase()); res.setUrl("urn:uuid:" + UUID.randomUUID().toString().toLowerCase());
res.setExcludeNested(true); res.setExcludeNested(true);
res.setIncludeDesignations(false); res.setIncludeDesignations(false);
return res; return res;
} }
@ -296,68 +296,70 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
} }
} }
private String getCodeDisplay(CodeSystem cs, String code) throws TerminologyServiceException { private String getCodeDisplay(CodeSystem cs, String code) throws TerminologyServiceException {
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), code); ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), code);
if (def == null) if (def == null)
throw new TerminologyServiceException("Unable to find code '" + code + "' in code system " + cs.getUrl()); throw new TerminologyServiceException("Unable to find code '" + code + "' in code system " + cs.getUrl());
return def.getDisplay(); return def.getDisplay();
} }
private ConceptDefinitionComponent getConceptForCode(List<ConceptDefinitionComponent> clist, String code) { private ConceptDefinitionComponent getConceptForCode(List<ConceptDefinitionComponent> clist, String code) {
for (ConceptDefinitionComponent c : clist) { for (ConceptDefinitionComponent c : clist) {
if (code.equals(c.getCode())) if (code.equals(c.getCode()))
return c; return c;
ConceptDefinitionComponent v = getConceptForCode(c.getConcept(), code); ConceptDefinitionComponent v = getConceptForCode(c.getConcept(), code);
if (v != null) if (v != null)
return v; return v;
} }
return null; return null;
} }
private void handleCompose(ValueSetComposeComponent compose, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile) throws ETooCostly, FileNotFoundException, IOException, FHIRException { private void handleCompose(ValueSetComposeComponent compose, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile)
// Exclude comes first because we build up a map of things to exclude throws ETooCostly, FileNotFoundException, IOException, FHIRException {
for (ConceptSetComponent inc : compose.getExclude()) // Exclude comes first because we build up a map of things to exclude
excludeCodes(inc, params); for (ConceptSetComponent inc : compose.getExclude())
excludeCodes(inc, params);
canBeHeirarchy = !profile.getExcludeNested() && excludeKeys.isEmpty() && excludeSystems.isEmpty(); canBeHeirarchy = !profile.getExcludeNested() && excludeKeys.isEmpty() && excludeSystems.isEmpty();
boolean first = true; boolean first = true;
for (ConceptSetComponent inc : compose.getInclude()) { for (ConceptSetComponent inc : compose.getInclude()) {
if (first == true) if (first == true)
first = false; first = false;
else else
canBeHeirarchy = false; canBeHeirarchy = false;
includeCodes(inc, params, profile); includeCodes(inc, params, profile);
} }
} }
private ValueSet importValueSet(String value, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile) throws ETooCostly, TerminologyServiceException, FileNotFoundException, IOException { private ValueSet importValueSet(String value, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile)
if (value == null) throws ETooCostly, TerminologyServiceException, FileNotFoundException, IOException {
throw new TerminologyServiceException("unable to find value set with no identity"); if (value == null)
ValueSet vs = context.fetchResource(ValueSet.class, value); throw new TerminologyServiceException("unable to find value set with no identity");
if (vs == null) ValueSet vs = context.fetchResource(ValueSet.class, value);
throw new TerminologyServiceException("Unable to find imported value set " + value); if (vs == null)
ValueSetExpansionOutcome vso = factory.getExpander().expand(vs, profile); throw new TerminologyServiceException("Unable to find imported value set " + value);
ValueSetExpansionOutcome vso = factory.getExpander().expand(vs, profile);
if (vso.getError() != null) if (vso.getError() != null)
throw new TerminologyServiceException("Unable to expand imported value set: "+vso.getError()); throw new TerminologyServiceException("Unable to expand imported value set: " + vso.getError());
if (vso.getService() != null) if (vso.getService() != null)
throw new TerminologyServiceException("Unable to expand imported value set "+value); throw new TerminologyServiceException("Unable to expand imported value set " + value);
if (vs.hasVersion()) if (vs.hasVersion())
if (!existsInParams(params, "version", new UriType(vs.getUrl()+"|"+vs.getVersion()))) if (!existsInParams(params, "version", new UriType(vs.getUrl() + "|" + vs.getVersion())))
params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(vs.getUrl()+"|"+vs.getVersion()))); params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(vs.getUrl() + "|" + vs.getVersion())));
for (ValueSetExpansionParameterComponent p : vso.getValueset().getExpansion().getParameter()) { for (ValueSetExpansionParameterComponent p : vso.getValueset().getExpansion().getParameter()) {
if (!existsInParams(params, p.getName(), p.getValue())) if (!existsInParams(params, p.getName(), p.getValue()))
params.add(p); params.add(p);
} }
canBeHeirarchy = false; // if we're importing a value set, we have to be combining, so we won't try for a heirarchy canBeHeirarchy = false; // if we're importing a value set, we have to be combining, so we won't try for a heirarchy
return vso.getValueset(); return vso.getValueset();
} }
private void copyImportContains(List<ValueSetExpansionContainsComponent> list, ValueSetExpansionContainsComponent parent, ExpansionProfile profile, List<ValueSet> filter) { private void copyImportContains(List<ValueSetExpansionContainsComponent> list, ValueSetExpansionContainsComponent parent, ExpansionProfile profile, List<ValueSet> filter) {
for (ValueSetExpansionContainsComponent c : list) { for (ValueSetExpansionContainsComponent c : list) {
ValueSetExpansionContainsComponent np = addCode(c.getSystem(), c.getCode(), c.getDisplay(), parent, null, profile, c.getAbstract(), c.getInactive(), filter); ValueSetExpansionContainsComponent np = addCode(c.getSystem(), c.getCode(), c.getDisplay(), parent, null, profile, c.getAbstract(), c.getInactive(), filter);
copyImportContains(c.getContains(), np, profile, filter); copyImportContains(c.getContains(), np, profile, filter);
} }
} }
private void includeCodes(ConceptSetComponent inc, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile) throws ETooCostly, FileNotFoundException, IOException, FHIRException { private void includeCodes(ConceptSetComponent inc, List<ValueSetExpansionParameterComponent> params, ExpansionProfile profile) throws ETooCostly, FileNotFoundException, IOException, FHIRException {
List<ValueSet> imports = new ArrayList<ValueSet>(); List<ValueSet> imports = new ArrayList<ValueSet>();
@ -367,9 +369,9 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
if (!inc.hasSystem()) { if (!inc.hasSystem()) {
if (imports.isEmpty()) // though this is not supposed to be the case if (imports.isEmpty()) // though this is not supposed to be the case
return; return;
ValueSet base = imports.get(0); ValueSet base = imports.get(0);
imports.remove(0); imports.remove(0);
copyImportContains(base.getExpansion().getContains(), null, profile, imports); copyImportContains(base.getExpansion().getContains(), null, profile, imports);
} else { } else {
CodeSystem cs = context.fetchCodeSystem(inc.getSystem()); CodeSystem cs = context.fetchCodeSystem(inc.getSystem());
if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(inc.getSystem())) { if ((cs == null || cs.getContent() != CodeSystemContentMode.COMPLETE) && context.supportsSystem(inc.getSystem())) {
@ -379,15 +381,15 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
if (cs == null) { if (cs == null) {
if (context.isNoTerminologyServer()) if (context.isNoTerminologyServer())
throw new NoTerminologyServiceException("unable to find code system "+inc.getSystem().toString()); throw new NoTerminologyServiceException("unable to find code system " + inc.getSystem().toString());
else else
throw new TerminologyServiceException("unable to find code system "+inc.getSystem().toString()); throw new TerminologyServiceException("unable to find code system " + inc.getSystem().toString());
} }
if (cs.getContent() != CodeSystemContentMode.COMPLETE) if (cs.getContent() != CodeSystemContentMode.COMPLETE)
throw new TerminologyServiceException("Code system "+inc.getSystem().toString()+" is incomplete"); throw new TerminologyServiceException("Code system " + inc.getSystem().toString() + " is incomplete");
if (cs.hasVersion()) if (cs.hasVersion())
if (!existsInParams(params, "version", new UriType(cs.getUrl()+"|"+cs.getVersion()))) if (!existsInParams(params, "version", new UriType(cs.getUrl() + "|" + cs.getVersion())))
params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(cs.getUrl()+"|"+cs.getVersion()))); params.add(new ValueSetExpansionParameterComponent().setName("version").setValue(new UriType(cs.getUrl() + "|" + cs.getVersion())));
if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) { if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) {
// special case - add all the code system // special case - add all the code system
@ -399,7 +401,8 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
if (!inc.getConcept().isEmpty()) { if (!inc.getConcept().isEmpty()) {
canBeHeirarchy = false; canBeHeirarchy = false;
for (ConceptReferenceComponent c : inc.getConcept()) { for (ConceptReferenceComponent c : inc.getConcept()) {
addCode(inc.getSystem(), c.getCode(), Utilities.noString(c.getDisplay()) ? getCodeDisplay(cs, c.getCode()) : c.getDisplay(), null, convertDesignations(c.getDesignation()), profile, false, CodeSystemUtilities.isInactive(cs, c.getCode()), imports); addCode(inc.getSystem(), c.getCode(), Utilities.noString(c.getDisplay()) ? getCodeDisplay(cs, c.getCode()) : c.getDisplay(), null, convertDesignations(c.getDesignation()), profile, false,
CodeSystemUtilities.isInactive(cs, c.getCode()), imports);
} }
} }
if (inc.getFilter().size() > 1) { if (inc.getFilter().size() > 1) {
@ -412,13 +415,13 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
// special: all codes in the target code system under the value // special: all codes in the target code system under the value
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue()); ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def == null) if (def == null)
throw new TerminologyServiceException("Code '"+fc.getValue()+"' not found in system '"+inc.getSystem()+"'"); throw new TerminologyServiceException("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
addCodeAndDescendents(cs, inc.getSystem(), def, null, profile, imports); addCodeAndDescendents(cs, inc.getSystem(), def, null, profile, imports);
} else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.DESCENDENTOF) { } else if ("concept".equals(fc.getProperty()) && fc.getOp() == FilterOperator.DESCENDENTOF) {
// special: all codes in the target code system under the value // special: all codes in the target code system under the value
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue()); ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def == null) if (def == null)
throw new TerminologyServiceException("Code '"+fc.getValue()+"' not found in system '"+inc.getSystem()+"'"); throw new TerminologyServiceException("Code '" + fc.getValue() + "' not found in system '" + inc.getSystem() + "'");
for (ConceptDefinitionComponent c : def.getConcept()) for (ConceptDefinitionComponent c : def.getConcept())
addCodeAndDescendents(cs, inc.getSystem(), c, null, profile, imports); addCodeAndDescendents(cs, inc.getSystem(), c, null, profile, imports);
} else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) { } else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) {
@ -428,7 +431,8 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
if (def != null) { if (def != null) {
if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) { if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) {
if (def.getDisplay().contains(fc.getValue())) { if (def.getDisplay().contains(fc.getValue())) {
addCode(inc.getSystem(), def.getCode(), def.getDisplay(), null, def.getDesignation(), profile, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def), imports); addCode(inc.getSystem(), def.getCode(), def.getDisplay(), null, def.getDesignation(), profile, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def),
imports);
} }
} }
} }
@ -449,12 +453,12 @@ public class ValueSetExpanderSimple implements ValueSetExpander {
return res; return res;
} }
private String key(String uri, String code) { private String key(String uri, String code) {
return "{" + uri + "}" + code; return "{" + uri + "}" + code;
} }
private String key(ValueSetExpansionContainsComponent c) { private String key(ValueSetExpansionContainsComponent c) {
return key(c.getSystem(), c.getCode()); return key(c.getSystem(), c.getCode());
} }
} }

View File

@ -145,6 +145,12 @@
<![CDATA[<code>IBundleProvider</code> called <code>getUuid()</code>]]>. This <![CDATA[<code>IBundleProvider</code> called <code>getUuid()</code>]]>. This
method may return <![CDATA[<code>null</code>]]> in any current cases. method may return <![CDATA[<code>null</code>]]> in any current cases.
</action> </action>
<action type="fix">
Expanding a ValueSet in JPA server did not correctly apply
<![CDATA[<code>?filter=</code>]]> parameter when the ValueSet
being expanded had codes included explicitly (i.e. not by
is-a relationship). Thanks to David Hay for reporting!
</action>
</release> </release>
<release version="2.2" date="2016-12-20"> <release version="2.2" date="2016-12-20">
<action type="add"> <action type="add">