Hide keyboard shortcuts

Hot-keys on this page

r m x p   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

1#!/usr/bin/env python 

2 

3r""" 

4camcops_server/cc_modules/cc_snomed.py 

5 

6=============================================================================== 

7 

8 Copyright (C) 2012-2020 Rudolf Cardinal (rudolf@pobox.com). 

9 

10 This file is part of CamCOPS. 

11 

12 CamCOPS is free software: you can redistribute it and/or modify 

13 it under the terms of the GNU General Public License as published by 

14 the Free Software Foundation, either version 3 of the License, or 

15 (at your option) any later version. 

16 

17 CamCOPS is distributed in the hope that it will be useful, 

18 but WITHOUT ANY WARRANTY; without even the implied warranty of 

19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

20 GNU General Public License for more details. 

21 

22 You should have received a copy of the GNU General Public License 

23 along with CamCOPS. If not, see <https://www.gnu.org/licenses/>. 

24 

25=============================================================================== 

26 

27**CamCOPS code to support SNOMED-CT.** 

28 

29Note that the licensing arrangements for SNOMED-CT mean that the actual codes 

30must be separate (and not part of the CamCOPS code). See the documentation. 

31 

32Some tests: 

33 

34.. code-block:: python 

35 

36 import logging 

37 from cardinal_pythonlib.logs import main_only_quicksetup_rootlogger 

38 from camcops_server.cc_modules.cc_request import get_command_line_request 

39 from camcops_server.cc_modules.cc_snomed import * 

40 from camcops_server.tasks.phq9 import Phq9 

41 main_only_quicksetup_rootlogger(level=logging.DEBUG) 

42 req = get_command_line_request() 

43 

44 # --------------------------------------------------------------------- 

45 # Read the XML, etc. 

46 # --------------------------------------------------------------------- 

47 

48 print(VALID_SNOMED_LOOKUPS) 

49 concepts = get_all_snomed_concepts(req.config.snomed_xml_filename) 

50 

51 # --------------------------------------------------------------------- 

52 # Test a task, and loading SNOMED data from XML via the CamCOPS config 

53 # --------------------------------------------------------------------- 

54 

55 phq9 = Phq9() 

56 print("\n".join(str(x) for x in phq9.get_snomed_codes(req))) 

57 phq9.q1 = 2 

58 phq9.q2 = 2 

59 phq9.q3 = 2 

60 phq9.q4 = 2 

61 phq9.q5 = 2 

62 phq9.q6 = 2 

63 phq9.q7 = 2 

64 phq9.q8 = 2 

65 phq9.q9 = 2 

66 phq9.q10 = 2 

67 print("\n".join(repr(x) for x in phq9.get_snomed_codes(req))) 

68 print("\n".join(str(x) for x in phq9.get_snomed_codes(req))) 

69 

70Note diagnostic coding maps: 

71 

72- https://www.nlm.nih.gov/research/umls/mapping_projects/icd9cm_to_snomedct.html 

73- https://www.nlm.nih.gov/research/umls/mapping_projects/snomedct_to_icd10cm.html 

74 

75Other testing: 

76 

77.. code-block:: bash 

78 

79 camcops_server dev_cli --verbose 

80 

81.. code-block:: python 

82 

83 from camcops_server.cc_modules.cc_snomed import * 

84 

85 athena_concepts = get_athena_concepts(config.athena_concept_tsv_filename) 

86 relationships = get_athena_concept_relationships(config.athena_concept_relationship_tsv_filename) 

87 rel_ids = set(r.relationship_id for r in relationships) 

88 icd9, icd10 = get_icd9cm_icd10_snomed_concepts(config.athena_concept_tsv_filename, config.athena_concept_relationship_tsv_filename) 

89 

90 ac = get_athena_concepts(config.athena_concept_tsv_filename, vocabulary_ids=[AthenaVocabularyId.SNOMED], concept_codes=["4303690"]) 

91 

92Regarding temporal coding: 

93 

94- SNOMED CT has some concepts relating to time (e.g. "time aspect", "single 

95 point in time", "date of birth", "date of event", "time (property)", "time 

96 (attribute)"). HOWEVER, note that SNOMED-CT isn't intended for recording 

97 event dates: 

98 

99 - https://confluence.ihtsdotools.org/display/DOCRSG/3.5.+Safely+representing+the+context+of+recorded+codes 

100 

101 "Contextual information that is not represented by SNOMED CT 

102 

103 Clinical statements that contain SNOMED CT Concept representations will 

104 be associated with some information which is not intended to be 

105 represented using SNOMED CT: 

106 

107 - Dates, times of an activity of recording and activity; 

108 - Quantitative information including ranges and durations; 

109 - Identifiers or names of authors, providers of information or other 

110 parties involved in a recorded activity. 

111 - SNOMED CT is not intended to represent this information. Appropriate 

112 constructs in a standardized or proprietary record architecture 

113 should be used to relate this information to SNOMED CT encoded 

114 clinical statements." 

115 

116 - https://confluence.ihtsdotools.org/display/DOCRSG/3.4.+Record+architectures%2C+structures+and+semantics 

117 

118 "Using SNOMED CT in standard architectures 

119 

120 ... 

121 

122 Each health record component has the potential to include: 

123 

124 - Dates and times of actual and planned events. 

125 - Associations with people, organizations, devices and other entities 

126 that participate or are used in relations to a recorded event or 

127 plan. 

128 - Codes or other representations that name or provide the semantic 

129 information container, link, or statement: 

130  

131 - SNOMED CT fulfills this role in a structured health record. 

132 

133 - Additional data including text, numeric values, images and other 

134 digital data." 

135 

136- That explains why the grammar has no explanation of how to represent 

137 dates/times, perhaps! 

138 

139""" # noqa 

140 

141from collections import OrderedDict 

142import csv 

143import logging 

144from typing import Dict, List, Optional, Set, Tuple, Union 

145import xml.etree.cElementTree as ElementTree 

146 

147from cardinal_pythonlib.athena_ohdsi import ( 

148 AthenaConceptRow, 

149 AthenaRelationshipId, 

150 AthenaVocabularyId, 

151 get_athena_concept_relationships, 

152 get_athena_concepts, 

153) 

154from cardinal_pythonlib.logs import BraceStyleAdapter 

155from cardinal_pythonlib.reprfunc import simple_repr 

156# noinspection PyUnresolvedReferences 

157from cardinal_pythonlib.snomed import ( # noqa: F401 

158 SnomedAttribute, 

159 SnomedAttributeGroup, 

160 SnomedAttributeSet, 

161 SnomedConcept as SnomedConceptCardinalPythonlib, 

162 SnomedExpression as SnomedExpressionCardinalPythonlib, 

163 SnomedFocusConcept, 

164 SnomedRefinement, 

165 SnomedValue, 

166) 

167 

168from camcops_server.cc_modules.cc_cache import cache_region_static, fkg 

169from camcops_server.cc_modules.cc_xml import XmlDataTypes, XmlElement 

170 

171log = BraceStyleAdapter(logging.getLogger(__name__)) 

172 

173 

174# ============================================================================= 

175# Constants 

176# ============================================================================= 

177 

178# ----------------------------------------------------------------------------- 

179# Internal 

180# ----------------------------------------------------------------------------- 

181 

182SNOMED_XML_NAME = "snomed_ct_expression" 

183 

184# ----------------------------------------------------------------------------- 

185# ICD-9-CM; from the client: camcops --print_icd9_codes 

186# ----------------------------------------------------------------------------- 

187 

188CLIENT_ICD9CM_CODES = set(""" 

189290.0 290.1 290.10 290.11 290.12 290.13 290.2 290.20 290.21 290.3 290.4 290.40 

190290.41 290.42 290.43 290.8 290.9 291.0 291.1 291.2 291.3 291.4 291.5 291.8 

191291.81 291.82 291.89 291.9 292.0 292.1 292.11 292.12 292.2 292.8 292.81 292.82 

192292.83 292.84 292.85 292.89 292.9 293.0 293.1 293.8 293.81 293.82 293.83 293.84 

193293.89 293.9 294.0 294.1 294.10 294.11 294.2 294.20 294.21 294.8 294.9 295.0 

194295.00 295.01 295.02 295.03 295.04 295.05 295.1 295.10 295.11 295.12 295.13 

195295.14 295.15 295.2 295.20 295.21 295.22 295.23 295.24 295.25 295.3 295.30 

196295.31 295.32 295.33 295.34 295.35 295.4 295.40 295.41 295.42 295.43 295.44 

197295.45 295.5 295.50 295.51 295.52 295.53 295.54 295.55 295.6 295.60 295.61 

198295.62 295.63 295.64 295.65 295.7 295.70 295.71 295.72 295.73 295.74 295.75 

199295.8 295.80 295.81 295.82 295.83 295.84 295.85 295.9 295.90 295.91 295.92 

200295.93 295.94 295.95 296.0 296.00 296.01 296.02 296.03 296.04 296.05 296.06 

201296.1 296.10 296.11 296.12 296.13 296.14 296.15 296.16 296.2 296.20 296.21 

202296.22 296.23 296.24 296.25 296.26 296.3 296.30 296.31 296.32 296.33 296.34 

203296.35 296.36 296.4 296.40 296.41 296.42 296.43 296.44 296.45 296.46 296.5 

204296.50 296.51 296.52 296.53 296.54 296.55 296.56 296.6 296.60 296.61 296.62 

205296.63 296.64 296.65 296.66 296.7 296.8 296.80 296.81 296.82 296.89 296.9 

206296.90 296.99 297.0 297.1 297.2 297.3 297.8 297.9 298.0 298.1 298.2 298.3 298.4 

207298.8 298.9 299.0 299.00 299.01 299.1 299.10 299.11 299.8 299.80 299.81 299.9 

208299.90 299.91 300.0 300.00 300.01 300.02 300.09 300.1 300.10 300.11 300.12 

209300.13 300.14 300.15 300.16 300.19 300.2 300.20 300.21 300.22 300.23 300.29 

210300.3 300.4 300.5 300.6 300.7 300.8 300.81 300.82 300.89 300.9 301.0 301.1 

211301.10 301.11 301.12 301.13 301.2 301.20 301.21 301.22 301.3 301.4 301.5 301.50 

212301.51 301.59 301.6 301.7 301.8 301.81 301.82 301.83 301.84 301.89 301.9 302.0 

213302.1 302.2 302.3 302.4 302.5 302.50 302.51 302.52 302.53 302.6 302.7 302.70 

214302.71 302.72 302.73 302.74 302.75 302.76 302.79 302.8 302.81 302.82 302.83 

215302.84 302.85 302.89 302.9 303.0 303.00 303.01 303.02 303.03 303.0x 303.9 

216303.90 303.91 303.92 303.93 304.0 304.00 304.01 304.02 304.03 304.1 304.10 

217304.11 304.12 304.13 304.2 304.20 304.21 304.22 304.23 304.3 304.30 304.31 

218304.32 304.33 304.4 304.40 304.41 304.42 304.43 304.5 304.50 304.51 304.52 

219304.53 304.6 304.60 304.61 304.62 304.63 304.7 304.70 304.71 304.72 304.73 

220304.8 304.80 304.81 304.82 304.83 304.9 304.90 304.91 304.92 304.93 305.0 

221305.00 305.01 305.02 305.03 305.0x 305.1 305.10 305.11 305.12 305.13 305.2 

222305.20 305.21 305.22 305.23 305.2x 305.3 305.30 305.31 305.32 305.33 305.3x 

223305.4 305.40 305.41 305.42 305.43 305.4x 305.5 305.50 305.51 305.52 305.53 

224305.5x 305.6 305.60 305.61 305.62 305.63 305.6x 305.7 305.70 305.71 305.72 

225305.73 305.7x 305.8 305.80 305.81 305.82 305.83 305.8x 305.9 305.90 305.91 

226305.92 305.93 305.9x 306.0 306.1 306.2 306.3 306.4 306.50 306.51 306.52 306.53 

227306.59 306.6 306.7 306.8 306.9 307.0 307.1 307.2 307.20 307.21 307.22 307.23 

228307.3 307.4 307.40 307.41 307.42 307.43 307.44 307.45 307.46 307.47 307.48 

229307.49 307.5 307.50 307.51 307.52 307.53 307.54 307.59 307.6 307.7 307.8 307.80 

230307.81 307.89 307.9 308.0 308.1 308.2 308.3 308.4 308.9 309.0 309.1 309.2 

231309.21 309.22 309.23 309.24 309.28 309.29 309.3 309.4 309.8 309.81 309.82 

232309.83 309.89 309.9 310.0 310.1 310.2 310.8 310.81 310.89 310.9 311 312.0 

233312.00 312.01 312.02 312.03 312.1 312.10 312.11 312.12 312.13 312.2 312.20 

234312.21 312.22 312.23 312.3 312.30 312.31 312.32 312.33 312.34 312.35 312.39 

235312.4 312.8 312.81 312.82 312.89 312.9 313.0 313.1 313.2 313.21 313.22 313.23 

236313.3 313.8 313.81 313.82 313.83 313.89 313.9 314.0 314.00 314.01 314.1 314.2 

237314.8 314.9 315.0 315.00 315.01 315.02 315.09 315.1 315.2 315.3 315.31 315.32 

238315.34 315.35 315.39 315.4 315.5 315.8 315.9 316 317 318.0 318.1 318.2 319 

239V71.09 

240""".split()) 

241 

242# ----------------------------------------------------------------------------- 

243# ICD-10; from the client: camcops --print_icd10_codes 

244# ----------------------------------------------------------------------------- 

245 

