fix: update DDMA eligibility check worker for new deltadentalma.com sign-in and search flow
Delta Dental MA moved their provider login from providers.deltadentalma.com/onboarding to www.deltadentalma.com/sign-in with new form field selectors, and moved eligibility search behind a "Group/Health Connector" search page keyed by subscriber ID + birthdate instead of the old member-search form. Updates login, OTP/MFA handling, and member search/selection across the eligibility and claim DDMA workers and their session helpers.
This commit is contained in:
@@ -410,7 +410,7 @@ async def ddma_eligibility(request: Request):
|
|||||||
waiting_jobs += 1
|
waiting_jobs += 1
|
||||||
|
|
||||||
# run in background (queued under semaphore)
|
# 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}
|
return {"status": "started", "session_id": sid}
|
||||||
|
|
||||||
@@ -698,7 +698,7 @@ async def ddma_claim(request: Request):
|
|||||||
|
|
||||||
asyncio.create_task(_ddma_claim_worker_wrapper(
|
asyncio.create_task(_ddma_claim_worker_wrapper(
|
||||||
sid, body,
|
sid, body,
|
||||||
url="https://providers.deltadentalma.com/onboarding/start/"
|
url="https://www.deltadentalma.com/sign-in"
|
||||||
))
|
))
|
||||||
|
|
||||||
return {"status": "started", "session_id": sid}
|
return {"status": "started", "session_id": sid}
|
||||||
|
|||||||
@@ -130,17 +130,12 @@ async def start_ddma_claim_run(sid: str, data: dict, url: str):
|
|||||||
if otp_value:
|
if otp_value:
|
||||||
print(f"[DDMA Claim OTP poll {poll+1}] OTP received from app, typing it in...")
|
print(f"[DDMA Claim OTP poll {poll+1}] OTP received from app, typing it in...")
|
||||||
try:
|
try:
|
||||||
otp_input = driver.find_element(By.XPATH,
|
otp_input = driver.find_element(By.XPATH, "//input[@data-test-id='MfaCode']")
|
||||||
"//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.clear()
|
otp_input.clear()
|
||||||
otp_input.send_keys(otp_value)
|
otp_input.send_keys(otp_value)
|
||||||
try:
|
try:
|
||||||
verify_btn = driver.find_element(By.XPATH,
|
verify_btn = driver.find_element(By.XPATH,
|
||||||
"//button[@type='button' and @aria-label='Verify']")
|
"//button[@data-test-id='ConfirmSubmitButton']")
|
||||||
verify_btn.click()
|
verify_btn.click()
|
||||||
except Exception:
|
except Exception:
|
||||||
otp_input.send_keys("\n")
|
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)
|
# Check if OTP input still visible (user hasn't finished)
|
||||||
try:
|
try:
|
||||||
driver.find_element(By.XPATH,
|
driver.find_element(By.XPATH, "//input[@data-test-id='MfaCode']")
|
||||||
"//input[contains(@aria-label,'Verification') or "
|
|
||||||
"contains(@placeholder,'verification') or @type='tel']"
|
|
||||||
)
|
|
||||||
print(f"[DDMA Claim OTP poll {poll+1}] OTP input still visible — waiting...")
|
print(f"[DDMA Claim OTP poll {poll+1}] OTP input still visible — waiting...")
|
||||||
except Exception:
|
except Exception:
|
||||||
# OTP input gone — try navigating to members
|
# 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...")
|
print("[DDMA Claim OTP] OTP input gone, navigating to members page...")
|
||||||
try:
|
try:
|
||||||
driver.get("https://providers.deltadentalma.com/members")
|
driver.get("https://www.deltadentalma.com/private/provider/dashboard")
|
||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -189,7 +181,7 @@ async def start_ddma_claim_run(sid: str, data: dict, url: str):
|
|||||||
# Final attempt — navigate directly to members page
|
# Final attempt — navigate directly to members page
|
||||||
try:
|
try:
|
||||||
print("[DDMA Claim OTP] Final attempt — navigating to members page...")
|
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)
|
await asyncio.sleep(3)
|
||||||
WebDriverWait(driver, 10).until(
|
WebDriverWait(driver, 10).until(
|
||||||
EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]'))
|
EC.presence_of_element_located((By.XPATH, '//input[@placeholder="Search by member ID"]'))
|
||||||
|
|||||||
@@ -146,13 +146,11 @@ async def start_ddma_run(sid: str, data: dict, url: str):
|
|||||||
if otp_value:
|
if otp_value:
|
||||||
print(f"[OTP poll {poll+1}] OTP received from app, typing it in...")
|
print(f"[OTP poll {poll+1}] OTP received from app, typing it in...")
|
||||||
try:
|
try:
|
||||||
otp_input = driver.find_element(By.XPATH,
|
otp_input = driver.find_element(By.XPATH, "//input[@data-test-id='MfaCode']")
|
||||||
"//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.clear()
|
otp_input.clear()
|
||||||
otp_input.send_keys(otp_value)
|
otp_input.send_keys(otp_value)
|
||||||
try:
|
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()
|
verify_btn.click()
|
||||||
except Exception:
|
except Exception:
|
||||||
otp_input.send_keys("\n")
|
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:
|
if "member" in current_url or "dashboard" in current_url or "eligibility" in current_url:
|
||||||
try:
|
try:
|
||||||
member_search = WebDriverWait(driver, 5).until(
|
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!")
|
print("[OTP] Member search found — login successful!")
|
||||||
login_success = True
|
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)
|
# Check if OTP input is still visible (user hasn't finished)
|
||||||
try:
|
try:
|
||||||
otp_input_elem = driver.find_element(By.XPATH,
|
otp_input_elem = driver.find_element(By.XPATH, "//input[@data-test-id='MfaCode']")
|
||||||
"//input[contains(@aria-label,'Verification') or contains(@placeholder,'verification') or @type='tel']"
|
|
||||||
)
|
|
||||||
print(f"[OTP poll {poll+1}] OTP input still visible - waiting...")
|
print(f"[OTP poll {poll+1}] OTP input still visible - waiting...")
|
||||||
except Exception:
|
except Exception:
|
||||||
# OTP input gone — may mean login is completing; try members page
|
# 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...")
|
print("[OTP] OTP input gone, trying to navigate to members page...")
|
||||||
try:
|
try:
|
||||||
driver.get("https://providers.deltadentalma.com/members")
|
driver.get("https://www.deltadentalma.com/private/provider/dashboard")
|
||||||
await asyncio.sleep(2)
|
await asyncio.sleep(2)
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -200,10 +196,10 @@ async def start_ddma_run(sid: str, data: dict, url: str):
|
|||||||
# Final check — navigate directly to members page
|
# Final check — navigate directly to members page
|
||||||
try:
|
try:
|
||||||
print("[OTP] Final attempt - navigating to members page...")
|
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)
|
await asyncio.sleep(3)
|
||||||
member_search = WebDriverWait(driver, 10).until(
|
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!")
|
print("[OTP] Member search found — login successful!")
|
||||||
login_success = True
|
login_success = True
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from datetime import date
|
|||||||
|
|
||||||
from ddma_browser_manager import get_browser_manager
|
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)
|
# 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__))
|
_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...")
|
print("[DDMA Claim login] Forcing logout due to credential change...")
|
||||||
browser_manager = get_browser_manager()
|
browser_manager = get_browser_manager()
|
||||||
try:
|
try:
|
||||||
self.driver.get("https://providers.deltadentalma.com/")
|
self.driver.get("https://www.deltadentalma.com/")
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
for selector in [
|
for selector in [
|
||||||
"//button[contains(text(), 'Log out') or contains(text(), 'Logout') or contains(text(), 'Sign out')]",
|
"//button[contains(text(), 'Log out') or contains(text(), 'Logout') or contains(text(), 'Sign out')]",
|
||||||
@@ -100,10 +100,10 @@ class AutomationDDMAClaimSubmit:
|
|||||||
try:
|
try:
|
||||||
current_url = self.driver.current_url
|
current_url = self.driver.current_url
|
||||||
print(f"[DDMA Claim login] Current URL: {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)
|
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
|
# Navigate to members page to confirm
|
||||||
if "member" not in current_url.lower():
|
if "member" not in current_url.lower():
|
||||||
try:
|
try:
|
||||||
@@ -132,7 +132,7 @@ class AutomationDDMAClaimSubmit:
|
|||||||
# Check if session redirected us straight to member search
|
# Check if session redirected us straight to member search
|
||||||
try:
|
try:
|
||||||
current_url = self.driver.current_url
|
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(
|
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, '//input[@placeholder="Search by member ID"]'))
|
||||||
)
|
)
|
||||||
@@ -165,8 +165,7 @@ class AutomationDDMAClaimSubmit:
|
|||||||
try:
|
try:
|
||||||
otp_candidate = WebDriverWait(self.driver, 10).until(
|
otp_candidate = WebDriverWait(self.driver, 10).until(
|
||||||
EC.presence_of_element_located((By.XPATH,
|
EC.presence_of_element_located((By.XPATH,
|
||||||
"//input[contains(@aria-label,'Verification code') or "
|
"//input[@data-test-id='MfaCode']"
|
||||||
"contains(@placeholder,'Enter your verification code')]"
|
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
if otp_candidate:
|
if otp_candidate:
|
||||||
@@ -191,7 +190,7 @@ class AutomationDDMAClaimSubmit:
|
|||||||
# Fill login form
|
# Fill login form
|
||||||
try:
|
try:
|
||||||
email_field = WebDriverWait(self.driver, 10).until(
|
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:
|
except TimeoutException:
|
||||||
return "ERROR: Login form not found"
|
return "ERROR: Login form not found"
|
||||||
@@ -200,21 +199,13 @@ class AutomationDDMAClaimSubmit:
|
|||||||
email_field.send_keys(self.massddma_username)
|
email_field.send_keys(self.massddma_username)
|
||||||
|
|
||||||
password_field = wait.until(
|
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.clear()
|
||||||
password_field.send_keys(self.massddma_password)
|
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(
|
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()
|
login_button.click()
|
||||||
|
|
||||||
@@ -225,8 +216,7 @@ class AutomationDDMAClaimSubmit:
|
|||||||
try:
|
try:
|
||||||
otp_candidate = WebDriverWait(self.driver, 30).until(
|
otp_candidate = WebDriverWait(self.driver, 30).until(
|
||||||
EC.presence_of_element_located((By.XPATH,
|
EC.presence_of_element_located((By.XPATH,
|
||||||
"//input[contains(@aria-lable,'Verification code') or "
|
"//input[@data-test-id='MfaCode']"
|
||||||
"contains(@placeholder,'Enter your verification code')]"
|
|
||||||
))
|
))
|
||||||
)
|
)
|
||||||
if otp_candidate:
|
if otp_candidate:
|
||||||
@@ -240,8 +230,8 @@ class AutomationDDMAClaimSubmit:
|
|||||||
return "SUCCESS"
|
return "SUCCESS"
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
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():
|
||||||
return "ERROR: LOGIN FAILED: Still on login/onboarding page"
|
return "ERROR: LOGIN FAILED: Still on login page"
|
||||||
print("[DDMA Claim login] Assuming login succeeded")
|
print("[DDMA Claim login] Assuming login succeeded")
|
||||||
return "SUCCESS"
|
return "SUCCESS"
|
||||||
|
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import time
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import base64
|
import base64
|
||||||
|
import calendar
|
||||||
|
|
||||||
from ddma_browser_manager import get_browser_manager
|
from ddma_browser_manager import get_browser_manager
|
||||||
|
|
||||||
@@ -45,7 +46,7 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
|||||||
|
|
||||||
# Try to click logout button if visible
|
# Try to click logout button if visible
|
||||||
try:
|
try:
|
||||||
self.driver.get("https://providers.deltadentalma.com/")
|
self.driver.get("https://www.deltadentalma.com/")
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
logout_selectors = [
|
logout_selectors = [
|
||||||
@@ -99,27 +100,27 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
|||||||
current_url = self.driver.current_url
|
current_url = self.driver.current_url
|
||||||
print(f"[login] Current URL: {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)
|
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")
|
print("[login] Already on logged-in page - skipping login entirely")
|
||||||
if "member" not in current_url.lower():
|
if "member" not in current_url.lower():
|
||||||
try:
|
try:
|
||||||
member_search = WebDriverWait(self.driver, 5).until(
|
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")
|
print("[login] Found member search input - returning ALREADY_LOGGED_IN")
|
||||||
return "ALREADY_LOGGED_IN"
|
return "ALREADY_LOGGED_IN"
|
||||||
except TimeoutException:
|
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}")
|
print(f"[login] Navigating to members page: {members_url}")
|
||||||
self.driver.get(members_url)
|
self.driver.get(members_url)
|
||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
member_search = WebDriverWait(self.driver, 5).until(
|
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")
|
print("[login] Member search found - ALREADY_LOGGED_IN")
|
||||||
return "ALREADY_LOGGED_IN"
|
return "ALREADY_LOGGED_IN"
|
||||||
@@ -137,9 +138,9 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
|||||||
current_url = self.driver.current_url
|
current_url = self.driver.current_url
|
||||||
print(f"[login] URL after navigation: {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(
|
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:
|
if member_search:
|
||||||
print("[login] Session valid - skipping login")
|
print("[login] Session valid - skipping login")
|
||||||
@@ -172,7 +173,7 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
|||||||
try:
|
try:
|
||||||
otp_candidate = WebDriverWait(self.driver, 10).until(
|
otp_candidate = WebDriverWait(self.driver, 10).until(
|
||||||
EC.presence_of_element_located(
|
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:
|
if otp_candidate:
|
||||||
@@ -189,7 +190,7 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
|||||||
time.sleep(2)
|
time.sleep(2)
|
||||||
try:
|
try:
|
||||||
member_search = WebDriverWait(self.driver, 5).until(
|
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:
|
if member_search:
|
||||||
print("[login] Already authenticated after modal dismiss")
|
print("[login] Already authenticated after modal dismiss")
|
||||||
@@ -200,30 +201,21 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
|||||||
# Fill login form
|
# Fill login form
|
||||||
try:
|
try:
|
||||||
email_field = WebDriverWait(self.driver, 10).until(
|
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:
|
except TimeoutException:
|
||||||
print("[login] Could not find login form - page may have changed")
|
print("[login] Could not find login form - page may have changed")
|
||||||
return "ERROR: Login form not found"
|
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.clear()
|
||||||
email_field.send_keys(self.massddma_username)
|
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.clear()
|
||||||
password_field.send_keys(self.massddma_password)
|
password_field.send_keys(self.massddma_password)
|
||||||
|
|
||||||
# Remember me
|
login_button = wait.until(EC.element_to_be_clickable((By.XPATH, "//button[@data-test-id='SigninSubmitButton']")))
|
||||||
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.click()
|
login_button.click()
|
||||||
|
|
||||||
# Save credentials hash after login attempt
|
# Save credentials hash after login attempt
|
||||||
@@ -234,7 +226,7 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
|||||||
try:
|
try:
|
||||||
otp_candidate = WebDriverWait(self.driver, 30).until(
|
otp_candidate = WebDriverWait(self.driver, 30).until(
|
||||||
EC.presence_of_element_located(
|
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:
|
if otp_candidate:
|
||||||
@@ -247,7 +239,7 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
|||||||
current_url = self.driver.current_url.lower()
|
current_url = self.driver.current_url.lower()
|
||||||
if "member" in current_url or "dashboard" in current_url:
|
if "member" in current_url or "dashboard" in current_url:
|
||||||
member_search = WebDriverWait(self.driver, 5).until(
|
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")
|
print("[login] Login successful - now on member search page")
|
||||||
return "SUCCESS"
|
return "SUCCESS"
|
||||||
@@ -264,7 +256,7 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
|||||||
except TimeoutException:
|
except TimeoutException:
|
||||||
pass
|
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")
|
print("[login] Login failed - still on login page")
|
||||||
return "ERROR: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)
|
print("[login] Exception during login:", e)
|
||||||
return f"ERROR:LOGIN FAILED: {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):
|
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)
|
wait = WebDriverWait(self.driver, 30)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
fields = []
|
fields = []
|
||||||
if self.memberId:
|
if self.memberId:
|
||||||
fields.append(f"ID: {self.memberId}")
|
fields.append(f"SubscriberID: {self.memberId}")
|
||||||
if self.firstName:
|
|
||||||
fields.append(f"FirstName: {self.firstName}")
|
|
||||||
if self.lastName:
|
|
||||||
fields.append(f"LastName: {self.lastName}")
|
|
||||||
if self.dateOfBirth:
|
if self.dateOfBirth:
|
||||||
fields.append(f"DOB: {self.dateOfBirth}")
|
fields.append(f"DOB: {self.dateOfBirth}")
|
||||||
print(f"[DDMA step1] Starting search with: {', '.join(fields)}")
|
print(f"[DDMA step1] Starting search with: {', '.join(fields)}")
|
||||||
|
|
||||||
# Wait for the search page to be fully loaded before interacting
|
search_page_url = "https://www.deltadentalma.com/private/provider/check-patient-benefits-search"
|
||||||
wait.until(EC.presence_of_element_located(
|
|
||||||
(By.XPATH, '//input[@placeholder="Search by member ID"]')
|
|
||||||
))
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
# Select provider from dropdown based on NPI settings
|
# Try navigating straight to the search page first — avoids any click/visibility issues
|
||||||
self._select_provider_dropdown()
|
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):
|
if "check-patient-benefits-search" not in self.driver.current_url.lower():
|
||||||
el.click()
|
# Direct deep-link didn't stick (e.g. bounced back to sign-in/dashboard) —
|
||||||
el.send_keys(Keys.CONTROL, "a")
|
# fall back to clicking the Group/Health Connector link from the dashboard.
|
||||||
el.send_keys(Keys.BACKSPACE)
|
print(f"[DDMA step1] Direct navigation redirected to: {self.driver.current_url} — falling back to clicking the link")
|
||||||
el.send_keys(value)
|
|
||||||
|
|
||||||
# 1. Fill Member ID if provided
|
if "private/provider/dashboard" not in self.driver.current_url.lower():
|
||||||
if self.memberId:
|
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:
|
try:
|
||||||
member_id_input = wait.until(EC.presence_of_element_located(
|
connector_link = WebDriverWait(self.driver, 20).until(
|
||||||
(By.XPATH, '//input[@placeholder="Search by member ID"]')
|
EC.element_to_be_clickable((By.XPATH, link_xpath))
|
||||||
))
|
)
|
||||||
member_id_input.clear()
|
connector_link.click()
|
||||||
member_id_input.send_keys(self.memberId)
|
except TimeoutException:
|
||||||
print(f"[DDMA step1] Entered Member ID: {self.memberId}")
|
# Element may exist but be hidden (e.g. a responsive duplicate) — try any match via JS click
|
||||||
time.sleep(0.2)
|
candidates = self.driver.find_elements(By.XPATH, link_xpath)
|
||||||
except Exception as e:
|
print(f"[DDMA step1] element_to_be_clickable timed out; found {len(candidates)} matching link(s) in DOM")
|
||||||
print(f"[DDMA step1] Warning: Could not fill Member ID: {e}")
|
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:
|
if self.dateOfBirth:
|
||||||
try:
|
try:
|
||||||
dob_parts = self.dateOfBirth.split("-")
|
dob_parts = self.dateOfBirth.split("-")
|
||||||
@@ -379,54 +334,47 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
|||||||
month = dob_parts[1].zfill(2)
|
month = dob_parts[1].zfill(2)
|
||||||
day = dob_parts[2].zfill(2)
|
day = dob_parts[2].zfill(2)
|
||||||
|
|
||||||
dob_container = wait.until(
|
birthdate_input = wait.until(EC.element_to_be_clickable(
|
||||||
EC.presence_of_element_located(
|
(By.XPATH, "//input[@data-test-id='InStateSearchBirthdate']")
|
||||||
(By.XPATH, "//div[@data-testid='member-search_date-of-birth']")
|
))
|
||||||
)
|
birthdate_input.click()
|
||||||
)
|
birthdate_input.send_keys(Keys.CONTROL, "a")
|
||||||
|
birthdate_input.send_keys(Keys.BACKSPACE)
|
||||||
month_elem = dob_container.find_element(By.XPATH, ".//span[@data-type='month' and @contenteditable='true']")
|
birthdate_input.send_keys(f"{month}{day}{year}")
|
||||||
day_elem = dob_container.find_element(By.XPATH, ".//span[@data-type='day' and @contenteditable='true']")
|
print(f"[DDMA step1] Filled Birthdate: {month}/{day}/{year}")
|
||||||
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}")
|
|
||||||
except Exception as e:
|
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)
|
time.sleep(0.3)
|
||||||
|
|
||||||
# Click Search button
|
# Click Search button
|
||||||
continue_btn = wait.until(EC.element_to_be_clickable(
|
search_btn = wait.until(EC.element_to_be_clickable(
|
||||||
(By.XPATH, '//button[@data-testid="member-search_search-button"]')
|
(By.XPATH, "//button[@data-test-id='InStateSearchButton']")
|
||||||
))
|
))
|
||||||
continue_btn.click()
|
search_btn.click()
|
||||||
print("[DDMA step1] Clicked Search button")
|
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:
|
try:
|
||||||
WebDriverWait(self.driver, 15).until(
|
WebDriverWait(self.driver, 15).until(
|
||||||
EC.any_of(
|
EC.presence_of_element_located((By.XPATH, "//table//tbody//tr"))
|
||||||
EC.presence_of_element_located((By.XPATH, "//tbody//tr")),
|
|
||||||
EC.presence_of_element_located((By.XPATH, '//div[@data-testid="member-search-result-no-results"]')),
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
time.sleep(3)
|
time.sleep(2)
|
||||||
except TimeoutException:
|
except TimeoutException:
|
||||||
pass # proceed and let step2 handle missing results
|
print("[DDMA step1] Warning: No results table appeared within timeout")
|
||||||
|
return "ERROR: INVALID SEARCH CRITERIA"
|
||||||
# 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] Search completed successfully")
|
print("[DDMA step1] Search completed successfully")
|
||||||
return "Success"
|
return "Success"
|
||||||
@@ -455,99 +403,93 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
|||||||
foundMemberId = self.memberId or ""
|
foundMemberId = self.memberId or ""
|
||||||
patientName = ""
|
patientName = ""
|
||||||
|
|
||||||
# Extract patient name from search results row
|
# The Members table lists the whole family sharing this subscriber ID —
|
||||||
try:
|
# find the row matching this patient's DOB (falls back to last name, then first row).
|
||||||
first_row = self.driver.find_element(By.XPATH, "(//tbody//tr)[1]")
|
rows = self.driver.find_elements(By.XPATH, "//table//tbody//tr")
|
||||||
row_text = first_row.text.strip()
|
print(f"[DDMA step2] Found {len(rows)} family member row(s)")
|
||||||
print(f"[DDMA step2] First row text: {row_text[:150]}...")
|
|
||||||
|
|
||||||
lines = row_text.split('\n') if row_text else []
|
target_row = None
|
||||||
for line in lines:
|
|
||||||
line = line.strip()
|
if self.dateOfBirth and rows:
|
||||||
if not line:
|
try:
|
||||||
continue
|
dob_parts = self.dateOfBirth.split("-")
|
||||||
if re.match(r"^[A-Za-z\s\-']{2,60}$", line):
|
dob_year, dob_month, dob_day = dob_parts[0], int(dob_parts[1]), int(dob_parts[2])
|
||||||
patientName = line
|
expected_dob = f"{calendar.month_abbr[dob_month]} {dob_day}, {dob_year}".lower()
|
||||||
print(f"[DDMA step2] Extracted patient name from row: '{patientName}'")
|
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
|
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:
|
try:
|
||||||
short_wait = WebDriverWait(self.driver, 3)
|
WebDriverWait(self.driver, 15).until(
|
||||||
status_link = short_wait.until(EC.presence_of_element_located((
|
lambda d: "check-patient-benefits-search" not in d.current_url
|
||||||
By.XPATH,
|
)
|
||||||
"(//tbody//tr)[1]//a[contains(@href, 'member-eligibility-search')]"
|
print(f"[DDMA step2] Navigated to detail page: {self.driver.current_url}")
|
||||||
)))
|
|
||||||
eligibilityText = status_link.text.strip().lower()
|
|
||||||
print(f"[DDMA step2] Found eligibility status: {eligibilityText}")
|
|
||||||
except Exception:
|
except Exception:
|
||||||
try:
|
print(f"[DDMA step2] Warning: Still on search page: {self.driver.current_url}")
|
||||||
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
|
|
||||||
|
|
||||||
# Find the member-details URL from the first row
|
# Wait for meaningful content on the detail page
|
||||||
print("[DDMA step2] Looking for patient detail link...")
|
for selector in [
|
||||||
detail_url = None
|
"//*[contains(text(),'Date of Birth') or contains(text(),'Address') or contains(text(),'Member ID')]",
|
||||||
|
"//table", "//h1", "//h2",
|
||||||
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:
|
|
||||||
try:
|
try:
|
||||||
link_el = WebDriverWait(self.driver, 5).until(
|
WebDriverWait(self.driver, 15).until(
|
||||||
EC.presence_of_element_located((By.XPATH, selector))
|
EC.presence_of_element_located((By.XPATH, selector))
|
||||||
)
|
)
|
||||||
href = link_el.get_attribute("href")
|
print(f"[DDMA step2] Detail page content loaded: {selector}")
|
||||||
if href and "member-details" in href:
|
break
|
||||||
detail_url = href
|
|
||||||
print(f"[DDMA step2] Found detail URL: {href}")
|
|
||||||
break
|
|
||||||
except Exception:
|
except Exception:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if detail_url:
|
time.sleep(3) # Let JavaScript finish rendering all sections
|
||||||
# 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)
|
|
||||||
|
|
||||||
# Confirm we actually landed on the detail page (not redirected away)
|
# Fallback: refine patient name from the detail page text if row extraction failed
|
||||||
try:
|
if not patientName:
|
||||||
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
|
|
||||||
try:
|
try:
|
||||||
page_lines = self.driver.execute_script(
|
page_lines = self.driver.execute_script(
|
||||||
"return document.body.innerText;"
|
"return document.body.innerText;"
|
||||||
@@ -602,8 +544,6 @@ class AutomationDeltaDentalMAEligibilityCheck:
|
|||||||
break
|
break
|
||||||
if patientName:
|
if patientName:
|
||||||
break
|
break
|
||||||
else:
|
|
||||||
print("[DDMA step2] Warning: Could not find patient detail link")
|
|
||||||
|
|
||||||
# Clean patient name — strip any remaining date artifacts
|
# Clean patient name — strip any remaining date artifacts
|
||||||
if patientName:
|
if patientName:
|
||||||
|
|||||||
Reference in New Issue
Block a user