709 the conceptmap operation called translate needs to be implemented (#923)

The ConceptMap operation $translate has been implemented.
This commit is contained in:
Diederik Muylwyk 2018-05-10 10:52:56 -04:00 committed by GitHub
parent a0c40cf98a
commit d97fb8f5cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
45 changed files with 6804 additions and 74 deletions

View File

@ -30,7 +30,6 @@ import org.slf4j.LoggerFactory;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.atomic.AtomicInteger;

View File

@ -91,7 +91,11 @@ ca.uhn.fhir.jpa.dao.SearchBuilder.invalidNumberPrefix=Unable to handle number pr
ca.uhn.fhir.jpa.provider.BaseJpaProvider.cantCombintAtAndSince=Unable to combine _at and _since parameters for history operation
ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl.cannotCreateDuplicateConceptMapUrl=Can not create multiple ConceptMap resources with ConceptMap.url "{0}", already have one with resource ID: {1}
ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl.cannotCreateDuplicateCodeSystemUri=Can not create multiple code systems with URI "{0}", already have one with resource ID: {1}
ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl.expansionTooLarge=Expansion of ValueSet produced too many codes (maximum {0}) - Operation aborted!
ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl.expansionTooLarge=Expansion of ValueSet produced too many codes (maximum {0}) - Operation aborted!
ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoConceptMapDstu3.matchesFound=Matches found!
ca.uhn.fhir.jpa.dao.dstu3.FhirResourceDaoConceptMapDstu3.noMatchesFound=No matches found!
ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoConceptMapR4.matchesFound=Matches found!
ca.uhn.fhir.jpa.dao.r4.FhirResourceDaoConceptMapR4.noMatchesFound=No matches found!

View File

@ -469,6 +469,16 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava-testlib</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

View File

@ -79,13 +79,8 @@ import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.domain.SliceImpl;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallback;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Nullable;
import javax.persistence.*;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
@ -335,6 +330,13 @@ public abstract class BaseHapiFhirDao<T extends IBaseResource> implements IDao {
doExpungeEverythingQuery("DELETE from " + TermConceptParentChildLink.class.getSimpleName() + " d");
return null;
});
txTemplate.execute(t -> {
doExpungeEverythingQuery("DELETE from " + TermConceptMapGroupElementTarget.class.getSimpleName() + " d");
doExpungeEverythingQuery("DELETE from " + TermConceptMapGroupElement.class.getSimpleName() + " d");
doExpungeEverythingQuery("DELETE from " + TermConceptMapGroup.class.getSimpleName() + " d");
doExpungeEverythingQuery("DELETE from " + TermConceptMap.class.getSimpleName() + " d");
return null;
});
txTemplate.execute(t -> {
doExpungeEverythingQuery("DELETE from " + TermConceptProperty.class.getSimpleName() + " d");
doExpungeEverythingQuery("DELETE from " + TermConceptDesignation.class.getSimpleName() + " d");

View File

@ -57,6 +57,16 @@ public class DaoConfig {
*/
private static final Integer DEFAULT_MAXIMUM_SEARCH_RESULT_COUNT_IN_TRANSACTION = null;
private IndexEnabledEnum myIndexMissingFieldsEnabled = IndexEnabledEnum.DISABLED;
/**
* Default value for {@link #setTranslationCachesExpireAfterWriteInMinutes(Long)}: 60 minutes
*
* @see #setTranslationCachesExpireAfterWriteInMinutes(Long)
*/
public static final Long DEFAULT_TRANSLATION_CACHES_EXPIRE_AFTER_WRITE_IN_MINUTES = 60L;
/**
* update setter javadoc if default changes
*/
private Long myTranslationCachesExpireAfterWriteInMinutes = DEFAULT_TRANSLATION_CACHES_EXPIRE_AFTER_WRITE_IN_MINUTES;
/**
* update setter javadoc if default changes
*/
@ -557,6 +567,22 @@ public class DaoConfig {
myReuseCachedSearchResultsForMillis = theReuseCachedSearchResultsForMillis;
}
/**
* Specifies the duration in minutes for which values will be retained after being
* written to the terminology translation cache. Defaults to 60.
*/
public Long getTranslationCachesExpireAfterWriteInMinutes() {
return myTranslationCachesExpireAfterWriteInMinutes;
}
/**
* Specifies the duration in minutes for which values will be retained after being
* written to the terminology translation cache. Defaults to 60.
*/
public void setTranslationCachesExpireAfterWriteInMinutes(Long translationCachesExpireAfterWriteInMinutes) {
myTranslationCachesExpireAfterWriteInMinutes = translationCachesExpireAfterWriteInMinutes;
}
/**
* This setting may be used to advise the server that any references found in
* resources that have any of the base URLs given here will be replaced with

View File

@ -0,0 +1,30 @@
package ca.uhn.fhir.jpa.dao;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.term.TranslationRequest;
import ca.uhn.fhir.jpa.term.TranslationResult;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseResource;
public interface IFhirResourceDaoConceptMap<T extends IBaseResource> extends IFhirResourceDao<T> {
TranslationResult translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails);
}

View File

@ -28,6 +28,7 @@ import ca.uhn.fhir.jpa.search.JpaRuntimeSearchParam;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.jpa.term.VersionIndependentConcept;
import ca.uhn.fhir.jpa.util.BaseIterator;
import ca.uhn.fhir.jpa.util.ScrollableResultsIterator;
import ca.uhn.fhir.model.api.*;
import ca.uhn.fhir.model.base.composite.BaseCodingDt;
import ca.uhn.fhir.model.base.composite.BaseIdentifierDt;
@ -59,9 +60,6 @@ import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.tuple.Pair;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.engine.spi.EntityKey;
import org.hibernate.engine.spi.PersistenceContext;
import org.hibernate.internal.SessionImpl;
import org.hibernate.query.Query;
import org.hl7.fhir.dstu3.model.BaseResource;
import org.hl7.fhir.instance.model.api.IAnyResource;
@ -2193,7 +2191,7 @@ public class SearchBuilder implements ISearchBuilder {
Query<Long> hibernateQuery = (Query<Long>) query;
hibernateQuery.setFetchSize(myFetchSize);
ScrollableResults scroll = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY);
myResultsIterator = new ScrollableResultsIterator(scroll);
myResultsIterator = new ScrollableResultsIterator<>(scroll);
// If the query resulted in extra results being requested
if (myAlsoIncludePids != null) {
@ -2275,42 +2273,6 @@ public class SearchBuilder implements ISearchBuilder {
}
}
public class ScrollableResultsIterator extends BaseIterator<Long> implements Iterator<Long> {
private Long myNext;
private ScrollableResults myScroll;
public ScrollableResultsIterator(ScrollableResults theScroll) {
myScroll = theScroll;
}
private void ensureHaveNext() {
if (myNext == null) {
if (myScroll.next()) {
myNext = (Long) myScroll.get(0);
} else {
myNext = NO_MORE;
}
}
}
@Override
public boolean hasNext() {
ensureHaveNext();
return myNext != NO_MORE;
}
@Override
public Long next() {
ensureHaveNext();
Validate.isTrue(myNext != NO_MORE);
Long next = myNext;
myNext = null;
return next;
}
}
private class UniqueIndexIterator implements Iterator<Long> {
private final Set<String> myUniqueQueryStrings;
private Iterator<Long> myWrap = null;

View File

@ -0,0 +1,41 @@
package ca.uhn.fhir.jpa.dao.data;
import ca.uhn.fhir.jpa.entity.TermConceptMap;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import java.util.Optional;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface ITermConceptMapDao extends JpaRepository<TermConceptMap, Long> {
@Query("DELETE FROM TermConceptMap cm WHERE cm.myId = :pid")
@Modifying
void deleteTermConceptMapById(@Param("pid") Long theId);
@Query("SELECT cm FROM TermConceptMap cm WHERE cm.myResourcePid = :resource_pid")
Optional<TermConceptMap> findTermConceptMapByResourcePid(@Param("resource_pid") Long theResourcePid);
@Query("SELECT cm FROM TermConceptMap cm WHERE cm.myUrl = :url")
Optional<TermConceptMap> findTermConceptMapByUrl(@Param("url") String theUrl);
}

View File

@ -0,0 +1,33 @@
package ca.uhn.fhir.jpa.dao.data;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroup;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface ITermConceptMapGroupDao extends JpaRepository<TermConceptMapGroup, Long> {
@Query("DELETE FROM TermConceptMapGroup g WHERE g.myConceptMap.myId = :pid")
@Modifying
void deleteTermConceptMapGroupById(@Param("pid") Long theId);
}

View File

@ -0,0 +1,33 @@
package ca.uhn.fhir.jpa.dao.data;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface ITermConceptMapGroupElementDao extends JpaRepository<TermConceptMapGroupElement, Long> {
@Query("DELETE FROM TermConceptMapGroupElement e WHERE e.myConceptMapGroup.myConceptMap.myId = :pid")
@Modifying
void deleteTermConceptMapGroupElementById(@Param("pid") Long theId);
}

View File

@ -0,0 +1,33 @@
package ca.uhn.fhir.jpa.dao.data;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Modifying;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
public interface ITermConceptMapGroupElementTargetDao extends JpaRepository<TermConceptMapGroupElementTarget, Long> {
@Query("DELETE FROM TermConceptMapGroupElementTarget t WHERE t.myConceptMapGroupElement.myConceptMapGroup.myConceptMap.myId = :pid")
@Modifying
void deleteTermConceptMapGroupElementTargetById(@Param("pid") Long theId);
}

View File

@ -0,0 +1,176 @@
package ca.uhn.fhir.jpa.dao.dstu3;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoConceptMap;
import ca.uhn.fhir.jpa.term.TranslationMatch;
import ca.uhn.fhir.jpa.term.TranslationRequest;
import ca.uhn.fhir.jpa.term.TranslationResult;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.hl7.fhir.convertors.VersionConvertor_30_40;
import org.hl7.fhir.dstu3.model.ConceptMap;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class FhirResourceDaoConceptMapDstu3 extends FhirResourceDaoDstu3<ConceptMap> implements IFhirResourceDaoConceptMap<ConceptMap> {
@Autowired
private IHapiTerminologySvc myHapiTerminologySvc;
@Override
public TranslationResult translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) {
if (theTranslationRequest.hasReverse() && theTranslationRequest.getReverseAsBoolean()) {
return buildReverseTranslationResult(myHapiTerminologySvc.translateWithReverse(theTranslationRequest));
}
return buildTranslationResult(myHapiTerminologySvc.translate(theTranslationRequest));
}
private TranslationResult buildTranslationResult(List<TermConceptMapGroupElementTarget> theTargets) {
TranslationResult retVal = new TranslationResult();
String msg;
if (theTargets.isEmpty()) {
retVal.setResult(new BooleanType(false));
msg = getContext().getLocalizer().getMessage(
FhirResourceDaoConceptMapDstu3.class,
"noMatchesFound");
retVal.setMessage(new StringType(msg));
} else {
retVal.setResult(new BooleanType(true));
msg = getContext().getLocalizer().getMessage(
FhirResourceDaoConceptMapDstu3.class,
"matchesFound");
retVal.setMessage(new StringType(msg));
TranslationMatch translationMatch;
Set<TermConceptMapGroupElementTarget> targetsToReturn = new HashSet<>();
for (TermConceptMapGroupElementTarget target : theTargets) {
if (targetsToReturn.add(target)) {
translationMatch = new TranslationMatch();
translationMatch.setEquivalence(new CodeType(target.getEquivalence().toCode()));
translationMatch.setConcept(
new Coding()
.setCode(target.getCode())
.setSystem(target.getSystem())
.setVersion(target.getSystemVersion())
.setDisplay(target.getDisplay())
);
translationMatch.setSource(new UriType(target.getConceptMapUrl()));
retVal.addMatch(translationMatch);
}
}
}
return retVal;
}
private TranslationResult buildReverseTranslationResult(List<TermConceptMapGroupElement> theElements) {
TranslationResult retVal = new TranslationResult();
String msg;
if (theElements.isEmpty()) {
retVal.setResult(new BooleanType(false));
msg = getContext().getLocalizer().getMessage(
FhirResourceDaoConceptMapDstu3.class,
"noMatchesFound");
retVal.setMessage(new StringType(msg));
} else {
retVal.setResult(new BooleanType(true));
msg = getContext().getLocalizer().getMessage(
FhirResourceDaoConceptMapDstu3.class,
"matchesFound");
retVal.setMessage(new StringType(msg));
TranslationMatch translationMatch;
Set<TermConceptMapGroupElement> elementsToReturn = new HashSet<>();
for (TermConceptMapGroupElement element : theElements) {
if (elementsToReturn.add(element)) {
translationMatch = new TranslationMatch();
translationMatch.setConcept(
new Coding()
.setCode(element.getCode())
.setSystem(element.getSystem())
.setVersion(element.getSystemVersion())
.setDisplay(element.getDisplay())
);
translationMatch.setSource(new UriType(element.getConceptMapUrl()));
retVal.addMatch(translationMatch);
}
}
}
return retVal;
}
@Override
protected ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
ConceptMap conceptMap = (ConceptMap) theResource;
if (conceptMap != null && isNotBlank(conceptMap.getUrl())) {
// Convert from DSTU3 to R4
try {
myHapiTerminologySvc.storeTermConceptMapAndChildren(retVal, VersionConvertor_30_40.convertConceptMap(conceptMap));
} catch (FHIRException fe) {
throw new InternalErrorException(fe);
}
}
return retVal;
}
}

View File

@ -0,0 +1,167 @@
package ca.uhn.fhir.jpa.dao.r4;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoConceptMap;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.jpa.term.TranslationMatch;
import ca.uhn.fhir.jpa.term.TranslationRequest;
import ca.uhn.fhir.jpa.term.TranslationResult;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.model.*;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public class FhirResourceDaoConceptMapR4 extends FhirResourceDaoR4<ConceptMap> implements IFhirResourceDaoConceptMap<ConceptMap> {
@Autowired
private IHapiTerminologySvc myHapiTerminologySvc;
@Override
public TranslationResult translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) {
if (theTranslationRequest.hasReverse() && theTranslationRequest.getReverseAsBoolean()) {
return buildReverseTranslationResult(myHapiTerminologySvc.translateWithReverse(theTranslationRequest));
}
return buildTranslationResult(myHapiTerminologySvc.translate(theTranslationRequest));
}
private TranslationResult buildTranslationResult(List<TermConceptMapGroupElementTarget> theTargets) {
TranslationResult retVal = new TranslationResult();
String msg;
if (theTargets.isEmpty()) {
retVal.setResult(new BooleanType(false));
msg = getContext().getLocalizer().getMessage(
FhirResourceDaoConceptMapR4.class,
"noMatchesFound");
retVal.setMessage(new StringType(msg));
} else {
retVal.setResult(new BooleanType(true));
msg = getContext().getLocalizer().getMessage(
FhirResourceDaoConceptMapR4.class,
"matchesFound");
retVal.setMessage(new StringType(msg));
TranslationMatch translationMatch;
Set<TermConceptMapGroupElementTarget> targetsToReturn = new HashSet<>();
for (TermConceptMapGroupElementTarget target : theTargets) {
if (targetsToReturn.add(target)) {
translationMatch = new TranslationMatch();
translationMatch.setEquivalence(new CodeType(target.getEquivalence().toCode()));
translationMatch.setConcept(
new Coding()
.setCode(target.getCode())
.setSystem(target.getSystem())
.setVersion(target.getSystemVersion())
.setDisplay(target.getDisplay())
);
translationMatch.setSource(new UriType(target.getConceptMapUrl()));
retVal.addMatch(translationMatch);
}
}
}
return retVal;
}
private TranslationResult buildReverseTranslationResult(List<TermConceptMapGroupElement> theElements) {
TranslationResult retVal = new TranslationResult();
String msg;
if (theElements.isEmpty()) {
retVal.setResult(new BooleanType(false));
msg = getContext().getLocalizer().getMessage(
FhirResourceDaoConceptMapR4.class,
"noMatchesFound");
retVal.setMessage(new StringType(msg));
} else {
retVal.setResult(new BooleanType(true));
msg = getContext().getLocalizer().getMessage(
FhirResourceDaoConceptMapR4.class,
"matchesFound");
retVal.setMessage(new StringType(msg));
TranslationMatch translationMatch;
Set<TermConceptMapGroupElement> elementsToReturn = new HashSet<>();
for (TermConceptMapGroupElement element : theElements) {
if (elementsToReturn.add(element)) {
translationMatch = new TranslationMatch();
translationMatch.setConcept(
new Coding()
.setCode(element.getCode())
.setSystem(element.getSystem())
.setVersion(element.getSystemVersion())
.setDisplay(element.getDisplay())
);
translationMatch.setSource(new UriType(element.getConceptMapUrl()));
retVal.addMatch(translationMatch);
}
}
}
return retVal;
}
@Override
protected ResourceTable updateEntity(RequestDetails theRequestDetails, IBaseResource theResource, ResourceTable theEntity, Date theDeletedTimestampOrNull, boolean thePerformIndexing,
boolean theUpdateVersion, Date theUpdateTime, boolean theForceUpdate, boolean theCreateNewHistoryEntry) {
ResourceTable retVal = super.updateEntity(theRequestDetails, theResource, theEntity, theDeletedTimestampOrNull, thePerformIndexing, theUpdateVersion, theUpdateTime, theForceUpdate, theCreateNewHistoryEntry);
ConceptMap conceptMap = (ConceptMap) theResource;
if (conceptMap != null && isNotBlank(conceptMap.getUrl())) {
myHapiTerminologySvc.storeTermConceptMapAndChildren(retVal, conceptMap);
}
return retVal;
}
}

View File

@ -11,7 +11,6 @@ import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hibernate.search.annotations.*;
import org.hl7.fhir.r4.model.Coding;
import org.springframework.validation.ValidationUtils;
import javax.annotation.Nonnull;
import javax.persistence.*;
@ -49,7 +48,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
@Index(name = "IDX_CONCEPT_INDEXSTATUS", columnList = "INDEX_STATUS")
})
public class TermConcept implements Serializable {
private static final int MAX_DESC_LENGTH = 400;
protected static final int MAX_DESC_LENGTH = 400;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(TermConcept.class);
private static final long serialVersionUID = 1L;

View File

@ -0,0 +1,105 @@
package ca.uhn.fhir.jpa.entity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "TRM_CONCEPT_MAP", uniqueConstraints = {
@UniqueConstraint(name = "IDX_CONCEPT_MAP_URL", columnNames = {"URL"})
})
public class TermConceptMap implements Serializable {
@Id()
@SequenceGenerator(name = "SEQ_CONCEPT_MAP_PID", sequenceName = "SEQ_CONCEPT_MAP_PID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_MAP_PID")
@Column(name = "PID")
private Long myId;
@OneToOne()
@JoinColumn(name = "RES_ID", referencedColumnName = "RES_ID", nullable = false, updatable = false, foreignKey = @ForeignKey(name = "FK_TRMCONCEPTMAP_RES"))
private ResourceTable myResource;
@Column(name = "RES_ID", insertable = false, updatable = false)
private Long myResourcePid;
@Column(name = "SOURCE_URL", nullable = false, length = 200)
private String mySource;
@Column(name = "TARGET_URL", nullable = false, length = 200)
private String myTarget;
@Column(name = "URL", length = 200)
private String myUrl;
@OneToMany(mappedBy = "myConceptMap")
private List<TermConceptMapGroup> myConceptMapGroups;
public List<TermConceptMapGroup> getConceptMapGroups() {
if (myConceptMapGroups == null) {
myConceptMapGroups = new ArrayList<>();
}
return myConceptMapGroups;
}
public Long getId() {
return myId;
}
public ResourceTable getResource() {
return myResource;
}
public void setResource(ResourceTable resource) {
myResource = resource;
}
public Long getResourcePid() {
return myResourcePid;
}
public void setResourcePid(Long resourcePid) {
myResourcePid = resourcePid;
}
public String getSource() {
return mySource;
}
public void setSource(String source) {
mySource = source;
}
public String getTarget() {
return myTarget;
}
public void setTarget(String target) {
myTarget = target;
}
public String getUrl() {
return myUrl;
}
public void setUrl(String theUrl) {
myUrl = theUrl;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("myId", myId)
.append("myResource", myResource.toString())
.append("myResourcePid", myResourcePid)
.append("mySource", mySource)
.append("myTarget", myTarget)
.append("myUrl", myUrl)
.append("myConceptMapGroups - size", myConceptMapGroups.size())
.toString();
}
}

View File

@ -0,0 +1,131 @@
package ca.uhn.fhir.jpa.entity;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "TRM_CONCEPT_MAP_GROUP")
public class TermConceptMapGroup implements Serializable {
@Id()
@SequenceGenerator(name = "SEQ_CONCEPT_MAP_GROUP_PID", sequenceName = "SEQ_CONCEPT_MAP_GROUP_PID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_MAP_GROUP_PID")
@Column(name = "PID")
private Long myId;
@ManyToOne()
@JoinColumn(name = "CONCEPT_MAP_PID", nullable = false, referencedColumnName = "PID", foreignKey=@ForeignKey(name="FK_TCMGROUP_CONCEPTMAP"))
private TermConceptMap myConceptMap;
@Column(name = "SOURCE_URL", nullable = false, length = 200)
private String mySource;
@Column(name = "SOURCE_VERSION", length = 50)
private String mySourceVersion;
@Column(name = "TARGET_URL", nullable = false, length = 200)
private String myTarget;
@Column(name = "TARGET_VERSION", length = 50)
private String myTargetVersion;
@OneToMany(mappedBy = "myConceptMapGroup")
private List<TermConceptMapGroupElement> myConceptMapGroupElements;
private String myConceptMapUrl;
private String mySourceValueSet;
private String myTargetValueSet;
public TermConceptMap getConceptMap() {
return myConceptMap;
}
public void setConceptMap(TermConceptMap theTermConceptMap) {
myConceptMap = theTermConceptMap;
}
public List<TermConceptMapGroupElement> getConceptMapGroupElements() {
if (myConceptMapGroupElements == null) {
myConceptMapGroupElements = new ArrayList<>();
}
return myConceptMapGroupElements;
}
public String getConceptMapUrl() {
if (myConceptMapUrl == null) {
myConceptMapUrl = getConceptMap().getUrl();
}
return myConceptMapUrl;
}
public Long getId() {
return myId;
}
public String getSource() {
return mySource;
}
public void setSource(String theSource) {
this.mySource = theSource;
}
public String getSourceValueSet() {
if (mySourceValueSet == null) {
mySourceValueSet = getConceptMap().getSource();
}
return mySourceValueSet;
}
public String getSourceVersion() {
return mySourceVersion;
}
public void setSourceVersion(String theSourceVersion) {
mySourceVersion = theSourceVersion;
}
public String getTarget() {
return myTarget;
}
public void setTarget(String theTarget) {
this.myTarget = theTarget;
}
public String getTargetValueSet() {
if (myTargetValueSet == null) {
myTargetValueSet = getConceptMap().getTarget();
}
return myTargetValueSet;
}
public String getTargetVersion() {
return myTargetVersion;
}
public void setTargetVersion(String theTargetVersion) {
myTargetVersion = theTargetVersion;
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("myId", myId)
.append("myConceptMap - id", myConceptMap.getId())
.append("mySource", mySource)
.append("mySourceVersion", mySourceVersion)
.append("myTarget", myTarget)
.append("myTargetVersion", myTargetVersion)
.append("myConceptMapGroupElements - size", myConceptMapGroupElements.size())
.append("myConceptMapUrl", this.getConceptMapUrl())
.append("mySourceValueSet", this.getSourceValueSet())
.append("myTargetValueSet", this.getTargetValueSet())
.toString();
}
}

View File

@ -0,0 +1,144 @@
package ca.uhn.fhir.jpa.entity;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name = "TRM_CONCEPT_MAP_GRP_ELEMENT", indexes = {
@Index(name = "IDX_CNCPT_MAP_GRP_CD", columnList = "SOURCE_CODE")
})
public class TermConceptMapGroupElement implements Serializable {
@Id()
@SequenceGenerator(name = "SEQ_CONCEPT_MAP_GRP_ELM_PID", sequenceName = "SEQ_CONCEPT_MAP_GRP_ELM_PID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CONCEPT_MAP_GRP_ELM_PID")
@Column(name = "PID")
private Long myId;
@ManyToOne()
@JoinColumn(name = "CONCEPT_MAP_GROUP_PID", nullable = false, referencedColumnName = "PID", foreignKey=@ForeignKey(name="FK_TCMGELEMENT_GROUP"))
private TermConceptMapGroup myConceptMapGroup;
@Column(name = "SOURCE_CODE", nullable = false, length = 50)
private String myCode;
@Column(name = "SOURCE_DISPLAY", length = TermConcept.MAX_DESC_LENGTH)
private String myDisplay;
@OneToMany(mappedBy = "myConceptMapGroupElement")
private List<TermConceptMapGroupElementTarget> myConceptMapGroupElementTargets;
private String myConceptMapUrl;
private String mySystem;
private String mySystemVersion;
private String myValueSet;
public String getCode() {
return myCode;
}
public void setCode(String theCode) {
myCode = theCode;
}
public TermConceptMapGroup getConceptMapGroup() {
return myConceptMapGroup;
}
public void setConceptMapGroup(TermConceptMapGroup theTermConceptMapGroup) {
myConceptMapGroup = theTermConceptMapGroup;
}
public List<TermConceptMapGroupElementTarget> getConceptMapGroupElementTargets() {
if (myConceptMapGroupElementTargets == null) {
myConceptMapGroupElementTargets = new ArrayList<>();
}
return myConceptMapGroupElementTargets;
}
public String getConceptMapUrl() {
if (myConceptMapUrl == null) {
myConceptMapUrl = getConceptMapGroup().getConceptMap().getUrl();
}
return myConceptMapUrl;
}
public String getDisplay() {
return myDisplay;
}
public void setDisplay(String theDisplay) {
myDisplay = theDisplay;
}
public Long getId() {
return myId;
}
public String getSystem() {
if (mySystem == null) {
mySystem = getConceptMapGroup().getSource();
}
return mySystem;
}
public String getSystemVersion() {
if (mySystemVersion == null) {
mySystemVersion = getConceptMapGroup().getSourceVersion();
}
return mySystemVersion;
}
public String getValueSet() {
if (myValueSet == null) {
myValueSet = getConceptMapGroup().getSourceValueSet();
}
return myValueSet;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TermConceptMapGroupElement)) return false;
TermConceptMapGroupElement that = (TermConceptMapGroupElement) o;
return new EqualsBuilder()
.append(getCode(), that.getCode())
.append(getSystem(), that.getSystem())
.append(getSystemVersion(), that.getSystemVersion())
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(getCode())
.append(getSystem())
.append(getSystemVersion())
.toHashCode();
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("myId", myId)
.append("myConceptMapGroup - id", myConceptMapGroup.getId())
.append("myCode", myCode)
.append("myDisplay", myDisplay)
.append("myConceptMapGroupElementTargets - size", myConceptMapGroupElementTargets.size())
.append("myConceptMapUrl", this.getConceptMapUrl())
.append("mySystem", this.getSystem())
.append("mySystemVersion", this.getSystemVersion())
.append("myValueSet", this.getValueSet())
.toString();
}
}

View File

@ -0,0 +1,146 @@
package ca.uhn.fhir.jpa.entity;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
import javax.persistence.*;
import java.io.Serializable;
@Entity
@Table(name = "TRM_CONCEPT_MAP_GRP_ELM_TGT", indexes = {
@Index(name = "IDX_CNCPT_MP_GRP_ELM_TGT_CD", columnList = "TARGET_CODE")
})
public class TermConceptMapGroupElementTarget implements Serializable {
@Id()
@SequenceGenerator(name = "SEQ_CNCPT_MAP_GRP_ELM_TGT_PID", sequenceName = "SEQ_CNCPT_MAP_GRP_ELM_TGT_PID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_CNCPT_MAP_GRP_ELM_TGT_PID")
@Column(name = "PID")
private Long myId;
@ManyToOne()
@JoinColumn(name = "CONCEPT_MAP_GRP_ELM_PID", nullable = false, referencedColumnName = "PID", foreignKey=@ForeignKey(name="FK_TCMGETARGET_ELEMENT"))
private TermConceptMapGroupElement myConceptMapGroupElement;
@Column(name = "TARGET_CODE", nullable = false, length = 50)
private String myCode;
@Column(name = "TARGET_DISPLAY", length = TermConcept.MAX_DESC_LENGTH)
private String myDisplay;
@Enumerated(EnumType.STRING)
@Column(name = "TARGET_EQUIVALENCE", length = 50)
private ConceptMapEquivalence myEquivalence;
private String myConceptMapUrl;
private String mySystem;
private String mySystemVersion;
private String myValueSet;
public String getCode() {
return myCode;
}
public void setCode(String theCode) {
myCode = theCode;
}
public TermConceptMapGroupElement getConceptMapGroupElement() {
return myConceptMapGroupElement;
}
public void setConceptMapGroupElement(TermConceptMapGroupElement theTermConceptMapGroupElement) {
myConceptMapGroupElement = theTermConceptMapGroupElement;
}
public String getConceptMapUrl() {
if (myConceptMapUrl == null) {
myConceptMapUrl = getConceptMapGroupElement().getConceptMapGroup().getConceptMap().getUrl();
}
return myConceptMapUrl;
}
public String getDisplay() {
return myDisplay;
}
public void setDisplay(String theDisplay) {
myDisplay = theDisplay;
}
public ConceptMapEquivalence getEquivalence() {
return myEquivalence;
}
public void setEquivalence(ConceptMapEquivalence theEquivalence) {
myEquivalence = theEquivalence;
}
public Long getId() {
return myId;
}
public String getSystem() {
if (mySystem == null) {
mySystem = getConceptMapGroupElement().getConceptMapGroup().getTarget();
}
return mySystem;
}
public String getSystemVersion() {
if (mySystemVersion == null) {
mySystemVersion = getConceptMapGroupElement().getConceptMapGroup().getTargetVersion();
}
return mySystemVersion;
}
public String getValueSet() {
if (myValueSet == null) {
myValueSet = getConceptMapGroupElement().getConceptMapGroup().getTargetValueSet();
}
return myValueSet;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TermConceptMapGroupElementTarget)) return false;
TermConceptMapGroupElementTarget that = (TermConceptMapGroupElementTarget) o;
return new EqualsBuilder()
.append(getCode(), that.getCode())
.append(getEquivalence(), that.getEquivalence())
.append(getSystem(), that.getSystem())
.append(getSystemVersion(), that.getSystemVersion())
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(getCode())
.append(getEquivalence())
.append(getSystem())
.append(getSystemVersion())
.toHashCode();
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE)
.append("myId", myId)
.append("myConceptMapGroupElement - id", myConceptMapGroupElement.getId())
.append("myCode", myCode)
.append("myDisplay", myDisplay)
.append("myEquivalence", myEquivalence.toCode())
.append("myConceptMapUrl", this.getConceptMapUrl())
.append("mySystem", this.getSystem())
.append("mySystemVersion", this.getSystemVersion())
.append("myValueSet", this.getValueSet())
.toString();
}
}

View File

@ -0,0 +1,153 @@
package ca.uhn.fhir.jpa.provider.dstu3;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoConceptMap;
import ca.uhn.fhir.jpa.term.TranslationRequest;
import ca.uhn.fhir.jpa.term.TranslationResult;
import ca.uhn.fhir.jpa.util.JpaConstants;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.convertors.VersionConvertor_30_40;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.exceptions.FHIRException;
import javax.servlet.http.HttpServletRequest;
public class BaseJpaResourceProviderConceptMapDstu3 extends JpaResourceProviderDstu3<ConceptMap> {
@Operation(name = JpaConstants.OPERATION_TRANSLATE, idempotent = true, returnParameters = {
@OperationParam(name = "result", type = BooleanType.class, min = 1, max = 1),
@OperationParam(name = "message", type = StringType.class, min = 0, max = 1),
})
public Parameters translate(
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdType theId,
@OperationParam(name = "code", min = 0, max = 1) CodeType theSourceCode,
@OperationParam(name = "system", min = 0, max = 1) UriType theSourceCodeSystem,
@OperationParam(name = "version", min = 0, max = 1) StringType theSourceCodeSystemVersion,
@OperationParam(name = "source", min = 0, max = 1) UriType theSourceValueSet,
@OperationParam(name = "coding", min = 0, max = 1) Coding theSourceCoding,
@OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConcept theSourceCodeableConcept,
@OperationParam(name = "target", min = 0, max = 1) UriType theTargetValueSet,
@OperationParam(name = "targetsystem", min = 0, max = 1) UriType theTargetCodeSystem,
@OperationParam(name = "reverse", min = 0, max = 1) BooleanType theReverse,
RequestDetails theRequestDetails
) {
boolean haveSourceCode = theSourceCode != null
&& theSourceCode.hasValue();
boolean haveSourceCodeSystem = theSourceCodeSystem != null
&& theSourceCodeSystem.hasValue();
boolean haveSourceCodeSystemVersion = theSourceCodeSystemVersion != null
&& theSourceCodeSystemVersion.hasValue();
boolean haveSourceValueSet = theSourceValueSet != null
&& theSourceValueSet.hasValue();
boolean haveSourceCoding = theSourceCoding != null
&& theSourceCoding.hasCode();
boolean haveSourceCodeableConcept= theSourceCodeableConcept != null
&& theSourceCodeableConcept.hasCoding()
&& theSourceCodeableConcept.getCodingFirstRep().hasCode();
boolean haveTargetValueSet = theTargetValueSet != null
&& theTargetValueSet.hasValue();
boolean haveTargetCodeSystem = theTargetCodeSystem != null
&& theTargetCodeSystem.hasValue();
boolean haveReverse = theReverse != null;
boolean haveId = theId != null && theId.hasIdPart();
// <editor-fold desc="Filters">
if ((!haveSourceCode && !haveSourceCoding && !haveSourceCodeableConcept)
|| moreThanOneTrue(haveSourceCode, haveSourceCoding, haveSourceCodeableConcept)) {
throw new InvalidRequestException("One (and only one) of the in parameters (code, coding, codeableConcept) must be provided, to identify the code that is to be translated.");
}
TranslationRequest translationRequest = new TranslationRequest();
try {
// Convert from DSTU3 to R4
if (haveSourceCode) {
translationRequest.getCodeableConcept().addCoding().setCodeElement(VersionConvertor_30_40.convertCode(theSourceCode));
if (haveSourceCodeSystem) {
translationRequest.getCodeableConcept().getCodingFirstRep().setSystemElement(VersionConvertor_30_40.convertUri(theSourceCodeSystem));
}
if (haveSourceCodeSystemVersion) {
translationRequest.getCodeableConcept().getCodingFirstRep().setVersionElement(VersionConvertor_30_40.convertString(theSourceCodeSystemVersion));
}
} else if (haveSourceCoding) {
translationRequest.getCodeableConcept().addCoding(VersionConvertor_30_40.convertCoding(theSourceCoding));
} else {
translationRequest.setCodeableConcept(VersionConvertor_30_40.convertCodeableConcept(theSourceCodeableConcept));
}
if (haveSourceValueSet) {
translationRequest.setSource(VersionConvertor_30_40.convertUri(theSourceValueSet));
}
if (haveTargetValueSet) {
translationRequest.setTarget(VersionConvertor_30_40.convertUri(theTargetValueSet));
}
if (haveTargetCodeSystem) {
translationRequest.setTargetSystem(VersionConvertor_30_40.convertUri(theTargetCodeSystem));
}
if (haveReverse) {
translationRequest.setReverse(VersionConvertor_30_40.convertBoolean(theReverse));
}
if (haveId) {
translationRequest.setResourceId(theId.getIdPartAsLong());
}
} catch (FHIRException fe) {
throw new InternalErrorException(fe);
}
startRequest(theServletRequest);
try {
IFhirResourceDaoConceptMap<ConceptMap> dao = (IFhirResourceDaoConceptMap<ConceptMap>) getDao();
TranslationResult result = dao.translate(translationRequest, theRequestDetails);
// Convert from R4 to DSTU3
return VersionConvertor_30_40.convertParameters(result.toParameters());
} catch (FHIRException fe) {
throw new InternalErrorException(fe);
} finally {
endRequest(theServletRequest);
}
}
private static boolean moreThanOneTrue(boolean... theBooleans) {
boolean haveOne = false;
for (boolean next : theBooleans) {
if (next) {
if (haveOne) {
return true;
} else {
haveOne = true;
}
}
}
return false;
}
}

View File

@ -0,0 +1,142 @@
package ca.uhn.fhir.jpa.provider.r4;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import ca.uhn.fhir.jpa.dao.IFhirResourceDaoConceptMap;
import ca.uhn.fhir.jpa.term.TranslationRequest;
import ca.uhn.fhir.jpa.term.TranslationResult;
import ca.uhn.fhir.jpa.util.JpaConstants;
import ca.uhn.fhir.rest.annotation.IdParam;
import ca.uhn.fhir.rest.annotation.Operation;
import ca.uhn.fhir.rest.annotation.OperationParam;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import org.hl7.fhir.r4.model.*;
import javax.servlet.http.HttpServletRequest;
public class BaseJpaResourceProviderConceptMapR4 extends JpaResourceProviderR4<ConceptMap> {
@Operation(name = JpaConstants.OPERATION_TRANSLATE, idempotent = true, returnParameters = {
@OperationParam(name = "result", type = BooleanType.class, min = 1, max = 1),
@OperationParam(name = "message", type = StringType.class, min = 0, max = 1),
})
public Parameters translate(
HttpServletRequest theServletRequest,
@IdParam(optional = true) IdType theId,
@OperationParam(name = "code", min = 0, max = 1) CodeType theSourceCode,
@OperationParam(name = "system", min = 0, max = 1) UriType theSourceCodeSystem,
@OperationParam(name = "version", min = 0, max = 1) StringType theSourceCodeSystemVersion,
@OperationParam(name = "source", min = 0, max = 1) UriType theSourceValueSet,
@OperationParam(name = "coding", min = 0, max = 1) Coding theSourceCoding,
@OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConcept theSourceCodeableConcept,
@OperationParam(name = "target", min = 0, max = 1) UriType theTargetValueSet,
@OperationParam(name = "targetsystem", min = 0, max = 1) UriType theTargetCodeSystem,
@OperationParam(name = "reverse", min = 0, max = 1) BooleanType theReverse,
RequestDetails theRequestDetails
) {
boolean haveSourceCode = theSourceCode != null
&& theSourceCode.hasCode();
boolean haveSourceCodeSystem = theSourceCodeSystem != null
&& theSourceCodeSystem.hasValue();
boolean haveSourceCodeSystemVersion = theSourceCodeSystemVersion != null
&& theSourceCodeSystemVersion.hasValue();
boolean haveSourceValueSet = theSourceValueSet != null
&& theSourceValueSet.hasValue();
boolean haveSourceCoding = theSourceCoding != null
&& theSourceCoding.hasCode();
boolean haveSourceCodeableConcept= theSourceCodeableConcept != null
&& theSourceCodeableConcept.hasCoding()
&& theSourceCodeableConcept.getCodingFirstRep().hasCode();
boolean haveTargetValueSet = theTargetValueSet != null
&& theTargetValueSet.hasValue();
boolean haveTargetCodeSystem = theTargetCodeSystem != null
&& theTargetCodeSystem.hasValue();
boolean haveReverse = theReverse != null;
boolean haveId = theId != null && theId.hasIdPart();
// <editor-fold desc="Filters">
if ((!haveSourceCode && !haveSourceCoding && !haveSourceCodeableConcept)
|| moreThanOneTrue(haveSourceCode, haveSourceCoding, haveSourceCodeableConcept)) {
throw new InvalidRequestException("One (and only one) of the in parameters (code, coding, codeableConcept) must be provided, to identify the code that is to be translated.");
}
TranslationRequest translationRequest = new TranslationRequest();
if (haveSourceCode) {
translationRequest.getCodeableConcept().addCoding().setCodeElement(theSourceCode);
if (haveSourceCodeSystem) {
translationRequest.getCodeableConcept().getCodingFirstRep().setSystemElement(theSourceCodeSystem);
}
if (haveSourceCodeSystemVersion) {
translationRequest.getCodeableConcept().getCodingFirstRep().setVersionElement(theSourceCodeSystemVersion);
}
} else if (haveSourceCoding) {
translationRequest.getCodeableConcept().addCoding(theSourceCoding);
} else {
translationRequest.setCodeableConcept(theSourceCodeableConcept);
}
if (haveSourceValueSet) {
translationRequest.setSource(theSourceValueSet);
}
if (haveTargetValueSet) {
translationRequest.setTarget(theTargetValueSet);
}
if (haveTargetCodeSystem) {
translationRequest.setTargetSystem(theTargetCodeSystem);
}
if (haveReverse) {
translationRequest.setReverse(theReverse);
}
if (haveId) {
translationRequest.setResourceId(theId.getIdPartAsLong());
}
startRequest(theServletRequest);
try {
IFhirResourceDaoConceptMap<ConceptMap> dao = (IFhirResourceDaoConceptMap<ConceptMap>) getDao();
TranslationResult result = dao.translate(translationRequest, theRequestDetails);
return result.toParameters();
} finally {
endRequest(theServletRequest);
}
}
private static boolean moreThanOneTrue(boolean... theBooleans) {
boolean haveOne = false;
for (boolean next : theBooleans) {
if (next) {
if (haveOne) {
return true;
} else {
haveOne = true;
}
}
}
return false;
}
}

View File

@ -27,24 +27,32 @@ import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem;
import ca.uhn.fhir.jpa.dao.data.*;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.util.ScrollableResultsIterator;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.ObjectUtil;
import ca.uhn.fhir.util.StopWatch;
import ca.uhn.fhir.util.ValidateUtil;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Stopwatch;
import com.google.common.collect.ArrayListMultimap;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.time.DateUtils;
import org.apache.lucene.search.Query;
import org.hibernate.ScrollMode;
import org.hibernate.ScrollableResults;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.FullTextQuery;
import org.hibernate.search.query.dsl.BooleanJunction;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.CodeSystem;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.ValueSet;
import org.springframework.beans.factory.annotation.Autowired;
@ -58,9 +66,12 @@ import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.PostConstruct;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.PersistenceContextType;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.*;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@ -69,14 +80,26 @@ import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc {
public static final int DEFAULT_FETCH_SIZE = 250;
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseHapiTerminologySvcImpl.class);
private static final Object PLACEHOLDER_OBJECT = new Object();
private static boolean ourForceSaveDeferredAlwaysForUnitTest;
private static boolean ourLastResultsFromTranslationCache; // For testing.
private static boolean ourLastResultsFromTranslationWithReverseCache; // For testing.
@Autowired
protected ITermCodeSystemDao myCodeSystemDao;
@Autowired
protected ITermConceptDao myConceptDao;
@Autowired
protected ITermConceptMapDao myConceptMapDao;
@Autowired
protected ITermConceptMapGroupDao myConceptMapGroupDao;
@Autowired
protected ITermConceptMapGroupElementDao myConceptMapGroupElementDao;
@Autowired
protected ITermConceptMapGroupElementTargetDao myConceptMapGroupElementTargetDao;
@Autowired
protected ITermConceptPropertyDao myConceptPropertyDao;
@Autowired
protected ITermConceptDesignationDao myConceptDesignationDao;
@ -102,6 +125,12 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
@Autowired(required = false)
private IFhirResourceDaoCodeSystem<?, ?, ?> myCodeSystemResourceDao;
private Cache<TranslationQuery, List<TermConceptMapGroupElementTarget>> myTranslationCache;
private Cache<TranslationQuery, List<TermConceptMapGroupElement>> myTranslationWithReverseCache;
private int myFetchSize = DEFAULT_FETCH_SIZE;
private void addCodeIfNotAlreadyAdded(String theCodeSystem, ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, TermConcept theConcept) {
if (theAddedCodes.add(theConcept.getCode())) {
ValueSet.ValueSetExpansionContainsComponent contains = theExpansionComponent.addContains();
@ -160,6 +189,23 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
return retVal;
}
@PostConstruct
public void buildTranslationCaches() {
Long timeout = myDaoConfig.getTranslationCachesExpireAfterWriteInMinutes();
myTranslationCache =
Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(timeout, TimeUnit.MINUTES)
.build();
myTranslationWithReverseCache =
Caffeine.newBuilder()
.maximumSize(10000)
.expireAfterWrite(timeout, TimeUnit.MINUTES)
.build();
}
protected abstract IIdType createOrUpdateCodeSystem(CodeSystem theCodeSystemResource);
protected abstract void createOrUpdateConceptMap(ConceptMap theNextConceptMap);
@ -869,6 +915,112 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
return csId;
}
@Override
@Transactional
public void storeTermConceptMapAndChildren(ResourceTable theResourceTable, ConceptMap theConceptMap) {
ourLog.info("Storing TermConceptMap...");
ValidateUtil.isTrueOrThrowInvalidRequest(theResourceTable != null, "No resource supplied.");
ValidateUtil.isNotBlankOrThrowInvalidRequest(theConceptMap.getUrl(), "No URL supplied.");
TermConceptMap termConceptMap = new TermConceptMap();
termConceptMap.setResource(theResourceTable);
termConceptMap.setUrl(theConceptMap.getUrl());
// Get existing entity so it can be deleted.
Optional<TermConceptMap> optionalExistingTermConceptMapById = myConceptMapDao.findTermConceptMapByResourcePid(termConceptMap.getResourcePid());
/*
* For now we always delete old versions. At some point, it would be nice to allow configuration to keep old versions.
*/
if (optionalExistingTermConceptMapById.isPresent()) {
Long id = optionalExistingTermConceptMapById.get().getId();
ourLog.info("Deleting existing TermConceptMap {} and its children...", id);
myConceptMapGroupElementTargetDao.deleteTermConceptMapGroupElementTargetById(id);
myConceptMapGroupElementDao.deleteTermConceptMapGroupElementById(id);
myConceptMapGroupDao.deleteTermConceptMapGroupById(id);
myConceptMapDao.deleteTermConceptMapById(id);
ourLog.info("Done deleting existing TermConceptMap {} and its children.", id);
ourLog.info("Flushing...");
myConceptMapGroupElementTargetDao.flush();
myConceptMapGroupElementDao.flush();
myConceptMapGroupDao.flush();
myConceptMapDao.flush();
ourLog.info("Done flushing.");
}
/*
* Do the upload.
*/
String conceptMapUrl = termConceptMap.getUrl();
Optional<TermConceptMap> optionalExistingTermConceptMapByUrl = myConceptMapDao.findTermConceptMapByUrl(conceptMapUrl);
if (!optionalExistingTermConceptMapByUrl.isPresent()) {
try {
String source = theConceptMap.getSourceUriType().getValueAsString();
if (isNotBlank(source)) {
termConceptMap.setSource(source);
}
String target = theConceptMap.getTargetUriType().getValueAsString();
if (isNotBlank(target)) {
termConceptMap.setTarget(target);
}
} catch (FHIRException fe) {
throw new InternalErrorException(fe);
}
myConceptMapDao.save(termConceptMap);
if (theConceptMap.hasGroup()) {
TermConceptMapGroup termConceptMapGroup;
for (ConceptMap.ConceptMapGroupComponent group : theConceptMap.getGroup()) {
termConceptMapGroup = new TermConceptMapGroup();
termConceptMapGroup.setConceptMap(termConceptMap);
termConceptMapGroup.setSource(group.getSource());
termConceptMapGroup.setSourceVersion(group.getSourceVersion());
termConceptMapGroup.setTarget(group.getTarget());
termConceptMapGroup.setTargetVersion(group.getTargetVersion());
myConceptMapGroupDao.save(termConceptMapGroup);
if (group.hasElement()) {
TermConceptMapGroupElement termConceptMapGroupElement;
for (ConceptMap.SourceElementComponent element : group.getElement()) {
termConceptMapGroupElement = new TermConceptMapGroupElement();
termConceptMapGroupElement.setConceptMapGroup(termConceptMapGroup);
termConceptMapGroupElement.setCode(element.getCode());
termConceptMapGroupElement.setDisplay(element.getDisplay());
myConceptMapGroupElementDao.save(termConceptMapGroupElement);
if (element.hasTarget()) {
TermConceptMapGroupElementTarget termConceptMapGroupElementTarget;
for (ConceptMap.TargetElementComponent target : element.getTarget()) {
termConceptMapGroupElementTarget = new TermConceptMapGroupElementTarget();
termConceptMapGroupElementTarget.setConceptMapGroupElement(termConceptMapGroupElement);
termConceptMapGroupElementTarget.setCode(target.getCode());
termConceptMapGroupElementTarget.setDisplay(target.getDisplay());
termConceptMapGroupElementTarget.setEquivalence(target.getEquivalence());
myConceptMapGroupElementTargetDao.saveAndFlush(termConceptMapGroupElementTarget);
}
}
}
}
}
}
} else {
TermConceptMap existingTermConceptMap = optionalExistingTermConceptMapByUrl.get();
String msg = myContext.getLocalizer().getMessage(
BaseHapiTerminologySvcImpl.class,
"cannotCreateDuplicateConceptMapUrl",
conceptMapUrl,
existingTermConceptMap.getResourcePid());
throw new UnprocessableEntityException(msg);
}
ourLog.info("Done storing TermConceptMap.");
}
@Override
public boolean supportsSystem(String theSystem) {
TermCodeSystem cs = getCodeSystem(theSystem);
@ -883,6 +1035,166 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
return retVal;
}
@Override
public List<TermConceptMapGroupElementTarget> translate(TranslationRequest theTranslationRequest) {
List<TermConceptMapGroupElementTarget> retVal = new ArrayList<>();
CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<TermConceptMapGroupElementTarget> query = criteriaBuilder.createQuery(TermConceptMapGroupElementTarget.class);
Root<TermConceptMapGroupElementTarget> root = query.from(TermConceptMapGroupElementTarget.class);
Join<TermConceptMapGroupElementTarget, TermConceptMapGroupElement> elementJoin = root.join("myConceptMapGroupElement");
Join<TermConceptMapGroupElement, TermConceptMapGroup> groupJoin = elementJoin.join("myConceptMapGroup");
Join<TermConceptMapGroup, TermConceptMap> conceptMapJoin = groupJoin.join("myConceptMap");
List<TranslationQuery> translationQueries = theTranslationRequest.getTranslationQueries();
List<TermConceptMapGroupElementTarget> cachedTargets;
ArrayList<Predicate> predicates;
Coding coding;
for (TranslationQuery translationQuery : translationQueries) {
cachedTargets = myTranslationCache.getIfPresent(translationQuery);
if (cachedTargets == null) {
final List<TermConceptMapGroupElementTarget> targets = new ArrayList<>();
predicates = new ArrayList<>();
coding = translationQuery.getCoding();
if (coding.hasCode()) {
predicates.add(criteriaBuilder.equal(elementJoin.get("myCode"), coding.getCode()));
} else {
throw new InvalidRequestException("A code must be provided for translation to occur.");
}
if (coding.hasSystem()) {
predicates.add(criteriaBuilder.equal(groupJoin.get("mySource"), coding.getSystem()));
}
if (coding.hasVersion()) {
predicates.add(criteriaBuilder.equal(groupJoin.get("mySourceVersion"), coding.getVersion()));
}
if (translationQuery.hasTargetSystem()) {
predicates.add(criteriaBuilder.equal(groupJoin.get("myTarget"), translationQuery.getTargetSystem().getValueAsString()));
}
if (translationQuery.hasSource()) {
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getSource().getValueAsString()));
}
if (translationQuery.hasTarget()) {
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myTarget"), translationQuery.getTarget().getValueAsString()));
}
if (translationQuery.hasResourceId()) {
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myResourcePid"), translationQuery.getResourceId()));
}
Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0]));
query.where(outerPredicate);
// Use scrollable results.
final TypedQuery<TermConceptMapGroupElementTarget> typedQuery = myEntityManager.createQuery(query.select(root));
org.hibernate.query.Query<TermConceptMapGroupElementTarget> hibernateQuery = (org.hibernate.query.Query<TermConceptMapGroupElementTarget>) typedQuery;
hibernateQuery.setFetchSize(myFetchSize);
ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY);
Iterator<TermConceptMapGroupElementTarget> scrollableResultsIterator = new ScrollableResultsIterator<>(scrollableResults);
while (scrollableResultsIterator.hasNext()) {
targets.add(scrollableResultsIterator.next());
}
ourLastResultsFromTranslationCache = false; // For testing.
myTranslationCache.get(translationQuery, k -> targets);
retVal.addAll(targets);
} else {
ourLastResultsFromTranslationCache = true; // For testing.
retVal.addAll(cachedTargets);
}
}
return retVal;
}
@Override
public List<TermConceptMapGroupElement> translateWithReverse(TranslationRequest theTranslationRequest) {
List<TermConceptMapGroupElement> retVal = new ArrayList<>();
CriteriaBuilder criteriaBuilder = myEntityManager.getCriteriaBuilder();
CriteriaQuery<TermConceptMapGroupElement> query = criteriaBuilder.createQuery(TermConceptMapGroupElement.class);
Root<TermConceptMapGroupElement> root = query.from(TermConceptMapGroupElement.class);
Join<TermConceptMapGroupElement, TermConceptMapGroupElementTarget> targetJoin = root.join("myConceptMapGroupElementTargets");
Join<TermConceptMapGroupElement, TermConceptMapGroup> groupJoin = root.join("myConceptMapGroup");
Join<TermConceptMapGroup, TermConceptMap> conceptMapJoin = groupJoin.join("myConceptMap");
List<TranslationQuery> translationQueries = theTranslationRequest.getTranslationQueries();
List<TermConceptMapGroupElement> cachedElements;
ArrayList<Predicate> predicates;
Coding coding;
for (TranslationQuery translationQuery : translationQueries) {
cachedElements = myTranslationWithReverseCache.getIfPresent(translationQuery);
if (cachedElements == null) {
final List<TermConceptMapGroupElement> elements = new ArrayList<>();
predicates = new ArrayList<>();
coding = translationQuery.getCoding();
if (coding.hasCode()) {
predicates.add(criteriaBuilder.equal(targetJoin.get("myCode"), coding.getCode()));
} else {
throw new InvalidRequestException("A code must be provided for translation to occur.");
}
if (coding.hasSystem()) {
predicates.add(criteriaBuilder.equal(groupJoin.get("myTarget"), coding.getSystem()));
}
if (coding.hasVersion()) {
predicates.add(criteriaBuilder.equal(groupJoin.get("myTargetVersion"), coding.getVersion()));
}
if (translationQuery.hasTargetSystem()) {
predicates.add(criteriaBuilder.equal(groupJoin.get("mySource"), translationQuery.getTargetSystem().getValueAsString()));
}
if (translationQuery.hasSource()) {
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myTarget"), translationQuery.getSource().getValueAsString()));
}
if (translationQuery.hasTarget()) {
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("mySource"), translationQuery.getTarget().getValueAsString()));
}
if (translationQuery.hasResourceId()) {
predicates.add(criteriaBuilder.equal(conceptMapJoin.get("myResourcePid"), translationQuery.getResourceId()));
}
Predicate outerPredicate = criteriaBuilder.and(predicates.toArray(new Predicate[0]));
query.where(outerPredicate);
// Use scrollable results.
final TypedQuery<TermConceptMapGroupElement> typedQuery = myEntityManager.createQuery(query.select(root));
org.hibernate.query.Query<TermConceptMapGroupElement> hibernateQuery = (org.hibernate.query.Query<TermConceptMapGroupElement>) typedQuery;
hibernateQuery.setFetchSize(myFetchSize);
ScrollableResults scrollableResults = hibernateQuery.scroll(ScrollMode.FORWARD_ONLY);
Iterator<TermConceptMapGroupElement> scrollableResultsIterator = new ScrollableResultsIterator<>(scrollableResults);
while (scrollableResultsIterator.hasNext()) {
elements.add(scrollableResultsIterator.next());
}
ourLastResultsFromTranslationWithReverseCache = false; // For testing.
myTranslationWithReverseCache.get(translationQuery, k -> elements);
retVal.addAll(elements);
} else {
ourLastResultsFromTranslationWithReverseCache = true; // For testing.
retVal.addAll(cachedElements);
}
}
return retVal;
}
private int validateConceptForStorage(TermConcept theConcept, TermCodeSystemVersion theCodeSystem, ArrayList<String> theConceptsStack,
IdentityHashMap<TermConcept, Object> theAllConcepts) {
ValidateUtil.isTrueOrThrowInvalidRequest(theConcept.getCodeSystemVersion() != null, "CodesystemValue is null");
@ -921,6 +1233,54 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
}
}
/**
* This method is present only for unit tests, do not call from client code
*/
@VisibleForTesting
static void clearOurLastResultsFromTranslationCache() {
ourLastResultsFromTranslationCache = false;
}
/**
* This method is present only for unit tests, do not call from client code
*/
@VisibleForTesting
static void clearOurLastResultsFromTranslationWithReverseCache() {
ourLastResultsFromTranslationWithReverseCache = false;
}
/**
* This method is present only for unit tests, do not call from client code
*/
@VisibleForTesting
void clearTranslationCache() {
myTranslationCache.invalidateAll();
}
/**
* This method is present only for unit tests, do not call from client code
*/
@VisibleForTesting()
void clearTranslationWithReverseCache() {
myTranslationWithReverseCache.invalidateAll();
}
/**
* This method is present only for unit tests, do not call from client code
*/
@VisibleForTesting
static boolean isOurLastResultsFromTranslationCache() {
return ourLastResultsFromTranslationCache;
}
/**
* This method is present only for unit tests, do not call from client code
*/
@VisibleForTesting
static boolean isOurLastResultsFromTranslationWithReverseCache() {
return ourLastResultsFromTranslationWithReverseCache;
}
/**
* This method is present only for unit tests, do not call from client code
*/
@ -928,6 +1288,4 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc
public static void setForceSaveDeferredAlwaysForUnitTest(boolean theForceSaveDeferredAlwaysForUnitTest) {
ourForceSaveDeferredAlwaysForUnitTest = theForceSaveDeferredAlwaysForUnitTest;
}
}

