simple-icd-10-cm 1.3.0__py3-none-any.whl → 1.5.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.
@@ -2,6 +2,9 @@
2
2
  # Copyright (c) 2021-2025 Stefano Travasci
3
3
  # Read the full LICENCES at https://github.com/StefanoTrv/simple_icd_10_cm/blob/master/LICENSE
4
4
 
5
+ from __future__ import annotations
6
+
7
+ import warnings, inspect
5
8
  from typing import Optional
6
9
  import xml.etree.ElementTree as ET
7
10
 
@@ -13,6 +16,47 @@ except ImportError:
13
16
 
14
17
  from . import data # relative-import the "package" containing the data
15
18
 
19
+ # --- Class and function for warning management ---
20
+ class _SimpleICD10CMWarning(UserWarning):
21
+ pass
22
+
23
+ def _user_stacklevel() -> int:
24
+ for i, frameinfo in enumerate(inspect.stack()[1:], start=1):
25
+ module = inspect.getmodule(frameinfo.frame)
26
+ if module is None or not module.__name__.startswith("simple_icd_10_cm"):
27
+ return i
28
+ return 1
29
+
30
+ def _check_node_has_text(text : str, node_tag : str, main_node_tag : str | None, code_this : _CodeTree, code_parent : _CodeTree | None) -> bool:
31
+ if main_node_tag is not None:
32
+ msg_subject = f"XML <{node_tag}> node of parent XML node <{main_node_tag}>"
33
+ msg_end = f"Ignoring this <{node_tag}> node."
34
+ else:
35
+ msg_subject = f"XML <{node_tag}> node"
36
+ msg_end = "Ignoring this node."
37
+
38
+ if text is None:
39
+ if code_this.name != "":
40
+ error_msg = f"{msg_subject} with no text was found in data for code \"{code_this.name}\". {msg_end}"
41
+ elif code_parent is not None:
42
+ error_msg = f"{msg_subject} with no text was found in data for child of \"{code_parent.name}\" with unknown name. {msg_end}"
43
+ else:
44
+ error_msg = f"{msg_subject} with no text was found in data of parentless code of unknown name. {msg_end}"
45
+ warnings.warn(error_msg,category=_SimpleICD10CMWarning,stacklevel=_user_stacklevel())
46
+ return False
47
+ return True
48
+
49
+ def _warning_extensionless_seven_chr_def_note(code_this : _CodeTree, code_parent : _CodeTree | None) -> None:
50
+ if code_this.name != "":
51
+ msg_where = f"code {code_this.name}"
52
+ elif code_parent is not None:
53
+ msg_where = f"child code of \"{code_parent.name}\" with unknown name"
54
+ else:
55
+ msg_where = f"parentless code of unknown name"
56
+ error_msg = f"In data of {msg_where}, found <note> XML element in a <sevenChrDef> XML element with not preceded by an <extension> element. Ignoring this <note> element."
57
+ warnings.warn(error_msg,category=_SimpleICD10CMWarning,stacklevel=_user_stacklevel())
58
+ # --- --- ---
59
+
16
60
  _chapter_list: list["_CodeTree"] = []
17
61
 
18
62
  _code_to_node: dict[str, "_CodeTree"] = {}
@@ -23,11 +67,15 @@ _all_codes_list_no_dots: list[str] = []
23
67
 
24
68
  _code_to_index_dictionary: dict[str, int] = {}
25
69
 
26
- _all_codes_package_file_name:str = 'code-list-April-2025.txt'
27
- _classification_data_package_file_name:str = 'icd10cm-tabular-April-2025.xml'
70
+ _all_codes_package_file_name:str = 'code-list-April-2026.txt'
71
+ _classification_data_package_file_name:str = 'icd10c-tabular-April-1-2026.xml'
28
72
 
29
73
  class _CodeTree:
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):
74
+ def __append_string_to_field_list(self, xml_node, parent_tag : str, list_to_be_appended : list[str]) -> None:
75
+ if _check_node_has_text(xml_node.text, xml_node.tag, parent_tag, self, self.parent):
76
+ list_to_be_appended.append(xml_node.text)
77
+
78
+ def __init__(self, tree, parent : _CodeTree | None = 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):
31
79
  #initialize all the values
32
80
  self.name = ""
33
81
  self.description = ""
