diff --git a/apps/SeleniumService/agent.py b/apps/SeleniumService/agent.py index 3915aaab..3bbdc9a2 100755 --- a/apps/SeleniumService/agent.py +++ b/apps/SeleniumService/agent.py @@ -410,7 +410,7 @@ async def ddma_eligibility(request: Request): waiting_jobs += 1 # run in background (queued under semaphore) - asyncio.create_task(_ddma_worker_wrapper(sid, data, url="https://providers.deltadentalma.com/onboarding/start/")) + asyncio.create_task(_ddma_worker_wrapper(sid, data, url="https://www.deltadentalma.com/sign-in")) return {"status": "started", "session_id": sid} @@ -698,7 +698,7 @@ async def ddma_claim(request: Request): asyncio.create_task(_ddma_claim_worker_wrapper( sid, body, - url="https://providers.deltadentalma.com/onboarding/start/" + url="https://www.deltadentalma.com/sign-in" )) return {"status": "started", "session_id": sid} diff --git a/apps/SeleniumService/helpers_ddma_claim.py b/apps/SeleniumService/helpers_ddma_claim.py index 22f8b61d..d251abf5 100644 --- a/apps/SeleniumService/helpers_ddma_claim.py +++ b/apps/SeleniumService/helpers_ddma_claim.py @@ -130,17 +130,12 @@ async def start_ddma_claim_run(sid: str, data: dict, url: str): if otp_value: print(f"[DDMA Claim OTP poll {poll+1}] OTP received from app, typing it in...") try: - otp_input = driver.find_element(By.XPATH, - "//input[contains(@aria-label,'Verification') or " - "contains(@placeholder,'verification') or @type='tel' or " - "contains(@aria-lable,'Verification code') or " - "contains(@placeholder,'Enter your verification code')]" - ) + otp_input = driver.find_element(By.XPATH, "//input[@data-test-id='MfaCode']") otp_input.clear() otp_input.send_keys(otp_value) try: verify_btn = driver.find_element(By.XPATH, - "//button[@type='button' and @aria-label='Verify']") + "//button[@data-test-id='ConfirmSubmitButton']") verify_btn.click() except Exception: otp_input.send_keys("\n") @@ -167,17 +162,14 @@ async def start_ddma_claim_run(sid: str, data: dict, url: str): # Check if OTP input still visible (user hasn't finished) try: - driver.find_element(By.XPATH, - "//input[contains(@aria-label,'Verification') or " - "contains(@placeholder,'verification') or @type='tel']" - ) + driver.find_element(By.XPATH, "//input[@data-test-id='MfaCode']") print(f"[DDMA Claim OTP poll {poll+1}] OTP input still visible — waiting...") except Exception: # OTP input gone — try navigating to members - if "onboarding" in current_url or "start" in current_url: + if "sign-in" in current_url: print("[DDMA Claim OTP] OTP input gone, navigating to members page...") try: - driver.get("https://providers.deltadentalma.com/members") + driver.get("https://www.deltadentalma.com/private/provider/dashboard") await asyncio.sleep(2) except Exception: pass @@ -189,7 +181,7 @@ async def start_ddma_claim_run(sid: str, data: dict, url: str): # Final attempt — navigate directly to members page try: print("[DDMA Claim OTP] Final attempt — navigating to members page...") - driver.get("https://providers.deltadentalma.com/members") + driver.get("https://www.deltadentalma.com/private/provider/dashboard") await asyncio.sleep(3) WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]')) diff --git a/apps/SeleniumService/helpers_ddma_eligibility.py b/apps/SeleniumService/helpers_ddma_eligibility.py index e4748727..2ef53135 100755 --- a/apps/SeleniumService/helpers_ddma_eligibility.py +++ b/apps/SeleniumService/helpers_ddma_eligibility.py @@ -146,13 +146,11 @@ async def start_ddma_run(sid: str, data: dict, url: str): if otp_value: print(f"[OTP poll {poll+1}] OTP received from app, typing it in...") try: - otp_input = driver.find_element(By.XPATH, - "//input[contains(@aria-label,'Verification') or contains(@placeholder,'verification') or @type='tel' or contains(@aria-lable,'Verification code') or contains(@placeholder,'Enter your verification code')]" - ) + otp_input = driver.find_element(By.XPATH, "//input[@data-test-id='MfaCode']") otp_input.clear() otp_input.send_keys(otp_value) try: - verify_btn = driver.find_element(By.XPATH, "//button[@type='button' and @aria-label='Verify']") + verify_btn = driver.find_element(By.XPATH, "//button[@data-test-id='ConfirmSubmitButton']") verify_btn.click() except Exception: otp_input.send_keys("\n") @@ -169,7 +167,7 @@ async def start_ddma_run(sid: str, data: dict, url: str): if "member" in current_url or "dashboard" in current_url or "eligibility" in current_url: try: member_search = WebDriverWait(driver, 5).until( - EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]')) + EC.presence_of_element_located((By.XPATH, "//a[contains(@href, 'check-patient-benefits-search')]")) ) print("[OTP] Member search found — login successful!") login_success = True @@ -179,16 +177,14 @@ async def start_ddma_run(sid: str, data: dict, url: str): # Check if OTP input is still visible (user hasn't finished) try: - otp_input_elem = driver.find_element(By.XPATH, - "//input[contains(@aria-label,'Verification') or contains(@placeholder,'verification') or @type='tel']" - ) + otp_input_elem = driver.find_element(By.XPATH, "//input[@data-test-id='MfaCode']") print(f"[OTP poll {poll+1}] OTP input still visible - waiting...") except Exception: # OTP input gone — may mean login is completing; try members page - if "onboarding" in current_url or "start" in current_url: + if "sign-in" in current_url: print("[OTP] OTP input gone, trying to navigate to members page...") try: - driver.get("https://providers.deltadentalma.com/members") + driver.get("https://www.deltadentalma.com/private/provider/dashboard") await asyncio.sleep(2) except Exception: pass @@ -200,10 +196,10 @@ async def start_ddma_run(sid: str, data: dict, url: str): # Final check — navigate directly to members page try: print("[OTP] Final attempt - navigating to members page...") - driver.get("https://providers.deltadentalma.com/members") + driver.get("https://www.deltadentalma.com/private/provider/dashboard") await asyncio.sleep(3) member_search = WebDriverWait(driver, 10).until( - EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]')) + EC.presence_of_element_located((By.XPATH, "//a[contains(@href, 'check-patient-benefits-search')]")) ) print("[OTP] Member search found — login successful!") login_success = True diff --git a/apps/SeleniumService/selenium_DDMA_claimSubmitWorker.py b/apps/SeleniumService/selenium_DDMA_claimSubmitWorker.py index 30da312a..1a66cdff 100644 --- a/apps/SeleniumService/selenium_DDMA_claimSubmitWorker.py +++ b/apps/SeleniumService/selenium_DDMA_claimSubmitWorker.py @@ -13,7 +13,7 @@ from datetime import date from ddma_browser_manager import get_browser_manager -MEMBERS_URL = "https://providers.deltadentalma.com/members" +MEMBERS_URL = "https://www.deltadentalma.com/private/provider/dashboard" # Absolute path to the Backend app root (two levels up from this file's SeleniumService dir) _SERVICE_DIR = os.path.dirname(os.path.abspath(__file__)) @@ -61,7 +61,7 @@ class AutomationDDMAClaimSubmit: print("[DDMA Claim login] Forcing logout due to credential change...") browser_manager = get_browser_manager() try: - self.driver.get("https://providers.deltadentalma.com/") + self.driver.get("https://www.deltadentalma.com/") time.sleep(2) for selector in [ "//button[contains(text(), 'Log out') or contains(text(), 'Logout') or contains(text(), 'Sign out')]", @@ -100,10 +100,10 @@ class AutomationDDMAClaimSubmit: try: current_url = self.driver.current_url print(f"[DDMA Claim login] Current URL: {current_url}") - logged_in_patterns = ["member", "dashboard", "eligibility", "search", "patients"] + logged_in_patterns = ["member", "dashboard", "eligibility", "search", "patients", "private/provider"] is_logged_in_url = any(p in current_url.lower() for p in logged_in_patterns) - if is_logged_in_url and "onboarding" not in current_url.lower(): + if is_logged_in_url and "sign-in" not in current_url.lower(): # Navigate to members page to confirm if "member" not in current_url.lower(): try: @@ -132,7 +132,7 @@ class AutomationDDMAClaimSubmit: # Check if session redirected us straight to member search try: current_url = self.driver.current_url - if "onboarding" not in current_url.lower(): + if "sign-in" not in current_url.lower(): member_search = WebDriverWait(self.driver, 3).until( EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]')) ) @@ -165,8 +165,7 @@ class AutomationDDMAClaimSubmit: try: otp_candidate = WebDriverWait(self.driver, 10).until( EC.presence_of_element_located((By.XPATH, - "//input[contains(@aria-label,'Verification code') or " - "contains(@placeholder,'Enter your verification code')]" + "//input[@data-test-id='MfaCode']" )) ) if otp_candidate: @@ -191,7 +190,7 @@ class AutomationDDMAClaimSubmit: # Fill login form try: email_field = WebDriverWait(self.driver, 10).until( - EC.element_to_be_clickable((By.XPATH, "//input[@name='username' and @type='text']")) + EC.element_to_be_clickable((By.XPATH, "//input[@data-test-id='Username']")) ) except TimeoutException: return "ERROR: Login form not found" @@ -200,21 +199,13 @@ class AutomationDDMAClaimSubmit: email_field.send_keys(self.massddma_username) password_field = wait.until( - EC.presence_of_element_located((By.XPATH, "//input[@name='password' and @type='password']")) + EC.presence_of_element_located((By.XPATH, "//input[@data-test-id='Password']")) ) password_field.clear() password_field.send_keys(self.massddma_password) - try: - remember_me = wait.until(EC.element_to_be_clickable( - (By.XPATH, "//label[.//span[contains(text(),'Remember me')]]") - )) - remember_me.click() - except Exception: - print("[DDMA Claim login] Remember me checkbox not found (continuing)") - login_button = wait.until(EC.element_to_be_clickable( - (By.XPATH, "//button[@type='submit' and @aria-label='Sign in']") + (By.XPATH, "//button[@data-test-id='SigninSubmitButton']") )) login_button.click() @@ -225,8 +216,7 @@ class AutomationDDMAClaimSubmit: try: otp_candidate = WebDriverWait(self.driver, 30).until( EC.presence_of_element_located((By.XPATH, - "//input[contains(@aria-lable,'Verification code') or " - "contains(@placeholder,'Enter your verification code')]" + "//input[@data-test-id='MfaCode']" )) ) if otp_candidate: @@ -240,8 +230,8 @@ class AutomationDDMAClaimSubmit: return "SUCCESS" except Exception: pass - if "onboarding" in self.driver.current_url.lower() or "login" in self.driver.current_url.lower(): - return "ERROR: LOGIN FAILED: Still on login/onboarding page" + if "sign-in" in self.driver.current_url.lower(): + return "ERROR: LOGIN FAILED: Still on login page" print("[DDMA Claim login] Assuming login succeeded") return "SUCCESS" diff --git a/apps/SeleniumService/selenium_DDMA_eligibilityCheckWorker.py b/apps/SeleniumService/selenium_DDMA_eligibilityCheckWorker.py index 3d75a32d..d1a1b1fd 100755 --- a/apps/SeleniumService/selenium_DDMA_eligibilityCheckWorker.py +++ b/apps/SeleniumService/selenium_DDMA_eligibilityCheckWorker.py @@ -10,6 +10,7 @@ import time import os import re import base64 +import calendar from ddma_browser_manager import get_browser_manager @@ -45,7 +46,7 @@ class AutomationDeltaDentalMAEligibilityCheck: # Try to click logout button if visible try: - self.driver.get("https://providers.deltadentalma.com/") + self.driver.get("https://www.deltadentalma.com/") time.sleep(2) logout_selectors = [ @@ -99,27 +100,27 @@ class AutomationDeltaDentalMAEligibilityCheck: current_url = self.driver.current_url print(f"[login] Current URL: {current_url}") - logged_in_patterns = ["member", "dashboard", "eligibility", "search", "patients"] + logged_in_patterns = ["member", "dashboard", "eligibility", "search", "patients", "private/provider"] is_logged_in_url = any(pattern in current_url.lower() for pattern in logged_in_patterns) - if is_logged_in_url and "onboarding" not in current_url.lower(): + if is_logged_in_url and "sign-in" not in current_url.lower(): print("[login] Already on logged-in page - skipping login entirely") if "member" not in current_url.lower(): try: member_search = WebDriverWait(self.driver, 5).until( - EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]')) + EC.presence_of_element_located((By.XPATH, "//a[contains(@href, 'check-patient-benefits-search')]")) ) print("[login] Found member search input - returning ALREADY_LOGGED_IN") return "ALREADY_LOGGED_IN" except TimeoutException: - members_url = "https://providers.deltadentalma.com/members" + members_url = "https://www.deltadentalma.com/private/provider/dashboard" print(f"[login] Navigating to members page: {members_url}") self.driver.get(members_url) time.sleep(2) try: member_search = WebDriverWait(self.driver, 5).until( - EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]')) + EC.presence_of_element_located((By.XPATH, "//a[contains(@href, 'check-patient-benefits-search')]")) ) print("[login] Member search found - ALREADY_LOGGED_IN") return "ALREADY_LOGGED_IN" @@ -137,9 +138,9 @@ class AutomationDeltaDentalMAEligibilityCheck: current_url = self.driver.current_url print(f"[login] URL after navigation: {current_url}") - if "onboarding" not in current_url.lower(): + if "sign-in" not in current_url.lower(): member_search = WebDriverWait(self.driver, 3).until( - EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]')) + EC.presence_of_element_located((By.XPATH, "//a[contains(@href, 'check-patient-benefits-search')]")) ) if member_search: print("[login] Session valid - skipping login") @@ -172,7 +173,7 @@ class AutomationDeltaDentalMAEligibilityCheck: try: otp_candidate = WebDriverWait(self.driver, 10).until( EC.presence_of_element_located( - (By.XPATH, "//input[contains(@aria-lable,'Verification code') or contains(@placeholder,'Enter your verification code') or contains(@aria-label,'Verification code')]") + (By.XPATH, "//input[@data-test-id='MfaCode']") ) ) if otp_candidate: @@ -189,7 +190,7 @@ class AutomationDeltaDentalMAEligibilityCheck: time.sleep(2) try: member_search = WebDriverWait(self.driver, 5).until( - EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]')) + EC.presence_of_element_located((By.XPATH, "//a[contains(@href, 'check-patient-benefits-search')]")) ) if member_search: print("[login] Already authenticated after modal dismiss") @@ -200,30 +201,21 @@ class AutomationDeltaDentalMAEligibilityCheck: # Fill login form try: email_field = WebDriverWait(self.driver, 10).until( - EC.element_to_be_clickable((By.XPATH, "//input[@name='username' and @type='text']")) + EC.element_to_be_clickable((By.XPATH, "//input[@data-test-id='Username']")) ) except TimeoutException: print("[login] Could not find login form - page may have changed") return "ERROR: Login form not found" - email_field = wait.until(EC.presence_of_element_located((By.XPATH, "//input[@name='username' and @type='text']"))) + email_field = wait.until(EC.presence_of_element_located((By.XPATH, "//input[@data-test-id='Username']"))) email_field.clear() email_field.send_keys(self.massddma_username) - password_field = wait.until(EC.presence_of_element_located((By.XPATH, "//input[@name='password' and @type='password']"))) + password_field = wait.until(EC.presence_of_element_located((By.XPATH, "//input[@data-test-id='Password']"))) password_field.clear() password_field.send_keys(self.massddma_password) - # Remember me - try: - remember_me_checkbox = wait.until(EC.element_to_be_clickable( - (By.XPATH, "//label[.//span[contains(text(),'Remember me')]]") - )) - remember_me_checkbox.click() - except Exception: - print("[login] Remember me checkbox not found (continuing).") - - login_button = wait.until(EC.element_to_be_clickable((By.XPATH, "//button[@type='submit' and @aria-label='Sign in']"))) + login_button = wait.until(EC.element_to_be_clickable((By.XPATH, "//button[@data-test-id='SigninSubmitButton']"))) login_button.click() # Save credentials hash after login attempt @@ -234,7 +226,7 @@ class AutomationDeltaDentalMAEligibilityCheck: try: otp_candidate = WebDriverWait(self.driver, 30).until( EC.presence_of_element_located( - (By.XPATH, "//input[contains(@aria-lable,'Verification code') or contains(@placeholder,'Enter your verification code')]") + (By.XPATH, "//input[@data-test-id='MfaCode']") ) ) if otp_candidate: @@ -247,7 +239,7 @@ class AutomationDeltaDentalMAEligibilityCheck: current_url = self.driver.current_url.lower() if "member" in current_url or "dashboard" in current_url: member_search = WebDriverWait(self.driver, 5).until( - EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]')) + EC.presence_of_element_located((By.XPATH, "//a[contains(@href, 'check-patient-benefits-search')]")) ) print("[login] Login successful - now on member search page") return "SUCCESS" @@ -264,7 +256,7 @@ class AutomationDeltaDentalMAEligibilityCheck: except TimeoutException: pass - if "onboarding" in self.driver.current_url.lower() or "login" in self.driver.current_url.lower(): + if "sign-in" in self.driver.current_url.lower(): print("[login] Login failed - still on login page") return "ERROR:LOGIN FAILED: Still on login page" @@ -275,103 +267,66 @@ class AutomationDeltaDentalMAEligibilityCheck: print("[login] Exception during login:", e) return f"ERROR:LOGIN FAILED: {e}" - def _select_provider_dropdown(self): - """ - Click the provider dropdown on the member search page and select the option - matching self.providerName (case-insensitive). Falls back to the first option. - The button data-testid is 'member-search_provider_select-btn'. - """ - try: - short_wait = WebDriverWait(self.driver, 5) - try: - provider_btn = short_wait.until( - EC.element_to_be_clickable( - (By.XPATH, '//button[@data-testid="member-search_provider_select-btn"]') - ) - ) - except TimeoutException: - print("[DDMA step1] No provider dropdown found — skipping") - return - - provider_btn.click() - time.sleep(0.5) - - try: - WebDriverWait(self.driver, 5).until( - EC.presence_of_element_located((By.XPATH, "//*[@role='option']")) - ) - except TimeoutException: - print("[DDMA step1] Provider listbox did not open") - return - - options = self.driver.find_elements(By.XPATH, "//*[@role='option']") - print(f"[DDMA step1] Provider options: {[o.text.strip() for o in options]}") - - target = (self.providerName or "").strip().lower() - selected = False - - if target: - for opt in options: - if target in opt.text.lower(): - opt.click() - print(f"[DDMA step1] Selected provider: '{opt.text.strip()}'") - selected = True - break - - if not selected and options: - options[0].click() - print(f"[DDMA step1] No match for '{self.providerName}', selected first: '{options[0].text.strip()}'") - - time.sleep(0.3) - - except Exception as e: - print(f"[DDMA step1] Provider selection error (non-fatal): {e}") - def step1(self): - """Fill search form with all available fields (flexible search).""" + """Open the Group/Health Connector search page and search by Subscriber ID + Birthdate.""" wait = WebDriverWait(self.driver, 30) try: fields = [] if self.memberId: - fields.append(f"ID: {self.memberId}") - if self.firstName: - fields.append(f"FirstName: {self.firstName}") - if self.lastName: - fields.append(f"LastName: {self.lastName}") + fields.append(f"SubscriberID: {self.memberId}") if self.dateOfBirth: fields.append(f"DOB: {self.dateOfBirth}") print(f"[DDMA step1] Starting search with: {', '.join(fields)}") - # Wait for the search page to be fully loaded before interacting - wait.until(EC.presence_of_element_located( - (By.XPATH, '//input[@placeholder="Search by member ID"]') - )) - time.sleep(1) + search_page_url = "https://www.deltadentalma.com/private/provider/check-patient-benefits-search" - # Select provider from dropdown based on NPI settings - self._select_provider_dropdown() + # Try navigating straight to the search page first — avoids any click/visibility issues + print(f"[DDMA step1] Navigating directly to search page: {search_page_url}") + self.driver.get(search_page_url) + time.sleep(2) - def replace_with_sendkeys(el, value): - el.click() - el.send_keys(Keys.CONTROL, "a") - el.send_keys(Keys.BACKSPACE) - el.send_keys(value) + if "check-patient-benefits-search" not in self.driver.current_url.lower(): + # Direct deep-link didn't stick (e.g. bounced back to sign-in/dashboard) — + # fall back to clicking the Group/Health Connector link from the dashboard. + print(f"[DDMA step1] Direct navigation redirected to: {self.driver.current_url} — falling back to clicking the link") - # 1. Fill Member ID if provided - if self.memberId: + if "private/provider/dashboard" not in self.driver.current_url.lower(): + dashboard_url = "https://www.deltadentalma.com/private/provider/dashboard" + print(f"[DDMA step1] Navigating to dashboard: {dashboard_url}") + self.driver.get(dashboard_url) + time.sleep(2) + + link_xpath = "//a[contains(@href, 'check-patient-benefits-search')]" try: - member_id_input = wait.until(EC.presence_of_element_located( - (By.XPATH, '//input[@placeholder="Search by member ID"]') - )) - member_id_input.clear() - member_id_input.send_keys(self.memberId) - print(f"[DDMA step1] Entered Member ID: {self.memberId}") - time.sleep(0.2) - except Exception as e: - print(f"[DDMA step1] Warning: Could not fill Member ID: {e}") + connector_link = WebDriverWait(self.driver, 20).until( + EC.element_to_be_clickable((By.XPATH, link_xpath)) + ) + connector_link.click() + except TimeoutException: + # Element may exist but be hidden (e.g. a responsive duplicate) — try any match via JS click + candidates = self.driver.find_elements(By.XPATH, link_xpath) + print(f"[DDMA step1] element_to_be_clickable timed out; found {len(candidates)} matching link(s) in DOM") + if candidates: + try: + self.driver.execute_script("arguments[0].click();", candidates[0]) + except Exception as js_err: + print(f"[DDMA step1] JS click failed: {js_err}") + print(f"[DDMA step1] Current URL: {self.driver.current_url}") + return "ERROR: Could not open Group/Health Connector search page" + else: + print(f"[DDMA step1] No Group/Health Connector link found in DOM. Current URL: {self.driver.current_url}") + return "ERROR: Could not open Group/Health Connector search page" - # 2. Fill DOB if provided + try: + wait.until(lambda d: "check-patient-benefits-search" in d.current_url) + except TimeoutException: + print(f"[DDMA step1] Click did not navigate to search page. Current URL: {self.driver.current_url}") + return "ERROR: Could not open Group/Health Connector search page" + + print(f"[DDMA step1] On Group/Health Connector search page: {self.driver.current_url}") + + # Fill Birthdate (masked mm/dd/yyyy input — send digits only, mask inserts slashes) if self.dateOfBirth: try: dob_parts = self.dateOfBirth.split("-") @@ -379,54 +334,47 @@ class AutomationDeltaDentalMAEligibilityCheck: month = dob_parts[1].zfill(2) day = dob_parts[2].zfill(2) - dob_container = wait.until( - EC.presence_of_element_located( - (By.XPATH, "//div[@data-testid='member-search_date-of-birth']") - ) - ) - - month_elem = dob_container.find_element(By.XPATH, ".//span[@data-type='month' and @contenteditable='true']") - day_elem = dob_container.find_element(By.XPATH, ".//span[@data-type='day' and @contenteditable='true']") - year_elem = dob_container.find_element(By.XPATH, ".//span[@data-type='year' and @contenteditable='true']") - - replace_with_sendkeys(month_elem, month) - time.sleep(0.05) - replace_with_sendkeys(day_elem, day) - time.sleep(0.05) - replace_with_sendkeys(year_elem, year) - print(f"[DDMA step1] Filled DOB: {month}/{day}/{year}") + birthdate_input = wait.until(EC.element_to_be_clickable( + (By.XPATH, "//input[@data-test-id='InStateSearchBirthdate']") + )) + birthdate_input.click() + birthdate_input.send_keys(Keys.CONTROL, "a") + birthdate_input.send_keys(Keys.BACKSPACE) + birthdate_input.send_keys(f"{month}{day}{year}") + print(f"[DDMA step1] Filled Birthdate: {month}/{day}/{year}") except Exception as e: - print(f"[DDMA step1] Warning: Could not fill DOB: {e}") + print(f"[DDMA step1] Warning: Could not fill Birthdate: {e}") + + # Fill Subscriber ID (== member ID) + if self.memberId: + try: + subscriber_input = wait.until(EC.element_to_be_clickable( + (By.XPATH, "//input[@data-test-id='InStateSearchSubscriberId']") + )) + subscriber_input.clear() + subscriber_input.send_keys(self.memberId) + print(f"[DDMA step1] Filled Subscriber ID: {self.memberId}") + except Exception as e: + print(f"[DDMA step1] Warning: Could not fill Subscriber ID: {e}") time.sleep(0.3) # Click Search button - continue_btn = wait.until(EC.element_to_be_clickable( - (By.XPATH, '//button[@data-testid="member-search_search-button"]') + search_btn = wait.until(EC.element_to_be_clickable( + (By.XPATH, "//button[@data-test-id='InStateSearchButton']") )) - continue_btn.click() + search_btn.click() print("[DDMA step1] Clicked Search button") - # Wait for either results row or no-results message (up to 15s) + # Wait for the Members results table to render try: WebDriverWait(self.driver, 15).until( - EC.any_of( - EC.presence_of_element_located((By.XPATH, "//tbody//tr")), - EC.presence_of_element_located((By.XPATH, '//div[@data-testid="member-search-result-no-results"]')), - ) + EC.presence_of_element_located((By.XPATH, "//table//tbody//tr")) ) - time.sleep(3) + time.sleep(2) except TimeoutException: - pass # proceed and let step2 handle missing results - - # Check for no-results error - try: - error_msg = self.driver.find_element(By.XPATH, '//div[@data-testid="member-search-result-no-results"]') - if error_msg: - print("[DDMA step1] Error: No results found") - return "ERROR: INVALID SEARCH CRITERIA" - except Exception: - pass + print("[DDMA step1] Warning: No results table appeared within timeout") + return "ERROR: INVALID SEARCH CRITERIA" print("[DDMA step1] Search completed successfully") return "Success" @@ -455,99 +403,93 @@ class AutomationDeltaDentalMAEligibilityCheck: foundMemberId = self.memberId or "" patientName = "" - # Extract patient name from search results row - try: - first_row = self.driver.find_element(By.XPATH, "(//tbody//tr)[1]") - row_text = first_row.text.strip() - print(f"[DDMA step2] First row text: {row_text[:150]}...") + # The Members table lists the whole family sharing this subscriber ID — + # find the row matching this patient's DOB (falls back to last name, then first row). + rows = self.driver.find_elements(By.XPATH, "//table//tbody//tr") + print(f"[DDMA step2] Found {len(rows)} family member row(s)") - lines = row_text.split('\n') if row_text else [] - for line in lines: - line = line.strip() - if not line: - continue - if re.match(r"^[A-Za-z\s\-']{2,60}$", line): - patientName = line - print(f"[DDMA step2] Extracted patient name from row: '{patientName}'") + target_row = None + + if self.dateOfBirth and rows: + try: + dob_parts = self.dateOfBirth.split("-") + dob_year, dob_month, dob_day = dob_parts[0], int(dob_parts[1]), int(dob_parts[2]) + expected_dob = f"{calendar.month_abbr[dob_month]} {dob_day}, {dob_year}".lower() + for row in rows: + cells = row.find_elements(By.XPATH, "./td") + if len(cells) >= 3 and cells[2].text.strip().lower() == expected_dob: + target_row = row + break + except Exception as e: + print(f"[DDMA step2] DOB matching error (non-fatal): {e}") + + if target_row is None and self.lastName and rows: + for row in rows: + if self.lastName.strip().lower() in row.text.lower(): + target_row = row break - except Exception as e: - print(f"[DDMA step2] Error reading first row: {e}") + if target_row is None and rows: + target_row = rows[0] + print("[DDMA step2] No DOB/name match — defaulting to first family member row") + + if target_row is not None: + try: + cells = target_row.find_elements(By.XPATH, "./td") + if cells: + patientName = cells[0].text.strip() + print(f"[DDMA step2] Extracted patient name from row: '{patientName}'") + if len(cells) >= 2 and cells[1].text.strip(): + foundMemberId = cells[1].text.strip() + if len(cells) >= 7: + status_text = cells[6].text.strip().lower() + if "active" in status_text: + eligibilityText = "active" + elif "inactive" in status_text or "termed" in status_text: + eligibilityText = "inactive" + else: + eligibilityText = status_text + print(f"[DDMA step2] Found eligibility status: {eligibilityText}") + except Exception as e: + print(f"[DDMA step2] Error reading matched row cells: {e}") + + # Click into the member's detail page + try: + name_link = target_row.find_element(By.XPATH, ".//a") + name_link.click() + print(f"[DDMA step2] Clicked into detail page for: {patientName}") + except Exception as e: + print(f"[DDMA step2] Warning: Could not click patient name link: {e}") + else: + print("[DDMA step2] Warning: No family member rows found in search results") + + # Wait for the detail page to load (URL moves away from the search page) try: - short_wait = WebDriverWait(self.driver, 3) - status_link = short_wait.until(EC.presence_of_element_located(( - By.XPATH, - "(//tbody//tr)[1]//a[contains(@href, 'member-eligibility-search')]" - ))) - eligibilityText = status_link.text.strip().lower() - print(f"[DDMA step2] Found eligibility status: {eligibilityText}") + WebDriverWait(self.driver, 15).until( + lambda d: "check-patient-benefits-search" not in d.current_url + ) + print(f"[DDMA step2] Navigated to detail page: {self.driver.current_url}") except Exception: - try: - alt_status = self.driver.find_element(By.XPATH, "//*[contains(text(),'Active') or contains(text(),'Inactive') or contains(text(),'Eligible')]") - eligibilityText = alt_status.text.strip().lower() - if "active" in eligibilityText or "eligible" in eligibilityText: - eligibilityText = "active" - elif "inactive" in eligibilityText: - eligibilityText = "inactive" - print(f"[DDMA step2] Found eligibility via alternative: {eligibilityText}") - except Exception: - pass + print(f"[DDMA step2] Warning: Still on search page: {self.driver.current_url}") - # Find the member-details URL from the first row - print("[DDMA step2] Looking for patient detail link...") - detail_url = None - - link_selectors = [ - "(//table//tbody//tr)[1]//td[1]//a", - "(//tbody//tr)[1]//a[contains(@href, 'member-details')]", - "(//tbody//tr)[1]//a[contains(@href, 'member')]", - "//a[contains(@href, 'member-details')]", - ] - for selector in link_selectors: + # Wait for meaningful content on the detail page + for selector in [ + "//*[contains(text(),'Date of Birth') or contains(text(),'Address') or contains(text(),'Member ID')]", + "//table", "//h1", "//h2", + ]: try: - link_el = WebDriverWait(self.driver, 5).until( + WebDriverWait(self.driver, 15).until( EC.presence_of_element_located((By.XPATH, selector)) ) - href = link_el.get_attribute("href") - if href and "member-details" in href: - detail_url = href - print(f"[DDMA step2] Found detail URL: {href}") - break + print(f"[DDMA step2] Detail page content loaded: {selector}") + break except Exception: continue - if detail_url: - # Always navigate via driver.get() — this blocks until the new page loads, - # unlike click() which returns immediately and causes a race condition. - print(f"[DDMA step2] Navigating to detail page: {detail_url}") - self.driver.get(detail_url) + time.sleep(3) # Let JavaScript finish rendering all sections - # Confirm we actually landed on the detail page (not redirected away) - try: - WebDriverWait(self.driver, 15).until( - lambda d: "member-details" in d.current_url - ) - print(f"[DDMA step2] Confirmed on detail page: {self.driver.current_url}") - except Exception: - print(f"[DDMA step2] Warning: URL after navigation: {self.driver.current_url}") - - # Wait for meaningful content on the detail page - for selector in [ - "//*[contains(text(),'Date of Birth') or contains(text(),'Address') or contains(text(),'Member ID')]", - "//table", "//h1", "//h2", - ]: - try: - WebDriverWait(self.driver, 15).until( - EC.presence_of_element_located((By.XPATH, selector)) - ) - print(f"[DDMA step2] Detail page content loaded: {selector}") - break - except Exception: - continue - - time.sleep(3) # Let JavaScript finish rendering all sections - - # Get full page text as lines + # Fallback: refine patient name from the detail page text if row extraction failed + if not patientName: try: page_lines = self.driver.execute_script( "return document.body.innerText;" @@ -602,8 +544,6 @@ class AutomationDeltaDentalMAEligibilityCheck: break if patientName: break - else: - print("[DDMA step2] Warning: Could not find patient detail link") # Clean patient name — strip any remaining date artifacts if patientName: