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 static final Logger ourLog = LoggerFactory.getLogger(LoincHandler.class);
43  	private final Map<String, TermConcept> myCode2Concept;
44  	private final TermCodeSystemVersion myCodeSystemVersion;
45  	private final Map<String, CodeSystem.PropertyType> myPropertyNames;
46  	private final Map<PartTypeAndPartName, String> myPartTypeAndPartNameToPartNumber;
47  
48  	public LoincHandler(TermCodeSystemVersion theCodeSystemVersion, Map<String, TermConcept> theCode2concept, Map<String, CodeSystem.PropertyType> thePropertyNames, Map<PartTypeAndPartName, String> thePartTypeAndPartNameToPartNumber) {
49  		myCodeSystemVersion = theCodeSystemVersion;
50  		myCode2Concept = theCode2concept;
51  		myPropertyNames = thePropertyNames;
52  		myPartTypeAndPartNameToPartNumber = thePartTypeAndPartNameToPartNumber;
53  	}
54  
55  	@Override
56  	public void accept(CSVRecord theRecord) {
57  		String code = trim(theRecord.get("LOINC_NUM"));
58  		if (isNotBlank(code)) {
59  			String longCommonName = trim(theRecord.get("LONG_COMMON_NAME"));
60  			String shortName = trim(theRecord.get("SHORTNAME"));
61  			String consumerName = trim(theRecord.get("CONSUMER_NAME"));
62  			String display = TerminologyLoaderSvcImpl.firstNonBlank(longCommonName, shortName, consumerName);
63  
64  			TermConcept concept = new TermConcept(myCodeSystemVersion, code);
65  			concept.setDisplay(display);
66  
67  			if (!display.equalsIgnoreCase(shortName)) {
68  				concept
69  					.addDesignation()
70  					.setUseDisplay("ShortName")
71  					.setValue(shortName);
72  			}
73  
74  			for (String nextPropertyName : myPropertyNames.keySet()) {
75  				if (!theRecord.toMap().containsKey(nextPropertyName)) {
76  					continue;
77  				}
78  
79  				CodeSystem.PropertyType nextPropertyType = myPropertyNames.get(nextPropertyName);
80  
81  				String nextPropertyValue = theRecord.get(nextPropertyName);
82  				if (isNotBlank(nextPropertyValue)) {
83  					nextPropertyValue = trim(nextPropertyValue);
84  
85  					switch (nextPropertyType) {
86  						case STRING:
87  							concept.addPropertyString(nextPropertyName, nextPropertyValue);
88  							break;
89  						case CODING:
90  							// FIXME: handle "Ser/Plas^Donor"
91  							String propertyValue = nextPropertyValue;
92  							if (nextPropertyName.equals("COMPONENT")) {
93  								if (propertyValue.contains("^")) {
94  									propertyValue = propertyValue.substring(0, propertyValue.indexOf("^"));
95  								} else if (propertyValue.contains("/")) {
96  									propertyValue = propertyValue.substring(0, propertyValue.indexOf("/"));
97  								}
98  							}
99  
100 							PartTypeAndPartName key = new PartTypeAndPartName(nextPropertyName, propertyValue);
101 							String partNumber = myPartTypeAndPartNameToPartNumber.get(key);
102 
103 							if (partNumber == null && nextPropertyName.equals("TIME_ASPCT")) {
104 								key = new PartTypeAndPartName("TIME", nextPropertyValue);
105 								partNumber = myPartTypeAndPartNameToPartNumber.get(key);
106 							}
107 							if (partNumber == null && nextPropertyName.equals("METHOD_TYP")) {
108 								key = new PartTypeAndPartName("METHOD", nextPropertyValue);
109 								partNumber = myPartTypeAndPartNameToPartNumber.get(key);
110 							}
111 							if (partNumber == null && nextPropertyName.equals("SCALE_TYP")) {
112 								key = new PartTypeAndPartName("SCALE", nextPropertyValue);
113 								partNumber = myPartTypeAndPartNameToPartNumber.get(key);
114 							}
115 
116 							if (partNumber == null && nextPropertyName.equals("SYSTEM") && nextPropertyValue.startsWith("^")) {
117 								continue;
118 							}
119 
120 							if (isNotBlank(partNumber)) {
121 								concept.addPropertyCoding(nextPropertyName, IHapiTerminologyLoaderSvc.LOINC_URI, partNumber, nextPropertyValue);
122 							} else {
123 								String msg = "Unable to find part code with TYPE[" + key.getPartType() + "] and NAME[" + nextPropertyValue + "] (using name " + propertyValue + ")";
124 								ourLog.warn(msg);
125 //								throw new InternalErrorException(msg);
126 							}
127 							break;
128 						case DECIMAL:
129 						case CODE:
130 						case INTEGER:
131 						case BOOLEAN:
132 						case DATETIME:
133 						case NULL:
134 							throw new InternalErrorException("Don't know how to handle LOINC property of type: " + nextPropertyType);
135 					}
136 
137 				}
138 			}
139 
140 			Validate.isTrue(!myCode2Concept.containsKey(code), "The code %s has appeared more than once", code);
141 			myCode2Concept.put(code, concept);
142 		}
143 	}
144 }