@@ -46,59 +94,94 @@ class _CodeTree:
46
94
  self.use_additional_code_ancestor = use_additional_code_ancestor
47
95
  self.code_first = ""
48
96
  self.code_first_ancestor = code_first_ancestor
97
+ self.code_also = ""
98
+ self.code_also_ancestor = code_also_ancestor
99
+ self.notes = ""
100
+ self.notes_ancestor = notes_ancestor
49
101
 
50
102
  #reads the data from the subtrees
51
103
  new_seven_chr_def_ancestor=seven_chr_def_ancestor
52
104
  new_seven_chr_note_ancestor=seven_chr_note_ancestor
53
105
  new_use_additional_code_ancestor=use_additional_code_ancestor
54
106
  new_code_first_ancestor=code_first_ancestor
107
+ new_code_also_ancestor=code_also_ancestor
108
+ new_notes_ancestor=notes_ancestor
109
+ use_additional_code_list=[]
110
+ code_first_list=[]
111
+ code_also_list=[]
112
+ notes_list=[]
55
113
  if "id" in tree.attrib: #the name of sections is an attribute instead of text inside an XML element
56
114
  self.name=tree.attrib["id"]
57
115
  for subtree in tree:
58
116
  if subtree.tag=="section" or subtree.tag=="diag": #creates a new child for this node
59
- 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))
117
+ # This is only correct because the XML elements for the children codes always follow the XML elements for this code's data
118
+ 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))
60
119
  elif subtree.tag=="name":
61
- self.name=subtree.text
120
+ if subtree.text is not None:
121
+ self.name=subtree.text
122
+ else:
123
+ if self.parent is not None:
124
+ err_msg = f"Found <name> XML element containing no text in child of code {self.parent.name}."
125
+ else:
126
+ err_msg = "Found <name> XML element containing no text in parentless code."
127
+ raise ValueError(err_msg)
62
128
  elif subtree.tag=="desc":
63
- self.description=subtree.text
129
+ if _check_node_has_text(subtree.text, subtree.tag, None, self, self.parent):
130
+ self.description=subtree.text
64
131
  elif subtree.tag=="excludes1":
65
132
  for note in subtree:
66
- self.excludes1.append(note.text)
133
+ self.__append_string_to_field_list(note,subtree.tag,self.excludes1)
67
134
  elif subtree.tag=="excludes2":
68
135
  for note in subtree:
69
- self.excludes2.append(note.text)
136
+ self.__append_string_to_field_list(note,subtree.tag,self.excludes2)
70
137
  elif subtree.tag=="includes":
71
138
  for note in subtree:
72
- self.includes.append(note.text)
139
+ self.__append_string_to_field_list(note,subtree.tag,self.includes)
73
140
  elif subtree.tag=="inclusionTerm":
74
141
  for note in subtree:
75
- self.inclusion_term.append(note.text)
142
+ self.__append_string_to_field_list(note,subtree.tag,self.inclusion_term)
76
143
  elif subtree.tag=="sevenChrDef":
77
144
  last_char = None
78
145
  for extension in subtree:
79
146
  if extension.tag=="extension":
80
- self.seven_chr_def[extension.attrib["char"]]=extension.text
147
+ self.seven_chr_def[extension.attrib["char"]]=extension.text if extension.text is not None else "" # in the absurd case of a extension with an attribute but no text
81
148
  last_char = extension.attrib["char"]
82
149
  elif extension.tag=="note":
83
- self.seven_chr_def[last_char]=self.seven_chr_def[last_char]+"/"+extension.text
150
+ if last_char is None:
151
+ _warning_extensionless_seven_chr_def_note(self, self.parent)
152
+ else:
153
+ if _check_node_has_text(extension.text,extension.tag,subtree.tag,self,self.parent):
154
+ self.seven_chr_def[last_char]=self.seven_chr_def[last_char]+"/"+extension.text
84
155
  new_seven_chr_def_ancestor=self
85
156
  elif subtree.tag=="sevenChrNote":
86
- self.seven_chr_note=subtree[0].text
87
- new_seven_chr_note_ancestor=self
157
+ if _check_node_has_text(subtree[0].text, subtree.tag, None, self, self.parent):
158
+ self.seven_chr_note=subtree[0].text
159
+ new_seven_chr_note_ancestor=self
88
160
  elif subtree.tag=="useAdditionalCode":
