From f7059da02a5f022f4ddebc57fd1cdfc3523dacf8 Mon Sep 17 00:00:00 2001 From: Michael SCHAL Date: Thu, 8 Jan 2026 11:41:34 +0100 Subject: [PATCH] corretion --- app/routers/items.py | 25 ++++++++- app/services/scheduler_service.py | 6 ++ .../src/components/dashboard/ItemCard.jsx | 17 +++++- frontend/src/pages/Dashboard.jsx | 12 ++++ task.md | 55 ++++++------------- 5 files changed, 73 insertions(+), 42 deletions(-) diff --git a/app/routers/items.py b/app/routers/items.py index 6e88802..f52a42b 100644 --- a/app/routers/items.py +++ b/app/routers/items.py @@ -39,7 +39,7 @@ def update_item_category(item_id: int, category: str | None = None, db: Session item = db.query(models.Item).filter(models.Item.id == item_id).first() if not item: raise HTTPException(status_code=404, detail="Item not found") - + # Treat empty string as None item.category = category if category and category.strip() else None db.commit() @@ -77,7 +77,7 @@ def get_price_history(item_id: int, db: Session = Depends(database.get_db)): item = db.query(models.Item).filter(models.Item.id == item_id).first() if not item: raise HTTPException(status_code=404, detail="Item not found") - + # Get price history sorted by timestamp descending (most recent first) price_history = ( db.query(models.PriceHistory) @@ -85,5 +85,24 @@ def get_price_history(item_id: int, db: Session = Depends(database.get_db)): .order_by(models.PriceHistory.timestamp.desc()) .all() ) - + return price_history + + +@router.patch("/{item_id}/availability") +def update_item_availability(item_id: int, available: bool = True, db: Session = Depends(database.get_db)): + """Mark an item as available or unavailable manually""" + item = db.query(models.Item).filter(models.Item.id == item_id).first() + if not item: + raise HTTPException(status_code=404, detail="Item not found") + + item.is_available = available + # Also clear the error if marking as available + if available and item.last_error and "indisponible" in item.last_error.lower(): + item.last_error = None + db.commit() + db.refresh(item) + return { + "message": f"Item marked as {'available' if available else 'unavailable'}", + "is_available": item.is_available, + } diff --git a/app/services/scheduler_service.py b/app/services/scheduler_service.py index 98b0798..a5b1e23 100644 --- a/app/services/scheduler_service.py +++ b/app/services/scheduler_service.py @@ -57,6 +57,12 @@ def _update_db_result( item.current_price = price item.current_price_confidence = p_conf + # Auto-reset availability if price was successfully extracted + # A valid price proves the product is still available + if item.is_available is False: + item.is_available = True + logger.info(f"Item {item_id} marked as available again (price found: {price})") + session.add( models.PriceHistory( item_id=item.id, diff --git a/frontend/src/components/dashboard/ItemCard.jsx b/frontend/src/components/dashboard/ItemCard.jsx index 3484587..0cc05a3 100644 --- a/frontend/src/components/dashboard/ItemCard.jsx +++ b/frontend/src/components/dashboard/ItemCard.jsx @@ -10,7 +10,7 @@ import { Label } from '@/components/ui/label'; import { Marquee } from '@/components/ui/marquee'; import { cn } from '@/lib/utils'; -export function ItemCard({ item, onEdit, onDelete, onCheck, onZoom, onCategoryUpdate, categories, onViewHistory }) { +export function ItemCard({ item, onEdit, onDelete, onCheck, onZoom, onCategoryUpdate, categories, onViewHistory, onMarkAvailable }) { const { t } = useTranslation(); const [showCategorySelect, setShowCategorySelect] = useState(false); const [showNewCategoryDialog, setShowNewCategoryDialog] = useState(false); @@ -73,11 +73,24 @@ export function ItemCard({ item, onEdit, onDelete, onCheck, onZoom, onCategoryUp {/* Product Unavailable Overlay */} {isProductUnavailable && (
-
+
Produit retiré du site + {onMarkAvailable && ( + + )}
)} diff --git a/frontend/src/pages/Dashboard.jsx b/frontend/src/pages/Dashboard.jsx index 49e9427..72aad2b 100644 --- a/frontend/src/pages/Dashboard.jsx +++ b/frontend/src/pages/Dashboard.jsx @@ -102,6 +102,17 @@ export default function Dashboard() { } }; + const handleMarkAvailable = async (itemId) => { + try { + await axios.patch(`${API_URL}/items/${itemId}/availability?available=true`); + toast.success('Produit marqué comme disponible'); + refreshItems(); + } catch (error) { + console.error('Error marking item as available:', error); + toast.error('Erreur lors de la mise à jour'); + } + }; + const filteredItems = items.filter(item => { const term = searchTerm.toLowerCase(); const matchesSearch = ( @@ -170,6 +181,7 @@ export default function Dashboard() { onZoom={setZoomedImage} onCategoryUpdate={handleCategoryUpdate} onViewHistory={setHistoryItem} + onMarkAvailable={handleMarkAvailable} categories={categories} /> ))} diff --git a/task.md b/task.md index 86cb895..273c28c 100644 --- a/task.md +++ b/task.md @@ -1,43 +1,24 @@ -# Task Board: Catalog Image Fix +# Correction Statut Disponibilité Produit B&M -## 🚀 Current Focus +## Contexte -- [ ] Investigate Comparator Price Extraction (User reported false prices) -- [x] Fix Catalog Images (Generic/Missing icons) -- [/] **Amazon Login Wall Fix** (Redirected by User) +Le produit "Lot de 24 bougies blanches" sur bmstores.fr est affiché comme "Produit retiré du site" dans Priceflow alors qu'il est bien disponible sur le site (0,88€). -## 📋 Master Plan +## Master Plan -- [x] Analyze `verify_extraction_logic.py`, `ai_service.py`, `ai_schema.py`, `tracking_scraper_service.py`. -- [x] Proposal Phase: Vision Priority accepted. -- [x] Implementation Phase: - - [x] Update `ai_schema.py`. - - [x] Fix `scheduler_service.py`. -- [x] Verification Phase: Verified with `verify_vision_priority.py`. -- [x] **Catalog Retrieval Fix**: - - [x] Reproduce failure (Confirmed Browserless issue). - - [x] Fix `cataloguemate_scraper.py` with HTTP fallback. - - [x] Cleanup debug files. -- [x] **Catalog Image Fix**: - - [x] Analyze page HTML for correct image selectors (Found Thumbor URL). - - [x] **Check PDF availability** (Browser Task). - - [x] **Refine Scraper Logic**: - - [x] Update `cataloguemate_scraper.py` to grab `src` and prioritize Thumbor. - - [x] Update `routers/catalogues.py` to clean up bad images. - - [x] **Cleanup Bad Data**: - - [x] Run cleanup endpoint. - - [x] Verify fix with new scrape. -- [/] **Comparator Price Fix**: - - [ ] Reproduce "False Price" issue with script. - - [ ] Audit `improved_search_service.py` extraction logic (Lowest Price vs AI). - - [ ] Implement robust price validation/filtering. - - [ ] Verify fix. +- [x] Correction automatique du statut lors d'un refresh réussi + - [x] Modifier `_update_db_result` dans `scheduler_service.py` pour remettre `is_available = True` quand un prix est détecté +- [x] Endpoint API pour correction manuelle + - [x] Ajouter un endpoint `PATCH /{item_id}/availability` dans `items.py` router + - [x] Permettre de modifier `is_available` via l'API +- [x] Interface utilisateur + - [x] Ajouter `onMarkAvailable` prop dans `ItemCard.jsx` + - [x] Ajouter le handler `handleMarkAvailable` dans `Dashboard.jsx` + - [x] Afficher un bouton "Marquer comme disponible" dans l'overlay des produits indisponibles -## 📝 Progress Log +## Progress Log -- **2025-12-22**: Vision Priority implemented and verified. -- **2025-12-22**: Fixed Catalog Retrieval using HTTP fallback. -- **2025-12-22**: Investigating generic icon issue. Found images are served via Thumbor. -- **2025-12-22**: **FIXED**: Scraper now targets Thumbor images. Wiped bad catalogs. -- **2025-12-24**: User prioritized Amazon Login Wall fix. -- **2025-12-24**: Reproduction script `test_amazon_scraper.py` patched to use local browser. Verifying issue. +- Diagnostic effectué : produit B&M bien disponible (0,88€) mais marqué indisponible dans la DB +- Modifié `scheduler_service.py` : reset auto de `is_available` quand prix détecté +- Ajouté endpoint API `PATCH /api/items/{id}/availability` +- Ajouté bouton dans l'overlay frontend pour correction manuelle