hccinfhir 0.1.0__py3-none-any.whl → 0.1.2__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 +47 -1
- hccinfhir/data/ra_coefficients_2026.csv +8414 -0
- hccinfhir/data/ra_dx_to_cc_2026.csv +58986 -0
- hccinfhir/data/ra_eligible_cpt_hcpcs_2026.csv +6748 -0
- hccinfhir/data/ra_hierarchies_2026.csv +725 -0
- hccinfhir/datamodels.py +4 -2
- hccinfhir/filter.py +17 -19
- hccinfhir/hccinfhir.py +6 -5
- hccinfhir/model_calculate.py +20 -3
- hccinfhir/model_coefficients.py +3 -1
- hccinfhir/model_demographics.py +8 -0
- hccinfhir/model_dx_to_cc.py +1 -1
- hccinfhir/model_hierarchies.py +26 -22
- hccinfhir/sample_utils.py +252 -0
- hccinfhir/samples.py +252 -0
- {hccinfhir-0.1.0.dist-info → hccinfhir-0.1.2.dist-info}/METADATA +102 -24
- {hccinfhir-0.1.0.dist-info → hccinfhir-0.1.2.dist-info}/RECORD +19 -13
- {hccinfhir-0.1.0.dist-info → hccinfhir-0.1.2.dist-info}/WHEEL +0 -0
- {hccinfhir-0.1.0.dist-info → hccinfhir-0.1.2.dist-info}/licenses/LICENSE +0 -0
hccinfhir/datamodels.py
CHANGED
|
@@ -14,11 +14,13 @@ ModelName = Literal[
|
|
|
14
14
|
ProcFilteringFilename = Literal[
|
|
15
15
|
"ra_eligible_cpt_hcpcs_2023.csv",
|
|
16
16
|
"ra_eligible_cpt_hcpcs_2024.csv",
|
|
17
|
-
"ra_eligible_cpt_hcpcs_2025.csv"
|
|
17
|
+
"ra_eligible_cpt_hcpcs_2025.csv",
|
|
18
|
+
"ra_eligible_cpt_hcpcs_2026.csv"
|
|
18
19
|
]
|
|
19
20
|
|
|
20
21
|
DxCCMappingFilename = Literal[
|
|
21
|
-
"ra_dx_to_cc_2025.csv"
|
|
22
|
+
"ra_dx_to_cc_2025.csv",
|
|
23
|
+
"ra_dx_to_cc_2026.csv"
|
|
22
24
|
]
|
|
23
25
|
|
|
24
26
|
class ServiceLevelData(BaseModel):
|
hccinfhir/filter.py
CHANGED
|
@@ -3,7 +3,7 @@ from hccinfhir.datamodels import ServiceLevelData
|
|
|
3
3
|
from hccinfhir.utils import load_proc_filtering
|
|
4
4
|
|
|
5
5
|
# use import importlib.resources to load the professional_cpt_fn file as a list of strings
|
|
6
|
-
professional_cpt_default_fn = '
|
|
6
|
+
professional_cpt_default_fn = 'ra_eligible_cpt_hcpcs_2025.csv'
|
|
7
7
|
professional_cpt_default = load_proc_filtering(professional_cpt_default_fn)
|
|
8
8
|
|
|
9
9
|
def apply_filter(
|
|
@@ -14,28 +14,26 @@ def apply_filter(
|
|
|
14
14
|
) -> List[ServiceLevelData]:
|
|
15
15
|
# tob (Type of Bill) Filter is based on:
|
|
16
16
|
# https://www.hhs.gov/guidance/sites/default/files/hhs-guidance-documents/2012181486-wq-092916_ra_webinar_slides_5cr_092816.pdf
|
|
17
|
-
# https://www.hhs.gov/guidance/sites/default/files/hhs-guidance-documents/
|
|
17
|
+
# https://www.hhs.gov/guidance/sites/default/files/hhs-guidance-documents/FinalEncounterDataDiagnosisFilteringLogic.pdf
|
|
18
18
|
|
|
19
|
-
#
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
# NOTE: If no facility_type or service_type, then the claim is professional, in our implementation.
|
|
20
|
+
# NOTE: The original CMS logic is for the "record" level, not the service level.
|
|
21
|
+
# Thus, when preparing the service level data, put all diagnosis codes into the diagnosis field.
|
|
22
22
|
|
|
23
|
-
# Break down the outpatient ToB into facility and service types
|
|
24
|
-
outpatient_facility_types = {tob[0] for tob in outpatient_tob}
|
|
25
|
-
outpatient_service_types = {tob[1] for tob in outpatient_tob}
|
|
26
|
-
|
|
27
|
-
# If ServiceLevelData has a facility_type and service_type, then filter the data based on the facility_type and service_type
|
|
28
|
-
# If not, then filter the data based on the CPT code
|
|
29
23
|
filtered_data = []
|
|
30
24
|
for item in data:
|
|
31
|
-
if item.facility_type
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
25
|
+
item_tob = '?' if item.facility_type is None else item.facility_type
|
|
26
|
+
item_tob += '?' if item.service_type is None else item.service_type
|
|
27
|
+
item_tob += 'X'
|
|
28
|
+
|
|
29
|
+
if '?' in item_tob: # professional claims
|
|
30
|
+
if item.procedure_code in professional_cpt:
|
|
37
31
|
filtered_data.append(item)
|
|
38
32
|
else:
|
|
39
|
-
if
|
|
40
|
-
filtered_data.append(item)
|
|
33
|
+
if item_tob in inpatient_tob:
|
|
34
|
+
filtered_data.append(item)
|
|
35
|
+
elif item_tob in outpatient_tob:
|
|
36
|
+
if item.procedure_code in professional_cpt:
|
|
37
|
+
filtered_data.append(item)
|
|
38
|
+
|
|
41
39
|
return filtered_data
|
hccinfhir/hccinfhir.py
CHANGED
|
@@ -16,16 +16,16 @@ class HCCInFHIR:
|
|
|
16
16
|
def __init__(self,
|
|
17
17
|
filter_claims: bool = True,
|
|
18
18
|
model_name: ModelName = "CMS-HCC Model V28",
|
|
19
|
-
proc_filtering_filename: ProcFilteringFilename = "
|
|
20
|
-
dx_cc_mapping_filename: DxCCMappingFilename = "
|
|
19
|
+
proc_filtering_filename: ProcFilteringFilename = "ra_eligible_cpt_hcpcs_2026.csv",
|
|
20
|
+
dx_cc_mapping_filename: DxCCMappingFilename = "ra_dx_to_cc_2026.csv"):
|
|
21
21
|
"""
|
|
22
22
|
Initialize the HCCInFHIR processor.
|
|
23
23
|
|
|
24
24
|
Args:
|
|
25
25
|
filter_claims: Whether to apply filtering rules to claims. Default is True.
|
|
26
26
|
model_name: The name of the model to use for the calculation. Default is "CMS-HCC Model V28".
|
|
27
|
-
proc_filtering_filename: The filename of the professional cpt filtering file. Default is "
|
|
28
|
-
dx_cc_mapping_filename: The filename of the dx to cc mapping file. Default is "
|
|
27
|
+
proc_filtering_filename: The filename of the professional cpt filtering file. Default is "ra_eligible_cpt_hcpcs_2026.csv".
|
|
28
|
+
dx_cc_mapping_filename: The filename of the dx to cc mapping file. Default is "ra_dx_to_cc_2026.csv".
|
|
29
29
|
"""
|
|
30
30
|
self.filter_claims = filter_claims
|
|
31
31
|
self.model_name = model_name
|
|
@@ -81,8 +81,9 @@ class HCCInFHIR:
|
|
|
81
81
|
|
|
82
82
|
# Extract and filter service level data
|
|
83
83
|
sld_list = extract_sld_list(eob_list)
|
|
84
|
+
|
|
84
85
|
if self.filter_claims:
|
|
85
|
-
sld_list = apply_filter(sld_list, self.professional_cpt)
|
|
86
|
+
sld_list = apply_filter(sld_list, professional_cpt=self.professional_cpt)
|
|
86
87
|
|
|
87
88
|
# Calculate RAF score
|
|
88
89
|
unique_dx_codes = self._get_unique_diagnosis_codes(sld_list)
|
hccinfhir/model_calculate.py
CHANGED
|
@@ -8,7 +8,7 @@ from hccinfhir.model_interactions import apply_interactions
|
|
|
8
8
|
from hccinfhir.utils import load_dx_to_cc_mapping, load_is_chronic
|
|
9
9
|
|
|
10
10
|
# Load default mappings from csv file
|
|
11
|
-
mapping_file_default = '
|
|
11
|
+
mapping_file_default = 'ra_dx_to_cc_2026.csv'
|
|
12
12
|
dx_to_cc_default = load_dx_to_cc_mapping(mapping_file_default)
|
|
13
13
|
|
|
14
14
|
# Load default mappings from csv file
|
|
@@ -87,8 +87,25 @@ def calculate_raf(diagnosis_codes: List[str],
|
|
|
87
87
|
if is_chronic_mapping.get((hcc, model_name), False):
|
|
88
88
|
hcc_chronic.add(hcc)
|
|
89
89
|
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
demographic_interactions = {}
|
|
91
|
+
for key, value in interactions.items():
|
|
92
|
+
if key.startswith('NMCAID_'):
|
|
93
|
+
demographic_interactions[key] = value
|
|
94
|
+
elif key.startswith('MCAID_'):
|
|
95
|
+
demographic_interactions[key] = value
|
|
96
|
+
elif key.startswith('LTI_'):
|
|
97
|
+
demographic_interactions[key] = value
|
|
98
|
+
elif key.startswith('OriginallyDisabled_'):
|
|
99
|
+
demographic_interactions[key] = value
|
|
100
|
+
|
|
101
|
+
coefficients_demographics = apply_coefficients(demographics,
|
|
102
|
+
set(),
|
|
103
|
+
demographic_interactions,
|
|
104
|
+
model_name)
|
|
105
|
+
coefficients_chronic_only = apply_coefficients(demographics,
|
|
106
|
+
hcc_chronic,
|
|
107
|
+
demographic_interactions,
|
|
108
|
+
model_name)
|
|
92
109
|
|
|
93
110
|
# Calculate risk scores
|
|
94
111
|
risk_score = sum(coefficients.values())
|
hccinfhir/model_coefficients.py
CHANGED
|
@@ -3,7 +3,7 @@ import importlib.resources
|
|
|
3
3
|
from hccinfhir.datamodels import ModelName, Demographics
|
|
4
4
|
|
|
5
5
|
# Load default mappings from csv file
|
|
6
|
-
coefficients_file_default = '
|
|
6
|
+
coefficients_file_default = 'ra_coefficients_2026.csv'
|
|
7
7
|
coefficients_default: Dict[Tuple[str, ModelName], float] = {} # (diagnosis_code, model_name) -> value
|
|
8
8
|
|
|
9
9
|
try:
|
|
@@ -80,6 +80,7 @@ def get_coefficent_prefix(demographics: Demographics,
|
|
|
80
80
|
return 'INS_'
|
|
81
81
|
|
|
82
82
|
if demographics.new_enrollee:
|
|
83
|
+
|
|
83
84
|
return 'SNPNE_' if demographics.snp else 'NE_'
|
|
84
85
|
|
|
85
86
|
# Community case
|
|
@@ -139,5 +140,6 @@ def apply_coefficients(demographics: Demographics,
|
|
|
139
140
|
value = coefficients[key]
|
|
140
141
|
output[interaction_key] = value
|
|
141
142
|
|
|
143
|
+
|
|
142
144
|
return output
|
|
143
145
|
|
hccinfhir/model_demographics.py
CHANGED
|
@@ -57,6 +57,14 @@ def categorize_demographics(age: Union[int, float],
|
|
|
57
57
|
raise ValueError("Sex must be 'M', 'F', '1', or '2'")
|
|
58
58
|
|
|
59
59
|
# Determine if person is disabled or originally disabled
|
|
60
|
+
# SAS code:
|
|
61
|
+
# DISABL = (&AGEF < 65 & &OREC ne "0");
|
|
62
|
+
# ORIGDS = (&OREC = '1')*(DISABL = 0);
|
|
63
|
+
# The vairable names can be misleading.
|
|
64
|
+
# disabled is true if the person is disabled and the age is less than 65
|
|
65
|
+
# - basically, the person is in Medicare due to disability not due to age
|
|
66
|
+
# orig_disabled is true if the person started Medicare due to disability, but now aged in
|
|
67
|
+
# - basically, the person is in Medicare due to age (not disability anymore)
|
|
60
68
|
disabled = age < 65 and (orec is not None and orec != "0")
|
|
61
69
|
orig_disabled = (orec is not None and orec == '1') and not disabled
|
|
62
70
|
|
hccinfhir/model_dx_to_cc.py
CHANGED
|
@@ -3,7 +3,7 @@ from hccinfhir.datamodels import ModelName
|
|
|
3
3
|
from hccinfhir.utils import load_dx_to_cc_mapping
|
|
4
4
|
|
|
5
5
|
# Load default mappings from csv file
|
|
6
|
-
mapping_file_default = '
|
|
6
|
+
mapping_file_default = 'ra_dx_to_cc_2026.csv'
|
|
7
7
|
dx_to_cc_default = load_dx_to_cc_mapping(mapping_file_default)
|
|
8
8
|
|
|
9
9
|
def get_cc(
|
hccinfhir/model_hierarchies.py
CHANGED
|
@@ -2,29 +2,33 @@ from typing import Dict, Set, Tuple
|
|
|
2
2
|
import importlib.resources
|
|
3
3
|
from hccinfhir.datamodels import ModelName
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
5
|
+
def load_hierarchies(hierarchies_file: str) -> Dict[Tuple[str, ModelName], Set[str]]:
|
|
6
|
+
"""Load hierarchies from a CSV file."""
|
|
7
|
+
hierarchies = {}
|
|
8
|
+
try:
|
|
9
|
+
with importlib.resources.open_text('hccinfhir.data', hierarchies_file) as f:
|
|
10
|
+
for line in f.readlines()[1:]: # Skip header
|
|
11
|
+
try:
|
|
12
|
+
cc_parent, cc_child, model_domain, model_version, _ = line.strip().split(',')
|
|
13
|
+
if model_domain == 'ESRD':
|
|
14
|
+
model_name = f"CMS-HCC {model_domain} Model {model_version}"
|
|
15
|
+
else:
|
|
16
|
+
model_name = f"{model_domain} Model {model_version}"
|
|
17
|
+
key = (cc_parent, model_name)
|
|
18
|
+
if key not in hierarchies:
|
|
19
|
+
hierarchies[key] = {cc_child}
|
|
20
|
+
else:
|
|
21
|
+
hierarchies[key].add(cc_child)
|
|
22
|
+
except ValueError:
|
|
23
|
+
continue # Skip malformed lines
|
|
24
|
+
except Exception as e:
|
|
25
|
+
print(f"Error loading mapping file: {e}")
|
|
26
|
+
hierarchies = {}
|
|
27
|
+
return hierarchies
|
|
8
28
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
try:
|
|
13
|
-
cc_parent, cc_child, model_domain, model_version, _ = line.strip().split(',')
|
|
14
|
-
if model_domain == 'ESRD':
|
|
15
|
-
model_name = f"CMS-HCC {model_domain} Model {model_version}"
|
|
16
|
-
else:
|
|
17
|
-
model_name = f"{model_domain} Model {model_version}"
|
|
18
|
-
key = (cc_parent, model_name)
|
|
19
|
-
if key not in hierarchies_default:
|
|
20
|
-
hierarchies_default[key] = {cc_child}
|
|
21
|
-
else:
|
|
22
|
-
hierarchies_default[key].add(cc_child)
|
|
23
|
-
except ValueError:
|
|
24
|
-
continue # Skip malformed lines
|
|
25
|
-
except Exception as e:
|
|
26
|
-
print(f"Error loading mapping file: {e}")
|
|
27
|
-
hierarchies_default = {}
|
|
29
|
+
# Load default mappings from csv file
|
|
30
|
+
hierarchies_file_default = 'ra_hierarchies_2026.csv'
|
|
31
|
+
hierarchies_default: Dict[Tuple[str, ModelName], Set[str]] = load_hierarchies(hierarchies_file_default)
|
|
28
32
|
|
|
29
33
|
def apply_hierarchies(
|
|
30
34
|
cc_set: Set[str], # Set of active CCs
|
|
@@ -0,0 +1,252 @@
|
|
|
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()
|