Coverage for cc_modules/cc_snomed.py: 60%

455 statements  

« prev     ^ index     » next       coverage.py v6.5.0, created at 2022-11-08 23:14 +0000

1#!/usr/bin/env python 

2 

3r""" 

4camcops_server/cc_modules/cc_snomed.py 

5 

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

7 

8 Copyright (C) 2012, University of Cambridge, Department of Psychiatry. 

9 Created by Rudolf Cardinal (rnc1001@cam.ac.uk). 

10 

11 This file is part of CamCOPS. 

12 

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

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

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

16 (at your option) any later version. 

17 

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

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

20 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

21 GNU General Public License for more details. 

22 

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

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

25 

26=============================================================================== 

27 

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

29 

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

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

32 

33Some tests: 

34 

35.. code-block:: python 

36 

37 import logging 

38 from cardinal_pythonlib.logs import main_only_quicksetup_rootlogger 

39 from camcops_server.cc_modules.cc_request import get_command_line_request 

40 from camcops_server.cc_modules.cc_snomed import * 

41 from camcops_server.tasks.phq9 import Phq9 

42 main_only_quicksetup_rootlogger(level=logging.DEBUG) 

43 req = get_command_line_request() 

44 

45 # --------------------------------------------------------------------- 

46 # Read the XML, etc. 

47 # --------------------------------------------------------------------- 

48 

49 print(VALID_SNOMED_LOOKUPS) 

50 concepts = get_all_snomed_concepts(req.config.snomed_xml_filename) 

51 

52 # --------------------------------------------------------------------- 

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

54 # --------------------------------------------------------------------- 

55 

56 phq9 = Phq9() 

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

58 phq9.q1 = 2 

59 phq9.q2 = 2 

60 phq9.q3 = 2 

61 phq9.q4 = 2 

62 phq9.q5 = 2 

63 phq9.q6 = 2 

64 phq9.q7 = 2 

65 phq9.q8 = 2 

66 phq9.q9 = 2 

67 phq9.q10 = 2 

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

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

70 

71Note diagnostic coding maps: 

72 

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

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

75 

76Other testing: 

77 

78.. code-block:: bash 

79 

80 camcops_server dev_cli --verbose 

81 

82.. code-block:: python 

83 

84 from camcops_server.cc_modules.cc_snomed import * 

85 

86 athena_concepts = get_athena_concepts(config.athena_concept_tsv_filename) 

87 relationships = get_athena_concept_relationships(config.athena_concept_relationship_tsv_filename) 

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

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

90 

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

92 

93Regarding temporal coding: 

94 

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

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

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

98 event dates: 

99 

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

101 

102 "Contextual information that is not represented by SNOMED CT 

103 

104 Clinical statements that contain SNOMED CT Concept representations will 

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

106 represented using SNOMED CT: 

107 

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

109 - Quantitative information including ranges and durations; 

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

111 parties involved in a recorded activity. 

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

113 constructs in a standardized or proprietary record architecture 

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

115 clinical statements." 

116 

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

118 

119 "Using SNOMED CT in standard architectures 

120 

121 ... 

122 

123 Each health record component has the potential to include: 

124 

125 - Dates and times of actual and planned events. 

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

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

128 plan. 

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

130 information container, link, or statement: 

131 

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

133 

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

135 digital data." 

136 

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

138 dates/times, perhaps! 

139 

140""" # noqa 

141 

142from collections import OrderedDict 

143import csv 

144import logging 

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

146import xml.etree.cElementTree as ElementTree 

147 

148from cardinal_pythonlib.athena_ohdsi import ( 

149 AthenaConceptRow, 

150 AthenaRelationshipId, 

151 AthenaVocabularyId, 

152 get_athena_concept_relationships, 

153 get_athena_concepts, 

154) 

155from cardinal_pythonlib.logs import BraceStyleAdapter 

156from cardinal_pythonlib.reprfunc import simple_repr 

157 

158# noinspection PyUnresolvedReferences 

159from cardinal_pythonlib.snomed import ( # noqa: F401 

160 SnomedAttribute, 

161 SnomedAttributeGroup, 

162 SnomedAttributeSet, 

163 SnomedConcept as SnomedConceptCardinalPythonlib, 

164 SnomedExpression as SnomedExpressionCardinalPythonlib, 

165 SnomedFocusConcept, 

166 SnomedRefinement, 

167 SnomedValue, 

168) 

169 

170from camcops_server.cc_modules.cc_cache import cache_region_static, fkg 

171from camcops_server.cc_modules.cc_xml import XmlDataTypes, XmlElement 

172 

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

174 

175 

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

177# Constants 

178# ============================================================================= 

179 

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

181# Internal 

182# ----------------------------------------------------------------------------- 

183 

184SNOMED_XML_NAME = "snomed_ct_expression" 

185 

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

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

188# ----------------------------------------------------------------------------- 

189 

190CLIENT_ICD9CM_CODES = set( 

191 """ 

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

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

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

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

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

197295.00 295.01 295.02 295.03 295.04 295.05 295.1 295.10 295.11 295.12 295.13 

198295.14 295.15 295.2 295.20 295.21 295.22 295.23 295.24 295.25 295.3 295.30 

199295.31 295.32 295.33 295.34 295.35 295.4 295.40 295.41 295.42 295.43 295.44 

200295.45 295.5 295.50 295.51 295.52 295.53 295.54 295.55 295.6 295.60 295.61 

201295.62 295.63 295.64 295.65 295.7 295.70 295.71 295.72 295.73 295.74 295.75 

202295.8 295.80 295.81 295.82 295.83 295.84 295.85 295.9 295.90 295.91 295.92 

203295.93 295.94 295.95 296.0 296.00 296.01 296.02 296.03 296.04 296.05 296.06 

204296.1 296.10 296.11 296.12 296.13 296.14 296.15 296.16 296.2 296.20 296.21 

205296.22 296.23 296.24 296.25 296.26 296.3 296.30 296.31 296.32 296.33 296.34 

206296.35 296.36 296.4 296.40 296.41 296.42 296.43 296.44 296.45 296.46 296.5 

207296.50 296.51 296.52 296.53 296.54 296.55 296.56 296.6 296.60 296.61 296.62 

208296.63 296.64 296.65 296.66 296.7 296.8 296.80 296.81 296.82 296.89 296.9 

209296.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 

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

211299.90 299.91 300.0 300.00 300.01 300.02 300.09 300.1 300.10 300.11 300.12 

212300.13 300.14 300.15 300.16 300.19 300.2 300.20 300.21 300.22 300.23 300.29 

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

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

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

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

217302.71 302.72 302.73 302.74 302.75 302.76 302.79 302.8 302.81 302.82 302.83 

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

219303.90 303.91 303.92 303.93 304.0 304.00 304.01 304.02 304.03 304.1 304.10 

220304.11 304.12 304.13 304.2 304.20 304.21 304.22 304.23 304.3 304.30 304.31 

221304.32 304.33 304.4 304.40 304.41 304.42 304.43 304.5 304.50 304.51 304.52 

222304.53 304.6 304.60 304.61 304.62 304.63 304.7 304.70 304.71 304.72 304.73 

223304.8 304.80 304.81 304.82 304.83 304.9 304.90 304.91 304.92 304.93 305.0 

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

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

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

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

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

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

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

231307.3 307.4 307.40 307.41 307.42 307.43 307.44 307.45 307.46 307.47 307.48 

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

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

234309.21 309.22 309.23 309.24 309.28 309.29 309.3 309.4 309.8 309.81 309.82 

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

236312.00 312.01 312.02 312.03 312.1 312.10 312.11 312.12 312.13 312.2 312.20 

237312.21 312.22 312.23 312.3 312.30 312.31 312.32 312.33 312.34 312.35 312.39 

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

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

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

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

242V71.09 

243""".split() 

244) 

