diff --git a/CHANGELOG.md b/CHANGELOG.md index 623e305..f6693cd 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -53,11 +53,14 @@ Le format est basé sur [Keep a Changelog](https://keepachangelog.com/fr/1.0.0/) - Web UI: popup ajout produit central + favicon - API: logs Uvicorn exposes pour l UI - Parsing prix: gestion des separateurs de milliers (espace, NBSP, point) -- API/DB: description + msrp + images/specs exposes, reduction calculee +- API/DB: exposition des champs Amazon enrichis (note, badge, stock texte, modele) +- Web UI: carte produit analytique avec resume, historique plein format et actions compactes +- Web UI: slider colonnes responsive + modal ajout produit scrollable avec footer sticky ### Corrigé - Migration Alembic: down_revision aligne sur 20260114_02 - Amazon: extraction images via data-a-dynamic-image + filtrage logos +- API: suppression du calcul automatique des reductions (valeurs explicites uniquement) --- diff --git a/README.md b/README.md index c2c7800..e47d482 100755 --- a/README.md +++ b/README.md @@ -152,6 +152,8 @@ Guide de migration JSON -> DB: `MIGRATION_GUIDE.md` L'API est protegee par un token simple. +Note: l endpoint `/products` expose des champs Amazon explicites (asin, note, badge Choix d Amazon, stock_text/in_stock, model_number/model_name, main_image/gallery_images). Les reductions ne sont plus calculees cote API. + ```bash export PW_API_TOKEN=change_me docker compose up -d api diff --git a/TODO.md b/TODO.md index e9770d6..971a785 100755 --- a/TODO.md +++ b/TODO.md @@ -170,6 +170,7 @@ Liste des tâches priorisées pour le développement de PriceWatch. - [x] Tests performance (100+ produits) - [x] CRUD produits - [x] Historique prix +- [ ] Ajouter migration DB pour les nouveaux champs Amazon (note, badge, stock texte, modele) ### Documentation - [x] Migration guide (JSON -> DB) diff --git a/analytics-ui/app.py b/analytics-ui/app.py index e3883ef..e54180b 100644 --- a/analytics-ui/app.py +++ b/analytics-ui/app.py @@ -76,6 +76,81 @@ def _serialize_decimal(value): return value +def fetch_product_history(product_id: int) -> Tuple[List[Dict[str, Any]], Optional[str]]: + """Récupère l'historique complet des scraps pour un produit.""" + rows: List[Dict[str, Any]] = [] + try: + with get_db_connection() as conn: + with conn.cursor(cursor_factory=RealDictCursor) as cur: + cur.execute( + """ + SELECT + ph.id, + ph.price, + ph.shipping_cost, + ph.stock_status, + ph.fetch_method, + ph.fetch_status, + ph.fetched_at + FROM price_history ph + WHERE ph.product_id = %s + ORDER BY ph.fetched_at DESC + """, + (product_id,), + ) + fetched = cur.fetchall() + for item in fetched: + serialized = {key: _serialize_decimal(value) for key, value in item.items()} + if serialized.get("fetched_at"): + serialized["fetched_at"] = serialized["fetched_at"].strftime( + "%Y-%m-%d %H:%M:%S" + ) + rows.append(serialized) + return rows, None + except Exception as exc: + return rows, str(exc) + + +def fetch_all_price_history(limit: int = 500) -> Tuple[List[Dict[str, Any]], Optional[str]]: + """Récupère toutes les entrées de price_history avec infos produit.""" + rows: List[Dict[str, Any]] = [] + try: + with get_db_connection() as conn: + with conn.cursor(cursor_factory=RealDictCursor) as cur: + cur.execute( + """ + SELECT + ph.id, + ph.product_id, + p.source, + p.reference, + p.title, + ph.price, + ph.shipping_cost, + ph.stock_status, + ph.fetch_method, + ph.fetch_status, + ph.fetched_at + FROM price_history ph + LEFT JOIN products p ON p.id = ph.product_id + ORDER BY ph.fetched_at DESC + LIMIT %s + """, + (limit,), + ) + fetched = cur.fetchall() + for item in fetched: + serialized = {key: _serialize_decimal(value) for key, value in item.items()} + if serialized.get("fetched_at"): + serialized["fetched_at"] = serialized["fetched_at"].strftime( + "%Y-%m-%d %H:%M:%S" + ) + rows.append(serialized) + return rows, None + except Exception as exc: + return rows, str(exc) + + def fetch_products_list(limit: int = 200) -> Tuple[List[Dict[str, Any]], Optional[str]]: rows: List[Dict[str, Any]] = [] try: @@ -260,6 +335,68 @@ TEMPLATE = """ + + Historique complet des scraps + + + Charger l'historique du produit sélectionné + + + + + + + Date + Prix + Frais port + Stock + Méthode + Statut + + + + Sélectionnez un produit puis cliquez sur "Charger l'historique" + + + + + + + Parcourir la table price_history + + + Charger price_history + Précédent + Suivant + 0 / 0 + + + + ID + - + Product ID + - + Store + - + Référence + - + Titre produit + - + Prix + - + Frais de port + - + Stock + - + Méthode + - + Statut + - + Date scraping + - + + +