last
This commit is contained in:
@@ -63,6 +63,7 @@ def create_product_with_snapshot(
|
||||
"description",
|
||||
"carateristique",
|
||||
"details",
|
||||
"categorie_amazon",
|
||||
]
|
||||
snapshot_data = {k: data_dict.pop(k) for k in snapshot_fields if k in data_dict}
|
||||
|
||||
@@ -118,15 +119,23 @@ def remove_product(db: Session, product: models.Product) -> None:
|
||||
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 list_snapshots(
|
||||
db: Session, product_id: int, days: int | None = None, limit: int = 1000
|
||||
) -> list[models.ProductSnapshot]:
|
||||
"""Retourne les snapshots d'un produit, filtrés par nombre de jours si spécifié."""
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
query = db.query(models.ProductSnapshot).filter(
|
||||
models.ProductSnapshot.produit_id == product_id
|
||||
)
|
||||
|
||||
# Filtrer par date si days est spécifié
|
||||
if days is not None and days > 0:
|
||||
cutoff_date = datetime.utcnow() - timedelta(days=days)
|
||||
query = query.filter(models.ProductSnapshot.scrape_le >= cutoff_date)
|
||||
|
||||
return query.order_by(models.ProductSnapshot.scrape_le.desc()).limit(limit).all()
|
||||
|
||||
|
||||
def get_latest_snapshot(db: Session, product_id: int) -> models.ProductSnapshot | None:
|
||||
return (
|
||||
@@ -215,6 +224,7 @@ def _enrich_product_with_snapshot(db: Session, product: models.Product) -> dict:
|
||||
"description": snapshot.description,
|
||||
"carateristique": carateristique,
|
||||
"details": details,
|
||||
"categorie_amazon": snapshot.categorie_amazon,
|
||||
"dernier_scrape": snapshot.scrape_le,
|
||||
"statut_scrap": snapshot.statut_scrap,
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ class Product(Base):
|
||||
cree_le = Column(DateTime, default=datetime.utcnow)
|
||||
modifie_le = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||||
|
||||
snapshots = relationship("ProductSnapshot", back_populates="product")
|
||||
snapshots = relationship("ProductSnapshot", back_populates="product", cascade="all, delete-orphan")
|
||||
|
||||
|
||||
class ScrapeRun(Base):
|
||||
@@ -67,6 +67,7 @@ class ProductSnapshot(Base):
|
||||
description = Column(Text, nullable=True)
|
||||
carateristique = Column(Text, nullable=True) # JSON object
|
||||
details = Column(Text, nullable=True) # JSON object
|
||||
categorie_amazon = Column(Text, nullable=True) # Catégorie depuis breadcrumb Amazon
|
||||
chemin_json_brut = Column(Text, nullable=True)
|
||||
statut_scrap = Column(String(32), default="ok")
|
||||
message_erreur = Column(Text, nullable=True)
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from datetime import datetime
|
||||
from typing import Any, Optional
|
||||
|
||||
from pydantic import BaseModel, HttpUrl
|
||||
from pydantic import BaseModel, ConfigDict, HttpUrl, field_validator
|
||||
|
||||
|
||||
class ProductBase(BaseModel):
|
||||
@@ -30,13 +31,12 @@ class ProductUpdate(BaseModel):
|
||||
|
||||
|
||||
class ProductRead(ProductBase):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
cree_le: datetime
|
||||
modifie_le: datetime
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class ProductSnapshotBase(BaseModel):
|
||||
prix_actuel: Optional[float]
|
||||
@@ -55,22 +55,48 @@ class ProductSnapshotBase(BaseModel):
|
||||
description: Optional[str] = None
|
||||
carateristique: Optional[dict[str, Any]] = None
|
||||
details: Optional[dict[str, Any]] = None
|
||||
categorie_amazon: Optional[str] = None
|
||||
statut_scrap: Optional[str]
|
||||
message_erreur: Optional[str]
|
||||
|
||||
|
||||
class ProductSnapshotRead(ProductSnapshotBase):
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
produit_id: int
|
||||
scrape_le: datetime
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
@field_validator("a_propos", mode="before")
|
||||
@classmethod
|
||||
def parse_a_propos(cls, v: Any) -> list[str] | None:
|
||||
if v is None:
|
||||
return None
|
||||
if isinstance(v, str):
|
||||
try:
|
||||
return json.loads(v)
|
||||
except json.JSONDecodeError:
|
||||
return None
|
||||
return v
|
||||
|
||||
@field_validator("carateristique", "details", mode="before")
|
||||
@classmethod
|
||||
def parse_json_dict(cls, v: Any) -> dict[str, Any] | None:
|
||||
if v is None:
|
||||
return None
|
||||
if isinstance(v, str):
|
||||
try:
|
||||
return json.loads(v)
|
||||
except json.JSONDecodeError:
|
||||
return None
|
||||
return v
|
||||
|
||||
|
||||
class ProductWithSnapshot(ProductBase):
|
||||
"""Produit enrichi avec les données du dernier snapshot."""
|
||||
|
||||
model_config = ConfigDict(from_attributes=True)
|
||||
|
||||
id: int
|
||||
cree_le: datetime
|
||||
modifie_le: datetime
|
||||
@@ -92,12 +118,10 @@ class ProductWithSnapshot(ProductBase):
|
||||
description: Optional[str] = None
|
||||
carateristique: Optional[dict[str, Any]] = None
|
||||
details: Optional[dict[str, Any]] = None
|
||||
categorie_amazon: Optional[str] = None
|
||||
dernier_scrape: Optional[datetime] = None
|
||||
statut_scrap: Optional[str] = None
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class ProductCreateWithSnapshot(ProductBase):
|
||||
"""Création d'un produit avec données de snapshot initiales (depuis preview)."""
|
||||
@@ -119,3 +143,4 @@ class ProductCreateWithSnapshot(ProductBase):
|
||||
description: Optional[str] = None
|
||||
carateristique: Optional[dict[str, Any]] = None
|
||||
details: Optional[dict[str, Any]] = None
|
||||
categorie_amazon: Optional[str] = None
|
||||
|
||||
Reference in New Issue
Block a user