feat: stocker et afficher les données étendues dans le modal détail
- Ajout des champs a_propos, description, carateristique, details au modèle ProductSnapshot - Sérialisation JSON pour les listes et dictionnaires - Modification du CRUD pour stocker/lire les données étendues - Modification du runner pour passer les données lors du scrape - AddProductModal envoie les données étendues lors de la création - La base SQLite doit être recréée (suppression de suivi.db) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user