claude fix: improve product scraping and debugging features

This commit is contained in:
2026-01-19 21:26:45 +01:00
parent dcb25e0163
commit 20bdc7ff70
131 changed files with 544285 additions and 59 deletions
+5
View File
@@ -26,6 +26,11 @@
- [x] composant PriceChart avec chart.js + react-chartjs-2
- [x] affichage min/max/tendance
- [x] couleurs selon tendance (vert/jaune/rouge)
- [x] page Settings avec config frontend/backend (Étape 7)
- [x] corrections bugs UX (Étape 8)
- [x] refresh après ajout produit
- [x] décalage horaire dates (UTC → local)
- [x] popup prévisualisation avant enregistrement avec scrape preview
## Phase 3 - Industrialisation
- [ ] dockeriser backend + frontend + scheduler
+11 -10
View File
@@ -1,11 +1,10 @@
from __future__ import annotations
from fastapi import APIRouter, BackgroundTasks, Depends, HTTPException, status
from fastapi import APIRouter, Depends, HTTPException, status
from sqlalchemy.orm import Session
from backend.app.api.deps import get_db
from backend.app.db import crud, schemas
from backend.app.scraper.runner import scrape_product
router = APIRouter(prefix="/products", tags=["products"])
@@ -16,16 +15,18 @@ def list_products(skip: int = 0, limit: int = 50, db: Session = Depends(get_db))
return crud.list_products_with_snapshots(db, skip=skip, limit=limit)
@router.post("", response_model=schemas.ProductRead, status_code=status.HTTP_201_CREATED)
@router.post("", response_model=schemas.ProductWithSnapshot, status_code=status.HTTP_201_CREATED)
def create_product(
payload: schemas.ProductCreate,
background_tasks: BackgroundTasks,
payload: schemas.ProductCreateWithSnapshot,
db: Session = Depends(get_db),
) -> schemas.ProductRead:
product = crud.create_product(db, payload)
# Déclenche automatiquement le scraping après création
background_tasks.add_task(scrape_product, product.id)
return product
) -> schemas.ProductWithSnapshot:
"""
Crée un produit avec ses données de snapshot initiales.
Les données proviennent de la prévisualisation (/scrape/preview).
"""
product = crud.create_product_with_snapshot(db, payload)
# Retourner le produit enrichi avec le snapshot créé
return crud.get_product_with_snapshot(db, product.id)
@router.get("/{product_id}", response_model=schemas.ProductWithSnapshot)
+19 -2
View File
@@ -1,12 +1,29 @@
from __future__ import annotations
from fastapi import APIRouter, BackgroundTasks
from fastapi import APIRouter, BackgroundTasks, HTTPException
from pydantic import BaseModel, HttpUrl
from backend.app.scraper.runner import scrape_all, scrape_product
from backend.app.scraper.runner import scrape_all, scrape_preview, scrape_product
router = APIRouter(prefix="/scrape", tags=["scrape"])
class PreviewRequest(BaseModel):
url: HttpUrl
@router.post("/preview")
def preview_scrape(payload: PreviewRequest):
"""
Scrape une URL Amazon sans enregistrer en base.
Retourne les données pour prévisualisation avant ajout.
"""
result = scrape_preview(str(payload.url))
if not result["success"] and result["error"]:
raise HTTPException(status_code=422, detail=result["error"])
return result
@router.post("/product/{product_id}")
def trigger_single(product_id: int, background_tasks: BackgroundTasks):
# on délègue le vrai travail à un background task rapide
+52
View File
@@ -38,6 +38,58 @@ def create_product(db: Session, data: schemas.ProductCreate) -> models.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",
]
snapshot_data = {k: data_dict.pop(k) for k in snapshot_fields if k in data_dict}
# 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)
+17
View File
@@ -87,3 +87,20 @@ class ProductWithSnapshot(ProductBase):
class Config:
orm_mode = True
class ProductCreateWithSnapshot(ProductBase):
"""Création d'un produit avec données de snapshot initiales (depuis preview)."""
# Données du snapshot initial
prix_actuel: Optional[float] = None
prix_conseille: Optional[float] = None
prix_min_30j: Optional[float] = None
etat_stock: Optional[str] = None
en_stock: Optional[bool] = None
note: Optional[float] = None
nombre_avis: Optional[int] = None
prime: Optional[bool] = None
choix_amazon: Optional[bool] = None
offre_limitee: Optional[bool] = None
exclusivite_amazon: Optional[bool] = None
+6
View File
@@ -44,5 +44,11 @@ def health() -> dict[str, str]:
return {"statut": "ok"}
@app.get("/version")
def version() -> dict[str, str]:
"""Retourne la version du backend."""
return {"version": "0.1.0"}
def main() -> FastAPI:
return app
File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

File diff suppressed because one or more lines are too long
Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 MiB

Some files were not shown because too many files have changed in this diff Show More