Files
DentalManagementMH06/apps/SeleniumService/selenium_UnitedDH_preAuthWorker.py
ff 3978b16d7d feat: United/DentalHub + Tufts SCO pre-auth, cloud storage search & file thumbnails
- Add United/DentalHub pre-authorization Selenium worker, helpers, backend route/processor/client, and frontend OTP modal + wired button
- Add Tufts SCO pre-authorization Selenium worker, helpers, backend route/processor/client, and frontend OTP modal + wired button
- Fix btnSubmitAuthorization selector for UnitedDH preauth step2
- Fix Tufts SCO preauth step3 to target <span>pre-authorization</span> button
- Cloud storage search: default to "both" mode so patient folder names match on first search
- Cloud storage folder panel: auto-scroll to panel when opened from search results
- Cloud storage files: show inline image thumbnails and PDF badge in file grid

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 00:03:58 -04:00

1238 lines
60 KiB
Python

"""
United/DentalHub Pre-Authorization Worker.
Portal: app.dentalhub.com (United SCO)
Flow (mirrors claim worker):
1. Navigate to eligibility page, fill member ID + DOB + payer, continue through
Select Insurance popup and Provider & Location page → land on Selected Patient page.
2. Click Submit Pre-Authorization button on Selected Patient page.
3. Pre-auth page is pre-filled — select payer and click Continue.
4. Select Insurance popup — click Ok.
5. Practitioner & Location page — click Continue only (no dropdowns).
6. Date Entry / pre-auth form — fill CDT codes, tooth, billed amount, attach files, submit.
7. Capture pre-auth number and PDF from confirmation page.
"""
from selenium import webdriver
from selenium.common.exceptions import WebDriverException, TimeoutException
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.common.by import By
from selenium.webdriver.common.keys import Keys
from selenium.webdriver.common.action_chains import ActionChains
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from webdriver_manager.chrome import ChromeDriverManager
import time
import os
import base64
import json
import re
from unitedsco_browser_manager import get_browser_manager
_SERVICE_DIR = os.path.dirname(os.path.abspath(__file__))
_BACKEND_CWD = os.path.normpath(os.path.join(_SERVICE_DIR, "..", "Backend"))
class AutomationUnitedDHPreAuth:
def __init__(self, data):
self.headless = False
self.driver = None
claim = data.get("claim", {}) if isinstance(data, dict) else {}
self.memberId = claim.get("memberId", "")
self.dateOfBirth = claim.get("dateOfBirth", "")
self.firstName = claim.get("firstName", "")
self.lastName = claim.get("lastName", "")
self.serviceDate = claim.get("serviceDate", "")
self.serviceLines = claim.get("serviceLines", [])
self.claimFiles = claim.get("claimFiles", [])
self.patientName = claim.get("patientName", "")
self.remarks = claim.get("remarks", "")
self.uniteddh_username = claim.get("uniteddhUsername", "")
self.uniteddh_password = claim.get("uniteddhPassword", "")
self.download_dir = get_browser_manager().download_dir
os.makedirs(self.download_dir, exist_ok=True)
def config_driver(self):
self.driver = get_browser_manager().get_driver(self.headless)
def _force_logout(self):
try:
print("[UnitedDH PreAuth login] Forcing logout due to credential change...")
browser_manager = get_browser_manager()
try:
self.driver.get("https://app.dentalhub.com/app/dashboard")
time.sleep(2)
for selector in [
"//button[contains(text(),'Log out') or contains(text(),'Logout') or contains(text(),'Sign out')]",
"//a[contains(text(),'Log out') or contains(text(),'Logout')]",
"//button[@aria-label='Log out' or @aria-label='Logout']",
]:
try:
btn = WebDriverWait(self.driver, 3).until(EC.element_to_be_clickable((By.XPATH, selector)))
btn.click()
print("[UnitedDH PreAuth login] Clicked logout button")
time.sleep(2)
break
except TimeoutException:
continue
except Exception as e:
print(f"[UnitedDH PreAuth login] Could not click logout button: {e}")
try:
self.driver.delete_all_cookies()
print("[UnitedDH PreAuth login] Cleared all cookies")
except Exception as e:
print(f"[UnitedDH PreAuth login] Error clearing cookies: {e}")
browser_manager.clear_credentials_hash()
return True
except Exception as e:
print(f"[UnitedDH PreAuth login] Error during forced logout: {e}")
return False
def login(self, url):
wait = WebDriverWait(self.driver, 30)
browser_manager = get_browser_manager()
try:
if self.uniteddh_username and browser_manager.credentials_changed(self.uniteddh_username):
self._force_logout()
self.driver.get(url)
time.sleep(2)
try:
current_url = self.driver.current_url
print(f"[UnitedDH PreAuth login] Current URL: {current_url}")
if "app.dentalhub.com" in current_url and "login" not in current_url.lower():
try:
WebDriverWait(self.driver, 3).until(
EC.presence_of_element_located((By.XPATH,
'//input[contains(@placeholder,"Search")] | //*[contains(@class,"dashboard")] | //nav'))
)
print("[UnitedDH PreAuth login] Already logged in")
return "ALREADY_LOGGED_IN"
except TimeoutException:
pass
except Exception as e:
print(f"[UnitedDH PreAuth login] Error checking current state: {e}")
self.driver.get(url)
time.sleep(3)
current_url = self.driver.current_url
print(f"[UnitedDH PreAuth login] After navigation URL: {current_url}")
if "app.dentalhub.com" in current_url and "login" not in current_url.lower():
print("[UnitedDH PreAuth login] Already on dashboard")
return "ALREADY_LOGGED_IN"
try:
WebDriverWait(self.driver, 3).until(
EC.presence_of_element_located((By.XPATH,
"//input[@type='tel' or contains(@placeholder,'code') or contains(@aria-label,'Verification')]"))
)
print("[UnitedDH PreAuth login] OTP input found")
return "OTP_REQUIRED"
except TimeoutException:
pass
if "app.dentalhub.com" in current_url:
try:
login_btn = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((By.XPATH,
"//button[contains(text(),'LOGIN') or contains(text(),'Log In') or contains(text(),'Login')]"))
)
login_btn.click()
print("[UnitedDH PreAuth login] Clicked LOGIN button")
time.sleep(5)
except TimeoutException:
print("[UnitedDH PreAuth login] No LOGIN button found, proceeding...")
current_url = self.driver.current_url
print(f"[UnitedDH PreAuth login] After LOGIN click URL: {current_url}")
if "b2clogin.com" in current_url or "login" in current_url.lower():
print("[UnitedDH PreAuth login] On B2C login page - filling credentials")
try:
send_code_btn = self.driver.find_element(By.XPATH,
"//button[@id='sendCode'] | //input[@id='sendCode'] | "
"//button[contains(text(),'Text Me') or contains(text(),'Send Code')]"
)
if send_code_btn.is_displayed():
print("[UnitedDH PreAuth login] Already on phone verification page - clicking 'Text Me'")
self.driver.execute_script("arguments[0].click();", send_code_btn)
time.sleep(3)
return "OTP_REQUIRED"
except Exception:
pass
try:
email_field = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.XPATH,
"//input[@id='signInName' or @name='signInName' or @name='Email address' or @type='email']"))
)
email_field.clear()
email_field.send_keys(self.uniteddh_username)
print(f"[UnitedDH PreAuth login] Entered username: {self.uniteddh_username}")
password_field = WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((By.XPATH,
"//input[@id='password' or @type='password']"))
)
password_field.clear()
password_field.send_keys(self.uniteddh_password)
print("[UnitedDH PreAuth login] Entered password")
signin_button = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.XPATH,
"//button[@id='next'] | //button[@type='submit' and contains(text(),'Sign')]"))
)
signin_button.click()
print("[UnitedDH PreAuth login] Clicked Sign in button")
if self.uniteddh_username:
browser_manager.save_credentials_hash(self.uniteddh_username)
time.sleep(5)
try:
continue_btn = self.driver.find_element(By.XPATH, "//button[contains(text(),'Continue')]")
phone_elements = self.driver.find_elements(By.XPATH, "//*[contains(text(),'Phone')]")
if continue_btn and phone_elements:
print("[UnitedDH PreAuth login] MFA method selection page detected")
try:
phone_radio = self.driver.find_element(By.XPATH,
"//input[@type='radio' and (contains(@value,'phone') or contains(@value,'Phone'))] | "
"//label[contains(text(),'Phone')]/preceding-sibling::input[@type='radio'] | "
"//input[@type='radio']"
)
if phone_radio and not phone_radio.is_selected():
phone_radio.click()
print("[UnitedDH PreAuth login] Selected 'Phone' radio button")
except Exception as radio_err:
print(f"[UnitedDH PreAuth login] Could not click Phone radio: {radio_err}")
time.sleep(1)
continue_btn.click()
print("[UnitedDH PreAuth login] Clicked 'Continue' on MFA selection page")
time.sleep(3)
except Exception:
pass
try:
send_code_btn = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((By.XPATH,
"//button[@id='sendCode'] | //input[@id='sendCode'] | "
"//button[contains(text(),'Text Me') or contains(text(),'Send Code')]"))
)
print("[UnitedDH PreAuth login] Found 'Text Me' / Send Code button")
self.driver.execute_script("arguments[0].click();", send_code_btn)
time.sleep(3)
return "OTP_REQUIRED"
except TimeoutException:
pass
try:
WebDriverWait(self.driver, 5).until(
EC.presence_of_element_located((By.XPATH,
"//input[@type='tel' or contains(@placeholder,'code') or contains(@aria-label,'Verification')]"))
)
print("[UnitedDH PreAuth login] OTP input appeared after sign-in")
return "OTP_REQUIRED"
except TimeoutException:
pass
current_url = self.driver.current_url
if "app.dentalhub.com" in current_url and "login" not in current_url.lower():
print("[UnitedDH PreAuth login] Login succeeded without OTP")
return "SUCCESS"
print(f"[UnitedDH PreAuth login] Unexpected state - URL: {current_url}")
return "SUCCESS"
except Exception as e:
return f"ERROR: Login failed - {e}"
if "app.dentalhub.com" in current_url:
return "ALREADY_LOGGED_IN"
return "SUCCESS"
except Exception as e:
return f"ERROR: Login exception - {e}"
# ── Helpers ────────────────────────────────────────────────────────────────
def _check_for_error_dialog(self):
error_patterns = [
("Patient Not Found", "Patient Not Found - please check the Subscriber ID, DOB, and Payer selection"),
("Insufficient Information", "Insufficient Information - need Subscriber ID + DOB, or First Name + Last Name + DOB"),
("No Eligibility", "No eligibility information found for this patient"),
("Error", None),
]
for pattern, default_msg in error_patterns:
try:
dialog_elem = self.driver.find_element(By.XPATH,
f"//modal-container//*[contains(text(),'{pattern}')] | "
f"//div[contains(@class,'modal')]//*[contains(text(),'{pattern}')]"
)
if dialog_elem.is_displayed():
try:
modal = self.driver.find_element(By.XPATH, "//modal-container | //div[contains(@class,'modal-dialog')]")
dialog_text = modal.text.strip()[:200]
except Exception:
dialog_text = dialog_elem.text.strip()[:200]
print(f"[UnitedDH PreAuth] Error dialog detected: {dialog_text}")
try:
dismiss_btn = self.driver.find_element(By.XPATH,
"//modal-container//button[contains(text(),'Ok') or contains(text(),'OK') or contains(text(),'Close')] | "
"//div[contains(@class,'modal')]//button[contains(text(),'Ok') or contains(text(),'OK') or contains(text(),'Close')]"
)
dismiss_btn.click()
print("[UnitedDH PreAuth] Dismissed error dialog")
time.sleep(1)
except Exception:
try:
close_btn = self.driver.find_element(By.XPATH, "//modal-container//button[@class='close']")
close_btn.click()
except Exception:
pass
error_msg = default_msg if default_msg else f"ERROR: {dialog_text}"
return f"ERROR: {error_msg}"
except Exception:
continue
return None
def _format_dob(self, dob_str):
if dob_str and "-" in dob_str:
dob_parts = dob_str.split("-")
if len(dob_parts) == 3:
return f"{dob_parts[1]}/{dob_parts[2]}/{dob_parts[0]}"
return dob_str
def _hide_browser(self):
try:
try:
self.driver.get("about:blank")
time.sleep(0.5)
except Exception:
pass
try:
self.driver.minimize_window()
print("[UnitedDH PreAuth] Browser window minimized")
return
except Exception:
pass
try:
self.driver.set_window_position(-10000, -10000)
print("[UnitedDH PreAuth] Browser window moved off-screen")
return
except Exception:
pass
try:
import subprocess
subprocess.run(["xdotool", "getactivewindow", "windowminimize"],
timeout=3, capture_output=True)
print("[UnitedDH PreAuth] Browser minimized via xdotool")
except Exception:
pass
except Exception as e:
print(f"[UnitedDH PreAuth] Could not hide browser: {e}")
# ── Pre-auth steps ─────────────────────────────────────────────────────────
def step1_search_patient(self):
"""
Navigate to the eligibility page, fill member ID + DOB + payer, continue through
the Select Insurance popup and Provider & Location dropdowns to land on the
Selected Patient results page. (Identical to the claim worker's step1.)
"""
from selenium.webdriver.common.action_chains import ActionChains
try:
print(f"[UnitedDH PreAuth] step1: memberId={self.memberId}, dob={self.dateOfBirth}")
self.driver.get("https://app.dentalhub.com/app/patient/eligibility")
time.sleep(3)
print(f"[UnitedDH PreAuth] step1 URL: {self.driver.current_url}")
try:
WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((By.ID, "firstName_Back"))
)
print("[UnitedDH PreAuth] step1: Patient Information form loaded")
except TimeoutException:
print("[UnitedDH PreAuth] step1: Patient Information form not found")
return "ERROR: step1 - Patient Information form not found"
# Fill Subscriber ID
if self.memberId:
subscriber_id_selectors = [
"//input[@id='subscriberId_Front']",
"//input[@id='subscriberId_Back' or @id='subscriberID_Back']",
"//input[@id='memberId_Back' or @id='memberid_Back']",
"//input[@id='medicaidId_Back']",
"//label[contains(text(),'Subscriber ID')]/..//input[not(@id='firstName_Back') and not(@id='lastName_Back') and not(@id='dateOfBirth_Back')]",
"//input[contains(@placeholder,'Subscriber') or contains(@placeholder,'subscriber')]",
"//input[contains(@placeholder,'Medicaid') or contains(@placeholder,'medicaid')]",
"//input[contains(@placeholder,'Member') or contains(@placeholder,'member')]",
]
subscriber_filled = False
for sel in subscriber_id_selectors:
try:
sid_input = self.driver.find_element(By.XPATH, sel)
if sid_input.is_displayed():
sid_input.clear()
sid_input.send_keys(self.memberId)
field_id = sid_input.get_attribute("id") or "unknown"
print(f"[UnitedDH PreAuth] step1: Subscriber ID entered: {self.memberId} (field='{field_id}')")
subscriber_filled = True
break
except Exception:
continue
if not subscriber_filled:
try:
all_inputs = self.driver.find_elements(By.XPATH, "//form//input[@type='text' or not(@type)]")
known_ids = {'firstName_Back', 'lastName_Back', 'dateOfBirth_Back', 'procedureDate_Back', 'insurerId'}
for inp in all_inputs:
inp_id = inp.get_attribute("id") or ""
if inp_id not in known_ids and inp.is_displayed():
inp.clear()
inp.send_keys(self.memberId)
print(f"[UnitedDH PreAuth] step1: Subscriber ID in fallback field id='{inp_id}'")
subscriber_filled = True
break
except Exception as e2:
print(f"[UnitedDH PreAuth] step1: Fallback subscriber field error: {e2}")
if not subscriber_filled:
print(f"[UnitedDH PreAuth] step1: WARNING - Could not find Subscriber ID field")
# Fill Date of Birth
try:
dob_input = self.driver.find_element(By.ID, "dateOfBirth_Back")
dob_input.clear()
dob_formatted = self._format_dob(self.dateOfBirth)
dob_input.send_keys(dob_formatted)
print(f"[UnitedDH PreAuth] step1: DOB entered: {dob_formatted}")
except Exception as e:
print(f"[UnitedDH PreAuth] step1: Error entering DOB: {e}")
return "ERROR: step1 - Could not enter Date of Birth"
time.sleep(1)
# Dismiss any blocking overlays
try:
self.driver.execute_script("""
var dialogs = document.querySelectorAll('[role="dialog"], .cdk-overlay-container');
dialogs.forEach(function(d) { d.style.display = 'none'; });
""")
except Exception:
pass
# Select Payer: UnitedHealthcare Massachusetts
print("[UnitedDH PreAuth] step1: Selecting Payer...")
payer_selected = False
try:
payer_selectors = [
"//label[contains(text(),'Payer')]/following-sibling::ng-select",
"//label[contains(text(),'Payer')]/..//ng-select",
"//ng-select[contains(@placeholder,'Payer') or contains(@placeholder,'payer')]",
"//ng-select[.//input[contains(@placeholder,'Search by Payers')]]",
]
payer_ng_select = None
for sel in payer_selectors:
try:
elem = self.driver.find_element(By.XPATH, sel)
if elem.is_displayed():
payer_ng_select = elem
break
except Exception:
continue
if payer_ng_select:
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", payer_ng_select)
time.sleep(0.5)
payer_ng_select.click()
time.sleep(1)
search_input = payer_ng_select.find_element(By.XPATH,
".//input[contains(@type,'text') or contains(@role,'combobox')]")
search_input.clear()
search_input.send_keys("UnitedHealthcare Massachusetts")
print("[UnitedDH PreAuth] step1: Typed payer search text")
time.sleep(2)
search_input.send_keys(Keys.ENTER)
print("[UnitedDH PreAuth] step1: Pressed Enter to select Payer")
time.sleep(0.5)
payer_selected = True
else:
print("[UnitedDH PreAuth] step1: Could not find Payer ng-select element")
except Exception as e:
print(f"[UnitedDH PreAuth] step1: Payer selection error: {e}")
if not payer_selected:
print("[UnitedDH PreAuth] step1: WARNING - Could not select Payer")
time.sleep(1)
# Click Continue (Patient Info)
try:
continue_btn = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.XPATH, "//button[contains(text(),'Continue')]"))
)
continue_btn.click()
print("[UnitedDH PreAuth] step1: Clicked Continue (Patient Info)")
time.sleep(3)
error_result = self._check_for_error_dialog()
if error_result:
return error_result
except Exception as e:
return f"ERROR: step1 - Could not click Continue: {e}"
# Click Ok on Select Insurance popup
print("[UnitedDH PreAuth] step1: Checking for Select Insurance popup...")
try:
ok_btn = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.XPATH,
"//button[contains(@class,'btn-primary') and "
"(normalize-space(.)='Ok' or normalize-space(.)='OK' or normalize-space(.)='Okay')] | "
"//modal-container//button[normalize-space(.)='Ok' or normalize-space(.)='OK' or normalize-space(.)='Okay'] | "
"//div[contains(@class,'modal')]//button[normalize-space(.)='Ok' or normalize-space(.)='OK' or normalize-space(.)='Okay']"
))
)
try:
self.driver.execute_script("arguments[0].click();", ok_btn)
print("[UnitedDH PreAuth] step1: Clicked OK on Select Insurance popup (JS)")
except Exception:
ok_btn.click()
print("[UnitedDH PreAuth] step1: Clicked OK on Select Insurance popup (direct)")
try:
WebDriverWait(self.driver, 10).until(EC.staleness_of(ok_btn))
print("[UnitedDH PreAuth] step1: Select Insurance modal closed")
except TimeoutException:
print("[UnitedDH PreAuth] step1: Modal staleness timeout — continuing anyway")
WebDriverWait(self.driver, 5).until(
EC.presence_of_element_located((By.XPATH,
"//label[@for='treatmentLocation'] | //label[@for='paymentGroupId']"))
)
except TimeoutException:
print("[UnitedDH PreAuth] step1: Select Insurance popup not found — proceeding")
# Provider & Location page — select Treatment Location and Billing Entity, then Continue
print("[UnitedDH PreAuth] step1: Waiting for Provider & Location page...")
try:
WebDriverWait(self.driver, 20).until(
EC.visibility_of_element_located((By.XPATH, "//label[@for='paymentGroupId']"))
)
print("[UnitedDH PreAuth] step1: Selecting Treatment Location...")
location_selected = False
try:
location_ng = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.XPATH,
"//label[@for='treatmentLocation']/following-sibling::ng-select | "
"//label[@for='treatmentLocation']/..//ng-select"
))
)
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", location_ng)
arrow = location_ng.find_element(By.CSS_SELECTOR, ".ng-arrow-wrapper")
arrow.click()
first_option = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, ".ng-dropdown-panel .ng-option"))
)
option_text = first_option.text.strip()
first_option.click()
print(f"[UnitedDH PreAuth] step1: Selected Treatment Location: {option_text}")
location_selected = True
except Exception as e:
print(f"[UnitedDH PreAuth] step1: Treatment Location selection failed: {e}")
if not location_selected:
print("[UnitedDH PreAuth] step1: WARNING - Could not select Treatment Location")
print("[UnitedDH PreAuth] step1: Selecting Billing Entity...")
billing_selected = False
try:
billing_ng = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.XPATH,
"//label[@for='paymentGroupId']/following-sibling::ng-select | "
"//label[@for='paymentGroupId']/..//ng-select"
))
)
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", billing_ng)
arrow = billing_ng.find_element(By.CSS_SELECTOR, ".ng-arrow-wrapper")
arrow.click()
first_option = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((By.CSS_SELECTOR, ".ng-dropdown-panel .ng-option"))
)
option_text = first_option.text.strip()
first_option.click()
print(f"[UnitedDH PreAuth] step1: Selected Billing Entity: {option_text}")
billing_selected = True
except Exception as e:
print(f"[UnitedDH PreAuth] step1: Billing Entity selection failed: {e}")
if not billing_selected:
print("[UnitedDH PreAuth] step1: WARNING - Could not select Billing Entity")
continue_btn2 = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.XPATH, "//button[contains(text(),'Continue')]"))
)
continue_btn2.click()
print("[UnitedDH PreAuth] step1: Clicked Continue (Provider & Location) → Selected Patient page")
time.sleep(5)
except TimeoutException:
try:
results_elem = self.driver.find_element(By.XPATH,
"//*[contains(text(),'Selected Patient') or contains(@id,'patient-name') or contains(@id,'eligibility')]"
)
if results_elem.is_displayed():
print("[UnitedDH PreAuth] step1: Already on Selected Patient page")
return "OK"
except Exception:
pass
print("[UnitedDH PreAuth] step1: Continue not found on Provider & Location page — proceeding")
except Exception as e:
print(f"[UnitedDH PreAuth] step1: Error clicking Continue on Provider & Location page: {e}")
error_result = self._check_for_error_dialog()
if error_result:
return error_result
error_result = self._check_for_error_dialog()
if error_result:
return error_result
print("[UnitedDH PreAuth] step1: Patient search complete — on Selected Patient page")
return "OK"
except Exception as e:
return f"ERROR: step1_search_patient - {e}"
def step2_click_preauth_button(self):
"""
On the Selected Patient results page, click the Submit Pre-Authorization button.
Tries pre-auth-specific IDs first, then falls back to text matching.
"""
try:
print("[UnitedDH PreAuth] step2: Looking for Submit Pre-Authorization button...")
time.sleep(2)
# Try pre-auth specific button IDs first
preauth_selectors = [
(By.ID, "btnSubmitAuthorization"),
(By.ID, "btnSubmitPreAuth"),
(By.ID, "btnPreAuth"),
(By.ID, "btnPreAuthorization"),
(By.XPATH,
"//button[contains(normalize-space(.),'Submit Authorization') or "
"contains(normalize-space(.),'Pre-Auth') or "
"contains(normalize-space(.),'Pre Authorization') or "
"contains(normalize-space(.),'PreAuth') or "
"contains(normalize-space(.),'Prior Auth')]"
),
]
for by, selector in preauth_selectors:
try:
btn = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((by, selector))
)
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", btn)
time.sleep(0.5)
btn.click()
print(f"[UnitedDH PreAuth] step2: Clicked pre-auth button ({selector})")
time.sleep(4)
print(f"[UnitedDH PreAuth] step2 URL: {self.driver.current_url}")
return "OK"
except (TimeoutException, Exception):
continue
return "ERROR: step2 - Could not find Submit Pre-Authorization button on Selected Patient page"
except Exception as e:
return f"ERROR: step2_click_preauth_button - {e}"
def step3_continue_prefilled(self):
"""
Pre-auth page: member ID and DOB are pre-filled.
Select Payer by typing "UnitedHealthcare Massachusetts" + Enter, then click Continue.
"""
try:
print("[UnitedDH PreAuth] step3: Pre-auth page — selecting Payer then clicking Continue...")
WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((By.XPATH,
"//label[contains(text(),'Payer')] | //ng-select"
))
)
payer_selected = False
try:
payer_selectors = [
"//label[contains(text(),'Payer')]/following-sibling::ng-select",
"//label[contains(text(),'Payer')]/..//ng-select",
"//ng-select[contains(@placeholder,'Payer') or contains(@placeholder,'payer')]",
"//ng-select[.//input[contains(@placeholder,'Search by Payers')]]",
]
payer_ng_select = None
for sel in payer_selectors:
try:
elem = self.driver.find_element(By.XPATH, sel)
if elem.is_displayed():
payer_ng_select = elem
break
except Exception:
continue
if payer_ng_select:
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", payer_ng_select)
time.sleep(0.5)
payer_ng_select.click()
time.sleep(1)
search_input = payer_ng_select.find_element(By.XPATH,
".//input[contains(@type,'text') or contains(@role,'combobox')]")
search_input.clear()
search_input.send_keys("UnitedHealthcare Massachusetts")
print("[UnitedDH PreAuth] step3: Typed payer search text")
time.sleep(2)
search_input.send_keys(Keys.ENTER)
print("[UnitedDH PreAuth] step3: Pressed Enter to select Payer")
time.sleep(0.5)
payer_selected = True
else:
print("[UnitedDH PreAuth] step3: Could not find Payer ng-select element")
except Exception as e:
print(f"[UnitedDH PreAuth] step3: Payer selection error: {e}")
if not payer_selected:
print("[UnitedDH PreAuth] step3: WARNING - Could not select Payer")
time.sleep(1)
continue_btn = WebDriverWait(self.driver, 15).until(
EC.element_to_be_clickable((By.XPATH, "//button[contains(text(),'Continue')]"))
)
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", continue_btn)
continue_btn.click()
print("[UnitedDH PreAuth] step3: Clicked Continue")
time.sleep(4)
error_result = self._check_for_error_dialog()
if error_result:
return error_result
print(f"[UnitedDH PreAuth] step3 URL: {self.driver.current_url}")
return "OK"
except Exception as e:
return f"ERROR: step3_continue_prefilled - {e}"
def step4_select_insurance_ok(self):
"""Click Ok on the Select Insurance popup."""
try:
print("[UnitedDH PreAuth] step4: Waiting for Select Insurance popup...")
try:
ok_btn = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.XPATH,
"//button[@type='button' and contains(@class,'btn-primary') and "
"(normalize-space(text())='Ok' or normalize-space(text())='OK')] | "
"//modal-container//button[normalize-space(.)='Ok' or normalize-space(.)='OK'] | "
"//div[contains(@class,'modal')]//button[normalize-space(.)='Ok' or normalize-space(.)='OK']"
))
)
ActionChains(self.driver).move_to_element(ok_btn).pause(0.5).click().perform()
print("[UnitedDH PreAuth] step4: Clicked Ok on Select Insurance popup")
try:
WebDriverWait(self.driver, 8).until(EC.staleness_of(ok_btn))
print("[UnitedDH PreAuth] step4: Select Insurance modal closed")
except TimeoutException:
print("[UnitedDH PreAuth] step4: Modal staleness timeout — continuing anyway")
except TimeoutException:
print("[UnitedDH PreAuth] step4: Select Insurance popup not found — proceeding")
print(f"[UnitedDH PreAuth] step4 URL: {self.driver.current_url}")
return "OK"
except Exception as e:
return f"ERROR: step4_select_insurance_ok - {e}"
def step5_practitioner_continue(self):
"""Practitioner & Location page — click Continue only, no dropdown selections."""
try:
print("[UnitedDH PreAuth] step5: Waiting for Practitioner & Location page...")
try:
WebDriverWait(self.driver, 20).until(
EC.visibility_of_element_located((By.XPATH,
"//label[@for='treatmentLocation'] | //label[@for='paymentGroupId'] | "
"//label[@for='paymentGroup']"
))
)
print("[UnitedDH PreAuth] step5: Practitioner & Location page loaded")
except TimeoutException:
print("[UnitedDH PreAuth] step5: Practitioner & Location labels not found — trying Continue anyway")
continue_btn = WebDriverWait(self.driver, 15).until(
EC.element_to_be_clickable((By.XPATH,
"//button[contains(@class,'btn-primary') and contains(normalize-space(text()),'Continue')] | "
"//button[contains(normalize-space(text()),'Continue')]"
))
)
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", continue_btn)
continue_btn.click()
print("[UnitedDH PreAuth] step5: Clicked Continue — waiting for Code Entry page")
time.sleep(3)
try:
WebDriverWait(self.driver, 10).until(
EC.presence_of_element_located((By.ID, "procedureCode"))
)
print("[UnitedDH PreAuth] step5: Code Entry page loaded (procedureCode found)")
except TimeoutException:
print("[UnitedDH PreAuth] step5: procedureCode input not found — proceeding anyway")
print(f"[UnitedDH PreAuth] step5 URL: {self.driver.current_url}")
return "OK"
except Exception as e:
return f"ERROR: step5_practitioner_continue - {e}"
def step6_fill_preauth_form(self):
"""
Fill CDT codes, tooth, billed amount for each service line.
Same structure as the claim form step.
"""
try:
active_lines = [
ln for ln in self.serviceLines
if str(ln.get("procedureCode") or "").strip()
]
print(f"[UnitedDH PreAuth] step6: {len(active_lines)} service line(s)")
if not active_lines:
print("[UnitedDH PreAuth] step6: No service lines — skipping")
return "OK"
for idx, line in enumerate(active_lines):
code = str(line.get("procedureCode") or "").strip().upper()
billed = str(
line.get("totalBilled") or
line.get("billedAmount") or
line.get("fee") or ""
).strip()
tooth = str(line.get("toothNumber") or line.get("tooth_number") or "").strip()
surface = str(line.get("toothSurface") or line.get("tooth_surface") or "").strip().upper()
print(f"[UnitedDH PreAuth] step6: line {idx}: code={code}, billed={billed}, tooth={tooth}, surface={surface}")
# Click btnAddItem to open/activate the procedure row
try:
add_btn = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.ID, "btnAddItem"))
)
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", add_btn)
add_btn.click()
print(f"[UnitedDH PreAuth] step6: clicked btnAddItem to open row {idx}")
time.sleep(1)
except Exception as e:
print(f"[UnitedDH PreAuth] step6: could not click btnAddItem to open row {idx}: {e}")
# Type CDT code
try:
proc_input = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.ID, "procedureCode"))
)
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", proc_input)
self.driver.execute_script("arguments[0].click();", proc_input)
proc_input.send_keys(Keys.CONTROL + "a")
proc_input.send_keys(Keys.DELETE)
proc_input.send_keys(code)
print(f"[UnitedDH PreAuth] step6: typed procedure code: {code}")
time.sleep(0.5)
except Exception as e:
print(f"[UnitedDH PreAuth] step6: could not type procedure code for row {idx}: {e}")
continue
# Click btnAddItem to confirm code and reveal billed amount input
try:
add_btn = WebDriverWait(self.driver, 8).until(
EC.element_to_be_clickable((By.ID, "btnAddItem"))
)
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", add_btn)
add_btn.click()
print(f"[UnitedDH PreAuth] step6: clicked btnAddItem to reveal billedAmount for row {idx}")
time.sleep(1.5)
except Exception as e:
print(f"[UnitedDH PreAuth] step6: could not click btnAddItem for billed amount row {idx}: {e}")
continue
# Fill tooth number
if tooth:
try:
tooth_input = WebDriverWait(self.driver, 5).until(
EC.element_to_be_clickable((By.ID, "tooth"))
)
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", tooth_input)
tooth_input.click()
tooth_input.send_keys(Keys.CONTROL + "a")
tooth_input.send_keys(Keys.DELETE)
tooth_input.send_keys(tooth)
print(f"[UnitedDH PreAuth] step6: entered tooth number: {tooth}")
time.sleep(0.3)
except Exception as e:
print(f"[UnitedDH PreAuth] step6: could not fill tooth number for row {idx}: {e}")
# Click surface boxes
if surface:
try:
surface_boxes = self.driver.find_elements(By.XPATH,
"//div[contains(@class,'claim-add-item-group__box')]")
if surface_boxes:
for letter in surface:
if not letter.strip():
continue
try:
box = self.driver.find_element(By.XPATH,
f"//div[contains(@class,'claim-add-item-group__box') "
f"and not(contains(@class,'--disabled')) "
f"and @id='{letter}']"
)
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", box)
box.click()
print(f"[UnitedDH PreAuth] step6: clicked surface '{letter}'")
time.sleep(0.2)
except Exception:
print(f"[UnitedDH PreAuth] step6: surface '{letter}' not found or disabled")
else:
print(f"[UnitedDH PreAuth] step6: no surface boxes on page — skipping")
except Exception as e:
print(f"[UnitedDH PreAuth] step6: surface click error for row {idx}: {e}")
# Fill billed amount
if billed:
try:
billed_input = WebDriverWait(self.driver, 8).until(
EC.element_to_be_clickable((By.ID, "billedAmount"))
)
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", billed_input)
billed_input.click()
billed_input.send_keys(Keys.CONTROL + "a")
billed_input.send_keys(Keys.DELETE)
billed_input.send_keys(billed)
print(f"[UnitedDH PreAuth] step6: entered billed amount: {billed}")
time.sleep(0.5)
except Exception as e:
print(f"[UnitedDH PreAuth] step6: could not fill billed amount for row {idx}: {e}")
# Click the span "Add" button to confirm the row
try:
span_add = WebDriverWait(self.driver, 8).until(
EC.element_to_be_clickable((By.XPATH,
"//span[contains(@class,'ng-star-inserted') and normalize-space(text())='Add'] | "
"//button[normalize-space(text())='Add' and not(@id='btnAddItem')] | "
"//span[normalize-space(text())='Add']"
))
)
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", span_add)
span_add.click()
print(f"[UnitedDH PreAuth] step6: clicked span Add — row {idx} confirmed")
time.sleep(1)
except Exception as e:
print(f"[UnitedDH PreAuth] step6: could not click span Add for row {idx}: {e}")
# Other coverage: click "No" (second radio button)
try:
print("[UnitedDH PreAuth] step6: selecting 'No' for Other coverage")
radio_buttons = WebDriverWait(self.driver, 8).until(
lambda d: d.find_elements(By.XPATH, "//input[@type='radio']")
)
if len(radio_buttons) >= 2:
no_radio = radio_buttons[1]
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", no_radio)
no_radio.click()
print("[UnitedDH PreAuth] step6: Clicked 'No' (2nd radio) for Other coverage")
else:
print(f"[UnitedDH PreAuth] step6: Only {len(radio_buttons)} radio button(s) found — skipping")
time.sleep(0.5)
except Exception as e:
print(f"[UnitedDH PreAuth] step6: Could not click 'No' for Other coverage (non-fatal): {e}")
print("[UnitedDH PreAuth] step6: Done filling pre-auth form")
return "OK"
except Exception as e:
return f"ERROR: step6_fill_preauth_form - {e}"
def step7_attach_files(self):
"""
If there are claim files:
1. Click the fa-caret-up dropdown icon to reveal the Add Document button
2. Click id="upload-document"
3. Send the absolute file path to the file input
"""
try:
if not self.claimFiles:
print("[UnitedDH PreAuth] step7: No files to attach")
return "OK"
try:
caret = WebDriverWait(self.driver, 8).until(
EC.element_to_be_clickable((By.XPATH,
"//em[contains(@class,'fa-caret-up')] | "
"//i[contains(@class,'fa-caret-up')] | "
"//*[contains(@class,'fa') and contains(@class,'fa-caret-up')]"
))
)
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", caret)
caret.click()
print("[UnitedDH PreAuth] step7: Clicked caret-up to expand Attached Documents")
time.sleep(1)
except Exception as e:
print(f"[UnitedDH PreAuth] step7: Could not click caret (section may already be open): {e}")
attached = 0
for cf in self.claimFiles:
relative_path = cf.get("filePath") or ""
if not relative_path:
print(f"[UnitedDH PreAuth] step7: Skipping file with no filePath: {cf}")
continue
abs_path = os.path.normpath(os.path.join(_BACKEND_CWD, relative_path.lstrip("/")))
if not os.path.isfile(abs_path):
print(f"[UnitedDH PreAuth] step7: File not found on disk: {abs_path}")
continue
print(f"[UnitedDH PreAuth] step7: Attaching: {abs_path}")
try:
upload_btn = WebDriverWait(self.driver, 10).until(
EC.element_to_be_clickable((By.ID, "upload-document"))
)
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", upload_btn)
upload_btn.click()
time.sleep(1)
file_input = WebDriverWait(self.driver, 8).until(
EC.presence_of_element_located((By.XPATH, "//input[@type='file']"))
)
self.driver.execute_script("arguments[0].style.display='block';", file_input)
file_input.send_keys(abs_path)
time.sleep(1.5)
print(f"[UnitedDH PreAuth] step7: Attached: {os.path.basename(abs_path)}")
attached += 1
except Exception as e:
print(f"[UnitedDH PreAuth] step7: Could not attach {abs_path}: {e}")
print(f"[UnitedDH PreAuth] step7: Attached {attached}/{len(self.claimFiles)} file(s)")
return "OK"
except Exception as e:
return f"ERROR: step7_attach_files - {e}"
def step8_submit_preauth(self):
"""
Click Submit Pre-Authorization (or Submit Claim) on the form page, then click
"View Status and History" on the post-submit popup.
"""
try:
print(f"[UnitedDH PreAuth] step8: submitting pre-auth — URL: {self.driver.current_url}")
submit_btn = WebDriverWait(self.driver, 15).until(
EC.element_to_be_clickable((By.XPATH,
"//button[contains(normalize-space(.),'Submit Pre-Auth') or "
"contains(normalize-space(.),'Submit Pre Authorization') or "
"contains(normalize-space(.),'Submit Pre-Authorization') or "
"contains(normalize-space(.),'Submit Claim')] | "
"//button[contains(@class,'btn-primary') and ("
"contains(normalize-space(text()),'Submit Pre') or "
"contains(normalize-space(text()),'Submit Claim'))]"
))
)
self.driver.execute_script("arguments[0].scrollIntoView({block:'center'});", submit_btn)
time.sleep(0.5)
submit_btn.click()
print("[UnitedDH PreAuth] step8: Clicked Submit — waiting for post-submit popup")
time.sleep(3)
try:
view_btn = WebDriverWait(self.driver, 15).until(
EC.element_to_be_clickable((By.XPATH,
"//button[contains(normalize-space(.),'View Status and History')] | "
"//a[contains(normalize-space(.),'View Status and History')]"
))
)
view_btn.click()
print("[UnitedDH PreAuth] step8: Clicked 'View Status and History'")
time.sleep(3)
except TimeoutException:
print("[UnitedDH PreAuth] step8: Post-submit popup not found — proceeding to step9")
print(f"[UnitedDH PreAuth] step8: URL after popup: {self.driver.current_url}")
return "OK"
except Exception as e:
return f"ERROR: step8_submit_preauth - {e}"
def step9_save_confirmation_pdf(self):
"""
On the Status & History page, read the pre-auth/reference number from the
first row, then save the page as PDF.
"""
try:
print("[UnitedDH PreAuth] step9: waiting for Status & History page")
WebDriverWait(self.driver, 40).until(
lambda d: "status" in d.current_url.lower() or "history" in d.current_url.lower()
or d.find_elements(By.XPATH, "//td | //th[contains(text(),'Reference')]")
)
time.sleep(4)
print(f"[UnitedDH PreAuth] step9: Status & History URL: {self.driver.current_url}")
self.driver.refresh()
print("[UnitedDH PreAuth] step9: Page refreshed — waiting for table to reload")
WebDriverWait(self.driver, 30).until(
EC.presence_of_element_located((By.XPATH, "//table//tr[td]"))
)
time.sleep(4)
preauth_number = None
try:
first_ref = WebDriverWait(self.driver, 20).until(
EC.presence_of_element_located((By.XPATH,
"(//table//tr[not(th)]/td[2] | "
"//table//tr[td]/td[contains(normalize-space(.),'2026') or "
" contains(normalize-space(.),'2025')])[1]"
))
)
ref_text = first_ref.text.strip()
match = re.search(r'\b(\d{14})\b', ref_text)
if match:
preauth_number = match.group(1)
else:
match = re.search(r'\b(\d{10,})\b', ref_text)
if match:
preauth_number = match.group(1)
print(f"[UnitedDH PreAuth] step9: Pre-auth number: {preauth_number!r} (cell: {ref_text!r})")
except Exception as e:
print(f"[UnitedDH PreAuth] step9: Could not read first-row reference number: {e}")
try:
body_text = self.driver.find_element(By.TAG_NAME, "body").text
match = re.search(r'\b(\d{14})\b', body_text)
if match:
preauth_number = match.group(1)
print(f"[UnitedDH PreAuth] step9: Pre-auth number (body scan): {preauth_number}")
except Exception:
pass
shared_downloads = os.path.join(_SERVICE_DIR, "downloads")
os.makedirs(shared_downloads, exist_ok=True)
safe_member = "".join(c for c in str(self.memberId) if c.isalnum() or c in "-_.")
safe_preauth = ("_" + preauth_number[:20]) if preauth_number else ""
timestamp = time.strftime("%Y%m%d_%H%M%S")
pdf_filename = f"uniteddh_preauth_confirmation_{safe_member}{safe_preauth}_{timestamp}.pdf"
pdf_path = os.path.join(shared_downloads, pdf_filename)
try:
pdf_data = self.driver.execute_cdp_cmd("Page.printToPDF", {
"printBackground": True,
"paperWidth": 8.5,
"paperHeight": 11,
"marginTop": 0.4,
"marginBottom": 0.4,
"marginLeft": 0.4,
"marginRight": 0.4,
})
pdf_bytes = base64.b64decode(pdf_data["data"])
with open(pdf_path, "wb") as f:
f.write(pdf_bytes)
print(f"[UnitedDH PreAuth] step9: PDF saved: {pdf_path}")
except Exception as e:
print(f"[UnitedDH PreAuth] step9: PDF capture failed: {e}")
return f"ERROR: step9 PDF failed: {e}"
self._hide_browser()
return {
"status": "success",
"pdf_path": pdf_path,
"preAuthNumber": preauth_number,
}
except Exception as e:
return f"ERROR: step9_save_confirmation_pdf - {e}"
# ── Main workflow ──────────────────────────────────────────────────────────
def main_workflow(self, url):
try:
self.config_driver()
login_result = self.login(url)
print(f"[main_workflow] Login result: {login_result}")
if login_result == "OTP_REQUIRED":
return {"status": "otp_required", "message": "OTP required after login"}
if isinstance(login_result, str) and login_result.startswith("ERROR"):
return {"status": "error", "message": login_result}
step1_result = self.step1_search_patient()
print(f"[main_workflow] step1 result: {step1_result}")
if isinstance(step1_result, str) and step1_result.startswith("ERROR"):
return {"status": "error", "message": step1_result}
step2_result = self.step2_click_preauth_button()
print(f"[main_workflow] step2 result: {step2_result}")
if isinstance(step2_result, str) and step2_result.startswith("ERROR"):
return {"status": "error", "message": step2_result}
step3_result = self.step3_continue_prefilled()
print(f"[main_workflow] step3 result: {step3_result}")
if isinstance(step3_result, str) and step3_result.startswith("ERROR"):
return {"status": "error", "message": step3_result}
step4_result = self.step4_select_insurance_ok()
print(f"[main_workflow] step4 result: {step4_result}")
if isinstance(step4_result, str) and step4_result.startswith("ERROR"):
return {"status": "error", "message": step4_result}
step5_result = self.step5_practitioner_continue()
print(f"[main_workflow] step5 result: {step5_result}")
if isinstance(step5_result, str) and step5_result.startswith("ERROR"):
return {"status": "error", "message": step5_result}
step6_result = self.step6_fill_preauth_form()
print(f"[main_workflow] step6 result: {step6_result}")
if isinstance(step6_result, str) and step6_result.startswith("ERROR"):
return {"status": "error", "message": step6_result}
step7_result = self.step7_attach_files()
print(f"[main_workflow] step7 result: {step7_result}")
if isinstance(step7_result, str) and step7_result.startswith("ERROR"):
return {"status": "error", "message": step7_result}
step8_result = self.step8_submit_preauth()
print(f"[main_workflow] step8 result: {step8_result}")
if isinstance(step8_result, str) and step8_result.startswith("ERROR"):
return {"status": "error", "message": step8_result}
step9_result = self.step9_save_confirmation_pdf()
print(f"[main_workflow] step9 result: {step9_result}")
return step9_result
except Exception as e:
return {"status": "error", "message": str(e)}