Files
stack/tests/bcda/test_cclf_v3.py
kert 63e9c0029a
Some checks failed
CI / lint (push) Successful in 58s
CI / notebooks-smoke (push) Successful in 1m44s
CI / test (push) Failing after 3m3s
Infra CI / notebooks (push) Successful in 1m13s
Infra CI / docs (push) Successful in 1m46s
Infra CI / llm (push) Successful in 49s
Infra CI / mc (push) Failing after 36s
Infra CI / zotero (push) Successful in 27s
Infra CI / api (push) Successful in 1m9s
Infra CI / bcda (push) Successful in 1m37s
CI / lint (pull_request) Successful in 41s
CI / notebooks-smoke (pull_request) Successful in 1m49s
CI / test (pull_request) Failing after 2m50s
Infra CI / docs (pull_request) Successful in 17s
Infra CI / notebooks (pull_request) Successful in 59s
Infra CI / api (pull_request) Successful in 14s
Infra CI / bcda (pull_request) Successful in 14s
Infra CI / llm (pull_request) Successful in 20s
Infra CI / zotero (pull_request) Successful in 14s
Infra CI / mc (pull_request) Failing after 34s
feat(cms): vendor the institutional provider universe (CCN ↔ NPI ↔ TIN) from data.cms.gov; give carrier lines their billed TIN
"No TIN available" is a fact about the data, not a bug in ours.
Institutional claims never carry a TIN: the real Large-entity v3 pull has
0 billing TINs on every claim type and 0 CCNs on every institutional
claim (the dictionary's NamingSystem/CCN identifier is not emitted and
half the institutional claims have no billing provider at all), and
CCLF1 has an OSCAR/CCN and NPIs but no TIN either. The only TIN on
claims is the carrier/DME CLM_RNDRG_PRVDR_TAX_NUM, the TIN the line was
billed under, so the connector now gives professional and DME lines that
value as billing_tin too (institutional stays NULL; the facility is its
NPI and CCN). CMS publishes no provider TIN or EIN on data.cms.gov —
checked every enrollment, ownership, change-of-ownership, Provider of
Services and cost-report family in the catalog.

What can be vendored is CCN ↔ NPI ↔ organization. cms.datacms resolves
a dataset family to its newest version in the data.cms.gov catalog,
downloads the CSV (or pages the API) and names columns like the
generated cms.table models. `stack load cms` (aco.load.cms) loads the
institutional universe — hospital, SNF, HHA, hospice, FQHC and RHC
enrollments, the Provider of Services file and the three cost reports —
as cms.* with provenance columns and ingest_log rows, then derives
cms.institutional_provider, cms.ccn_crosswalk (PECOS ∪ POS-only CCNs,
65,161 rows today), cms.provider_tin_observed (NPI → TIN as seen on our
own claims) and cms.ccn_tin (CCN → NPI → TIN, with cms.aco_participant_tin
as the authoritative hook when a participant list is loaded). The
command prints facility-NPI → CCN → TIN coverage; the Data page gets the
same query. On the sandbox nothing resolves — its 8 facility NPIs are
synthetic — which is the expected answer there.

refs #1139
2026-10-25 17:33:13 -04:00

926 lines
35 KiB
Python