161
+ # NOTE: multiple useAdditionalCode elements may be present, so self.use_additional_code should always be appended and never overwritten
89
162
  for i in range(0,len(subtree)):#in case there are multiple lines
90
- self.use_additional_code=self.use_additional_code+"\n"+subtree[i].text
163
+ self.__append_string_to_field_list(subtree[i],subtree.tag,use_additional_code_list)
91
164
  new_use_additional_code_ancestor=self
92
165
  elif subtree.tag=="codeFirst":
93
166
  for i in range(0,len(subtree)):#in case there are multiple lines
94
- self.code_first=self.code_first+"\n"+subtree[i].text
167
+ self.__append_string_to_field_list(subtree[i],subtree.tag,code_first_list)
95
168
  new_code_first_ancestor=self
169
+ elif subtree.tag=="codeAlso":
170
+ # see NOTE for useAdditionalCode
171
+ for i in range(0,len(subtree)):#in case there are multiple lines
172
+ self.__append_string_to_field_list(subtree[i],subtree.tag,code_also_list)
173
+ new_code_also_ancestor=self
174
+ elif subtree.tag=="notes":
175
+ # see NOTE for useAdditionalCode
176
+ for i in range(0,len(subtree)):#in case there are multiple lines
177
+ self.__append_string_to_field_list(subtree[i],subtree.tag,notes_list)
178
+ new_notes_ancestor=self
96
179
 
97
- #cleans the use_additional_code and code_first fields from extra new lines
98
- if self.use_additional_code!="" and self.use_additional_code[0]=="\n":
99
- self.use_additional_code=self.use_additional_code[1:]
100
- if self.code_first!="" and self.code_first[0]=="\n":
101
- self.code_first=self.code_first[1:]
180
+ #merges the use_additional_code, code_first, code_also and notes fields into multiline strings
181
+ self.use_additional_code = "\n".join(use_additional_code_list)
182
+ self.code_first = "\n".join(code_first_list)
183
+ self.code_also = "\n".join(code_also_list)
184
+ self.notes = "\n".join(notes_list)
102
185
 
103
186
  #sets the type
104
187
  if tree.tag=="chapter":
@@ -130,9 +213,15 @@ class _CodeTree:
130
213
  for extension in dictionary:
131
214
  if((extended_name[:3]+extended_name[4:]+extension) in all_confirmed_codes):#checks if there's a special rule that excludes this new code
132
215
  new_XML = "<diag_ext><name>"+extended_name+extension+"</name><desc>"+self.description+", "+dictionary[extension]+"</desc></diag_ext>"
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))
216
+ 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))
134
217
 
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
218
+ def _load_codes(all_codes_file_path:Optional[str] = None, classification_data_file_path:Optional[str] = None, suppress_warnings:Optional[bool] = None) -> None: # either both or none of the strings must be None
219
+ if suppress_warnings is not None:
220
+ if suppress_warnings:
221
+ warnings.filterwarnings("ignore", category=_SimpleICD10CMWarning)
222
+ else:
223
+ warnings.simplefilter("default", category=_SimpleICD10CMWarning)
224
+
136
225
  #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
137
226
  if all_codes_file_path is None:
138
227
  assert classification_data_file_path is None
@@ -170,9 +259,9 @@ def _load_codes(all_codes_file_path:Optional[str] = None, classification_data_fi
170
259
 
171
260
  _load_codes()
172
261
 
173
- def change_version(all_codes_file_path:Optional[str] = None, classification_data_file_path:Optional[str] = None) -> None:
262
+ def change_version(all_codes_file_path:Optional[str] = None, classification_data_file_path:Optional[str] = None, suppress_warnings:Optional[bool] = None) -> None:
174
263
  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)
264
+ _load_codes(all_codes_file_path=all_codes_file_path,classification_data_file_path=classification_data_file_path,suppress_warnings=suppress_warnings)
176
265
  else:
177
266
  ac_error = "None" if all_codes_file_path is None else "\"" + all_codes_file_path + "\""
178
267
  cd_error = "None" if classification_data_file_path is None else "\"" + classification_data_file_path + "\""