245 

246# ----------------------------------------------------------------------------- 

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

248# ----------------------------------------------------------------------------- 

249 

250CLIENT_ICD10_CODES = set( 

251 """ 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

288F05.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 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

350F62.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 

351F65 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 

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

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

354F73.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 

355F80 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 

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

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

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

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

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

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

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

363R45 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 

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

547""".split() 

548) 

549 

550 

551# ============================================================================= 

552# The SnomedConcept class, amended 

553# ============================================================================= 

554 

555 

556class SnomedConcept(SnomedConceptCardinalPythonlib): 

557 @classmethod 

558 def create( 

559 cls, concept: SnomedConceptCardinalPythonlib 

560 ) -> "SnomedConcept": 

561 """ 

562 Creates a CamCOPS-friendly 

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

564 :class:`cardinal_pythonlib.snomed.SnomedConcept`. 

565 

566 The result has extra methods. 

567 """ 

568 return SnomedConcept(identifier=concept.identifier, term=concept.term) 

569 

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

571 """ 

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

573 SNOMED-CT object. 

574 

575 Args: 

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

577 """ 

578 return XmlElement( 

579 name=SNOMED_XML_NAME, 

580 value=self.as_string(longform), 

581 datatype=XmlDataTypes.STRING, 

582 ) 

583 

584 

585# ============================================================================= 

586# The SnomedExpression class, amended 

587# ============================================================================= 

588 

589 

590class SnomedExpression(SnomedExpressionCardinalPythonlib): 

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

592 """ 

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

594 SNOMED-CT object. 

595 

596 Args: 

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

598 """ 

599 return XmlElement( 

600 name=SNOMED_XML_NAME, 

601 value=self.as_string(longform), 

602 datatype=XmlDataTypes.STRING, 

603 ) 

604 

605 

606# ============================================================================= 

607# The CamCOPS XML file format for SNOMED-CT 

608# ============================================================================= 

609 

610ROOT_TAG = "snomed_concepts" 

611CONCEPT_TAG = "concept" 

612LOOKUP_TAG = "lookup" 

613LOOKUP_NAME_ATTR = "name" 

614LOOKUP_ATTR = "lookup" 

615ID_TAG = "id" 

616TERM_TAG = "term" 

617AUTOGEN_COMMENT = ( 

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

619 "edit" 

620) 

621 

622 

623def get_snomed_concepts_from_xml( 

624 xml_filename: str, 

625) -> Dict[str, Union[SnomedConcept, List[SnomedConcept]]]: 

626 """ 

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

628 format. 

629 

630 Args: 

631 xml_filename: XML filename to read 

632 

633 Returns: 

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

635 :class:`SnomedConcept` objects 

636 

637 """ 

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

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

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

641 root = tree.getroot() 

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

643 find_lookup = "./{tag}[@{attr}]".format( 

644 tag=LOOKUP_TAG, attr=LOOKUP_NAME_ATTR 

645 ) 

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

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

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

649 for lookuproot in root.findall(find_lookup): 

650 # Extract info from the XML 

651 lookupname = lookuproot.attrib.get(LOOKUP_NAME_ATTR) 

652 for conceptroot in lookuproot.findall(find_concept): 

653 id_node = conceptroot.find(find_id_in_concept) 

654 identifier = int(id_node.text) 

655 name_node = conceptroot.find(find_name_in_concept) 

656 name = name_node.text or "" 

657 # Stash it 

658 concept = SnomedConcept(identifier, name) 

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

660 concepts_for_lookup.append(concept) 

661 # Done 

662 return all_concepts 

663 

664 

665def write_snomed_concepts_to_xml( 

666 xml_filename: str, 

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

668 comment: str = AUTOGEN_COMMENT, 

669) -> None: 

670 """ 

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

672 

673 Args: 

674 xml_filename: XML filename to write 

675 concepts: dictionary mapping lookup codes to a list of 

676 :class:`SnomedConcept` objects 

677 comment: comment for XML file 

678 """ 

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

680 root = ElementTree.Element(ROOT_TAG) 

681 comment_element = ElementTree.Comment(comment) 

682 root.insert(0, comment_element) 

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

684 l_el = ElementTree.SubElement( 

685 root, LOOKUP_TAG, {LOOKUP_NAME_ATTR: lookup} 

686 ) 

687 for concept in concepts_for_lookup: 

688 c_el = ElementTree.SubElement(l_el, CONCEPT_TAG) 

689 i_el = ElementTree.SubElement(c_el, ID_TAG) 

690 i_el.text = str(concept.identifier) 

691 n_el = ElementTree.SubElement(c_el, TERM_TAG) 

692 n_el.text = concept.term 

693 tree = ElementTree.ElementTree(root) 

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

695 tree.write(xml_filename) 

696 

697 

698# ============================================================================= 

699# SNOMED-CT concepts for CamCOPS tasks 

700# ============================================================================= 

701 

702# ----------------------------------------------------------------------------- 

703# CamCOPS lookup codes for SNOMED-CT concepts 

704# ----------------------------------------------------------------------------- 

705 

706 

707class SnomedLookup(object): 

708 """ 

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

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

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

712 install additional data. 

713 

714 - UK license: 

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

716 

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

718 

719 Abbreviations: 

720 

721 - "Finding" is not abbreviated 

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

723 - "Procedure" is not abbreviated 

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

725 - "Situation" is not abbreviated 

726 

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

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

729 

730 """ # noqa 

731 

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

733 

734 # ------------------------------------------------------------------------- 

735 # SNOMED-CT core concepts 

736 # ------------------------------------------------------------------------- 

737 

738 # Base concepts 

739 OBSERVABLE_ENTITY = "observable_entity" 

740 

741 # Abstract physical quantities 

742 MASS = "mass" 

743 LENGTH = "length" 

744 

745 # Saying "units" 

746 UNIT_OF_MEASURE = "unit_of_measure" 

747 

748 # Base physical units 

749 KILOGRAM = "kilogram" 

750 METRE = "metre" 

751 CENTIMETRE = "centimetre" 

752 

753 # Compound physical units 

754 KG_PER_SQ_M = "kilogram_per_square_metre" 

755 

756 # ------------------------------------------------------------------------- 

757 # Scales 

