From 488890155cb525b00ace204ce386d9de7b0c8a8c Mon Sep 17 00:00:00 2001 From: James Agnew Date: Sat, 21 Apr 2018 14:32:17 -0400 Subject: [PATCH] Loinc uploader updates --- hapi-fhir-jpaserver-base/pom.xml | 4 +- .../jpa/dao/IFhirResourceDaoCodeSystem.java | 25 ++- .../TermConceptPropertyFieldBridge.java | 5 + ...aseJpaResourceProviderCodeSystemDstu3.java | 26 ++-- .../BaseJpaResourceProviderCodeSystemR4.java | 22 ++- .../jpa/term/BaseHapiTerminologySvcImpl.java | 1 - .../jpa/term/TerminologyLoaderSvcImpl.java | 56 ++++--- ...BaseHandler.java => BaseLoincHandler.java} | 46 ++++-- .../BaseLoincTop2000LabResultsHandler.java | 9 +- .../term/loinc/LoincAnswerListHandler.java | 24 ++- .../loinc/LoincDocumentOntologyHandler.java | 18 +-- .../uhn/fhir/jpa/term/loinc/LoincHandler.java | 66 +++++++- .../LoincIeeeMedicalDeviceCodeHandler.java | 7 +- .../LoincImagingDocumentCodeHandler.java | 9 +- .../fhir/jpa/term/loinc/LoincPartHandler.java | 17 ++- .../LoincPartRelatedCodeMappingHandler.java | 36 +++-- .../term/loinc/LoincRsnaPlaybookHandler.java | 11 +- .../LoincTop2000LabResultsSiHandler.java | 5 +- .../LoincTop2000LabResultsUsHandler.java | 5 +- .../loinc/LoincUniversalOrderSetHandler.java | 8 +- .../jpa/term/loinc/PartTypeAndPartName.java | 57 +++++++ ...minologyLoaderSvcIntegrationDstu3Test.java | 143 +++++++++++++++--- .../term/TerminologyLoaderSvcLoincTest.java | 23 +-- .../src/test/resources/loinc/Part_Beta_1.csv | 30 ++++ .../resources/loinc/loincupload.properties | 7 + 25 files changed, 493 insertions(+), 167 deletions(-) rename hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/{BaseHandler.java => BaseLoincHandler.java} (82%) create mode 100644 hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/PartTypeAndPartName.java create mode 100644 hapi-fhir-jpaserver-base/src/test/resources/loinc/loincupload.properties diff --git a/hapi-fhir-jpaserver-base/pom.xml b/hapi-fhir-jpaserver-base/pom.xml index 4bf8b6d08b4..3a3b8566e53 100644 --- a/hapi-fhir-jpaserver-base/pom.xml +++ b/hapi-fhir-jpaserver-base/pom.xml @@ -717,10 +717,10 @@ - ${basedir}/src/main/resources + ${project.basedir}/src/main/resources - ${basedir}/target/generated-resources/tinder + ${project.basedir}/target/generated-resources/tinder diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoCodeSystem.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoCodeSystem.java index ca5fc4d5231..c652c2a9b22 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoCodeSystem.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/dao/IFhirResourceDaoCodeSystem.java @@ -8,7 +8,10 @@ import org.hl7.fhir.instance.model.api.IIdType; import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.hl7.fhir.r4.model.*; +import java.util.Collections; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; import static org.apache.commons.lang3.StringUtils.isNotBlank; @@ -21,9 +24,9 @@ import static org.apache.commons.lang3.StringUtils.isNotBlank; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -122,7 +125,7 @@ public interface IFhirResourceDaoCodeSystem ext } } - public Parameters toParameters() { + public Parameters toParameters(List> theProperties) { Parameters retVal = new Parameters(); retVal.addParameter().setName("name").setValue(new StringType(getCodeSystemDisplayName())); @@ -133,7 +136,23 @@ public interface IFhirResourceDaoCodeSystem ext retVal.addParameter().setName("abstract").setValue(new BooleanType(isCodeIsAbstract())); if (myProperties != null) { + + Set properties = Collections.emptySet(); + if (theProperties != null) { + properties = theProperties + .stream() + .map(IPrimitiveType::getValueAsString) + .collect(Collectors.toSet()); + } + for (IContextValidationSupport.BaseConceptProperty next : myProperties) { + + if (!properties.isEmpty()) { + if (!properties.contains(next.getPropertyName())) { + continue; + } + } + Parameters.ParametersParameterComponent property = retVal.addParameter().setName("property"); property .addPart() diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyFieldBridge.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyFieldBridge.java index 917dd88268d..aacf7e28bbd 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyFieldBridge.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/entity/TermConceptPropertyFieldBridge.java @@ -53,6 +53,11 @@ public class TermConceptPropertyFieldBridge implements FieldBridge, StringBridge 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); + } } } } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java index c9272a94793..2647192dfbc 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/dstu3/BaseJpaResourceProviderCodeSystemDstu3.java @@ -20,25 +20,21 @@ package ca.uhn.fhir.jpa.provider.dstu3; * #L% */ -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -import javax.servlet.http.HttpServletRequest; - -import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; -import org.hl7.fhir.convertors.VersionConvertor_30_40; -import org.hl7.fhir.dstu3.model.*; - import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; +import org.hl7.fhir.convertors.VersionConvertor_30_40; +import org.hl7.fhir.dstu3.model.*; import org.hl7.fhir.exceptions.FHIRException; +import javax.servlet.http.HttpServletRequest; +import java.util.List; + public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderDstu3 { - //@formatter:off @SuppressWarnings("unchecked") @Operation(name = "$lookup", idempotent = true, returnParameters= { @OperationParam(name="name", type=StringType.class, min=1), @@ -50,17 +46,17 @@ public class BaseJpaResourceProviderCodeSystemDstu3 extends JpaResourceProviderD HttpServletRequest theServletRequest, @OperationParam(name="code", min=0, max=1) CodeType theCode, @OperationParam(name="system", min=0, max=1) UriType theSystem, - @OperationParam(name="coding", min=0, max=1) Coding theCoding, - RequestDetails theRequestDetails + @OperationParam(name="coding", min=0, max=1) Coding theCoding, + @OperationParam(name = "property", min = 0, max = OperationParam.MAX_UNLIMITED) List theProperties, + RequestDetails theRequestDetails ) { - //@formatter:on - + startRequest(theServletRequest); try { IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); result.throwNotFoundIfAppropriate(); - org.hl7.fhir.r4.model.Parameters parametersR4 = result.toParameters(); + org.hl7.fhir.r4.model.Parameters parametersR4 = result.toParameters(theProperties); return VersionConvertor_30_40.convertParameters(parametersR4); } catch (FHIRException e) { throw new InternalErrorException(e); diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java index 34b2fd8715d..a92209b84eb 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/provider/r4/BaseJpaResourceProviderCodeSystemR4.java @@ -20,22 +20,18 @@ package ca.uhn.fhir.jpa.provider.r4; * #L% */ -import static org.apache.commons.lang3.StringUtils.isNotBlank; - -import javax.servlet.http.HttpServletRequest; - -import org.hl7.fhir.r4.model.*; - import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem.LookupCodeResult; import ca.uhn.fhir.rest.annotation.Operation; import ca.uhn.fhir.rest.annotation.OperationParam; import ca.uhn.fhir.rest.api.server.RequestDetails; -import ca.uhn.fhir.rest.server.exceptions.ResourceNotFoundException; +import org.hl7.fhir.r4.model.*; + +import javax.servlet.http.HttpServletRequest; +import java.util.List; public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 { - //@formatter:off @SuppressWarnings("unchecked") @Operation(name = "$lookup", idempotent = true, returnParameters= { @OperationParam(name="name", type=StringType.class, min=1), @@ -47,17 +43,17 @@ public class BaseJpaResourceProviderCodeSystemR4 extends JpaResourceProviderR4 theProperties, + RequestDetails theRequestDetails ) { - //@formatter:on - + startRequest(theServletRequest); try { IFhirResourceDaoCodeSystem dao = (IFhirResourceDaoCodeSystem) getDao(); LookupCodeResult result = dao.lookupCode(theCode, theSystem, theCoding, theRequestDetails); result.throwNotFoundIfAppropriate(); - return result.toParameters(); + return result.toParameters(theProperties); } finally { endRequest(theServletRequest); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java index 5115d760599..4f6f6547a8a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/BaseHapiTerminologySvcImpl.java @@ -292,7 +292,6 @@ public abstract class BaseHapiTerminologySvcImpl implements IHapiTerminologySvc } else { -// bool.must(qb.keyword().onField("myProperties").matching(nextFilter.getStringProperty()+"="+nextFilter.getValue()).createQuery()); bool.must(qb.phrase().onField("myProperties").sentence(nextFilter.getProperty() + "=" + nextFilter.getValue()).createQuery()); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImpl.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImpl.java index a16289f4c56..dd207acc9ef 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImpl.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcImpl.java @@ -64,6 +64,7 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc { public static final String LOINC_ANSWERLIST_FILE = "AnswerList_Beta_1.csv"; public static final String LOINC_ANSWERLIST_LINK_FILE = "LoincAnswerListLink_Beta_1.csv"; public static final String LOINC_DOCUMENT_ONTOLOGY_FILE = "DocumentOntology.csv"; + public static final String LOINC_UPLOAD_PROPERTIES_FILE = "loincupload.properties"; public static final String LOINC_FILE = "loinc.csv"; public static final String LOINC_HIERARCHY_FILE = "MULTI-AXIAL_HIERARCHY.CSV"; public static final String LOINC_PART_FILE = "Part_Beta_1.csv"; @@ -173,6 +174,7 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc { descriptors.verifyMandatoryFilesExist(mandatoryFilenameFragments); List optionalFilenameFragments = Arrays.asList( + LOINC_UPLOAD_PROPERTIES_FILE, LOINC_ANSWERLIST_FILE, LOINC_ANSWERLIST_LINK_FILE, LOINC_PART_FILE, @@ -222,17 +224,37 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc { throw new InternalErrorException("Failed to load loinc.xml", e); } - Set propertyNames = new HashSet<>(); + Map propertyNamesToTypes = new HashMap<>(); for (CodeSystem.PropertyComponent nextProperty : loincCs.getProperty()) { - if (isNotBlank(nextProperty.getCode())) { - propertyNames.add(nextProperty.getCode()); + String nextPropertyCode = nextProperty.getCode(); + CodeSystem.PropertyType nextPropertyType = nextProperty.getType(); + if (isNotBlank(nextPropertyCode)) { + propertyNamesToTypes.put(nextPropertyCode, nextPropertyType); } } IRecordHandler handler; + Properties uploadProperties = new Properties(); + for (FileDescriptor next : theDescriptors.getUncompressedFileDescriptors()) { + if (next.getFilename().endsWith("loincupload.properties")) { + try { + try (InputStream inputStream = next.getInputStream()) { + uploadProperties.load(inputStream); + } + } catch (IOException e) { + throw new InternalErrorException("Failed to read loincupload.properties", e); + } + } + } + + // Part file + handler = new LoincPartHandler(codeSystemVersion, code2concept); + iterateOverZipFile(theDescriptors, LOINC_PART_FILE, handler, ',', QuoteMode.NON_NUMERIC); + Map partTypeAndPartNameToPartNumber = ((LoincPartHandler) handler).getPartTypeAndPartNameToPartNumber(); + // Loinc Codes - handler = new LoincHandler(codeSystemVersion, code2concept, propertyNames); + handler = new LoincHandler(codeSystemVersion, code2concept, propertyNamesToTypes, partTypeAndPartNameToPartNumber); iterateOverZipFile(theDescriptors, LOINC_FILE, handler, ',', QuoteMode.NON_NUMERIC); // Loinc Hierarchy @@ -240,51 +262,47 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc { iterateOverZipFile(theDescriptors, LOINC_HIERARCHY_FILE, handler, ',', QuoteMode.NON_NUMERIC); // Answer lists (ValueSets of potential answers/values for loinc "questions") - handler = new LoincAnswerListHandler(codeSystemVersion, code2concept, propertyNames, valueSets, conceptMaps); + handler = new LoincAnswerListHandler(codeSystemVersion, code2concept, valueSets, conceptMaps, uploadProperties); iterateOverZipFile(theDescriptors, LOINC_ANSWERLIST_FILE, handler, ',', QuoteMode.NON_NUMERIC); // Answer list links (connects loinc observation codes to answerlist codes) handler = new LoincAnswerListLinkHandler(code2concept, valueSets); iterateOverZipFile(theDescriptors, LOINC_ANSWERLIST_LINK_FILE, handler, ',', QuoteMode.NON_NUMERIC); - // Part file - handler = new LoincPartHandler(codeSystemVersion, code2concept); - iterateOverZipFile(theDescriptors, LOINC_PART_FILE, handler, ',', QuoteMode.NON_NUMERIC); - // Part link file handler = new LoincPartLinkHandler(codeSystemVersion, code2concept); iterateOverZipFile(theDescriptors, LOINC_PART_LINK_FILE, handler, ',', QuoteMode.NON_NUMERIC); // Part related code mapping - handler = new LoincPartRelatedCodeMappingHandler(codeSystemVersion, code2concept, valueSets, conceptMaps); + handler = new LoincPartRelatedCodeMappingHandler(codeSystemVersion, code2concept, valueSets, conceptMaps, uploadProperties); iterateOverZipFile(theDescriptors, LOINC_PART_RELATED_CODE_MAPPING_FILE, handler, ',', QuoteMode.NON_NUMERIC); // Document Ontology File - handler = new LoincDocumentOntologyHandler(codeSystemVersion, code2concept, propertyNames, valueSets, conceptMaps); + handler = new LoincDocumentOntologyHandler(code2concept, propertyNamesToTypes, valueSets, conceptMaps, uploadProperties); iterateOverZipFile(theDescriptors, LOINC_DOCUMENT_ONTOLOGY_FILE, handler, ',', QuoteMode.NON_NUMERIC); // RSNA Playbook file - handler = new LoincRsnaPlaybookHandler(codeSystemVersion, code2concept, propertyNames, valueSets, conceptMaps); + handler = new LoincRsnaPlaybookHandler(code2concept, valueSets, conceptMaps, uploadProperties); iterateOverZipFile(theDescriptors, LOINC_RSNA_PLAYBOOK_FILE, handler, ',', QuoteMode.NON_NUMERIC); // Top 2000 Codes - US - handler = new LoincTop2000LabResultsUsHandler(code2concept, valueSets, conceptMaps); + handler = new LoincTop2000LabResultsUsHandler(code2concept, valueSets, conceptMaps, uploadProperties); iterateOverZipFile(theDescriptors, LOINC_TOP2000_COMMON_LAB_RESULTS_US_FILE, handler, ',', QuoteMode.NON_NUMERIC); // Top 2000 Codes - SI - handler = new LoincTop2000LabResultsSiHandler(code2concept, valueSets, conceptMaps); + handler = new LoincTop2000LabResultsSiHandler(code2concept, valueSets, conceptMaps, uploadProperties); iterateOverZipFile(theDescriptors, LOINC_TOP2000_COMMON_LAB_RESULTS_SI_FILE, handler, ',', QuoteMode.NON_NUMERIC); // Universal Lab Order ValueSet - handler = new LoincUniversalOrderSetHandler(code2concept, valueSets, conceptMaps); + handler = new LoincUniversalOrderSetHandler(code2concept, valueSets, conceptMaps, uploadProperties); iterateOverZipFile(theDescriptors, LOINC_UNIVERSAL_LAB_ORDER_VALUESET_FILE, handler, ',', QuoteMode.NON_NUMERIC); // IEEE Medical Device Codes - handler = new LoincIeeeMedicalDeviceCodeHandler(code2concept, valueSets, conceptMaps); + handler = new LoincIeeeMedicalDeviceCodeHandler(code2concept, valueSets, conceptMaps, uploadProperties); iterateOverZipFile(theDescriptors, LOINC_IEEE_MEDICAL_DEVICE_CODE_MAPPING_TABLE_CSV, handler, ',', QuoteMode.NON_NUMERIC); // Imaging Document Codes - handler = new LoincImagingDocumentCodeHandler(code2concept, valueSets, conceptMaps); + handler = new LoincImagingDocumentCodeHandler(code2concept, valueSets, conceptMaps, uploadProperties); iterateOverZipFile(theDescriptors, LOINC_IMAGING_DOCUMENT_CODES_FILE, handler, ',', QuoteMode.NON_NUMERIC); IOUtils.closeQuietly(theDescriptors); @@ -367,8 +385,8 @@ public class TerminologyLoaderSvcImpl implements IHapiTerminologyLoaderSvc { private void storeCodeSystem(RequestDetails theRequestDetails, final TermCodeSystemVersion theCodeSystemVersion, CodeSystem theCodeSystem, List theValueSets, List theConceptMaps) { Validate.isTrue(theCodeSystem.getContent() == CodeSystem.CodeSystemContentMode.NOTPRESENT); - List valueSets = ObjectUtils.defaultIfNull(theValueSets, Collections.emptyList()); - List conceptMaps = ObjectUtils.defaultIfNull(theConceptMaps, Collections.emptyList()); + List valueSets = ObjectUtils.defaultIfNull(theValueSets, Collections.emptyList()); + List conceptMaps = ObjectUtils.defaultIfNull(theConceptMaps, Collections.emptyList()); myTermSvc.setProcessDeferred(false); if (myTermSvcDstu3 != null) { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincHandler.java similarity index 82% rename from hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java rename to hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincHandler.java index 31af0aeff9b..49ae11ce88f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincHandler.java @@ -30,21 +30,31 @@ import org.hl7.fhir.r4.model.ValueSet; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Properties; import static org.apache.commons.lang3.StringUtils.*; -abstract class BaseHandler implements IRecordHandler { +public abstract class BaseLoincHandler implements IRecordHandler { + public static final String LOINC_COPYRIGHT_STATEMENT = "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/"; + /** + * This is NOT the LOINC CodeSystem URI! It is just + * the website URL to LOINC. + */ + public static final String LOINC_WEBSITE_URL = "https://loinc.org"; + public static final String REGENSTRIEF_INSTITUTE_INC = "Regenstrief Institute, Inc."; private final List myConceptMaps; private final Map myIdToConceptMaps = new HashMap<>(); private final List myValueSets; private final Map myIdToValueSet = new HashMap<>(); private final Map myCode2Concept; + private final Properties myUploadProperties; - BaseHandler(Map theCode2Concept, List theValueSets, List theConceptMaps) { + BaseLoincHandler(Map theCode2Concept, List theValueSets, List theConceptMaps, Properties theUploadProperties) { myValueSets = theValueSets; myCode2Concept = theCode2Concept; myConceptMaps = theConceptMaps; + myUploadProperties = theUploadProperties; } void addCodeAsIncludeToValueSet(ValueSet theVs, String theCodeSystemUrl, String theCode, String theDisplayName) { @@ -100,20 +110,25 @@ abstract class BaseHandler implements IRecordHandler { conceptMap.setId(theMapping.getConceptMapId()); conceptMap.setUrl(theMapping.getConceptMapUri()); conceptMap.setName(theMapping.getConceptMapName()); - conceptMap.setPublisher("Regentrief Institute, Inc."); + conceptMap.setVersion(myUploadProperties.getProperty("conceptmap.version")); + conceptMap.setPublisher(REGENSTRIEF_INSTITUTE_INC); conceptMap.addContact() - .setName("Regentrief Institute, Inc.") + .setName(REGENSTRIEF_INSTITUTE_INC) .addTelecom() .setSystem(ContactPoint.ContactPointSystem.URL) - .setValue("https://loinc.org"); - conceptMap.setCopyright(theCopyright); + .setValue(LOINC_WEBSITE_URL); + String copyright = theCopyright; + if (!copyright.contains("LOINC")) { + copyright = LOINC_COPYRIGHT_STATEMENT + ". " + copyright; + } + conceptMap.setCopyright(copyright); myIdToConceptMaps.put(theMapping.getConceptMapId(), conceptMap); myConceptMaps.add(conceptMap); } else { conceptMap = myIdToConceptMaps.get(theMapping.getConceptMapId()); } - if (isNotBlank(theMapping.getCopyright())) { + if (isBlank(theMapping.getCopyright())) { conceptMap.setCopyright(theMapping.getCopyright()); } @@ -164,21 +179,28 @@ abstract class BaseHandler implements IRecordHandler { } } - ValueSet getValueSet(String theValueSetId, String theValueSetUri, String theValueSetName) { + ValueSet getValueSet(String theValueSetId, String theValueSetUri, String theValueSetName, String theVersionPropertyName) { + + String version = null; + if (isNotBlank(theVersionPropertyName)) { + version = myUploadProperties.getProperty(theVersionPropertyName); + } + ValueSet vs; if (!myIdToValueSet.containsKey(theValueSetId)) { vs = new ValueSet(); vs.setUrl(theValueSetUri); vs.setId(theValueSetId); + vs.setVersion(version); vs.setName(theValueSetName); vs.setStatus(Enumerations.PublicationStatus.ACTIVE); - vs.setPublisher("Regenstrief Institute, Inc."); + vs.setPublisher(REGENSTRIEF_INSTITUTE_INC); vs.addContact() - .setName("Regenstrief Institute, Inc.") + .setName(REGENSTRIEF_INSTITUTE_INC) .addTelecom() .setSystem(ContactPoint.ContactPointSystem.URL) - .setValue("https://loinc.org"); - vs.setCopyright("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/"); + .setValue(LOINC_WEBSITE_URL); + vs.setCopyright(LOINC_COPYRIGHT_STATEMENT); myIdToValueSet.put(theValueSetId, vs); myValueSets.add(vs); } else { diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java index 3a937e3deb6..723943d89db 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/BaseLoincTop2000LabResultsHandler.java @@ -29,17 +29,18 @@ import org.hl7.fhir.r4.model.ValueSet; import java.util.List; import java.util.Map; +import java.util.Properties; import static org.apache.commons.lang3.StringUtils.trim; -public class BaseLoincTop2000LabResultsHandler extends BaseHandler implements IRecordHandler { +public class BaseLoincTop2000LabResultsHandler extends BaseLoincHandler implements IRecordHandler { private String myValueSetId; private String myValueSetUri; private String myValueSetName; - public BaseLoincTop2000LabResultsHandler(Map theCode2concept, List theValueSets, String theValueSetId, String theValueSetUri, String theValueSetName, List theConceptMaps) { - super(theCode2concept, theValueSets, theConceptMaps); + public BaseLoincTop2000LabResultsHandler(Map theCode2concept, List theValueSets, String theValueSetId, String theValueSetUri, String theValueSetName, List theConceptMaps, Properties theUploadProperties) { + super(theCode2concept, theValueSets, theConceptMaps, theUploadProperties); myValueSetId = theValueSetId; myValueSetUri = theValueSetUri; myValueSetName = theValueSetName; @@ -50,7 +51,7 @@ public class BaseLoincTop2000LabResultsHandler extends BaseHandler implements IR String loincNumber = trim(theRecord.get("LOINC #")); String displayName = trim(theRecord.get("Long Common Name")); - ValueSet valueSet = getValueSet(myValueSetId, myValueSetUri, myValueSetName); + ValueSet valueSet = getValueSet(myValueSetId, myValueSetUri, myValueSetName, null); addCodeAsIncludeToValueSet(valueSet, IHapiTerminologyLoaderSvc.LOINC_URI, loincNumber, displayName); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java index 1b88875ba24..2653727a172 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincAnswerListHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -27,27 +27,21 @@ import org.apache.commons.csv.CSVRecord; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ValueSet; -import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; +import java.util.Properties; import static org.apache.commons.lang3.StringUtils.*; -public class LoincAnswerListHandler extends BaseHandler { +public class LoincAnswerListHandler extends BaseLoincHandler { private final Map myCode2Concept; private final TermCodeSystemVersion myCodeSystemVersion; - private final Set myPropertyNames; - private final List myValueSets; - private final Map myIdToValueSet = new HashMap<>(); - public LoincAnswerListHandler(TermCodeSystemVersion theCodeSystemVersion, Map theCode2concept, Set thePropertyNames, List theValueSets, List theConceptMaps) { - super(theCode2concept, theValueSets, theConceptMaps); + public LoincAnswerListHandler(TermCodeSystemVersion theCodeSystemVersion, Map theCode2concept, List theValueSets, List theConceptMaps, Properties theUploadProperties) { + super(theCode2concept, theValueSets, theConceptMaps, theUploadProperties); myCodeSystemVersion = theCodeSystemVersion; myCode2Concept = theCode2concept; - myPropertyNames = thePropertyNames; - myValueSets = theValueSets; } @Override @@ -91,11 +85,11 @@ public class LoincAnswerListHandler extends BaseHandler { } // Answer list ValueSet - ValueSet vs = getValueSet(answerListId, "urn:oid:" + answerListOid, answerListName); + ValueSet vs = getValueSet(answerListId, "http://loinc.org/vs/" + answerListId, answerListName, "answerlist.version"); if (vs.getIdentifier().isEmpty()) { vs.addIdentifier() - .setSystem(IHapiTerminologyLoaderSvc.LOINC_URI) - .setValue(answerListId); + .setSystem("urn:ietf:rfc:3986") + .setValue("urn:oid:" + answerListOid); } vs diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java index fa3d23b1341..60aaa01a98f 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincDocumentOntologyHandler.java @@ -20,33 +20,31 @@ 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; import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.apache.commons.csv.CSVRecord; +import org.hl7.fhir.r4.model.CodeSystem; import org.hl7.fhir.r4.model.ConceptMap; import org.hl7.fhir.r4.model.ValueSet; -import java.util.*; +import java.util.List; +import java.util.Map; +import java.util.Properties; import static org.apache.commons.lang3.StringUtils.trim; -public class LoincDocumentOntologyHandler extends BaseHandler implements IRecordHandler { +public class LoincDocumentOntologyHandler extends BaseLoincHandler implements IRecordHandler { public static final String DOCUMENT_ONTOLOGY_CODES_VS_ID = "loinc-document-ontology"; public static final String DOCUMENT_ONTOLOGY_CODES_VS_URI = "http://loinc.org/vs/loinc-document-ontology"; public static final String DOCUMENT_ONTOLOGY_CODES_VS_NAME = "LOINC Document Ontology Codes"; private final Map myCode2Concept; - private final TermCodeSystemVersion myCodeSystemVersion; - private final Set myPropertyNames; - public LoincDocumentOntologyHandler(TermCodeSystemVersion theCodeSystemVersion, Map theCode2concept, Set thePropertyNames, List theValueSets, List theConceptMaps) { - super(theCode2concept, theValueSets, theConceptMaps); - myCodeSystemVersion = theCodeSystemVersion; + public LoincDocumentOntologyHandler(Map theCode2concept, Map thePropertyNames, List theValueSets, List theConceptMaps, Properties theUploadProperties) { + super(theCode2concept, theValueSets, theConceptMaps, theUploadProperties); myCode2Concept = theCode2concept; - myPropertyNames = thePropertyNames; } @Override @@ -59,7 +57,7 @@ public class LoincDocumentOntologyHandler extends BaseHandler implements IRecord String partName = trim(theRecord.get("PartName")); // RSNA Codes VS - ValueSet vs = getValueSet(DOCUMENT_ONTOLOGY_CODES_VS_ID, DOCUMENT_ONTOLOGY_CODES_VS_URI, DOCUMENT_ONTOLOGY_CODES_VS_NAME); + ValueSet vs = getValueSet(DOCUMENT_ONTOLOGY_CODES_VS_ID, DOCUMENT_ONTOLOGY_CODES_VS_URI, DOCUMENT_ONTOLOGY_CODES_VS_NAME, null); addCodeAsIncludeToValueSet(vs, IHapiTerminologyLoaderSvc.LOINC_URI, loincNumber, null); // Part Properties diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java index bbdf5583efa..ffe982db7f5 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -22,13 +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.term.IHapiTerminologyLoaderSvc; import ca.uhn.fhir.jpa.term.IRecordHandler; import ca.uhn.fhir.jpa.term.TerminologyLoaderSvcImpl; +import ca.uhn.fhir.rest.server.exceptions.InternalErrorException; import org.apache.commons.csv.CSVRecord; import org.apache.commons.lang3.Validate; +import org.hl7.fhir.r4.model.CodeSystem; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.util.Map; -import java.util.Set; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.trim; @@ -37,12 +41,14 @@ public class LoincHandler implements IRecordHandler { private final Map myCode2Concept; private final TermCodeSystemVersion myCodeSystemVersion; - private final Set myPropertyNames; + private final Map myPropertyNames; + private final Map myPartTypeAndPartNameToPartNumber; - public LoincHandler(TermCodeSystemVersion theCodeSystemVersion, Map theCode2concept, Set thePropertyNames) { + public LoincHandler(TermCodeSystemVersion theCodeSystemVersion, Map theCode2concept, Map thePropertyNames, Map thePartTypeAndPartNameToPartNumber) { myCodeSystemVersion = theCodeSystemVersion; myCode2Concept = theCode2concept; myPropertyNames = thePropertyNames; + myPartTypeAndPartNameToPartNumber = thePartTypeAndPartNameToPartNumber; } @Override @@ -64,13 +70,57 @@ public class LoincHandler implements IRecordHandler { .setValue(shortName); } - for (String nextPropertyName : myPropertyNames) { + for (String nextPropertyName : myPropertyNames.keySet()) { if (!theRecord.toMap().containsKey(nextPropertyName)) { continue; } + + CodeSystem.PropertyType nextPropertyType = myPropertyNames.get(nextPropertyName); + String nextPropertyValue = theRecord.get(nextPropertyName); if (isNotBlank(nextPropertyValue)) { - concept.addPropertyString(nextPropertyName, nextPropertyValue); + nextPropertyValue = trim(nextPropertyValue); + + switch (nextPropertyType) { + case STRING: + concept.addPropertyString(nextPropertyName, nextPropertyValue); + break; + case CODING: + PartTypeAndPartName key = new PartTypeAndPartName(nextPropertyName, nextPropertyValue); + 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; + } + +// Validate.notBlank(partNumber, "Unknown part: " + key); + if (isNotBlank(partNumber)) { + concept.addPropertyCoding(nextPropertyName, IHapiTerminologyLoaderSvc.LOINC_URI, partNumber, nextPropertyValue); + } else { + ourLog.warn("Unable to find part code with TYPE[{}] and NAME[{}]", key.getPartType(), key.getPartName()); + } + break; + case CODE: + case INTEGER: + case BOOLEAN: + case DATETIME: + case NULL: + throw new InternalErrorException("Don't know how to handle LOINC property of type: " + nextPropertyType); + } + } } @@ -78,5 +128,5 @@ public class LoincHandler implements IRecordHandler { myCode2Concept.put(code, concept); } } - +private static final Logger ourLog = LoggerFactory.getLogger(LoincHandler.class); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java index db42e7eff6b..204497bc5ce 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincIeeeMedicalDeviceCodeHandler.java @@ -30,10 +30,11 @@ import org.hl7.fhir.r4.model.ValueSet; import java.util.List; import java.util.Map; +import java.util.Properties; import static org.apache.commons.lang3.StringUtils.trim; -public class LoincIeeeMedicalDeviceCodeHandler extends BaseHandler implements IRecordHandler { +public class LoincIeeeMedicalDeviceCodeHandler extends BaseLoincHandler implements IRecordHandler { public static final String LOINC_IEEE_CM_ID = "LOINC-IEEE-MEDICAL-DEVICE-CM"; public static final String LOINC_IEEE_CM_URI = "http://loinc.org/fhir/loinc-ieee-device-code-mappings"; @@ -43,8 +44,8 @@ public class LoincIeeeMedicalDeviceCodeHandler extends BaseHandler implements IR /** * Constructor */ - public LoincIeeeMedicalDeviceCodeHandler(Map theCode2concept, List theValueSets, List theConceptMaps) { - super(theCode2concept, theValueSets, theConceptMaps); + public LoincIeeeMedicalDeviceCodeHandler(Map theCode2concept, List theValueSets, List theConceptMaps, Properties theUploadProperties) { + super(theCode2concept, theValueSets, theConceptMaps, theUploadProperties); } @Override diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java index d6fb64d19bc..e61f33f402d 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincImagingDocumentCodeHandler.java @@ -29,17 +29,18 @@ import org.hl7.fhir.r4.model.ValueSet; import java.util.List; import java.util.Map; +import java.util.Properties; import static org.apache.commons.lang3.StringUtils.trim; -public class LoincImagingDocumentCodeHandler extends BaseHandler implements IRecordHandler { +public class LoincImagingDocumentCodeHandler extends BaseLoincHandler implements IRecordHandler { public static final String VS_ID = "loinc-imaging-document-codes"; public static final String VS_URI = "http://loinc.org/fhir/loinc-imaging-document-codes"; public static final String VS_NAME = "LOINC Imaging Document Codes"; - public LoincImagingDocumentCodeHandler(Map theCode2concept, List theValueSets, List theConceptMaps) { - super(theCode2concept, theValueSets, theConceptMaps); + public LoincImagingDocumentCodeHandler(Map theCode2concept, List theValueSets, List theConceptMaps, Properties theUploadProperties) { + super(theCode2concept, theValueSets, theConceptMaps, theUploadProperties); } @Override @@ -47,7 +48,7 @@ public class LoincImagingDocumentCodeHandler extends BaseHandler implements IRec String loincNumber = trim(theRecord.get("LOINC_NUM")); String displayName = trim(theRecord.get("LONG_COMMON_NAME")); - ValueSet valueSet = getValueSet(VS_ID, VS_URI, VS_NAME); + ValueSet valueSet = getValueSet(VS_ID, VS_URI, VS_NAME,null); addCodeAsIncludeToValueSet(valueSet, IHapiTerminologyLoaderSvc.LOINC_URI, loincNumber, displayName); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartHandler.java index d85bba8f196..40fe67b12d1 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -24,6 +24,7 @@ import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; import ca.uhn.fhir.jpa.entity.TermConcept; import ca.uhn.fhir.jpa.term.IRecordHandler; import org.apache.commons.csv.CSVRecord; +import org.apache.commons.lang3.Validate; import org.hl7.fhir.r4.model.ValueSet; import java.util.HashMap; @@ -37,6 +38,7 @@ public class LoincPartHandler implements IRecordHandler { private final Map myCode2Concept; private final TermCodeSystemVersion myCodeSystemVersion; private final Map myIdToValueSet = new HashMap<>(); + private final Map myPartTypeAndPartNameToPartNumber = new HashMap<>(); public LoincPartHandler(TermCodeSystemVersion theCodeSystemVersion, Map theCode2concept) { myCodeSystemVersion = theCodeSystemVersion; @@ -58,13 +60,17 @@ public class LoincPartHandler implements IRecordHandler { // return; // } + PartTypeAndPartName partTypeAndPartName = new PartTypeAndPartName(partTypeName, partName); + String previousValue = myPartTypeAndPartNameToPartNumber.put(partTypeAndPartName, partNumber); + Validate.isTrue(previousValue == null, "Already had part: " + partTypeAndPartName); + TermConcept concept = myCode2Concept.get(partNumber); if (concept == null) { concept = new TermConcept(myCodeSystemVersion, partNumber); concept.setDisplay(partName); myCode2Concept.put(partNumber, concept); } - + if (isNotBlank(partDisplayName)) { concept.addDesignation() .setConcept(concept) @@ -74,4 +80,9 @@ public class LoincPartHandler implements IRecordHandler { } + public Map getPartTypeAndPartNameToPartNumber() { + return myPartTypeAndPartNameToPartNumber; + } + + } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java index 2988328a4c6..9a277eb4d01 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincPartRelatedCodeMappingHandler.java @@ -9,9 +9,9 @@ package ca.uhn.fhir.jpa.term.loinc; * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -32,22 +32,23 @@ import org.hl7.fhir.r4.model.ValueSet; import java.util.List; import java.util.Map; +import java.util.Properties; import static org.apache.commons.lang3.StringUtils.defaultString; import static org.apache.commons.lang3.StringUtils.trim; -public class LoincPartRelatedCodeMappingHandler extends BaseHandler implements IRecordHandler { +public class LoincPartRelatedCodeMappingHandler extends BaseLoincHandler implements IRecordHandler { - public static final String LOINC_PART_MAP_ID = "LOINC-PART-MAP"; - public static final String LOINC_PART_MAP_URI = "http://loinc.org/cm/loinc-parts-to-snomed-ct"; - public static final String LOINC_PART_MAP_NAME = "LOINC Part Map"; + 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"; 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 myCode2Concept; private final TermCodeSystemVersion myCodeSystemVersion; private final List myConceptMaps; - public LoincPartRelatedCodeMappingHandler(TermCodeSystemVersion theCodeSystemVersion, Map theCode2concept, List theValueSets, List theConceptMaps) { - super(theCode2concept, theValueSets, theConceptMaps); + public LoincPartRelatedCodeMappingHandler(TermCodeSystemVersion theCodeSystemVersion, Map theCode2concept, List theValueSets, List theConceptMaps, Properties theUploadProperties) { + super(theCode2concept, theValueSets, theConceptMaps, theUploadProperties); myCodeSystemVersion = theCodeSystemVersion; myCode2Concept = theCode2concept; myConceptMaps = theConceptMaps; @@ -86,11 +87,24 @@ public class LoincPartRelatedCodeMappingHandler extends BaseHandler implements I throw new InternalErrorException("Unknown MapType '" + mapType + "' for PartNumber: " + partNumber); } + String loincPartMapId; + String loincPartMapUri; + String loincPartMapName; + switch (extCodeSystem) { + case IHapiTerminologyLoaderSvc.SCT_URI: + loincPartMapId = LOINC_SCT_PART_MAP_ID; + loincPartMapUri = LOINC_SCT_PART_MAP_URI; + loincPartMapName = LOINC_SCT_PART_MAP_NAME; + break; + default: + throw new InternalErrorException("Don't know how to handle mapping to system: " + extCodeSystem); + } + addConceptMapEntry( new ConceptMapping() - .setConceptMapId(LOINC_PART_MAP_ID) - .setConceptMapUri(LOINC_PART_MAP_URI) - .setConceptMapName(LOINC_PART_MAP_NAME) + .setConceptMapId(loincPartMapId) + .setConceptMapUri(loincPartMapUri) + .setConceptMapName(loincPartMapName) .setSourceCodeSystem(IHapiTerminologyLoaderSvc.LOINC_URI) .setSourceCode(partNumber) .setSourceDisplay(partName) diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java index a9555fd78b5..2bc0e4e8015 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincRsnaPlaybookHandler.java @@ -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; @@ -35,7 +34,7 @@ import java.util.*; import static org.apache.commons.lang3.StringUtils.isNotBlank; import static org.apache.commons.lang3.StringUtils.trim; -public class LoincRsnaPlaybookHandler extends BaseHandler implements IRecordHandler { +public class LoincRsnaPlaybookHandler extends BaseLoincHandler implements IRecordHandler { public static final String RSNA_CODES_VS_ID = "loinc-rsna-radiology-playbook"; public static final String RSNA_CODES_VS_URI = "http://loinc.org/vs/loinc-rsna-radiology-playbook"; @@ -59,8 +58,6 @@ public class LoincRsnaPlaybookHandler extends BaseHandler implements IRecordHand public static final String RPID_CS_URI = RID_CS_URI; 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/RSNA Radiology Playbook and the LOINC Part File contain content from RadLex® (http://rsna.org/RadLex.aspx), copyright © 2005-2017, The Radiological Society of North America, Inc., available at no cost under the license at http://www.rsna.org/uploadedFiles/RSNA/Content/Informatics/RadLex_License_Agreement_and_Terms_of_Use_V2_Final.pdf."; private final Map myCode2Concept; - private final TermCodeSystemVersion myCodeSystemVersion; - private final Set myPropertyNames; private final List myValueSets; private final Map myIdToValueSet = new HashMap<>(); private final Set myCodesInRsnaPlaybookValueSet = new HashSet<>(); @@ -68,11 +65,9 @@ public class LoincRsnaPlaybookHandler extends BaseHandler implements IRecordHand /** * Constructor */ - public LoincRsnaPlaybookHandler(TermCodeSystemVersion theCodeSystemVersion, Map theCode2concept, Set thePropertyNames, List theValueSets, List theConceptMaps) { - super(theCode2concept, theValueSets, theConceptMaps); - myCodeSystemVersion = theCodeSystemVersion; + public LoincRsnaPlaybookHandler(Map theCode2concept, List theValueSets, List theConceptMaps, Properties theUploadProperties) { + super(theCode2concept, theValueSets, theConceptMaps, theUploadProperties); myCode2Concept = theCode2concept; - myPropertyNames = thePropertyNames; myValueSets = theValueSets; } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java index 91c7e959491..4354e26093a 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsSiHandler.java @@ -26,6 +26,7 @@ import org.hl7.fhir.r4.model.ValueSet; import java.util.List; import java.util.Map; +import java.util.Properties; public class LoincTop2000LabResultsSiHandler extends BaseLoincTop2000LabResultsHandler { @@ -33,8 +34,8 @@ public class LoincTop2000LabResultsSiHandler extends BaseLoincTop2000LabResultsH public static final String TOP_2000_SI_VS_URI = "http://loinc.org/vs/top-2000-lab-observations-si"; public static final String TOP_2000_SI_VS_NAME = "Top 2000 Lab Results SI"; - public LoincTop2000LabResultsSiHandler(Map theCode2concept, List theValueSets, List theConceptMaps) { - super(theCode2concept, theValueSets, TOP_2000_SI_VS_ID, TOP_2000_SI_VS_URI, TOP_2000_SI_VS_NAME, theConceptMaps); + public LoincTop2000LabResultsSiHandler(Map theCode2concept, List theValueSets, List theConceptMaps, Properties theUploadProperties) { + super(theCode2concept, theValueSets, TOP_2000_SI_VS_ID, TOP_2000_SI_VS_URI, TOP_2000_SI_VS_NAME, theConceptMaps, theUploadProperties); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java index 64f11b92303..b513ae24993 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincTop2000LabResultsUsHandler.java @@ -26,6 +26,7 @@ import org.hl7.fhir.r4.model.ValueSet; import java.util.List; import java.util.Map; +import java.util.Properties; public class LoincTop2000LabResultsUsHandler extends BaseLoincTop2000LabResultsHandler { @@ -33,8 +34,8 @@ public class LoincTop2000LabResultsUsHandler extends BaseLoincTop2000LabResultsH public static final String TOP_2000_US_VS_URI = "http://loinc.org/vs/top-2000-lab-observations-us"; public static final String TOP_2000_US_VS_NAME = "Top 2000 Lab Results US"; - public LoincTop2000LabResultsUsHandler(Map theCode2concept, List theValueSets, List theConceptMaps) { - super(theCode2concept, theValueSets, TOP_2000_US_VS_ID, TOP_2000_US_VS_URI, TOP_2000_US_VS_NAME, theConceptMaps); + public LoincTop2000LabResultsUsHandler(Map theCode2concept, List theValueSets, List theConceptMaps, Properties theUploadProperties) { + super(theCode2concept, theValueSets, TOP_2000_US_VS_ID, TOP_2000_US_VS_URI, TOP_2000_US_VS_NAME, theConceptMaps, theUploadProperties); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java index cce69e825fe..c8d0ffb7179 100644 --- a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/LoincUniversalOrderSetHandler.java @@ -31,14 +31,14 @@ import java.util.*; import static org.apache.commons.lang3.StringUtils.trim; -public class LoincUniversalOrderSetHandler extends BaseHandler implements IRecordHandler { +public class LoincUniversalOrderSetHandler extends BaseLoincHandler implements IRecordHandler { public static final String VS_ID = "loinc-universal-order-set-vs"; public static final String VS_URI = "http://loinc.org/fhir/loinc-universal-order-set"; public static final String VS_NAME = "LOINC Universal Order Set"; - public LoincUniversalOrderSetHandler(Map theCode2concept, List theValueSets, List theConceptMaps) { - super(theCode2concept, theValueSets, theConceptMaps); + public LoincUniversalOrderSetHandler(Map theCode2concept, List theValueSets, List theConceptMaps, Properties theUploadProperties) { + super(theCode2concept, theValueSets, theConceptMaps, theUploadProperties); } @Override @@ -47,7 +47,7 @@ public class LoincUniversalOrderSetHandler extends BaseHandler implements IRecor String displayName = trim(theRecord.get("LONG_COMMON_NAME")); String orderObs = trim(theRecord.get("ORDER_OBS")); - ValueSet valueSet = getValueSet(VS_ID, VS_URI, VS_NAME); + ValueSet valueSet = getValueSet(VS_ID, VS_URI, VS_NAME, null); addCodeAsIncludeToValueSet(valueSet, IHapiTerminologyLoaderSvc.LOINC_URI, loincNumber, displayName); } diff --git a/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/PartTypeAndPartName.java b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/PartTypeAndPartName.java new file mode 100644 index 00000000000..173d4dbb562 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/main/java/ca/uhn/fhir/jpa/term/loinc/PartTypeAndPartName.java @@ -0,0 +1,57 @@ +package ca.uhn.fhir.jpa.term.loinc; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; + +public class PartTypeAndPartName { + private final String myPartType; + private final String myPartName; + + public PartTypeAndPartName(String thePartType, String thePartName) { + myPartType = thePartType; + myPartName = thePartName; + } + + @Override + public boolean equals(Object theO) { + if (this == theO) { + return true; + } + + if (theO == null || getClass() != theO.getClass()) { + return false; + } + + PartTypeAndPartName that = (PartTypeAndPartName) theO; + + return new EqualsBuilder() + .append(myPartType, that.myPartType) + .append(myPartName, that.myPartName) + .isEquals(); + } + + public String getPartName() { + return myPartName; + } + + public String getPartType() { + return myPartType; + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(myPartType) + .append(myPartName) + .toHashCode(); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("partType", myPartType) + .append("partName", myPartName) + .toString(); + } +} diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java index 710a59ce74f..6d2ab2a578b 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcIntegrationDstu3Test.java @@ -4,8 +4,10 @@ import ca.uhn.fhir.jpa.dao.DaoConfig; import ca.uhn.fhir.jpa.dao.IFhirResourceDaoCodeSystem; import ca.uhn.fhir.jpa.dao.dstu3.BaseJpaDstu3Test; import ca.uhn.fhir.util.TestUtil; +import com.google.common.collect.Lists; import org.hl7.fhir.convertors.VersionConvertor_30_40; import org.hl7.fhir.dstu3.model.*; +import org.hl7.fhir.instance.model.api.IPrimitiveType; import org.junit.After; import org.junit.AfterClass; import org.junit.Before; @@ -14,13 +16,14 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; +import java.util.List; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; import static org.hamcrest.Matchers.containsInAnyOrder; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertThat; +import static org.hamcrest.Matchers.empty; +import static org.junit.Assert.*; public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test { @@ -39,8 +42,83 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test { myDaoConfig.setDeferIndexingForCodesystemsOfSize(20000); } + private Optional findProperty(Parameters theParameters, String thePropertyName) { + return theParameters + .getParameter() + .stream() + .filter(t -> t.getName().equals("property")) + .filter(t -> ((PrimitiveType) t.getPart().get(0).getValue()).getValueAsString().equals(thePropertyName)) + .map(t -> (T) t.getPart().get(1).getValue()) + .findFirst(); + } + + private Optional getPropertyPart(Parameters theParameters, String thePropName, String thePart) { + return theParameters + .getParameter() + .stream() + .filter(t -> t.getName().equals(thePropName)) + .flatMap(t -> t.getPart().stream()) + .filter(t -> t.getName().equals(thePart)) + .map(t -> (T) t.getValue()) + .findFirst(); + } + @Test - public void testExpandWithProperty() throws Exception { + public void testExpandWithPropertyCoding() throws Exception { + ZipCollectionBuilder files = new ZipCollectionBuilder(); + TerminologyLoaderSvcLoincTest.addLoincMandatoryFilesToZip(files); + TerminologyLoaderSvcLoincTest.addLoincOptionalFilesToZip(files); + myLoader.loadLoinc(files.getFiles(), mySrd); + + // Search by code + ValueSet input = new ValueSet(); + input + .getCompose() + .addInclude() + .setSystem(IHapiTerminologyLoaderSvc.LOINC_URI) + .addFilter() + .setProperty("SCALE_TYP") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("LP7753-9"); + ValueSet expanded = myValueSetDao.expand(input, null); + Set 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")); + + // Search by display name + input = new ValueSet(); + input + .getCompose() + .addInclude() + .setSystem(IHapiTerminologyLoaderSvc.LOINC_URI) + .addFilter() + .setProperty("SCALE_TYP") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("Qn"); + 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")); + + // Search by something that doesn't match + input = new ValueSet(); + input + .getCompose() + .addInclude() + .setSystem(IHapiTerminologyLoaderSvc.LOINC_URI) + .addFilter() + .setProperty("SCALE_TYP") + .setOp(ValueSet.FilterOperator.EQUAL) + .setValue("Qn999"); + expanded = myValueSetDao.expand(input, null); + codes = toExpandedCodes(expanded); + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded)); + assertThat(codes, empty()); + } + + @Test + public void testExpandWithPropertyString() throws Exception { ZipCollectionBuilder files = new ZipCollectionBuilder(); TerminologyLoaderSvcLoincTest.addLoincMandatoryFilesToZip(files); TerminologyLoaderSvcLoincTest.addLoincOptionalFilesToZip(files); @@ -52,13 +130,14 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test { .addInclude() .setSystem(IHapiTerminologyLoaderSvc.LOINC_URI) .addFilter() - .setProperty("SCALE_TYP") + .setProperty("CLASS") .setOp(ValueSet.FilterOperator.EQUAL) - .setValue("Ord"); + .setValue("EKG.MEAS"); ValueSet expanded = myValueSetDao.expand(input, null); Set codes = toExpandedCodes(expanded); ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(expanded)); - assertThat(codes, containsInAnyOrder("1001-7", "61438-8")); + 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")); } @Test @@ -69,25 +148,53 @@ public class TerminologyLoaderSvcIntegrationDstu3Test extends BaseJpaDstu3Test { myLoader.loadLoinc(files.getFiles(), mySrd); IFhirResourceDaoCodeSystem.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("10013-1"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URI), null, mySrd); - org.hl7.fhir.r4.model.Parameters parametersR4 = result.toParameters(); + org.hl7.fhir.r4.model.Parameters parametersR4 = result.toParameters(null); Parameters parameters = VersionConvertor_30_40.convertParameters(parametersR4); ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(parameters)); - assertEquals("SYSTEM", this.getPropertyPart(parameters, "property", "code").get().getValueAsString()); - assertEquals("Heart", this.getPropertyPart(parameters, "property", "value").get().getValueAsString()); + Optional propertyValue = findProperty(parameters, "SCALE_TYP"); + assertTrue(propertyValue.isPresent()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, propertyValue.get().getSystem()); + assertEquals("LP7753-9", propertyValue.get().getCode()); + assertEquals("Qn", propertyValue.get().getDisplay()); + + propertyValue = findProperty(parameters, "COMPONENT"); + assertTrue(propertyValue.isPresent()); + + Optional propertyValueString = findProperty(parameters, "ORDER_OBS"); + assertTrue(propertyValueString.isPresent()); + assertEquals("Observation", propertyValueString.get().getValue()); + + propertyValueString = findProperty(parameters, "CLASSTYPE"); + assertTrue(propertyValueString.isPresent()); + assertEquals("2", propertyValueString.get().getValue()); } - private Optional getPropertyPart(Parameters theParameters, String thePropName, String thePart) { - return theParameters - .getParameter() - .stream() - .filter(t -> t.getName().equals(thePropName)) - .flatMap(t -> t.getPart().stream()) - .filter(t -> t.getName().equals(thePart)) - .map(t -> (T)t.getValue()) - .findFirst(); + @Test + public void testLookupWithPropertiesExplicit() throws Exception { + ZipCollectionBuilder files = new ZipCollectionBuilder(); + TerminologyLoaderSvcLoincTest.addLoincMandatoryFilesToZip(files); + TerminologyLoaderSvcLoincTest.addLoincOptionalFilesToZip(files); + myLoader.loadLoinc(files.getFiles(), mySrd); + + IFhirResourceDaoCodeSystem.LookupCodeResult result = myCodeSystemDao.lookupCode(new StringType("10013-1"), new StringType(IHapiTerminologyLoaderSvc.LOINC_URI), null, mySrd); + List> properties = Lists.newArrayList(new CodeType("SCALE_TYP")); + org.hl7.fhir.r4.model.Parameters parametersR4 = result.toParameters(properties); + Parameters parameters = VersionConvertor_30_40.convertParameters(parametersR4); + + ourLog.info(myFhirCtx.newXmlParser().setPrettyPrint(true).encodeResourceToString(parameters)); + + Optional propertyValueCoding = findProperty(parameters, "SCALE_TYP"); + assertTrue(propertyValueCoding.isPresent()); + assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, propertyValueCoding.get().getSystem()); + assertEquals("LP7753-9", propertyValueCoding.get().getCode()); + assertEquals("Qn", propertyValueCoding.get().getDisplay()); + + propertyValueCoding = findProperty(parameters, "COMPONENT"); + assertFalse(propertyValueCoding.isPresent()); + } private Set toExpandedCodes(ValueSet theExpanded) { diff --git a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java index 7a233c20956..d98f7a9f0ae 100644 --- a/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java +++ b/hapi-fhir-jpaserver-base/src/test/java/ca/uhn/fhir/jpa/term/TerminologyLoaderSvcLoincTest.java @@ -110,8 +110,10 @@ public class TerminologyLoaderSvcLoincTest { // Normal loinc code code = concepts.get("10013-1"); assertEquals("10013-1", code.getCode()); - assertEquals("Elpot", code.getStringProperty("PROPERTY")); - assertEquals("Pt", code.getStringProperty("TIME_ASPCT")); + assertEquals(IHapiTerminologyLoaderSvc.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("R' wave amplitude in lead I", code.getDisplay()); // Loinc code with answer @@ -135,10 +137,11 @@ public class TerminologyLoaderSvcLoincTest { // AnswerList valueSet vs = valueSets.get("LL1001-8"); - assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, vs.getIdentifier().get(0).getSystem()); - assertEquals("LL1001-8", vs.getIdentifier().get(0).getValue()); + assertEquals("Beta.1", vs.getVersion()); + assertEquals("urn:ietf:rfc:3986", vs.getIdentifier().get(0).getSystem()); + assertEquals("urn:oid:1.3.6.1.4.1.12009.10.1.166", vs.getIdentifier().get(0).getValue()); assertEquals("PhenX05_14_30D freq amts", vs.getName()); - assertEquals("urn:oid:1.3.6.1.4.1.12009.10.1.166", vs.getUrl()); + assertEquals("http://loinc.org/vs/LL1001-8", vs.getUrl()); assertEquals(1, vs.getCompose().getInclude().size()); assertEquals(7, vs.getCompose().getInclude().get(0).getConcept().size()); assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, vs.getCompose().getInclude().get(0).getSystem()); @@ -151,10 +154,12 @@ public class TerminologyLoaderSvcLoincTest { assertEquals("adjusted for maternal weight", code.getDisplay()); // Part Mappings - conceptMap = conceptMaps.get(LoincPartRelatedCodeMappingHandler.LOINC_PART_MAP_ID); + conceptMap = conceptMaps.get(LoincPartRelatedCodeMappingHandler.LOINC_SCT_PART_MAP_ID); assertEquals(null, conceptMap.getSource()); assertEquals(null, conceptMap.getTarget()); - assertEquals("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. This may incur a fee in SNOMED International non-Member countries.", conceptMap.getCopyright()); + assertEquals(LoincPartRelatedCodeMappingHandler.LOINC_SCT_PART_MAP_URI, conceptMap.getUrl()); + assertEquals("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.", conceptMap.getCopyright()); + assertEquals("Beta.1", conceptMap.getVersion()); assertEquals(1, conceptMap.getGroup().size()); group = conceptMap.getGroup().get(0); assertEquals(IHapiTerminologyLoaderSvc.LOINC_URI, group.getSource()); @@ -320,9 +325,6 @@ public class TerminologyLoaderSvcLoincTest { // Normal loinc code TermConcept code = concepts.get("10013-1"); assertEquals("10013-1", code.getCode()); - assertEquals("Elpot", code.getStringProperty("PROPERTY")); - assertEquals("Pt", code.getStringProperty("TIME_ASPCT")); - assertEquals("R' wave amplitude in lead I", code.getDisplay()); // No valuesets or conceptmaps get created assertThat(valueSets.keySet(), empty()); @@ -350,6 +352,7 @@ public class TerminologyLoaderSvcLoincTest { } static void addLoincOptionalFilesToZip(ZipCollectionBuilder theFiles) throws IOException { + theFiles.addFileZip("/loinc/", "loincupload.properties"); theFiles.addFileZip("/loinc/", "AnswerList_Beta_1.csv", TerminologyLoaderSvcImpl.LOINC_ANSWERLIST_FILE); theFiles.addFileZip("/loinc/", TerminologyLoaderSvcImpl.LOINC_ANSWERLIST_LINK_FILE, TerminologyLoaderSvcImpl.LOINC_ANSWERLIST_LINK_FILE); theFiles.addFileZip("/loinc/", TerminologyLoaderSvcImpl.LOINC_PART_FILE, TerminologyLoaderSvcImpl.LOINC_PART_FILE); diff --git a/hapi-fhir-jpaserver-base/src/test/resources/loinc/Part_Beta_1.csv b/hapi-fhir-jpaserver-base/src/test/resources/loinc/Part_Beta_1.csv index d1d4f60fb7b..8da9e598437 100644 --- a/hapi-fhir-jpaserver-base/src/test/resources/loinc/Part_Beta_1.csv +++ b/hapi-fhir-jpaserver-base/src/test/resources/loinc/Part_Beta_1.csv @@ -8,3 +8,33 @@ "LP173484-9","ADJUSTMENT","W hyperextension)",,"ACTIVE" "LP6244-0","METHOD","EKG","Electrocardiogram (EKG)","ACTIVE" "LP18172-4","COMPONENT","Interferon.beta","Interferon beta","ACTIVE" +"LP7289-4","SYSTEM","Heart","Heart","ACTIVE" +"LP6960-1","TIME","Pt","Point in time (spot)","ACTIVE" +"LP6802-5","PROPERTY","Elpot","Electrical Potential (Voltage)","ACTIVE" +"LP7753-9","SCALE","Qn","Qn","ACTIVE" +"LP31101-6","COMPONENT","R' wave amplitude.lead I","R' wave amplitude.lead I","ACTIVE" +"LP31102-4","COMPONENT","R' wave amplitude.lead II","R' wave amplitude.lead II","ACTIVE" +"LP31103-2","COMPONENT","R' wave amplitude.lead III","R' wave amplitude.lead III","ACTIVE" +"LP31104-0","COMPONENT","R' wave amplitude.lead V1","R' wave amplitude.lead V1","ACTIVE" +"LP31105-7","COMPONENT","R' wave amplitude.lead V2","R' wave amplitude.lead V2","ACTIVE" +"LP31106-5","COMPONENT","R' wave amplitude.lead V3","R' wave amplitude.lead V3","ACTIVE" +"LP31107-3","COMPONENT","R' wave amplitude.lead V4","R' wave amplitude.lead V4","ACTIVE" +"LP31108-1","COMPONENT","R' wave amplitude.lead V5","R' wave amplitude.lead V5","ACTIVE" +"LP31109-9","COMPONENT","R' wave amplitude.lead V6","R' wave amplitude.lead V6","ACTIVE" +"LP31110-7","COMPONENT","R' wave duration.lead AVF","R' wave duration.lead AVF","ACTIVE" +"LP30269-2","SYSTEM","Ser/Plas^donor",,"ACTIVE" +"LP149220-8","PROPERTY","Pr","Presence","ACTIVE" +"LP7751-3","SCALE","Ord","Ord","ACTIVE" +"LP37904-7","COMPONENT","DBG Ab","DBG Ab","ACTIVE" +"LP6813-2","PROPERTY","Find","Finding","ACTIVE" +"LP95333-8","METHOD","PhenX","PhenX","ACTIVE" +"LP102627-9","COMPONENT","Each time you ate bread, toast or dinner rolls, how much did you usually eat in the past 30D","Each time you ate bread, toast or dinner rolls, how much did you usually eat in the past 30 days","ACTIVE" +"LP6879-3","PROPERTY","Time","Time (e.g. seconds)","ACTIVE" +"LP31088-5","COMPONENT","R wave duration.lead AVR","R wave duration.lead AVR","ACTIVE" +"LP206647-2","SYSTEM","Neck>Thyroid gland","Thyroid gland","ACTIVE" +"LP208655-3","METHOD","NM","NM","ACTIVE" +"LP32888-7","SCALE","Doc","Doc","ACTIVE" +"LP31534-8","COMPONENT","Study report","Study report","ACTIVE" +"LP7057-5","SYSTEM","Bld","Blood","ACTIVE" +"LP6838-9","PROPERTY","NFr","Number Fraction","ACTIVE" +"LP6141-8","METHOD","Automated count","Automated count","ACTIVE" diff --git a/hapi-fhir-jpaserver-base/src/test/resources/loinc/loincupload.properties b/hapi-fhir-jpaserver-base/src/test/resources/loinc/loincupload.properties new file mode 100644 index 00000000000..7a7aab2ab36 --- /dev/null +++ b/hapi-fhir-jpaserver-base/src/test/resources/loinc/loincupload.properties @@ -0,0 +1,7 @@ + +# This is the version identifier for the AnswerList file +answerlist.version=Beta.1 + +# This is the version identifier for uploaded ConceptMap resources +conceptmap.version=Beta.1 +