Merge pull request #270 from R0m1k3/antigravity

Fix: Deep fix for Action.com availability false positives. Advanced t…
This commit is contained in:
LogiFlow authored and GitHub committed 2026-01-13 13:59:08 +01:00
commit 907510bb70
3 files changed
+74 -36

No files matched your search

+32 -23
View File
@@ -1,9 +1,11 @@
from bs4 import BeautifulSoup from bs4 import BeautifulSoup
from app.services.parsers.base_parser import BaseParser, ProductResult from app.services.parsers.base_parser import BaseParser, ProductResult
import logging import logging
import re
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class ActionParser(BaseParser): class ActionParser(BaseParser):
def __init__(self): def __init__(self):
super().__init__("action.com", "https://www.action.com") super().__init__("action.com", "https://www.action.com")
@@ -11,55 +13,62 @@ class ActionParser(BaseParser):
def parse_search_results(self, html: str, query: str, search_url: str) -> list[ProductResult]: def parse_search_results(self, html: str, query: str, search_url: str) -> list[ProductResult]:
soup = BeautifulSoup(html, "html.parser") soup = BeautifulSoup(html, "html.parser")
results = [] results = []
# Action products # Action products
# Config: a.group[href^='/fr-fr/p/'] # Config: a.group[href^='/fr-fr/p/']
links = soup.select("a[href^='/fr-fr/p/']") links = soup.select("a[href^='/fr-fr/p/']")
seen_urls = set() seen_urls = set()
for link in links: for link in links:
try: try:
href = link.get('href') href = link.get("href")
if not href or href in seen_urls: if not href or href in seen_urls:
continue continue
seen_urls.add(href) seen_urls.add(href)
url = self.make_absolute_url(href) url = self.make_absolute_url(href)
# Action usually has the card content inside the link # Action usually has the card content inside the link
title = None title = None
title_el = link.select_one("[class*='title'], h3, h4") title_el = link.select_one("[class*='title'], h3, h4")
if title_el: if title_el:
title = title_el.get_text(strip=True) title = title_el.get_text(strip=True)
else: else:
title = link.get_text(strip=True) # Fallback to specific spans or remove price info from text
# Often Action cards have a .title span
title = link.get_text(" ", strip=True)
# Remove price-like patterns from title if we had to get whole link text
title = re.sub(r"\d+,\d+\s*€.*$", "", title)
title = re.sub(r"\d+\s*€.*$", "", title)
if not title: if not title:
continue continue
img_url = self.extract_image_url(link) img_url = self.extract_image_url(link)
price = None price = None
price_el = link.select_one("[class*='price']") price_el = link.select_one("[class*='price']")
if price_el: if price_el:
price = self.parse_price_text(price_el.get_text()) price = self.parse_price_text(price_el.get_text())
results.append(ProductResult( results.append(
title=title, ProductResult(
url=url, title=title,
source="Action", url=url,
price=price, source="Action",
currency="EUR", price=price,
in_stock=True, currency="EUR",
image_url=img_url, in_stock=True,
snippet=f"Product from Action" image_url=img_url,
)) snippet=f"Product from Action",
)
)
except Exception as e: except Exception as e:
logger.error(f"Error parsing Action product: {e}") logger.error(f"Error parsing Action product: {e}")
continue continue
logger.info(f"ActionParser found {len(results)} results") logger.info(f"ActionParser found {len(results)} results")
return results return results
+30 -1
View File
@@ -54,10 +54,17 @@ def _normalize_title(title: str) -> str:
title = re.sub(r"([a-zA-Z])(\d)", r"\1 \2", title) title = re.sub(r"([a-zA-Z])(\d)", r"\1 \2", title)
title = re.sub(r"(\d)([a-zA-Z])", r"\1 \2", title) title = re.sub(r"(\d)([a-zA-Z])", r"\1 \2", title)
# Remove extra spaces and punctuation that might differ between sites # Remove non-word characters (punctuation) early to simplify regex
title = re.sub(r"[^\w\s]", " ", title) title = re.sub(r"[^\w\s]", " ", title)
title = " ".join(title.split()) title = " ".join(title.split())
# Remove technical metadata at the end (Action specific noise)
# Match various patterns like "0 09 pce 342" or "100 pièces" etc.
title = re.sub(r"\s+\d+\s+\d+\s+pce.*$", "", title)
title = re.sub(r"\s+\d+\s+pièces.*$", "", title)
title = re.sub(r"\s+\d+\s+€.*$", "", title)
title = re.sub(r"\s+\d+\s+\d+\s+€.*$", "", title)
return title.strip() return title.strip()
@@ -363,6 +370,28 @@ async def process_item_check(item_id: int):
f"Stored: '{item_data['name']}', Page title: '{page_title}'" f"Stored: '{item_data['name']}', Page title: '{page_title}'"
) )
# For Action.com, if we haven't confirmed unavailability above,
# don't mark as unavailable yet, just mark as potential replacement error
# unless the title is almost completely different
if is_action:
# Check if at least some significant words match
norm_stored = _normalize_title(item_data["name"])
norm_page = _normalize_title(page_title)
stored_words = set(norm_stored.split())
page_words = set(norm_page.split())
intersection = stored_words & page_words
if len(intersection) >= 2 or (len(stored_words) <= 3 and len(intersection) >= 1):
logger.warning(
f"Borderline title match for Action.com item {item_id}. Keeping as available but marking error."
)
await loop.run_in_executor(
None,
_update_db_error,
item_id,
f"Titre différent mais produit probablement correct : {page_title}",
)
return
await loop.run_in_executor( await loop.run_in_executor(
None, None,
_update_db_product_unavailable, _update_db_product_unavailable,
+12 -12
View File
@@ -1,21 +1,21 @@
# Nettoyage et Commit du Projet # Deep Debugging Action.com Availability
## Contexte ## Context
Après avoir corrigé les problèmes de disponibilité d'Action.com et Amazon, il est nécessaire de nettoyer le répertoire racine des scripts de diagnostic et fichiers temporaires avant de committer les changements. Some products on Action.com are still marked as unavailable even after the first round of fixes.
## Focus Actuel ## Current Focus
Identification et suppression des fichiers inutiles, suivi du commit et push. Identifying why specific Action.com products fail the availability check.
## Master Plan ## Master Plan
- [ ] Identifier les fichiers non essentiels à l'application - [ ] List currently unavailable Action.com items from the database (if possible) or logs
- [ ] Supprimer les fichiers de diagnostic et scripts temporaires - [ ] Reproduce the check for a specific problematic item
- [ ] Vérifier que l'application fonctionne toujours (build/syntaxe) - [ ] Analyze the HTML and Title for these items
- [ ] Committer les changements vers le dépôt Git - [ ] Refine the matching logic or the unavailability detection
- [ ] Pusher les changements - [ ] Verify fix with multiple Action.com items
## Log de Progression ## Progress Log
- [ ] Planification du nettoyage commencée. - [/] Task started.