# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Commandes Essentielles ### Backend (Poetry) ```bash poetry install # Installation des dépendances poetry run pytest # Tests unitaires poetry run ruff check backend/ # Linting poetry run mypy backend/ # Vérification de types poetry run coverage run -m pytest && poetry run coverage report -m # Couverture ``` ### Scraper CLI (test manuel) ```bash SCRAPE_TEST_MAX=0 poetry run python backend/app/scraper/run_scrape_tests.py # Avec navigateur visible si blocage Amazon: SCRAPE_TEST_HEADFUL_ON_BLOCK=1 SCRAPE_TEST_MAX=0 poetry run python backend/app/scraper/run_scrape_tests.py ``` ### Serveur de développement ```bash poetry run uvicorn backend.app.main:app --reload # Backend (port 8008) cd frontend && npm run dev # Frontend (port 5173) ``` ### Frontend (npm) ```bash cd frontend && npm install && npm run dev # Dev cd frontend && npm run build # Build production ``` ## Architecture ### Stack technique - **Backend**: FastAPI + SQLAlchemy (SQLite) + APScheduler + Loguru - **Scraper**: Playwright (Chromium) + BeautifulSoup4, contexte fr-FR - **Frontend**: React 18 + Vite + Zustand + SCSS Gruvbox dark ### Structure clé ``` ├── agent.md ├── backend │ ├── app │ │ ├── api │ │ │ ├── deps.py │ │ │ ├── __init__.py │ │ │ ├── routes_config.py │ │ │ ├── routes_products.py │ │ │ └── routes_scrape.py │ │ ├── core │ │ │ ├── config.py │ │ │ ├── logging.py │ │ │ ├── __pycache__ │ │ │ └── scheduler.py │ │ ├── db │ │ │ ├── crud.py │ │ │ ├── database.py │ │ │ ├── models.py │ │ │ └── schemas.py │ │ ├── __init__.py │ │ ├── main.py │ │ ├── __pycache__ │ │ │ └── __init__.cpython-313.pyc │ │ ├── samples │ │ │ ├── amazon_product.html │ │ │ ├── debug │ │ │ ├── scrape_fields.json │ │ │ ├── scrape_test.json │ │ │ ├── scrap_result.json │ │ │ └── storage_state.json │ │ ├── scraper │ │ │ ├── amazon │ │ │ ├── browser.py │ │ │ ├── normalize.py │ │ │ ├── __pycache__ │ │ │ ├── runner.py │ │ │ └── run_scrape_tests.py │ │ ├── services │ │ │ ├── __init__.py │ │ │ ├── pricing.py │ │ │ └── __pycache__ │ │ └── tests │ │ ├── __init__.py │ │ ├── __pycache__ │ │ ├── test_normalize.py │ │ ├── test_parser_samples.py │ │ └── test_pricing.py │ ├── config_backend.json │ ├── data │ │ ├── raw │ │ └── screenshots │ ├── __init__.py │ ├── logs │ └── __pycache__ │ └── __init__.cpython-313.pyc ├── CHANGELOG.md ├── CLAUDE.md ├── consigne codex.md ├── docker ├── docs │ ├── ARCHITECTURE.md │ ├── db_structure.md │ ├── frontend_structure.md │ ├── scrap.md │ └── tools.md ├── frontend │ ├── config_frontend.json │ ├── package.json │ ├── public │ │ └── index.html │ ├── src │ │ ├── api │ │ │ └── client.js │ │ ├── app │ │ ├── App.jsx │ │ ├── components │ │ │ └── ProductCard.jsx │ │ ├── main.jsx │ │ └── styles │ │ └── global.scss │ └── vite.config.js ├── kanban.md ├── pyproject.toml ├── README.md └── TODO.md ``` ### Flux de scraping 1. Playwright charge la page avec délais aléatoires (1-3s) 2. Détection blocage (captcha/robot) → artéfacts debug (screenshot + HTML) 3. Extraction via CSS selectors → normalisation (prix, stock, ratings) 4. Sauvegarde: snapshot DB + raw JSON dans `backend/data/raw/` ### Base de données (SQLite) - **products**: URL, ASIN, titre, catégorie, statut actif - **product_snapshots**: prix, stock, ratings, badges, status scrape, lien vers raw JSON - **scrape_runs**: métadonnées de chaque exécution (success/fail counts) ### Configuration - `backend/config_backend.json`: intervalle scrape, timeout, browser settings - `frontend/config_frontend.json`: thème, layout grille, champs visibles - `.env`: variables d'environnement (copier `.env.example`) ## Règles de développement ### Libertés accordées - Créer/modifier fichiers backend, frontend, docs - Refactorer pour lisibilité/robustesse - Lancer tests et scripts de validation ### Actions nécessitant approbation - Refonte architecture (stack, framework) - Suppression/reset données utilisateur (tables, raw JSON, logs) - Migrations DB irréversibles ### Qualité code - Chaque scrape produit un log clair dans `backend/logs/scrap.log` (rotation 10MB, 7j) - Erreurs scraping = résilience (log + artéfacts) sans crash global - Logique métier critique accompagnée de tests ### Commits - Messages courts explicites: `feat: add scraper runner` - Un commit = une unité cohérente - Pas de commits fourre-tout ## Notes importantes - Le scraping est volontairement lent (15-20 produits/jour) pour éviter blocage Amazon - Raw JSON conservé 30 jours dans `backend/data/raw/` - Pre-commit hooks configurés (Ruff + MyPy): `pre-commit install` # Schéma ASCII (UI globale) Objectif : reproduire l’esprit de la capture (vignette + section prix + graphe), en améliorant la lisibilité. ``` ┌──────────────────────────────────────────────────────────────────────────┐ │ suivi_produits [Add Product] [Refresh] [Settings] FE vX BE vY │ │ (header fixed) [debug] (⋯) │ ├──────────────────────────────────────────────────────────────────────────┤ │ Grid (cols = config_frontend.json) │ │ │ │ ┌──────────────────────────── Card Produit ──────────────────────────┐ │ │ │ Boutique + Titre (2 lignes) │ │ │ │ Amazon │ │ │ │ Samsung SSD Interne 9100 Pro… │ │ │ │ │ │ │ │ ┌───────────────┐ ┌──────────────────────────────────────────┐ │ │ │ │ │ Image │ │ ACTUEL 249€99 │ │ │ │ │ │ (non rognée) │ │ PRIX CONSEILLÉ 329€99 (si présent) │ │ │ │ │ │ │ │ RÉDUCTION -24% (si présent) │ │ │ │ │ └───────────────┘ │ STOCK Disponible │ │ │ │ │ │ NOTE 4,7 (967) │ │ │ │ │ │ CHOIX AMAZON Oui/Non │ │ │ │ │ │ PRIME Oui/Non │ │ │ │ │ │ DEAL Oui/Non │ │ │ │ │ │ Ref: ASIN [Lien produit] │ │ │ │ │ └──────────────────────────────────────────┘ │ │ │ │ │ │ │ │ ┌────────────────────── Graph 30j (clair) ─────────────────────┐ │ │ │ │ │ ligne + points, axes lisibles, tooltip │ │ │ │ │ └──────────────────────────────────────────────────────────────┘ │ │ │ │ │ │ │ │ Min 249€99 Max 249€99 Tendance → stable +0.0% Dernier: now │ │ │ │ Catégorie: SSD Type: NVMe │ │ │ │ │ │ │ │ [Scrap] [Edit] [Delete] [Détail] │ │ │ └────────────────────────────────────────────────────────────────────┘ │ │ │ └──────────────────────────────────────────────────────────────────────────┘ ``` page de debug et log qui affiche le contenue des differentes tables sqlite dans des section distincte, une section log qui affiche les log json de scrap ## Vignette produit : exigences UI - Image **non tronquée** : object-fit `contain`, fond neutre, padding. - Section prix alignée au niveau de l’image (descendue comme sur la capture). - “Boutique + titre” sur 2 lignes, pleine largeur, icône boutique. - Badges : Amazon Choice / Prime / Deal / Exclusive (chips). - Graph 30j : - axes + labels lisibles - points visibles - min/max/tendance affichés sous le graphe - couleurs + flèches : - baisse : vert + flèche ↓ - stable : jaune/orange + → - hausse : rouge + ↑ - Responsive : - `columns` paramétrable (desktop) - mobile : 1 colonne + layout empilé Contraintes clés : - Scraping : **Python + Playwright** (robuste, peu de produits : 15–20/jour). - Stockage : **JSON** (raw scrap) + persistance en **SQLite** (historique prix + métriques). - Config : - `config_backend.json` (paramètres scraping + backend + catégories/types) - `config_frontend.json` (paramètres UI + colonnes + thème + bouton mode text/icon) - Logs : fichier de log des scrapes (rotation simple). - UI : **Gruvbox vintage dark**, moderne (ombres, arrondis, typo lisible), responsive. icon fa - Déploiement : test en mode .env puis deploiement final dans **docker-compose** (backend + frontend) + cron/worker pour scrapes périodiques. - Repo : sur mon serveur Gitea, nom : **suivi_produits** : https://gitea.maison43.duckdns.org/gilles/suivi_produit --- ## Objectif produit (fonctionnel) Application self-hosted pour suivre l’évolution de produits Amazon.fr (puis autres stores). L’utilisateur ajoute des URLs de produits, l’app : - scrape les données clés (prix, stock, note, badges, image, etc.) - stocke un **snapshot** à chaque scraping - affiche des **vignettes produit** + **graphique historique** clair (tendance, min/max, %) - propose actions : **Scrap**, **Edit**, **Delete**, **Détail** - lance un **scraping planifié** (cron) sur tous les produits à intervalle défini. --- ## Données à capturer (Amazon.fr) ### Champs de base (toujours) - `url` (canonique) + `asin` - `title` (nom produit) - `image_main_url` (image principale) - `price_current` (valeur numérique + devise) - `stock_status` (texte) + `in_stock` (bool) - `rating_value` (float) + `rating_count` (int) ### Champs conditionnels (si présents) - `price_list` / prix conseillé / prix barré (si affiché) - `discount_percent` (si affiché ou calculé) - `lowest_30d_price` (si mention “prix le plus bas des 30 derniers jours”) - `amazon_choice` (badge) - `limited_time_deal` (offre à durée limitée) - `prime_eligible` (badge prime / livraison prime) - `amazon_exclusive` (mention exclusivité) ### Calculs à faire côté app (pas “inventer”) > Important : **ne pas “calculer une réduction” si le champ source n’existe pas**. on ne calcule rien sauf demande explicite de ma part ( peut etre tendance dans courbe historique) --- ## Méthode de scraping (sûre/efficace) ### Stratégie Playwright - Navigateur Chromium. - Context : - locale `fr-FR` - timezone `Europe/Paris` - viewport réaliste (ex. 1366×768 ou 1920×1080) - user-agent récent - Rythme : faible (15–20 produits/jour) + **delays aléatoires** 1–3s entre pages. - Détection blocages : - si page contient captcha / robot-check → marquer scrap “blocked” + screenshot + html dump pour debug. - Résilience : - retry 1–2 fois max avec backoff, sinon échec contrôlé. ### Sélecteurs (approche robuste) - Priorité : **IDs stables** (ex : `#productTitle`, `#acrCustomerReviewText`, `#availability`) - Prix : gérer variantes (prix fractionné, promo, etc.) - Fallback : si sélecteur absent, log “missing field”, ne pas planter. ### Artifacts de debug À chaque scrap : - sauvegarder un JSON “raw” normalisé - en cas d’échec : `page.screenshot()` + `page.content()` dans un dossier `debug/` horodaté. --- ## Architecture cible ### Backend - API HTTP (proposé : **FastAPI**) : - CRUD produits - déclenchement scrap (produit / tous) - lecture historique + agrégats (min/max/tendance) - lecture/écriture configs frontend/backend - Worker de scraping (Playwright) séparé en module “scraper” - Scheduler (cron interne ou cron container) qui appelle `scrape_all` ### Frontend - SPA simple (proposé : **Vite + React** ou **Svelte**) ou HTML server-side minimal (selon simplicité). - Thème **Gruvbox vintage dark** : - fond #282828, cartes #3c3836, texte #ebdbb2 - accent orange #fe8019, jaune #fabd2f, vert #b8bb26 - Responsive : nombre de colonnes configurable. - utilisation de popup lors de l'ajout de produit ou acces a setting ### Stockage - le stockage se fais uniquement lors de l'enregistrement du produit - SQLite : tables normalisées (produits, snapshots, tags/catégories/types) - JSON “raw” : archivage optionnel (dossier `data/raw/YYYY-MM/...json`)