Merge branch 'master' of github.com:jamesagnew/hapi-fhir

This commit is contained in:
James Agnew 2019-07-17 10:57:43 -04:00
commit 76384abefb
23 changed files with 1081 additions and 176 deletions

View File

@ -20,16 +20,16 @@ package ca.uhn.fhir.jpa.dao.data;
* #L%
*/
import ca.uhn.fhir.jpa.entity.TermValueSetCode;
import ca.uhn.fhir.jpa.entity.TermValueSetConcept;
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;
public interface ITermValueSetCodeDao extends JpaRepository<TermValueSetCode, Long> {
public interface ITermValueSetConceptDao extends JpaRepository<TermValueSetConcept, Long> {
@Query("DELETE FROM TermValueSetCode vsc WHERE vsc.myValueSet.myId = :pid")
@Query("DELETE FROM TermValueSetConcept vsc WHERE vsc.myValueSet.myId = :pid")
@Modifying
void deleteTermValueSetCodesByValueSetId(@Param("pid") Long theValueSetId);
void deleteTermValueSetConceptsByValueSetId(@Param("pid") Long theValueSetId);
}

View File

@ -0,0 +1,35 @@
package ca.uhn.fhir.jpa.dao.data;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 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.entity.TermValueSetConceptDesignation;
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;
public interface ITermValueSetConceptDesignationDao extends JpaRepository<TermValueSetConceptDesignation, Long> {
@Query("DELETE FROM TermValueSetConceptDesignation vscd WHERE vscd.myConcept.myValueSet.myId = :pid")
@Modifying
void deleteTermValueSetConceptDesignationsByValueSetId(@Param("pid") Long theValueSetId);
}

View File

@ -26,6 +26,7 @@ import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
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.TermConceptDesignation;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
@ -51,8 +52,7 @@ import java.util.Date;
import java.util.List;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.*;
public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> {
@ -193,6 +193,21 @@ public class FhirResourceDaoCodeSystemDstu3 extends FhirResourceDaoDstu3<CodeSys
termConcept.setDisplay(next.getDisplay());
termConcept.addChildren(toPersistedConcepts(next.getConcept(), theCodeSystemVersion), RelationshipTypeEnum.ISA);
retVal.add(termConcept);
for (CodeSystem.ConceptDefinitionDesignationComponent designationComponent : next.getDesignation()) {
if (isNotBlank(designationComponent.getValue())) {
TermConceptDesignation designation = termConcept.addDesignation();
designation.setLanguage(designationComponent.hasLanguage() ? designationComponent.getLanguage() : null);
if (designationComponent.hasUse()) {
designation.setUseSystem(designationComponent.getUse().hasSystem() ? designationComponent.getUse().getSystem() : null);
designation.setUseCode(designationComponent.getUse().hasCode() ? designationComponent.getUse().getCode() : null);
designation.setUseDisplay(designationComponent.getUse().hasDisplay() ? designationComponent.getUse().getDisplay() : null);
}
designation.setValue(designationComponent.getValue());
}
}
// TODO: DM 2019-07-16 - We should also populate TermConceptProperty entities here.
}
}

View File

@ -96,7 +96,8 @@ public class ExpungeEverythingService {
counter.addAndGet(expungeEverythingByType(ResourceLink.class));
counter.addAndGet(expungeEverythingByType(SearchResult.class));
counter.addAndGet(expungeEverythingByType(SearchInclude.class));
counter.addAndGet(expungeEverythingByType(TermValueSetCode.class));
counter.addAndGet(expungeEverythingByType(TermValueSetConceptDesignation.class));
counter.addAndGet(expungeEverythingByType(TermValueSetConcept.class));
counter.addAndGet(expungeEverythingByType(TermValueSet.class));
counter.addAndGet(expungeEverythingByType(TermConceptParentChildLink.class));
counter.addAndGet(expungeEverythingByType(TermConceptMapGroupElementTarget.class));

View File

@ -26,8 +26,8 @@ import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemVersionDao;
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.TermConceptDesignation;
import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.model.entity.BaseHasResource;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.jpa.searchparam.SearchParameterMap;
import ca.uhn.fhir.jpa.util.LogicUtil;
@ -52,8 +52,7 @@ import java.util.Date;
import java.util.List;
import java.util.Set;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.*;
public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4<CodeSystem> implements IFhirResourceDaoCodeSystem<CodeSystem, Coding, CodeableConcept> {
@ -168,6 +167,21 @@ public class FhirResourceDaoCodeSystemR4 extends FhirResourceDaoR4<CodeSystem> i
termConcept.setDisplay(next.getDisplay());
termConcept.addChildren(toPersistedConcepts(next.getConcept(), theCodeSystemVersion), RelationshipTypeEnum.ISA);
retVal.add(termConcept);
for (CodeSystem.ConceptDefinitionDesignationComponent designationComponent : next.getDesignation()) {
if (isNotBlank(designationComponent.getValue())) {
TermConceptDesignation designation = termConcept.addDesignation();
designation.setLanguage(designationComponent.hasLanguage() ? designationComponent.getLanguage() : null);
if (designationComponent.hasUse()) {
designation.setUseSystem(designationComponent.getUse().hasSystem() ? designationComponent.getUse().getSystem() : null);
designation.setUseCode(designationComponent.getUse().hasCode() ? designationComponent.getUse().getCode() : null);
designation.setUseDisplay(designationComponent.getUse().hasDisplay() ? designationComponent.getUse().getDisplay() : null);
}
designation.setValue(designationComponent.getValue());
}
}
// TODO: DM 2019-07-16 - We should also populate TermConceptProperty entities here.
}
}

View File

