simple-icd-10-cm 1.1.2__py3-none-any.whl → 1.3.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.
- simple_icd_10_cm/data/code-list-April-2025.txt +97902 -0
- simple_icd_10_cm/data/{icd10cm_tabular_2021.xml → icd10cm-tabular-April-2025.xml} +9970 -959
- simple_icd_10_cm/simple_icd_10_cm.py +133 -99
- {simple_icd_10_cm-1.1.2.dist-info → simple_icd_10_cm-1.3.0.dist-info}/METADATA +498 -451
- simple_icd_10_cm-1.3.0.dist-info/RECORD +10 -0
- {simple_icd_10_cm-1.1.2.dist-info → simple_icd_10_cm-1.3.0.dist-info}/WHEEL +1 -1
- {simple_icd_10_cm-1.1.2.dist-info → simple_icd_10_cm-1.3.0.dist-info/licenses}/LICENSE +1 -1
- simple_icd_10_cm/data/icd10cm-order-Jan-2021.txt +0 -95358
- simple_icd_10_cm-1.1.2.dist-info/RECORD +0 -10
- {simple_icd_10_cm-1.1.2.dist-info → simple_icd_10_cm-1.3.0.dist-info}/top_level.txt +0 -0
|
@@ -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,15 +13,18 @@ except ImportError:
|
|
|
8
13
|
|
|
9
14
|
from . import data # relative-import the "package" containing the data
|
|
10
15
|
|
|
11
|
-
|
|
16
|
+
_chapter_list: list["_CodeTree"] = []
|
|
17
|
+
|
|
18
|
+
_code_to_node: dict[str, "_CodeTree"] = {}
|
|
12
19
|
|
|
13
|
-
|
|
20
|
+
_all_codes_list: list[str] = []
|
|
14
21
|
|
|
15
|
-
|
|
22
|
+
_all_codes_list_no_dots: list[str] = []
|
|
16
23
|
|
|
17
|
-
|
|
24
|
+
_code_to_index_dictionary: dict[str, int] = {}
|
|
18
25
|
|
|
19
|
-
|
|
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
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):
|
|
@@ -105,15 +113,15 @@ class _CodeTree:
|
|
|
105
113
|
self.type="subcategory"
|
|
106
114
|
|
|
107
115
|
#adds the new node to the dictionary
|
|
108
|
-
if self.name not in
|
|
109
|
-
|
|
116
|
+
if self.name not in _code_to_node:#in case a section has the same name of a code (ex B99)
|
|
117
|
+
_code_to_node[self.name]=self
|
|
110
118
|
|
|
111
119
|
#if this code is a leaf, it adds to its children the codes created by adding the seventh character
|
|
112
120
|
if len(self.children)==0 and (self.seven_chr_def!={} or self.seven_chr_def_ancestor!=None) and self.type!="extended subcategory":
|
|
113
121
|
if self.seven_chr_def!={}:
|
|
114
122
|
dictionary = self.seven_chr_def
|
|
115
123
|
else:
|
|
116
|
-
dictionary = self.seven_chr_def_ancestor.seven_chr_def
|
|
124
|
+
dictionary = self.seven_chr_def_ancestor.seven_chr_def # type: ignore[reportOptionalMemberAccess]
|
|
117
125
|
extended_name=self.name
|
|
118
126
|
if len(extended_name)==3:
|
|
119
127
|
extended_name=extended_name+"."
|
|
@@ -124,128 +132,154 @@ class _CodeTree:
|
|
|
124
132
|
new_XML = "<diag_ext><name>"+extended_name+extension+"</name><desc>"+self.description+", "+dictionary[extension]+"</desc></diag_ext>"
|
|
125
133
|
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))
|
|
126
134
|
|
|
127
|
-
def _load_codes():
|
|
135
|
+
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
136
|
#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
|
-
|
|
137
|
+
if all_codes_file_path is None:
|
|
138
|
+
assert classification_data_file_path is None
|
|
139
|
+
text = pkg_resources.read_text(data, _all_codes_package_file_name)
|
|
140
|
+
else:
|
|
141
|
+
assert classification_data_file_path is not None
|
|
142
|
+
with open(all_codes_file_path, encoding="utf-8") as f:
|
|
143
|
+
text = f.read()
|
|
130
144
|
global all_confirmed_codes
|
|
131
145
|
all_confirmed_codes = set()
|
|
132
|
-
lines=
|
|
146
|
+
lines=text.split("\n")
|
|
133
147
|
for line in lines:
|
|
134
|
-
all_confirmed_codes.add(line[
|
|
148
|
+
all_confirmed_codes.add(line.split(" ")[0].replace(".",""))
|
|
135
149
|
|
|
136
150
|
#creates the tree
|
|
137
|
-
|
|
151
|
+
if classification_data_file_path is None:
|
|
152
|
+
root = ET.fromstring(pkg_resources.read_text(data, _classification_data_package_file_name))
|
|
153
|
+
else:
|
|
154
|
+
with open(classification_data_file_path, encoding="utf-8") as f:
|
|
155
|
+
text = f.read()
|
|
156
|
+
root = ET.fromstring(text)
|
|
138
157
|
root.remove(root[0])
|
|
139
158
|
root.remove(root[0])
|
|
159
|
+
if len(_chapter_list)>0: #empties data structures only if needed and if the files have been read with no error
|
|
160
|
+
_chapter_list.clear()
|
|
161
|
+
_code_to_node.clear()
|
|
162
|
+
_all_codes_list.clear()
|
|
163
|
+
_all_codes_list_no_dots.clear()
|
|
164
|
+
_code_to_index_dictionary.clear()
|
|
140
165
|
for child in root:
|
|
141
|
-
|
|
166
|
+
_chapter_list.append(_CodeTree(child))
|
|
142
167
|
|
|
143
168
|
del all_confirmed_codes #deletes this list since it won't be needed anymore
|
|
144
169
|
|
|
145
170
|
|
|
146
171
|
_load_codes()
|
|
147
172
|
|
|
148
|
-
def
|
|
173
|
+
def change_version(all_codes_file_path:Optional[str] = None, classification_data_file_path:Optional[str] = None) -> None:
|
|
174
|
+
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):
|
|
175
|
+
_load_codes(all_codes_file_path=all_codes_file_path,classification_data_file_path=classification_data_file_path)
|
|
176
|
+
else:
|
|
177
|
+
ac_error = "None" if all_codes_file_path is None else "\"" + all_codes_file_path + "\""
|
|
178
|
+
cd_error = "None" if classification_data_file_path is None else "\"" + classification_data_file_path + "\""
|
|
179
|
+
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")
|
|
180
|
+
|
|
181
|
+
def _add_dot_to_code(code:str) -> str:
|
|
149
182
|
if len(code)<4 or code[3]==".":
|
|
150
183
|
return code
|
|
151
|
-
elif code[:3]+"."+code[3:] in
|
|
184
|
+
elif code[:3]+"."+code[3:] in _code_to_node:
|
|
152
185
|
return code[:3]+"."+code[3:]
|
|
153
186
|
else:
|
|
154
187
|
return code
|
|
155
188
|
|
|
156
|
-
def is_valid_item(code):
|
|
157
|
-
return code in
|
|
189
|
+
def is_valid_item(code:str) -> bool:
|
|
190
|
+
return code in _code_to_node or len(code)>=4 and code[:3]+"."+code[3:] in _code_to_node
|
|
158
191
|
|
|
159
|
-
def is_chapter(code):
|
|
192
|
+
def is_chapter(code:str) -> bool:
|
|
160
193
|
code = _add_dot_to_code(code)
|
|
161
|
-
if code in
|
|
162
|
-
return
|
|
194
|
+
if code in _code_to_node:
|
|
195
|
+
return _code_to_node[code].type=="chapter"
|
|
163
196
|
else:
|
|
164
197
|
return False
|
|
165
198
|
|
|
166
|
-
def is_block(code):
|
|
199
|
+
def is_block(code:str) -> bool:
|
|
167
200
|
code = _add_dot_to_code(code)
|
|
168
|
-
if code in
|
|
169
|
-
|
|
201
|
+
if code in _code_to_node:
|
|
202
|
+
#second half of the or is for sections containing a single category
|
|
203
|
+
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
204
|
else:
|
|
171
205
|
return False
|
|
172
206
|
|
|
173
|
-
def is_category(code):
|
|
207
|
+
def is_category(code:str) -> bool:
|
|
174
208
|
code = _add_dot_to_code(code)
|
|
175
|
-
if code in
|
|
176
|
-
return
|
|
209
|
+
if code in _code_to_node:
|
|
210
|
+
return _code_to_node[code].type=="category"
|
|
177
211
|
else:
|
|
178
212
|
return False
|
|
179
213
|
|
|
180
|
-
def is_subcategory(code, include_extended_subcategories=True):
|
|
214
|
+
def is_subcategory(code:str, include_extended_subcategories:bool=True) -> bool:
|
|
181
215
|
code = _add_dot_to_code(code)
|
|
182
|
-
if code in
|
|
183
|
-
return
|
|
216
|
+
if code in _code_to_node:
|
|
217
|
+
return _code_to_node[code].type=="subcategory" or _code_to_node[code].type=="extended subcategory" and include_extended_subcategories
|
|
184
218
|
else:
|
|
185
219
|
return False
|
|
186
220
|
|
|
187
|
-
def is_extended_subcategory(code):
|
|
221
|
+
def is_extended_subcategory(code:str) -> bool:
|
|
188
222
|
code = _add_dot_to_code(code)
|
|
189
|
-
if code in
|
|
190
|
-
return
|
|
223
|
+
if code in _code_to_node:
|
|
224
|
+
return _code_to_node[code].type=="extended subcategory"
|
|
191
225
|
else:
|
|
192
226
|
return False
|
|
193
227
|
|
|
194
|
-
def is_category_or_subcategory(code):
|
|
228
|
+
def is_category_or_subcategory(code:str) -> bool:
|
|
195
229
|
return is_subcategory(code) or is_category(code)
|
|
196
230
|
|
|
197
|
-
def is_chapter_or_block(code):
|
|
231
|
+
def is_chapter_or_block(code:str) -> bool:
|
|
198
232
|
return is_block(code) or is_chapter(code)
|
|
199
233
|
|
|
200
|
-
def get_description(code, prioritize_blocks=False):
|
|
234
|
+
def get_description(code:str, prioritize_blocks:bool=False) -> str:
|
|
201
235
|
if not is_valid_item(code):
|
|
202
236
|
raise ValueError("The code \""+code+"\" does not exist.")
|
|
203
|
-
node =
|
|
237
|
+
node = _code_to_node[_add_dot_to_code(code)]
|
|
204
238
|
if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
|
|
205
239
|
return node.parent.description
|
|
206
240
|
else:
|
|
207
241
|
return node.description
|
|
208
242
|
|
|
209
|
-
def get_excludes1(code, prioritize_blocks=False):
|
|
243
|
+
def get_excludes1(code:str, prioritize_blocks:bool=False) -> list[str]:
|
|
210
244
|
if not is_valid_item(code):
|
|
211
245
|
raise ValueError("The code \""+code+"\" does not exist.")
|
|
212
|
-
node =
|
|
246
|
+
node = _code_to_node[_add_dot_to_code(code)]
|
|
213
247
|
if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
|
|
214
248
|
return node.parent.excludes1.copy()
|
|
215
249
|
else:
|
|
216
250
|
return node.excludes1.copy()
|
|
217
251
|
|
|
218
|
-
def get_excludes2(code, prioritize_blocks=False):
|
|
252
|
+
def get_excludes2(code:str, prioritize_blocks:bool=False) -> list[str]:
|
|
219
253
|
if not is_valid_item(code):
|
|
220
254
|
raise ValueError("The code \""+code+"\" does not exist.")
|
|
221
|
-
node =
|
|
255
|
+
node = _code_to_node[_add_dot_to_code(code)]
|
|
222
256
|
if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
|
|
223
257
|
return node.parent.excludes2.copy()
|
|
224
258
|
else:
|
|
225
259
|
return node.excludes2.copy()
|
|
226
260
|
|
|
227
|
-
def get_includes(code, prioritize_blocks=False):
|
|
261
|
+
def get_includes(code:str, prioritize_blocks:bool=False) -> list[str]:
|
|
228
262
|
if not is_valid_item(code):
|
|
229
263
|
raise ValueError("The code \""+code+"\" does not exist.")
|
|
230
|
-
node =
|
|
264
|
+
node = _code_to_node[_add_dot_to_code(code)]
|
|
231
265
|
if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
|
|
232
266
|
return node.parent.includes.copy()
|
|
233
267
|
else:
|
|
234
268
|
return node.includes.copy()
|
|
235
269
|
|
|
236
|
-
def get_inclusion_term(code, prioritize_blocks=False):
|
|
270
|
+
def get_inclusion_term(code:str, prioritize_blocks:bool=False) -> list[str]:
|
|
237
271
|
if not is_valid_item(code):
|
|
238
272
|
raise ValueError("The code \""+code+"\" does not exist.")
|
|
239
|
-
node =
|
|
273
|
+
node = _code_to_node[_add_dot_to_code(code)]
|
|
240
274
|
if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
|
|
241
275
|
return node.parent.inclusion_term.copy()
|
|
242
276
|
else:
|
|
243
277
|
return node.inclusion_term.copy()
|
|
244
278
|
|
|
245
|
-
def get_seven_chr_def(code, search_in_ancestors=False, prioritize_blocks=False):
|
|
279
|
+
def get_seven_chr_def(code:str, search_in_ancestors:bool=False, prioritize_blocks:bool=False) -> dict[str,str]:
|
|
246
280
|
if not is_valid_item(code):
|
|
247
281
|
raise ValueError("The code \""+code+"\" does not exist.")
|
|
248
|
-
node =
|
|
282
|
+
node = _code_to_node[_add_dot_to_code(code)]
|
|
249
283
|
if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
|
|
250
284
|
node = node.parent
|
|
251
285
|
res = node.seven_chr_def.copy()
|
|
@@ -254,10 +288,10 @@ def get_seven_chr_def(code, search_in_ancestors=False, prioritize_blocks=False):
|
|
|
254
288
|
else:
|
|
255
289
|
return res
|
|
256
290
|
|
|
257
|
-
def get_seven_chr_note(code, search_in_ancestors=False, prioritize_blocks=False):
|
|
291
|
+
def get_seven_chr_note(code:str, search_in_ancestors:bool=False, prioritize_blocks:bool=False) -> str:
|
|
258
292
|
if not is_valid_item(code):
|
|
259
293
|
raise ValueError("The code \""+code+"\" does not exist.")
|
|
260
|
-
node =
|
|
294
|
+
node = _code_to_node[_add_dot_to_code(code)]
|
|
261
295
|
if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
|
|
262
296
|
node = node.parent
|
|
263
297
|
res = node.seven_chr_note
|
|
@@ -266,10 +300,10 @@ def get_seven_chr_note(code, search_in_ancestors=False, prioritize_blocks=False)
|
|
|
266
300
|
else:
|
|
267
301
|
return res
|
|
268
302
|
|
|
269
|
-
def get_use_additional_code(code, search_in_ancestors=False, prioritize_blocks=False):
|
|
303
|
+
def get_use_additional_code(code:str, search_in_ancestors:bool=False, prioritize_blocks:bool=False) -> str:
|
|
270
304
|
if not is_valid_item(code):
|
|
271
305
|
raise ValueError("The code \""+code+"\" does not exist.")
|
|
272
|
-
node =
|
|
306
|
+
node = _code_to_node[_add_dot_to_code(code)]
|
|
273
307
|
if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
|
|
274
308
|
node = node.parent
|
|
275
309
|
res = node.use_additional_code
|
|
@@ -278,10 +312,10 @@ def get_use_additional_code(code, search_in_ancestors=False, prioritize_blocks=F
|
|
|
278
312
|
else:
|
|
279
313
|
return res
|
|
280
314
|
|
|
281
|
-
def get_code_first(code, search_in_ancestors=False, prioritize_blocks=False):
|
|
315
|
+
def get_code_first(code:str, search_in_ancestors:bool=False, prioritize_blocks:bool=False) -> str:
|
|
282
316
|
if not is_valid_item(code):
|
|
283
317
|
raise ValueError("The code \""+code+"\" does not exist.")
|
|
284
|
-
node =
|
|
318
|
+
node = _code_to_node[_add_dot_to_code(code)]
|
|
285
319
|
if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
|
|
286
320
|
node = node.parent
|
|
287
321
|
res = node.code_first
|
|
@@ -290,10 +324,10 @@ def get_code_first(code, search_in_ancestors=False, prioritize_blocks=False):
|
|
|
290
324
|
else:
|
|
291
325
|
return res
|
|
292
326
|
|
|
293
|
-
def get_parent(code, prioritize_blocks=False):
|
|
327
|
+
def get_parent(code:str, prioritize_blocks:bool=False) -> str:
|
|
294
328
|
if not is_valid_item(code):
|
|
295
329
|
raise ValueError("The code \""+code+"\" does not exist.")
|
|
296
|
-
node =
|
|
330
|
+
node = _code_to_node[_add_dot_to_code(code)]
|
|
297
331
|
if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
|
|
298
332
|
node = node.parent
|
|
299
333
|
if node.parent!=None:
|
|
@@ -301,10 +335,10 @@ def get_parent(code, prioritize_blocks=False):
|
|
|
301
335
|
else:
|
|
302
336
|
return ""
|
|
303
337
|
|
|
304
|
-
def get_children(code, prioritize_blocks=False):
|
|
338
|
+
def get_children(code:str, prioritize_blocks:bool=False) -> list[str]:
|
|
305
339
|
if not is_valid_item(code):
|
|
306
340
|
raise ValueError("The code \""+code+"\" does not exist.")
|
|
307
|
-
node =
|
|
341
|
+
node = _code_to_node[_add_dot_to_code(code)]
|
|
308
342
|
if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
|
|
309
343
|
node = node.parent
|
|
310
344
|
res = []
|
|
@@ -312,18 +346,18 @@ def get_children(code, prioritize_blocks=False):
|
|
|
312
346
|
res.append(child.name)
|
|
313
347
|
return res
|
|
314
348
|
|
|
315
|
-
def is_leaf(code, prioritize_blocks=False):
|
|
349
|
+
def is_leaf(code:str, prioritize_blocks:bool=False) -> bool:
|
|
316
350
|
if not is_valid_item(code):
|
|
317
351
|
raise ValueError("The code \""+code+"\" does not exist.")
|
|
318
|
-
node =
|
|
352
|
+
node = _code_to_node[_add_dot_to_code(code)]
|
|
319
353
|
if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
|
|
320
354
|
node = node.parent
|
|
321
355
|
return len(node.children)==0
|
|
322
356
|
|
|
323
|
-
def get_full_data(code, search_in_ancestors=False, prioritize_blocks=False):
|
|
357
|
+
def get_full_data(code:str, search_in_ancestors:bool=False, prioritize_blocks:bool=False) -> str:
|
|
324
358
|
if not is_valid_item(code):
|
|
325
359
|
raise ValueError("The code \""+code+"\" does not exist.")
|
|
326
|
-
node =
|
|
360
|
+
node = _code_to_node[_add_dot_to_code(code)]
|
|
327
361
|
if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
|
|
328
362
|
node = node.parent
|
|
329
363
|
str = "Name:\n"+node.name+"\nDescription:\n"+node.description
|
|
@@ -367,10 +401,10 @@ def get_full_data(code, search_in_ancestors=False, prioritize_blocks=False):
|
|
|
367
401
|
str = str + child.name + ", "
|
|
368
402
|
return str[:-2]
|
|
369
403
|
|
|
370
|
-
def get_ancestors(code,
|
|
404
|
+
def get_ancestors(code:str,prioritize_blocks:bool=False) -> list[str]:
|
|
371
405
|
if not is_valid_item(code):
|
|
372
406
|
raise ValueError("The code \""+code+"\" does not exist.")
|
|
373
|
-
node =
|
|
407
|
+
node = _code_to_node[_add_dot_to_code(code)]
|
|
374
408
|
if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
|
|
375
409
|
node = node.parent
|
|
376
410
|
result = []
|
|
@@ -379,33 +413,33 @@ def get_ancestors(code, prioritize_blocks=False):
|
|
|
379
413
|
node=node.parent
|
|
380
414
|
return result
|
|
381
415
|
|
|
382
|
-
def get_descendants(code,
|
|
416
|
+
def get_descendants(code:str,prioritize_blocks:bool=False) -> list[str]:
|
|
383
417
|
if not is_valid_item(code):
|
|
384
418
|
raise ValueError("The code \""+code+"\" does not exist.")
|
|
385
|
-
node =
|
|
419
|
+
node = _code_to_node[_add_dot_to_code(code)]
|
|
386
420
|
if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
|
|
387
421
|
node = node.parent
|
|
388
422
|
result = []
|
|
389
423
|
_add_children_to_list(node, result)
|
|
390
424
|
return result
|
|
391
425
|
|
|
392
|
-
def _add_children_to_list(node, list):
|
|
426
|
+
def _add_children_to_list(node:_CodeTree, list:list[str]) -> None:
|
|
393
427
|
for child in node.children:
|
|
394
428
|
list.append(child.name)
|
|
395
429
|
_add_children_to_list(child,list)
|
|
396
430
|
|
|
397
|
-
def is_ancestor(a,b,prioritize_blocks_a=False,prioritize_blocks_b=False):
|
|
431
|
+
def is_ancestor(a:str,b:str,prioritize_blocks_a:bool=False,prioritize_blocks_b:bool=False) -> bool:
|
|
398
432
|
if not is_valid_item(a):
|
|
399
433
|
raise ValueError("The code \""+a+"\" does not exist.")
|
|
400
|
-
node =
|
|
434
|
+
node = _code_to_node[_add_dot_to_code(a)]
|
|
401
435
|
if prioritize_blocks_a and node.parent!=None and node.parent.name==node.name:
|
|
402
436
|
node = node.parent
|
|
403
437
|
return a in get_ancestors(b, prioritize_blocks=prioritize_blocks_b) and (a!=b or prioritize_blocks_a)
|
|
404
438
|
|
|
405
|
-
def is_descendant(a,b,prioritize_blocks_a=False,prioritize_blocks_b=False):
|
|
439
|
+
def is_descendant(a:str,b:str,prioritize_blocks_a:bool=False,prioritize_blocks_b:bool=False) -> bool:
|
|
406
440
|
return is_ancestor(b,a,prioritize_blocks_a=prioritize_blocks_b,prioritize_blocks_b=prioritize_blocks_a)
|
|
407
441
|
|
|
408
|
-
def get_nearest_common_ancestor(a,b,prioritize_blocks_a=False,prioritize_blocks_b=False):
|
|
442
|
+
def get_nearest_common_ancestor(a:str,b:str,prioritize_blocks_a:bool=False,prioritize_blocks_b:bool=False) -> str:
|
|
409
443
|
anc_a = [_add_dot_to_code(a)] + get_ancestors(a, prioritize_blocks=prioritize_blocks_a)
|
|
410
444
|
anc_b = [_add_dot_to_code(b)] + get_ancestors(b, prioritize_blocks=prioritize_blocks_b)
|
|
411
445
|
if len(anc_b) > len(anc_a):
|
|
@@ -417,50 +451,50 @@ def get_nearest_common_ancestor(a,b,prioritize_blocks_a=False,prioritize_blocks_
|
|
|
417
451
|
return anc
|
|
418
452
|
return ""
|
|
419
453
|
|
|
420
|
-
def get_all_codes(with_dots=True):
|
|
421
|
-
if
|
|
422
|
-
|
|
423
|
-
_add_tree_to_list(chapter)
|
|
454
|
+
def get_all_codes(with_dots:bool=True) -> list[str]:
|
|
455
|
+
if _all_codes_list==[]:
|
|
456
|
+
_fill_all_codes_list()
|
|
424
457
|
if with_dots:
|
|
425
|
-
return
|
|
458
|
+
return _all_codes_list.copy()
|
|
426
459
|
else:
|
|
427
|
-
return
|
|
460
|
+
return _all_codes_list_no_dots.copy()
|
|
428
461
|
|
|
429
|
-
def
|
|
430
|
-
|
|
462
|
+
def _fill_all_codes_list() -> None:
|
|
463
|
+
for chapter in _chapter_list:
|
|
464
|
+
_add_tree_to_list(chapter)
|
|
465
|
+
|
|
466
|
+
def _add_tree_to_list(tree:_CodeTree) -> None:
|
|
467
|
+
_all_codes_list.append(tree.name)
|
|
431
468
|
if(len(tree.name)>4 and tree.name[3]=="."):
|
|
432
|
-
|
|
469
|
+
_all_codes_list_no_dots.append(tree.name[:3]+tree.name[4:])
|
|
433
470
|
else:
|
|
434
|
-
|
|
471
|
+
_all_codes_list_no_dots.append(tree.name)
|
|
435
472
|
for child in tree.children:
|
|
436
473
|
_add_tree_to_list(child)
|
|
437
474
|
|
|
438
|
-
def get_index(code):
|
|
475
|
+
def get_index(code:str) -> int: # type: ignore[reportReturnType]
|
|
439
476
|
if not is_valid_item(code):
|
|
440
477
|
raise ValueError("The code \""+code+"\" does not exist.")
|
|
441
478
|
code = _add_dot_to_code(code)
|
|
442
|
-
if
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
return code_to_index_dictionary[code]
|
|
479
|
+
if _all_codes_list==[]:
|
|
480
|
+
_fill_all_codes_list()
|
|
481
|
+
if code in _code_to_index_dictionary:
|
|
482
|
+
return _code_to_index_dictionary[code]
|
|
447
483
|
else:
|
|
448
484
|
i=0
|
|
449
|
-
for c in
|
|
485
|
+
for c in _all_codes_list:
|
|
450
486
|
if c==code:
|
|
451
|
-
|
|
487
|
+
_code_to_index_dictionary[code]=i
|
|
452
488
|
return i
|
|
453
489
|
else:
|
|
454
490
|
i=i+1
|
|
455
491
|
|
|
456
|
-
def remove_dot(code):
|
|
457
|
-
if
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
_add_tree_to_list(chapter)
|
|
466
|
-
return all_codes_list[get_index(code)]
|
|
492
|
+
def remove_dot(code:str) -> str:
|
|
493
|
+
if _all_codes_list==[]:
|
|
494
|
+
_fill_all_codes_list()
|
|
495
|
+
return _all_codes_list_no_dots[get_index(code)]
|
|
496
|
+
|
|
497
|
+
def add_dot(code:str) -> str:
|
|
498
|
+
if _all_codes_list==[]:
|
|
499
|
+
_fill_all_codes_list()
|
|
500
|
+
return _all_codes_list[get_index(code)]
|