hccinfhir 0.2.6__tar.gz → 0.2.8__tar.gz
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-0.2.6 → hccinfhir-0.2.8}/PKG-INFO +1 -1
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/datamodels.py +1 -1
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/extractor_834.py +43 -5
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/model_calculate.py +2 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/model_coefficients.py +5 -1
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/model_interactions.py +1 -1
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/pyproject.toml +1 -1
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/.gitignore +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/LICENSE +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/README.md +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/__init__.py +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/constants.py +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/data/__init__.py +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/data/hcc_is_chronic.csv +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/data/hcc_is_chronic_without_esrd_model.csv +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/data/ph_race_and_ethnicity_cdc_v1.3.csv +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/data/ra_coefficients_2025.csv +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/data/ra_coefficients_2026.csv +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/data/ra_dx_to_cc_2025.csv +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/data/ra_dx_to_cc_2026.csv +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/data/ra_eligible_cpt_hcpcs_2023.csv +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/data/ra_eligible_cpt_hcpcs_2024.csv +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/data/ra_eligible_cpt_hcpcs_2025.csv +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/data/ra_eligible_cpt_hcpcs_2026.csv +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/data/ra_hierarchies_2025.csv +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/data/ra_hierarchies_2026.csv +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/data/ra_labels_2026.csv +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/defaults.py +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/extractor.py +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/extractor_837.py +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/extractor_fhir.py +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/filter.py +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/hccinfhir.py +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/model_demographics.py +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/model_dx_to_cc.py +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/model_hierarchies.py +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/__init__.py +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_834_01.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_834_02.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_834_03.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_834_04.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_834_05.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_834_06.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_837_0.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_837_1.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_837_10.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_837_11.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_837_12.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_837_2.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_837_3.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_837_4.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_837_5.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_837_6.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_837_7.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_837_8.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_837_9.txt +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_eob_1.json +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_eob_2.json +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_eob_200.ndjson +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/sample_files/sample_eob_3.json +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/samples.py +0 -0
- {hccinfhir-0.2.6 → hccinfhir-0.2.8}/hccinfhir/utils.py +0 -0
|
@@ -276,7 +276,7 @@ class EnrollmentData(BaseModel):
|
|
|
276
276
|
is_partial_benefit_dual: Partial Benefit Dual (uses CPA_/CPD_ prefix)
|
|
277
277
|
medicare_status_code: QMB, SLMB, QI, QDWI, etc.
|
|
278
278
|
medi_cal_aid_code: California Medi-Cal aid code
|
|
279
|
-
medi_cal_eligibility_status: Medi-Cal eligibility status
|
|
279
|
+
medi_cal_eligibility_status: Medi-Cal eligibility status (derived: "Active"/"Terminated"/None)
|
|
280
280
|
|
|
281
281
|
# CA DHCS / FAME Specific
|
|
282
282
|
fame_county_id: FAME county ID (REF*ZX or N4*CY)
|
|
@@ -216,6 +216,36 @@ def is_new_enrollee(coverage_start_date: Optional[str], reference_date: Optional
|
|
|
216
216
|
return False
|
|
217
217
|
|
|
218
218
|
|
|
219
|
+
def derive_medi_cal_eligibility_status(coverage_end_date: Optional[str], report_date: Optional[str]) -> Optional[str]:
|
|
220
|
+
"""Derive Medi-Cal eligibility status from coverage end date and report date.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
coverage_end_date: Coverage end date in YYYY-MM-DD format
|
|
224
|
+
report_date: Report date in YYYY-MM-DD format
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
"Active" if coverage extends through or beyond report month
|
|
228
|
+
"Terminated" if coverage ended before report month
|
|
229
|
+
None if no coverage_end_date
|
|
230
|
+
"""
|
|
231
|
+
if not coverage_end_date:
|
|
232
|
+
return None
|
|
233
|
+
try:
|
|
234
|
+
end_date = datetime.strptime(coverage_end_date, "%Y-%m-%d").date()
|
|
235
|
+
if report_date:
|
|
236
|
+
ref_date = datetime.strptime(report_date, "%Y-%m-%d").date()
|
|
237
|
+
else:
|
|
238
|
+
ref_date = date.today()
|
|
239
|
+
# Get first day of report month for comparison
|
|
240
|
+
first_of_report_month = ref_date.replace(day=1)
|
|
241
|
+
if end_date < first_of_report_month:
|
|
242
|
+
return "Terminated"
|
|
243
|
+
else:
|
|
244
|
+
return "Active"
|
|
245
|
+
except (ValueError, AttributeError):
|
|
246
|
+
return None
|
|
247
|
+
|
|
248
|
+
|
|
219
249
|
def contains_any_keyword(text: str, keywords: set) -> bool:
|
|
220
250
|
"""Check if text contains any of the keywords"""
|
|
221
251
|
text_upper = text.upper()
|
|
@@ -326,10 +356,15 @@ def parse_ref_dx(value: str, member: MemberContext) -> None:
|
|
|
326
356
|
|
|
327
357
|
|
|
328
358
|
def parse_ref_17(value: str, member: MemberContext) -> None:
|
|
329
|
-
"""REF*17: YYYYMM
|
|
359
|
+
"""REF*17: YYYYMM;YYYYMMDD;YYYYMM (redetermination date; death date; reporting month)"""
|
|
360
|
+
# Position 0: FAME redetermination date (YYYYMM)
|
|
330
361
|
yyyymm = get_composite_part(value, 0)
|
|
331
362
|
if yyyymm and len(yyyymm) >= 6:
|
|
332
363
|
member.fame_redetermination_date = f"{yyyymm[:4]}-{yyyymm[4:6]}-01"
|
|
364
|
+
# Position 1: FAME death date (YYYYMMDD)
|
|
365
|
+
death_date_str = get_composite_part(value, 1)
|
|
366
|
+
if death_date_str and len(death_date_str) == 8:
|
|
367
|
+
member.fame_death_date = parse_date(death_date_str)
|
|
333
368
|
|
|
334
369
|
|
|
335
370
|
# ============================================================================
|
|
@@ -503,6 +538,7 @@ def _finalize_member(member: MemberContext, source: str, report_date: str) -> En
|
|
|
503
538
|
dual_code = determine_dual_status(member)
|
|
504
539
|
is_fbd, is_pbd = classify_dual_benefit_level(dual_code)
|
|
505
540
|
new_enrollee = is_new_enrollee(member.coverage_start_date)
|
|
541
|
+
medi_cal_elig_status = derive_medi_cal_eligibility_status(member.coverage_end_date, report_date)
|
|
506
542
|
|
|
507
543
|
hcp_history = [
|
|
508
544
|
HCPCoveragePeriod(
|
|
@@ -530,6 +566,7 @@ def _finalize_member(member: MemberContext, source: str, report_date: str) -> En
|
|
|
530
566
|
dual_elgbl_cd=dual_code, is_full_benefit_dual=is_fbd, is_partial_benefit_dual=is_pbd,
|
|
531
567
|
medicare_status_code=member.medicare_status_code,
|
|
532
568
|
medi_cal_aid_code=member.medi_cal_aid_code,
|
|
569
|
+
medi_cal_eligibility_status=medi_cal_elig_status,
|
|
533
570
|
fame_county_id=member.fame_county_id, case_number=member.case_number,
|
|
534
571
|
fame_card_issue_date=member.fame_card_issue_date,
|
|
535
572
|
fame_redetermination_date=member.fame_redetermination_date,
|
|
@@ -583,6 +620,11 @@ def parse_834_enrollment(segments: List[List[str]], source: str = None, report_d
|
|
|
583
620
|
member.maintenance_type = get_segment_value(segment, 3)
|
|
584
621
|
member.maintenance_reason_code = get_segment_value(segment, 4)
|
|
585
622
|
member.benefit_status_code = get_segment_value(segment, 5)
|
|
623
|
+
# INS12 is Member Death Date when INS11 = D8
|
|
624
|
+
if get_segment_value(segment, 11) == 'D8':
|
|
625
|
+
death_str = get_segment_value(segment, 12)
|
|
626
|
+
if death_str:
|
|
627
|
+
member.death_date = parse_date(death_str)
|
|
586
628
|
|
|
587
629
|
# REF - Reference identifiers
|
|
588
630
|
elif seg_id == 'REF' and len(segment) >= 3:
|
|
@@ -641,9 +683,6 @@ def parse_834_enrollment(segments: List[List[str]], source: str = None, report_d
|
|
|
641
683
|
if sex in X12_SEX_CODE_MAPPING:
|
|
642
684
|
member.sex = X12_SEX_CODE_MAPPING[sex]
|
|
643
685
|
member.race = parse_race_code(get_segment_value(segment, 5))
|
|
644
|
-
death_str = get_segment_value(segment, 6)
|
|
645
|
-
if death_str and len(death_str) >= 8:
|
|
646
|
-
member.death_date = parse_date(death_str[:8])
|
|
647
686
|
|
|
648
687
|
# DTP - Dates
|
|
649
688
|
elif seg_id == 'DTP' and len(segment) >= 4:
|
|
@@ -675,7 +714,6 @@ def parse_834_enrollment(segments: List[List[str]], source: str = None, report_d
|
|
|
675
714
|
member.has_medicare = True
|
|
676
715
|
elif qualifier == '435':
|
|
677
716
|
member.death_date = parsed
|
|
678
|
-
member.fame_death_date = parsed
|
|
679
717
|
|
|
680
718
|
# HD - Health coverage
|
|
681
719
|
elif seg_id == 'HD' and len(segment) >= 4:
|
|
@@ -115,6 +115,8 @@ def calculate_raf(diagnosis_codes: List[str],
|
|
|
115
115
|
demographic_interactions[key] = value
|
|
116
116
|
elif key.startswith('OriginallyDisabled_'):
|
|
117
117
|
demographic_interactions[key] = value
|
|
118
|
+
elif key == 'LTIMCAID':
|
|
119
|
+
demographic_interactions[key] = value
|
|
118
120
|
|
|
119
121
|
coefficients_demographics = apply_coefficients(demographics,
|
|
120
122
|
set(),
|
|
@@ -101,7 +101,11 @@ def apply_coefficients(demographics: Demographics,
|
|
|
101
101
|
|
|
102
102
|
# Apply the coefficients
|
|
103
103
|
for hcc in hcc_set:
|
|
104
|
-
|
|
104
|
+
# For RxHCC models, use RXHCC prefix instead of HCC
|
|
105
|
+
if 'RxHCC' in model_name:
|
|
106
|
+
key = (f"{prefix}RXHCC{hcc}".lower(), model_name)
|
|
107
|
+
else:
|
|
108
|
+
key = (f"{prefix}HCC{hcc}".lower(), model_name)
|
|
105
109
|
|
|
106
110
|
if key in coefficients:
|
|
107
111
|
value = coefficients[key]
|
|
@@ -309,7 +309,7 @@ def create_disease_interactions(model_name: ModelName,
|
|
|
309
309
|
'HCC85_gRenal_V24': diagnostic_cats['CHF'] * diagnostic_cats['RENAL_V24'],
|
|
310
310
|
'gCopdCF_CARD_RESP_FAIL': diagnostic_cats['gCopdCF'] * diagnostic_cats['CARD_RESP_FAIL'],
|
|
311
311
|
'HCC85_HCC96': int('85' in hcc_set) * int('96' in hcc_set),
|
|
312
|
-
'
|
|
312
|
+
'gSubstanceUseDisorder_gPsych': diagnostic_cats['gSubstanceUseDisorder_V24'] * diagnostic_cats['gPsychiatric_V24'],
|
|
313
313
|
'SEPSIS_PRESSURE_ULCER': diagnostic_cats['SEPSIS'] * diagnostic_cats['PRESSURE_ULCER'],
|
|
314
314
|
'SEPSIS_ARTIF_OPENINGS': diagnostic_cats['SEPSIS'] * int('188' in hcc_set),
|
|
315
315
|
'ART_OPENINGS_PRESS_ULCER': int('188' in hcc_set) * diagnostic_cats['PRESSURE_ULCER'],
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|