
001package ca.uhn.fhir.jpa.term.loinc; 002 003/*- 004 * #%L 005 * HAPI FHIR JPA Server 006 * %% 007 * Copyright (C) 2014 - 2023 Smile CDR, Inc. 008 * %% 009 * Licensed under the Apache License, Version 2.0 (the "License"); 010 * you may not use this file except in compliance with the License. 011 * You may obtain a copy of the License at 012 * 013 * http://www.apache.org/licenses/LICENSE-2.0 014 * 015 * Unless required by applicable law or agreed to in writing, software 016 * distributed under the License is distributed on an "AS IS" BASIS, 017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 018 * See the License for the specific language governing permissions and 019 * limitations under the License. 020 * #L% 021 */ 022 023import ca.uhn.fhir.jpa.entity.TermConcept; 024import ca.uhn.fhir.jpa.term.IZipContentsHandlerCsv; 025import org.apache.commons.lang3.StringUtils; 026import org.hl7.fhir.r4.model.ConceptMap; 027import org.hl7.fhir.r4.model.ContactPoint; 028import org.hl7.fhir.r4.model.Enumerations; 029import org.hl7.fhir.r4.model.ValueSet; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033import java.util.HashMap; 034import java.util.List; 035import java.util.Map; 036import java.util.Properties; 037 038import static ca.uhn.fhir.jpa.term.loinc.LoincUploadPropertiesEnum.*; 039import static org.apache.commons.lang3.StringUtils.*; 040 041public abstract class BaseLoincHandler implements IZipContentsHandlerCsv { 042 private static final Logger ourLog = LoggerFactory.getLogger(BaseLoincHandler.class); 043 044 /** 045 * This is <b>NOT</b> the LOINC CodeSystem URI! It is just 046 * the website URL to LOINC. 047 */ 048 public static final String LOINC_WEBSITE_URL = "https://loinc.org"; 049 public static final String REGENSTRIEF_INSTITUTE_INC = "Regenstrief Institute, Inc."; 050 private final List<ConceptMap> myConceptMaps; 051 private final Map<String, ConceptMap> myIdToConceptMaps = new HashMap<>(); 052 private final List<ValueSet> myValueSets; 053 private final Map<String, ValueSet> myIdToValueSet = new HashMap<>(); 054 private final Map<String, TermConcept> myCode2Concept; 055 protected final Properties myUploadProperties; 056 protected String myLoincCopyrightStatement; 057 058 BaseLoincHandler(Map<String, TermConcept> theCode2Concept, List<ValueSet> theValueSets, 059 List<ConceptMap> theConceptMaps, Properties theUploadProperties) { 060 this(theCode2Concept, theValueSets, theConceptMaps, theUploadProperties, null); 061 } 062 063 BaseLoincHandler(Map<String, TermConcept> theCode2Concept, List<ValueSet> theValueSets, 064 List<ConceptMap> theConceptMaps, Properties theUploadProperties, String theCopyrightStatement) { 065 myValueSets = theValueSets; 066 myValueSets.forEach(t -> myIdToValueSet.put(t.getId(), t)); 067 myCode2Concept = theCode2Concept; 068 myConceptMaps = theConceptMaps; 069 myConceptMaps.forEach(t -> myIdToConceptMaps.put(t.getId(), t)); 070 myUploadProperties = theUploadProperties; 071 myLoincCopyrightStatement = theCopyrightStatement; 072 } 073 074 void addCodeAsIncludeToValueSet(ValueSet theVs, String theCodeSystemUrl, String theCode, String theDisplayName) { 075 ValueSet.ConceptSetComponent include = null; 076 for (ValueSet.ConceptSetComponent next : theVs.getCompose().getInclude()) { 077 if (next.getSystem().equals(theCodeSystemUrl)) { 078 include = next; 079 break; 080 } 081 } 082 if (include == null) { 083 include = theVs.getCompose().addInclude(); 084 include.setSystem(theCodeSystemUrl); 085 if (StringUtils.isNotBlank(theVs.getVersion())) { 086 include.setVersion(theVs.getVersion()); 087 } 088 } 089 090 boolean found = false; 091 for (ValueSet.ConceptReferenceComponent next : include.getConcept()) { 092 if (next.getCode().equals(theCode)) { 093 found = true; 094 } 095 } 096 if (!found) { 097 098 String displayName = theDisplayName; 099 if (isBlank(displayName)) { 100 TermConcept concept = myCode2Concept.get(theCode); 101 if (concept != null) { 102 displayName = concept.getDisplay(); 103 } 104 } 105 106 include 107 .addConcept() 108 .setCode(theCode) 109 .setDisplay(displayName); 110 111 } 112 } 113 114 115 void addConceptMapEntry(ConceptMapping theMapping, String theExternalCopyright) { 116 if (isBlank(theMapping.getSourceCode())) { 117 return; 118 } 119 if (isBlank(theMapping.getTargetCode())) { 120 return; 121 } 122 123 ConceptMap conceptMap; 124 if (!myIdToConceptMaps.containsKey(theMapping.getConceptMapId())) { 125 conceptMap = new ConceptMap(); 126 conceptMap.setId(theMapping.getConceptMapId()); 127 conceptMap.setUrl(theMapping.getConceptMapUri()); 128 conceptMap.setName(theMapping.getConceptMapName()); 129 conceptMap.setVersion(theMapping.getConceptMapVersion()); 130 conceptMap.setPublisher(REGENSTRIEF_INSTITUTE_INC); 131 conceptMap.addContact() 132 .setName(REGENSTRIEF_INSTITUTE_INC) 133 .addTelecom() 134 .setSystem(ContactPoint.ContactPointSystem.URL) 135 .setValue(LOINC_WEBSITE_URL); 136 137 String copyright = theExternalCopyright; 138 if (!copyright.contains("LOINC")) { 139 copyright = myLoincCopyrightStatement + 140 (myLoincCopyrightStatement.endsWith(".") ? " " : ". ") + copyright; 141 } 142 conceptMap.setCopyright(copyright); 143 144 myIdToConceptMaps.put(theMapping.getConceptMapId(), conceptMap); 145 myConceptMaps.add(conceptMap); 146 } else { 147 conceptMap = myIdToConceptMaps.get(theMapping.getConceptMapId()); 148 } 149 150 if (isBlank(theMapping.getCopyright())) { 151 conceptMap.setCopyright(theMapping.getCopyright()); 152 } 153 154 ConceptMap.SourceElementComponent source = null; 155 ConceptMap.ConceptMapGroupComponent group = null; 156 157 for (ConceptMap.ConceptMapGroupComponent next : conceptMap.getGroup()) { 158 if (next.getSource().equals(theMapping.getSourceCodeSystem())) { 159 if (next.getTarget().equals(theMapping.getTargetCodeSystem())) { 160 if (!defaultString(theMapping.getTargetCodeSystemVersion()).equals(defaultString(next.getTargetVersion()))) { 161 continue; 162 } 163 group = next; 164 break; 165 } 166 } 167 } 168 if (group == null) { 169 group = conceptMap.addGroup(); 170 group.setSource(theMapping.getSourceCodeSystem()); 171 group.setSourceVersion(theMapping.getSourceCodeSystemVersion()); 172 group.setTarget(theMapping.getTargetCodeSystem()); 173 group.setTargetVersion(defaultIfBlank(theMapping.getTargetCodeSystemVersion(), null)); 174 } 175 176 for (ConceptMap.SourceElementComponent next : group.getElement()) { 177 if (next.getCode().equals(theMapping.getSourceCode())) { 178 source = next; 179 } 180 } 181 if (source == null) { 182 source = group.addElement(); 183 source.setCode(theMapping.getSourceCode()); 184 source.setDisplay(theMapping.getSourceDisplay()); 185 } 186 187 boolean found = false; 188 for (ConceptMap.TargetElementComponent next : source.getTarget()) { 189 if (next.getCode().equals(theMapping.getTargetCode())) { 190 found = true; 191 } 192 } 193 if (!found) { 194 source 195 .addTarget() 196 .setCode(theMapping.getTargetCode()) 197 .setDisplay(theMapping.getTargetDisplay()) 198 .setEquivalence(theMapping.getEquivalence()); 199 } else { 200 ourLog.info("Not going to add a mapping from [{}/{}] to [{}/{}] because one already exists", theMapping.getSourceCodeSystem(), theMapping.getSourceCode(), theMapping.getTargetCodeSystem(), theMapping.getTargetCode()); 201 } 202 } 203 204 ValueSet getValueSet(String theValueSetId, String theValueSetUri, String theValueSetName, String theVersionPropertyName) { 205 206 String version; 207 String codeSystemVersion = myUploadProperties.getProperty(LOINC_CODESYSTEM_VERSION.getCode()); 208 if (isNotBlank(theVersionPropertyName)) { 209 if (codeSystemVersion != null) { 210 version = myUploadProperties.getProperty(theVersionPropertyName) + "-" + codeSystemVersion; 211 } else { 212 version = myUploadProperties.getProperty(theVersionPropertyName); 213 } 214 } else { 215 version = codeSystemVersion; 216 } 217 218 ValueSet vs; 219 if (!myIdToValueSet.containsKey(theValueSetId)) { 220 vs = new ValueSet(); 221 vs.setUrl(theValueSetUri); 222 vs.setId(theValueSetId); 223 vs.setVersion(version); 224 vs.setStatus(Enumerations.PublicationStatus.ACTIVE); 225 vs.setPublisher(REGENSTRIEF_INSTITUTE_INC); 226 vs.addContact() 227 .setName(REGENSTRIEF_INSTITUTE_INC) 228 .addTelecom() 229 .setSystem(ContactPoint.ContactPointSystem.URL) 230 .setValue(LOINC_WEBSITE_URL); 231 vs.setCopyright(myLoincCopyrightStatement); 232 myIdToValueSet.put(theValueSetId, vs); 233 myValueSets.add(vs); 234 } else { 235 vs = myIdToValueSet.get(theValueSetId); 236 } 237 238 if (isBlank(vs.getName()) && isNotBlank(theValueSetName)) { 239 vs.setName(theValueSetName); 240 } 241 242 return vs; 243 } 244 245 246 static class ConceptMapping { 247 248 private String myCopyright; 249 private String myConceptMapId; 250 private String myConceptMapUri; 251 private String myConceptMapVersion; 252 private String myConceptMapName; 253 private String mySourceCodeSystem; 254 private String mySourceCodeSystemVersion; 255 private String mySourceCode; 256 private String mySourceDisplay; 257 private String myTargetCodeSystem; 258 private String myTargetCode; 259 private String myTargetDisplay; 260 private Enumerations.ConceptMapEquivalence myEquivalence; 261 private String myTargetCodeSystemVersion; 262 263 String getConceptMapId() { 264 return myConceptMapId; 265 } 266 267 ConceptMapping setConceptMapId(String theConceptMapId) { 268 myConceptMapId = theConceptMapId; 269 return this; 270 } 271 272 String getConceptMapName() { 273 return myConceptMapName; 274 } 275 276 ConceptMapping setConceptMapName(String theConceptMapName) { 277 myConceptMapName = theConceptMapName; 278 return this; 279 } 280 281 String getConceptMapUri() { 282 return myConceptMapUri; 283 } 284 285 ConceptMapping setConceptMapUri(String theConceptMapUri) { 286 myConceptMapUri = theConceptMapUri; 287 return this; 288 } 289 290 String getConceptMapVersion() { 291 return myConceptMapVersion; 292 } 293 294 ConceptMapping setConceptMapVersion(String theConceptMapVersion) { 295 myConceptMapVersion = theConceptMapVersion; 296 return this; 297 } 298 299 String getCopyright() { 300 return myCopyright; 301 } 302 303 ConceptMapping setCopyright(String theCopyright) { 304 myCopyright = theCopyright; 305 return this; 306 } 307 308 Enumerations.ConceptMapEquivalence getEquivalence() { 309 return myEquivalence; 310 } 311 312 ConceptMapping setEquivalence(Enumerations.ConceptMapEquivalence theEquivalence) { 313 myEquivalence = theEquivalence; 314 return this; 315 } 316 317 String getSourceCode() { 318 return mySourceCode; 319 } 320 321 ConceptMapping setSourceCode(String theSourceCode) { 322 mySourceCode = theSourceCode; 323 return this; 324 } 325 326 String getSourceCodeSystem() { 327 return mySourceCodeSystem; 328 } 329 330 ConceptMapping setSourceCodeSystem(String theSourceCodeSystem) { 331 mySourceCodeSystem = theSourceCodeSystem; 332 return this; 333 } 334 335 String getSourceCodeSystemVersion() { 336 return mySourceCodeSystemVersion; 337 } 338 339 ConceptMapping setSourceCodeSystemVersion(String theSourceCodeSystemVersion) { 340 mySourceCodeSystemVersion = theSourceCodeSystemVersion; 341 return this; 342 } 343 344 String getSourceDisplay() { 345 return mySourceDisplay; 346 } 347 348 ConceptMapping setSourceDisplay(String theSourceDisplay) { 349 mySourceDisplay = theSourceDisplay; 350 return this; 351 } 352 353 String getTargetCode() { 354 return myTargetCode; 355 } 356 357 ConceptMapping setTargetCode(String theTargetCode) { 358 myTargetCode = theTargetCode; 359 return this; 360 } 361 362 String getTargetCodeSystem() { 363 return myTargetCodeSystem; 364 } 365 366 ConceptMapping setTargetCodeSystem(String theTargetCodeSystem) { 367 myTargetCodeSystem = theTargetCodeSystem; 368 return this; 369 } 370 371 String getTargetCodeSystemVersion() { 372 return myTargetCodeSystemVersion; 373 } 374 375 ConceptMapping setTargetCodeSystemVersion(String theTargetCodeSystemVersion) { 376 myTargetCodeSystemVersion = theTargetCodeSystemVersion; 377 return this; 378 } 379 380 String getTargetDisplay() { 381 return myTargetDisplay; 382 } 383 384 ConceptMapping setTargetDisplay(String theTargetDisplay) { 385 myTargetDisplay = theTargetDisplay; 386 return this; 387 } 388 389 } 390}