ValueSet enhancements
This commit is contained in:
parent
218937e425
commit
2dc445fd10
|
@ -36,19 +36,19 @@ import ca.uhn.fhir.jpa.entity.Search;
|
|||
public interface ISearchDao extends JpaRepository<Search, Long> {
|
||||
|
||||
@Query("SELECT s FROM Search s WHERE s.myUuid = :uuid")
|
||||
public Search findByUuid(@Param("uuid") String theUuid);
|
||||
Search findByUuid(@Param("uuid") String theUuid);
|
||||
|
||||
@Query("SELECT s.myId FROM Search s WHERE s.mySearchLastReturned < :cutoff")
|
||||
public Slice<Long> findWhereLastReturnedBefore(@Param("cutoff") Date theCutoff, Pageable thePage);
|
||||
Slice<Long> findWhereLastReturnedBefore(@Param("cutoff") Date theCutoff, Pageable thePage);
|
||||
|
||||
// @Query("SELECT s FROM Search s WHERE s.myCreated < :cutoff")
|
||||
// public Collection<Search> findWhereCreatedBefore(@Param("cutoff") Date theCutoff);
|
||||
|
||||
@Query("SELECT s FROM Search s WHERE s.myResourceType = :type AND mySearchQueryStringHash = :hash AND s.myCreated > :cutoff")
|
||||
public Collection<Search> find(@Param("type") String theResourceType, @Param("hash") int theHashCode, @Param("cutoff") Date theCreatedCutoff);
|
||||
Collection<Search> find(@Param("type") String theResourceType, @Param("hash") int theHashCode, @Param("cutoff") Date theCreatedCutoff);
|
||||
|
||||
@Modifying
|
||||
@Query("UPDATE Search s SET s.mySearchLastReturned = :last WHERE s.myId = :pid")
|
||||
public void updateSearchLastReturned(@Param("pid") long thePid, @Param("last") Date theDate);
|
||||
void updateSearchLastReturned(@Param("pid") long thePid, @Param("last") Date theDate);
|
||||
|
||||
}
|
||||
|
|
|
@ -94,10 +94,9 @@ public class Search implements Serializable {
|
|||
@OneToMany(mappedBy="mySearch")
|
||||
private Collection<SearchResult> myResults;
|
||||
|
||||
// TODO: change nullable to false after 2.5
|
||||
@NotNull
|
||||
@Temporal(TemporalType.TIMESTAMP)
|
||||
@Column(name="SEARCH_LAST_RETURNED", nullable=true, updatable=false)
|
||||
@Column(name="SEARCH_LAST_RETURNED", nullable=false, updatable=false)
|
||||
private Date mySearchLastReturned;
|
||||
|
||||
@Lob()
|
||||
|
|
|
@ -36,7 +36,7 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
|||
*/
|
||||
public class TermConceptPropertyFieldBridge implements FieldBridge, StringBridge {
|
||||
|
||||
public static final String PROP_PREFIX = "PROP__";
|
||||
public static final String CONCEPT_FIELD_PROPERTY_PREFIX = "PROP";
|
||||
|
||||
/**
|
||||
* Constructor
|
||||
|
@ -52,26 +52,18 @@ public class TermConceptPropertyFieldBridge implements FieldBridge, StringBridge
|
|||
|
||||
@Override
|
||||
public void set(String theName, Object theValue, Document theDocument, LuceneOptions theLuceneOptions) {
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<TermConceptProperty> properties = (Collection<TermConceptProperty>) theValue;
|
||||
|
||||
if (properties != null) {
|
||||
for (TermConceptProperty next : properties) {
|
||||
// String propValue = next.getKey() + "=" + next.getValue();
|
||||
// theLuceneOptions.addFieldToDocument(theName, propValue, theDocument);
|
||||
//
|
||||
// if (next.getType() == TermConceptPropertyTypeEnum.CODING) {
|
||||
// propValue = next.getKey() + "=" + next.getDisplay();
|
||||
// theLuceneOptions.addFieldToDocument(theName, propValue, theDocument);
|
||||
// }
|
||||
|
||||
theDocument.add(new StringField("PROP"+next.getKey(), next.getValue(), Field.Store.YES));
|
||||
theDocument.add(new StringField(CONCEPT_FIELD_PROPERTY_PREFIX + next.getKey(), next.getValue(), Field.Store.YES));
|
||||
|
||||
if (next.getType() == TermConceptPropertyTypeEnum.CODING) {
|
||||
if (isNotBlank(next.getDisplay())) {
|
||||
theDocument.add(new StringField("PROP" + next.getKey(), next.getDisplay(), Field.Store.YES));
|
||||
theDocument.add(new StringField(CONCEPT_FIELD_PROPERTY_PREFIX + next.getKey(), next.getDisplay(), Field.Store.YES));
|
||||
}
|
||||
// theLuceneOptions.addFieldToDocument("PROPmyProperties", next.getKey() + "=" + next.getDisplay(), theDocument);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,9 +40,11 @@ import com.github.benmanes.caffeine.cache.Caffeine;
|
|||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.google.common.collect.ArrayListMultimap;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.apache.commons.lang3.time.DateUtils;
|
||||
import org.apache.lucene.index.Term;
|
||||
import org.apache.lucene.queries.TermsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
import org.apache.lucene.search.RegexpQuery;
|
||||
import org.hibernate.ScrollMode;
|
||||
|
@ -81,7 +83,6 @@ import java.util.*;
|
|||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.google.common.base.Ascii.toLowerCase;
|
||||
import static org.apache.commons.lang3.StringUtils.isBlank;
|
||||
import static org.apache.commons.lang3.StringUtils.isNotBlank;
|
||||
|
||||
|
@ -134,10 +135,14 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
|
|||
private int myFetchSize = DEFAULT_FETCH_SIZE;
|
||||
private ApplicationContext myApplicationContext;
|
||||
|
||||
private void addCodeIfNotAlreadyAdded(String theCodeSystem, ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, TermConcept theConcept) {
|
||||
if (theAddedCodes.add(theConcept.getCode())) {
|
||||
/**
|
||||
* @param theAdd If true, add the code. If false, remove the code.
|
||||
*/
|
||||
private void addCodeIfNotAlreadyAdded(String theCodeSystem, ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, TermConcept theConcept, boolean theAdd) {
|
||||
String code = theConcept.getCode();
|
||||
if (theAdd && theAddedCodes.add(code)) {
|
||||
ValueSet.ValueSetExpansionContainsComponent contains = theExpansionComponent.addContains();
|
||||
contains.setCode(theConcept.getCode());
|
||||
contains.setCode(code);
|
||||
contains.setSystem(theCodeSystem);
|
||||
contains.setDisplay(theConcept.getDisplay());
|
||||
for (TermConceptDesignation nextDesignation : theConcept.getDesignations()) {
|
||||
|
@ -150,18 +155,32 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
|
|||
.setDisplay(nextDesignation.getUseDisplay());
|
||||
}
|
||||
}
|
||||
|
||||
if (!theAdd && theAddedCodes.remove(code)) {
|
||||
removeCodeFromExpansion(theCodeSystem, code, theExpansionComponent);
|
||||
}
|
||||
}
|
||||
|
||||
private void addConceptsToList(ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, String theSystem, List<CodeSystem.ConceptDefinitionComponent> theConcept) {
|
||||
private void removeCodeFromExpansion(String theCodeSystem, String theCode, ValueSet.ValueSetExpansionComponent theExpansionComponent) {
|
||||
theExpansionComponent
|
||||
.getContains()
|
||||
.removeIf(t ->
|
||||
theCodeSystem.equals(t.getSystem()) &&
|
||||
theCode.equals(t.getCode()));
|
||||
}
|
||||
|
||||
private void addConceptsToList(ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, String theSystem, List<CodeSystem.ConceptDefinitionComponent> theConcept, boolean theAdd) {
|
||||
for (CodeSystem.ConceptDefinitionComponent next : theConcept) {
|
||||
if (!theAddedCodes.contains(next.getCode())) {
|
||||
theAddedCodes.add(next.getCode());
|
||||
if (theAdd && theAddedCodes.add(next.getCode())) {
|
||||
ValueSet.ValueSetExpansionContainsComponent contains = theExpansionComponent.addContains();
|
||||
contains.setCode(next.getCode());
|
||||
contains.setSystem(theSystem);
|
||||
contains.setDisplay(next.getDisplay());
|
||||
}
|
||||
addConceptsToList(theExpansionComponent, theAddedCodes, theSystem, next.getConcept());
|
||||
if (!theAdd && theAddedCodes.remove(next.getCode())) {
|
||||
removeCodeFromExpansion(theSystem, next.getCode(), theExpansionComponent);
|
||||
}
|
||||
addConceptsToList(theExpansionComponent, theAddedCodes, theSystem, next.getConcept(), theAdd);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -299,9 +318,25 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
|
|||
public ValueSet expandValueSet(ValueSet theValueSetToExpand) {
|
||||
ValueSet.ValueSetExpansionComponent expansionComponent = new ValueSet.ValueSetExpansionComponent();
|
||||
Set<String> addedCodes = new HashSet<>();
|
||||
boolean haveIncludeCriteria = false;
|
||||
|
||||
// Handle includes
|
||||
for (ValueSet.ConceptSetComponent include : theValueSetToExpand.getCompose().getInclude()) {
|
||||
boolean add = true;
|
||||
expandValueSetHandleIncludeOrExclude(expansionComponent, addedCodes, include, add);
|
||||
}
|
||||
|
||||
// Handle excludes
|
||||
for (ValueSet.ConceptSetComponent include : theValueSetToExpand.getCompose().getExclude()) {
|
||||
boolean add = false;
|
||||
expandValueSetHandleIncludeOrExclude(expansionComponent, addedCodes, include, add);
|
||||
}
|
||||
|
||||
ValueSet valueSet = new ValueSet();
|
||||
valueSet.setExpansion(expansionComponent);
|
||||
return valueSet;
|
||||
}
|
||||
|
||||
public void expandValueSetHandleIncludeOrExclude(ValueSet.ValueSetExpansionComponent theExpansionComponent, Set<String> theAddedCodes, ValueSet.ConceptSetComponent include, boolean theAdd) {
|
||||
String system = include.getSystem();
|
||||
if (isNotBlank(system)) {
|
||||
ourLog.info("Starting expansion around code system: {}", system);
|
||||
|
@ -309,19 +344,25 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
|
|||
TermCodeSystem cs = myCodeSystemDao.findByCodeSystemUri(system);
|
||||
if (cs != null) {
|
||||
TermCodeSystemVersion csv = cs.getCurrentVersion();
|
||||
FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager);
|
||||
QueryBuilder qb = em.getSearchFactory().buildQueryBuilder().forEntity(TermConcept.class).get();
|
||||
BooleanJunction<?> bool = qb.bool();
|
||||
|
||||
bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery());
|
||||
|
||||
/*
|
||||
* Include Concepts
|
||||
*/
|
||||
for (ValueSet.ConceptReferenceComponent next : include.getConcept()) {
|
||||
String nextCode = next.getCode();
|
||||
if (isNotBlank(nextCode) && !addedCodes.contains(nextCode)) {
|
||||
haveIncludeCriteria = true;
|
||||
TermConcept code = findCode(system, nextCode);
|
||||
if (code != null) {
|
||||
addCodeIfNotAlreadyAdded(system, expansionComponent, addedCodes, code);
|
||||
}
|
||||
}
|
||||
|
||||
String codes = include
|
||||
.getConcept()
|
||||
.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(ValueSet.ConceptReferenceComponent::getCode)
|
||||
.filter(StringUtils::isNotBlank)
|
||||
.collect(Collectors.joining(" "));
|
||||
if (isNotBlank(codes)) {
|
||||
bool.must(qb.keyword().onField("myCode").matching(codes).createQuery());
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -329,13 +370,6 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
|
|||
*/
|
||||
|
||||
if (include.getFilter().size() > 0) {
|
||||
haveIncludeCriteria = true;
|
||||
|
||||
FullTextEntityManager em = org.hibernate.search.jpa.Search.getFullTextEntityManager(myEntityManager);
|
||||
QueryBuilder qb = em.getSearchFactory().buildQueryBuilder().forEntity(TermConcept.class).get();
|
||||
BooleanJunction<?> bool = qb.bool();
|
||||
|
||||
bool.must(qb.keyword().onField("myCodeSystemVersionPid").matching(csv.getPid()).createQuery());
|
||||
|
||||
for (ValueSet.ConceptSetFilterComponent nextFilter : include.getFilter()) {
|
||||
if (isBlank(nextFilter.getValue()) && nextFilter.getOp() == null && isBlank(nextFilter.getProperty())) {
|
||||
|
@ -373,25 +407,41 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
|
|||
|
||||
if (nextFilter.getOp() == ValueSet.FilterOperator.REGEX) {
|
||||
|
||||
/*
|
||||
* We treat the regex filter as a match on the regex
|
||||
* anywhere in the property string. The spec does not
|
||||
* say whether or not this is the right behaviour, but
|
||||
* there are examples that seem to suggest that it is.
|
||||
*/
|
||||
String value = nextFilter.getValue();
|
||||
if (value.endsWith("$")) {
|
||||
value = value.substring(0,value.length() - 1);
|
||||
value = value.substring(0, value.length() - 1);
|
||||
} else if (value.endsWith(".*") == false) {
|
||||
value = value + ".*";
|
||||
}
|
||||
Term term = new Term("PROP"+nextFilter.getProperty(), value);
|
||||
if (value.startsWith("^") == false && value.startsWith(".*") == false) {
|
||||
value = ".*" + value;
|
||||
} else if (value.startsWith("^")) {
|
||||
value = value.substring(1);
|
||||
}
|
||||
|
||||
Term term = new Term(TermConceptPropertyFieldBridge.CONCEPT_FIELD_PROPERTY_PREFIX + nextFilter.getProperty(), value);
|
||||
RegexpQuery query = new RegexpQuery(term);
|
||||
bool.must(query);
|
||||
|
||||
|
||||
} else {
|
||||
|
||||
bool.must(qb.phrase().onField("PROP" + nextFilter.getProperty()).sentence(nextFilter.getValue()).createQuery());
|
||||
// bool.must(qb.phrase().onField("myProperties").sentence(nextFilter.getProperty() + "=" + nextFilter.getValue()).createQuery());
|
||||
String value = nextFilter.getValue();
|
||||
Term term = new Term(TermConceptPropertyFieldBridge.CONCEPT_FIELD_PROPERTY_PREFIX + nextFilter.getProperty(), value);
|
||||
bool.must(new TermsQuery(term));
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Query luceneQuery = bool.createQuery();
|
||||
FullTextQuery jpaQuery = em.createFullTextQuery(luceneQuery, TermConcept.class);
|
||||
jpaQuery.setMaxResults(1000);
|
||||
|
@ -404,17 +454,7 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
|
|||
ourLog.info("Expansion completed in {}ms", sw.getMillis());
|
||||
|
||||
for (TermConcept nextConcept : result) {
|
||||
addCodeIfNotAlreadyAdded(system, expansionComponent, addedCodes, nextConcept);
|
||||
}
|
||||
|
||||
expansionComponent.setTotal(jpaQuery.getResultSize());
|
||||
}
|
||||
|
||||
if (!haveIncludeCriteria) {
|
||||
List<TermConcept> allCodes = findCodes(system);
|
||||
for (TermConcept nextConcept : allCodes) {
|
||||
addCodeIfNotAlreadyAdded(system, expansionComponent, addedCodes, nextConcept);
|
||||
}
|
||||
addCodeIfNotAlreadyAdded(system, theExpansionComponent, theAddedCodes, nextConcept, theAdd);
|
||||
}
|
||||
|
||||
} else {
|
||||
|
@ -428,31 +468,30 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc,
|
|||
if (include.getConcept().isEmpty() == false) {
|
||||
for (ValueSet.ConceptReferenceComponent next : include.getConcept()) {
|
||||
String nextCode = next.getCode();
|
||||
if (isNotBlank(nextCode) && !addedCodes.contains(nextCode)) {
|
||||
if (isNotBlank(nextCode) && !theAddedCodes.contains(nextCode)) {
|
||||
CodeSystem.ConceptDefinitionComponent code = findCode(codeSystemFromContext.getConcept(), nextCode);
|
||||
if (code != null) {
|
||||
addedCodes.add(nextCode);
|
||||
ValueSet.ValueSetExpansionContainsComponent contains = expansionComponent.addContains();
|
||||
if (theAdd && theAddedCodes.add(nextCode)) {
|
||||
ValueSet.ValueSetExpansionContainsComponent contains = theExpansionComponent.addContains();
|
||||
contains.setCode(nextCode);
|
||||
contains.setSystem(system);
|
||||
contains.setDisplay(code.getDisplay());
|
||||
}
|
||||
if (!theAdd && theAddedCodes.remove(nextCode)) {
|
||||
removeCodeFromExpansion(system, nextCode, theExpansionComponent);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
List<CodeSystem.ConceptDefinitionComponent> concept = codeSystemFromContext.getConcept();
|
||||
addConceptsToList(expansionComponent, addedCodes, system, concept);
|
||||
addConceptsToList(theExpansionComponent, theAddedCodes, system, concept, theAdd);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ValueSet valueSet = new ValueSet();
|
||||
valueSet.setExpansion(expansionComponent);
|
||||
return valueSet;
|
||||
}
|
||||
|
||||
protected List<VersionIndependentConcept> expandValueSetAndReturnVersionIndependentConcepts(org.hl7.fhir.r4.model.ValueSet theValueSetToExpandR4) {
|
||||
org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionComponent expandedR4 = expandValueSet(theValueSetToExpandR4).getExpansion();
|
||||
|
||||
|
|
|
@ -275,7 +275,7 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc {
|
|||
iterateOverZipFile(theDescriptors, LOINC_PART_LINK_FILE, handler, ',', QuoteMode.NON_NUMERIC);
|
||||
|
||||
// Part related code mapping
|
||||
handler = new LoincPartRelatedCodeMappingHandler(codeSystemVersion, code2concept, valueSets, conceptMaps, uploadProperties);
|
||||
handler = new LoincPartRelatedCodeMappingHandler(code2concept, valueSets, conceptMaps, uploadProperties);
|
||||
iterateOverZipFile(theDescriptors, LOINC_PART_RELATED_CODE_MAPPING_FILE, handler, ',', QuoteMode.NON_NUMERIC);
|
||||
|
||||
// Document Ontology File
|
||||
|
|
|
@ -20,7 +20,6 @@ package ca.uhn.fhir.jpa.term.loinc;
|
|||
* #L%
|
||||
*/
|
||||
|
||||
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
|
||||
import ca.uhn.fhir.jpa.entity.TermConcept;
|
||||
import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc;
|
||||
import ca.uhn.fhir.jpa.term.IRecordHandler;
|
||||
|
@ -41,23 +40,20 @@ public class LoincPartRelatedCodeMappingHandler extends BaseLoincHandler impleme
|
|||
|
||||
public static final String LOINC_SCT_PART_MAP_ID = "loinc-parts-to-snomed-ct";
|
||||
public static final String LOINC_SCT_PART_MAP_URI = "http://loinc.org/cm/loinc-parts-to-snomed-ct";
|
||||
public static final String LOINC_SCT_PART_MAP_NAME = "LOINC Part Map to SNOMED CT";
|
||||
public static final String LOINC_RXNORM_PART_MAP_ID = "loinc-parts-to-rxnorm";
|
||||
public static final String LOINC_RXNORM_PART_MAP_URI = "http://loinc.org/cm/loinc-parts-to-rxnorm";
|
||||
public static final String LOINC_RXNORM_PART_MAP_NAME = "LOINC Part Map to RxNORM";
|
||||
public static final String LOINC_RADLEX_PART_MAP_ID = "loinc-parts-to-radlex";
|
||||
public static final String LOINC_RADLEX_PART_MAP_URI = "http://loinc.org/cm/loinc-parts-to-radlex";
|
||||
public static final String LOINC_RADLEX_PART_MAP_NAME = "LOINC Part Map to RADLEX";
|
||||
private static final String LOINC_SCT_PART_MAP_NAME = "LOINC Part Map to SNOMED CT";
|
||||
private static final String LOINC_RXNORM_PART_MAP_ID = "loinc-parts-to-rxnorm";
|
||||
private static final String LOINC_RXNORM_PART_MAP_URI = "http://loinc.org/cm/loinc-parts-to-rxnorm";
|
||||
private static final String LOINC_RXNORM_PART_MAP_NAME = "LOINC Part Map to RxNORM";
|
||||
private static final String LOINC_RADLEX_PART_MAP_ID = "loinc-parts-to-radlex";
|
||||
private static final String LOINC_RADLEX_PART_MAP_URI = "http://loinc.org/cm/loinc-parts-to-radlex";
|
||||
private static final String LOINC_RADLEX_PART_MAP_NAME = "LOINC Part Map to RADLEX";
|
||||
private static final String CM_COPYRIGHT = "This content from LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at https://loinc.org/license/. The LOINC Part File, LOINC/SNOMED CT Expression Association and Map Sets File, RELMA database and associated search index files include SNOMED Clinical Terms (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights are reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO. Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org. Under the terms of the Affiliate License, use of SNOMED CT in countries that are not IHTSDO Members is subject to reporting and fee payment obligations. However, IHTSDO agrees to waive the requirements to report and pay fees for use of SNOMED CT content included in the LOINC Part Mapping and LOINC Term Associations for purposes that support or enable more effective use of LOINC. This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.";
|
||||
private final Map<String, TermConcept> myCode2Concept;
|
||||
private final TermCodeSystemVersion myCodeSystemVersion;
|
||||
private final List<ConceptMap> myConceptMaps;
|
||||
private static final String LOINC_PUBCHEM_PART_MAP_URI = "http://pubchem.ncbi.nlm.nih.gov";
|
||||
private static final String LOINC_PUBCHEM_PART_MAP_ID = "loinc-parts-to-pubchem";
|
||||
private static final String LOINC_PUBCHEM_PART_MAP_NAME = "LOINC Part Map to PubChem";
|
||||
|
||||
public LoincPartRelatedCodeMappingHandler(TermCodeSystemVersion theCodeSystemVersion, Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps, Properties theUploadProperties) {
|
||||
public LoincPartRelatedCodeMappingHandler(Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps, Properties theUploadProperties) {
|
||||
super(theCode2concept, theValueSets, theConceptMaps, theUploadProperties);
|
||||
myCodeSystemVersion = theCodeSystemVersion;
|
||||
myCode2Concept = theCode2concept;
|
||||
myConceptMaps = theConceptMaps;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -112,8 +108,16 @@ public class LoincPartRelatedCodeMappingHandler extends BaseLoincHandler impleme
|
|||
loincPartMapUri = LOINC_RADLEX_PART_MAP_URI;
|
||||
loincPartMapName = LOINC_RADLEX_PART_MAP_NAME;
|
||||
break;
|
||||
case "http://pubchem.ncbi.nlm.nih.gov":
|
||||
loincPartMapId = LOINC_PUBCHEM_PART_MAP_ID;
|
||||
loincPartMapUri = LOINC_PUBCHEM_PART_MAP_URI;
|
||||
loincPartMapName = LOINC_PUBCHEM_PART_MAP_NAME;
|
||||
break;
|
||||
default:
|
||||
throw new InternalErrorException("Don't know how to handle mapping to system: " + extCodeSystem);
|
||||
loincPartMapId = extCodeSystem.replaceAll("[^a-zA-Z]", "");
|
||||
loincPartMapUri = extCodeSystem;
|
||||
loincPartMapName = "Unknown Mapping";
|
||||
break;
|
||||
}
|
||||
|
||||
addConceptMapEntry(
|
||||
|
|
|
@ -18,7 +18,7 @@ import org.junit.runner.RunWith;
|
|||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Captor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
|
|
|
@ -34,6 +34,12 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
@Autowired
|
||||
private ITermCodeSystemDao myTermCodeSystemDao;
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize());
|
||||
BaseHapiTerminologySvcImpl.setForceSaveDeferredAlwaysForUnitTest(false);
|
||||
}
|
||||
|
||||
private IIdType createCodeSystem() {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setUrl(CS_URL);
|
||||
|
@ -104,6 +110,31 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
return id;
|
||||
}
|
||||
|
||||
public void createLoincSystemWithSomeCodes() {
|
||||
runInTransaction(() -> {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setUrl(CS_URL);
|
||||
codeSystem.setContent(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 code;
|
||||
code = new TermConcept(cs, "50015-7");
|
||||
code.addPropertyString("SYSTEM", "Bld/Bone mar^Donor");
|
||||
cs.getConcepts().add(code);
|
||||
|
||||
code = new TermConcept(cs, "43343-3");
|
||||
code.addPropertyString("SYSTEM", "Ser");
|
||||
cs.getConcepts().add(code);
|
||||
|
||||
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs);
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateDuplicateCodeSystemUri() {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
|
@ -144,95 +175,37 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testExpandValueSetPropertySearchWithRegexInclude() {
|
||||
runInTransaction(()->{
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setUrl(CS_URL);
|
||||
codeSystem.setContent(CodeSystemContentMode.NOTPRESENT);
|
||||
IIdType id = myCodeSystemDao.create(codeSystem, mySrd).getId().toUnqualified();
|
||||
public void testCreatePropertiesAndDesignationsWithDeferredConcepts() {
|
||||
myDaoConfig.setDeferIndexingForCodesystemsOfSize(1);
|
||||
BaseHapiTerminologySvcImpl.setForceSaveDeferredAlwaysForUnitTest(true);
|
||||
|
||||
ResourceTable table = myResourceTableDao.findById(id.getIdPartAsLong()).orElseThrow(IllegalArgumentException::new);
|
||||
createCodeSystem();
|
||||
|
||||
TermCodeSystemVersion cs = new TermCodeSystemVersion();
|
||||
cs.setResource(table);
|
||||
Validate.notNull(myTermSvc);
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
|
||||
TermConcept code;
|
||||
code = new TermConcept(cs, "50015-7");
|
||||
code.addPropertyString("SYSTEM", "Bld/Bone mar^Donor");
|
||||
cs.getConcepts().add(code);
|
||||
|
||||
code = new TermConcept(cs, "43343-3");
|
||||
code.addPropertyString("SYSTEM", "Ser");
|
||||
cs.getConcepts().add(code);
|
||||
|
||||
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs);
|
||||
});
|
||||
|
||||
List<String> codes;
|
||||
ValueSet vs;
|
||||
ValueSet outcome;
|
||||
ValueSet.ConceptSetComponent include;
|
||||
|
||||
// Include
|
||||
vs = new ValueSet();
|
||||
include = vs.getCompose().addInclude();
|
||||
ValueSet vs = new ValueSet();
|
||||
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include
|
||||
.addFilter()
|
||||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue(".*\\^Donor$");
|
||||
// .setValue("\\\\^Donor$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7"));
|
||||
}
|
||||
include.addConcept().setCode("childAAB");
|
||||
ValueSet outcome = myTermSvc.expandValueSet(vs);
|
||||
|
||||
List<String> codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("childAAB"));
|
||||
|
||||
@Test
|
||||
public void testExpandValueSetPropertySearchWithRegexExclude() {
|
||||
CodeSystem codeSystem = new CodeSystem();
|
||||
codeSystem.setUrl(CS_URL);
|
||||
codeSystem.setContent(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 code;
|
||||
code = new TermConcept(cs, "50015-7");
|
||||
code.addPropertyString("SYSTEM", "Bld/Bone mar^Donor");
|
||||
cs.getConcepts().add(code);
|
||||
|
||||
code = new TermConcept(cs, "43343-3");
|
||||
code.addPropertyString("SYSTEM", "Ser");
|
||||
cs.getConcepts().add(code);
|
||||
|
||||
myTermSvc.storeNewCodeSystemVersion(table.getId(), CS_URL, "SYSTEM NAME", cs);
|
||||
|
||||
List<String> codes;
|
||||
ValueSet vs;
|
||||
ValueSet outcome;
|
||||
ValueSet.ConceptSetComponent exclude;
|
||||
|
||||
// Include
|
||||
vs = new ValueSet();
|
||||
vs.getCompose()
|
||||
.addInclude()
|
||||
.setSystem(CS_URL);
|
||||
|
||||
exclude = vs.getCompose().addExclude();
|
||||
exclude.setSystem(CS_URL);
|
||||
exclude
|
||||
.addFilter()
|
||||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue("\\\\^Donor$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-3"));
|
||||
ValueSet.ValueSetExpansionContainsComponent concept = outcome.getExpansion().getContains().get(0);
|
||||
assertEquals("childAAB", concept.getCode());
|
||||
assertEquals("http://example.com/my_code_system", concept.getSystem());
|
||||
assertEquals(null, concept.getDisplay());
|
||||
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
|
||||
|
@ -286,6 +259,123 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandValueSetPropertySearchWithRegexExclude() {
|
||||
createLoincSystemWithSomeCodes();
|
||||
|
||||
List<String> codes;
|
||||
ValueSet vs;
|
||||
ValueSet outcome;
|
||||
ValueSet.ConceptSetComponent exclude;
|
||||
|
||||
// Include
|
||||
vs = new ValueSet();
|
||||
vs.getCompose()
|
||||
.addInclude()
|
||||
.setSystem(CS_URL);
|
||||
|
||||
exclude = vs.getCompose().addExclude();
|
||||
exclude.setSystem(CS_URL);
|
||||
exclude
|
||||
.addFilter()
|
||||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue(".*\\^Donor$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandValueSetPropertySearchWithRegexInclude() {
|
||||
// create codes with "SYSTEM" property "Bld/Bone mar^Donor" and "Ser"
|
||||
createLoincSystemWithSomeCodes();
|
||||
|
||||
List<String> codes;
|
||||
ValueSet vs;
|
||||
ValueSet outcome;
|
||||
ValueSet.ConceptSetComponent include;
|
||||
|
||||
// Include
|
||||
vs = new ValueSet();
|
||||
include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include
|
||||
.addFilter()
|
||||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue(".*\\^Donor$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7"));
|
||||
|
||||
// Include
|
||||
vs = new ValueSet();
|
||||
include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include
|
||||
.addFilter()
|
||||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue("\\^Donor$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7"));
|
||||
|
||||
// Include
|
||||
vs = new ValueSet();
|
||||
include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include
|
||||
.addFilter()
|
||||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue("\\^Dono$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, empty());
|
||||
|
||||
// Include
|
||||
vs = new ValueSet();
|
||||
include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include
|
||||
.addFilter()
|
||||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue("^Donor$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, empty());
|
||||
|
||||
// Include
|
||||
vs = new ValueSet();
|
||||
include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include
|
||||
.addFilter()
|
||||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue("\\^Dono");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("50015-7"));
|
||||
|
||||
// Include
|
||||
vs = new ValueSet();
|
||||
include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include
|
||||
.addFilter()
|
||||
.setProperty("SYSTEM")
|
||||
.setOp(ValueSet.FilterOperator.REGEX)
|
||||
.setValue("^Ser$");
|
||||
outcome = myTermSvc.expandValueSet(vs);
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("43343-3"));
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExpandValueSetWholeSystem() {
|
||||
createCodeSystem();
|
||||
|
@ -301,73 +391,6 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
assertThat(codes, containsInAnyOrder("ParentWithNoChildrenA", "ParentWithNoChildrenB", "ParentWithNoChildrenC", "ParentA", "childAAA", "childAAB", "childAA", "childAB", "ParentB"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropertiesAndDesignationsPreservedInExpansion() {
|
||||
createCodeSystem();
|
||||
|
||||
List<String> codes;
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include.addConcept().setCode("childAAB");
|
||||
ValueSet outcome = myTermSvc.expandValueSet(vs);
|
||||
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("childAAB"));
|
||||
|
||||
ValueSet.ValueSetExpansionContainsComponent concept = outcome.getExpansion().getContains().get(0);
|
||||
assertEquals("childAAB", concept.getCode());
|
||||
assertEquals("http://example.com/my_code_system", concept.getSystem());
|
||||
assertEquals(null, concept.getDisplay());
|
||||
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());
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
myDaoConfig.setDeferIndexingForCodesystemsOfSize(new DaoConfig().getDeferIndexingForCodesystemsOfSize());
|
||||
BaseHapiTerminologySvcImpl.setForceSaveDeferredAlwaysForUnitTest(false);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testCreatePropertiesAndDesignationsWithDeferredConcepts() {
|
||||
myDaoConfig.setDeferIndexingForCodesystemsOfSize(1);
|
||||
BaseHapiTerminologySvcImpl.setForceSaveDeferredAlwaysForUnitTest(true);
|
||||
|
||||
createCodeSystem();
|
||||
|
||||
Validate.notNull(myTermSvc);
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
myTermSvc.saveDeferred();
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include.addConcept().setCode("childAAB");
|
||||
ValueSet outcome = myTermSvc.expandValueSet(vs);
|
||||
|
||||
List<String> codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("childAAB"));
|
||||
|
||||
ValueSet.ValueSetExpansionContainsComponent concept = outcome.getExpansion().getContains().get(0);
|
||||
assertEquals("childAAB", concept.getCode());
|
||||
assertEquals("http://example.com/my_code_system", concept.getSystem());
|
||||
assertEquals(null, concept.getDisplay());
|
||||
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 testFindCodesAbove() {
|
||||
IIdType id = createCodeSystem();
|
||||
|
@ -469,6 +492,31 @@ public class TerminologySvcImplDstu3Test extends BaseJpaDstu3Test {
|
|||
assertThat(codes, empty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPropertiesAndDesignationsPreservedInExpansion() {
|
||||
createCodeSystem();
|
||||
|
||||
List<String> codes;
|
||||
|
||||
ValueSet vs = new ValueSet();
|
||||
ValueSet.ConceptSetComponent include = vs.getCompose().addInclude();
|
||||
include.setSystem(CS_URL);
|
||||
include.addConcept().setCode("childAAB");
|
||||
ValueSet outcome = myTermSvc.expandValueSet(vs);
|
||||
|
||||
codes = toCodesContains(outcome.getExpansion().getContains());
|
||||
assertThat(codes, containsInAnyOrder("childAAB"));
|
||||
|
||||
ValueSet.ValueSetExpansionContainsComponent concept = outcome.getExpansion().getContains().get(0);
|
||||
assertEquals("childAAB", concept.getCode());
|
||||
assertEquals("http://example.com/my_code_system", concept.getSystem());
|
||||
assertEquals(null, concept.getDisplay());
|
||||
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 testReindexTerminology() {
|
||||
IIdType id = createCodeSystem();
|
||||
|
|
|
@ -9,3 +9,4 @@
|
|||
"LP15791-4","Phenylketones","COMPONENT","102642008","Phenylketones (substance)","http://snomed.info/sct","Exact","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP15721-1","Malonate","COMPONENT","102648007","Malonic acid (substance)","http://snomed.info/sct","Exact","Both","http://snomed.info/sct/900000000000207008/version/20170731","This material includes SNOMED Clinical Terms® (SNOMED CT®) which is used by permission of the International Health Terminology Standards Development Organisation (IHTSDO) under license. All rights reserved. SNOMED CT® was originally created by The College of American Pathologists. “SNOMED” and “SNOMED CT” are registered trademarks of the IHTSDO.This material includes content from the US Edition to SNOMED CT, which is developed and maintained by the U.S. National Library of Medicine and is available to authorized UMLS Metathesaurus Licensees from the UTS Downloads site at https://uts.nlm.nih.gov.Use of SNOMED CT content is subject to the terms and conditions set forth in the SNOMED CT Affiliate License Agreement. It is the responsibility of those implementing this product to ensure they are appropriately licensed and for more information on the license, including how to register as an Affiliate Licensee, please refer to http://www.snomed.org/snomed-ct/get-snomed-ct or info@snomed.org<mailto:info@snomed.org>. This may incur a fee in SNOMED International non-Member countries."
|
||||
"LP15842-5","Pyridoxine","COMPONENT","1054","Pyridoxine","http://pubchem.ncbi.nlm.nih.gov","Exact",,,
|
||||
"LP15842-5","Pyridoxine","COMPONENT","1054","Pyridoxine","http://foo/bar","Exact",,,
|
||||
|
|
|
Loading…
Reference in New Issue