Update for Loinc 2.68 (#1917)

* Work on loinc updates

* Work on loinc upload

* Clean up CLI config

* Add changelog

* Update hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java

Co-authored-by: IanMMarshall <49525404+IanMMarshall@users.noreply.github.com>

* Update hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TermLoaderSvcImpl.java

Co-authored-by: IanMMarshall <49525404+IanMMarshall@users.noreply.github.com>

* Update hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/provider/r4/TerminologyUploaderProviderR4Test.java

Co-authored-by: IanMMarshall <49525404+IanMMarshall@users.noreply.github.com>

* Update hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUploadPropertiesEnum.java

Co-authored-by: IanMMarshall <49525404+IanMMarshall@users.noreply.github.com>

* Update hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java

Co-authored-by: IanMMarshall <49525404+IanMMarshall@users.noreply.github.com>

* Clean up imports

* LOINC fixes

* Loinc loader fixes

Co-authored-by: IanMMarshall <49525404+IanMMarshall@users.noreply.github.com>
This commit is contained in:
James Agnew 2020-06-18 16:55:11 -04:00 committed by GitHub
parent 4119cbb0c4
commit 46c00f4efb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 835 additions and 614 deletions

View File

@ -20,28 +20,18 @@ package ca.uhn.fhir.jpa.demo;
* #L%
*/
import ca.uhn.fhir.context.ConfigurationException;
import ca.uhn.fhir.jpa.api.config.DaoConfig;
import ca.uhn.fhir.jpa.model.config.PartitionSettings;
import ca.uhn.fhir.jpa.model.entity.ModelConfig;
import ca.uhn.fhir.jpa.search.LuceneSearchMappingFactory;
import ca.uhn.fhir.jpa.search.elastic.ElasticsearchHibernatePropertiesBuilder;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.commons.lang3.time.DateUtils;
import org.hibernate.dialect.H2Dialect;
import org.hibernate.search.elasticsearch.cfg.ElasticsearchIndexStatus;
import org.hibernate.search.elasticsearch.cfg.IndexSchemaManagementStrategy;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import pl.allegro.tech.embeddedelasticsearch.EmbeddedElastic;
import pl.allegro.tech.embeddedelasticsearch.PopularProperties;
import javax.annotation.PreDestroy;
import javax.sql.DataSource;
import java.io.IOException;
import java.util.Properties;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import static org.apache.commons.lang3.StringUtils.isNotBlank;
@ -110,55 +100,7 @@ public class CommonConfig {
extraProperties.put("hibernate.search.autoregister_listeners", "false");
}
return configureElasticearch(extraProperties);
}
private Properties configureElasticearch(Properties theExtraProperties) {
String elasticsearchHost = "localhost";
String elasticsearchUserId = "";
String elasticsearchPassword = "";
int elasticsearchPort = embeddedElasticSearch().getHttpPort();
new ElasticsearchHibernatePropertiesBuilder()
.setDebugRefreshAfterWrite(true)
.setDebugPrettyPrintJsonLog(true)
.setIndexSchemaManagementStrategy(IndexSchemaManagementStrategy.CREATE)
.setIndexManagementWaitTimeoutMillis(10000)
.setRequiredIndexStatus(ElasticsearchIndexStatus.YELLOW)
.setRestUrl("http://" + elasticsearchHost + ":" + elasticsearchPort)
.setUsername(elasticsearchUserId)
.setPassword(elasticsearchPassword)
.apply(theExtraProperties);
return theExtraProperties;
}
@Bean
public EmbeddedElastic embeddedElasticSearch() {
String ELASTIC_VERSION = "6.5.4";
EmbeddedElastic embeddedElastic = null;
try {
embeddedElastic = EmbeddedElastic.builder()
.withElasticVersion(ELASTIC_VERSION)
.withSetting(PopularProperties.TRANSPORT_TCP_PORT, 0)
.withSetting(PopularProperties.HTTP_PORT, 0)
.withSetting(PopularProperties.CLUSTER_NAME, UUID.randomUUID())
.withStartTimeout(60, TimeUnit.SECONDS)
.build()
.start();
} catch (IOException | InterruptedException e) {
throw new ConfigurationException(e);
}
return embeddedElastic;
}
@PreDestroy
public void stop() {
embeddedElasticSearch().stop();
return extraProperties;
}
@Bean

View File

@ -0,0 +1,4 @@
---
type: add
issue: 1917
title: "The LOINC importer has been updated to support the file format used by the LOINC 2.68 release."

View File