@ -103,7 +103,6 @@ public class TermConceptDesignation implements Serializable {
public TermConceptDesignation setUseSystem(String theUseSystem) {
ValidateUtil.isNotTooLongOrThrowIllegalArgument(theUseSystem, MAX_LENGTH,
"Use system exceeds maximum length (" + MAX_LENGTH + "): " + length(theUseSystem));
myUseSystem = theUseSystem;
return this;
}

View File

@ -66,7 +66,7 @@ public class TermValueSet implements Serializable {
private String myName;
@OneToMany(mappedBy = "myValueSet")
private List<TermValueSetCode> myCodes;
private List<TermValueSetConcept> myConcepts;
public Long getId() {
return myId;
@ -102,12 +102,12 @@ public class TermValueSet implements Serializable {
return this;
}
public List<TermValueSetCode> getCodes() {
if (myCodes == null) {
myCodes = new ArrayList<>();
public List<TermValueSetConcept> getConcepts() {
if (myConcepts == null) {
myConcepts = new ArrayList<>();
}
return myCodes;
return myConcepts;
}
@Override
@ -138,7 +138,7 @@ public class TermValueSet implements Serializable {
.append(myResource != null ? ("myResource=" + myResource.toString()) : ("myResource=(null)"))
.append("myResourcePid", myResourcePid)
.append("myName", myName)
.append(myCodes != null ? ("myCodes - size=" + myCodes.size()) : ("myCodes=(null)"))
.append(myConcepts != null ? ("myConcepts - size=" + myConcepts.size()) : ("myConcepts=(null)"))
.toString();
}
}

View File

@ -29,20 +29,22 @@ import org.apache.commons.lang3.builder.ToStringStyle;
import javax.annotation.Nonnull;
import javax.persistence.*;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import static org.apache.commons.lang3.StringUtils.left;
import static org.apache.commons.lang3.StringUtils.length;
@Table(name = "TRM_VALUESET_CODE", indexes = {
@Index(name = "IDX_VALUESET_CODE_CS_CD", columnList = "SYSTEM, CODE")
@Table(name = "TRM_VALUESET_CONCEPT", indexes = {
@Index(name = "IDX_VALUESET_CONCEPT_CS_CD", columnList = "SYSTEM, CODE")
})
@Entity()
public class TermValueSetCode implements Serializable {
public class TermValueSetConcept implements Serializable {
private static final long serialVersionUID = 1L;
@Id()
@SequenceGenerator(name = "SEQ_VALUESET_CODE_PID", sequenceName = "SEQ_VALUESET_CODE_PID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_VALUESET_CODE_PID")
@SequenceGenerator(name = "SEQ_VALUESET_CONCEPT_PID", sequenceName = "SEQ_VALUESET_CONCEPT_PID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_VALUESET_CONCEPT_PID")
@Column(name = "PID")
private Long myId;
@ -65,6 +67,9 @@ public class TermValueSetCode implements Serializable {
@Column(name = "DISPLAY", nullable = true, length = TermConcept.MAX_DESC_LENGTH)
private String myDisplay;
@OneToMany(mappedBy = "myConcept")
private List<TermValueSetConceptDesignation> myDesignations;
public Long getId() {
return myId;
}
@ -73,7 +78,7 @@ public class TermValueSetCode implements Serializable {
return myValueSet;
}
public TermValueSetCode setValueSet(TermValueSet theValueSet) {
public TermValueSetConcept setValueSet(TermValueSet theValueSet) {
myValueSet = theValueSet;
return this;
}
@ -98,7 +103,7 @@ public class TermValueSetCode implements Serializable {
return mySystem;
}
public TermValueSetCode setSystem(@Nonnull String theSystem) {
public TermValueSetConcept setSystem(@Nonnull String theSystem) {
ValidateUtil.isNotBlankOrThrowIllegalArgument(theSystem, "theSystem must not be null or empty");
ValidateUtil.isNotTooLongOrThrowIllegalArgument(theSystem, TermCodeSystem.MAX_URL_LENGTH,
"System exceeds maximum length (" + TermCodeSystem.MAX_URL_LENGTH + "): " + length(theSystem));
@ -110,7 +115,7 @@ public class TermValueSetCode implements Serializable {
return myCode;
}
public TermValueSetCode setCode(@Nonnull String theCode) {
public TermValueSetConcept setCode(@Nonnull String theCode) {
ValidateUtil.isNotBlankOrThrowIllegalArgument(theCode, "theCode must not be null or empty");
ValidateUtil.isNotTooLongOrThrowIllegalArgument(theCode, TermConcept.MAX_CODE_LENGTH,
"Code exceeds maximum length (" + TermConcept.MAX_CODE_LENGTH + "): " + length(theCode));
@ -122,18 +127,26 @@ public class TermValueSetCode implements Serializable {
return myDisplay;
}
public TermValueSetCode setDisplay(String theDisplay) {
public TermValueSetConcept setDisplay(String theDisplay) {
myDisplay = left(theDisplay, TermConcept.MAX_DESC_LENGTH);
return this;
}
public List<TermValueSetConceptDesignation> getDesignations() {
if (myDesignations == null) {
myDesignations = new ArrayList<>();
}
return myDesignations;
}
@Override
public boolean equals(Object theO) {
if (this == theO) return true;
if (!(theO instanceof TermValueSetCode)) return false;
if (!(theO instanceof TermValueSetConcept)) return false;
TermValueSetCode that = (TermValueSetCode) theO;
TermValueSetConcept that = (TermValueSetConcept) theO;
return new EqualsBuilder()
.append(getValueSetUrl(), that.getValueSetUrl())
@ -161,6 +174,7 @@ public class TermValueSetCode implements Serializable {
.append("mySystem", mySystem)
.append("myCode", myCode)
.append("myDisplay", myDisplay)
.append(myDesignations != null ? ("myDesignations - size=" + myDesignations.size()) : ("myDesignations=(null)"))
.toString();
}
}

View File

@ -0,0 +1,177 @@
package ca.uhn.fhir.jpa.entity;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 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.util.ValidateUtil;
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.annotation.Nonnull;
import javax.persistence.*;
import java.io.Serializable;
import static org.apache.commons.lang3.StringUtils.left;
import static org.apache.commons.lang3.StringUtils.length;
@Table(name = "TRM_VALUESET_C_DESIGNATION", indexes = {
@Index(name = "IDX_VALUESET_C_DSGNTN_VAL", columnList = "VAL")
})
@Entity()
public class TermValueSetConceptDesignation implements Serializable {
private static final long serialVersionUID = 1L;
public static final int MAX_LENGTH = 500;
@Id()
@SequenceGenerator(name = "SEQ_VALUESET_C_DSGNTN_PID", sequenceName = "SEQ_VALUESET_C_DSGNTN_PID")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_VALUESET_C_DSGNTN_PID")
@Column(name = "PID")
private Long myId;
@ManyToOne()
@JoinColumn(name = "VALUESET_CONCEPT_PID", referencedColumnName = "PID", nullable = false, foreignKey = @ForeignKey(name = "FK_TRM_VALUESET_CONCEPT_PID"))
private TermValueSetConcept myConcept;
@Column(name = "LANG", nullable = true, length = MAX_LENGTH)
private String myLanguage;
@Column(name = "USE_SYSTEM", nullable = true, length = MAX_LENGTH)
private String myUseSystem;
@Column(name = "USE_CODE", nullable = true, length = MAX_LENGTH)
private String myUseCode;
@Column(name = "USE_DISPLAY", nullable = true, length = MAX_LENGTH)
private String myUseDisplay;
@Column(name = "VAL", nullable = false, length = MAX_LENGTH)
private String myValue;
public Long getId() {
return myId;
}
public TermValueSetConcept getConcept() {
return myConcept;
}
public TermValueSetConceptDesignation setConcept(TermValueSetConcept theConcept) {
myConcept = theConcept;
return this;
}
public String getLanguage() {
return myLanguage;
}
public TermValueSetConceptDesignation setLanguage(String theLanguage) {
ValidateUtil.isNotTooLongOrThrowIllegalArgument(theLanguage, MAX_LENGTH,
"Language exceeds maximum length (" + MAX_LENGTH + "): " + length(theLanguage));
myLanguage = theLanguage;
return this;
}
public String getUseSystem() {
return myUseSystem;
}
public TermValueSetConceptDesignation setUseSystem(String theUseSystem) {
ValidateUtil.isNotTooLongOrThrowIllegalArgument(theUseSystem, MAX_LENGTH,
"Use system exceeds maximum length (" + MAX_LENGTH + "): " + length(theUseSystem));
myUseSystem = theUseSystem;
return this;
}
public String getUseCode() {
return myUseCode;
}
public TermValueSetConceptDesignation setUseCode(String theUseCode) {
ValidateUtil.isNotTooLongOrThrowIllegalArgument(theUseCode, MAX_LENGTH,
"Use code exceeds maximum length (" + MAX_LENGTH + "): " + length(theUseCode));
myUseCode = theUseCode;
return this;
}
public String getUseDisplay() {
return myUseDisplay;
}
public TermValueSetConceptDesignation setUseDisplay(String theUseDisplay) {
myUseDisplay = left(theUseDisplay, MAX_LENGTH);
return this;
}
public String getValue() {
return myValue;
}
public TermValueSetConceptDesignation setValue(@Nonnull String theValue) {
ValidateUtil.isNotBlankOrThrowIllegalArgument(theValue, "theValue must not be null or empty");
ValidateUtil.isNotTooLongOrThrowIllegalArgument(theValue, MAX_LENGTH,
"Value exceeds maximum length (" + MAX_LENGTH + "): " + length(theValue));
myValue = theValue;
return this;
}
@Override
public boolean equals(Object theO) {
if (this == theO) return true;
if (!(theO instanceof TermValueSetConceptDesignation)) return false;
TermValueSetConceptDesignation that = (TermValueSetConceptDesignation) theO;
return new EqualsBuilder()
.append(getLanguage(), that.getLanguage())
.append(getUseSystem(), that.getUseSystem())
.append(getUseCode(), that.getUseCode())
.append(getUseDisplay(), that.getUseDisplay())
.append(getValue(), that.getValue())
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(getLanguage())
.append(getUseSystem())
.append(getUseCode())
.append(getUseDisplay())
.append(getValue())
.toHashCode();
}
@Override
public String toString() {
return new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE)
.append("myId", myId)
.append(myConcept != null ? ("myConcept - id=" + myConcept.getId()) : ("myConcept=(null)"))
.append("myLanguage", myLanguage)
.append("myUseSystem", myUseSystem)
.append("myUseCode", myUseCode)
.append("myUseDisplay", myUseDisplay)
.append("myValue", myValue)
.toString();
}
}

View File

@ -91,8 +91,7 @@ import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Supplier;
import java.util.stream.Collectors;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.*;
public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc, ApplicationContextAware {
public static final int DEFAULT_FETCH_SIZE = 250;
@ -121,7 +120,9 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
@Autowired
protected ITermValueSetDao myValueSetDao;
@Autowired
protected ITermValueSetCodeDao myValueSetCodeDao;
protected ITermValueSetConceptDao myValueSetConceptDao;
@Autowired
protected ITermValueSetConceptDesignationDao myValueSetConceptDesignationDao;
@Autowired
protected FhirContext myContext;
@PersistenceContext(type = PersistenceContextType.TRANSACTION)
@ -152,54 +153,39 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
@Autowired(required = false)
private IFulltextSearchSvc myFulltextSearchSvc;
private void addCodeIfNotAlreadyAdded(ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, TermConcept theConcept, boolean theAdd, AtomicInteger theCodeCounter) {
private void addCodeIfNotAlreadyAdded(IValueSetCodeAccumulator theValueSetCodeAccumulator, Set<String> theAddedCodes, TermConcept theConcept, boolean theAdd, AtomicInteger theCodeCounter) {
String codeSystem = theConcept.getCodeSystemVersion().getCodeSystem().getCodeSystemUri();
String code = theConcept.getCode();
String display = theConcept.getDisplay();
Collection<TermConceptDesignation> designations = theConcept.getDesignations();
addCodeIfNotAlreadyAdded(theExpansionComponent, theAddedCodes, designations, theAdd, theCodeCounter, codeSystem, code, display);
addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, designations, theAdd, theCodeCounter, codeSystem, code, display);
}
private void addCodeIfNotAlreadyAdded(ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, Collection<TermConceptDesignation> theDesignations, boolean theAdd, AtomicInteger theCodeCounter, String theCodeSystem, String theCode, String theDisplay) {
if (isNotBlank(theCode) && theAdd && theAddedCodes.add(theCode)) {
ValueSet.ValueSetExpansionContainsComponent contains = theExpansionComponent.addContains();
contains.setCode(theCode);
contains.setSystem(theCodeSystem);
contains.setDisplay(theDisplay);
if (theDesignations != null) {
for (TermConceptDesignation nextDesignation : theDesignations) {
contains
.addDesignation()
.setValue(nextDesignation.getValue())
.getUse()
.setSystem(nextDesignation.getUseSystem())
.setCode(nextDesignation.getUseCode())
.setDisplay(nextDesignation.getUseDisplay());
private void addCodeIfNotAlreadyAdded(IValueSetCodeAccumulator theValueSetCodeAccumulator, Set<String> theAddedCodes, Collection<TermConceptDesignation> theDesignations, boolean theAdd, AtomicInteger theCodeCounter, String theCodeSystem, String theCode, String theDisplay) {
if (isNoneBlank(theCodeSystem, theCode)) {
if (theAdd && theAddedCodes.add(theCodeSystem + "|" + theCode)) {
theValueSetCodeAccumulator.includeCodeWithDesignations(theCodeSystem, theCode, theDisplay, theDesignations);
theCodeCounter.incrementAndGet();
}
if (!theAdd && theAddedCodes.remove(theCodeSystem + "|" + theCode)) {
theValueSetCodeAccumulator.excludeCode(theCodeSystem, theCode);
theCodeCounter.decrementAndGet();
}
}
}
private void addConceptsToList(IValueSetCodeAccumulator theValueSetCodeAccumulator, Set<String> theAddedCodes, String theSystem, List<CodeSystem.ConceptDefinitionComponent> theConcept, boolean theAdd) {
for (CodeSystem.ConceptDefinitionComponent next : theConcept) {
if (isNoneBlank(theSystem, next.getCode())) {
if (theAdd && theAddedCodes.add(theSystem + "|" + next.getCode())) {
theValueSetCodeAccumulator.includeCode(theSystem, next.getCode(), next.getDisplay());
}
if (!theAdd && theAddedCodes.remove(theSystem + "|" + next.getCode())) {
theValueSetCodeAccumulator.excludeCode(theSystem, next.getCode());
}
}
theCodeCounter.incrementAndGet();
}
if (!theAdd && theAddedCodes.remove(theCode)) {
removeCodeFromExpansion(theCodeSystem, theCode, theExpansionComponent);
theCodeCounter.decrementAndGet();
}
}
private void addConceptsToList(ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, String theSystem, List<CodeSystem.ConceptDefinitionComponent> theConcept, boolean theAdd) {
for (CodeSystem.ConceptDefinitionComponent next : theConcept) {
if (theAdd && theAddedCodes.add(next.getCode())) {
ValueSet.ValueSetExpansionContainsComponent contains = theExpansionComponent.addContains();
contains.setCode(next.getCode());
contains.setSystem(theSystem);
contains.setDisplay(next.getDisplay());
}
if (!theAdd && theAddedCodes.remove(next.getCode())) {
removeCodeFromExpansion(theSystem, next.getCode(), theExpansionComponent);
}
addConceptsToList(theExpansionComponent, theAddedCodes, theSystem, next.getConcept(), theAdd);
addConceptsToList(theValueSetCodeAccumulator, theAddedCodes, theSystem, next.getConcept(), theAdd);
}
}
@ -400,12 +386,14 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
TermValueSet existingTermValueSet = optionalExistingTermValueSetById.get();
ourLog.info("Deleting existing TermValueSet {} and its children...", existingTermValueSet.getId());
myValueSetCodeDao.deleteTermValueSetCodesByValueSetId(existingTermValueSet.getId());
myValueSetConceptDesignationDao.deleteTermValueSetConceptDesignationsByValueSetId(existingTermValueSet.getId());
myValueSetConceptDao.deleteTermValueSetConceptsByValueSetId(existingTermValueSet.getId());
myValueSetDao.deleteTermValueSetById(existingTermValueSet.getId());
ourLog.info("Done deleting existing TermValueSet {} and its children.", existingTermValueSet.getId());
ourLog.info("Flushing...");
myValueSetCodeDao.flush();
myValueSetConceptDesignationDao.flush();
myValueSetConceptDao.flush();
myValueSetDao.flush();
ourLog.info("Done flushing.");
}
@ -466,26 +454,13 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
@Transactional(propagation = Propagation.REQUIRED)
public ValueSet expandValueSet(ValueSet theValueSetToExpand) {
ValueSet.ValueSetExpansionComponent expansionComponent = new ValueSet.ValueSetExpansionComponent();
ValueSetExpansionComponentWithCodeAccumulator expansionComponent = new ValueSetExpansionComponentWithCodeAccumulator();
expansionComponent.setIdentifier(UUID.randomUUID().toString());
expansionComponent.setTimestamp(new Date());
Set<String> addedCodes = new HashSet<>();
AtomicInteger codeCounter = new AtomicInteger(0);
// Handle includes
ourLog.debug("Handling includes");
for (ValueSet.ConceptSetComponent include : theValueSetToExpand.getCompose().getInclude()) {
boolean add = true;
expandValueSetHandleIncludeOrExclude(expansionComponent, addedCodes, include, add, codeCounter);
}
// Handle excludes
ourLog.debug("Handling excludes");
for (ValueSet.ConceptSetComponent include : theValueSetToExpand.getCompose().getExclude()) {
boolean add = false;
expandValueSetHandleIncludeOrExclude(expansionComponent, addedCodes, include, add, codeCounter);
}
expandValueSet(theValueSetToExpand, expansionComponent, codeCounter);
expansionComponent.setTotal(codeCounter.get());
@ -496,6 +471,30 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
return valueSet;
}
@Override
@Transactional(propagation = Propagation.REQUIRED)
public void expandValueSet(ValueSet theValueSetToExpand, IValueSetCodeAccumulator theValueSetCodeAccumulator) {
expandValueSet(theValueSetToExpand, theValueSetCodeAccumulator, new AtomicInteger(0));
}
private void expandValueSet(ValueSet theValueSetToExpand, IValueSetCodeAccumulator theValueSetCodeAccumulator, AtomicInteger theCodeCounter) {
Set<String> addedCodes = new HashSet<>();
// Handle includes
ourLog.debug("Handling includes");
for (ValueSet.ConceptSetComponent include : theValueSetToExpand.getCompose().getInclude()) {
boolean add = true;
expandValueSetHandleIncludeOrExclude(theValueSetCodeAccumulator, addedCodes, include, add, theCodeCounter);
}
// Handle excludes
ourLog.debug("Handling excludes");
for (ValueSet.ConceptSetComponent include : theValueSetToExpand.getCompose().getExclude()) {
boolean add = false;
expandValueSetHandleIncludeOrExclude(theValueSetCodeAccumulator, addedCodes, include, add, theCodeCounter);
}
}
protected List<VersionIndependentConcept> expandValueSetAndReturnVersionIndependentConcepts(org.hl7.fhir.r4.model.ValueSet theValueSetToExpandR4) {
org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent expandedR4 = expandValueSet(theValueSetToExpandR4).getExpansion();
@ -507,7 +506,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
return retVal;
}
public void expandValueSetHandleIncludeOrExclude(ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, ValueSet.ConceptSetComponent theInclude, boolean theAdd, AtomicInteger theCodeCounter) {
private void expandValueSetHandleIncludeOrExclude(IValueSetCodeAccumulator theValueSetCodeAccumulator, Set<String> theAddedCodes, ValueSet.ConceptSetComponent theInclude, boolean theAdd, AtomicInteger theCodeCounter) {
String system = theInclude.getSystem();
boolean hasSystem = isNotBlank(system);
boolean hasValueSet = theInclude.getValueSet().size() > 0;
@ -525,7 +524,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
* since we're going to do it without the database.
*/
if (myFulltextSearchSvc == null) {
expandWithoutHibernateSearch(theExpansionComponent, theAddedCodes, theInclude, system, theAdd, theCodeCounter);
expandWithoutHibernateSearch(theValueSetCodeAccumulator, theAddedCodes, theInclude, system, theAdd, theCodeCounter);
return;
}
@ -649,7 +648,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
for (Object next : jpaQuery.getResultList()) {
count.incrementAndGet();
TermConcept concept = (TermConcept) next;
addCodeIfNotAlreadyAdded(theExpansionComponent, theAddedCodes, concept, theAdd, theCodeCounter);
addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, concept, theAdd, theCodeCounter);
}
@ -670,24 +669,21 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
if (theInclude.getConcept().isEmpty() == false) {
for (ValueSet.ConceptReferenceComponent next : theInclude.getConcept()) {
String nextCode = next.getCode();
if (isNotBlank(nextCode) && !theAddedCodes.contains(nextCode)) {
if (isNoneBlank(system, nextCode) && !theAddedCodes.contains(system + "|" + nextCode)) {
CodeSystem.ConceptDefinitionComponent code = findCode(codeSystemFromContext.getConcept(), nextCode);
if (code != null) {
if (theAdd && theAddedCodes.add(nextCode)) {
ValueSet.ValueSetExpansionContainsComponent contains = theExpansionComponent.addContains();
contains.setCode(nextCode);
contains.setSystem(system);
contains.setDisplay(code.getDisplay());
if (theAdd && theAddedCodes.add(system + "|" + nextCode)) {
theValueSetCodeAccumulator.includeCode(system, nextCode, code.getDisplay());
}
if (!theAdd && theAddedCodes.remove(nextCode)) {
removeCodeFromExpansion(system, nextCode, theExpansionComponent);
if (!theAdd && theAddedCodes.remove(system + "|" + nextCode)) {
theValueSetCodeAccumulator.excludeCode(system, nextCode);
}
}
}
}
} else {
List<CodeSystem.ConceptDefinitionComponent> concept = codeSystemFromContext.getConcept();
addConceptsToList(theExpansionComponent, theAddedCodes, system, concept, theAdd);
addConceptsToList(theValueSetCodeAccumulator, theAddedCodes, system, concept, theAdd);
}
}
@ -702,12 +698,12 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
myConceptDao
.findByCodeSystemAndCode(codeSystem.getCurrentVersion(), nextConcept.getCode())
.ifPresent(concept ->
addCodeIfNotAlreadyAdded(theExpansionComponent, theAddedCodes, concept, theAdd, theCodeCounter)
addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, concept, theAdd, theCodeCounter)
);
}
if (!theAdd && theAddedCodes.remove(nextConcept.getCode())) {
removeCodeFromExpansion(nextConcept.getSystem(), nextConcept.getCode(), theExpansionComponent);
if (isNoneBlank(nextConcept.getSystem(), nextConcept.getCode()) && !theAdd && theAddedCodes.remove(nextConcept.getSystem() + "|" + nextConcept.getCode())) {
theValueSetCodeAccumulator.excludeCode(nextConcept.getSystem(), nextConcept.getCode());
}
}
@ -717,9 +713,11 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
}
}
private void expandWithoutHibernateSearch(ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, ValueSet.ConceptSetComponent theInclude, String theSystem, boolean theAdd, AtomicInteger theCodeCounter) {
private void expandWithoutHibernateSearch(IValueSetCodeAccumulator theValueSetCodeAccumulator, Set<String> theAddedCodes, ValueSet.ConceptSetComponent theInclude, String theSystem, boolean theAdd, AtomicInteger theCodeCounter) {
ourLog.trace("Hibernate search is not enabled");
Validate.isTrue(theExpansionComponent.getParameter().isEmpty(), "Can not expand ValueSet with parameters - Hibernate Search is not enabled on this server.");
if (theValueSetCodeAccumulator instanceof ValueSetExpansionComponentWithCodeAccumulator) {
Validate.isTrue(((ValueSetExpansionComponentWithCodeAccumulator) theValueSetCodeAccumulator).getParameter().isEmpty(), "Can not expand ValueSet with parameters - Hibernate Search is not enabled on this server.");
}
Validate.isTrue(theInclude.getFilter().isEmpty(), "Can not expand ValueSet with filters - Hibernate Search is not enabled on this server.");
Validate.isTrue(isNotBlank(theSystem), "Can not expand ValueSet without explicit system - Hibernate Search is not enabled on this server.");
@ -727,7 +725,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
if (!theSystem.equals(theInclude.getSystem())) {
continue;
}
addCodeIfNotAlreadyAdded(theExpansionComponent, theAddedCodes, null, theAdd, theCodeCounter, theSystem, next.getCode(), next.getDisplay());
addCodeIfNotAlreadyAdded(theValueSetCodeAccumulator, theAddedCodes, null, theAdd, theCodeCounter, theSystem, next.getCode(), next.getDisplay());
}
}
@ -1038,14 +1036,6 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
}
private void removeCodeFromExpansion(String theCodeSystem, String theCode, ValueSet.ValueSetExpansionComponent theExpansionComponent) {
theExpansionComponent
.getContains()
.removeIf(t ->
theCodeSystem.equals(t.getSystem()) &&
theCode.equals(t.getCode()));
}
private int saveConcept(TermConcept theConcept) {
int retVal = 0;
@ -1144,7 +1134,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
List<TermCodeSystemVersion> existing = myCodeSystemVersionDao.findByCodeSystemResource(theCodeSystemResourcePid);
/*
* For now we always delete old versions.. At some point it would be nice to allow configuration to keep old versions
* For now we always delete old versions. At some point it would be nice to allow configuration to keep old versions.
*/
ourLog.info("Deleting old code system versions");
@ -1318,8 +1308,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
TermConceptMapGroupElement termConceptMapGroupElement;
for (ConceptMap.SourceElementComponent element : group.getElement()) {
if (isBlank(element.getCode())) {
// FIXME: JA - send this to an interceptor message so it can be
// output
// FIXME: JA - send this to an interceptor message so it can be output
continue;
}
termConceptMapGroupElement = new TermConceptMapGroupElement();
@ -1386,26 +1375,53 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
Optional<TermValueSet> optionalExistingTermValueSetByUrl = myValueSetDao.findByUrl(url);
if (!optionalExistingTermValueSetByUrl.isPresent()) {
myValueSetDao.save(termValueSet);
int codesSaved = 0;
int conceptsSaved = 0;
int designationsSaved = 0;
// FIXME: DM 2019-07-15 - Here we should call expandValueSet(ValueSet theValueSetToExpand, IValueSetCodeAccumulator theValueSetCodeAccumulator).
// FIXME: DM 2019-07-15 - We need an implementation IValueSetCodeAccumulator that saves ValueSetConcept records and their children.
ValueSet expandedValueSet = expandValueSet(theValueSet);
if (expandedValueSet.hasExpansion()) {
if (expandedValueSet.getExpansion().hasTotal() && expandedValueSet.getExpansion().getTotal() > 0) {
TermValueSetCode code;
TermValueSetConcept concept;
for (ValueSet.ValueSetExpansionContainsComponent contains : expandedValueSet.getExpansion().getContains()) {
ValidateUtil.isNotBlankOrThrowInvalidRequest(contains.getSystem(), "ValueSet contains a code with no system value");
ValidateUtil.isNotBlankOrThrowInvalidRequest(contains.getCode(), "ValueSet contains a code with no code value");
ValidateUtil.isNotBlankOrThrowInvalidRequest(contains.getSystem(), "ValueSet contains a concept with no system value");
ValidateUtil.isNotBlankOrThrowInvalidRequest(contains.getCode(), "ValueSet contains a concept with no code value");
code = new TermValueSetCode();
code.setValueSet(termValueSet);
code.setSystem(contains.getSystem());
code.setCode(contains.getCode());
code.setDisplay(contains.hasDisplay() ? contains.getDisplay() : null);
myValueSetCodeDao.save(code);
concept = new TermValueSetConcept();
concept.setValueSet(termValueSet);
concept.setSystem(contains.getSystem());
concept.setCode(contains.getCode());
concept.setDisplay(contains.hasDisplay() ? contains.getDisplay() : null);
myValueSetConceptDao.save(concept);
if (codesSaved++ % 250 == 0) {
ourLog.info("Have pre-expanded {} codes in ValueSet", codesSaved);
myValueSetCodeDao.flush();
TermValueSetConceptDesignation designation;
for (ValueSet.ConceptReferenceDesignationComponent containedDesignation : contains.getDesignation()) {
ValidateUtil.isNotBlankOrThrowInvalidRequest(containedDesignation.getValue(), "ValueSet contains a concept designation with no value");
designation = new TermValueSetConceptDesignation();
designation.setConcept(concept);
designation.setLanguage(containedDesignation.hasLanguage() ? containedDesignation.getLanguage() : null);
if (containedDesignation.hasUse()) {
designation.setUseSystem(containedDesignation.getUse().hasSystem() ? containedDesignation.getUse().getSystem() : null);
designation.setUseCode(containedDesignation.getUse().hasCode() ? containedDesignation.getUse().getCode() : null);
designation.setUseDisplay(containedDesignation.getUse().hasDisplay() ? containedDesignation.getUse().getDisplay() : null);
}
designation.setValue(containedDesignation.getValue());
myValueSetConceptDesignationDao.save(designation);
if (designationsSaved++ % 250 == 0) {
ourLog.info("Have pre-expanded {} designations in ValueSet", designationsSaved);
myValueSetConceptDesignationDao.flush();
}
}
// TODO: DM 2019-07-16 - We need TermValueSetConceptProperty, similar to TermConceptProperty.
// TODO: DM 2019-07-16 - We should also populate TermValueSetConceptProperty entities here.
if (conceptsSaved++ % 250 == 0) {
ourLog.info("Have pre-expanded {} concepts in ValueSet", conceptsSaved);
myValueSetConceptDao.flush();
}
}
}

View File

@ -86,6 +86,11 @@ public class HapiTerminologySvcDstu2 extends BaseHapiTerminologySvcImpl {
throw new UnsupportedOperationException();
}
@Override
public void expandValueSet(IBaseResource theValueSetToExpand, IValueSetCodeAccumulator theValueSetCodeAccumulator) {
throw new UnsupportedOperationException();
}
@Override
public List<VersionIndependentConcept> expandValueSet(String theValueSet) {
throw new UnsupportedOperationException();

View File

@ -25,8 +25,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Optional;
import static org.apache.commons.lang3.StringUtils.isBlank;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
import static org.apache.commons.lang3.StringUtils.*;
/*
* #%L
@ -171,6 +170,19 @@ public class HapiTerminologySvcDstu3 extends BaseHapiTerminologySvcImpl implemen
}
}
@Override
public void expandValueSet(IBaseResource theValueSetToExpand, IValueSetCodeAccumulator theValueSetCodeAccumulator) {
ValueSet valueSetToExpand = (ValueSet) theValueSetToExpand;
try {
org.hl7.fhir.r4.model.ValueSet valueSetToExpandR4;
valueSetToExpandR4 = VersionConvertor_30_40.convertValueSet(valueSetToExpand);
super.expandValueSet(valueSetToExpandR4, theValueSetCodeAccumulator);
} catch (FHIRException e) {
throw new InternalErrorException(e);
}
}
@Override
public List<VersionIndependentConcept> expandValueSet(String theValueSet) {
ValueSet vs = myValidationSupport.fetchResource(myContext, ValueSet.class, theValueSet);

View File

@ -131,6 +131,11 @@ public class HapiTerminologySvcR4 extends BaseHapiTerminologySvcImpl implements
return super.expandValueSet(valueSetToExpand);
}
@Override
public void expandValueSet(IBaseResource theValueSetToExpand, IValueSetCodeAccumulator theValueSetCodeAccumulator) {
ValueSet valueSetToExpand = (ValueSet) theValueSetToExpand;
super.expandValueSet(valueSetToExpand, theValueSetCodeAccumulator);
}
@Override
public ValueSetExpander.ValueSetExpansionOutcome expandValueSet(FhirContext theContext, ConceptSetComponent theInclude) {

View File

@ -41,11 +41,15 @@ public interface IHapiTerminologySvc {
ValueSet expandValueSet(ValueSet theValueSetToExpand);
void expandValueSet(ValueSet theValueSetToExpand, IValueSetCodeAccumulator theValueSetCodeAccumulator);
/**
* Version independent
*/
IBaseResource expandValueSet(IBaseResource theValueSetToExpand);
void expandValueSet(IBaseResource theValueSetToExpand, IValueSetCodeAccumulator theValueSetCodeAccumulator);
List<VersionIndependentConcept> expandValueSet(String theValueSet);
Optional<TermConcept> findCode(String theCodeSystem, String theCode);

View File

@ -0,0 +1,35 @@
package ca.uhn.fhir.jpa.term;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 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.entity.TermConceptDesignation;
import java.util.Collection;
public interface IValueSetCodeAccumulator {
void includeCode(String theSystem, String theCode, String theDisplay);
void includeCodeWithDesignations(String theSystem, String theCode, String theDisplay, Collection<TermConceptDesignation> theDesignations);
void excludeCode(String theSystem, String theCode);
}

View File

@ -0,0 +1,65 @@
package ca.uhn.fhir.jpa.term;
/*
* #%L
* HAPI FHIR JPA Server
* %%
* Copyright (C) 2014 - 2019 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.entity.TermConceptDesignation;
import org.hl7.fhir.r4.model.ValueSet;
import java.util.Collection;
public class ValueSetExpansionComponentWithCodeAccumulator extends ValueSet.ValueSetExpansionComponent implements IValueSetCodeAccumulator {
@Override
public void includeCode(String theSystem, String theCode, String theDisplay) {
ValueSet.ValueSetExpansionContainsComponent contains = this.addContains();
contains.setSystem(theSystem);
contains.setCode(theCode);
contains.setDisplay(theDisplay);
}
@Override
public void includeCodeWithDesignations(String theSystem, String theCode, String theDisplay, Collection<TermConceptDesignation> theDesignations) {
ValueSet.ValueSetExpansionContainsComponent contains = this.addContains();
contains.setSystem(theSystem);
contains.setCode(theCode);
contains.setDisplay(theDisplay);
if (theDesignations != null) {
for (TermConceptDesignation termConceptDesignation : theDesignations) {
contains
.addDesignation()
.setValue(termConceptDesignation.getValue())
.setLanguage(termConceptDesignation.getLanguage())
.getUse()
.setSystem(termConceptDesignation.getUseSystem())
.setCode(termConceptDesignation.getUseCode())
.setDisplay(termConceptDesignation.getUseDisplay());
}
}
}
@Override
public void excludeCode(String theSystem, String theCode) {
this
.getContains()
.removeIf(t ->
theSystem.equals(t.getSystem()) &&
theCode.equals(t.getCode()));
}
}

View File

@ -16,7 +16,6 @@ import ca.uhn.fhir.jpa.search.IStaleSearchDeletingSvc;
import ca.uhn.fhir.jpa.search.reindex.IResourceReindexingSvc;
import ca.uhn.fhir.jpa.searchparam.registry.ISearchParamRegistry;
import ca.uhn.fhir.jpa.sp.ISearchParamPresenceSvc;
import ca.uhn.fhir.jpa.subscription.SubscriptionInterceptorLoader;
import ca.uhn.fhir.jpa.term.BaseHapiTerminologySvcImpl;
import ca.uhn.fhir.jpa.term.IHapiTerminologySvc;
import ca.uhn.fhir.jpa.util.ResourceCountCache;
@ -27,7 +26,6 @@ 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;
@ -59,7 +57,6 @@ import java.io.InputStream;
import java.util.Map;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {TestDstu3Config.class})
@ -240,6 +237,10 @@ public abstract class BaseJpaDstu3Test extends BaseJpaTest {
@Qualifier("myTaskDaoDstu3")
protected IFhirResourceDao<Task> myTaskDao;
@Autowired
protected ITermConceptDao myTermConceptDao;
@Autowired
protected ITermCodeSystemDao myTermCodeSystemDao;
@Autowired
protected IHapiTerminologySvc myTermSvc;
@Autowired
protected PlatformTransactionManager myTransactionMgr;

View File

@ -130,6 +130,8 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
@Autowired
protected ITermConceptDao myTermConceptDao;
@Autowired
protected ITermConceptDesignationDao myTermConceptDesignationDao;
@Autowired
@Qualifier("myConditionDaoR4")
protected IFhirResourceDao<Condition> myConditionDao;
@Autowired
@ -293,7 +295,9 @@ public abstract class BaseJpaR4Test extends BaseJpaTest {
@Autowired
protected ITermValueSetDao myTermValueSetDao;
@Autowired
protected ITermValueSetCodeDao myTermValueSetCodeDao;
protected ITermValueSetConceptDao myTermValueSetConceptDao;
@Autowired
protected ITermValueSetConceptDesignationDao myTermValueSetConceptDesignationDao;
@Autowired
protected ITermConceptMapDao myTermConceptMapDao;
@Autowired

View File

@ -1,12 +1,12 @@
package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.data.ITermCodeSystemDao;
import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
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.TermConceptParentChildLink.RelationshipTypeEnum;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.server.exceptions.InvalidRequestException;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
@ -19,7 +19,11 @@ import org.hl7.fhir.r4.model.ValueSet;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
import java.util.ArrayList;
import java.util.List;
@ -30,11 +34,13 @@ import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
private static final Logger ourLog = LoggerFactory.getLogger(TerminologySvcImplDstu3Test.class);
private static final String CS_URL = "http://example.com/my_code_system";
private static final String CS_URL_2 = "http://example.com/my_code_system2";
@Autowired
private ITermCodeSystemDao myTermCodeSystemDao;
private IIdType myExtensionalCsId;
private IIdType myExtensionalVsId;
@After
public void after() {
@ -76,6 +82,7 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
childAAB.addPropertyString("propA", "valueAAB");
childAAB.addPropertyString("propB", "foo");
childAAB.addDesignation()
.setLanguage("D1L")
.setUseSystem("D1S")
.setUseCode("D1C")
.setUseDisplay("D1D")
@ -546,13 +553,13 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
assertEquals("childAAB", concept.getCode());
assertEquals("http://example.com/my_code_system", concept.getSystem());
assertEquals(null, concept.getDisplay());
assertEquals("D1L", concept.getDesignation().get(0).getLanguage());
assertEquals("D1S", concept.getDesignation().get(0).getUse().getSystem());
assertEquals("D1C", concept.getDesignation().get(0).getUse().getCode());
assertEquals("D1D", concept.getDesignation().get(0).getUse().getDisplay());
assertEquals("D1V", concept.getDesignation().get(0).getValue());
}
@Test
public void testStoreCodeSystemInvalidCyclicLoop() {
CodeSystem codeSystem = new CodeSystem();
@ -585,6 +592,103 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
}
}
@Test
public void testStoreTermCodeSystemAndNestedChildren() {
IIdType codeSystemId = createCodeSystem();
CodeSystem codeSystemResource = myCodeSystemDao.read(codeSystemId);
ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystemResource));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
TermCodeSystem codeSystem = myTermCodeSystemDao.findByResourcePid(codeSystemId.getIdPartAsLong());
assertEquals(CS_URL, codeSystem.getCodeSystemUri());
assertEquals("SYSTEM NAME", codeSystem.getName());
TermCodeSystemVersion codeSystemVersion = codeSystem.getCurrentVersion();
assertEquals(9, codeSystemVersion.getConcepts().size());
List<TermConcept> concepts = myTermConceptDao.findByCodeSystemVersion(codeSystemVersion);
TermConcept parentWithNoChildrenA = concepts.get(0);
assertEquals("ParentWithNoChildrenA", parentWithNoChildrenA.getCode());
assertNull(parentWithNoChildrenA.getDisplay());
assertEquals(0, parentWithNoChildrenA.getChildren().size());
assertEquals(0, parentWithNoChildrenA.getParents().size());
assertEquals(0, parentWithNoChildrenA.getDesignations().size());
assertEquals(0, parentWithNoChildrenA.getProperties().size());
TermConcept parentWithNoChildrenB = concepts.get(1);
assertEquals("ParentWithNoChildrenB", parentWithNoChildrenB.getCode());
assertNull(parentWithNoChildrenB.getDisplay());
assertEquals(0, parentWithNoChildrenB.getChildren().size());
assertEquals(0, parentWithNoChildrenB.getParents().size());
assertEquals(0, parentWithNoChildrenB.getDesignations().size());
assertEquals(0, parentWithNoChildrenB.getProperties().size());
TermConcept parentWithNoChildrenC = concepts.get(2);
assertEquals("ParentWithNoChildrenC", parentWithNoChildrenC.getCode());
assertNull(parentWithNoChildrenC.getDisplay());
assertEquals(0, parentWithNoChildrenC.getChildren().size());
assertEquals(0, parentWithNoChildrenC.getParents().size());
assertEquals(0, parentWithNoChildrenC.getDesignations().size());
assertEquals(0, parentWithNoChildrenC.getProperties().size());
TermConcept parentA = concepts.get(3);
assertEquals("ParentA", parentA.getCode());
assertNull(parentA.getDisplay());
assertEquals(2, parentA.getChildren().size());
assertEquals(0, parentA.getParents().size());
assertEquals(0, parentA.getDesignations().size());
assertEquals(0, parentA.getProperties().size());
TermConcept childAA = concepts.get(4);
assertEquals("childAA", childAA.getCode());
assertNull(childAA.getDisplay());
assertEquals(2, childAA.getChildren().size());
assertEquals(1, childAA.getParents().size());
assertSame(parentA, childAA.getParents().iterator().next().getParent());
assertEquals(0, childAA.getDesignations().size());
assertEquals(0, childAA.getProperties().size());
TermConcept childAAA = concepts.get(5);
assertEquals("childAAA", childAAA.getCode());
assertNull(childAAA.getDisplay());
assertEquals(0, childAAA.getChildren().size());
assertEquals(1, childAAA.getParents().size());
assertSame(childAA, childAAA.getParents().iterator().next().getParent());
assertEquals(0, childAAA.getDesignations().size());
assertEquals(2, childAAA.getProperties().size());
TermConcept childAAB = concepts.get(6);
assertEquals("childAAB", childAAB.getCode());
assertNull(childAAB.getDisplay());
assertEquals(0, childAAB.getChildren().size());
assertEquals(1, childAAB.getParents().size());
assertSame(childAA, childAAB.getParents().iterator().next().getParent());
assertEquals(1, childAAB.getDesignations().size());
assertEquals(2, childAAB.getProperties().size());
TermConcept childAB = concepts.get(7);
assertEquals("childAB", childAB.getCode());
assertNull(childAB.getDisplay());
assertEquals(0, childAB.getChildren().size());
assertEquals(1, childAB.getParents().size());
assertSame(parentA, childAB.getParents().iterator().next().getParent());
assertEquals(0, childAB.getDesignations().size());
assertEquals(0, childAB.getProperties().size());
TermConcept parentB = concepts.get(8);
assertEquals("ParentB", parentB.getCode());
assertNull(parentB.getDisplay());
assertEquals(0, parentB.getChildren().size());
assertEquals(0, parentB.getParents().size());
assertEquals(0, parentB.getDesignations().size());
assertEquals(0, parentB.getProperties().size());
}
});
}
/**
* Check that a custom ValueSet against a custom CodeSystem expands correctly
*/

View File

@ -3,6 +3,7 @@ package ca.uhn.fhir.jpa.term;
import ca.uhn.fhir.jpa.dao.DaoConfig;
import ca.uhn.fhir.jpa.dao.r4.BaseJpaR4Test;
import ca.uhn.fhir.jpa.entity.*;
import ca.uhn.fhir.jpa.model.entity.ResourceTable;
import ca.uhn.fhir.rest.server.exceptions.UnprocessableEntityException;
import ca.uhn.fhir.util.TestUtil;
import org.hl7.fhir.instance.model.api.IIdType;
@ -10,6 +11,7 @@ import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.r4.model.Enumerations.ConceptMapEquivalence;
import org.junit.*;
import org.junit.rules.ExpectedException;
import org.mockito.Mock;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.transaction.TransactionStatus;
@ -21,6 +23,9 @@ import java.util.List;
import java.util.Optional;
import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
public class TerminologySvcImplR4Test extends BaseJpaR4Test {
private static final Logger ourLog = LoggerFactory.getLogger(TerminologySvcImplR4Test.class);
@ -30,6 +35,9 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
private IIdType myExtensionalCsId;
private IIdType myExtensionalVsId;
@Mock
IValueSetCodeAccumulator myValueSetCodeAccumulator;
@Before
public void before() {
myDaoConfig.setAllowExternalReferences(true);
@ -40,6 +48,57 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
myDaoConfig.setAllowExternalReferences(new DaoConfig().isAllowExternalReferences());
myDaoConfig.setPreExpandValueSetsExperimental(new DaoConfig().isPreExpandValueSetsExperimental());
}
private IIdType createCodeSystem() {
CodeSystem codeSystem = new CodeSystem();
codeSystem.setUrl(CS_URL);
codeSystem.setContent(CodeSystem.CodeSystemContentMode.NOTPRESENT);
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new);
TermCodeSystemVersion cs = new TermCodeSystemVersion();
cs.setResource(table);
TermConcept parent;
parent = new TermConcept(cs, "ParentWithNoChildrenA");
cs.getConcepts().add(parent);
parent = new TermConcept(cs, "ParentWithNoChildrenB");
cs.getConcepts().add(parent);
parent = new TermConcept(cs, "ParentWithNoChildrenC");
cs.getConcepts().add(parent);
TermConcept parentA = new TermConcept(cs, "ParentA");
cs.getConcepts().add(parentA);
TermConcept childAA = new TermConcept(cs, "childAA");
parentA.addChild(childAA, TermConceptParentChildLink.RelationshipTypeEnum.ISA);
TermConcept childAAA = new TermConcept(cs, "childAAA");
childAAA.addPropertyString("propA", "valueAAA");
childAAA.addPropertyString("propB", "foo");
childAA.addChild(childAAA, TermConceptParentChildLink.RelationshipTypeEnum.ISA);
TermConcept childAAB = new TermConcept(cs, "childAAB");
childAAB.addPropertyString("propA", "valueAAB");
childAAB.addPropertyString("propB", "foo");
childAAB.addDesignation()
.setUseSystem("D1S")
.setUseCode("D1C")
.setUseDisplay("D1D")
.setValue("D1V");
childAA.addChild(childAAB, TermConceptParentChildLink.RelationshipTypeEnum.ISA);
TermConcept childAB = new TermConcept(cs, "childAB");
parentA.addChild(childAB, TermConceptParentChildLink.RelationshipTypeEnum.ISA);
TermConcept parentB = new TermConcept(cs, "ParentB");
cs.getConcepts().add(parentB);
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs);
return id;
}
private void createAndPersistConceptMap() {
ConceptMap conceptMap = createConceptMap();
@ -60,11 +119,21 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
loadAndPersistValueSet();
}
private void loadAndPersistCodeSystemAndValueSetWithDesignations() throws IOException {
loadAndPersistCodeSystemWithDesignations();
loadAndPersistValueSet();
}
private void loadAndPersistCodeSystem() throws IOException {
CodeSystem codeSystem = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs.xml");
persistCodeSystem(codeSystem);
}
private void loadAndPersistCodeSystemWithDesignations() throws IOException {
CodeSystem codeSystem = loadResourceFromClasspath(CodeSystem.class, "/extensional-case-3-cs-with-designations.xml");
persistCodeSystem(codeSystem);
}
private void persistCodeSystem(CodeSystem theCodeSystem) {
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
@ -202,6 +271,173 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
loadAndPersistValueSet();
}
@Test
public void testExpandValueSetWithValueSetCodeAccumulator() {
createCodeSystem();
ValueSet vs = new ValueSet();
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
include.setSystem(CS_URL);
myTermSvc.expandValueSet(vs, myValueSetCodeAccumulator);
verify(myValueSetCodeAccumulator, times(9)).includeCodeWithDesignations(anyString(), anyString(), nullable(String.class), anyCollection());
}
@Test
public void testStoreTermCodeSystemAndChildren() throws Exception {
loadAndPersistCodeSystemWithDesignations();
CodeSystem codeSystem = myCodeSystemDao.read(myExtensionalCsId);
ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
TermCodeSystem codeSystem = myTermCodeSystemDao.findByResourcePid(myExtensionalCsId.getIdPartAsLong());
assertEquals("http://acme.org", codeSystem.getCodeSystemUri());
assertNull(codeSystem.getName());
TermCodeSystemVersion codeSystemVersion = codeSystem.getCurrentVersion();
assertEquals(24, codeSystemVersion.getConcepts().size());
List<TermConcept> concepts = myTermConceptDao.findByCodeSystemVersion(codeSystemVersion);
TermConcept concept = concepts.get(0);
assertEquals("8450-9", concept.getCode());
assertEquals("Systolic blood pressure--expiration", concept.getDisplay());
assertEquals(1, concept.getDesignations().size());
TermConceptDesignation designation = concept.getDesignations().iterator().next();
assertEquals("nl", designation.getLanguage());
assertEquals("http://snomed.info/sct", designation.getUseSystem());
assertEquals("900000000000013009", designation.getUseCode());
assertEquals("Synonym", designation.getUseDisplay());
assertEquals("Systolische bloeddruk - expiratie", designation.getValue());
concept = concepts.get(1);
assertEquals("11378-7", concept.getCode());
assertEquals("Systolic blood pressure at First encounter", concept.getDisplay());
assertEquals(0, concept.getDesignations().size());
// ...
concept = concepts.get(22);
assertEquals("8491-3", concept.getCode());
assertEquals("Systolic blood pressure 1 hour minimum", concept.getDisplay());
assertEquals(1, concept.getDesignations().size());
designation = concept.getDesignations().iterator().next();
assertEquals("nl", designation.getLanguage());
assertEquals("http://snomed.info/sct", designation.getUseSystem());
assertEquals("900000000000013009", designation.getUseCode());
assertEquals("Synonym", designation.getUseDisplay());
assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue());
concept = concepts.get(23);
assertEquals("8492-1", concept.getCode());
assertEquals("Systolic blood pressure 8 hour minimum", concept.getDisplay());
assertEquals(0, concept.getDesignations().size());
}
});
}
@Test
public void testStoreTermCodeSystemAndNestedChildren() {
IIdType codeSystemId = createCodeSystem();
CodeSystem codeSystemResource = myCodeSystemDao.read(codeSystemId);
ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystemResource));
new TransactionTemplate(myTxManager).execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus theStatus) {
TermCodeSystem codeSystem = myTermCodeSystemDao.findByResourcePid(codeSystemId.getIdPartAsLong());
assertEquals(CS_URL, codeSystem.getCodeSystemUri());
assertEquals("SYSTEM NAME", codeSystem.getName());
TermCodeSystemVersion codeSystemVersion = codeSystem.getCurrentVersion();
assertEquals(9, codeSystemVersion.getConcepts().size());
List<TermConcept> concepts = myTermConceptDao.findByCodeSystemVersion(codeSystemVersion);
TermConcept parentWithNoChildrenA = concepts.get(0);
assertEquals("ParentWithNoChildrenA", parentWithNoChildrenA.getCode());
assertNull(parentWithNoChildrenA.getDisplay());
assertEquals(0, parentWithNoChildrenA.getChildren().size());
assertEquals(0, parentWithNoChildrenA.getParents().size());
assertEquals(0, parentWithNoChildrenA.getDesignations().size());
assertEquals(0, parentWithNoChildrenA.getProperties().size());
TermConcept parentWithNoChildrenB = concepts.get(1);
assertEquals("ParentWithNoChildrenB", parentWithNoChildrenB.getCode());
assertNull(parentWithNoChildrenB.getDisplay());
assertEquals(0, parentWithNoChildrenB.getChildren().size());
assertEquals(0, parentWithNoChildrenB.getParents().size());
assertEquals(0, parentWithNoChildrenB.getDesignations().size());
assertEquals(0, parentWithNoChildrenB.getProperties().size());
TermConcept parentWithNoChildrenC = concepts.get(2);
assertEquals("ParentWithNoChildrenC", parentWithNoChildrenC.getCode());
assertNull(parentWithNoChildrenC.getDisplay());
assertEquals(0, parentWithNoChildrenC.getChildren().size());
assertEquals(0, parentWithNoChildrenC.getParents().size());
assertEquals(0, parentWithNoChildrenC.getDesignations().size());
assertEquals(0, parentWithNoChildrenC.getProperties().size());
TermConcept parentA = concepts.get(3);
assertEquals("ParentA", parentA.getCode());
assertNull(parentA.getDisplay());
assertEquals(2, parentA.getChildren().size());
assertEquals(0, parentA.getParents().size());
assertEquals(0, parentA.getDesignations().size());
assertEquals(0, parentA.getProperties().size());
TermConcept childAA = concepts.get(4);
assertEquals("childAA", childAA.getCode());
assertNull(childAA.getDisplay());
assertEquals(2, childAA.getChildren().size());
assertEquals(1, childAA.getParents().size());
assertSame(parentA, childAA.getParents().iterator().next().getParent());
assertEquals(0, childAA.getDesignations().size());
assertEquals(0, childAA.getProperties().size());
TermConcept childAAA = concepts.get(5);
assertEquals("childAAA", childAAA.getCode());
assertNull(childAAA.getDisplay());
assertEquals(0, childAAA.getChildren().size());
assertEquals(1, childAAA.getParents().size());
assertSame(childAA, childAAA.getParents().iterator().next().getParent());
assertEquals(0, childAAA.getDesignations().size());
assertEquals(2, childAAA.getProperties().size());
TermConcept childAAB = concepts.get(6);
assertEquals("childAAB", childAAB.getCode());
assertNull(childAAB.getDisplay());
assertEquals(0, childAAB.getChildren().size());
assertEquals(1, childAAB.getParents().size());
assertSame(childAA, childAAB.getParents().iterator().next().getParent());
assertEquals(1, childAAB.getDesignations().size());
assertEquals(2, childAAB.getProperties().size());
TermConcept childAB = concepts.get(7);
assertEquals("childAB", childAB.getCode());
assertNull(childAB.getDisplay());
assertEquals(0, childAB.getChildren().size());
assertEquals(1, childAB.getParents().size());
assertSame(parentA, childAB.getParents().iterator().next().getParent());
assertEquals(0, childAB.getDesignations().size());
assertEquals(0, childAB.getProperties().size());
TermConcept parentB = concepts.get(8);
assertEquals("ParentB", parentB.getCode());
assertNull(parentB.getDisplay());
assertEquals(0, parentB.getChildren().size());
assertEquals(0, parentB.getParents().size());
assertEquals(0, parentB.getDesignations().size());
assertEquals(0, parentB.getProperties().size());
}
});
}
@Test
public void testStoreTermConceptMapAndChildren() {
createAndPersistConceptMap();
@ -384,7 +620,7 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
public void testStoreTermValueSetAndChildren() throws Exception {
myDaoConfig.setPreExpandValueSetsExperimental(true);
loadAndPersistCodeSystemAndValueSet();
loadAndPersistCodeSystemAndValueSetWithDesignations();
CodeSystem codeSystem = myCodeSystemDao.read(myExtensionalCsId);
ourLog.info("CodeSystem:\n" + myFhirCtx.newJsonParser().setPrettyPrint(true).encodeResourceToString(codeSystem));
@ -406,33 +642,51 @@ public class TerminologySvcImplR4Test extends BaseJpaR4Test {
ourLog.info("ValueSet:\n" + valueSet.toString());
assertEquals("http://www.healthintersections.com.au/fhir/ValueSet/extensional-case-2", valueSet.getUrl());
assertEquals("Terminology Services Connectation #1 Extensional case #2", valueSet.getName());
assertEquals(codeSystem.getConcept().size(), valueSet.getCodes().size());
assertEquals(codeSystem.getConcept().size(), valueSet.getConcepts().size());
TermValueSetCode code = valueSet.getCodes().get(0);
ourLog.info("Code:\n" + code.toString());
assertEquals("http://acme.org", code.getSystem());
assertEquals("8450-9", code.getCode());
assertEquals("Systolic blood pressure--expiration", code.getDisplay());
TermValueSetConcept concept = valueSet.getConcepts().get(0);
ourLog.info("Code:\n" + concept.toString());
assertEquals("http://acme.org", concept.getSystem());
assertEquals("8450-9", concept.getCode());
assertEquals("Systolic blood pressure--expiration", concept.getDisplay());
assertEquals(1, concept.getDesignations().size());
code = valueSet.getCodes().get(1);
ourLog.info("Code:\n" + code.toString());
assertEquals("http://acme.org", code.getSystem());
assertEquals("11378-7", code.getCode());
assertEquals("Systolic blood pressure at First encounter", code.getDisplay());
TermValueSetConceptDesignation designation = concept.getDesignations().get(0);
assertEquals("nl", designation.getLanguage());
assertEquals("http://snomed.info/sct", designation.getUseSystem());
assertEquals("900000000000013009", designation.getUseCode());
assertEquals("Synonym", designation.getUseDisplay());
assertEquals("Systolische bloeddruk - expiratie", designation.getValue());
concept = valueSet.getConcepts().get(1);
ourLog.info("Code:\n" + concept.toString());
assertEquals("http://acme.org", concept.getSystem());
assertEquals("11378-7", concept.getCode());
assertEquals("Systolic blood pressure at First encounter", concept.getDisplay());
assertEquals(0, concept.getDesignations().size());
// ...
code = valueSet.getCodes().get(22);
ourLog.info("Code:\n" + code.toString());
assertEquals("http://acme.org", code.getSystem());
assertEquals("8491-3", code.getCode());
assertEquals("Systolic blood pressure 1 hour minimum", code.getDisplay());
concept = valueSet.getConcepts().get(22);
ourLog.info("Code:\n" + concept.toString());
assertEquals("http://acme.org", concept.getSystem());
assertEquals("8491-3", concept.getCode());
assertEquals("Systolic blood pressure 1 hour minimum", concept.getDisplay());
assertEquals(1, concept.getDesignations().size());
code = valueSet.getCodes().get(23);
ourLog.info("Code:\n" + code.toString());
assertEquals("http://acme.org", code.getSystem());
assertEquals("8492-1", code.getCode());
assertEquals("Systolic blood pressure 8 hour minimum", code.getDisplay());
designation = concept.getDesignations().get(0);
assertEquals("nl", designation.getLanguage());
assertEquals("http://snomed.info/sct", designation.getUseSystem());
assertEquals("900000000000013009", designation.getUseCode());
assertEquals("Synonym", designation.getUseDisplay());
assertEquals("Systolische bloeddruk minimaal 1 uur", designation.getValue());
concept = valueSet.getConcepts().get(23);
ourLog.info("Code:\n" + concept.toString());
assertEquals("http://acme.org", concept.getSystem());
assertEquals("8492-1", concept.getCode());
assertEquals("Systolic blood pressure 8 hour minimum", concept.getDisplay());
assertEquals(0, concept.getDesignations().size());
}
});
}

View File

@ -0,0 +1,117 @@
<CodeSystem xmlns="http://hl7.org/fhir">
<url value="http://acme.org" />
<concept>
<code value="8450-9" />
<display value="Systolic blood pressure--expiration" />
<designation>
<language value="nl"/>
<use>
<system value="http://snomed.info/sct"/>
<code value="900000000000013009"/>
<display value="Synonym"/>
</use>
<value value="Systolische bloeddruk - expiratie"/>
</designation>
</concept>
<concept>
<code value="11378-7" />
<display value="Systolic blood pressure at First encounter" />
</concept>
<concept>
<code value="8493-9" />
<display value="Systolic blood pressure 10 hour minimum" />
</concept>
<concept>
<code value="8494-7" />
<display value="Systolic blood pressure 12 hour minimum" />
</concept>
<concept>
<code value="8495-4" />
<display value="Systolic blood pressure 24 hour minimum" />
</concept>
<concept>
<code value="8451-7" />
<display value="Systolic blood pressure--inspiration" />
</concept>
<concept>
<code value="8452-5" />
<display value="Systolic blood pressure.inspiration - expiration" />
</concept>
<concept>
<code value="8459-0" />
<display value="Systolic blood pressure--sitting" />
</concept>
<concept>
<code value="8460-8" />
<display value="Systolic blood pressure--standing" />
</concept>
<concept>
<code value="8461-6" />
<display value="Systolic blood pressure--supine" />
</concept>
<concept>
<code value="8479-8" />
<display value="Systolic blood pressure by palpation" />
</concept>
<concept>
<code value="8480-6" />
<display value="Systolic blood pressure" />
</concept>
<concept>
<code value="8481-4" />
<display value="Systolic blood pressure 1 hour maximum" />
</concept>
<concept>
<code value="8482-2" />
<display value="Systolic blood pressure 8 hour maximum" />
</concept>
<concept>
<code value="8483-0" />
<display value="Systolic blood pressure 10 hour maximum" />
</concept>
<concept>
<code value="8484-8" />
<display value="Systolic blood pressure 12 hour maximum" />
</concept>
<concept>
<code value="8485-5" />
<display value="Systolic blood pressure 24 hour maximum" />
</concept>
<concept>
<code value="8486-3" />
<display value="Systolic blood pressure 1 hour mean" />
</concept>
<concept>
<code value="8487-1" />
<display value="Systolic blood pressure 8 hour mean" />
</concept>
<concept>
<code value="8488-9" />
<display value="Systolic blood pressure 10 hour mean" />
</concept>
<concept>
<code value="8489-7" />
<display value="Systolic blood pressure 12 hour mean" />
</concept>
<concept>
<code value="8490-5" />
<display value="Systolic blood pressure 24 hour mean" />
</concept>
<concept>
<code value="8491-3" />
<display value="Systolic blood pressure 1 hour minimum" />
<designation>
<language value="nl"/>
<use>
<system value="http://snomed.info/sct"/>
<code value="900000000000013009"/>
<display value="Synonym"/>
</use>
<value value="Systolische bloeddruk minimaal 1 uur"/>
</designation>
</concept>
<concept>
<code value="8492-1" />
<display value="Systolic blood pressure 8 hour minimum" />
</concept>
</CodeSystem>

View File

@ -23,6 +23,7 @@ package ca.uhn.fhir.jpa.migrate.tasks;
import ca.uhn.fhir.jpa.entity.TermCodeSystem;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermValueSet;
import ca.uhn.fhir.jpa.entity.TermValueSetConceptDesignation;
import ca.uhn.fhir.jpa.migrate.DriverTypeEnum;
import ca.uhn.fhir.jpa.migrate.taskdef.AddColumnTask;
import ca.uhn.fhir.jpa.migrate.taskdef.ArbitrarySqlTask;
@ -96,23 +97,43 @@ public class HapiFhirJpaMigrationTasks extends BaseMigrationTasks<VersionEnum> {
.references("HFJ_RESOURCE", "RES_ID");
termValueSetTable.addColumn("NAME").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, TermValueSet.MAX_NAME_LENGTH);
// TermValueSetCode
version.startSectionWithMessage("Processing table: TRM_VALUESET_CODE");
version.addIdGenerator("SEQ_VALUESET_CODE_PID");
Builder.BuilderAddTableByColumns termValueSetCodeTable = version.addTableByColumns("TRM_VALUESET_CODE", "PID");
termValueSetCodeTable.addColumn("PID").nonNullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.LONG);
termValueSetCodeTable.addColumn("VALUESET_PID").nonNullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.LONG);
termValueSetCodeTable
// TermValueSetConcept
version.startSectionWithMessage("Processing table: TRM_VALUESET_CONCEPT");
version.addIdGenerator("SEQ_VALUESET_CONCEPT_PID");
Builder.BuilderAddTableByColumns termValueSetConceptTable = version.addTableByColumns("TRM_VALUESET_CONCEPT", "PID");
termValueSetConceptTable.addColumn("PID").nonNullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.LONG);
termValueSetConceptTable.addColumn("VALUESET_PID").nonNullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.LONG);
termValueSetConceptTable
.addForeignKey("FK_TRM_VALUESET_PID")
.toColumn("VALUESET_PID")
.references("TRM_VALUESET", "PID");
termValueSetCodeTable.addColumn("SYSTEM").nonNullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, TermCodeSystem.MAX_URL_LENGTH);
termValueSetCodeTable.addColumn("CODE").nonNullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, TermConcept.MAX_CODE_LENGTH);
termValueSetCodeTable
.addIndex("IDX_VALUESET_CODE_CS_CD")
termValueSetConceptTable.addColumn("SYSTEM").nonNullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, TermCodeSystem.MAX_URL_LENGTH);
termValueSetConceptTable.addColumn("CODE").nonNullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, TermConcept.MAX_CODE_LENGTH);
termValueSetConceptTable
.addIndex("IDX_VALUESET_CONCEPT_CS_CD")
.unique(false)
.withColumns("SYSTEM", "CODE");
termValueSetCodeTable.addColumn("DISPLAY").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, TermConcept.MAX_DESC_LENGTH);
termValueSetConceptTable.addColumn("DISPLAY").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, TermConcept.MAX_DESC_LENGTH);
// TermValueSetConceptDesignation
version.startSectionWithMessage("Processing table: TRM_VALUESET_C_DESIGNATION");
version.addIdGenerator("SEQ_VALUESET_C_DSGNTN_PID");
Builder.BuilderAddTableByColumns termValueSetConceptDesignationTable = version.addTableByColumns("TRM_VALUESET_C_DESIGNATION", "PID");
termValueSetConceptDesignationTable.addColumn("PID").nonNullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.LONG);
termValueSetConceptDesignationTable.addColumn("VALUESET_CONCEPT_PID").nonNullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.LONG);
termValueSetConceptDesignationTable
.addForeignKey("FK_TRM_VALUESET_CONCEPT_PID")
.toColumn("VALUESET_CONCEPT_PID")
.references("TRM_VALUESET_CONCEPT", "PID");
termValueSetConceptDesignationTable.addColumn("LANG").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, TermValueSetConceptDesignation.MAX_LENGTH);
termValueSetConceptDesignationTable.addColumn("USE_SYSTEM").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, TermValueSetConceptDesignation.MAX_LENGTH);
termValueSetConceptDesignationTable.addColumn("USE_CODE").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, TermValueSetConceptDesignation.MAX_LENGTH);
termValueSetConceptDesignationTable.addColumn("USE_DISPLAY").nullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, TermValueSetConceptDesignation.MAX_LENGTH);
termValueSetConceptDesignationTable.addColumn("VAL").nonNullable().type(BaseTableColumnTypeTask.ColumnTypeEnum.STRING, TermValueSetConceptDesignation.MAX_LENGTH);
termValueSetConceptDesignationTable
.addIndex("IDX_VALUESET_C_DSGNTN_VAL")
.unique(false)
.withColumns("VAL");
}

View File

@ -289,6 +289,13 @@
content, which is more efficient than storing base 64 encoded text to represent
the binary as was previously done.
</action>
<action type="add">
Creating/updating CodeSystems now persist <![CDATA[<code>CodeSystem.concept.designation</code>]]> to
the terminology tables.
</action>
<action type="add">
Expanded ValueSets now populate <![CDATA[<code>ValueSet.expansion.contains.designation.language</code>]]>.
</action>
</release>
<release version="3.8.0" date="2019-05-30" description="Hippo">
<action type="fix">