docs: README v3 — chiffres live vérifiés, pastilles dynamiques, sections complètes
1 changed file +89 −19
modified
README.md
+89 −19
@@ -13,18 +13,32 @@ | ||
| 13 | 13 | [](https://www.auto-ka.com/doc/auto-ka-documentation.pdf) |
| 14 | 14 |  |
| 15 | 15 |  |
| 16 | − | |
| 16 | + | |
| 17 | + | |
| 18 | +[](https://www.auto-ka.com) | |
| 19 | +[](https://www.auto-ka.com/sources) | |
| 20 | +[](https://www.auto-ka.com/sources) | |
| 21 | +[](https://www.auto-ka.com) | |
| 22 | +[](https://www.auto-ka.com) | |
| 23 | +[](https://www.auto-ka.com/stats) | |
| 24 | + | |
| 17 | 25 |  |
| 18 | 26 |  |
| 19 | − | |
| 27 | + | |
| 28 | + | |
| 20 | 29 |  |
| 21 | − | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 22 | 34 | |
| 23 | 35 | </div> |
| 24 | 36 | |
| 37 | +> Les pastilles de la deuxième rangée sont **dynamiques** : elles interrogent [`/api/stats`](https://www.auto-ka.com/api/stats) en direct. | |
| 38 | + | |
| 25 | 39 | **Auto-Ka** est un agrégateur indépendant de véhicules usagés couvrant la province de Québec. Chercher une auto usagée, c'est normalement ouvrir des dizaines de sites de concessionnaires — chacun avec sa navigation, ses filtres, son format. Auto-Ka retourne le problème : un **connecteur dédié par commerce** visite chaque site **à la source** (aucune plateforme d'annonces revendue), **normalise** chaque véhicule vers un schéma unique et **détecte les changements en continu**. |
| 26 | 40 | |
| 27 | −Les sites de concessionnaires n'offrent pas de webhooks ; Auto-Ka en reproduit l'équivalent : synchronisation périodique + hash de contenu → **arrivages**, **baisses de prix** et **ventes** détectés automatiquement. Un véhicule qui disparaît du site source est marqué vendu — et sa page renvoie alors un vrai `410 Gone` aux moteurs de recherche. En date du 2026-08-24, le parc compte **48 456 véhicules** provenant de **138 concessionnaires** (129 sources actives) répartis dans les **18 régions** du Québec, avec **16 969 rappels** de sécurité croisés et **9 889 doublons VIN** masqués. | |
| 41 | +Les sites de concessionnaires n'offrent pas de webhooks ; Auto-Ka en reproduit l'équivalent : synchronisation périodique + hash de contenu → **arrivages**, **baisses de prix** et **ventes** détectés automatiquement. Un véhicule qui disparaît du site source est marqué vendu — et sa page renvoie alors un vrai `410 Gone` aux moteurs de recherche. Au 2026-08-24, le parc compte **48 388 véhicules** provenant de **138 concessionnaires** (**129 sources actives**, sur un registre de **143 sources**) répartis dans les **18 régions** du Québec, avec **16 969 rappels** de sécurité croisés et **10 158 doublons VIN** masqués. | |
| 28 | 42 | |
| 29 | 43 | ## Visite guidée |
| 30 | 44 | |
@@ -49,7 +63,7 @@ Les sites de concessionnaires n'offrent pas de webhooks ; Auto-Ka en reproduit l | ||
| 49 | 63 | - **Fiches véhicule complètes** — galerie photos, caractéristiques standardisées, **VIN**, équipements, **historique de prix**, véhicules similaires toutes sources confondues, et lien direct vers l'annonce originale du concessionnaire, toujours. |
| 50 | 64 | - **Trois verticales, un moteur** — le champ `kind` (`auto` / `moto` / `scooter`) traverse tout le pipeline ; motos et scooters ont leurs onglets dédiés. |
| 51 | 65 | - **Suivi des baisses de prix** — le diff engine (upsert par hash de contenu) détecte nouveau / modifié / vendu, avec délai de grâce et détection de dérive. |
| 52 | −- **Rappels de sécurité** — croisement avec les rappels constructeurs (`autoka/recalls.py`), 16 969 rappels référencés. | |
| 66 | +- **Rappels de sécurité** — croisement avec les rappels constructeurs (`autoka/recalls.py`), 16 969 rappels référencés (au 2026-08-24). | |
| 53 | 67 | - **Déduplication par VIN** — les annonces multi-sites d'un même véhicule sont détectées et masquées (`autoka/dedup.py`). |
| 54 | 68 | - **Statistiques du marché en direct** — tuiles de synthèse, distributions de prix, répartitions par région / marque / carrosserie, et **rapports PDF personnalisés** (stats v3 : catalogue, rendu au choix, ReportBuilder — `autoka/pdfgen.py`, `kapdf.py`). |
| 55 | 69 | - **SEO programmatique massif** — rendu serveur complet (`autoka/seo.py`) : title/meta uniques, canonical, Open Graph, hreflang `fr-CA`, JSON-LD `Car`/`Motorcycle` + `Offer` + `BreadcrumbList`, pages programmatiques (`/usagees/{marque}`, `/region/…`, `/ville/…`, `/carrosserie/…`, `/motos/…`), sitemaps dynamiques avec `lastmod` réels, vendu → 410, inconnu → 404. |
@@ -57,6 +71,41 @@ Les sites de concessionnaires n'offrent pas de webhooks ; Auto-Ka en reproduit l | ||
| 57 | 71 | - **Connecteurs auto-découvrants** — ajouter un concessionnaire = une sous-classe de `BaseConnector` (~6 lignes) déposée dans `autoka/connectors/` (familles D2C Media, SM360, AMVOQ/AutoUsagee, EvalAuto, Convertus, PowerGo, HGrégoire…). |
| 58 | 72 | - **Fidélité** — aucun prix inventé : si la source n'affiche pas de prix, `price = null` (« Prix sur demande ») ; politesse de crawl (délai entre requêtes, backoff, User-Agent identifié). |
| 59 | 73 | |
| 74 | +## Démarrage rapide | |
| 75 | + | |
| 76 | +```bash | |
| 77 | +ssh M4M64b && cd ~/auto-ka # source de vérité : le nœud (aucune copie laptop) | |
| 78 | + | |
| 79 | +# Backend | |
| 80 | +python3 -m venv .venv | |
| 81 | +.venv/bin/pip install -r requirements.txt | |
| 82 | +.venv/bin/python run.py sync # synchroniser toutes les sources | |
| 83 | +.venv/bin/python run.py sync desmeules kijiji # ...ou seulement certaines (id du registre data/sources.json) | |
| 84 | +.venv/bin/python run.py serve 8080 # API + frontend + SSR en local | |
| 85 | + | |
| 86 | +# Frontend (build servi ensuite par FastAPI) | |
| 87 | +cd frontend && npm install && npm run build && cd .. | |
| 88 | +cd frontend && npm run dev # ...ou serveur de dev Vite | |
| 89 | + | |
| 90 | +# Boucle de synchronisation continue (ce que fait PM2 en prod) | |
| 91 | +.venv/bin/python run.py watch 120 # re-parcourt les sources, cycle ≈ 2 h | |
| 92 | +``` | |
| 93 | + | |
| 94 | +`run.py` est le seul point d'entrée : `sync [source ...]` · `watch [minutes]` (défaut 120) · `serve [port]` (défaut 8080). | |
| 95 | + | |
| 96 | +## Variables d'environnement | |
| 97 | + | |
| 98 | +Noms seulement — les valeurs vivent dans `.env` sur le nœud (jamais versionnées). | |
| 99 | + | |
| 100 | +| Variable | Rôle | | |
| 101 | +|---|---| | |
| 102 | +| `AUTOKA_BASE_URL` | URL publique canonique (SEO, sitemaps, SSO) | | |
| 103 | +| `SESSION_SECRET` | Secret de session (cookies signés) | | |
| 104 | +| `KA_SSO_SECRET` · `KA_HUB_URL` | SSO KA ID via le hub groupe-ka.com | | |
| 105 | +| `FIRECRAWL_API_KEY` · `SCRAPFLY_API_KEY` | Escalade anti-bot des connecteurs (`_resilient.py`) | | |
| 106 | +| `AUTOKA_MAX_PAGES` · `AUTOKA_MAX_DETAILS` | Bornes générales de crawl (optionnelles) | | |
| 107 | +| `AUTOKA_AUTOTRADER_MAX` · `AUTOKA_CARGURUS_MAX` · `AUTOKA_OTOGO_MAX` · `AUTOKA_AUTOUSAGEE_MAX` | Bornes par portail (optionnelles) | | |
| 108 | + | |
| 60 | 109 | ## Architecture |
| 61 | 110 | |
| 62 | 111 | | Composant | Technologie | Rôle | |
@@ -69,11 +118,11 @@ Les sites de concessionnaires n'offrent pas de webhooks ; Auto-Ka en reproduit l | ||
| 69 | 118 | |
| 70 | 119 | ### Processus PM2 |
| 71 | 120 | |
| 72 | −| Processus | Commande | Rôle | | |
| 121 | +| Processus | Commande de démarrage | Rôle | | |
| 73 | 122 | |---|---|---| |
| 74 | −| `auto-ka-web` | `.venv/bin/python run.py serve 8095` | Sert l'API `/api/*`, le frontend buildé et le rendu serveur SEO — le seul processus exposé (via ngrok) | | |
| 75 | −| `auto-ka-sync` | `.venv/bin/python run.py watch 120` | Watcher de resynchronisation : reparcourt les 129 sources actives en boucle, **cycle complet ≈ 2 h**, alimente le diff engine | | |
| 76 | −| `auto-ka-ngrok` | `ngrok http --url=www.auto-ka.com 8095` | Tunnel public vers le domaine www.auto-ka.com | | |
| 123 | +| `auto-ka-web` | `pm2 start .venv/bin/python --name auto-ka-web -- run.py serve 8095` | Sert l'API `/api/*`, le frontend buildé et le rendu serveur SEO — le seul processus exposé (via ngrok) | | |
| 124 | +| `auto-ka-sync` | `pm2 start .venv/bin/python --name auto-ka-sync -- run.py watch 120` | Watcher de resynchronisation : reparcourt les sources actives en boucle, **cycle complet ≈ 2 h**, alimente le diff engine | | |
| 125 | +| `auto-ka-ngrok` | `pm2 start ~/bin/ngrok --name auto-ka-ngrok -- http --url=www.auto-ka.com 8095` | Tunnel public vers le domaine www.auto-ka.com | | |
| 77 | 126 | |
| 78 | 127 | ### API principale |
| 79 | 128 | |
@@ -84,7 +133,8 @@ Les sites de concessionnaires n'offrent pas de webhooks ; Auto-Ka en reproduit l | ||
| 84 | 133 | | GET | `/api/vehicles/{uid}/recalls` | Rappels de sécurité croisés pour ce véhicule | |
| 85 | 134 | | GET | `/api/facets` | Facettes dynamiques (compteurs par marque, région, carrosserie…) pour les filtres | |
| 86 | 135 | | GET | `/api/dealers` · `/api/sources` | Registre des concessionnaires / état des sources | |
| 87 | −| GET | `/api/stats` · `/api/stats/dashboard` · `/api/stats/detailed` | Statistiques du marché (tuiles, distributions, répartitions) | | |
| 136 | +| GET | `/api/stats` | Tuiles de synthèse JSON (total, sources, régions, prix/km/année moyens, rappels, doublons VIN, répartitions) — alimente les pastilles dynamiques ci-dessus | | |
| 137 | +| GET | `/api/stats/dashboard` · `/api/stats/detailed` | Tableau de bord analytique (distributions, ajouts, fraîcheur, fenêtre `?syncs_since_h` pour la supervision api-ka) | | |
| 88 | 138 | | GET | `/api/stats/catalog` · `/api/stats/report` · `/api/stats/rapport.pdf` | Catalogue stats v3 + rapports PDF | |
| 89 | 139 | | POST | `/api/stats/report/custom` | Rapport PDF personnalisé (ReportBuilder) | |
| 90 | 140 | | POST | `/api/sync` | Déclencher une synchronisation | |
@@ -94,23 +144,31 @@ S'y ajoutent les routes SSR SEO (`/vehicule/{uid}/{slug}`, `/usagees/{marque}[/{ | ||
| 94 | 144 | |
| 95 | 145 | ### Connecteurs et sources |
| 96 | 146 | |
| 97 | −**30 modules** dans `autoka/connectors/` couvrent **138 concessionnaires** (129 sources actives), recensés dans `data/sources.json`. Un module = soit un commerce, soit une **famille de plateforme** dont chaque concessionnaire est une sous-classe (~6 lignes). | |
| 147 | +**31 modules** dans `autoka/connectors/` (+ socle `base.py` / `_resilient.py`) couvrent le registre `data/sources.json` : **143 sources** au 2026-08-24 — **142 actives**, 1 en pause (`albioccasion`, rate-limit 429, connecteur prêt) — soit **138 concessionnaires** géolocalisés et **129 sources** livrant actuellement des véhicules. Un module = soit un commerce, soit une **famille de plateforme** dont chaque concessionnaire est une sous-classe (~6 lignes). | |
| 98 | 148 | |
| 99 | −| Type | Modules (exemples) | | |
| 149 | +| Type | Modules (nombre de sources couvertes) | | |
| 100 | 150 | |---|---| |
| 101 | −| Familles multi-concessionnaires | `d2c_dealers` (D2C Media), `sm360_dealers` (SM360), `convertus_fc` (Convertus), `gatsby_dealers`, `magnetis_dealers`, `vvu_dealers`, `central_dealers`, `moto_dealers` (motos/scooters) | | |
| 102 | −| Regroupements & bannières | `autousagee` (AMVOQ/AutoUsagée), `hgregoire`, `automobileendirect`, `leprixdugros`, `megacentre`, `clubautozone` | | |
| 103 | −| Portails & inventaires | `autotrader`, `cargurus`, `kijiji`, `otogo`, `okaze`, `classeauto`, `ototr` | | |
| 104 | −| Commerces individuels | `desmeules`, `dupontford`, `stefoychrysler`, `montjolichrysler`, `albioccasion`, `occasionbeaucage`, `occasioncharlevoix`, `jlkauto`, `yannicklaberge`, `autodurocher`… | | |
| 151 | +| Familles multi-concessionnaires | `d2c_dealers` (D2C Media, **66**), `sm360_dealers` (SM360, **15**), `moto_dealers` (motos/scooters, **13**), `central_dealers` (AMVOQ theme_central, **9**), `gatsby_dealers` (EvalAuto/autoroot, **6**), `vvu_dealers` (**4**), `magnetis_dealers` (SyncAuto, **4**), `convertus_fc` (Convertus, **3**) | | |
| 152 | +| Regroupements & bannières (1 source chacun) | `autousagee` (AMVOQ/AutoUsagée), `hgregoire`, `automobileendirect`, `leprixdugros`, `megacentre`, `clubautozone` | | |
| 153 | +| Portails & inventaires (1 source chacun) | `autotrader`, `cargurus`, `kijiji`, `otogo`, `okaze`, `classeauto`, `ototr` | | |
| 154 | +| Commerces individuels (1 source chacun) | `desmeules`, `dupontford`, `stefoychrysler`, `montjolichrysler`, `albioccasion`, `occasionbeaucage`, `occasioncharlevoix`, `jlkauto`, `yannicklaberge`, `autodurocher` | | |
| 105 | 155 | |
| 106 | −Infrastructure commune : `base.py` (classe `BaseConnector`, normalisation, politesse de crawl) et `_resilient.py` (retries/backoff, escalade anti-bot). La documentation générée des connecteurs vit dans `docs/` (`scripts/gen_connector_docs.py`). | |
| 156 | +Infrastructure commune : `base.py` (classe `BaseConnector`, normalisation, politesse de crawl) et `_resilient.py` (retries/backoff, escalade anti-bot Oxylabs → Scrapfly → Bright Data). La documentation générée des connecteurs vit dans `docs/` (`scripts/gen_connector_docs.py`). | |
| 107 | 157 | |
| 108 | 158 | ### Diff engine & déduplication VIN |
| 109 | 159 | |
| 110 | 160 | 1. Chaque annonce normalisée reçoit un **hash de contenu** ; l'upsert dans SQLite classe le véhicule **nouveau / modifié / inchangé**. |
| 111 | 161 | 2. Tout changement de prix est **journalisé** → l'historique de prix affiché sur la fiche. |
| 112 | 162 | 3. Une annonce absente du site source est marquée **vendue** après un **délai de grâce** (tolère les ratés de crawl), avec **détection de dérive** pour éviter les faux positifs quand une source change de structure ; sa page publique renvoie alors `410 Gone`. |
| 113 | −4. La **déduplication par VIN** (`autoka/dedup.py`) détecte le même véhicule annoncé sur plusieurs sites et masque les doublons (9 889 masqués) — le VIN sert aussi au croisement des **rappels** (`autoka/recalls.py`). | |
| 163 | +4. La **déduplication par VIN** (`autoka/dedup.py`) détecte le même véhicule annoncé sur plusieurs sites et masque les doublons (**10 158** masqués au 2026-08-24) — le VIN sert aussi au croisement des **rappels** (`autoka/recalls.py`). | |
| 164 | + | |
| 165 | +## Données & conformité | |
| 166 | + | |
| 167 | +- **Provenance** — chaque véhicule est lu **directement sur le site du concessionnaire** (ou du portail), jamais via une plateforme d'annonces revendue ; le lien vers l'annonce originale est toujours affiché sur la fiche. | |
| 168 | +- **Cadence** — le watcher `auto-ka-sync` reparcourt les sources actives en continu, **cycle complet ≈ 2 heures** ; chaque passage est horodaté (fraîcheur visible sur `/sources` et via `?syncs_since_h`). | |
| 169 | +- **Fidélité** — aucun prix inventé (`price = null` → « Prix sur demande »), aucun champ deviné ; un véhicule disparu de la source est marqué vendu, jamais falsifié. | |
| 170 | +- **Politesse de crawl** — délai entre requêtes, retries avec backoff, User-Agent identifié ; l'escalade anti-bot (`_resilient.py`) n'est utilisée que là où le site le rend nécessaire. | |
| 171 | +- **Avertissement** — Auto-Ka est un index **indépendant**, sans affiliation avec les concessionnaires référencés ; les prix et disponibilités sont indicatifs et font foi chez le commerçant. Les rappels proviennent des données publiques de Transports Canada. | |
| 114 | 172 | |
| 115 | 173 | ## Structure du repo |
| 116 | 174 | |
@@ -120,7 +178,7 @@ Infrastructure commune : `base.py` (classe `BaseConnector`, normalisation, polit | ||
| 120 | 178 | | `requirements.txt` | Dépendances backend (FastAPI, uvicorn, requests, bs4, reportlab, fpdf2, pillow) | |
| 121 | 179 | | `autoka/` | Backend Python : schéma, normalisation, ingestion, db, web, seo, auth KA ID, favoris, stats, PDF, rappels, dédup + `connectors/` | |
| 122 | 180 | | `frontend/` | SPA React 18 + Vite + TypeScript (build servi par FastAPI) — inclut la page `/doc` (`frontend/public/doc/`) | |
| 123 | −| `data/` | `autoka.db` (SQLite), `sources.json` (registre des concessionnaires), `villes_gps.json` | | |
| 181 | +| `data/` | `autoka.db` (SQLite), `sources.json` (registre des 143 sources), `villes_gps.json` | | |
| 124 | 182 | | `docs/` | Captures d'écran + documentation générée des connecteurs | |
| 125 | 183 | | `scripts/` | Outillage (`gen_connector_docs.py`) | |
| 126 | 184 | |
@@ -130,6 +188,18 @@ Infrastructure commune : `base.py` (classe `BaseConnector`, normalisation, polit | ||
| 130 | 188 | - **Guide PDF téléchargeable** : [auto-ka-documentation.pdf](https://www.auto-ka.com/doc/auto-ka-documentation.pdf). |
| 131 | 189 | - Le guide est aussi lié depuis le pied de page du site ; ses captures vivent dans `frontend/public/doc/img/`. |
| 132 | 190 | |
| 191 | +## Historique | |
| 192 | + | |
| 193 | +| Date | Commit | Jalon | | |
| 194 | +|---|---|---| | |
| 195 | +| 2026-08-12 | `f840db2` | Naissance d'Auto-Ka — agrégateur de voitures usagées du Québec | | |
| 196 | +| 2026-08-12 | `a41549d` | Verticales **motos et scooters** : onglets dédiés + 13 concessionnaires | | |
| 197 | +| 2026-08-18 | `bbc070b` | Vague 2 : **dédup VIN** inter-sources, Kijiji particuliers, **rappels Transports Canada** | | |
| 198 | +| 2026-08-18 | `4a7b276` | Vague 3 : grands portails — AutoTrader.ca/AutoHebdo, Otogo.ca, CarGurus.ca | | |
| 199 | +| 2026-08-23 | `19b54a5` | Connecteur AutoUsagee.ca (portail AMVOQ) : **+11 924 annonces** (367 marchands, 157 villes) | | |
| 200 | +| 2026-08-23 | `dab7d55` | Stats v3 : **rapports PDF personnalisés** (catalogue, ReportBuilder) | | |
| 201 | +| 2026-08-24 | `c1f8843` | Page documentation `/doc` (guide + captures) + PDF téléchargeable | | |
| 202 | + | |
| 133 | 203 | ## Développement (remote-first) |
| 134 | 204 | |
| 135 | 205 | La **source de vérité est le repo git sur le nœud M4M64b** (`~/auto-ka`) — **il n'existe aucune copie laptop**. Toute modification se fait sur le nœud via SSH ; le remote `origin` = **spbgit** (git perso [https://git.spboucher.ai](https://git.spboucher.ai)), via l'alias SSH `gitsrv` configuré sur le nœud → `gitsrv:srv/git/auto-ka.git` (bare repos hébergés sur M3U96a). Pas GitHub. |
| 136 | 206 | |