@ -105,11 +105,11 @@ public class FhirResourceDaoCodeSystemR4 extends BaseHapiFhirResourceDao<CodeSys
system = theSystem.getValue();
}
ourLog.info("Looking up {} / {}", system, code);
ourLog.debug("Looking up {} / {}", system, code);
if (myValidationSupport.isCodeSystemSupported(new ValidationSupportContext(myValidationSupport), system)) {
ourLog.info("Code system {} is supported", system);
ourLog.debug("Code system {} is supported", system);
IValidationSupport.LookupCodeResult retVal = myValidationSupport.lookupCode(new ValidationSupportContext(myValidationSupport), system, code);
if (retVal != null) {
return retVal;

View File

@ -152,7 +152,9 @@ public class TermConcept implements Serializable {
property.setType(thePropertyType);
property.setKey(thePropertyName);
property.setValue(thePropertyValue);
if (!getProperties().contains(property)) {
getProperties().add(property);
}
return property;
}

View File

@ -22,12 +22,25 @@ package ca.uhn.fhir.jpa.entity;
import ca.uhn.fhir.util.ValidateUtil;
import org.apache.commons.lang3.Validate;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.commons.lang3.builder.ToStringBuilder;
import org.apache.commons.lang3.builder.ToStringStyle;
import org.hibernate.validator.constraints.NotBlank;
import javax.annotation.Nonnull;
import javax.persistence.*;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.ForeignKey;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.Lob;
import javax.persistence.ManyToOne;
import javax.persistence.SequenceGenerator;
import javax.persistence.Table;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
@ -38,11 +51,9 @@ import static org.apache.commons.lang3.StringUtils.length;
@Table(name = "TRM_CONCEPT_PROPERTY", uniqueConstraints = {
})
public class TermConceptProperty implements Serializable {
private static final long serialVersionUID = 1L;
private static final int MAX_LENGTH = 500;
public static final int MAX_PROPTYPE_ENUM_LENGTH = 6;
private static final long serialVersionUID = 1L;
private static final int MAX_LENGTH = 500;
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "CONCEPT_PID", referencedColumnName = "PID", foreignKey = @ForeignKey(name = "FK_CONCEPTPROP_CONCEPT"))
private TermConcept myConcept;
@ -69,14 +80,6 @@ public class TermConceptProperty implements Serializable {
private byte[] myValueLob;
@Column(name = "PROP_TYPE", nullable = false, length = MAX_PROPTYPE_ENUM_LENGTH)
private TermConceptPropertyTypeEnum myType;
/**
* Constructor
*/
public TermConceptProperty() {
super();
}
/**
* Relevant only for properties of type {@link TermConceptPropertyTypeEnum#CODING}
*/
@ -88,6 +91,13 @@ public class TermConceptProperty implements Serializable {
@Column(name = "PROP_DISPLAY", length = MAX_LENGTH, nullable = true)
private String myDisplay;
/**
* Constructor
*/
public TermConceptProperty() {
super();
}
/**
* Relevant only for properties of type {@link TermConceptPropertyTypeEnum#CODING}
*/
@ -178,10 +188,6 @@ public class TermConceptProperty implements Serializable {
return myValueLob;
}
public String getValueLobAsString() {
return new String(myValueLob, StandardCharsets.UTF_8);
}
public TermConceptProperty setValueLob(byte[] theValueLob) {
myValueLob = theValueLob;
return this;
@ -192,6 +198,10 @@ public class TermConceptProperty implements Serializable {
return this;
}
public String getValueLobAsString() {
return new String(myValueLob, StandardCharsets.UTF_8);
}
public TermConceptProperty setCodeSystemVersion(TermCodeSystemVersion theCodeSystemVersion) {
myCodeSystemVersion = theCodeSystemVersion;
return this;
@ -210,4 +220,35 @@ public class TermConceptProperty implements Serializable {
.toString();
}
@Override
public boolean equals(Object theO) {
if (this == theO) {
return true;
}
if (theO == null || getClass() != theO.getClass()) {
return false;
}
TermConceptProperty that = (TermConceptProperty) theO;
return new EqualsBuilder()
.append(myKey, that.myKey)
.append(myValue, that.myValue)
.append(myType, that.myType)
.append(myCodeSystem, that.myCodeSystem)
.append(myDisplay, that.myDisplay)
.isEquals();
}
@Override
public int hashCode() {
return new HashCodeBuilder(17, 37)
.append(myKey)
.append(myValue)
.append(myType)
.append(myCodeSystem)
.append(myDisplay)
.toHashCode();
}
}

View File

@ -103,11 +103,6 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc {
@Override
public UploadStatistics loadLoinc(List<FileDescriptor> theFiles, RequestDetails theRequestDetails) {
try (LoadedFileDescriptors descriptors = new LoadedFileDescriptors(theFiles)) {
List<String> loincUploadPropertiesFragment = Collections.singletonList(
LOINC_UPLOAD_PROPERTIES_FILE.getCode()
);
descriptors.verifyMandatoryFilesExist(loincUploadPropertiesFragment);
Properties uploadProperties = getProperties(descriptors, LOINC_UPLOAD_PROPERTIES_FILE.getCode());
List<String> mandatoryFilenameFragments = Arrays.asList(
@ -119,7 +114,8 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc {
uploadProperties.getProperty(LOINC_IEEE_MEDICAL_DEVICE_CODE_MAPPING_TABLE_FILE.getCode(), LOINC_IEEE_MEDICAL_DEVICE_CODE_MAPPING_TABLE_FILE_DEFAULT.getCode()),
uploadProperties.getProperty(LOINC_IMAGING_DOCUMENT_CODES_FILE.getCode(), LOINC_IMAGING_DOCUMENT_CODES_FILE_DEFAULT.getCode()),
uploadProperties.getProperty(LOINC_PART_FILE.getCode(), LOINC_PART_FILE_DEFAULT.getCode()),
uploadProperties.getProperty(LOINC_PART_LINK_FILE.getCode(), LOINC_PART_LINK_FILE_DEFAULT.getCode()),
uploadProperties.getProperty(LOINC_PART_LINK_FILE_PRIMARY.getCode(), LOINC_PART_LINK_FILE_PRIMARY_DEFAULT.getCode()),
uploadProperties.getProperty(LOINC_PART_LINK_FILE_SUPPLEMENTARY.getCode(), LOINC_PART_LINK_FILE_SUPPLEMENTARY_DEFAULT.getCode()),
uploadProperties.getProperty(LOINC_PART_RELATED_CODE_MAPPING_FILE.getCode(), LOINC_PART_RELATED_CODE_MAPPING_FILE_DEFAULT.getCode()),
uploadProperties.getProperty(LOINC_RSNA_PLAYBOOK_FILE.getCode(), LOINC_RSNA_PLAYBOOK_FILE_DEFAULT.getCode()),
uploadProperties.getProperty(LOINC_TOP2000_COMMON_LAB_RESULTS_SI_FILE.getCode(), LOINC_TOP2000_COMMON_LAB_RESULTS_SI_FILE_DEFAULT.getCode()),
@ -238,6 +234,13 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc {
@NotNull
private Properties getProperties(LoadedFileDescriptors theDescriptors, String thePropertiesFile) {
Properties retVal = new Properties();
try (InputStream propertyStream = TermLoaderSvcImpl.class.getResourceAsStream("/ca/uhn/fhir/jpa/term/loinc/loincupload.properties")) {
retVal.load(propertyStream);
} catch (IOException e) {
throw new InternalErrorException("Failed to process loinc.properties", e);
}
for (FileDescriptor next : theDescriptors.getUncompressedFileDescriptors()) {
if (next.getFilename().endsWith(thePropertiesFile)) {
try {
@ -425,10 +428,6 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc {
handler = new LoincRsnaPlaybookHandler(code2concept, valueSets, conceptMaps, theUploadProperties);
iterateOverZipFile(theDescriptors, theUploadProperties.getProperty(LOINC_RSNA_PLAYBOOK_FILE.getCode(), LOINC_RSNA_PLAYBOOK_FILE_DEFAULT.getCode()), handler, ',', QuoteMode.NON_NUMERIC, false);
// Part link
handler = new LoincPartLinkHandler(codeSystemVersion, code2concept);
iterateOverZipFile(theDescriptors, theUploadProperties.getProperty(LOINC_PART_LINK_FILE.getCode(), LOINC_PART_LINK_FILE_DEFAULT.getCode()), handler, ',', QuoteMode.NON_NUMERIC, false);
// Part related code mapping
handler = new LoincPartRelatedCodeMappingHandler(code2concept, valueSets, conceptMaps, theUploadProperties);
iterateOverZipFile(theDescriptors, theUploadProperties.getProperty(LOINC_PART_RELATED_CODE_MAPPING_FILE.getCode(), LOINC_PART_RELATED_CODE_MAPPING_FILE_DEFAULT.getCode()), handler, ',', QuoteMode.NON_NUMERIC, false);
@ -469,6 +468,11 @@ public class TermLoaderSvcImpl implements ITermLoaderSvc {
handler = new LoincParentGroupFileHandler(code2concept, valueSets, conceptMaps, theUploadProperties);
iterateOverZipFile(theDescriptors, theUploadProperties.getProperty(LOINC_PARENT_GROUP_FILE.getCode(), LOINC_PARENT_GROUP_FILE_DEFAULT.getCode()), handler, ',', QuoteMode.NON_NUMERIC, false);
// Part link
handler = new LoincPartLinkHandler(codeSystemVersion, code2concept, propertyNamesToTypes);
iterateOverZipFile(theDescriptors, theUploadProperties.getProperty(LOINC_PART_LINK_FILE_PRIMARY.getCode(), LOINC_PART_LINK_FILE_PRIMARY_DEFAULT.getCode()), handler, ',', QuoteMode.NON_NUMERIC, false);
iterateOverZipFile(theDescriptors, theUploadProperties.getProperty(LOINC_PART_LINK_FILE_SUPPLEMENTARY.getCode(), LOINC_PART_LINK_FILE_SUPPLEMENTARY_DEFAULT.getCode()), handler, ',', QuoteMode.NON_NUMERIC, false);
IOUtils.closeQuietly(theDescriptors);
valueSets.add(getValueSetLoincAll());

View File

@ -86,44 +86,9 @@ public class LoincHandler implements IRecordHandler {
concept.addPropertyString(nextPropertyName, nextPropertyValue);
break;
case CODING:
// TODO: handle "Ser/Plas^Donor"
String propertyValue = nextPropertyValue;
if (nextPropertyName.equals("COMPONENT")) {
if (propertyValue.contains("^")) {
propertyValue = propertyValue.substring(0, propertyValue.indexOf("^"));
} else if (propertyValue.contains("/")) {
propertyValue = propertyValue.substring(0, propertyValue.indexOf("/"));
}
}
PartTypeAndPartName key = new PartTypeAndPartName(nextPropertyName, propertyValue);
String partNumber = myPartTypeAndPartNameToPartNumber.get(key);
if (partNumber == null && nextPropertyName.equals("TIME_ASPCT")) {
key = new PartTypeAndPartName("TIME", nextPropertyValue);
partNumber = myPartTypeAndPartNameToPartNumber.get(key);
}
if (partNumber == null && nextPropertyName.equals("METHOD_TYP")) {
key = new PartTypeAndPartName("METHOD", nextPropertyValue);
partNumber = myPartTypeAndPartNameToPartNumber.get(key);
}
if (partNumber == null && nextPropertyName.equals("SCALE_TYP")) {
key = new PartTypeAndPartName("SCALE", nextPropertyValue);
partNumber = myPartTypeAndPartNameToPartNumber.get(key);
}
if (partNumber == null && nextPropertyName.equals("SYSTEM") && nextPropertyValue.startsWith("^")) {
continue;
}
if (isNotBlank(partNumber)) {
concept.addPropertyCoding(nextPropertyName, ITermLoaderSvc.LOINC_URI, partNumber, nextPropertyValue);
} else {
String msg = "Unable to find part code with TYPE[" + key.getPartType() + "] and NAME[" + nextPropertyValue + "] (using name " + propertyValue + ")";
ourLog.warn(msg);
// throw new InternalErrorException(msg);
}
// These are handles by the LOINC PartLink file
break;
case DECIMAL:
case CODE:
case INTEGER:

View File

@ -22,12 +22,17 @@ package ca.uhn.fhir.jpa.term.loinc;
import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
import ca.uhn.fhir.jpa.entity.TermConcept;
import ca.uhn.fhir.jpa.entity.TermConceptProperty;
import ca.uhn.fhir.jpa.term.IRecordHandler;
import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc;
import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
import org.apache.commons.csv.CSVRecord;
import org.hl7.fhir.r4.model.CodeSystem;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Map;
import java.util.Optional;
import static org.apache.commons.lang3.StringUtils.trim;
@ -36,40 +41,65 @@ public class LoincPartLinkHandler implements IRecordHandler {
private static final Logger ourLog = LoggerFactory.getLogger(LoincPartLinkHandler.class);
private final Map<String, TermConcept> myCode2Concept;
private final TermCodeSystemVersion myCodeSystemVersion;
private final Map<String, CodeSystem.PropertyType> myPropertyNames;
private Long myPartCount;
public LoincPartLinkHandler(TermCodeSystemVersion theCodeSystemVersion, Map<String, TermConcept> theCode2concept) {
public LoincPartLinkHandler(TermCodeSystemVersion theCodeSystemVersion, Map<String, TermConcept> theCode2concept, Map<String, CodeSystem.PropertyType> thePropertyNames) {
myCodeSystemVersion = theCodeSystemVersion;
myCode2Concept = theCode2concept;
myPropertyNames = thePropertyNames;
}
@Override
public void accept(CSVRecord theRecord) {
String loincNumber = trim(theRecord.get("LoincNumber"));
String longCommonName = trim(theRecord.get("LongCommonName"));
String property = trim(theRecord.get("Property"));
String partName = trim(theRecord.get("PartName"));
String partNumber = trim(theRecord.get("PartNumber"));
/*
* Property has the form http://loinc.org/property/COMPONENT
* but we want just the COMPONENT part
*/
int lastSlashIdx = property.lastIndexOf("/");
String propertyPart = property.substring(lastSlashIdx + 1);
TermConcept loincConcept = myCode2Concept.get(loincNumber);
TermConcept partConcept = myCode2Concept.get(partNumber);
if (loincConcept == null) {
ourLog.warn("No loinc code: {}", loincNumber);
return;
throw new InternalErrorException("Unknown loinc code: " + loincNumber);
}
if (partConcept == null) {
if (myPartCount == null) {
myPartCount = myCode2Concept
.keySet()
.stream()
.filter(t->t.startsWith("LP"))
.count();
}
ourLog.debug("No part code: {} - Have {} part codes", partNumber, myPartCount);
CodeSystem.PropertyType propertyType = myPropertyNames.get(propertyPart);
if (propertyType == null) {
return;
}
// For now we're ignoring these
String expectedValue;
if (propertyType == CodeSystem.PropertyType.STRING) {
expectedValue = partName;
} else if (propertyType == CodeSystem.PropertyType.CODING) {
expectedValue = partNumber;
} else {
throw new InternalErrorException("Don't know how to handle property of type: " + propertyType);
}
Optional<TermConceptProperty> existingProprty = loincConcept
.getProperties()
.stream()
.filter(t -> t.getKey().equals(propertyPart))
.filter(t -> t.getValue().equals(expectedValue))
.findFirst();
if (existingProprty.isPresent()) {
return;
}
ourLog.info("Adding new property {} = {}", propertyPart, partNumber);
if (propertyType == CodeSystem.PropertyType.STRING) {
loincConcept.addPropertyString(propertyPart, partName);
} else {
loincConcept.addPropertyCoding(propertyPart, ITermLoaderSvc.LOINC_URI, partNumber, partName);
}
}
}

View File

@ -88,6 +88,9 @@ public class LoincPartRelatedCodeMappingHandler extends BaseLoincHandler impleme
case "wider":
equivalence = Enumerations.ConceptMapEquivalence.WIDER;
break;
case "relatedto":
equivalence = Enumerations.ConceptMapEquivalence.RELATEDTO;
break;
default:
throw new InternalErrorException("Unknown equivalence '" + mapType + "' for PartNumber: " + partNumber);
}

View File

@ -67,9 +67,13 @@ public enum LoincUploadPropertiesEnum {
// Part
LOINC_PART_FILE("loinc.part.file"),
LOINC_PART_FILE_DEFAULT("AccessoryFiles/PartFile/Part.csv"),
// Part link
LOINC_PART_LINK_FILE("loinc.part.link.file"),
LOINC_PART_LINK_FILE_DEFAULT("AccessoryFiles/PartFile/LoincPartLink.csv"),
LOINC_PART_LINK_FILE_PRIMARY("loinc.part.link.primary.file"),
LOINC_PART_LINK_FILE_PRIMARY_DEFAULT("AccessoryFiles/PartFile/LoincPartLink_Primary.csv"),
LOINC_PART_LINK_FILE_SUPPLEMENTARY("loinc.part.link.supplementary.file"),
LOINC_PART_LINK_FILE_SUPPLEMENTARY_DEFAULT("AccessoryFiles/PartFile/LoincPartLink_Supplementary.csv"),
// Part related code mapping
LOINC_PART_RELATED_CODE_MAPPING_FILE("loinc.part.related.code.mapping.file"),
LOINC_PART_RELATED_CODE_MAPPING_FILE_DEFAULT("AccessoryFiles/PartFile/PartRelatedCodeMapping.csv"),

View File

@ -1,33 +1,30 @@
<!--
LOINC is a well maintained, version independent code system
LOINC is a freely available international standard for tests, measurements, and observations. It is a well maintained, version independent code system.
This CodeSystem resource describes 'LOINC' independent of
any particular version. There are notes about changes for
version specific LOINC code system resources.
Use of LOINC is governed by the LOINC License: https://loinc.org/license/
Note that the following set of codes constitute the
LOINC code systems:
This CodeSystem resource describes 'LOINC' independent of any particular version. There are notes about changes for version specific LOINC code system resources.
Note that the following set of codes are defined by the LOINC code systems:
- the main LOINC codes
- the LOINC answer codes (LA-) and the LOINC answer list codes (LL-)
- the Part codes in the Multiaxial Hierarchy
- the Part codes for the properties.
- the LOINC Answer codes (LA-) and the LOINC Answer list codes (LL-)
- the LOINC Part codes (LP-) in the Multiaxial Hierarchy
- the LOINC Part codes (LP-) for the properties
Note: there are license restrictions on the use of LOINC Part codes
- the LOINC Group codes (LG-)
Note: presently the LOINC Group codes are used to identify these roll-up groups as ValueSets, but are not yet loaded as codes in the CodeSystem
Servers may generate variants of this for the LOINC version(s) and features they support.
File Version: 0.6
-->
<!--
Version History of this specification
0.1 | published 2016 11 18
0.2 | published 2017 03 10 (fixed rad properties, removed list-specific LA properties, typos)
0.3 | published 2017 05 09 (removed CHNGE_TYP based on LOINC Committee recommendation, change filter types from code vs coding -> which allows use of the LP codes or the Part names)
0.4 | published 2018 02 09 (fixed multiaxial hierarchy relationship, added clarifying statement about English as the language for filters)
-->
<CodeSystem xmlns="http://hl7.org/fhir">
<id value="loinc"/>
<!-- This url is unchanged for all versions of LOINC. There
can only be one correct Code System resource for each value of the
version attribute (at least, only one per server) -->
<!--
This url is unchanged for all versions of LOINC. There can only be one correct Code System resource for each value of the version attribute (at least, only one per server).
-->
<url value="http://loinc.org"/>
<!-- the HL7 v3 OID assigned to LOINC -->
@ -37,11 +34,13 @@ Version History of this specification
</identifier>
<!--
// if a version is specified:
<version value="2.59"/>
If a version is specified:
<version value="2.68"/>
-->
<!-- if a specific version is specified, the name should carry this information should be in the name (e.g. LOINC_259) and title -->
<!--
If a specific version is specified, the name should carry this information (e.g. LOINC_268).
-->
<name value="LOINC"/>
<title value="LOINC Code System"/>
<status value="active"/>
@ -55,71 +54,66 @@ Version History of this specification
</contact>
<!--
<date value=[date for this version]"/>
<date value=2020-06/>
-->
<description value="LOINC is a freely available international standard for tests, measurements, and observations"/>
<copyright value="This content from LOINC® is copyright © 1995 Regenstrief Institute, Inc. and the LOINC Committee, and available at no cost under the license at http://loinc.org/terms-of-use"/>
<copyright value="This material contains content from LOINC (http://loinc.org). LOINC is copyright ©1995-2020, Regenstrief Institute, Inc. and the Logical Observation Identifiers Names and Codes (LOINC) Committee and is available at no cost under the license at http://loinc.org/license. LOINC® is a registered United States trademark of Regenstrief Institute, Inc."/>
<caseSensitive value="false"/>
<valueSet value=" http://loinc.org/vs"/>
<!--
for a version specific reference:
<valueSet value="http://loinc.org/2.56/vs"/>
For a version specific reference:
<valueSet value="http://loinc.org/2.68/vs"/>
-->
<!--
It's at the discretion of servers whether to present fragments of LOINC heirarchically or not, when
using the code system resource. But, if they are heirarchical, the Hierarchy SHALL be based on the is-a relationship that is derived from the LOINC Multiaxial Hierarchy.
It's at the discretion of servers whether to present fragments of LOINC hierarchically or not, when using the code system resource. But, if they are hierarchical, the Hierarchy SHALL be based on the is-a relationship that is derived from the LOINC Multiaxial Hierarchy.
-->
<HierarchyMeaning value="is-a"/>
<hierarchyMeaning value="is-a"/>
<compositional value="false"/> <!-- no compositional grammar in LOINC -->
<versionNeeded value="false"/>
<!-- this canonical definition of LOINC does not include the content.
Servers may choose to include fragments (but not, due to size constraints, all of LOINC) -->
<!--
This canonical definition of LOINC does not include the LOINC content, which is distributed separately for portability.
Servers may choose to include fragments of LOINC for illustration purposes.
-->
<content value="not-present"/>
<!-- <count value="65000"/>... if working with a specific version, you could nominate a count of the total number of concepts (including the answers, Hierarchy, etc.) -->
<!--
<count value="65000"/>
If working with a specific version, you could nominate a count of the total number of concepts (including the answers, Hierarchy, etc.). In this canonical definition we do not.
-->
<!--
FILTERS
Generally defined filters for specifying value sets
In LOINC, all the properties can be used as filters too, but they are not defined explicitly as filters as well.
Note that parent/child/ancestor/descendant are defined by FHIR, but repeated here to document them clearly.
In LOINC, all the properties can also be used as filters, but they are not defined explicitly as filters.
Parent/child properties are as defined by FHIR. Note that at this time the LOINC code system resource does not support ancestor/descendant relationships.
For illustration purposes, consider this slice of the LOINC Multiaxial Hierarchy when reading the descriptions:
For illustration purposes, consider this slice of the LOINC Multiaxial Hierarchy when reading the descriptions below:
Microbiology [LP31755-9]
Microbiology [LP7819-8]
Microorganism [LP14559-6]
Virus [LP14855-8]
Zika virus [LP200137-0]
Zika virus RNA | XXX [LP203413-2]
Zika virus RNA | XXX [LP203271-4]
Zika virus RNA | XXX | Microbiology [LP379670-5]
Zika virus RNA [Presence] in Unspecified specimen by Probe and target amplification method [79190-5]
Language Note: The filters defined here are specified using the default LOINC language - English (US). Requests are meant to be specified and interpreted on the English version. The return can be in a specified language (if supported by the server). But note that not all filters/properties have language translations available.
-->
<filter>
<code value="parent"/>
<description value="Allows for the selection of a set of codes based on their appearance in the LOINC Multiaxial Hierarchy. parent selects immediate children only. For example, the code '79190-5' has the parent 'LP203413-2'"/>
<description value="Allows for the selection of a set of codes based on their appearance in the LOINC Multiaxial Hierarchy. Parent selects immediate parent only. For example, the code '79190-5' has the parent 'LP379670-5'"/>
<operator value="="/>
<value value="A Part code"/>
</filter>
<filter>
<code value="child"/>
<description value="Allows for the selection of a set of codes based on their appearance in the LOINC Multiaxial Hierarchy. child selects immediate children only. For example, the code 'LP203413-2' has the child '79190-5'"/>
<operator value="in"/>
<value value="A comma separated list of Part codes"/>
</filter>
<filter>
<code value="ancestor"/>
<description value="Allows for the selection of a set of codes based on their appearance in the LOINC Multiaxial Hierarchy. ancestor includes parents transitively, e.g. 'LP203413-2' eventually has an ancestor 'LP14559-6', so the code '79190-5' is in the set of codes that have ancestor=LP14559-6"/>
<description value="Allows for the selection of a set of codes based on their appearance in the LOINC Multiaxial Hierarchy. Child selects immediate children only. For example, the code 'LP379670-5' has the child '79190-5'. Only LOINC Parts have children; LOINC codes do not have any children because they are leaf nodes."/>
<operator value="="/>
<value value="A Part code"/>
</filter>
<filter>
<code value="descendant"/>
<description value="Allows for the selection of a set of codes based on their appearance in the LOINC Multiaxial Hierarchy. descendant includes children transitively, e.g. 'LP14559-6' eventually has a descendant 'LP203413-2', so the code '79190-5' is in the set of codes that have descendant=LP14559-6"/>
<operator value="in"/>
<value value="A comma separated list of Part codes"/>
<value value="A comma separated list of Part or LOINC codes"/>
</filter>
<filter>
<code value="copyright"/>
@ -127,13 +121,22 @@ Version History of this specification
<operator value="="/>
<value value="LOINC | 3rdParty"/>
</filter>
<!-- properties. There are 3 kinds of properties:
fhir: display, designation; these are not described here since they are inherent in the specification
infrastructural: defined by FHIR, but documented here for LOINC
LOINC properties: defined by the main LOINC table
concept model: defined by the LOINC Multiaxial Hierarchy
<!--
PROPERTIES
There are 4 kinds of properties that apply to all LOINC codes:
1. FHIR: display, designation; these are not described here since they are inherent in the specification
2. Infrastructural: defined by FHIR, but documented here for the LOINC Multiaxial Hierarchy
3. Primary LOINC properties: defined by the main LOINC table
4. Secondary LOINC properties: defined by the LoincPartLink table
Additionally, there are 2 kinds of properties specific to Document ontology and Radiology codes, respectively:
1. LOINC/RSNA Radiology Playbook properties
2. Document Ontology properties
-->
<!-- first, the infrastructural properties - inherited from FHIR, but documented here -->
<!--
Infrastructural properties - inherited from FHIR, but documented here for the LOINC Multiaxial Hierarchy.
-->
<property>
<code value="parent"/>
<uri value="http://hl7.org/fhir/concept-properties#parent"/>
@ -146,20 +149,20 @@ Version History of this specification
<description value="A child code in the Multiaxial Hierarchy"/>
<type value=""/>
</property>
<!--
LOINC properties.
These apply to the main LOINC codes, but not the Multiaxial Hierarchy, the answer lists, or the part codes.
<!--
Primary LOINC properties.
These apply to the main LOINC codes, but not the Multiaxial Hierarchy, Answer lists, or the Part codes.
Notes:
SHORTNAME = display & LONG_COMMON_NAME = definition
Properties are specified as type "code", which are LOINC Part codes (LP-).
It is anticipated that the LOINC Part codes to be used in these properties will be published in the June 2017 LOINC release.
In the LOINC code system resource, the display element = LONG_COMMON_NAME
Many properties are specified as type "coding", which allows use of LOINC Part codes (LP-) and the display text. LOINC Parts and their associations to LOINC terms are published in the LOINC Part File.
The properties defined here follow the guidance of the LOINC Users' Manual, which states that they should be expressed with the LOINC attributes contained in the LOINC Table. Properties that are not defined in the LOINC Table use FHIR-styled names.
-->
<property>
<code value="STATUS"/>
<uri value="http://loinc.org/property/STATUS"/>
<description value="Status of the term. Within LOINC, codes with STATUS=DEPRECATED are considered inactive. Current values: ACTIVE, TRIAL, DISCOURAGED, and DEPRECATED"/>
<!-- DV NOTE: changed this from boolean to string -->
<type value="string"/>
</property>
<property>
@ -202,26 +205,12 @@ Version History of this specification
<code value="CLASS"/>
<uri value="http://loinc.org/property/CLASS"/>
<description value="An arbitrary classification of the terms for grouping related observations together"/>
<type value="string"/>
<type value="Coding"/>
</property>
<!-- Note: removed in 0.3
<property>
<code value="CHNG_TYPE"/>
<uri value="http://loinc.org/property/CHNG_TYPE"/>
<description value="A classification of the type of change made to a LOINC term, e.g. DEL=deprecated, ADD=add"/>
<type value="string"/>
</property>
-->
<property>
<code value="VersionLastChanged"/>
<uri value="http://loinc.org/property/VersionLastChanged"/>
<description value="The LOINC version number in which the record has last changed. For new records, this field contains the same value as the FirstPublishedRelease property."/>
<type value="string"/>
</property>
<property>
<code value="CONSUMER_NAME"/>
<uri value="http://loinc.org/property/CONSUMER_NAME"/>
<description value="An experimental (beta) consumer friendly name for this item. The intent is to provide a test name that health care consumers will recognize; it will be similar to the names that might appear on a lab report"/>
<description value="The LOINC version number in which the record has last changed. For new records, this field contains the same value as the VersionFirstReleased property."/>
<type value="string"/>
</property>
<property>
@ -260,22 +249,139 @@ Version History of this specification
<description value="A value of Y in this field indicates that this LOINC code can be sent by a payer as part of an HL7 Attachment request for additional information."/>
<type value="string"/>
</property>
<property>
<code value="DisplayName"/>
<uri value="http://loinc.org/property/DisplayName"/>
<description value="A name that is more 'clinician-friendly' compared to the current LOINC Short Name, Long Common Name, and Fully Specified Name. It is created algorithmically from the manually crafted display text for each Part and is generally more concise than the Long Common Name."/>
<type value="string"/>
</property>
<property>
<code value="answer-list"/>
<uri value="http://loinc.org/property/answer-list"/>
<description value="An answer list associated with this LOINC code (if there are matching answer lists defined)."/>
<type value="Coding"/>
</property>
<!-- LOINC/RSNA Radiology Playbook properties. These apply only to terms in the LOINC/RSNA Radiology Playbook File.
<!--
Secondary LOINC properties.
These properties also apply to the main LOINC codes, but not the Multiaxial Hierarchy, Answer lists, or the Part codes.
Notes:
Properties are specified as type "code", which are LOINC Part codes (LP-)
Converted the attribute names from LOINC style to FHIR style b/c they contained periods
Maneuver sub-attributes are being released in 2016 12.
These properties are defined in the LoincPartLink table.
-->
<property>
<code value="analyte"/>
<uri value="http://loinc.org/property/analyte"/>
<description value="First sub-part of the Component, i.e., the part of the Component before the first carat"/>
<type value="Coding"/>
</property>
<property>
<code value="analyte-core"/>
<uri value="http://loinc.org/property/analyte-core"/>
<description value="The primary part of the analyte without the suffix"/>
<type value="Coding"/>
</property>
<property>
<code value="analyte-suffix"/>
<uri value="http://loinc.org/property/analyte-suffix"/>
<description value="The suffix part of the analyte, if present, e.g., Ab or DNA"/>
<type value="Coding"/>
</property>
<property>
<code value="analyte-numerator"/>
<uri value="http://loinc.org/property/analyte-numerator"/>
<description value="The numerator part of the analyte, i.e., everything before the slash in analytes that contain a divisor"/>
<type value="Coding"/>
</property>
<property>
<code value="analyte-divisor"/>
<uri value="http://loinc.org/property/analyte-divisor"/>
<description value="The divisor part of the analyte, if present, i.e., after the slash and before the first carat"/>
<type value="Coding"/>
</property>
<property>
<code value="analyte-divisor-suffix"/>
<uri value="http://loinc.org/property/analyte-divisor-suffix"/>
<description value="The suffix part of the divisor, if present"/>
<type value="Coding"/>
</property>
<property>
<code value="challenge"/>
<uri value="http://loinc.org/property/challenge"/>
<description value="Second sub-part of the Component, i.e., after the first carat"/>
<type value="Coding"/>
</property>
<property>
<code value="adjustment"/>
<uri value="http://loinc.org/property/adjustment"/>
<description value="Third sub-part of the Component, i.e., after the second carat"/>
<type value="Coding"/>
</property>
<property>
<code value="count"/>
<uri value="http://loinc.org/property/count"/>
<description value="Fourth sub-part of the Component, i.e., after the third carat"/>
<type value="Coding"/>
</property>
<property>
<code value="time-core"/>
<uri value="http://loinc.org/property/time-core"/>
<description value="The primary part of the Time"/>
<type value="Coding"/>
</property>
<property>
<code value="time-modifier"/>
<uri value="http://loinc.org/property/time-modifier"/>
<description value="The modifier of the Time value, such as mean or max"/>
<type value="Coding"/>
</property>
<property>
<code value="system-core"/>
<uri value="http://loinc.org/property/system-core"/>
<description value="The primary part of the System, i.e., without the super system"/>
<type value="Coding"/>
</property>
<property>
<code value="super-system"/>
<uri value="http://loinc.org/property/super-system"/>
<description value="The super system part of the System, if present. The super system represents the source of the specimen when the source is someone or something other than the patient whose chart the result will be stored in. For example, fetus is the super system for measurements done on obstetric ultrasounds, because the fetus is being measured and that measurement is being recorded in the patient's (mother's) chart."/>
<type value="Coding"/>
</property>
<property>
<code value="analyte-gene"/>
<uri value="http://loinc.org/property/analyte-gene"/>
<description value="The specific gene represented in the analyte"/>
<type value="Coding"/>
</property>
<property>
<code value="category"/>
<uri value="http://loinc.org/property/category"/>
<description value="A single LOINC term can be assigned one or more categories based on both programmatic and manual tagging. Category properties also utilize LOINC Class Parts."/>
<type value="Coding"/>
</property>
<property>
<code value="search"/>
<uri value="http://loinc.org/property/search"/>
<description value="Synonyms, fragments, and other Parts that are linked to a term to enable more encompassing search results."/>
<type value="Coding"/>
</property>
<!--
LOINC/RSNA Radiology Playbook properties. These apply only to terms in the LOINC/RSNA Radiology Playbook File.
Notes:
Properties are specified as type "coding", which are represented by LOINC Part codes (LP-) and their display names.
The attribute names here use FHIR styled names rather than their original LOINC style names because the original names contain periods.
-->
<property>
<code value="rad-modality-modality-type"/>
<uri value="http://loinc.org/property/rad-modality-type"/>
<uri value="http://loinc.org/property/rad-modality-modality-type"/>
<description value="Modality is used to represent the device used to acquire imaging information."/>
<type value="Coding"/>
</property>
<property>
<code value="rad-modality-modality-subtype"/>
<uri value="http://loinc.org/property/rad-modality-subtype"/>
<uri value="http://loinc.org/property/rad-modality-modality-subtype"/>
<description value="Modality subtype may be optionally included to signify a particularly common or evocative configuration of the modality."/>
<type value="Coding"/>
</property>
@ -304,7 +410,7 @@ Version History of this specification
<type value="Coding"/>
</property>
<property>
<code value="rad-view-view-aggregation"/>
<code value="rad-view-aggregation"/>
<uri value="http://loinc.org/property/rad-view-aggregation"/>
<description value="Aggregation describes the extent of the imaging performed, whether in quantitative terms (e.g., '3 or more views') or subjective terms (e.g., 'complete')."/>
<type value="Coding"/>
@ -324,7 +430,7 @@ Version History of this specification
<property>
<code value="rad-timing"/>
<uri value="http://loinc.org/property/rad-timing"/>
<description value="The Timing/Existence property used in conjunction with pharmaceutical and manueuver properties. It specifies whether or not the imaging occurs in the presence of the administered pharmaceutical or a manuever designed to test some dynamic aspect of anatomy or physiology ."/>
<description value="The Timing/Existence property used in conjunction with pharmaceutical and maneuver properties. It specifies whether or not the imaging occurs in the presence of the administered pharmaceutical or a maneuver designed to test some dynamic aspect of anatomy or physiology ."/>
<type value="Coding"/>
</property>
<property>
@ -336,7 +442,7 @@ Version History of this specification
<property>
<code value="rad-pharmaceutical-route"/>
<uri value="http://loinc.org/property/rad-pharmaceutical-route"/>
<description value="Route specifies the route of administration of the pharmeceutical."/>
<description value="Route specifies the route of administration of the pharmaceutical."/>
<type value="Coding"/>
</property>
<property>
@ -375,11 +481,15 @@ Version History of this specification
<description value="Subject is intended for use when there is a need to distinguish between the patient associated with an imaging study, and the target of the study."/>
<type value="Coding"/>
</property>
<!-- Document Ontology properties. These apply only to terms in the LOINC Document Ontology File
Notes:
Properties are specified as type "code", which are LOINC Part codes (LP-)
Converted the attribute names from LOINC style to FHIR style b/c they contained periods
<!--
Document Ontology properties.
These apply only to terms in the LOINC Document Ontology File
Notes
Properties are specified as type "coding", which are represented by LOINC Part codes (LP-) and their display names.
The attribute names here use FHIR styled names rather than their original LOINC style names because those contain periods.
-->
<property>
<code value="document-kind"/>
<uri value="http://loinc.org/property/document-kind"/>
@ -389,7 +499,7 @@ Version History of this specification
<property>
<code value="document-role"/>
<uri value="http://loinc.org/property/document-role"/>
<description value="Characterizes the training or professional level of the author of the document, but does not break down to specialty or subspecialty.."/>
<description value="Characterizes the training or professional level of the author of the document, but does not break down to specialty or subspecialty."/>
<type value="Coding"/>
</property>
<property>
@ -410,21 +520,16 @@ Version History of this specification
<description value="Characterizes the kind of service or activity provided to/for the patient (or other subject of the service) that is described in the document."/>
<type value="Coding"/>
</property>
<!-- Answer list related properties -->
<property>
<code value="answer-list"/>
<uri value="http://loinc.org/property/answer-list"/>
<description value="An answer list associated with this LOINC code (if there are matching answer lists defined). Only on normal LOINC Codes"/>
<type value="Coding"/>
</property>
<!-- Note: We expect to add an AnswerListType property when LOINC publishes new answer list file format in June 2017 -->
<property>
<code value="answers-for"/>
<uri value="http://loinc.org/property/answers-for"/>
<description value="A LOINC Code for which this answer list is used. Only on normal LL- Codes"/>
<description value="A LOINC Code for which this answer list is used."/>
<type value="Coding"/>
</property>
<!-- Note for future consideration. These are properties of LA codes in the context of a particular list. Not global properties
<!-- Note for future consideration. These are properties of LA codes in the context of a particular list. Not global properties.
<property>
<code value="sequence"/>
<uri value="http://loinc.org/property/sequence"/>
@ -440,3 +545,4 @@ Version History of this specification
-->
</CodeSystem>

View File

@ -0,0 +1,83 @@
#################
### MANDATORY ###
#################
# Answer lists (ValueSets of potential answers/values for LOINC "questions")
## File must be present
loinc.answerlist.file=AccessoryFiles/AnswerFile/AnswerList.csv
# Answer list links (connects LOINC observation codes to answer list codes)
## File must be present
loinc.answerlist.link.file=AccessoryFiles/AnswerFile/LoincAnswerListLink.csv
# Document ontology
## File must be present
loinc.document.ontology.file=AccessoryFiles/DocumentOntology/DocumentOntology.csv
# LOINC codes
## File must be present
loinc.file=LoincTable/Loinc.csv
# LOINC hierarchy
## File must be present
loinc.hierarchy.file=AccessoryFiles/MultiAxialHierarchy/MultiAxialHierarchy.csv
# IEEE medical device codes
## File must be present
loinc.ieee.medical.device.code.mapping.table.file=AccessoryFiles/LoincIeeeMedicalDeviceCodeMappingTable/LoincIeeeMedicalDeviceCodeMappingTable.csv
# Imaging document codes
## File must be present
loinc.imaging.document.codes.file=AccessoryFiles/ImagingDocuments/ImagingDocumentCodes.csv
# Part
## File must be present
loinc.part.file=AccessoryFiles/PartFile/Part.csv
# Part link
## File must be present
loinc.part.link.primary.file=AccessoryFiles/PartFile/LoincPartLink_Primary.csv
loinc.part.link.supplementary.file=AccessoryFiles/PartFile/LoincPartLink_Supplementary.csv
# Part related code mapping
## File must be present
loinc.part.related.code.mapping.file=AccessoryFiles/PartFile/PartRelatedCodeMapping.csv
# RSNA playbook
## File must be present
loinc.rsna.playbook.file=AccessoryFiles/LoincRsnaRadiologyPlaybook/LoincRsnaRadiologyPlaybook.csv
# Top 2000 codes - SI
## File must be present
loinc.top2000.common.lab.results.si.file=AccessoryFiles/Top2000Results/SI/Top2000CommonLabResultsSi.csv
# Top 2000 codes - US
## File must be present
loinc.top2000.common.lab.results.us.file=AccessoryFiles/Top2000Results/US/Top2000CommonLabResultsUs.csv
# Universal lab order ValueSet
## File must be present
loinc.universal.lab.order.valueset.file=AccessoryFiles/LoincUniversalLabOrdersValueSet/LoincUniversalLabOrdersValueSet.csv
################
### OPTIONAL ###
################
# This is the version identifier for the answer list file
## Key may be omitted
loinc.answerlist.version=Beta.1
# This is the version identifier for uploaded ConceptMap resources
## Key may be omitted
loinc.conceptmap.version=Beta.1
# Group
## Default value if key not provided: AccessoryFiles/GroupFile/Group.csv
## File may be omitted
loinc.group.file=AccessoryFiles/GroupFile/Group.csv
# Group terms
## Default value if key not provided: AccessoryFiles/GroupFile/GroupLoincTerms.csv
## File may be omitted
loinc.group.terms.file=AccessoryFiles/GroupFile/GroupLoincTerms.csv
# Parent group
## Default value if key not provided: AccessoryFiles/GroupFile/ParentGroup.csv
## File may be omitted
loinc.parent.group.file=AccessoryFiles/GroupFile/ParentGroup.csv

View File

@ -492,7 +492,8 @@ public class TerminologyUploaderProviderR4Test extends BaseResourceProviderR4Tes
addFile(zos, LOINC_GROUP_FILE_DEFAULT.getCode());
addFile(zos, LOINC_GROUP_TERMS_FILE_DEFAULT.getCode());
addFile(zos, LOINC_PARENT_GROUP_FILE_DEFAULT.getCode());
addFile(zos, LOINC_PART_LINK_FILE_DEFAULT.getCode());
addFile(zos, LOINC_PART_LINK_FILE_PRIMARY_DEFAULT.getCode());
addFile(zos, LOINC_PART_LINK_FILE_SUPPLEMENTARY_DEFAULT.getCode());
addFile(zos, LOINC_PART_RELATED_CODE_MAPPING_FILE_DEFAULT.getCode());
addFile(zos, LOINC_DOCUMENT_ONTOLOGY_FILE_DEFAULT.getCode());
addFile(zos, LOINC_RSNA_PLAYBOOK_FILE_DEFAULT.getCode());

View File

@ -86,7 +86,7 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test {
Set<String> codes = toExpandedCodes(expanded);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded));
ourLog.info("Codes: {}", codes);
assertThat(codes, containsInAnyOrder("10019-8", "10013-1", "10014-9", "10016-4", "17788-1", "10000-8", "10017-2", "10015-6", "10020-6", "10018-0"));
assertThat(codes, containsInAnyOrder("10013-1"));
// Search by display name
input = new ValueSet();
@ -101,7 +101,7 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test {
expanded = myValueSetDao.expand(input, null);
codes = toExpandedCodes(expanded);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded));
assertThat(codes, containsInAnyOrder("10019-8", "10013-1", "10014-9", "10016-4", "17788-1", "10000-8", "10017-2", "10015-6", "10020-6", "10018-0"));
assertThat(codes, containsInAnyOrder("10013-1"));
// Search by something that doesn't match
input = new ValueSet();
@ -149,7 +149,7 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test {
Set<String> codes = toExpandedCodes(expanded);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded));
ourLog.info("Codes: {}", codes);
assertThat(codes, containsInAnyOrder("10019-8", "10013-1", "10014-9", "10000-8", "10016-4", "10017-2", "10015-6", "10020-6", "10018-0"));
assertThat(codes, containsInAnyOrder("10013-1"));
}
@Test
@ -188,7 +188,7 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test {
TerminologyLoaderSvcLoincTest.addLoincMandatoryFilesToZip(files);
myLoader.loadLoinc(files.getFiles(), mySrd);
IValidationSupport.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("17788-1"), new StringType(ITermLoaderSvc.LOINC_URI), null, mySrd);
IValidationSupport.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("10013-1"), new StringType(ITermLoaderSvc.LOINC_URI), null, mySrd);
Parameters parameters = (Parameters) result.toParameters(myFhirCtx, null);
ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(parameters));
@ -196,8 +196,8 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test {
Optional<Coding> propertyValue = findProperty(parameters, "COMPONENT");
assertTrue(propertyValue.isPresent());
assertEquals(ITermLoaderSvc.LOINC_URI, propertyValue.get().getSystem());
assertEquals("LP19258-0", propertyValue.get().getCode());
assertEquals("Large unstained cells/100 leukocytes", propertyValue.get().getDisplay());
assertEquals("LP31101-6", propertyValue.get().getCode());
assertEquals("R' wave amplitude.lead I", propertyValue.get().getDisplay());
}
@Test

View File

@ -76,18 +76,20 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest {
// Normal LOINC code
code = concepts.get("10013-1");
assertEquals("10013-1", code.getCode());
// Coding Property
assertEquals(ITermLoaderSvc.LOINC_URI, code.getCodingProperties("PROPERTY").get(0).getSystem());
assertEquals("LP6802-5", code.getCodingProperties("PROPERTY").get(0).getCode());
assertEquals("Elpot", code.getCodingProperties("PROPERTY").get(0).getDisplay());
assertEquals("EKG.MEAS", code.getStringProperty("CLASS"));
// String Property
assertEquals("2", code.getStringProperty("CLASSTYPE"));
assertEquals("R' wave amplitude in lead I", code.getDisplay());
// Coding Property from Part File
assertEquals(ITermLoaderSvc.LOINC_URI, code.getCodingProperties("TIME_ASPCT").get(0).getSystem());
assertEquals("LP6960-1", code.getCodingProperties("TIME_ASPCT").get(0).getCode());
assertEquals("Pt", code.getCodingProperties("TIME_ASPCT").get(0).getDisplay());
// Code with component that has a divisor
code = concepts.get("17788-1");
assertEquals("17788-1", code.getCode());
assertEquals(1, code.getCodingProperties("COMPONENT").size());
assertEquals("http://loinc.org", code.getCodingProperties("COMPONENT").get(0).getSystem());
assertEquals("LP19258-0", code.getCodingProperties("COMPONENT").get(0).getCode());
// LOINC code with answer
code = concepts.get("61438-8");
@ -379,7 +381,8 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest {
theFiles.addFileZip("/loinc/", LOINC_ANSWERLIST_LINK_FILE_DEFAULT.getCode());
theFiles.addFileZip("/loinc/", LOINC_ANSWERLIST_LINK_DUPLICATE_FILE_DEFAULT.getCode());
theFiles.addFileZip("/loinc/", LOINC_PART_FILE_DEFAULT.getCode());
theFiles.addFileZip("/loinc/", LOINC_PART_LINK_FILE_DEFAULT.getCode());
theFiles.addFileZip("/loinc/", LOINC_PART_LINK_FILE_PRIMARY_DEFAULT.getCode());
theFiles.addFileZip("/loinc/", LOINC_PART_LINK_FILE_SUPPLEMENTARY_DEFAULT.getCode());
theFiles.addFileZip("/loinc/", LOINC_PART_RELATED_CODE_MAPPING_FILE_DEFAULT.getCode());
theFiles.addFileZip("/loinc/", LOINC_DOCUMENT_ONTOLOGY_FILE_DEFAULT.getCode());
theFiles.addFileZip("/loinc/", LOINC_RSNA_PLAYBOOK_FILE_DEFAULT.getCode());
@ -413,7 +416,10 @@ public class TerminologyLoaderSvcLoincTest extends BaseLoaderTest {
assertEquals(ITermLoaderSvc.LOINC_URI, code.getCodingProperties("PROPERTY").get(0).getSystem());
assertEquals("LP6802-5", code.getCodingProperties("PROPERTY").get(0).getCode());
assertEquals("Elpot", code.getCodingProperties("PROPERTY").get(0).getDisplay());
assertEquals("EKG.MEAS", code.getStringProperty("CLASS"));
assertEquals(ITermLoaderSvc.LOINC_URI, code.getCodingProperties("PROPERTY").get(0).getSystem());
assertEquals("LP6802-5", code.getCodingProperties("PROPERTY").get(0).getCode());
assertEquals("Elpot", code.getCodingProperties("PROPERTY").get(0).getDisplay());
assertEquals("2", code.getStringProperty("CLASSTYPE"));
assertEquals("R' wave amplitude in lead I", code.getDisplay());
// Codes with parent and child properties

View File

@ -1,10 +0,0 @@
"LoincNumber","LongCommonName","PartNumber","PartName","PartTypeName","LinkTypeName"
"10000-8","R wave duration in lead AVR","LP31088-5","R wave duration.lead AVR","COMPONENT","Primary"
"10000-8","R wave duration in lead AVR","LP6244-0","EKG","METHOD","Primary"
"10000-8","R wave duration in lead AVR","LP6879-3","Time","PROPERTY","Primary"
"10000-8","R wave duration in lead AVR","LP6960-1","Pt","TIME","Primary"
"10000-8","R wave duration in lead AVR","LP7289-4","Heart","SYSTEM","Primary"
"10000-8","R wave duration in lead AVR","LP7753-9","Qn","SCALE","Primary"
"10000-8","R wave duration in lead AVR","LP7795-0","EKG.MEAS","CLASS","Primary"
"10000-8","R wave duration in lead AVR","LP14259-3","Lead","COMPONENT","Search"
"10000-8","R wave duration in lead AVR","LP14744-4","Duration","COMPONENT","Search"
1 LoincNumber LongCommonName PartNumber PartName PartTypeName LinkTypeName
2 10000-8 R wave duration in lead AVR LP31088-5 R wave duration.lead AVR COMPONENT Primary
3 10000-8 R wave duration in lead AVR LP6244-0 EKG METHOD Primary
4 10000-8 R wave duration in lead AVR LP6879-3 Time PROPERTY Primary
5 10000-8 R wave duration in lead AVR LP6960-1 Pt TIME Primary
6 10000-8 R wave duration in lead AVR LP7289-4 Heart SYSTEM Primary
7 10000-8 R wave duration in lead AVR LP7753-9 Qn SCALE Primary
8 10000-8 R wave duration in lead AVR LP7795-0 EKG.MEAS CLASS Primary
9 10000-8 R wave duration in lead AVR LP14259-3 Lead COMPONENT Search
10 10000-8 R wave duration in lead AVR LP14744-4 Duration COMPONENT Search

View File

@ -0,0 +1,7 @@
"LoincNumber","LongCommonName" ,"PartNumber","PartName" ,"PartCodeSystem" ,"PartTypeName","LinkTypeName","Property"
"10013-1" ,"R' wave amplitude in lead I","LP31101-6" ,"R' wave amplitude.lead I","http://loinc.org","COMPONENT" ,"Primary" ,"http://loinc.org/property/COMPONENT"
"10013-1" ,"R' wave amplitude in lead I","LP6802-5" ,"Elpot" ,"http://loinc.org","PROPERTY" ,"Primary" ,"http://loinc.org/property/PROPERTY"
"10013-1" ,"R' wave amplitude in lead I","LP6960-1" ,"Pt" ,"http://loinc.org","TIME" ,"Primary" ,"http://loinc.org/property/TIME_ASPCT"
"10013-1" ,"R' wave amplitude in lead I","LP7289-4" ,"Heart" ,"http://loinc.org","SYSTEM" ,"Primary" ,"http://loinc.org/property/SYSTEM"
"10013-1" ,"R' wave amplitude in lead I","LP7753-9" ,"Qn" ,"http://loinc.org","SCALE" ,"Primary" ,"http://loinc.org/property/SCALE_TYP"
"10013-1" ,"R' wave amplitude in lead I","LP6244-0" ,"EKG" ,"http://loinc.org","METHOD" ,"Primary" ,"http://loinc.org/property/METHOD_TYP"
Can't render this file because it contains an unexpected character in line 1 and column 30.

View File

@ -0,0 +1,13 @@
"LoincNumber","LongCommonName" ,"PartNumber","PartName" ,"PartCodeSystem" ,"PartTypeName","LinkTypeName" ,"Property"
"10013-1" ,"R' wave amplitude in lead I","LP31101-6" ,"R' wave amplitude.lead I","http://loinc.org","COMPONENT" ,"DetailedModel" ,"http://loinc.org/property/analyte"
"10013-1" ,"R' wave amplitude in lead I","LP6802-5" ,"Elpot" ,"http://loinc.org","PROPERTY" ,"DetailedModel" ,"http://loinc.org/property/PROPERTY"
"10013-1" ,"R' wave amplitude in lead I","LP6960-1" ,"Pt" ,"http://loinc.org","TIME" ,"DetailedModel" ,"http://loinc.org/property/time-core"
"10013-1" ,"R' wave amplitude in lead I","LP7289-4" ,"Heart" ,"http://loinc.org","SYSTEM" ,"DetailedModel" ,"http://loinc.org/property/system-core"
"10013-1" ,"R' wave amplitude in lead I","LP7753-9" ,"Qn" ,"http://loinc.org","SCALE" ,"DetailedModel" ,"http://loinc.org/property/SCALE_TYP"
"10013-1" ,"R' wave amplitude in lead I","LP6244-0" ,"EKG" ,"http://loinc.org","METHOD" ,"DetailedModel" ,"http://loinc.org/property/METHOD_TYP"
"10013-1" ,"R' wave amplitude in lead I","LP31101-6" ,"R' wave amplitude.lead I","http://loinc.org","COMPONENT" ,"SyntaxEnhancement","http://loinc.org/property/analyte-core"
"10013-1" ,"R' wave amplitude in lead I","LP190563-9","Cardiology" ,"http://loinc.org","CLASS" ,"Metadata" ,"http://loinc.org/property/category"
"10013-1" ,"R' wave amplitude in lead I","LP29708-2" ,"Cardiology" ,"http://loinc.org","CLASS" ,"Metadata" ,"http://loinc.org/property/category"
"10013-1" ,"R' wave amplitude in lead I","LP7787-7" ,"Clinical" ,"http://loinc.org","CLASS" ,"Metadata" ,"http://loinc.org/property/category"
"10013-1" ,"R' wave amplitude in lead I","LP7795-0" ,"EKG measurements" ,"http://loinc.org","CLASS" ,"Metadata" ,"http://loinc.org/property/category"
"10013-1" ,"R' wave amplitude in lead I","LP7795-0" ,"EKG.MEAS" ,"http://loinc.org","CLASS" ,"Metadata" ,"http://loinc.org/property/CLASS"
Can't render this file because it contains an unexpected character in line 1 and column 30.

View File

@ -3,68 +3,57 @@
#################
# Answer lists (ValueSets of potential answers/values for LOINC "questions")
## Default value if key not provided: AccessoryFiles/AnswerFile/AnswerList.csv
## File must be present
loinc.answerlist.file=AccessoryFiles/AnswerFile/AnswerList.csv
# Answer list links (connects LOINC observation codes to answer list codes)
## Default value if key not provided: AccessoryFiles/AnswerFile/LoincAnswerListLink.csv
## File must be present
loinc.answerlist.link.file=AccessoryFiles/AnswerFile/LoincAnswerListLink.csv
# Document ontology
## Default value if key not provided: AccessoryFiles/DocumentOntology/DocumentOntology.csv
## File must be present
loinc.document.ontology.file=AccessoryFiles/DocumentOntology/DocumentOntology.csv
# LOINC codes
## Default value if key not provided: LoincTable/Loinc.csv
## File must be present
loinc.file=LoincTable/Loinc.csv
# LOINC hierarchy
## Default value if key not provided: AccessoryFiles/MultiAxialHierarchy/MultiAxialHierarchy.csv
## File must be present
loinc.hierarchy.file=AccessoryFiles/MultiAxialHierarchy/MultiAxialHierarchy.csv
# IEEE medical device codes
## Default value if key not provided: AccessoryFiles/LoincIeeeMedicalDeviceCodeMappingTable/LoincIeeeMedicalDeviceCodeMappingTable.csv
## File must be present
loinc.ieee.medical.device.code.mapping.table.file=AccessoryFiles/LoincIeeeMedicalDeviceCodeMappingTable/LoincIeeeMedicalDeviceCodeMappingTable.csv
# Imaging document codes
## Default value if key not provided: AccessoryFiles/ImagingDocuments/ImagingDocumentCodes.csv
## File must be present
loinc.imaging.document.codes.file=AccessoryFiles/ImagingDocuments/ImagingDocumentCodes.csv
# Part
## Default value if key not provided: AccessoryFiles/PartFile/Part.csv
## File must be present
loinc.part.file=AccessoryFiles/PartFile/Part.csv
# Part link
## Default value if key not provided: AccessoryFiles/PartFile/LoincPartLink.csv
## File must be present
loinc.part.link.file=AccessoryFiles/PartFile/LoincPartLink.csv
loinc.part.link.primary.file=AccessoryFiles/PartFile/LoincPartLink_Primary.csv
loinc.part.link.supplementary.file=AccessoryFiles/PartFile/LoincPartLink_Supplementary.csv
# Part related code mapping
## Default value if key not provided: AccessoryFiles/PartFile/PartRelatedCodeMapping.csv
## File must be present
loinc.part.related.code.mapping.file=AccessoryFiles/PartFile/PartRelatedCodeMapping.csv
# RSNA playbook
## Default value if key not provided: AccessoryFiles/LoincRsnaRadiologyPlaybook/LoincRsnaRadiologyPlaybook.csv
## File must be present
loinc.rsna.playbook.file=AccessoryFiles/LoincRsnaRadiologyPlaybook/LoincRsnaRadiologyPlaybook.csv
# Top 2000 codes - SI
## Default value if key not provided: AccessoryFiles/Top2000Results/SI/Top2000CommonLabResultsSi.csv
## File must be present
loinc.top2000.common.lab.results.si.file=AccessoryFiles/Top2000Results/SI/Top2000CommonLabResultsSi.csv
# Top 2000 codes - US
## Default value if key not provided: AccessoryFiles/Top2000Results/US/Top2000CommonLabResultsUs.csv
## File must be present
loinc.top2000.common.lab.results.us.file=AccessoryFiles/Top2000Results/US/Top2000CommonLabResultsUs.csv
# Universal lab order ValueSet
## Default value if key not provided: AccessoryFiles/LoincUniversalLabOrdersValueSet/LoincUniversalLabOrdersValueSet.csv
## File must be present
loinc.universal.lab.order.valueset.file=AccessoryFiles/LoincUniversalLabOrdersValueSet/LoincUniversalLabOrdersValueSet.csv

View File

@ -6,10 +6,30 @@ import ca.uhn.fhir.context.FhirContext;
import ca.uhn.fhir.model.api.annotation.Block;
import ca.uhn.fhir.parser.DataFormatException;
import org.hamcrest.Matchers;
import org.hl7.fhir.instance.model.api.*;
import org.hl7.fhir.r4.model.*;
import org.hl7.fhir.instance.model.api.IBase;
import org.hl7.fhir.instance.model.api.IBaseExtension;
import org.hl7.fhir.instance.model.api.IBaseReference;
import org.hl7.fhir.instance.model.api.IBaseResource;
import org.hl7.fhir.instance.model.api.IPrimitiveType;
import org.hl7.fhir.r4.model.BooleanType;
import org.hl7.fhir.r4.model.Bundle;
import org.hl7.fhir.r4.model.Enumeration;
import org.hl7.fhir.r4.model.Enumerations;
import org.hl7.fhir.r4.model.Extension;
import org.hl7.fhir.r4.model.Identifier;
import org.hl7.fhir.r4.model.MarkdownType;
import org.hl7.fhir.r4.model.Money;
import org.hl7.fhir.r4.model.Observation;
import org.hl7.fhir.r4.model.Organization;
import org.hl7.fhir.r4.model.Patient;
import org.hl7.fhir.r4.model.Patient.LinkType;
import org.hl7.fhir.r4.model.Practitioner;
import org.hl7.fhir.r4.model.PrimitiveType;
import org.hl7.fhir.r4.model.Quantity;
import org.hl7.fhir.r4.model.Reference;
import org.hl7.fhir.r4.model.SimpleQuantity;
import org.hl7.fhir.r4.model.StringType;
import org.hl7.fhir.r4.model.ValueSet;
import org.junit.AfterClass;
import org.junit.Assert;
import org.junit.Test;
@ -19,11 +39,22 @@ import org.slf4j.LoggerFactory;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.*;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import static org.hamcrest.Matchers.containsInAnyOrder;
import static org.junit.Assert.*;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;