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:
commit
ba15a574ab
205 files changed
+56772
No files matched your search
@@ -0,0 +1,251 @@
|
||||
r"""
|
||||
conditions/j2930_j2001_update.py — document condition #5 (CPT `J2930 &
|
||||
J2001`): both codes get the same treatment, uncheck + zero the Billed
|
||||
Fee. See that module's own docstring for why J2930 is NOT replaced with
|
||||
J2919 despite the document's wording — the client corrected an earlier
|
||||
build that attempted the replacement.
|
||||
|
||||
python tests\test_conditions_j2930_j2001_update.py
|
||||
|
||||
Uses hcfa_claim.html: this is the screen the checkbox/fee primitive
|
||||
(`zero_and_uncheck_cpt_rows`) is actually verified against (see
|
||||
test_hcfa_claim.py's own note on ub_claim.html's checkboxes carrying no
|
||||
ng-click at all). Neither J2001 nor J2930 has a row of its own in that
|
||||
fixture, so `_inject_cpt_row()` adds one for whichever code a case needs,
|
||||
the same way the other condition test files inject their own rows,
|
||||
rather than growing the shared fixture.
|
||||
"""
|
||||
|
||||
import sys
|
||||
import pathlib
|
||||
import inspect
|
||||
|
||||
ROOT = pathlib.Path(__file__).resolve().parent.parent
|
||||
sys.path.insert(0, str(ROOT / "src"))
|
||||
|
||||
FIXTURE = pathlib.Path(__file__).resolve().parent / "fixtures" / "hcfa_claim.html"
|
||||
|
||||
from ecw_bot.conditions import j2930_j2001_update # noqa: E402
|
||||
from ecw_bot.conditions.base import ConditionResult, run_all # noqa: E402
|
||||
from ecw_bot.pages import claim_form # noqa: E402
|
||||
from ecw_bot.selectors import claim_form as sel # noqa: E402
|
||||
|
||||
CASES = []
|
||||
|
||||
|
||||
def case(fn):
|
||||
CASES.append(fn)
|
||||
return fn
|
||||
|
||||
|
||||
def _inject_cpt_row(page, code, row_id="CPTROW_3", checked=True, fee="45.00"):
|
||||
page.evaluate(
|
||||
"""(args) => {
|
||||
const tbody = document.querySelector(
|
||||
'#claimCPTCodesTable8888888888888 tbody');
|
||||
const addRowTr = tbody.querySelector('tr:last-child');
|
||||
const tr = document.createElement('tr');
|
||||
tr.id = args.rowId;
|
||||
tr.innerHTML =
|
||||
'<td><input type="checkbox" ng-click="updateBillToIns(3)"'
|
||||
+ (args.checked ? ' ng-checked="cpt.BillToIns==\\'1\\'" checked' : '')
|
||||
+ '></td>'
|
||||
+ '<td><input data-fieldname="claimCPTCode" value="' + args.code + '" readonly></td>'
|
||||
+ '<td><input data-fieldname="ClaimCPTBilledFee" value="' + args.fee + '"></td>';
|
||||
tbody.insertBefore(tr, addRowTr);
|
||||
}""", {"code": code, "rowId": row_id, "checked": checked, "fee": fee})
|
||||
|
||||
|
||||
def _inject_j2001_row(page, checked=True, fee="45.00"):
|
||||
_inject_cpt_row(page, "J2001", checked=checked, fee=fee)
|
||||
|
||||
|
||||
@case
|
||||
def detect_true_when_j2001_is_on_the_claim():
|
||||
data = {"cpt": [{"code": "99213"}, {"code": "J2001"}]}
|
||||
assert j2930_j2001_update.detect("C1", data) is True
|
||||
|
||||
|
||||
@case
|
||||
def detect_false_when_j2001_is_absent():
|
||||
data = {"cpt": [{"code": "99213"}, {"code": "J7298"}]}
|
||||
assert j2930_j2001_update.detect("C1", data) is False
|
||||
|
||||
|
||||
@case
|
||||
def detect_true_when_j2930_is_on_the_claim_alone():
|
||||
data = {"cpt": [{"code": "J2930"}]}
|
||||
assert j2930_j2001_update.detect("C1", data) is True
|
||||
|
||||
|
||||
@case
|
||||
def detect_ignores_case_and_spaces_like_the_helper_does():
|
||||
"""
|
||||
Review, 2026-10-12: zero_and_uncheck_cpt_rows() matches ignoring case,
|
||||
so detect() must too — or a "j2001" is never excluded.
|
||||
"""
|
||||
for code in ("j2001", " J2930 ", "j2930"):
|
||||
data = {"cpt": [{"code": code}]}
|
||||
assert j2930_j2001_update.detect("C1", data) is True, code
|
||||
|
||||
|
||||
@case
|
||||
def detect_false_with_no_cpt_key_at_all():
|
||||
assert j2930_j2001_update.detect("C1", {}) is False
|
||||
|
||||
|
||||
@case
|
||||
def apply_unchecks_and_zeroes_j2001_only(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_j2001_row(page, checked=True, fee="45.00")
|
||||
|
||||
data = {"cpt": [{"code": "J2001"}]}
|
||||
result = j2930_j2001_update.apply(page, "9103000", data)
|
||||
assert isinstance(result, ConditionResult)
|
||||
assert result.condition_name == "j2930_j2001_update"
|
||||
assert result.changed is True
|
||||
assert len(result.changes) == 1, result.changes
|
||||
|
||||
c = result.changes[0]
|
||||
assert c["code"] == "J2001", c
|
||||
assert c["unchecked"] is True, c
|
||||
assert c["fee"] == {"before": "45.00", "after": "0.00"}, c
|
||||
|
||||
j2001_row = page.locator(sel.SEL_CPT_ROWS).nth(3)
|
||||
assert j2001_row.locator(sel.SEL_CPT_ASSIGN_TO_PATIENT).first.is_checked() is False
|
||||
assert j2001_row.locator(sel.SEL_CPT_BILLED_FEE).first.input_value() == "0.00"
|
||||
|
||||
row0_fee = page.locator(sel.SEL_CPT_ROWS).nth(0).locator(
|
||||
sel.SEL_CPT_BILLED_FEE).first
|
||||
assert row0_fee.input_value() == "210.00", \
|
||||
"99213 must be left alone — only J2001 is targeted"
|
||||
|
||||
|
||||
@case
|
||||
def apply_unchecks_and_zeroes_j2930_the_same_way(page):
|
||||
"""
|
||||
The client's correction, proven directly: J2930 gets exactly the same
|
||||
uncheck + zero-fee treatment as J2001 — no row removal, no J2919
|
||||
ever added.
|
||||
"""
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_cpt_row(page, "J2930", checked=True, fee="120.00")
|
||||
|
||||
data = {"cpt": [{"code": "J2930"}]}
|
||||
result = j2930_j2001_update.apply(page, "9103000", data)
|
||||
assert result.changed is True
|
||||
assert len(result.changes) == 1, result.changes
|
||||
|
||||
c = result.changes[0]
|
||||
assert c["code"] == "J2930", c
|
||||
assert c["unchecked"] is True, c
|
||||
assert c["fee"] == {"before": "120.00", "after": "0.00"}, c
|
||||
|
||||
assert claim_form.cpt_code_present(page, "J2930"), \
|
||||
"J2930's row must still exist — unchecked/zeroed, never removed"
|
||||
|
||||
|
||||
@case
|
||||
def apply_handles_both_codes_on_the_same_claim(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_cpt_row(page, "J2001", row_id="CPTROW_3", checked=True, fee="45.00")
|
||||
_inject_cpt_row(page, "J2930", row_id="CPTROW_4", checked=True, fee="120.00")
|
||||
|
||||
data = {"cpt": [{"code": "J2001"}, {"code": "J2930"}]}
|
||||
result = j2930_j2001_update.apply(page, "9103000", data)
|
||||
assert result.changed is True
|
||||
|
||||
by_code = {c["code"]: c for c in result.changes}
|
||||
assert set(by_code) == {"J2001", "J2930"}, result.changes
|
||||
assert by_code["J2001"]["fee"]["after"] == "0.00", by_code
|
||||
assert by_code["J2930"]["fee"]["after"] == "0.00", by_code
|
||||
|
||||
row0_fee = page.locator(sel.SEL_CPT_ROWS).nth(0).locator(
|
||||
sel.SEL_CPT_BILLED_FEE).first
|
||||
assert row0_fee.input_value() == "210.00", \
|
||||
"99213 must be left alone — neither target code"
|
||||
|
||||
|
||||
@case
|
||||
def apply_is_a_no_op_once_j2001_is_already_excluded(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_j2001_row(page, checked=False, fee="0.00")
|
||||
|
||||
data = {"cpt": [{"code": "J2001"}]}
|
||||
result = j2930_j2001_update.apply(page, "9103000", data)
|
||||
assert result.changed is False
|
||||
assert result.changes == []
|
||||
|
||||
|
||||
@case
|
||||
def apply_is_a_no_op_when_j2001_is_not_on_the_claim(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
data = {"cpt": [{"code": "99213"}]}
|
||||
result = j2930_j2001_update.apply(page, "9103000", data)
|
||||
assert result.changed is False
|
||||
assert result.changes == []
|
||||
|
||||
|
||||
@case
|
||||
def describe_names_j2001_and_the_fee_change(page):
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_j2001_row(page, checked=True, fee="45.00")
|
||||
data = {"cpt": [{"code": "J2001"}]}
|
||||
result = j2930_j2001_update.apply(page, "9103000", data)
|
||||
text = j2930_j2001_update.describe(result)
|
||||
assert "J2001" in text, text
|
||||
assert "45.00" in text and "0.00" in text, text
|
||||
|
||||
|
||||
@case
|
||||
def run_all_fires_j2930_j2001_update_alongside_others_independently(page):
|
||||
"""
|
||||
Against the REAL registry, not fakes — proves this condition is wired
|
||||
into conditions.CONDITIONS and does not collide with the depression-DX
|
||||
or telehealth conditions, which key off icd/progress_notes, not cpt.
|
||||
"""
|
||||
page.goto(FIXTURE.as_uri())
|
||||
_inject_j2001_row(page, checked=True, fee="45.00")
|
||||
data = {"cpt": [{"code": "J2001"}]}
|
||||
results = run_all(page, "9103000", data)
|
||||
assert len(results) == 1, results
|
||||
assert results[0].condition_name == "j2930_j2001_update", results
|
||||
|
||||
|
||||
def main() -> 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)
|
||||
page = browser.new_page()
|
||||
|
||||
for fn in CASES:
|
||||
name = fn.__name__.replace("_", " ")
|
||||
try:
|
||||
if len(inspect.signature(fn).parameters) == 0:
|
||||
fn()
|
||||
else:
|
||||
fn(page)
|
||||
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(CASES) - failures}/{len(CASES)} passed")
|
||||
return 1 if failures else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user