hccinfhir 0.1.9__py3-none-any.whl → 0.2.1__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/constants.py +240 -0
- hccinfhir/datamodels.py +39 -8
- hccinfhir/defaults.py +31 -0
- hccinfhir/extractor_834.py +52 -71
- hccinfhir/extractor_837.py +2 -2
- hccinfhir/hccinfhir.py +27 -10
- hccinfhir/model_calculate.py +27 -23
- hccinfhir/model_coefficients.py +4 -31
- hccinfhir/model_demographics.py +26 -29
- hccinfhir/model_dx_to_cc.py +6 -11
- hccinfhir/model_hierarchies.py +6 -35
- hccinfhir/model_interactions.py +7 -7
- hccinfhir/samples.py +50 -5
- hccinfhir/utils.py +217 -44
- hccinfhir-0.2.1.dist-info/METADATA +946 -0
- {hccinfhir-0.1.9.dist-info → hccinfhir-0.2.1.dist-info}/RECORD +19 -17
- hccinfhir-0.1.9.dist-info/METADATA +0 -782
- {hccinfhir-0.1.9.dist-info → hccinfhir-0.2.1.dist-info}/WHEEL +0 -0
- {hccinfhir-0.1.9.dist-info → hccinfhir-0.2.1.dist-info}/licenses/LICENSE +0 -0
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
|
-
from typing import Dict, Tuple, Optional
|
|
2
|
-
import importlib.resources
|
|
1
|
+
from typing import Dict, Tuple, Optional, Set
|
|
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
|
|
|
@@ -91,10 +65,10 @@ def get_coefficent_prefix(demographics: Demographics,
|
|
|
91
65
|
|
|
92
66
|
|
|
93
67
|
def apply_coefficients(demographics: Demographics,
|
|
94
|
-
hcc_set:
|
|
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_demographics.py
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
from typing import Union, Optional
|
|
2
2
|
from hccinfhir.datamodels import Demographics, PrefixOverride
|
|
3
|
+
from hccinfhir.constants import (
|
|
4
|
+
FULL_BENEFIT_DUAL_CODES,
|
|
5
|
+
PARTIAL_BENEFIT_DUAL_CODES,
|
|
6
|
+
OREC_ESRD_CODES,
|
|
7
|
+
CREC_ESRD_CODES,
|
|
8
|
+
ESRD_PREFIXES,
|
|
9
|
+
NEW_ENROLLEE_PREFIXES,
|
|
10
|
+
COMMUNITY_PREFIXES,
|
|
11
|
+
INSTITUTIONAL_PREFIXES,
|
|
12
|
+
FULL_BENEFIT_DUAL_PREFIXES,
|
|
13
|
+
PARTIAL_BENEFIT_DUAL_PREFIXES,
|
|
14
|
+
NON_DUAL_PREFIXES,
|
|
15
|
+
)
|
|
3
16
|
|
|
4
17
|
def categorize_demographics(age: Union[int, float],
|
|
5
18
|
sex: str,
|
|
@@ -75,56 +88,40 @@ def categorize_demographics(age: Union[int, float],
|
|
|
75
88
|
disabled = age < 65 and (orec is not None and orec != "0")
|
|
76
89
|
orig_disabled = (orec is not None and orec == '1') and not disabled
|
|
77
90
|
|
|
78
|
-
# Reference: https://resdac.org/cms-data/variables/medicare-medicaid-dual-eligibility-code-january
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
# Partial benefit dual codes
|
|
83
|
-
pbd_codes = {'01', '03', '05', '06'}
|
|
84
|
-
|
|
85
|
-
is_fbd = dual_elgbl_cd in fbd_codes
|
|
86
|
-
is_pbd = dual_elgbl_cd in pbd_codes
|
|
91
|
+
# Reference: https://resdac.org/cms-data/variables/medicare-medicaid-dual-eligibility-code-january
|
|
92
|
+
is_fbd = dual_elgbl_cd in FULL_BENEFIT_DUAL_CODES
|
|
93
|
+
is_pbd = dual_elgbl_cd in PARTIAL_BENEFIT_DUAL_CODES
|
|
87
94
|
|
|
88
|
-
|
|
89
|
-
|
|
95
|
+
# ESRD detection from OREC/CREC (CMS official codes: 2=ESRD, 3=DIB+ESRD)
|
|
96
|
+
esrd_orec = orec in OREC_ESRD_CODES
|
|
97
|
+
esrd_crec = crec in CREC_ESRD_CODES if crec else False
|
|
90
98
|
esrd = esrd_orec or esrd_crec
|
|
91
99
|
|
|
92
100
|
# Override demographics based on prefix_override
|
|
93
101
|
if prefix_override:
|
|
94
|
-
# ESRD model prefixes
|
|
95
|
-
esrd_prefixes = {'DI_', 'DNE_', 'GI_', 'GNE_', 'GFPA_', 'GFPN_', 'GNPA_', 'GNPN_'}
|
|
96
|
-
# CMS-HCC new enrollee prefixes
|
|
97
|
-
new_enrollee_prefixes = {'NE_', 'SNPNE_', 'DNE_', 'GNE_'}
|
|
98
|
-
# CMS-HCC community prefixes
|
|
99
|
-
community_prefixes = {'CNA_', 'CND_', 'CFA_', 'CFD_', 'CPA_', 'CPD_'}
|
|
100
|
-
# Institutionalized prefix
|
|
101
|
-
institutional_prefixes = {'INS_', 'GI_'}
|
|
102
|
-
|
|
103
|
-
# TODO: RxHCC prefixes
|
|
104
|
-
|
|
105
102
|
# Set esrd flag
|
|
106
|
-
if prefix_override in
|
|
103
|
+
if prefix_override in ESRD_PREFIXES:
|
|
107
104
|
esrd = True
|
|
108
105
|
|
|
109
106
|
# Set new_enrollee flag
|
|
110
|
-
if prefix_override in
|
|
107
|
+
if prefix_override in NEW_ENROLLEE_PREFIXES:
|
|
111
108
|
new_enrollee = True
|
|
112
|
-
elif prefix_override in
|
|
109
|
+
elif prefix_override in COMMUNITY_PREFIXES or prefix_override in INSTITUTIONAL_PREFIXES:
|
|
113
110
|
new_enrollee = False
|
|
114
111
|
|
|
115
112
|
# Set dual eligibility flags based on prefix
|
|
116
|
-
if prefix_override in
|
|
113
|
+
if prefix_override in FULL_BENEFIT_DUAL_PREFIXES:
|
|
117
114
|
is_fbd = True
|
|
118
115
|
is_pbd = False
|
|
119
|
-
elif prefix_override in
|
|
116
|
+
elif prefix_override in PARTIAL_BENEFIT_DUAL_PREFIXES:
|
|
120
117
|
is_fbd = False
|
|
121
118
|
is_pbd = True
|
|
122
|
-
elif prefix_override in
|
|
119
|
+
elif prefix_override in NON_DUAL_PREFIXES:
|
|
123
120
|
is_fbd = False
|
|
124
121
|
is_pbd = False
|
|
125
122
|
|
|
126
123
|
# Set lti flag based on prefix
|
|
127
|
-
if prefix_override in
|
|
124
|
+
if prefix_override in INSTITUTIONAL_PREFIXES:
|
|
128
125
|
lti = True
|
|
129
126
|
|
|
130
127
|
result_dict = {
|
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/model_interactions.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from hccinfhir.datamodels import Demographics, ModelName
|
|
2
|
-
from typing import Optional
|
|
2
|
+
from typing import Optional, List, Set, Dict
|
|
3
3
|
|
|
4
|
-
def has_any_hcc(hcc_list:
|
|
4
|
+
def has_any_hcc(hcc_list: List[str], hcc_set: Set[str]) -> int:
|
|
5
5
|
"""Returns 1 if any HCC in the list is present, 0 otherwise"""
|
|
6
6
|
return int(bool(set(hcc_list) & hcc_set))
|
|
7
7
|
|
|
@@ -81,7 +81,7 @@ def create_dual_interactions(demographics: Demographics) -> dict:
|
|
|
81
81
|
|
|
82
82
|
return interactions
|
|
83
83
|
|
|
84
|
-
def create_hcc_counts(hcc_set:
|
|
84
|
+
def create_hcc_counts(hcc_set: Set[str]) -> Dict:
|
|
85
85
|
"""Creates HCC count variables"""
|
|
86
86
|
counts = {}
|
|
87
87
|
hcc_count = len(hcc_set)
|
|
@@ -95,7 +95,7 @@ def create_hcc_counts(hcc_set: set[str]) -> dict:
|
|
|
95
95
|
|
|
96
96
|
return counts
|
|
97
97
|
|
|
98
|
-
def get_diagnostic_categories(model_name: ModelName, hcc_set:
|
|
98
|
+
def get_diagnostic_categories(model_name: ModelName, hcc_set: Set[str]) -> Dict:
|
|
99
99
|
"""Creates disease categories based on model version"""
|
|
100
100
|
categories = {}
|
|
101
101
|
|
|
@@ -343,9 +343,9 @@ def create_disease_interactions(model_name: ModelName,
|
|
|
343
343
|
|
|
344
344
|
return interactions
|
|
345
345
|
|
|
346
|
-
def apply_interactions(demographics: Demographics,
|
|
347
|
-
hcc_set:
|
|
348
|
-
model_name: ModelName = "CMS-HCC Model V28") ->
|
|
346
|
+
def apply_interactions(demographics: Demographics,
|
|
347
|
+
hcc_set: Set[str],
|
|
348
|
+
model_name: ModelName = "CMS-HCC Model V28") -> Dict:
|
|
349
349
|
"""
|
|
350
350
|
Calculate HCC interactions across CMS models. Handles CMS-HCC, ESRD, and RxHCC models.
|
|
351
351
|
"""
|
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.
|