001/*-
002 * #%L
003 * HAPI FHIR JPA Server
004 * %%
005 * Copyright (C) 2014 - 2024 Smile CDR, Inc.
006 * %%
007 * Licensed under the Apache License, Version 2.0 (the "License");
008 * you may not use this file except in compliance with the License.
009 * You may obtain a copy of the License at
010 *
011 *      http://www.apache.org/licenses/LICENSE-2.0
012 *
013 * Unless required by applicable law or agreed to in writing, software
014 * distributed under the License is distributed on an "AS IS" BASIS,
015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016 * See the License for the specific language governing permissions and
017 * limitations under the License.
018 * #L%
019 */
020package ca.uhn.fhir.jpa.term.loinc;
021
022import ca.uhn.fhir.i18n.Msg;
023import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
024import ca.uhn.fhir.jpa.entity.TermConcept;
025import ca.uhn.fhir.jpa.entity.TermConceptProperty;
026import ca.uhn.fhir.jpa.term.IZipContentsHandlerCsv;
027import ca.uhn.fhir.jpa.term.api.ITermLoaderSvc;
028import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
029import org.apache.commons.csv.CSVRecord;
030import org.hl7.fhir.r4.model.CodeSystem;
031import org.slf4j.Logger;
032import org.slf4j.LoggerFactory;
033
034import java.util.Map;
035import java.util.Optional;
036
037import static org.apache.commons.lang3.StringUtils.trim;
038
039public class LoincPartLinkHandler implements IZipContentsHandlerCsv {
040
041        private static final Logger ourLog = LoggerFactory.getLogger(LoincPartLinkHandler.class);
042        private final Map<String, TermConcept> myCode2Concept;
043        private final TermCodeSystemVersion myCodeSystemVersion;
044        private final Map<String, CodeSystem.PropertyType> myPropertyNames;
045        private Long myPartCount;
046
047        public LoincPartLinkHandler(
048                        TermCodeSystemVersion theCodeSystemVersion,
049                        Map<String, TermConcept> theCode2concept,
050                        Map<String, CodeSystem.PropertyType> thePropertyNames) {
051                myCodeSystemVersion = theCodeSystemVersion;
052                myCode2Concept = theCode2concept;
053                myPropertyNames = thePropertyNames;
054        }
055
056        @Override
057        public void accept(CSVRecord theRecord) {
058
059                String loincNumber = trim(theRecord.get("LoincNumber"));
060                String property = trim(theRecord.get("Property"));
061                String partName = trim(theRecord.get("PartName"));
062                String partNumber = trim(theRecord.get("PartNumber"));
063
064                /*
065                 * Property has the form http://loinc.org/property/COMPONENT
066                 * but we want just the COMPONENT part
067                 */
068                int lastSlashIdx = property.lastIndexOf("/");
069                String propertyPart = property.substring(lastSlashIdx + 1);
070
071                TermConcept loincConcept = myCode2Concept.get(loincNumber);
072                if (loincConcept == null) {
073                        throw new InternalErrorException(Msg.code(913) + "Unknown loinc code: " + loincNumber);
074                }
075
076                CodeSystem.PropertyType propertyType = myPropertyNames.get(propertyPart);
077                if (propertyType == null) {
078                        return;
079                }
080
081                String expectedValue;
082                if (propertyType == CodeSystem.PropertyType.STRING) {
083                        expectedValue = partName;
084                } else if (propertyType == CodeSystem.PropertyType.CODING) {
085                        expectedValue = partNumber;
086                } else {
087                        throw new InternalErrorException(
088                                        Msg.code(914) + "Don't know how to handle property of type: " + propertyType);
089                }
090
091                Optional<TermConceptProperty> existingProprty = loincConcept.getProperties().stream()
092                                .filter(t -> t.getKey().equals(propertyPart))
093                                .filter(t -> t.getValue().equals(expectedValue))
094                                .findFirst();
095                if (existingProprty.isPresent()) {
096                        return;
097                }
098
099                ourLog.debug("Adding new property {} = {}", propertyPart, partNumber);
100                if (propertyType == CodeSystem.PropertyType.STRING) {
101                        loincConcept.addPropertyString(propertyPart, partName);
102                } else {
103                        loincConcept.addPropertyCoding(propertyPart, ITermLoaderSvc.LOINC_URI, partNumber, partName);
104                }
105        }
106}