hccinfhir 0.2.0__py3-none-any.whl → 0.2.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 CHANGED
@@ -9,7 +9,7 @@ from .hccinfhir import HCCInFHIR
9
9
  from .extractor import extract_sld, extract_sld_list
10
10
  from .filter import apply_filter
11
11
  from .model_calculate import calculate_raf
12
- from .datamodels import Demographics, ServiceLevelData, RAFResult, ModelName
12
+ from .datamodels import Demographics, ServiceLevelData, RAFResult, ModelName, HCCDetail
13
13
 
14
14
  # Sample data functions
15
15
  from .samples import (
@@ -37,6 +37,7 @@ __all__ = [
37
37
  "ServiceLevelData",
38
38
  "RAFResult",
39
39
  "ModelName",
40
+ "HCCDetail",
40
41
 
41
42
  # Sample data
42
43
  "SampleData",
hccinfhir/constants.py ADDED
@@ -0,0 +1,240 @@
1
+ """
2
+ CMS Risk Adjustment Domain Constants
3
+
4
+ This module contains constants used across the HCC risk adjustment system,
5
+ including dual eligibility codes, OREC/CREC values, and state-specific mappings.
6
+
7
+ References:
8
+ - CMS Rate Announcement and Call Letter
9
+ - Medicare Advantage Enrollment and Disenrollment Guidance
10
+ - X12 834 Implementation Guides
11
+ """
12
+
13
+ from typing import Set, Dict
14
+
15
+ # =============================================================================
16
+ # DUAL ELIGIBILITY CODES
17
+ # =============================================================================
18
+ # CMS Dual Eligibility Status Codes (Medicare + Medicaid)
19
+ # Used in coefficient prefix selection (CNA_, CFA_, CPA_, etc.)
20
+
21
+ VALID_DUAL_CODES: Set[str] = {'00', '01', '02', '03', '04', '05', '06', '08'}
22
+
23
+ # Non-Dual Eligible
24
+ NON_DUAL_CODE: str = '00'
25
+
26
+ # Full Benefit Dual Eligible (receive both Medicare and full Medicaid benefits)
27
+ # Uses CFA_ (Community, Full Benefit Dual, Aged) or CFD_ (Disabled) prefixes
28
+ FULL_BENEFIT_DUAL_CODES: Set[str] = {
29
+ '02', # QMB Plus (Qualified Medicare Beneficiary Plus)
30
+ '04', # SLMB Plus (Specified Low-Income Medicare Beneficiary Plus)
31
+ '08', # Other Full Benefit Dual Eligible
32
+ }
33
+
34
+ # Partial Benefit Dual Eligible (Medicare + limited Medicaid)
35
+ # Uses CPA_ (Community, Partial Benefit Dual, Aged) or CPD_ (Disabled) prefixes
36
+ PARTIAL_BENEFIT_DUAL_CODES: Set[str] = {
37
+ '01', # QMB Only
38
+ '03', # SLMB Only
39
+ '05', # QDWI (Qualified Disabled and Working Individual)
40
+ '06', # QI (Qualifying Individual)
41
+ }
42
+
43
+ # =============================================================================
44
+ # OREC - Original Reason for Entitlement Code
45
+ # =============================================================================
46
+ # Determines if beneficiary has ESRD and affects coefficient prefix selection
47
+
48
+ VALID_OREC_VALUES: Set[str] = {'0', '1', '2', '3'}
49
+
50
+ OREC_DESCRIPTIONS: Dict[str, str] = {
51
+ '0': 'Old Age and Survivors Insurance (OASI)',
52
+ '1': 'Disability Insurance Benefits (DIB)',
53
+ '2': 'ESRD - End-Stage Renal Disease',
54
+ '3': 'DIB and ESRD',
55
+ }
56
+
57
+ # OREC codes indicating ESRD status (per CMS documentation)
58
+ OREC_ESRD_CODES: Set[str] = {'2', '3'}
59
+
60
+ # =============================================================================
61
+ # CREC - Current Reason for Entitlement Code
62
+ # =============================================================================
63
+ # Current entitlement status (may differ from OREC)
64
+
65
+ VALID_CREC_VALUES: Set[str] = {'0', '1', '2', '3'}
66
+
67
+ CREC_DESCRIPTIONS: Dict[str, str] = {
68
+ '0': 'Old Age and Survivors Insurance (OASI)',
69
+ '1': 'Disability Insurance Benefits (DIB)',
70
+ '2': 'ESRD - End-Stage Renal Disease',
71
+ '3': 'DIB and ESRD',
72
+ }
73
+
74
+ # CREC codes indicating ESRD status
75
+ CREC_ESRD_CODES: Set[str] = {'2', '3'}
76
+
77
+ # =============================================================================
78
+ # COEFFICIENT PREFIX GROUPS
79
+ # =============================================================================
80
+ # Used for prefix_override logic in model_demographics.py
81
+
82
+ # ESRD model prefixes
83
+ ESRD_PREFIXES: Set[str] = {'DI_', 'DNE_', 'GI_', 'GNE_', 'GFPA_', 'GFPN_', 'GNPA_', 'GNPN_'}
84
+
85
+ # CMS-HCC new enrollee prefixes
86
+ NEW_ENROLLEE_PREFIXES: Set[str] = {'NE_', 'SNPNE_', 'DNE_', 'GNE_'}
87
+
88
+ # CMS-HCC community prefixes
89
+ COMMUNITY_PREFIXES: Set[str] = {'CNA_', 'CND_', 'CFA_', 'CFD_', 'CPA_', 'CPD_'}
90
+
91
+ # Institutionalized prefixes
92
+ INSTITUTIONAL_PREFIXES: Set[str] = {'INS_', 'GI_'}
93
+
94
+ # Full Benefit Dual prefixes
95
+ FULL_BENEFIT_DUAL_PREFIXES: Set[str] = {'CFA_', 'CFD_', 'GFPA_', 'GFPN_'}
96
+
97
+ # Partial Benefit Dual prefixes
98
+ PARTIAL_BENEFIT_DUAL_PREFIXES: Set[str] = {'CPA_', 'CPD_'}
99
+
100
+ # Non-Dual prefixes
101
+ NON_DUAL_PREFIXES: Set[str] = {'CNA_', 'CND_', 'GNPA_', 'GNPN_'}
102
+
103
+ # =============================================================================
104
+ # DEMOGRAPHIC CODES
105
+ # =============================================================================
106
+
107
+ VALID_SEX_CODES: Set[str] = {'M', 'F'}
108
+
109
+ # X12 834 Gender Code mappings
110
+ X12_SEX_CODE_MAPPING: Dict[str, str] = {
111
+ 'M': 'M',
112
+ 'F': 'F',
113
+ '1': 'M', # X12 numeric code
114
+ '2': 'F', # X12 numeric code
115
+ }
116
+
117
+ # =============================================================================
118
+ # X12 834 MAINTENANCE TYPE CODES
119
+ # =============================================================================
120
+ # INS03 - Maintenance Type Code
121
+
122
+ MAINTENANCE_TYPE_CHANGE: str = '001'
123
+ MAINTENANCE_TYPE_ADD: str = '021'
124
+ MAINTENANCE_TYPE_CANCEL: str = '024'
125
+ MAINTENANCE_TYPE_REINSTATE: str = '025'
126
+
127
+ MAINTENANCE_TYPE_DESCRIPTIONS: Dict[str, str] = {
128
+ '001': 'Change',
129
+ '021': 'Addition',
130
+ '024': 'Cancellation/Termination',
131
+ '025': 'Reinstatement',
132
+ }
133
+
134
+ # =============================================================================
135
+ # STATE-SPECIFIC MAPPINGS
136
+ # =============================================================================
137
+
138
+ # -----------------------------------------------------------------------------
139
+ # California DHCS Medi-Cal Aid Codes
140
+ # -----------------------------------------------------------------------------
141
+ # Maps California-specific aid codes to CMS dual eligibility codes
142
+ # Source: California DHCS 834 Implementation Guide
143
+
144
+ MEDI_CAL_AID_CODES: Dict[str, str] = {
145
+ # Full Benefit Dual (QMB Plus, SLMB Plus)
146
+ '4N': '02', # QMB Plus - Aged
147
+ '4P': '02', # QMB Plus - Disabled
148
+ '5B': '04', # SLMB Plus - Aged
149
+ '5D': '04', # SLMB Plus - Disabled
150
+
151
+ # Partial Benefit Dual (QMB Only, SLMB Only, QI)
152
+ '4M': '01', # QMB Only - Aged
153
+ '4O': '01', # QMB Only - Disabled
154
+ '5A': '03', # SLMB Only - Aged
155
+ '5C': '03', # SLMB Only - Disabled
156
+ '5E': '06', # QI - Aged
157
+ '5F': '06', # QI - Disabled
158
+ }
159
+
160
+ # -----------------------------------------------------------------------------
161
+ # Medicare Status Code Mappings
162
+ # -----------------------------------------------------------------------------
163
+ # Maps Medicare status codes (from various sources) to CMS dual eligibility codes
164
+ # Used in X12 834 REF*ABB segment and other payer files
165
+
166
+ MEDICARE_STATUS_CODE_MAPPING: Dict[str, str] = {
167
+ # QMB - Qualified Medicare Beneficiary
168
+ 'QMB': '01', # QMB Only (Partial)
169
+ 'QMBONLY': '01',
170
+ 'QMBPLUS': '02', # QMB Plus (Full Benefit)
171
+ 'QMB+': '02',
172
+
173
+ # SLMB - Specified Low-Income Medicare Beneficiary
174
+ 'SLMB': '03', # SLMB Only (Partial)
175
+ 'SLMBONLY': '03',
176
+ 'SLMBPLUS': '04', # SLMB Plus (Full Benefit)
177
+ 'SLMB+': '04',
178
+
179
+ # Other dual eligibility programs
180
+ 'QDWI': '05', # Qualified Disabled and Working Individual
181
+ 'QI': '06', # Qualifying Individual
182
+ 'QI1': '06',
183
+ 'FBDE': '08', # Full Benefit Dual Eligible (Other)
184
+ 'OTHERFULL': '08',
185
+ }
186
+
187
+ # =============================================================================
188
+ # HELPER FUNCTIONS
189
+ # =============================================================================
190
+
191
+ def is_full_benefit_dual(dual_code: str) -> bool:
192
+ """Check if dual eligibility code is Full Benefit Dual"""
193
+ return dual_code in FULL_BENEFIT_DUAL_CODES
194
+
195
+ def is_partial_benefit_dual(dual_code: str) -> bool:
196
+ """Check if dual eligibility code is Partial Benefit Dual"""
197
+ return dual_code in PARTIAL_BENEFIT_DUAL_CODES
198
+
199
+ def is_esrd_by_orec(orec: str) -> bool:
200
+ """Check if OREC indicates ESRD status"""
201
+ return orec in OREC_ESRD_CODES
202
+
203
+ def is_esrd_by_crec(crec: str) -> bool:
204
+ """Check if CREC indicates ESRD status"""
205
+ return crec in CREC_ESRD_CODES
206
+
207
+ def normalize_medicare_status_code(status: str) -> str:
208
+ """Normalize Medicare status code (uppercase, no spaces/hyphens)"""
209
+ if not status:
210
+ return ''
211
+ return status.upper().replace(' ', '').replace('-', '')
212
+
213
+ def map_medicare_status_to_dual_code(status: str) -> str:
214
+ """Map Medicare status code to dual eligibility code
215
+
216
+ Args:
217
+ status: Medicare status code (e.g., 'QMB Plus', 'SLMB', 'QI')
218
+
219
+ Returns:
220
+ Dual eligibility code ('01'-'08') or '00' if not found
221
+ """
222
+ if not status:
223
+ return NON_DUAL_CODE
224
+
225
+ normalized = normalize_medicare_status_code(status)
226
+ return MEDICARE_STATUS_CODE_MAPPING.get(normalized, NON_DUAL_CODE)
227
+
228
+ def map_aid_code_to_dual_status(aid_code: str) -> str:
229
+ """Map California Medi-Cal aid code to dual eligibility code
230
+
231
+ Args:
232
+ aid_code: California aid code (e.g., '4N', '5B')
233
+
234
+ Returns:
235
+ Dual eligibility code ('01'-'08') or '00' if not found
236
+ """
237
+ if not aid_code:
238
+ return NON_DUAL_CODE
239
+
240
+ return MEDI_CAL_AID_CODES.get(aid_code, NON_DUAL_CODE)