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>
This commit is contained in:
ManikandanandClaude Opus 5.5 committed 2026-10-23 18:38:17 +05:30
commit ba15a574ab
205 files changed
+56772

No files matched your search

+251
View File
@@ -0,0 +1,251 @@
r"""
UB Claim form extraction, against a real browser.
python tests\test_claim_form.py
This one launches a **local headless chromium** and loads
`tests\fixtures\ub_claim.html`. It never touches eCW: no proxy, no login, no
2FA mail, no attempt against the guard. So unlike a live run it costs nothing
and can be run as often as you like — which matters, because the alternative
way to test this code is a three-minute login and one of four hourly attempts.
What it is really checking is the thing most likely to break silently:
* **No selector depends on eCW's per-open timestamp.** The fixture uses
`9999999999999` where the captured DOM had `1788790287089`. A selector
with the number baked in matches nothing here, and the assertions fail.
* **Values are read from inputs, not text.** The ICD code, every CPT column,
the dates and the claim status all live in inputs or selects. Reading
`textContent` returns blanks for them and the run looks successful.
* **eCW's blank padding rows are dropped.** Every grid is padded with empty
`ng-repeat` rows to hold a fixed height, plus a `*` entry row. Counting
those would report five CPT lines on a claim that has two.
"""
import sys
import pathlib
ROOT = pathlib.Path(__file__).resolve().parent.parent
sys.path.insert(0, str(ROOT / "src"))
FIXTURE = pathlib.Path(__file__).resolve().parent / "fixtures" / "ub_claim.html"
from ecw_bot.pages import claim_form # noqa: E402
from ecw_bot.selectors import claim_form as sel # noqa: E402
CHECKS = []
def check(fn):
CHECKS.append(fn)
return fn
@check
def claim_and_patient(page, data):
assert data["claim_no"] == "9104000", data["claim_no"]
p = data["patient"]
assert p["name"] == "Sample, Pat, Q", p["name"]
assert p["dob"] == "05/11/1979", p["dob"]
assert p["account_no"] == "900123", p["account_no"]
assert p["phone"] == "555-010-0100", p["phone"]
# The visible text is ellipsised by eCW's own filter; title= has it whole.
assert p["address"] == "100 SAMPLE ST, , CO 80000-0000", p["address"]
@check
def header_reads_inputs_and_the_select(page, data):
h = data["header"]
assert h["claim_date"] == "06/29/2026", h["claim_date"]
assert h["bill_type"] == "131", h["bill_type"]
assert h["facility"] == "MWC:Brighton Womens Clinic", h["facility"]
assert h["attending"] == "Clinician, Casey M", h["attending"]
# Both halves of the status: the code is what eCW submits, the label is what
# a person recognises, and only checking one of them has burned us before.
assert h["claim_status_code"] == "108ME", h["claim_status_code"]
assert h["claim_status_label"] == "Medcor Ready", h["claim_status_label"]
@check
def icd_code_comes_from_the_input(page, data):
icd = data["icd"]
assert len(icd) == 1, f"expected 1 ICD row, got {len(icd)}: {icd}"
row = icd[0]
assert row["Code"] == "R69", row
assert row["Name"] == "Illness, unspecified", row
@check
def cpt_grid_is_keyed_by_input_id(page, data):
cpt = data["cpt"]
# Two real rows. The bottom "*" entry row has no CPTROW_ id and must not
# become a third.
assert len(cpt) == 2, f"expected 2 CPT rows, got {len(cpt)}: {cpt}"
a, b = cpt
assert a["code"] == "H0023" and a["mod1"] == "GT", a
assert a["pos"] == "11" and a["units"] == "1" and a["billed_fee"] == "74.00", a
assert a["sdos"] == "06/29/2026" and a["edos"] == "06/29/2026", a
assert a["icd1"] == "1" and a["icd2"] == "", a
assert "Behavioral Health Outreach" in a["description"], a
assert b["code"] == "90791" and b["units"] == "2", b
assert b["billed_fee"] == "150.00", b
@check
def revenue_and_totals(page, data):
rev = data["revenue"]
assert len(rev) == 1, f"expected 1 revenue row, got {len(rev)}: {rev}"
row = rev[0]
assert row["42 RV CC"] == "0900", row
assert row["43 Description"] == "Psychological Services", row
assert row["44 HCPCS/Rates"] == "H0023 GT", row
assert row["47 Total Charges"] == "74.00", row
t = data["totals"]
assert t["total_charges"] == "74.00", t
assert t["charges"] == "224.00" and t["balance"] == "224.00", t
assert t["pt_balance"] == "0.00", t
@check
def insurance_and_payments(page, data):
ins = data["insurance"]
assert len(ins) == 1, f"expected 1 insurance row, got {len(ins)}: {ins}"
assert ins[0]["Name"] == "BH COLORADO ACCESS", ins[0]
assert ins[0]["Subscriber No."] == "J000000", ins[0]
assert ins[0]["State"] == "CO", ins[0]
pay = data["payments"]
assert len(pay) == 1, f"expected 1 payment row, got {len(pay)}: {pay}"
assert pay[0]["Id"] == "2000001", pay[0]
assert pay[0]["Date"] == "06/29/2026", pay[0]
assert pay[0]["From"] == "Patient", pay[0]
@check
def notes_and_empty_error_table(page, data):
assert data["notes"] == "Awaiting coding review.", data["notes"]
# "Error Logs ()" on the live claim — an empty tbody must be [], not a row
# of blanks.
assert data["errors"] == [], data["errors"]
@check
def cpt_hcpcs_tab_opens(page, data):
assert claim_form.open_cpt_hcpcs_tab(page) is True
assert page.locator(sel.SEL_CPT_GRID).first.is_visible()
@check
def progress_notes_is_discovered_not_assumed(page, data):
"""
The whole chain: click, find the note wherever it landed, read it, close.
The fixture serves the note in an iframe, as eCW does. `opened == "frame"`
is the assertion that matters - an earlier version of this code read the
hosting modal's innerText and would have come back with nothing, because
iframe content is not part of it.
"""
pn = claim_form.open_progress_notes(page)
assert pn["opened"] == "frame", pn
# Structure, not a text blob: eCW's own prisma-section labels.
assert pn["assessments"][0]["code"] == "R69", pn["assessments"]
assert pn["procedure_codes"][0]["code"] == "H0023", pn["procedure_codes"]
assert pn["procedure_codes"][0]["modifiers"] == ["GT"], pn["procedure_codes"]
assert pn["signoff_status"] == "Completed", pn["signoff_status"]
assert pn["chart_locked"] is True, pn["chart_locked"]
# And the viewer is gone, so the Cancel button underneath is reachable.
assert page.locator(sel.SEL_PN_CLOSE).count() == 0 or page.locator(".modal#progressNoteViewerDiv9999999999999").first .is_visible() is False
@check
def summary_is_readable(page, data):
data = dict(data)
data["progress_notes"] = {"opened": "modal", "text": "x" * 120}
text = claim_form.summary(data)
for want in ("9104000", "Sample, Pat, Q", "Medcor Ready", "H0023",
"90791", "224.00", "Prog. Notes"):
assert want in text, f"{want!r} missing from summary:\n{text}"
@check
def no_selector_carries_the_timestamp(page, data):
"""
No selector VALUE in selectors/claim_form.py contains a long digit run.
Parsed with ast rather than scanned as text: the module docstring quotes the
real timestamped ids on purpose, to explain the hazard, and a text scan
flagged those quotes as the bug they warn about. Only actual assignments
are checked.
The fixture stamps 9999999999999 where the captured DOM had 1788790287089,
so a selector with the number baked in also fails the assertions above —
this check is what names the cause instead of leaving eleven empty fields.
"""
import ast
import re
src = (ROOT / "src" / "ecw_bot" / "selectors" / "claim_form.py").read_text(
encoding="utf-8")
for node in ast.parse(src).body:
if not isinstance(node, ast.Assign):
continue
for const in ast.walk(node.value):
if isinstance(const, ast.Constant) and isinstance(const.value, str):
assert not re.search(r"\d{6,}", const.value), (
f"timestamped selector: "
f"{[t.id for t in node.targets if isinstance(t, ast.Name)]}"
f" = {const.value!r}")
def _run() -> int:
if not FIXTURE.exists():
print(f" FAIL fixture missing: {FIXTURE}")
return 1
try:
from playwright.sync_api import sync_playwright
except ImportError:
print(" SKIP playwright is not installed")
return 0
failures = 0
with sync_playwright() as pw:
browser = pw.chromium.launch(headless=True) # local only, no proxy
page = browser.new_page()
page.goto(FIXTURE.as_uri())
data = claim_form.extract(page)
for fn in CHECKS:
name = fn.__name__.replace("_", " ")
try:
fn(page, data)
print(f" ok {name}")
except AssertionError as exc:
print(f" FAIL {name}: {exc}")
failures += 1
except Exception as exc:
print(f" ERROR {name}: {type(exc).__name__}: {exc}")
failures += 1
browser.close()
print(f"\n{len(CHECKS) - failures}/{len(CHECKS)} passed")
return 1 if failures else 0
def main() -> int:
"""
shot() writes into the operator's dated Screenshots\ folder; a test
run must not, so the cases run with it pointed at a temp folder.
"""
import tempfile
from ecw_bot import settings as _cfg
orig = _cfg.SCREENSHOTS
with tempfile.TemporaryDirectory() as tmp:
_cfg.SCREENSHOTS = pathlib.Path(tmp)
try:
return _run()
finally:
_cfg.SCREENSHOTS = orig
if __name__ == "__main__":
sys.exit(main())