SPB Git forge

spb/immo-ka

Public

Immo-Ka — agrégateur des propriétés à vendre au Québec (73 connecteurs, ~40 000 annonces, React+FastAPI)

112commits 1branches 0releases
125.4 MBsize
maindefault branch
13 days agolast push
Python 47.5% HTML 27.9% TypeScript 15.5% CSS 7.2% JavaScript 2%

docs: README à jour avec screenshot

Simon-Pierre Boucher committed 1 mo ago (Aug 18, 2026) parent 6b87064

2 changed files +74 −91

modified README.md +74 −91
@@ -2,10 +2,12 @@
2 2
3 3 # Immo·Ka
4 4
5 −### Toutes les propriétés à vendre du Québec. Un seul endroit.
5 +### **Toutes les propriétés à vendre du Québec. Un seul endroit.**
6 6
7 7 **[www.immo-ka.com](https://www.immo-ka.com)**
8 8
9 +![Aperçu de Immo-Ka](docs/screenshot.png)
10 +
9 11 ![Python](https://img.shields.io/badge/Python-3.14-141814?style=for-the-badge&logo=python&logoColor=c7f230)
10 12 ![FastAPI](https://img.shields.io/badge/FastAPI-API_+_SSR-141814?style=for-the-badge&logo=fastapi&logoColor=c7f230)
11 13 ![React](https://img.shields.io/badge/React_18-Vite_+_TS-141814?style=for-the-badge&logo=react&logoColor=c7f230)
@@ -20,22 +22,23 @@
20 22 ![MàJ](https://img.shields.io/badge/mise_%C3%A0_jour-automatique-e23744?style=flat-square)
21 23 ![SEO](https://img.shields.io/badge/SEO-rendu_serveur_+_sitemaps-e23744?style=flat-square)
22 24
23 −**Fiches enrichies** : galerie photo complète · caractéristiques Centris · pièces &
24 −dimensions · **estimation de valeur Vrai-Prix** (fourchette + confiance + analyse) ·
25 −**statistiques de quartier** (recensement StatCan, proximité, îlot de chaleur) ·
26 −carte 3D Ka Maps · historique de prix · favoris **KA ID** partagés dans tout le
27 −Groupe KA · **référencement programmatique** (une page indexable par ville et par
28 −type, HTML complet rendu côté serveur).
29 −
30 −*Agrégateur indépendant des maisons, condos, plex et terrains à vendre au Québec —
31 −un connecteur dédié par agence de courtage, chaque propriété normalisée vers un
32 −schéma unique, dédupliquée par numéro Centris, avec lien direct vers l'annonce
33 −originale. Toujours à jour, automatiquement.*
34 −
35 25 </div>
36 26
37 27 ---
38 28
29 +## Description
30 +
31 +**Immo-Ka** est un **agrégateur immobilier indépendant** : toutes les **maisons, condos, plex et terrains à vendre au Québec**, réunis au même endroit. Chercher une propriété au Québec, c'est normalement jongler entre les sites de RE/MAX, Royal LePage, Sutton, Via Capitale, Century 21 et des dizaines d'autres bannières — chacun avec sa navigation, ses filtres, son format. **Immo-Ka retourne le problème** : **un connecteur dédié par agence de courtage** visite chaque source, **normalise chaque propriété vers un schéma unique** (`PropertyListing`), **déduplique par numéro Centris**, et détecte les changements en continu — avec **lien direct vers l'annonce originale**. **Toujours à jour, automatiquement.**
32 +
33 +> Les sites d'agences n'offrent pas de webhooks. Immo-Ka reproduit l'équivalent :
34 +> **synchronisation périodique + hash de contenu** → ajouts, **changements de prix** et
35 +> retraits (propriété vendue) détectés automatiquement, avec **délai de grâce** contre
36 +> les ratés ponctuels et **détection de dérive** par source.
37 +
38 +C'est le pendant « à vendre » de [Lou-Ka](https://www.lou-ka.com) (location) — même architecture, schéma adapté à la vente. Immo-Ka fait partie de l'univers **Groupe KA** (Lou-Ka, Auto-Ka, Fabri-Ka, Food-Ka, Ora-Ka, Vrai-Prix…) : compte **KA ID** unique et **favoris partagés** entre toutes les plateformes.
39 +
40 +**Fiches enrichies** : galerie photo complète · caractéristiques Centris · pièces & dimensions · **estimation de valeur Vrai-Prix** (fourchette + confiance + analyse) · **statistiques de quartier** (recensement StatCan, proximité, îlot de chaleur) · **carte 3D Ka Maps** · historique de prix · favoris **KA ID** · **référencement programmatique** (une page indexable par ville et par type, HTML complet rendu côté serveur).
41 +
39 42 ## Aperçu
40 43
41 44 | Accueil — moteur de recherche | Grille de résultats |
@@ -62,45 +65,25 @@ originale. Toujours à jour, automatiquement.*
62 65
63 66 </div>
64 67
65 −---
66 −
67 −## Pourquoi Immo-Ka ?
68 −
69 −Chercher une propriété à vendre au Québec, c'est jongler entre les sites de RE/MAX,
70 −Royal LePage, Sutton, Via Capitale, Century 21 et des dizaines d'autres bannières —
71 −chacun avec sa navigation, ses filtres, son format. **Immo-Ka retourne le problème** :
72 −un connecteur par agence visite chaque source, normalise chaque propriété vers un
73 −schéma unique, et détecte les changements en continu.
74 −
75 −> Les sites d'agences n'offrent pas de webhooks. Immo-Ka reproduit l'équivalent :
76 −> **synchronisation périodique + hash de contenu** → ajouts, changements de prix et
77 −> retraits (propriété vendue) détectés automatiquement, avec délai de grâce contre
78 −> les ratés ponctuels et détection de dérive par source.
79 −
80 −C'est le pendant « à vendre » de [Lou-Ka](https://www.lou-ka.com) (location) — même
81 −architecture, schéma adapté à la vente. Immo-Ka fait partie de l'univers
82 −**Groupe KA** (Lou-Ka, Auto-Ka, Fabri-Ka, Food-Ka, Ora-Ka, Vrai-Prix) : compte
83 −**KA ID** unique et favoris partagés entre toutes les plateformes.
84 −
85 −## Le kit complet
68 +## Fonctionnalités
86 69
87 70 | | Fonctionnalité | Détails |
88 71 |---|---|---|
89 −| 🔎 | **Recherche & filtres** | texte libre (adresse, ville, n° MLS), ville + secteur, type, agence, prix, chambres, salles de bain, superficie ; tri prix/récence ; état dans l'URL (partageable) |
90 −| 🗺 | **Carte Ka Maps** | framework carto maison du Groupe KA (moteur Mapbox GL 3D) : grappes de prix, marqueurs colorés selon l'écart au Vrai-Prix, « rechercher en déplaçant la carte », popups éditoriales, bascule 2D/3D |
91 −| 🏠 | **Fiche complète** | galerie + lightbox (jusqu'à 40+ photos 1600 px), fiche technique à rangées, tableau des caractéristiques Centris, pièces & dimensions par étage, inclusions, description, mini-carte d'emplacement (5/15 min à pied) |
92 −| 💰 | **Vrai-Prix** | estimation de valeur marchande (modèle hédonique + comparables du moteur [Vrai-Prix](https://github.com/spboucher-ai/vrai-prix)) : jauge P10–P90, marqueurs estimation/prix demandé, verdict (sur-évalué / aligné / sous l'estimation), lien vers l'analyse détaillée |
72 +| 🔎 | **Recherche & filtres** | texte libre (adresse, ville, n° MLS), ville + secteur, type, agence, prix, chambres, salles de bain, superficie ; tri prix/récence ; **état dans l'URL** (partageable) |
73 +| 🗺 | **Carte Ka Maps** | framework carto maison du Groupe KA (moteur **Mapbox GL 3D**) : grappes de prix, marqueurs colorés selon l'écart au Vrai-Prix, « rechercher en déplaçant la carte », popups éditoriales, bascule 2D/3D |
74 +| 🏠 | **Fiche complète** | galerie + lightbox (jusqu'à **40+ photos 1600 px**), fiche technique à rangées, tableau des caractéristiques Centris, pièces & dimensions par étage, inclusions, description, mini-carte d'emplacement (5/15 min à pied) |
75 +| 💰 | **Vrai-Prix** | **estimation de valeur marchande** (modèle hédonique + comparables du moteur [Vrai-Prix](https://github.com/spboucher-ai/vrai-prix)) : jauge P10–P90, marqueurs estimation/prix demandé, verdict (sur-évalué / aligné / sous l'estimation), lien vers l'analyse détaillée |
93 76 | 📈 | **Historique de prix** | baisses et hausses du prix demandé horodatées (table `price_log`) |
94 −| 🏘 | **Quartier** | aire de diffusion du recensement (± 500 habitants) : revenu médian, loyer moyen, âge médian, % locataires, % français, proximité épiceries/parcs/soins (StatCan), îlot de chaleur (INSPQ) |
95 −| 📊 | **Stats** | volumes par type et par agence, prix moyens, **écart prix demandé vs estimation Vrai-Prix par bannière** (médiane, P25–P75, % sur-évalués) |
96 −| 🏢 | **Agences** | registre éclaté par bannière → sous-agence (bureau), avec compte d'annonces dédupliqué |
97 −| ♥ | **Favoris & KA ID** | connexion « Se connecter avec KA ID » (SSO du hub groupe-ka.com, JWT HS256), favoris stockés au hub — le même cœur ♥ suit l'utilisateur sur toutes les plateformes Ka |
98 −| 🔍 | **SEO programmatique** | HTML complet rendu serveur sur chaque route, une page indexable par ville / type / ville+type, sitemaps, données structurées — voir section dédiée |
77 +| 🏘 | **Quartier** | aire de diffusion du recensement (± 500 habitants) : revenu médian, loyer moyen, âge médian, % locataires, % français, proximité épiceries/parcs/soins (**StatCan**), îlot de chaleur (**INSPQ**) |
78 +| 📊 | **Stats** | volumes par type et par agence, prix moyens, **écart prix demandé vs estimation Vrai-Prix par bannière** (médiane, P25–P75, % sur-évalués) + **rapport PDF Groupe-KA** |
79 +| 🏢 | **Agences** | registre éclaté par bannière → sous-agence (bureau), avec compte d'annonces **dédupliqué** |
80 +| ♥ | **Favoris & KA ID** | connexion « Se connecter avec **KA ID** » (SSO du hub groupe-ka.com, JWT HS256), favoris stockés au hub — le même cœur ♥ suit l'utilisateur sur **toutes les plateformes Ka** |
81 +| 💬 | **KA Agent** | bulle de chat Groupe KA intégrée (widget `ka-agent.js`) |
82 +| 🔍 | **SEO programmatique** | HTML complet **rendu serveur** sur chaque route, une page indexable par ville / type / ville+type, sitemaps, données structurées — voir section dédiée |
99 83
100 84 ## Couverture (métriques en direct)
101 85
102 −**57 900+ propriétés actives** · 26 sources avec annonces · 22 bannières ·
103 −159 sous-agences · 2 825 villes · prix moyen ≈ 683 000 $
86 +**57 900+ propriétés actives** · **26 sources** avec annonces · **22 bannières** · **159 sous-agences** · **2 825 villes** · prix moyen ≈ **683 000 $**
104 87
105 88 | Source | Annonces | Méthode technique |
106 89 |---|---:|---|
@@ -118,11 +101,19 @@ architecture, schéma adapté à la vente. Immo-Ka fait partie de l'univers
118 101 | Barnes · Profusion (Christie's) · KW · M Immobilier · Sotheby's · L'Expert PM | ~1 200 | Algolia, WordPress JSON-LD, HTML SSR, Centris |
119 102 | Sous-agences RE/MAX · Via Capitale · Century 21 | *backup* | 60+ connecteurs `*_ag_*` auto-générés, dédup par n° Centris |
120 103
121 −Les sous-agences servent de plan B : si le flux central d'une bannière tombe, elles
122 −prennent le relais **sans double-comptage** (déduplication pré-calculée par numéro
123 −Centris, colonne `dup_hidden`).
104 +Les sous-agences servent de plan B : si le flux central d'une bannière tombe, elles prennent le relais **sans double-comptage** (déduplication pré-calculée par numéro Centris, colonne `dup_hidden`).
105 +
106 +## Stack technique
107 +
108 +- **Backend** : **Python 3.14** · **FastAPI** (API JSON + rendu SEO serveur + service du frontend) · **SQLite en mode WAL** (accès concurrents lecture/écriture)
109 +- **Frontend** : **React 18 + Vite + TypeScript** · react-router · design system **ka-ui** (badge + footer communs du Groupe KA)
110 +- **Cartographie** : **Ka Maps** — framework carto partagé du Groupe KA sur moteur **Mapbox GL 3D**
111 +- **Ingestion** : **95 connecteurs** (API JSON internes — Meilisearch, Algolia, source.immo, wp-json —, JSON-LD, sitemaps, **Firecrawl** pour les sites SPA/anti-bot)
112 +- **Enrichissement** : géocodage avec cache, POI de proximité, quartier **StatCan/INSPQ**, estimations **Vrai-Prix**
113 +- **Auth** : **SSO KA ID** (JWT HS256 du hub groupe-ka.com, stdlib pure)
114 +- **Exploitation** : **PM2** + tunnel **ngrok**
124 115
125 −## Architecture
116 +### Architecture
126 117
127 118 ```mermaid
128 119 flowchart LR
@@ -156,31 +147,18 @@ flowchart LR
156 147
157 148 ## Référencement (SEO)
158 149
159 −Le SPA React est doublé d'un **rendu HTML côté serveur** (`immoka/seo.py`, branché
160 −sur le catch-all FastAPI) : chaque URL livre son contenu complet dès la première
161 −requête, sans exécution JavaScript — React prend le relais au montage.
162 −
163 −- **Chaque page** a un `<title>`, une meta description, un canonical et des balises
164 − `og:`/`twitter:` uniques, plus le contenu réel (H1, stats, liste d'annonces avec
165 − liens) directement dans le HTML initial.
166 −- **Pages programmatiques** alignées sur les recherches réelles :
167 − `/a-vendre/{ville}` (1 589 pages), `/a-vendre/{ville}/{type}` (2 702 pages),
168 − `/type/{type}` (21 pages) — statistiques (nombre, prix médian/moyen), liste
169 − paginée (48/page), maillage interne (types de la ville, villes voisines),
170 − seuil de qualité (≥ 3 annonces).
171 −- **Fiches** : URL stable et lisible `/propriete/{uid}/{slug-adresse-ville}`
172 − (301 depuis l'uid nu), JSON-LD `RealEstateListing` + `Offer` (prix CAD, adresse,
173 − géolocalisation, photos) + `BreadcrumbList`.
174 −- **Cycle de vie propre** : propriété retirée/vendue → **410 Gone** avec liens de
175 − sortie ; uid inconnu → **404** réel. Fini les soft-404.
176 −- **`sitemap.xml`** : index + sous-sitemaps (57 900+ fiches, villes, villes-types,
177 − types, pages) avec `lastmod`, régénérés automatiquement. **`robots.txt`** propre.
178 −- Le slug est calculé par le **même algorithme** en Python (`seo.py`) et en
179 − TypeScript (`api.ts`) — les liens du SPA et du serveur coïncident.
150 +Le SPA React est doublé d'un **rendu HTML côté serveur** (`immoka/seo.py`, branché sur le catch-all FastAPI) : chaque URL livre son contenu complet dès la première requête, **sans exécution JavaScript** — React prend le relais au montage.
151 +
152 +- **Chaque page** a un `<title>`, une meta description, un canonical et des balises `og:`/`twitter:` uniques, plus le contenu réel (H1, stats, liste d'annonces avec liens) directement dans le HTML initial.
153 +- **Pages programmatiques** alignées sur les recherches réelles : `/a-vendre/{ville}` (**1 589 pages**), `/a-vendre/{ville}/{type}` (**2 702 pages**), `/type/{type}` (21 pages) — statistiques (nombre, prix médian/moyen), liste paginée (48/page), maillage interne (types de la ville, villes voisines), seuil de qualité (≥ 3 annonces).
154 +- **Fiches** : URL stable et lisible `/propriete/{uid}/{slug-adresse-ville}` (301 depuis l'uid nu), JSON-LD `RealEstateListing` + `Offer` (prix CAD, adresse, géolocalisation, photos) + `BreadcrumbList`.
155 +- **Cycle de vie propre** : propriété retirée/vendue → **410 Gone** avec liens de sortie ; uid inconnu → **404** réel. Fini les soft-404.
156 +- **`sitemap.xml`** : index + sous-sitemaps (**57 900+ fiches**, villes, villes-types, types, pages) avec `lastmod`, régénérés automatiquement. **`robots.txt`** propre.
157 +- Le slug est calculé par le **même algorithme** en Python (`seo.py`) et en TypeScript (`api.ts`) — les liens du SPA et du serveur coïncident.
180 158
181 159 ## API
182 160
183 −Toutes les réponses sont en JSON, compressées (gzip), CORS ouvert.
161 +Toutes les réponses sont en **JSON**, compressées (gzip), CORS ouvert.
184 162
185 163 | Endpoint | Description |
186 164 |---|---|
@@ -197,7 +175,7 @@ Toutes les réponses sont en JSON, compressées (gzip), CORS ouvert.
197 175 ## Structure du projet
198 176
199 177 ```
200 −agent-courtage/
178 +immo-ka/
201 179 ├── run.py # point d'entrée : list · sync · serve · watch
202 180 ├── immoka/
203 181 │ ├── web.py # API FastAPI + service du frontend + catch-all SEO
@@ -221,7 +199,7 @@ agent-courtage/
221 199 └── docs/screenshots/ # captures de ce README
222 200 ```
223 201
224 −## Démarrage rapide
202 +## Démarrage local
225 203
226 204 ```bash
227 205 python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
@@ -247,29 +225,31 @@ curl -s localhost:8090/sitemap.xml
247 225
248 226 ## Ajouter une agence
249 227
250 −1. Créer `immoka/connectors/<id>.py` : sous-classe de `BaseConnector`, définir
251 − `source_id`, implémenter `fetch() -> list[PropertyListing]`. Le registre est
252 − **auto-découvrant** — aucun fichier partagé à modifier.
228 +1. Créer `immoka/connectors/<id>.py` : sous-classe de `BaseConnector`, définir `source_id`, implémenter `fetch() -> list[PropertyListing]`. Le registre est **auto-découvrant** — aucun fichier partagé à modifier.
253 229 2. Ajouter l'entrée dans `data/sources.json`.
254 230 3. Tester : `.venv/bin/python run.py sync <id>`.
255 231
256 −Pour une bannière à sous-agences (RE/MAX, Via Capitale, Century 21), un module
257 −génère un connecteur par sous-agence depuis un registre JSON — nommer les
258 −`source_id` en `<banniere>_ag_<slug>` active automatiquement la déduplication.
232 +Pour une bannière à sous-agences (RE/MAX, Via Capitale, Century 21), un module génère un connecteur par sous-agence depuis un registre JSON — nommer les `source_id` en `<banniere>_ag_<slug>` active automatiquement la déduplication.
259 233
260 234 ## Déploiement
261 235
262 −Déployé sur le cluster MacLustr (nœud M4M64a) via PM2 :
236 +Déployé sur le cluster **MacLustr**, nœud **M4M64a** (`~/apps/immo-ka`), **port 8096**, exposé sur **[www.immo-ka.com](https://www.immo-ka.com)** via **PM2** :
263 237
264 −| Processus | Rôle |
238 +| Processus PM2 | Rôle |
265 239 |---|---|
266 −| `immo-ka-web` | API + frontend + rendu SEO (`run.py serve 8096`) |
267 −| `immo-ka-sync` | watcher de synchronisation (`run.py watch`) |
268 −| `immo-ka-ngrok` | tunnel `www.immo-ka.com` |
240 +| **`immo-ka-web`** | API + frontend + rendu SEO (`run.py serve 8096`) |
241 +| **`immo-ka-sync`** | watcher de synchronisation (`run.py watch`) |
242 +| **`immo-ka-ngrok`** | tunnel ngrok → **www.immo-ka.com** |
243 +
244 +Base **SQLite en mode WAL** pour les accès concurrents lecture/écriture. Variables d'environnement sur le nœud (`.env`) : `KA_SSO_SECRET`, `KA_HUB_URL`, `IMMOKA_BASE_URL`, `AUTH_SECRET`, `FIRECRAWL_API_KEY`.
245 +
246 +## Développement remote-first
269 247
270 −Base SQLite en mode WAL pour les accès concurrents lecture/écriture. Variables
271 −d'environnement sur le nœud (`.env`) : `KA_SSO_SECRET`, `KA_HUB_URL`,
272 −`IMMOKA_BASE_URL`, `AUTH_SECRET`, `FIRECRAWL_API_KEY`.
248 +⚠️ **La source de vérité est le repo git sur le nœud M4M64a** (`~/apps/immo-ka`) — **il n'existe aucune copie laptop**. Toute modification se fait **sur le nœud via SSH** : édition, build frontend, `pm2 restart immo-ka-web`, puis `git add/commit/push` **depuis le nœud**.
249 +
250 +- **Remote `origin` = spbgit** (le git perso — **git.spboucher.ai**, bare repo `~/srv/git/immo-ka.git` sur M3U96a), joignable via l'**alias SSH `gitsrv`** configuré sur le nœud. **Pas GitHub.**
251 +- Le push fonctionne grâce à l'**agent forwarding** actif pendant une session SSH depuis le laptop.
252 +- Référence complète (ports, PM2, gotchas gitsrv) : `~/Desktop/cluster-skill/KA-REMOTE-DEV.md`.
273 253
274 254 ---
275 255
@@ -282,7 +262,10 @@ d'environnement sur le nœud (`.env`) : `KA_SSO_SECRET`, `KA_HUB_URL`,
282 262
283 263 Développé par **Simon-Pierre Boucher** — [contact@spboucher.ai](mailto:contact@spboucher.ai)
284 264
285 −*Agrégateur indépendant — les annonces proviennent des sites publics des agences de
286 −courtage et sont rafraîchies automatiquement ; chaque fiche renvoie vers l'annonce
287 −originale de l'agence. Conditions d'utilisation et politique de confidentialité :
288 −`/conditions` et `/confidentialite` sur [www.immo-ka.com](https://www.immo-ka.com).*
265 +*Agrégateur indépendant — les annonces proviennent des sites publics des agences de courtage et sont rafraîchies automatiquement ; chaque fiche renvoie vers l'annonce originale de l'agence. Conditions d'utilisation et politique de confidentialité : `/conditions` et `/confidentialite` sur [www.immo-ka.com](https://www.immo-ka.com).*
266 +
267 +<div align="center">
268 +
269 +**Un service <a href="https://www.groupe-ka.com">Groupe Ka</a>**
270 +
271 +</div>
added docs/screenshot.png +0 −0

Binary file not shown.