Files
ecw-charge-validation/src/ecw_bot/core/payer_groups.py
T
ManikandanandClaude Opus 5.5 ba15a574ab Initial import: eCW Charge Validation bot and results Portal
Snapshot of the working project as of 2026-10-23 (local 2d4d4fc), one
commit with no earlier history: the tracked files were de-identified
first (synthetic claim ids and patient data only; see Context.md
section 7). Credentials, run logs, screenshots, claim reports and client
workbooks are gitignored and not included.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 18:38:17 +05:30

82 lines
3.5 KiB
Python

"""
Payer -> Group Name lookup for the categorization step.
Exact-string match against a small hand-edited table (`payer_groups.json`,
next to this package) — no fuzzy or substring matching. eCW's own PAYER
values already differ by case and spacing for what is really the same
carrier (see `tests/test_payer_groups.py`'s docstring), so "close enough"
matching has no safe way to pick which known string a new one is closest to.
An unrecognized payer must come back as None and get a human's attention —
never a guess, on billing data.
Since 2026-10-06 the table is the team's own template: the Payer Name ->
Payer Group Name pairs of their Claim Status Dashboard, imported with
`tools/import_payer_groups.py` (re-run it when the dashboard changes). Adding
a payer by hand is still a one-line edit to `payer_groups.json`.
"""
import json
from pathlib import Path
_CONFIG_PATH = Path(__file__).resolve().parent.parent / "payer_groups.json"
def load_mapping(path=None) -> dict:
"""The payer -> group table. `path` defaults to the shipped config."""
p = Path(path) if path else _CONFIG_PATH
if not p.exists():
raise FileNotFoundError(
f"{p} not found — the payer group table is missing")
data = json.loads(p.read_text(encoding="utf-8"))
if not isinstance(data, dict):
raise ValueError(
f"{p} does not contain a JSON object (payer -> group name) — "
f"got {type(data).__name__}")
return data
def classify(payer: str, mapping: dict) -> str:
"""The Group Name for this exact PAYER string, or None if unmapped."""
return mapping.get((payer or "").strip())
# ── Group names the CONDITIONS decide on ─────────────────────────────────────
# Since 2026-10-06 `payer_groups.json` is the team's own template — the
# "Payer Name" -> "Payer Group Name" pairs of their Claim Status Dashboard
# (tools/import_payer_groups.py) — so its group names are the dashboard's
# ("Commercial Ins", "RAE (Behavioral Health)", "Dental - CHP+/DentaQuest"...)
# and that is what the Group Name columns show. The condition modules were
# written against the earlier, shorter names, which stay the vocabulary of
# their rules; this table translates. A name not listed (including every
# earlier short name) passes through unchanged.
RULE_GROUP = {
"Commercial Ins": "Commercial",
"RAE (Behavioral Health)": "RAE payer",
"Regional Accountable Entity-RAE (Reporting Group)": "RAE payer",
"Dental - CHP+/DentaQuest": "Dentaquest",
"Self Pay": "Program", # the clinic's programs (Outreach, slides…)
"Payer Not In Group": None, # the dashboard's own "unclassified"
}
# A payer whose condition rule differs from its dashboard group. Empty since
# 2026-10-21: the Medicare Advantage plans (AARP Medicare Advantage, Aetna
# Medicare ADV) were kept on Medicare here until the operator ruled; the
# ruling is "follow the dashboard", so they are Commercial for every
# condition (#21 95/93 not #22 G2025; no #14, #19, #27-#29; #18 / #20 add).
RULE_OVERRIDES = {}
def rule_group(payer: str, mapping: dict) -> str:
"""
The group a condition's rule should use for this PAYER, or None if
unmapped: the payer's own override if it has one, else its Group Name
translated through RULE_GROUP.
"""
key = (payer or "").strip()
if key in RULE_OVERRIDES:
return RULE_OVERRIDES[key]
group = classify(key, mapping)
if group is None:
return None
return RULE_GROUP.get(group, group)