hccinfhir 0.1.9__py3-none-any.whl → 0.2.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.
- hccinfhir/__init__.py +2 -0
- hccinfhir/datamodels.py +39 -8
- hccinfhir/defaults.py +31 -0
- hccinfhir/hccinfhir.py +27 -10
- hccinfhir/model_calculate.py +27 -23
- hccinfhir/model_coefficients.py +2 -29
- hccinfhir/model_dx_to_cc.py +6 -11
- hccinfhir/model_hierarchies.py +6 -35
- hccinfhir/samples.py +50 -5
- hccinfhir/utils.py +217 -44
- hccinfhir-0.2.0.dist-info/METADATA +946 -0
- {hccinfhir-0.1.9.dist-info → hccinfhir-0.2.0.dist-info}/RECORD +14 -13
- hccinfhir-0.1.9.dist-info/METADATA +0 -782
- {hccinfhir-0.1.9.dist-info → hccinfhir-0.2.0.dist-info}/WHEEL +0 -0
- {hccinfhir-0.1.9.dist-info → hccinfhir-0.2.0.dist-info}/licenses/LICENSE +0 -0
hccinfhir/__init__.py
CHANGED
|
@@ -18,6 +18,7 @@ from .samples import (
|
|
|
18
18
|
get_eob_sample_list,
|
|
19
19
|
get_837_sample,
|
|
20
20
|
get_837_sample_list,
|
|
21
|
+
get_834_sample,
|
|
21
22
|
list_available_samples
|
|
22
23
|
)
|
|
23
24
|
|
|
@@ -43,5 +44,6 @@ __all__ = [
|
|
|
43
44
|
"get_eob_sample_list",
|
|
44
45
|
"get_837_sample",
|
|
45
46
|
"get_837_sample_list",
|
|
47
|
+
"get_834_sample",
|
|
46
48
|
"list_available_samples"
|
|
47
49
|
]
|
hccinfhir/datamodels.py
CHANGED
|
@@ -11,16 +11,47 @@ ModelName = Literal[
|
|
|
11
11
|
"RxHCC Model V08"
|
|
12
12
|
]
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
# Filename types: allow bundled filenames (with autocomplete) OR any custom string path
|
|
15
|
+
ProcFilteringFilename = Union[
|
|
16
|
+
Literal[
|
|
17
|
+
"ra_eligible_cpt_hcpcs_2023.csv",
|
|
18
|
+
"ra_eligible_cpt_hcpcs_2024.csv",
|
|
19
|
+
"ra_eligible_cpt_hcpcs_2025.csv",
|
|
20
|
+
"ra_eligible_cpt_hcpcs_2026.csv"
|
|
21
|
+
],
|
|
22
|
+
str # Allow any custom file path
|
|
19
23
|
]
|
|
20
24
|
|
|
21
|
-
DxCCMappingFilename =
|
|
22
|
-
|
|
23
|
-
|
|
25
|
+
DxCCMappingFilename = Union[
|
|
26
|
+
Literal[
|
|
27
|
+
"ra_dx_to_cc_2025.csv",
|
|
28
|
+
"ra_dx_to_cc_2026.csv"
|
|
29
|
+
],
|
|
30
|
+
str
|
|
31
|
+
]
|
|
32
|
+
|
|
33
|
+
HierarchiesFilename = Union[
|
|
34
|
+
Literal[
|
|
35
|
+
"ra_hierarchies_2025.csv",
|
|
36
|
+
"ra_hierarchies_2026.csv"
|
|
37
|
+
],
|
|
38
|
+
str
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
IsChronicFilename = Union[
|
|
42
|
+
Literal[
|
|
43
|
+
"hcc_is_chronic.csv",
|
|
44
|
+
"hcc_is_chronic_without_esrd_model.csv"
|
|
45
|
+
],
|
|
46
|
+
str
|
|
47
|
+
]
|
|
48
|
+
|
|
49
|
+
CoefficientsFilename = Union[
|
|
50
|
+
Literal[
|
|
51
|
+
"ra_coefficients_2025.csv",
|
|
52
|
+
"ra_coefficients_2026.csv"
|
|
53
|
+
],
|
|
54
|
+
str
|
|
24
55
|
]
|
|
25
56
|
|
|
26
57
|
PrefixOverride = Literal[
|
hccinfhir/defaults.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Central location for all default data file loading.
|
|
3
|
+
|
|
4
|
+
This module loads all default data files at import time and provides them
|
|
5
|
+
to the public API functions (calculate_raf) and tests. Internal model_*
|
|
6
|
+
modules should not load their own defaults - they should receive data
|
|
7
|
+
as explicit parameters.
|
|
8
|
+
|
|
9
|
+
Default files are for the 2026 model year.
|
|
10
|
+
"""
|
|
11
|
+
from typing import Dict, Set, Tuple
|
|
12
|
+
from hccinfhir.datamodels import ModelName
|
|
13
|
+
from hccinfhir.utils import (
|
|
14
|
+
load_dx_to_cc_mapping,
|
|
15
|
+
load_hierarchies,
|
|
16
|
+
load_is_chronic,
|
|
17
|
+
load_coefficients,
|
|
18
|
+
load_proc_filtering
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
# Load all default data files once at module import time
|
|
22
|
+
# These are used by:
|
|
23
|
+
# - calculate_raf() function for direct usage
|
|
24
|
+
# - HCCInFHIR class (though it can override with custom files)
|
|
25
|
+
# - Tests that need default data
|
|
26
|
+
|
|
27
|
+
dx_to_cc_default: Dict[Tuple[str, ModelName], Set[str]] = load_dx_to_cc_mapping('ra_dx_to_cc_2026.csv')
|
|
28
|
+
hierarchies_default: Dict[Tuple[str, ModelName], Set[str]] = load_hierarchies('ra_hierarchies_2026.csv')
|
|
29
|
+
is_chronic_default: Dict[Tuple[str, ModelName], bool] = load_is_chronic('hcc_is_chronic.csv')
|
|
30
|
+
coefficients_default: Dict[Tuple[str, ModelName], float] = load_coefficients('ra_coefficients_2026.csv')
|
|
31
|
+
proc_filtering_default: Set[str] = load_proc_filtering('ra_eligible_cpt_hcpcs_2026.csv')
|
hccinfhir/hccinfhir.py
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
from typing import List, Dict, Any, Union, Optional
|
|
1
|
+
from typing import List, Dict, Any, Union, Optional, Tuple, Set
|
|
2
2
|
from hccinfhir.extractor import extract_sld_list
|
|
3
3
|
from hccinfhir.filter import apply_filter
|
|
4
4
|
from hccinfhir.model_calculate import calculate_raf
|
|
5
|
-
from hccinfhir.datamodels import Demographics, ServiceLevelData, RAFResult, ModelName, ProcFilteringFilename, DxCCMappingFilename, PrefixOverride
|
|
6
|
-
from hccinfhir.utils import load_proc_filtering, load_dx_to_cc_mapping
|
|
5
|
+
from hccinfhir.datamodels import Demographics, ServiceLevelData, RAFResult, ModelName, ProcFilteringFilename, DxCCMappingFilename, HierarchiesFilename, IsChronicFilename, CoefficientsFilename, PrefixOverride
|
|
6
|
+
from hccinfhir.utils import load_proc_filtering, load_dx_to_cc_mapping, load_hierarchies, load_is_chronic, load_coefficients
|
|
7
7
|
|
|
8
8
|
class HCCInFHIR:
|
|
9
9
|
"""
|
|
@@ -13,26 +13,40 @@ class HCCInFHIR:
|
|
|
13
13
|
of the hccinfhir library.
|
|
14
14
|
"""
|
|
15
15
|
|
|
16
|
-
def __init__(self,
|
|
17
|
-
filter_claims: bool = True,
|
|
16
|
+
def __init__(self,
|
|
17
|
+
filter_claims: bool = True,
|
|
18
18
|
model_name: ModelName = "CMS-HCC Model V28",
|
|
19
19
|
proc_filtering_filename: ProcFilteringFilename = "ra_eligible_cpt_hcpcs_2026.csv",
|
|
20
|
-
dx_cc_mapping_filename: DxCCMappingFilename = "ra_dx_to_cc_2026.csv"
|
|
20
|
+
dx_cc_mapping_filename: DxCCMappingFilename = "ra_dx_to_cc_2026.csv",
|
|
21
|
+
hierarchies_filename: HierarchiesFilename = "ra_hierarchies_2026.csv",
|
|
22
|
+
is_chronic_filename: IsChronicFilename = "hcc_is_chronic.csv",
|
|
23
|
+
coefficients_filename: CoefficientsFilename = "ra_coefficients_2026.csv"):
|
|
21
24
|
"""
|
|
22
25
|
Initialize the HCCInFHIR processor.
|
|
23
|
-
|
|
26
|
+
|
|
24
27
|
Args:
|
|
25
28
|
filter_claims: Whether to apply filtering rules to claims. Default is True.
|
|
26
29
|
model_name: The name of the model to use for the calculation. Default is "CMS-HCC Model V28".
|
|
27
|
-
proc_filtering_filename:
|
|
28
|
-
dx_cc_mapping_filename:
|
|
30
|
+
proc_filtering_filename: Filename or path to the CPT/HCPCS filtering file. Default is "ra_eligible_cpt_hcpcs_2026.csv".
|
|
31
|
+
dx_cc_mapping_filename: Filename or path to the diagnosis to CC mapping file. Default is "ra_dx_to_cc_2026.csv".
|
|
32
|
+
hierarchies_filename: Filename or path to the hierarchies file. Default is "ra_hierarchies_2026.csv".
|
|
33
|
+
is_chronic_filename: Filename or path to the chronic conditions file. Default is "hcc_is_chronic.csv".
|
|
34
|
+
coefficients_filename: Filename or path to the coefficients file. Default is "ra_coefficients_2026.csv".
|
|
29
35
|
"""
|
|
30
36
|
self.filter_claims = filter_claims
|
|
31
37
|
self.model_name = model_name
|
|
32
38
|
self.proc_filtering_filename = proc_filtering_filename
|
|
33
39
|
self.dx_cc_mapping_filename = dx_cc_mapping_filename
|
|
40
|
+
self.hierarchies_filename = hierarchies_filename
|
|
41
|
+
self.is_chronic_filename = is_chronic_filename
|
|
42
|
+
self.coefficients_filename = coefficients_filename
|
|
43
|
+
|
|
44
|
+
# Load all data files once at initialization
|
|
34
45
|
self.professional_cpt = load_proc_filtering(proc_filtering_filename)
|
|
35
46
|
self.dx_to_cc_mapping = load_dx_to_cc_mapping(dx_cc_mapping_filename)
|
|
47
|
+
self.hierarchies_mapping = load_hierarchies(hierarchies_filename)
|
|
48
|
+
self.is_chronic_mapping = load_is_chronic(is_chronic_filename)
|
|
49
|
+
self.coefficients_mapping = load_coefficients(coefficients_filename)
|
|
36
50
|
|
|
37
51
|
|
|
38
52
|
def _ensure_demographics(self, demographics: Union[Demographics, Dict[str, Any]]) -> Demographics:
|
|
@@ -47,7 +61,7 @@ class HCCInFHIR:
|
|
|
47
61
|
maci: float = 0.0,
|
|
48
62
|
norm_factor: float = 1.0,
|
|
49
63
|
frailty_score: float = 0.0) -> RAFResult:
|
|
50
|
-
"""Calculate RAF score using demographics data."""
|
|
64
|
+
"""Calculate RAF score using demographics data and loaded data files."""
|
|
51
65
|
return calculate_raf(
|
|
52
66
|
diagnosis_codes=diagnosis_codes,
|
|
53
67
|
model_name=self.model_name,
|
|
@@ -62,6 +76,9 @@ class HCCInFHIR:
|
|
|
62
76
|
lti=demographics.lti,
|
|
63
77
|
graft_months=demographics.graft_months,
|
|
64
78
|
dx_to_cc_mapping=self.dx_to_cc_mapping,
|
|
79
|
+
is_chronic_mapping=self.is_chronic_mapping,
|
|
80
|
+
hierarchies_mapping=self.hierarchies_mapping,
|
|
81
|
+
coefficients_mapping=self.coefficients_mapping,
|
|
65
82
|
prefix_override=prefix_override,
|
|
66
83
|
maci=maci,
|
|
67
84
|
norm_factor=norm_factor,
|
hccinfhir/model_calculate.py
CHANGED
|
@@ -5,15 +5,7 @@ from hccinfhir.model_dx_to_cc import apply_mapping
|
|
|
5
5
|
from hccinfhir.model_hierarchies import apply_hierarchies
|
|
6
6
|
from hccinfhir.model_coefficients import apply_coefficients
|
|
7
7
|
from hccinfhir.model_interactions import apply_interactions
|
|
8
|
-
from hccinfhir.
|
|
9
|
-
|
|
10
|
-
# Load default mappings from csv file
|
|
11
|
-
mapping_file_default = 'ra_dx_to_cc_2026.csv'
|
|
12
|
-
dx_to_cc_default = load_dx_to_cc_mapping(mapping_file_default)
|
|
13
|
-
|
|
14
|
-
# Load default mappings from csv file
|
|
15
|
-
mapping_file_default = 'hcc_is_chronic.csv'
|
|
16
|
-
is_chronic_default = load_is_chronic(mapping_file_default)
|
|
8
|
+
from hccinfhir.defaults import dx_to_cc_default, hierarchies_default, is_chronic_default, coefficients_default
|
|
17
9
|
|
|
18
10
|
def calculate_raf(diagnosis_codes: List[str],
|
|
19
11
|
model_name: ModelName = "CMS-HCC Model V28",
|
|
@@ -29,6 +21,8 @@ def calculate_raf(diagnosis_codes: List[str],
|
|
|
29
21
|
graft_months: int = None,
|
|
30
22
|
dx_to_cc_mapping: Dict[Tuple[str, ModelName], Set[str]] = dx_to_cc_default,
|
|
31
23
|
is_chronic_mapping: Dict[Tuple[str, ModelName], bool] = is_chronic_default,
|
|
24
|
+
hierarchies_mapping: Dict[Tuple[str, ModelName], Set[str]] = hierarchies_default,
|
|
25
|
+
coefficients_mapping: Dict[Tuple[str, ModelName], float] = coefficients_default,
|
|
32
26
|
prefix_override: Optional[PrefixOverride] = None,
|
|
33
27
|
maci: float = 0.0,
|
|
34
28
|
norm_factor: float = 1.0,
|
|
@@ -37,24 +31,32 @@ def calculate_raf(diagnosis_codes: List[str],
|
|
|
37
31
|
Calculate Risk Adjustment Factor (RAF) based on diagnosis codes and demographic information.
|
|
38
32
|
|
|
39
33
|
Args:
|
|
40
|
-
diagnosis_codes: List of ICD-10 diagnosis codes
|
|
41
|
-
model_name: Name of the HCC model to use
|
|
42
|
-
age: Patient's age
|
|
43
|
-
sex: Patient's sex ('M' or 'F')
|
|
44
|
-
dual_elgbl_cd: Dual eligibility code
|
|
45
|
-
orec: Original reason for entitlement code
|
|
46
|
-
crec: Current reason for entitlement code
|
|
47
|
-
new_enrollee: Whether the patient is a new enrollee
|
|
48
|
-
snp: Special Needs Plan indicator
|
|
49
|
-
low_income: Low income subsidy indicator
|
|
50
|
-
|
|
34
|
+
diagnosis_codes: List of ICD-10 diagnosis codes.
|
|
35
|
+
model_name: Name of the HCC model to use.
|
|
36
|
+
age: Patient's age.
|
|
37
|
+
sex: Patient's sex ('M' or 'F').
|
|
38
|
+
dual_elgbl_cd: Dual eligibility code.
|
|
39
|
+
orec: Original reason for entitlement code.
|
|
40
|
+
crec: Current reason for entitlement code.
|
|
41
|
+
new_enrollee: Whether the patient is a new enrollee.
|
|
42
|
+
snp: Special Needs Plan indicator.
|
|
43
|
+
low_income: Low income subsidy indicator.
|
|
44
|
+
lti: Long-term institutional status indicator.
|
|
45
|
+
graft_months: Number of months since transplant.
|
|
46
|
+
dx_to_cc_mapping: Mapping of diagnosis codes to condition categories; defaults to packaged 2026 mappings.
|
|
47
|
+
is_chronic_mapping: Mapping of HCCs to a chronic flag for the selected model; defaults to packaged mappings.
|
|
48
|
+
hierarchies_mapping: Mapping of parent HCCs to child HCCs for hierarchical rules; defaults to packaged 2026 mappings.
|
|
49
|
+
coefficients_mapping: Mapping of coefficient names to values; defaults to packaged 2026 mappings.
|
|
51
50
|
prefix_override: Optional prefix to override auto-detected demographic prefix.
|
|
52
51
|
Use when demographic categorization from orec/crec is incorrect.
|
|
53
52
|
Common values: 'DI_' (ESRD Dialysis), 'DNE_' (ESRD Dialysis New Enrollee),
|
|
54
53
|
'INS_' (Institutionalized), 'CFA_' (Community Full Dual Aged), etc.
|
|
54
|
+
maci: Medicare Advantage coding intensity adjustment applied to payment score.
|
|
55
|
+
norm_factor: Normalization factor applied to payment score.
|
|
56
|
+
frailty_score: Frailty adjustment added to payment score.
|
|
55
57
|
|
|
56
58
|
Returns:
|
|
57
|
-
|
|
59
|
+
RAFResult with the calculated risk scores, intermediate inputs, and metadata for the model run.
|
|
58
60
|
|
|
59
61
|
Raises:
|
|
60
62
|
ValueError: If input parameters are invalid
|
|
@@ -89,10 +91,10 @@ def calculate_raf(diagnosis_codes: List[str],
|
|
|
89
91
|
model_name,
|
|
90
92
|
dx_to_cc_mapping=dx_to_cc_mapping)
|
|
91
93
|
hcc_set = set(cc_to_dx.keys())
|
|
92
|
-
hcc_set = apply_hierarchies(hcc_set, model_name)
|
|
94
|
+
hcc_set = apply_hierarchies(hcc_set, model_name, hierarchies_mapping)
|
|
93
95
|
interactions = apply_interactions(demographics, hcc_set, model_name)
|
|
94
96
|
coefficients = apply_coefficients(demographics, hcc_set, interactions, model_name,
|
|
95
|
-
prefix_override=prefix_override)
|
|
97
|
+
coefficients_mapping, prefix_override=prefix_override)
|
|
96
98
|
|
|
97
99
|
hcc_chronic = set()
|
|
98
100
|
interactions_chronic = {}
|
|
@@ -116,11 +118,13 @@ def calculate_raf(diagnosis_codes: List[str],
|
|
|
116
118
|
set(),
|
|
117
119
|
demographic_interactions,
|
|
118
120
|
model_name,
|
|
121
|
+
coefficients_mapping,
|
|
119
122
|
prefix_override=prefix_override)
|
|
120
123
|
coefficients_chronic_only = apply_coefficients(demographics,
|
|
121
124
|
hcc_chronic,
|
|
122
125
|
interactions_chronic,
|
|
123
126
|
model_name,
|
|
127
|
+
coefficients_mapping,
|
|
124
128
|
prefix_override=prefix_override)
|
|
125
129
|
|
|
126
130
|
# Calculate risk scores
|
hccinfhir/model_coefficients.py
CHANGED
|
@@ -1,32 +1,6 @@
|
|
|
1
1
|
from typing import Dict, Tuple, Optional
|
|
2
|
-
import importlib.resources
|
|
3
2
|
from hccinfhir.datamodels import ModelName, Demographics, PrefixOverride
|
|
4
3
|
|
|
5
|
-
# Load default mappings from csv file
|
|
6
|
-
coefficients_file_default = 'ra_coefficients_2026.csv'
|
|
7
|
-
coefficients_default: Dict[Tuple[str, ModelName], float] = {} # (diagnosis_code, model_name) -> value
|
|
8
|
-
|
|
9
|
-
try:
|
|
10
|
-
with importlib.resources.open_text('hccinfhir.data', coefficients_file_default) as f:
|
|
11
|
-
for line in f.readlines()[1:]: # Skip header
|
|
12
|
-
try:
|
|
13
|
-
coefficient, value, model_domain, model_version = line.strip().split(',')
|
|
14
|
-
if model_domain == 'ESRD':
|
|
15
|
-
model_name = f"CMS-HCC {model_domain} Model V{model_version[-2:]}"
|
|
16
|
-
else:
|
|
17
|
-
model_name = f"{model_domain} Model V{model_version[-2:]}"
|
|
18
|
-
|
|
19
|
-
key = (coefficient.lower(), model_name)
|
|
20
|
-
if key not in coefficients_default:
|
|
21
|
-
coefficients_default[key] = float(value)
|
|
22
|
-
else:
|
|
23
|
-
coefficients_default[key] = float(value)
|
|
24
|
-
except ValueError:
|
|
25
|
-
continue # Skip malformed lines
|
|
26
|
-
except Exception as e:
|
|
27
|
-
print(f"Error loading mapping file: {e}")
|
|
28
|
-
coefficients_default = {}
|
|
29
|
-
|
|
30
4
|
def get_coefficent_prefix(demographics: Demographics,
|
|
31
5
|
model_name: ModelName = "CMS-HCC Model V28") -> str:
|
|
32
6
|
|
|
@@ -93,8 +67,8 @@ def get_coefficent_prefix(demographics: Demographics,
|
|
|
93
67
|
def apply_coefficients(demographics: Demographics,
|
|
94
68
|
hcc_set: set[str],
|
|
95
69
|
interactions: dict,
|
|
96
|
-
model_name: ModelName
|
|
97
|
-
coefficients: Dict[Tuple[str, ModelName], float]
|
|
70
|
+
model_name: ModelName,
|
|
71
|
+
coefficients: Dict[Tuple[str, ModelName], float],
|
|
98
72
|
prefix_override: Optional[PrefixOverride] = None) -> dict:
|
|
99
73
|
"""Apply risk adjustment coefficients to HCCs and interactions.
|
|
100
74
|
|
|
@@ -108,7 +82,6 @@ def apply_coefficients(demographics: Demographics,
|
|
|
108
82
|
interactions: Dictionary of interaction variables and their values (0 or 1)
|
|
109
83
|
model_name: Name of the risk adjustment model to use (default: "CMS-HCC Model V28")
|
|
110
84
|
coefficients: Dictionary mapping (variable, model) tuples to coefficient values
|
|
111
|
-
(default: coefficients_default)
|
|
112
85
|
prefix_override: Optional prefix to override auto-detected demographic prefix.
|
|
113
86
|
Common values: 'DI_' (ESRD Dialysis), 'DNE_' (ESRD Dialysis New Enrollee),
|
|
114
87
|
'INS_' (Institutionalized), 'CFA_' (Community Full Dual Aged), etc.
|
hccinfhir/model_dx_to_cc.py
CHANGED
|
@@ -1,15 +1,10 @@
|
|
|
1
1
|
from typing import List, Dict, Set, Tuple, Optional
|
|
2
2
|
from hccinfhir.datamodels import ModelName
|
|
3
|
-
from hccinfhir.utils import load_dx_to_cc_mapping
|
|
4
|
-
|
|
5
|
-
# Load default mappings from csv file
|
|
6
|
-
mapping_file_default = 'ra_dx_to_cc_2026.csv'
|
|
7
|
-
dx_to_cc_default = load_dx_to_cc_mapping(mapping_file_default)
|
|
8
3
|
|
|
9
4
|
def get_cc(
|
|
10
5
|
diagnosis_code: str,
|
|
11
|
-
model_name: ModelName
|
|
12
|
-
dx_to_cc_mapping: Dict[Tuple[str, ModelName], Set[str]]
|
|
6
|
+
model_name: ModelName,
|
|
7
|
+
dx_to_cc_mapping: Dict[Tuple[str, ModelName], Set[str]]
|
|
13
8
|
) -> Optional[Set[str]]:
|
|
14
9
|
"""
|
|
15
10
|
Get CC for a single diagnosis code.
|
|
@@ -17,7 +12,7 @@ def get_cc(
|
|
|
17
12
|
Args:
|
|
18
13
|
diagnosis_code: ICD-10 diagnosis code
|
|
19
14
|
model_name: HCC model name to use for mapping
|
|
20
|
-
dx_to_cc_mapping:
|
|
15
|
+
dx_to_cc_mapping: Mapping dictionary of (diagnosis_code, model_name) to CC codes
|
|
21
16
|
|
|
22
17
|
Returns:
|
|
23
18
|
CC code if found, None otherwise
|
|
@@ -26,8 +21,8 @@ def get_cc(
|
|
|
26
21
|
|
|
27
22
|
def apply_mapping(
|
|
28
23
|
diagnoses: List[str],
|
|
29
|
-
model_name: ModelName
|
|
30
|
-
dx_to_cc_mapping: Dict[Tuple[str, ModelName], Set[str]]
|
|
24
|
+
model_name: ModelName,
|
|
25
|
+
dx_to_cc_mapping: Dict[Tuple[str, ModelName], Set[str]]
|
|
31
26
|
) -> Dict[str, Set[str]]:
|
|
32
27
|
"""
|
|
33
28
|
Apply ICD-10 to CC mapping for a list of diagnosis codes.
|
|
@@ -35,7 +30,7 @@ def apply_mapping(
|
|
|
35
30
|
Args:
|
|
36
31
|
diagnoses: List of ICD-10 diagnosis codes
|
|
37
32
|
model_name: HCC model name to use for mapping
|
|
38
|
-
dx_to_cc_mapping:
|
|
33
|
+
dx_to_cc_mapping: Mapping dictionary of (diagnosis_code, model_name) to CC codes
|
|
39
34
|
|
|
40
35
|
Returns:
|
|
41
36
|
Dictionary mapping CCs to lists of diagnosis codes that map to them
|
hccinfhir/model_hierarchies.py
CHANGED
|
@@ -1,47 +1,18 @@
|
|
|
1
1
|
from typing import Dict, Set, Tuple
|
|
2
|
-
import
|
|
3
|
-
from hccinfhir.datamodels import ModelName
|
|
4
|
-
|
|
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
|
|
28
|
-
|
|
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)
|
|
2
|
+
from hccinfhir.datamodels import ModelName
|
|
32
3
|
|
|
33
4
|
def apply_hierarchies(
|
|
34
|
-
cc_set: Set[str],
|
|
35
|
-
model_name: ModelName
|
|
36
|
-
hierarchies: Dict[Tuple[str, ModelName], Set[str]]
|
|
5
|
+
cc_set: Set[str],
|
|
6
|
+
model_name: ModelName,
|
|
7
|
+
hierarchies: Dict[Tuple[str, ModelName], Set[str]]
|
|
37
8
|
) -> Set[str]:
|
|
38
9
|
"""
|
|
39
10
|
Apply hierarchical rules to a set of CCs based on model version.
|
|
40
11
|
|
|
41
12
|
Args:
|
|
42
|
-
|
|
13
|
+
cc_set: Set of current active CCs
|
|
43
14
|
model_name: HCC model name to use for hierarchy rules
|
|
44
|
-
hierarchies:
|
|
15
|
+
hierarchies: Mapping dictionary of (parent_cc, model_name) to child CCs
|
|
45
16
|
|
|
46
17
|
Returns:
|
|
47
18
|
Set of CCs after applying hierarchies
|
hccinfhir/samples.py
CHANGED
|
@@ -157,7 +157,36 @@ class SampleData:
|
|
|
157
157
|
raise FileNotFoundError(f"Sample 837 case {case_num} not found")
|
|
158
158
|
|
|
159
159
|
return output
|
|
160
|
-
|
|
160
|
+
|
|
161
|
+
@staticmethod
|
|
162
|
+
def get_834_sample(case_number: int = 1) -> str:
|
|
163
|
+
"""
|
|
164
|
+
Retrieve a specific 834 enrollment sample by case number.
|
|
165
|
+
|
|
166
|
+
Args:
|
|
167
|
+
case_number: The case number (currently only 1 is available). Default is 1.
|
|
168
|
+
|
|
169
|
+
Returns:
|
|
170
|
+
A string containing the 834 X12 enrollment data
|
|
171
|
+
|
|
172
|
+
Raises:
|
|
173
|
+
ValueError: If case_number is not 1
|
|
174
|
+
FileNotFoundError: If the sample file cannot be found
|
|
175
|
+
|
|
176
|
+
Example:
|
|
177
|
+
>>> sample_834 = SampleData.get_834_sample(1)
|
|
178
|
+
>>> print("ISA" in sample_834)
|
|
179
|
+
True
|
|
180
|
+
"""
|
|
181
|
+
if case_number != 1:
|
|
182
|
+
raise ValueError("case_number must be 1 (only one 834 sample currently available)")
|
|
183
|
+
|
|
184
|
+
try:
|
|
185
|
+
with importlib.resources.open_text('hccinfhir.sample_files', f'sample_834_0{case_number}.txt') as f:
|
|
186
|
+
return f.read()
|
|
187
|
+
except FileNotFoundError:
|
|
188
|
+
raise FileNotFoundError(f"Sample 834 case {case_number} not found")
|
|
189
|
+
|
|
161
190
|
@staticmethod
|
|
162
191
|
def list_available_samples() -> Dict[str, Any]:
|
|
163
192
|
"""
|
|
@@ -174,7 +203,7 @@ class SampleData:
|
|
|
174
203
|
return {
|
|
175
204
|
"eob_samples": [
|
|
176
205
|
"sample_eob_1.json",
|
|
177
|
-
"sample_eob_2.json",
|
|
206
|
+
"sample_eob_2.json",
|
|
178
207
|
"sample_eob_3.json",
|
|
179
208
|
"sample_eob_200.ndjson"
|
|
180
209
|
],
|
|
@@ -182,9 +211,12 @@ class SampleData:
|
|
|
182
211
|
"eob_list_size": 200,
|
|
183
212
|
"837_samples": [f"sample_837_{i}.txt" for i in range(13)],
|
|
184
213
|
"837_case_numbers": list(range(13)),
|
|
214
|
+
"834_samples": ["sample_834_01.txt"],
|
|
215
|
+
"834_case_numbers": [1],
|
|
185
216
|
"description": {
|
|
186
217
|
"eob": "Explanation of Benefits (FHIR resources) for testing HCC calculations",
|
|
187
|
-
"837": "X12 837 claim data for testing claim processing"
|
|
218
|
+
"837": "X12 837 claim data for testing claim processing",
|
|
219
|
+
"834": "X12 834 enrollment data for dual eligibility and demographics"
|
|
188
220
|
}
|
|
189
221
|
}
|
|
190
222
|
|
|
@@ -232,16 +264,29 @@ def get_837_sample(case_number: int = 0) -> str:
|
|
|
232
264
|
def get_837_sample_list(case_numbers: Optional[List[int]] = None) -> List[str]:
|
|
233
265
|
"""
|
|
234
266
|
Convenience function to get multiple 837 claim samples.
|
|
235
|
-
|
|
267
|
+
|
|
236
268
|
Args:
|
|
237
269
|
case_numbers: List of case numbers to retrieve. If None, returns all 12 samples.
|
|
238
|
-
|
|
270
|
+
|
|
239
271
|
Returns:
|
|
240
272
|
A list of 837 X12 claim data strings
|
|
241
273
|
"""
|
|
242
274
|
return SampleData.get_837_sample_list(case_numbers)
|
|
243
275
|
|
|
244
276
|
|
|
277
|
+
def get_834_sample(case_number: int = 1) -> str:
|
|
278
|
+
"""
|
|
279
|
+
Convenience function to get an 834 enrollment sample.
|
|
280
|
+
|
|
281
|
+
Args:
|
|
282
|
+
case_number: The case number (currently only 1 is available). Default is 1.
|
|
283
|
+
|
|
284
|
+
Returns:
|
|
285
|
+
A string containing the 834 X12 enrollment data
|
|
286
|
+
"""
|
|
287
|
+
return SampleData.get_834_sample(case_number)
|
|
288
|
+
|
|
289
|
+
|
|
245
290
|
def list_available_samples() -> Dict[str, Any]:
|
|
246
291
|
"""
|
|
247
292
|
Convenience function to get information about available samples.
|