Some checks failed
CI / lint (push) Successful in 31s
CI / test (push) Failing after 2m16s
Infra CI / notebooks (push) Successful in 53s
Infra CI / docs (push) Successful in 1m30s
Infra CI / api (push) Successful in 1m0s
Infra CI / mc (push) Failing after 32s
Infra CI / llm (push) Successful in 47s
CI / notebooks-smoke (push) Successful in 1m40s
Infra CI / zotero (push) Successful in 16s
CI / lint (pull_request) Successful in 47s
Infra CI / zotero (pull_request) Successful in 14s
Infra CI / docs (pull_request) Successful in 19s
Infra CI / notebooks (pull_request) Successful in 56s
Infra CI / api (pull_request) Successful in 16s
CI / notebooks-smoke (pull_request) Successful in 1m33s
CI / test (pull_request) Failing after 2m35s
Infra CI / mc (pull_request) Failing after 32s
Infra CI / llm (pull_request) Successful in 14s
The connector (bcda.express.cclf / bcda.pipe.cclf) still routed on the v2 near-line RIC code, read v2-only amount columns, and had never run end to end: its contracts named CCLF columns the EOB table does not have, so the runner's exact-match check would have refused every stage. One header stage now carries the v3 semantics. _stg_claim_header drops NotFinalAction claims (9% of the sandbox), derives claim_family from EOB-TYPE → CLM-TYPE-CD → pde_id → RIC (v3 emits the RIC on only some claims), collapses a claim seen in two sources on its claim control number (NationalClaimsHistory/DDPS over SharedSystem, then newest last_updated; rows without a control number are never collapsed) and resolves the MBI. The four family stages are filters on it. One mapping produces input_layer.medical_claim for institutional, carrier and DME lines: admission/discharge from the C4BB admission period, NUBC type of bill (new clm_bill_type_cd on the EOB table; rebuilt as "0" + the three-character v3 frequency code when the coding is missing — the v2 fac+clsfctn+freq concatenation yields "88787J" on v3), MS-DRG, revenue centre, HCPCS + modifiers, attending→operating→rendering / rendering / rendering→ordering NPIs, billing Organization NPI and tax number, paid/allowed/charge/coinsurance/deductible from the consolidated v3 Adjudication amounts, paid date from clm_pd_dt → NCH weekly → CMS process. Part D uses the PDE id, prescribing care-team NPI, contained pharmacy NPI, NDC quantity, total drug cost as charge and paid-by-patient as copayment. Eligibility reads the Part A row (Part B fallback) for status, entitlement and enrollment, the Dual row for dual status, and accepts both CCW sex codes and the FHIR words the v2 flattener passed through. Contracts inherit: header stages subclass ExplanationOfBenefit, outputs subclass aco.table.input_layer.InputLayer*, so downstream pipelines run on BCDA output exactly as on CCLF output. load_bcda(run_pipeline=True), stack load bcda --run-pipeline and stack run bcda land input_layer.*. Large-entity sandbox pull: 221,021 medical lines on 28,282 claims (equal to the FinalAction non-PDE EOBs by type), 11,968 pharmacy rows, 9,582 eligibility rows; all 16 reconciliation checks in the notebook pass. Tests run the whole connector on CMS's official v3 samples through DuckDB. refs #1139
1674 lines
50 KiB
Python
1674 lines
50 KiB
Python
"""Tests for bcda.express — FHIR R4 flattening and CCLF transforms.
|
|
|
|
Tests cover two modules:
|
|
|
|
1. ``bcda.express.flatten`` — private helpers and public flatteners
|
|
(flatten_patient, flatten_eob, flatten_coverage).
|
|
|
|
2. ``bcda.express.cclf`` — narwhals @nw.narwhalify functions that
|
|
transform flattened BCDA resources into input_layer tables
|
|
(medical_claim, pharmacy_claim, eligibility).
|
|
|
|
Fixtures use polars DataFrames with minimal columns matching
|
|
function signatures.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import date
|
|
|
|
import polars as pl
|
|
import pytest
|
|
|
|
from bcda.express.cclf import stg_beneficiary_xref
|
|
from bcda.express.flatten import (
|
|
_coding_code,
|
|
_get_ext,
|
|
_get_ext_money,
|
|
_get_ext_quantity,
|
|
_get_identifier,
|
|
_get_supporting_info,
|
|
_parse_date,
|
|
_safe_get,
|
|
flatten_coverage,
|
|
flatten_eob,
|
|
flatten_patient,
|
|
)
|
|
|
|
# ════════════════════════════════════════════════════════════════
|
|
# Part 1: bcda.express.flatten — helpers
|
|
# ════════════════════════════════════════════════════════════════
|
|
|
|
|
|
class TestSafeGet:
|
|
"""_safe_get: nested dict traversal."""
|
|
|
|
def test_single_key(self) -> None:
|
|
assert _safe_get({"a": 1}, "a") == 1
|
|
|
|
def test_nested_keys(self) -> None:
|
|
d = {"a": {"b": {"c": 42}}}
|
|
assert _safe_get(d, "a", "b", "c") == 42
|
|
|
|
def test_missing_key_returns_none(self) -> None:
|
|
assert _safe_get({"a": 1}, "b") is None
|
|
|
|
def test_none_input_returns_none(self) -> None:
|
|
assert _safe_get(None, "a") is None
|
|
|
|
def test_non_dict_intermediate(self) -> None:
|
|
assert _safe_get({"a": "string"}, "a", "b") is None
|
|
|
|
|
|
class TestGetExt:
|
|
"""_get_ext: extract value from BB extensions."""
|
|
|
|
def test_value_coding(self) -> None:
|
|
exts = [
|
|
{
|
|
"url": "https://bb.cms.gov/vars/race",
|
|
"valueCoding": {"code": "1"},
|
|
}
|
|
]
|
|
assert _get_ext(exts, "race") == "1"
|
|
|
|
def test_value_code(self) -> None:
|
|
exts = [
|
|
{
|
|
"url": "https://bb.cms.gov/vars/sex",
|
|
"valueCode": "M",
|
|
}
|
|
]
|
|
assert _get_ext(exts, "sex") == "M"
|
|
|
|
def test_value_date(self) -> None:
|
|
exts = [
|
|
{
|
|
"url": "https://bb.cms.gov/vars/rfrnc_yr",
|
|
"valueDate": "2023",
|
|
}
|
|
]
|
|
assert _get_ext(exts, "rfrnc_yr") == "2023"
|
|
|
|
def test_value_identifier(self) -> None:
|
|
exts = [
|
|
{
|
|
"url": "https://bb.cms.gov/vars/id",
|
|
"valueIdentifier": {"value": "ABC"},
|
|
}
|
|
]
|
|
assert _get_ext(exts, "id") == "ABC"
|
|
|
|
def test_value_money(self) -> None:
|
|
exts = [
|
|
{
|
|
"url": "https://bb/vars/amt",
|
|
"valueMoney": {"value": 99.50},
|
|
}
|
|
]
|
|
assert _get_ext(exts, "amt") == "99.5"
|
|
|
|
def test_value_quantity(self) -> None:
|
|
exts = [
|
|
{
|
|
"url": "https://bb/vars/qty",
|
|
"valueQuantity": {"value": 3},
|
|
}
|
|
]
|
|
assert _get_ext(exts, "qty") == "3"
|
|
|
|
def test_none_extensions(self) -> None:
|
|
assert _get_ext(None, "x") is None
|
|
|
|
def test_empty_extensions(self) -> None:
|
|
assert _get_ext([], "x") is None
|
|
|
|
def test_no_match(self) -> None:
|
|
exts = [
|
|
{
|
|
"url": "https://bb/vars/other",
|
|
"valueCode": "Y",
|
|
}
|
|
]
|
|
assert _get_ext(exts, "nope") is None
|
|
|
|
def test_exact_url_match(self) -> None:
|
|
exts = [
|
|
{
|
|
"url": "dual_01",
|
|
"valueCoding": {"code": "00"},
|
|
}
|
|
]
|
|
assert _get_ext(exts, "dual_01") == "00"
|
|
|
|
|
|
class TestGetExtMoney:
|
|
"""_get_ext_money: extract valueMoney.value."""
|
|
|
|
def test_returns_float(self) -> None:
|
|
exts = [
|
|
{
|
|
"url": "https://bb/vars/amt",
|
|
"valueMoney": {"value": 123.45},
|
|
}
|
|
]
|
|
assert _get_ext_money(exts, "amt") == 123.45
|
|
|
|
def test_none_when_missing(self) -> None:
|
|
assert _get_ext_money(None, "x") is None
|
|
|
|
def test_no_match(self) -> None:
|
|
exts = [
|
|
{
|
|
"url": "https://bb/vars/other",
|
|
"valueMoney": {"value": 1.0},
|
|
}
|
|
]
|
|
assert _get_ext_money(exts, "nope") is None
|
|
|
|
|
|
class TestGetExtQuantity:
|
|
"""_get_ext_quantity: extract valueQuantity.value."""
|
|
|
|
def test_returns_float(self) -> None:
|
|
exts = [
|
|
{
|
|
"url": "https://bb/vars/cnt",
|
|
"valueQuantity": {"value": 7.0},
|
|
}
|
|
]
|
|
assert _get_ext_quantity(exts, "cnt") == 7.0
|
|
|
|
def test_none_when_missing(self) -> None:
|
|
assert _get_ext_quantity(None, "x") is None
|
|
|
|
|
|
class TestGetIdentifier:
|
|
"""_get_identifier: find identifier by system suffix."""
|
|
|
|
def test_finds_by_suffix(self) -> None:
|
|
ids = [
|
|
{
|
|
"system": "http://hl7.org/fhir/sid/bene_id",
|
|
"value": "B123",
|
|
}
|
|
]
|
|
assert _get_identifier(ids, "bene_id") == "B123"
|
|
|
|
def test_none_when_empty(self) -> None:
|
|
assert _get_identifier(None, "x") is None
|
|
|
|
def test_no_match(self) -> None:
|
|
ids = [
|
|
{
|
|
"system": "http://example.com/other",
|
|
"value": "V1",
|
|
}
|
|
]
|
|
assert _get_identifier(ids, "bene_id") is None
|
|
|
|
|
|
class TestGetSupportingInfo:
|
|
"""_get_supporting_info: find supportingInfo by category."""
|
|
|
|
def test_finds_by_code(self) -> None:
|
|
info = [
|
|
{
|
|
"category": {"coding": [{"code": "admtype"}]},
|
|
"code": {"coding": [{"code": "3"}]},
|
|
}
|
|
]
|
|
result = _get_supporting_info(info, "admtype")
|
|
assert result is not None
|
|
assert result["code"]["coding"][0]["code"] == "3"
|
|
|
|
def test_none_when_empty(self) -> None:
|
|
assert _get_supporting_info(None, "x") is None
|
|
|
|
def test_no_match(self) -> None:
|
|
info = [{"category": {"coding": [{"code": "other"}]}}]
|
|
assert _get_supporting_info(info, "admtype") is None
|
|
|
|
|
|
class TestParseDate:
|
|
"""_parse_date: YYYY-MM-DD and datetime parsing."""
|
|
|
|
def test_date_string(self) -> None:
|
|
assert _parse_date("2023-06-15") == date(2023, 6, 15)
|
|
|
|
def test_datetime_string(self) -> None:
|
|
assert _parse_date("2023-06-15T10:30:00Z") == date(2023, 6, 15)
|
|
|
|
def test_none_returns_none(self) -> None:
|
|
assert _parse_date(None) is None
|
|
|
|
def test_empty_returns_none(self) -> None:
|
|
assert _parse_date("") is None
|
|
|
|
def test_invalid_returns_none(self) -> None:
|
|
assert _parse_date("not-a-date") is None
|
|
|
|
|
|
class TestCodingCode:
|
|
"""_coding_code: CodeableConcept code extraction."""
|
|
|
|
def test_no_system_filter(self) -> None:
|
|
cc = {"coding": [{"code": "A01"}]}
|
|
assert _coding_code(cc) == "A01"
|
|
|
|
def test_with_system_filter(self) -> None:
|
|
cc = {
|
|
"coding": [
|
|
{
|
|
"system": "http://hl7.org/fhir/nch",
|
|
"code": "10",
|
|
},
|
|
{
|
|
"system": "http://hl7.org/fhir/eob",
|
|
"code": "20",
|
|
},
|
|
]
|
|
}
|
|
assert _coding_code(cc, "nch") == "10"
|
|
assert _coding_code(cc, "eob") == "20"
|
|
|
|
def test_none_input(self) -> None:
|
|
assert _coding_code(None) is None
|
|
|
|
def test_empty_coding(self) -> None:
|
|
assert _coding_code({"coding": []}) is None
|
|
|
|
|
|
# ════════════════════════════════════════════════════════════════
|
|
# Part 1b: bcda.express.flatten — public flatteners
|
|
# ════════════════════════════════════════════════════════════════
|
|
|
|
|
|
class TestFlattenPatient:
|
|
"""flatten_patient: FHIR Patient -> Patient model."""
|
|
|
|
@pytest.fixture
|
|
def minimal_patient(self) -> dict:
|
|
return {
|
|
"id": "PAT-001",
|
|
"meta": {"lastUpdated": "2024-01-15T12:00:00Z"},
|
|
"identifier": [
|
|
{
|
|
"system": "http://hl7.org/fhir/sid/us-mbi",
|
|
"value": "1AN0TE5MK72",
|
|
"type": {
|
|
"coding": [
|
|
{"extension": [{"valueCoding": {"code": "current"}}]}
|
|
]
|
|
},
|
|
},
|
|
{
|
|
"system": "http://ccw/bene_id",
|
|
"value": "BENE-99",
|
|
},
|
|
],
|
|
"name": [
|
|
{
|
|
"given": ["ALICE", "M"],
|
|
"family": "JOHNSON",
|
|
}
|
|
],
|
|
"birthDate": "1955-03-22",
|
|
"gender": "female",
|
|
"address": [
|
|
{
|
|
"line": ["100 MAIN ST"],
|
|
"city": "ANYTOWN",
|
|
"state": "MD",
|
|
"postalCode": "20601",
|
|
}
|
|
],
|
|
"extension": [
|
|
{
|
|
"url": "https://bb/vars/race",
|
|
"valueCoding": {"code": "1"},
|
|
},
|
|
{
|
|
"url": "http://hl7.org/us-core-race",
|
|
"extension": [
|
|
{
|
|
"url": "ombCategory",
|
|
"valueCoding": {"code": "2106-3"},
|
|
}
|
|
],
|
|
},
|
|
],
|
|
}
|
|
|
|
def test_returns_patient_model(self, minimal_patient: dict) -> None:
|
|
from bcda.table.patient import Patient
|
|
|
|
p = flatten_patient(minimal_patient)
|
|
assert isinstance(p, Patient)
|
|
|
|
def test_mbi_extraction(self, minimal_patient: dict) -> None:
|
|
p = flatten_patient(minimal_patient)
|
|
assert p.bene_mbi_id == "1AN0TE5MK72"
|
|
assert p.identifier_currency == "current"
|
|
|
|
def test_bene_id(self, minimal_patient: dict) -> None:
|
|
p = flatten_patient(minimal_patient)
|
|
assert p.bene_id == "BENE-99"
|
|
|
|
def test_demographics(self, minimal_patient: dict) -> None:
|
|
p = flatten_patient(minimal_patient)
|
|
assert p.bene_1st_name == "ALICE"
|
|
assert p.bene_midl_name == "M"
|
|
assert p.bene_last_name == "JOHNSON"
|
|
assert p.bene_dob == date(1955, 3, 22)
|
|
assert p.bene_sex_cd == "female"
|
|
|
|
def test_address(self, minimal_patient: dict) -> None:
|
|
p = flatten_patient(minimal_patient)
|
|
assert p.bene_line_1_adr == "100 MAIN ST"
|
|
assert p.geo_zip_plc_name == "ANYTOWN"
|
|
assert p.geo_usps_state_cd == "MD"
|
|
assert p.geo_zip5_cd == "20601"
|
|
|
|
def test_race_codes(self, minimal_patient: dict) -> None:
|
|
p = flatten_patient(minimal_patient)
|
|
assert p.bene_race_cd == "1"
|
|
assert p.us_core_race == "2106-3"
|
|
|
|
def test_historic_mbi(self) -> None:
|
|
"""Historic MBI populates prvs_num."""
|
|
resource = {
|
|
"identifier": [
|
|
{
|
|
"system": "http://hl7.org/us-mbi",
|
|
"value": "HIST-MBI",
|
|
"type": {
|
|
"coding": [
|
|
{"extension": [{"valueCoding": {"code": "historic"}}]}
|
|
]
|
|
},
|
|
"period": {
|
|
"start": "2020-01-01",
|
|
"end": "2022-12-31",
|
|
},
|
|
}
|
|
],
|
|
}
|
|
p = flatten_patient(resource)
|
|
assert p.prvs_num == "HIST-MBI"
|
|
assert p.efctv_bgn_dt == date(2020, 1, 1)
|
|
assert p.efctv_end_dt == date(2022, 12, 31)
|
|
|
|
|
|
class TestFlattenEob:
|
|
"""flatten_eob: FHIR EOB -> (header, items)."""
|
|
|
|
@pytest.fixture
|
|
def minimal_eob(self) -> dict:
|
|
return {
|
|
"id": "EOB-001",
|
|
"meta": {"lastUpdated": "2024-01-20T09:00:00Z"},
|
|
"patient": {"reference": "Patient/BENE-99"},
|
|
"identifier": [
|
|
{
|
|
"system": "http://ccw/clm_id",
|
|
"value": "CLM-100",
|
|
},
|
|
],
|
|
"type": {
|
|
"coding": [
|
|
{
|
|
"system": "http://ccw/nch_clm_type_cd",
|
|
"code": "71",
|
|
}
|
|
]
|
|
},
|
|
"billablePeriod": {
|
|
"start": "2023-06-01",
|
|
"end": "2023-06-10",
|
|
},
|
|
"extension": [
|
|
{
|
|
"url": "https://bb/vars/nch_near_line_rec_ident_cd",
|
|
"valueCoding": {"code": "V"},
|
|
}
|
|
],
|
|
"careTeam": [
|
|
{
|
|
"role": {"coding": [{"code": "attending"}]},
|
|
"provider": {
|
|
"identifier": {
|
|
"value": "1234567890",
|
|
"type": {"coding": [{"code": "npi"}]},
|
|
}
|
|
},
|
|
}
|
|
],
|
|
"diagnosis": [
|
|
{
|
|
"sequence": 1,
|
|
"diagnosisCodeableConcept": {
|
|
"coding": [
|
|
{
|
|
"system": "http://hl7.org/fhir/icd-10",
|
|
"code": "J44.1",
|
|
}
|
|
]
|
|
},
|
|
"type": [{"coding": [{"code": "principal"}]}],
|
|
}
|
|
],
|
|
"item": [
|
|
{
|
|
"sequence": 1,
|
|
"servicedPeriod": {
|
|
"start": "2023-06-01",
|
|
"end": "2023-06-05",
|
|
},
|
|
"productOrService": {"coding": [{"code": "99213"}]},
|
|
"quantity": {"value": 1.0},
|
|
"adjudication": [
|
|
{
|
|
"category": {
|
|
"coding": [
|
|
{
|
|
"system": "https://bluebutton.cms.gov/resources/codesystem/adjudication",
|
|
"code": "line_nch_pmt_amt",
|
|
}
|
|
]
|
|
},
|
|
"amount": {"value": 75.50},
|
|
}
|
|
],
|
|
}
|
|
],
|
|
}
|
|
|
|
def test_returns_tuple(self, minimal_eob: dict) -> None:
|
|
header, items = flatten_eob(minimal_eob)
|
|
from bcda.table.eob import (
|
|
EobItem,
|
|
ExplanationOfBenefit,
|
|
)
|
|
|
|
assert isinstance(header, ExplanationOfBenefit)
|
|
assert len(items) == 1
|
|
assert isinstance(items[0], EobItem)
|
|
|
|
def test_header_identifiers(self, minimal_eob: dict) -> None:
|
|
header, _ = flatten_eob(minimal_eob)
|
|
assert header.clm_id == "CLM-100"
|
|
assert header.bene_id == "BENE-99"
|
|
|
|
def test_header_dates(self, minimal_eob: dict) -> None:
|
|
header, _ = flatten_eob(minimal_eob)
|
|
assert header.clm_from_dt == date(2023, 6, 1)
|
|
assert header.clm_thru_dt == date(2023, 6, 10)
|
|
|
|
def test_header_near_line(self, minimal_eob: dict) -> None:
|
|
header, _ = flatten_eob(minimal_eob)
|
|
assert header.nch_near_line_rec_ident_cd == "V"
|
|
|
|
def test_header_diagnosis(self, minimal_eob: dict) -> None:
|
|
header, _ = flatten_eob(minimal_eob)
|
|
assert header.prncpl_dgns_cd == "J44.1"
|
|
assert header.clm_dgns_1_cd == "J44.1"
|
|
assert header.dgns_prcdr_icd_ind == "0"
|
|
|
|
def test_header_care_team(self, minimal_eob: dict) -> None:
|
|
header, _ = flatten_eob(minimal_eob)
|
|
assert header.atndg_prvdr_npi_num == "1234567890"
|
|
|
|
def test_item_amounts(self, minimal_eob: dict) -> None:
|
|
_, items = flatten_eob(minimal_eob)
|
|
assert items[0].clm_line_cvrd_pd_amt == 75.50
|
|
|
|
def test_item_clm_id_ref(self, minimal_eob: dict) -> None:
|
|
_, items = flatten_eob(minimal_eob)
|
|
assert items[0].clm_id == "CLM-100"
|
|
|
|
def test_item_service_dates(self, minimal_eob: dict) -> None:
|
|
_, items = flatten_eob(minimal_eob)
|
|
assert items[0].clm_line_from_dt == date(2023, 6, 1)
|
|
assert items[0].clm_line_thru_dt == date(2023, 6, 5)
|
|
|
|
def test_item_hcpcs(self, minimal_eob: dict) -> None:
|
|
_, items = flatten_eob(minimal_eob)
|
|
assert items[0].clm_line_hcpcs_cd == "99213"
|
|
|
|
def test_item_quantity(self, minimal_eob: dict) -> None:
|
|
_, items = flatten_eob(minimal_eob)
|
|
assert items[0].clm_line_srvc_unit_qty == 1.0
|
|
|
|
def test_no_items_returns_empty_list(
|
|
self,
|
|
) -> None:
|
|
resource = {
|
|
"identifier": [],
|
|
"item": [],
|
|
}
|
|
_, items = flatten_eob(resource)
|
|
assert items == []
|
|
|
|
|
|
class TestFlattenCoverage:
|
|
"""flatten_coverage: FHIR Coverage -> Coverage model."""
|
|
|
|
@pytest.fixture
|
|
def minimal_coverage(self) -> dict:
|
|
return {
|
|
"id": "COV-001",
|
|
"meta": {"lastUpdated": "2024-02-01T08:00:00Z"},
|
|
"status": "active",
|
|
"subscriberId": "1AN0TE5MK72",
|
|
"beneficiary": {"reference": "Patient/BENE-99"},
|
|
"type": {"coding": [{"code": "SUBSIDIZ"}]},
|
|
"class": [
|
|
{
|
|
"type": {"coding": [{"code": "group"}]},
|
|
"value": "Medicare",
|
|
},
|
|
{
|
|
"type": {"coding": [{"code": "plan"}]},
|
|
"value": "Part A",
|
|
},
|
|
],
|
|
"period": {
|
|
"start": "2020-01-01",
|
|
"end": "2025-12-31",
|
|
},
|
|
"extension": [
|
|
{
|
|
"url": "https://bb/vars/ms_cd",
|
|
"valueCoding": {"code": "10"},
|
|
},
|
|
{
|
|
"url": "https://bb/vars/orec",
|
|
"valueCoding": {"code": "0"},
|
|
},
|
|
],
|
|
}
|
|
|
|
def test_returns_coverage_model(self, minimal_coverage: dict) -> None:
|
|
from bcda.table.coverage import Coverage
|
|
|
|
c = flatten_coverage(minimal_coverage)
|
|
assert isinstance(c, Coverage)
|
|
|
|
def test_subscriber_id(self, minimal_coverage: dict) -> None:
|
|
c = flatten_coverage(minimal_coverage)
|
|
assert c.subscriber_id == "1AN0TE5MK72"
|
|
|
|
def test_part_a_dates(self, minimal_coverage: dict) -> None:
|
|
c = flatten_coverage(minimal_coverage)
|
|
assert c.bene_part_a_enrlmt_bgn_dt == date(2020, 1, 1)
|
|
assert c.mdcr_covg_a_end_dt == date(2025, 12, 31)
|
|
|
|
def test_extension_values(self, minimal_coverage: dict) -> None:
|
|
c = flatten_coverage(minimal_coverage)
|
|
assert c.ms_cd == "10"
|
|
assert c.orec == "0"
|
|
|
|
def test_class_group_plan(self, minimal_coverage: dict) -> None:
|
|
c = flatten_coverage(minimal_coverage)
|
|
assert c.class_group == "Medicare"
|
|
assert c.class_plan == "Part A"
|
|
|
|
def test_part_b_dates(self) -> None:
|
|
resource = {
|
|
"class": [
|
|
{
|
|
"type": {"coding": [{"code": "plan"}]},
|
|
"value": "Part B",
|
|
}
|
|
],
|
|
"period": {
|
|
"start": "2021-07-01",
|
|
"end": "2025-06-30",
|
|
},
|
|
"extension": [],
|
|
}
|
|
c = flatten_coverage(resource)
|
|
assert c.bene_part_b_enrlmt_bgn_dt == date(2021, 7, 1)
|
|
assert c.mdcr_covg_b_end_dt == date(2025, 6, 30)
|
|
assert c.bene_part_a_enrlmt_bgn_dt is None
|
|
|
|
def test_part_d_dates(self) -> None:
|
|
resource = {
|
|
"class": [
|
|
{
|
|
"type": {"coding": [{"code": "plan"}]},
|
|
"value": "Part D",
|
|
}
|
|
],
|
|
"period": {
|
|
"start": "2022-01-01",
|
|
"end": "2024-12-31",
|
|
},
|
|
"extension": [],
|
|
}
|
|
c = flatten_coverage(resource)
|
|
assert c.mdcr_covg_d_bgn_dt == date(2022, 1, 1)
|
|
assert c.mdcr_covg_d_end_dt == date(2024, 12, 31)
|
|
|
|
|
|
# ════════════════════════════════════════════════════════════════
|
|
# Part 2: bcda.express.cclf — narwhals transforms
|
|
# ════════════════════════════════════════════════════════════════
|
|
|
|
# ── shared fixtures for CCLF transforms ─────────────────────
|
|
|
|
|
|
@pytest.fixture
|
|
def patient_df() -> pl.DataFrame:
|
|
"""Minimal bcda.patient with two beneficiaries."""
|
|
return pl.DataFrame(
|
|
{
|
|
"bene_id": ["B001", "B002"],
|
|
"bene_mbi_id": ["MBI-A", "MBI-B"],
|
|
}
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def xref_df(patient_df: pl.DataFrame) -> pl.DataFrame:
|
|
"""Staged beneficiary xref from patient."""
|
|
return stg_beneficiary_xref(bcda__patient=patient_df)
|
|
|
|
|
|
@pytest.fixture
|
|
def eob_df() -> pl.DataFrame:
|
|
"""Minimal bcda.explanation_of_benefit with four
|
|
claim types (V, O, M, P)."""
|
|
return pl.DataFrame(
|
|
{
|
|
"bene_id": [
|
|
"B001",
|
|
"B001",
|
|
"B002",
|
|
"B002",
|
|
],
|
|
"clm_id": [
|
|
"CLM-1",
|
|
"CLM-2",
|
|
"CLM-3",
|
|
"CLM-4",
|
|
],
|
|
"pde_id": [None, None, None, "PDE-1"],
|
|
"nch_near_line_rec_ident_cd": [
|
|
"V",
|
|
"O",
|
|
"M",
|
|
"P",
|
|
],
|
|
"clm_from_dt": [
|
|
date(2023, 1, 1),
|
|
date(2023, 2, 1),
|
|
date(2023, 3, 1),
|
|
date(2023, 4, 1),
|
|
],
|
|
"clm_thru_dt": [
|
|
date(2023, 1, 10),
|
|
date(2023, 2, 10),
|
|
date(2023, 3, 10),
|
|
date(2023, 4, 10),
|
|
],
|
|
"dgns_prcdr_icd_ind": [
|
|
"0",
|
|
"0",
|
|
None,
|
|
None,
|
|
],
|
|
"clm_dgns_1_cd": [
|
|
"J44.1",
|
|
"M54.5",
|
|
None,
|
|
None,
|
|
],
|
|
"clm_dgns_2_cd": [None, None, None, None],
|
|
"clm_dgns_3_cd": [None, None, None, None],
|
|
"clm_dgns_4_cd": [None, None, None, None],
|
|
"clm_dgns_5_cd": [None, None, None, None],
|
|
"clm_dgns_6_cd": [None, None, None, None],
|
|
"clm_dgns_7_cd": [None, None, None, None],
|
|
"clm_dgns_8_cd": [None, None, None, None],
|
|
"clm_dgns_9_cd": [None, None, None, None],
|
|
"clm_dgns_10_cd": [
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"clm_dgns_11_cd": [
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"clm_dgns_12_cd": [
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"clm_poa_ind": ["Y", None, None, None],
|
|
"clm_prcdr_cd": [
|
|
"0BJ08ZZ",
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"clm_prcdr_prfrm_dt": [
|
|
date(2023, 1, 2),
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"dgns_drg_cd": [
|
|
"MS-DRG470",
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"clm_admsn_src_cd": [
|
|
"1",
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"clm_admsn_type_cd": [
|
|
"3",
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"bene_ptnt_stus_cd": [
|
|
"01",
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"clm_bill_fac_type_cd": [
|
|
"1",
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"clm_bill_clsfctn_cd": [
|
|
"1",
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"clm_bill_freq_cd": [
|
|
"1",
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"clm_mdcr_instnl_tot_chrg_amt": [
|
|
5000.0,
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"oprtg_prvdr_npi_num": [
|
|
"NPI-OP1",
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"fac_prvdr_npi_num": [
|
|
"NPI-FAC1",
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"rndrg_prvdr_npi_num": [
|
|
None,
|
|
"NPI-RN2",
|
|
None,
|
|
None,
|
|
],
|
|
"clm_rndrg_prvdr_tax_num": [
|
|
None,
|
|
"TAX-2",
|
|
None,
|
|
None,
|
|
],
|
|
"carr_clm_blg_npi_num": [
|
|
None,
|
|
"NPI-BLG2",
|
|
None,
|
|
None,
|
|
],
|
|
"clm_pos_cd": [None, "11", "12", None],
|
|
"ordrg_prvdr_npi_num": [
|
|
None,
|
|
None,
|
|
"NPI-ORD3",
|
|
None,
|
|
],
|
|
"payto_prvdr_npi_num": [
|
|
None,
|
|
None,
|
|
"NPI-PAY3",
|
|
None,
|
|
],
|
|
"clm_prsbng_prvdr_gnrc_id_num": [
|
|
None,
|
|
None,
|
|
None,
|
|
"NPI-PRE4",
|
|
],
|
|
"clm_srvc_prvdr_gnrc_id_num": [
|
|
None,
|
|
None,
|
|
None,
|
|
"NPI-DSP4",
|
|
],
|
|
}
|
|
)
|
|
|
|
|
|
@pytest.fixture
|
|
def eob_item_df() -> pl.DataFrame:
|
|
"""Minimal bcda.eob_item with matching clm_ids."""
|
|
return pl.DataFrame(
|
|
{
|
|
"clm_id": [
|
|
"CLM-1",
|
|
"CLM-2",
|
|
"CLM-3",
|
|
"CLM-4",
|
|
],
|
|
"clm_line_num": [1.0, 1.0, 1.0, 1.0],
|
|
"clm_line_from_dt": [
|
|
date(2023, 1, 1),
|
|
date(2023, 2, 1),
|
|
date(2023, 3, 1),
|
|
date(2023, 4, 1),
|
|
],
|
|
"clm_line_thru_dt": [
|
|
date(2023, 1, 5),
|
|
date(2023, 2, 5),
|
|
date(2023, 3, 5),
|
|
date(2023, 4, 5),
|
|
],
|
|
"clm_line_prod_rev_ctr_cd": [
|
|
"0301",
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"clm_line_hcpcs_cd": [
|
|
"99213",
|
|
"99214",
|
|
"E1390",
|
|
None,
|
|
],
|
|
"clm_line_ndc_cd": [
|
|
None,
|
|
None,
|
|
None,
|
|
"12345678901",
|
|
],
|
|
"hcpcs_1_mdfr_cd": [
|
|
"26",
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"hcpcs_2_mdfr_cd": [
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"hcpcs_3_mdfr_cd": [
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"hcpcs_4_mdfr_cd": [
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"hcpcs_5_mdfr_cd": [
|
|
None,
|
|
None,
|
|
None,
|
|
None,
|
|
],
|
|
"clm_line_srvc_unit_qty": [
|
|
1.0,
|
|
2.0,
|
|
None,
|
|
30.0,
|
|
],
|
|
"clm_line_cvrd_pd_amt": [
|
|
75.50,
|
|
120.00,
|
|
50.00,
|
|
10.00,
|
|
],
|
|
"clm_line_alowd_chrg_amt": [
|
|
100.00,
|
|
150.00,
|
|
60.00,
|
|
None,
|
|
],
|
|
"clm_line_bene_pmt_amt": [
|
|
None,
|
|
None,
|
|
None,
|
|
5.00,
|
|
],
|
|
"clm_line_days_suply_qty": [
|
|
None,
|
|
None,
|
|
None,
|
|
30.0,
|
|
],
|
|
"clm_line_rx_fill_num": [
|
|
None,
|
|
None,
|
|
None,
|
|
0.0,
|
|
],
|
|
}
|
|
)
|
|
|
|
|
|
# ── stg_beneficiary_xref ────────────────────────────────────
|
|
|
|
|
|
class TestStgBeneficiaryXref:
|
|
"""BCDA Patient -> MBI crosswalk."""
|
|
|
|
def test_output_columns(self, patient_df: pl.DataFrame) -> None:
|
|
result = stg_beneficiary_xref(bcda__patient=patient_df)
|
|
assert set(result.columns) == {
|
|
"bene_id",
|
|
"current_bene_mbi_id",
|
|
}
|
|
|
|
def test_dedup_on_bene_id(self) -> None:
|
|
df = pl.DataFrame(
|
|
{
|
|
"bene_id": ["B1", "B1", "B2"],
|
|
"bene_mbi_id": [
|
|
"MBI-1",
|
|
"MBI-1",
|
|
"MBI-2",
|
|
],
|
|
}
|
|
)
|
|
result = stg_beneficiary_xref(bcda__patient=df)
|
|
assert result.shape[0] == 2
|
|
assert result["bene_id"].n_unique() == result.shape[0]
|
|
|
|
def test_alias_current_bene_mbi_id(self, patient_df: pl.DataFrame) -> None:
|
|
result = stg_beneficiary_xref(bcda__patient=patient_df)
|
|
assert "current_bene_mbi_id" in result.columns
|
|
assert set(result["current_bene_mbi_id"].to_list()) == {"MBI-A", "MBI-B"}
|
|
|
|
|
|
class TestGetExtQuantityMatch:
|
|
"""_get_ext_quantity: cover the return-value path (line 155)."""
|
|
|
|
def test_returns_value_on_match(self) -> None:
|
|
exts = [
|
|
{
|
|
"url": "https://bb/vars/rev_cntr_unit_cnt",
|
|
"valueQuantity": {"value": 42.0},
|
|
}
|
|
]
|
|
assert _get_ext_quantity(exts, "rev_cntr_unit_cnt") == 42.0
|
|
|
|
def test_no_match_returns_none(self) -> None:
|
|
exts = [
|
|
{
|
|
"url": "https://bb/vars/other",
|
|
"valueQuantity": {"value": 1.0},
|
|
}
|
|
]
|
|
assert _get_ext_quantity(exts, "rev_cntr_unit_cnt") is None
|
|
|
|
|
|
class TestExtractCareTeamRoles:
|
|
"""_extract_care_team: cover all role branches (lines 348-379)."""
|
|
|
|
def test_no_role_coding_skips(self) -> None:
|
|
from bcda.express.flatten import _extract_care_team
|
|
|
|
care_team = [{"provider": {"identifier": {"value": "NPI1"}}}]
|
|
result = _extract_care_team(care_team)
|
|
assert result["atndg_prvdr_npi_num"] is None
|
|
|
|
def test_performing_role_with_npi_and_qualification(self) -> None:
|
|
from bcda.express.flatten import _extract_care_team
|
|
|
|
care_team = [
|
|
{
|
|
"role": {"coding": [{"code": "performing"}]},
|
|
"provider": {
|
|
"identifier": {
|
|
"value": "NPI-PERF",
|
|
"type": {"coding": [{"code": "npi"}]},
|
|
}
|
|
},
|
|
"qualification": {"coding": [{"code": "01"}]},
|
|
"extension": [
|
|
{
|
|
"url": "https://bb/vars/carr_line_prvdr_type_cd",
|
|
"valueCoding": {"code": "1"},
|
|
},
|
|
{
|
|
"url": "https://bb/vars/prtcptng_ind_cd",
|
|
"valueCoding": {"code": "2"},
|
|
},
|
|
],
|
|
}
|
|
]
|
|
result = _extract_care_team(care_team)
|
|
assert result["rndrg_prvdr_npi_num"] == "NPI-PERF"
|
|
assert result["clm_prvdr_spclty_cd"] == "01"
|
|
assert result["carr_line_prvdr_type_cd"] == "1"
|
|
assert result["prtcptng_ind_cd"] == "2"
|
|
|
|
def test_prescribing_role(self) -> None:
|
|
from bcda.express.flatten import _extract_care_team
|
|
|
|
care_team = [
|
|
{
|
|
"role": {"coding": [{"code": "prescribing"}]},
|
|
"provider": {
|
|
"identifier": {
|
|
"value": "PRES-001",
|
|
"type": {"coding": [{"code": "npi"}]},
|
|
}
|
|
},
|
|
}
|
|
]
|
|
result = _extract_care_team(care_team)
|
|
assert result["clm_prsbng_prvdr_gnrc_id_num"] == "PRES-001"
|
|
|
|
def test_dispensing_role(self) -> None:
|
|
from bcda.express.flatten import _extract_care_team
|
|
|
|
care_team = [
|
|
{
|
|
"role": {"coding": [{"code": "dispensing"}]},
|
|
"provider": {
|
|
"identifier": {
|
|
"value": "DISP-001",
|
|
"type": {"coding": [{"code": "npi"}]},
|
|
}
|
|
},
|
|
}
|
|
]
|
|
result = _extract_care_team(care_team)
|
|
assert result["clm_srvc_prvdr_gnrc_id_num"] == "DISP-001"
|
|
|
|
def test_referring_role_with_npi(self) -> None:
|
|
from bcda.express.flatten import _extract_care_team
|
|
|
|
care_team = [
|
|
{
|
|
"role": {"coding": [{"code": "referring"}]},
|
|
"provider": {
|
|
"identifier": {
|
|
"value": "REF-NPI",
|
|
"type": {"coding": [{"code": "npi"}]},
|
|
}
|
|
},
|
|
}
|
|
]
|
|
result = _extract_care_team(care_team)
|
|
assert result["ordrg_prvdr_npi_num"] == "REF-NPI"
|
|
|
|
def test_operating_role(self) -> None:
|
|
from bcda.express.flatten import _extract_care_team
|
|
|
|
care_team = [
|
|
{
|
|
"role": {"coding": [{"code": "operating"}]},
|
|
"provider": {
|
|
"identifier": {
|
|
"value": "OP-NPI",
|
|
"type": {"coding": [{"code": "npi"}]},
|
|
}
|
|
},
|
|
}
|
|
]
|
|
result = _extract_care_team(care_team)
|
|
assert result["oprtg_prvdr_npi_num"] == "OP-NPI"
|
|
|
|
def test_otheroperating_role(self) -> None:
|
|
from bcda.express.flatten import _extract_care_team
|
|
|
|
care_team = [
|
|
{
|
|
"role": {"coding": [{"code": "otheroperating"}]},
|
|
"provider": {
|
|
"identifier": {
|
|
"value": "OTHER-NPI",
|
|
"type": {"coding": [{"code": "npi"}]},
|
|
}
|
|
},
|
|
}
|
|
]
|
|
result = _extract_care_team(care_team)
|
|
assert result["othr_prvdr_npi_num"] == "OTHER-NPI"
|
|
|
|
|
|
class TestExtractDiagnosesGaps:
|
|
"""_extract_diagnoses: typed diagnoses and POA (lines 405-423)."""
|
|
|
|
def test_icd9_system(self) -> None:
|
|
from bcda.express.flatten import _extract_diagnoses
|
|
|
|
dx_list = [
|
|
{
|
|
"sequence": 1,
|
|
"diagnosisCodeableConcept": {
|
|
"coding": [
|
|
{
|
|
"system": "http://hl7.org/fhir/icd-9",
|
|
"code": "250.00",
|
|
}
|
|
]
|
|
},
|
|
}
|
|
]
|
|
result = _extract_diagnoses(dx_list)
|
|
assert result["dgns_prcdr_icd_ind"] == "9"
|
|
assert result["clm_dgns_1_cd"] == "250.00"
|
|
|
|
def test_admitting_type(self) -> None:
|
|
from bcda.express.flatten import _extract_diagnoses
|
|
|
|
dx_list = [
|
|
{
|
|
"sequence": 2,
|
|
"diagnosisCodeableConcept": {
|
|
"coding": [
|
|
{
|
|
"system": "http://hl7.org/fhir/icd-10",
|
|
"code": "R07.9",
|
|
}
|
|
]
|
|
},
|
|
"type": [{"coding": [{"code": "admitting"}]}],
|
|
}
|
|
]
|
|
result = _extract_diagnoses(dx_list)
|
|
assert result["admtg_dgns_cd"] == "R07.9"
|
|
assert result["clm_dgns_2_cd"] == "R07.9"
|
|
|
|
def test_poa_indicator(self) -> None:
|
|
from bcda.express.flatten import _extract_diagnoses
|
|
|
|
dx_list = [
|
|
{
|
|
"sequence": 1,
|
|
"diagnosisCodeableConcept": {
|
|
"coding": [
|
|
{
|
|
"system": "http://hl7.org/fhir/icd-10",
|
|
"code": "J44.1",
|
|
}
|
|
]
|
|
},
|
|
"extension": [
|
|
{
|
|
"url": "https://bb/vars/clm_poa_ind_sw1",
|
|
"valueCoding": {"code": "Y"},
|
|
}
|
|
],
|
|
}
|
|
]
|
|
result = _extract_diagnoses(dx_list)
|
|
assert result["clm_poa_ind"] == "Y"
|
|
|
|
def test_poa_value_code_fallback(self) -> None:
|
|
from bcda.express.flatten import _extract_diagnoses
|
|
|
|
dx_list = [
|
|
{
|
|
"sequence": 1,
|
|
"diagnosisCodeableConcept": {
|
|
"coding": [{"system": "icd-10", "code": "A00"}]
|
|
},
|
|
"extension": [
|
|
{
|
|
"url": "https://bb/vars/POA",
|
|
"valueCode": "N",
|
|
}
|
|
],
|
|
}
|
|
]
|
|
result = _extract_diagnoses(dx_list)
|
|
assert result["clm_poa_ind"] == "N"
|
|
|
|
|
|
class TestExtractItemNoncovered:
|
|
"""_extract_item: noncovered adjudication code (lines 471-472)."""
|
|
|
|
def test_noncovered_adjudication(self) -> None:
|
|
from bcda.express.flatten import _extract_item
|
|
|
|
item = {
|
|
"sequence": 1,
|
|
"adjudication": [
|
|
{
|
|
"category": {"coding": [{"code": "noncovered"}]},
|
|
"amount": {"value": 25.00},
|
|
}
|
|
],
|
|
}
|
|
result = _extract_item(item, "CLM-1")
|
|
assert result.clm_line_ncvrd_chrg_amt == 25.00
|
|
|
|
|
|
class TestFlattenEobContainedAndProvider:
|
|
"""flatten_eob: contained Organization, billing NPI (lines 588-620)."""
|
|
|
|
def test_contained_organization_prn_and_npi(self) -> None:
|
|
resource = {
|
|
"identifier": [{"system": "http://ccw/clm_id", "value": "CLM-C1"}],
|
|
"contained": [
|
|
{
|
|
"resourceType": "Organization",
|
|
"identifier": [
|
|
{
|
|
"type": {"coding": [{"code": "PRN"}]},
|
|
"value": "OSCAR-123",
|
|
},
|
|
{
|
|
"type": {"coding": [{"code": "npi"}]},
|
|
"value": "NPI-FAC-456",
|
|
},
|
|
],
|
|
}
|
|
],
|
|
"item": [],
|
|
}
|
|
header, _ = flatten_eob(resource)
|
|
assert header.prvdr_oscar_num == "OSCAR-123"
|
|
assert header.fac_prvdr_npi_num == "NPI-FAC-456"
|
|
|
|
def test_contained_non_organization_skipped(self) -> None:
|
|
resource = {
|
|
"identifier": [],
|
|
"contained": [
|
|
{
|
|
"resourceType": "Practitioner",
|
|
"identifier": [
|
|
{
|
|
"type": {"coding": [{"code": "PRN"}]},
|
|
"value": "SHOULD-SKIP",
|
|
}
|
|
],
|
|
}
|
|
],
|
|
"item": [],
|
|
}
|
|
header, _ = flatten_eob(resource)
|
|
assert header.prvdr_oscar_num is None
|
|
|
|
def test_billing_npi_from_provider(self) -> None:
|
|
resource = {
|
|
"identifier": [],
|
|
"provider": {
|
|
"identifier": {
|
|
"system": "http://hl7.org/fhir/blg_npi",
|
|
"value": "BLG-NPI-999",
|
|
}
|
|
},
|
|
"item": [],
|
|
}
|
|
header, _ = flatten_eob(resource)
|
|
assert header.carr_clm_blg_npi_num == "BLG-NPI-999"
|
|
|
|
def test_billing_npi_not_set_without_npi_system(self) -> None:
|
|
resource = {
|
|
"identifier": [],
|
|
"provider": {
|
|
"identifier": {
|
|
"system": "http://hl7.org/fhir/tax_id",
|
|
"value": "TAX-123",
|
|
}
|
|
},
|
|
"item": [],
|
|
}
|
|
header, _ = flatten_eob(resource)
|
|
assert header.carr_clm_blg_npi_num is None
|
|
|
|
|
|
class TestFlattenEobSupportingInfoHelpers:
|
|
"""flatten_eob: inner _si_code, _si_date, _si_quantity, _si_period."""
|
|
|
|
def test_si_code_returns_code(self) -> None:
|
|
resource = {
|
|
"identifier": [],
|
|
"supportingInfo": [
|
|
{
|
|
"category": {"coding": [{"code": "discharge-status"}]},
|
|
"code": {"coding": [{"code": "01"}]},
|
|
}
|
|
],
|
|
"item": [],
|
|
}
|
|
header, _ = flatten_eob(resource)
|
|
assert header.bene_ptnt_stus_cd == "01"
|
|
|
|
def test_si_date_returns_date(self) -> None:
|
|
resource = {
|
|
"identifier": [],
|
|
"supportingInfo": [
|
|
{
|
|
"category": {"coding": [{"code": "clmrecvddate"}]},
|
|
"timingDate": "2023-08-15",
|
|
}
|
|
],
|
|
"item": [],
|
|
}
|
|
header, _ = flatten_eob(resource)
|
|
assert header.clm_efctv_dt == date(2023, 8, 15)
|
|
|
|
def test_si_quantity_returns_value(self) -> None:
|
|
resource = {
|
|
"identifier": [],
|
|
"supportingInfo": [
|
|
{
|
|
"category": {"coding": [{"code": "nch_blood_pnts_frnshd_qty"}]},
|
|
"valueQuantity": {"value": 3.0},
|
|
}
|
|
],
|
|
"item": [],
|
|
}
|
|
header, _ = flatten_eob(resource)
|
|
assert header.nch_blood_pnts_frnshd_qty == 3.0
|
|
|
|
def test_si_period_returns_dates(self) -> None:
|
|
resource = {
|
|
"identifier": [],
|
|
"supportingInfo": [
|
|
{
|
|
"category": {"coding": [{"code": "admissionperiod"}]},
|
|
"timingPeriod": {
|
|
"start": "2023-07-01",
|
|
"end": "2023-07-10",
|
|
},
|
|
}
|
|
],
|
|
"item": [],
|
|
}
|
|
header, _ = flatten_eob(resource)
|
|
# admissionperiod is not directly on header but
|
|
# the EOB processing doesn't have admsn_start on the model
|
|
# — test that the code runs without error
|
|
assert header is not None
|
|
|
|
|
|
class TestFlattenEobTotalCharge:
|
|
"""flatten_eob: total charge from 'submitted' code (lines 671-673)."""
|
|
|
|
def test_total_charge_extraction(self) -> None:
|
|
resource = {
|
|
"identifier": [],
|
|
"total": [
|
|
{
|
|
"category": {"coding": [{"code": "submitted"}]},
|
|
"amount": {"value": 12500.00},
|
|
}
|
|
],
|
|
"item": [],
|
|
}
|
|
header, _ = flatten_eob(resource)
|
|
assert header.clm_mdcr_instnl_tot_chrg_amt == 12500.00
|
|
|
|
def test_total_charge_not_set_for_other_codes(self) -> None:
|
|
resource = {
|
|
"identifier": [],
|
|
"total": [
|
|
{
|
|
"category": {"coding": [{"code": "benefit"}]},
|
|
"amount": {"value": 999.00},
|
|
}
|
|
],
|
|
"item": [],
|
|
}
|
|
header, _ = flatten_eob(resource)
|
|
assert header.clm_mdcr_instnl_tot_chrg_amt is None
|
|
|
|
|
|
class TestFlattenEobProcedure:
|
|
"""flatten_eob: procedure extraction (lines 617-620)."""
|
|
|
|
def test_procedure_extraction(self) -> None:
|
|
resource = {
|
|
"identifier": [],
|
|
"procedure": [
|
|
{
|
|
"sequence": 1,
|
|
"procedureCodeableConcept": {"coding": [{"code": "0BJ08ZZ"}]},
|
|
"date": "2023-06-02",
|
|
}
|
|
],
|
|
"item": [],
|
|
}
|
|
header, _ = flatten_eob(resource)
|
|
assert header.clm_val_sqnc_num == "1"
|
|
assert header.clm_prcdr_cd == "0BJ08ZZ"
|
|
assert header.clm_prcdr_prfrm_dt == date(2023, 6, 2)
|
|
|
|
|
|
class TestBatchFlatteners:
|
|
"""flatten_patients, flatten_eobs, flatten_coverages (lines 911-931)."""
|
|
|
|
def test_flatten_patients_batch(self) -> None:
|
|
from bcda.express.flatten import flatten_patients
|
|
|
|
resources = [
|
|
{
|
|
"id": "P1",
|
|
"identifier": [],
|
|
},
|
|
{
|
|
"id": "P2",
|
|
"identifier": [],
|
|
},
|
|
]
|
|
result = flatten_patients(resources)
|
|
assert len(result) == 2
|
|
assert result[0].id == "P1"
|
|
assert result[1].id == "P2"
|
|
|
|
def test_flatten_eobs_batch(self) -> None:
|
|
from bcda.express.flatten import flatten_eobs
|
|
|
|
resources = [
|
|
{
|
|
"id": "E1",
|
|
"identifier": [{"system": "http://ccw/clm_id", "value": "C1"}],
|
|
"item": [
|
|
{
|
|
"sequence": 1,
|
|
"productOrService": {"coding": [{"code": "99213"}]},
|
|
}
|
|
],
|
|
},
|
|
{
|
|
"id": "E2",
|
|
"identifier": [],
|
|
"item": [],
|
|
},
|
|
]
|
|
headers, items = flatten_eobs(resources)
|
|
assert len(headers) == 2
|
|
assert len(items) == 1
|
|
assert headers[0].id == "E1"
|
|
assert headers[1].id == "E2"
|
|
assert items[0].clm_id == "C1"
|
|
|
|
def test_flatten_coverages_batch(self) -> None:
|
|
from bcda.express.flatten import flatten_coverages
|
|
|
|
resources = [
|
|
{
|
|
"id": "CV1",
|
|
"extension": [],
|
|
},
|
|
{
|
|
"id": "CV2",
|
|
"extension": [],
|
|
},
|
|
]
|
|
result = flatten_coverages(resources)
|
|
assert len(result) == 2
|
|
assert result[0].id == "CV1"
|
|
assert result[1].id == "CV2"
|
|
|
|
|
|
class TestReadNdjsonAndWriteParquet:
|
|
"""_read_ndjson and _write_parquet helpers (lines 939-956)."""
|
|
|
|
def test_read_ndjson(self, tmp_path) -> None:
|
|
from bcda.express.flatten import _read_ndjson
|
|
|
|
ndjson_file = tmp_path / "test.ndjson"
|
|
ndjson_file.write_text(
|
|
'{"id": "1", "name": "alice"}\n{"id": "2", "name": "bob"}\n\n'
|
|
)
|
|
result = _read_ndjson(ndjson_file)
|
|
assert len(result) == 2
|
|
assert result[0]["id"] == "1"
|
|
assert result[1]["name"] == "bob"
|
|
|
|
def test_write_parquet_empty_rows(self, tmp_path) -> None:
|
|
import fsspec
|
|
|
|
from bcda.express.flatten import _write_parquet
|
|
|
|
fs, _ = fsspec.core.url_to_fs(str(tmp_path))
|
|
path = str(tmp_path / "empty.parquet")
|
|
n = _write_parquet(fs, path, [])
|
|
assert n == 0
|
|
|
|
def test_write_parquet_with_rows(self, tmp_path) -> None:
|
|
import fsspec
|
|
|
|
from bcda.express.flatten import _write_parquet
|
|
|
|
fs, _ = fsspec.core.url_to_fs(str(tmp_path))
|
|
path = str(tmp_path / "sub" / "data.parquet")
|
|
rows = [
|
|
{"id": "1", "val": 10},
|
|
{"id": "2", "val": 20},
|
|
]
|
|
n = _write_parquet(fs, path, rows)
|
|
assert n == 2
|
|
# Verify file was created and is readable
|
|
import pyarrow.parquet as pq
|
|
|
|
table = pq.read_table(path)
|
|
assert table.num_rows == 2
|
|
|
|
|
|
class TestFlattenExport:
|
|
"""flatten_export orchestrator (lines 992-1078)."""
|
|
|
|
def test_flatten_export_all_files(self, tmp_path) -> None:
|
|
import json
|
|
from unittest.mock import patch
|
|
|
|
from bcda.express.flatten import flatten_export
|
|
|
|
ndjson_dir = tmp_path / "ndjson"
|
|
ndjson_dir.mkdir()
|
|
store_dir = tmp_path / "store"
|
|
|
|
# Patient.ndjson
|
|
patient = {
|
|
"id": "PAT-1",
|
|
"identifier": [],
|
|
}
|
|
(ndjson_dir / "Patient.ndjson").write_text(json.dumps(patient) + "\n")
|
|
|
|
# ExplanationOfBenefit.ndjson
|
|
eob = {
|
|
"id": "EOB-1",
|
|
"identifier": [{"system": "http://ccw/clm_id", "value": "C1"}],
|
|
"item": [
|
|
{
|
|
"sequence": 1,
|
|
"productOrService": {"coding": [{"code": "99213"}]},
|
|
}
|
|
],
|
|
}
|
|
(ndjson_dir / "ExplanationOfBenefit.ndjson").write_text(json.dumps(eob) + "\n")
|
|
|
|
# Coverage.ndjson
|
|
coverage = {
|
|
"id": "COV-1",
|
|
"extension": [],
|
|
}
|
|
(ndjson_dir / "Coverage.ndjson").write_text(json.dumps(coverage) + "\n")
|
|
|
|
with patch("bcda.log.setup"):
|
|
counts = flatten_export(ndjson_dir, store_dir)
|
|
|
|
assert counts["bcda.patient"] == 1
|
|
assert counts["bcda.explanation_of_benefit"] == 1
|
|
assert counts["bcda.eob_item"] == 1
|
|
assert counts["bcda.coverage"] == 1
|
|
|
|
# Verify parquet files exist
|
|
assert (store_dir / "flat" / "patient.parquet").exists()
|
|
assert (store_dir / "flat" / "explanation_of_benefit.parquet").exists()
|
|
assert (store_dir / "flat" / "eob_item.parquet").exists()
|
|
assert (store_dir / "flat" / "coverage.parquet").exists()
|
|
|
|
def test_flatten_export_missing_files(self, tmp_path) -> None:
|
|
from unittest.mock import patch
|
|
|
|
from bcda.express.flatten import flatten_export
|
|
|
|
ndjson_dir = tmp_path / "empty_ndjson"
|
|
ndjson_dir.mkdir()
|
|
store_dir = tmp_path / "store2"
|
|
|
|
with patch("bcda.log.setup"):
|
|
counts = flatten_export(ndjson_dir, store_dir)
|
|
|
|
assert counts == {}
|
|
|
|
def test_flatten_export_patient_only(self, tmp_path) -> None:
|
|
import json
|
|
from unittest.mock import patch
|
|
|
|
from bcda.express.flatten import flatten_export
|
|
|
|
ndjson_dir = tmp_path / "ndjson_pat"
|
|
ndjson_dir.mkdir()
|
|
store_dir = tmp_path / "store3"
|
|
|
|
patient = {"id": "P1", "identifier": []}
|
|
(ndjson_dir / "Patient.ndjson").write_text(json.dumps(patient) + "\n")
|
|
|
|
with patch("bcda.log.setup"):
|
|
counts = flatten_export(ndjson_dir, store_dir)
|
|
|
|
assert "bcda.patient" in counts
|
|
assert "bcda.explanation_of_benefit" not in counts
|
|
assert "bcda.coverage" not in counts
|
|
|
|
|
|
# ── v3 coverage shape through the CCLF demographics stage ───────
|