246CLIENT_ICD10_CODES = set(""" 

247F00 F00.0 F00.00 F00.000 F00.001 F00.002 F00.01 F00.010 F00.011 F00.012 F00.02 

248F00.020 F00.021 F00.022 F00.03 F00.030 F00.031 F00.032 F00.04 F00.040 F00.041 

249F00.042 F00.1 F00.10 F00.100 F00.101 F00.102 F00.11 F00.110 F00.111 F00.112 

250F00.12 F00.120 F00.121 F00.122 F00.13 F00.130 F00.131 F00.132 F00.14 F00.140 

251F00.141 F00.142 F00.2 F00.20 F00.200 F00.201 F00.202 F00.21 F00.210 F00.211 

252F00.212 F00.22 F00.220 F00.221 F00.222 F00.23 F00.230 F00.231 F00.232 F00.24 

253F00.240 F00.241 F00.242 F00.9 F00.90 F00.900 F00.901 F00.902 F00.91 F00.910 

254F00.911 F00.912 F00.92 F00.920 F00.921 F00.922 F00.93 F00.930 F00.931 F00.932 

255F00.94 F00.940 F00.941 F00.942 F01 F01.0 F01.00 F01.000 F01.001 F01.002 F01.01 

256F01.010 F01.011 F01.012 F01.02 F01.020 F01.021 F01.022 F01.03 F01.030 F01.031 

257F01.032 F01.04 F01.040 F01.041 F01.042 F01.1 F01.10 F01.100 F01.101 F01.102 

258F01.11 F01.110 F01.111 F01.112 F01.12 F01.120 F01.121 F01.122 F01.13 F01.130 

259F01.131 F01.132 F01.14 F01.140 F01.141 F01.142 F01.2 F01.20 F01.200 F01.201 

260F01.202 F01.21 F01.210 F01.211 F01.212 F01.22 F01.220 F01.221 F01.222 F01.23 

261F01.230 F01.231 F01.232 F01.24 F01.240 F01.241 F01.242 F01.3 F01.30 F01.300 

262F01.301 F01.302 F01.31 F01.310 F01.311 F01.312 F01.32 F01.320 F01.321 F01.322 

263F01.33 F01.330 F01.331 F01.332 F01.34 F01.340 F01.341 F01.342 F01.8 F01.80 

264F01.800 F01.801 F01.802 F01.81 F01.810 F01.811 F01.812 F01.82 F01.820 F01.821 

265F01.822 F01.83 F01.830 F01.831 F01.832 F01.84 F01.840 F01.841 F01.842 F01.9 

266F01.90 F01.900 F01.901 F01.902 F01.91 F01.910 F01.911 F01.912 F01.92 F01.920 

267F01.921 F01.922 F01.93 F01.930 F01.931 F01.932 F01.94 F01.940 F01.941 F01.942 

268F02 F02.0 F02.00 F02.000 F02.001 F02.002 F02.01 F02.010 F02.011 F02.012 F02.02 

269F02.020 F02.021 F02.022 F02.03 F02.030 F02.031 F02.032 F02.04 F02.040 F02.041 

270F02.042 F02.1 F02.10 F02.100 F02.101 F02.102 F02.11 F02.110 F02.111 F02.112 

271F02.12 F02.120 F02.121 F02.122 F02.13 F02.130 F02.131 F02.132 F02.14 F02.140 

272F02.141 F02.142 F02.2 F02.20 F02.200 F02.201 F02.202 F02.21 F02.210 F02.211 

273F02.212 F02.22 F02.220 F02.221 F02.222 F02.23 F02.230 F02.231 F02.232 F02.24 

274F02.240 F02.241 F02.242 F02.3 F02.30 F02.300 F02.301 F02.302 F02.31 F02.310 

275F02.311 F02.312 F02.32 F02.320 F02.321 F02.322 F02.33 F02.330 F02.331 F02.332 

276F02.34 F02.340 F02.341 F02.342 F02.4 F02.40 F02.400 F02.401 F02.402 F02.41 

277F02.410 F02.411 F02.412 F02.42 F02.420 F02.421 F02.422 F02.43 F02.430 F02.431 

278F02.432 F02.44 F02.440 F02.441 F02.442 F02.8 F02.80 F02.800 F02.801 F02.802 

279F02.81 F02.810 F02.811 F02.812 F02.82 F02.820 F02.821 F02.822 F02.83 F02.830 

280F02.831 F02.832 F02.84 F02.840 F02.841 F02.842 F03 F03.0 F03.00 F03.000 F03.001 

281F03.002 F03.01 F03.010 F03.011 F03.012 F03.02 F03.020 F03.021 F03.022 F03.03 

282F03.030 F03.031 F03.032 F03.04 F03.040 F03.041 F03.042 F04 F05 F05.0 F05.1 

283F05.8 F05.9 F06 F06.0 F06.1 F06.2 F06.3 F06.4 F06.5 F06.6 F06.7 F06.8 F06.9 F07 

284F07.0 F07.1 F07.2 F07.8 F07.9 F09 F10 F10.0 F10.00 F10.01 F10.02 F10.03 F10.04 

285F10.05 F10.06 F10.07 F10.1 F10.2 F10.20 F10.200 F10.201 F10.202 F10.21 F10.22 

286F10.23 F10.24 F10.241 F10.242 F10.25 F10.26 F10.3 F10.30 F10.31 F10.4 F10.40 

287F10.41 F10.5 F10.50 F10.51 F10.52 F10.53 F10.54 F10.55 F10.56 F10.6 F10.7 

288F10.70 F10.71 F10.72 F10.73 F10.74 F10.75 F10.8 F10.9 F11 F11.0 F11.00 F11.01 

289F11.02 F11.03 F11.04 F11.05 F11.06 F11.1 F11.2 F11.20 F11.200 F11.201 F11.202 

290F11.21 F11.22 F11.23 F11.24 F11.241 F11.242 F11.25 F11.26 F11.3 F11.30 F11.31 

291F11.4 F11.40 F11.41 F11.5 F11.50 F11.51 F11.52 F11.53 F11.54 F11.55 F11.56 

292F11.6 F11.7 F11.70 F11.71 F11.72 F11.73 F11.74 F11.75 F11.8 F11.9 F12 F12.0 

293F12.00 F12.01 F12.02 F12.03 F12.04 F12.05 F12.06 F12.1 F12.2 F12.20 F12.200 

294F12.201 F12.202 F12.21 F12.22 F12.23 F12.24 F12.241 F12.242 F12.25 F12.26 F12.3 

295F12.30 F12.31 F12.4 F12.40 F12.41 F12.5 F12.50 F12.51 F12.52 F12.53 F12.54 

296F12.55 F12.56 F12.6 F12.7 F12.70 F12.71 F12.72 F12.73 F12.74 F12.75 F12.8 F12.9 

297F13 F13.0 F13.00 F13.01 F13.02 F13.03 F13.04 F13.05 F13.06 F13.1 F13.2 F13.20 

298F13.200 F13.201 F13.202 F13.21 F13.22 F13.23 F13.24 F13.241 F13.242 F13.25 

299F13.26 F13.3 F13.30 F13.31 F13.4 F13.40 F13.41 F13.5 F13.50 F13.51 F13.52 

300F13.53 F13.54 F13.55 F13.56 F13.6 F13.7 F13.70 F13.71 F13.72 F13.73 F13.74 

301F13.75 F13.8 F13.9 F14 F14.0 F14.00 F14.01 F14.02 F14.03 F14.04 F14.05 F14.06 

302F14.1 F14.2 F14.20 F14.200 F14.201 F14.202 F14.21 F14.22 F14.23 F14.24 F14.241 

303F14.242 F14.25 F14.26 F14.3 F14.30 F14.31 F14.4 F14.40 F14.41 F14.5 F14.50 

304F14.51 F14.52 F14.53 F14.54 F14.55 F14.56 F14.6 F14.7 F14.70 F14.71 F14.72 

305F14.73 F14.74 F14.75 F14.8 F14.9 F15 F15.0 F15.00 F15.01 F15.02 F15.03 F15.04 

306F15.05 F15.06 F15.1 F15.2 F15.20 F15.200 F15.201 F15.202 F15.21 F15.22 F15.23 

307F15.24 F15.241 F15.242 F15.25 F15.26 F15.3 F15.30 F15.31 F15.4 F15.40 F15.41 

308F15.5 F15.50 F15.51 F15.52 F15.53 F15.54 F15.55 F15.56 F15.6 F15.7 F15.70 

309F15.71 F15.72 F15.73 F15.74 F15.75 F15.8 F15.9 F16 F16.0 F16.00 F16.01 F16.02 

310F16.03 F16.04 F16.05 F16.06 F16.1 F16.2 F16.20 F16.200 F16.201 F16.202 F16.21 

311F16.22 F16.23 F16.24 F16.241 F16.242 F16.25 F16.26 F16.3 F16.30 F16.31 F16.4 

312F16.40 F16.41 F16.5 F16.50 F16.51 F16.52 F16.53 F16.54 F16.55 F16.56 F16.6 

313F16.7 F16.70 F16.71 F16.72 F16.73 F16.74 F16.75 F16.8 F16.9 F17 F17.0 F17.00 

314F17.01 F17.02 F17.03 F17.04 F17.05 F17.06 F17.1 F17.2 F17.20 F17.200 F17.201 

315F17.202 F17.21 F17.22 F17.23 F17.24 F17.241 F17.242 F17.25 F17.26 F17.3 F17.30 

316F17.31 F17.4 F17.40 F17.41 F17.5 F17.50 F17.51 F17.52 F17.53 F17.54 F17.55 

317F17.56 F17.6 F17.7 F17.70 F17.71 F17.72 F17.73 F17.74 F17.75 F17.8 F17.9 F18 

318F18.0 F18.00 F18.01 F18.02 F18.03 F18.04 F18.05 F18.06 F18.1 F18.2 F18.20 

319F18.200 F18.201 F18.202 F18.21 F18.22 F18.23 F18.24 F18.241 F18.242 F18.25 

320F18.26 F18.3 F18.30 F18.31 F18.4 F18.40 F18.41 F18.5 F18.50 F18.51 F18.52 

321F18.53 F18.54 F18.55 F18.56 F18.6 F18.7 F18.70 F18.71 F18.72 F18.73 F18.74 

322F18.75 F18.8 F18.9 F19 F19.0 F19.00 F19.01 F19.02 F19.03 F19.04 F19.05 F19.06 

323F19.1 F19.2 F19.20 F19.200 F19.201 F19.202 F19.21 F19.22 F19.23 F19.24 F19.241 

324F19.242 F19.25 F19.26 F19.3 F19.30 F19.31 F19.4 F19.40 F19.41 F19.5 F19.50 

325F19.51 F19.52 F19.53 F19.54 F19.55 F19.56 F19.6 F19.7 F19.70 F19.71 F19.72 

326F19.73 F19.74 F19.75 F19.8 F19.9 F20 F20.0 F20.1 F20.2 F20.3 F20.4 F20.5 F20.6 

327F20.8 F20.9 F21 F22 F22.0 F22.8 F22.9 F23 F23.0 F23.1 F23.2 F23.3 F23.8 F23.9 

328F23.90 F23.91 F24 F25 F25.0 F25.00 F25.01 F25.1 F25.10 F25.11 F25.2 F25.20 

329F25.21 F25.8 F25.80 F25.81 F25.9 F25.90 F25.91 F28 F29 F30 F30.0 F30.1 F30.2 

330F30.20 F30.21 F30.8 F30.9 F31 F31.0 F31.1 F31.2 F31.20 F31.21 F31.3 F31.30 

331F31.31 F31.4 F31.5 F31.50 F31.51 F31.6 F31.7 F31.8 F31.9 F32 F32.0 F32.00 

332F32.01 F32.1 F32.10 F32.11 F32.2 F32.3 F32.30 F32.31 F32.8 F32.9 F33 F33.0 

333F33.00 F33.01 F33.1 F33.10 F33.11 F33.2 F33.3 F33.30 F33.31 F33.4 F33.8 F33.9 

334F34 F34.0 F34.1 F34.8 F34.9 F38 F38.0 F38.00 F38.1 F38.10 F38.8 F39 F40 F40.0 

335F40.00 F40.01 F40.1 F40.2 F40.8 F40.9 F41 F41.0 F41.00 F41.01 F41.1 F41.2 F41.3 

336F41.8 F41.9 F42 F42.0 F42.1 F42.2 F42.8 F42.9 F43 F43.0 F43.00 F43.01 F43.02 

337F43.1 F43.2 F43.20 F43.21 F43.22 F43.23 F43.24 F43.25 F43.28 F43.8 F43.9 F44 

338F44.0 F44.1 F44.2 F44.3 F44.4 F44.5 F44.6 F44.7 F44.8 F44.80 F44.81 F44.82 

339F44.88 F44.9 F45 F45.0 F45.1 F45.2 F45.3 F45.30 F45.31 F45.32 F45.33 F45.34 

340F45.38 F45.4 F45.8 F45.9 F48 F48.0 F48.1 F48.8 F48.9 F50 F50.0 F50.1 F50.2 

341F50.3 F50.4 F50.5 F50.8 F50.9 F51 F51.0 F51.1 F51.2 F51.3 F51.4 F51.5 F51.8 

342F51.9 F52 F52.0 F52.1 F52.10 F52.11 F52.2 F52.3 F52.4 F52.5 F52.6 F52.7 F52.8 

343F52.9 F53 F53.0 F53.1 F53.8 F53.9 F54 F55 F59 F60 F60.0 F60.1 F60.2 F60.3 

344F60.30 F60.31 F60.4 F60.5 F60.6 F60.7 F60.8 F60.9 F61 F62 F62.0 F62.1 F62.8 

345F62.9 F63 F63.0 F63.1 F63.2 F63.3 F63.8 F63.9 F64 F64.0 F64.1 F64.2 F64.8 F64.9 

346F65 F65.0 F65.1 F65.2 F65.3 F65.4 F65.5 F65.6 F65.8 F65.9 F66 F66.0 F66.1 F66.2 

347F66.8 F66.9 F66.90 F66.91 F66.92 F66.98 F68 F68.0 F68.1 F68.8 F69 F70 F70.0 

348F70.1 F70.8 F70.9 F71 F71.0 F71.1 F71.8 F71.9 F72 F72.0 F72.1 F72.8 F72.9 F73 

349F73.0 F73.1 F73.8 F73.9 F78 F78.0 F78.1 F78.8 F78.9 F79 F79.0 F79.1 F79.8 F79.9 

350F80 F80.0 F80.1 F80.2 F80.3 F80.8 F80.9 F81 F81.0 F81.1 F81.2 F81.3 F81.8 F81.9 

351F82 F83 F84 F84.0 F84.1 F84.10 F84.11 F84.12 F84.2 F84.3 F84.4 F84.5 F84.8 

352F84.9 F88 F89 F90 F90.0 F90.1 F90.8 F90.9 F91 F91.0 F91.1 F91.2 F91.3 F91.8 

353F91.9 F92 F92.0 F92.8 F92.9 F93 F93.0 F93.1 F93.2 F93.3 F93.8 F93.80 F93.9 F94 

354F94.0 F94.1 F94.2 F94.8 F94.9 F95 F95.0 F95.1 F95.2 F95.8 F95.9 F98 F98.0 

355F98.00 F98.01 F98.02 F98.1 F98.10 F98.11 F98.12 F98.2 F98.3 F98.4 F98.40 F98.41 

356F98.42 F98.5 F98.6 F98.8 F98.9 F99 R40 R40.0 R40.1 R40.2 R41 R41.0 R41.1 R41.2 

357R41.3 R41.8 R42 R43 R43.0 R43.1 R43.2 R43.8 R44 R44.0 R44.1 R44.2 R44.3 R44.8 

358R45 R45.0 R45.1 R45.2 R45.3 R45.4 R45.5 R45.6 R45.7 R45.8 R46 R46.0 R46.1 R46.2 

359R46.3 R46.4 R46.5 R46.6 R46.7 R46.8 X60 X60.0 X60.00 X60.01 X60.02 X60.03 

360X60.04 X60.08 X60.09 X60.1 X60.10 X60.11 X60.12 X60.13 X60.14 X60.18 X60.19 

361X60.2 X60.20 X60.21 X60.22 X60.23 X60.24 X60.28 X60.29 X60.3 X60.30 X60.31 

362X60.32 X60.33 X60.34 X60.38 X60.39 X60.4 X60.40 X60.41 X60.42 X60.43 X60.44 

363X60.48 X60.49 X60.5 X60.50 X60.51 X60.52 X60.53 X60.54 X60.58 X60.59 X60.6 

364X60.60 X60.61 X60.62 X60.63 X60.64 X60.68 X60.69 X60.7 X60.70 X60.71 X60.72 

365X60.73 X60.74 X60.78 X60.79 X60.8 X60.80 X60.81 X60.82 X60.83 X60.84 X60.88 

366X60.89 X60.9 X60.90 X60.91 X60.92 X60.93 X60.94 X60.98 X60.99 X61 X61.0 X61.00 

367X61.01 X61.02 X61.03 X61.04 X61.08 X61.09 X61.1 X61.10 X61.11 X61.12 X61.13 

368X61.14 X61.18 X61.19 X61.2 X61.20 X61.21 X61.22 X61.23 X61.24 X61.28 X61.29 

369X61.3 X61.30 X61.31 X61.32 X61.33 X61.34 X61.38 X61.39 X61.4 X61.40 X61.41 

370X61.42 X61.43 X61.44 X61.48 X61.49 X61.5 X61.50 X61.51 X61.52 X61.53 X61.54 

371X61.58 X61.59 X61.6 X61.60 X61.61 X61.62 X61.63 X61.64 X61.68 X61.69 X61.7 

372X61.70 X61.71 X61.72 X61.73 X61.74 X61.78 X61.79 X61.8 X61.80 X61.81 X61.82 

373X61.83 X61.84 X61.88 X61.89 X61.9 X61.90 X61.91 X61.92 X61.93 X61.94 X61.98 

374X61.99 X62 X62.0 X62.00 X62.01 X62.02 X62.03 X62.04 X62.08 X62.09 X62.1 X62.10 

375X62.11 X62.12 X62.13 X62.14 X62.18 X62.19 X62.2 X62.20 X62.21 X62.22 X62.23 

376X62.24 X62.28 X62.29 X62.3 X62.30 X62.31 X62.32 X62.33 X62.34 X62.38 X62.39 

377X62.4 X62.40 X62.41 X62.42 X62.43 X62.44 X62.48 X62.49 X62.5 X62.50 X62.51 

378X62.52 X62.53 X62.54 X62.58 X62.59 X62.6 X62.60 X62.61 X62.62 X62.63 X62.64 

379X62.68 X62.69 X62.7 X62.70 X62.71 X62.72 X62.73 X62.74 X62.78 X62.79 X62.8 

380X62.80 X62.81 X62.82 X62.83 X62.84 X62.88 X62.89 X62.9 X62.90 X62.91 X62.92 

381X62.93 X62.94 X62.98 X62.99 X63 X63.0 X63.00 X63.01 X63.02 X63.03 X63.04 X63.08 

382X63.09 X63.1 X63.10 X63.11 X63.12 X63.13 X63.14 X63.18 X63.19 X63.2 X63.20 

383X63.21 X63.22 X63.23 X63.24 X63.28 X63.29 X63.3 X63.30 X63.31 X63.32 X63.33 

384X63.34 X63.38 X63.39 X63.4 X63.40 X63.41 X63.42 X63.43 X63.44 X63.48 X63.49 

385X63.5 X63.50 X63.51 X63.52 X63.53 X63.54 X63.58 X63.59 X63.6 X63.60 X63.61 

386X63.62 X63.63 X63.64 X63.68 X63.69 X63.7 X63.70 X63.71 X63.72 X63.73 X63.74 

387X63.78 X63.79 X63.8 X63.80 X63.81 X63.82 X63.83 X63.84 X63.88 X63.89 X63.9 

388X63.90 X63.91 X63.92 X63.93 X63.94 X63.98 X63.99 X64 X64.0 X64.00 X64.01 X64.02 

389X64.03 X64.04 X64.08 X64.09 X64.1 X64.10 X64.11 X64.12 X64.13 X64.14 X64.18 

390X64.19 X64.2 X64.20 X64.21 X64.22 X64.23 X64.24 X64.28 X64.29 X64.3 X64.30 

391X64.31 X64.32 X64.33 X64.34 X64.38 X64.39 X64.4 X64.40 X64.41 X64.42 X64.43 

392X64.44 X64.48 X64.49 X64.5 X64.50 X64.51 X64.52 X64.53 X64.54 X64.58 X64.59 

393X64.6 X64.60 X64.61 X64.62 X64.63 X64.64 X64.68 X64.69 X64.7 X64.70 X64.71 

394X64.72 X64.73 X64.74 X64.78 X64.79 X64.8 X64.80 X64.81 X64.82 X64.83 X64.84 

395X64.88 X64.89 X64.9 X64.90 X64.91 X64.92 X64.93 X64.94 X64.98 X64.99 X65 X65.0 

396X65.00 X65.01 X65.02 X65.03 X65.04 X65.08 X65.09 X65.1 X65.10 X65.11 X65.12 

397X65.13 X65.14 X65.18 X65.19 X65.2 X65.20 X65.21 X65.22 X65.23 X65.24 X65.28 

398X65.29 X65.3 X65.30 X65.31 X65.32 X65.33 X65.34 X65.38 X65.39 X65.4 X65.40 

399X65.41 X65.42 X65.43 X65.44 X65.48 X65.49 X65.5 X65.50 X65.51 X65.52 X65.53 

400X65.54 X65.58 X65.59 X65.6 X65.60 X65.61 X65.62 X65.63 X65.64 X65.68 X65.69 

401X65.7 X65.70 X65.71 X65.72 X65.73 X65.74 X65.78 X65.79 X65.8 X65.80 X65.81 

402X65.82 X65.83 X65.84 X65.88 X65.89 X65.9 X65.90 X65.91 X65.92 X65.93 X65.94 

403X65.98 X65.99 X66 X66.0 X66.00 X66.01 X66.02 X66.03 X66.04 X66.08 X66.09 X66.1 

404X66.10 X66.11 X66.12 X66.13 X66.14 X66.18 X66.19 X66.2 X66.20 X66.21 X66.22 

405X66.23 X66.24 X66.28 X66.29 X66.3 X66.30 X66.31 X66.32 X66.33 X66.34 X66.38 

406X66.39 X66.4 X66.40 X66.41 X66.42 X66.43 X66.44 X66.48 X66.49 X66.5 X66.50 

407X66.51 X66.52 X66.53 X66.54 X66.58 X66.59 X66.6 X66.60 X66.61 X66.62 X66.63 

408X66.64 X66.68 X66.69 X66.7 X66.70 X66.71 X66.72 X66.73 X66.74 X66.78 X66.79 

409X66.8 X66.80 X66.81 X66.82 X66.83 X66.84 X66.88 X66.89 X66.9 X66.90 X66.91 

410X66.92 X66.93 X66.94 X66.98 X66.99 X67 X67.0 X67.00 X67.01 X67.02 X67.03 X67.04 

411X67.08 X67.09 X67.1 X67.10 X67.11 X67.12 X67.13 X67.14 X67.18 X67.19 X67.2 

412X67.20 X67.21 X67.22 X67.23 X67.24 X67.28 X67.29 X67.3 X67.30 X67.31 X67.32 

413X67.33 X67.34 X67.38 X67.39 X67.4 X67.40 X67.41 X67.42 X67.43 X67.44 X67.48 

414X67.49 X67.5 X67.50 X67.51 X67.52 X67.53 X67.54 X67.58 X67.59 X67.6 X67.60 

415X67.61 X67.62 X67.63 X67.64 X67.68 X67.69 X67.7 X67.70 X67.71 X67.72 X67.73 

416X67.74 X67.78 X67.79 X67.8 X67.80 X67.81 X67.82 X67.83 X67.84 X67.88 X67.89 

417X67.9 X67.90 X67.91 X67.92 X67.93 X67.94 X67.98 X67.99 X68 X68.0 X68.00 X68.01 

418X68.02 X68.03 X68.04 X68.08 X68.09 X68.1 X68.10 X68.11 X68.12 X68.13 X68.14 

419X68.18 X68.19 X68.2 X68.20 X68.21 X68.22 X68.23 X68.24 X68.28 X68.29 X68.3 

420X68.30 X68.31 X68.32 X68.33 X68.34 X68.38 X68.39 X68.4 X68.40 X68.41 X68.42 

421X68.43 X68.44 X68.48 X68.49 X68.5 X68.50 X68.51 X68.52 X68.53 X68.54 X68.58 

422X68.59 X68.6 X68.60 X68.61 X68.62 X68.63 X68.64 X68.68 X68.69 X68.7 X68.70 

423X68.71 X68.72 X68.73 X68.74 X68.78 X68.79 X68.8 X68.80 X68.81 X68.82 X68.83 

424X68.84 X68.88 X68.89 X68.9 X68.90 X68.91 X68.92 X68.93 X68.94 X68.98 X68.99 X69 

425X69.0 X69.00 X69.01 X69.02 X69.03 X69.04 X69.08 X69.09 X69.1 X69.10 X69.11 

426X69.12 X69.13 X69.14 X69.18 X69.19 X69.2 X69.20 X69.21 X69.22 X69.23 X69.24 

427X69.28 X69.29 X69.3 X69.30 X69.31 X69.32 X69.33 X69.34 X69.38 X69.39 X69.4 

428X69.40 X69.41 X69.42 X69.43 X69.44 X69.48 X69.49 X69.5 X69.50 X69.51 X69.52 

429X69.53 X69.54 X69.58 X69.59 X69.6 X69.60 X69.61 X69.62 X69.63 X69.64 X69.68 

430X69.69 X69.7 X69.70 X69.71 X69.72 X69.73 X69.74 X69.78 X69.79 X69.8 X69.80 

431X69.81 X69.82 X69.83 X69.84 X69.88 X69.89 X69.9 X69.90 X69.91 X69.92 X69.93 

432X69.94 X69.98 X69.99 X70 X70.0 X70.00 X70.01 X70.02 X70.03 X70.04 X70.08 X70.09 

433X70.1 X70.10 X70.11 X70.12 X70.13 X70.14 X70.18 X70.19 X70.2 X70.20 X70.21 

434X70.22 X70.23 X70.24 X70.28 X70.29 X70.3 X70.30 X70.31 X70.32 X70.33 X70.34 

435X70.38 X70.39 X70.4 X70.40 X70.41 X70.42 X70.43 X70.44 X70.48 X70.49 X70.5 

436X70.50 X70.51 X70.52 X70.53 X70.54 X70.58 X70.59 X70.6 X70.60 X70.61 X70.62 

437X70.63 X70.64 X70.68 X70.69 X70.7 X70.70 X70.71 X70.72 X70.73 X70.74 X70.78 

438X70.79 X70.8 X70.80 X70.81 X70.82 X70.83 X70.84 X70.88 X70.89 X70.9 X70.90 

439X70.91 X70.92 X70.93 X70.94 X70.98 X70.99 X71 X71.0 X71.00 X71.01 X71.02 X71.03 

440X71.04 X71.08 X71.09 X71.1 X71.10 X71.11 X71.12 X71.13 X71.14 X71.18 X71.19 

441X71.2 X71.20 X71.21 X71.22 X71.23 X71.24 X71.28 X71.29 X71.3 X71.30 X71.31 

442X71.32 X71.33 X71.34 X71.38 X71.39 X71.4 X71.40 X71.41 X71.42 X71.43 X71.44 

443X71.48 X71.49 X71.5 X71.50 X71.51 X71.52 X71.53 X71.54 X71.58 X71.59 X71.6 

444X71.60 X71.61 X71.62 X71.63 X71.64 X71.68 X71.69 X71.7 X71.70 X71.71 X71.72 

445X71.73 X71.74 X71.78 X71.79 X71.8 X71.80 X71.81 X71.82 X71.83 X71.84 X71.88 

446X71.89 X71.9 X71.90 X71.91 X71.92 X71.93 X71.94 X71.98 X71.99 X72 X72.0 X72.00 

447X72.01 X72.02 X72.03 X72.04 X72.08 X72.09 X72.1 X72.10 X72.11 X72.12 X72.13 

448X72.14 X72.18 X72.19 X72.2 X72.20 X72.21 X72.22 X72.23 X72.24 X72.28 X72.29 

449X72.3 X72.30 X72.31 X72.32 X72.33 X72.34 X72.38 X72.39 X72.4 X72.40 X72.41 

450X72.42 X72.43 X72.44 X72.48 X72.49 X72.5 X72.50 X72.51 X72.52 X72.53 X72.54 

451X72.58 X72.59 X72.6 X72.60 X72.61 X72.62 X72.63 X72.64 X72.68 X72.69 X72.7 

452X72.70 X72.71 X72.72 X72.73 X72.74 X72.78 X72.79 X72.8 X72.80 X72.81 X72.82 

453X72.83 X72.84 X72.88 X72.89 X72.9 X72.90 X72.91 X72.92 X72.93 X72.94 X72.98 

454X72.99 X73 X73.0 X73.00 X73.01 X73.02 X73.03 X73.04 X73.08 X73.09 X73.1 X73.10 

455X73.11 X73.12 X73.13 X73.14 X73.18 X73.19 X73.2 X73.20 X73.21 X73.22 X73.23 

456X73.24 X73.28 X73.29 X73.3 X73.30 X73.31 X73.32 X73.33 X73.34 X73.38 X73.39 

457X73.4 X73.40 X73.41 X73.42 X73.43 X73.44 X73.48 X73.49 X73.5 X73.50 X73.51 

458X73.52 X73.53 X73.54 X73.58 X73.59 X73.6 X73.60 X73.61 X73.62 X73.63 X73.64 

459X73.68 X73.69 X73.7 X73.70 X73.71 X73.72 X73.73 X73.74 X73.78 X73.79 X73.8 

460X73.80 X73.81 X73.82 X73.83 X73.84 X73.88 X73.89 X73.9 X73.90 X73.91 X73.92 

461X73.93 X73.94 X73.98 X73.99 X74 X74.0 X74.00 X74.01 X74.02 X74.03 X74.04 X74.08 

462X74.09 X74.1 X74.10 X74.11 X74.12 X74.13 X74.14 X74.18 X74.19 X74.2 X74.20 

463X74.21 X74.22 X74.23 X74.24 X74.28 X74.29 X74.3 X74.30 X74.31 X74.32 X74.33 

464X74.34 X74.38 X74.39 X74.4 X74.40 X74.41 X74.42 X74.43 X74.44 X74.48 X74.49 

465X74.5 X74.50 X74.51 X74.52 X74.53 X74.54 X74.58 X74.59 X74.6 X74.60 X74.61 

466X74.62 X74.63 X74.64 X74.68 X74.69 X74.7 X74.70 X74.71 X74.72 X74.73 X74.74 

467X74.78 X74.79 X74.8 X74.80 X74.81 X74.82 X74.83 X74.84 X74.88 X74.89 X74.9 

468X74.90 X74.91 X74.92 X74.93 X74.94 X74.98 X74.99 X75 X75.0 X75.00 X75.01 X75.02 

469X75.03 X75.04 X75.08 X75.09 X75.1 X75.10 X75.11 X75.12 X75.13 X75.14 X75.18 

470X75.19 X75.2 X75.20 X75.21 X75.22 X75.23 X75.24 X75.28 X75.29 X75.3 X75.30 

471X75.31 X75.32 X75.33 X75.34 X75.38 X75.39 X75.4 X75.40 X75.41 X75.42 X75.43 

472X75.44 X75.48 X75.49 X75.5 X75.50 X75.51 X75.52 X75.53 X75.54 X75.58 X75.59 

473X75.6 X75.60 X75.61 X75.62 X75.63 X75.64 X75.68 X75.69 X75.7 X75.70 X75.71 

474X75.72 X75.73 X75.74 X75.78 X75.79 X75.8 X75.80 X75.81 X75.82 X75.83 X75.84 

475X75.88 X75.89 X75.9 X75.90 X75.91 X75.92 X75.93 X75.94 X75.98 X75.99 X76 X76.0 

476X76.00 X76.01 X76.02 X76.03 X76.04 X76.08 X76.09 X76.1 X76.10 X76.11 X76.12 

477X76.13 X76.14 X76.18 X76.19 X76.2 X76.20 X76.21 X76.22 X76.23 X76.24 X76.28 

478X76.29 X76.3 X76.30 X76.31 X76.32 X76.33 X76.34 X76.38 X76.39 X76.4 X76.40 

479X76.41 X76.42 X76.43 X76.44 X76.48 X76.49 X76.5 X76.50 X76.51 X76.52 X76.53 

480X76.54 X76.58 X76.59 X76.6 X76.60 X76.61 X76.62 X76.63 X76.64 X76.68 X76.69 

481X76.7 X76.70 X76.71 X76.72 X76.73 X76.74 X76.78 X76.79 X76.8 X76.80 X76.81 

482X76.82 X76.83 X76.84 X76.88 X76.89 X76.9 X76.90 X76.91 X76.92 X76.93 X76.94 

483X76.98 X76.99 X77 X77.0 X77.00 X77.01 X77.02 X77.03 X77.04 X77.08 X77.09 X77.1 

484X77.10 X77.11 X77.12 X77.13 X77.14 X77.18 X77.19 X77.2 X77.20 X77.21 X77.22 

485X77.23 X77.24 X77.28 X77.29 X77.3 X77.30 X77.31 X77.32 X77.33 X77.34 X77.38 

486X77.39 X77.4 X77.40 X77.41 X77.42 X77.43 X77.44 X77.48 X77.49 X77.5 X77.50 

487X77.51 X77.52 X77.53 X77.54 X77.58 X77.59 X77.6 X77.60 X77.61 X77.62 X77.63 

488X77.64 X77.68 X77.69 X77.7 X77.70 X77.71 X77.72 X77.73 X77.74 X77.78 X77.79 

489X77.8 X77.80 X77.81 X77.82 X77.83 X77.84 X77.88 X77.89 X77.9 X77.90 X77.91 

490X77.92 X77.93 X77.94 X77.98 X77.99 X78 X78.0 X78.00 X78.01 X78.02 X78.03 X78.04 

491X78.08 X78.09 X78.1 X78.10 X78.11 X78.12 X78.13 X78.14 X78.18 X78.19 X78.2 

492X78.20 X78.21 X78.22 X78.23 X78.24 X78.28 X78.29 X78.3 X78.30 X78.31 X78.32 

493X78.33 X78.34 X78.38 X78.39 X78.4 X78.40 X78.41 X78.42 X78.43 X78.44 X78.48 

494X78.49 X78.5 X78.50 X78.51 X78.52 X78.53 X78.54 X78.58 X78.59 X78.6 X78.60 

495X78.61 X78.62 X78.63 X78.64 X78.68 X78.69 X78.7 X78.70 X78.71 X78.72 X78.73 

496X78.74 X78.78 X78.79 X78.8 X78.80 X78.81 X78.82 X78.83 X78.84 X78.88 X78.89 

497X78.9 X78.90 X78.91 X78.92 X78.93 X78.94 X78.98 X78.99 X79 X79.0 X79.00 X79.01 

498X79.02 X79.03 X79.04 X79.08 X79.09 X79.1 X79.10 X79.11 X79.12 X79.13 X79.14 

499X79.18 X79.19 X79.2 X79.20 X79.21 X79.22 X79.23 X79.24 X79.28 X79.29 X79.3 

500X79.30 X79.31 X79.32 X79.33 X79.34 X79.38 X79.39 X79.4 X79.40 X79.41 X79.42 

501X79.43 X79.44 X79.48 X79.49 X79.5 X79.50 X79.51 X79.52 X79.53 X79.54 X79.58 

502X79.59 X79.6 X79.60 X79.61 X79.62 X79.63 X79.64 X79.68 X79.69 X79.7 X79.70 

503X79.71 X79.72 X79.73 X79.74 X79.78 X79.79 X79.8 X79.80 X79.81 X79.82 X79.83 

504X79.84 X79.88 X79.89 X79.9 X79.90 X79.91 X79.92 X79.93 X79.94 X79.98 X79.99 X80 

505X80.0 X80.00 X80.01 X80.02 X80.03 X80.04 X80.08 X80.09 X80.1 X80.10 X80.11 

506X80.12 X80.13 X80.14 X80.18 X80.19 X80.2 X80.20 X80.21 X80.22 X80.23 X80.24 

507X80.28 X80.29 X80.3 X80.30 X80.31 X80.32 X80.33 X80.34 X80.38 X80.39 X80.4 

508X80.40 X80.41 X80.42 X80.43 X80.44 X80.48 X80.49 X80.5 X80.50 X80.51 X80.52 

509X80.53 X80.54 X80.58 X80.59 X80.6 X80.60 X80.61 X80.62 X80.63 X80.64 X80.68 

510X80.69 X80.7 X80.70 X80.71 X80.72 X80.73 X80.74 X80.78 X80.79 X80.8 X80.80 

511X80.81 X80.82 X80.83 X80.84 X80.88 X80.89 X80.9 X80.90 X80.91 X80.92 X80.93 

512X80.94 X80.98 X80.99 X81 X81.0 X81.00 X81.01 X81.02 X81.03 X81.04 X81.08 X81.09 

513X81.1 X81.10 X81.11 X81.12 X81.13 X81.14 X81.18 X81.19 X81.2 X81.20 X81.21 

514X81.22 X81.23 X81.24 X81.28 X81.29 X81.3 X81.30 X81.31 X81.32 X81.33 X81.34 

515X81.38 X81.39 X81.4 X81.40 X81.41 X81.42 X81.43 X81.44 X81.48 X81.49 X81.5 

516X81.50 X81.51 X81.52 X81.53 X81.54 X81.58 X81.59 X81.6 X81.60 X81.61 X81.62 

517X81.63 X81.64 X81.68 X81.69 X81.7 X81.70 X81.71 X81.72 X81.73 X81.74 X81.78 

518X81.79 X81.8 X81.80 X81.81 X81.82 X81.83 X81.84 X81.88 X81.89 X81.9 X81.90 

519X81.91 X81.92 X81.93 X81.94 X81.98 X81.99 X82 X82.0 X82.00 X82.01 X82.02 X82.03 

520X82.04 X82.08 X82.09 X82.1 X82.10 X82.11 X82.12 X82.13 X82.14 X82.18 X82.19 

521X82.2 X82.20 X82.21 X82.22 X82.23 X82.24 X82.28 X82.29 X82.3 X82.30 X82.31 

522X82.32 X82.33 X82.34 X82.38 X82.39 X82.4 X82.40 X82.41 X82.42 X82.43 X82.44 

523X82.48 X82.49 X82.5 X82.50 X82.51 X82.52 X82.53 X82.54 X82.58 X82.59 X82.6 

524X82.60 X82.61 X82.62 X82.63 X82.64 X82.68 X82.69 X82.7 X82.70 X82.71 X82.72 

525X82.73 X82.74 X82.78 X82.79 X82.8 X82.80 X82.81 X82.82 X82.83 X82.84 X82.88 

526X82.89 X82.9 X82.90 X82.91 X82.92 X82.93 X82.94 X82.98 X82.99 X83 X83.0 X83.00 

527X83.01 X83.02 X83.03 X83.04 X83.08 X83.09 X83.1 X83.10 X83.11 X83.12 X83.13 

528X83.14 X83.18 X83.19 X83.2 X83.20 X83.21 X83.22 X83.23 X83.24 X83.28 X83.29 

529X83.3 X83.30 X83.31 X83.32 X83.33 X83.34 X83.38 X83.39 X83.4 X83.40 X83.41 

530X83.42 X83.43 X83.44 X83.48 X83.49 X83.5 X83.50 X83.51 X83.52 X83.53 X83.54 

531X83.58 X83.59 X83.6 X83.60 X83.61 X83.62 X83.63 X83.64 X83.68 X83.69 X83.7 

532X83.70 X83.71 X83.72 X83.73 X83.74 X83.78 X83.79 X83.8 X83.80 X83.81 X83.82 

533X83.83 X83.84 X83.88 X83.89 X83.9 X83.90 X83.91 X83.92 X83.93 X83.94 X83.98 

534X83.99 X84 X84.0 X84.00 X84.01 X84.02 X84.03 X84.04 X84.08 X84.09 X84.1 X84.10 

535X84.11 X84.12 X84.13 X84.14 X84.18 X84.19 X84.2 X84.20 X84.21 X84.22 X84.23 

536X84.24 X84.28 X84.29 X84.3 X84.30 X84.31 X84.32 X84.33 X84.34 X84.38 X84.39 

537X84.4 X84.40 X84.41 X84.42 X84.43 X84.44 X84.48 X84.49 X84.5 X84.50 X84.51 

538X84.52 X84.53 X84.54 X84.58 X84.59 X84.6 X84.60 X84.61 X84.62 X84.63 X84.64 

539X84.68 X84.69 X84.7 X84.70 X84.71 X84.72 X84.73 X84.74 X84.78 X84.79 X84.8 

540X84.80 X84.81 X84.82 X84.83 X84.84 X84.88 X84.89 X84.9 X84.90 X84.91 X84.92 

541X84.93 X84.94 X84.98 X84.99 Z00.4 Z03.2 Z71.1 

542""".split()) 

