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
"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
926 lines
35 KiB
Python
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]
|