# 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`)