Files
ecw-charge-validation/tests/test_conditions_medicaid_pregnancy_th.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

257 lines
9.5 KiB
Python

r"""
conditions/medicaid_pregnancy_th.py — document #23: a pregnancy visit on
Medicaid (not RAE) gets the TH modifier on its E&M / primary line, in M1
(overwriting what M1 held — operator 2026-10-21); Z32.00-Z32.02 (pregnancy test) do not count; a
Medicaid telehealth pregnancy visit gets TH instead of GT (telehealth #21).
python tests\test_conditions_medicaid_pregnancy_th.py
Uses hcfa_claim.html with injected rows (data-fieldname inputs, like
test_conditions_bh_h_code_modifier.py).
"""
import io
import sys
import inspect
import pathlib
import contextlib
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 medicaid_pregnancy_th as cond # noqa: E402
from ecw_bot.conditions import telehealth # noqa: E402
from ecw_bot.conditions import CONDITIONS # noqa: E402
from ecw_bot.conditions.base import ConditionResult # noqa: E402
from ecw_bot.core import charge_summary as cs # noqa: E402
from ecw_bot.selectors import claim_form as sel # noqa: E402
CASES = []
MEDICAID = [{"Name": "APM2 Medicaid"}]
RAE = [{"Name": "BH COLORADO ACCESS"}]
COMMERCIAL = [{"Name": "AARP"}]
def case(fn):
CASES.append(fn)
return fn
def _data(ins=MEDICAID, icds=("O09.90",), lines=(("99214", ""),)):
cpt = [{"code": c, "billed_fee": "100.00", "billed": True, "mod1": m1,
"mod2": "", "mod3": "", "mod4": ""} for c, m1 in lines]
return {"screen": "hcfa", "insurance": list(ins),
"icd": [{"Code": i} for i in icds], "cpt": cpt}
def _inject(page, code="99213", mods=("", "", "", ""), row_id="CPTROW_7"):
page.evaluate(
"""(a) => {
const tbody = document.querySelector(
'#claimCPTCodesTable8888888888888 tbody');
const last = tbody.querySelector('tr:last-child');
const tr = document.createElement('tr');
tr.id = a.rowId;
let html = '<td><input type="checkbox" ng-click="updateBillToIns(3)" checked></td>'
+ '<td><input data-fieldname="claimCPTCode" value="' + a.code + '" readonly></td>'
+ '<td><input data-fieldname="ClaimCPTPOS" type="text" value="11"></td>';
a.mods.forEach((m, i) => {
html += '<td><input data-fieldname="ClaimCPTMOD' + (i + 1)
+ '" type="text" value="' + m + '"></td>';
});
html += '<td><input data-fieldname="ClaimCPTBilledFee" value="100.00"></td>';
tr.innerHTML = html;
tbody.insertBefore(tr, last);
}""", {"code": code, "mods": list(mods), "rowId": row_id})
def _mods_of(page, code):
rows = page.locator(sel.SEL_CPT_ROWS)
for i in range(rows.count()):
row = rows.nth(i)
c = row.locator(sel.SEL_CPT_CODE).first
if c.count() and c.input_value().strip() == code:
return [row.locator(s).first.input_value() for s in sel.SEL_CPT_MOD]
return None
def _apply(page, data):
with contextlib.redirect_stdout(io.StringIO()):
return cond.apply(page, "C1", data)
# ── detect (no browser) ──────────────────────────────────────────────────────
@case
def a_pregnancy_icd_on_medicaid_fires():
for icd in ("O09.90", "O26.891", "Z34.81", "Z33.1", "Z3A.12", "Z36.89"):
assert cond.detect("C1", _data(icds=("Z00.00", icd))), icd
@case
def pregnancy_test_icds_alone_do_not_fire():
assert not cond.detect("C1", _data(icds=("Z32.00",)))
assert not cond.detect("C1", _data(icds=("Z32.01", "Z32.02", "R10.9")))
assert cond.detect("C1", _data(icds=("Z32.01", "O09.90")))
@case
def medicaid_only_not_rae_or_others():
assert not cond.detect("C1", _data(ins=RAE))
assert not cond.detect("C1", _data(ins=COMMERCIAL))
assert not cond.detect("C1", _data(ins=[]))
assert not cond.detect("C1", {})
@case
def th_already_on_the_line_is_nothing():
data = _data()
data["cpt"][0]["mod2"] = "th"
assert not cond.detect("C1", data)
@case
def the_e_and_m_line_is_the_target_else_the_first_billed():
data = _data(lines=(("90471", ""), ("99214", ""), ("99213", "")))
assert cond.target_code(data) == "99214"
data = _data(lines=(("59025", ""), ("81025", "")))
assert cond.target_code(data) == "59025"
data = _data(lines=(("99213", ""),))
data["cpt"][0]["billed_fee"] = "0.00"
assert cond.target_code(data) is None
assert not cond.detect("C1", data)
# ── apply (browser) ──────────────────────────────────────────────────────────
@case
def th_goes_in_an_empty_m1(page):
page.goto(FIXTURE.as_uri())
_inject(page, "99214", ("", "", "", ""))
result = _apply(page, _data())
assert isinstance(result, ConditionResult) and result.changed, result
assert result.review == "", result.review
assert _mods_of(page, "99214") == ["TH", "", "", ""]
c = result.changes[0]
assert c["code"] == "99214" and c["box"] == 1 and c["icds"] == ["O09.90"]
assert "TH modifier added on 99214" in cond.summary_comment(result)
assert "M1" in cond.describe(result)
@case
def m1_in_use_is_overwritten_with_th(page):
page.goto(FIXTURE.as_uri())
_inject(page, "99214", ("25", "", "", ""))
result = _apply(page, _data(lines=(("99214", "25"),)))
assert _mods_of(page, "99214") == ["TH", "", "", ""]
c = result.changes[0]
assert c["box"] == 1 and c["before"] == "25" and result.review == ""
assert "TH modifier added on 99214, replacing 25" in cond.summary_comment(result)
assert "M1 25 -> TH" in cond.describe(result)
@case
def all_four_boxes_full_still_overwrites_m1_only(page):
page.goto(FIXTURE.as_uri())
_inject(page, "99214", ("25", "59", "XU", "GA"))
result = _apply(page, _data(lines=(("99214", "25"),)))
assert result.changed is True and result.review == "", result
assert _mods_of(page, "99214") == ["TH", "59", "XU", "GA"]
@case
def th_in_another_box_is_left_alone(page):
page.goto(FIXTURE.as_uri())
_inject(page, "99214", ("25", "TH", "", ""))
result = _apply(page, _data(lines=(("99214", "25"),)))
assert result.changed is False and result.review == "", result
assert _mods_of(page, "99214") == ["25", "TH", "", ""]
@case
def th_already_on_the_live_line_is_nothing(page):
page.goto(FIXTURE.as_uri())
_inject(page, "99214", ("TH", "", "", ""))
result = _apply(page, _data())
assert result.changed is False and result.review == "", result
@case
def a_line_missing_from_the_live_grid_is_review(page):
page.goto(FIXTURE.as_uri())
result = _apply(page, _data(lines=(("99215", ""),)))
assert result.changed is False and "could not be read" in result.review
# ── telehealth #21: TH, not GT ───────────────────────────────────────────────
@case
def telehealth_writes_th_not_gt_on_a_medicaid_pregnancy_visit(page):
page.goto(FIXTURE.as_uri())
data = _data()
data["progress_notes"] = {"opened": "tab",
"text": "Session Setting (PoS): TeleHealth"}
rows = page.locator(sel.SEL_CPT_ROWS)
first = rows.nth(0).locator(sel.SEL_CPT_CODE).first.input_value().strip()
with contextlib.redirect_stdout(io.StringIO()):
result = telehealth.apply(page, "C1", data)
assert result.changed, result
assert result.changes[0]["modifier"] == "TH", result.changes
assert _mods_of(page, first)[0] == "TH"
@case
def telehealth_keeps_gt_for_rae_and_for_no_pregnancy(page):
for ins, icds in ((RAE, ("O09.90",)), (MEDICAID, ("Z32.01",))):
page.goto(FIXTURE.as_uri())
data = _data(ins=ins, icds=icds)
data["progress_notes"] = {"opened": "tab",
"text": "Session Setting (PoS): TeleHealth"}
with contextlib.redirect_stdout(io.StringIO()):
result = telehealth.apply(page, "C1", data)
assert result.changes[0]["modifier"] == "GT", (ins, icds)
# ── registry and Charge Summary ──────────────────────────────────────────────
@case
def it_is_registered_after_telehealth_and_before_priced_line_check():
names = [m.NAME for m in CONDITIONS]
assert names.index("telehealth") < names.index("medicaid_pregnancy_th") \
< names.index("priced_line_check"), names
assert "medicaid_pregnancy_th" in cs.COMMENTS
def main() -> int:
from playwright.sync_api import sync_playwright
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())