diff --git a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/ValueSetExpansionOptions.java b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/ValueSetExpansionOptions.java index eae7f604bb5..5d9fafc57fc 100644 --- a/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/ValueSetExpansionOptions.java +++ b/hapi-fhir-base/src/main/java/ca/uhn/fhir/context/support/ValueSetExpansionOptions.java @@ -35,6 +35,8 @@ public class ValueSetExpansionOptions { private boolean myIncludeHierarchy; private String myFilter; + private String myDisplayLanguage; + public String getFilter() { return myFilter; } @@ -118,4 +120,13 @@ public class ValueSetExpansionOptions { .setOffset(theOffset) .setCount(theCount); } + + public String getTheDisplayLanguage() { + return myDisplayLanguage; + } + + public ValueSetExpansionOptions setTheDisplayLanguage(String theDisplayLanguage) { + myDisplayLanguage = theDisplayLanguage; + return this; + } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java index 7674fd582fd..b01be57e32c 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/ValueSetOperationProvider.java @@ -50,11 +50,13 @@ import org.hl7.fhir.instance.model.api.IBaseResource; import org.hl7.fhir.instance.model.api.ICompositeType; import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; +import org.hl7.fhir.r4.model.CodeType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; +import javax.annotation.Nullable; import javax.servlet.http.HttpServletRequest; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -108,6 +110,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider { @OperationParam(name = "contextDirection", min = 0, max = 1, typeName = "string") IPrimitiveType theContextDirection, @OperationParam(name = "offset", min = 0, max = 1, typeName = "integer") IPrimitiveType theOffset, @OperationParam(name = "count", min = 0, max = 1, typeName = "integer") IPrimitiveType theCount, + @OperationParam(name = JpaConstants.OPERATION_EXPAND_PARAM_DISPLAY_LANGUAGE, min = 0, max = 1, typeName = "code") IPrimitiveType theDisplayLanguage, @OperationParam(name = JpaConstants.OPERATION_EXPAND_PARAM_INCLUDE_HIERARCHY, min = 0, max = 1, typeName = "boolean") IPrimitiveType theIncludeHierarchy, RequestDetails theRequestDetails) { @@ -143,7 +146,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider { throw new InvalidRequestException(Msg.code(1134) + "$expand must EITHER be invoked at the instance level, or have a url specified, or have a ValueSet specified. Can not combine these options."); } - ValueSetExpansionOptions options = createValueSetExpansionOptions(myDaoConfig, theOffset, theCount, theIncludeHierarchy, theFilter); + ValueSetExpansionOptions options = createValueSetExpansionOptions(myDaoConfig, theOffset, theCount, theIncludeHierarchy, theFilter, theDisplayLanguage); startRequest(theServletRequest); try { @@ -265,7 +268,7 @@ public class ValueSetOperationProvider extends BaseJpaProvider { } - public static ValueSetExpansionOptions createValueSetExpansionOptions(DaoConfig theDaoConfig, IPrimitiveType theOffset, IPrimitiveType theCount, IPrimitiveType theIncludeHierarchy, IPrimitiveType theFilter) { + public static ValueSetExpansionOptions createValueSetExpansionOptions(DaoConfig theDaoConfig, IPrimitiveType theOffset, IPrimitiveType theCount, IPrimitiveType theIncludeHierarchy, IPrimitiveType theFilter, IPrimitiveType theDisplayLanguage) { int offset = theDaoConfig.getPreExpandValueSetsDefaultOffset(); if (theOffset != null && theOffset.hasValue()) { if (theOffset.getValue() >= 0) { @@ -299,6 +302,10 @@ public class ValueSetOperationProvider extends BaseJpaProvider { options.setFilter(theFilter.getValue()); } + if( theDisplayLanguage != null ) { + options.setTheDisplayLanguage(theDisplayLanguage.getValue()); + } + return options; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java index 971d2b637d3..e7c7eedcd77 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseTermReadSvcImpl.java @@ -308,6 +308,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { .collect(joining(" ")); } + Collection designations = theConcept.getDesignations(); if (StringUtils.isNotEmpty(theValueSetIncludeVersion)) { return addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, designations, theAdd, codeSystem + "|" + theValueSetIncludeVersion, code, display, sourceConceptPid, directParentPids, codeSystemVersion); @@ -526,7 +527,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { String expansionTimestamp = toHumanReadableExpansionTimestamp(termValueSet); String msg = myContext.getLocalizer().getMessage(BaseTermReadSvcImpl.class, "valueSetExpandedUsingPreExpansion", expansionTimestamp); theAccumulator.addMessage(msg); - expandConcepts(theAccumulator, termValueSet, theFilter, theAdd, isOracleDialect()); + expandConcepts(theExpansionOptions, theAccumulator, termValueSet, theFilter, theAdd, isOracleDialect()); } @Nonnull @@ -543,7 +544,7 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { return myHibernatePropertiesProvider.getDialect() instanceof org.hibernate.dialect.Oracle12cDialect; } - private void expandConcepts(IValueSetConceptAccumulator theAccumulator, TermValueSet theTermValueSet, ExpansionFilter theFilter, boolean theAdd, boolean theOracle) { + private void expandConcepts(ValueSetExpansionOptions theExpansionOptions, IValueSetConceptAccumulator theAccumulator, TermValueSet theTermValueSet, ExpansionFilter theFilter, boolean theAdd, boolean theOracle) { // NOTE: if you modifiy the logic here, look to `expandConceptsOracle` and see if your new code applies to its copy pasted sibling Integer offset = theAccumulator.getSkipCountRemaining(); offset = ObjectUtils.defaultIfNull(offset, 0); @@ -612,12 +613,15 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { // TODO: DM 2019-08-17 - Implement includeDesignations parameter for $expand operation to designations optional. if (conceptView.getDesignationPid() != null) { TermConceptDesignation designation = new TermConceptDesignation(); - designation.setUseSystem(conceptView.getDesignationUseSystem()); - designation.setUseCode(conceptView.getDesignationUseCode()); - designation.setUseDisplay(conceptView.getDesignationUseDisplay()); - designation.setValue(conceptView.getDesignationVal()); - designation.setLanguage(conceptView.getDesignationLang()); - pidToDesignations.put(conceptPid, designation); + + if(isValueSetDisplayLanguageMatch(theExpansionOptions, conceptView.getDesignationLang() )) { + designation.setUseSystem(conceptView.getDesignationUseSystem()); + designation.setUseCode(conceptView.getDesignationUseCode()); + designation.setUseDisplay(conceptView.getDesignationUseDisplay()); + designation.setValue(conceptView.getDesignationVal()); + designation.setLanguage(conceptView.getDesignationLang()); + pidToDesignations.put(conceptPid, designation); + } if (++designationsExpanded % 250 == 0) { logDesignationsExpanded("Expansion of designations in progress. ", theTermValueSet, designationsExpanded); @@ -668,6 +672,19 @@ public abstract class BaseTermReadSvcImpl implements ITermReadSvc { logConceptsExpanded("Finished expanding concepts. ", theTermValueSet, conceptsExpanded); } + static boolean isValueSetDisplayLanguageMatch(ValueSetExpansionOptions theExpansionOptions, String theStoredLang){ + if( theExpansionOptions == null) { + return true; + } + + if(theExpansionOptions.getTheDisplayLanguage() == null || theStoredLang == null) { + return true; + } + + return theExpansionOptions.getTheDisplayLanguage().equalsIgnoreCase(theStoredLang); + + } + private void logConceptsExpanded(String theLogDescriptionPrefix, TermValueSet theTermValueSet, int theConceptsExpanded) { if (theConceptsExpanded > 0) { ourLog.debug("{}Have expanded {} concepts in ValueSet[{}]", theLogDescriptionPrefix, theConceptsExpanded, theTermValueSet.getUrl()); diff --git a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java index 7e1251ef7f7..f4af3acfff4 100644 --- a/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java +++ b/hapi-fhir-jpaserver-model/src/main/java/ca/uhn/fhir/jpa/model/util/JpaConstants.java @@ -257,9 +257,11 @@ public class JpaConstants { * Parameter for the $expand operation */ public static final String OPERATION_EXPAND_PARAM_INCLUDE_HIERARCHY = "includeHierarchy"; + public static final String OPERATION_EXPAND_PARAM_DISPLAY_LANGUAGE = "displayLanguage"; public static final String HEADER_UPSERT_EXISTENCE_CHECK = "X-Upsert-Extistence-Check"; public static final String HEADER_UPSERT_EXISTENCE_CHECK_DISABLED = "disabled"; + /** * Parameters for the rewrite history operation */ diff --git a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java index bc72339607d..9125794edd7 100644 --- a/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java +++ b/hapi-fhir-jpaserver-test-utilities/src/test/java/ca/uhn/fhir/jpa/term/ValueSetExpansionR4Test.java @@ -630,6 +630,46 @@ public class ValueSetExpansionR4Test extends BaseTermR4Test { assertExpandedValueSetContainsConcept(expandedValueSet, "http://acme.org", "8492-1", "Systolic blood pressure 8 hour minimum", 0); } + @Test + public void testExpandTermValueSetAndChildrenWithCountWithDisplayLanguage() throws Exception { + myDaoConfig.setPreExpandValueSets(true); + + loadAndPersistCodeSystemAndValueSetWithDesignations(HttpVerb.POST); + + CodeSystem codeSystem = myCodeSystemDao.read(myExtensionalCsId); + ourLog.info("CodeSystem:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem)); + + ValueSet valueSet = myValueSetDao.read(myExtensionalVsId); + ourLog.info("ValueSet:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(valueSet)); + + myTermSvc.preExpandDeferredValueSetsToTerminologyTables(); + + List expandedConceptCodes = getExpandedConceptsByValueSetUrl("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2"); + + ValueSetExpansionOptions options = new ValueSetExpansionOptions() + .setOffset(0) + .setCount(23) + .setTheDisplayLanguage("nl"); + ValueSet expandedValueSet = myTermSvc.expandValueSet(options, valueSet); + ourLog.info("Expanded ValueSet:\n" + myFhirContext.newJsonParser().setPrettyPrint(true).encodeResourceToString(expandedValueSet)); + + assertEquals(codeSystem.getConcept().size(), expandedValueSet.getExpansion().getTotal()); + assertEquals(myDaoConfig.getPreExpandValueSetsDefaultOffset(), expandedValueSet.getExpansion().getOffset()); + assertEquals(2, expandedValueSet.getExpansion().getParameter().size()); + assertEquals("offset", expandedValueSet.getExpansion().getParameter().get(0).getName()); + assertEquals(0, expandedValueSet.getExpansion().getParameter().get(0).getValueIntegerType().getValue().intValue()); + assertEquals("count", expandedValueSet.getExpansion().getParameter().get(1).getName()); + assertEquals(23, expandedValueSet.getExpansion().getParameter().get(1).getValueIntegerType().getValue().intValue()); + assertEquals(23, expandedValueSet.getExpansion().getContains().size()); + + ValueSet.ValueSetExpansionContainsComponent concept = assertExpandedValueSetContainsConcept(expandedValueSet, "http://acme.org", "8450-9", "Systolic blood pressure--expiration", 1); + assertThat(concept.getDesignation().size() , is(equalTo(1))); + assertConceptContainsDesignation(concept, "nl", "http://snomed.info/sct", "900000000000013009", "Synonym", "Systolische bloeddruk - expiratie"); + + //It is enough to test that the sublist returned is the correct one. + assertThat(ValueSetTestUtil.toCodes(expandedValueSet), is(equalTo(expandedConceptCodes.subList(0, 23)))); + } + @Test public void testExpandTermValueSetAndChildrenWithCount() throws Exception { myDaoConfig.setPreExpandValueSets(true);