diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..05b85c7 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,327 @@ +# 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`) \ No newline at end of file diff --git a/TODO.md b/TODO.md index 874235b..ef5a7a1 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,22 @@ # TODO -- [ ] init repo pour backend FastAPI + logging + config -- [ ] créer scraper Amazon via Playwright avec parser robuste -- [ ] définir UI Gruvbox + carte produit + graphique 30j -- [ ] intégrer scheduler APScheduler pour `scrape_all` + +## Phase 1 - Backend & Scraper (TERMINÉ ✓) +- [x] init repo pour backend FastAPI + logging + config +- [x] créer scraper Amazon via Playwright avec parser robuste +- [x] modèle SQLAlchemy (products, product_snapshots, scrape_runs) +- [x] API CRUD produits + endpoints scraping +- [x] tests unitaires parser, normalisation, pricing +- [x] intégrer scheduler APScheduler pour `scrape_all` + +## Phase 2 - Frontend (EN COURS) +- [ ] connecter App.jsx à l'API backend (fetch produits) +- [ ] implémenter ProductCard avec données réelles +- [ ] ajouter formulaire d'ajout de produit (URL Amazon) +- [ ] graphique Chart.js historique 30j +- [ ] store Zustand pour état global + +## Phase 3 - Industrialisation - [ ] dockeriser backend + frontend + scheduler -- [ ] ajouter page debug/logs affichant tables SQLite +- [ ] docker-compose avec volumes persistants +- [ ] page debug/logs affichant tables SQLite +- [ ] tests E2E frontend diff --git a/docs/plans/2026-01-18-frontend-phase2-design.md b/docs/plans/2026-01-18-frontend-phase2-design.md new file mode 100644 index 0000000..511b1f9 --- /dev/null +++ b/docs/plans/2026-01-18-frontend-phase2-design.md @@ -0,0 +1,193 @@ +# Plan Frontend Phase 2 - suivi_produits + +**Date** : 2026-01-18 +**Statut** : Validé +**Priorité** : Fonctionnel d'abord, puis visualisation + +--- + +## Contexte + +Le backend (Phase 1) est terminé : +- Scraper Playwright fonctionnel (9/9 produits OK) +- API FastAPI avec CRUD produits complet +- Modèles SQLAlchemy (products, snapshots, scrape_runs) +- Scheduler APScheduler +- Tests unitaires (7 tests OK) + +Le frontend est squelettique : App.jsx basique, ProductCard vide, pas de store. + +--- + +## Décisions de design + +| Question | Choix | +|----------|-------| +| Priorité | Fonctionnel d'abord | +| Ajout produit | URL seule (backend extrait ASIN + scrape immédiat) | +| State management | Zustand | +| Page debug | Dès le début (utile pendant le dev) | +| Routing | React Router (évolutivité multi-stores) | + +--- + +## Architecture Frontend + +``` +frontend/src/ +├── api/ +│ └── client.js # API calls (extensible par store) +├── components/ +│ ├── common/ # Composants réutilisables +│ │ ├── Modal.jsx +│ │ ├── Badge.jsx +│ │ └── PriceDisplay.jsx +│ ├── products/ +│ │ ├── ProductCard.jsx +│ │ ├── ProductGrid.jsx +│ │ └── AddProductForm.jsx +│ └── debug/ +│ ├── DbTable.jsx +│ └── LogViewer.jsx +├── pages/ +│ ├── HomePage.jsx # Grille produits +│ ├── DebugPage.jsx # Tables + logs +│ └── SettingsPage.jsx # (futur) Config frontend/backend +├── stores/ +│ └── useProductStore.js +├── hooks/ # (futur) Custom hooks +├── styles/ +│ ├── _variables.scss # Couleurs Gruvbox +│ └── global.scss +├── App.jsx # React Router setup +└── main.jsx +``` + +--- + +## Endpoints API + +| Endpoint | Méthode | Statut | Description | +|----------|---------|--------|-------------| +| `/products` | GET | Existe | Liste paginée | +| `/products` | POST | Existe | Créer produit | +| `/products/{id}` | GET/PUT/DELETE | Existe | CRUD | +| `/products/{id}/snapshots` | GET | À créer | Historique prix | +| `/products/{id}/scrape` | POST | À vérifier | Scrape un produit | +| `/scrape/all` | POST | À vérifier | Scrape tous | +| `/debug/tables` | GET | À créer | Dump tables SQLite | +| `/debug/logs` | GET | À créer | Derniers logs scrap | + +--- + +## Plan d'implémentation + +### Étape 1 : Page Debug (fondation) +**Objectif** : Avoir une vue sur les données pendant le développement + +Backend : +- [ ] Créer endpoint `GET /debug/tables` (dump products, snapshots, scrape_runs) +- [ ] Créer endpoint `GET /debug/logs` (lecture `backend/logs/scrap.log`) + +Frontend : +- [ ] Installer react-router-dom +- [ ] Setup React Router avec 2 routes (/, /debug) +- [ ] Créer `DebugPage.jsx` avec composants DbTable et LogViewer +- [ ] Afficher les 3 tables SQLite dans des sections distinctes +- [ ] Afficher les logs JSON de scraping + +--- + +### Étape 2 : Store Zustand + Liste produits +**Objectif** : Connexion frontend-backend fonctionnelle + +Frontend : +- [ ] Installer zustand +- [ ] Créer `useProductStore.js` avec actions (fetch, add, delete, scrape) +- [ ] Enrichir `client.js` avec toutes les fonctions fetch +- [ ] Connecter `HomePage.jsx` au store +- [ ] Afficher les produits existants (version basique) + +--- + +### Étape 3 : Ajout de produit +**Objectif** : Permettre d'ajouter un produit via URL Amazon + +Backend : +- [ ] Modifier `POST /products` pour accepter juste l'URL +- [ ] Extraire automatiquement l'ASIN de l'URL +- [ ] Déclencher scrape automatique après création + +Frontend : +- [ ] Créer `Modal.jsx` (composant réutilisable) +- [ ] Créer `AddProductForm.jsx` (input URL + validation) +- [ ] Intégrer dans Header avec bouton "Add Product" +- [ ] Gestion loading/error dans l'UI +- [ ] Refresh automatique après ajout + +--- + +### Étape 4 : Actions sur produit +**Objectif** : Pouvoir scraper et supprimer un produit + +Backend : +- [ ] Vérifier/créer endpoint `POST /products/{id}/scrape` + +Frontend : +- [ ] Ajouter boutons Scrap/Delete sur ProductCard +- [ ] Modal de confirmation avant suppression +- [ ] Feedback visuel pendant le scraping (spinner) +- [ ] Refresh automatique après action + +--- + +### Étape 5 : Amélioration visuelle ProductCard +**Objectif** : Vignette produit complète selon le schéma CLAUDE.md + +Frontend : +- [ ] Image non tronquée (object-fit: contain) +- [ ] Section prix (actuel, conseillé, réduction) +- [ ] Badges (Prime, Choix Amazon, Deal, Exclusivité) +- [ ] Note + nombre d'avis +- [ ] Stock status +- [ ] Responsive grid (colonnes configurables via config_frontend.json) + +--- + +### Étape 6 : Graphique historique (Phase 2.5) +**Objectif** : Visualiser l'évolution des prix sur 30 jours + +Backend : +- [ ] Créer endpoint `GET /products/{id}/snapshots` + +Frontend : +- [ ] Installer chart.js + react-chartjs-2 +- [ ] Créer composant PriceChart +- [ ] Intégrer dans ProductCard +- [ ] Afficher min/max/tendance sous le graphique +- [ ] Couleurs selon tendance (vert baisse, orange stable, rouge hausse) + +--- + +## Thème visuel + +Gruvbox vintage dark (défini dans CLAUDE.md) : +- Fond : #282828 +- Cartes : #3c3836 +- Texte : #ebdbb2 +- Accent orange : #fe8019 +- Accent jaune : #fabd2f +- Accent vert : #b8bb26 + +Icônes : Font Awesome + +--- + +## Critères de succès Phase 2 + +- [ ] Un utilisateur peut ajouter un produit via URL Amazon +- [ ] Le scraping se déclenche automatiquement +- [ ] Les produits s'affichent dans une grille responsive +- [ ] On peut supprimer un produit +- [ ] On peut déclencher un scrape manuel +- [ ] La page debug montre l'état des tables et logs diff --git a/kanban.md b/kanban.md index af258a8..ec34a8d 100644 --- a/kanban.md +++ b/kanban.md @@ -1,15 +1,22 @@ # Kanban ## Backlog -- Initialiser FastAPI + SQLite -- Parser Amazon + tests -- UI vignettes + graphique +- Docker Compose setup +- Page debug/logs SQLite +- Tests E2E frontend ## Doing -- En cours : structure repo +- Frontend: connecter App.jsx à l'API +- Frontend: ProductCard avec données réelles +- Frontend: formulaire ajout produit ## Review -- +- Scheduler APScheduler (fonctionnel, à tester en charge) ## Done -- +- Backend FastAPI + SQLite + logging +- Modèles SQLAlchemy (products, snapshots, runs) +- API CRUD produits + endpoints scraping +- Scraper Playwright + parser Amazon +- Tests unitaires (7 tests OK) +- Tests CLI scraper (9/9 produits OK)