# Prompt Codex — Projet **suivi_produits** (Amazon.fr → extensible multi-stores)

## Rôle de Codex
Tu es un **agent senior** en développement **Python web**, **Playwright**, **SQL/SQLite**, **architecture logicielle**, **UI frontend** (responsive). 
tu me parlera en francais dans la discussion et le code devra etre commenté regulieremnt en francais aussi.
Tu dois :
1) faire un **brainstorming** pragmatique, 2) proposer un **plan** en phases, 3) définir une **structure de projet** claire, 4) implémenter un **MVP** testable, 5) industrialiser (tests, logs, cron, docker-compose), 6) préparer l’**évolution multi-boutiques**.

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

---

## Structure de projet (proposée et a adpater)
```
suivi_produits/
  README.md
  TODO.md
  CHANGELOG.md
  kanban.md
  docs/
  backend/
    app/
      main.py
      api/
        routes_products.py
        routes_scrape.py
        routes_config.py
        routes_stats.py
      core/
        config.py            # charge config_backend.json
        logging.py
        scheduler.py         # déclenche scrapes (optionnel)
      db/
        database.py          # sqlite connection
        models.py            # SQLAlchemy models
        schemas.py           # pydantic
        crud.py
        migrations/          # si besoin plus tard
      scraper/
        amazon/
          parser.py          # extraction DOM -> dict normalisé
          selectors.md       # doc sélecteurs
        browser.py           # création context Playwright
        runner.py            # scrap 1 / scrap all
        normalize.py         # parsing prix, notes, booléens
      services/
        pricing.py           # calculs 30j si données
        images.py
      tests/
        test_normalize.py
        test_parser_samples.py
      samples/
        amazon_product.html  # snapshots HTML (tests)
    config_backend.json
    logs/
      scrap.log
    data/
      raw/
      screenshots/

  frontend/
    src/
      app/
      components/
      styles/
      api/
    public/
    config_frontend.json

  docker/
    docker-compose.yml
    backend.Dockerfile
    frontend.Dockerfile
    nginx.conf (option)
```

---

## 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é

---

## `config_backend.json` (spécification)
Contenu attendu :
- `app`: { `env`, `version`, `base_url`, `log_level` }
- `scrape`:
  - `interval_minutes` (pour cron/worker)
  - `headless` (bool)
  - `timeout_ms`
  - `retries`
  - `delay_range_ms`: [min,max]
  - `user_agent`
  - `viewport`: {w,h}
  - `locale`, `timezone`
  - `proxy` (option)  => non pas de proxy
- `stores_enabled`: ["amazon_fr"]
- `taxonomy`:
  - `categories`: ["SSD", "CPU", ...]
  - `types_by_category`: { "SSD": ["NVMe", "SATA"], ... }

---

## `config_frontend.json` (spécification)
- `ui`:
  - `theme`: "gruvbox_vintage_dark"
  - 'button_mode': " text/icon"
  - `columns_desktop`: 3 (ex) => slider
  - `card_density`: "comfortable"|"compact"
  - `show_fields`: { flags }
  - `refresh_auto_seconds`
- `versions`: { `frontend`, `backend_expected` }

---

## Schéma base de données (SQLite)
Proposer un schéma minimal + extensible.

### Tables
1) `products`
- `id` (PK)
- `store` (ex: amazon_fr)
- `url`
- `asin`
- `title`
- `image_url`
- `category`
- `type`
- `is_active` (bool)
- `created_at`, `updated_at`

2) `scrape_runs`
- `id` (PK)
- `started_at`, `ended_at`
- `status` (success/partial/failed)
- `items_total`, `items_ok`, `items_failed`
- `log_path` (option)

3) `product_snapshots`
- `id` (PK)
- `product_id` (FK → products.id)
- `scraped_at`
- `price_current`
- `price_list` (nullable)
- `lowest_30d_price` (nullable)
- `stock_text` (nullable)
- `in_stock` (nullable)
- `rating_value` (nullable)
- `rating_count` (nullable)
- `prime_eligible` (nullable)
- `amazon_choice` (nullable)
- `limited_time_deal` (nullable)
- `amazon_exclusive` (nullable)
- `raw_json_path` (nullable)
- `scrape_status` (ok/blocked/missing_fields/error)
- `error_message` (nullable)

4) (option) `tags`
- `id`, `name`

