diff --git a/backend/app/db/crud.py b/backend/app/db/crud.py index 52e33bb..1405488 100644 --- a/backend/app/db/crud.py +++ b/backend/app/db/crud.py @@ -1,5 +1,7 @@ from __future__ import annotations +import json + from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import Session @@ -57,9 +59,21 @@ def create_product_with_snapshot( "choix_amazon", "offre_limitee", "exclusivite_amazon", + "a_propos", + "description", + "carateristique", + "details", ] snapshot_data = {k: data_dict.pop(k) for k in snapshot_fields if k in data_dict} + # Sérialiser les champs JSON pour le stockage + if snapshot_data.get("a_propos") is not None: + snapshot_data["a_propos"] = json.dumps(snapshot_data["a_propos"], ensure_ascii=False) + if snapshot_data.get("carateristique") is not None: + snapshot_data["carateristique"] = json.dumps(snapshot_data["carateristique"], ensure_ascii=False) + if snapshot_data.get("details") is not None: + snapshot_data["details"] = json.dumps(snapshot_data["details"], ensure_ascii=False) + # Convertir les HttpUrl en strings pour SQLite if data_dict.get("url"): data_dict["url"] = str(data_dict["url"]) @@ -161,6 +175,28 @@ def _enrich_product_with_snapshot(db: Session, product: models.Product) -> dict: if snapshot.prix_actuel and snapshot.prix_conseille: reduction = round((1 - snapshot.prix_actuel / snapshot.prix_conseille) * 100) + # Désérialiser les champs JSON + a_propos = None + if snapshot.a_propos: + try: + a_propos = json.loads(snapshot.a_propos) + except json.JSONDecodeError: + a_propos = None + + carateristique = None + if snapshot.carateristique: + try: + carateristique = json.loads(snapshot.carateristique) + except json.JSONDecodeError: + carateristique = None + + details = None + if snapshot.details: + try: + details = json.loads(snapshot.details) + except json.JSONDecodeError: + details = None + result.update( { "prix_actuel": snapshot.prix_actuel, @@ -175,6 +211,10 @@ def _enrich_product_with_snapshot(db: Session, product: models.Product) -> dict: "choix_amazon": snapshot.choix_amazon, "offre_limitee": snapshot.offre_limitee, "exclusivite_amazon": snapshot.exclusivite_amazon, + "a_propos": a_propos, + "description": snapshot.description, + "carateristique": carateristique, + "details": details, "dernier_scrape": snapshot.scrape_le, "statut_scrap": snapshot.statut_scrap, } diff --git a/backend/app/db/models.py b/backend/app/db/models.py index e64ff87..2866011 100644 --- a/backend/app/db/models.py +++ b/backend/app/db/models.py @@ -62,6 +62,11 @@ class ProductSnapshot(Base): choix_amazon = Column(Boolean, nullable=True) offre_limitee = Column(Boolean, nullable=True) exclusivite_amazon = Column(Boolean, nullable=True) + # Données étendues (stockées en JSON) + a_propos = Column(Text, nullable=True) # JSON array + description = Column(Text, nullable=True) + carateristique = Column(Text, nullable=True) # JSON object + details = Column(Text, nullable=True) # JSON object chemin_json_brut = Column(Text, nullable=True) statut_scrap = Column(String(32), default="ok") message_erreur = Column(Text, nullable=True) diff --git a/backend/app/db/schemas.py b/backend/app/db/schemas.py index 5f8fe1c..ec69910 100644 --- a/backend/app/db/schemas.py +++ b/backend/app/db/schemas.py @@ -1,7 +1,7 @@ from __future__ import annotations from datetime import datetime -from typing import Optional +from typing import Any, Optional from pydantic import BaseModel, HttpUrl @@ -50,6 +50,11 @@ class ProductSnapshotBase(BaseModel): choix_amazon: Optional[bool] offre_limitee: Optional[bool] exclusivite_amazon: Optional[bool] + # Données étendues + a_propos: Optional[list[str]] = None + description: Optional[str] = None + carateristique: Optional[dict[str, Any]] = None + details: Optional[dict[str, Any]] = None statut_scrap: Optional[str] message_erreur: Optional[str] @@ -82,6 +87,11 @@ class ProductWithSnapshot(ProductBase): choix_amazon: Optional[bool] = None offre_limitee: Optional[bool] = None exclusivite_amazon: Optional[bool] = None + # Données étendues + a_propos: Optional[list[str]] = None + description: Optional[str] = None + carateristique: Optional[dict[str, Any]] = None + details: Optional[dict[str, Any]] = None dernier_scrape: Optional[datetime] = None statut_scrap: Optional[str] = None @@ -104,3 +114,8 @@ class ProductCreateWithSnapshot(ProductBase): choix_amazon: Optional[bool] = None offre_limitee: Optional[bool] = None exclusivite_amazon: Optional[bool] = None + # Données étendues + a_propos: Optional[list[str]] = None + description: Optional[str] = None + carateristique: Optional[dict[str, Any]] = None + details: Optional[dict[str, Any]] = None diff --git a/backend/app/scraper/runner.py b/backend/app/scraper/runner.py index 14a9905..04565bc 100644 --- a/backend/app/scraper/runner.py +++ b/backend/app/scraper/runner.py @@ -93,6 +93,19 @@ def _create_snapshot( # Mettre à jour le produit avec titre/image si manquants _update_product_from_scrape(session, product, data) + # Sérialiser les données étendues en JSON + a_propos = data.get("a_propos") + if a_propos is not None: + a_propos = json.dumps(a_propos, ensure_ascii=False) + + carateristique = data.get("carateristique") + if carateristique is not None: + carateristique = json.dumps(carateristique, ensure_ascii=False) + + details = data.get("details") + if details is not None: + details = json.dumps(details, ensure_ascii=False) + snapshot = models.ProductSnapshot( produit_id=product.id, run_scrap_id=run.id, @@ -107,6 +120,10 @@ def _create_snapshot( choix_amazon=data.get("choix_amazon"), offre_limitee=data.get("offre_limitee"), exclusivite_amazon=data.get("exclusivite_amazon"), + a_propos=a_propos, + description=data.get("description"), + carateristique=carateristique, + details=details, chemin_json_brut=str(raw_json_path) if raw_json_path else None, statut_scrap=status, message_erreur=error_message, diff --git a/frontend/src/components/products/AddProductModal.jsx b/frontend/src/components/products/AddProductModal.jsx index ef28ee7..7f5829f 100644 --- a/frontend/src/components/products/AddProductModal.jsx +++ b/frontend/src/components/products/AddProductModal.jsx @@ -100,6 +100,11 @@ const AddProductModal = ({ onClose }) => { choix_amazon: data.choix_amazon ?? null, offre_limitee: data.offre_limitee ?? null, exclusivite_amazon: data.exclusivite_amazon ?? null, + // Données étendues + a_propos: data.a_propos ?? null, + description: data.description ?? null, + carateristique: data.carateristique ?? null, + details: data.details ?? null, }); onClose();