View Javadoc
1   package ca.uhn.fhir.jpa.term.loinc;
2   
3   /*-
4    * #%L
5    * HAPI FHIR JPA Server
6    * %%
7    * Copyright (C) 2014 - 2018 University Health Network
8    * %%
9    * Licensed under the Apache License, Version 2.0 (the "License");
10   * you may not use this file except in compliance with the License.
11   * You may obtain a copy of the License at
12   * 
13   *      http://www.apache.org/licenses/LICENSE-2.0
14   * 
15   * Unless required by applicable law or agreed to in writing, software
16   * distributed under the License is distributed on an "AS IS" BASIS,
17   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18   * See the License for the specific language governing permissions and
19   * limitations under the License.
20   * #L%
21   */
22  
23  import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
24  import ca.uhn.fhir.jpa.entity.TermConcept;
25  import ca.uhn.fhir.jpa.term.IHapiTerminologyLoaderSvc;
26  import ca.uhn.fhir.jpa.term.IRecordHandler;
27  import ca.uhn.fhir.jpa.term.TerminologyLoaderSvcImpl;
28  import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
29  import org.apache.commons.csv.CSVRecord;
30  import org.apache.commons.lang3.Validate;
31  import org.hl7.fhir.r4.model.CodeSystem;
32  import org.slf4j.Logger;
33  import org.slf4j.LoggerFactory;
34  
35  import java.util.Map;
36  
37  import static org.apache.commons.lang3.StringUtils.isNotBlank;
38  import static org.apache.commons.lang3.StringUtils.trim;
39  
40  public class LoincHandler implements IRecordHandler {
41  
42  	private final Map<String, TermConcept> myCode2Concept;
43  	private final TermCodeSystemVersion myCodeSystemVersion;
44  	private final Map<String, CodeSystem.PropertyType> myPropertyNames;
45  	private final Map<PartTypeAndPartName, String> myPartTypeAndPartNameToPartNumber;
46  
47  	public LoincHandler(TermCodeSystemVersion theCodeSystemVersion, Map<String, TermConcept> theCode2concept, Map<String, CodeSystem.PropertyType> thePropertyNames, Map<PartTypeAndPartName, String> thePartTypeAndPartNameToPartNumber) {
48  		myCodeSystemVersion = theCodeSystemVersion;
49  		myCode2Concept = theCode2concept;
50  		myPropertyNames = thePropertyNames;
51  		myPartTypeAndPartNameToPartNumber = thePartTypeAndPartNameToPartNumber;
52  	}
53  
54  	@Override
55  	public void accept(CSVRecord theRecord) {
56  		String code = trim(theRecord.get("LOINC_NUM"));
57  		if (isNotBlank(code)) {
58  			String longCommonName = trim(theRecord.get("LONG_COMMON_NAME"));
59  			String shortName = trim(theRecord.get("SHORTNAME"));
60  			String consumerName = trim(theRecord.get("CONSUMER_NAME"));
61  			String display = TerminologyLoaderSvcImpl.firstNonBlank(longCommonName, shortName, consumerName);
62  
63  			TermConcept concept = new TermConcept(myCodeSystemVersion, code);
64  			concept.setDisplay(display);
65  
66  			if (!display.equalsIgnoreCase(shortName)) {
67  				concept
68  					.addDesignation()
69  					.setUseDisplay("ShortName")
70  					.setValue(shortName);
71  			}
72  
73  			for (String nextPropertyName : myPropertyNames.keySet()) {
74  				if (!theRecord.toMap().containsKey(nextPropertyName)) {
75  					continue;
76  				}
77  
78  				CodeSystem.PropertyType nextPropertyType = myPropertyNames.get(nextPropertyName);
79  
80  				String nextPropertyValue = theRecord.get(nextPropertyName);
81  				if (isNotBlank(nextPropertyValue)) {
82  					nextPropertyValue = trim(nextPropertyValue);
83  
84  					switch (nextPropertyType) {
85  						case STRING:
86  							concept.addPropertyString(nextPropertyName, nextPropertyValue);
87  							break;
88  						case CODING:
89  							PartTypeAndPartName key = new PartTypeAndPartName(nextPropertyName, nextPropertyValue);
90  							String partNumber = myPartTypeAndPartNameToPartNumber.get(key);
91  
92  							if (partNumber == null && nextPropertyName.equals("TIME_ASPCT")) {
93  								key = new PartTypeAndPartName("TIME", nextPropertyValue);
94  								partNumber = myPartTypeAndPartNameToPartNumber.get(key);
95  							}
96  							if (partNumber == null && nextPropertyName.equals("METHOD_TYP")) {
97  								key = new PartTypeAndPartName("METHOD", nextPropertyValue);
98  								partNumber = myPartTypeAndPartNameToPartNumber.get(key);
99  							}
100 							if (partNumber == null && nextPropertyName.equals("SCALE_TYP")) {
101 								key = new PartTypeAndPartName("SCALE", nextPropertyValue);
102 								partNumber = myPartTypeAndPartNameToPartNumber.get(key);
103 							}
104 
105 							if (partNumber == null && nextPropertyName.equals("SYSTEM") && nextPropertyValue.startsWith("^")) {
106 								continue;
107 							}
108 
109 //							Validate.notBlank(partNumber, "Unknown part: " + key);
110 							if (isNotBlank(partNumber)) {
111 								concept.addPropertyCoding(nextPropertyName, IHapiTerminologyLoaderSvc.LOINC_URI, partNumber, nextPropertyValue);
112 							} else {
113 								ourLog.warn("Unable to find part code with TYPE[{}] and NAME[{}]", key.getPartType(), key.getPartName());
114 							}
115 							break;
116 						case DECIMAL:
117 						case CODE:
118 						case INTEGER:
119 						case BOOLEAN:
120 						case DATETIME:
121 						case NULL:
122 							throw new InternalErrorException("Don't know how to handle LOINC property of type: " + nextPropertyType);
123 					}
124 
125 				}
126 			}
127 
128 			Validate.isTrue(!myCode2Concept.containsKey(code), "The code %s has appeared more than once", code);
129 			myCode2Concept.put(code, concept);
130 		}
131 	}
132 private static final Logger ourLog = LoggerFactory.getLogger(LoincHandler.class);
133 }