From 7284598c9707b1220306ca707363e8647c35d308 Mon Sep 17 00:00:00 2001 From: Gitead Date: Tue, 19 May 2026 23:05:48 -0400 Subject: [PATCH] fix: improve eligibility checks for MassHealth and DentaQuest - MassHealth: detect SSO/portal maintenance page and return clear error instead of cryptic step1 timeout; wait for SSO redirect to complete before running step1; add modal dismissal and failure screenshot/logging - DentaQuest: detect maintenance page in login and step1; search by member ID + DOB only (remove first/last name to prevent stale data from previous patient being submitted) Co-Authored-By: Claude Sonnet 4.6 --- ...enium_DentaQuest_eligibilityCheckWorker.py | 56 +++++++------- .../selenium_eligibilityCheckWorker.py | 75 ++++++++++++++++++- 2 files changed, 98 insertions(+), 33 deletions(-) diff --git a/apps/SeleniumService/selenium_DentaQuest_eligibilityCheckWorker.py b/apps/SeleniumService/selenium_DentaQuest_eligibilityCheckWorker.py index f89f63c7..145ff768 100644 --- a/apps/SeleniumService/selenium_DentaQuest_eligibilityCheckWorker.py +++ b/apps/SeleniumService/selenium_DentaQuest_eligibilityCheckWorker.py @@ -82,6 +82,21 @@ class AutomationDentaQuestEligibilityCheck: print(f"[DentaQuest login] Error during forced logout: {e}") return False + def _is_maintenance_page(self) -> bool: + """Return True if the current page is DentaQuest's maintenance/capacity error page.""" + try: + body = self.driver.find_element(By.TAG_NAME, "body").text + markers = [ + "temporarily unable to service", + "maintenance downtime", + "capacity problems", + "Please try again later", + ] + body_lower = body.lower() + return any(m.lower() in body_lower for m in markers) + except Exception: + return False + def login(self, url): wait = WebDriverWait(self.driver, 30) browser_manager = get_browser_manager() @@ -114,7 +129,11 @@ class AutomationDentaQuestEligibilityCheck: # Navigate to login URL self.driver.get(url) time.sleep(3) - + + if self._is_maintenance_page(): + print("[DentaQuest login] Maintenance page detected") + return "ERROR: DentaQuest server is temporarily unavailable (maintenance). Please try again later." + current_url = self.driver.current_url print(f"[DentaQuest login] After navigation URL: {current_url}") @@ -264,7 +283,11 @@ class AutomationDentaQuestEligibilityCheck: fields.append(f"LastName: {self.lastName}") fields.append(f"DOB: {self.dateOfBirth}") print(f"[DentaQuest step1] Starting member search with: {', '.join(fields)}") - + + if self._is_maintenance_page(): + print("[DentaQuest step1] Maintenance page detected") + return "ERROR: DentaQuest server is temporarily unavailable (maintenance). Please try again later." + # Wait for page to be ready time.sleep(2) @@ -346,8 +369,7 @@ class AutomationDentaQuestEligibilityCheck: fill_date_by_testid("member-search_date-of-birth", dob_month, dob_day, dob_year, "Date of Birth") time.sleep(0.3) - # 3. Fill ALL available search fields (flexible search) - # Fill Member ID if provided + # 3. Fill Member ID only (search by member ID + DOB only) if self.memberId: try: member_id_input = wait.until(EC.presence_of_element_located( @@ -359,32 +381,6 @@ class AutomationDentaQuestEligibilityCheck: time.sleep(0.2) except Exception as e: print(f"[DentaQuest step1] Warning: Could not fill member ID: {e}") - - # Fill First Name if provided - if self.firstName: - try: - first_name_input = wait.until(EC.presence_of_element_located( - (By.XPATH, '//input[@placeholder="First name - 1 char minimum" or contains(@placeholder,"first name") or contains(@name,"firstName") or contains(@id,"firstName")]') - )) - first_name_input.clear() - first_name_input.send_keys(self.firstName) - print(f"[DentaQuest step1] Entered first name: {self.firstName}") - time.sleep(0.2) - except Exception as e: - print(f"[DentaQuest step1] Warning: Could not fill first name: {e}") - - # Fill Last Name if provided - if self.lastName: - try: - last_name_input = wait.until(EC.presence_of_element_located( - (By.XPATH, '//input[@placeholder="Last name - 2 char minimum" or contains(@placeholder,"last name") or contains(@name,"lastName") or contains(@id,"lastName")]') - )) - last_name_input.clear() - last_name_input.send_keys(self.lastName) - print(f"[DentaQuest step1] Entered last name: {self.lastName}") - time.sleep(0.2) - except Exception as e: - print(f"[DentaQuest step1] Warning: Could not fill last name: {e}") time.sleep(0.3) diff --git a/apps/SeleniumService/selenium_eligibilityCheckWorker.py b/apps/SeleniumService/selenium_eligibilityCheckWorker.py index 4510739f..ce651dbc 100755 --- a/apps/SeleniumService/selenium_eligibilityCheckWorker.py +++ b/apps/SeleniumService/selenium_eligibilityCheckWorker.py @@ -30,8 +30,8 @@ class AutomationMassHealthEligibilityCheck: year, month, day = parts self.dateOfBirth = f"{month.zfill(2)}{day.zfill(2)}{year}" - self.download_dir = os.path.abspath("downloads") - os.makedirs(self.download_dir, exist_ok=True) + self.download_dir = os.path.join(os.path.dirname(os.path.abspath(__file__)), "downloads") + os.makedirs(self.download_dir, exist_ok=True) def config_driver(self): options = webdriver.ChromeOptions() @@ -51,10 +51,30 @@ class AutomationMassHealthEligibilityCheck: driver = webdriver.Chrome(service=s, options=options) self.driver = driver + def _is_maintenance_page(self) -> bool: + """Return True if the current page is a server maintenance/capacity error page.""" + try: + body = self.driver.find_element(By.TAG_NAME, "body").text + markers = [ + "temporarily unable to service", + "maintenance downtime", + "capacity problems", + "Please try again later", + ] + body_lower = body.lower() + return any(m.lower() in body_lower for m in markers) + except Exception: + return False + def login(self): wait = WebDriverWait(self.driver, 30) try: + # Check immediately if the portal is showing a maintenance page + if self._is_maintenance_page(): + print("[login] Maintenance page detected on initial load") + return "ERROR: MassHealth portal is temporarily unavailable (maintenance). Please try again later." + # Step 1: Click the SIGN IN button on the initial page signin_button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "a.btn.btn-block.btn-primary[href='https://connectsso.masshealth-dental.org/mhprovider/index.html']"))) signin_button.click() @@ -76,6 +96,23 @@ class AutomationMassHealthEligibilityCheck: login_button = wait.until(EC.element_to_be_clickable((By.CSS_SELECTOR, "input[type='submit'][name='submit'][value='Login']"))) login_button.click() + # Wait for SSO redirect to complete and land on the MassHealth portal. + # Must check the hostname only — the SSO URL itself contains "provider.masshealth-dental.org" + # as a query parameter, so a plain substring check would match too early. + print("[login] Waiting for SSO redirect to provider.masshealth-dental.org ...") + try: + WebDriverWait(self.driver, 30).until( + lambda d: d.current_url.startswith("https://provider.masshealth-dental.org") + ) + print(f"[login] Redirect complete. URL: {self.driver.current_url}") + except TimeoutException: + print(f"[login] Redirect timeout. Still on: {self.driver.current_url}") + return "ERROR: Login redirect timed out — check credentials or portal availability." + + if self._is_maintenance_page(): + print("[login] Maintenance page detected after login submission") + return "ERROR: MassHealth portal is temporarily unavailable (maintenance). Please try again later." + return "Success" except Exception as e: @@ -90,6 +127,26 @@ class AutomationMassHealthEligibilityCheck: print(f"[step1] current URL after login: {self.driver.current_url}") print(f"[step1] page title: {self.driver.title}") + if self._is_maintenance_page(): + print("[step1] Maintenance page detected") + return "ERROR: MassHealth portal is temporarily unavailable (maintenance). Please try again later." + + # Dismiss any post-login modal/alert dialogs (session warnings, notices, etc.) + for btn_xpath in [ + "//button[contains(text(),'OK') or contains(text(),'Ok') or contains(text(),'ok')]", + "//button[contains(text(),'Continue') or contains(text(),'Accept') or contains(text(),'Close')]", + "//button[contains(text(),'Dismiss') or contains(text(),'Got it')]", + "//a[contains(@class,'close') or @data-dismiss='modal']", + ]: + try: + btn = WebDriverWait(self.driver, 3).until(EC.element_to_be_clickable((By.XPATH, btn_xpath))) + btn.click() + print(f"[step1] Dismissed modal via: {btn_xpath}") + time.sleep(1) + break + except Exception: + pass + substep = "patient_management" patient_mgmt = wait.until( EC.presence_of_element_located( @@ -162,7 +219,19 @@ class AutomationMassHealthEligibilityCheck: return "Success" except Exception as e: print(f"[step1] FAILED at substep='{substep}': {e}") - print(f"[step1] URL at failure: {self.driver.current_url}") + try: + print(f"[step1] URL at failure: {self.driver.current_url}") + print(f"[step1] Page title: {self.driver.title}") + body_text = self.driver.find_element(By.TAG_NAME, "body").text + print(f"[step1] Page body (first 500 chars): {body_text[:500]}") + except Exception: + pass + try: + ss_path = os.path.join(self.download_dir, f"step1_failure_{substep}_{int(time.time())}.png") + self.driver.save_screenshot(ss_path) + print(f"[step1] Screenshot saved: {ss_path}") + except Exception as ss_err: + print(f"[step1] Could not save screenshot: {ss_err}") return f"ERROR:STEP1:{substep}" def _cell_text(self, cell):