simple-icd-10-cm 1.2.0__py3-none-any.whl → 1.4.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,3 +1,8 @@
1
+ # simple_icd_10_cm is released under the MIT License
2
+ # Copyright (c) 2021-2025 Stefano Travasci
3
+ # Read the full LICENCES at https://github.com/StefanoTrv/simple_icd_10_cm/blob/master/LICENSE
4
+
5
+ from typing import Optional
1
6
  import xml.etree.ElementTree as ET
2
7
 
3
8
  try:
@@ -8,18 +13,21 @@ except ImportError:
8
13
 
9
14
  from . import data # relative-import the "package" containing the data
10
15
 
11
- chapter_list: list["_CodeTree"] = []
16
+ _chapter_list: list["_CodeTree"] = []
17
+
18
+ _code_to_node: dict[str, "_CodeTree"] = {}
12
19
 
13
- code_to_node: dict[str, "_CodeTree"] = {}
20
+ _all_codes_list: list[str] = []
14
21
 
15
- all_codes_list: list[str] = []
22
+ _all_codes_list_no_dots: list[str] = []
16
23
 
17
- all_codes_list_no_dots: list[str] = []
24
+ _code_to_index_dictionary: dict[str, int] = {}
18
25
 
19
- code_to_index_dictionary: dict[str, int] = {}
26
+ _all_codes_package_file_name:str = 'code-list-April-2025.txt'
27
+ _classification_data_package_file_name:str = 'icd10cm-tabular-April-2025.xml'
20
28
 
21
29
  class _CodeTree:
22
- def __init__(self, tree, parent = None, seven_chr_def_ancestor = None, seven_chr_note_ancestor = None, use_additional_code_ancestor = None, code_first_ancestor = None):
30
+ def __init__(self, tree, parent = None, seven_chr_def_ancestor = None, seven_chr_note_ancestor = None, use_additional_code_ancestor = None, code_first_ancestor = None, code_also_ancestor = None, notes_ancestor = None):
23
31
  #initialize all the values
24
32
  self.name = ""
25
33
  self.description = ""
@@ -38,17 +46,24 @@ class _CodeTree:
38
46
  self.use_additional_code_ancestor = use_additional_code_ancestor
39
47
  self.code_first = ""
40
48
  self.code_first_ancestor = code_first_ancestor
49
+ self.code_also = ""
50
+ self.code_also_ancestor = code_also_ancestor
51
+ self.notes = ""
52
+ self.notes_ancestor = notes_ancestor
41
53
 
42
54
  #reads the data from the subtrees
43
55
  new_seven_chr_def_ancestor=seven_chr_def_ancestor
44
56
  new_seven_chr_note_ancestor=seven_chr_note_ancestor
45
57
  new_use_additional_code_ancestor=use_additional_code_ancestor
46
58
  new_code_first_ancestor=code_first_ancestor
59
+ new_code_also_ancestor=code_also_ancestor
60
+ new_notes_ancestor=notes_ancestor
47
61
  if "id" in tree.attrib: #the name of sections is an attribute instead of text inside an XML element
48
62
  self.name=tree.attrib["id"]
49
63
  for subtree in tree:
50
64
  if subtree.tag=="section" or subtree.tag=="diag": #creates a new child for this node
51
- self.children.append(_CodeTree(subtree,parent=self,seven_chr_def_ancestor=new_seven_chr_def_ancestor,seven_chr_note_ancestor=new_seven_chr_note_ancestor,use_additional_code_ancestor=new_use_additional_code_ancestor,code_first_ancestor=new_code_first_ancestor))
65
+ # This is only correct because the XML elements for the children codes always follow the XML elements for this code's data
66
+ self.children.append(_CodeTree(subtree,parent=self,seven_chr_def_ancestor=new_seven_chr_def_ancestor,seven_chr_note_ancestor=new_seven_chr_note_ancestor,use_additional_code_ancestor=new_use_additional_code_ancestor,code_first_ancestor=new_code_first_ancestor,code_also_ancestor=new_code_also_ancestor,notes_ancestor=new_notes_ancestor))
52
67
  elif subtree.tag=="name":
