hccinfhir 0.1.3__py3-none-any.whl → 0.1.5__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.
- hccinfhir/__init__.py +2 -2
- hccinfhir/datamodels.py +17 -15
- hccinfhir/extractor_837.py +89 -22
- hccinfhir/hccinfhir.py +6 -5
- hccinfhir/model_calculate.py +19 -15
- hccinfhir/model_interactions.py +1 -0
- hccinfhir/sample_files/sample_837_12.txt +113 -0
- hccinfhir/samples.py +15 -15
- hccinfhir-0.1.5.dist-info/METADATA +693 -0
- hccinfhir-0.1.5.dist-info/RECORD +49 -0
- hccinfhir/sample_utils.py +0 -252
- hccinfhir-0.1.3.dist-info/METADATA +0 -390
- hccinfhir-0.1.3.dist-info/RECORD +0 -49
- /hccinfhir/{samples → sample_files}/__init__.py +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_0.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_1.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_10.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_11.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_2.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_3.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_4.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_5.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_6.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_7.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_8.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_837_9.txt +0 -0
- /hccinfhir/{samples → sample_files}/sample_eob_1.json +0 -0
- /hccinfhir/{samples → sample_files}/sample_eob_2.json +0 -0
- /hccinfhir/{samples → sample_files}/sample_eob_200.ndjson +0 -0
- /hccinfhir/{samples → sample_files}/sample_eob_3.json +0 -0
- {hccinfhir-0.1.3.dist-info → hccinfhir-0.1.5.dist-info}/WHEEL +0 -0
- {hccinfhir-0.1.3.dist-info → hccinfhir-0.1.5.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
hccinfhir/__init__.py,sha256=G_5m6jm3_BK5NdcZWoi0NEKJEsE_LjAU1RaLaL9xNPU,1043
|
|
2
|
+
hccinfhir/datamodels.py,sha256=Vp64Blwq8qWe-q1slCK8jMO9SIErUgkXusGZ0laXiWo,5936
|
|
3
|
+
hccinfhir/extractor.py,sha256=xL9c2VT-e2I7_c8N8j4Og42UEgVuCzyn9WFp3ntM5Ro,1822
|
|
4
|
+
hccinfhir/extractor_837.py,sha256=Jhxb5glRA8Yi8lbRwdltggXCvr_84nlsZEMRIgxyy4A,13169
|
|
5
|
+
hccinfhir/extractor_fhir.py,sha256=Rg_L0Vg5tz_L2VJ_jvZwWz6RMlPAkHwj4LiK-OWQvrQ,8458
|
|
6
|
+
hccinfhir/filter.py,sha256=j_yD2g6RBXVUV9trKkWzsQ35x3fRvfKUPvEXKUefI64,2007
|
|
7
|
+
hccinfhir/hccinfhir.py,sha256=6jvLoJF1TcqTGXbDA-_l3os6-xO68SKRr2l26LjzlgM,6982
|
|
8
|
+
hccinfhir/model_calculate.py,sha256=fPhSqfxN9-9EPyp8z-O8XZWMXmLU6Hun7OhXAgsgDFw,5688
|
|
9
|
+
hccinfhir/model_coefficients.py,sha256=ZsVY0S_X_BzDvcCmzCEf31v8uixbGmPAsR6nVEyCbIA,5530
|
|
10
|
+
hccinfhir/model_demographics.py,sha256=7W5NLW7aAjpn25BJzqfP8iSouD9uA6epGguJJ6BPuC0,7043
|
|
11
|
+
hccinfhir/model_dx_to_cc.py,sha256=guJny2Mb9z8YRNWCXGSIE3APbE06zwnA2NRkjAeUs60,1765
|
|
12
|
+
hccinfhir/model_hierarchies.py,sha256=0kdBmF_8e_ikMHBDhlw2I7jT3DupHfUn6o5mWj7v3Yo,2910
|
|
13
|
+
hccinfhir/model_interactions.py,sha256=Ev1m6d7cHpmlJ3xk6l0EHYCTIyPynbOMR9HnLPrtj_A,20344
|
|
14
|
+
hccinfhir/samples.py,sha256=cqytZ-N08cPGqnUXJfzrEhhHW2qL7SDPYLD9-CHXZIU,8446
|
|
15
|
+
hccinfhir/utils.py,sha256=AAHwzMSW8O9VZp1KLcdlN3OeBbxQtqQRtbTTdrKf7M0,2784
|
|
16
|
+
hccinfhir/data/__init__.py,sha256=SGiSkpGrnxbvtEFMMlk82NFHOE50hFXcgKwKUSuVZUg,45
|
|
17
|
+
hccinfhir/data/hcc_is_chronic.csv,sha256=eVVI4_8mQNkiBiNO3kattfT_zfcV18XgmiltdzZEXSo,17720
|
|
18
|
+
hccinfhir/data/ra_coefficients_2025.csv,sha256=I0S2hoJlfig-D0oSFxy0b3Piv7m9AzOGo2CwR6bcQ9w,215191
|
|
19
|
+
hccinfhir/data/ra_coefficients_2026.csv,sha256=0gfjGgVdIEWkBO01NaAbTLMzHCYINA0rf_zl8ojngCY,288060
|
|
20
|
+
hccinfhir/data/ra_dx_to_cc_2025.csv,sha256=4N7vF6VZndkl7d3Fo0cGsbAPAZdCjAizSH8BOKsZNAo,1618924
|
|
21
|
+
hccinfhir/data/ra_dx_to_cc_2026.csv,sha256=YT9HwQFUddL_bxuE9nxHWsBtZzojINL0DzABBMp6kms,1751007
|
|
22
|
+
hccinfhir/data/ra_eligible_cpt_hcpcs_2023.csv,sha256=VVoA4s0hsFmcRIugyFdbvSoeLcn7M7z0DITT6l4YqL8,39885
|
|
23
|
+
hccinfhir/data/ra_eligible_cpt_hcpcs_2024.csv,sha256=CawKImfCb8fFMDbWwqvNLRyRAda_u9N8Q3ne8QAAe54,40191
|
|
24
|
+
hccinfhir/data/ra_eligible_cpt_hcpcs_2025.csv,sha256=-tMvv2su5tsSbGUh6fZZCMUEkXInBpcTtbUCi2o_UwI,40359
|
|
25
|
+
hccinfhir/data/ra_eligible_cpt_hcpcs_2026.csv,sha256=EYGN7k_rgCpJe59lL_yNInUcCkdETDWGSFTXII3LZ0Y,40497
|
|
26
|
+
hccinfhir/data/ra_hierarchies_2025.csv,sha256=HQSPNloe6mvvwMgv8ZwYAfWKkT2b2eUvm4JQy6S_mVQ,13045
|
|
27
|
+
hccinfhir/data/ra_hierarchies_2026.csv,sha256=A6ZQZb0rpRWrySBB_KA5S4PGtMxWuzB2guU3aBE09v0,19596
|
|
28
|
+
hccinfhir/sample_files/__init__.py,sha256=SGiSkpGrnxbvtEFMMlk82NFHOE50hFXcgKwKUSuVZUg,45
|
|
29
|
+
hccinfhir/sample_files/sample_837_0.txt,sha256=eggrD259uHa05z2dfxWBpUDseSDp_AQcLyN_adpHyTw,5295
|
|
30
|
+
hccinfhir/sample_files/sample_837_1.txt,sha256=E155MdemSDYoXokuTXUZ6Br_RGGedYv5t5dh-eMRmuk,1322
|
|
31
|
+
hccinfhir/sample_files/sample_837_10.txt,sha256=zSJXI78vHAksA7FFQEVLvepefdpMM2_AexLyoDimV3Q,1129
|
|
32
|
+
hccinfhir/sample_files/sample_837_11.txt,sha256=suVxlZEllEOy6dYgbzGIphKW6hDAKdYQTZFM3Ob84q4,817
|
|
33
|
+
hccinfhir/sample_files/sample_837_12.txt,sha256=XpEacfnnd5SMYtgByVVbVP1Re1Vi-0t4g4egxzSYz_s,2885
|
|
34
|
+
hccinfhir/sample_files/sample_837_2.txt,sha256=2bvXzaiopr7pO3D3pL7nLxcpgqKXrhu3lbulveJDJ-I,1125
|
|
35
|
+
hccinfhir/sample_files/sample_837_3.txt,sha256=_B1Ktpsg7bOnA5BwZ7AUB0o7MME5AmLGuxzHq42FWok,1113
|
|
36
|
+
hccinfhir/sample_files/sample_837_4.txt,sha256=jpzyiHKmRGMqN1ij9s1X4zRj0a2PXwtee4mjn4BRF3g,1077
|
|
37
|
+
hccinfhir/sample_files/sample_837_5.txt,sha256=eYG4URk3zAr5UsnBKO5Wezo8dnL_HXGCMmXWJu0nxPA,1300
|
|
38
|
+
hccinfhir/sample_files/sample_837_6.txt,sha256=pnKQf2cVcZpUl8PGeYzZxRxjKOMcISN2dBObIMtLx7E,1331
|
|
39
|
+
hccinfhir/sample_files/sample_837_7.txt,sha256=5Y-x7ZA9d_qGaIrpVGfXKQBXqbxKiQ5CWOwEW1XGlds,1253
|
|
40
|
+
hccinfhir/sample_files/sample_837_8.txt,sha256=J-zm0fzp6F19r2SWfHcCtab9ezd3c4YaSRO-RatJ-c4,1250
|
|
41
|
+
hccinfhir/sample_files/sample_837_9.txt,sha256=nIIurZ0tLHw6ZfDM__U-WqV8Q0u4K3WP56-r3BnAEv8,1341
|
|
42
|
+
hccinfhir/sample_files/sample_eob_1.json,sha256=_NGSVR2ysFpx-DcTvyga6dFCzhQ8Vi9fNzQEMPz5awU,3124
|
|
43
|
+
hccinfhir/sample_files/sample_eob_2.json,sha256=FcnJcx0ApOczxjJ_uxVLzCep9THfNf4xs9Yf7hxk8e4,1769
|
|
44
|
+
hccinfhir/sample_files/sample_eob_200.ndjson,sha256=CxpjeQ1DCMUzZILaM68UEhfxO0p45YGhDDoCZeq8PxU,1917986
|
|
45
|
+
hccinfhir/sample_files/sample_eob_3.json,sha256=4BW4wOMBEEU9RDfJR15rBEvk0KNHyuMEh3e055y87Hc,2306
|
|
46
|
+
hccinfhir-0.1.5.dist-info/METADATA,sha256=2FjYMUdQ67RQHeayhpmAhCuVyEGYSnUxcVbyC1mTn8w,21979
|
|
47
|
+
hccinfhir-0.1.5.dist-info/WHEEL,sha256=C2FUgwZgiLbznR-k0b_5k3Ai_1aASOXDss3lzCUsUug,87
|
|
48
|
+
hccinfhir-0.1.5.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
49
|
+
hccinfhir-0.1.5.dist-info/RECORD,,
|
hccinfhir/sample_utils.py
DELETED
|
@@ -1,252 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Sample Data Module for HCCInFHIR
|
|
3
|
-
|
|
4
|
-
This module provides easy access to sample data files for testing and demonstration purposes.
|
|
5
|
-
End users can call functions to retrieve sample EOB (Explanation of Benefits) and 837 claim data.
|
|
6
|
-
"""
|
|
7
|
-
|
|
8
|
-
import importlib.resources
|
|
9
|
-
import json
|
|
10
|
-
from typing import List, Dict, Any, Union, Optional
|
|
11
|
-
from pathlib import Path
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
class SampleData:
|
|
15
|
-
"""
|
|
16
|
-
A class that provides access to sample data files included with the HCCInFHIR package.
|
|
17
|
-
|
|
18
|
-
This class allows end users to easily retrieve sample EOB and 837 claim data
|
|
19
|
-
for testing, development, and demonstration purposes.
|
|
20
|
-
"""
|
|
21
|
-
|
|
22
|
-
@staticmethod
|
|
23
|
-
def get_eob_sample(case_number: int = 1) -> Dict[str, Any]:
|
|
24
|
-
"""
|
|
25
|
-
Retrieve a specific EOB sample by case number.
|
|
26
|
-
|
|
27
|
-
Args:
|
|
28
|
-
case_number: The case number (1, 2, or 3). Default is 1.
|
|
29
|
-
|
|
30
|
-
Returns:
|
|
31
|
-
A dictionary containing the EOB data
|
|
32
|
-
|
|
33
|
-
Raises:
|
|
34
|
-
ValueError: If case_number is not 1, 2, or 3
|
|
35
|
-
FileNotFoundError: If the sample file cannot be found
|
|
36
|
-
|
|
37
|
-
Example:
|
|
38
|
-
>>> sample_data = SampleData.get_eob_sample(1)
|
|
39
|
-
>>> print(sample_data['resourceType'])
|
|
40
|
-
'ExplanationOfBenefit'
|
|
41
|
-
"""
|
|
42
|
-
if case_number not in [1, 2, 3]:
|
|
43
|
-
raise ValueError("case_number must be 1, 2, or 3")
|
|
44
|
-
|
|
45
|
-
try:
|
|
46
|
-
with importlib.resources.open_text('hccinfhir.samples', f'sample_eob_{case_number}.json') as f:
|
|
47
|
-
return json.load(f)
|
|
48
|
-
except FileNotFoundError:
|
|
49
|
-
raise FileNotFoundError(f"Sample EOB case {case_number} not found")
|
|
50
|
-
|
|
51
|
-
@staticmethod
|
|
52
|
-
def get_eob_sample_list(limit: Optional[int] = None) -> List[Dict[str, Any]]:
|
|
53
|
-
"""
|
|
54
|
-
Retrieve a list of EOB samples from the large sample file.
|
|
55
|
-
|
|
56
|
-
Args:
|
|
57
|
-
limit: Maximum number of samples to return. If None, returns all 200 samples.
|
|
58
|
-
|
|
59
|
-
Returns:
|
|
60
|
-
A list of EOB data dictionaries
|
|
61
|
-
|
|
62
|
-
Raises:
|
|
63
|
-
FileNotFoundError: If the sample file cannot be found
|
|
64
|
-
|
|
65
|
-
Example:
|
|
66
|
-
>>> # Get first 10 samples
|
|
67
|
-
>>> samples = SampleData.get_eob_sample_list(limit=10)
|
|
68
|
-
>>> print(len(samples))
|
|
69
|
-
10
|
|
70
|
-
|
|
71
|
-
>>> # Get all 200 samples
|
|
72
|
-
>>> all_samples = SampleData.get_eob_sample_list()
|
|
73
|
-
>>> print(len(all_samples))
|
|
74
|
-
200
|
|
75
|
-
"""
|
|
76
|
-
try:
|
|
77
|
-
output = []
|
|
78
|
-
with importlib.resources.open_text('hccinfhir.samples', 'sample_eob_200.ndjson') as f:
|
|
79
|
-
for i, line in enumerate(f):
|
|
80
|
-
if limit is not None and i >= limit:
|
|
81
|
-
break
|
|
82
|
-
eob_data = json.loads(line)
|
|
83
|
-
output.append(eob_data)
|
|
84
|
-
return output
|
|
85
|
-
except FileNotFoundError:
|
|
86
|
-
raise FileNotFoundError("Sample EOB list file not found")
|
|
87
|
-
|
|
88
|
-
@staticmethod
|
|
89
|
-
def get_837_sample(case_number: int = 0) -> str:
|
|
90
|
-
"""
|
|
91
|
-
Retrieve a specific 837 claim sample by case number.
|
|
92
|
-
|
|
93
|
-
Args:
|
|
94
|
-
case_number: The case number (0 through 11). Default is 0.
|
|
95
|
-
|
|
96
|
-
Returns:
|
|
97
|
-
A string containing the 837 X12 claim data
|
|
98
|
-
|
|
99
|
-
Raises:
|
|
100
|
-
ValueError: If case_number is not between 0 and 11
|
|
101
|
-
FileNotFoundError: If the sample file cannot be found
|
|
102
|
-
|
|
103
|
-
Example:
|
|
104
|
-
>>> sample_837 = SampleData.get_837_sample(0)
|
|
105
|
-
>>> print("ISA" in sample_837)
|
|
106
|
-
True
|
|
107
|
-
"""
|
|
108
|
-
if case_number < 0 or case_number > 11:
|
|
109
|
-
raise ValueError("case_number must be between 0 and 11")
|
|
110
|
-
|
|
111
|
-
try:
|
|
112
|
-
with importlib.resources.open_text('hccinfhir.samples', f'sample_837_{case_number}.txt') as f:
|
|
113
|
-
return f.read()
|
|
114
|
-
except FileNotFoundError:
|
|
115
|
-
raise FileNotFoundError(f"Sample 837 case {case_number} not found")
|
|
116
|
-
|
|
117
|
-
@staticmethod
|
|
118
|
-
def get_837_sample_list(case_numbers: Optional[List[int]] = None) -> List[str]:
|
|
119
|
-
"""
|
|
120
|
-
Retrieve multiple 837 claim samples.
|
|
121
|
-
|
|
122
|
-
Args:
|
|
123
|
-
case_numbers: List of case numbers to retrieve. If None, returns all 12 samples.
|
|
124
|
-
|
|
125
|
-
Returns:
|
|
126
|
-
A list of 837 X12 claim data strings
|
|
127
|
-
|
|
128
|
-
Raises:
|
|
129
|
-
ValueError: If any case_number is not between 0 and 11
|
|
130
|
-
FileNotFoundError: If any sample file cannot be found
|
|
131
|
-
|
|
132
|
-
Example:
|
|
133
|
-
>>> # Get specific cases
|
|
134
|
-
>>> samples = SampleData.get_837_sample_list([0, 1, 2])
|
|
135
|
-
>>> print(len(samples))
|
|
136
|
-
3
|
|
137
|
-
|
|
138
|
-
>>> # Get all samples
|
|
139
|
-
>>> all_samples = SampleData.get_837_sample_list()
|
|
140
|
-
>>> print(len(all_samples))
|
|
141
|
-
12
|
|
142
|
-
"""
|
|
143
|
-
if case_numbers is None:
|
|
144
|
-
case_numbers = list(range(12)) # 0 through 11
|
|
145
|
-
|
|
146
|
-
# Validate case numbers
|
|
147
|
-
for case_num in case_numbers:
|
|
148
|
-
if case_num < 0 or case_num > 11:
|
|
149
|
-
raise ValueError(f"case_number {case_num} must be between 0 and 11")
|
|
150
|
-
|
|
151
|
-
output = []
|
|
152
|
-
for case_num in case_numbers:
|
|
153
|
-
try:
|
|
154
|
-
with importlib.resources.open_text('hccinfhir.samples', f'sample_837_{case_num}.txt') as f:
|
|
155
|
-
output.append(f.read())
|
|
156
|
-
except FileNotFoundError:
|
|
157
|
-
raise FileNotFoundError(f"Sample 837 case {case_num} not found")
|
|
158
|
-
|
|
159
|
-
return output
|
|
160
|
-
|
|
161
|
-
@staticmethod
|
|
162
|
-
def list_available_samples() -> Dict[str, Any]:
|
|
163
|
-
"""
|
|
164
|
-
Get information about all available sample data.
|
|
165
|
-
|
|
166
|
-
Returns:
|
|
167
|
-
A dictionary containing information about available samples
|
|
168
|
-
|
|
169
|
-
Example:
|
|
170
|
-
>>> info = SampleData.list_available_samples()
|
|
171
|
-
>>> print(info['eob_samples'])
|
|
172
|
-
['sample_eob_1.json', 'sample_eob_2.json', 'sample_eob_3.json', 'sample_eob_200.ndjson']
|
|
173
|
-
"""
|
|
174
|
-
return {
|
|
175
|
-
"eob_samples": [
|
|
176
|
-
"sample_eob_1.json",
|
|
177
|
-
"sample_eob_2.json",
|
|
178
|
-
"sample_eob_3.json",
|
|
179
|
-
"sample_eob_200.ndjson"
|
|
180
|
-
],
|
|
181
|
-
"eob_case_numbers": [1, 2, 3],
|
|
182
|
-
"eob_list_size": 200,
|
|
183
|
-
"837_samples": [f"sample_837_{i}.txt" for i in range(12)],
|
|
184
|
-
"837_case_numbers": list(range(12)),
|
|
185
|
-
"description": {
|
|
186
|
-
"eob": "Explanation of Benefits (FHIR resources) for testing HCC calculations",
|
|
187
|
-
"837": "X12 837 claim data for testing claim processing"
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
# Convenience functions for easy access
|
|
193
|
-
def get_eob_sample(case_number: int = 1) -> Dict[str, Any]:
|
|
194
|
-
"""
|
|
195
|
-
Convenience function to get an EOB sample.
|
|
196
|
-
|
|
197
|
-
Args:
|
|
198
|
-
case_number: The case number (1, 2, or 3). Default is 1.
|
|
199
|
-
|
|
200
|
-
Returns:
|
|
201
|
-
A dictionary containing the EOB data
|
|
202
|
-
"""
|
|
203
|
-
return SampleData.get_eob_sample(case_number)
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
def get_eob_sample_list(limit: Optional[int] = None) -> List[Dict[str, Any]]:
|
|
207
|
-
"""
|
|
208
|
-
Convenience function to get a list of EOB samples.
|
|
209
|
-
|
|
210
|
-
Args:
|
|
211
|
-
limit: Maximum number of samples to return. If None, returns all 200 samples.
|
|
212
|
-
|
|
213
|
-
Returns:
|
|
214
|
-
A list of EOB data dictionaries
|
|
215
|
-
"""
|
|
216
|
-
return SampleData.get_eob_sample_list(limit)
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
def get_837_sample(case_number: int = 0) -> str:
|
|
220
|
-
"""
|
|
221
|
-
Convenience function to get an 837 claim sample.
|
|
222
|
-
|
|
223
|
-
Args:
|
|
224
|
-
case_number: The case number (0 through 11). Default is 0.
|
|
225
|
-
|
|
226
|
-
Returns:
|
|
227
|
-
A string containing the 837 X12 claim data
|
|
228
|
-
"""
|
|
229
|
-
return SampleData.get_837_sample(case_number)
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
def get_837_sample_list(case_numbers: Optional[List[int]] = None) -> List[str]:
|
|
233
|
-
"""
|
|
234
|
-
Convenience function to get multiple 837 claim samples.
|
|
235
|
-
|
|
236
|
-
Args:
|
|
237
|
-
case_numbers: List of case numbers to retrieve. If None, returns all 12 samples.
|
|
238
|
-
|
|
239
|
-
Returns:
|
|
240
|
-
A list of 837 X12 claim data strings
|
|
241
|
-
"""
|
|
242
|
-
return SampleData.get_837_sample_list(case_numbers)
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
def list_available_samples() -> Dict[str, Any]:
|
|
246
|
-
"""
|
|
247
|
-
Convenience function to get information about available samples.
|
|
248
|
-
|
|
249
|
-
Returns:
|
|
250
|
-
A dictionary containing information about available samples
|
|
251
|
-
"""
|
|
252
|
-
return SampleData.list_available_samples()
|
|
@@ -1,390 +0,0 @@
|
|
|
1
|
-
Metadata-Version: 2.3
|
|
2
|
-
Name: hccinfhir
|
|
3
|
-
Version: 0.1.3
|
|
4
|
-
Summary: HCC Algorithm for FHIR Resources
|
|
5
|
-
Project-URL: Homepage, https://github.com/mimilabs/hccinfhir
|
|
6
|
-
Project-URL: Issues, https://github.com/mimilabs/hccinfhir/issues
|
|
7
|
-
Author-email: Yubin Park <yubin.park@mimilabs.ai>
|
|
8
|
-
Classifier: License :: OSI Approved :: Apache Software License
|
|
9
|
-
Classifier: Operating System :: OS Independent
|
|
10
|
-
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Requires-Python: >=3.8
|
|
12
|
-
Requires-Dist: pydantic>=2.10.3
|
|
13
|
-
Description-Content-Type: text/markdown
|
|
14
|
-
|
|
15
|
-
# `hccinfhir` (HCC in FHIR)
|
|
16
|
-
A Python library for extracting standardized service-level data from FHIR ExplanationOfBenefit resources, with a focus on supporting HCC (Hierarchical Condition Category) risk adjustment calculations.
|
|
17
|
-
|
|
18
|
-
## Features
|
|
19
|
-
- Extract diagnosis codes, procedures, providers, and other key data elements from FHIR EOBs
|
|
20
|
-
- Support for both BCDA (Blue Button 2.0) and standard FHIR R4 formats
|
|
21
|
-
- Pydantic models for type safety and data validation
|
|
22
|
-
- Standardized Service Level Data (SLD) output format
|
|
23
|
-
- Multiple HCC model support (V22, V24, V28, ESRD V21, ESRD V24, RxHCC V08)
|
|
24
|
-
- Flexible input options: FHIR EOBs, service data, or direct diagnosis codes
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
## Installation
|
|
28
|
-
```bash
|
|
29
|
-
pip install hccinfhir
|
|
30
|
-
```
|
|
31
|
-
|
|
32
|
-
## Why FHIR-Based HCC Processing?
|
|
33
|
-
Risk Adjustment calculations traditionally rely on processed claims data, leading to information loss and reconciliation challenges. `hccinfhir` processes FHIR resources directly because:
|
|
34
|
-
- FHIR represents the source of truth with complete clinical and administrative data
|
|
35
|
-
- Risk Adjustment requires multiple data elements beyond diagnosis codes
|
|
36
|
-
- Direct processing eliminates data transformation errors and simplifies reconciliation
|
|
37
|
-
|
|
38
|
-
## Data Model & Flexibility
|
|
39
|
-
While built for native FHIR processing, `hccinfhir` works with any data source that can be transformed into the SLD (Service Level Data) format:
|
|
40
|
-
|
|
41
|
-
```python
|
|
42
|
-
sld = [{
|
|
43
|
-
"procedure_code": "99214",
|
|
44
|
-
"diagnosis_codes": ["E11.9", "I10"],
|
|
45
|
-
"claim_type": "71",
|
|
46
|
-
"provider_specialty": "01",
|
|
47
|
-
"service_date": "2024-01-15"
|
|
48
|
-
}, ...]
|
|
49
|
-
```
|
|
50
|
-
|
|
51
|
-
Or, for direct risk score calculation from a list of diagnosis codes, you only need the model name, diagnosis codes, and basic demographic factors:
|
|
52
|
-
|
|
53
|
-
```python
|
|
54
|
-
from hccinfhir.model_calculate import calculate_raf
|
|
55
|
-
|
|
56
|
-
diagnosis_codes = ['E119', 'I509'] # Diabetes without complications, Heart failure
|
|
57
|
-
age = 67
|
|
58
|
-
sex = 'F'
|
|
59
|
-
model_name = "CMS-HCC Model V24"
|
|
60
|
-
|
|
61
|
-
result = calculate_raf(
|
|
62
|
-
diagnosis_codes=diagnosis_codes,
|
|
63
|
-
model_name=model_name,
|
|
64
|
-
age=age,
|
|
65
|
-
sex=sex
|
|
66
|
-
)
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
For more details on the SLD format, see the `datamodels.py` file.
|
|
71
|
-
|
|
72
|
-
## Sample Data
|
|
73
|
-
|
|
74
|
-
The package includes comprehensive sample data for testing and demonstration purposes:
|
|
75
|
-
|
|
76
|
-
```python
|
|
77
|
-
from hccinfhir import (
|
|
78
|
-
get_eob_sample,
|
|
79
|
-
get_eob_sample_list,
|
|
80
|
-
get_837_sample,
|
|
81
|
-
get_837_sample_list,
|
|
82
|
-
list_available_samples
|
|
83
|
-
)
|
|
84
|
-
|
|
85
|
-
# Get individual EOB samples (cases 1, 2, or 3)
|
|
86
|
-
eob_data = get_eob_sample(1)
|
|
87
|
-
|
|
88
|
-
# Get multiple EOB samples (up to 200 available)
|
|
89
|
-
eob_list = get_eob_sample_list(limit=10)
|
|
90
|
-
|
|
91
|
-
# Get 837 claim samples (cases 0 through 11)
|
|
92
|
-
claim_data = get_837_sample(0)
|
|
93
|
-
|
|
94
|
-
# Get information about available samples
|
|
95
|
-
info = list_available_samples()
|
|
96
|
-
```
|
|
97
|
-
|
|
98
|
-
For detailed usage examples, see the `examples/sample_data_usage.py` file.
|
|
99
|
-
|
|
100
|
-
## Core Components
|
|
101
|
-
|
|
102
|
-
### 1. Extractor Module
|
|
103
|
-
Processes FHIR ExplanationOfBenefit resources to extract Minimum Data Elements (MDE):
|
|
104
|
-
```python
|
|
105
|
-
from hccinfhir.extractor import extract_sld, extract_sld_list
|
|
106
|
-
|
|
107
|
-
sld = extract_sld(eob_data) # Process single EOB
|
|
108
|
-
|
|
109
|
-
sld_list = extract_sld_list([eob1, eob2]) # Process multiple EOBs
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
### 2. Filter Module
|
|
113
|
-
Implements claim filtering rules:
|
|
114
|
-
- Inpatient/outpatient criteria - Type of Bill + Eligible CPT/HCPCS
|
|
115
|
-
- Professional service requirements - Eligible CPT/HCPCS
|
|
116
|
-
- Provider validation (Not in scope for this release, applicable to RAPS)
|
|
117
|
-
```python
|
|
118
|
-
from hccinfhir.filter import apply_filter
|
|
119
|
-
|
|
120
|
-
filtered_sld = apply_filter(sld_list)
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
### 3. Logic Module
|
|
125
|
-
Implements core HCC calculation logic:
|
|
126
|
-
- Maps diagnosis codes to HCC categories
|
|
127
|
-
- Applies hierarchical rules and interactions
|
|
128
|
-
- Calculates final RAF scores
|
|
129
|
-
- Integrates with standard CMS data files
|
|
130
|
-
|
|
131
|
-
```python
|
|
132
|
-
from hccinfhir.model_calculate import calculate_raf
|
|
133
|
-
|
|
134
|
-
diagnosis_codes = ['E119', 'I509'] # Diabetes without complications, Heart failure
|
|
135
|
-
result = calculate_raf(
|
|
136
|
-
diagnosis_codes=diagnosis_codes,
|
|
137
|
-
model_name="CMS-HCC Model V24",
|
|
138
|
-
age=67,
|
|
139
|
-
sex='F'
|
|
140
|
-
)
|
|
141
|
-
```
|
|
142
|
-
|
|
143
|
-
### 4. HCCInFHIR Class
|
|
144
|
-
The main processor class that integrates extraction, filtering, and calculation components:
|
|
145
|
-
|
|
146
|
-
```python
|
|
147
|
-
from hccinfhir.hccinfhir import HCCInFHIR
|
|
148
|
-
from hccinfhir.datamodels import Demographics
|
|
149
|
-
|
|
150
|
-
# Initialize with custom configuration
|
|
151
|
-
hcc_processor = HCCInFHIR(
|
|
152
|
-
filter_claims=True, # Enable claim filtering
|
|
153
|
-
model_name="CMS-HCC Model V28", # Choose HCC model version
|
|
154
|
-
proc_filtering_filename="ra_eligible_cpt_hcpcs_2025.csv", # CPT/HCPCS filtering rules
|
|
155
|
-
dx_cc_mapping_filename="ra_dx_to_cc_2025.csv" # Diagnosis to CC mapping
|
|
156
|
-
)
|
|
157
|
-
|
|
158
|
-
# Define beneficiary demographics
|
|
159
|
-
demographics = {
|
|
160
|
-
age=67,
|
|
161
|
-
sex='F'
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
# Method 1: Process FHIR EOB resources
|
|
165
|
-
raf_result = hcc_processor.run(eob_list, demographics)
|
|
166
|
-
|
|
167
|
-
# Method 2: Process service level data
|
|
168
|
-
service_data = [{
|
|
169
|
-
"procedure_code": "99214",
|
|
170
|
-
"claim_diagnosis_codes": ["E11.9", "I10"],
|
|
171
|
-
"claim_type": "71",
|
|
172
|
-
"service_date": "2024-01-15"
|
|
173
|
-
}]
|
|
174
|
-
raf_result = hcc_processor.run_from_service_data(service_data, demographics)
|
|
175
|
-
|
|
176
|
-
# Method 3: Direct diagnosis processing
|
|
177
|
-
diagnosis_codes = ['E119', 'I509']
|
|
178
|
-
raf_result = hcc_processor.calculate_from_diagnosis(diagnosis_codes, demographics)
|
|
179
|
-
|
|
180
|
-
# RAF Result contains:
|
|
181
|
-
print(f"Risk Score: {raf_result['risk_score']}")
|
|
182
|
-
print(f"HCC List: {raf_result['hcc_list']}")
|
|
183
|
-
print(f"CC to Diagnosis Mapping: {raf_result['cc_to_dx']}")
|
|
184
|
-
print(f"Applied Coefficients: {raf_result['coefficients']}")
|
|
185
|
-
print(f"Applied Interactions: {raf_result['interactions']}")
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
The HCCInFHIR class provides three main processing methods:
|
|
189
|
-
|
|
190
|
-
1. `run(eob_list, demographics)`: Process FHIR ExplanationOfBenefit resources
|
|
191
|
-
- Extracts service data from FHIR resources
|
|
192
|
-
- Applies filtering rules if enabled
|
|
193
|
-
- Calculates RAF scores using the specified model
|
|
194
|
-
|
|
195
|
-
2. `run_from_service_data(service_data, demographics)`: Process standardized service data
|
|
196
|
-
- Accepts pre-formatted service level data
|
|
197
|
-
- Validates data structure using Pydantic models
|
|
198
|
-
- Applies filtering and calculates RAF scores
|
|
199
|
-
|
|
200
|
-
3. `calculate_from_diagnosis(diagnosis_codes, demographics)`: Direct diagnosis processing
|
|
201
|
-
- Processes raw diagnosis codes without service context
|
|
202
|
-
- Useful for quick RAF calculations or validation
|
|
203
|
-
- Bypasses service-level filtering
|
|
204
|
-
|
|
205
|
-
Each method returns a RAFResult containing:
|
|
206
|
-
- Final risk score
|
|
207
|
-
- List of HCCs
|
|
208
|
-
- Mapping of condition categories to diagnosis codes
|
|
209
|
-
- Applied coefficients and interactions
|
|
210
|
-
- Processed service level data (when applicable)
|
|
211
|
-
|
|
212
|
-
## Testing
|
|
213
|
-
|
|
214
|
-
After installing `hatch`
|
|
215
|
-
```bash
|
|
216
|
-
$ hatch shell
|
|
217
|
-
$ pip install -e .
|
|
218
|
-
$ pytest tests/*
|
|
219
|
-
```
|
|
220
|
-
|
|
221
|
-
## Dependencies
|
|
222
|
-
- Pydantic >= 2.10.3
|
|
223
|
-
- Standard Python libraries
|
|
224
|
-
|
|
225
|
-
## Research: FHIR BCDA and 837 Field Mapping Analysis
|
|
226
|
-
|
|
227
|
-
### Core Identifiers
|
|
228
|
-
| Field | 837 Source | FHIR BCDA Source | Alignment Analysis |
|
|
229
|
-
|-------|------------|------------------|-------------------|
|
|
230
|
-
| claim_id | CLM01 segment | eob.id | ✓ Direct mapping |
|
|
231
|
-
| patient_id | NM109 when NM101='IL' | eob.patient.reference (last part after '/') | ✓ Aligned but different formats |
|
|
232
|
-
|
|
233
|
-
### Provider Information
|
|
234
|
-
| Field | 837 Source | FHIR BCDA Source | Alignment Analysis |
|
|
235
|
-
|-------|------------|-------------------|-------------------|
|
|
236
|
-
| performing_provider_npi | NM109 when NM101='82' and NM108='XX' | careTeam member with role 'performing'/'rendering' | ✓ Aligned |
|
|
237
|
-
| billing_provider_npi | NM109 when NM101='85' and NM108='XX' | contained resources with NPI system identifier | ✓ Conceptually aligned |
|
|
238
|
-
| provider_specialty | PRV03 when PRV01='PE' | careTeam member qualification with specialty system | ✓ Aligned but different code systems |
|
|
239
|
-
|
|
240
|
-
### Claim Type Information
|
|
241
|
-
| Field | 837 Source | FHIR BCDA Source | Alignment Analysis |
|
|
242
|
-
|-------|------------|-------------------|-------------------|
|
|
243
|
-
| claim_type | GS08 (mapped via CLAIM_TYPES) | eob.type with claim_type system | ✓ Aligned but different coding |
|
|
244
|
-
| facility_type | CLM05-1 (837I only) | facility.extension with facility_type system | ✓ Aligned for institutional claims |
|
|
245
|
-
| service_type | CLM05-2 (837I only) | extension or eob.type with service_type system | ✓ Aligned for institutional claims |
|
|
246
|
-
|
|
247
|
-
### Service Line Information
|
|
248
|
-
| Field | 837 Source | FHIR BCDA Source | Alignment Analysis |
|
|
249
|
-
|-------|------------|-------------------|-------------------|
|
|
250
|
-
| procedure_code | SV1/SV2 segment, element 2 | item.productOrService with pr system | ✓ Aligned |
|
|
251
|
-
| ndc | LIN segment after service line | item.productOrService with ndc system or extension | ✓ Aligned but different locations |
|
|
252
|
-
| quantity | SV1/SV2 element 4 | item.quantity.value | ✓ Direct mapping |
|
|
253
|
-
| quantity_unit | SV1/SV2 element 5 | item.quantity.unit | ✓ Direct mapping |
|
|
254
|
-
| service_date | DTP segment with qualifier 472 | item.servicedPeriod or eob.billablePeriod | ✓ Aligned |
|
|
255
|
-
| place_of_service | SV1 element 6 | item.locationCodeableConcept with place_of_service system | ✓ Aligned |
|
|
256
|
-
| modifiers | SV1/SV2 segment, additional qualifiers | item.modifier with pr system | ✓ Aligned |
|
|
257
|
-
|
|
258
|
-
### Diagnosis Information
|
|
259
|
-
| Field | 837 Source | FHIR BCDA Source | Alignment Analysis |
|
|
260
|
-
|-------|------------|-------------------|-------------------|
|
|
261
|
-
| linked_diagnosis_codes | SV1/SV2 diagnosis pointers + HI segment codes | item.diagnosisSequence + diagnosis lookup | ✓ Aligned but different structure |
|
|
262
|
-
| claim_diagnosis_codes | HI segment codes | diagnosis array with icd10cm/icd10 systems | ✓ Aligned |
|
|
263
|
-
|
|
264
|
-
### Additional Fields
|
|
265
|
-
| Field | 837 Source | FHIR BCDA Source | Alignment Analysis |
|
|
266
|
-
|-------|------------|-------------------|-------------------|
|
|
267
|
-
| allowed_amount | Not available in 837 | item.adjudication with 'eligible' category | ⚠️ Only in FHIR |
|
|
268
|
-
|
|
269
|
-
### Key Differences and Notes
|
|
270
|
-
|
|
271
|
-
1. **Structural Differences**:
|
|
272
|
-
- 837 uses a segment-based approach with positional elements
|
|
273
|
-
- FHIR uses a nested object structure with explicit systems and codes
|
|
274
|
-
|
|
275
|
-
2. **Code Systems**:
|
|
276
|
-
- FHIR explicitly defines systems for each code (via SYSTEMS constant)
|
|
277
|
-
- 837 uses implicit coding based on segment position and qualifiers
|
|
278
|
-
|
|
279
|
-
3. **Data Validation**:
|
|
280
|
-
- FHIR implementation uses Pydantic models for validation
|
|
281
|
-
- 837 implements manual validation and parsing
|
|
282
|
-
|
|
283
|
-
4. **Diagnosis Handling**:
|
|
284
|
-
- 837: Direct parsing from HI segment with position-based lookup
|
|
285
|
-
- FHIR: Uses sequence numbers and separate diagnosis array
|
|
286
|
-
|
|
287
|
-
5. **Provider Information**:
|
|
288
|
-
- 837: Direct from NM1 segments with role qualifiers
|
|
289
|
-
- FHIR: Through careTeam structure with role coding
|
|
290
|
-
|
|
291
|
-
### TODO: Enhancement Suggestions
|
|
292
|
-
|
|
293
|
-
1. Consider adding validation for code systems in 837 parser to match FHIR's explicitness
|
|
294
|
-
2. Standardize date handling between both implementations
|
|
295
|
-
3. Add support for allowed_amount in 837 if available in different segments
|
|
296
|
-
4. Consider adding more robust error handling in both implementations
|
|
297
|
-
|
|
298
|
-
## Data Files
|
|
299
|
-
|
|
300
|
-
`ra_dx_to_cc_mapping_2026.csv`
|
|
301
|
-
```sql
|
|
302
|
-
WITH latest_years AS (
|
|
303
|
-
SELECT
|
|
304
|
-
model_name,
|
|
305
|
-
MAX(year) as latest_year
|
|
306
|
-
FROM mimi_ws_1.cmspayment.ra_dx_to_cc_mapping
|
|
307
|
-
WHERE model_type = 'Initial'
|
|
308
|
-
AND year <= 2026 -- Don't go beyond 2026
|
|
309
|
-
GROUP BY model_name
|
|
310
|
-
)
|
|
311
|
-
SELECT
|
|
312
|
-
r.diagnosis_code,
|
|
313
|
-
r.cc,
|
|
314
|
-
r.model_name
|
|
315
|
-
FROM mimi_ws_1.cmspayment.ra_dx_to_cc_mapping r
|
|
316
|
-
INNER JOIN latest_years l
|
|
317
|
-
ON r.model_name = l.model_name
|
|
318
|
-
AND r.year = l.latest_year
|
|
319
|
-
WHERE r.model_type = 'Initial'
|
|
320
|
-
ORDER BY r.model_name, r.diagnosis_code;
|
|
321
|
-
```
|
|
322
|
-
|
|
323
|
-
`ra_hierarchies_2026.csv`
|
|
324
|
-
```sql
|
|
325
|
-
WITH latest_dates AS (
|
|
326
|
-
SELECT
|
|
327
|
-
model_domain,
|
|
328
|
-
model_version,
|
|
329
|
-
model_fullname,
|
|
330
|
-
MAX(eff_last_date) as latest_eff_last_date
|
|
331
|
-
FROM mimi_ws_1.cmspayment.ra_hierarchies
|
|
332
|
-
GROUP BY model_domain, model_version, model_fullname
|
|
333
|
-
)
|
|
334
|
-
SELECT
|
|
335
|
-
r.cc_parent,
|
|
336
|
-
r.cc_child,
|
|
337
|
-
r.model_domain,
|
|
338
|
-
r.model_version,
|
|
339
|
-
r.model_fullname
|
|
340
|
-
FROM mimi_ws_1.cmspayment.ra_hierarchies r
|
|
341
|
-
INNER JOIN latest_dates l
|
|
342
|
-
ON r.model_domain = l.model_domain
|
|
343
|
-
AND r.model_version = l.model_version
|
|
344
|
-
AND r.model_fullname = l.model_fullname
|
|
345
|
-
AND r.eff_last_date = l.latest_eff_last_date
|
|
346
|
-
ORDER BY r.model_domain, r.model_version, r.model_fullname, r.cc_parent, r.cc_child;
|
|
347
|
-
```
|
|
348
|
-
|
|
349
|
-
`ra_coefficients_2026.csv`
|
|
350
|
-
```sql
|
|
351
|
-
WITH preferred_records AS (
|
|
352
|
-
SELECT
|
|
353
|
-
model_domain,
|
|
354
|
-
model_version,
|
|
355
|
-
MAX(eff_last_date) as latest_eff_last_date
|
|
356
|
-
FROM mimi_ws_1.cmspayment.ra_coefficients
|
|
357
|
-
GROUP BY model_domain, model_version
|
|
358
|
-
)
|
|
359
|
-
SELECT
|
|
360
|
-
r.coefficient,
|
|
361
|
-
r.value,
|
|
362
|
-
r.model_domain,
|
|
363
|
-
r.model_version
|
|
364
|
-
FROM mimi_ws_1.cmspayment.ra_coefficients r
|
|
365
|
-
INNER JOIN preferred_records p
|
|
366
|
-
ON r.model_domain = p.model_domain
|
|
367
|
-
AND r.model_version = p.model_version
|
|
368
|
-
AND r.eff_last_date = p.latest_eff_last_date
|
|
369
|
-
ORDER BY r.model_domain, r.model_version, r.coefficient;
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
`ra_eligible_cpt_hcpcs_2026.csv`
|
|
373
|
-
```sql
|
|
374
|
-
SELECT DISTINCT cpt_hcpcs_code
|
|
375
|
-
FROM mimi_ws_1.cmspayment.ra_eligible_cpt_hcpcs
|
|
376
|
-
WHERE is_included = 'yes' AND YEAR(mimi_src_file_date) = 2025;
|
|
377
|
-
```
|
|
378
|
-
|
|
379
|
-
## Contributing
|
|
380
|
-
Join us at [mimilabs](https://mimilabs.ai/signup). Reference data available in MIMILabs data lakehouse.
|
|
381
|
-
|
|
382
|
-
## Publishing (only for those maintainers...)
|
|
383
|
-
Inside the hatch
|
|
384
|
-
```bash
|
|
385
|
-
$ hatch build
|
|
386
|
-
$ hatch publish
|
|
387
|
-
```
|
|
388
|
-
|
|
389
|
-
## License
|
|
390
|
-
Apache License 2.0
|