@@ -324,6 +413,30 @@ def get_code_first(code:str, search_in_ancestors:bool=False, prioritize_blocks:b
324
413
  else:
325
414
  return res
326
415
 
416
+ def get_code_also(code:str, search_in_ancestors:bool=False, prioritize_blocks:bool=False) -> str:
417
+ if not is_valid_item(code):
418
+ raise ValueError("The code \""+code+"\" does not exist.")
419
+ node = _code_to_node[_add_dot_to_code(code)]
420
+ if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
421
+ node = node.parent
422
+ res = node.code_also
423
+ if search_in_ancestors and res=="" and node.code_also_ancestor!=None:
424
+ return node.code_also_ancestor.code_also
425
+ else:
426
+ return res
427
+
428
+ def get_notes(code:str, search_in_ancestors:bool=False, prioritize_blocks:bool=False) -> str:
429
+ if not is_valid_item(code):
430
+ raise ValueError("The code \""+code+"\" does not exist.")
431
+ node = _code_to_node[_add_dot_to_code(code)]
432
+ if prioritize_blocks and node.parent!=None and node.parent.name==node.name:
433
+ node = node.parent
434
+ res = node.notes
435
+ if search_in_ancestors and res=="" and node.notes_ancestor!=None:
436
+ return node.notes_ancestor.notes
437
+ else:
438
+ return res
439
+
327
440
  def get_parent(code:str, prioritize_blocks:bool=False) -> str:
328
441
  if not is_valid_item(code):
329
442
  raise ValueError("The code \""+code+"\" does not exist.")
@@ -393,6 +506,12 @@ def get_full_data(code:str, search_in_ancestors:bool=False, prioritize_blocks:bo
393
506
  code_first=get_code_first(code,search_in_ancestors=search_in_ancestors,prioritize_blocks=prioritize_blocks)
394
507
  if code_first!="":
395
508
  str = str + "\ncode first:\n" + code_first
509
+ code_also=get_code_also(code,search_in_ancestors=search_in_ancestors,prioritize_blocks=prioritize_blocks)
510
+ if code_also!="":
511
+ str = str + "\ncode also:\n" + code_also
512
+ notes=get_notes(code,search_in_ancestors=search_in_ancestors,prioritize_blocks=prioritize_blocks)
513
+ if notes!="":
514
+ str = str + "\nnotes:\n" + notes
396
515
  if node.children==[]:
397
516
  str = str + "\nChildren:\nNone--"
398
517
  else:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: simple_icd_10_cm
3
- Version: 1.3.0
3
+ Version: 1.5.0
4
4
  Summary: A simple python library for ICD-10-CM codes
5
5
  Home-page: https://github.com/StefanoTrv/simple_icd_10_CM
6
6
  Author: Stefano Travasci
@@ -53,6 +53,8 @@ A simple python library for ICD-10-CM codes
53
53
  * [get_seven_chr_def(code, search_in_ancestors=False, prioritize_blocks=False)](#get_seven_chr_defcode-search_in_ancestorsfalse-prioritize_blocksfalse)
54
54
  * [get_use_additional_code(code, search_in_ancestors=False, prioritize_blocks=False)](#get_use_additional_codecode-search_in_ancestorsfalse-prioritize_blocksfalse)
55
55
  * [get_code_first(code, search_in_ancestors=False, prioritize_blocks=False)](#get_code_firstcode-search_in_ancestorsfalse-prioritize_blocksfalse)
56
+ * [get_code_also(code, search_in_ancestors=False, prioritize_blocks=False)](#get_code_alsocode-search_in_ancestorsfalse-prioritize_blocksfalse)
57
+ * [get_notes(code, search_in_ancestors=False, prioritize_blocks=False)](#get_notescode-search_in_ancestorsfalse-prioritize_blocksfalse)
56
58
  * [get_full_data(code, search_in_ancestors=False, prioritize_blocks=False)](#get_full_datacode-search_in_ancestorsfalse-prioritize_blocksfalse)
57
59
  * [get_parent(code, prioritize_blocks=False)](#get_parentcode-prioritize_blocksfalse)
58
60
  * [get_children(code, prioritize_blocks=False)](#get_childrencode-prioritize_blocksfalse)
@@ -66,10 +68,16 @@ A simple python library for ICD-10-CM codes
66
68
  * [get_index(code)](#get_indexcode)
67
69
  * [remove_dot(code)](#remove_dotcode)
68
70
  * [add_dot(code)](#add_dotcode)
69
- * [change_version(all_codes_file_path=None, classification_data_file_path=None)](#change_versionall_codes_file_pathnone-classification_data_file_pathnone)
71
+ * [change_version(all_codes_file_path=None, classification_data_file_path=None, suppress_warnings=None)](#change_versionall_codes_file_pathnone-classification_data_file_pathnone-suppress_warningsnone)
70
72
  * [Conclusion](#conclusion)
71
73
 
72
74
  ## Release notes
75
+ * **1.5.0**:
76
+ * The default ICD-10-CM release of the library is now the **April 2026 release**
77
+ * Certain kinds of defects in the data will now emit warnings instead of crashing the library
78
+ * Small refactoring for my own sanity
79
+ * Thanks to @Qwemep on Github for her valuable insights!
80
+ * **1.4.0**: The data in the fields "codeAlso" and "notes" can now be retrieved using the get_code_also() and get_notes() functions
73
81
  * **1.3.0**:
74
82
  * Users can now use their preferred ICD-10-CM release by providing properly formatted files as inputs
75
83
  * The default ICD-10-CM release of the library is now the **April 2025 release**
@@ -89,7 +97,7 @@ The objective of this library is to provide a simple instrument for dealing with
89
97
  If you are looking for a library that deals with ICD-10 codes instead of ICD-10-CM codes, you can check the [simple_icd_10 library](https://github.com/StefanoTrv/simple_icd_10), which is based on the 2019 version of ICD-10. If you are interested in the ICD-11 MMS classification, you can check out instead the [simple_icd_11 library](https://github.com/StefanoTrv/simple_icd_11).
90
98
  There is also a Java version of this library, [SimpleICD10CM-Java-edition](https://github.com/StefanoTrv/SimpleICD10CM-Java-edition).
91
99
 
92
- The data used in this library was taken from the [website of the CDC]((https://www.cdc.gov/nchs/icd/icd-10-cm/files.html). This library currently uses the **April 2025 release of ICD-10-CM**. This package can be configured to use other releases of the ICD-10-CM classification, read the section [Using other ICD-10-CM releases](#using-other-icd-10-cm-releases) for more details.
100
+ The data used in this library was taken from the [website of the CDC](https://www.cdc.gov/nchs/icd/icd-10-cm/files.html). This library currently uses the **April 2026 release of ICD-10-CM**. This package can be configured to use other releases of the ICD-10-CM classification, read the section [Using other ICD-10-CM releases](#using-other-icd-10-cm-releases) for more details.
93
101
 
94
102
  If you feel like supporting me, please check out the [Conclusion section](#conclusion).
95
103
 
@@ -125,14 +133,14 @@ cm.change_version(classification_data_file_path="icd10cm_tabular_2021.xml")
125
133
 
126
134
  The file specified in `all_codes_file_path` should contain all the unique codes of the desired release. However, it is sufficient that it contains all the seven-characters codes that must be generated programmatically, that is all the valid codes that are not explicitly listed in the XML file (see [About the special seventh character](#about-the-special-seventh-character) for more information). Each line in this file must contain a single code. Codes can be written in the format with or without the dot. In each line, the code may be followed by any text, as long as there is at least one space character immediately after the code. The `icd10cm-codes` text files available from the [CDC's website](https://www.cdc.gov/nchs/icd/icd-10-cm/files.html) can be used for this purpose, without modification.
127
135
  The file specified in `classification_data_file_path` is the XML containing the whole classification. This typically corresponds to the `icd10cm-tabular` XML files available from the [CDC's website](https://www.cdc.gov/nchs/icd/icd-10-cm/files.html). If using XML files from another origin, note that, in the official releases, the first two children of the root XML element are not part of the structure of the classification, and are therefore skipped by the library.
128
- Examples of both file types can be found in the [all-data folder](https://github.com/StefanoTrv/simple_icd_10_CM/tree/master/all-data), which includes data for the current default release and for a previous release.
136
+ Examples of both file types can be found in the [all-data folder](https://github.com/StefanoTrv/simple_icd_10_CM/tree/master/all-data), which includes data for the current default release and for previous releases.
129
137
 
130
138
  ## The format of the codes
131
139
  Subcategories codes can be written in two different ways: with a dot (for example "I13.1") and with no dot (for example "I131"). The functions in this library can receive as input codes in both these formats. The codes returned by the functions will always be in the format with the dot.
132
140
  You can easily change the format of a code by using the [`remove_dot`](#remove_dotcode) and [`add_dot`](#add_dotcode) functions.
133
141
 
134
142
  ## About the file "Instructional Notations.md"
135
- The file [Instructional Notations.md](https://github.com/StefanoTrv/simple_icd_10_CM/blob/master/Instructional%20Notations.md) contains the introduction present in the file `icd10cm-tabular-April-2025.xml` (the file that contains the whole ICD-10-CM classification), copied there in a more accessible and readable format. There you can find an explanation about the meaning of most of the additional fields that can accompany a code.
143
+ The file [Instructional Notations.md](https://github.com/StefanoTrv/simple_icd_10_CM/blob/master/Instructional%20Notations.md) contains the introduction present in the file `icd10cm-tabular-April-2026.xml` (the file that contains the whole ICD-10-CM classification), copied there in a more accessible and readable format. There you can find an explanation about the meaning of most of the additional fields that can accompany a code.
136
144
 
137
145
  ## Blocks containing only one category
138
146
  Unlike ICD-10, ICD-10-CM includes blocks of categories that contain only one category (and its subcategories). These blocks are named after the category that they contain, which means that ICD-10-CM contains blocks and categories that have the same exact code. This is a problem: because of this questionable decision, we can't know for sure if the code "B99", for example, refers to the category "B99" or to the block with the same name. This can be seen in the following example, where "B99" is recognized as both a block and a category:
@@ -158,9 +166,9 @@ cm.is_block("I12") and cm.is_category("I12")
158
166
  ```
159
167
 
160
168
  ## About the special seventh character
161
- The file `icd10cm-tabular-April-2025.xml`, which is the XML file that contains the whole ICD-10-CM classification, doesn't have an entry for all the codes generated by adding the "special" seventh character, but it often contains instead rules that explain how to generate these codes in the "sevenChrDef" field (and sometimes in the "sevenChrNote" field too, just to complicate things a little bit...). You can find more about the structure of these particular codes in [Instructional Notations.md](https://github.com/StefanoTrv/simple_icd_10_CM/blob/master/Instructional%20Notations.md).
169
+ The file `icd10cm-tabular-April-2026.xml`, which is the XML file that contains the whole ICD-10-CM classification, doesn't have an entry for all the codes generated by adding the "special" seventh character, but it often contains instead rules that explain how to generate these codes in the "sevenChrDef" field (and sometimes in the "sevenChrNote" field too, just to complicate things a little bit...). You can find more about the structure of these particular codes in [Instructional Notations.md](https://github.com/StefanoTrv/simple_icd_10_CM/blob/master/Instructional%20Notations.md).
162
170
  Due to the lack of a complete entry for these missing codes, I had to decide how they would be handled in this library. So I decided that their only field would be the description, composed of the description of their parent followed by the description of the meaning of the additional character, with a comma between the two: this description appears in official documents about ICD-10-CM, so it's not my invention but the actual official format. All the other fields are empty, but the optional argument `search_in_ancestors` of certain functions can be used to automatically retrieve the content of certain fields from the ancestors of the code (see the description of the specific functions in the [Documentation](#documentation) for more details).
163
- Seven-characters codes that have an entry in `icd10cm-tabular-April-2025.xml` are treated like any other code, and their data is taken directly from the file.
171
+ Seven-characters codes that have an entry in `icd10cm-tabular-April-2026.xml` are treated like any other code, and their data is taken directly from the file.
164
172
  If you need to know whether a code has been automatically generated using a rule described in a "sevenChrDef" field, you can use the [`is_extended_subcategory`](#is_extended_subcategorycode) function. Only the codes generated programmatically are classified as "extended subcategories", the seven-characters codes explicitly listed in the data are classified as "subcategories".
165
173
 
166
174
  ## Documentation
@@ -347,6 +355,30 @@ cm.get_code_first("S04.01")
347
355
  cm.get_code_first("S04.01",search_in_ancestors=True)
348
356
  #'any associated intracranial injury (S06.-)'
349
357
  ```
358
+ ### get_code_also(code, search_in_ancestors=False, prioritize_blocks=False)
359
+ This function takes a string as input. If the string is a valid ICD-10-CM code, it returns a **string** containing the data of the "codeAlso" field of this code, otherwise it raises a ValueError. If this code does not have a "codeAlso" field, it returns an empty string. Please see [Instructional Notations](https://github.com/StefanoTrv/simple_icd_10_CM/blob/master/Instructional%20Notations.md) if you have doubts about the meaning of this field. When the optional argument `search_in_ancestors` is set to True, if the given code doesn't have a "codeAlso" field but one of its ancestor does, the "codeAlso" data of the closer ancestor that contains such a field is returned. For the meaning of the optional argument `prioritize_blocks`, please see [Blocks containing only one category](#blocks-containing-only-one-category).
360
+ ```python
361
+ cm.get_code_also("I82.41")
362
+ #''
363
+ cm.get_code_also("Z23")
364
+ #', if applicable, encounter for immunization safety counseling (Z71.85)'
365
+ cm.get_code_also("Z49.0")
366
+ #''
367
+ cm.get_code_also("Z49.0",search_in_ancestors=True)
368
+ #'associated end stage renal disease (N18.6)'
369
+ ```
370
+ ### get_notes(code, search_in_ancestors=False, prioritize_blocks=False)
371
+ This function takes a string as input. If the string is a valid ICD-10-CM code, it returns a **string** containing the data of the "notes" field of this code, otherwise it raises a ValueError. If this code does not have a "notes" field, it returns an empty string. When the optional argument `search_in_ancestors` is set to True, if the given code doesn't have a "notes" field but one of its ancestor does, the "notes" data of the closer ancestor that contains such a field is returned. For the meaning of the optional argument `prioritize_blocks`, please see [Blocks containing only one category](#blocks-containing-only-one-category).
372
+ ```python
373
+ cm.get_notes("I82.41")
374
+ #''
375
+ cm.get_notes("Z23")
376
+ #'procedure codes are required to identify the types of immunizations given'
377
+ cm.get_notes("C91.00")
378
+ #''
379
+ cm.get_notes("C91.00",search_in_ancestors=True)
380
+ #'Codes in subcategory C91.0- should only be used for T-cell and B-cell precursor leukemia'
381
+ ```
350
382
  ### get_full_data(code, search_in_ancestors=False, prioritize_blocks=False)
351
383
  This function takes a string as input. If the string is a valid ICD-10-CM code, it returns a string containing all the available data of the code, otherwise it raises a ValueError. The empty fields are omitted from the string, except for the list of children (see second example below). When the optional argument `search_in_ancestors` is set to True, if the given code doesn't have a certain field but one of its ancestor does, the data of the closer ancestor that contains such a field is returned: see the previous functions to know which are the fields that are influenced by this argument and which are not. For the meaning of the optional argument `prioritize_blocks`, please see [Blocks containing only one category](#blocks-containing-only-one-category).
352
384
  ```python
@@ -477,8 +509,8 @@ cm.add_dot("K00-K14")
477
509
  #'K00-K14'
478
510
  ```
479
511
 
480
- ### change_version(all_codes_file_path=None, classification_data_file_path=None)
481
- This function can be used to load any ICD-10-CM release provided by the user, or to revert to using the default release for the package. Its usage is explained in the paragraph [Using other ICD-10-CM releases](#using-other-icd-10-cm-releases).
512
+ ### change_version(all_codes_file_path=None, classification_data_file_path=None, suppress_warnings=None)
513
+ This function can be used to load any ICD-10-CM release provided by the user, or to revert to using the default release for the package. Its usage is explained in the paragraph [Using other ICD-10-CM releases](#using-other-icd-10-cm-releases). The optional argument `suppress_warnings` can be used to hide the warnings that are emitted when some kinds of small inconsistencies are detected in the loaded data. Setting this parameter to `True` will hide the warnings, setting it to `False` will show them, setting it to `None` or omitting it will not change whether warnings are hidden or shown. It is recommended that you always omit this parameter.
482
514
 
483
515
  ## Conclusion
484
516
  This should be everything you need to know about the simple_icd_10_cm library. Please contact me if you find any mistake, bug, missing feature or anything else that could be improved or made easier to comprehend, both in this documentation and in the library itself as well as in the [Showcase notebook](https://github.com/StefanoTrv/simple_icd_10_CM/blob/master/Showcase%20notebook.ipynb). You can also contact me if you need any help using this library, but I may not be able to help with questions about the ICD-10-CM classification itself.
@@ -489,7 +521,8 @@ If you find this library useful and are feeling generous, consider making a dona
489
521
 
490
522
  ---
491
523
 
492
- Paypal: [![Donate](https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif)](https://www.paypal.com/donate?hosted_button_id=9HMMFAZE248VN)
524
+ [![ko-fi](https://ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/J3J81G2OSG)
525
+ [![Paypal](https://pics.paypal.com/00/s/NDU5Y2RmMjQtMTVmYi00MDIyLTgyMzMtMTFiNDY1MjUzMDA5/file.PNG)](https://www.paypal.com/donate?hosted_button_id=9HMMFAZE248VN)
493
526
 
494
527
  Curecoin: BKxCWuWzsqtLzAvAjtpsHpJ7LqFHPubqft
495
528
 
@@ -0,0 +1,10 @@
1
+ simple_icd_10_cm/__init__.py,sha256=Z1KTvT112WEfhmGoTFyloLMte55xISt0t0CQAeUysRk,31
2
+ simple_icd_10_cm/simple_icd_10_cm.py,sha256=2B50YqwjD8EIPyGN3DEvYpr7-Bxg8ATEuwfpXXoCbgA,29509
3
+ simple_icd_10_cm/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ simple_icd_10_cm/data/code-list-April-2026.txt,sha256=31TZdDo0mfSk_BKILtt84Nl1tEraWiolWXqlbQwqwcQ,799043
5
+ simple_icd_10_cm/data/icd10c-tabular-April-1-2026.xml,sha256=8WH4GCr_POOip44gL4JZwI6u4sZwqeRbAHJEXFIwKTU,9747661
6
+ simple_icd_10_cm-1.5.0.dist-info/licenses/LICENSE,sha256=ESrc5DumgzIiwvf1981qtBgmqpE4pH8H8x1vxcIzVlI,1099
7
+ simple_icd_10_cm-1.5.0.dist-info/METADATA,sha256=K5C8NQjNEIfUR9uoAiXqNXrYQQLK91HNsYde8LGQV6E,40985
8
+ simple_icd_10_cm-1.5.0.dist-info/WHEEL,sha256=wUyA8OaulRlbfwMtmQsvNngGrxQHAvkKcvRmdizlJi0,92
9
+ simple_icd_10_cm-1.5.0.dist-info/top_level.txt,sha256=6SUf2VHvXP1HmldCp4mtErBEUJHSI0FH39s9aYDAmx4,17
10
+ simple_icd_10_cm-1.5.0.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (80.9.0)
2
+ Generator: setuptools (80.10.2)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2021-2025 Stefano Travasci
3
+ Copyright (c) 2021-2026 Stefano Travasci
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
@@ -1,10 +0,0 @@
1
- simple_icd_10_cm/__init__.py,sha256=Z1KTvT112WEfhmGoTFyloLMte55xISt0t0CQAeUysRk,31
2
- simple_icd_10_cm/simple_icd_10_cm.py,sha256=v60WTRDzXHg0qKE2EGGcGaRkg7YXid6fNVylCA1nZm4,22666
3
- simple_icd_10_cm/data/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- simple_icd_10_cm/data/code-list-April-2025.txt,sha256=75Pd9bOV14HTokVY-lw2v5jD_Dt3sYb2Dm_xlaNNcWI,794065
5
- simple_icd_10_cm/data/icd10cm-tabular-April-2025.xml,sha256=1s31KD81oQLXfq0jbsjIkp8iun8zVtut_xjt4Bo6PeU,9658391
6
- simple_icd_10_cm-1.3.0.dist-info/licenses/LICENSE,sha256=1xfY0kTzNfuYqsJWuzVqnKQUB-vJFxLS2w3AeZRTJgw,1099
7
- simple_icd_10_cm-1.3.0.dist-info/METADATA,sha256=8YzpgNXrbzPrdV7iER5d6I_ZgyhK854LEFoHH7Z6D5o,37479
8
- simple_icd_10_cm-1.3.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
- simple_icd_10_cm-1.3.0.dist-info/top_level.txt,sha256=6SUf2VHvXP1HmldCp4mtErBEUJHSI0FH39s9aYDAmx4,17
10
- simple_icd_10_cm-1.3.0.dist-info/RECORD,,