View File

@ -40,9 +40,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*
* http://www.apache.org/licenses/LICENSE-2.0
*
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.

View File

@ -1,10 +1,9 @@
package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.rest.api.server.RequestDetails;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.ValueSet;
import java.util.List;
@ -69,6 +68,11 @@ public interface IHapiTerminologySvc {
*/
IIdType storeNewCodeSystemVersion(org.hl7.fhir.r4.model.CodeSystem theCodeSystemResource, TermCodeSystemVersion theCodeSystemVersion, RequestDetails theRequestDetails, List<org.hl7.fhir.r4.model.ValueSet> theValueSets, List<org.hl7.fhir.r4.model.ConceptMap> theConceptMaps);
void storeTermConceptMapAndChildren(ResourceTable theResourceTable, ConceptMap theConceptMap);
boolean supportsSystem(String theCodeSystem);
List<TermConceptMapGroupElementTarget> translate(TranslationRequest theTranslationRequest);
List<TermConceptMapGroupElement> translateWithReverse(TranslationRequest theTranslationRequest);
}

View File

@ -0,0 +1,74 @@
package ca.uhn.fhir.jpa.term;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.r4.model.CodeType;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r4.model.UriType;
public class TranslationMatch {
private Coding myConcept;
private CodeType myEquivalence;
private UriType mySource;
public TranslationMatch() {
super();
}
public Coding getConcept() {
return myConcept;
}
public void setConcept(Coding theConcept) {
myConcept = theConcept;
}
public CodeType getEquivalence() {
return myEquivalence;
}
public void setEquivalence(CodeType theEquivalence) {
myEquivalence = theEquivalence;
}
public UriType getSource() {
return mySource;
}
public void setSource(UriType theSource) {
mySource = theSource;
}
public void toParameterParts(ParametersParameterComponent theParam) {
if (myEquivalence != null) {
theParam.addPart().setName("equivalence").setValue(myEquivalence);
}
if (myConcept != null) {
theParam.addPart().setName("concept").setValue(myConcept);
}
if (mySource != null) {
theParam.addPart().setName("source").setValue(mySource);
}
}
}

