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