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