View File

@ -0,0 +1,128 @@
package ca.uhn.fhir.jpa.term;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.UriType;
public class TranslationQuery {
private Coding myCoding;
private Long myResourceId;
private UriType mySource;
private UriType myTarget;
private UriType myTargetSystem;
public TranslationQuery() {
super();
myCoding = new Coding();
}
public Coding getCoding() {
return myCoding;
}
public void setCoding(Coding theCoding) {
myCoding = theCoding;
}
public boolean hasResourceId() {
return myResourceId != null;
}
public Long getResourceId() {
return myResourceId;
}
public void setResourceId(Long theResourceId) {
myResourceId = theResourceId;
}
public boolean hasSource() {
return mySource != null && mySource.hasValue();
}
public UriType getSource() {
return mySource;
}
public void setSource(UriType theSource) {
mySource = theSource;
}
public boolean hasTarget() {
return myTarget != null && myTarget.hasValue();
}
public UriType getTarget() {
return myTarget;
}
public void setTarget(UriType theTarget) {
myTarget = theTarget;
}
public boolean hasTargetSystem() {
return myTargetSystem != null && myTargetSystem.hasValue();
}
public UriType getTargetSystem() {
return myTargetSystem;
}
public void setTargetSystem(UriType theTargetSystem) {
myTargetSystem = theTargetSystem;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof TranslationQuery)) return false;
TranslationQuery that = (TranslationQuery) o;
return new EqualsBuilder()
.append(getCoding().getCode(), that.getCoding().getCode())
.append(getCoding().getSystem(), that.getCoding().getSystem())
.append(getCoding().getVersion(), that.getCoding().getVersion())
.append(getResourceId(), that.getResourceId())
.append(getSource(), that.getSource())
.append(getTarget(), that.getTarget())
.append(getTargetSystem(), that.getTargetSystem())
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(getCoding().getCode())
.append(getCoding().getSystem())
.append(getCoding().getVersion())
.append(getResourceId())
.append(getSource())
.append(getTarget())
.append(getTargetSystem())
.toHashCode();
}
}

