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: refonte du README (pastilles + captures d écran à jour)

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

3 changed files +86 −145

modified README.md +86 −145
@@ -1,183 +1,124 @@
1 −<div align="center">
2 −
3 1 # Auto·Ka
4 2
5 −### **Toutes les voitures usagées à vendre au Québec. Un seul endroit.**
6 −
7 −**[www.auto-ka.com](https://www.auto-ka.com)**
8 −
9 −![Python](https://img.shields.io/badge/Python-3.14-17181c?style=for-the-badge&logo=python&logoColor=ff5a2a)
10 −![FastAPI](https://img.shields.io/badge/FastAPI-API_+_SSR-17181c?style=for-the-badge&logo=fastapi&logoColor=ff5a2a)
11 −![React](https://img.shields.io/badge/React_18-Vite_+_TS-17181c?style=for-the-badge&logo=react&logoColor=ff5a2a)
12 −![SQLite](https://img.shields.io/badge/SQLite-diff_engine-17181c?style=for-the-badge&logo=sqlite&logoColor=ff5a2a)
13 −![PM2](https://img.shields.io/badge/PM2-production-17181c?style=for-the-badge&logoColor=ff5a2a)
14 −
15 −</div>
3 +**Toutes les voitures, motos et scooters usagés à vendre au Québec — agrégés à la source, un seul endroit.**
16 4
17 −![Aperçu de Auto-Ka](docs/screenshot.png)
5 +[![Site](https://img.shields.io/website?url=https%3A%2F%2Fwww.auto-ka.com&style=flat-square&label=www.auto-ka.com)](https://www.auto-ka.com)
6 +![Nœud](https://img.shields.io/badge/n%C5%93ud-M4M64b-1f6feb?style=flat-square)
7 +![Port](https://img.shields.io/badge/port-8095-555?style=flat-square)
8 +![PM2](https://img.shields.io/badge/process-PM2-2b037a?style=flat-square)
9 +![Python](https://img.shields.io/badge/Python-3.14-3776ab?style=flat-square&logo=python&logoColor=white)
10 +![FastAPI](https://img.shields.io/badge/FastAPI-API%20%2B%20SSR-009688?style=flat-square&logo=fastapi&logoColor=white)
11 +![React](https://img.shields.io/badge/React%2018-Vite%20%2B%20TS-61dafb?style=flat-square&logo=react&logoColor=black)
12 +![SQLite](https://img.shields.io/badge/SQLite-diff%20engine-003b57?style=flat-square&logo=sqlite&logoColor=white)
13 +![Groupe KA](https://img.shields.io/badge/Groupe-KA-b7f000?style=flat-square)
18 14
19 −---
20 −
21 −## Description
15 +**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**.
22 16
23 −**Auto-Ka** est un **agrégateur indépendant de voitures, motos et scooters 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**.
17 +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.
24 18
25 −> Les sites de concessionnaires n'offrent pas de webhooks. Auto-Ka 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.
19 +## Captures d'écran
26 20
27 −### Les chiffres
28 −
29 −| Métrique | Valeur |
30 −|---|---|
31 −| Véhicules en vente | **17 698** — 17 082 autos · 591 motos · 25 scooters |
32 −| Concessionnaires connectés | **138** (125 autos + 13 motos/scooters) |
33 −| Couverture | **18 régions** · **72 villes** · **83 marques** |
34 −| Annonces avec prix affiché | **99,6 %** (jamais de prix inventé) |
35 −| Kilométrage / VIN | **99,7 %** / **95,1 %** |
36 −| Photos par véhicule (moyenne) | **~16** (galeries complètes) |
37 −| Prix moyen / médian du parc | **28 529 $** / **24 995 $** |
38 −| URLs indexables (SEO) | **18 475** — 17 698 fiches + 777 pages de recherche |
39 −| Cycle de rafraîchissement | **2 h** (watcher PM2 + enrichissement progressif) |
21 +<p align="center">
22 + <img src="docs/screenshots/auto-ka-desktop.png" width="640" alt="Accueil — desktop">
23 + <img src="docs/screenshots/auto-ka-mobile.png" width="200" alt="Accueil — mobile">
24 +</p>
40 25
41 26 ## Fonctionnalités
42 27
43 −- **Recherche unifiée** — filtres **marque / modèle / année / prix / km / carburant / motricité / carrosserie / région / ville / concessionnaire**, avec facettes dynamiques et tri.
44 −- **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.
45 −- **Trois verticales, un moteur** — le champ `kind` (**`auto` / `moto` / `scooter`**) traverse tout le pipeline ; motos et scooters ont leurs onglets dédiés (13 concessionnaires **PowerGo**).
46 −- **Suivi des baisses de prix** — le **diff engine** (upsert par hash de contenu) détecte nouveau / modifié / vendu, avec délai de grâce de 2 syncs et détection de dérive.
47 −- **Statistiques du marché en direct** — tuiles de synthèse, distribution des prix, répartitions par région / marque / carrosserie, baisses récentes, et **rapport PDF** généré à la demande (`autoka/pdfgen.py`).
48 −- **SEO programmatique massif** — la SPA React est servie avec un **rendu serveur complet** (`autoka/seo.py`) : title/meta uniques, canonical, Open Graph, hreflang `fr-CA`, **JSON-LD `Car`/`Motorcycle` + `Offer` + `BreadcrumbList`**, **~900 pages programmatiques** (`/usagees/{marque}`, `/usagees/{marque}/{modele}`, `/region/…`, `/ville/…`, `/carrosserie/…`, `/motos/…`), **sitemaps dynamiques** (18 475 URLs, `lastmod` réels), vendu → **410**, inconnu → **404**.
49 −- **Compte KA ID** — SSO **« Se connecter avec KA ID »** via le hub [groupe-ka.com](https://www.groupe-ka.com) : même compte sur toutes les plateformes du groupe, page **/profil**, **favoris ♥ « Mon univers Ka »** centralisés.
50 −- **Connecteurs auto-découvrants** — ajouter un concessionnaire = **~6 lignes** : une sous-classe de `BaseConnector` avec un `source_id` déposée dans `autoka/connectors/`, c'est tout.
51 −
52 −### Principes
53 −
54 −1. **À la source** — les annonces viennent des sites des concessionnaires eux-mêmes, jamais des plateformes d'agrégation existantes.
55 −2. **Politesse** — délai ≥ 1 s entre requêtes, cache des pages détail, backoff sur rate-limit, User-Agent identifié.
56 −3. **Fidélité** — **aucun prix inventé** : si la source n'affiche pas de prix, `price = null` (« Prix sur demande »).
57 −4. **Traçabilité** — chaque fiche renvoie vers l'annonce originale.
58 −5. **Robustesse** — un connecteur qui casse n'affecte jamais les autres ; détection de dérive (chute de volume → retraits suspendus + alerte).
59 −6. **Honnêteté envers les moteurs** — le HTML servi aux robots est le même que celui servi aux humains.
28 +- **Recherche unifiée** — filtres marque / modèle / année / prix / km / carburant / motricité / carrosserie / région / ville / concessionnaire, facettes dynamiques et tris multiples.
29 +- **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.
30 +- **Trois verticales, un moteur** — le champ `kind` (`auto` / `moto` / `scooter`) traverse tout le pipeline ; motos et scooters ont leurs onglets dédiés.
31 +- **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.
32 +- **Rappels de sécurité** — croisement avec les rappels constructeurs (`autoka/recalls.py`), 16 969 rappels référencés.
33 +- **Déduplication par VIN** — les annonces multi-sites d'un même véhicule sont détectées et masquées (`autoka/dedup.py`).
34 +- **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`).
35 +- **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.
36 +- **Compte KA ID** — SSO « Se connecter avec KA ID » via le hub groupe-ka.com : même compte partout, page profil, favoris « Mon univers Ka » centralisés.
37 +- **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…).
38 +- **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é).
39 +
40 +## Architecture
41 +
42 +| Composant | Technologie | Rôle |
43 +|---|---|---|
44 +| Backend | Python 3.14 · FastAPI · Uvicorn | API `/api/*`, rendu serveur SEO, service du frontend (`autoka/web.py`, `autoka/seo.py`) |
45 +| Base de données | SQLite (`data/autoka.db`) | Diff engine : hash de contenu, historique de prix, cycle de vie des annonces, rappels, dédup VIN |
46 +| Connecteurs | requests · BeautifulSoup · Firecrawl/Scrapfly (sites derrière anti-bot) | 1 adaptateur par plateforme de concessionnaires, 1 sous-classe par commerce |
47 +| Frontend | React 18 · Vite · TypeScript | Design « éditorial sharp », accent orange racing — filtres, fiches, stats (build → `frontend/dist/`) |
48 +| PDF | reportlab · fpdf2 | Rapports « Le marché de l'occasion » personnalisables |
60 49
61 −## Stack technique
50 +Trois processus PM2 assurent la production :
62 51
63 −| Couche | Technologie | Rôle |
52 +| Processus | Commande | Rôle |
64 53 |---|---|---|
65 −| **Backend** | **Python 3.14** · **FastAPI** · **Uvicorn** | API `/api/*` + rendu serveur SEO + service du frontend |
66 −| **Base de données** | **SQLite** | Diff engine : hash de contenu, historique de prix, cycle de vie des annonces |
67 −| **Connecteurs** | requests · BeautifulSoup · Firecrawl/Scrapfly (sites derrière Cloudflare) | **1 adaptateur par plateforme** : D2C Media ×50, SM360 ×8, AMVOQ ×6, EvalAuto ×11, Convertus ×4, PowerGo ×13, HGrégoire, AED, customs… |
68 −| **Frontend** | **React 18** · **Vite** · **TypeScript** | Design « éditorial sharp », accent orange racing — filtres, fiches, stats |
69 −| **PDF** | reportlab · fpdf2 (`autoka/pdfgen.py`, `kapdf.py`) | Rapport « Le marché de l'occasion » |
70 −| **Production** | **PM2** · **ngrok** | 3 processus résilients + tunnel www.auto-ka.com |
54 +| `auto-ka-web` | `.venv/bin/python run.py serve 8095` | API + frontend + SSR SEO |
55 +| `auto-ka-sync` | `.venv/bin/python run.py watch 120` | Resynchronisation périodique des sources (cycle 2 h) |
56 +| `auto-ka-ngrok` | `ngrok http --url=www.auto-ka.com 8095` | Tunnel public vers le domaine |
71 57
72 −## Structure du projet
58 +## Structure du repo
73 59
74 −```
75 −auto-ka/
76 −├── run.py # Point d'entrée : sync | watch | serve
77 −├── requirements.txt # Dépendances backend
78 −├── autoka/
79 −│ ├── connectors/ # 1 module par plateforme, 1 sous-classe par concessionnaire
80 −│ ├── schema.py # Schéma Vehicle standardisé (kind: auto/moto/scooter)
81 −│ ├── normalize.py # Prix/km/année, marques canoniques, ville → région
82 −│ ├── ingest.py # Pipeline d'ingestion
83 −│ ├── db.py # SQLite — diff engine, hash, historique de prix
84 −│ ├── web.py # API FastAPI (/api/vehicles, /api/facets, /api/stats…)
85 −│ ├── seo.py # Rendu serveur SEO : HTML complet, JSON-LD, sitemaps, 410/404
86 −│ ├── auth.py # SSO KA ID (hub groupe-ka.com)
87 −│ ├── hubprofile.py # Profil Groupe KA
88 −│ ├── hubfav.py # Favoris « Mon univers Ka »
89 −│ ├── marketstats.py # Statistiques du marché
90 −│ ├── statsdash.py # Tableau de bord analytique
91 −│ └── pdfgen.py · kapdf.py# Rapport PDF
92 −├── frontend/ # React 18 + Vite + TypeScript (build → dist/)
93 −├── data/ # sources.json (registre des concessionnaires) + base
94 −└── docs/ # Captures d'écran
95 −```
60 +| Répertoire / fichier | Rôle |
61 +|---|---|
62 +| `run.py` | Point d'entrée CLI : `sync` · `watch` · `serve` |
63 +| `requirements.txt` | Dépendances backend (FastAPI, uvicorn, requests, bs4, reportlab, fpdf2, pillow) |
64 +| `autoka/` | Backend Python : schéma, normalisation, ingestion, db, web, seo, auth KA ID, favoris, stats, PDF, rappels, dédup + `connectors/` |
65 +| `frontend/` | SPA React 18 + Vite + TypeScript (build servi par FastAPI) |
66 +| `data/` | `autoka.db` (SQLite), `sources.json` (registre des concessionnaires), `villes_gps.json` |
67 +| `docs/` | Captures d'écran + documentation générée des connecteurs |
68 +| `scripts/` | Outillage (`gen_connector_docs.py`) |
69 +
70 +## Développement (remote-first)
96 71
97 −## Démarrage local
72 +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.
98 73
99 74 ```bash
100 −git clone https://git.spboucher.ai/auto-ka.git && cd auto-ka
75 +ssh M4M64b
76 +cd ~/auto-ka
101 77
102 78 # Backend
103 79 python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
80 +.venv/bin/python run.py sync # synchroniser toutes les sources (ou : run.py sync <source_id>)
81 +.venv/bin/python run.py serve 8080 # servir en local
104 82
105 83 # Frontend
106 84 cd frontend && npm install && npm run build && cd ..
107 85
108 −# Sites derrière Cloudflare (optionnel)
109 −echo "FIRECRAWL_API_KEY=fc-votre-cle" > .env
86 +# Après un changement en production
87 +pm2 restart auto-ka-web # (ou auto-ka-sync selon le changement)
110 88
111 −# Ingestion puis service
112 −.venv/bin/python run.py sync # toutes les sources (ou : run.py sync forceoccasion)
113 −.venv/bin/python run.py serve 8080 # → http://localhost:8080
114 −.venv/bin/python run.py watch 120 # resynchronisation en boucle (minutes)
89 +# Versionner depuis le nœud (agent forwarding actif)
90 +git add <fichiers> && git commit -m "..." && git push origin main
115 91 ```
116 92
117 −### Ajouter un concessionnaire (≈ 6 lignes)
118 −
119 −```python
120 −# dans autoka/connectors/d2c_dealers.py
121 −class MonConcessionnaire(D2CConnector):
122 − source_id = "monconcessionnaire"
123 − base_url = "https://www.monconcessionnaire.ca"
124 − dealer_name = "Mon Concessionnaire"
125 − city = "Sherbrooke"
126 −```
127 −
128 −Puis `.venv/bin/python run.py sync monconcessionnaire` — l'inventaire apparaît sur le site avec fiches, galeries, liens sources… et dans le sitemap. Ajouter l'entrée correspondante dans `data/sources.json` pour la page **Sources**.
129 −
130 −### API
131 −
132 −| Endpoint | Description |
133 −|---|---|
134 −| `GET /api/vehicles?make=&model=&region=&city=&price_max=&km_max=&fuel=&sort=` | Recherche filtrée et triée (`kind=auto\|moto\|scooter`) |
135 −| `GET /api/vehicles/{uid}` | Fiche complète (photos, historique de prix, similaires) |
136 −| `GET /api/facets?make=&kind=` | Valeurs distinctes pour construire les filtres |
137 −| `GET /api/sources` | Registre des concessionnaires + compteurs + dernière sync |
138 −| `GET /api/stats` · `GET /api/stats/detailed` | Totaux, moyennes, top marques/régions, baisses de prix |
139 −| `GET /api/stats/rapport.pdf` | Rapport PDF « Le marché de l'occasion » |
140 −| `POST /api/sync` | Déclenche une synchronisation en arrière-plan |
141 −| `GET /api/auth/ka/login` · `/api/favorites` | SSO KA ID + favoris hub |
142 −| `GET /robots.txt` · `/sitemap.xml` | SEO — sitemaps dynamiques avec lastmod |
143 −
144 93 ## Déploiement
145 94
146 −L'application tourne en production sur le **nœud M4M64b** du cluster MacLustr, sur le **port 8095**, exposée publiquement via **ngrok** sur **[www.auto-ka.com](https://www.auto-ka.com)**.
95 +- **Nœud** : M4M64b (Mac Studio, cluster MacLustr) — répertoire `~/auto-ka`
96 +- **Port local** : 8095
97 +- **Processus PM2** : `auto-ka-web` (API + frontend + SSR), `auto-ka-sync` (watcher de synchronisation), `auto-ka-ngrok` (tunnel)
98 +- **Exposition publique** : tunnel ngrok → [https://www.auto-ka.com](https://www.auto-ka.com)
147 99
148 −Trois processus **PM2** :
100 +Philosophie d'exploitation : on ne pousse que le code — le serveur maintient ses données lui-même.
149 101
150 −| Processus PM2 | Commande | Rôle |
151 −|---|---|---|
152 −| **`auto-ka-web`** | `.venv/bin/python run.py serve 8095` | **API + frontend + SSR SEO** |
153 −| **`auto-ka-sync`** | `.venv/bin/python run.py watch 120` | **Resynchronisation toutes les 2 h** |
154 −| **`auto-ka-ngrok`** | `ngrok http --url=www.auto-ka.com 8095` | **Tunnel public** |
102 +## Écosystème Groupe KA
155 103
156 −Philosophie d'exploitation : **on ne pousse que le code — le serveur maintient ses données lui-même.**
157 −
158 −## Développement remote-first (IMPORTANT)
159 −
160 −- La **source de vérité** est le **repo git sur le nœud M4M64b** (`~/auto-ka`) — **il n'existe aucune copie laptop**.
161 −- Toute modification se fait **sur le nœud via SSH** : édition, build, `pm2 restart`, puis `git add / commit / push` **depuis le nœud** (agent forwarding actif).
162 −- Le remote **`origin` = spbgit** (git perso **[git.spboucher.ai](https://git.spboucher.ai)**), via l'**alias SSH `gitsrv`** → `gitsrv:srv/git/auto-ka.git`. **Pas GitHub.**
163 −
164 −## Captures supplémentaires
165 −
166 −| | |
104 +| Plateforme | Rôle |
167 105 |---|---|
168 −| Fiche véhicule | ![Fiche véhicule](docs/screenshot-vehicle.png) |
169 −| Statistiques du marché | ![Statistiques](docs/screenshot-stats.png) |
170 −| Verticale motos | ![Motos](docs/screenshot-motos.png) |
171 −| Page programmatique SEO | ![SEO marque](docs/screenshot-seo-marque.png) |
106 +| [groupe-ka.com](https://www.groupe-ka.com) | Portail |
107 +| [lou-ka.com](https://www.lou-ka.com) | Logements à louer |
108 +| [immo-ka.com](https://www.immo-ka.com) | Propriétés à vendre |
109 +| [vrai-prix.com](https://www.vrai-prix.com) | Estimation immobilière |
110 +| [auto-ka.com](https://www.auto-ka.com) | Véhicules |
111 +| [fabri-ka.com](https://www.fabri-ka.com) | Produits québécois |
112 +| [food-ka.com](https://www.food-ka.com) | Épicerie / alimentation |
113 +| [resto-ka.com](https://www.resto-ka.com) | Restaurants |
114 +| [sorti-ka.com](https://www.sorti-ka.com) | Sorties et événements |
115 +| [job-ka.com](https://www.job-ka.com) | Emplois |
116 +| [crea-ka.com](https://www.crea-ka.com) | Créateurs |
117 +| [trouve-ka.com](https://www.trouve-ka.com) | Petites annonces |
118 +| [api-ka.com](https://www.api-ka.com) | API de données |
172 119
173 120 ---
174 121
175 −<div align="center">
176 −
177 −**Simon-Pierre Boucher** — [contact@spboucher.ai](mailto:contact@spboucher.ai) · [git.spboucher.ai](https://git.spboucher.ai)
178 −
179 −© 2026 Simon-Pierre Boucher — tous droits réservés.
180 −
181 −Un service **Groupe Ka**
122 +© Groupe KA — Simon-Pierre Boucher · [contact@spboucher.ai](mailto:contact@spboucher.ai)
182 123
183 −</div>
124 +Ce repo vit sur **spbgit** ([git.spboucher.ai](https://git.spboucher.ai)) — la source de vérité est le clone sur le nœud M4M64b.
added docs/screenshots/auto-ka-desktop.png +0 −0

Binary file not shown.

added docs/screenshots/auto-ka-mobile.png +0 −0

Binary file not shown.