758 # ------------------------------------------------------------------------- 

759 

760 # ACE-R 

761 ACE_R_SCALE = "ace_r_scale" 

762 ACE_R_SUBSCALE_ATTENTION_ORIENTATION = ( 

763 "ace_r_subscale_attention_orientation" 

764 ) 

765 ACE_R_SUBSCALE_FLUENCY = "ace_r_subscale_fluency" 

766 ACE_R_SUBSCALE_LANGUAGE = "ace_r_subscale_language" 

767 ACE_R_SUBSCALE_MEMORY = "ace_r_subscale_memory" 

768 ACE_R_SUBSCALE_VISUOSPATIAL = "ace_r_subscale_visuospatial" 

769 ACE_R_SCORE = "ace_r_observable_score" 

770 ACE_R_SUBSCORE_ATTENTION_ORIENTATION = ( 

771 "ace_r_observable_subscore_attention_orientation" 

772 ) 

773 ACE_R_SUBSCORE_FLUENCY = "ace_r_observable_subscore_fluency" 

774 ACE_R_SUBSCORE_LANGUAGE = "ace_r_observable_subscore_language" 

775 ACE_R_SUBSCORE_MEMORY = "ace_r_observable_subscore_memory" 

776 ACE_R_SUBSCORE_VISUOSPATIAL = "ace_r_observable_subscore_visuospatial" 

777 ACE_R_PROCEDURE_ASSESSMENT = "ace_r_procedure_assessment" 

778 ACE_R_PROCEDURE_ASSESSMENT_SUBSCALE_ATTENTION_ORIENTATION = ( 

779 "ace_r_procedure_assessment_subscale_attention_orientation" 

780 ) 

781 ACE_R_PROCEDURE_ASSESSMENT_SUBSCALE_FLUENCY = ( 

782 "ace_r_procedure_assessment_subscale_fluency" 

783 ) 

784 ACE_R_PROCEDURE_ASSESSMENT_SUBSCALE_LANGUAGE = ( 

785 "ace_r_procedure_assessment_subscale_language" 

786 ) 

787 ACE_R_PROCEDURE_ASSESSMENT_SUBSCALE_MEMORY = ( 

788 "ace_r_procedure_assessment_subscale_memory" 

789 ) 

790 ACE_R_PROCEDURE_ASSESSMENT_SUBSCALE_VISUOSPATIAL = ( 

791 "ace_r_procedure_assessment_subscale_visuospatial" 

792 ) 

793 

794 # AIMS 

795 AIMS_SCALE = "aims_scale" 

796 AIMS_TOTAL_SCORE = "aims_observable_total_score" 

797 AIMS_PROCEDURE_ASSESSMENT = "aims_procedure_assessment" 

798 

799 # AUDIT, AUDIT-C 

800 AUDIT_SCALE = "audit_scale" 

801 AUDIT_SCORE = "audit_observable_score" 

802 AUDIT_PROCEDURE_ASSESSMENT = "audit_procedure_assessment" 

803 AUDITC_SCALE = "auditc_scale" 

804 AUDITC_SCORE = "auditc_observable_score" 

805 AUDITC_PROCEDURE_ASSESSMENT = "auditc_procedure_assessment" 

806 

807 # BADLS 

808 BADLS_SCALE = "badls_scale" 

809 BADLS_SCORE = "badls_observable_score" 

810 BADLS_PROCEDURE_ASSESSMENT = "badls_procedure_assessment" 

811 

812 # BDI 

813 BDI_SCALE = "bdi_scale" 

814 BDI_SCORE = "bdi_observable_score" 

815 BDI_PROCEDURE_ASSESSMENT = "bdi_procedure_assessment" 

816 BDI_II_SCORE = "bdi_ii_observable_score" 

817 BDI_II_PROCEDURE_ASSESSMENT = "bdi_ii_procedure_assessment" 

818 

819 # BMI 

820 BMI_OBSERVABLE = "bmi_observable" 

821 BMI_PROCEDURE_MEASUREMENT = "bmi_procedure_measurement" 

822 BODY_HEIGHT_OBSERVABLE = "body_height_observable" 

823 BODY_WEIGHT_OBSERVABLE = "body_weight_observable" 

824 WAIST_CIRCUMFERENCE_PROCEDURE_MEASUREMENT = ( 

825 "waist_circumference_procedure_measurement" 

826 ) 

827 WAIST_CIRCUMFERENCE_OBSERVABLE = "waist_circumference_observable" 

828 

829 # BPRS, BPRS-E 

830 BPRS1962_SCALE = "bprs1962_scale" 

831 # no observable/procedure 

832 

833 # CAGE 

834 CAGE_SCALE = "cage_scale" 

835 CAGE_SCORE = "cage_observable_score" 

836 CAGE_PROCEDURE_ASSESSMENT = "cage_procedure_assessment" 

837 

838 # CAPE-42: none 

839 # CAPS: none 

840 # Cardinal RN, Expectation-Detection: none 

841 # CBI-R: none 

842 # CECA-Q3: none 

843 # CESD, CESD-R: none 

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

845 # CIS-R: none 

846 

847 # CIWA-Ar 

848 CIWA_AR_SCALE = "ciwa_ar_scale" 

849 CIWA_AR_SCORE = "ciwa_ar_observable_score" 

850 CIWA_AR_PROCEDURE_ASSESSMENT = "ciwa_ar_procedure_assessment" 

851 

852 # Clinical: progress note 

853 PROGRESS_NOTE_PROCEDURE = "progress_note_procedure" 

854 CLINICAL_NOTE = "clinical_note" 

855 

856 # Clinical: photograph 

857 PHOTOGRAPH_PROCEDURE = "photograph_procedure" 

858 PHOTOGRAPH_PHYSICAL_OBJECT = "photograph_physical_object" 

859 

860 # Clinical: psychiatric clerking 

861 # ... "location": not obvious 

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

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

864 # 

865 # Deprecated between v20191001 and v20210929: 

866 # PSYCHIATRIC_ASSESSMENT_PROCEDURE = "psychiatric_assessment_procedure" 

867 DIAGNOSTIC_PSYCHIATRIC_INTERVIEW_PROCEDURE = ( 

868 "diagnostic_psychiatric_interview_procedure" 

869 ) 

870 

871 PSYCLERK_REASON_FOR_REFERRAL = "psyclerk_reason_for_referral" 

872 PSYCLERK_PRESENTING_ISSUE = "psyclerk_presenting_issue" 

873 PSYCLERK_SYSTEMS_REVIEW = "psyclerk_systems_review" 

874 PSYCLERK_COLLATERAL_HISTORY = "psyclerk_collateral_history" 

875 

876 PSYCLERK_PAST_MEDICAL_SURGICAL_MENTAL_HEALTH_HISTORY = ( 

877 "psyclerk_past_medical_surgical_mental_health_history" 

878 ) 

879 PSYCLERK_PROCEDURES = "psyclerk_procedures" 

880 PSYCLERK_ALLERGIES_ADVERSE_REACTIONS = ( 

881 "psyclerk_allergies_adverse_reactions" 

882 ) 

