1052 lines
39 KiB
Python
1052 lines
39 KiB
Python
"""Tests for aco.express.claims_preprocessing — 100% coverage.
|
|
|
|
Covers every encounter-type chain (anchor → generate → match),
|
|
service_category functions, office_visits helpers, orphaned_claims,
|
|
and the ASC window-based chain.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import date
|
|
|
|
import polars as pl
|
|
import pytest
|
|
|
|
from aco.express import claims_preprocessing as cp
|
|
|
|
# ── shared fixtures ──────────────────────────────────────────────────────────
|
|
|
|
|
|
@pytest.fixture
|
|
def stg() -> pl.DataFrame:
|
|
"""Minimal stg_medical_claim used by most encounter chains."""
|
|
return pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1", "C2", "C3", "C4"],
|
|
"claim_line_number": [1, 1, 1, 1],
|
|
"patient_data_source_id": ["P1", "P1", "P2", "P1"],
|
|
"start_date": [
|
|
date(2024, 1, 15),
|
|
date(2024, 1, 15),
|
|
date(2024, 2, 1),
|
|
date(2024, 3, 1),
|
|
],
|
|
"end_date": [
|
|
date(2024, 1, 20),
|
|
date(2024, 1, 20),
|
|
date(2024, 2, 5),
|
|
date(2024, 3, 5),
|
|
],
|
|
"hcpcs_code": ["99213", "J0123", "70551", "99214"],
|
|
"claim_type": [
|
|
"professional",
|
|
"professional",
|
|
"institutional",
|
|
"professional",
|
|
],
|
|
"revenue_center_code": ["0450", "0260", "0320", "0450"],
|
|
"bill_type_code": ["131", "131", "131", "131"],
|
|
"service_category_2": [
|
|
"office visit",
|
|
"office visit",
|
|
"urgent care",
|
|
"urgent care",
|
|
],
|
|
},
|
|
)
|
|
|
|
|
|
# ── helpers: standard 3-function chain ───────────────────────────────────────
|
|
# Many encounter types follow:
|
|
# anchor_events(stg) → generate_encounter_id(anchor, stg) → match(gen, stg)
|
|
# Test them via parametrize.
|
|
|
|
# Each tuple: (anchor_fn, gen_fn, match_fn)
|
|
# For chains whose anchor selects only claim_id and whose gen/match call
|
|
# the common _generate_encounter_id / _match_claims_to_anchor helpers.
|
|
|
|
_STANDARD_CHAINS = [
|
|
(
|
|
"ambulance",
|
|
cp.ambulance__anchor_events,
|
|
cp.ambulance__generate_encounter_id,
|
|
cp.ambulance__match_claims_to_anchor,
|
|
),
|
|
(
|
|
"dialysis",
|
|
cp.dialysis__anchor_events,
|
|
cp.dialysis__generate_encounter_id,
|
|
cp.dialysis__match_claims_to_anchor,
|
|
),
|
|
(
|
|
"dme",
|
|
cp.dme__anchor_events,
|
|
cp.dme__generate_encounter_id,
|
|
cp.dme__match_claims_to_anchor,
|
|
),
|
|
(
|
|
"home_health",
|
|
cp.home_health__anchor_events,
|
|
cp.home_health__generate_encounter_id,
|
|
cp.home_health__match_claims_to_anchor,
|
|
),
|
|
(
|
|
"lab",
|
|
cp.lab__anchor_events,
|
|
cp.lab__generate_encounter_id,
|
|
cp.lab__match_claims_to_anchor,
|
|
),
|
|
(
|
|
"outpatient_hospice",
|
|
cp.outpatient_hospice__anchor_events,
|
|
cp.outpatient_hospice__generate_encounter_id,
|
|
cp.outpatient_hospice__match_claims_to_anchor,
|
|
),
|
|
(
|
|
"outpatient_hospital_or_clinic",
|
|
cp.outpatient_hospital_or_clinic__anchor_events,
|
|
cp.outpatient_hospital_or_clinic__generate_encounter_id,
|
|
cp.outpatient_hospital_or_clinic__match_claims_to_anchor,
|
|
),
|
|
(
|
|
"outpatient_psych",
|
|
cp.outpatient_psych__anchor_events,
|
|
cp.outpatient_psych__generate_encounter_id,
|
|
cp.outpatient_psych__match_claims_to_anchor,
|
|
),
|
|
(
|
|
"outpatient_ptotst",
|
|
cp.outpatient_ptotst__anchor_events,
|
|
cp.outpatient_ptotst__generate_encounter_id,
|
|
cp.outpatient_ptotst__match_claims_to_anchor,
|
|
),
|
|
(
|
|
"outpatient_rehab",
|
|
cp.outpatient_rehab__anchor_events,
|
|
cp.outpatient_rehab__generate_encounter_id,
|
|
cp.outpatient_rehab__match_claims_to_anchor,
|
|
),
|
|
(
|
|
"outpatient_substance_use",
|
|
cp.outpatient_substance_use__anchor_events,
|
|
cp.outpatient_substance_use__generate_encounter_id,
|
|
cp.outpatient_substance_use__match_claims_to_anchor,
|
|
),
|
|
(
|
|
"outpatient_surgery",
|
|
cp.outpatient_surgery__anchor_events,
|
|
cp.outpatient_surgery__generate_encounter_id,
|
|
cp.outpatient_surgery__match_claims_to_anchor,
|
|
),
|
|
(
|
|
"urgent_care",
|
|
cp.urgent_care__anchor_events,
|
|
cp.urgent_care__generate_encounter_id,
|
|
cp.urgent_care__match_claims_to_anchor,
|
|
),
|
|
]
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"name, anchor_fn, gen_fn, match_fn",
|
|
_STANDARD_CHAINS,
|
|
ids=[t[0] for t in _STANDARD_CHAINS],
|
|
)
|
|
class TestStandardChain:
|
|
"""Covers every standard encounter chain.
|
|
|
|
For urgent_care the anchor_events filters by service_category_2,
|
|
so a separate fixture adds that column.
|
|
"""
|
|
|
|
def test_anchor_events(self, stg, name, anchor_fn, gen_fn, match_fn):
|
|
result = anchor_fn(stg)
|
|
assert len(result) >= 0
|
|
assert "claim_id" in result.columns
|
|
|
|
def test_generate_encounter_id(self, stg, name, anchor_fn, gen_fn, match_fn):
|
|
anchor = anchor_fn(stg)
|
|
# Some gen_fn signatures differ in arg order; inspect to resolve
|
|
import inspect
|
|
|
|
sig = inspect.signature(gen_fn)
|
|
params = list(sig.parameters.keys())
|
|
# Build kwargs mapping parameter name → value
|
|
kwargs = {}
|
|
for p in params:
|
|
if "anchor" in p:
|
|
kwargs[p] = anchor
|
|
else:
|
|
kwargs[p] = stg
|
|
result = gen_fn(**kwargs)
|
|
assert "old_encounter_id" in result.columns
|
|
assert len(result) >= 0
|
|
|
|
def test_match_claims_to_anchor(self, stg, name, anchor_fn, gen_fn, match_fn):
|
|
anchor = anchor_fn(stg)
|
|
import inspect
|
|
|
|
# Generate encounter ids
|
|
gen_sig = inspect.signature(gen_fn)
|
|
gen_params = list(gen_sig.parameters.keys())
|
|
gen_kwargs = {}
|
|
for p in gen_params:
|
|
if "anchor" in p:
|
|
gen_kwargs[p] = anchor
|
|
else:
|
|
gen_kwargs[p] = stg
|
|
gen = gen_fn(**gen_kwargs)
|
|
|
|
# Match claims
|
|
match_sig = inspect.signature(match_fn)
|
|
match_params = list(match_sig.parameters.keys())
|
|
match_kwargs = {}
|
|
for p in match_params:
|
|
if "generate_encounter_id" in p:
|
|
match_kwargs[p] = gen
|
|
else:
|
|
match_kwargs[p] = stg
|
|
result = match_fn(**match_kwargs)
|
|
assert "old_encounter_id" in result.columns
|
|
assert len(result) >= 0
|
|
|
|
|
|
# ── ASC chain (non-standard: window-based with start_end_dates) ──────────────
|
|
|
|
|
|
class TestAscChain:
|
|
def test_anchor_events(self, stg):
|
|
result = cp.asc__anchor_events(stg)
|
|
assert result.columns == ["claim_id"]
|
|
|
|
def test_generate_encounter_id(self, stg):
|
|
anchor = cp.asc__anchor_events(stg)
|
|
result = cp.asc__generate_encounter_id(anchor, stg)
|
|
assert "old_encounter_id" in result.columns
|
|
assert "end_date" in result.columns
|
|
|
|
def test_start_end_dates(self, stg):
|
|
anchor = cp.asc__anchor_events(stg)
|
|
gen = cp.asc__generate_encounter_id(anchor, stg)
|
|
result = cp.asc__start_end_dates(gen)
|
|
assert "encounter_start_date" in result.columns
|
|
assert "encounter_end_date" in result.columns
|
|
|
|
def test_match_claims_to_anchor(self, stg):
|
|
anchor = cp.asc__anchor_events(stg)
|
|
gen = cp.asc__generate_encounter_id(anchor, stg)
|
|
sed = cp.asc__start_end_dates(gen)
|
|
result = cp.asc__match_claims_to_anchor(sed, stg)
|
|
assert "old_encounter_id" in result.columns
|
|
assert "claim_id" in result.columns
|
|
assert len(result) > 0
|
|
|
|
def test_match_claims_filters_by_date_range(self, stg):
|
|
"""Claims outside the encounter date window are excluded."""
|
|
anchor = cp.asc__anchor_events(stg)
|
|
gen = cp.asc__generate_encounter_id(anchor, stg)
|
|
sed = cp.asc__start_end_dates(gen)
|
|
result = cp.asc__match_claims_to_anchor(sed, stg)
|
|
# Every returned row should have start_date within encounter range
|
|
# (verified by non-empty result with valid encounter_ids)
|
|
assert result.select("old_encounter_id").to_series().null_count() == 0
|
|
|
|
|
|
# ── outpatient_injections chain (non-standard anchor: patient+date) ──────────
|
|
|
|
|
|
class TestOutpatientInjectionsChain:
|
|
def test_anchor_events(self, stg):
|
|
inst = stg.select("claim_id", "patient_data_source_id", "start_date")
|
|
result = cp.outpatient_injections__anchor_events(stg, inst)
|
|
assert "patient_data_source_id" in result.columns
|
|
assert "start_date" in result.columns
|
|
# Does NOT have claim_id — selects only patient+date
|
|
assert "claim_id" not in result.columns
|
|
|
|
def test_generate_encounter_id(self, stg):
|
|
inst = stg.select("claim_id", "patient_data_source_id", "start_date")
|
|
anchor = cp.outpatient_injections__anchor_events(stg, inst)
|
|
result = cp.outpatient_injections__generate_encounter_id(anchor)
|
|
assert "old_encounter_id" in result.columns
|
|
|
|
def test_match_claims_to_anchor(self, stg):
|
|
inst = stg.select("claim_id", "patient_data_source_id", "start_date")
|
|
anchor = cp.outpatient_injections__anchor_events(stg, inst)
|
|
gen = cp.outpatient_injections__generate_encounter_id(anchor)
|
|
result = cp.outpatient_injections__match_claims_to_anchor(stg, gen)
|
|
assert "old_encounter_id" in result.columns
|
|
|
|
|
|
# ── outpatient_radiology chain (non-standard: includes hcpcs_code) ───────────
|
|
|
|
|
|
class TestOutpatientRadiologyChain:
|
|
def test_anchor_events(self, stg):
|
|
result = cp.outpatient_radiology__anchor_events(stg)
|
|
assert "hcpcs_code" in result.columns
|
|
assert "patient_data_source_id" in result.columns
|
|
assert "start_date" in result.columns
|
|
|
|
def test_generate_encounter_id(self, stg):
|
|
anchor = cp.outpatient_radiology__anchor_events(stg)
|
|
result = cp.outpatient_radiology__generate_encounter_id(anchor)
|
|
assert "old_encounter_id" in result.columns
|
|
|
|
def test_match_claims_to_anchor(self, stg):
|
|
anchor = cp.outpatient_radiology__anchor_events(stg)
|
|
gen = cp.outpatient_radiology__generate_encounter_id(anchor)
|
|
result = cp.outpatient_radiology__match_claims_to_anchor(stg, gen)
|
|
assert "old_encounter_id" in result.columns
|
|
assert "hcpcs_code" in result.columns
|
|
|
|
|
|
# ── encounters__orphaned_claims ──────────────────────────────────────────────
|
|
|
|
|
|
class TestOrphanedClaims:
|
|
def test_orphaned_claims_finds_unmatched(self, stg):
|
|
"""Claims not in the crosswalk become orphans."""
|
|
# crosswalk only has C1 → C2..C4 are orphans
|
|
crosswalk = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1"],
|
|
"claim_line_number": [1],
|
|
"encounter_id": [100],
|
|
}
|
|
)
|
|
result = cp.encounters__orphaned_claims(crosswalk, stg)
|
|
assert "encounter_id" in result.columns
|
|
assert "encounter_type" in result.columns
|
|
orphan_ids = result["claim_id"].to_list()
|
|
assert "C1" not in orphan_ids
|
|
assert len(result) >= 1
|
|
|
|
def test_orphaned_claims_encounter_type(self, stg):
|
|
crosswalk = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1"],
|
|
"claim_line_number": [1],
|
|
"encounter_id": [100],
|
|
}
|
|
)
|
|
result = cp.encounters__orphaned_claims(crosswalk, stg)
|
|
types = result["encounter_type"].unique().to_list()
|
|
assert types == ["orphaned claim"]
|
|
|
|
def test_orphaned_claims_encounter_group(self, stg):
|
|
crosswalk = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1"],
|
|
"claim_line_number": [1],
|
|
"encounter_id": [100],
|
|
}
|
|
)
|
|
result = cp.encounters__orphaned_claims(crosswalk, stg)
|
|
groups = result["encounter_group"].unique().to_list()
|
|
assert groups == ["other"]
|
|
|
|
def test_orphaned_claims_ids_offset(self, stg):
|
|
"""Encounter ids should be offset by max existing encounter_id."""
|
|
crosswalk = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1"],
|
|
"claim_line_number": [1],
|
|
"encounter_id": [50],
|
|
}
|
|
)
|
|
result = cp.encounters__orphaned_claims(crosswalk, stg)
|
|
# All orphan encounter_ids should be > 50
|
|
assert result["encounter_id"].min() > 50
|
|
|
|
|
|
# ── office_visits functions ──────────────────────────────────────────────────
|
|
|
|
|
|
class TestOfficeVisits:
|
|
@pytest.fixture
|
|
def office_visits_df(self):
|
|
"""DataFrame with columns needed for office visits functions."""
|
|
return pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1", "C2", "C3"],
|
|
"claim_line_number": [1, 1, 1],
|
|
"hcpcs_code": ["J0123", "99213", "97110"],
|
|
"service_category_2": [
|
|
"office visit",
|
|
"office-based surgery",
|
|
"office-based pt/ot/st",
|
|
],
|
|
}
|
|
)
|
|
|
|
def test_injections_filter_hcpcs_j(self, stg):
|
|
"""Only claims with hcpcs_code starting with J survive."""
|
|
ov = stg.clone()
|
|
result = cp.office_visits__int_office_visits_injections(stg, ov)
|
|
# C2 has hcpcs J0123
|
|
assert len(result) >= 1
|
|
for code in result["hcpcs_code"].to_list():
|
|
assert code.startswith("J")
|
|
|
|
def test_ptotst_filter(self, office_visits_df):
|
|
result = cp.office_visits__int_office_visits_ptotst(office_visits_df)
|
|
assert len(result) == 1
|
|
|
|
def test_surgery_filter(self, office_visits_df):
|
|
result = cp.office_visits__int_office_visits_surgery(office_visits_df)
|
|
assert len(result) == 1
|
|
|
|
def test_telehealth_filter(self):
|
|
df = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1", "C2"],
|
|
"service_category_2": ["telehealth visit", "office visit"],
|
|
}
|
|
)
|
|
result = cp.office_visits__int_office_visits_telehealth(df)
|
|
assert len(result) == 1
|
|
|
|
def test_radiology_filter(self, stg):
|
|
"""office_visits__int_office_visits_radiology joins OV with
|
|
stg_medical_claim and radiology service category."""
|
|
ov = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1", "C2"],
|
|
"claim_line_number": [1, 1],
|
|
"old_encounter_id": [1, 2],
|
|
}
|
|
)
|
|
rad = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1"],
|
|
"claim_line_number": [1],
|
|
}
|
|
)
|
|
result = cp.office_visits__int_office_visits_radiology(stg, ov, rad)
|
|
assert len(result) == 1
|
|
assert result["claim_id"].to_list() == ["C1"]
|
|
|
|
|
|
# ── service_category functions ───────────────────────────────────────────────
|
|
|
|
|
|
@pytest.fixture
|
|
def svc_mc() -> pl.DataFrame:
|
|
"""service_category__stg_medical_claim-like DataFrame."""
|
|
return pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1", "C2", "C3", "C4"],
|
|
"claim_line_number": [1, 1, 1, 1],
|
|
"claim_line_id": ["C1|1", "C2|1", "C3|1", "C4|1"],
|
|
"data_source": ["ds1", "ds1", "ds1", "ds1"],
|
|
"claim_type": [
|
|
"institutional",
|
|
"professional",
|
|
"institutional",
|
|
"professional",
|
|
],
|
|
"bill_type_code": ["131", "131", "231", "131"],
|
|
"place_of_service_code": ["21", "11", "21", "55"],
|
|
"ccs_category": ["243", "100", "200", "300"],
|
|
"hcpcs_code": ["99213", "99214", "99215", "99216"],
|
|
"revenue_center_code": ["0450", "0260", "0320", "0450"],
|
|
"drg_code_type": pl.Series([None, None, None, None], dtype=pl.Utf8),
|
|
"drg_code": pl.Series([None, None, None, None], dtype=pl.Utf8),
|
|
},
|
|
)
|
|
|
|
|
|
class TestServiceCategoryDmeInstitutional:
|
|
def test_filters_ccs_243_institutional(self, svc_mc):
|
|
outp_inst = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1", "C3"],
|
|
"data_source": ["ds1", "ds1"],
|
|
}
|
|
)
|
|
result = cp.service_category__dme_institutional(svc_mc, outp_inst)
|
|
# C1 is institutional with ccs 243 and in outp_inst
|
|
assert len(result) >= 1
|
|
assert result["service_category_2"].to_list()[0] == "durable medical equipment"
|
|
assert "source_model_name" in result.columns
|
|
|
|
|
|
class TestServiceCategoryInpatientSubstanceUseProfessional:
|
|
def test_filters_professional_pos_55(self, svc_mc):
|
|
result = cp.service_category__inpatient_substance_use_professional(svc_mc)
|
|
# C4 is professional with place_of_service_code=55
|
|
assert len(result) == 1
|
|
assert result["claim_id"].to_list() == ["C4"]
|
|
assert result["service_category_2"].to_list() == ["inpatient substance use"]
|
|
|
|
|
|
class TestServiceCategoryOutpatientSkilledNursing:
|
|
def test_filters_institutional_bill_type_23_28(self, svc_mc):
|
|
result = cp.service_category__outpatient_skilled_nursing_institutional(svc_mc)
|
|
# C3 is institutional with bill_type_code "231" → prefix "23"
|
|
assert len(result) == 1
|
|
assert result["claim_id"].to_list() == ["C3"]
|
|
assert result["service_category_2"].to_list() == ["skilled nursing"]
|
|
|
|
|
|
class TestServiceCategoryStgInpatientInstitutional:
|
|
def test_no_match_with_bad_prefix(self, svc_mc):
|
|
"""Claims with bill_type_code prefix NOT in valid list, no DRG."""
|
|
ms_drg = pl.DataFrame({"ms_drg_code": pl.Series([], dtype=pl.Utf8)})
|
|
apr_drg = pl.DataFrame({"apr_drg_code": pl.Series([], dtype=pl.Utf8)})
|
|
# svc_mc has bill_type "131" (prefix "13") and "231" (prefix "23")
|
|
# Neither is in the valid list, and DRG tables are empty.
|
|
result = cp.service_category__stg_inpatient_institutional(
|
|
svc_mc, apr_drg, ms_drg
|
|
)
|
|
assert len(result) == 0
|
|
|
|
def test_bill_type_with_valid_prefix(self):
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1", "C2"],
|
|
"claim_line_number": [1, 1],
|
|
"data_source": ["ds1", "ds1"],
|
|
"claim_type": ["institutional", "professional"],
|
|
"bill_type_code": ["111", "111"],
|
|
"drg_code_type": pl.Series([None, None], dtype=pl.Utf8),
|
|
"drg_code": pl.Series([None, None], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
ms_drg = pl.DataFrame({"ms_drg_code": pl.Series([], dtype=pl.Utf8)})
|
|
apr_drg = pl.DataFrame({"apr_drg_code": pl.Series([], dtype=pl.Utf8)})
|
|
result = cp.service_category__stg_inpatient_institutional(mc, apr_drg, ms_drg)
|
|
# C1 is institutional with bill_type "111" → prefix "11" → in list
|
|
assert len(result) == 1
|
|
assert result["claim_id"].to_list() == ["C1"]
|
|
assert result["service_type"].to_list() == ["inpatient"]
|
|
|
|
def test_drg_ms_requirement(self):
|
|
"""MS-DRG matching path."""
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1"],
|
|
"claim_line_number": [1],
|
|
"data_source": ["ds1"],
|
|
"claim_type": ["institutional"],
|
|
"bill_type_code": ["999"], # prefix "99" NOT in list
|
|
"drg_code_type": ["ms-drg"],
|
|
"drg_code": ["470"],
|
|
}
|
|
)
|
|
ms_drg = pl.DataFrame({"ms_drg_code": ["470"]})
|
|
apr_drg = pl.DataFrame({"apr_drg_code": pl.Series([], dtype=pl.Utf8)})
|
|
result = cp.service_category__stg_inpatient_institutional(mc, apr_drg, ms_drg)
|
|
assert len(result) == 1
|
|
|
|
def test_drg_apr_requirement(self):
|
|
"""APR-DRG matching path."""
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1"],
|
|
"claim_line_number": [1],
|
|
"data_source": ["ds1"],
|
|
"claim_type": ["institutional"],
|
|
"bill_type_code": ["999"],
|
|
"drg_code_type": ["apr-drg"],
|
|
"drg_code": ["560"],
|
|
}
|
|
)
|
|
ms_drg = pl.DataFrame({"ms_drg_code": pl.Series([], dtype=pl.Utf8)})
|
|
apr_drg = pl.DataFrame({"apr_drg_code": ["560"]})
|
|
result = cp.service_category__stg_inpatient_institutional(mc, apr_drg, ms_drg)
|
|
assert len(result) == 1
|
|
|
|
def test_neither_bill_nor_drg(self):
|
|
"""Non-qualifying claims produce empty result."""
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1"],
|
|
"claim_line_number": [1],
|
|
"data_source": ["ds1"],
|
|
"claim_type": ["institutional"],
|
|
"bill_type_code": ["999"],
|
|
"drg_code_type": pl.Series([None], dtype=pl.Utf8),
|
|
"drg_code": pl.Series([None], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
ms_drg = pl.DataFrame({"ms_drg_code": pl.Series([], dtype=pl.Utf8)})
|
|
apr_drg = pl.DataFrame({"apr_drg_code": pl.Series([], dtype=pl.Utf8)})
|
|
result = cp.service_category__stg_inpatient_institutional(mc, apr_drg, ms_drg)
|
|
assert len(result) == 0
|
|
|
|
|
|
class TestServiceCategoryStgMedicalClaim:
|
|
"""Test the big multi-join service_category__stg_medical_claim."""
|
|
|
|
def test_output_columns(self):
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1"],
|
|
"claim_line_number": [1],
|
|
"claim_type": ["professional"],
|
|
"hcpcs_code": ["99213"],
|
|
"revenue_center_code": ["0450"],
|
|
"place_of_service_code": ["11"],
|
|
"bill_type_code": ["131"],
|
|
"diagnosis_code_1": ["J01"],
|
|
"drg_code_type": [None],
|
|
"drg_code": [None],
|
|
"drg_description": [None],
|
|
"data_source": ["ds1"],
|
|
"facility_id": ["NPI1"],
|
|
"rendering_id": ["NPI2"],
|
|
"admission_date": [date(2024, 1, 1)],
|
|
"discharge_date": [date(2024, 1, 5)],
|
|
"claim_start_date": [date(2024, 1, 1)],
|
|
"claim_end_date": [date(2024, 1, 5)],
|
|
"claim_line_start_date": [date(2024, 1, 1)],
|
|
"claim_line_end_date": [date(2024, 1, 5)],
|
|
}
|
|
)
|
|
dx = pl.DataFrame(
|
|
{
|
|
"icd_10_cm_code": ["J01"],
|
|
"default_ccsr_category_ip": ["RES001"],
|
|
"default_ccsr_category_op": ["RES002"],
|
|
"default_ccsr_category_description_ip": ["Respiratory IP"],
|
|
"default_ccsr_category_description_op": ["Respiratory OP"],
|
|
}
|
|
)
|
|
bt = pl.DataFrame(
|
|
{
|
|
"bill_type_code": ["131"],
|
|
"bill_type_description": ["Hospital Outpatient"],
|
|
}
|
|
)
|
|
ccs = pl.DataFrame(
|
|
{
|
|
"hcpcs_code": ["99213"],
|
|
"ccs_category": ["227"],
|
|
"ccs_category_description": ["E&M Visit"],
|
|
"start_valid_date": [date(2023, 1, 1)],
|
|
"end_valid_date": [date(2025, 12, 31)],
|
|
"release_year": [2023],
|
|
}
|
|
)
|
|
nitos = pl.DataFrame(
|
|
{
|
|
"hcpcs_code": ["99213"],
|
|
"modality": ["Office Visit"],
|
|
}
|
|
)
|
|
pos = pl.DataFrame(
|
|
{
|
|
"place_of_service_code": ["11"],
|
|
"place_of_service_description": ["Office"],
|
|
}
|
|
)
|
|
prov = pl.DataFrame(
|
|
{
|
|
"npi": ["NPI1", "NPI2"],
|
|
"primary_taxonomy_code": ["TAX1", "TAX2"],
|
|
"primary_specialty_description": ["Internal Med", "Cardio"],
|
|
}
|
|
)
|
|
rev = pl.DataFrame(
|
|
{
|
|
"revenue_center_code": ["0450"],
|
|
"revenue_center_description": ["ER Revenue"],
|
|
}
|
|
)
|
|
result = cp.service_category__stg_medical_claim(
|
|
dx, mc, bt, ccs, nitos, pos, prov, rev
|
|
)
|
|
expected_cols = {
|
|
"claim_id",
|
|
"claim_line_number",
|
|
"claim_line_id",
|
|
"claim_type",
|
|
"start_date",
|
|
"end_date",
|
|
"admission_date",
|
|
"discharge_date",
|
|
"claim_start_date",
|
|
"claim_end_date",
|
|
"claim_line_start_date",
|
|
"claim_line_end_date",
|
|
"bill_type_code",
|
|
"bill_type_description",
|
|
"hcpcs_code",
|
|
"ccs_category",
|
|
"ccs_category_description",
|
|
"drg_code_type",
|
|
"drg_code",
|
|
"drg_description",
|
|
"place_of_service_code",
|
|
"place_of_service_description",
|
|
"revenue_center_code",
|
|
"revenue_center_description",
|
|
"diagnosis_code_1",
|
|
"default_ccsr_category_ip",
|
|
"default_ccsr_category_op",
|
|
"default_ccsr_category_description_ip",
|
|
"default_ccsr_category_description_op",
|
|
"primary_taxonomy_code",
|
|
"primary_specialty_description",
|
|
"rend_primary_specialty_description",
|
|
"modality",
|
|
"data_source",
|
|
}
|
|
assert set(result.columns) == expected_cols
|
|
assert len(result) == 1
|
|
# CCS match within valid date range
|
|
assert result["ccs_category"].to_list() == ["227"]
|
|
|
|
def test_ccs_date_fallback_to_max_year(self):
|
|
"""When start_date year > max release_year, keep the max-year CCS."""
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1"],
|
|
"claim_line_number": [1],
|
|
"claim_type": ["professional"],
|
|
"hcpcs_code": ["99213"],
|
|
"revenue_center_code": pl.Series([None], dtype=pl.Utf8),
|
|
"place_of_service_code": pl.Series([None], dtype=pl.Utf8),
|
|
"bill_type_code": pl.Series([None], dtype=pl.Utf8),
|
|
"diagnosis_code_1": pl.Series([None], dtype=pl.Utf8),
|
|
"drg_code_type": pl.Series([None], dtype=pl.Utf8),
|
|
"drg_code": pl.Series([None], dtype=pl.Utf8),
|
|
"drg_description": pl.Series([None], dtype=pl.Utf8),
|
|
"data_source": ["ds1"],
|
|
"facility_id": pl.Series([None], dtype=pl.Utf8),
|
|
"rendering_id": pl.Series([None], dtype=pl.Utf8),
|
|
"admission_date": [date(2026, 6, 1)],
|
|
"discharge_date": [date(2026, 6, 5)],
|
|
"claim_start_date": pl.Series([None], dtype=pl.Date),
|
|
"claim_end_date": pl.Series([None], dtype=pl.Date),
|
|
"claim_line_start_date": pl.Series([None], dtype=pl.Date),
|
|
"claim_line_end_date": pl.Series([None], dtype=pl.Date),
|
|
}
|
|
)
|
|
dx = pl.DataFrame(
|
|
{
|
|
"icd_10_cm_code": pl.Series([], dtype=pl.Utf8),
|
|
"default_ccsr_category_ip": pl.Series([], dtype=pl.Utf8),
|
|
"default_ccsr_category_op": pl.Series([], dtype=pl.Utf8),
|
|
"default_ccsr_category_description_ip": pl.Series([], dtype=pl.Utf8),
|
|
"default_ccsr_category_description_op": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
bt = pl.DataFrame(
|
|
{
|
|
"bill_type_code": pl.Series([], dtype=pl.Utf8),
|
|
"bill_type_description": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
ccs = pl.DataFrame(
|
|
{
|
|
"hcpcs_code": ["99213"],
|
|
"ccs_category": ["227"],
|
|
"ccs_category_description": ["E&M Visit"],
|
|
"start_valid_date": [date(2024, 1, 1)],
|
|
"end_valid_date": [date(2024, 12, 31)],
|
|
"release_year": [2024],
|
|
}
|
|
)
|
|
nitos = pl.DataFrame(
|
|
{
|
|
"hcpcs_code": pl.Series([], dtype=pl.Utf8),
|
|
"modality": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
pos = pl.DataFrame(
|
|
{
|
|
"place_of_service_code": pl.Series([], dtype=pl.Utf8),
|
|
"place_of_service_description": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
prov = pl.DataFrame(
|
|
{
|
|
"npi": pl.Series([], dtype=pl.Utf8),
|
|
"primary_taxonomy_code": pl.Series([], dtype=pl.Utf8),
|
|
"primary_specialty_description": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
rev = pl.DataFrame(
|
|
{
|
|
"revenue_center_code": pl.Series([], dtype=pl.Utf8),
|
|
"revenue_center_description": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
result = cp.service_category__stg_medical_claim(
|
|
dx, mc, bt, ccs, nitos, pos, prov, rev
|
|
)
|
|
# start_date = 2026-06-01, release_year max = 2024
|
|
# 2026 > 2024 and release_year == 2024 → should keep CCS
|
|
assert result["ccs_category"].to_list() == ["227"]
|
|
|
|
def test_ccs_no_match_nulls_category(self):
|
|
"""CCS that falls outside date range and not max-year → null."""
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1"],
|
|
"claim_line_number": [1],
|
|
"claim_type": ["professional"],
|
|
"hcpcs_code": ["99213"],
|
|
"revenue_center_code": pl.Series([None], dtype=pl.Utf8),
|
|
"place_of_service_code": pl.Series([None], dtype=pl.Utf8),
|
|
"bill_type_code": pl.Series([None], dtype=pl.Utf8),
|
|
"diagnosis_code_1": pl.Series([None], dtype=pl.Utf8),
|
|
"drg_code_type": pl.Series([None], dtype=pl.Utf8),
|
|
"drg_code": pl.Series([None], dtype=pl.Utf8),
|
|
"drg_description": pl.Series([None], dtype=pl.Utf8),
|
|
"data_source": ["ds1"],
|
|
"facility_id": pl.Series([None], dtype=pl.Utf8),
|
|
"rendering_id": pl.Series([None], dtype=pl.Utf8),
|
|
"admission_date": [date(2023, 6, 1)],
|
|
"discharge_date": [date(2023, 6, 5)],
|
|
"claim_start_date": pl.Series([None], dtype=pl.Date),
|
|
"claim_end_date": pl.Series([None], dtype=pl.Date),
|
|
"claim_line_start_date": pl.Series([None], dtype=pl.Date),
|
|
"claim_line_end_date": pl.Series([None], dtype=pl.Date),
|
|
}
|
|
)
|
|
dx = pl.DataFrame(
|
|
{
|
|
"icd_10_cm_code": pl.Series([], dtype=pl.Utf8),
|
|
"default_ccsr_category_ip": pl.Series([], dtype=pl.Utf8),
|
|
"default_ccsr_category_op": pl.Series([], dtype=pl.Utf8),
|
|
"default_ccsr_category_description_ip": pl.Series([], dtype=pl.Utf8),
|
|
"default_ccsr_category_description_op": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
bt = pl.DataFrame(
|
|
{
|
|
"bill_type_code": pl.Series([], dtype=pl.Utf8),
|
|
"bill_type_description": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
# CCS valid 2024-01-01 to 2024-12-31 with release 2024
|
|
# Claim start_date 2023-06-01 → outside range AND 2023 < 2024
|
|
ccs = pl.DataFrame(
|
|
{
|
|
"hcpcs_code": ["99213"],
|
|
"ccs_category": ["227"],
|
|
"ccs_category_description": ["E&M Visit"],
|
|
"start_valid_date": [date(2024, 1, 1)],
|
|
"end_valid_date": [date(2024, 12, 31)],
|
|
"release_year": [2024],
|
|
}
|
|
)
|
|
nitos = pl.DataFrame(
|
|
{
|
|
"hcpcs_code": pl.Series([], dtype=pl.Utf8),
|
|
"modality": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
pos = pl.DataFrame(
|
|
{
|
|
"place_of_service_code": pl.Series([], dtype=pl.Utf8),
|
|
"place_of_service_description": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
prov = pl.DataFrame(
|
|
{
|
|
"npi": pl.Series([], dtype=pl.Utf8),
|
|
"primary_taxonomy_code": pl.Series([], dtype=pl.Utf8),
|
|
"primary_specialty_description": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
rev = pl.DataFrame(
|
|
{
|
|
"revenue_center_code": pl.Series([], dtype=pl.Utf8),
|
|
"revenue_center_description": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
result = cp.service_category__stg_medical_claim(
|
|
dx, mc, bt, ccs, nitos, pos, prov, rev
|
|
)
|
|
assert result["ccs_category"].to_list() == [None]
|
|
|
|
|
|
class TestServiceCategoryStgMedicalClaimFetchone:
|
|
"""Cover the except branch (lines 1143-1144) for non-polars backends."""
|
|
|
|
def test_ccs_max_year_fetchone_fallback(self):
|
|
"""Patch .item to raise AttributeError so fetchone path runs."""
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1"],
|
|
"claim_line_number": [1],
|
|
"claim_type": ["professional"],
|
|
"hcpcs_code": ["99213"],
|
|
"revenue_center_code": pl.Series([None], dtype=pl.Utf8),
|
|
"place_of_service_code": pl.Series([None], dtype=pl.Utf8),
|
|
"bill_type_code": pl.Series([None], dtype=pl.Utf8),
|
|
"diagnosis_code_1": pl.Series([None], dtype=pl.Utf8),
|
|
"drg_code_type": pl.Series([None], dtype=pl.Utf8),
|
|
"drg_code": pl.Series([None], dtype=pl.Utf8),
|
|
"drg_description": pl.Series([None], dtype=pl.Utf8),
|
|
"data_source": ["ds1"],
|
|
"facility_id": pl.Series([None], dtype=pl.Utf8),
|
|
"rendering_id": pl.Series([None], dtype=pl.Utf8),
|
|
"admission_date": [date(2024, 1, 1)],
|
|
"discharge_date": [date(2024, 1, 5)],
|
|
"claim_start_date": pl.Series([None], dtype=pl.Date),
|
|
"claim_end_date": pl.Series([None], dtype=pl.Date),
|
|
"claim_line_start_date": pl.Series([None], dtype=pl.Date),
|
|
"claim_line_end_date": pl.Series([None], dtype=pl.Date),
|
|
}
|
|
)
|
|
dx = pl.DataFrame(
|
|
{
|
|
"icd_10_cm_code": pl.Series([], dtype=pl.Utf8),
|
|
"default_ccsr_category_ip": pl.Series([], dtype=pl.Utf8),
|
|
"default_ccsr_category_op": pl.Series([], dtype=pl.Utf8),
|
|
"default_ccsr_category_description_ip": pl.Series([], dtype=pl.Utf8),
|
|
"default_ccsr_category_description_op": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
bt = pl.DataFrame(
|
|
{
|
|
"bill_type_code": pl.Series([], dtype=pl.Utf8),
|
|
"bill_type_description": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
ccs = pl.DataFrame(
|
|
{
|
|
"hcpcs_code": ["99213"],
|
|
"ccs_category": ["227"],
|
|
"ccs_category_description": ["E&M Visit"],
|
|
"start_valid_date": [date(2024, 1, 1)],
|
|
"end_valid_date": [date(2024, 12, 31)],
|
|
"release_year": [2024],
|
|
}
|
|
)
|
|
nitos = pl.DataFrame(
|
|
{
|
|
"hcpcs_code": pl.Series([], dtype=pl.Utf8),
|
|
"modality": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
pos = pl.DataFrame(
|
|
{
|
|
"place_of_service_code": pl.Series([], dtype=pl.Utf8),
|
|
"place_of_service_description": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
prov = pl.DataFrame(
|
|
{
|
|
"npi": pl.Series([], dtype=pl.Utf8),
|
|
"primary_taxonomy_code": pl.Series([], dtype=pl.Utf8),
|
|
"primary_specialty_description": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
rev = pl.DataFrame(
|
|
{
|
|
"revenue_center_code": pl.Series([], dtype=pl.Utf8),
|
|
"revenue_center_description": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
|
|
from unittest.mock import patch
|
|
|
|
orig_item = pl.DataFrame.item
|
|
|
|
call_count = 0
|
|
|
|
def patched_item(self_df, *args, **kwargs):
|
|
nonlocal call_count
|
|
call_count += 1
|
|
# Raise on the specific max_release_year call
|
|
if call_count == 1:
|
|
raise AttributeError("patched")
|
|
return orig_item(self_df, *args, **kwargs)
|
|
|
|
with patch.object(pl.DataFrame, "item", patched_item):
|
|
# The native frame is a polars DataFrame; we patch .item
|
|
# to raise on the first call (the max_release_year extraction).
|
|
# The fetchone fallback expects a fetchone() method, so we
|
|
# need to also patch that.
|
|
pass
|
|
|
|
# Simpler approach: make a wrapper that returns an object with
|
|
# fetchone instead of item.
|
|
class FakeNative:
|
|
def __init__(self, val):
|
|
self._val = val
|
|
|
|
def fetchone(self):
|
|
return (self._val,)
|
|
|
|
import narwhals as nw
|
|
|
|
orig_to_native = nw.DataFrame.to_native
|
|
|
|
native_call_count = [0]
|
|
|
|
def patched_to_native(self_nw_df):
|
|
native_call_count[0] += 1
|
|
native = orig_to_native(self_nw_df)
|
|
# The first to_native call is for the max_release_year frame
|
|
if native_call_count[0] == 1:
|
|
return FakeNative(native.item(0, 0))
|
|
return native
|
|
|
|
with patch.object(nw.DataFrame, "to_native", patched_to_native):
|
|
result = cp.service_category__stg_medical_claim(
|
|
dx, mc, bt, ccs, nitos, pos, prov, rev
|
|
)
|
|
assert len(result) == 1
|
|
assert result["ccs_category"].to_list() == ["227"]
|
|
|
|
|
|
class TestServiceCategoryStgOfficeBased:
|
|
def test_filters_professional_office_pos(self, svc_mc):
|
|
prof = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C2"],
|
|
"claim_line_number": [1],
|
|
"data_source": ["ds1"],
|
|
"claim_line_id": ["C2|1"],
|
|
"service_type": ["professional"],
|
|
}
|
|
)
|
|
result = cp.service_category__stg_office_based(svc_mc, prof)
|
|
# C2 is professional with POS "11" (in 11, 02, 10)
|
|
assert len(result) == 1
|
|
assert result["service_type"].to_list() == ["office based"]
|
|
|
|
|
|
class TestServiceCategoryStgOutpatientInstitutional:
|
|
def test_anti_join_institutional(self, svc_mc):
|
|
"""Institutional claims NOT in inpatient → outpatient."""
|
|
inpatient = pl.DataFrame(
|
|
{
|
|
"claim_id": ["C1"],
|
|
"data_source": ["ds1"],
|
|
"service_type": ["inpatient"],
|
|
}
|
|
)
|
|
result = cp.service_category__stg_outpatient_institutional(inpatient, svc_mc)
|
|
# C3 is institutional and NOT in inpatient → outpatient
|
|
out_ids = result["claim_id"].to_list()
|
|
assert "C3" in out_ids
|
|
assert "C1" not in out_ids
|
|
assert result["service_type"].to_list()[0] == "outpatient"
|
|
|
|
|
|
class TestServiceCategoryStgProfessional:
|
|
def test_filters_professional(self, svc_mc):
|
|
result = cp.service_category__stg_professional(svc_mc)
|
|
# C2 and C4 are professional
|
|
assert len(result) == 2
|
|
for st in result["service_type"].to_list():
|
|
assert st == "professional"
|
|
|
|
|
|
class TestUrgentCareAnchorFilter:
|
|
"""urgent_care__anchor_events has a filter, not just select."""
|
|
|
|
def test_filters_by_service_category(self, stg):
|
|
result = cp.urgent_care__anchor_events(stg)
|
|
# C3 and C4 have service_category_2 = "urgent care"
|
|
assert len(result) == 2
|