543 

544 

545# ============================================================================= 

546# The SnomedConcept class, amended 

547# ============================================================================= 

548 

549class SnomedConcept(SnomedConceptCardinalPythonlib): 

550 @classmethod 

551 def create(cls, concept: SnomedConceptCardinalPythonlib) \ 

552 -> "SnomedConcept": 

553 """ 

554 Creates a CamCOPS-friendly 

555 :class:`camcops_server.cc_modules.cc_snomed.SnomedConcept` from a 

556 :class:`cardinal_pythonlib.snomed.SnomedConcept`. 

557 

558 The result has extra methods. 

559 """ 

560 return SnomedConcept(identifier=concept.identifier, 

561 term=concept.term) 

562 

563 def xml_element(self, longform: bool = True) -> XmlElement: 

564 """ 

565 Returns a :class:`camcops_server.cc_modules.cc_xml.XmlElement` for this 

566 SNOMED-CT object. 

567 

568 Args: 

569 longform: print SNOMED-CT concepts in long form? 

570 """ 

571 return XmlElement( 

572 name=SNOMED_XML_NAME, 

573 value=self.as_string(longform), 

574 datatype=XmlDataTypes.STRING 

575 ) 

576 

577 

578# ============================================================================= 

579# The SnomedExpression class, amended 

580# ============================================================================= 

581 

582class SnomedExpression(SnomedExpressionCardinalPythonlib): 

583 def xml_element(self, longform: bool = True) -> XmlElement: 

584 """ 

585 Returns a :class:`camcops_server.cc_modules.cc_xml.XmlElement` for this 

586 SNOMED-CT object. 

587 

588 Args: 

589 longform: print SNOMED-CT expressions in long form? 

590 """ 

591 return XmlElement( 

592 name=SNOMED_XML_NAME, 

593 value=self.as_string(longform), 

594 datatype=XmlDataTypes.STRING 

595 ) 

596 

597 

598# ============================================================================= 

599# The CamCOPS XML file format for SNOMED-CT 

600# ============================================================================= 

601 

602ROOT_TAG = "snomed_concepts" 

603CONCEPT_TAG = "concept" 

604LOOKUP_TAG = "lookup" 

605LOOKUP_NAME_ATTR = "name" 

606LOOKUP_ATTR = "lookup" 

607ID_TAG = "id" 

608TERM_TAG = "term" 

609AUTOGEN_COMMENT = ( 

610 "Autogenerated XML (see camcops_server.cc_modules.cc_snomed.py); do not " 

611 "edit" 

612) 

613 

614 

615def get_snomed_concepts_from_xml(xml_filename: str) \ 

616 -> Dict[str, Union[SnomedConcept, List[SnomedConcept]]]: 

617 """ 

618 Reads in all SNOMED-CT concepts from an XML file according to the CamCOPS 

619 format. 

620 

621 Args: 

622 xml_filename: XML filename to read 

623 

624 Returns: 

625 dict: mapping each lookup code found to a list of 

626 :class:`SnomedConcept` objects 

627 

628 """ 

629 log.info("Reading SNOMED-CT XML file: {}", xml_filename) 

630 parser = ElementTree.XMLParser(encoding="UTF-8") 

631 tree = ElementTree.parse(xml_filename, parser=parser) 

632 root = tree.getroot() 

633 all_concepts = {} # type: Dict[str, List[SnomedConcept]] 

634 find_lookup = "./{tag}[@{attr}]".format(tag=LOOKUP_TAG, attr=LOOKUP_NAME_ATTR) # noqa 

635 find_concept = "./{tag}".format(tag=CONCEPT_TAG) 

636 find_id_in_concept = "./{tag}".format(tag=ID_TAG) 

637 find_name_in_concept = "./{tag}".format(tag=TERM_TAG) 

638 for lookuproot in root.findall(find_lookup): 

639 # Extract info from the XML 

640 lookupname = lookuproot.attrib.get(LOOKUP_NAME_ATTR) 

641 for conceptroot in lookuproot.findall(find_concept): 

642 id_node = conceptroot.find(find_id_in_concept) 

643 identifier = int(id_node.text) 

644 name_node = conceptroot.find(find_name_in_concept) 

645 name = name_node.text or "" 

646 # Stash it 

647 concept = SnomedConcept(identifier, name) 

648 concepts_for_lookup = all_concepts.setdefault(lookupname, []) 

649 concepts_for_lookup.append(concept) 

650 # Done 

651 return all_concepts 

652 

653 

654def write_snomed_concepts_to_xml( 

655 xml_filename: str, 

656 concepts: Dict[str, List[SnomedConcept]], 

657 comment: str = AUTOGEN_COMMENT) -> None: 

658 """ 

659 Writes SNOMED-CT concepts to an XML file in the CamCOPS format. 

660 

661 Args: 

662 xml_filename: XML filename to write 

663 concepts: dictionary mapping lookup codes to a list of 

664 :class:`SnomedConcept` objects 

665 comment: comment for XML file 

666 """ 

667 # https://stackoverflow.com/questions/3605680/creating-a-simple-xml-file-using-python 

668 root = ElementTree.Element(ROOT_TAG) 

669 comment_element = ElementTree.Comment(comment) 

670 root.insert(0, comment_element) 

671 for lookup, concepts_for_lookup in concepts.items(): 

672 l_el = ElementTree.SubElement( 

673 root, LOOKUP_TAG, {LOOKUP_NAME_ATTR: lookup}) 

674 for concept in concepts_for_lookup: 

675 c_el = ElementTree.SubElement(l_el, CONCEPT_TAG) 

676 i_el = ElementTree.SubElement(c_el, ID_TAG) 

677 i_el.text = str(concept.identifier) 

678 n_el = ElementTree.SubElement(c_el, TERM_TAG) 

679 n_el.text = concept.term 

680 tree = ElementTree.ElementTree(root) 

681 log.info("Writing to {!r}", xml_filename) 

682 tree.write(xml_filename) 

683 

684 

685# ============================================================================= 

686# SNOMED-CT concepts for CamCOPS tasks 

687# ============================================================================= 

688 

689# ----------------------------------------------------------------------------- 

690# CamCOPS lookup codes for SNOMED-CT concepts 

691# ----------------------------------------------------------------------------- 

692 

693class SnomedLookup(object): 

694 """ 

695 We're not allowed to embed SNOMED-CT codes in the CamCOPS code. Therefore, 

696 within CamCOPS, we use string constants represented in this class. If the 

697 local institution is allowed (e.g. in the UK, as below), then it can 

698 install additional data. 

699 

700 - UK license: 

701 https://isd.digital.nhs.uk/trud3/user/guest/group/0/pack/26/subpack/101/licences 

702 

703 - To find codes: https://termbrowser.nhs.uk/ 

704 

705 Abbreviations: 

706 

707 - "Finding" is not abbreviated 

708 - "Obs" or "observable" is short for "observable entity" 

709 - "Procedure" is not abbreviated 

710 - "Scale" is short for "assessment scale" 

711 - "Situation" is not abbreviated 

712 

713 Variable names are designed for clear code. Value strings are designed for 

714 clear XML that matches SNOMED-CT, in the format TASK_CONCEPTTYPE_NAME. 

715 

716 """ # noqa 

717 # https://snomedbrowser.com/Codes/Details/XXX # noqa 

718 

719 # ------------------------------------------------------------------------- 

720 # SNOMED-CT core concepts 

721 # ------------------------------------------------------------------------- 

722 

723 # Abstract physical quantities 

724 MASS = "mass" 

725 LENGTH = "length" 

726 

727 # Saying "units" 

728 UNIT_OF_MEASURE = "unit_of_measure" 

729 

730 # Base physical units 

731 KILOGRAM = "kilogram" 

732 METRE = "metre" 

733 CENTIMETRE = "centimetre" 

734 

735 # Compound physical units 

736 KG_PER_SQ_M = "kilogram_per_square_metre" 

737 

738 # ------------------------------------------------------------------------- 

739 # Scales 

740 # ------------------------------------------------------------------------- 

741 

742 # ACE-R 

743 ACE_R_SCALE = "ace_r_scale" 

744 ACE_R_SUBSCALE_ATTENTION_ORIENTATION = "ace_r_subscale_attention_orientation" # noqa 

745 ACE_R_SUBSCALE_FLUENCY = "ace_r_subscale_fluency" 

746 ACE_R_SUBSCALE_LANGUAGE = "ace_r_subscale_language" 

747 ACE_R_SUBSCALE_MEMORY = "ace_r_subscale_memory" 

748 ACE_R_SUBSCALE_VISUOSPATIAL = "ace_r_subscale_visuospatial" 

749 ACE_R_SCORE = "ace_r_observable_score" 

750 ACE_R_SUBSCORE_ATTENTION_ORIENTATION = "ace_r_observable_subscore_attention_orientation" # noqa 

751 ACE_R_SUBSCORE_FLUENCY = "ace_r_observable_subscore_fluency" 

752 ACE_R_SUBSCORE_LANGUAGE = "ace_r_observable_subscore_language" 

753 ACE_R_SUBSCORE_MEMORY = "ace_r_observable_subscore_memory" 

754 ACE_R_SUBSCORE_VISUOSPATIAL = "ace_r_observable_subscore_visuospatial" 

755 ACE_R_PROCEDURE_ASSESSMENT = "ace_r_procedure_assessment" 

756 ACE_R_PROCEDURE_ASSESSMENT_SUBSCALE_ATTENTION_ORIENTATION = "ace_r_procedure_assessment_subscale_attention_orientation" # noqa 

757 ACE_R_PROCEDURE_ASSESSMENT_SUBSCALE_FLUENCY = "ace_r_procedure_assessment_subscale_fluency" # noqa 

758 ACE_R_PROCEDURE_ASSESSMENT_SUBSCALE_LANGUAGE = "ace_r_procedure_assessment_subscale_language" # noqa 

759 ACE_R_PROCEDURE_ASSESSMENT_SUBSCALE_MEMORY = "ace_r_procedure_assessment_subscale_memory" # noqa 

760 ACE_R_PROCEDURE_ASSESSMENT_SUBSCALE_VISUOSPATIAL = "ace_r_procedure_assessment_subscale_visuospatial" # noqa 

761 

762 # AIMS 

763 AIMS_SCALE = "aims_scale" 

764 AIMS_TOTAL_SCORE = "aims_observable_total_score" 

765 AIMS_PROCEDURE_ASSESSMENT = "aims_procedure_assessment" 

766 

767 # AUDIT, AUDIT-C 

768 AUDIT_SCALE = "audit_scale" 

769 AUDIT_SCORE = "audit_observable_score" 

770 AUDIT_PROCEDURE_ASSESSMENT = "audit_procedure_assessment" 

771 AUDITC_SCALE = "auditc_scale" 

772 AUDITC_SCORE = "auditc_observable_score" 

773 AUDITC_PROCEDURE_ASSESSMENT = "auditc_procedure_assessment" 

774 

775 # BADLS 

776 BADLS_SCALE = "badls_scale" 

777 BADLS_SCORE = "badls_observable_score" 

778 BADLS_PROCEDURE_ASSESSMENT = "badls_procedure_assessment" 

779 

780 # BDI 

781 BDI_SCALE = "bdi_scale" 

782 BDI_SCORE = "bdi_observable_score" 

783 BDI_PROCEDURE_ASSESSMENT = "bdi_procedure_assessment" 

784 BDI_II_SCORE = "bdi_ii_observable_score" 

785 BDI_II_PROCEDURE_ASSESSMENT = "bdi_ii_procedure_assessment" 

786 

787 # BMI 

788 BMI_OBSERVABLE = "bmi_observable" 

789 BMI_PROCEDURE_MEASUREMENT = "bmi_procedure_measurement" 

790 BODY_HEIGHT_OBSERVABLE = "body_height_observable" 

791 BODY_WEIGHT_OBSERVABLE = "body_weight_observable" 

792 WAIST_CIRCUMFERENCE_PROCEDURE_MEASUREMENT = "waist_circumference_procedure_measurement" # noqa 

793 WAIST_CIRCUMFERENCE_OBSERVABLE = "waist_circumference_observable" 

794 

795 # BPRS, BPRS-E 

796 BPRS1962_SCALE = "bprs1962_scale" 

797 # no observable/procedure 

798 

799 # CAGE 

800 CAGE_SCALE = "cage_scale" 

801 CAGE_SCORE = "cage_observable_score" 

802 CAGE_PROCEDURE_ASSESSMENT = "cage_procedure_assessment" 

803 

804 # CAPE-42: none 

805 # CAPS: none 

806 # Cardinal RN, Expectation-Detection: none 

807 # CBI-R: none 

808 # CECA-Q3: none 

809 # CESD, CESD-R: none 

810 # CGI, CGI-I, CGI-SCH: none 

811 # CIS-R: none 

812 

813 # CIWA-Ar 

814 CIWA_AR_SCALE = "ciwa_ar_scale" 

815 CIWA_AR_SCORE = "ciwa_ar_observable_score" 

816 CIWA_AR_PROCEDURE_ASSESSMENT = "ciwa_ar_procedure_assessment" 

817 

818 # Clinical: progress note 

819 PROGRESS_NOTE_PROCEDURE = "progress_note_procedure" 

820 CLINICAL_NOTE = "clinical_note" 

821 

822 # Clinical: photograph 

823 PHOTOGRAPH_PROCEDURE = "photograph_procedure" 

824 PHOTOGRAPH_PHYSICAL_OBJECT = "photograph_physical_object" 

825 

826 # Clinical: psychiatric clerking 

827 # ... "location": not obvious 

828 # ... "contact type" is an AoMRC heading, but I'm not sure the observable 

829 # entity of "Initial contact type" is right. 

830 PSYCHIATRIC_ASSESSMENT_PROCEDURE = "psychiatric_assessment_procedure" 

831 

832 PSYCLERK_REASON_FOR_REFERRAL = "psyclerk_reason_for_referral" 

833 PSYCLERK_PRESENTING_ISSUE = "psyclerk_presenting_issue" 

834 PSYCLERK_SYSTEMS_REVIEW = "psyclerk_systems_review" 

835 PSYCLERK_COLLATERAL_HISTORY = "psyclerk_collateral_history" 

836 

837 PSYCLERK_PAST_MEDICAL_SURGICAL_MENTAL_HEALTH_HISTORY = "psyclerk_past_medical_surgical_mental_health_history" # noqa 

838 PSYCLERK_PROCEDURES = "psyclerk_procedures" 

839 PSYCLERK_ALLERGIES_ADVERSE_REACTIONS = "psyclerk_allergies_adverse_reactions" # noqa 

840 PSYCLERK_MEDICATIONS_MEDICAL_DEVICES = "psyclerk_medications_medical_devices" # noqa 

841 PSYCLERK_DRUG_SUBSTANCE_USE = "psyclerk_drug_substance_use" 

842 PSYCLERK_FAMILY_HISTORY = "psyclerk_family_history" 

843 PSYCLERK_DEVELOPMENTAL_HISTORY = "psyclerk_developmental_history" 

844 PSYCLERK_SOCIAL_PERSONAL_HISTORY = "psyclerk_social_personal_history" 

845 PSYCLERK_PERSONALITY = "psyclerk_personality" 

846 PSYCLERK_PRISON_RECORD_CRIMINAL_ACTIVITY = "psyclerk_prison_record_criminal_activity" # noqa 

847 PSYCLERK_SOCIAL_HISTORY_BASELINE = "psyclerk_social_history_baseline" 

848 

849 PSYCLERK_MSE_APPEARANCE = "psyclerk_mse_appearance" 

850 PSYCLERK_MSE_BEHAVIOUR = "psyclerk_mse_behaviour" 

851 PSYCLERK_MSE_SPEECH = "psyclerk_mse_speech" 

852 PSYCLERK_MSE_MOOD = "psyclerk_mse_mood" 

853 PSYCLERK_MSE_AFFECT = "psyclerk_mse_affect" 

854 PSYCLERK_MSE_THOUGHT = "psyclerk_mse_thought" 

855 PSYCLERK_MSE_PERCEPTION = "psyclerk_mse_perception" 

856 PSYCLERK_MSE_COGNITION = "psyclerk_mse_cognition" 

857 PSYCLERK_MSE_INSIGHT = "psyclerk_mse_insight" 

858 

859 PSYCLERK_PHYSEXAM_GENERAL = "psyclerk_physexam_general" 

860 PSYCLERK_PHYSEXAM_CARDIOVASCULAR = "psyclerk_physexam_cardiovascular" 

861 PSYCLERK_PHYSEXAM_RESPIRATORY = "psyclerk_physexam_respiratory" 

862 PSYCLERK_PHYSEXAM_ABDOMINAL = "psyclerk_physexam_abdominal" 

863 PSYCLERK_PHYSEXAM_NEUROLOGICAL = "psyclerk_physexam_neurological" 

864 

865 PSYCLERK_ASSESSMENT_SCALES = "psyclerk_assessment_scales" 

866 PSYCLERK_INVESTIGATIONS_RESULTS = "psyclerk_investigations_results" 

867 

868 PSYCLERK_SAFETY_ALERTS = "psyclerk_safety_alerts" 

869 PSYCLERK_RISK_ASSESSMENT = "psyclerk_risk_assessment" 

870 PSYCLERK_RELEVANT_LEGAL_INFORMATION = "psyclerk_relevant_legal_information" 

871 

872 PSYCLERK_CURRENT_PROBLEMS = "psyclerk_current_problems" 

873 PSYCLERK_PATIENT_CARER_CONCERNS = "psyclerk_patient_carer_concerns" 

874 PSYCLERK_CLINICAL_NARRATIVE = "psyclerk_clinical_narrative" 

875 PSYCLERK_MANAGEMENT_PLAN = "psyclerk_management_plan" 

876 PSYCLERK_INFORMATION_GIVEN = "psyclerk_information_given" 

877 

878 # CPFT/CUH LPS: none 

879 # COPE: none 

880 

881 # CORE-10 

882 CORE10_SCALE = "core10_scale" 

883 CORE10_SCORE = "core10_observable_score" 

884 CORE10_PROCEDURE_ASSESSMENT = "core10_procedure_assessment" 

885 

886 # DAD: none 

887 

888 # DAST 

889 DAST_SCALE = "dast_scale" 

890 # I think the similarly named codes represent urine screening. 

891 

892 # Deakin JB, antibody: none 

893 # Demo questionnaire: none 

894 # DEMQOL, DEMQOL-Proxy: none 

895 # Distress Thermometer: none 

896 

897 # EQ-5D-5L 

898 EQ5D5L_SCALE = "eq5d5l_scale" 

899 EQ5D5L_INDEX_VALUE = "eq5d5l_observable_index_value" 

900 EQ5D5L_PAIN_DISCOMFORT_SCORE = "eq5d5l_observable_pain_discomfort_score" 

901 EQ5D5L_USUAL_ACTIVITIES_SCORE = "eq5d5l_observable_usual_activities_score" 

902 EQ5D5L_ANXIETY_DEPRESSION_SCORE = "eq5d5l_observable_anxiety_depression_score" # noqa 

903 EQ5D5L_MOBILITY_SCORE = "eq5d5l_observable_mobility_score" 

904 EQ5D5L_SELF_CARE_SCORE = "eq5d5l_observable_self_care_score" 

905 EQ5D5L_PROCEDURE_ASSESSMENT = "eq5d5l_procedure_assessment" 

906 

907 # FACT-G: none 

908 

909 # FAST: none 

910 FAST_SCALE = "fast_scale" 

911 FAST_SCORE = "fast_observable_score" 

912 FAST_PROCEDURE_ASSESSMENT = "fast_procedure_assessment" 

913 

914 # IRAC: none 

915 # FFT: none 

916 

917 # Patient Satisfaction Scale 

918 # Not sure. See XML 

919 

920 # Referrer Satisfaction Scale (patient-specific, survey): none 

921 # Frontotemporal Dementia Rating Scale: none 

922 

923 # GAD-7 

924 GAD7_SCALE = "gad7_scale" 

925 GAD7_SCORE = "gad7_observable_score" 

926 GAD7_PROCEDURE_ASSESSMENT = "gad7_procedure_assessment" 

927 

928 # GAF 

929 GAF_SCALE = "gaf_scale" 

930 # no observable/procedure 

931 

932 # GDS-15 

933 GDS15_SCALE = "gds15_scale" 

934 GDS15_SCORE = "gds15_observable_score" 

935 GDS15_PROCEDURE_ASSESSMENT = "gds15_procedure_assessment" 

936 

937 # UK GMC Patient Questionnaire: none 

938 

939 # HADS, HADS-respondent 

940 HADS_SCALE = "hads_scale" 

941 HADS_ANXIETY_SCORE = "hads_observable_anxiety_score" 

942 HADS_DEPRESSION_SCORE = "hads_observable_depression_score" 

943 HADS_PROCEDURE_ASSESSMENT = "hads_procedure_assessment" 

944 

945 # HAMA: none 

946 

947 # HAMD 

948 HAMD_SCALE = "hamd_scale" 

949 HAMD_SCORE = "hamd_observable_score" 

950 HAMD_PROCEDURE_ASSESSMENT = "hamd_procedure_assessment" 

951 

952 # HAMD-7: none 

953 

954 # HoNOS, HoNOS-65+, HoNOSCA, etc. (there are others too) 

955 HONOSCA_SCALE = "honos_childrenadolescents_scale" 

956 HONOSCA_SECTION_A_SCALE = "honos_childrenadolescents_section_a_scale" 

957 HONOSCA_SECTION_B_SCALE = "honos_childrenadolescents_section_b_scale" 

958 HONOSCA_SCORE = "honos_childrenadolescents_observable_score" 

959 HONOSCA_SECTION_A_SCORE = "honos_childrenadolescents_observable_section_a_score" # noqa 

960 HONOSCA_SECTION_B_SCORE = "honos_childrenadolescents_observable_section_b_score" # noqa 

961 HONOSCA_SECTION_A_PLUS_B_SCORE = "honos_childrenadolescents_observable_section_a_plus_b_score" # noqa 

962 HONOSCA_PROCEDURE_ASSESSMENT = "honos_childrenadolescents_procedure_assessment" # noqa 

963 # 

964 HONOS65_SCALE = "honos_olderadults_scale" 

965 HONOS65_SCORE = "honos_olderadults_observable_score" 

966 HONOS65_PROCEDURE_ASSESSMENT = "honos_olderadults_procedure_assessment" 

967 # 

968 HONOSWA_SCALE = "honos_workingage_scale" 

969 HONOSWA_SUBSCALE_1_OVERACTIVE = "honos_workingage_subscale_1_overactive" 

970 HONOSWA_SUBSCALE_2_SELFINJURY = "honos_workingage_subscale_2_selfinjury" 

971 HONOSWA_SUBSCALE_3_SUBSTANCE = "honos_workingage_subscale_3_substance" 

972 HONOSWA_SUBSCALE_4_COGNITIVE = "honos_workingage_subscale_4_cognitive" 

973 HONOSWA_SUBSCALE_5_PHYSICAL = "honos_workingage_subscale_5_physical" 

974 HONOSWA_SUBSCALE_6_PSYCHOSIS = "honos_workingage_subscale_6_psychosis" 

975 HONOSWA_SUBSCALE_7_DEPRESSION = "honos_workingage_subscale_7_depression" 

976 HONOSWA_SUBSCALE_8_OTHERMENTAL = "honos_workingage_subscale_8_othermental" 

977 HONOSWA_SUBSCALE_9_RELATIONSHIPS = "honos_workingage_subscale_9_relationships" # noqa 

978 HONOSWA_SUBSCALE_10_ADL = "honos_workingage_subscale_10_adl" 

979 HONOSWA_SUBSCALE_11_LIVINGCONDITIONS = "honos_workingage_subscale_11_livingconditions" # noqa 

980 HONOSWA_SUBSCALE_12_OCCUPATION = "honos_workingage_subscale_12_occupation" 

981 HONOSWA_SCORE = "honos_workingage_observable_score" 

982 HONOSWA_1_OVERACTIVE_SCORE = "honos_workingage_observable_1_overactive_score" # noqa 

983 HONOSWA_2_SELFINJURY_SCORE = "honos_workingage_observable_2_selfinjury_score" # noqa 

984 HONOSWA_3_SUBSTANCE_SCORE = "honos_workingage_observable_3_substance_score" 

985 HONOSWA_4_COGNITIVE_SCORE = "honos_workingage_observable_4_cognitive_score" 

986 HONOSWA_5_PHYSICAL_SCORE = "honos_workingage_observable_5_physical_score" 

987 HONOSWA_6_PSYCHOSIS_SCORE = "honos_workingage_observable_6_psychosis_score" 

988 HONOSWA_7_DEPRESSION_SCORE = "honos_workingage_observable_7_depression_score" # noqa 

989 HONOSWA_8_OTHERMENTAL_SCORE = "honos_workingage_observable_8_othermental_score" # noqa 

990 HONOSWA_9_RELATIONSHIPS_SCORE = "honos_workingage_observable_9_relationships_score" # noqa 

991 HONOSWA_10_ADL_SCORE = "honos_workingage_observable_10_adl_score" 

992 HONOSWA_11_LIVINGCONDITIONS_SCORE = "honos_workingage_observable_11_livingconditions_score" # noqa 

993 HONOSWA_12_OCCUPATION_SCORE = "honos_workingage_observable_12_occupation_score" # noqa 

994 HONOSWA_PROCEDURE_ASSESSMENT = "honos_workingage_procedure_assessment" 

995 

996 # ICD-9-CM: see below; separate file 

997 

998 # ICD-10: see below; separate file 

999 

1000 # IDED3D: none 

1001 

1002 # IES-R 

1003 IESR_SCALE = "iesr_scale" 

1004 IESR_SCORE = "iesr_observable_score" 

1005 IESR_PROCEDURE_ASSESSMENT = "iesr_procedure_assessment" 

1006 

1007 # INECO Frontal Screening: none 

1008 # Khandaker G, Insight: none 

1009 

1010 # MAST, SMAST 

1011 MAST_SCALE = "mast_scale" 

1012 MAST_SCORE = "mast_observable_score" 

1013 MAST_PROCEDURE_ASSESSMENT = "mast_procedure_assessment" 

1014 

1015 SMAST_SCALE = "smast_scale" 

1016 # SMAST: no observable/procedure 

1017 

1018 # MDS-UPDRS 

1019 UPDRS_SCALE = "updrs_scale" 

1020 # MDS-UPDRS: no observable/procedure 

1021 

1022 # MoCA 

1023 MOCA_SCALE = "moca_scale" 

1024 MOCA_SCORE = "moca_observable_score" 

1025 MOCA_PROCEDURE_ASSESSMENT = "moca_procedure_assessment" 

1026 

1027 # NART 

1028 NART_SCALE = "nart_scale" 

1029 NART_SCORE = "nart_observable_score" 

1030 NART_PROCEDURE_ASSESSMENT = "nart_procedure_assessment" 

1031 

1032 # NPI-Q: none 

