diff --git a/apps/Backend/src/queue/processors/ddmaEligibilityProcessor.ts b/apps/Backend/src/queue/processors/ddmaEligibilityProcessor.ts index b316101c..4fe6de1d 100644 --- a/apps/Backend/src/queue/processors/ddmaEligibilityProcessor.ts +++ b/apps/Backend/src/queue/processors/ddmaEligibilityProcessor.ts @@ -138,8 +138,9 @@ async function processDdmaResult( // 4) Determine and update eligibility status + insurance provider name const eligStatus = (seleniumResult?.eligibility ?? "").toLowerCase(); - const newStatus = - eligStatus === "active" || eligStatus === "y" ? "ACTIVE" : "INACTIVE"; + let newStatus = "UNKNOWN"; + if (eligStatus === "active" || eligStatus === "y") newStatus = "ACTIVE"; + else if (eligStatus === "inactive" || eligStatus === "n" || eligStatus === "termed") newStatus = "INACTIVE"; await storage.updatePatient(patient.id, { status: newStatus, insuranceProvider: "Delta Dental MA", diff --git a/apps/SeleniumService/selenium_DDMA_eligibilityCheckWorker.py b/apps/SeleniumService/selenium_DDMA_eligibilityCheckWorker.py index d1a1b1fd..2f4f5479 100755 --- a/apps/SeleniumService/selenium_DDMA_eligibilityCheckWorker.py +++ b/apps/SeleniumService/selenium_DDMA_eligibilityCheckWorker.py @@ -366,16 +366,26 @@ class AutomationDeltaDentalMAEligibilityCheck: search_btn.click() print("[DDMA step1] Clicked Search button") - # Wait for the Members results table to render + # Wait for either the Members results table to render, or (when the + # Subscriber ID + DOB match exactly one family member) DDMA to skip + # the results list and navigate straight to the patient detail page. + # Note: the detail page URL is nested UNDER the search page path + # (".../check-patient-benefits-search/check-patient-benefit-details"), + # so "check-patient-benefits-search" alone can't distinguish them — + # only the detail page's own "-benefit-details" segment can. try: WebDriverWait(self.driver, 15).until( - EC.presence_of_element_located((By.XPATH, "//table//tbody//tr")) + lambda d: d.find_elements(By.XPATH, "//table//tbody//tr") + or "check-patient-benefit-details" in d.current_url.lower() ) time.sleep(2) except TimeoutException: print("[DDMA step1] Warning: No results table appeared within timeout") return "ERROR: INVALID SEARCH CRITERIA" + if "check-patient-benefit-details" in self.driver.current_url.lower(): + print(f"[DDMA step1] Search navigated directly to detail page: {self.driver.current_url}") + print("[DDMA step1] Search completed successfully") return "Success" @@ -390,26 +400,40 @@ class AutomationDeltaDentalMAEligibilityCheck: try: import re - # Wait for results table, then pause for full render - try: - WebDriverWait(self.driver, 10).until( - EC.presence_of_element_located((By.XPATH, "//tbody//tr")) - ) - time.sleep(2) # Let the row content fully render after table appears - except TimeoutException: - print("[DDMA step2] Warning: Results table not found within timeout") - eligibilityText = "unknown" foundMemberId = self.memberId or "" - patientName = "" + # If the caller already knows this patient (e.g. an existing patient record + # being rechecked), use that name directly — no need to scrape/parse it off + # the DDMA page, only the eligibility status is actually needed. + patientName = f"{self.firstName} {self.lastName}".strip() if self.firstName and self.lastName else "" + known_patient_name = bool(patientName) - # 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)") + # DDMA now sometimes skips the family-members results table entirely and + # navigates straight to the single-patient detail page (when Subscriber ID + + # DOB match exactly one person). In that case there's no members table to + # parse here — skip straight to the detail-page fallback extraction below. + already_on_detail_page = "check-patient-benefit-details" in self.driver.current_url.lower() + rows = [] target_row = None + if not already_on_detail_page: + # Wait for results table, then pause for full render + try: + WebDriverWait(self.driver, 10).until( + EC.presence_of_element_located((By.XPATH, "//tbody//tr")) + ) + time.sleep(2) # Let the row content fully render after table appears + except TimeoutException: + print("[DDMA step2] Warning: Results table not found within timeout") + + # 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)") + else: + print(f"[DDMA step2] Already on detail page: {self.driver.current_url}") + if self.dateOfBirth and rows: try: dob_parts = self.dateOfBirth.split("-") @@ -436,17 +460,17 @@ class AutomationDeltaDentalMAEligibilityCheck: if target_row is not None: try: cells = target_row.find_elements(By.XPATH, "./td") - if cells: + if cells and not known_patient_name: 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: + if "inactive" in status_text or "termed" in status_text: eligibilityText = "inactive" + elif "active" in status_text: + eligibilityText = "active" else: eligibilityText = status_text print(f"[DDMA step2] Found eligibility status: {eligibilityText}") @@ -463,10 +487,10 @@ class AutomationDeltaDentalMAEligibilityCheck: 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) + # Wait for the detail page to load (URL gains the "-benefit-details" segment) try: WebDriverWait(self.driver, 15).until( - lambda d: "check-patient-benefits-search" not in d.current_url + lambda d: "check-patient-benefit-details" in d.current_url.lower() ) print(f"[DDMA step2] Navigated to detail page: {self.driver.current_url}") except Exception: @@ -522,11 +546,13 @@ class AutomationDeltaDentalMAEligibilityCheck: # forward past accessibility text to the first plausible name value. for i, line in enumerate(page_lines): # Case 1: "Name : Value" or "Name: Value" on the same line + # (DDMA's detail page labels this "Subscriber name:", so that + # prefix must be accepted rather than excluded) same_line = re.match( - r'^(?:member\s+)?name\s*[:\-]\s*(.+)$', line, re.IGNORECASE + r'^(?:member\s+|subscriber\s+)?name\s*[:\-]\s*(.+)$', line, re.IGNORECASE ) if same_line and not re.search( - r'(provider|group|subscriber|plan)\s+name', line, re.IGNORECASE + r'(provider|group|plan)\s+name', line, re.IGNORECASE ): candidate = same_line.group(1).strip() if looks_like_name(candidate): @@ -535,7 +561,7 @@ class AutomationDeltaDentalMAEligibilityCheck: break # Case 2: "Name" or "Name:" alone on a line — scan forward for value - elif re.match(r'^(?:member\s+)?name\s*:?\s*$', line, re.IGNORECASE): + elif re.match(r'^(?:member\s+|subscriber\s+)?name\s*:?\s*$', line, re.IGNORECASE): for j in range(i + 1, min(i + 6, len(page_lines))): candidate = page_lines[j].strip() if looks_like_name(candidate): @@ -554,6 +580,27 @@ class AutomationDeltaDentalMAEligibilityCheck: if not patientName: print("[DDMA step2] Could not extract patient name from detail page") + # Fallback: refine eligibility status from the detail page's "Coverage is:" + # label if the family-table row extraction didn't set it (e.g. DDMA + # navigated straight to the detail page, skipping the members table). + if eligibilityText == "unknown": + try: + page_text = self.driver.execute_script("return document.body.innerText;") + coverage_match = re.search( + r'coverage\s+is\s*[:\-]?\s*([A-Za-z]+)', page_text, re.IGNORECASE + ) + if coverage_match: + status_text = coverage_match.group(1).strip().lower() + if "inactive" in status_text or "termed" in status_text: + eligibilityText = "inactive" + elif "active" in status_text: + eligibilityText = "active" + else: + eligibilityText = status_text + print(f"[DDMA step2] Extracted eligibility from 'Coverage is:' label: '{eligibilityText}'") + except Exception as e: + print(f"[DDMA step2] Could not extract eligibility from 'Coverage is:' label: {e}") + # Wait for the page to be fully ready before capturing PDF try: WebDriverWait(self.driver, 30).until(