883 PSYCLERK_MEDICATIONS_MEDICAL_DEVICES = ( 

884 "psyclerk_medications_medical_devices" 

885 ) 

886 PSYCLERK_DRUG_SUBSTANCE_USE = "psyclerk_drug_substance_use" 

887 PSYCLERK_FAMILY_HISTORY = "psyclerk_family_history" 

888 PSYCLERK_DEVELOPMENTAL_HISTORY = "psyclerk_developmental_history" 

889 PSYCLERK_SOCIAL_PERSONAL_HISTORY = "psyclerk_social_personal_history" 

890 PSYCLERK_PERSONALITY = "psyclerk_personality" 

891 PSYCLERK_PRISON_RECORD_CRIMINAL_ACTIVITY = ( 

892 "psyclerk_prison_record_criminal_activity" 

893 ) 

894 PSYCLERK_SOCIAL_HISTORY_BASELINE = "psyclerk_social_history_baseline" 

895 

896 PSYCLERK_MSE_APPEARANCE = "psyclerk_mse_appearance" 

897 PSYCLERK_MSE_BEHAVIOUR = "psyclerk_mse_behaviour" 

898 PSYCLERK_MSE_SPEECH = "psyclerk_mse_speech" 

899 PSYCLERK_MSE_MOOD = "psyclerk_mse_mood" 

900 PSYCLERK_MSE_AFFECT = "psyclerk_mse_affect" 

901 PSYCLERK_MSE_THOUGHT = "psyclerk_mse_thought" 

902 PSYCLERK_MSE_PERCEPTION = "psyclerk_mse_perception" 

903 PSYCLERK_MSE_COGNITION = "psyclerk_mse_cognition" 

904 PSYCLERK_MSE_INSIGHT = "psyclerk_mse_insight" 

905 

906 PSYCLERK_PHYSEXAM_GENERAL = "psyclerk_physexam_general" 

907 PSYCLERK_PHYSEXAM_CARDIOVASCULAR = "psyclerk_physexam_cardiovascular" 

908 PSYCLERK_PHYSEXAM_RESPIRATORY = "psyclerk_physexam_respiratory" 

909 PSYCLERK_PHYSEXAM_ABDOMINAL = "psyclerk_physexam_abdominal" 

910 PSYCLERK_PHYSEXAM_NEUROLOGICAL = "psyclerk_physexam_neurological" 

911 

912 PSYCLERK_ASSESSMENT_SCALES = "psyclerk_assessment_scales" 

913 PSYCLERK_INVESTIGATIONS_RESULTS = "psyclerk_investigations_results" 

914 

915 PSYCLERK_SAFETY_ALERTS = "psyclerk_safety_alerts" 

916 PSYCLERK_RISK_ASSESSMENT = "psyclerk_risk_assessment" 

917 PSYCLERK_RELEVANT_LEGAL_INFORMATION = "psyclerk_relevant_legal_information" 

918 

919 PSYCLERK_CURRENT_PROBLEMS = "psyclerk_current_problems" 

920 PSYCLERK_PATIENT_CARER_CONCERNS = "psyclerk_patient_carer_concerns" 

921 PSYCLERK_CLINICAL_NARRATIVE = "psyclerk_clinical_narrative" 

922 PSYCLERK_MANAGEMENT_PLAN = "psyclerk_management_plan" 

923 PSYCLERK_INFORMATION_GIVEN = "psyclerk_information_given" 

924 

925 # CPFT/CUH LPS: none 

926 # COPE: none 

927 

928 # CORE-10 

929 CORE10_SCALE = "core10_scale" 

930 CORE10_SCORE = "core10_observable_score" 

931 CORE10_PROCEDURE_ASSESSMENT = "core10_procedure_assessment" 

932 

933 # DAD: none 

934 

935 # DAST 

936 DAST_SCALE = "dast_scale" 

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

938 

939 # Deakin JB, antibody: none 

940 # Demo questionnaire: none 

941 # DEMQOL, DEMQOL-Proxy: none 

942 # Distress Thermometer: none 

943 

944 # EQ-5D-5L 

945 EQ5D5L_SCALE = "eq5d5l_scale" 

946 EQ5D5L_INDEX_VALUE = "eq5d5l_observable_index_value" 

947 EQ5D5L_PAIN_DISCOMFORT_SCORE = "eq5d5l_observable_pain_discomfort_score" 

948 EQ5D5L_USUAL_ACTIVITIES_SCORE = "eq5d5l_observable_usual_activities_score" 

949 EQ5D5L_ANXIETY_DEPRESSION_SCORE = ( 

950 "eq5d5l_observable_anxiety_depression_score" 

951 ) 

952 EQ5D5L_MOBILITY_SCORE = "eq5d5l_observable_mobility_score" 

953 EQ5D5L_SELF_CARE_SCORE = "eq5d5l_observable_self_care_score" 

954 EQ5D5L_PROCEDURE_ASSESSMENT = "eq5d5l_procedure_assessment" 

955 

956 # FACT-G: none 

957 

958 # FAST: none 

959 FAST_SCALE = "fast_scale" 

960 FAST_SCORE = "fast_observable_score" 

961 FAST_PROCEDURE_ASSESSMENT = "fast_procedure_assessment" 

962 

963 # IRAC: none 

964 # FFT: none 

965 

966 # Patient Satisfaction Scale 

967 # Not sure. See XML 

968 

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

970 # Frontotemporal Dementia Rating Scale: none 

971 

972 # GAD-7 

973 GAD7_SCALE = "gad7_scale" 

974 GAD7_SCORE = "gad7_observable_score" 

975 GAD7_PROCEDURE_ASSESSMENT = "gad7_procedure_assessment" 

976 

977 # GAF 

978 GAF_SCALE = "gaf_scale" 

979 # no observable/procedure 

980 

981 # GDS-15 

982 GDS15_SCALE = "gds15_scale" 

983 GDS15_SCORE = "gds15_observable_score" 

984 GDS15_PROCEDURE_ASSESSMENT = "gds15_procedure_assessment" 

985 

986 # UK GMC Patient Questionnaire: none 

987 

988 # HADS, HADS-respondent 

989 HADS_SCALE = "hads_scale" 

990 HADS_ANXIETY_SCORE = "hads_observable_anxiety_score" 

991 HADS_DEPRESSION_SCORE = "hads_observable_depression_score" 

992 HADS_PROCEDURE_ASSESSMENT = "hads_procedure_assessment" 

993 

994 # HAMA: none 

995 

996 # HAMD 

997 HAMD_SCALE = "hamd_scale" 

998 HAMD_SCORE = "hamd_observable_score" 

999 HAMD_PROCEDURE_ASSESSMENT = "hamd_procedure_assessment" 

1000 

1001 # HAMD-7: none 

1002 

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

1004 HONOSCA_SCALE = "honos_childrenadolescents_scale" 

1005 HONOSCA_SECTION_A_SCALE = "honos_childrenadolescents_section_a_scale" 

1006 HONOSCA_SECTION_B_SCALE = "honos_childrenadolescents_section_b_scale" 

1007 HONOSCA_SCORE = "honos_childrenadolescents_observable_score" 

1008 HONOSCA_SECTION_A_SCORE = ( 

1009 "honos_childrenadolescents_observable_section_a_score" 

1010 ) 

1011 HONOSCA_SECTION_B_SCORE = ( 

1012 "honos_childrenadolescents_observable_section_b_score" 

1013 ) 

1014 HONOSCA_SECTION_A_PLUS_B_SCORE = ( 

1015 "honos_childrenadolescents_observable_section_a_plus_b_score" 

1016 ) 

1017 HONOSCA_PROCEDURE_ASSESSMENT = ( 

1018 "honos_childrenadolescents_procedure_assessment" 

1019 ) 

1020 # 

1021 HONOS65_SCALE = "honos_olderadults_scale" 

1022 HONOS65_SCORE = "honos_olderadults_observable_score" 

1023 HONOS65_PROCEDURE_ASSESSMENT = "honos_olderadults_procedure_assessment" 

1024 # 

1025 HONOSWA_SCALE = "honos_workingage_scale" 

1026 HONOSWA_SUBSCALE_1_OVERACTIVE = "honos_workingage_subscale_1_overactive" 

1027 HONOSWA_SUBSCALE_2_SELFINJURY = "honos_workingage_subscale_2_selfinjury" 

1028 HONOSWA_SUBSCALE_3_SUBSTANCE = "honos_workingage_subscale_3_substance" 

1029 HONOSWA_SUBSCALE_4_COGNITIVE = "honos_workingage_subscale_4_cognitive" 

1030 HONOSWA_SUBSCALE_5_PHYSICAL = "honos_workingage_subscale_5_physical" 

1031 HONOSWA_SUBSCALE_6_PSYCHOSIS = "honos_workingage_subscale_6_psychosis" 

1032 HONOSWA_SUBSCALE_7_DEPRESSION = "honos_workingage_subscale_7_depression" 

1033 HONOSWA_SUBSCALE_8_OTHERMENTAL = "honos_workingage_subscale_8_othermental" 

1034 HONOSWA_SUBSCALE_9_RELATIONSHIPS = ( 

1035 "honos_workingage_subscale_9_relationships" 

1036 ) 

1037 HONOSWA_SUBSCALE_10_ADL = "honos_workingage_subscale_10_adl" 

1038 HONOSWA_SUBSCALE_11_LIVINGCONDITIONS = ( 

1039 "honos_workingage_subscale_11_livingconditions" 

1040 ) 

1041 HONOSWA_SUBSCALE_12_OCCUPATION = "honos_workingage_subscale_12_occupation" 

1042 HONOSWA_SCORE = "honos_workingage_observable_score" 

1043 HONOSWA_1_OVERACTIVE_SCORE = ( 

1044 "honos_workingage_observable_1_overactive_score" 

1045 ) 

1046 HONOSWA_2_SELFINJURY_SCORE = ( 

1047 "honos_workingage_observable_2_selfinjury_score" 

1048 ) 

1049 HONOSWA_3_SUBSTANCE_SCORE = "honos_workingage_observable_3_substance_score" 

1050 HONOSWA_4_COGNITIVE_SCORE = "honos_workingage_observable_4_cognitive_score" 

1051 HONOSWA_5_PHYSICAL_SCORE = "honos_workingage_observable_5_physical_score" 

1052 HONOSWA_6_PSYCHOSIS_SCORE = "honos_workingage_observable_6_psychosis_score" 

1053 HONOSWA_7_DEPRESSION_SCORE = ( 

1054 "honos_workingage_observable_7_depression_score" 

1055 ) 

1056 HONOSWA_8_OTHERMENTAL_SCORE = ( 

1057 "honos_workingage_observable_8_othermental_score" 

1058 ) 

1059 HONOSWA_9_RELATIONSHIPS_SCORE = ( 

1060 "honos_workingage_observable_9_relationships_score" 

1061 ) 

1062 HONOSWA_10_ADL_SCORE = "honos_workingage_observable_10_adl_score" 

1063 HONOSWA_11_LIVINGCONDITIONS_SCORE = ( 

1064 "honos_workingage_observable_11_livingconditions_score" 

1065 ) 

1066 HONOSWA_12_OCCUPATION_SCORE = ( 

1067 "honos_workingage_observable_12_occupation_score" 

1068 ) 

1069 HONOSWA_PROCEDURE_ASSESSMENT = "honos_workingage_procedure_assessment" 

1070 

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

1072 

1073 # ICD-10: see below; separate file 

1074 

1075 # IDED3D: none 

1076 

1077 # IES-R 

1078 IESR_SCALE = "iesr_scale" 

1079 IESR_SCORE = "iesr_observable_score" 

1080 IESR_PROCEDURE_ASSESSMENT = "iesr_procedure_assessment" 

1081 

1082 # INECO Frontal Screening: none 

1083 # Khandaker G, Insight: none 

1084 

1085 # MAST, SMAST 

1086 MAST_SCALE = "mast_scale" 

1087 MAST_SCORE = "mast_observable_score" 

1088 MAST_PROCEDURE_ASSESSMENT = "mast_procedure_assessment" 

1089 

1090 SMAST_SCALE = "smast_scale" 

1091 # SMAST: no observable/procedure 

1092 

1093 # MDS-UPDRS 

1094 UPDRS_SCALE = "updrs_scale" 

1095 # MDS-UPDRS: no observable/procedure 

1096 

1097 # MoCA 

1098 MOCA_SCALE = "moca_scale" 

1099 MOCA_SCORE = "moca_observable_score" 

1100 MOCA_PROCEDURE_ASSESSMENT = "moca_procedure_assessment" 

1101 

1102 # NART 

1103 NART_SCALE = "nart_scale" 

1104 NART_SCORE = "nart_observable_score" 

1105 NART_PROCEDURE_ASSESSMENT = "nart_procedure_assessment" 

1106 

1107 # NPI-Q: none 

1108 

1109 # PANSS 

1110 PANSS_SCALE = "panss_scale" 

1111 # PANSS: no observable/procedure 

1112 

1113 # PCL and variants: none 

1114 

1115 # PDSS 

1116 PDSS_SCALE = "pdss_scale" 

1117 PDSS_SCORE = "pdss_observable_score" 

1118 # PDSS: no procedure 

1119 

1120 # "Perinatal" and "scale": none 

1121 

1122 # PHQ-9 

1123 PHQ9_FINDING_NEGATIVE_SCREENING_FOR_DEPRESSION = ( 

1124 "phq9_finding_negative_screening_for_depression" 

1125 ) 

1126 PHQ9_FINDING_POSITIVE_SCREENING_FOR_DEPRESSION = ( 

1127 "phq9_finding_positive_screening_for_depression" 

1128 ) 

1129 PHQ9_SCORE = "phq9_observable_score" 

1130 PHQ9_PROCEDURE_DEPRESSION_SCREENING = "phq9_procedure_depression_screening" 

1131 PHQ9_SCALE = ( 

1132 "phq9_scale" # https://snomedbrowser.com/Codes/Details/758711000000105 

1133 ) 

1134 

1135 # PHQ-15 

1136 PHQ15_SCORE = "phq15_observable_score" 

1137 PHQ15_PROCEDURE = "phq15_procedure_assessment" 

1138 PHQ15_SCALE = "phq15_scale" 

1139 

1140 # PIIOS or "parent infant scale": none 

1141 

1142 # PSWQ 

1143 PSWQ_SCALE = "pswq_scale" 

1144 PSWQ_SCORE = "pswq_observable_score" 

1145 PSWQ_PROCEDURE_ASSESSMENT = "pswq_procedure_assessment" 

1146 

1147 # QOL-Basic, QOL-SG 

1148 # Unsure, but there is this: 

1149 QOL_SCALE = "qol_scale" 

1150 # ... with no observable/procedure 

1151 

1152 # RAND-36: none directly 

1153 

1154 # SLUMS: none 

1155 

1156 # WEMWBS, SWEMWBS 

1157 WEMWBS_SCALE = "wemwbs_scale" 

1158 WEMWBS_SCORE = "wemwbs_observable_score" 

1159 WEMWBS_PROCEDURE_ASSESSMENT = "wemwbs_procedure_assessment" 

1160 # 

1161 SWEMWBS_SCALE = "swemwbs_scale" 

1162 SWEMWBS_SCORE = "swemwbs_observable_score" 

1163 SWEMWBS_PROCEDURE_ASSESSMENT = "swemwbs_procedure_assessment" 

1164 

1165 # WSAS 

1166 WSAS_SCALE = "wsas_scale" 

1167 WSAS_SCORE = "wsas_observable_score" 

1168 WSAS_WORK_SCORE = "wsas_observable_work_score" 

1169 WSAS_RELATIONSHIPS_SCORE = "wsas_observable_relationships_score" 

1170 WSAS_HOME_MANAGEMENT_SCORE = "wsas_observable_home_management_score" 

1171 WSAS_SOCIAL_LEISURE_SCORE = "wsas_observable_social_leisure_score" 

1172 WSAS_PRIVATE_LEISURE_SCORE = "wsas_observable_private_leisure_score" 

1173 WSAS_PROCEDURE_ASSESSMENT = "wsas_procedure_assessment" 

1174 

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

1176 # ZBI: none 

1177 

1178 

1179# ----------------------------------------------------------------------------- 

1180# Perform the lookup 

1181# ----------------------------------------------------------------------------- 

1182 

1183VALID_SNOMED_LOOKUPS = set( 

1184 [ 

1185 getattr(SnomedLookup, k) 

1186 for k in dir(SnomedLookup) 

1187 if not k.startswith("_") 

1188 ] 

1189) 

1190 

1191 

1192@cache_region_static.cache_on_arguments(function_key_generator=fkg) 

1193def get_all_task_snomed_concepts( 

1194 xml_filename: str, 

1195) -> Dict[str, SnomedConcept]: 

1196 """ 

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

1198 file for this. 

1199 

1200 Args: 

1201 xml_filename: XML filename to read 

1202 

1203 Returns: 

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

1205 

1206 """ 

1207 xml_concepts = get_snomed_concepts_from_xml(xml_filename) 

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

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

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

1211 # Check it 

1212 if lookup not in VALID_SNOMED_LOOKUPS: 

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

1214 continue 

