from __future__ import annotations import json from sqlalchemy.exc import IntegrityError from sqlalchemy.orm import Session from backend.app.db import models, schemas """ CRUD minimal pour manipuler les produits dans la base SQLite. """ def get_product(db: Session, product_id: int) -> models.Product | None: return db.query(models.Product).filter(models.Product.id == product_id).first() def list_products(db: Session, skip: int = 0, limit: int = 100) -> list[models.Product]: return db.query(models.Product).offset(skip).limit(limit).all() def create_product(db: Session, data: schemas.ProductCreate) -> models.Product: # Convertir les HttpUrl en strings pour SQLite data_dict = data.model_dump() if data_dict.get("url"): data_dict["url"] = str(data_dict["url"]) if data_dict.get("url_image"): data_dict["url_image"] = str(data_dict["url_image"]) product = models.Product(**data_dict) db.add(product) try: db.commit() except IntegrityError: db.rollback() raise db.refresh(product) return product def create_product_with_snapshot( db: Session, data: schemas.ProductCreateWithSnapshot ) -> models.Product: """Crée un produit avec un snapshot initial (données issues de la prévisualisation).""" data_dict = data.model_dump() # Extraire les champs du snapshot snapshot_fields = [ "prix_actuel", "prix_conseille", "prix_min_30j", "etat_stock", "en_stock", "note", "nombre_avis", "prime", "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"]) if data_dict.get("url_image"): data_dict["url_image"] = str(data_dict["url_image"]) # Créer le produit product = models.Product(**data_dict) db.add(product) try: db.commit() except IntegrityError: db.rollback() raise db.refresh(product) # Créer le snapshot initial si on a des données has_snapshot_data = any(v is not None for v in snapshot_data.values()) if has_snapshot_data: snapshot = models.ProductSnapshot( produit_id=product.id, statut_scrap="preview", **snapshot_data, ) db.add(snapshot) db.commit() return product def update_product(db: Session, product: models.Product, changes: schemas.ProductUpdate) -> models.Product: for field, value in changes.dict(exclude_unset=True).items(): setattr(product, field, value) db.add(product) db.commit() db.refresh(product) return product def remove_product(db: Session, product: models.Product) -> None: db.delete(product) db.commit() def list_snapshots(db: Session, product_id: int, limit: int = 30) -> list[models.ProductSnapshot]: return ( db.query(models.ProductSnapshot) .filter(models.ProductSnapshot.produit_id == product_id) .order_by(models.ProductSnapshot.scrape_le.desc()) .limit(limit) .all() ) def get_latest_snapshot(db: Session, product_id: int) -> models.ProductSnapshot | None: return ( db.query(models.ProductSnapshot) .filter(models.ProductSnapshot.produit_id == product_id) .order_by(models.ProductSnapshot.scrape_le.desc()) .first() ) def get_product_with_snapshot(db: Session, product_id: int) -> dict | None: """Retourne un produit enrichi avec les données du dernier snapshot.""" product = get_product(db, product_id) if not product: return None return _enrich_product_with_snapshot(db, product) def list_products_with_snapshots(db: Session, skip: int = 0, limit: int = 100) -> list[dict]: """Retourne la liste des produits enrichis avec leurs derniers snapshots.""" products = list_products(db, skip=skip, limit=limit) return [_enrich_product_with_snapshot(db, p) for p in products] def _enrich_product_with_snapshot(db: Session, product: models.Product) -> dict: """Ajoute les données du dernier snapshot au produit.""" snapshot = get_latest_snapshot(db, product.id) result = { "id": product.id, "boutique": product.boutique, "url": str(product.url), "asin": product.asin, "titre": product.titre, "url_image": str(product.url_image) if product.url_image else None, "categorie": product.categorie, "type": product.type, "actif": product.actif, "cree_le": product.cree_le, "modifie_le": product.modifie_le, } if snapshot: # Calcul de la réduction en pourcentage reduction = None 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, "prix_conseille": snapshot.prix_conseille, "prix_min_30j": snapshot.prix_min_30j, "reduction_pourcent": reduction, "etat_stock": snapshot.etat_stock, "en_stock": snapshot.en_stock, "note": snapshot.note, "nombre_avis": snapshot.nombre_avis, "prime": snapshot.prime, "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, } ) return result