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.TermConcept;
24  import ca.uhn.fhir.jpa.term.IRecordHandler;
25  import org.hl7.fhir.r4.model.ConceptMap;
26  import org.hl7.fhir.r4.model.ContactPoint;
27  import org.hl7.fhir.r4.model.Enumerations;
28  import org.hl7.fhir.r4.model.ValueSet;
29  import org.slf4j.Logger;
30  import org.slf4j.LoggerFactory;
31  
32  import java.util.HashMap;
33  import java.util.List;
34  import java.util.Map;
35  import java.util.Properties;
36  
37  import static org.apache.commons.lang3.StringUtils.*;
38  
39  public abstract class BaseLoincHandler implements IRecordHandler {
40  	private static final Logger ourLog = LoggerFactory.getLogger(BaseLoincHandler.class);
41  	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/";
42  
43  	/**
44  	 * This is <b>NOT</b> the LOINC CodeSystem URI! It is just
45  	 * the website URL to LOINC.
46  	 */
47  	public static final String LOINC_WEBSITE_URL = "https://loinc.org";
48  	public static final String REGENSTRIEF_INSTITUTE_INC = "Regenstrief Institute, Inc.";
49  	private final List<ConceptMap> myConceptMaps;
50  	private final Map<String, ConceptMap> myIdToConceptMaps = new HashMap<>();
51  	private final List<ValueSet> myValueSets;
52  	private final Map<String, ValueSet> myIdToValueSet = new HashMap<>();
53  	private final Map<String, TermConcept> myCode2Concept;
54  	private final Properties myUploadProperties;
55  
56  	BaseLoincHandler(Map<String, TermConcept> theCode2Concept, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps, Properties theUploadProperties) {
57  		myValueSets = theValueSets;
58  		myValueSets.forEach(t -> myIdToValueSet.put(t.getId(), t));
59  		myCode2Concept = theCode2Concept;
60  		myConceptMaps = theConceptMaps;
61  		myConceptMaps.forEach(t -> myIdToConceptMaps.put(t.getId(), t));
62  		myUploadProperties = theUploadProperties;
63  	}
64  
65  	void addCodeAsIncludeToValueSet(ValueSet theVs, String theCodeSystemUrl, String theCode, String theDisplayName) {
66  		ValueSet.ConceptSetComponent include = null;
67  		for (ValueSet.ConceptSetComponent next : theVs.getCompose().getInclude()) {
68  			if (next.getSystem().equals(theCodeSystemUrl)) {
69  				include = next;
70  				break;
71  			}
72  		}
73  		if (include == null) {
74  			include = theVs.getCompose().addInclude();
75  			include.setSystem(theCodeSystemUrl);
76  		}
77  
78  		boolean found = false;
79  		for (ValueSet.ConceptReferenceComponent next : include.getConcept()) {
80  			if (next.getCode().equals(theCode)) {
81  				found = true;
82  			}
83  		}
84  		if (!found) {
85  
86  			String displayName = theDisplayName;
87  			if (isBlank(displayName)) {
88  				TermConcept concept = myCode2Concept.get(theCode);
89  				if (concept != null) {
90  					displayName = concept.getDisplay();
91  				}
92  			}
93  
94  			include
95  				.addConcept()
96  				.setCode(theCode)
97  				.setDisplay(displayName);
98  
99  		}
100 	}
101 
102 
103 	void addConceptMapEntry(ConceptMapping theMapping, String theCopyright) {
104 		if (isBlank(theMapping.getSourceCode())) {
105 			return;
106 		}
107 		if (isBlank(theMapping.getTargetCode())) {
108 			return;
109 		}
110 
111 		ConceptMap conceptMap;
112 		if (!myIdToConceptMaps.containsKey(theMapping.getConceptMapId())) {
113 			conceptMap = new ConceptMap();
114 			conceptMap.setId(theMapping.getConceptMapId());
115 			conceptMap.setUrl(theMapping.getConceptMapUri());
116 			conceptMap.setName(theMapping.getConceptMapName());
117 			conceptMap.setVersion(myUploadProperties.getProperty("conceptmap.version"));
118 			conceptMap.setPublisher(REGENSTRIEF_INSTITUTE_INC);
119 			conceptMap.addContact()
120 				.setName(REGENSTRIEF_INSTITUTE_INC)
121 				.addTelecom()
122 				.setSystem(ContactPoint.ContactPointSystem.URL)
123 				.setValue(LOINC_WEBSITE_URL);
124 			String copyright = theCopyright;
125 			if (!copyright.contains("LOINC")) {
126 				copyright = LOINC_COPYRIGHT_STATEMENT + ". " + copyright;
127 			}
128 			conceptMap.setCopyright(copyright);
129 			myIdToConceptMaps.put(theMapping.getConceptMapId(), conceptMap);
130 			myConceptMaps.add(conceptMap);
131 		} else {
132 			conceptMap = myIdToConceptMaps.get(theMapping.getConceptMapId());
133 		}
134 
135 		if (isBlank(theMapping.getCopyright())) {
136 			conceptMap.setCopyright(theMapping.getCopyright());
137 		}
138 
139 		ConceptMap.SourceElementComponent source = null;
140 		ConceptMap.ConceptMapGroupComponent group = null;
141 
142 		for (ConceptMap.ConceptMapGroupComponent next : conceptMap.getGroup()) {
143 			if (next.getSource().equals(theMapping.getSourceCodeSystem())) {
144 				if (next.getTarget().equals(theMapping.getTargetCodeSystem())) {
145 					if (!defaultString(theMapping.getTargetCodeSystemVersion()).equals(defaultString(next.getTargetVersion()))) {
146 						continue;
147 					}
148 					group = next;
149 					break;
150 				}
151 			}
152 		}
153 		if (group == null) {
154 			group = conceptMap.addGroup();
155 			group.setSource(theMapping.getSourceCodeSystem());
156 			group.setTarget(theMapping.getTargetCodeSystem());
157 			group.setTargetVersion(defaultIfBlank(theMapping.getTargetCodeSystemVersion(), null));
158 		}
159 
160 		for (ConceptMap.SourceElementComponent next : group.getElement()) {
161 			if (next.getCode().equals(theMapping.getSourceCode())) {
162 				source = next;
163 			}
164 		}
165 		if (source == null) {
166 			source = group.addElement();
167 			source.setCode(theMapping.getSourceCode());
168 			source.setDisplay(theMapping.getSourceDisplay());
169 		}
170 
171 		boolean found = false;
172 		for (ConceptMap.TargetElementComponent next : source.getTarget()) {
173 			if (next.getCode().equals(theMapping.getTargetCode())) {
174 				found = true;
175 			}
176 		}
177 		if (!found) {
178 			source
179 				.addTarget()
180 				.setCode(theMapping.getTargetCode())
181 				.setDisplay(theMapping.getTargetDisplay())
182 				.setEquivalence(theMapping.getEquivalence());
183 		} else {
184 			ourLog.info("Not going to add a mapping from [{}/{}] to [{}/{}] because one already exists", theMapping.getSourceCodeSystem(), theMapping.getSourceCode(), theMapping.getTargetCodeSystem(), theMapping.getTargetCode());
185 		}
186 	}
187 
188 	ValueSet getValueSet(String theValueSetId, String theValueSetUri, String theValueSetName, String theVersionPropertyName) {
189 
190 		String version = null;
191 		if (isNotBlank(theVersionPropertyName)) {
192 			version = myUploadProperties.getProperty(theVersionPropertyName);
193 		}
194 
195 		ValueSet vs;
196 		if (!myIdToValueSet.containsKey(theValueSetId)) {
197 			vs = new ValueSet();
198 			vs.setUrl(theValueSetUri);
199 			vs.setId(theValueSetId);
200 			vs.setVersion(version);
201 			vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
202 			vs.setPublisher(REGENSTRIEF_INSTITUTE_INC);
203 			vs.addContact()
204 				.setName(REGENSTRIEF_INSTITUTE_INC)
205 				.addTelecom()
206 				.setSystem(ContactPoint.ContactPointSystem.URL)
207 				.setValue(LOINC_WEBSITE_URL);
208 			vs.setCopyright(LOINC_COPYRIGHT_STATEMENT);
209 			myIdToValueSet.put(theValueSetId, vs);
210 			myValueSets.add(vs);
211 		} else {
212 			vs = myIdToValueSet.get(theValueSetId);
213 		}
214 
215 		if (isBlank(vs.getName()) && isNotBlank(theValueSetName)) {
216 			vs.setName(theValueSetName);
217 		}
218 
219 		return vs;
220 	}
221 
222 
223 	static class ConceptMapping {
224 
225 		private String myCopyright;
226 		private String myConceptMapId;
227 		private String myConceptMapUri;
228 		private String myConceptMapName;
229 		private String mySourceCodeSystem;
230 		private String mySourceCode;
231 		private String mySourceDisplay;
232 		private String myTargetCodeSystem;
233 		private String myTargetCode;
234 		private String myTargetDisplay;
235 		private Enumerations.ConceptMapEquivalence myEquivalence;
236 		private String myTargetCodeSystemVersion;
237 
238 		String getConceptMapId() {
239 			return myConceptMapId;
240 		}
241 
242 		ConceptMapping setConceptMapId(String theConceptMapId) {
243 			myConceptMapId = theConceptMapId;
244 			return this;
245 		}
246 
247 		String getConceptMapName() {
248 			return myConceptMapName;
249 		}
250 
251 		ConceptMapping setConceptMapName(String theConceptMapName) {
252 			myConceptMapName = theConceptMapName;
253 			return this;
254 		}
255 
256 		String getConceptMapUri() {
257 			return myConceptMapUri;
258 		}
259 
260 		ConceptMapping setConceptMapUri(String theConceptMapUri) {
261 			myConceptMapUri = theConceptMapUri;
262 			return this;
263 		}
264 
265 		String getCopyright() {
266 			return myCopyright;
267 		}
268 
269 		ConceptMapping setCopyright(String theCopyright) {
270 			myCopyright = theCopyright;
271 			return this;
272 		}
273 
274 		Enumerations.ConceptMapEquivalence getEquivalence() {
275 			return myEquivalence;
276 		}
277 
278 		ConceptMapping setEquivalence(Enumerations.ConceptMapEquivalence theEquivalence) {
279 			myEquivalence = theEquivalence;
280 			return this;
281 		}
282 
283 		String getSourceCode() {
284 			return mySourceCode;
285 		}
286 
287 		ConceptMapping setSourceCode(String theSourceCode) {
288 			mySourceCode = theSourceCode;
289 			return this;
290 		}
291 
292 		String getSourceCodeSystem() {
293 			return mySourceCodeSystem;
294 		}
295 
296 		ConceptMapping setSourceCodeSystem(String theSourceCodeSystem) {
297 			mySourceCodeSystem = theSourceCodeSystem;
298 			return this;
299 		}
300 
301 		String getSourceDisplay() {
302 			return mySourceDisplay;
303 		}
304 
305 		ConceptMapping setSourceDisplay(String theSourceDisplay) {
306 			mySourceDisplay = theSourceDisplay;
307 			return this;
308 		}
309 
310 		String getTargetCode() {
311 			return myTargetCode;
312 		}
313 
314 		ConceptMapping setTargetCode(String theTargetCode) {
315 			myTargetCode = theTargetCode;
316 			return this;
317 		}
318 
319 		String getTargetCodeSystem() {
320 			return myTargetCodeSystem;
321 		}
322 
323 		ConceptMapping setTargetCodeSystem(String theTargetCodeSystem) {
324 			myTargetCodeSystem = theTargetCodeSystem;
325 			return this;
326 		}
327 
328 		String getTargetCodeSystemVersion() {
329 			return myTargetCodeSystemVersion;
330 		}
331 
332 		ConceptMapping setTargetCodeSystemVersion(String theTargetCodeSystemVersion) {
333 			myTargetCodeSystemVersion = theTargetCodeSystemVersion;
334 			return this;
335 		}
336 
337 		String getTargetDisplay() {
338 			return myTargetDisplay;
339 		}
340 
341 		ConceptMapping setTargetDisplay(String theTargetDisplay) {
342 			myTargetDisplay = theTargetDisplay;
343 			return this;
344 		}
345 
346 	}
347 }