View File

@ -0,0 +1,155 @@
package ca.uhn.fhir.jpa.term;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.CodeableConcept;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.UriType;
import java.util.ArrayList;
import java.util.List;
public class TranslationRequest {
private CodeableConcept myCodeableConcept;
private Long myResourceId;
private BooleanType myReverse;
private UriType mySource;
private UriType myTarget;
private UriType myTargetSystem;
public TranslationRequest() {
super();
myCodeableConcept = new CodeableConcept();
}
public CodeableConcept getCodeableConcept() {
return myCodeableConcept;
}
public void setCodeableConcept(CodeableConcept theCodeableConcept) {
myCodeableConcept = theCodeableConcept;
}
public boolean hasResourceId() {
return myResourceId != null;
}
public Long getResourceId() {
return myResourceId;
}
public void setResourceId(Long theResourceId) {
myResourceId = theResourceId;
}
public boolean hasReverse() {
return myReverse != null;
}
public BooleanType getReverse() {
return myReverse;
}
public boolean getReverseAsBoolean() {
if (hasReverse()) {
return myReverse.booleanValue();
}
return false;
}
public void setReverse(BooleanType theReverse) {
myReverse = theReverse;
}
public void setReverse(boolean theReverse) {
myReverse = new BooleanType(theReverse);
}
public boolean hasSource() {
return mySource != null && mySource.hasValue();
}
public UriType getSource() {
return mySource;
}
public void setSource(UriType theSource) {
mySource = theSource;
}
public boolean hasTarget() {
return myTarget != null && myTarget.hasValue();
}
public UriType getTarget() {
return myTarget;
}
public void setTarget(UriType theTarget) {
myTarget = theTarget;
}
public boolean hasTargetSystem() {
return myTargetSystem != null && myTargetSystem.hasValue();
}
public UriType getTargetSystem() {
return myTargetSystem;
}
public void setTargetSystem(UriType theTargetSystem) {
myTargetSystem = theTargetSystem;
}
public List<TranslationQuery> getTranslationQueries() {
List<TranslationQuery> retVal = new ArrayList<>();
TranslationQuery translationQuery;
for (Coding coding : getCodeableConcept().getCoding()) {
translationQuery = new TranslationQuery();
translationQuery.setCoding(coding);
if (this.hasResourceId()) {
translationQuery.setResourceId(this.getResourceId());
}
if (this.hasSource()) {
translationQuery.setSource(this.getSource());
}
if (this.hasTarget()) {
translationQuery.setTarget(this.getTarget());
}
if (this.hasTargetSystem()) {
translationQuery.setTargetSystem(this.getTargetSystem());
}
retVal.add(translationQuery);
}
return retVal;
}
}

