$translate operation implementation for remote terminology (#3552)
This commit is contained in:
parent
f7a31287b6
commit
68c8523046
|
@ -29,6 +29,7 @@ import org.apache.commons.lang3.Validate;
|
||||||
import org.apache.commons.lang3.builder.EqualsBuilder;
|
import org.apache.commons.lang3.builder.EqualsBuilder;
|
||||||
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
import org.apache.commons.lang3.builder.HashCodeBuilder;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
@ -806,21 +807,43 @@ public interface IValidationSupport {
|
||||||
|
|
||||||
|
|
||||||
class TranslateCodeRequest {
|
class TranslateCodeRequest {
|
||||||
private final String mySourceSystemUrl;
|
private List<IBaseCoding> myCodings;
|
||||||
private final String mySourceCode;
|
|
||||||
private final String myTargetSystemUrl;
|
private final String myTargetSystemUrl;
|
||||||
private final int myHashCode;
|
private final String myConceptMapUrl;
|
||||||
|
private final String myConceptMapVersion;
|
||||||
|
private final String mySourceValueSetUrl;
|
||||||
|
private final String myTargetValueSetUrl;
|
||||||
|
private final Long myResourcePid;
|
||||||
|
private final boolean myReverse;
|
||||||
|
|
||||||
public TranslateCodeRequest(String theSourceSystemUrl, String theSourceCode, String theTargetSystemUrl) {
|
public TranslateCodeRequest(List<IBaseCoding> theCodings, String theTargetSystemUrl) {
|
||||||
mySourceSystemUrl = theSourceSystemUrl;
|
myCodings = theCodings;
|
||||||
mySourceCode = theSourceCode;
|
|
||||||
myTargetSystemUrl = theTargetSystemUrl;
|
myTargetSystemUrl = theTargetSystemUrl;
|
||||||
|
myConceptMapUrl = null;
|
||||||
|
myConceptMapVersion = null;
|
||||||
|
mySourceValueSetUrl = null;
|
||||||
|
myTargetValueSetUrl = null;
|
||||||
|
myResourcePid = null;
|
||||||
|
myReverse = false;
|
||||||
|
}
|
||||||
|
|
||||||
myHashCode = new HashCodeBuilder(17, 37)
|
public TranslateCodeRequest(
|
||||||
.append(mySourceSystemUrl)
|
List<IBaseCoding> theCodings,
|
||||||
.append(mySourceCode)
|
String theTargetSystemUrl,
|
||||||
.append(myTargetSystemUrl)
|
String theConceptMapUrl,
|
||||||
.toHashCode();
|
String theConceptMapVersion,
|
||||||
|
String theSourceValueSetUrl,
|
||||||
|
String theTargetValueSetUrl,
|
||||||
|
Long theResourcePid,
|
||||||
|
boolean theReverse) {
|
||||||
|
myCodings = theCodings;
|
||||||
|
myTargetSystemUrl = theTargetSystemUrl;
|
||||||
|
myConceptMapUrl = theConceptMapUrl;
|
||||||
|
myConceptMapVersion = theConceptMapVersion;
|
||||||
|
mySourceValueSetUrl = theSourceValueSetUrl;
|
||||||
|
myTargetValueSetUrl = theTargetValueSetUrl;
|
||||||
|
myResourcePid = theResourcePid;
|
||||||
|
myReverse = theReverse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -836,28 +859,62 @@ public interface IValidationSupport {
|
||||||
TranslateCodeRequest that = (TranslateCodeRequest) theO;
|
TranslateCodeRequest that = (TranslateCodeRequest) theO;
|
||||||
|
|
||||||
return new EqualsBuilder()
|
return new EqualsBuilder()
|
||||||
.append(mySourceSystemUrl, that.mySourceSystemUrl)
|
.append(myCodings, that.myCodings)
|
||||||
.append(mySourceCode, that.mySourceCode)
|
|
||||||
.append(myTargetSystemUrl, that.myTargetSystemUrl)
|
.append(myTargetSystemUrl, that.myTargetSystemUrl)
|
||||||
|
.append(myConceptMapUrl, that.myConceptMapUrl)
|
||||||
|
.append(myConceptMapVersion, that.myConceptMapVersion)
|
||||||
|
.append(mySourceValueSetUrl, that.mySourceValueSetUrl)
|
||||||
|
.append(myTargetValueSetUrl, that.myTargetValueSetUrl)
|
||||||
|
.append(myResourcePid, that.myResourcePid)
|
||||||
|
.append(myReverse, that.myReverse)
|
||||||
.isEquals();
|
.isEquals();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
return myHashCode;
|
return new HashCodeBuilder(17, 37)
|
||||||
|
.append(myCodings)
|
||||||
|
.append(myTargetSystemUrl)
|
||||||
|
.append(myConceptMapUrl)
|
||||||
|
.append(myConceptMapVersion)
|
||||||
|
.append(mySourceValueSetUrl)
|
||||||
|
.append(myTargetValueSetUrl)
|
||||||
|
.append(myResourcePid)
|
||||||
|
.append(myReverse)
|
||||||
|
.toHashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getSourceSystemUrl() {
|
public List<IBaseCoding> getCodings() {
|
||||||
return mySourceSystemUrl;
|
return myCodings;
|
||||||
}
|
|
||||||
|
|
||||||
public String getSourceCode() {
|
|
||||||
return mySourceCode;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTargetSystemUrl() {
|
public String getTargetSystemUrl() {
|
||||||
return myTargetSystemUrl;
|
return myTargetSystemUrl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getConceptMapUrl() {
|
||||||
|
return myConceptMapUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getConceptMapVersion() {
|
||||||
|
return myConceptMapVersion;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSourceValueSetUrl() {
|
||||||
|
return mySourceValueSetUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTargetValueSetUrl() {
|
||||||
|
return myTargetValueSetUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Long getResourcePid() {
|
||||||
|
return myResourcePid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isReverse() {
|
||||||
|
return myReverse;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
type: add
|
||||||
|
issue: 3442
|
||||||
|
title: "Provided a Remote Terminology Service implementation for the $translate operation."
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao.dstu3;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap;
|
||||||
|
@ -38,19 +39,19 @@ import org.hl7.fhir.exceptions.FHIRException;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
public class FhirResourceDaoConceptMapDstu3 extends BaseHapiFhirResourceDao<ConceptMap> implements IFhirResourceDaoConceptMap<ConceptMap> {
|
public class FhirResourceDaoConceptMapDstu3 extends BaseHapiFhirResourceDao<ConceptMap> implements IFhirResourceDaoConceptMap<ConceptMap> {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ITermConceptMappingSvc myTermConceptMappingSvc;
|
private ITermConceptMappingSvc myTermConceptMappingSvc;
|
||||||
|
@Autowired
|
||||||
|
private IValidationSupport myValidationSupport;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TranslateConceptResults translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) {
|
public TranslateConceptResults translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) {
|
||||||
if (theTranslationRequest.hasReverse() && theTranslationRequest.getReverseAsBoolean()) {
|
IValidationSupport.TranslateCodeRequest translateCodeRequest = theTranslationRequest.asTranslateCodeRequest();
|
||||||
return myTermConceptMappingSvc.translateWithReverse(theTranslationRequest);
|
return myValidationSupport.translateConcept(translateCodeRequest);
|
||||||
}
|
|
||||||
|
|
||||||
return myTermConceptMappingSvc.translate(theTranslationRequest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao.r4;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap;
|
||||||
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
|
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
|
||||||
|
@ -29,23 +30,28 @@ import ca.uhn.fhir.jpa.model.entity.ResourceTable;
|
||||||
import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc;
|
import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc;
|
||||||
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
import ca.uhn.fhir.rest.api.server.RequestDetails;
|
||||||
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
import ca.uhn.fhir.rest.api.server.storage.TransactionDetails;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
import org.hl7.fhir.r4.model.ConceptMap;
|
import org.hl7.fhir.r4.model.ConceptMap;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
public class FhirResourceDaoConceptMapR4 extends BaseHapiFhirResourceDao<ConceptMap> implements IFhirResourceDaoConceptMap<ConceptMap> {
|
public class FhirResourceDaoConceptMapR4 extends BaseHapiFhirResourceDao<ConceptMap> implements IFhirResourceDaoConceptMap<ConceptMap> {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ITermConceptMappingSvc myTermConceptMappingSvc;
|
private ITermConceptMappingSvc myTermConceptMappingSvc;
|
||||||
|
@Autowired
|
||||||
|
private IValidationSupport myValidationSupport;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TranslateConceptResults translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) {
|
public TranslateConceptResults translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) {
|
||||||
if (theTranslationRequest.hasReverse() && theTranslationRequest.getReverseAsBoolean()) {
|
IValidationSupport.TranslateCodeRequest translateCodeRequest = theTranslationRequest.asTranslateCodeRequest();
|
||||||
return myTermConceptMappingSvc.translateWithReverse(theTranslationRequest);
|
return myValidationSupport.translateConcept(translateCodeRequest);
|
||||||
}
|
|
||||||
|
|
||||||
return myTermConceptMappingSvc.translate(theTranslationRequest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -20,6 +20,7 @@ package ca.uhn.fhir.jpa.dao.r5;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||||
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap;
|
import ca.uhn.fhir.jpa.api.dao.IFhirResourceDaoConceptMap;
|
||||||
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
|
import ca.uhn.fhir.jpa.api.model.TranslationRequest;
|
||||||
|
@ -35,19 +36,19 @@ import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.r5.model.ConceptMap;
|
import org.hl7.fhir.r5.model.ConceptMap;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
public class FhirResourceDaoConceptMapR5 extends BaseHapiFhirResourceDao<ConceptMap> implements IFhirResourceDaoConceptMap<ConceptMap> {
|
public class FhirResourceDaoConceptMapR5 extends BaseHapiFhirResourceDao<ConceptMap> implements IFhirResourceDaoConceptMap<ConceptMap> {
|
||||||
@Autowired
|
@Autowired
|
||||||
private ITermConceptMappingSvc myTermConceptMappingSvc;
|
private ITermConceptMappingSvc myTermConceptMappingSvc;
|
||||||
|
@Autowired
|
||||||
|
private IValidationSupport myValidationSupport;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public TranslateConceptResults translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) {
|
public TranslateConceptResults translate(TranslationRequest theTranslationRequest, RequestDetails theRequestDetails) {
|
||||||
if (theTranslationRequest.hasReverse() && theTranslationRequest.getReverseAsBoolean()) {
|
IValidationSupport.TranslateCodeRequest translateCodeRequest = theTranslationRequest.asTranslateCodeRequest();
|
||||||
return myTermConceptMappingSvc.translateWithReverse(theTranslationRequest);
|
return myValidationSupport.translateConcept(translateCodeRequest);
|
||||||
}
|
|
||||||
|
|
||||||
return myTermConceptMappingSvc.translate(theTranslationRequest);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -47,6 +47,8 @@ import org.apache.commons.lang3.StringUtils;
|
||||||
import org.hibernate.ScrollMode;
|
import org.hibernate.ScrollMode;
|
||||||
import org.hibernate.ScrollableResults;
|
import org.hibernate.ScrollableResults;
|
||||||
import org.hl7.fhir.exceptions.FHIRException;
|
import org.hl7.fhir.exceptions.FHIRException;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||||
import org.hl7.fhir.r4.model.BooleanType;
|
import org.hl7.fhir.r4.model.BooleanType;
|
||||||
import org.hl7.fhir.r4.model.CodeType;
|
import org.hl7.fhir.r4.model.CodeType;
|
||||||
import org.hl7.fhir.r4.model.CodeableConcept;
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
@ -118,16 +120,10 @@ public class TermConceptMappingSvcImpl implements ITermConceptMappingSvc {
|
||||||
@Override
|
@Override
|
||||||
@Transactional
|
@Transactional
|
||||||
public TranslateConceptResults translateConcept(TranslateCodeRequest theRequest) {
|
public TranslateConceptResults translateConcept(TranslateCodeRequest theRequest) {
|
||||||
|
TranslationRequest request = TranslationRequest.fromTranslateCodeRequest(theRequest);
|
||||||
CodeableConcept sourceCodeableConcept = new CodeableConcept();
|
if (request.hasReverse() && request.getReverseAsBoolean()) {
|
||||||
sourceCodeableConcept
|
return translateWithReverse(request);
|
||||||
.addCoding()
|
}
|
||||||
.setSystem(theRequest.getSourceSystemUrl())
|
|
||||||
.setCode(theRequest.getSourceCode());
|
|
||||||
|
|
||||||
TranslationRequest request = new TranslationRequest();
|
|
||||||
request.setCodeableConcept(sourceCodeableConcept);
|
|
||||||
request.setTargetSystem(new UriType(theRequest.getTargetSystemUrl()));
|
|
||||||
|
|
||||||
return translate(request);
|
return translate(request);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ import ca.uhn.fhir.jpa.test.BaseJpaR4Test;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.r4.model.CanonicalType;
|
import org.hl7.fhir.r4.model.CanonicalType;
|
||||||
import org.hl7.fhir.r4.model.CodeableConcept;
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
import org.hl7.fhir.r4.model.ConceptMap;
|
import org.hl7.fhir.r4.model.ConceptMap;
|
||||||
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
|
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
|
||||||
import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
|
import org.hl7.fhir.r4.model.Enumerations.PublicationStatus;
|
||||||
|
@ -30,6 +31,7 @@ import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -1139,7 +1141,9 @@ public class FhirResourceDaoR4ConceptMapTest extends BaseJpaR4Test {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
List<TranslateConceptResult> translationResults = myValidationSupport.translateConcept(new IValidationSupport.TranslateCodeRequest("http://source", "source1", "http://target")).getResults();
|
CodeableConcept sourceCodeableConcept = new CodeableConcept();
|
||||||
|
sourceCodeableConcept.addCoding(new Coding("http://source", "source1", null));
|
||||||
|
List<TranslateConceptResult> translationResults = myValidationSupport.translateConcept(new IValidationSupport.TranslateCodeRequest(Collections.unmodifiableList(sourceCodeableConcept.getCoding()), "http://target")).getResults();
|
||||||
assertThat(translationResults.toString(), translationResults, hasItem(
|
assertThat(translationResults.toString(), translationResults, hasItem(
|
||||||
new TranslateConceptResult()
|
new TranslateConceptResult()
|
||||||
.setSystem("http://target")
|
.setSystem("http://target")
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package ca.uhn.fhir.jpa.term;
|
package ca.uhn.fhir.jpa.term;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
import ca.uhn.fhir.context.support.TranslateConceptResult;
|
import ca.uhn.fhir.context.support.TranslateConceptResult;
|
||||||
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
|
@ -8,14 +9,19 @@ import ca.uhn.fhir.jpa.entity.TermConceptMap;
|
||||||
import ca.uhn.fhir.jpa.entity.TermConceptMapGroup;
|
import ca.uhn.fhir.jpa.entity.TermConceptMapGroup;
|
||||||
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
|
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElement;
|
||||||
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
|
import ca.uhn.fhir.jpa.entity.TermConceptMapGroupElementTarget;
|
||||||
|
import ca.uhn.fhir.jpa.term.api.ITermConceptMappingSvc;
|
||||||
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
|
||||||
import org.hl7.fhir.instance.model.api.IIdType;
|
import org.hl7.fhir.instance.model.api.IIdType;
|
||||||
import org.hl7.fhir.r4.model.CanonicalType;
|
import org.hl7.fhir.r4.model.CanonicalType;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
import org.hl7.fhir.r4.model.ConceptMap;
|
import org.hl7.fhir.r4.model.ConceptMap;
|
||||||
import org.hl7.fhir.r4.model.Enumerations;
|
import org.hl7.fhir.r4.model.Enumerations;
|
||||||
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
import org.hl7.fhir.r4.model.UriType;
|
import org.hl7.fhir.r4.model.UriType;
|
||||||
import org.hl7.fhir.r4.model.codesystems.HttpVerb;
|
import org.hl7.fhir.r4.model.codesystems.HttpVerb;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.data.domain.PageRequest;
|
import org.springframework.data.domain.PageRequest;
|
||||||
|
@ -25,6 +31,7 @@ import org.springframework.transaction.support.TransactionCallbackWithoutResult;
|
||||||
import org.springframework.transaction.support.TransactionTemplate;
|
import org.springframework.transaction.support.TransactionTemplate;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
@ -32,6 +39,11 @@ import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
import static org.junit.jupiter.api.Assertions.fail;
|
||||||
|
import static org.mockito.Mockito.doReturn;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.spy;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class TermConceptMappingSvcImplTest extends BaseTermR4Test {
|
public class TermConceptMappingSvcImplTest extends BaseTermR4Test {
|
||||||
private static final Logger ourLog = LoggerFactory.getLogger(TermConceptMappingSvcImplTest.class);
|
private static final Logger ourLog = LoggerFactory.getLogger(TermConceptMappingSvcImplTest.class);
|
||||||
|
@ -1547,6 +1559,100 @@ public class TermConceptMappingSvcImplTest extends BaseTermR4Test {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTranslateCodeRequestToTranslationRequestMapping() {
|
||||||
|
CodeableConcept codeableConcept = new CodeableConcept();
|
||||||
|
Coding coding = new Coding("theSourceSystemUrl", "theSourceCode", null);
|
||||||
|
codeableConcept.addCoding(coding);
|
||||||
|
|
||||||
|
IValidationSupport.TranslateCodeRequest theRequest = new IValidationSupport.TranslateCodeRequest(
|
||||||
|
Collections.unmodifiableList(codeableConcept.getCoding()),
|
||||||
|
"theTargetSystemUrl",
|
||||||
|
"theConceptMapUrl",
|
||||||
|
"theConceptMapVersion",
|
||||||
|
"theSourceValueSetUrl",
|
||||||
|
"theTargetValueSetUrl",
|
||||||
|
0L,
|
||||||
|
false
|
||||||
|
);
|
||||||
|
|
||||||
|
CodeableConcept sourceCodeableConcept = new CodeableConcept();
|
||||||
|
sourceCodeableConcept
|
||||||
|
.addCoding()
|
||||||
|
.setSystem(coding.getSystem())
|
||||||
|
.setCode(coding.getCode());
|
||||||
|
|
||||||
|
TranslationRequest expected = new TranslationRequest();
|
||||||
|
expected.setCodeableConcept(sourceCodeableConcept);
|
||||||
|
expected.setConceptMapVersion(new StringType(theRequest.getConceptMapVersion()));
|
||||||
|
expected.setUrl(new UriType(theRequest.getConceptMapUrl()));
|
||||||
|
expected.setSource(new UriType(theRequest.getSourceValueSetUrl()));
|
||||||
|
expected.setTarget(new UriType(theRequest.getTargetValueSetUrl()));
|
||||||
|
expected.setTargetSystem(new UriType(theRequest.getTargetSystemUrl()));
|
||||||
|
expected.setResourceId(theRequest.getResourcePid());
|
||||||
|
expected.setReverse(theRequest.isReverse());
|
||||||
|
|
||||||
|
ITermConceptMappingSvc mock = mock(TermConceptMappingSvcImpl.class);
|
||||||
|
ArgumentCaptor<TranslationRequest> argument = ArgumentCaptor.forClass(TranslationRequest.class);
|
||||||
|
when(mock.translate(expected)).thenReturn(new TranslateConceptResults());
|
||||||
|
when(mock.translateConcept(theRequest)).thenCallRealMethod();
|
||||||
|
mock.translateConcept(theRequest);
|
||||||
|
verify(mock).translate(argument.capture());
|
||||||
|
assertSameTranslationRequest(expected, argument.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTranslateCodeRequestWithReverseToTranslationRequestMapping() {
|
||||||
|
CodeableConcept codeableConcept = new CodeableConcept();
|
||||||
|
Coding coding = new Coding("theSourceSystemUrl", "theSourceCode", null);
|
||||||
|
codeableConcept.addCoding(coding);
|
||||||
|
|
||||||
|
IValidationSupport.TranslateCodeRequest theRequest = new IValidationSupport.TranslateCodeRequest(
|
||||||
|
Collections.unmodifiableList(codeableConcept.getCoding()),
|
||||||
|
"theTargetSystemUrl",
|
||||||
|
"theConceptMapUrl",
|
||||||
|
"theConceptMapVersion",
|
||||||
|
"theSourceValueSetUrl",
|
||||||
|
"theTargetValueSetUrl",
|
||||||
|
0L,
|
||||||
|
true
|
||||||
|
);
|
||||||
|
|
||||||
|
CodeableConcept sourceCodeableConcept = new CodeableConcept();
|
||||||
|
sourceCodeableConcept
|
||||||
|
.addCoding()
|
||||||
|
.setSystem(coding.getSystem())
|
||||||
|
.setCode(coding.getCode());
|
||||||
|
|
||||||
|
TranslationRequest expected = new TranslationRequest();
|
||||||
|
expected.setCodeableConcept(sourceCodeableConcept);
|
||||||
|
expected.setConceptMapVersion(new StringType(theRequest.getConceptMapVersion()));
|
||||||
|
expected.setUrl(new UriType(theRequest.getConceptMapUrl()));
|
||||||
|
expected.setSource(new UriType(theRequest.getSourceValueSetUrl()));
|
||||||
|
expected.setTarget(new UriType(theRequest.getTargetValueSetUrl()));
|
||||||
|
expected.setTargetSystem(new UriType(theRequest.getTargetSystemUrl()));
|
||||||
|
expected.setResourceId(theRequest.getResourcePid());
|
||||||
|
expected.setReverse(theRequest.isReverse());
|
||||||
|
|
||||||
|
ITermConceptMappingSvc mock = mock(TermConceptMappingSvcImpl.class);
|
||||||
|
ArgumentCaptor<TranslationRequest> argument = ArgumentCaptor.forClass(TranslationRequest.class);
|
||||||
|
when(mock.translate(expected)).thenReturn(new TranslateConceptResults());
|
||||||
|
when(mock.translateConcept(theRequest)).thenCallRealMethod();
|
||||||
|
mock.translateConcept(theRequest);
|
||||||
|
verify(mock).translateWithReverse(argument.capture());
|
||||||
|
assertSameTranslationRequest(expected, argument.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertSameTranslationRequest(TranslationRequest expected, TranslationRequest actual) {
|
||||||
|
assertTrue(expected.getCodeableConcept().equalsDeep(actual.getCodeableConcept()));
|
||||||
|
assertEquals(expected.getConceptMapVersion().asStringValue(), actual.getConceptMapVersion().asStringValue());
|
||||||
|
assertEquals(expected.getUrl().asStringValue(), actual.getUrl().asStringValue());
|
||||||
|
assertEquals(expected.getSource().asStringValue(), actual.getSource().asStringValue());
|
||||||
|
assertEquals(expected.getTarget().asStringValue(), actual.getTarget().asStringValue());
|
||||||
|
assertEquals(expected.getTargetSystem().asStringValue(), actual.getTargetSystem().asStringValue());
|
||||||
|
assertEquals(expected.getResourceId(), actual.getResourceId());
|
||||||
|
assertEquals(expected.getReverseAsBoolean(), actual.getReverseAsBoolean());
|
||||||
|
}
|
||||||
|
|
||||||
private void createAndPersistConceptMap() {
|
private void createAndPersistConceptMap() {
|
||||||
ConceptMap conceptMap = createConceptMap();
|
ConceptMap conceptMap = createConceptMap();
|
||||||
|
|
|
@ -36,9 +36,11 @@ import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.Multimap;
|
import com.google.common.collect.Multimap;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
import org.hl7.fhir.instance.model.api.IBase;
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -153,12 +155,17 @@ public class ResponseTerminologyTranslationInterceptor extends BaseResponseTermi
|
||||||
if (!foundSystemsToCodes.containsKey(wantTargetSystem)) {
|
if (!foundSystemsToCodes.containsKey(wantTargetSystem)) {
|
||||||
|
|
||||||
for (String code : foundSystemsToCodes.get(nextSourceSystem)) {
|
for (String code : foundSystemsToCodes.get(nextSourceSystem)) {
|
||||||
TranslateConceptResults translateConceptResults = myValidationSupport.translateConcept(new IValidationSupport.TranslateCodeRequest(nextSourceSystem, code, wantTargetSystem));
|
List<IBaseCoding> codings = new ArrayList<IBaseCoding>();
|
||||||
|
codings.add(createCodingFromPrimitives(nextSourceSystem, code, null));
|
||||||
|
TranslateConceptResults translateConceptResults = myValidationSupport.translateConcept(new IValidationSupport.TranslateCodeRequest(codings, wantTargetSystem));
|
||||||
if (translateConceptResults != null) {
|
if (translateConceptResults != null) {
|
||||||
List<TranslateConceptResult> mappings = translateConceptResults.getResults();
|
List<TranslateConceptResult> mappings = translateConceptResults.getResults();
|
||||||
for (TranslateConceptResult nextMapping : mappings) {
|
for (TranslateConceptResult nextMapping : mappings) {
|
||||||
|
|
||||||
IBase newCoding = createCodingFromMappingTarget(nextMapping);
|
IBase newCoding = createCodingFromPrimitives(
|
||||||
|
nextMapping.getSystem(),
|
||||||
|
nextMapping.getCode(),
|
||||||
|
nextMapping.getDisplay());
|
||||||
|
|
||||||
// Add coding to existing CodeableConcept
|
// Add coding to existing CodeableConcept
|
||||||
myCodeableConceptCodingChild.getMutator().addValue(theElement, newCoding);
|
myCodeableConceptCodingChild.getMutator().addValue(theElement, newCoding);
|
||||||
|
@ -174,14 +181,14 @@ public class ResponseTerminologyTranslationInterceptor extends BaseResponseTermi
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private IBase createCodingFromMappingTarget(TranslateConceptResult nextMapping) {
|
private IBaseCoding createCodingFromPrimitives(String system, String code, String display) {
|
||||||
IBase newCoding = myCodingDefinitition.newInstance();
|
IBaseCoding newCoding = (IBaseCoding) myCodingDefinitition.newInstance();
|
||||||
IPrimitiveType<?> newSystem = myUriDefinition.newInstance(nextMapping.getSystem());
|
IPrimitiveType<?> newSystem = myUriDefinition.newInstance(system);
|
||||||
myCodingSystemChild.getMutator().addValue(newCoding, newSystem);
|
myCodingSystemChild.getMutator().addValue(newCoding, newSystem);
|
||||||
IPrimitiveType<?> newCode = myCodeDefinition.newInstance(nextMapping.getCode());
|
IPrimitiveType<?> newCode = myCodeDefinition.newInstance(code);
|
||||||
myCodingCodeChild.getMutator().addValue(newCoding, newCode);
|
myCodingCodeChild.getMutator().addValue(newCoding, newCode);
|
||||||
if (isNotBlank(nextMapping.getDisplay())) {
|
if (isNotBlank(display)) {
|
||||||
IPrimitiveType<?> newDisplay = myStringDefinition.newInstance(nextMapping.getDisplay());
|
IPrimitiveType<?> newDisplay = myStringDefinition.newInstance(display);
|
||||||
myCodingDisplayChild.getMutator().addValue(newCoding, newDisplay);
|
myCodingDisplayChild.getMutator().addValue(newCoding, newDisplay);
|
||||||
}
|
}
|
||||||
return newCoding;
|
return newCoding;
|
||||||
|
|
|
@ -20,7 +20,9 @@ package ca.uhn.fhir.jpa.api.model;
|
||||||
* #L%
|
* #L%
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||||
import org.hl7.fhir.r4.model.BooleanType;
|
import org.hl7.fhir.r4.model.BooleanType;
|
||||||
import org.hl7.fhir.r4.model.CodeableConcept;
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
import org.hl7.fhir.r4.model.Coding;
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
@ -28,6 +30,7 @@ import org.hl7.fhir.r4.model.StringType;
|
||||||
import org.hl7.fhir.r4.model.UriType;
|
import org.hl7.fhir.r4.model.UriType;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class TranslationRequest {
|
public class TranslationRequest {
|
||||||
|
@ -208,4 +211,40 @@ public class TranslationRequest {
|
||||||
public boolean hasTargetSystem() {
|
public boolean hasTargetSystem() {
|
||||||
return myTargetSystem != null && myTargetSystem.hasValue();
|
return myTargetSystem != null && myTargetSystem.hasValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public IValidationSupport.TranslateCodeRequest asTranslateCodeRequest() {
|
||||||
|
return new IValidationSupport.TranslateCodeRequest(
|
||||||
|
Collections.unmodifiableList(this.getCodeableConcept().getCoding()),
|
||||||
|
this.getTargetSystem() != null ? this.getTargetSystem().asStringValue() : null,
|
||||||
|
this.getUrl() != null ? this.getUrl().asStringValue() : null,
|
||||||
|
this.getConceptMapVersion() != null ? this.getConceptMapVersion().asStringValue() : null,
|
||||||
|
this.getSource() != null ? this.getSource().asStringValue() : null,
|
||||||
|
this.getTarget() != null ? this.getTarget().asStringValue() : null,
|
||||||
|
this.getResourceId(),
|
||||||
|
this.getReverseAsBoolean()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static TranslationRequest fromTranslateCodeRequest(IValidationSupport.TranslateCodeRequest theRequest) {
|
||||||
|
CodeableConcept sourceCodeableConcept = new CodeableConcept();
|
||||||
|
for (IBaseCoding aCoding : theRequest.getCodings()) {
|
||||||
|
sourceCodeableConcept
|
||||||
|
.addCoding()
|
||||||
|
.setSystem(aCoding.getSystem())
|
||||||
|
.setCode(aCoding.getCode())
|
||||||
|
.setVersion(((Coding) aCoding).getVersion());
|
||||||
|
}
|
||||||
|
|
||||||
|
TranslationRequest translationRequest = new TranslationRequest();
|
||||||
|
translationRequest.setCodeableConcept(sourceCodeableConcept);
|
||||||
|
translationRequest.setConceptMapVersion(new StringType(theRequest.getConceptMapVersion()));
|
||||||
|
translationRequest.setUrl(new UriType(theRequest.getConceptMapUrl()));
|
||||||
|
translationRequest.setSource(new UriType(theRequest.getSourceValueSetUrl()));
|
||||||
|
translationRequest.setTarget(new UriType(theRequest.getTargetValueSetUrl()));
|
||||||
|
translationRequest.setTargetSystem(new UriType(theRequest.getTargetSystemUrl()));
|
||||||
|
translationRequest.setResourceId(theRequest.getResourcePid());
|
||||||
|
translationRequest.setReverse(theRequest.isReverse());
|
||||||
|
return translationRequest;
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,11 @@
|
||||||
package org.hl7.fhir.common.hapi.validation.support;
|
package org.hl7.fhir.common.hapi.validation.support;
|
||||||
|
|
||||||
|
import ca.uhn.fhir.context.BaseRuntimeChildDefinition;
|
||||||
|
import ca.uhn.fhir.context.BaseRuntimeElementCompositeDefinition;
|
||||||
|
import ca.uhn.fhir.context.BaseRuntimeElementDefinition;
|
||||||
|
import ca.uhn.fhir.context.RuntimePrimitiveDatatypeDefinition;
|
||||||
|
import ca.uhn.fhir.context.support.TranslateConceptResult;
|
||||||
|
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||||
import ca.uhn.fhir.i18n.Msg;
|
import ca.uhn.fhir.i18n.Msg;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.FhirVersionEnum;
|
import ca.uhn.fhir.context.FhirVersionEnum;
|
||||||
|
@ -11,20 +17,29 @@ import ca.uhn.fhir.rest.client.api.IGenericClient;
|
||||||
import ca.uhn.fhir.util.BundleUtil;
|
import ca.uhn.fhir.util.BundleUtil;
|
||||||
import ca.uhn.fhir.util.JsonUtil;
|
import ca.uhn.fhir.util.JsonUtil;
|
||||||
import ca.uhn.fhir.util.ParametersUtil;
|
import ca.uhn.fhir.util.ParametersUtil;
|
||||||
|
import ca.uhn.fhir.util.StringUtil;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
import org.apache.commons.lang3.Validate;
|
import org.apache.commons.lang3.Validate;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBase;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
import org.hl7.fhir.instance.model.api.IBaseBundle;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
import org.hl7.fhir.instance.model.api.IBaseParameters;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
|
import org.hl7.fhir.instance.model.api.IPrimitiveType;
|
||||||
import org.hl7.fhir.r4.model.CodeSystem;
|
import org.hl7.fhir.r4.model.CodeSystem;
|
||||||
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
import org.hl7.fhir.r4.model.ValueSet;
|
import org.hl7.fhir.r4.model.ValueSet;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.sql.Array;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
|
import java.util.Optional;
|
||||||
|
|
||||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||||
|
@ -331,6 +346,23 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
||||||
return fetchValueSet(theValueSetUrl) != null;
|
return fetchValueSet(theValueSetUrl) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TranslateConceptResults translateConcept(TranslateCodeRequest theRequest) {
|
||||||
|
IGenericClient client = provideClient();
|
||||||
|
FhirContext fhirContext = client.getFhirContext();
|
||||||
|
|
||||||
|
IBaseParameters params = buildTranslateInputParameters(fhirContext, theRequest);
|
||||||
|
|
||||||
|
IBaseParameters outcome = client
|
||||||
|
.operation()
|
||||||
|
.onType("ConceptMap")
|
||||||
|
.named("$translate")
|
||||||
|
.withParameters(params)
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
return translateOutcomeToResults(fhirContext, outcome);
|
||||||
|
}
|
||||||
|
|
||||||
private IGenericClient provideClient() {
|
private IGenericClient provideClient() {
|
||||||
IGenericClient retVal = myCtx.newRestfulGenericClient(myBaseUrl);
|
IGenericClient retVal = myCtx.newRestfulGenericClient(myBaseUrl);
|
||||||
for (Object next : myClientInterceptors) {
|
for (Object next : myClientInterceptors) {
|
||||||
|
@ -442,4 +474,103 @@ public class RemoteTerminologyServiceValidationSupport extends BaseValidationSup
|
||||||
myClientInterceptors.add(theClientInterceptor);
|
myClientInterceptors.add(theClientInterceptor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private IBaseParameters buildTranslateInputParameters(FhirContext fhirContext, TranslateCodeRequest theRequest) {
|
||||||
|
IBaseParameters params = ParametersUtil.newInstance(fhirContext);
|
||||||
|
if (!StringUtils.isEmpty(theRequest.getConceptMapUrl())) {
|
||||||
|
ParametersUtil.addParameterToParametersUri(fhirContext, params, "url", theRequest.getConceptMapUrl());
|
||||||
|
}
|
||||||
|
if (!StringUtils.isEmpty(theRequest.getConceptMapVersion())) {
|
||||||
|
ParametersUtil.addParameterToParametersString(fhirContext, params, "conceptMapVersion", theRequest.getConceptMapVersion());
|
||||||
|
}
|
||||||
|
if (theRequest.getCodings() != null) {
|
||||||
|
addCodingsToTranslateParameters(fhirContext, theRequest.getCodings(), params);
|
||||||
|
}
|
||||||
|
if (!StringUtils.isEmpty(theRequest.getSourceValueSetUrl())) {
|
||||||
|
ParametersUtil.addParameterToParametersUri(fhirContext, params, "source", theRequest.getSourceValueSetUrl());
|
||||||
|
}
|
||||||
|
if (!StringUtils.isEmpty(theRequest.getTargetValueSetUrl())) {
|
||||||
|
ParametersUtil.addParameterToParametersUri(fhirContext, params, "target", theRequest.getTargetValueSetUrl());
|
||||||
|
}
|
||||||
|
if (!StringUtils.isEmpty(theRequest.getTargetSystemUrl())) {
|
||||||
|
ParametersUtil.addParameterToParametersUri(fhirContext, params, "targetsystem", theRequest.getTargetSystemUrl());
|
||||||
|
}
|
||||||
|
if (theRequest.isReverse()) {
|
||||||
|
ParametersUtil.addParameterToParametersBoolean(fhirContext, params, "reverse", theRequest.isReverse());
|
||||||
|
}
|
||||||
|
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addCodingsToTranslateParameters(FhirContext fhirContext, List<IBaseCoding> theCodings, IBaseParameters theParams) {
|
||||||
|
BaseRuntimeElementCompositeDefinition<?> codeableConceptDef = (BaseRuntimeElementCompositeDefinition<?>) Objects.requireNonNull(fhirContext.getElementDefinition("CodeableConcept"));
|
||||||
|
BaseRuntimeChildDefinition codings = codeableConceptDef.getChildByName("coding");
|
||||||
|
BaseRuntimeElementCompositeDefinition<?> codingDef = (BaseRuntimeElementCompositeDefinition<?>) Objects.requireNonNull(fhirContext.getElementDefinition("Coding"));
|
||||||
|
BaseRuntimeChildDefinition codingSystemChild = codingDef.getChildByName("system");
|
||||||
|
BaseRuntimeChildDefinition codingCodeChild = codingDef.getChildByName("code");
|
||||||
|
BaseRuntimeElementDefinition<IPrimitiveType<?>> systemDef = (RuntimePrimitiveDatatypeDefinition) fhirContext.getElementDefinition("uri");
|
||||||
|
BaseRuntimeElementDefinition<IPrimitiveType<?>> codeDef = (RuntimePrimitiveDatatypeDefinition) fhirContext.getElementDefinition("code");
|
||||||
|
|
||||||
|
IBase codeableConcept = codeableConceptDef.newInstance();
|
||||||
|
|
||||||
|
for (IBaseCoding aCoding : theCodings) {
|
||||||
|
IBaseCoding newCoding = (IBaseCoding) codingDef.newInstance();
|
||||||
|
|
||||||
|
IPrimitiveType<?> newSystem = systemDef.newInstance(aCoding.getSystem());
|
||||||
|
codingSystemChild.getMutator().addValue(newCoding, newSystem);
|
||||||
|
IPrimitiveType<?> newCode = codeDef.newInstance(aCoding.getCode());
|
||||||
|
codingCodeChild.getMutator().addValue(newCoding, newCode);
|
||||||
|
|
||||||
|
codings.getMutator().addValue(codeableConcept, newCoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
ParametersUtil.addParameterToParameters(fhirContext, theParams, "codeableConcept", codeableConcept);
|
||||||
|
}
|
||||||
|
|
||||||
|
private TranslateConceptResults translateOutcomeToResults(FhirContext fhirContext, IBaseParameters outcome) {
|
||||||
|
Optional<String> result = ParametersUtil.getNamedParameterValueAsString(fhirContext, outcome, "result");
|
||||||
|
Optional<String> message = ParametersUtil.getNamedParameterValueAsString(fhirContext, outcome, "message");
|
||||||
|
List<IBase> matches = ParametersUtil.getNamedParameters(fhirContext, outcome, "match");
|
||||||
|
|
||||||
|
TranslateConceptResults retVal = new TranslateConceptResults();
|
||||||
|
if (result.isPresent()) {
|
||||||
|
retVal.setResult(Boolean.parseBoolean(result.get()));
|
||||||
|
}
|
||||||
|
if (message.isPresent()) {
|
||||||
|
retVal.setMessage(message.get());
|
||||||
|
}
|
||||||
|
if (!matches.isEmpty()) {
|
||||||
|
retVal.setResults(matchesToTranslateConceptResults(fhirContext, matches));
|
||||||
|
}
|
||||||
|
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<TranslateConceptResult> matchesToTranslateConceptResults(FhirContext fhirContext, List<IBase> theMatches) {
|
||||||
|
List<TranslateConceptResult> resultList = new ArrayList();
|
||||||
|
for (IBase m : theMatches) {
|
||||||
|
TranslateConceptResult match = new TranslateConceptResult();
|
||||||
|
String equivalence = ParametersUtil.getParameterPartValueAsString(fhirContext, m, "equivalence");
|
||||||
|
Optional<IBase> concept = ParametersUtil.getParameterPartValue(fhirContext, m, "concept");
|
||||||
|
String source = ParametersUtil.getParameterPartValueAsString(fhirContext, m, "source");
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(equivalence)) {
|
||||||
|
match.setEquivalence(equivalence);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (concept.isPresent()) {
|
||||||
|
IBaseCoding matchedCoding = (IBaseCoding) concept.get();
|
||||||
|
match.setSystem(matchedCoding.getSystem());
|
||||||
|
match.setCode(matchedCoding.getCode());
|
||||||
|
match.setDisplay(matchedCoding.getDisplay());
|
||||||
|
|
||||||
|
if (StringUtils.isNotBlank(source)) {
|
||||||
|
match.setConceptMapUrl(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
resultList.add(match);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return resultList;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,8 @@ package org.hl7.fhir.common.hapi.validation.support;
|
||||||
import ca.uhn.fhir.context.FhirContext;
|
import ca.uhn.fhir.context.FhirContext;
|
||||||
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
import ca.uhn.fhir.context.support.ConceptValidationOptions;
|
||||||
import ca.uhn.fhir.context.support.IValidationSupport;
|
import ca.uhn.fhir.context.support.IValidationSupport;
|
||||||
|
import ca.uhn.fhir.context.support.TranslateConceptResult;
|
||||||
|
import ca.uhn.fhir.context.support.TranslateConceptResults;
|
||||||
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
import ca.uhn.fhir.jpa.model.util.JpaConstants;
|
||||||
import ca.uhn.fhir.parser.IJsonLikeParser;
|
import ca.uhn.fhir.parser.IJsonLikeParser;
|
||||||
import ca.uhn.fhir.rest.annotation.IdParam;
|
import ca.uhn.fhir.rest.annotation.IdParam;
|
||||||
|
@ -20,11 +22,14 @@ import ca.uhn.fhir.rest.server.IResourceProvider;
|
||||||
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
import ca.uhn.fhir.test.utilities.server.RestfulServerExtension;
|
||||||
import ca.uhn.fhir.util.ParametersUtil;
|
import ca.uhn.fhir.util.ParametersUtil;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import org.hl7.fhir.instance.model.api.IBaseCoding;
|
||||||
import org.hl7.fhir.instance.model.api.IBaseResource;
|
import org.hl7.fhir.instance.model.api.IBaseResource;
|
||||||
import org.hl7.fhir.r4.model.BooleanType;
|
import org.hl7.fhir.r4.model.BooleanType;
|
||||||
import org.hl7.fhir.r4.model.CodeSystem;
|
import org.hl7.fhir.r4.model.CodeSystem;
|
||||||
import org.hl7.fhir.r4.model.CodeType;
|
import org.hl7.fhir.r4.model.CodeType;
|
||||||
|
import org.hl7.fhir.r4.model.CodeableConcept;
|
||||||
import org.hl7.fhir.r4.model.Coding;
|
import org.hl7.fhir.r4.model.Coding;
|
||||||
|
import org.hl7.fhir.r4.model.ConceptMap;
|
||||||
import org.hl7.fhir.r4.model.IdType;
|
import org.hl7.fhir.r4.model.IdType;
|
||||||
import org.hl7.fhir.r4.model.Parameters;
|
import org.hl7.fhir.r4.model.Parameters;
|
||||||
import org.hl7.fhir.r4.model.StringType;
|
import org.hl7.fhir.r4.model.StringType;
|
||||||
|
@ -58,7 +63,18 @@ public class RemoteTerminologyServiceValidationSupportTest {
|
||||||
private static final String CODE_SYSTEM_VERSION_AS_TEXT = "v2.1.12";
|
private static final String CODE_SYSTEM_VERSION_AS_TEXT = "v2.1.12";
|
||||||
private static final String CODE = "CODE";
|
private static final String CODE = "CODE";
|
||||||
private static final String VALUE_SET_URL = "http://value.set/url";
|
private static final String VALUE_SET_URL = "http://value.set/url";
|
||||||
|
private static final String TARGET_SYSTEM = "http://target.system/url";
|
||||||
|
private static final String CONCEPT_MAP_URL = "http://concept.map/url";
|
||||||
|
private static final String CONCEPT_MAP_VERSION = "2.1";
|
||||||
|
private static final String SOURCE_VALUE_SET_URL = "http://source.vs.system/url";
|
||||||
|
private static final String TARGET_VALUE_SET_URL = "http://target.vs.system/url";
|
||||||
|
private static final String TARGET_CODE = "CODE";
|
||||||
|
private static final String TARGET_CODE_DISPLAY = "code";
|
||||||
|
private static final boolean REVERSE = true;
|
||||||
|
private static final String EQUIVALENCE_CODE = "equivalent";
|
||||||
|
|
||||||
private static final String ERROR_MESSAGE = "This is an error message";
|
private static final String ERROR_MESSAGE = "This is an error message";
|
||||||
|
private static final String SUCCESS_MESSAGE = "This is a success message";
|
||||||
|
|
||||||
private static FhirContext ourCtx = FhirContext.forR4Cached();
|
private static FhirContext ourCtx = FhirContext.forR4Cached();
|
||||||
|
|
||||||
|
@ -68,6 +84,7 @@ public class RemoteTerminologyServiceValidationSupportTest {
|
||||||
private MyValueSetProvider myValueSetProvider;
|
private MyValueSetProvider myValueSetProvider;
|
||||||
private RemoteTerminologyServiceValidationSupport mySvc;
|
private RemoteTerminologyServiceValidationSupport mySvc;
|
||||||
private MyCodeSystemProvider myCodeSystemProvider;
|
private MyCodeSystemProvider myCodeSystemProvider;
|
||||||
|
private MyConceptMapProvider myConceptMapProvider;
|
||||||
|
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
public void before() {
|
public void before() {
|
||||||
|
@ -77,6 +94,9 @@ public class RemoteTerminologyServiceValidationSupportTest {
|
||||||
myCodeSystemProvider = new MyCodeSystemProvider();
|
myCodeSystemProvider = new MyCodeSystemProvider();
|
||||||
myRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider);
|
myRestfulServerExtension.getRestfulServer().registerProvider(myCodeSystemProvider);
|
||||||
|
|
||||||
|
myConceptMapProvider = new MyConceptMapProvider();
|
||||||
|
myRestfulServerExtension.getRestfulServer().registerProvider(myConceptMapProvider);
|
||||||
|
|
||||||
String baseUrl = "http://localhost:" + myRestfulServerExtension.getPort();
|
String baseUrl = "http://localhost:" + myRestfulServerExtension.getPort();
|
||||||
|
|
||||||
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
|
mySvc = new RemoteTerminologyServiceValidationSupport(ourCtx);
|
||||||
|
@ -277,6 +297,91 @@ public class RemoteTerminologyServiceValidationSupportTest {
|
||||||
assertEquals(null, outcome);
|
assertEquals(null, outcome);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTranslateCode_AllInParams_AllOutParams() {
|
||||||
|
myConceptMapProvider.myNextReturnParams = new Parameters();
|
||||||
|
myConceptMapProvider.myNextReturnParams.addParameter("result", true);
|
||||||
|
myConceptMapProvider.myNextReturnParams.addParameter("message", ERROR_MESSAGE);
|
||||||
|
|
||||||
|
TranslateConceptResults expectedResults = new TranslateConceptResults();
|
||||||
|
expectedResults.setResult(true);
|
||||||
|
|
||||||
|
// Add 2 matches
|
||||||
|
addMatchToTranslateRequest(myConceptMapProvider.myNextReturnParams);
|
||||||
|
addMatchToTranslateRequest(myConceptMapProvider.myNextReturnParams);
|
||||||
|
|
||||||
|
List<TranslateConceptResult> translateResults = new ArrayList<>();
|
||||||
|
TranslateConceptResult singleResult = new TranslateConceptResult();
|
||||||
|
singleResult
|
||||||
|
.setEquivalence(EQUIVALENCE_CODE)
|
||||||
|
.setSystem(TARGET_SYSTEM)
|
||||||
|
.setCode(TARGET_CODE)
|
||||||
|
.setConceptMapUrl(CONCEPT_MAP_URL)
|
||||||
|
.setDisplay(TARGET_CODE_DISPLAY);
|
||||||
|
translateResults.add(singleResult);
|
||||||
|
translateResults.add(singleResult);
|
||||||
|
expectedResults.setResults(translateResults);
|
||||||
|
|
||||||
|
CodeableConcept codeableConcept = new CodeableConcept();
|
||||||
|
codeableConcept.addCoding(new Coding(CODE_SYSTEM, CODE, null));
|
||||||
|
|
||||||
|
IValidationSupport.TranslateCodeRequest request = new IValidationSupport.TranslateCodeRequest(
|
||||||
|
Collections.unmodifiableList(codeableConcept.getCoding()),
|
||||||
|
TARGET_SYSTEM,
|
||||||
|
CONCEPT_MAP_URL,
|
||||||
|
CONCEPT_MAP_VERSION,
|
||||||
|
SOURCE_VALUE_SET_URL,
|
||||||
|
TARGET_VALUE_SET_URL,
|
||||||
|
null,
|
||||||
|
REVERSE);
|
||||||
|
|
||||||
|
TranslateConceptResults results = mySvc.translateConcept(request);
|
||||||
|
|
||||||
|
assertEquals(results.getResult(), true);
|
||||||
|
assertEquals(results.getResults().size(), 2);
|
||||||
|
for(TranslateConceptResult result : results.getResults()) {
|
||||||
|
assertEquals(singleResult, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
assertTrue(codeableConcept.equalsDeep(myConceptMapProvider.myLastCodeableConcept));
|
||||||
|
assertEquals(TARGET_SYSTEM, myConceptMapProvider.myLastTargetCodeSystem.getValue());
|
||||||
|
assertEquals(CONCEPT_MAP_URL, myConceptMapProvider.myLastConceptMapUrl.getValue());
|
||||||
|
assertEquals(CONCEPT_MAP_VERSION, myConceptMapProvider.myLastConceptMapVersion.getValue());
|
||||||
|
assertEquals(SOURCE_VALUE_SET_URL, myConceptMapProvider.myLastSourceValueSet.getValue());
|
||||||
|
assertEquals(TARGET_VALUE_SET_URL, myConceptMapProvider.myLastTargetValueSet.getValue());
|
||||||
|
assertEquals(REVERSE, myConceptMapProvider.myLastReverse.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testTranslateCode_NoInParams_NoOutParams() {
|
||||||
|
myConceptMapProvider.myNextReturnParams = new Parameters();
|
||||||
|
|
||||||
|
List<IBaseCoding> codings = new ArrayList<>();
|
||||||
|
codings.add(new Coding(null, null, null));
|
||||||
|
IValidationSupport.TranslateCodeRequest request = new IValidationSupport.TranslateCodeRequest(codings, null);
|
||||||
|
|
||||||
|
TranslateConceptResults results = mySvc.translateConcept(request);
|
||||||
|
|
||||||
|
assertEquals(results.getResult(), false);
|
||||||
|
assertEquals(results.getResults().size(), 0);
|
||||||
|
|
||||||
|
assertNull(myConceptMapProvider.myLastCodeableConcept);
|
||||||
|
assertNull(myConceptMapProvider.myLastTargetCodeSystem);
|
||||||
|
assertNull(myConceptMapProvider.myLastConceptMapUrl);
|
||||||
|
assertNull(myConceptMapProvider.myLastConceptMapVersion);
|
||||||
|
assertNull(myConceptMapProvider.myLastSourceValueSet);
|
||||||
|
assertNull(myConceptMapProvider.myLastTargetValueSet);
|
||||||
|
assertNull(myConceptMapProvider.myLastReverse);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addMatchToTranslateRequest(Parameters params) {
|
||||||
|
Parameters.ParametersParameterComponent matchParam = params.addParameter().setName("match");
|
||||||
|
matchParam.addPart().setName("equivalence").setValue(new CodeType(EQUIVALENCE_CODE));
|
||||||
|
Coding value = new Coding(TARGET_SYSTEM, TARGET_CODE, TARGET_CODE_DISPLAY);
|
||||||
|
matchParam.addPart().setName("concept").setValue(value);
|
||||||
|
matchParam.addPart().setName("source").setValue(new UriType(CONCEPT_MAP_URL));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remote terminology services can be used to validate codes when code system is present,
|
* Remote terminology services can be used to validate codes when code system is present,
|
||||||
* even when inferSystem is true
|
* even when inferSystem is true
|
||||||
|
@ -636,5 +741,50 @@ public class RemoteTerminologyServiceValidationSupportTest {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class MyConceptMapProvider implements IResourceProvider {
|
||||||
|
private UriType myLastConceptMapUrl;
|
||||||
|
private StringType myLastConceptMapVersion;
|
||||||
|
private CodeableConcept myLastCodeableConcept;
|
||||||
|
private UriType myLastSourceValueSet;
|
||||||
|
private UriType myLastTargetValueSet;
|
||||||
|
private UriType myLastTargetCodeSystem;
|
||||||
|
private BooleanType myLastReverse;
|
||||||
|
|
||||||
|
private int myInvocationCount;
|
||||||
|
private Parameters myNextReturnParams;
|
||||||
|
|
||||||
|
@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 = "url", min = 0, max = 1) UriType theConceptMapUrl,
|
||||||
|
@OperationParam(name = "conceptMapVersion", min = 0, max = 1) StringType theConceptMapVersion,
|
||||||
|
@OperationParam(name = "codeableConcept", min = 0, max = 1) CodeableConcept theSourceCodeableConcept,
|
||||||
|
@OperationParam(name = "source", min = 0, max = 1) UriType theSourceValueSet,
|
||||||
|
@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
|
||||||
|
) {
|
||||||
|
myInvocationCount++;
|
||||||
|
myLastConceptMapUrl = theConceptMapUrl;
|
||||||
|
myLastConceptMapVersion = theConceptMapVersion;
|
||||||
|
myLastCodeableConcept = theSourceCodeableConcept;
|
||||||
|
myLastSourceValueSet = theSourceValueSet;
|
||||||
|
myLastTargetValueSet = theTargetValueSet;
|
||||||
|
myLastTargetCodeSystem = theTargetCodeSystem;
|
||||||
|
myLastReverse = theReverse;
|
||||||
|
return myNextReturnParams;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Class<? extends IBaseResource> getResourceType() {
|
||||||
|
return ConceptMap.class;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue