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.
- 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 +189 -103
- {simple_icd_10_cm-1.2.0.dist-info → simple_icd_10_cm-1.4.0.dist-info}/METADATA +106 -27
- simple_icd_10_cm-1.4.0.dist-info/RECORD +10 -0
- {simple_icd_10_cm-1.2.0.dist-info → simple_icd_10_cm-1.4.0.dist-info}/WHEEL +1 -1
- {simple_icd_10_cm-1.2.0.dist-info → simple_icd_10_cm-1.4.0.dist-info/licenses}/LICENSE +1 -1
- simple_icd_10_cm/data/icd10cm-order-Jan-2021.txt +0 -95358
- simple_icd_10_cm-1.2.0.dist-info/RECORD +0 -10
- {simple_icd_10_cm-1.2.0.dist-info → simple_icd_10_cm-1.4.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,18 +13,21 @@ 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
|
-
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
|
-
|
|
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
|
|
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
|
|
109
|
-
|
|
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
|
-
|
|
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=
|
|
168
|
+
lines=text.split("\n")
|
|
133
169
|
for line in lines:
|
|
134
|
-
all_confirmed_codes.add(line[
|
|
170
|
+
all_confirmed_codes.add(line.split(" ")[0].replace(".",""))
|
|
135
171
|
|
|
136
172
|
#creates the tree
|
|
137
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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:
|
|
157
|
-
return code in
|
|
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:
|
|
214
|
+
def is_chapter(code:str) -> bool:
|
|
160
215
|
code = _add_dot_to_code(code)
|
|
161
|
-
if code in
|
|
162
|
-
return
|
|
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:
|
|
221
|
+
def is_block(code:str) -> bool:
|
|
167
222
|
code = _add_dot_to_code(code)
|
|
168
|
-
if code in
|
|
169
|
-
|
|
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:
|
|
229
|
+
def is_category(code:str) -> bool:
|
|
174
230
|
code = _add_dot_to_code(code)
|
|
175
|
-
if code in
|
|
176
|
-
return
|
|
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:
|
|
236
|
+
def is_subcategory(code:str, include_extended_subcategories:bool=True) -> bool:
|
|
181
237
|
code = _add_dot_to_code(code)
|
|
182
|
-
if code in
|
|
183
|
-
return
|
|
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:
|
|
243
|
+
def is_extended_subcategory(code:str) -> bool:
|
|
188
244
|
code = _add_dot_to_code(code)
|
|
189
|
-
if code in
|
|
190
|
-
return
|
|
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:
|
|
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:
|
|
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:
|
|
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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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 =
|
|
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
|
|
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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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 =
|
|
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:
|
|
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 =
|
|
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 =
|
|
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
|
|
422
|
-
|
|
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
|
|
510
|
+
return _all_codes_list.copy()
|
|
426
511
|
else:
|
|
427
|
-
return
|
|
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
|
-
|
|
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
|
-
|
|
521
|
+
_all_codes_list_no_dots.append(tree.name[:3]+tree.name[4:])
|
|
433
522
|
else:
|
|
434
|
-
|
|
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:
|
|
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
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
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
|
|
537
|
+
for c in _all_codes_list:
|
|
450
538
|
if c==code:
|
|
451
|
-
|
|
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:
|
|
457
|
-
if
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
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)]
|