docs: README à jour avec screenshot
2 changed files +83 −14
modified
README.md
+83 −14
@@ -6,6 +6,8 @@ | ||
| 6 | 6 | |
| 7 | 7 | **[www.food-ka.com](https://www.food-ka.com)** |
| 8 | 8 | |
| 9 | + | |
| 10 | + | |
| 9 | 11 |  |
| 10 | 12 |  |
| 11 | 13 |  |
@@ -17,9 +19,9 @@ | ||
| 17 | 19 |  |
| 18 | 20 |  |
| 19 | 21 | |
| 20 | −*Agrégateur indépendant de produits d'épicerie — chaque produit avec son prix courant, | |
| 21 | −son prix régulier, son prix unitaire comparable ($/100 g) et un lien direct vers la | |
| 22 | −fiche originale de la bannière. Toujours à jour, automatiquement.* | |
| 22 | +*Agrégateur indépendant de produits d'épicerie — chaque produit avec son **prix courant**, | |
| 23 | +son **prix régulier**, son **prix unitaire comparable ($/100 g)** et un lien direct vers la | |
| 24 | +fiche originale de la bannière. **Toujours à jour, automatiquement.*** | |
| 23 | 25 | |
| 24 | 26 | </div> |
| 25 | 27 | |
@@ -29,14 +31,27 @@ fiche originale de la bannière. Toujours à jour, automatiquement.* | ||
| 29 | 31 | |
| 30 | 32 | Comparer les prix d'épicerie au Québec, c'est ouvrir Metro, IGA, Maxi, Super C, |
| 31 | 33 | Provigo, Walmart… chacun avec sa propre navigation, son propre panier, son propre |
| 32 | −format. **Food-Ka retourne le problème** : un connecteur dédié par bannière visite | |
| 33 | −chaque site, normalise chaque produit vers un schéma unique, et détecte les | |
| 34 | −changements de prix en continu. | |
| 34 | +format. **Food-Ka retourne le problème** : un **connecteur dédié par bannière** visite | |
| 35 | +chaque site, **normalise chaque produit** vers un schéma unique, et **détecte les | |
| 36 | +changements de prix en continu**. | |
| 35 | 37 | |
| 36 | 38 | > Les épiceries n'offrent pas de webhooks. Food-Ka reproduit l'équivalent : |
| 37 | 39 | > **synchronisation périodique + hash de contenu** → nouveaux produits, changements |
| 38 | 40 | > de prix et retraits détectés automatiquement. Chaque variation de prix est |
| 39 | −> historisée (`price_log`) — les soldes deviennent traçables. | |
| 41 | +> **historisée** (`price_log`) — les soldes deviennent traçables. | |
| 42 | + | |
| 43 | +## Fonctionnalités | |
| 44 | + | |
| 45 | +- **Agrégation multi-bannières** — **24 connecteurs actifs** (sur **30 bannières recensées**), **22 000+ produits** couvrant **tout le Québec**. | |
| 46 | +- **Prix unitaire comparable** — chaque produit ramené en **$/100 g** pour comparer l'incomparable. | |
| 47 | +- **Détection des soldes** — prix courant vs **prix régulier**, tri par **rabais**, historique complet des variations. | |
| 48 | +- **Comparaison inter-bannières** — chaque fiche produit montre les **équivalents chez les autres bannières**. | |
| 49 | +- **Connexion KA ID** — **SSO du Groupe Ka** (courriel + Google) : un seul compte (`ka_id`) valable sur **toutes les plateformes ·Ka** (`foodka/auth.py`, profil via `hubprofile.py`). | |
| 50 | +- **Favoris** — cœur sur chaque produit, **favoris synchronisés au compte KA ID** via le hub central (`foodka/hubfav.py`, page **Profil** dans le frontend). | |
| 51 | +- **Recherche et filtres** — catégorie, bannière, marque, fourchette de prix, soldes, texte libre ; tris prix / prix unitaire / rabais / récents. | |
| 52 | +- **Stats Groupe KA** — tableau de bord analytique commun avec **export PDF** (`statsdash.py`, `kapdf.py`). | |
| 53 | +- **PWA installable** — design « éditorial sharp » (Space Grotesk, accent lime, ticker temps réel), **mobile-first**, cibles tactiles ≥ 44 px. | |
| 54 | +- **Design system ka-ui** — tokens, footer, badge et écosystème **Groupe Ka** partagés (13 sites), page `/contact`, widget **KA Agent** (bulle de chat IA). | |
| 40 | 55 | |
| 41 | 56 | ## L'architecture en 30 secondes |
| 42 | 57 | |
@@ -56,7 +71,41 @@ flowchart LR | ||
| 56 | 71 | F --> U["🛒 Consommateur"] |
| 57 | 72 | ``` |
| 58 | 73 | |
| 59 | −| Couche | Rôle | Fichiers | | |
| 74 | +## Stack technique | |
| 75 | + | |
| 76 | +| Couche | Techno | Rôle | | |
| 77 | +|---|---|---| | |
| 78 | +| **Backend** | **Python 3.14 + FastAPI** | API REST, sync, auth KA ID, favoris | | |
| 79 | +| **Frontend** | **React 18 + Vite + TypeScript** | PWA, react-router, design ka-ui | | |
| 80 | +| **Stockage** | **SQLite** (`data/foodka.db`) | produits, hash, `price_log`, `sync_log` | | |
| 81 | +| **Scraping** | requests + **Scrapfly** / **Firecrawl** | HTML, APIs JSON, `__NEXT_DATA__`, anti-bot | | |
| 82 | +| **Process** | **PM2** + **ngrok** | résilience (auto-restart) + tunnel | | |
| 83 | + | |
| 84 | +## Structure du projet | |
| 85 | + | |
| 86 | +``` | |
| 87 | +food-ka/ | |
| 88 | +├── run.py # CLI : sync · serve · watch | |
| 89 | +├── requirements.txt | |
| 90 | +├── foodka/ # backend Python | |
| 91 | +│ ├── web.py # API FastAPI (produits, facettes, stats, sync) | |
| 92 | +│ ├── auth.py # connexion KA ID (SSO Groupe Ka) | |
| 93 | +│ ├── hubfav.py # favoris synchronisés au hub KA ID | |
| 94 | +│ ├── hubprofile.py # profil utilisateur KA ID | |
| 95 | +│ ├── schema.py # Product normalisé + prix unitaire $/100 g | |
| 96 | +│ ├── db.py # diff engine (hash, upsert, price_log) | |
| 97 | +│ ├── ingest.py # orchestration des synchronisations | |
| 98 | +│ ├── normalize.py # catégories canoniques, parsing des prix | |
| 99 | +│ ├── statsdash.py # tableau de bord Stats Groupe KA | |
| 100 | +│ ├── kapdf.py / pdfgen.py# export PDF | |
| 101 | +│ └── connectors/ # 1 module auto-découvert par bannière | |
| 102 | +├── frontend/ # React 18 + Vite + TS (PWA, ka-ui vendorisé) | |
| 103 | +├── data/ # foodka.db + sources.json (registre des bannières) | |
| 104 | +├── docs/ # screenshot.png | |
| 105 | +└── tests/ | |
| 106 | +``` | |
| 107 | + | |
| 108 | +| Couche | Détail | Fichiers | | |
| 60 | 109 | |---|---|---| |
| 61 | 110 | | **Connecteurs** | 1 module Python par bannière : HTML rendu serveur, API JSON ouvertes (Shopify `products.json`, WooCommerce Store API), `__NEXT_DATA__` (Loblaw), ou **Scrapfly** (anti-bot ASP + rendu JS) / **Firecrawl** pour les sites protégés | `foodka/connectors/*.py` | |
| 62 | 111 | | **Schéma** | `Product` standardisé : nom, marque, format, prix, prix régulier, **prix unitaire $/100 g**, catégorie canonique, images | `foodka/schema.py` | |
@@ -64,10 +113,10 @@ flowchart LR | ||
| 64 | 113 | | **API** | Filtres catégorie / bannière / marque / prix / soldes / recherche, tris (prix, prix unitaire, rabais), facettes, stats | `foodka/web.py` | |
| 65 | 114 | | **Frontend** | Design « éditorial sharp » : Space Grotesk, ombres décalées, accent lime, ticker temps réel, fiches produit avec comparaison inter-bannières, PWA installable | `frontend/` | |
| 66 | 115 | |
| 67 | −## Démarrage rapide | |
| 116 | +## Démarrage local | |
| 68 | 117 | |
| 69 | 118 | ```bash |
| 70 | −git clone https://github.com/spboucher-ai/food-ka.git && cd food-ka | |
| 119 | +git clone gitsrv:srv/git/food-ka.git && cd food-ka | |
| 71 | 120 | |
| 72 | 121 | # Backend |
| 73 | 122 | python3 -m venv .venv && .venv/bin/pip install -r requirements.txt |
@@ -128,6 +177,10 @@ correspondante dans `data/sources.json` pour la page **Sources**. | ||
| 128 | 177 | | `GET /api/stats` | Totaux, soldes actifs, meilleures aubaines, journal de synchronisation | |
| 129 | 178 | | `POST /api/sync` | Déclenche une synchronisation en arrière-plan | |
| 130 | 179 | |
| 180 | +L'authentification **KA ID** et les **favoris** exposent leurs propres routes | |
| 181 | +(`foodka/auth.py`, `foodka/hubfav.py`) adossées au **hub SSO central du Groupe Ka** | |
| 182 | +(groupe-ka.com). | |
| 183 | + | |
| 131 | 184 | ## Couverture |
| 132 | 185 | |
| 133 | 186 | **Grandes bannières** — Metro, Super C, IGA (Voilà), Maxi, Provigo, Walmart Canada, |
@@ -142,19 +195,33 @@ Chaque bannière non-connectable est **documentée avec sa raison** dans | ||
| 142 | 195 | `data/sources.json` (ex. : prix liés à une session Instacart/DoorDash, catalogue |
| 143 | 196 | sans prix, anti-bot strict). |
| 144 | 197 | |
| 145 | −## Production | |
| 198 | +## Déploiement (production) | |
| 146 | 199 | |
| 147 | −Déployé sous **PM2** (3 processus) derrière **ngrok** : | |
| 200 | +- **Nœud** : **M4M64b** (Mac Studio, 16 cœurs / 64 Go) — répertoire `~/apps/food-ka` | |
| 201 | +- **Port** : **8097** | |
| 202 | +- **Domaine** : **[www.food-ka.com](https://www.food-ka.com)** (tunnel **ngrok**) | |
| 203 | +- **Process manager** : **PM2** (3 processus, auto-restart) : | |
| 148 | 204 | |
| 149 | 205 | ``` |
| 150 | −food-ka-web .venv/bin/python run.py serve 8096 # API + frontend | |
| 206 | +food-ka-web .venv/bin/python run.py serve 8097 # API + frontend | |
| 151 | 207 | food-ka-sync .venv/bin/python run.py watch 360 # resync aux 6 h |
| 152 | −food-ka-ngrok ngrok http --url=www.food-ka.com 8096 # tunnel | |
| 208 | +food-ka-ngrok ngrok http --url=www.food-ka.com 8097 # tunnel | |
| 153 | 209 | ``` |
| 154 | 210 | |
| 155 | 211 | Philosophie d'exploitation : **on ne pousse que le code — le serveur maintient ses |
| 156 | 212 | données lui-même.** |
| 157 | 213 | |
| 214 | +## Développement remote-first (IMPORTANT) | |
| 215 | + | |
| 216 | +La **source de vérité est le repo git SUR le nœud M4M64b** (`~/apps/food-ka`), **pas | |
| 217 | +une copie locale**. Toute modification se fait **via SSH sur le nœud** : édition, | |
| 218 | +`npm run build` du frontend, `pm2 restart food-ka-web`, puis commit/push **depuis le | |
| 219 | +nœud** (agent forwarding actif). | |
| 220 | + | |
| 221 | +- **Remote `origin` = spbgit** (git perso, **git.spboucher.ai**) via l'**alias SSH `gitsrv`** : | |
| 222 | + `gitsrv:srv/git/food-ka.git` (bare repo hébergé sur M3U96a). **Pas GitHub.** | |
| 223 | +- Ne **jamais** éditer d'éventuelles copies laptop — elles ne sont pas synchronisées. | |
| 224 | + | |
| 158 | 225 | ## Principes |
| 159 | 226 | |
| 160 | 227 | 1. **Politesse** — délai ≥ 0,5 s entre requêtes, périmètre de crawl borné, |
@@ -183,4 +250,6 @@ données lui-même.** | ||
| 183 | 250 | |
| 184 | 251 | © 2026 Simon-Pierre Boucher — tous droits réservés. |
| 185 | 252 | |
| 253 | +**Un service [Groupe Ka](https://www.groupe-ka.com)** | |
| 254 | + | |
| 186 | 255 | </div> |
added
docs/screenshot.png
+0 −0
Binary file not shown.