"""bcda.express.cclf — the connector pipeline on v2- and v3-shaped tables.
Routing, final-action filtering, cross-source dedupe, the shared
medical_claim mapping, Part D, demographics and eligibility. Fixtures
are minimal frames; the official-sample run lives in test_pipe_cclf.
"""
from __future__ import annotations
from datetime import date
import narwhals as nw
import polars as pl
import pytest
from bcda.express import cclf as ex
from bcda.express.cclf import (
eligibility,
int_dme_medical_claim,
int_institutional_medical_claim,
int_physician_medical_claim,
medical_claim,
pharmacy_claim,
stg_beneficiary_demographics,
stg_beneficiary_xref,
stg_claim_header,
stg_part_a_header,
stg_part_b_dme_header,
stg_part_b_physician_header,
stg_part_d_header,
)
from bcda.table.cclf_pipe import (
BcdaEligibility,
BcdaMedicalClaim,
BcdaPharmacyClaim,
BcdaStgBeneficiaryDemographics,
BcdaStgClaimHeader,
)
# ── fixtures ─────────────────────────────────────────────────
@pytest.fixture
def xref() -> pl.DataFrame:
return stg_beneficiary_xref(
bcda__patient=pl.DataFrame(
{"bene_id": ["B1", "B2"], "bene_mbi_id": ["MBI-1", "MBI-2"]}
)
)
def _eob(rows: list[dict]) -> pl.DataFrame:
"""Header frame from sparse dicts; missing columns are filled by the stage."""
cols: dict[str, list] = {}
for r in rows:
for k in r:
cols.setdefault(k, [])
for r in rows:
for k in cols:
cols[k].append(r.get(k))
return pl.DataFrame(cols)
@pytest.fixture
def v3_headers() -> pl.DataFrame:
"""Seven v3 claims: one per family, a NotFinalAction, and a SharedSystem/NCH pair."""
return _eob(
[
{
"clm_id": "I1",
"clm_cntl_num": "CCN-I1",
"bene_id": "B1",
"eob_type": "INPATIENT",
"clm_type_cd": "60",
"system_type": "NationalClaimsHistory",
"final_action": "FinalAction",
"last_updated": "2026-01-02",
"clm_from_dt": date(2025, 1, 1),
"clm_thru_dt": date(2025, 1, 9),
"clm_actv_care_from_dt": date(2024, 12, 31),
"clm_dschrg_dt": date(2025, 1, 9),
"clm_admsn_src_cd": "7",
"clm_admsn_type_cd": "1",
"bene_ptnt_stus_cd": "01",
"clm_bill_type_cd": "0111",
"clm_bill_fac_type_cd": "1",
"clm_bill_clsfctn_cd": "11",
"clm_bill_freq_cd": "111",
"dgns_drg_cd": "470",
"dgns_prcdr_icd_ind": "0",
"clm_dgns_1_cd": "I10",
"clm_poa_ind": "Y",
"clm_prcdr_cd": "0SRC0J9",
"clm_prcdr_prfrm_dt": date(2025, 1, 2),
"atndg_prvdr_npi_num": "ATT",
"oprtg_prvdr_npi_num": "OPR",
"fac_prvdr_npi_num": "FAC",
"clm_blg_prvdr_tax_num": "TIN-FAC",
"clm_nch_wkly_proc_dt": date(2025, 1, 20),
"clm_mdcr_instnl_tot_chrg_amt": 9999.0,
},
{
"clm_id": "C1",
"clm_cntl_num": "CCN-C1",
"bene_id": "B1",
"eob_type": "CARRIER",
"clm_type_cd": "71",
"system_type": "NationalClaimsHistory",
"final_action": "FinalAction",
"last_updated": "2026-01-02",
"clm_from_dt": date(2025, 2, 1),
"clm_thru_dt": date(2025, 2, 1),
"dgns_prcdr_icd_ind": "0",
"clm_dgns_1_cd": "E11.9",
"rndrg_prvdr_npi_num": "RND",
"clm_rndrg_prvdr_tax_num": "TIN-RND",
"carr_clm_blg_npi_num": "BLG",
"clm_cms_proc_dt": date(2025, 2, 10),
},
{
"clm_id": "D1",
"clm_cntl_num": "CCN-D1",
"bene_id": "B2",
"eob_type": "DME",
"clm_type_cd": "82",
"system_type": "NationalClaimsHistory",
"final_action": "FinalAction",
"last_updated": "2026-01-02",
"clm_from_dt": date(2025, 3, 1),
"clm_thru_dt": date(2025, 3, 1),
"ordrg_prvdr_npi_num": "ORD",
"payto_prvdr_npi_num": "PAY",
},
{
"clm_id": "P1",
"pde_id": "P1",
"clm_cntl_num": "CCN-P1",
"bene_id": "B2",
"eob_type": "PDE",
"clm_type_cd": "1",
"system_type": "DDPS",
"final_action": "FinalAction",
"last_updated": "2026-01-02",
"clm_from_dt": date(2025, 4, 1),
"clm_thru_dt": date(2025, 4, 1),
"clm_prsbng_prvdr_gnrc_id_num": "PRS",
"fac_prvdr_npi_num": "PHARM",
"clm_srvc_prvdr_gnrc_id_num": "LIC",
"clm_line_days_suply_qty": 30.0,
"clm_line_rx_fill_num": 2.0,
"clm_pd_dt": date(2025, 4, 3),
"clm_bene_pmt_amt": 7.5,
"tot_rx_cst_amt": 120.0,
},
{
"clm_id": "X1",
"clm_cntl_num": "CCN-X1",
"bene_id": "B1",
"eob_type": "SNF",
"clm_type_cd": "20",
"system_type": "NationalClaimsHistory",
"final_action": "NotFinalAction",
"last_updated": "2026-01-02",
"clm_from_dt": date(2025, 5, 1),
"clm_thru_dt": date(2025, 5, 2),
},
{
"clm_id": "S1",
"clm_cntl_num": "CCN-PAIR",
"bene_id": "B1",
"eob_type": "OUTPATIENT",
"clm_type_cd": "40",
"system_type": "SharedSystem",
"final_action": "FinalAction",
"last_updated": "2026-01-05",
"clm_from_dt": date(2025, 6, 1),
"clm_thru_dt": date(2025, 6, 1),
},
{
"clm_id": "N1",
"clm_cntl_num": "CCN-PAIR",
"bene_id": "B1",
"eob_type": "OUTPATIENT",
"clm_type_cd": "40",
"system_type": "NationalClaimsHistory",
"final_action": "FinalAction",
"last_updated": "2026-01-01",
"clm_from_dt": date(2025, 6, 1),
"clm_thru_dt": date(2025, 6, 1),
},
]
)
@pytest.fixture
def v3_items() -> pl.DataFrame:
return _eob(
[
{
"clm_id": "I1",
"clm_line_num": 1.0,
"clm_line_from_dt": date(2025, 1, 1),
"clm_line_thru_dt": date(2025, 1, 9),
"clm_line_prod_rev_ctr_cd": "0120",
"clm_line_hcpcs_cd": "99223",
"clm_line_srvc_unit_qty": 1.4,
"clm_line_cvrd_pd_amt": 100.0,
"rev_cntr_tot_chrg_amt": 250.0,
"rev_cntr_coinsrnc_wge_adjstd_c": 10.0,
"rev_cntr_cash_ddctbl_amt": 5.0,
},
{
"clm_id": "I1",
"clm_line_num": 2.0,
"clm_line_from_dt": date(2025, 1, 2),
"clm_line_thru_dt": date(2025, 1, 2),
"clm_line_prod_rev_ctr_cd": "0450",
"clm_line_hcpcs_cd": None,
"clm_line_srvc_unit_qty": 2.0,
"clm_line_cvrd_pd_amt": 50.0,
"rev_cntr_tot_chrg_amt": 75.0,
},
{
"clm_id": "C1",
"clm_line_num": 1.0,
"clm_line_from_dt": date(2025, 2, 1),
"clm_line_thru_dt": date(2025, 2, 1),
"clm_line_hcpcs_cd": "99213",
"hcpcs_1_mdfr_cd": "25",
"clm_pos_cd": "11",
"clm_line_srvc_unit_qty": 1.0,
"clm_line_cvrd_pd_amt": 60.0,
"clm_line_alowd_chrg_amt": 75.0,
"clm_line_sbmtd_chrg_amt": 150.0,
"clm_line_coinsrnc_amt": 15.0,
"clm_line_bene_ptb_ddctbl_amt": 0.0,
},
{
"clm_id": "D1",
"clm_line_num": 1.0,
"clm_line_from_dt": date(2025, 3, 1),
"clm_line_thru_dt": date(2025, 3, 31),
"clm_line_hcpcs_cd": "E1390",
"clm_pos_cd": "12",
"clm_line_srvc_unit_qty": 1.0,
"clm_line_cvrd_pd_amt": 80.0,
"clm_line_alowd_chrg_amt": 100.0,
"clm_line_sbmtd_chrg_amt": 120.0,
},
{
"clm_id": "P1",
"clm_line_num": 1.0,
"clm_line_from_dt": date(2025, 4, 1),
"clm_line_thru_dt": date(2025, 4, 1),
"clm_line_ndc_cd": "00002143380",
"clm_line_ndc_qty": 30.0,
"clm_line_srvc_unit_qty": 1.0,
"tot_rx_cst_amt": 118.0,
},
{
"clm_id": "N1",
"clm_line_num": 1.0,
"clm_line_from_dt": date(2025, 6, 1),
"clm_line_thru_dt": date(2025, 6, 1),
"clm_line_hcpcs_cd": "G0463",
"clm_line_cvrd_pd_amt": 30.0,
},
{
"clm_id": "X1",
"clm_line_num": 1.0,
"clm_line_from_dt": date(2025, 5, 1),
"clm_line_thru_dt": date(2025, 5, 1),
"clm_line_cvrd_pd_amt": 1.0,
},
]
)
@pytest.fixture
def v2_headers() -> pl.DataFrame:
"""v2-shaped headers: routed by RIC code, no tags, no control numbers."""
return _eob(
[
{
"clm_id": "V1",
"bene_id": "B1",
"nch_near_line_rec_ident_cd": "V",
"clm_from_dt": date(2023, 1, 1),
"clm_thru_dt": date(2023, 1, 5),
"clm_bill_fac_type_cd": "1",
"clm_bill_clsfctn_cd": "1",
"clm_bill_freq_cd": "1",
"dgns_drg_cd": "MS-DRG470",
"oprtg_prvdr_npi_num": "OP",
"fac_prvdr_npi_num": "FACV",
"clm_mdcr_instnl_tot_chrg_amt": 5000.0,
},
{
"clm_id": "O1",
"bene_id": "B1",
"nch_near_line_rec_ident_cd": "O",
"clm_from_dt": date(2023, 2, 1),
"clm_thru_dt": date(2023, 2, 1),
"rndrg_prvdr_npi_num": "RN",
"carr_clm_blg_npi_num": "BL",
},
{
"clm_id": "M1",
"bene_id": "B2",
"nch_near_line_rec_ident_cd": "M",
"clm_from_dt": date(2023, 3, 1),
"clm_thru_dt": date(2023, 3, 1),
"ordrg_prvdr_npi_num": "ORD",
"payto_prvdr_npi_num": "PAY",
},
{
"clm_id": "P2",
"pde_id": "PDE-2",
"bene_id": "B2",
"nch_near_line_rec_ident_cd": "P",
"clm_from_dt": date(2023, 4, 1),
"clm_thru_dt": date(2023, 4, 1),
"clm_prsbng_prvdr_gnrc_id_num": "PRS2",
"clm_srvc_prvdr_gnrc_id_num": "DSP2",
"clm_line_days_suply_qty": 30.0,
"clm_line_rx_fill_num": 0.0,
},
{
"clm_id": "W1",
"bene_id": "B1",
"nch_near_line_rec_ident_cd": "W",
"clm_from_dt": date(2023, 5, 1),
"clm_thru_dt": date(2023, 5, 1),
},
{
"clm_id": "Z1",
"bene_id": "B1",
"nch_near_line_rec_ident_cd": "Z",
"clm_from_dt": date(2023, 6, 1),
"clm_thru_dt": date(2023, 6, 1),
},
{
"clm_id": "T1",
"bene_id": "B1",
"clm_type_cd": "72",
"clm_from_dt": date(2023, 7, 1),
"clm_thru_dt": date(2023, 7, 1),
},
]
)
@pytest.fixture
def v2_items() -> pl.DataFrame:
return _eob(
[
{
"clm_id": "V1",
"clm_line_num": 1.0,
"clm_line_from_dt": date(2023, 1, 1),
"clm_line_thru_dt": date(2023, 1, 5),
"clm_line_prod_rev_ctr_cd": "0301",
"clm_line_hcpcs_cd": "99213",
"clm_line_srvc_unit_qty": 1.0,
"clm_line_cvrd_pd_amt": 75.5,
},
{
"clm_id": "O1",
"clm_line_num": 1.0,
"clm_line_from_dt": date(2023, 2, 1),
"clm_line_thru_dt": date(2023, 2, 1),
"clm_line_hcpcs_cd": "99214",
"clm_pos_cd": "11",
"clm_line_srvc_unit_qty": 2.0,
"clm_line_cvrd_pd_amt": 120.0,
"clm_line_alowd_chrg_amt": 150.0,
},
{
"clm_id": "M1",
"clm_line_num": 1.0,
"clm_line_from_dt": date(2023, 3, 1),
"clm_line_thru_dt": date(2023, 3, 1),
"clm_line_hcpcs_cd": "E1390",
"clm_pos_cd": "12",
"clm_line_cvrd_pd_amt": 50.0,
"clm_line_alowd_chrg_amt": 60.0,
},
{
"clm_id": "P2",
"clm_line_num": 1.0,
"clm_line_from_dt": date(2023, 4, 1),
"clm_line_thru_dt": date(2023, 4, 1),
"clm_line_ndc_cd": "12345678901",
"clm_line_srvc_unit_qty": 30.0,
"clm_line_cvrd_pd_amt": 10.0,
"clm_line_bene_pmt_amt": 5.0,
},
]
)
# ── helpers ──────────────────────────────────────────────────
class TestHelpers:
def test_case_nests(self) -> None:
df = pl.DataFrame({"x": [1, 2, 3]})
expr = ex._case(
(nw.col("x") == 1, nw.lit("a")),
(nw.col("x") == 2, nw.lit("b")),
default=nw.lit("z"),
)
out = nw.from_native(df).select(expr.alias("y")).to_native()
assert out["y"].to_list() == ["a", "b", "z"]
def test_ensure_adds_typed_nulls(self) -> None:
df = nw.from_native(pl.DataFrame({"a": [1]}))
out = ex._ensure(df, {"a": nw.Int64, "b": nw.String, "c": nw.Date}).to_native()
assert out.columns == ["a", "b", "c"]
assert out.schema["b"] == pl.String and out.schema["c"] == pl.Date
assert ex._ensure(df, {"a": nw.Int64}).to_native().columns == ["a"]
# ── stg_claim_header ─────────────────────────────────────────
class TestStgClaimHeader:
def test_v3_routing_final_action_and_dedupe(self, v3_headers, xref) -> None:
out = stg_claim_header(
bcda__explanation_of_benefit=v3_headers, bcda___stg_beneficiary_xref=xref
)
fam = dict(
zip(out["clm_id"].to_list(), out["claim_family"].to_list(), strict=True)
)
assert fam == {
"I1": "institutional",
"C1": "professional",
"D1": "dme",
"P1": "pharmacy",
"N1": "institutional", # NCH row wins the CCN-PAIR
}
assert "X1" not in fam # NotFinalAction
assert "S1" not in fam # SharedSystem twin dropped
# sparse fixture: every output column is in the contract; the runner's
# exact-match check is exercised on real tables in test_pipe_cclf
assert set(out.columns) <= set(BcdaStgClaimHeader.column_names())
assert {"claim_family", "current_bene_mbi_id"} <= set(out.columns)
mbi = dict(
zip(
out["clm_id"].to_list(),
out["current_bene_mbi_id"].to_list(),
strict=True,
)
)
assert mbi["I1"] == "MBI-1" and mbi["D1"] == "MBI-2"
def test_newest_wins_within_a_source(self, xref) -> None:
eob = _eob(
[
{
"clm_id": "old",
"clm_cntl_num": "C",
"bene_id": "B1",
"eob_type": "CARRIER",
"system_type": "NationalClaimsHistory",
"final_action": "FinalAction",
"last_updated": "2026-01-01",
},
{
"clm_id": "new",
"clm_cntl_num": "C",
"bene_id": "B1",
"eob_type": "CARRIER",
"system_type": "NationalClaimsHistory",
"final_action": "FinalAction",
"last_updated": "2026-02-01",
},
]
)
out = stg_claim_header(
bcda__explanation_of_benefit=eob, bcda___stg_beneficiary_xref=xref
)
assert out["clm_id"].to_list() == ["new"]
def test_v2_routing_fallbacks(self, v2_headers, xref) -> None:
out = stg_claim_header(
bcda__explanation_of_benefit=v2_headers, bcda___stg_beneficiary_xref=xref
)
fam = dict(
zip(out["clm_id"].to_list(), out["claim_family"].to_list(), strict=True)
)
assert fam == {
"V1": "institutional",
"O1": "professional",
"M1": "dme",
"P2": "pharmacy",
"W1": "institutional", # Part B institutional RIC
"Z1": None, # unknown RIC → unrouted, kept for inspection
"T1": "professional", # CLM-TYPE-CD without an EOB-TYPE
}
assert len(out) == 7 # nothing without a control number is collapsed
def test_pde_id_routes_pharmacy_without_codes(self, xref) -> None:
eob = _eob([{"clm_id": "Q", "pde_id": "Q", "bene_id": "B1"}])
out = stg_claim_header(
bcda__explanation_of_benefit=eob, bcda___stg_beneficiary_xref=xref
)
assert out["claim_family"].to_list() == ["pharmacy"]
def test_family_filters(self, v3_headers, xref) -> None:
hdr = stg_claim_header(
bcda__explanation_of_benefit=v3_headers, bcda___stg_beneficiary_xref=xref
)
assert sorted(
stg_part_a_header(bcda___stg_claim_header=hdr)["clm_id"].to_list()
) == ["I1", "N1"]
assert stg_part_b_physician_header(bcda___stg_claim_header=hdr)[
"clm_id"
].to_list() == ["C1"]
assert stg_part_b_dme_header(bcda___stg_claim_header=hdr)[
"clm_id"
].to_list() == ["D1"]
assert stg_part_d_header(bcda___stg_claim_header=hdr)["clm_id"].to_list() == [
"P1"
]
# ── medical_claim ────────────────────────────────────────────
@pytest.fixture
def v3_medical(v3_headers, v3_items, xref) -> pl.DataFrame:
hdr = stg_claim_header(
bcda__explanation_of_benefit=v3_headers, bcda___stg_beneficiary_xref=xref
)
return medical_claim(
bcda___int_institutional_medical_claim=int_institutional_medical_claim(
bcda___stg_part_a_header=stg_part_a_header(bcda___stg_claim_header=hdr),
bcda__eob_item=v3_items,
),
bcda___int_physician_medical_claim=int_physician_medical_claim(
bcda___stg_part_b_physician_header=stg_part_b_physician_header(
bcda___stg_claim_header=hdr
),
bcda__eob_item=v3_items,
),
bcda___int_dme_medical_claim=int_dme_medical_claim(
bcda___stg_part_b_dme_header=stg_part_b_dme_header(
bcda___stg_claim_header=hdr
),
bcda__eob_item=v3_items,
),
)
class TestMedicalClaimV3:
def test_contract_and_rows(self, v3_medical) -> None:
assert set(v3_medical.columns) == set(BcdaMedicalClaim.column_names())
assert sorted(v3_medical["claim_id"].to_list()) == [
"C1",
"D1",
"I1",
"I1",
"N1",
]
assert v3_medical.filter(pl.col("claim_id") == "X1").is_empty()
def test_institutional_line(self, v3_medical) -> None:
r = v3_medical.filter(
(pl.col("claim_id") == "I1") & (pl.col("claim_line_number") == 1)
).to_dicts()[0]
assert r["claim_type"] == "institutional" and r["person_id"] == "MBI-1"
assert (r["admission_date"], r["discharge_date"]) == (
date(2024, 12, 31),
date(2025, 1, 9),
)
assert (
r["admit_source_code"],
r["admit_type_code"],
r["discharge_disposition_code"],
) == ("7", "1", "01")
assert r["bill_type_code"] == "0111" # NUBC, not "111111"
assert (r["drg_code_type"], r["drg_code"]) == ("ms-drg", "470")
assert r["revenue_center_code"] == "0120" and r["hcpcs_code"] == "99223"
assert r["service_unit_quantity"] == 1
assert (
r["rendering_npi"],
r["billing_npi"],
r["facility_npi"],
r["billing_tin"],
) == ("ATT", "FAC", "FAC", "TIN-FAC")
assert r["rendering_tin"] is None and r["place_of_service_code"] is None
assert (
r["paid_amount"],
r["charge_amount"],
r["coinsurance_amount"],
r["deductible_amount"],
) == (100.0, 250.0, 10.0, 5.0)
assert r["allowed_amount"] is None
assert r["paid_date"] == date(2025, 1, 20) # NCH weekly process date
assert (
r["diagnosis_code_type"],
r["diagnosis_code_1"],
r["diagnosis_poa_1"],
) == ("icd-10-cm", "I10", "Y")
assert (
r["procedure_code_type"],
r["procedure_code_1"],
r["procedure_date_1"],
) == ("icd-10-pcs", "0SRC0J9", date(2025, 1, 2))
assert r["diagnosis_code_25"] is None and r["procedure_code_2"] is None
assert r["in_network_flag"] == 1 and r["data_source"] == "medicare bcda"
line2 = v3_medical.filter(
(pl.col("claim_id") == "I1") & (pl.col("claim_line_number") == 2)
).to_dicts()[0]
assert line2["hcpcs_code"] is None and line2["charge_amount"] == 75.0
def test_professional_line(self, v3_medical) -> None:
r = v3_medical.filter(pl.col("claim_id") == "C1").to_dicts()[0]
assert r["claim_type"] == "professional"
assert r["place_of_service_code"] == "11" and r["hcpcs_modifier_1"] == "25"
assert (r["rendering_npi"], r["rendering_tin"], r["billing_npi"]) == (
"RND",
"TIN-RND",
"BLG",
)
# the carrier TIN is the TIN the line was billed under (CCLF5's only TIN)
assert r["billing_tin"] == "TIN-RND"
assert (
r["facility_npi"] is None
and r["bill_type_code"] is None
and r["admission_date"] is None
)
assert (
r["paid_amount"],
r["allowed_amount"],
r["charge_amount"],
r["coinsurance_amount"],
r["deductible_amount"],
) == (60.0, 75.0, 150.0, 15.0, 0.0)
assert r["paid_date"] == date(2025, 2, 10) # CMS process date fallback
assert (
r["diagnosis_code_1"] == "E11.9"
and r["procedure_code_type"] is None
and r["diagnosis_poa_1"] is None
)
def test_dme_line(self, v3_medical) -> None:
r = v3_medical.filter(pl.col("claim_id") == "D1").to_dicts()[0]
assert r["claim_type"] == "professional" and r["person_id"] == "MBI-2"
assert (r["rendering_npi"], r["billing_npi"]) == (
"ORD",
"PAY",
) # ordering / pay-to fallbacks
assert r["place_of_service_code"] == "12" and r["paid_date"] is None
def test_bill_type_fallback_from_v3_freq_code(self, xref) -> None:
"""No NUBC coding captured → leading zero + the 3-char v3 freq code."""
eob = _eob(
[
{
"clm_id": "H",
"clm_cntl_num": "C",
"bene_id": "B1",
"eob_type": "HHA",
"final_action": "FinalAction",
"clm_bill_fac_type_cd": "3",
"clm_bill_clsfctn_cd": "32",
"clm_bill_freq_cd": "329",
},
]
)
items = _eob([{"clm_id": "H", "clm_line_num": 1.0}])
hdr = stg_claim_header(
bcda__explanation_of_benefit=eob, bcda___stg_beneficiary_xref=xref
)
out = int_institutional_medical_claim(
bcda___stg_part_a_header=stg_part_a_header(bcda___stg_claim_header=hdr),
bcda__eob_item=items,
)
assert out["bill_type_code"].to_list() == ["0329"]
def test_pair_survivor_is_nch(self, v3_medical) -> None:
assert v3_medical.filter(pl.col("claim_id") == "N1")[
"hcpcs_code"
].to_list() == ["G0463"]
class TestMedicalClaimV2:
def test_v2_frames_map(self, v2_headers, v2_items, xref) -> None:
hdr = stg_claim_header(
bcda__explanation_of_benefit=v2_headers, bcda___stg_beneficiary_xref=xref
)
inst = int_institutional_medical_claim(
bcda___stg_part_a_header=stg_part_a_header(bcda___stg_claim_header=hdr),
bcda__eob_item=v2_items,
)
prof = int_physician_medical_claim(
bcda___stg_part_b_physician_header=stg_part_b_physician_header(
bcda___stg_claim_header=hdr
),
bcda__eob_item=v2_items,
)
dme = int_dme_medical_claim(
bcda___stg_part_b_dme_header=stg_part_b_dme_header(
bcda___stg_claim_header=hdr
),
bcda__eob_item=v2_items,
)
out = medical_claim(
bcda___int_institutional_medical_claim=inst,
bcda___int_physician_medical_claim=prof,
bcda___int_dme_medical_claim=dme,
)
assert set(out.columns) == set(BcdaMedicalClaim.column_names())
v1 = out.filter(pl.col("claim_id") == "V1").to_dicts()[0]
assert v1["bill_type_code"] == "111" # v2 fac + clsfctn + freq
assert (
v1["drg_code"] == "470" and v1["charge_amount"] == 5000.0
) # header total fallback
assert (v1["rendering_npi"], v1["billing_npi"]) == ("OP", "FACV")
assert (v1["admission_date"], v1["discharge_date"]) == (
date(2023, 1, 1),
date(2023, 1, 5),
)
assert v1["paid_date"] is None and v1["coinsurance_amount"] is None
o1 = out.filter(pl.col("claim_id") == "O1").to_dicts()[0]
assert (o1["rendering_npi"], o1["billing_npi"], o1["allowed_amount"]) == (
"RN",
"BL",
150.0,
)
m1 = out.filter(pl.col("claim_id") == "M1").to_dicts()[0]
assert (m1["rendering_npi"], m1["billing_npi"], m1["paid_amount"]) == (
"ORD",
"PAY",
50.0,
)
# W1 and T1 route but have no lines; Z1 is unrouted
assert sorted(set(out["claim_id"].to_list())) == ["M1", "O1", "V1"]
# ── pharmacy_claim ───────────────────────────────────────────
class TestPharmacyClaim:
def test_v3(self, v3_headers, v3_items, xref) -> None:
hdr = stg_claim_header(
bcda__explanation_of_benefit=v3_headers, bcda___stg_beneficiary_xref=xref
)
out = pharmacy_claim(
bcda___stg_part_d_header=stg_part_d_header(bcda___stg_claim_header=hdr),
bcda__eob_item=v3_items,
)
assert set(out.columns) == set(BcdaPharmacyClaim.column_names())
r = out.to_dicts()[0]
assert (r["claim_id"], r["claim_line_number"], r["person_id"]) == (
"P1",
1,
"MBI-2",
)
assert (r["prescribing_provider_npi"], r["dispensing_provider_npi"]) == (
"PRS",
"PHARM",
)
assert (r["dispensing_date"], r["paid_date"]) == (
date(2025, 4, 1),
date(2025, 4, 3),
)
assert (r["ndc_code"], r["quantity"], r["days_supply"], r["refills"]) == (
"00002143380",
30,
30,
2,
)
assert (
r["paid_amount"] is None
) # sandbox emits no plan-paid; header clm_pmt_amt is NULL too
assert (r["charge_amount"], r["copayment_amount"]) == (
118.0,
7.5,
) # line drug cost, header paid-by-patient
assert r["allowed_amount"] is None and r["deductible_amount"] is None
def test_v2(self, v2_headers, v2_items, xref) -> None:
hdr = stg_claim_header(
bcda__explanation_of_benefit=v2_headers, bcda___stg_beneficiary_xref=xref
)
out = pharmacy_claim(
bcda___stg_part_d_header=stg_part_d_header(bcda___stg_claim_header=hdr),
bcda__eob_item=v2_items,
)
r = out.to_dicts()[0]
assert r["claim_id"] == "PDE-2" and r["dispensing_provider_npi"] == "DSP2"
assert (r["quantity"], r["days_supply"], r["refills"]) == (30, 30, 0)
assert (r["paid_amount"], r["copayment_amount"], r["charge_amount"]) == (
10.0,
5.0,
None,
)
assert r["paid_date"] == date(2023, 4, 1)
# ── eligibility ──────────────────────────────────────────────
def _patients() -> pl.DataFrame:
return pl.DataFrame(
{
"bene_id": ["B1", "B2", "B3"],
"bene_mbi_id": ["M1", "M2", "M3"],
"bene_sex_cd": ["1", "female", "9"],
"bene_race_cd": ["1", "5", None],
"bene_dob": [date(1950, 1, 1), date(1960, 2, 2), None],
"bene_death_dt": [None, date(2025, 1, 1), None],
"bene_1st_name": ["a", "b", "c"],
"bene_midl_name": ["x", None, None],
"bene_last_name": ["A", "B", "C"],
"bene_line_1_adr": ["1 st", None, None],
"geo_zip_plc_name": ["Town", None, None],
"geo_usps_state_cd": ["TX", "VA", None],
"geo_zip5_cd": ["77550", None, None],
}
)
class TestEligibility:
def test_v3_coverage_rows(self) -> None:
coverage = pl.DataFrame(
{
"subscriber_id": ["M1", "M1", "M1", "M2", "M2", "M3"],
"class_plan": [
"Part C",
"Part B",
"Part A",
"Part B",
"Dual Medicare/Medicaid",
"Part A",
],
"status": [
"active",
"active",
"cancelled",
"active",
"active",
"active",
],
"ms_cd": [None, "10", "20", "10", None, "30"],
"orec": [None, "0", "1", "0", None, "2"],
"dual_stus_cd": [None, None, None, None, "08", None],
"bene_part_a_enrlmt_bgn_dt": [
None,
None,
date(2020, 1, 1),
None,
None,
date(2021, 1, 1),
],
"mdcr_covg_a_end_dt": [
None,
None,
date(2024, 12, 31),
None,
None,
None,
],
"bene_part_b_enrlmt_bgn_dt": [
None,
date(2020, 6, 1),
None,
date(2022, 1, 1),
None,
None,
],
"mdcr_covg_b_end_dt": [None, None, None, None, None, None],
}
)
demo = stg_beneficiary_demographics(
bcda__patient=_patients(), bcda__coverage=coverage
).sort("bene_mbi_id")
assert set(demo.columns) == set(BcdaStgBeneficiaryDemographics.column_names())
assert demo["coverage_plan"].to_list() == ["Part A", "Part B", "Part A"]
assert demo["ms_cd"].to_list() == ["20", "10", "30"]
assert demo["dual_stus_cd_dual"].to_list() == [None, "08", None]
assert demo["enrollment_start_date"].to_list() == [
date(2020, 1, 1),
date(2022, 1, 1),
date(2021, 1, 1),
]
elig = eligibility(bcda___stg_beneficiary_demographics=demo).sort("person_id")
assert set(elig.columns) == set(BcdaEligibility.column_names())
assert elig["gender"].to_list() == ["male", "female", "unknown"]
assert elig["race"].to_list() == ["white", "hispanic", "unknown"]
assert elig["ethnicity"].to_list() == [None, "hispanic", None]
assert elig["enrollment_status"].to_list() == ["inactive", "active", "active"]
assert elig["dual_status_code"].to_list() == [None, "08", None]
assert elig["medicare_status_code"].to_list() == ["20", "10", "30"]
assert elig["original_reason_entitlement_code"].to_list() == ["1", "0", "2"]
assert elig["enrollment_end_date"].to_list() == [date(2024, 12, 31), None, None]
assert elig["death_flag"].to_list() == [0, 1, 0]
assert (
elig["payer_type"].to_list() == ["medicare"] * 3
and elig["hospice_flag"].null_count() == 3
)
def test_v2_coverage_without_plan_columns(self) -> None:
coverage = pl.DataFrame(
{
"subscriber_id": ["M1", "M3"],
"ms_cd": ["10", "30"],
"orec": ["0", "2"],
"dual_01": ["04", None],
"bene_part_a_enrlmt_bgn_dt": [date(2019, 1, 1), None],
"mdcr_covg_a_end_dt": [None, None],
}
)
demo = stg_beneficiary_demographics(
bcda__patient=_patients(), bcda__coverage=coverage
).sort("bene_mbi_id")
assert demo["coverage_plan"].to_list() == [None, None, None]
elig = eligibility(bcda___stg_beneficiary_demographics=demo).sort("person_id")
assert elig["dual_status_code"].to_list() == ["04", None, None]
assert elig["medicare_status_code"].to_list() == ["10", None, "30"]
assert elig["enrollment_status"].null_count() == 3
assert elig["enrollment_start_date"].to_list() == [date(2019, 1, 1), None, None]