1547 lines
53 KiB
Python
1547 lines
53 KiB
Python
"""Tests for aco.express.provider_attribution — uncovered functions.
|
|
|
|
Covers int_provider_classification, int_primary_care_claims,
|
|
int_current_steps, and int_yearly_steps with polars DataFrames.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import datetime
|
|
from unittest.mock import patch
|
|
|
|
import polars as pl
|
|
import pytest
|
|
|
|
from aco.express.provider_attribution import (
|
|
int_current_steps,
|
|
int_primary_care_claims,
|
|
int_provider_classification,
|
|
int_yearly_steps,
|
|
)
|
|
|
|
# ---- shared fixtures -------------------------------------------------------
|
|
|
|
D = datetime.date
|
|
|
|
|
|
@pytest.fixture
|
|
def provider_df() -> pl.DataFrame:
|
|
"""terminology.provider rows — two individuals and one org."""
|
|
return pl.DataFrame(
|
|
{
|
|
"npi": ["NPI_A", "NPI_B", "NPI_ORG"],
|
|
"primary_taxonomy_code": ["TAX01", "TAX02", "TAX03"],
|
|
"primary_specialty_description": [
|
|
"Family Medicine",
|
|
"Cardiology",
|
|
"Hospital",
|
|
],
|
|
"entity_type_description": [
|
|
"Individual",
|
|
"Individual",
|
|
"Organization",
|
|
],
|
|
}
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def taxonomy_crosswalk_df() -> pl.DataFrame:
|
|
"""taxonomy crosswalk: taxonomy_code -> medicare_specialty_code."""
|
|
return pl.DataFrame(
|
|
{
|
|
"provider_taxonomy_code": ["TAX01", "TAX02"],
|
|
"medicare_specialty_code": ["8", "6"],
|
|
}
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def specialty_assignment_df() -> pl.DataFrame:
|
|
"""provider specialty assignment codes seed."""
|
|
return pl.DataFrame(
|
|
{
|
|
"specialty_code": ["08", "06", "97"],
|
|
"primary_care_physician_step1": ["yes", "no", "no"],
|
|
"specialist_physician_step_2": ["no", "yes", "no"],
|
|
"physician": [1, 1, 0],
|
|
}
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def provider_classification_df() -> pl.DataFrame:
|
|
"""Pre-built provider classification for downstream tests."""
|
|
return pl.DataFrame(
|
|
{
|
|
"provider_id": ["NPI_A", "NPI_B", "NPI_C"],
|
|
"prov_specialty": [
|
|
"Family Medicine",
|
|
"Cardiology",
|
|
"Nurse Practitioner",
|
|
],
|
|
"provider_bucket": ["pcp", "specialist", "npp"],
|
|
}
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def calendar_df() -> pl.DataFrame:
|
|
"""reference_data.calendar — daily rows for Jan-Dec 2024 and
|
|
Jan-Jun 2025, plus one row per month for 2023.
|
|
We include enough to span 24-month windows."""
|
|
rows = []
|
|
for yr in (2023, 2024, 2025):
|
|
end_m = 12 if yr <= 2024 else 6
|
|
for m in range(1, end_m + 1):
|
|
for d in (1, 15):
|
|
dt = D(yr, m, d)
|
|
ym = int(f"{yr}{m:02d}")
|
|
rows.append(
|
|
{
|
|
"full_date": dt,
|
|
"year": yr,
|
|
"month": m,
|
|
"year_month": f"{yr}{m:02d}",
|
|
"first_day_of_month": D(yr, m, 1),
|
|
"last_day_of_month": D(yr, m, 28),
|
|
"year_month_int": ym,
|
|
}
|
|
)
|
|
return pl.DataFrame(rows)
|
|
|
|
|
|
@pytest.fixture
|
|
def member_months_df() -> pl.DataFrame:
|
|
"""member_months for 6 persons across 2024."""
|
|
rows = []
|
|
for pid in ("P1", "P2", "P3", "P4", "P5", "P6"):
|
|
for m in range(1, 13):
|
|
rows.append(
|
|
{
|
|
"person_id": pid,
|
|
"year_month": f"2024{m:02d}",
|
|
}
|
|
)
|
|
return pl.DataFrame(rows)
|
|
|
|
|
|
@pytest.fixture
|
|
def medical_claim_df() -> pl.DataFrame:
|
|
"""medical claims touching persons P1-P6 with various
|
|
rendering NPIs and HCPCS codes."""
|
|
return pl.DataFrame(
|
|
{
|
|
"claim_id": [
|
|
"C01",
|
|
"C02",
|
|
"C03",
|
|
"C04",
|
|
"C05",
|
|
"C06",
|
|
"C07",
|
|
"C08",
|
|
],
|
|
"claim_line_number": [1, 1, 1, 1, 1, 1, 1, 1],
|
|
"person_id": [
|
|
"P1",
|
|
"P2",
|
|
"P3",
|
|
"P4",
|
|
"P5",
|
|
"P5",
|
|
"P6",
|
|
"P6",
|
|
],
|
|
"claim_start_date": [
|
|
D(2024, 3, 1),
|
|
D(2024, 4, 1),
|
|
D(2024, 5, 1),
|
|
D(2024, 6, 1),
|
|
D(2024, 7, 1),
|
|
D(2024, 8, 1),
|
|
D(2024, 9, 1),
|
|
D(2024, 10, 1),
|
|
],
|
|
"claim_end_date": [
|
|
D(2024, 3, 15),
|
|
D(2024, 4, 15),
|
|
D(2024, 5, 15),
|
|
D(2024, 6, 15),
|
|
D(2024, 7, 15),
|
|
D(2024, 8, 15),
|
|
D(2024, 9, 15),
|
|
D(2024, 10, 15),
|
|
],
|
|
"allowed_amount": [
|
|
100.0,
|
|
200.0,
|
|
300.0,
|
|
400.0,
|
|
500.0,
|
|
600.0,
|
|
700.0,
|
|
800.0,
|
|
],
|
|
"paid_amount": [
|
|
80.0,
|
|
160.0,
|
|
240.0,
|
|
320.0,
|
|
400.0,
|
|
480.0,
|
|
560.0,
|
|
640.0,
|
|
],
|
|
"rendering_id": [
|
|
"NPI_A",
|
|
"NPI_B",
|
|
"NPI_C",
|
|
"NPI_A",
|
|
"NPI_B",
|
|
"NPI_C",
|
|
"NPI_A",
|
|
"NPI_B",
|
|
],
|
|
"hcpcs_code": [
|
|
"99213",
|
|
"99213",
|
|
"99213",
|
|
"99213",
|
|
"99213",
|
|
"99213",
|
|
"99213",
|
|
"99213",
|
|
],
|
|
"data_source": ["t"] * 8,
|
|
"encounter_id": [
|
|
"E01",
|
|
"E02",
|
|
"E03",
|
|
"E04",
|
|
"E05",
|
|
"E06",
|
|
"E07",
|
|
"E08",
|
|
],
|
|
}
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def hcpcs_codes_df() -> pl.DataFrame:
|
|
"""Primary-care HCPCS codes seed."""
|
|
return pl.DataFrame({"hcpcs_code": ["99213", "99214"]})
|
|
|
|
|
|
def _stg_medical_claim(medical_claim_df: pl.DataFrame) -> pl.DataFrame:
|
|
"""Apply stg_core__medical_claim transform inline."""
|
|
return medical_claim_df.rename({"rendering_id": "rendering_npi"}).select(
|
|
"claim_id",
|
|
"claim_line_number",
|
|
"person_id",
|
|
"claim_start_date",
|
|
"claim_end_date",
|
|
"allowed_amount",
|
|
"paid_amount",
|
|
"rendering_npi",
|
|
"hcpcs_code",
|
|
"data_source",
|
|
"encounter_id",
|
|
)
|
|
|
|
|
|
# ── int_provider_classification ─────────────────────────────────
|
|
|
|
|
|
class TestIntProviderClassification:
|
|
"""Joins provider -> crosswalk -> specialty codes, classifies."""
|
|
|
|
def test_returns_dataframe(
|
|
self, provider_df, taxonomy_crosswalk_df, specialty_assignment_df
|
|
) -> None:
|
|
result = int_provider_classification(
|
|
provider_df, taxonomy_crosswalk_df, specialty_assignment_df
|
|
)
|
|
assert isinstance(result, pl.DataFrame)
|
|
|
|
def test_output_columns(
|
|
self, provider_df, taxonomy_crosswalk_df, specialty_assignment_df
|
|
) -> None:
|
|
result = int_provider_classification(
|
|
provider_df, taxonomy_crosswalk_df, specialty_assignment_df
|
|
)
|
|
assert set(result.columns) == {
|
|
"provider_id",
|
|
"prov_specialty",
|
|
"provider_bucket",
|
|
}
|
|
|
|
def test_filters_to_individual(
|
|
self, provider_df, taxonomy_crosswalk_df, specialty_assignment_df
|
|
) -> None:
|
|
result = int_provider_classification(
|
|
provider_df, taxonomy_crosswalk_df, specialty_assignment_df
|
|
)
|
|
# NPI_ORG is Organization — should be excluded
|
|
ids = result["provider_id"].to_list()
|
|
assert "NPI_ORG" not in ids
|
|
|
|
def test_pcp_classification(
|
|
self, provider_df, taxonomy_crosswalk_df, specialty_assignment_df
|
|
) -> None:
|
|
result = int_provider_classification(
|
|
provider_df, taxonomy_crosswalk_df, specialty_assignment_df
|
|
)
|
|
row_a = result.filter(pl.col("provider_id") == "NPI_A")
|
|
assert len(row_a) == 1
|
|
assert row_a["provider_bucket"][0] == "pcp"
|
|
|
|
def test_specialist_classification(
|
|
self, provider_df, taxonomy_crosswalk_df, specialty_assignment_df
|
|
) -> None:
|
|
result = int_provider_classification(
|
|
provider_df, taxonomy_crosswalk_df, specialty_assignment_df
|
|
)
|
|
row_b = result.filter(pl.col("provider_id") == "NPI_B")
|
|
assert len(row_b) == 1
|
|
assert row_b["provider_bucket"][0] == "specialist"
|
|
|
|
def test_npp_classification(self) -> None:
|
|
"""Non-physician provider (physician == 0)."""
|
|
prov = pl.DataFrame(
|
|
{
|
|
"npi": ["NPI_N"],
|
|
"primary_taxonomy_code": ["TAX97"],
|
|
"primary_specialty_description": ["Nurse Practitioner"],
|
|
"entity_type_description": ["Individual"],
|
|
}
|
|
)
|
|
xwalk = pl.DataFrame(
|
|
{
|
|
"provider_taxonomy_code": ["TAX97"],
|
|
"medicare_specialty_code": ["97"],
|
|
}
|
|
)
|
|
assign = pl.DataFrame(
|
|
{
|
|
"specialty_code": ["97"],
|
|
"primary_care_physician_step1": ["no"],
|
|
"specialist_physician_step_2": ["no"],
|
|
"physician": [0],
|
|
}
|
|
)
|
|
result = int_provider_classification(prov, xwalk, assign)
|
|
assert result["provider_bucket"][0] == "npp"
|
|
|
|
def test_unknown_classification(self) -> None:
|
|
"""physician==1 but neither pcp nor specialist -> unknown."""
|
|
prov = pl.DataFrame(
|
|
{
|
|
"npi": ["NPI_U"],
|
|
"primary_taxonomy_code": ["TAX_U"],
|
|
"primary_specialty_description": ["Unknown Spec"],
|
|
"entity_type_description": ["Individual"],
|
|
}
|
|
)
|
|
xwalk = pl.DataFrame(
|
|
{
|
|
"provider_taxonomy_code": ["TAX_U"],
|
|
"medicare_specialty_code": ["99"],
|
|
}
|
|
)
|
|
assign = pl.DataFrame(
|
|
{
|
|
"specialty_code": ["99"],
|
|
"primary_care_physician_step1": ["no"],
|
|
"specialist_physician_step_2": ["no"],
|
|
"physician": [1],
|
|
}
|
|
)
|
|
result = int_provider_classification(prov, xwalk, assign)
|
|
assert result["provider_bucket"][0] == "unknown"
|
|
|
|
def test_priority_picks_best_bucket(self) -> None:
|
|
"""If a provider maps to two taxonomy codes yielding pcp and
|
|
specialist, pcp (priority 1) wins."""
|
|
prov = pl.DataFrame(
|
|
{
|
|
"npi": ["NPI_M", "NPI_M"],
|
|
"primary_taxonomy_code": ["TAX_A", "TAX_B"],
|
|
"primary_specialty_description": [
|
|
"Family Medicine",
|
|
"Cardiology",
|
|
],
|
|
"entity_type_description": [
|
|
"Individual",
|
|
"Individual",
|
|
],
|
|
}
|
|
)
|
|
xwalk = pl.DataFrame(
|
|
{
|
|
"provider_taxonomy_code": ["TAX_A", "TAX_B"],
|
|
"medicare_specialty_code": ["8", "6"],
|
|
}
|
|
)
|
|
assign = pl.DataFrame(
|
|
{
|
|
"specialty_code": ["08", "06"],
|
|
"primary_care_physician_step1": ["yes", "no"],
|
|
"specialist_physician_step_2": ["no", "yes"],
|
|
"physician": [1, 1],
|
|
}
|
|
)
|
|
result = int_provider_classification(prov, xwalk, assign)
|
|
assert len(result) == 1
|
|
assert result["provider_bucket"][0] == "pcp"
|
|
|
|
|
|
# ── int_primary_care_claims ──────────────────────────────────────
|
|
|
|
|
|
class TestIntPrimaryClaims:
|
|
"""Joins claims with calendar, member months, provider, etc."""
|
|
|
|
def test_returns_dataframe(
|
|
self,
|
|
medical_claim_df,
|
|
member_months_df,
|
|
calendar_df,
|
|
provider_classification_df,
|
|
hcpcs_codes_df,
|
|
) -> None:
|
|
mc = _stg_medical_claim(medical_claim_df)
|
|
# Provider table with individuals matching our NPIs
|
|
prov = pl.DataFrame(
|
|
{
|
|
"npi": ["NPI_A", "NPI_B", "NPI_C"],
|
|
"primary_taxonomy_code": ["T1", "T2", "T3"],
|
|
"primary_specialty_description": [
|
|
"Family Medicine",
|
|
"Cardiology",
|
|
"Nurse Practitioner",
|
|
],
|
|
"entity_type_description": [
|
|
"Individual",
|
|
"Individual",
|
|
"Individual",
|
|
],
|
|
}
|
|
)
|
|
result = int_primary_care_claims(
|
|
mc,
|
|
member_months_df,
|
|
calendar_df,
|
|
prov,
|
|
provider_classification_df,
|
|
hcpcs_codes_df,
|
|
)
|
|
assert isinstance(result, pl.DataFrame)
|
|
|
|
def test_output_columns(
|
|
self,
|
|
medical_claim_df,
|
|
member_months_df,
|
|
calendar_df,
|
|
provider_classification_df,
|
|
hcpcs_codes_df,
|
|
) -> None:
|
|
mc = _stg_medical_claim(medical_claim_df)
|
|
prov = pl.DataFrame(
|
|
{
|
|
"npi": ["NPI_A", "NPI_B", "NPI_C"],
|
|
"primary_taxonomy_code": ["T1", "T2", "T3"],
|
|
"primary_specialty_description": [
|
|
"Family Medicine",
|
|
"Cardiology",
|
|
"Nurse Practitioner",
|
|
],
|
|
"entity_type_description": [
|
|
"Individual",
|
|
"Individual",
|
|
"Individual",
|
|
],
|
|
}
|
|
)
|
|
result = int_primary_care_claims(
|
|
mc,
|
|
member_months_df,
|
|
calendar_df,
|
|
prov,
|
|
provider_classification_df,
|
|
hcpcs_codes_df,
|
|
)
|
|
expected_cols = {
|
|
"person_id",
|
|
"provider_id",
|
|
"provider_bucket",
|
|
"prov_specialty",
|
|
"encounter_id",
|
|
"claim_id",
|
|
"claim_year_month",
|
|
"claim_year_month_int",
|
|
"claim_year",
|
|
"claim_end_date",
|
|
"allowed_amount",
|
|
}
|
|
assert set(result.columns) == expected_cols
|
|
|
|
def test_fills_null_bucket_with_other_individual(
|
|
self,
|
|
medical_claim_df,
|
|
member_months_df,
|
|
calendar_df,
|
|
hcpcs_codes_df,
|
|
) -> None:
|
|
"""Provider not in classification -> bucket = other_individual."""
|
|
mc = _stg_medical_claim(medical_claim_df)
|
|
prov = pl.DataFrame(
|
|
{
|
|
"npi": ["NPI_A", "NPI_B", "NPI_C"],
|
|
"primary_taxonomy_code": ["T1", "T2", "T3"],
|
|
"primary_specialty_description": [
|
|
"Family Medicine",
|
|
"Cardiology",
|
|
"Nurse Practitioner",
|
|
],
|
|
"entity_type_description": [
|
|
"Individual",
|
|
"Individual",
|
|
"Individual",
|
|
],
|
|
}
|
|
)
|
|
# Empty classification — no providers classified
|
|
empty_pc = pl.DataFrame(
|
|
{
|
|
"provider_id": pl.Series([], dtype=pl.Utf8),
|
|
"prov_specialty": pl.Series([], dtype=pl.Utf8),
|
|
"provider_bucket": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
result = int_primary_care_claims(
|
|
mc,
|
|
member_months_df,
|
|
calendar_df,
|
|
prov,
|
|
empty_pc,
|
|
hcpcs_codes_df,
|
|
)
|
|
if len(result) > 0:
|
|
buckets = result["provider_bucket"].unique().to_list()
|
|
assert buckets == ["other_individual"]
|
|
|
|
def test_filters_to_primary_care_hcpcs(
|
|
self,
|
|
medical_claim_df,
|
|
member_months_df,
|
|
calendar_df,
|
|
provider_classification_df,
|
|
) -> None:
|
|
"""Claims with non-matching HCPCS codes are excluded."""
|
|
mc = _stg_medical_claim(medical_claim_df)
|
|
prov = pl.DataFrame(
|
|
{
|
|
"npi": ["NPI_A", "NPI_B", "NPI_C"],
|
|
"primary_taxonomy_code": ["T1", "T2", "T3"],
|
|
"primary_specialty_description": [
|
|
"Family Medicine",
|
|
"Cardiology",
|
|
"Nurse Practitioner",
|
|
],
|
|
"entity_type_description": [
|
|
"Individual",
|
|
"Individual",
|
|
"Individual",
|
|
],
|
|
}
|
|
)
|
|
# HCPCS codes that do not match any claim
|
|
no_match_hcpcs = pl.DataFrame({"hcpcs_code": ["XXXXX"]})
|
|
result = int_primary_care_claims(
|
|
mc,
|
|
member_months_df,
|
|
calendar_df,
|
|
prov,
|
|
provider_classification_df,
|
|
no_match_hcpcs,
|
|
)
|
|
assert len(result) == 0
|
|
|
|
|
|
# ── int_current_steps ────────────────────────────────────────────
|
|
|
|
|
|
def _build_primary_care_claims(
|
|
persons: list[str],
|
|
providers: list[str],
|
|
buckets: list[str],
|
|
claim_dates: list[datetime.date],
|
|
ym_ints: list[int],
|
|
) -> pl.DataFrame:
|
|
"""Build a primary-care-claims frame for step tests."""
|
|
n = len(persons)
|
|
return pl.DataFrame(
|
|
{
|
|
"person_id": persons,
|
|
"provider_id": providers,
|
|
"provider_bucket": buckets,
|
|
"prov_specialty": ["Spec"] * n,
|
|
"encounter_id": [f"E{i}" for i in range(n)],
|
|
"claim_id": [f"C{i}" for i in range(n)],
|
|
"claim_year_month": [str(ym) for ym in ym_ints],
|
|
"claim_year_month_int": ym_ints,
|
|
"claim_year": [str(ym)[:4] for ym in ym_ints],
|
|
"claim_end_date": claim_dates,
|
|
"allowed_amount": [100.0] * n,
|
|
}
|
|
)
|
|
|
|
|
|
class TestIntCurrentSteps:
|
|
"""5-step attribution waterfall for current period."""
|
|
|
|
@pytest.fixture
|
|
def prov_df(self) -> pl.DataFrame:
|
|
return pl.DataFrame(
|
|
{
|
|
"npi": ["NPI_A", "NPI_B", "NPI_C", "NPI_D", "NPI_E"],
|
|
"primary_taxonomy_code": ["T"] * 5,
|
|
"primary_specialty_description": ["Spec"] * 5,
|
|
"entity_type_description": ["Individual"] * 5,
|
|
}
|
|
)
|
|
|
|
@pytest.fixture
|
|
def pc_df(self) -> pl.DataFrame:
|
|
return pl.DataFrame(
|
|
{
|
|
"provider_id": [
|
|
"NPI_A",
|
|
"NPI_B",
|
|
"NPI_C",
|
|
"NPI_D",
|
|
"NPI_E",
|
|
],
|
|
"prov_specialty": ["Spec"] * 5,
|
|
"provider_bucket": [
|
|
"pcp",
|
|
"specialist",
|
|
"npp",
|
|
"pcp",
|
|
"other_individual",
|
|
],
|
|
}
|
|
)
|
|
|
|
@pytest.fixture
|
|
def cal_df(self) -> pl.DataFrame:
|
|
"""Calendar rows spanning 2023-01 through 2025-06."""
|
|
rows = []
|
|
for yr in (2023, 2024, 2025):
|
|
end_m = 12 if yr <= 2024 else 6
|
|
for m in range(1, end_m + 1):
|
|
dt = D(yr, m, 1)
|
|
ym = int(f"{yr}{m:02d}")
|
|
rows.append(
|
|
{
|
|
"full_date": dt,
|
|
"year": yr,
|
|
"month": m,
|
|
"year_month": f"{yr}{m:02d}",
|
|
"first_day_of_month": dt,
|
|
"last_day_of_month": D(yr, m, 28),
|
|
"year_month_int": ym,
|
|
}
|
|
)
|
|
# Second day in month to match claim_start_date
|
|
dt2 = D(yr, m, 15)
|
|
rows.append(
|
|
{
|
|
"full_date": dt2,
|
|
"year": yr,
|
|
"month": m,
|
|
"year_month": f"{yr}{m:02d}",
|
|
"first_day_of_month": dt,
|
|
"last_day_of_month": D(yr, m, 28),
|
|
"year_month_int": ym,
|
|
}
|
|
)
|
|
return pl.DataFrame(rows)
|
|
|
|
@pytest.fixture
|
|
def mm_df(self) -> pl.DataFrame:
|
|
"""Member months for P1-P5 across 2024."""
|
|
rows = []
|
|
for pid in ("P1", "P2", "P3", "P4", "P5"):
|
|
for m in range(1, 13):
|
|
rows.append({"person_id": pid, "year_month": f"2024{m:02d}"})
|
|
return pl.DataFrame(rows)
|
|
|
|
@pytest.fixture
|
|
def mc_df(self) -> pl.DataFrame:
|
|
"""Minimal medical claims for step-5 all-rendering path."""
|
|
return pl.DataFrame(
|
|
{
|
|
"claim_id": ["MC1"],
|
|
"claim_line_number": [1],
|
|
"person_id": ["P5"],
|
|
"claim_start_date": [D(2024, 5, 1)],
|
|
"claim_end_date": [D(2024, 5, 15)],
|
|
"allowed_amount": [50.0],
|
|
"paid_amount": [40.0],
|
|
"rendering_npi": ["NPI_E"],
|
|
"hcpcs_code": ["99999"],
|
|
"data_source": ["t"],
|
|
"encounter_id": ["EM1"],
|
|
}
|
|
)
|
|
|
|
def test_returns_all_step_columns(
|
|
self, pc_df, prov_df, cal_df, mm_df, mc_df
|
|
) -> None:
|
|
"""Basic run with one person assigned at step 1."""
|
|
pcc = _build_primary_care_claims(
|
|
persons=["P1"],
|
|
providers=["NPI_A"],
|
|
buckets=["pcp"],
|
|
claim_dates=[D(2024, 6, 15)],
|
|
ym_ints=[202406],
|
|
)
|
|
with patch("datetime.date") as mock_date:
|
|
mock_date.today.return_value = D(2025, 1, 15)
|
|
mock_date.side_effect = lambda *a, **k: D(*a, **k)
|
|
result = int_current_steps(pcc, pc_df, mc_df, mm_df, cal_df, prov_df)
|
|
expected = {
|
|
"person_id",
|
|
"provider_id",
|
|
"provider_bucket",
|
|
"prov_specialty",
|
|
"step",
|
|
"allowed_amount",
|
|
"visits",
|
|
"step_description",
|
|
}
|
|
assert set(result.columns) == expected
|
|
|
|
def test_step1_pcp_npp(self, pc_df, prov_df, cal_df, mm_df, mc_df):
|
|
"""Step 1 assigns PCP/NPP claims in 12-month window."""
|
|
pcc = _build_primary_care_claims(
|
|
persons=["P1"],
|
|
providers=["NPI_A"],
|
|
buckets=["pcp"],
|
|
claim_dates=[D(2024, 6, 15)],
|
|
ym_ints=[202406],
|
|
)
|
|
with patch("datetime.date") as mock_date:
|
|
mock_date.today.return_value = D(2025, 1, 15)
|
|
mock_date.side_effect = lambda *a, **k: D(*a, **k)
|
|
result = int_current_steps(pcc, pc_df, mc_df, mm_df, cal_df, prov_df)
|
|
s1 = result.filter(pl.col("step") == 1)
|
|
assert len(s1) >= 1
|
|
assert s1["step_description"][0] == ("12-month PCP/NPP primary-care HCPCS")
|
|
|
|
def test_step2_specialist(self, pc_df, prov_df, cal_df, mm_df, mc_df):
|
|
"""Step 2 picks up specialists for persons not assigned in
|
|
step 1."""
|
|
pcc = _build_primary_care_claims(
|
|
persons=["P2"],
|
|
providers=["NPI_B"],
|
|
buckets=["specialist"],
|
|
claim_dates=[D(2024, 6, 15)],
|
|
ym_ints=[202406],
|
|
)
|
|
with patch("datetime.date") as mock_date:
|
|
mock_date.today.return_value = D(2025, 1, 15)
|
|
mock_date.side_effect = lambda *a, **k: D(*a, **k)
|
|
result = int_current_steps(pcc, pc_df, mc_df, mm_df, cal_df, prov_df)
|
|
s2 = result.filter(pl.col("step") == 2)
|
|
assert len(s2) == 1
|
|
assert s2["step_description"][0] == ("12-month specialist primary-care HCPCS")
|
|
|
|
def test_step3_24month_pcp_npp(self, pc_df, prov_df, cal_df, mm_df, mc_df):
|
|
"""Step 3 uses 24-month PCP/NPP for unassigned.
|
|
|
|
We need as_of to be recent so that the older claim falls
|
|
outside the 12-month window but inside the 24-month window.
|
|
Include a recent dummy claim to push as_of forward.
|
|
"""
|
|
pcc = _build_primary_care_claims(
|
|
# Dummy recent claim for P1 (will land in step1) to
|
|
# push as_of to 2024-11-15
|
|
# P3's claim at 2023-12-15 is outside 12mo (as_of -
|
|
# 335d ~ 2023-12-15) but inside 24mo (as_of - 700d ~
|
|
# 2023-01-15)
|
|
persons=["P1", "P3"],
|
|
providers=["NPI_A", "NPI_C"],
|
|
buckets=["pcp", "npp"],
|
|
claim_dates=[D(2024, 11, 15), D(2023, 6, 15)],
|
|
ym_ints=[202411, 202306],
|
|
)
|
|
with patch("datetime.date") as mock_date:
|
|
mock_date.today.return_value = D(2025, 1, 15)
|
|
mock_date.side_effect = lambda *a, **k: D(*a, **k)
|
|
result = int_current_steps(pcc, pc_df, mc_df, mm_df, cal_df, prov_df)
|
|
s3 = result.filter(pl.col("step") == 3)
|
|
assert len(s3) == 1
|
|
assert s3["step_description"][0] == ("24-month PCP/NPP primary-care HCPCS")
|
|
|
|
def test_step4_24month_any(self, pc_df, prov_df, cal_df, mm_df, mc_df):
|
|
"""Step 4 uses 24-month any classification."""
|
|
pcc = _build_primary_care_claims(
|
|
persons=["P4"],
|
|
providers=["NPI_E"],
|
|
buckets=["other_individual"],
|
|
claim_dates=[D(2023, 6, 15)],
|
|
ym_ints=[202306],
|
|
)
|
|
with patch("datetime.date") as mock_date:
|
|
mock_date.today.return_value = D(2025, 1, 15)
|
|
mock_date.side_effect = lambda *a, **k: D(*a, **k)
|
|
result = int_current_steps(pcc, pc_df, mc_df, mm_df, cal_df, prov_df)
|
|
s4 = result.filter(pl.col("step") == 4)
|
|
assert len(s4) == 1
|
|
assert s4["step_description"][0] == (
|
|
"24-month primary-care HCPCS (any classification)"
|
|
)
|
|
|
|
def test_step5_any_rendering(self, pc_df, prov_df, cal_df, mm_df):
|
|
"""Step 5 uses all rendering claims for still-unassigned.
|
|
|
|
We need at least one PCC claim to give a valid as_of date
|
|
(otherwise max(claim_end_date) is null). Put a dummy claim
|
|
for P1 to anchor as_of, and P5 only has rendering claims.
|
|
"""
|
|
pcc = _build_primary_care_claims(
|
|
persons=["P1"],
|
|
providers=["NPI_A"],
|
|
buckets=["pcp"],
|
|
claim_dates=[D(2024, 5, 15)],
|
|
ym_ints=[202405],
|
|
)
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": ["MC5"],
|
|
"claim_line_number": [1],
|
|
"person_id": ["P5"],
|
|
"claim_start_date": [D(2024, 5, 1)],
|
|
"claim_end_date": [D(2024, 5, 15)],
|
|
"allowed_amount": [50.0],
|
|
"paid_amount": [40.0],
|
|
"rendering_npi": ["NPI_E"],
|
|
"hcpcs_code": ["99999"],
|
|
"data_source": ["t"],
|
|
"encounter_id": ["EM5"],
|
|
}
|
|
)
|
|
with patch("datetime.date") as mock_date:
|
|
mock_date.today.return_value = D(2025, 1, 15)
|
|
mock_date.side_effect = lambda *a, **k: D(*a, **k)
|
|
result = int_current_steps(pcc, pc_df, mc, mm_df, cal_df, prov_df)
|
|
s5 = result.filter(pl.col("step") == 5)
|
|
assert len(s5) == 1
|
|
assert s5["step_description"][0] == "24-month any rendering NPI"
|
|
|
|
def test_as_of_date_uses_max_claim_end_when_past(
|
|
self, pc_df, prov_df, cal_df, mm_df, mc_df
|
|
):
|
|
"""When max(claim_end_date) < today, uses max claim date."""
|
|
pcc = _build_primary_care_claims(
|
|
persons=["P1"],
|
|
providers=["NPI_A"],
|
|
buckets=["pcp"],
|
|
claim_dates=[D(2024, 3, 15)],
|
|
ym_ints=[202403],
|
|
)
|
|
with patch("datetime.date") as mock_date:
|
|
mock_date.today.return_value = D(2026, 1, 1)
|
|
mock_date.side_effect = lambda *a, **k: D(*a, **k)
|
|
result = int_current_steps(pcc, pc_df, mc_df, mm_df, cal_df, prov_df)
|
|
assert isinstance(result, pl.DataFrame)
|
|
|
|
def test_as_of_date_uses_today_when_future(
|
|
self, pc_df, prov_df, cal_df, mm_df, mc_df
|
|
):
|
|
"""When max(claim_end_date) > today, uses today."""
|
|
pcc = _build_primary_care_claims(
|
|
persons=["P1"],
|
|
providers=["NPI_A"],
|
|
buckets=["pcp"],
|
|
claim_dates=[D(2099, 1, 1)],
|
|
ym_ints=[209901],
|
|
)
|
|
with patch("datetime.date") as mock_date:
|
|
mock_date.today.return_value = D(2025, 1, 15)
|
|
mock_date.side_effect = lambda *a, **k: D(*a, **k)
|
|
result = int_current_steps(pcc, pc_df, mc_df, mm_df, cal_df, prov_df)
|
|
assert isinstance(result, pl.DataFrame)
|
|
|
|
def test_all_five_steps_populated(self, pc_df, prov_df, cal_df, mm_df):
|
|
"""With carefully crafted data, all 5 steps are populated."""
|
|
# P1: PCP claim in 12mo -> step1
|
|
# P2: specialist claim in 12mo -> step2
|
|
# P3: NPP claim in 24mo only -> step3
|
|
# P4: other_individual claim in 24mo only -> step4
|
|
# P5: no primary care claim, only rendering -> step5
|
|
pcc = _build_primary_care_claims(
|
|
persons=["P1", "P2", "P3", "P4"],
|
|
providers=["NPI_A", "NPI_B", "NPI_C", "NPI_E"],
|
|
buckets=["pcp", "specialist", "npp", "other_individual"],
|
|
claim_dates=[
|
|
D(2024, 6, 15),
|
|
D(2024, 6, 15),
|
|
D(2023, 6, 15),
|
|
D(2023, 6, 15),
|
|
],
|
|
ym_ints=[202406, 202406, 202306, 202306],
|
|
)
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": ["MCR5"],
|
|
"claim_line_number": [1],
|
|
"person_id": ["P5"],
|
|
"claim_start_date": [D(2024, 5, 1)],
|
|
"claim_end_date": [D(2024, 5, 15)],
|
|
"allowed_amount": [50.0],
|
|
"paid_amount": [40.0],
|
|
"rendering_npi": ["NPI_D"],
|
|
"hcpcs_code": ["99999"],
|
|
"data_source": ["t"],
|
|
"encounter_id": ["EM55"],
|
|
}
|
|
)
|
|
with patch("datetime.date") as mock_date:
|
|
mock_date.today.return_value = D(2025, 1, 15)
|
|
mock_date.side_effect = lambda *a, **k: D(*a, **k)
|
|
result = int_current_steps(pcc, pc_df, mc, mm_df, cal_df, prov_df)
|
|
steps = sorted(result["step"].unique().to_list())
|
|
assert steps == [1, 2, 3, 4, 5]
|
|
|
|
|
|
# ── int_yearly_steps ─────────────────────────────────────────────
|
|
|
|
|
|
class TestIntYearlySteps:
|
|
"""Per-year 5-step attribution waterfall."""
|
|
|
|
@pytest.fixture
|
|
def person_years_df(self) -> pl.DataFrame:
|
|
return pl.DataFrame(
|
|
{
|
|
"person_id": ["P1", "P2", "P3", "P4", "P5"],
|
|
"performance_year": [2024, 2024, 2024, 2024, 2024],
|
|
}
|
|
).cast({"person_id": pl.Utf8, "performance_year": pl.Int32})
|
|
|
|
@pytest.fixture
|
|
def pc_df(self) -> pl.DataFrame:
|
|
return pl.DataFrame(
|
|
{
|
|
"provider_id": [
|
|
"NPI_A",
|
|
"NPI_B",
|
|
"NPI_C",
|
|
"NPI_D",
|
|
"NPI_E",
|
|
],
|
|
"prov_specialty": ["Spec"] * 5,
|
|
"provider_bucket": [
|
|
"pcp",
|
|
"specialist",
|
|
"npp",
|
|
"pcp",
|
|
"other_individual",
|
|
],
|
|
}
|
|
)
|
|
|
|
@pytest.fixture
|
|
def prov_df(self) -> pl.DataFrame:
|
|
return pl.DataFrame(
|
|
{
|
|
"npi": ["NPI_A", "NPI_B", "NPI_C", "NPI_D", "NPI_E"],
|
|
"primary_taxonomy_code": ["T"] * 5,
|
|
"primary_specialty_description": ["Spec"] * 5,
|
|
"entity_type_description": ["Individual"] * 5,
|
|
}
|
|
)
|
|
|
|
@pytest.fixture
|
|
def cal_df(self) -> pl.DataFrame:
|
|
rows = []
|
|
for yr in (2023, 2024, 2025):
|
|
end_m = 12 if yr <= 2024 else 6
|
|
for m in range(1, end_m + 1):
|
|
for d in (1, 15):
|
|
dt = D(yr, m, d)
|
|
ym = int(f"{yr}{m:02d}")
|
|
rows.append(
|
|
{
|
|
"full_date": dt,
|
|
"year": yr,
|
|
"month": m,
|
|
"year_month": f"{yr}{m:02d}",
|
|
"first_day_of_month": D(yr, m, 1),
|
|
"last_day_of_month": D(yr, m, 28),
|
|
"year_month_int": ym,
|
|
}
|
|
)
|
|
return pl.DataFrame(rows)
|
|
|
|
@pytest.fixture
|
|
def mm_df(self) -> pl.DataFrame:
|
|
rows = []
|
|
for pid in ("P1", "P2", "P3", "P4", "P5"):
|
|
for m in range(1, 13):
|
|
rows.append({"person_id": pid, "year_month": f"2024{m:02d}"})
|
|
return pl.DataFrame(rows)
|
|
|
|
def test_returns_dataframe(
|
|
self, person_years_df, pc_df, prov_df, cal_df, mm_df
|
|
) -> None:
|
|
pcc = _build_primary_care_claims(
|
|
persons=["P1"],
|
|
providers=["NPI_A"],
|
|
buckets=["pcp"],
|
|
claim_dates=[D(2024, 6, 15)],
|
|
ym_ints=[202406],
|
|
)
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": pl.Series([], dtype=pl.Utf8),
|
|
"claim_line_number": pl.Series([], dtype=pl.Int64),
|
|
"person_id": pl.Series([], dtype=pl.Utf8),
|
|
"claim_start_date": pl.Series([], dtype=pl.Date),
|
|
"claim_end_date": pl.Series([], dtype=pl.Date),
|
|
"allowed_amount": pl.Series([], dtype=pl.Float64),
|
|
"paid_amount": pl.Series([], dtype=pl.Float64),
|
|
"rendering_npi": pl.Series([], dtype=pl.Utf8),
|
|
"hcpcs_code": pl.Series([], dtype=pl.Utf8),
|
|
"data_source": pl.Series([], dtype=pl.Utf8),
|
|
"encounter_id": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
result = int_yearly_steps(
|
|
person_years_df, pcc, pc_df, mc, mm_df, cal_df, prov_df
|
|
)
|
|
assert isinstance(result, pl.DataFrame)
|
|
|
|
def test_output_columns(
|
|
self, person_years_df, pc_df, prov_df, cal_df, mm_df
|
|
) -> None:
|
|
pcc = _build_primary_care_claims(
|
|
persons=["P1"],
|
|
providers=["NPI_A"],
|
|
buckets=["pcp"],
|
|
claim_dates=[D(2024, 6, 15)],
|
|
ym_ints=[202406],
|
|
)
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": pl.Series([], dtype=pl.Utf8),
|
|
"claim_line_number": pl.Series([], dtype=pl.Int64),
|
|
"person_id": pl.Series([], dtype=pl.Utf8),
|
|
"claim_start_date": pl.Series([], dtype=pl.Date),
|
|
"claim_end_date": pl.Series([], dtype=pl.Date),
|
|
"allowed_amount": pl.Series([], dtype=pl.Float64),
|
|
"paid_amount": pl.Series([], dtype=pl.Float64),
|
|
"rendering_npi": pl.Series([], dtype=pl.Utf8),
|
|
"hcpcs_code": pl.Series([], dtype=pl.Utf8),
|
|
"data_source": pl.Series([], dtype=pl.Utf8),
|
|
"encounter_id": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
result = int_yearly_steps(
|
|
person_years_df, pcc, pc_df, mc, mm_df, cal_df, prov_df
|
|
)
|
|
expected = {
|
|
"person_id",
|
|
"performance_year",
|
|
"provider_id",
|
|
"provider_bucket",
|
|
"prov_specialty",
|
|
"step",
|
|
"step_description",
|
|
"allowed_amount",
|
|
"visits",
|
|
}
|
|
assert set(result.columns) == expected
|
|
|
|
def test_step1_yearly_pcp(
|
|
self, person_years_df, pc_df, prov_df, cal_df, mm_df
|
|
) -> None:
|
|
"""Step 1 assigns PCP/NPP in the performance year."""
|
|
pcc = _build_primary_care_claims(
|
|
persons=["P1"],
|
|
providers=["NPI_A"],
|
|
buckets=["pcp"],
|
|
claim_dates=[D(2024, 6, 15)],
|
|
ym_ints=[202406],
|
|
)
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": pl.Series([], dtype=pl.Utf8),
|
|
"claim_line_number": pl.Series([], dtype=pl.Int64),
|
|
"person_id": pl.Series([], dtype=pl.Utf8),
|
|
"claim_start_date": pl.Series([], dtype=pl.Date),
|
|
"claim_end_date": pl.Series([], dtype=pl.Date),
|
|
"allowed_amount": pl.Series([], dtype=pl.Float64),
|
|
"paid_amount": pl.Series([], dtype=pl.Float64),
|
|
"rendering_npi": pl.Series([], dtype=pl.Utf8),
|
|
"hcpcs_code": pl.Series([], dtype=pl.Utf8),
|
|
"data_source": pl.Series([], dtype=pl.Utf8),
|
|
"encounter_id": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
result = int_yearly_steps(
|
|
person_years_df, pcc, pc_df, mc, mm_df, cal_df, prov_df
|
|
)
|
|
s1 = result.filter(pl.col("step") == 1)
|
|
assert len(s1) >= 1
|
|
assert s1["performance_year"][0] == 2024
|
|
|
|
def test_all_five_steps_yearly(
|
|
self, person_years_df, pc_df, prov_df, cal_df, mm_df
|
|
) -> None:
|
|
"""With crafted data, all 5 steps are populated for a year."""
|
|
# P1: PCP 2024 -> step1
|
|
# P2: specialist 2024 -> step2
|
|
# P3: NPP 2023 (24mo window) -> step3
|
|
# P4: other 2023 (24mo window) -> step4
|
|
# P5: rendering only -> step5
|
|
pcc = _build_primary_care_claims(
|
|
persons=["P1", "P2", "P3", "P4"],
|
|
providers=["NPI_A", "NPI_B", "NPI_C", "NPI_E"],
|
|
buckets=["pcp", "specialist", "npp", "other_individual"],
|
|
claim_dates=[
|
|
D(2024, 6, 15),
|
|
D(2024, 6, 15),
|
|
D(2023, 6, 15),
|
|
D(2023, 6, 15),
|
|
],
|
|
ym_ints=[202406, 202406, 202306, 202306],
|
|
)
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": ["MCR5"],
|
|
"claim_line_number": [1],
|
|
"person_id": ["P5"],
|
|
"claim_start_date": [D(2024, 5, 1)],
|
|
"claim_end_date": [D(2024, 5, 15)],
|
|
"allowed_amount": [50.0],
|
|
"paid_amount": [40.0],
|
|
"rendering_npi": ["NPI_D"],
|
|
"hcpcs_code": ["99999"],
|
|
"data_source": ["t"],
|
|
"encounter_id": ["EM55"],
|
|
}
|
|
)
|
|
result = int_yearly_steps(
|
|
person_years_df, pcc, pc_df, mc, mm_df, cal_df, prov_df
|
|
)
|
|
steps = sorted(result["step"].unique().to_list())
|
|
assert steps == [1, 2, 3, 4, 5]
|
|
|
|
def test_empty_years_returns_empty(self, pc_df, prov_df, cal_df, mm_df):
|
|
"""When person_years is empty, returns empty frame."""
|
|
empty_py = pl.DataFrame(
|
|
{
|
|
"person_id": pl.Series([], dtype=pl.Utf8),
|
|
"performance_year": pl.Series([], dtype=pl.Int32),
|
|
}
|
|
)
|
|
pcc = pl.DataFrame(
|
|
{
|
|
"person_id": pl.Series([], dtype=pl.Utf8),
|
|
"provider_id": pl.Series([], dtype=pl.Utf8),
|
|
"provider_bucket": pl.Series([], dtype=pl.Utf8),
|
|
"prov_specialty": pl.Series([], dtype=pl.Utf8),
|
|
"encounter_id": pl.Series([], dtype=pl.Utf8),
|
|
"claim_id": pl.Series([], dtype=pl.Utf8),
|
|
"claim_year_month": pl.Series([], dtype=pl.Utf8),
|
|
"claim_year_month_int": pl.Series([], dtype=pl.Int64),
|
|
"claim_year": pl.Series([], dtype=pl.Utf8),
|
|
"claim_end_date": pl.Series([], dtype=pl.Date),
|
|
"allowed_amount": pl.Series([], dtype=pl.Float64),
|
|
}
|
|
)
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": pl.Series([], dtype=pl.Utf8),
|
|
"claim_line_number": pl.Series([], dtype=pl.Int64),
|
|
"person_id": pl.Series([], dtype=pl.Utf8),
|
|
"claim_start_date": pl.Series([], dtype=pl.Date),
|
|
"claim_end_date": pl.Series([], dtype=pl.Date),
|
|
"allowed_amount": pl.Series([], dtype=pl.Float64),
|
|
"paid_amount": pl.Series([], dtype=pl.Float64),
|
|
"rendering_npi": pl.Series([], dtype=pl.Utf8),
|
|
"hcpcs_code": pl.Series([], dtype=pl.Utf8),
|
|
"data_source": pl.Series([], dtype=pl.Utf8),
|
|
"encounter_id": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
result = int_yearly_steps(empty_py, pcc, pc_df, mc, mm_df, cal_df, prov_df)
|
|
assert len(result) == 0
|
|
|
|
def test_step_descriptions(
|
|
self, person_years_df, pc_df, prov_df, cal_df, mm_df
|
|
) -> None:
|
|
"""Verify step_description labels for each step."""
|
|
pcc = _build_primary_care_claims(
|
|
persons=["P1", "P2", "P3", "P4"],
|
|
providers=["NPI_A", "NPI_B", "NPI_C", "NPI_E"],
|
|
buckets=["pcp", "specialist", "npp", "other_individual"],
|
|
claim_dates=[
|
|
D(2024, 6, 15),
|
|
D(2024, 6, 15),
|
|
D(2023, 6, 15),
|
|
D(2023, 6, 15),
|
|
],
|
|
ym_ints=[202406, 202406, 202306, 202306],
|
|
)
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": ["MCR5"],
|
|
"claim_line_number": [1],
|
|
"person_id": ["P5"],
|
|
"claim_start_date": [D(2024, 5, 1)],
|
|
"claim_end_date": [D(2024, 5, 15)],
|
|
"allowed_amount": [50.0],
|
|
"paid_amount": [40.0],
|
|
"rendering_npi": ["NPI_D"],
|
|
"hcpcs_code": ["99999"],
|
|
"data_source": ["t"],
|
|
"encounter_id": ["EM55"],
|
|
}
|
|
)
|
|
result = int_yearly_steps(
|
|
person_years_df, pcc, pc_df, mc, mm_df, cal_df, prov_df
|
|
)
|
|
desc_map = {
|
|
1: "12-month PCP/NPP primary-care HCPCS",
|
|
2: "12-month specialist primary-care HCPCS",
|
|
3: "24-month PCP/NPP primary-care HCPCS",
|
|
4: "24-month primary-care HCPCS (any classification)",
|
|
5: "24-month any rendering NPI",
|
|
}
|
|
for step_num, expected_desc in desc_map.items():
|
|
rows = result.filter(pl.col("step") == step_num)
|
|
if len(rows) > 0:
|
|
assert rows["step_description"][0] == expected_desc
|
|
|
|
|
|
# ── backend-agnostic fallback paths ─────────────────────────────
|
|
|
|
|
|
class TestCurrentStepsFallback:
|
|
"""Exercise the try/except fallback for .item() in
|
|
int_current_steps (lines 263-264)."""
|
|
|
|
def test_fetchone_fallback(self):
|
|
"""When .item() raises AttributeError, falls back to
|
|
.fetchone()."""
|
|
pcc = _build_primary_care_claims(
|
|
persons=["P1"],
|
|
providers=["NPI_A"],
|
|
buckets=["pcp"],
|
|
claim_dates=[D(2024, 6, 15)],
|
|
ym_ints=[202406],
|
|
)
|
|
pc = pl.DataFrame(
|
|
{
|
|
"provider_id": ["NPI_A"],
|
|
"prov_specialty": ["Spec"],
|
|
"provider_bucket": ["pcp"],
|
|
}
|
|
)
|
|
cal_rows = []
|
|
for yr in (2023, 2024, 2025):
|
|
end_m = 12 if yr <= 2024 else 6
|
|
for m in range(1, end_m + 1):
|
|
for d in (1, 15):
|
|
dt = D(yr, m, d)
|
|
ym = int(f"{yr}{m:02d}")
|
|
cal_rows.append(
|
|
{
|
|
"full_date": dt,
|
|
"year": yr,
|
|
"month": m,
|
|
"year_month": f"{yr}{m:02d}",
|
|
"first_day_of_month": D(yr, m, 1),
|
|
"last_day_of_month": D(yr, m, 28),
|
|
"year_month_int": ym,
|
|
}
|
|
)
|
|
cal = pl.DataFrame(cal_rows)
|
|
mm = pl.DataFrame(
|
|
[{"person_id": "P1", "year_month": f"2024{m:02d}"} for m in range(1, 13)]
|
|
)
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": pl.Series([], dtype=pl.Utf8),
|
|
"claim_line_number": pl.Series([], dtype=pl.Int64),
|
|
"person_id": pl.Series([], dtype=pl.Utf8),
|
|
"claim_start_date": pl.Series([], dtype=pl.Date),
|
|
"claim_end_date": pl.Series([], dtype=pl.Date),
|
|
"allowed_amount": pl.Series([], dtype=pl.Float64),
|
|
"paid_amount": pl.Series([], dtype=pl.Float64),
|
|
"rendering_npi": pl.Series([], dtype=pl.Utf8),
|
|
"hcpcs_code": pl.Series([], dtype=pl.Utf8),
|
|
"data_source": pl.Series([], dtype=pl.Utf8),
|
|
"encounter_id": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
prov = pl.DataFrame(
|
|
{
|
|
"npi": ["NPI_A"],
|
|
"primary_taxonomy_code": ["T"],
|
|
"primary_specialty_description": ["Spec"],
|
|
"entity_type_description": ["Individual"],
|
|
}
|
|
)
|
|
|
|
_orig_item = pl.DataFrame.item
|
|
|
|
def _broken_item(self, *a, **kw):
|
|
raise AttributeError("no item method")
|
|
|
|
class _FakeCursor:
|
|
"""Mimic a DB cursor that returns a scalar via
|
|
fetchone."""
|
|
|
|
def __init__(self, val):
|
|
self._val = val
|
|
|
|
def fetchone(self):
|
|
return (self._val,)
|
|
|
|
# Patch pl.DataFrame.item so the try block fails, then
|
|
# patch to_native to return our cursor-like object
|
|
with (
|
|
patch.object(pl.DataFrame, "item", _broken_item),
|
|
patch("datetime.date") as mock_date,
|
|
):
|
|
mock_date.today.return_value = D(2025, 1, 15)
|
|
mock_date.side_effect = lambda *a, **k: D(*a, **k)
|
|
|
|
# We also need to_native to return something with
|
|
# fetchone. Patch the narwhals DataFrame's to_native.
|
|
import narwhals as nw
|
|
|
|
_orig_to_native = nw.DataFrame.to_native
|
|
|
|
_first_call = [True]
|
|
|
|
def _fake_to_native(self_df):
|
|
native = _orig_to_native(self_df)
|
|
if _first_call[0] and "max_dt" in self_df.columns:
|
|
_first_call[0] = False
|
|
return _FakeCursor(D(2024, 6, 15))
|
|
return native
|
|
|
|
with patch.object(nw.DataFrame, "to_native", _fake_to_native):
|
|
result = int_current_steps(pcc, pc, mc, mm, cal, prov)
|
|
assert isinstance(result, pl.DataFrame)
|
|
|
|
|
|
class TestYearlyStepsFallback:
|
|
"""Exercise try/except fallback paths in int_yearly_steps
|
|
(lines 586-590)."""
|
|
|
|
def test_fetchall_fallback(self):
|
|
"""When ['performance_year'] fails, falls back to
|
|
fetchall."""
|
|
py_ = pl.DataFrame(
|
|
{
|
|
"person_id": ["P1"],
|
|
"performance_year": pl.Series([2024], dtype=pl.Int32),
|
|
}
|
|
)
|
|
pcc = _build_primary_care_claims(
|
|
persons=["P1"],
|
|
providers=["NPI_A"],
|
|
buckets=["pcp"],
|
|
claim_dates=[D(2024, 6, 15)],
|
|
ym_ints=[202406],
|
|
)
|
|
pc = pl.DataFrame(
|
|
{
|
|
"provider_id": ["NPI_A"],
|
|
"prov_specialty": ["Spec"],
|
|
"provider_bucket": ["pcp"],
|
|
}
|
|
)
|
|
cal_rows = []
|
|
for yr in (2023, 2024, 2025):
|
|
end_m = 12 if yr <= 2024 else 6
|
|
for m in range(1, end_m + 1):
|
|
for d in (1, 15):
|
|
dt = D(yr, m, d)
|
|
ym = int(f"{yr}{m:02d}")
|
|
cal_rows.append(
|
|
{
|
|
"full_date": dt,
|
|
"year": yr,
|
|
"month": m,
|
|
"year_month": f"{yr}{m:02d}",
|
|
"first_day_of_month": D(yr, m, 1),
|
|
"last_day_of_month": D(yr, m, 28),
|
|
"year_month_int": ym,
|
|
}
|
|
)
|
|
cal = pl.DataFrame(cal_rows)
|
|
mm = pl.DataFrame(
|
|
[{"person_id": "P1", "year_month": f"2024{m:02d}"} for m in range(1, 13)]
|
|
)
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": pl.Series([], dtype=pl.Utf8),
|
|
"claim_line_number": pl.Series([], dtype=pl.Int64),
|
|
"person_id": pl.Series([], dtype=pl.Utf8),
|
|
"claim_start_date": pl.Series([], dtype=pl.Date),
|
|
"claim_end_date": pl.Series([], dtype=pl.Date),
|
|
"allowed_amount": pl.Series([], dtype=pl.Float64),
|
|
"paid_amount": pl.Series([], dtype=pl.Float64),
|
|
"rendering_npi": pl.Series([], dtype=pl.Utf8),
|
|
"hcpcs_code": pl.Series([], dtype=pl.Utf8),
|
|
"data_source": pl.Series([], dtype=pl.Utf8),
|
|
"encounter_id": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
prov = pl.DataFrame(
|
|
{
|
|
"npi": ["NPI_A"],
|
|
"primary_taxonomy_code": ["T"],
|
|
"primary_specialty_description": ["Spec"],
|
|
"entity_type_description": ["Individual"],
|
|
}
|
|
)
|
|
|
|
import narwhals as nw
|
|
|
|
_orig_to_native = nw.DataFrame.to_native
|
|
|
|
class _FakeFetchall:
|
|
"""Mimic a DB result with fetchall() returning rows."""
|
|
|
|
def __init__(self, rows):
|
|
self._rows = rows
|
|
|
|
def fetchall(self):
|
|
return self._rows
|
|
|
|
_call_count = [0]
|
|
|
|
def _fake_to_native(self_df):
|
|
native = _orig_to_native(self_df)
|
|
_call_count[0] += 1
|
|
if list(self_df.columns) == ["performance_year"] and len(self_df) > 0:
|
|
return _FakeFetchall([(2024,)])
|
|
return native
|
|
|
|
with patch.object(nw.DataFrame, "to_native", _fake_to_native):
|
|
result = int_yearly_steps(py_, pcc, pc, mc, mm, cal, prov)
|
|
assert isinstance(result, pl.DataFrame)
|
|
assert len(result) >= 1
|
|
|
|
def test_iter_rows_fallback(self):
|
|
"""When both ['col'] and fetchall fail, falls back to
|
|
iter_rows."""
|
|
py_ = pl.DataFrame(
|
|
{
|
|
"person_id": ["P1"],
|
|
"performance_year": pl.Series([2024], dtype=pl.Int32),
|
|
}
|
|
)
|
|
pcc = _build_primary_care_claims(
|
|
persons=["P1"],
|
|
providers=["NPI_A"],
|
|
buckets=["pcp"],
|
|
claim_dates=[D(2024, 6, 15)],
|
|
ym_ints=[202406],
|
|
)
|
|
pc = pl.DataFrame(
|
|
{
|
|
"provider_id": ["NPI_A"],
|
|
"prov_specialty": ["Spec"],
|
|
"provider_bucket": ["pcp"],
|
|
}
|
|
)
|
|
cal_rows = []
|
|
for yr in (2023, 2024, 2025):
|
|
end_m = 12 if yr <= 2024 else 6
|
|
for m in range(1, end_m + 1):
|
|
for d in (1, 15):
|
|
dt = D(yr, m, d)
|
|
ym = int(f"{yr}{m:02d}")
|
|
cal_rows.append(
|
|
{
|
|
"full_date": dt,
|
|
"year": yr,
|
|
"month": m,
|
|
"year_month": f"{yr}{m:02d}",
|
|
"first_day_of_month": D(yr, m, 1),
|
|
"last_day_of_month": D(yr, m, 28),
|
|
"year_month_int": ym,
|
|
}
|
|
)
|
|
cal = pl.DataFrame(cal_rows)
|
|
mm = pl.DataFrame(
|
|
[{"person_id": "P1", "year_month": f"2024{m:02d}"} for m in range(1, 13)]
|
|
)
|
|
mc = pl.DataFrame(
|
|
{
|
|
"claim_id": pl.Series([], dtype=pl.Utf8),
|
|
"claim_line_number": pl.Series([], dtype=pl.Int64),
|
|
"person_id": pl.Series([], dtype=pl.Utf8),
|
|
"claim_start_date": pl.Series([], dtype=pl.Date),
|
|
"claim_end_date": pl.Series([], dtype=pl.Date),
|
|
"allowed_amount": pl.Series([], dtype=pl.Float64),
|
|
"paid_amount": pl.Series([], dtype=pl.Float64),
|
|
"rendering_npi": pl.Series([], dtype=pl.Utf8),
|
|
"hcpcs_code": pl.Series([], dtype=pl.Utf8),
|
|
"data_source": pl.Series([], dtype=pl.Utf8),
|
|
"encounter_id": pl.Series([], dtype=pl.Utf8),
|
|
}
|
|
)
|
|
prov = pl.DataFrame(
|
|
{
|
|
"npi": ["NPI_A"],
|
|
"primary_taxonomy_code": ["T"],
|
|
"primary_specialty_description": ["Spec"],
|
|
"entity_type_description": ["Individual"],
|
|
}
|
|
)
|
|
|
|
import narwhals as nw
|
|
|
|
_orig_to_native = nw.DataFrame.to_native
|
|
|
|
class _FakeIterRows:
|
|
"""Mimic a result where ['col'] and fetchall both
|
|
fail, but iter_rows works."""
|
|
|
|
def __init__(self, native):
|
|
self._native = native
|
|
|
|
def __getitem__(self, key):
|
|
raise KeyError(key)
|
|
|
|
def fetchall(self):
|
|
raise AttributeError("no fetchall")
|
|
|
|
def iter_rows(self):
|
|
return self._native.iter_rows()
|
|
|
|
def _fake_to_native(self_df):
|
|
native = _orig_to_native(self_df)
|
|
if list(self_df.columns) == ["performance_year"] and len(self_df) > 0:
|
|
return _FakeIterRows(native)
|
|
return native
|
|
|
|
with patch.object(nw.DataFrame, "to_native", _fake_to_native):
|
|
result = int_yearly_steps(py_, pcc, pc, mc, mm, cal, prov)
|
|
assert isinstance(result, pl.DataFrame)
|
|
assert len(result) >= 1
|