View File

@ -0,0 +1,88 @@
package ca.uhn.fhir.jpa.term;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r4.model.StringType;
import java.util.ArrayList;
import java.util.List;
public class TranslationResult {
private List<TranslationMatch> myMatches;
private StringType myMessage;
private BooleanType myResult;
public TranslationResult() {
super();
myMatches = new ArrayList<>();
}
public List<TranslationMatch> getMatches() {
return myMatches;
}
public void setMatches(List<TranslationMatch> theMatches) {
myMatches = theMatches;
}
public boolean addMatch(TranslationMatch theMatch) {
return myMatches.add(theMatch);
}
public StringType getMessage() {
return myMessage;
}
public void setMessage(StringType theMessage) {
myMessage = theMessage;
}
public BooleanType getResult() {
return myResult;
}
public void setResult(BooleanType theMatched) {
myResult = theMatched;
}
public Parameters toParameters() {
Parameters retVal = new Parameters();
if (myResult != null) {
retVal.addParameter().setName("result").setValue(myResult);
}
if (myMessage != null) {
retVal.addParameter().setName("message").setValue(myMessage);
}
for (TranslationMatch translationMatch : myMatches) {
ParametersParameterComponent matchParam = retVal.addParameter().setName("match");
translationMatch.toParameterParts(matchParam);
}
return retVal;
}
}

View File

@ -160,4 +160,8 @@ public class JpaConstants {
*/
public static final String OPERATION_META_ADD = "$meta-add";
/**
* Operation name for the $translate operation
*/
public static final String OPERATION_TRANSLATE = "$translate";
}

View File

@ -0,0 +1,63 @@
package ca.uhn.fhir.jpa.util;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2018 University Health Network
* %%
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
import org.apache.commons.lang3.Validate;
import org.hibernate.ScrollableResults;
import java.util.Iterator;
public class ScrollableResultsIterator<T extends Object> extends BaseIterator<T> implements Iterator<T> {
private boolean hasNext;
private T myNext;
private ScrollableResults myScroll;
public ScrollableResultsIterator(ScrollableResults theScroll) {
myScroll = theScroll;
}
@SuppressWarnings("unchecked")
private void ensureHaveNext() {
if (myNext == null) {
if (myScroll.next()) {
hasNext = true;
myNext = (T) myScroll.get(0);
} else {
hasNext = false;
}
}
}
@Override
public boolean hasNext() {
ensureHaveNext();
return hasNext;
}
@Override
public T next() {
ensureHaveNext();
Validate.isTrue(hasNext);
T next = myNext;
myNext = null;
return next;
}
}

View File

