From ed7f23d6574d42316f71223e566f3370daacb5ff Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Thu, 18 Dec 2025 08:14:56 +0100 Subject: [PATCH] chore: Update task plan to focus on removing obscuring popups from product screenshots. --- app/services/browserless_service.py | 82 +++++++++++++++++++---------- task.md | 24 +++++---- verify_popup.py | 33 ++++++++++++ 3 files changed, 103 insertions(+), 36 deletions(-) create mode 100644 verify_popup.py diff --git a/app/services/browserless_service.py b/app/services/browserless_service.py index bf45224..07c5f60 100644 --- a/app/services/browserless_service.py +++ b/app/services/browserless_service.py @@ -60,6 +60,20 @@ POPUP_SELECTORS = [ "span.a-button-inner > input.a-button-input[type='submit']", "form:has-text('Continuer les achats') input[type='submit']", "[aria-labelledby='continue-shopping-label']", + + # Generic & specific additions + "[aria-modal='true'] button", + "[class*='cookie'] button", + "[id*='cookie'] button", + "button:has-text('Non merci')", + "button:has-text('Continuer sans accepter')", + "button:has-text('Fermer')", + ".popin-close", + ".js-modal-close", + + # Specific targeted fix for "Stock Inconnu" / "Calendrier" type overlays if they use standard classes + "[id*='advent'] button", + "[class*='advent'] button", ] @@ -226,42 +240,56 @@ class BrowserlessService: @staticmethod async def _handle_popups(page: Page): - """Attempt to close popups and cookie banners with retry logic.""" - logger.debug("Attempting to close popups...") - - # First pass: Try to close all visible popups - closed_popups = [] - for popup_selector in POPUP_SELECTORS: + """Attempt to close popups and cookie banners with refined retry logic.""" + logger.debug("🛡️ Attempting to close popups...") + + async def try_close(selector: str): try: - if await page.locator(popup_selector).count() > 0: - logger.info(f"🚫 Closing popup: {popup_selector}") - await page.locator(popup_selector).first.click(timeout=2000) - closed_popups.append(popup_selector) - await page.wait_for_timeout(1000) + # Check for multiple elements + locator = page.locator(selector) + count = await locator.count() + if count > 0: + # Click the first visible and enabled one + for i in range(count): + el = locator.nth(i) + if await el.is_visible() and await el.is_enabled(): + logger.info(f"🚫 Closing popup via selector: {selector}") + await el.click(timeout=1000) + return True except Exception: pass + return False - # Try Escape key multiple times - for _ in range(2): + # Multiple passes to catch delayed popups (animations, etc.) + for pass_idx in range(3): + closed_something = False + + # 1. Standard Selectors + for selector in POPUP_SELECTORS: + if await try_close(selector): + closed_something = True + await page.wait_for_timeout(500) # Wait for animation + + # 2. Key presses (Escape) try: await page.keyboard.press("Escape") - await page.wait_for_timeout(500) + await page.wait_for_timeout(300) except Exception: pass - # Second pass: Verify and retry if any popups are still visible - if closed_popups: - logger.info(f"Verifying {len(closed_popups)} closed popup(s)...") - await page.wait_for_timeout(1000) - - for popup_selector in closed_popups: - try: - if await page.locator(popup_selector).count() > 0: - logger.warning(f"Popup reappeared, retrying: {popup_selector}") - await page.locator(popup_selector).first.click(timeout=2000) - await page.wait_for_timeout(1000) - except Exception: - pass + # If we didn't find anything in this pass and it's not the first, break early + if not closed_something and pass_idx > 0: + break + + await page.wait_for_timeout(1000) # Wait between passes + + # Final check: Force click typical overlay backdrops (risky but effective) + try: + # Click coordinates (top-right or top-left corner often safish to close modals) + # await page.mouse.click(10, 10) + pass + except Exception: + pass @staticmethod async def _extract_amazon_price(page: Page) -> str: diff --git a/task.md b/task.md index e34b57c..db7a343 100644 --- a/task.md +++ b/task.md @@ -1,17 +1,23 @@ -# Task: Fix Screenshot Update in Tracking +# Task: Remove Obscuring Windows from Product Screenshots ## Context -The user reports that forcing an update in the "Suivi" (Tracking) section does not update the screenshot as expected. + +The user has reported that screenshots in the "suivi produits" (product tracking) section still contain windows (popups, overlays) that obscure the content. A specific example provided shows a "Stock Inconnu" and "Calendrier de l'Avent" popup on a B&M website. ## Current Focus -Investigating the "force update" mechanism and why the screenshot is not refreshing. + +Investigating the scraping and screenshot logic to handle these popups. ## Master Plan -- [x] Investigate the "force update" API endpoint and backend logic. -- [x] Check how screenshots are captured and saved in `tracking_scraper_service.py` or similar. -- [x] Verify if the frontend is correctly requesting the update and displaying the new image. -- [x] Fix the issue preventing the screenshot from updating. -- [x] Verify the fix (Code review confirms relative paths are now consistent across the app). + +- [x] Explore codebase to locate scraping and screenshot logic +- [x] Identify specific handling for B&M or generic popup closing +- [x] Implement fix to close popups before screenshot +- [/] Verify fix (Requires App Rebuild) ## Progress Log -- Created `task.md`. + +- Initialized task.md +- Created implementation plan to expand popup selectors. +- Updated `browserless_service.py` with expanded selectors and retry logic. +- Attempted local verification but failed due to environment restrictions. diff --git a/verify_popup.py b/verify_popup.py new file mode 100644 index 0000000..6e9a969 --- /dev/null +++ b/verify_popup.py @@ -0,0 +1,33 @@ +import asyncio +import logging +import sys +from app.services.browserless_service import browserless_service + +# Configure logging +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +async def verify_popups(): + # URL that was reported to have issues + url = "https://www.bmstores.fr/363943-bougie-parfumee-avec-bijou-350g-senteurs-assorties" + # Fallback to search if that product is gone + fallback_url = "https://www.bmstores.fr/module/ambjolisearch/jolisearch?s=calendrier" + + logger.info(f"--- Testing Popup Handling on {url} ---") + + try: + content, screenshot_path = await browserless_service.get_page_content( + url, + extract_text=False + ) + + logger.info(f"Screenshot saved to: {screenshot_path}") + logger.info("Please inspect the screenshot to ensure no 'Stock Inconnu' or 'Calendrier' popups are visible.") + + except Exception as e: + logger.error(f"Verification failed: {e}") + finally: + await browserless_service.shutdown() + +if __name__ == "__main__": + asyncio.run(verify_popups())