53
68
  self.name=subtree.text
54
69
  elif subtree.tag=="desc":
@@ -78,6 +93,7 @@ class _CodeTree:
78
93
  self.seven_chr_note=subtree[0].text
79
94
  new_seven_chr_note_ancestor=self
80
95
  elif subtree.tag=="useAdditionalCode":
96
+ # NOTE: multiple useAdditionalCode elements may be present, so self.use_additional_code should always be appended and never overwritten
81
97
  for i in range(0,len(subtree)):#in case there are multiple lines
82
98
  self.use_additional_code=self.use_additional_code+"\n"+subtree[i].text
83
99
  new_use_additional_code_ancestor=self
@@ -85,12 +101,26 @@ class _CodeTree:
85
101
  for i in range(0,len(subtree)):#in case there are multiple lines
86
102
  self.code_first=self.code_first+"\n"+subtree[i].text
87
103
  new_code_first_ancestor=self
104
+ elif subtree.tag=="codeAlso":
105
+ # see NOTE for useAdditionalCode
106
+ for i in range(0,len(subtree)):#in case there are multiple lines
107
+ self.code_also=self.code_also+"\n"+subtree[i].text
108
+ new_code_also_ancestor=self
109
+ elif subtree.tag=="notes":
110
+ # see NOTE for useAdditionalCode
111
+ for i in range(0,len(subtree)):#in case there are multiple lines
112
+ self.notes=self.notes+"\n"+subtree[i].text
113
+ new_notes_ancestor=self
88
114
 
89
- #cleans the use_additional_code and code_first fields from extra new lines
115
+ #cleans the use_additional_code, code_first, code_also and notes fields from extra new lines
90
116
  if self.use_additional_code!="" and self.use_additional_code[0]=="\n":
91
117
  self.use_additional_code=self.use_additional_code[1:]
92
118
  if self.code_first!="" and self.code_first[0]=="\n":
93
119
  self.code_first=self.code_first[1:]
120
+ if self.code_also!="" and self.code_also[0]=="\n":
121
+ self.code_also=self.code_also[1:]
122
+ if self.notes!="" and self.notes[0]=="\n":
123
+ self.notes=self.notes[1:]
94
124
 
95
125
  #sets the type
96
126
  if tree.tag=="chapter":
@@ -105,15 +135,15 @@ class _CodeTree:
105
135
  self.type="subcategory"
106
136
 
107
137
  #adds the new node to the dictionary
108
- if self.name not in code_to_node:#in case a section has the same name of a code (ex B99)
109
- code_to_node[self.name]=self
138
+ if self.name not in _code_to_node:#in case a section has the same name of a code (ex B99)
139
+ _code_to_node[self.name]=self
110
140
 
111
141
  #if this code is a leaf, it adds to its children the codes created by adding the seventh character
112
142
  if len(self.children)==0 and (self.seven_chr_def!={} or self.seven_chr_def_ancestor!=None) and self.type!="extended subcategory":
113
143
  if self.seven_chr_def!={}:
114
144
  dictionary = self.seven_chr_def
115
145
  else:
116
- dictionary = self.seven_chr_def_ancestor.seven_chr_def
146
+ dictionary = self.seven_chr_def_ancestor.seven_chr_def # type: ignore[reportOptionalMemberAccess]
117
147
  extended_name=self.name
118
148
  if len(extended_name)==3:
119
149
  extended_name=extended_name+"."
@@ -122,130 +152,156 @@ class _CodeTree:
122
152
  for extension in dictionary:
123
153
  if((extended_name[:3]+extended_name[4:]+extension) in all_confirmed_codes):#checks if there's a special rule that excludes this new code
124
154
  new_XML = "<diag_ext><name>"+extended_name+extension+"</name><desc>"+self.description+", "+dictionary[extension]+"</desc></diag_ext>"
125
- self.children.append(_CodeTree(ET.fromstring(new_XML),parent=self,seven_chr_def_ancestor=new_seven_chr_def_ancestor,seven_chr_note_ancestor=new_seven_chr_note_ancestor,use_additional_code_ancestor=new_use_additional_code_ancestor,code_first_ancestor=new_code_first_ancestor))
155
+ self.children.append(_CodeTree(ET.fromstring(new_XML),parent=self,seven_chr_def_ancestor=new_seven_chr_def_ancestor,seven_chr_note_ancestor=new_seven_chr_note_ancestor,use_additional_code_ancestor=new_use_additional_code_ancestor,code_first_ancestor=new_code_first_ancestor,code_also_ancestor=new_code_also_ancestor,notes_ancestor=new_notes_ancestor))
126
156
 
127
- def _load_codes():
157
+ def _load_codes(all_codes_file_path:Optional[str] = None, classification_data_file_path:Optional[str] = None) -> None: # either both or none of the strings must be None
128
158
  #loads the list of all codes, to remove later from the tree the ones that do not exist for very specific rules not easily extracted from the XML file
129
- f = pkg_resources.read_text(data, 'icd10cm-order-Jan-2021.txt')
159
+ if all_codes_file_path is None:
160
+ assert classification_data_file_path is None
161
+ text = pkg_resources.read_text(data, _all_codes_package_file_name)
162
+ else:
163
+ assert classification_data_file_path is not None
164
+ with open(all_codes_file_path, encoding="utf-8") as f:
165
+ text = f.read()
130
166
  global all_confirmed_codes
131
167
  all_confirmed_codes = set()
132
- lines=f.split("\n")
168
+ lines=text.split("\n")
133
169
  for line in lines:
134
- all_confirmed_codes.add(line[6:13].strip())
170
+ all_confirmed_codes.add(line.split(" ")[0].replace(".",""))
135
171
 
136
172
  #creates the tree
137
- root = ET.fromstring(pkg_resources.read_text(data, 'icd10cm_tabular_2021.xml'))
173
+ if classification_data_file_path is None:
174
+ root = ET.fromstring(pkg_resources.read_text(data, _classification_data_package_file_name))
175
+ else:
176
+ with open(classification_data_file_path, encoding="utf-8") as f:
177
+ text = f.read()
178
+ root = ET.fromstring(text)
138
179
  root.remove(root[0])
139
180
  root.remove(root[0])
181
+ if len(_chapter_list)>0: #empties data structures only if needed and if the files have been read with no error
182
+ _chapter_list.clear()
183
+ _code_to_node.clear()
184
+ _all_codes_list.clear()
185
+ _all_codes_list_no_dots.clear()
186
+ _code_to_index_dictionary.clear()
140
187
  for child in root:
141
- chapter_list.append(_CodeTree(child))
188
+ _chapter_list.append(_CodeTree(child))
142
189
 
143
190
  del all_confirmed_codes #deletes this list since it won't be needed anymore
144
191
 
145
192
 
146
193
  _load_codes()
147
194
 
148
- def _add_dot_to_code(code):
195
+ def change_version(all_codes_file_path:Optional[str] = None, classification_data_file_path:Optional[str] = None) -> None:
196
+ if (all_codes_file_path is None and classification_data_file_path is None) or (all_codes_file_path is not None and classification_data_file_path is not None):
197
+ _load_codes(all_codes_file_path=all_codes_file_path,classification_data_file_path=classification_data_file_path)
198
+ else:
199
+ ac_error = "None" if all_codes_file_path is None else "\"" + all_codes_file_path + "\""
200
+ cd_error = "None" if classification_data_file_path is None else "\"" + classification_data_file_path + "\""
201
+ raise ValueError("Either both paths must be provided, or none.\nProvided values were:\n\t-all_codes_file_path is "+ac_error+"\n\t-classification_data_file_path is "+cd_error+"\n")
202
+
203
+ def _add_dot_to_code(code:str) -> str:
149
204
  if len(code)<4 or code[3]==".":
150
205
  return code
151
- elif code[:3]+"."+code[3:] in code_to_node:
206
+ elif code[:3]+"."+code[3:] in _code_to_node:
152
207
  return code[:3]+"."+code[3:]
153
208
  else:
154
209
  return code
155
210
 
156
- def is_valid_item(code: str) -> bool:
157
- return code in code_to_node or len(code)>=4 and code[:3]+"."+code[3:] in code_to_node
211
+ def is_valid_item(code:str) -> bool:
212
+ return code in _code_to_node or len(code)>=4 and code[:3]+"."+code[3:] in _code_to_node
158
213
 
159
- def is_chapter(code: str) -> bool:
214
+ def is_chapter(code:str) -> bool:
160
215
  code = _add_dot_to_code(code)
161
- if code in code_to_node:
162
- return code_to_node[code].type=="chapter"
216
+ if code in _code_to_node:
217
+ return _code_to_node[code].type=="chapter"
163
218
  else:
164
219
  return False
165
220
 
166
- def is_block(code: str) -> bool:
221
+ def is_block(code:str) -> bool:
167
222
  code = _add_dot_to_code(code)
168
- if code in code_to_node:
169
- return code_to_node[code].type=="section" or code_to_node[code].parent!=None and code_to_node[code].parent.name==code #second half of the or is for sections containing a single category
223
+ if code in _code_to_node:
224
+ #second half of the or is for sections containing a single category
225
+ return _code_to_node[code].type=="section" or _code_to_node[code].parent!=None and _code_to_node[code].parent.name==code # type: ignore[reportOptionalMemberAccess]
170
226
  else:
171
227
  return False
172
228
 
173
- def is_category(code: str) -> bool:
229
+ def is_category(code:str) -> bool:
174
230
  code = _add_dot_to_code(code)
175
- if code in code_to_node:
176
- return code_to_node[code].type=="category"
231
+ if code in _code_to_node:
232
+ return _code_to_node[code].type=="category"
177
233
  else:
178
234
  return False
179
235
 
180
- def is_subcategory(code: str, include_extended_subcategories=True) -> bool:
236
+ def is_subcategory(code:str, include_extended_subcategories:bool=True) -> bool:
181
237
  code = _add_dot_to_code(code)
182
- if code in code_to_node:
183
- return code_to_node[code].type=="subcategory" or code_to_node[code].type=="extended subcategory" and include_extended_subcategories
238
+ if code in _code_to_node:
239
+ return _code_to_node[code].type=="subcategory" or _code_to_node[code].type=="extended subcategory" and include_extended_subcategories
184
240
  else:
185
241
  return False
186
242
 
187
- def is_extended_subcategory(code: str) -> bool:
243
+ def is_extended_subcategory(code:str) -> bool:
188
244
  code = _add_dot_to_code(code)
189
- if code in code_to_node:
190
- return code_to_node[code].type=="extended subcategory"
245
+ if code in _code_to_node:
246
+ return _code_to_node[code].type=="extended subcategory"
191
247
  else:
192
248
  return False
193
249
 
194
- def is_category_or_subcategory(code: str) -> bool:
250
+ def is_category_or_subcategory(code:str) -> bool:
195
251
  return is_subcategory(code) or is_category(code)
196
252
 
197
- def is_chapter_or_block(code: str) -> bool:
253
+ def is_chapter_or_block(code:str) -> bool:
198
254
  return is_block(code) or is_chapter(code)
199
255
 
200
- def get_description(code: str, prioritize_blocks=False) -> str:
256
+ def get_description(code:str, prioritize_blocks:bool=False) -> str:
201
257
  if not is_valid_item(code):
202
258
  raise ValueError("The code \""+code+"\" does not exist.")
203
- node = code_to_node[_add_dot_to_code(code)]
259
+ node = _code_to_node[_add_dot_to_code(code)]
204
260
  if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
205
261
  return node.parent.description
206
262
  else:
207
263
  return node.description
208
264
 
209
- def get_excludes1(code: str, prioritize_blocks=False) -> list[str]:
265
+ def get_excludes1(code:str, prioritize_blocks:bool=False) -> list[str]:
210
266
  if not is_valid_item(code):
211
267
  raise ValueError("The code \""+code+"\" does not exist.")
212
- node = code_to_node[_add_dot_to_code(code)]
268
+ node = _code_to_node[_add_dot_to_code(code)]
213
269
  if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
214
270
  return node.parent.excludes1.copy()
215
271
  else:
216
272
  return node.excludes1.copy()
217
273
 
218
- def get_excludes2(code: str, prioritize_blocks=False) -> list[str]:
274
+ def get_excludes2(code:str, prioritize_blocks:bool=False) -> list[str]:
219
275
  if not is_valid_item(code):
220
276
  raise ValueError("The code \""+code+"\" does not exist.")
221
- node = code_to_node[_add_dot_to_code(code)]
277
+ node = _code_to_node[_add_dot_to_code(code)]
222
278
  if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
223
279
  return node.parent.excludes2.copy()
224
280
  else:
225
281
  return node.excludes2.copy()
226
282
 
227
- def get_includes(code: str, prioritize_blocks=False) -> list[str]:
283
+ def get_includes(code:str, prioritize_blocks:bool=False) -> list[str]:
228
284
  if not is_valid_item(code):
229
285
  raise ValueError("The code \""+code+"\" does not exist.")
230
- node = code_to_node[_add_dot_to_code(code)]
286
+ node = _code_to_node[_add_dot_to_code(code)]
231
287
  if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
232
288
  return node.parent.includes.copy()
233
289
  else:
234
290
  return node.includes.copy()
235
291
 
236
- def get_inclusion_term(code: str, prioritize_blocks=False) -> list[str]:
292
+ def get_inclusion_term(code:str, prioritize_blocks:bool=False) -> list[str]:
237
293
  if not is_valid_item(code):
238
294
  raise ValueError("The code \""+code+"\" does not exist.")
239
- node = code_to_node[_add_dot_to_code(code)]
295
+ node = _code_to_node[_add_dot_to_code(code)]
240
296
  if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
241
297
  return node.parent.inclusion_term.copy()
242
298
  else:
243
299
  return node.inclusion_term.copy()
244
300
 
245
- def get_seven_chr_def(code: str, search_in_ancestors=False, prioritize_blocks=False) -> dict[str, str]:
301
+ def get_seven_chr_def(code:str, search_in_ancestors:bool=False, prioritize_blocks:bool=False) -> dict[str,str]:
246
302
  if not is_valid_item(code):
247
303
  raise ValueError("The code \""+code+"\" does not exist.")
248
- node = code_to_node[_add_dot_to_code(code)]
304
+ node = _code_to_node[_add_dot_to_code(code)]
249
305
  if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
250
306
  node = node.parent
251
307
  res = node.seven_chr_def.copy()
@@ -254,10 +310,10 @@ def get_seven_chr_def(code: str, search_in_ancestors=False, prioritize_blocks=Fa
254
310
  else:
255
311
  return res
256
312
 
257
- def get_seven_chr_note(code: str, search_in_ancestors=False, prioritize_blocks=False) -> str:
313
+ def get_seven_chr_note(code:str, search_in_ancestors:bool=False, prioritize_blocks:bool=False) -> str:
258
314
  if not is_valid_item(code):
259
315
  raise ValueError("The code \""+code+"\" does not exist.")
260
- node = code_to_node[_add_dot_to_code(code)]
316
+ node = _code_to_node[_add_dot_to_code(code)]
261
317
  if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
262
318
  node = node.parent
263
319
  res = node.seven_chr_note
@@ -266,10 +322,10 @@ def get_seven_chr_note(code: str, search_in_ancestors=False, prioritize_blocks=F
266
322
  else:
267
323
  return res
268
324
 
269
- def get_use_additional_code(code: str, search_in_ancestors=False, prioritize_blocks=False) -> str:
325
+ def get_use_additional_code(code:str, search_in_ancestors:bool=False, prioritize_blocks:bool=False) -> str:
270
326
  if not is_valid_item(code):
271
327
  raise ValueError("The code \""+code+"\" does not exist.")
272
- node = code_to_node[_add_dot_to_code(code)]
328
+ node = _code_to_node[_add_dot_to_code(code)]
273
329
  if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
274
330
  node = node.parent
275
331
  res = node.use_additional_code
@@ -278,10 +334,10 @@ def get_use_additional_code(code: str, search_in_ancestors=False, prioritize_blo
278
334
  else:
279
335
  return res
280
336
 
281
- def get_code_first(code: str, search_in_ancestors=False, prioritize_blocks=False) -> str:
337
+ def get_code_first(code:str, search_in_ancestors:bool=False, prioritize_blocks:bool=False) -> str:
282
338
  if not is_valid_item(code):
283
339
  raise ValueError("The code \""+code+"\" does not exist.")
284
- node = code_to_node[_add_dot_to_code(code)]
340
+ node = _code_to_node[_add_dot_to_code(code)]
285
341
  if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
286
342
  node = node.parent
287
343
  res = node.code_first
@@ -290,10 +346,34 @@ def get_code_first(code: str, search_in_ancestors=False, prioritize_blocks=False
290
346
  else:
291
347
  return res
292
348
 
293
- def get_parent(code: str, prioritize_blocks=False) -> str:
349
+ def get_code_also(code:str, search_in_ancestors:bool=False, prioritize_blocks:bool=False) -> str:
350
+ if not is_valid_item(code):
351
+ raise ValueError("The code \""+code+"\" does not exist.")
352
+ node = _code_to_node[_add_dot_to_code(code)]
353
+ if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
354
+ node = node.parent
355
+ res = node.code_also
356
+ if search_in_ancestors and res=="" and node.code_also_ancestor!=None:
357
+ return node.code_also_ancestor.code_also
358
+ else:
359
+ return res
360
+
361
+ def get_notes(code:str, search_in_ancestors:bool=False, prioritize_blocks:bool=False) -> str:
362
+ if not is_valid_item(code):
363
+ raise ValueError("The code \""+code+"\" does not exist.")
364
+ node = _code_to_node[_add_dot_to_code(code)]
365
+ if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
366
+ node = node.parent
367
+ res = node.notes
368
+ if search_in_ancestors and res=="" and node.notes_ancestor!=None:
369
+ return node.notes_ancestor.notes
370
+ else:
371
+ return res
372
+
373
+ def get_parent(code:str, prioritize_blocks:bool=False) -> str:
294
374
  if not is_valid_item(code):
295
375
  raise ValueError("The code \""+code+"\" does not exist.")
296
- node = code_to_node[_add_dot_to_code(code)]
376
+ node = _code_to_node[_add_dot_to_code(code)]
297
377
  if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
298
378
  node = node.parent
299
379
  if node.parent!=None:
@@ -301,10 +381,10 @@ def get_parent(code: str, prioritize_blocks=False) -> str:
301
381
  else:
302
382
  return ""
303
383
 
304
- def get_children(code: str, prioritize_blocks=False) -> list[str]:
384
+ def get_children(code:str, prioritize_blocks:bool=False) -> list[str]:
305
385
  if not is_valid_item(code):
306
386
  raise ValueError("The code \""+code+"\" does not exist.")
307
- node = code_to_node[_add_dot_to_code(code)]
387
+ node = _code_to_node[_add_dot_to_code(code)]
308
388
  if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
309
389
  node = node.parent
310
390
  res = []
@@ -312,18 +392,18 @@ def get_children(code: str, prioritize_blocks=False) -> list[str]:
312
392
  res.append(child.name)
313
393
  return res
314
394
 
315
- def is_leaf(code: str, prioritize_blocks=False) -> bool:
395
+ def is_leaf(code:str, prioritize_blocks:bool=False) -> bool:
316
396
  if not is_valid_item(code):
317
397
  raise ValueError("The code \""+code+"\" does not exist.")
318
- node = code_to_node[_add_dot_to_code(code)]
398
+ node = _code_to_node[_add_dot_to_code(code)]
319
399
  if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
320
400
  node = node.parent
321
401
  return len(node.children)==0
322
402
 
323
- def get_full_data(code: str, search_in_ancestors=False, prioritize_blocks=False) -> str:
403
+ def get_full_data(code:str, search_in_ancestors:bool=False, prioritize_blocks:bool=False) -> str:
324
404
  if not is_valid_item(code):
325
405
  raise ValueError("The code \""+code+"\" does not exist.")
326
- node = code_to_node[_add_dot_to_code(code)]
406
+ node = _code_to_node[_add_dot_to_code(code)]
327
407
  if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
328
408
  node = node.parent
329
409
  str = "Name:\n"+node.name+"\nDescription:\n"+node.description
@@ -359,6 +439,12 @@ def get_full_data(code: str, search_in_ancestors=False, prioritize_blocks=False)
359
439
  code_first=get_code_first(code,search_in_ancestors=search_in_ancestors,prioritize_blocks=prioritize_blocks)
360
440
  if code_first!="":
361
441
  str = str + "\ncode first:\n" + code_first
442
+ code_also=get_code_also(code,search_in_ancestors=search_in_ancestors,prioritize_blocks=prioritize_blocks)
443
+ if code_also!="":
444
+ str = str + "\ncode also:\n" + code_also
445
+ notes=get_notes(code,search_in_ancestors=search_in_ancestors,prioritize_blocks=prioritize_blocks)
446
+ if notes!="":
447
+ str = str + "\nnotes:\n" + notes
362
448
  if node.children==[]:
363
449
  str = str + "\nChildren:\nNone--"
364
450
  else:
@@ -367,10 +453,10 @@ def get_full_data(code: str, search_in_ancestors=False, prioritize_blocks=False)
367
453
  str = str + child.name + ", "
368
454
  return str[:-2]
369
455
 
370
- def get_ancestors(code: str,prioritize_blocks=False) -> list[str]:
456
+ def get_ancestors(code:str,prioritize_blocks:bool=False) -> list[str]:
371
457
  if not is_valid_item(code):
372
458
  raise ValueError("The code \""+code+"\" does not exist.")
373
- node = code_to_node[_add_dot_to_code(code)]
459
+ node = _code_to_node[_add_dot_to_code(code)]
374
460
  if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
375
461
  node = node.parent
376
462
  result = []
@@ -379,33 +465,33 @@ def get_ancestors(code: str,prioritize_blocks=False) -> list[str]:
379
465
  node=node.parent
380
466
  return result
381
467
 
382
- def get_descendants(code: str,prioritize_blocks=False) -> list[str]:
468
+ def get_descendants(code:str,prioritize_blocks:bool=False) -> list[str]:
383
469
  if not is_valid_item(code):
384
470
  raise ValueError("The code \""+code+"\" does not exist.")
385
- node = code_to_node[_add_dot_to_code(code)]
471
+ node = _code_to_node[_add_dot_to_code(code)]
386
472
  if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
387
473
  node = node.parent
388
474
  result = []
389
475
  _add_children_to_list(node, result)
390
476
  return result
391
477
 
392
- def _add_children_to_list(node, list):
478
+ def _add_children_to_list(node:_CodeTree, list:list[str]) -> None:
393
479
  for child in node.children:
394
480
  list.append(child.name)
395
481
  _add_children_to_list(child,list)
396
482
 
397
- def is_ancestor(a:str,b:str,prioritize_blocks_a=False,prioritize_blocks_b=False) -> bool:
483
+ def is_ancestor(a:str,b:str,prioritize_blocks_a:bool=False,prioritize_blocks_b:bool=False) -> bool:
398
484
  if not is_valid_item(a):
399
485
  raise ValueError("The code \""+a+"\" does not exist.")
400
- node = code_to_node[_add_dot_to_code(a)]
486
+ node = _code_to_node[_add_dot_to_code(a)]
401
487
  if prioritize_blocks_a and node.parent!=None and node.parent.name==node.name:
402
488
  node = node.parent
403
489
  return a in get_ancestors(b, prioritize_blocks=prioritize_blocks_b) and (a!=b or prioritize_blocks_a)
404
490
 
405
- def is_descendant(a:str,b:str,prioritize_blocks_a=False,prioritize_blocks_b=False) -> bool:
491
+ def is_descendant(a:str,b:str,prioritize_blocks_a:bool=False,prioritize_blocks_b:bool=False) -> bool:
406
492
  return is_ancestor(b,a,prioritize_blocks_a=prioritize_blocks_b,prioritize_blocks_b=prioritize_blocks_a)
407
493
 
408
- def get_nearest_common_ancestor(a:str,b:str,prioritize_blocks_a=False,prioritize_blocks_b=False) -> str:
494
+ def get_nearest_common_ancestor(a:str,b:str,prioritize_blocks_a:bool=False,prioritize_blocks_b:bool=False) -> str:
409
495
  anc_a = [_add_dot_to_code(a)] + get_ancestors(a, prioritize_blocks=prioritize_blocks_a)
410
496
  anc_b = [_add_dot_to_code(b)] + get_ancestors(b, prioritize_blocks=prioritize_blocks_b)
411
497
  if len(anc_b) > len(anc_a):
@@ -417,50 +503,50 @@ def get_nearest_common_ancestor(a:str,b:str,prioritize_blocks_a=False,prioritize
417
503
  return anc
418
504
  return ""
419
505
 
420
- def get_all_codes(with_dots=True) -> list[str]:
421
- if all_codes_list==[]:
422
- for chapter in chapter_list:
423
- _add_tree_to_list(chapter)
506
+ def get_all_codes(with_dots:bool=True) -> list[str]:
507
+ if _all_codes_list==[]:
508
+ _fill_all_codes_list()
424
509
  if with_dots:
425
- return all_codes_list.copy()
510
+ return _all_codes_list.copy()
426
511
  else:
427
- return all_codes_list_no_dots.copy()
512
+ return _all_codes_list_no_dots.copy()
513
+
514
+ def _fill_all_codes_list() -> None:
515
+ for chapter in _chapter_list:
516
+ _add_tree_to_list(chapter)
428
517
 
429
- def _add_tree_to_list(tree):
430
- all_codes_list.append(tree.name)
518
+ def _add_tree_to_list(tree:_CodeTree) -> None:
519
+ _all_codes_list.append(tree.name)
431
520
  if(len(tree.name)>4 and tree.name[3]=="."):
432
- all_codes_list_no_dots.append(tree.name[:3]+tree.name[4:])
521
+ _all_codes_list_no_dots.append(tree.name[:3]+tree.name[4:])
433
522
  else:
434
- all_codes_list_no_dots.append(tree.name)
523
+ _all_codes_list_no_dots.append(tree.name)
435
524
  for child in tree.children:
436
525
  _add_tree_to_list(child)
437
526
 
438
- def get_index(code: str) -> int:
527
+ def get_index(code:str) -> int: # type: ignore[reportReturnType]
439
528
  if not is_valid_item(code):
440
529
  raise ValueError("The code \""+code+"\" does not exist.")
441
530
  code = _add_dot_to_code(code)
442
- if all_codes_list==[]:
443
- for chapter in chapter_list:
444
- _add_tree_to_list(chapter)
445
- if code in code_to_index_dictionary:
446
- return code_to_index_dictionary[code]
531
+ if _all_codes_list==[]:
532
+ _fill_all_codes_list()
533
+ if code in _code_to_index_dictionary:
534
+ return _code_to_index_dictionary[code]
447
535
  else:
448
536
  i=0
449
- for c in all_codes_list:
537
+ for c in _all_codes_list:
450
538
  if c==code:
451
- code_to_index_dictionary[code]=i
539
+ _code_to_index_dictionary[code]=i
452
540
  return i
453
541
  else:
454
542
  i=i+1
455
543
 
456
- def remove_dot(code: str) -> str:
457
- if all_codes_list==[]:
458
- for chapter in chapter_list:
459
- _add_tree_to_list(chapter)
460
- return all_codes_list_no_dots[get_index(code)]
461
-
462
- def add_dot(code: str) -> str:
463
- if all_codes_list==[]:
464
- for chapter in chapter_list:
465
- _add_tree_to_list(chapter)
466
- return all_codes_list[get_index(code)]
544
+ def remove_dot(code:str) -> str:
545
+ if _all_codes_list==[]:
546
+ _fill_all_codes_list()
547
+ return _all_codes_list_no_dots[get_index(code)]
548
+
549
+ def add_dot(code:str) -> str:
550
+ if _all_codes_list==[]:
551
+ _fill_all_codes_list()
552
+ return _all_codes_list[get_index(code)]