SPB Git forge

spb/auto-ka

Public
61commits 1branches 0releases
14.4 MBsize
maindefault branch
12 days agolast push
Python 61.6% TypeScript 20.9% CSS 11.4% JavaScript 5.1% HTML 1.1%

docs: README v3 — chiffres live vérifiés, pastilles dynamiques, sections complètes

Simon-Pierre Boucher committed 1 mo ago (Aug 24, 2026) parent 6fda049

1 changed file +89 −19

modified README.md +89 −19
@@ -13,18 +13,32 @@
13 13 [![PDF](https://img.shields.io/badge/guide-PDF-ff5a2a?style=flat-square)](https://www.auto-ka.com/doc/auto-ka-documentation.pdf)
14 14 ![Nœud](https://img.shields.io/badge/n%C5%93ud-M4M64b-1f6feb?style=flat-square)
15 15 ![Port](https://img.shields.io/badge/port-8095-555?style=flat-square)
16 −![PM2](https://img.shields.io/badge/process-PM2-2b037a?style=flat-square)
16 +![PM2](https://img.shields.io/badge/PM2-3_processus-2b037a?style=flat-square)
17 +
18 +[![Véhicules](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.auto-ka.com%2Fapi%2Fstats&query=%24.total&label=v%C3%A9hicules&color=ff5a2a&style=flat-square&cacheSeconds=3600)](https://www.auto-ka.com)
19 +[![Sources actives](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.auto-ka.com%2Fapi%2Fstats&query=%24.sources&label=sources%20actives&color=ff5a2a&style=flat-square&cacheSeconds=3600)](https://www.auto-ka.com/sources)
20 +[![Concessionnaires](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.auto-ka.com%2Fapi%2Fstats&query=%24.dealers_total&label=concessionnaires&color=ff5a2a&style=flat-square&cacheSeconds=3600)](https://www.auto-ka.com/sources)
21 +[![Rappels](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.auto-ka.com%2Fapi%2Fstats&query=%24.recalls_total&label=rappels&color=ff5a2a&style=flat-square&cacheSeconds=3600)](https://www.auto-ka.com)
22 +[![Doublons VIN masqués](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.auto-ka.com%2Fapi%2Fstats&query=%24.vin_duplicates_masked&label=doublons%20VIN%20masqu%C3%A9s&color=ff5a2a&style=flat-square&cacheSeconds=3600)](https://www.auto-ka.com)
23 +[![Régions](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.auto-ka.com%2Fapi%2Fstats&query=%24.regions&label=r%C3%A9gions&color=ff5a2a&style=flat-square&cacheSeconds=3600)](https://www.auto-ka.com/stats)
24 +
17 25 ![Python](https://img.shields.io/badge/Python-3.14-3776ab?style=flat-square&logo=python&logoColor=white)
18 26 ![FastAPI](https://img.shields.io/badge/FastAPI-API%20%2B%20SSR-009688?style=flat-square&logo=fastapi&logoColor=white)
19 −![React](https://img.shields.io/badge/React%2018-Vite%20%2B%20TS-61dafb?style=flat-square&logo=react&logoColor=black)
27 +![React](https://img.shields.io/badge/React%2018-TS-61dafb?style=flat-square&logo=react&logoColor=black)
28 +![Vite](https://img.shields.io/badge/Vite-build-646cff?style=flat-square&logo=vite&logoColor=white)
20 29 ![SQLite](https://img.shields.io/badge/SQLite-diff%20engine-003b57?style=flat-square&logo=sqlite&logoColor=white)
21 −![Groupe KA](https://img.shields.io/badge/Groupe-KA-b7f000?style=flat-square)
30 +![PM2](https://img.shields.io/badge/PM2-process%20manager-2b037a?style=flat-square&logo=pm2&logoColor=white)
31 +![ngrok](https://img.shields.io/badge/ngrok-tunnel-1f1e37?style=flat-square&logo=ngrok&logoColor=white)
32 +![Groupe KA](https://img.shields.io/badge/Groupe-KA-ff5a2a?style=flat-square)
33 +![spbgit](https://img.shields.io/badge/remote--first-spbgit-8250df?style=flat-square&logo=git&logoColor=white)
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