From 65b26610b1fffcc017692fdd16ae673cb02d893f Mon Sep 17 00:00:00 2001 From: Gitead Date: Thu, 14 May 2026 12:06:33 -0400 Subject: [PATCH] refactor: duplicate member details tab for parallel service history & accumulator Instead of navigating back and forth, step4 opens the member details URL in a new tab. Tab A clicks service history and CDP-prints the history PDF. Tab B clicks View Accumulator and CDP-prints the accumulator PDF (waits up to 15s for vm.hasResults, then captures whatever is on screen). Eliminates the Chrome freeze from back-navigation and the empty-accumulator race condition. Co-Authored-By: Claude Sonnet 4.6 --- ..._eligibilityHistoryRemainingCheckWorker.py | 115 ++++++++---------- 1 file changed, 54 insertions(+), 61 deletions(-) diff --git a/apps/SeleniumService/selenium_CMSP_eligibilityHistoryRemainingCheckWorker.py b/apps/SeleniumService/selenium_CMSP_eligibilityHistoryRemainingCheckWorker.py index bc675966..db8e60ed 100644 --- a/apps/SeleniumService/selenium_CMSP_eligibilityHistoryRemainingCheckWorker.py +++ b/apps/SeleniumService/selenium_CMSP_eligibilityHistoryRemainingCheckWorker.py @@ -16,6 +16,8 @@ class AutomationCMSPEligibilityHistoryRemainingCheck: self.driver = None self.extracted_data = {} self.eligibility_tab = None + self.member_details_tab = None + self.accumulator_tab = None self.data = data.get("data") @@ -296,12 +298,36 @@ class AutomationCMSPEligibilityHistoryRemainingCheck: print(f"[step3b_member_details_pdf] failed: {e}") return None - # ── step 4 — click "VIEW SERVICE HISTORY" → service history page ───────────── + # ── step 4 — duplicate member details tab ─────────────────────────────────── + # Opens the same member details URL in a new tab so that Tab A can navigate + # to service history while Tab B navigates to accumulator independently. - def step4_view_service_history(self): - wait = WebDriverWait(self.driver, 30) - substep = "service_history_link" + def step4_open_duplicate_tab(self): try: + self.member_details_tab = self.driver.current_window_handle + member_details_url = self.driver.current_url + + self.driver.execute_script("window.open(arguments[0]);", member_details_url) + time.sleep(3) + + all_tabs = self.driver.window_handles + self.accumulator_tab = next( + t for t in all_tabs + if t not in [self.eligibility_tab, self.member_details_tab] + ) + print(f"[step4] duplicate tab opened: {self.accumulator_tab}") + return "Success" + except Exception as e: + print(f"[step4] FAILED: {e}") + return f"ERROR:STEP4:open_duplicate_tab" + + # ── step 5 — Tab A: service history → CDP print ────────────────────────────── + + def step5_service_history_pdf(self): + wait = WebDriverWait(self.driver, 30) + try: + self.driver.switch_to.window(self.member_details_tab) + history_link = wait.until( EC.element_to_be_clickable( (By.CSS_SELECTOR, "a.btn.btn-primary[href*='service-history']") @@ -310,21 +336,7 @@ class AutomationCMSPEligibilityHistoryRemainingCheck: self.driver.execute_script("arguments[0].scrollIntoView(true);", history_link) self.driver.execute_script("arguments[0].click();", history_link) time.sleep(2) - print(f"[step4] navigated to service history, URL: {self.driver.current_url}") - return "Success" - except Exception as e: - print(f"[step4] FAILED at substep='{substep}': {e}") - return f"ERROR:STEP4:{substep}" - # ── step 5 — print history PDF via CDP, navigate back to member details ────── - # We do NOT click the "Printer Friendly Format" button here because on the - # service history page it calls window.print() (native dialog) which freezes - # Chrome. Instead we capture the page directly via CDP and then go back. - - def step5_history_pdf(self): - wait = WebDriverWait(self.driver, 30) - try: - # Wait for Angular to finish rendering the service history data wait.until(lambda d: d.execute_script("return document.readyState") == "complete") time.sleep(3) @@ -336,25 +348,23 @@ class AutomationCMSPEligibilityHistoryRemainingCheck: with open(pdf_path, "wb") as f: f.write(pdf_bytes) print("History PDF saved at:", pdf_path) - - # Go back to member details page where "View Accumulator" lives - self.driver.back() - time.sleep(2) - wait.until(lambda d: d.execute_script("return document.readyState") == "complete") - print(f"[step5] returned to member details, URL: {self.driver.current_url}") - return pdf_path except Exception as e: - print(f"[step5_history_pdf] failed: {e}") + print(f"[step5_service_history_pdf] failed: {e}") return None - # ── step 6 — click "View Accumulator" ─────────────────────────────────────── + # ── step 6 — Tab B: accumulator → wait for data → CDP print ───────────────── - def step6_click_view_accumulator(self): + def step6_accumulator_pdf(self): wait = WebDriverWait(self.driver, 30) - substep = "view_accumulator_link" try: - # ng-if="vm.showAccumulator" is the stable Angular condition on this link + self.driver.switch_to.window(self.accumulator_tab) + + # Wait for the duplicate member details page to fully load + wait.until(lambda d: d.execute_script("return document.readyState") == "complete") + time.sleep(2) + + # Click View Accumulator accumulator_link = wait.until( EC.element_to_be_clickable( (By.CSS_SELECTOR, "a.btn.btn-primary[ng-if='vm.showAccumulator']") @@ -363,36 +373,22 @@ class AutomationCMSPEligibilityHistoryRemainingCheck: self.driver.execute_script("arguments[0].scrollIntoView(true);", accumulator_link) self.driver.execute_script("arguments[0].click();", accumulator_link) time.sleep(2) - print(f"[step6] navigated to accumulator, URL: {self.driver.current_url}") - return "Success" - except Exception as e: - print(f"[step6] FAILED at substep='{substep}': {e}") - return f"ERROR:STEP6:{substep}" - # ── step 7 — print accumulator PDF ────────────────────────────────────────── + # Wait for accumulator page shell + wait.until(lambda d: d.execute_script("return document.readyState") == "complete") - def step7_accumulator_pdf(self): - try: - # Wait for the page shell to load - WebDriverWait(self.driver, 30).until( - lambda d: d.execute_script("return document.readyState") == "complete" - ) - - # Try to detect when Angular data is ready (button has ng-if="vm.hasResults"). - # Cap at 15 s — if the patient has no accumulator data the button never - # appears and we still want to capture whatever the page shows. + # Wait up to 15 s for vm.hasResults — captures data or "no results" state try: WebDriverWait(self.driver, 15).until( EC.visibility_of_element_located( (By.CSS_SELECTOR, "button.btn.btn-primary[ng-click='vm.printResults()']") ) ) - print("[step7] vm.hasResults is true — data loaded") + print("[step6] accumulator data loaded (vm.hasResults true)") except TimeoutException: - print("[step7] print button not visible after 15 s — patient may have no accumulator data, printing anyway") + print("[step6] no accumulator data for this patient — printing empty state") - # Extra pause so Angular finishes rendering table rows - time.sleep(5) + time.sleep(3) safe_member = "".join(c for c in str(self.memberId) if c.isalnum() or c in "-_.") pdf_filename = f"cmsp_accumulator_{safe_member}.pdf" @@ -404,7 +400,7 @@ class AutomationCMSPEligibilityHistoryRemainingCheck: print("Accumulator PDF saved at:", pdf_path) return pdf_path except Exception as e: - print(f"[step7_accumulator_pdf] failed: {e}") + print(f"[step6_accumulator_pdf] failed: {e}") return None # ── main workflow ──────────────────────────────────────────────────────────── @@ -433,23 +429,20 @@ class AutomationCMSPEligibilityHistoryRemainingCheck: member_details_pdf_path = self.step3b_member_details_pdf() - step4_result = self.step4_view_service_history() + # Duplicate the member details tab so service history and accumulator + # can be opened independently without back-navigation + step4_result = self.step4_open_duplicate_tab() if step4_result.startswith("ERROR"): return {"status": "partial", "message": step4_result, "pdf_path": eligibility_pdf_path, "member_details_pdf_path": member_details_pdf_path, "file_type": "pdf"} - history_pdf_path = self.step5_history_pdf() + # Tab A: service history PDF + history_pdf_path = self.step5_service_history_pdf() - step6_result = self.step6_click_view_accumulator() - if step6_result.startswith("ERROR"): - return {"status": "partial", "message": step6_result, - "pdf_path": eligibility_pdf_path, - "member_details_pdf_path": member_details_pdf_path, - "history_pdf_path": history_pdf_path, "file_type": "pdf"} - - accumulator_pdf_path = self.step7_accumulator_pdf() + # Tab B: accumulator PDF + accumulator_pdf_path = self.step6_accumulator_pdf() result = { "status": "success",