1033 

1034 # PANSS 

1035 PANSS_SCALE = "panss_scale" 

1036 # PANSS: no observable/procedure 

1037 

1038 # PCL and variants: none 

1039 

1040 # PDSS 

1041 PDSS_SCALE = "pdss_scale" 

1042 PDSS_SCORE = "pdss_observable_score" 

1043 # PDSS: no procedure 

1044 

1045 # "Perinatal" and "scale": none 

1046 

1047 # PHQ-9 

1048 PHQ9_FINDING_NEGATIVE_SCREENING_FOR_DEPRESSION = "phq9_finding_negative_screening_for_depression" # noqa 

1049 PHQ9_FINDING_POSITIVE_SCREENING_FOR_DEPRESSION = "phq9_finding_positive_screening_for_depression" # noqa 

1050 PHQ9_SCORE = "phq9_observable_score" 

1051 PHQ9_PROCEDURE_DEPRESSION_SCREENING = "phq9_procedure_depression_screening" # noqa 

1052 PHQ9_SCALE = "phq9_scale" # https://snomedbrowser.com/Codes/Details/758711000000105 # noqa 

1053 

1054 # PHQ-15 

1055 PHQ15_SCORE = "phq15_observable_score" 

1056 PHQ15_PROCEDURE = "phq15_procedure_assessment" 

1057 PHQ15_SCALE = "phq15_scale" 

1058 

1059 # PIIOS or "parent infant scale": none 

1060 

1061 # PSWQ 

1062 PSWQ_SCALE = "pswq_scale" 

1063 PSWQ_SCORE = "pswq_observable_score" 

1064 PSWQ_PROCEDURE_ASSESSMENT = "pswq_procedure_assessment" 

1065 

1066 # QOL-Basic, QOL-SG 

1067 # Unsure, but there is this: 

1068 QOL_SCALE = "qol_scale" 

1069 # ... with no observable/procedure 

1070 

1071 # RAND-36: none directly 

1072 

1073 # SLUMS: none 

1074 

1075 # WEMWBS, SWEMWBS 

1076 WEMWBS_SCALE = "wemwbs_scale" 

1077 WEMWBS_SCORE = "wemwbs_observable_score" 

1078 WEMWBS_PROCEDURE_ASSESSMENT = "wemwbs_procedure_assessment" 

1079 # 

1080 SWEMWBS_SCALE = "swemwbs_scale" 

1081 SWEMWBS_SCORE = "swemwbs_observable_score" 

1082 SWEMWBS_PROCEDURE_ASSESSMENT = "swemwbs_procedure_assessment" 

1083 

1084 # WSAS 

1085 WSAS_SCALE = "wsas_scale" 

1086 WSAS_SCORE = "wsas_observable_score" 

1087 WSAS_WORK_SCORE = "wsas_observable_work_score" 

1088 WSAS_RELATIONSHIPS_SCORE = "wsas_observable_relationships_score" 

1089 WSAS_HOME_MANAGEMENT_SCORE = "wsas_observable_home_management_score" 

1090 WSAS_SOCIAL_LEISURE_SCORE = "wsas_observable_social_leisure_score" 

1091 WSAS_PRIVATE_LEISURE_SCORE = "wsas_observable_private_leisure_score" 

1092 WSAS_PROCEDURE_ASSESSMENT = "wsas_procedure_assessment" 

1093 

1094 # Y-BOCS, Y-BOCS-SC: none 

1095 # ZBI: none 

1096 

1097 

1098# ----------------------------------------------------------------------------- 

1099# Perform the lookup 

1100# ----------------------------------------------------------------------------- 

1101 

1102VALID_SNOMED_LOOKUPS = set([getattr(SnomedLookup, k) for k in dir(SnomedLookup) 

1103 if not k.startswith("_")]) 

1104 

1105 

1106@cache_region_static.cache_on_arguments(function_key_generator=fkg) 

1107def get_all_task_snomed_concepts(xml_filename: str) \ 

1108 -> Dict[str, SnomedConcept]: 

1109 """ 

1110 Reads in all SNOMED-CT codes for CamCOPS tasks, from the custom CamCOPS XML 

1111 file for this. 

1112 

1113 Args: 

1114 xml_filename: XML filename to read 

1115 

1116 Returns: 

1117 dict: maps lookup strings to :class:`SnomedConcept` objects 

1118 

1119 """ 

1120 xml_concepts = get_snomed_concepts_from_xml(xml_filename) 

1121 camcops_concepts = {} # type: Dict[str, SnomedConcept] 

1122 identifiers_seen = set() # type: Set[int] 

1123 for lookup, concepts in xml_concepts.items(): 

1124 # Check it 

1125 if lookup not in VALID_SNOMED_LOOKUPS: 

1126 log.debug("Ignoring unknown SNOMED-CT lookup: {!r}", lookup) 

1127 continue 

1128 assert len(concepts) == 1, ( 

1129 f"More than one SNOMED-CT concept for lookup: {lookup!r}" 

1130 ) 

1131 concept = concepts[0] 

1132 assert concept.identifier not in identifiers_seen, ( 

1133 f"Duplicate SNOMED-CT identifier: {concept.identifier!r}") 

1134 identifiers_seen.add(concept.identifier) 

1135 # Stash it 

1136 camcops_concepts[lookup] = concept 

1137 # Check if any are missing 

1138 missing = sorted(list(VALID_SNOMED_LOOKUPS - set(camcops_concepts.keys()))) 

1139 if missing: 

1140 raise ValueError( 

1141 f"The following SNOMED-CT concepts required by CamCOPS are " 

1142 f"missing from the XML ({xml_filename!r}): {missing!r}") 

1143 # Done 

1144 return camcops_concepts 

1145 

1146 

1147# ============================================================================= 

1148# UMLS ICD-9-CM 

1149# ============================================================================= 

1150 

1151class UmlsIcd9SnomedRow(object): 

1152 """ 

1153 Simple information-holding class for a row of the ICD-9-CM TSV file, from 

1154 https://www.nlm.nih.gov/research/umls/mapping_projects/icd9cm_to_snomedct.html. 

1155 

1156 NOT CURRENTLY USED. 

1157 """ 

1158 HEADER = [ 

1159 "ICD_CODE", "ICD_NAME", "IS_CURRENT_ICD", "IP_USAGE", "OP_USAGE", 

1160 "AVG_USAGE", "IS_NEC", "SNOMED_CID", "SNOMED_FSN", "IS_1-1MAP", 

1161 "CORE_USAGE", "IN_CORE", 

1162 ] 

1163 

1164 @staticmethod 

1165 def to_float(x: str) -> Optional[float]: 

1166 return None if x == "NULL" else float(x) 

1167 

1168 def __init__(self, 

1169 icd_code: str, 

1170 icd_name: str, 

1171 is_current_icd: str, 

1172 ip_usage: str, 

1173 op_usage: str, 

1174 avg_usage: str, 

1175 is_nec: str, 

1176 snomed_cid: str, 

1177 snomed_fsn: str, 

1178 is_one_to_one_map: str, 

1179 core_usage: str, 

1180 in_core: str) -> None: 

1181 """ 

1182 Argument order is important. 

1183 

1184 Args: 

1185 icd_code: ICD-9-CM code 

1186 icd_name: Name of ICD-9-CM entity 

1187 is_current_icd: ? 

1188 ip_usage: ? 

1189 op_usage: ? 

1190 avg_usage: ? 

1191 is_nec: ? 

1192 snomed_cid: SNOMED-CT concept ID 

1193 snomed_fsn: SNOMED-CT fully specified name 

1194 is_one_to_one_map: ?; possibly always true in this dataset but not 

1195 true in a broader dataset including things other than 1:1 

1196 mappings? 

1197 core_usage: ? 

1198 in_core: ? 

1199 """ 

1200 self.icd_code = icd_code 

1201 self.icd_name = icd_name 

1202 self.is_current_icd = bool(int(is_current_icd)) 

1203 self.ip_usage = self.to_float(ip_usage) 

1204 self.op_usage = self.to_float(op_usage) 

1205 self.avg_usage = self.to_float(avg_usage) 

1206 self.is_nec = bool(int(is_nec)) 

1207 self.snomed_cid = int(snomed_cid) 

1208 self.snomed_fsn = snomed_fsn 

1209 self.is_one_to_one_map = bool(int(is_one_to_one_map)) 

1210 self.core_usage = self.to_float(core_usage) 

1211 self.in_core = bool(int(in_core)) 

1212 

1213 def __repr__(self) -> str: 

1214 return simple_repr(self, [ 

1215 "icd_code", "icd_name", "is_current_icd", "ip_usage", "op_usage", 

1216 "avg_usage", "is_nec", 

1217 "snomed_cid", "snomed_fsn", 

1218 "is_one_to_one_map", "core_usage", "in_core", 

1219 ]) 

1220 

1221 def snomed_concept(self) -> SnomedConcept: 

1222 """ 

1223 Returns the associated SNOMED-CT concept. 

1224 """ 

1225 return SnomedConcept(self.snomed_cid, self.snomed_fsn) 

1226 

1227 def __str__(self) -> str: 

1228 return ( 

1229 f"ICD-9-CM {self.icd_code} ({self.icd_name}) " 

1230 f"-> SNOMED-CT {self.snomed_concept()}" 

1231 ) 

1232 

1233 

1234@cache_region_static.cache_on_arguments(function_key_generator=fkg) 

1235def get_all_icd9cm_snomed_concepts_from_umls( 

1236 tsv_filename: str) -> Dict[str, SnomedConcept]: 

1237 """ 

1238 Reads in all ICD-9-CM SNOMED-CT codes that are supported by the client, 

1239 from the UMLS data file, from 

1240 https://www.nlm.nih.gov/research/umls/mapping_projects/icd9cm_to_snomedct.html. 

1241 

1242 Args: 

1243 tsv_filename: TSV filename to read 

1244 

1245 Returns: 

1246 dict: maps lookup strings to :class:`SnomedConcept` objects 

1247 

1248 NOT CURRENTLY USED. 

1249 """ 

1250 log.info("Loading SNOMED-CT ICD-9-CM codes from file: {}", tsv_filename) 

1251 concepts = {} # type: Dict[str, SnomedConcept] 

1252 with open(tsv_filename, 'r') as tsvin: 

1253 reader = csv.reader(tsvin, delimiter="\t") 

1254 header = next(reader, None) 

1255 if header != UmlsIcd9SnomedRow.HEADER: 

1256 raise ValueError( 

1257 f"ICD-9-CM TSV file has unexpected header: {header!r}; " 

1258 f"expected {UmlsIcd9SnomedRow.HEADER!r}") 

1259 for row in reader: 

1260 entry = UmlsIcd9SnomedRow(*row) 

1261 if entry.icd_code not in CLIENT_ICD9CM_CODES: 

1262 continue 

1263 if not entry.is_one_to_one_map: 

1264 continue 

1265 if entry.icd_code in concepts: 

1266 log.warning( 

1267 "Duplicate ICD-9-CM code found in SNOMED file {!r}: {!r}", 

1268 tsv_filename, entry.icd_code) 

1269 continue 

1270 concept = entry.snomed_concept() 

1271 # log.debug("{}", entry) 

1272 concepts[entry.icd_code] = concept 

1273 missing = CLIENT_ICD9CM_CODES - set(concepts.keys()) 

1274 if missing: 

1275 log.info("No SNOMED-CT codes for ICD-9-CM codes: {}", 

1276 ", ".join(sorted(missing))) 

1277 return concepts 

1278 

1279 

1280# ============================================================================= 

1281# UMLS ICD-10 

1282# ============================================================================= 

1283 

1284class UmlsSnomedToIcd10Row(object): 

1285 """ 

1286 Simple information-holding class for a row of the ICD-10-CM TSV file from 

1287 https://www.nlm.nih.gov/research/umls/mapping_projects/snomedct_to_icd10cm.html. 

1288 

1289 However, that is unhelpful (many to one). 

1290 

1291 NOT CURRENTLY USED. 

1292 """ 

1293 HEADER = [ 

1294 "id", "effectiveTime", "active", "moduleId", "refsetId", 

1295 "referencedComponentId", "referencedComponentName", "mapGroup", 

1296 "mapPriority", "mapRule", "mapAdvice", "mapTarget", "mapTargetName", 

1297 "correlationId", "mapCategoryId", "mapCategoryName", 

1298 ] 

1299 MAP_GOOD = "MAP SOURCE CONCEPT IS PROPERLY CLASSIFIED" 

1300 

1301 def __init__(self, 

1302 row_id: str, 

1303 effective_time: str, 

1304 active: str, 

1305 module_id: str, 

1306 refset_id: str, 

1307 referenced_component_id: str, 

1308 referenced_component_name: str, 

1309 map_group: str, 

1310 map_priority: str, 

1311 map_rule: str, 

1312 map_advice: str, 

1313 map_target: str, 

1314 map_target_name: str, 

1315 correlation_id: str, 

1316 map_category_id: str, 

1317 map_category_name: str) -> None: 

1318 """ 

1319 Argument order is important. 

1320 

1321 Args: 

1322 row_id: UUID format or similar 

1323 effective_time: date in YYYYMMDD format 

1324 active: ? 

1325 module_id: ? 

1326 refset_id: ? 

1327 referenced_component_id: SNOMED-CT concept ID 

1328 referenced_component_name: SNOMED-CT concept name 

1329 map_group: ?; e.g. 1 

1330 map_priority: ? but e.g. 1, 2; correlates with map_rule 

1331 map_rule: ?; e.g. "TRUE"; "OTHERWISE TRUE" 

1332 map_advice: ?, but e.g. "ALWAYS F32.2" or "ALWAYS F32.2 | 

1333 DESCENDANTS NOT EXHAUSTIVELY MAPPED" 

1334 map_target: ICD-10 code 

1335 map_target_name: ICD-10 name 

1336 correlation_id: a SNOMED-CT concept for the mapping, e.g. 

1337 447561005 = "SNOMED CT source code to target map code 

1338 correlation not specified (foundation metadata concept)" 

1339 map_category_id: a SNOMED-CT concept for the mapping, e.g. 

1340 447637006 = "Map source concept is properly classified 

1341 (foundation metadata concept)" 

1342 map_category_name: SNOMED-CT name corresponding to map_category_id, 

1343 e.g. "MAP SOURCE CONCEPT IS PROPERLY CLASSIFIED" 

1344 """ 

1345 self.row_id = row_id 

1346 self.effective_time = effective_time 

1347 self.active = bool(int(active)) 

1348 self.module_id = int(module_id) 

1349 self.refset_id = int(refset_id) 

1350 self.referenced_component_id = int(referenced_component_id) 

1351 self.referenced_component_name = referenced_component_name 

1352 self.map_group = int(map_group) 

1353 self.map_priority = int(map_priority) 

1354 self.map_rule = map_rule 

1355 self.map_advice = map_advice 

1356 self.map_target = map_target 

1357 self.map_target_name = map_target_name 

1358 self.correlation_id = int(correlation_id) 

1359 self.map_category_id = int(map_category_id) 

1360 self.map_category_name = map_category_name 

1361 

1362 def __repr__(self) -> str: 

1363 return simple_repr(self, [ 

1364 "row_id", "effective_time", "active", "module_id", "refset_id", 

1365 "referenced_component_id", "referenced_component_name", 

1366 "map_group", "map_priority", "map_rule", "map_advice", 

1367 "map_target", "map_target_name", 

1368 "correlation_id", "map_category_id", "map_category_name", 

1369 ]) 

1370 

1371 def snomed_concept(self) -> SnomedConcept: 

1372 """ 

1373 Returns the associated SNOMED-CT concept. 

1374 """ 

1375 return SnomedConcept(self.referenced_component_id, 

1376 self.referenced_component_name) 

1377 

1378 @property 

1379 def icd_code(self) -> str: 

1380 return self.map_target 

1381 

1382 @property 

1383 def icd_name(self) -> str: 

1384 return self.map_target_name 

1385 

1386 def __str__(self) -> str: 

1387 return ( 

1388 f"ICD-10 {self.icd_code} ({self.icd_name}) " 

1389 f"-> SNOMED-CT {self.snomed_concept()}" 

1390 ) 

1391 

1392 

1393def get_all_icd10_snomed_concepts_from_umls( 

1394 tsv_filename: str) -> Dict[str, SnomedConcept]: 

1395 """ 

1396 Reads in all ICD-10 SNOMED-CT codes that are supported by the client, 

1397 from the UMLS data file, from 

1398 https://www.nlm.nih.gov/research/umls/mapping_projects/snomedct_to_icd10cm.html. 

1399 

1400 Args: 

1401 tsv_filename: TSV filename to read 

1402 

1403 Returns: 

1404 dict: maps lookup strings to :class:`SnomedConcept` objects 

1405 

1406 NOT CURRENTLY USED. 

1407 """ 

1408 log.info("Loading SNOMED-CT ICD-10-CM codes from file: {}", tsv_filename) 

1409 concepts = {} # type: Dict[str, SnomedConcept] 

1410 with open(tsv_filename, 'r') as tsvin: 

1411 reader = csv.reader(tsvin, delimiter="\t") 

1412 header = next(reader, None) 

1413 if header != UmlsSnomedToIcd10Row.HEADER: 

1414 raise ValueError( 

1415 f"ICD-9-CM TSV file has unexpected header: {header!r}; " 

1416 f"expected {UmlsSnomedToIcd10Row.HEADER!r}") 

1417 for row in reader: 

1418 entry = UmlsSnomedToIcd10Row(*row) 

1419 if entry.icd_code not in CLIENT_ICD10_CODES: 

1420 continue 

1421 if entry.icd_code in concepts: 

1422 log.warning( 

1423 "Duplicate ICD-10-CM code found in SNOMED file {!r}: {!r}", 

1424 tsv_filename, entry.icd_code) 

1425 continue 

1426 concept = entry.snomed_concept() 

1427 # log.debug("{}", entry) 

1428 concepts[entry.icd_code] = concept 

1429 missing = CLIENT_ICD10_CODES - set(concepts.keys()) 

1430 if missing: 

1431 log.info("No SNOMED-CT codes for ICD-10 codes: {}", 

1432 ", ".join(sorted(missing))) 

1433 return concepts 

1434 

1435 

1436# ============================================================================= 

1437# Athena OHDSI mapping 

1438# ============================================================================= 

1439 

1440# ----------------------------------------------------------------------------- 

1441# Fetch ICD-9-CM and ICD-10 codes (shared for fewer passes through the files) 

1442# ----------------------------------------------------------------------------- 

1443 

1444def get_icd9cm_icd10_snomed_concepts_from_athena( 

1445 athena_concept_tsv_filename: str, 

1446 athena_concept_relationship_tsv_filename: str) \ 

1447 -> Tuple[Dict[str, List[SnomedConcept]], 

1448 Dict[str, List[SnomedConcept]]]: 

1449 """ 

1450 Takes Athena concept and concept-relationship files, and fetches details 

1451 of SNOMED-CT code for all ICD-9-CM and ICD-10[-CM] codes used by CamCOPS. 

1452 

1453 A bit of human review is required; this is probably preferable to using 

1454 gensim or some other automatic similarity check. 

1455 

1456 Args: 

1457 athena_concept_tsv_filename: 

1458 path to ``CONCEPT.csv`` (a tab-separated value file) 

1459 athena_concept_relationship_tsv_filename: 

1460 path to ``CONCEPT_RELATIONSHIP.csv`` (a tab-separated value file) 

1461 

1462 Returns: 

1463 tuple: ``icd9cm, icd10``, where each is a dictionary mapping ICD codes 

1464 to a list of mapped :class:`SnomedConcept` objects. 

1465 

1466 """ 

1467 athena_icd_concepts = get_athena_concepts( 

1468 tsv_filename=athena_concept_tsv_filename, 

1469 vocabulary_ids={AthenaVocabularyId.ICD9CM, AthenaVocabularyId.ICD10CM}, 

1470 concept_codes=CLIENT_ICD9CM_CODES | CLIENT_ICD10_CODES, 

1471 ) 

1472 athena_icd_concepts.sort(key=lambda x: x.concept_code) 

1473 relationships = get_athena_concept_relationships( 

1474 tsv_filename=athena_concept_relationship_tsv_filename, 

1475 concept_id_1_values=set(x.concept_id for x in athena_icd_concepts), 

1476 relationship_id_values={AthenaRelationshipId.MAPS_TO} 

1477 ) 

1478 athena_snomed_concepts = get_athena_concepts( 

1479 tsv_filename=athena_concept_tsv_filename, 

1480 vocabulary_ids={AthenaVocabularyId.SNOMED}, 

1481 concept_ids=set(x.concept_id_2 for x in relationships) 

1482 ) 

1483 snomed_concepts_icd9 = OrderedDict() # type: Dict[str, List[SnomedConcept]] # noqa 

1484 snomed_concepts_icd10 = OrderedDict() # type: Dict[str, List[SnomedConcept]] # noqa 

1485 for icd in athena_icd_concepts: 

1486 target = ( 

1487 snomed_concepts_icd9 

1488 if icd.vocabulary_id == AthenaVocabularyId.ICD9CM 

1489 else snomed_concepts_icd10 

1490 ) 

1491 icd_code = icd.concept_code 

1492 # log.debug("Processing icd = {}", icd) 

1493 possible_snomed = [] # type: List[AthenaConceptRow] 

1494 for rel in relationships: 

1495 if rel.concept_id_1 != icd.concept_id: 

1496 continue 

1497 # log.debug("Processing rel = {}", rel) 

1498 for snomed in athena_snomed_concepts: 

1499 if snomed.concept_id != rel.concept_id_2: 

1500 continue 

1501 # log.debug("Processing snomed = {}", snomed) 

1502 possible_snomed.append(snomed) 

1503 if possible_snomed: 

1504 sclist = [ 

1505 SnomedConcept.create(s.snomed_concept()) 

1506 for s in possible_snomed 

1507 ] 

1508 sclist.sort(key=lambda sc: sc.identifier) 

1509 log.debug("Mapping {} -> {}", icd, sclist) 

1510 target[icd_code] = sclist 

1511 else: 

1512 log.debug("No SNOMED code found for {}", icd) 

1513 return snomed_concepts_icd9, snomed_concepts_icd10 

1514 

1515 

1516# ----------------------------------------------------------------------------- 

1517# Fetch codes from Athena data set and write them to CamCOPS XML 

1518# ----------------------------------------------------------------------------- 

1519 

1520def send_athena_icd_snomed_to_xml( 

1521 athena_concept_tsv_filename: str, 

1522 athena_concept_relationship_tsv_filename: str, 

1523 icd9_xml_filename: str, 

1524 icd10_xml_filename) -> None: 

1525 """ 

1526 Reads SNOMED-CT codes for ICD-9-CM and ICD10 from Athena OHDSI files, and 

1527 writes 

1528 

1529 Args: 

1530 athena_concept_tsv_filename: 

1531 path to ``CONCEPT.csv`` (a tab-separated value file) 

1532 athena_concept_relationship_tsv_filename: 

1533 path to ``CONCEPT_RELATIONSHIP.csv`` (a tab-separated value file) 

1534 icd9_xml_filename: 

1535 ICD-9 XML filename to write 

1536 icd10_xml_filename: 

1537 ICD-10 XML filename to write 

1538 """ 

1539 icd9, icd10 = get_icd9cm_icd10_snomed_concepts_from_athena( 

1540 athena_concept_tsv_filename=athena_concept_tsv_filename, 

1541 athena_concept_relationship_tsv_filename=athena_concept_relationship_tsv_filename # noqa 

1542 ) 

1543 write_snomed_concepts_to_xml(icd9_xml_filename, icd9) 

1544 write_snomed_concepts_to_xml(icd10_xml_filename, icd10) 

1545 

1546 

1547# ----------------------------------------------------------------------------- 

1548# Fetch data from XML 

1549# ----------------------------------------------------------------------------- 

1550 

1551def get_multiple_snomed_concepts_from_xml(xml_filename: str, 

1552 valid_lookups: Set[str] = None, 

1553 require_all: bool = False) \ 

1554 -> Dict[str, List[SnomedConcept]]: 

1555 """ 

1556 Reads in all SNOMED-CT codes for ICD-9 or ICD-10, from the custom CamCOPS 

1557 XML file for this (made by e.g. :func:`send_athena_icd_snomed_to_xml`). 

1558 

1559 Args: 

1560 xml_filename: XML filename to read 

1561 valid_lookups: possible lookup values 

1562 require_all: require that ``valid_lookups`` is truthy and that all 

1563 values in it are present in the XML 

1564 

1565 Returns: 

1566 dict: maps lookup strings to lists of :class:`SnomedConcept` objects 

1567 

1568 """ 

1569 valid_lookups = set(valid_lookups or []) # type: Set[str] 

1570 xml_concepts = get_snomed_concepts_from_xml(xml_filename) 

1571 camcops_concepts = {} # type: Dict[str, List[SnomedConcept]] 

1572 for lookup, concepts in xml_concepts.items(): 

1573 # Check it 

1574 if valid_lookups and lookup not in valid_lookups: 

1575 log.debug("Ignoring unknown SNOMED-CT lookup: {!r}", lookup) 

1576 continue 

1577 # Stash it 

1578 camcops_concepts[lookup] = concepts 

1579 if require_all: 

1580 # Check if any are missing 

1581 assert valid_lookups, "require_all specified but valid_lookups missing" 

1582 if valid_lookups: 

1583 missing = sorted(list(valid_lookups - set(camcops_concepts.keys()))) 

1584 if missing: 

1585 msg = ( 

1586 f'The following {"required " if require_all else ""}' 

1587 f'SNOMED-CT concepts are missing from the XML ' 

1588 f'({xml_filename!r}): {missing!r}' 

1589 ) 

1590 if require_all: 

1591 log.critical(msg) 

1592 raise ValueError(msg) 

1593 else: 

1594 log.debug(msg) 

1595 # Done 

1596 return camcops_concepts 

1597 

1598 

1599@cache_region_static.cache_on_arguments(function_key_generator=fkg) 

1600def get_icd9_snomed_concepts_from_xml(xml_filename: str) \ 

1601 -> Dict[str, List[SnomedConcept]]: 

1602 """ 

1603 Reads in all ICD-9-CM SNOMED-CT codes from a custom CamCOPS XML file. 

1604 

1605 Args: 

1606 xml_filename: filename to read 

1607 

1608 Returns: 

1609 dict: maps ICD-9-CM codes to lists of :class:`SnomedConcept` objects 

1610 """ 

1611 return get_multiple_snomed_concepts_from_xml(xml_filename, 

1612 CLIENT_ICD9CM_CODES) 

1613 

1614 

1615@cache_region_static.cache_on_arguments(function_key_generator=fkg) 

1616def get_icd10_snomed_concepts_from_xml(xml_filename: str) \ 

1617 -> Dict[str, List[SnomedConcept]]: 

1618 """ 

1619 Reads in all ICD-10 SNOMED-CT codes from a custom CamCOPS XML file. 

1620 

1621 Args: 

1622 xml_filename: filename to read 

1623 

1624 Returns: 

1625 dict: maps ICD-10 codes to lists of :class:`SnomedConcept` objects 

1626 """ 

1627 return get_multiple_snomed_concepts_from_xml(xml_filename, 

1628 CLIENT_ICD10_CODES)