Files
suivi_produit/CLAUDE.md
gilles 9a6448facc docs: add frontend Phase 2 implementation plan
- Add detailed 6-step development plan for frontend
- Define architecture with React Router for multi-store evolution
- Document API endpoints needed (existing + to create)
- Update TODO.md and kanban.md with current status

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-18 19:21:19 +01:00

327 lines
15 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 lesprit 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 limage (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 : 1520/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). Lutilisateur ajoute des URLs de produits, lapp :
- 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 nexiste 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 (1520 produits/jour) + **delays aléatoires** 13s entre pages.
- Détection blocages :
- si page contient captcha / robot-check → marquer scrap “blocked” + screenshot + html dump pour debug.
- Résilience :
- retry 12 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`)