feat: MassHealth member detail demographics, wait for name/plan before PDF
- Extract gender, address (street/city/state/zip) and home phone from the MassHealth Member Detail page and save to the patient; phone is only filled when the patient has none - Shared extract_eligibility_row polls up to 30s until the matching results row has both name and plan before the PDF is printed (AngularJS fills cell text after the rows appear); used by all three MH eligibility workers - Fall back to the Member Detail heading for the name when the results table never filled it in Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
import time
|
||||
|
||||
from selenium.webdriver.common.by import By
|
||||
|
||||
# Cell texts of every row in the Eligible / Ineligible results tables.
|
||||
# innerText (not .text) so cells rendered off-screen still return their text.
|
||||
_ROWS_JS = """
|
||||
const out = [];
|
||||
for (const label of ['Eligible', 'Ineligible']) {
|
||||
const rows = document.evaluate(
|
||||
`//h4[normalize-space(text())='${label}']/following::table[1]/tbody/tr`,
|
||||
document, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
|
||||
for (let i = 0; i < rows.snapshotLength; i++) {
|
||||
const cells = Array.from(rows.snapshotItem(i).querySelectorAll('td'))
|
||||
.map(td => (td.innerText || td.textContent || '').trim());
|
||||
out.push([label, cells]);
|
||||
}
|
||||
}
|
||||
return out;
|
||||
"""
|
||||
|
||||
|
||||
def _normalize_id(s):
|
||||
return "".join(c for c in str(s) if c.isalnum()).lower()
|
||||
|
||||
|
||||
def extract_eligibility_row(driver, member_id, timeout=30):
|
||||
"""Wait for the MassHealth results row matching member_id and return
|
||||
{eligibility, firstName, lastName, insurance}.
|
||||
|
||||
AngularJS adds the rows first and fills the cell text afterwards — the
|
||||
member ID can appear before the name and plan do. Keep polling until the
|
||||
matching row has both a name and a plan, so callers never print the PDF
|
||||
before the data is captured. After the timeout, returns whatever was
|
||||
found ({"eligibility": None} if no row matched).
|
||||
"""
|
||||
norm_self = _normalize_id(member_id)
|
||||
best = {"eligibility": None}
|
||||
deadline = time.time() + timeout
|
||||
attempt = 0
|
||||
|
||||
while True:
|
||||
attempt += 1
|
||||
try:
|
||||
rows = driver.execute_script(_ROWS_JS) or []
|
||||
except Exception as e:
|
||||
print(f"[extraction] reading rows failed: {e}")
|
||||
rows = []
|
||||
|
||||
for label, cells in rows:
|
||||
if len(cells) < 3:
|
||||
continue
|
||||
norm_cell = _normalize_id(cells[2])
|
||||
if not (norm_self and norm_cell and (norm_self in norm_cell or norm_cell in norm_self)):
|
||||
continue
|
||||
|
||||
# name in cells[4], plan in cells[6] (fallback to last cell)
|
||||
full_name = cells[4] if len(cells) > 4 else ""
|
||||
plan_name = cells[6] if len(cells) > 6 else (cells[-1] if len(cells) > 4 else "")
|
||||
name_parts = full_name.split()
|
||||
best = {
|
||||
"eligibility": "Y" if label == "Eligible" else "N",
|
||||
"firstName": name_parts[0] if name_parts else "",
|
||||
"lastName": " ".join(name_parts[1:]),
|
||||
"insurance": plan_name,
|
||||
}
|
||||
if full_name and plan_name:
|
||||
print(f"[extraction] MATCHED {label} → name='{full_name}' plan='{plan_name}' (attempt {attempt})")
|
||||
return best
|
||||
break
|
||||
|
||||
if time.time() >= deadline:
|
||||
break
|
||||
if attempt % 5 == 1:
|
||||
state = "name/plan still empty" if best["eligibility"] else "no matching row yet"
|
||||
print(f"[extraction] {state} for memberId='{member_id}' (attempt {attempt}), waiting...")
|
||||
time.sleep(1)
|
||||
|
||||
print(f"[extraction] gave up after {timeout}s for memberId='{member_id}' → {best}")
|
||||
return best
|
||||
Reference in New Issue
Block a user