@ -49,6 +49,15 @@ import static org.mockito.Mockito.when;
public abstract class BaseJpaTest {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(BaseJpaTest.class);
protected static final String CM_URL = "http://example.com/my_concept_map";
protected static final String CS_URL = "http://example.com/my_code_system";
protected static final String CS_URL_2 = "http://example.com/my_code_system2";
protected static final String CS_URL_3 = "http://example.com/my_code_system3";
protected static final String CS_URL_4 = "http://example.com/my_code_system4";
protected static final String VS_URL = "http://example.com/my_value_set";
protected static final String VS_URL_2 = "http://example.com/my_value_set2";
protected ServletRequestDetails mySrd;
protected ArrayList<IServerInterceptor> myServerInterceptorList;
protected IRequestOperationCallback myRequestOperationCallback = mock(IRequestOperationCallback.class);

View File

@ -5,6 +5,7 @@ import ca.uhn.fhir.jpa.config.TestDstu3Config;
import ca.uhn.fhir.jpa.dao.*;
import ca.uhn.fhir.jpa.dao.data.*;
import ca.uhn.fhir.jpa.dao.dstu2.FhirResourceDaoDstu2SearchNoFtTest;
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
import ca.uhn.fhir.jpa.entity.ResourceIndexedSearchParamString;
import ca.uhn.fhir.jpa.entity.ResourceTable;
import ca.uhn.fhir.jpa.provider.dstu3.JpaSystemProviderDstu3;
@ -14,20 +15,22 @@ import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.jpa.util.ResourceCountCache;
import ca.uhn.fhir.jpa.util.SingleItemLoadingCache;
import ca.uhn.fhir.jpa.validation.JpaValidationSupportChainDstu3;
import ca.uhn.fhir.parser.IParser;
import ca.uhn.fhir.parser.StrictErrorHandler;
import ca.uhn.fhir.rest.api.Constants;
import ca.uhn.fhir.rest.api.EncodingEnum;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import ca.uhn.fhir.rest.server.interceptor.IServerInterceptor;
import ca.uhn.fhir.util.TestUtil;
import ca.uhn.fhir.util.UrlUtil;
import org.apache.commons.io.IOUtils;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.Search;
import org.hl7.fhir.convertors.VersionConvertor_30_40;
import org.hl7.fhir.dstu3.hapi.ctx.IValidationSupport;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.junit.After;
import org.junit.AfterClass;
@ -48,7 +51,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import static org.junit.Assert.*;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
@RunWith(SpringJUnit4ClassRunner.class)
@ -91,7 +94,7 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
protected IFhirResourceDao<CompartmentDefinition> myCompartmentDefinitionDao;
@Autowired
@Qualifier("myConceptMapDaoDstu3")
protected IFhirResourceDao<ConceptMap> myConceptMapDao;
protected IFhirResourceDaoConceptMap<ConceptMap> myConceptMapDao;
@Autowired
@Qualifier("myConditionDaoDstu3")
protected IFhirResourceDao<Condition> myConditionDao;
@ -229,6 +232,10 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
protected IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> myValueSetDao;
@Autowired
private JpaValidationSupportChainDstu3 myJpaValidationSupportChainDstu3;
@Autowired
protected ITermConceptMapDao myTermConceptMapDao;
@Autowired
protected ITermConceptMapGroupElementTargetDao myTermConceptMapGroupElementTargetDao;
@After()
public void afterCleanupDao() {
@ -315,4 +322,34 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
return uuid;
}
/**
* Creates a single {@link org.hl7.fhir.dstu3.model.ConceptMap} entity that includes:
* <br>
* <ul>
* <li>
* One group with two elements, each identifying one target apiece.
* </li>
* <li>
* One group with one element, identifying two targets.
* </li>
* <li>
* One group with one element, identifying a target that also appears
* in the first element of the first group.
* </li>
* </ul>
* </br>
* The first two groups identify the same source code system and different target code systems.
* </br>
* The first two groups also include an element with the same source code.
* </br>
*
* @return A {@link org.hl7.fhir.dstu3.model.ConceptMap} entity for testing.
*/
public static ConceptMap createConceptMap() {
try {
return VersionConvertor_30_40.convertConceptMap(BaseJpaR4Test.createConceptMap());
} catch (FHIRException fe) {
throw new InternalErrorException(fe);
}
}
}

View File

@ -0,0 +1,84 @@
package ca.uhn.fhir.jpa.dao.dstu3;
import ca.uhn.fhir.jpa.term.TranslationMatch;
import ca.uhn.fhir.jpa.term.TranslationRequest;
import ca.uhn.fhir.jpa.term.TranslationResult;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.dstu3.model.ConceptMap;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.UriType;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import static org.junit.Assert.*;
public class FhirResourceDaoDstu3ConceptMapTest extends BaseJpaDstu3Test {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(FhirResourceDaoDstu3ConceptMapTest.class);
private IIdType myConceptMapId;
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Before
@Transactional
public void before02() {
myConceptMapId = myConceptMapDao.create(createConceptMap(), mySrd).getId().toUnqualifiedVersionless();
}
@Test
public void testTranslateByCodeSystemsAndSourceCodeOneToMany() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
// <editor-fold desc="Map one source code to multiple target codes">
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setSystem(CS_URL)
.setCode("12345");
translationRequest.setTargetSystem(new UriType(CS_URL_3));
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(2, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertEquals(Enumerations.ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode());
Coding concept = translationMatch.getConcept();
assertEquals("56789", concept.getCode());
assertEquals("Target Code 56789", concept.getDisplay());
assertEquals(CS_URL_3, concept.getSystem());
assertEquals("Version 4", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
translationMatch = translationResult.getMatches().get(1);
assertEquals(Enumerations.ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence().getCode());
concept = translationMatch.getConcept();
assertEquals("67890", concept.getCode());
assertEquals("Target Code 67890", concept.getDisplay());
assertEquals(CS_URL_3, concept.getSystem());
assertEquals("Version 4", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
// </editor-fold>
}
});
}
}

View File

@ -29,6 +29,10 @@ import org.hibernate.search.jpa.Search;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.r4.hapi.ctx.IValidationSupport;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.ConceptMap.ConceptMapGroupComponent;
import org.hl7.fhir.r4.model.ConceptMap.SourceElementComponent;
import org.hl7.fhir.r4.model.ConceptMap.TargetElementComponent;
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
@ -48,13 +52,12 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import static org.junit.Assert.*;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TestR4Config.class})
public abstract class BaseJpaR4Test extends BaseJpaTest {
private static JpaValidationSupportChainR4 ourJpaValidationSupportChainR4;
private static IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> ourValueSetDao;
@ -102,7 +105,7 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
protected IFhirResourceDao<CompartmentDefinition> myCompartmentDefinitionDao;
@Autowired
@Qualifier("myConceptMapDaoR4")
protected IFhirResourceDao<ConceptMap> myConceptMapDao;
protected IFhirResourceDaoConceptMap<ConceptMap> myConceptMapDao;
@Autowired
@Qualifier("myConditionDaoR4")
protected IFhirResourceDao<Condition> myConditionDao;
@ -240,6 +243,10 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
protected IFhirResourceDaoValueSet<ValueSet, Coding, CodeableConcept> myValueSetDao;
@Autowired
private JpaValidationSupportChainR4 myJpaValidationSupportChainR4;
@Autowired
protected ITermConceptMapDao myTermConceptMapDao;
@Autowired
protected ITermConceptMapGroupElementTargetDao myTermConceptMapGroupElementTargetDao;
@After()
public void afterCleanupDao() {
@ -327,4 +334,141 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
return uuid;
}
/**
* Creates a single {@link org.hl7.fhir.r4.model.ConceptMap} entity that includes:
* <br>
* <ul>
* <li>
* One group with two elements, each identifying one target apiece.
* </li>
* <li>
* One group with one element, identifying two targets.
* </li>
* <li>
* One group with one element, identifying a target that also appears
* in the first element of the first group.
* </li>
* </ul>
* </br>
* The first two groups identify the same source code system and different target code systems.
* </br>
* The first two groups also include an element with the same source code.
*
* @return A {@link org.hl7.fhir.r4.model.ConceptMap} entity for testing.
*/
public static ConceptMap createConceptMap() {
// <editor-fold desc="ConceptMap">
ConceptMap conceptMap = new ConceptMap();
conceptMap.setUrl(CM_URL);
conceptMap.setSource(new UriType(VS_URL));
conceptMap.setTarget(new UriType(VS_URL_2));
// <editor-fold desc="ConceptMap.group(0)">
ConceptMapGroupComponent group = conceptMap.addGroup();
group.setSource(CS_URL);
group.setSourceVersion("Version 1");
group.setTarget(CS_URL_2);
group.setTargetVersion("Version 2");
// <editor-fold desc="ConceptMap.group(0).element(0))">
SourceElementComponent element = group.addElement();
element.setCode("12345");
element.setDisplay("Source Code 12345");
// <editor-fold desc="ConceptMap.group(0).element(0).target(0)">
TargetElementComponent target = element.addTarget();
target.setCode("34567");
target.setDisplay("Target Code 34567");
target.setEquivalence(ConceptMapEquivalence.EQUAL);
// End ConceptMap.group(0).element(0).target(0)
// </editor-fold>
// End ConceptMap.group(0).element(0)
// </editor-fold>
// <editor-fold desc="ConceptMap.group(0).element(1))">
element = group.addElement();
element.setCode("23456");
element.setDisplay("Source Code 23456");
// <editor-fold desc="ConceptMap.group(0).element(1).target(0)">
target = element.addTarget();
target.setCode("45678");
target.setDisplay("Target Code 45678");
target.setEquivalence(ConceptMapEquivalence.WIDER);
// End ConceptMap.group(0).element(1).target(0)
// </editor-fold>
// End ConceptMap.group(0).element(1)
// </editor-fold>
// End ConceptMap.group(0)
// </editor-fold>
// <editor-fold desc="ConceptMap.group(1)">
group = conceptMap.addGroup();
group.setSource(CS_URL);
group.setSourceVersion("Version 3");
group.setTarget(CS_URL_3);
group.setTargetVersion("Version 4");
// <editor-fold desc="ConceptMap.group(1).element(0))">
element = group.addElement();
element.setCode("12345");
element.setDisplay("Source Code 12345");
// <editor-fold desc="ConceptMap.group(1).element(0).target(0)">
target = element.addTarget();
target.setCode("56789");
target.setDisplay("Target Code 56789");
target.setEquivalence(ConceptMapEquivalence.EQUAL);
// End ConceptMap.group(1).element(0).target(0)
// </editor-fold>
// <editor-fold desc="ConceptMap.group(1).element(0).target(1)">
target = element.addTarget();
target.setCode("67890");
target.setDisplay("Target Code 67890");
target.setEquivalence(ConceptMapEquivalence.WIDER);
// End ConceptMap.group(1).element(0).target(1)
// </editor-fold>
// End ConceptMap.group(1).element(0)
// </editor-fold>
// End ConceptMap.group(1)
// </editor-fold>
// <editor-fold desc="ConceptMap.group(2)">
group = conceptMap.addGroup();
group.setSource(CS_URL_4);
group.setSourceVersion("Version 5");
group.setTarget(CS_URL_2);
group.setTargetVersion("Version 2");
// <editor-fold desc="ConceptMap.group(2).element(0))">
element = group.addElement();
element.setCode("78901");
element.setDisplay("Source Code 78901");
// <editor-fold desc="ConceptMap.group(2).element(0).target(0)">
target = element.addTarget();
target.setCode("34567");
target.setDisplay("Target Code 34567");
target.setEquivalence(ConceptMapEquivalence.NARROWER);
// End ConceptMap.group(2).element(0).target(0)
// </editor-fold>
// End ConceptMap.group(2).element(0)
// </editor-fold>
// End ConceptMap.group(2)
// </editor-fold>
// End ConceptMap
// </editor-fold>
return conceptMap;
}
}

View File