5) (option) `product_tags`
- `product_id`, `tag_id`

### Relations
- `products (1) ─── (N) product_snapshots`
- `scrape_runs (1) ─── (N) product_snapshots` (optionnel si on lie snapshots à run)

### Indices
- index `(product_id, scraped_at)`
- index `asin`

---
page web : logs et debug

## Plan de développement (phases)
### Phase 0 — Setup repo & conventions
- initialiser repo `suivi_produits`
- ajouter `README.md`, `TODO.md`, `CHANGELOG.md`, `kanban.md`
- définir conventions : formatage, lint, tests

### Phase 1 — MVP Backend + DB
- FastAPI + SQLite + SQLAlchemy
- endpoints :
  - `GET /health`
  - `GET/POST/PUT/DELETE /products`
  - `POST /scrape/product/{id}`
  - `POST /scrape/all`
  - `GET /products/{id}/history?days=30`
  - `GET/PUT /config/backend`

### Phase 2 — Scraper Amazon (Playwright)
- module `scraper/amazon/parser.py`
- normalisation des prix (ex: "249,99 €" → 249.99)
- gestion champs optionnels (pas d’invention)
- logs + debug artifacts
- tests unitaires sur HTML samples (fichiers dans `samples/`)

### Phase 3 — Frontend (vignettes + graph)
- grille responsive, colonnes paramétrables
- composant CardProduit
- graphique 30j (lib : chart.js / echarts / recharts selon stack)
- page Settings : frontend + backend config éditables

### Phase 4 — Scheduler / Cron
- job toutes les X minutes/heures (config)
- stockage des snapshots
- endpoint stats (dernier scrap, erreurs, taux de succès)

### Phase 5 — Docker Compose
- dockeriser backend + frontend
- volume persistant SQLite + logs + raw json
- doc d’installation

### Phase 6 — Évolution multi-stores
- abstraction `StoreScraper` (interface)
- un module par boutique : `scraper/<store_name>/...`
- routing : store → scraper

---

## Tests (exigences)
- Tests unitaires :
  - parsing prix FR
  - extraction rating/count
  - présence/absence champs optionnels
- Tests d’intégration :
  - scrap d’un produit réel (option “manual”) avec variable `RUN_LIVE_SCRAPE=1`
  - sinon, replay HTML sample

---

## Livrables attendus
- Code complet backend + frontend (MVP fonctionnel)
- `docker-compose.yml`
- docs :
  - `README.md` (install, run, config)
  - `TODO.md` (backlog)
  - `CHANGELOG.md` (semver simple)
  - `kanban.md` (colonnes : Backlog / Doing / Review / Done)
- Schéma DB + migrations (si besoin)

---

## Questions à poser (bloquantes ou importantes)
1) Frontend : tu préfères **React** (Vite) ou **Svelte** ? (sinon choisis la voie la plus simple et robuste)
2) Déclenchement cron : tu veux un **cron linux** (container cron) ou un **scheduler interne** (APScheduler) ?
3) Auth : l’app est-elle accessible uniquement en LAN (pas d’auth) ou tu veux un login simple ? => non
4) Stockage raw JSON : tu veux conserver **tous** les raw ou uniquement les derniers N jours ? => oui
5) Mode “captcha” : en cas de blocage, tu veux :
   - (a) abandon + log + retry plus tard => oui
   - (b) ouvrir navigateur headful pour résolution manuelle


Si pas de réponse, prends des décisions raisonnables : React+Vite, scheduler interne APScheduler, pas d’auth (LAN), raw conservé 30 jours, stratégie (a).

---

## Notes de qualité (non négociables)
- Ne jamais faire planter un scrap si un champ manque.
- Ne pas calculer des métriques si aucune donnée n’est disponible sur la page.
- UI : lisibilité d’abord (contraste, spacing, hiérarchie typographique).
- Logs : chaque scrap doit laisser une trace claire (start/end, erreurs, champs manquants).

---

## Démarrage immédiat (premières tâches)
1) Créer squelette repo + outils (poetry/pip-tools, pre-commit, ruff, mypy)
2) Implémenter DB + CRUD produits
3) Implémenter scrap d’un produit Amazon + snapshot
4) Afficher vignettes + graph 30j
5) Ajouter Settings + configs JSON éditables
6) Dockeriser
