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.IHapiTerminologyLoaderSvc;
25  import ca.uhn.fhir.jpa.term.IRecordHandler;
26  import ca.uhn.fhir.rest.server.exceptions.InternalErrorException;
27  import org.apache.commons.csv.CSVRecord;
28  import org.hl7.fhir.r4.model.ConceptMap;
29  import org.hl7.fhir.r4.model.Enumerations;
30  import org.hl7.fhir.r4.model.ValueSet;
31  
32  import java.util.*;
33  
34  import static org.apache.commons.lang3.StringUtils.isNotBlank;
35  import static org.apache.commons.lang3.StringUtils.trim;
36  
37  public class LoincRsnaPlaybookHandler extends BaseLoincHandler implements IRecordHandler {
38  
39  	public static final String RSNA_CODES_VS_ID = "loinc-rsna-radiology-playbook";
40  	public static final String RSNA_CODES_VS_URI = "http://loinc.org/vs/loinc-rsna-radiology-playbook";
41  	public static final String RSNA_CODES_VS_NAME = "LOINC/RSNA Radiology Playbook";
42  	public static final String RID_CS_URI = "http://www.radlex.org";
43  	/*
44  	 * About these being the same - Per Dan Vreeman:
45  	 * We had some discussion about this, and both
46  	 * RIDs (RadLex clinical terms) and RPIDs (Radlex Playbook Ids)
47  	 * belong to the same "code system" since they will never collide.
48  	 * The codesystem uri is "http://www.radlex.org". FYI, that's
49  	 * now listed on the FHIR page:
50  	 * https://www.hl7.org/fhir/terminologies-systems.html
51  	 * -ja
52  	 */
53  	public static final String RPID_CS_URI = RID_CS_URI;
54  	private static final String CM_COPYRIGHT = "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/. The LOINC/RSNA Radiology Playbook and the LOINC Part File contain content from RadLex® (http://rsna.org/RadLex.aspx), copyright © 2005-2017, The Radiological Society of North America, Inc., available at no cost under the license at http://www.rsna.org/uploadedFiles/RSNA/Content/Informatics/RadLex_License_Agreement_and_Terms_of_Use_V2_Final.pdf.";
55  	private final Map<String, TermConcept> myCode2Concept;
56  	private final List<ValueSet> myValueSets;
57  	private final Map<String, ValueSet> myIdToValueSet = new HashMap<>();
58  	private final Set<String> myCodesInRsnaPlaybookValueSet = new HashSet<>();
59  
60  	/**
61  	 * Constructor
62  	 */
63  	public LoincRsnaPlaybookHandler(Map<String, TermConcept> theCode2concept, List<ValueSet> theValueSets, List<ConceptMap> theConceptMaps, Properties theUploadProperties) {
64  		super(theCode2concept, theValueSets, theConceptMaps, theUploadProperties);
65  		myCode2Concept = theCode2concept;
66  		myValueSets = theValueSets;
67  	}
68  
69  	@Override
70  	public void accept(CSVRecord theRecord) {
71  
72  		String loincNumber = trim(theRecord.get("LoincNumber"));
73  		String longCommonName = trim(theRecord.get("LongCommonName"));
74  		String partNumber = trim(theRecord.get("PartNumber"));
75  		String partTypeName = trim(theRecord.get("PartTypeName"));
76  		String partName = trim(theRecord.get("PartName"));
77  		String partSequenceOrder = trim(theRecord.get("PartSequenceOrder"));
78  		String rid = trim(theRecord.get("RID"));
79  		String preferredName = trim(theRecord.get("PreferredName"));
80  		String rpid = trim(theRecord.get("RPID"));
81  		String longName = trim(theRecord.get("LongName"));
82  
83  		// RSNA Codes VS
84  		ValueSet vs;
85  		if (!myIdToValueSet.containsKey(RSNA_CODES_VS_ID)) {
86  			vs = new ValueSet();
87  			vs.setUrl(RSNA_CODES_VS_URI);
88  			vs.setId(RSNA_CODES_VS_ID);
89  			vs.setName(RSNA_CODES_VS_NAME);
90  			vs.setStatus(Enumerations.PublicationStatus.ACTIVE);
91  			myIdToValueSet.put(RSNA_CODES_VS_ID, vs);
92  			myValueSets.add(vs);
93  		} else {
94  			vs = myIdToValueSet.get(RSNA_CODES_VS_ID);
95  		}
96  
97  		if (!myCodesInRsnaPlaybookValueSet.contains(loincNumber)) {
98  			vs
99  				.getCompose()
100 				.getIncludeFirstRep()
101 				.setSystem(IHapiTerminologyLoaderSvc.LOINC_URI)
102 				.addConcept()
103 				.setCode(loincNumber)
104 				.setDisplay(longCommonName);
105 			myCodesInRsnaPlaybookValueSet.add(loincNumber);
106 		}
107 
108 		String loincCodePropName;
109 		switch (partTypeName) {
110 			case "Rad.Anatomic Location.Region Imaged":
111 				loincCodePropName = "rad-anatomic-location-region-imaged";
112 				break;
113 			case "Rad.Anatomic Location.Imaging Focus":
114 				loincCodePropName = "rad-anatomic-location-imaging-focus";
115 				break;
116 			case "Rad.Modality.Modality type":
117 				loincCodePropName = "rad-modality-modality-type";
118 				break;
119 			case "Rad.Modality.Modality subtype":
120 				loincCodePropName = "rad-modality-modality-subtype";
121 				break;
122 			case "Rad.Anatomic Location.Laterality":
123 				loincCodePropName = "rad-anatomic-location-laterality";
124 				break;
125 			case "Rad.Anatomic Location.Laterality.Presence":
126 				loincCodePropName = "rad-anatomic-location-laterality-presence";
127 				break;
128 			case "Rad.Guidance for.Action":
129 				loincCodePropName = "rad-guidance-for-action";
130 				break;
131 			case "Rad.Guidance for.Approach":
132 				loincCodePropName = "rad-guidance-for-approach";
133 				break;
134 			case "Rad.Guidance for.Object":
135 				loincCodePropName = "rad-guidance-for-object";
136 				break;
137 			case "Rad.Guidance for.Presence":
138 				loincCodePropName = "rad-guidance-for-presence";
139 				break;
140 			case "Rad.Maneuver.Maneuver type":
141 				loincCodePropName = "rad-maneuver-maneuver-type";
142 				break;
143 			case "Rad.Pharmaceutical.Route":
144 				loincCodePropName = "rad-pharmaceutical-route";
145 				break;
146 			case "Rad.Pharmaceutical.Substance Given":
147 				loincCodePropName = "rad-pharmaceutical-substance-given";
148 				break;
149 			case "Rad.Reason for Exam":
150 				loincCodePropName = "rad-reason-for-exam";
151 				break;
152 			case "Rad.Subject":
153 				loincCodePropName = "rad-subject";
154 				break;
155 			case "Rad.Timing":
156 				loincCodePropName = "rad-timing";
157 				break;
158 			case "Rad.View.Aggregation":
159 				loincCodePropName = "rad-view-view-aggregation";
160 				break;
161 			case "Rad.View.View type":
162 				loincCodePropName = "rad-view-view-type";
163 				break;
164 			default:
165 				throw new InternalErrorException("Unknown PartTypeName: " + partTypeName);
166 		}
167 
168 		TermConcept code = myCode2Concept.get(loincNumber);
169 		if (code != null) {
170 			code.addPropertyCoding(loincCodePropName, IHapiTerminologyLoaderSvc.LOINC_URI, partNumber, partName);
171 		}
172 
173 		// LOINC Part -> Radlex RID code mappings
174 		if (isNotBlank(rid)) {
175 			addConceptMapEntry(
176 				new ConceptMapping()
177 					.setConceptMapId(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_ID)
178 					.setConceptMapUri(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_URI)
179 					.setConceptMapName(LoincPartRelatedCodeMappingHandler.LOINC_PART_TO_RID_PART_MAP_NAME)
180 					.setSourceCodeSystem(IHapiTerminologyLoaderSvc.LOINC_URI)
181 					.setSourceCode(partNumber)
182 					.setSourceDisplay(partName)
183 					.setTargetCodeSystem(RID_CS_URI)
184 					.setTargetCode(rid)
185 					.setTargetDisplay(preferredName)
186 					.setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL),
187 				CM_COPYRIGHT);
188 		}
189 
190 		// LOINC Term -> Radlex RPID code mappings
191 		if (isNotBlank(rpid)) {
192 			addConceptMapEntry(
193 				new ConceptMapping()
194 					.setConceptMapId(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_ID)
195 					.setConceptMapUri(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_URI)
196 					.setConceptMapName(LoincPartRelatedCodeMappingHandler.LOINC_TERM_TO_RPID_PART_MAP_NAME)
197 					.setSourceCodeSystem(IHapiTerminologyLoaderSvc.LOINC_URI)
198 					.setSourceCode(loincNumber)
199 					.setSourceDisplay(longCommonName)
200 					.setTargetCodeSystem(RPID_CS_URI)
201 					.setTargetCode(rpid)
202 					.setTargetDisplay(longName)
203 					.setEquivalence(Enumerations.ConceptMapEquivalence.EQUAL),
204 				CM_COPYRIGHT);
205 		}
206 
207 	}
208 
209 
210 }