@ -0,0 +1,980 @@
package ca.uhn.fhir.jpa.dao.r4;
import ca.uhn.fhir.jpa.term.TranslationMatch;
import ca.uhn.fhir.jpa.term.TranslationRequest;
import ca.uhn.fhir.jpa.term.TranslationResult;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.instance.model.api.IIdType;
import org.hl7.fhir.r4.model.Coding;
import org.hl7.fhir.r4.model.ConceptMap;
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
import org.hl7.fhir.r4.model.UriType;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import static org.junit.Assert.*;
public class FhirResourceDaoR4ConceptMapTest extends BaseJpaR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(FhirResourceDaoR4ConceptMapTest.class);
private IIdType myConceptMapId;
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Before
@Transactional
public void before02() {
myConceptMapId = myConceptMapDao.create(createConceptMap(), mySrd).getId().toUnqualifiedVersionless();
}
@Test
public void testTranslateByCodeSystemsAndSourceCodeOneToMany() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
// <editor-fold desc="Map one source code to multiple target codes">
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setSystem(CS_URL)
.setCode("12345");
translationRequest.setTargetSystem(new UriType(CS_URL_3));
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(2, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode());
Coding concept = translationMatch.getConcept();
assertEquals("56789", concept.getCode());
assertEquals("Target Code 56789", concept.getDisplay());
assertEquals(CS_URL_3, concept.getSystem());
assertEquals("Version 4", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
translationMatch = translationResult.getMatches().get(1);
assertEquals(ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence().getCode());
concept = translationMatch.getConcept();
assertEquals("67890", concept.getCode());
assertEquals("Target Code 67890", concept.getDisplay());
assertEquals(CS_URL_3, concept.getSystem());
assertEquals("Version 4", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
// </editor-fold>
}
});
}
@Test
public void testTranslateByCodeSystemsAndSourceCodeOneToOne() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
// <editor-fold desc="Map one source code to one target code">
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setSystem(CS_URL)
.setCode("12345");
translationRequest.setTargetSystem(new UriType(CS_URL_2));
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(1, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode());
Coding concept = translationMatch.getConcept();
assertEquals("34567", concept.getCode());
assertEquals("Target Code 34567", concept.getDisplay());
assertEquals(CS_URL_2, concept.getSystem());
assertEquals("Version 2", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
// </editor-fold>
}
});
}
@Test
public void testTranslateByCodeSystemsAndSourceCodeUnmapped() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
// <editor-fold desc="Attempt to map unknown source code">
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setSystem(CS_URL)
.setCode("BOGUS");
translationRequest.setTargetSystem(new UriType(CS_URL_3));
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertFalse(translationResult.getResult().booleanValue());
assertEquals("No matches found!", translationResult.getMessage().getValueAsString());
assertEquals(0, translationResult.getMatches().size());
// </editor-fold>
}
});
}
@Test
public void testTranslateUsingPredicatesWithCodeOnly() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
/*
* Provided:
* source code
*/
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setCode("12345");
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(3, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode());
Coding concept = translationMatch.getConcept();
assertEquals("34567", concept.getCode());
assertEquals("Target Code 34567", concept.getDisplay());
assertEquals(CS_URL_2, concept.getSystem());
assertEquals("Version 2", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
translationMatch = translationResult.getMatches().get(1);
assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode());
concept = translationMatch.getConcept();
assertEquals("56789", concept.getCode());
assertEquals("Target Code 56789", concept.getDisplay());
assertEquals(CS_URL_3, concept.getSystem());
assertEquals("Version 4", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
translationMatch = translationResult.getMatches().get(2);
assertEquals(ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence().getCode());
concept = translationMatch.getConcept();
assertEquals("67890", concept.getCode());
assertEquals("Target Code 67890", concept.getDisplay());
assertEquals(CS_URL_3, concept.getSystem());
assertEquals("Version 4", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
}
});
}
@Test
public void testTranslateUsingPredicatesWithSourceSystem() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
/*
* Provided:
* source code
* source code system
*/
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setSystem(CS_URL)
.setCode("12345");
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(3, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode());
Coding concept = translationMatch.getConcept();
assertEquals("34567", concept.getCode());
assertEquals("Target Code 34567", concept.getDisplay());
assertEquals(CS_URL_2, concept.getSystem());
assertEquals("Version 2", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
translationMatch = translationResult.getMatches().get(1);
assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode());
concept = translationMatch.getConcept();
assertEquals("56789", concept.getCode());
assertEquals("Target Code 56789", concept.getDisplay());
assertEquals(CS_URL_3, concept.getSystem());
assertEquals("Version 4", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
translationMatch = translationResult.getMatches().get(2);
assertEquals(ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence().getCode());
concept = translationMatch.getConcept();
assertEquals("67890", concept.getCode());
assertEquals("Target Code 67890", concept.getDisplay());
assertEquals(CS_URL_3, concept.getSystem());
assertEquals("Version 4", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
}
});
}
@Test
public void testTranslateUsingPredicatesWithSourceSystemAndVersion1() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
/*
* Provided:
* source code
* source code system
* source code system version #1
*/
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setSystem(CS_URL)
.setCode("12345")
.setVersion("Version 1");
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(1, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode());
Coding concept = translationMatch.getConcept();
assertEquals("34567", concept.getCode());
assertEquals("Target Code 34567", concept.getDisplay());
assertEquals(CS_URL_2, concept.getSystem());
assertEquals("Version 2", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
}
});
}
@Test
public void testTranslateUsingPredicatesWithSourceSystemAndVersion3() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
/*
* Provided:
* source code
* source code system
* source code system version #3
*/
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setSystem(CS_URL)
.setCode("12345")
.setVersion("Version 3");
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(2, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode());
Coding concept = translationMatch.getConcept();
assertEquals("56789", concept.getCode());
assertEquals("Target Code 56789", concept.getDisplay());
assertEquals(CS_URL_3, concept.getSystem());
assertEquals("Version 4", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
translationMatch = translationResult.getMatches().get(1);
assertEquals(ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence().getCode());
concept = translationMatch.getConcept();
assertEquals("67890", concept.getCode());
assertEquals("Target Code 67890", concept.getDisplay());
assertEquals(CS_URL_3, concept.getSystem());
assertEquals("Version 4", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
}
});
}
@Test
public void testTranslateUsingPredicatesWithSourceAndTargetSystem2() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
/*
* Provided:
* source code
* source code system
* target code system #2
*/
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setSystem(CS_URL)
.setCode("12345");
translationRequest.setTargetSystem(new UriType(CS_URL_2));
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(1, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode());
Coding concept = translationMatch.getConcept();
assertEquals("34567", concept.getCode());
assertEquals("Target Code 34567", concept.getDisplay());
assertEquals(CS_URL_2, concept.getSystem());
assertEquals("Version 2", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
}
});
}
@Test
public void testTranslateUsingPredicatesWithSourceAndTargetSystem3() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
/*
* Provided:
* source code
* source code system
* target code system #3
*/
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setSystem(CS_URL)
.setCode("12345");
translationRequest.setTargetSystem(new UriType(CS_URL_3));
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(2, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode());
Coding concept = translationMatch.getConcept();
assertEquals("56789", concept.getCode());
assertEquals("Target Code 56789", concept.getDisplay());
assertEquals(CS_URL_3, concept.getSystem());
assertEquals("Version 4", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
translationMatch = translationResult.getMatches().get(1);
assertEquals(ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence().getCode());
concept = translationMatch.getConcept();
assertEquals("67890", concept.getCode());
assertEquals("Target Code 67890", concept.getDisplay());
assertEquals(CS_URL_3, concept.getSystem());
assertEquals("Version 4", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
}
});
}
@Test
public void testTranslateUsingPredicatesWithSourceValueSet() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
/*
* Provided:
* source code
* source value set
*/
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setCode("12345");
translationRequest.setSource(new UriType(VS_URL));
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(3, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode());
Coding concept = translationMatch.getConcept();
assertEquals("34567", concept.getCode());
assertEquals("Target Code 34567", concept.getDisplay());
assertEquals(CS_URL_2, concept.getSystem());
assertEquals("Version 2", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
translationMatch = translationResult.getMatches().get(1);
assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode());
concept = translationMatch.getConcept();
assertEquals("56789", concept.getCode());
assertEquals("Target Code 56789", concept.getDisplay());
assertEquals(CS_URL_3, concept.getSystem());
assertEquals("Version 4", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
translationMatch = translationResult.getMatches().get(2);
assertEquals(ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence().getCode());
concept = translationMatch.getConcept();
assertEquals("67890", concept.getCode());
assertEquals("Target Code 67890", concept.getDisplay());
assertEquals(CS_URL_3, concept.getSystem());
assertEquals("Version 4", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
}
});
}
@Test
public void testTranslateUsingPredicatesWithTargetValueSet() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
/*
* Provided:
* source code
* target value set
*/
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setCode("12345");
translationRequest.setTarget(new UriType(VS_URL_2));
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(3, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode());
Coding concept = translationMatch.getConcept();
assertEquals("34567", concept.getCode());
assertEquals("Target Code 34567", concept.getDisplay());
assertEquals(CS_URL_2, concept.getSystem());
assertEquals("Version 2", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
translationMatch = translationResult.getMatches().get(1);
assertEquals(ConceptMapEquivalence.EQUAL.toCode(), translationMatch.getEquivalence().getCode());
concept = translationMatch.getConcept();
assertEquals("56789", concept.getCode());
assertEquals("Target Code 56789", concept.getDisplay());
assertEquals(CS_URL_3, concept.getSystem());
assertEquals("Version 4", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
translationMatch = translationResult.getMatches().get(2);
assertEquals(ConceptMapEquivalence.WIDER.toCode(), translationMatch.getEquivalence().getCode());
concept = translationMatch.getConcept();
assertEquals("67890", concept.getCode());
assertEquals("Target Code 67890", concept.getDisplay());
assertEquals(CS_URL_3, concept.getSystem());
assertEquals("Version 4", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
}
});
}
@Test
public void testTranslateWithReverse() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
/*
* Provided:
* source code
* source code system
* target code system
* reverse = true
*/
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setSystem(CS_URL_2)
.setCode("34567");
translationRequest.setTargetSystem(new UriType(CS_URL_4));
translationRequest.setReverse(true);
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(1, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertNull(translationMatch.getEquivalence());
Coding concept = translationMatch.getConcept();
assertEquals("78901", concept.getCode());
assertEquals("Source Code 78901", concept.getDisplay());
assertEquals(CS_URL_4, concept.getSystem());
assertEquals("Version 5", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
}
});
}
@Test
public void testTranslateWithReverseByCodeSystemsAndSourceCodeUnmapped() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
// <editor-fold desc="Attempt to map unknown source code">
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setSystem(CS_URL_3)
.setCode("BOGUS");
translationRequest.setTargetSystem(new UriType(CS_URL));
translationRequest.setReverse(true);
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertFalse(translationResult.getResult().booleanValue());
assertEquals("No matches found!", translationResult.getMessage().getValueAsString());
assertEquals(0, translationResult.getMatches().size());
// </editor-fold>
}
});
}
@Test
public void testTranslateWithReverseUsingPredicatesWithCodeOnly() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
/*
* Provided:
* source code
* reverse = true
*/
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setCode("34567");
translationRequest.setReverse(true);
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(2, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertNull(translationMatch.getEquivalence());
Coding concept = translationMatch.getConcept();
assertEquals("12345", concept.getCode());
assertEquals("Source Code 12345", concept.getDisplay());
assertEquals(CS_URL, concept.getSystem());
assertEquals("Version 1", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
translationMatch = translationResult.getMatches().get(1);
assertNull(translationMatch.getEquivalence());
concept = translationMatch.getConcept();
assertEquals("78901", concept.getCode());
assertEquals("Source Code 78901", concept.getDisplay());
assertEquals(CS_URL_4, concept.getSystem());
assertEquals("Version 5", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
}
});
}
@Test
public void testTranslateWithReverseUsingPredicatesWithSourceSystem() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
/*
* Provided:
* source code
* source code system
* reverse = true
*/
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setSystem(CS_URL_2)
.setCode("34567");
translationRequest.setReverse(true);
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(2, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertNull(translationMatch.getEquivalence());
Coding concept = translationMatch.getConcept();
assertEquals("12345", concept.getCode());
assertEquals("Source Code 12345", concept.getDisplay());
assertEquals(CS_URL, concept.getSystem());
assertEquals("Version 1", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
translationMatch = translationResult.getMatches().get(1);
assertNull(translationMatch.getEquivalence());
concept = translationMatch.getConcept();
assertEquals("78901", concept.getCode());
assertEquals("Source Code 78901", concept.getDisplay());
assertEquals(CS_URL_4, concept.getSystem());
assertEquals("Version 5", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
}
});
}
@Test
public void testTranslateWithReverseUsingPredicatesWithSourceSystemAndVersion() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
/*
* Provided:
* source code
* source code system
* source code system version
* reverse = true
*/
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setSystem(CS_URL_2)
.setCode("34567")
.setVersion("Version 2");
translationRequest.setReverse(true);
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(2, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertNull(translationMatch.getEquivalence());
Coding concept = translationMatch.getConcept();
assertEquals("12345", concept.getCode());
assertEquals("Source Code 12345", concept.getDisplay());
assertEquals(CS_URL, concept.getSystem());
assertEquals("Version 1", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
translationMatch = translationResult.getMatches().get(1);
assertNull(translationMatch.getEquivalence());
concept = translationMatch.getConcept();
assertEquals("78901", concept.getCode());
assertEquals("Source Code 78901", concept.getDisplay());
assertEquals(CS_URL_4, concept.getSystem());
assertEquals("Version 5", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
}
});
}
@Test
public void testTranslateWithReverseUsingPredicatesWithSourceAndTargetSystem1() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
/*
* Provided:
* source code
* source code system
* target code system #1
* reverse = true
*/
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setSystem(CS_URL_2)
.setCode("34567");
translationRequest.setTargetSystem(new UriType(CS_URL));
translationRequest.setReverse(true);
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(1, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertNull(translationMatch.getEquivalence());
Coding concept = translationMatch.getConcept();
assertEquals("12345", concept.getCode());
assertEquals("Source Code 12345", concept.getDisplay());
assertEquals(CS_URL, concept.getSystem());
assertEquals("Version 1", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
}
});
}
@Test
public void testTranslateWithReverseUsingPredicatesWithSourceAndTargetSystem4() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
/*
* Provided:
* source code
* source code system
* target code system #4
* reverse = true
*/
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setSystem(CS_URL_2)
.setCode("34567");
translationRequest.setTargetSystem(new UriType(CS_URL_4));
translationRequest.setReverse(true);
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(1, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertNull(translationMatch.getEquivalence());
Coding concept = translationMatch.getConcept();
assertEquals("78901", concept.getCode());
assertEquals("Source Code 78901", concept.getDisplay());
assertEquals(CS_URL_4, concept.getSystem());
assertEquals("Version 5", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
}
});
}
@Test
public void testTranslateWithReverseUsingPredicatesWithSourceValueSet() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
/*
* Provided:
* source code
* source value set
* reverse = true
*/
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setCode("34567");
translationRequest.setSource(new UriType(VS_URL_2));
translationRequest.setReverse(true);
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(2, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertNull(translationMatch.getEquivalence());
Coding concept = translationMatch.getConcept();
assertEquals("12345", concept.getCode());
assertEquals("Source Code 12345", concept.getDisplay());
assertEquals(CS_URL, concept.getSystem());
assertEquals("Version 1", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
translationMatch = translationResult.getMatches().get(1);
assertNull(translationMatch.getEquivalence());
concept = translationMatch.getConcept();
assertEquals("78901", concept.getCode());
assertEquals("Source Code 78901", concept.getDisplay());
assertEquals(CS_URL_4, concept.getSystem());
assertEquals("Version 5", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
}
});
}
@Test
public void testTranslateWithReverseUsingPredicatesWithTargetValueSet() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
/*
* Provided:
* source code
* target value set
* reverse = true
*/
TranslationRequest translationRequest = new TranslationRequest();
translationRequest.getCodeableConcept().addCoding()
.setCode("34567");
translationRequest.setTarget(new UriType(VS_URL));
translationRequest.setReverse(true);
TranslationResult translationResult = myConceptMapDao.translate(translationRequest, null);
assertTrue(translationResult.getResult().booleanValue());
assertEquals("Matches found!", translationResult.getMessage().getValueAsString());
assertEquals(2, translationResult.getMatches().size());
TranslationMatch translationMatch = translationResult.getMatches().get(0);
assertNull(translationMatch.getEquivalence());
Coding concept = translationMatch.getConcept();
assertEquals("12345", concept.getCode());
assertEquals("Source Code 12345", concept.getDisplay());
assertEquals(CS_URL, concept.getSystem());
assertEquals("Version 1", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
translationMatch = translationResult.getMatches().get(1);
assertNull(translationMatch.getEquivalence());
concept = translationMatch.getConcept();
assertEquals("78901", concept.getCode());
assertEquals("Source Code 78901", concept.getDisplay());
assertEquals(CS_URL_4, concept.getSystem());
assertEquals("Version 5", concept.getVersion());
assertFalse(concept.getUserSelected());
assertEquals(CM_URL, translationMatch.getSource().getValueAsString());
}
});
}
}