1215 assert ( 

1216 len(concepts) == 1 

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

1218 concept = concepts[0] 

1219 assert ( 

1220 concept.identifier not in identifiers_seen 

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

1222 identifiers_seen.add(concept.identifier) 

1223 # Stash it 

1224 camcops_concepts[lookup] = concept 

1225 # Check if any are missing 

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

1227 if missing: 

1228 raise ValueError( 

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

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

1231 ) 

1232 # Done 

1233 return camcops_concepts 

1234 

1235 

1236# ============================================================================= 

1237# UMLS ICD-9-CM 

1238# ============================================================================= 

1239 

1240 

1241class UmlsIcd9SnomedRow(object): 

1242 """ 

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

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

1245 

1246 NOT CURRENTLY USED. 

1247 """ 

1248 

1249 HEADER = [ 

1250 "ICD_CODE", 

1251 "ICD_NAME", 

1252 "IS_CURRENT_ICD", 

1253 "IP_USAGE", 

1254 "OP_USAGE", 

1255 "AVG_USAGE", 

1256 "IS_NEC", 

1257 "SNOMED_CID", 

1258 "SNOMED_FSN", 

1259 "IS_1-1MAP", 

1260 "CORE_USAGE", 

1261 "IN_CORE", 

1262 ] 

1263 

1264 @staticmethod 

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

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

1267 

1268 def __init__( 

1269 self, 

1270 icd_code: str, 

1271 icd_name: str, 

1272 is_current_icd: str, 

1273 ip_usage: str, 

1274 op_usage: str, 

1275 avg_usage: str, 

1276 is_nec: str, 

1277 snomed_cid: str, 

1278 snomed_fsn: str, 

1279 is_one_to_one_map: str, 

1280 core_usage: str, 

1281 in_core: str, 

1282 ) -> None: 

1283 """ 

1284 Argument order is important. 

1285 

1286 Args: 

1287 icd_code: ICD-9-CM code 

1288 icd_name: Name of ICD-9-CM entity 

1289 is_current_icd: ? 

1290 ip_usage: ? 

1291 op_usage: ? 

1292 avg_usage: ? 

1293 is_nec: ? 

1294 snomed_cid: SNOMED-CT concept ID 

1295 snomed_fsn: SNOMED-CT fully specified name 

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

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

1298 mappings? 

1299 core_usage: ? 

1300 in_core: ? 

1301 """ 

1302 self.icd_code = icd_code 

1303 self.icd_name = icd_name 

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

1305 self.ip_usage = self.to_float(ip_usage) 

1306 self.op_usage = self.to_float(op_usage) 

1307 self.avg_usage = self.to_float(avg_usage) 

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

1309 self.snomed_cid = int(snomed_cid) 

1310 self.snomed_fsn = snomed_fsn 

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

1312 self.core_usage = self.to_float(core_usage) 

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

1314 

1315 def __repr__(self) -> str: 

1316 return simple_repr( 

1317 self, 

1318 [ 

1319 "icd_code", 

1320 "icd_name", 

1321 "is_current_icd", 

1322 "ip_usage", 

1323 "op_usage", 

1324 "avg_usage", 

1325 "is_nec", 

1326 "snomed_cid", 

1327 "snomed_fsn", 

1328 "is_one_to_one_map", 

1329 "core_usage", 

1330 "in_core", 

1331 ], 

1332 ) 

1333 

1334 def snomed_concept(self) -> SnomedConcept: 

1335 """ 

1336 Returns the associated SNOMED-CT concept. 

1337 """ 

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

1339 

1340 def __str__(self) -> str: 

1341 return ( 

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

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

1344 ) 

1345 

1346 

1347@cache_region_static.cache_on_arguments(function_key_generator=fkg) 

1348def get_all_icd9cm_snomed_concepts_from_umls( 

1349 tsv_filename: str, 

1350) -> Dict[str, SnomedConcept]: 

1351 """ 

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

1353 from the UMLS data file, from 

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

1355 

1356 Args: 

1357 tsv_filename: TSV filename to read 

1358 

1359 Returns: 

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

1361 

1362 NOT CURRENTLY USED. 

1363 """ 

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

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

1366 with open(tsv_filename, "r") as tsvin: 

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

1368 header = next(reader, None) 

1369 if header != UmlsIcd9SnomedRow.HEADER: 

1370 raise ValueError( 

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

1372 f"expected {UmlsIcd9SnomedRow.HEADER!r}" 

1373 ) 

1374 for row in reader: 

1375 entry = UmlsIcd9SnomedRow(*row) 

1376 if entry.icd_code not in CLIENT_ICD9CM_CODES: 

1377 continue 

1378 if not entry.is_one_to_one_map: 

1379 continue 

1380 if entry.icd_code in concepts: 

1381 log.warning( 

1382 "Duplicate ICD-9-CM code found in SNOMED file " 

1383 "{!r}: {!r}", 

1384 tsv_filename, 

1385 entry.icd_code, 

1386 ) 

1387 continue 

1388 concept = entry.snomed_concept() 

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

1390 concepts[entry.icd_code] = concept 

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

1392 if missing: 

1393 log.info( 

1394 "No SNOMED-CT codes for ICD-9-CM codes: {}", 

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

1396 ) 

1397 return concepts 

1398 

1399 

1400# ============================================================================= 

1401# UMLS ICD-10 

1402# ============================================================================= 

1403 

1404 

1405class UmlsSnomedToIcd10Row(object): 

1406 """ 

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

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

1409 

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

1411 

1412 NOT CURRENTLY USED. 

1413 """ 

1414 

1415 HEADER = [ 

1416 "id", 

1417 "effectiveTime", 

1418 "active", 

1419 "moduleId", 

1420 "refsetId", 

1421 "referencedComponentId", 

1422 "referencedComponentName", 

1423 "mapGroup", 

1424 "mapPriority", 

1425 "mapRule", 

1426 "mapAdvice", 

1427 "mapTarget", 

1428 "mapTargetName", 

1429 "correlationId", 

1430 "mapCategoryId", 

1431 "mapCategoryName", 

1432 ] 

1433 MAP_GOOD = "MAP SOURCE CONCEPT IS PROPERLY CLASSIFIED" 

1434 

1435 def __init__( 

1436 self, 

1437 row_id: str, 

1438 effective_time: str, 

1439 active: str, 

1440 module_id: str, 

1441 refset_id: str, 

1442 referenced_component_id: str, 

1443 referenced_component_name: str, 

1444 map_group: str, 

1445 map_priority: str, 

1446 map_rule: str, 

1447 map_advice: str, 

1448 map_target: str, 

1449 map_target_name: str, 

1450 correlation_id: str, 

1451 map_category_id: str, 

1452 map_category_name: str, 

1453 ) -> None: 

1454 """ 

1455 Argument order is important. 

1456 

1457 Args: 

1458 row_id: UUID format or similar 

1459 effective_time: date in YYYYMMDD format 

1460 active: ? 

1461 module_id: ? 

1462 refset_id: ? 

1463 referenced_component_id: SNOMED-CT concept ID 

1464 referenced_component_name: SNOMED-CT concept name 

1465 map_group: ?; e.g. 1 

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

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

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

1469 DESCENDANTS NOT EXHAUSTIVELY MAPPED" 

1470 map_target: ICD-10 code 

1471 map_target_name: ICD-10 name 

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

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

1474 correlation not specified (foundation metadata concept)" 

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

1476 447637006 = "Map source concept is properly classified 

1477 (foundation metadata concept)" 

1478 map_category_name: SNOMED-CT name corresponding to map_category_id, 

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

1480 """ 

1481 self.row_id = row_id 

1482 self.effective_time = effective_time 

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

1484 self.module_id = int(module_id) 

1485 self.refset_id = int(refset_id) 

1486 self.referenced_component_id = int(referenced_component_id) 

1487 self.referenced_component_name = referenced_component_name 

1488 self.map_group = int(map_group) 

1489 self.map_priority = int(map_priority) 

1490 self.map_rule = map_rule 

1491 self.map_advice = map_advice 

1492 self.map_target = map_target 

1493 self.map_target_name = map_target_name 

1494 self.correlation_id = int(correlation_id) 

1495 self.map_category_id = int(map_category_id) 

1496 self.map_category_name = map_category_name 

1497 

1498 def __repr__(self) -> str: 

1499 return simple_repr( 

1500 self, 

1501 [ 

1502 "row_id", 

1503 "effective_time", 

1504 "active", 

1505 "module_id", 

1506 "refset_id", 

1507 "referenced_component_id", 

1508 "referenced_component_name", 

1509 "map_group", 

1510 "map_priority", 

1511 "map_rule", 

1512 "map_advice", 

1513 "map_target", 

1514 "map_target_name", 

1515 "correlation_id", 

1516 "map_category_id", 

1517 "map_category_name", 

1518 ], 

1519 ) 

1520 

1521 def snomed_concept(self) -> SnomedConcept: 

1522 """ 

1523 Returns the associated SNOMED-CT concept. 

1524 """ 

1525 return SnomedConcept( 

1526 self.referenced_component_id, self.referenced_component_name 

1527 ) 

1528 

1529 @property 

1530 def icd_code(self) -> str: 

1531 return self.map_target 

1532 

1533 @property 

1534 def icd_name(self) -> str: 

1535 return self.map_target_name 

1536 

1537 def __str__(self) -> str: 

1538 return ( 

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

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

1541 ) 

1542 

1543 

1544def get_all_icd10_snomed_concepts_from_umls( 

1545 tsv_filename: str, 

1546) -> Dict[str, SnomedConcept]: 

1547 """ 

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

1549 from the UMLS data file, from 

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

1551 

1552 Args: 

1553 tsv_filename: TSV filename to read 

1554 

1555 Returns: 

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

1557 

1558 NOT CURRENTLY USED. 

1559 """ 

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

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

1562 with open(tsv_filename, "r") as tsvin: 

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

1564 header = next(reader, None) 

1565 if header != UmlsSnomedToIcd10Row.HEADER: 

1566 raise ValueError( 

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

1568 f"expected {UmlsSnomedToIcd10Row.HEADER!r}" 

1569 ) 

1570 for row in reader: 

1571 entry = UmlsSnomedToIcd10Row(*row) 

1572 if entry.icd_code not in CLIENT_ICD10_CODES: 

1573 continue 

1574 if entry.icd_code in concepts: 

1575 log.warning( 

1576 "Duplicate ICD-10-CM code found in SNOMED file " 

1577 "{!r}: {!r}", 

1578 tsv_filename, 

1579 entry.icd_code, 

1580 ) 

1581 continue 

1582 concept = entry.snomed_concept() 

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

1584 concepts[entry.icd_code] = concept 

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

1586 if missing: 

1587 log.info( 

1588 "No SNOMED-CT codes for ICD-10 codes: {}", 

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

1590 ) 

1591 return concepts 

1592 

1593 

1594# ============================================================================= 

1595# Athena OHDSI mapping 

1596# ============================================================================= 

1597 

1598# ----------------------------------------------------------------------------- 

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

1600# ----------------------------------------------------------------------------- 

1601 

1602 

1603def get_icd9cm_icd10_snomed_concepts_from_athena( 

1604 athena_concept_tsv_filename: str, 

1605 athena_concept_relationship_tsv_filename: str, 

1606) -> Tuple[Dict[str, List[SnomedConcept]], Dict[str, List[SnomedConcept]]]: 

1607 """ 

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

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

1610 

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

1612 gensim or some other automatic similarity check. 

1613 

1614 Args: 

1615 athena_concept_tsv_filename: 

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

1617 athena_concept_relationship_tsv_filename: 

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

1619 

1620 Returns: 

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

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

1623 

1624 """ 

1625 athena_icd_concepts = get_athena_concepts( 

1626 tsv_filename=athena_concept_tsv_filename, 

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

1628 concept_codes=CLIENT_ICD9CM_CODES | CLIENT_ICD10_CODES, 

1629 ) 

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

1631 relationships = get_athena_concept_relationships( 

1632 tsv_filename=athena_concept_relationship_tsv_filename, 

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

1634 relationship_id_values={AthenaRelationshipId.MAPS_TO}, 

1635 ) 

1636 athena_snomed_concepts = get_athena_concepts( 

1637 tsv_filename=athena_concept_tsv_filename, 

1638 vocabulary_ids={AthenaVocabularyId.SNOMED}, 

1639 concept_ids=set(x.concept_id_2 for x in relationships), 

1640 ) 

1641 snomed_concepts_icd9 = ( 

1642 OrderedDict() 

1643 ) # type: Dict[str, List[SnomedConcept]] # noqa 

1644 snomed_concepts_icd10 = ( 

1645 OrderedDict() 

1646 ) # type: Dict[str, List[SnomedConcept]] # noqa 

1647 for icd in athena_icd_concepts: 

1648 target = ( 

1649 snomed_concepts_icd9 

1650 if icd.vocabulary_id == AthenaVocabularyId.ICD9CM 

1651 else snomed_concepts_icd10 

1652 ) 

1653 icd_code = icd.concept_code 

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

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

1656 for rel in relationships: 

1657 if rel.concept_id_1 != icd.concept_id: 

1658 continue 

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

1660 for snomed in athena_snomed_concepts: 

1661 if snomed.concept_id != rel.concept_id_2: 

1662 continue 

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

1664 possible_snomed.append(snomed) 

1665 if possible_snomed: 

1666 sclist = [ 

1667 SnomedConcept.create(s.snomed_concept()) 

1668 for s in possible_snomed 

1669 ] 

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

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

1672 target[icd_code] = sclist 

1673 else: 

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

1675 return snomed_concepts_icd9, snomed_concepts_icd10 

1676 

1677 

1678# ----------------------------------------------------------------------------- 

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

1680# ----------------------------------------------------------------------------- 

1681 

1682 

1683def send_athena_icd_snomed_to_xml( 

1684 athena_concept_tsv_filename: str, 

1685 athena_concept_relationship_tsv_filename: str, 

1686 icd9_xml_filename: str, 

1687 icd10_xml_filename, 

1688) -> None: 

1689 """ 

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

1691 writes 

1692 

1693 Args: 

1694 athena_concept_tsv_filename: 

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

1696 athena_concept_relationship_tsv_filename: 

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

1698 icd9_xml_filename: 

1699 ICD-9 XML filename to write 

1700 icd10_xml_filename: 

1701 ICD-10 XML filename to write 

1702 """ 

1703 icd9, icd10 = get_icd9cm_icd10_snomed_concepts_from_athena( 

1704 athena_concept_tsv_filename=athena_concept_tsv_filename, 

1705 athena_concept_relationship_tsv_filename=athena_concept_relationship_tsv_filename, # noqa 

1706 ) 

1707 write_snomed_concepts_to_xml(icd9_xml_filename, icd9) 

1708 write_snomed_concepts_to_xml(icd10_xml_filename, icd10) 

1709 

1710 

1711# ----------------------------------------------------------------------------- 

1712# Fetch data from XML 

1713# ----------------------------------------------------------------------------- 

1714 

1715 

1716def get_multiple_snomed_concepts_from_xml( 

1717 xml_filename: str, 

1718 valid_lookups: Set[str] = None, 

1719 require_all: bool = False, 

1720) -> Dict[str, List[SnomedConcept]]: 

1721 """ 

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

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

1724 

1725 Args: 

1726 xml_filename: XML filename to read 

1727 valid_lookups: possible lookup values 

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

1729 values in it are present in the XML 

1730 

1731 Returns: 

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

1733 

1734 """ 

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

1736 xml_concepts = get_snomed_concepts_from_xml(xml_filename) 

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

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

1739 # Check it 

1740 if valid_lookups and lookup not in valid_lookups: 

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

1742 continue 

1743 # Stash it 

1744 camcops_concepts[lookup] = concepts 

1745 if require_all: 

1746 # Check if any are missing 

1747 assert valid_lookups, "require_all specified but valid_lookups missing" 

1748 if valid_lookups: 

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

1750 if missing: 

1751 msg = ( 

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

1753 f"SNOMED-CT concepts are missing from the XML " 

1754 f"({xml_filename!r}): {missing!r}" 

1755 ) 

1756 if require_all: 

1757 log.critical(msg) 

1758 raise ValueError(msg) 

1759 else: 

1760 log.debug(msg) 

1761 # Done 

1762 return camcops_concepts 

1763 

1764 

1765@cache_region_static.cache_on_arguments(function_key_generator=fkg) 

1766def get_icd9_snomed_concepts_from_xml( 

1767 xml_filename: str, 

1768) -> Dict[str, List[SnomedConcept]]: 

1769 """ 

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

1771 

1772 Args: 

1773 xml_filename: filename to read 

1774 

1775 Returns: 

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

1777 """ 

1778 return get_multiple_snomed_concepts_from_xml( 

1779 xml_filename, CLIENT_ICD9CM_CODES 

1780 ) 

1781 

1782 

1783@cache_region_static.cache_on_arguments(function_key_generator=fkg) 

1784def get_icd10_snomed_concepts_from_xml( 

1785 xml_filename: str, 

1786) -> Dict[str, List[SnomedConcept]]: 

1787 """ 

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

1789 

1790 Args: 

1791 xml_filename: filename to read 

1792 

1793 Returns: 

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

1795 """ 

1796 return get_multiple_snomed_concepts_from_xml( 

1797 xml_filename, CLIENT_ICD10_CODES 

1798 )