View File

@ -26,6 +26,8 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.dstu3.model.Bundle;
import org.hl7.fhir.dstu3.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.dstu3.model.Parameters;
import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.dstu3.model.Patient;
import org.junit.After;
import org.junit.AfterClass;
@ -201,4 +203,56 @@ public abstract class BaseResourceProviderDstu3Test extends BaseJpaDstu3Test {
TestUtil.clearAllStaticFieldsForUnitTest();
}
public static int getNumberOfParametersByName(Parameters theParameters, String theName) {
int retVal = 0;
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
retVal++;
}
}
return retVal;
}
public static ParametersParameterComponent getParameterByName(Parameters theParameters, String theName) {
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
return param;
}
}
return new ParametersParameterComponent();
}
public static List<ParametersParameterComponent> getParametersByName(Parameters theParameters, String theName) {
List<ParametersParameterComponent> params = new ArrayList<>();
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
params.add(param);
}
}
return params;
}
public static ParametersParameterComponent getPartByName(ParametersParameterComponent theParameter, String theName) {
for (ParametersParameterComponent part : theParameter.getPart()) {
if (part.getName().equals(theName)) {
return part;
}
}
return new ParametersParameterComponent();
}
public static boolean hasParameterByName(Parameters theParameters, String theName) {
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,89 @@
package ca.uhn.fhir.jpa.provider.dstu3;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.dstu3.model.*;
import org.hl7.fhir.dstu3.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.instance.model.api.IIdType;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.annotation.Transactional;
import static org.junit.Assert.*;
public class ResourceProviderDstu3ConceptMapTest extends BaseResourceProviderDstu3Test {
private static final Logger ourLog = LoggerFactory.getLogger(ResourceProviderDstu3ConceptMapTest.class);
private IIdType myConceptMapId;
@AfterClass
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
@Before
@Transactional
public void before02() {
myConceptMapId = myConceptMapDao.create(createConceptMap(), mySrd).getId().toUnqualifiedVersionless();
}
@Test
public void testTranslateByCodeSystemsAndSourceCodeOneToMany() {
ConceptMap conceptMap = myConceptMapDao.read(myConceptMapId);
ourLog.info("ConceptMap:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(conceptMap));
Parameters inParams = new Parameters();
inParams.addParameter().setName("system").setValue(new UriType(CS_URL));
inParams.addParameter().setName("targetsystem").setValue(new UriType(CS_URL_3));
inParams.addParameter().setName("code").setValue(new CodeType("12345"));
ourLog.info("Request Parameters:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(inParams));
Parameters respParams = ourClient
.operation()
.onType(ConceptMap.class)
.named("translate")
.withParameters(inParams)
.execute();
ourLog.info("Response Parameters\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(respParams));
ParametersParameterComponent param = getParameterByName(respParams, "result");
assertTrue(((BooleanType) param.getValue()).booleanValue());
param = getParameterByName(respParams, "message");
assertEquals("Matches found!", ((StringType) param.getValue()).getValueAsString());
assertEquals(2, getNumberOfParametersByName(respParams, "match"));
param = getParametersByName(respParams, "match").get(0);
assertEquals(3, param.getPart().size());
ParametersParameterComponent part = getPartByName(param, "equivalence");
assertEquals("equal", ((CodeType) part.getValue()).getValueAsString());
part = getPartByName(param, "concept");
Coding coding = (Coding) part.getValue();
assertEquals("56789", coding.getCode());
assertEquals("Target Code 56789", coding.getDisplay());
assertFalse(coding.getUserSelected());
assertEquals(CS_URL_3, coding.getSystem());
assertEquals("Version 4", coding.getVersion());
part = getPartByName(param, "source");
assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString());
param = getParametersByName(respParams, "match").get(1);
assertEquals(3, param.getPart().size());
part = getPartByName(param, "equivalence");
assertEquals("wider", ((CodeType) part.getValue()).getValueAsString());
part = getPartByName(param, "concept");
coding = (Coding) part.getValue();
assertEquals("67890", coding.getCode());
assertEquals("Target Code 67890", coding.getDisplay());
assertFalse(coding.getUserSelected());
assertEquals(CS_URL_3, coding.getSystem());
assertEquals("Version 4", coding.getVersion());
part = getPartByName(param, "source");
assertEquals(CM_URL, ((UriType) part.getValue()).getValueAsString());
}
}

View File

@ -27,6 +27,8 @@ import org.eclipse.jetty.servlet.ServletContextHandler;
import org.eclipse.jetty.servlet.ServletHolder;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent;
import org.hl7.fhir.r4.model.Parameters;
import org.hl7.fhir.r4.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r4.model.Patient;
import org.junit.After;
import org.junit.AfterClass;
@ -215,4 +217,56 @@ public abstract class BaseResourceProviderR4Test extends BaseJpaR4Test {
TestUtil.clearAllStaticFieldsForUnitTest();
}
public static int getNumberOfParametersByName(Parameters theParameters, String theName) {
int retVal = 0;
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
retVal++;
}
}
return retVal;
}
public static ParametersParameterComponent getParameterByName(Parameters theParameters, String theName) {
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
return param;
}
}
return new ParametersParameterComponent();
}
public static List<ParametersParameterComponent> getParametersByName(Parameters theParameters, String theName) {
List<ParametersParameterComponent> params = new ArrayList<>();
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
params.add(param);
}
}
return params;
}
public static ParametersParameterComponent getPartByName(ParametersParameterComponent theParameter, String theName) {
for (ParametersParameterComponent part : theParameter.getPart()) {
if (part.getName().equals(theName)) {
return part;
}
}
return new ParametersParameterComponent();
}
public static boolean hasParameterByName(Parameters theParameters, String theName) {
for (ParametersParameterComponent param : theParameters.getParameter()) {
if (param.getName().equals(theName)) {
return true;
}
}
return false;
}
}

View File

@ -430,5 +430,4 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
public static void afterClassClearContext() {
TestUtil.clearAllStaticFieldsForUnitTest();
}
}

View File

@ -18,7 +18,7 @@ public class PublicSecurityInterceptor extends AuthorizationInterceptor {
private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(PublicSecurityInterceptor.class);
private HashSet<String> myTokens;
public PublicSecurityInterceptor() {
String passwordsString = System.getProperty("fhir.tdlpass");
String[] passwords = passwordsString.split(",");

View File

@ -5,11 +5,7 @@ import java.util.*;
import org.apache.commons.lang3.StringUtils;
#if ( $version != 'dstu' && (${className} == 'Encounter' || ${className} == 'Patient' || ${className} == 'ValueSet' || ${className} == 'QuestionnaireAnswers' || ${className} == 'CodeSystem'))
import ca.uhn.fhir.jpa.provider${package_suffix}.BaseJpaResourceProvider${className}${versionCapitalized};
#else
import ca.uhn.fhir.jpa.provider${package_suffix}.JpaResourceProvider${versionCapitalized};
#end
import ca.uhn.fhir.jpa.provider${package_suffix}.*;
import ca.uhn.fhir.jpa.dao.SearchParameterMap;
import ca.uhn.fhir.model.api.Include;
import ca.uhn.fhir.model.api.annotation.*;
@ -26,7 +22,7 @@ import ca.uhn.fhir.rest.api.SortSpec;
public class ${className}ResourceProvider extends
## We have specialized base classes for RPs that handle certain resource types. These
## RPs implement type specific operations
#if ( $version != 'dstu' && (${className} == 'Encounter' || ${className} == 'Patient' || ${className} == 'ValueSet' || ${className} == 'QuestionnaireAnswers' || ${className} == 'CodeSystem'))
#if ( $version != 'dstu' && (${className} == 'Encounter' || ${className} == 'Patient' || ${className} == 'ValueSet' || ${className} == 'QuestionnaireAnswers' || ${className} == 'CodeSystem' || ($version != 'dstu2' && ${className} == 'ConceptMap')))
BaseJpaResourceProvider${className}${versionCapitalized}
#else
JpaResourceProvider${versionCapitalized}<${className}>

View File

@ -44,7 +44,11 @@
#foreach ( $res in $resources )
<bean id="my${res.name}Dao${versionCapitalized}"
## Some resource types have customized DAOs for resource specific functionality
#if ( ${versionCapitalized} != 'Dstu1' && ( ${res.name} == 'Bundle' || ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'ValueSet' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'SearchParameter'))
#if ( ${versionCapitalized} == 'Dstu3' && ${res.name} == 'ConceptMap')
class="ca.uhn.fhir.jpa.dao.FhirResourceDao${res.name}${versionCapitalized}">
#elseif ( ${versionCapitalized} == 'R4' && ${res.name} == 'ConceptMap')
class="ca.uhn.fhir.jpa.dao.FhirResourceDao${res.name}${versionCapitalized}">
#elseif ( ${versionCapitalized} != 'Dstu1' && ( ${res.name} == 'Bundle' || ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'ValueSet' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'SearchParameter'))
class="ca.uhn.fhir.jpa.dao.FhirResourceDao${res.name}${versionCapitalized}">
#else
class="ca.uhn.fhir.jpa.dao.FhirResourceDao${versionCapitalized}">
@ -59,4 +63,4 @@
#end
</beans>
</beans>

View File

@ -58,13 +58,21 @@ public abstract class BaseJavaConfig${versionCapitalized} extends ca.uhn.fhir.jp
IFhirResourceDaoCodeSystem<org.hl7.fhir.dstu3.model.CodeSystem, org.hl7.fhir.dstu3.model.Coding, org.hl7.fhir.dstu3.model.CodeableConcept>
#elseif ( ${versionCapitalized} == 'R4' && ${res.name} == 'CodeSystem' )
IFhirResourceDaoCodeSystem<org.hl7.fhir.r4.model.CodeSystem, org.hl7.fhir.r4.model.Coding, org.hl7.fhir.r4.model.CodeableConcept>
#elseif ( ${versionCapitalized} == 'Dstu3' && ${res.name} == 'ConceptMap' )
IFhirResourceDaoConceptMap<org.hl7.fhir.dstu3.model.ConceptMap>
#elseif ( ${versionCapitalized} == 'R4' && ${res.name} == 'ConceptMap' )
IFhirResourceDaoConceptMap<org.hl7.fhir.r4.model.ConceptMap>
#elseif ( ${versionCapitalized} != 'Dstu1' && ( ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'SearchParameter'))
IFhirResourceDao${res.name}<${resourcePackage}.${res.declaringClassNameComplete}>
#else
IFhirResourceDao<${resourcePackage}.${res.declaringClassNameComplete}>
#end
dao${res.declaringClassNameComplete}${versionCapitalized}() {
#if ( ${versionCapitalized} != 'Dstu1' && ( ${res.name} == 'Bundle' || ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'ValueSet' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'SearchParameter' || ${res.name} == 'CodeSystem'))
#if ( ${versionCapitalized} == 'Dstu3' && ${res.name} == 'ConceptMap' )
ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized} retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized}();
#elseif ( ${versionCapitalized} == 'R4' && ${res.name} == 'ConceptMap' )
ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized} retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized}();
#elseif ( ${versionCapitalized} != 'Dstu1' && ( ${res.name} == 'Bundle' || ${res.name} == 'Encounter' || ${res.name} == 'Everything' || ${res.name} == 'Patient' || ${res.name} == 'Subscription' || ${res.name} == 'ValueSet' || ${res.name} == 'QuestionnaireResponse' || ${res.name} == 'SearchParameter' || ${res.name} == 'CodeSystem'))
ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized} retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${res.name}${versionCapitalized}();
#else
ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${versionCapitalized}<${resourcePackage}.${res.declaringClassNameComplete}> retVal = new ca.uhn.fhir.jpa.dao${package_suffix}.FhirResourceDao${versionCapitalized}<${resourcePackage}.${res.declaringClassNameComplete}>();

View File

@ -120,6 +120,10 @@
Fix a bug in the DSTU2 QuestionnaireResponseValidator which prevented validation
on groups with only one question. Thanks David Gileadi for the pull request!
</action>
<action type="add" issue="709">
The <![CDATA[<code>ConceptMap</code>]] operation <![CDATA[<code>$translate</code>]]> has been
implemented.
</action>
</release>
<release version="3.3.0" date="2018-03-29">
<action type="add">
@ -694,7 +698,7 @@ ALTER TABLE hfj_res_ver ALTER COLUMN res_text DROP NOT NULL;</pre>
in FHIR resource (XML/JSON) format.
</action>
<action type="add">
The Binary resource endpoint now supports the `X-Security-Context` header when
The Binary resource endpoint now supports the <![CDATA[<code>X-Security-Context</code>]]> header when
reading or writing Binary contents using their native Content-Type (i.e exchanging
the raw binary with the server, as opposed to exchanging a FHIR resource).
</action>