SPB Git

spb/lou-ka

Public

Lou·Ka — tous les logements à louer du Québec, un seul endroit.

15.4 KB

# Lou·Ka

# Tous les logements à louer du Québec. Un seul endroit.

www.lou-ka.com — un service Groupe Ka

Aperçu de Lou-Ka

Python FastAPI React SQLite Mapbox Swift

Annonces Sources Connecteurs Villes Régions Géocodées


# Description

Chercher un appartement au Québec, c'est ouvrir des dizaines de sites web différents — chacun avec sa navigation, ses filtres, son format. Lou-Ka retourne le problème : un connecteur dédié par gestionnaire immobilier visite chaque site, normalise chaque annonce vers un schéma unique, et détecte les changements en continu.

Lou-Ka n'est pas une plateforme d'annonces : c'est un index fidèle et un agrégateur indépendant de logements locatifs. Aucun prix inventé, aucune coordonnée devinée, et chaque fiche renvoie vers l'annonce originale du gestionnaire via une passerelle de sortie transparente (/passerelle/{uid}).

Les sites d'agences n'offrent pas de webhooks. Lou-Ka reproduit l'équivalent : synchronisation périodique + hash de contenu → ajouts, mises à jour et retraits détectés automatiquement. Une annonce qui disparaît du site source disparaît de Lou-Ka (et répond 410 Gone aux moteurs de recherche).

En chiffres : 22 900+ annonces actives, 275 sources recensées, 212 connecteurs, 820 villes, 11 régions, ~70 % des annonces géolocalisées.

# Fonctionnalités

  • Recherche filtrée — ville, quartier, type (3½, 4½…), loyer min/max, animaux, meublé, superficie, texte libre.
  • Vue carte 3D — Lou-Ka Maps (framework @groupe-ka/ka-maps, moteur Mapbox GL JS) : clusters par prix moyen, « Rechercher dans cette zone », caméra partageable dans l'URL, bascule 3D/2D, synchro liste ↔ carte.
  • Fiches complètes — toutes les photos, digest structuré de la description (text mining regex FR, aucun LLM), badge marché, historique de prix (price_log), bloc « Le quartier » (recensement 2021, INSPQ, SPVM, écoles MEQ, POI OSM), fiche PDF avec QR code.
  • Pages villes SEO — /villes, /ville/{slug}, /ville/{slug}/{type} avec SSR léger, sitemaps dynamiques (chunks de 10 000), JSON-LD (RealEstateListing + Offer CAD), vrais 404/410.
  • Observatoire du marché — KPI temps réel, loyers médians par région/ville/taille, rapport de marché PDF multi-pages (/api/stats/rapport.pdf).
  • Registre des sources — chaque gestionnaire, son statut, sa dernière synchro ; toute source non connectable est documentée avec sa raison dans data/sources.json.
  • Comptes & SSO KA — « Se connecter avec KA » via le hub groupe-ka.com (JWT HS256, audience lou-ka, ka_id émis par le hub) + Google OAuth direct ; favoris synchronisés vers « Mon univers Ka » ; pages gestionnaires réclamables (/g/{source_id}) ; profil public opt-in /u/{ka_id}.
  • Widget KA Agent — bulle de chat IA du Groupe Ka intégrée au site.
  • PWA mobile-first installable + app iOS SwiftUI 100 % native (iOS 17+, TestFlight, dépôt séparé git.spboucher.ai/lou-ka-ios) avec moteur de recommandation on-device.
  • Robustesse — garde-fou anti-dérive (DRIFT_RATIO = 0.25) : si une source retourne soudainement beaucoup moins d'annonces, les retraits sont suspendus au lieu de vider l'inventaire.

# Visite guidée

Recherche filtrée Vue carte Lou-Ka Maps
Grille d'annonces Vue carte Lou-Ka Maps
Fiche complète Pages villes (SEO)
Fiche d'un logement Page ville Québec
Observatoire du marché Registre des sources
Page stats Registre des sources

# Stack technique

Couche Technologies Fichiers
Backend Python 3.14, FastAPI, uvicorn, SQLite (migrations auto, upsert par hash), reportlab/fpdf2 (PDF) — 8 dépendances louka/
Connecteurs 205 modules auto-découverts (pkgutil.iter_modules) : HTML, API JSON internes (Building Stack, RealVuu, Planpoint, Rentsync, source.immo, WordPress REST…), Firecrawl (Cloudflare), Scrapfly (anti-bot) louka/connectors/*.py
Text mining digest déterministe des descriptions — regex FR, < 5 ms/annonce, aucun LLM louka/textmine.py
Géocodage Nominatim (1 req/s) + Adresses Québec (MERN ArcGIS, mode EN LOT de 200) validé par bounding box provinciale louka/geocode.py
Frontend React 18 + Vite + TypeScript, design « éditorial sharp » Groupe Ka (Space Grotesk, accent), PWA frontend/
Cartographie @groupe-ka/ka-maps — framework partagé du Groupe Ka, Mapbox GL JS style Standard 3D, monté en React.lazy frontend/src/kamaps/
SEO SSR léger FastAPI, sitemaps dynamiques, JSON-LD, hreflang, GZip, /assets immuable 1 an louka/seo.py
iOS SwiftUI natif (iOS 17+, zéro dépendance externe), TestFlight dépôt séparé
Prod PM2 (3 processus) + ngrok sur le nœud M3U96b —

# Structure du dépôt

text
lou-ka/
├── run.py                      # point d'entrée CLI (sync / watch / serve / geocode / poi / quartier / record)
├── requirements.txt            # 8 dépendances backend (FastAPI, uvicorn, bs4, reportlab, fpdf2…)
├── louka/                      # paquet backend (Python 3.14)
│   ├── web.py                  #   app FastAPI : API JSON + service du build Vite
│   ├── seo.py                  #   SSR léger, pages programmatiques, sitemaps, robots, JSON-LD
│   ├── db.py                   #   SQLite : migrations auto, upsert par hash, anti-dérive
│   ├── schema.py               #   dataclass Listing
│   ├── normalize.py            #   normalisation (prix, dates ISO, types d'unités, adresses)
│   ├── textmine.py             #   digest déterministe des descriptions (regex FR)
│   ├── ingest.py               #   pipeline sync / watch
│   ├── auth.py                 #   SSO KA + Google OAuth, sessions HMAC-SHA256 (cookie httpOnly 30 j)
│   ├── accounts.py             #   rôles, favoris, réclamation de page gestionnaire
│   ├── profile.py / hubprofile.py / hubfav.py   # profils & intégration hub Groupe Ka
│   ├── geocode.py              #   Nominatim + Adresses Québec (mode EN LOT)
│   ├── poi.py / quartier.py    #   commodités OSM, stats de quartier
│   ├── marketstats.py          #   agrégats du marché (source unique stats/API/PDF)
│   ├── pdfgen.py               #   fiches PDF + rapport de marché (QR code)
│   ├── fixtures.py             #   enregistrement/rejeu HTTP pour tests hors-ligne
│   └── connectors/             #   212 connecteurs + base.py (auto-découverte)
├── frontend/                   # React 18 + Vite + TypeScript (pages, composants, kamaps/)
├── data/
│   ├── sources.json            #   registre des 265 sources (statut + raison si non connectable)
│   ├── louka.db                #   base de production (gitignorée)
│   └── quartier.db             #   base statique de quartier (gitignorée)
├── scripts/                    # build_recensement / build_contexte / build_environnement / merge_quartier / screenshots.mjs
├── tests/                      # pytest + fixtures HTTP rejouables hors-ligne
├── reports/                    # audits : fiches connecteurs, expansion par région, quartier…
└── docs/                       # screenshot.png, screenshots/, ka-maps-lou-ka.md

# Démarrage local

bash
git clone https://git.spboucher.ai/lou-ka.git && cd lou-ka

# Backend
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt

# Frontend (nécessite le framework ka-maps bâti à côté : ../ka-maps)
cd frontend && npm install && npm run build && cd ..

# (optionnel) sites JavaScript / anti-bot
cat > .env <<EOF
FIRECRAWL_API_KEY=fc-votre-cle
SCRAPFLY_KEY=votre-cle
EOF

# Ingestion puis service
.venv/bin/python run.py sync            # toutes les sources (ou : run.py sync logisco msi)
.venv/bin/python run.py serve 8080      # → http://localhost:8080
.venv/bin/python run.py watch 60        # resynchronisation en boucle (minutes)

En développement frontend : cd frontend && npm run dev (Vite sur :5173, proxy /api → :8080).

# CLI run.py

Commande Rôle
run.py sync [source ...] ingestion — toutes les sources ou une liste
run.py watch [minutes] boucle de resynchronisation (défaut 60)
run.py serve [port] uvicorn louka.web:app (défaut 8080)
run.py geocode [n] géocodage en lot (Adresses Québec, lots de 200)
run.py geocode1 [n] géocodage un-par-un (Nominatim, 1 req/s)
run.py poi [n] commodités OSM à proximité (Overpass)
run.py quartier [n] enrichissement quartier (recensement, INSPQ, SPVM)
run.py record <source> (ré)enregistre les fixtures HTTP d'un connecteur

# Variables d'environnement

run.py charge .env à la racine (parsing maison, aucune dépendance).

Variable Rôle
FIRECRAWL_API_KEY rendu JavaScript / contournement Cloudflare (optionnel)
SCRAPFLY_KEY scraping anti-bot ASP (optionnel)
SESSION_SECRET signature HMAC des sessions
GOOGLE_CLIENT_ID / GOOGLE_CLIENT_SECRET connexion Google OAuth
KA_SSO_SECRET secret partagé du SSO KA (JWT HS256)
KA_HUB_URL hub Groupe Ka (défaut https://www.groupe-ka.com)
LOUKA_BASE_URL URL publique (derrière ngrok/proxy)
LOUKA_*_DETAIL_LIMIT / LOUKA_*_MAX_PAGES limites par portail (Kijiji, LesPAC, RE/MAX…)
VITE_MAPBOX_TOKEN jeton Mapbox du frontend (repli public codé en dur)

# Tests

bash
.venv/bin/python -m pytest

Chaque connecteur est rejoué hors-ligne contre ses fixtures HTTP enregistrées (tests/fixtures/<source>/ + expected.json) ; la normalisation et le text mining ont leurs suites dédiées.

# API (aperçu)

Endpoint Description
GET /api/listings recherche filtrée : city, sector, unit_type, source, price_min/max, pets, furnished, available_by, area_min, q, limit, offset
GET /api/listings.geojson?bbox=O,S,E,N FeatureCollection pour la carte (+ totalGeocoded, totalMatching)
GET /api/listings/{uid} fiche complète : images, POI, quartier, digest, historique de prix
GET /api/listings/{uid}/pdf fiche de propriété PDF (QR code)
GET /api/facets villes, quartiers, types, sources — pour construire les filtres
GET /api/stats / /api/stats/detailed / /api/stats/rapport.pdf agrégats du marché + rapport PDF
GET /api/sources registre des 265 sources + compteurs + dernière synchro
POST /api/sync synchronisation en arrière-plan (?source=)
GET /api/auth/ka/login / /api/auth/google/login SSO KA & Google OAuth
GET /api/me · /api/favorites · /api/org/* · /api/users/{ka_id} session, favoris, pages gestionnaires, profils publics

Pages HTML servies par le SSR (louka/seo.py) : / · /villes · /ville/{slug}[/{type}] · /logement/{uid} (410 si retirée) · /g/{source} · /stats · /sources · /robots.txt · /sitemap*.xml

# Déploiement

En production sur le nœud M3U96b (Mac Studio, cluster MacLustr), dans ~/apps/lou-ka, sous PM2 (3 processus) derrière ngrok :

Processus PM2 Commande Rôle
lou-ka-web .venv/bin/python run.py serve 8095 API + SSR + frontend sur le port 8095
lou-ka-sync .venv/bin/python run.py watch 60 resynchronisation horaire des sources
lou-ka-ngrok ngrok http --url=www.lou-ka.com 8095 tunnel → https://www.lou-ka.com

Philosophie d'exploitation : on ne pousse que le code — le serveur maintient ses données lui-même (data/*.db, frontend/dist et .env ne sont jamais versionnés).

# Développement remote-first (IMPORTANT)

La source de vérité est le repo git sur le nœud M3U96b (~/apps/lou-ka), pas une copie locale sur le laptop. Toute modification se fait sur le nœud via SSH : édition, build frontend, pm2 restart lou-ka-web, puis commit/push depuis le nœud.

  • Remote origin = spbgit — le serveur git personnel git.spboucher.ai (bare repo ~/srv/git/lou-ka.git sur M3U96a, alias SSH gitsrv). Pas GitHub.
  • Le push fonctionne grâce à l'agent forwarding SSH actif pendant une session depuis le laptop.

# Principes

  1. Politesse — délai ≥ 0,5 s entre requêtes, garde-fous de crawl, User-Agent identifié (LouKaBot, page de transparence /bot).
  2. Fidélité — aucun prix inventé (price = null si absent), aucune coordonnée devinée (géocodage validé ou rien).
  3. Traçabilité — chaque fiche renvoie vers l'annonce originale via /passerelle/{uid}.
  4. Robustesse — un connecteur qui casse n'affecte jamais les autres ; le garde-fou anti-dérive suspend les retraits quand une source déraille.
  5. Vie privée — aucun traceur tiers, consentement honnête, profil public strictement opt-in.

# Écosystème Groupe Ka

Lou-Ka fait partie des plateformes du Groupe Ka, aux côtés d'Immo-Ka (propriétés à vendre), Vrai-Prix et les autres univers ·Ka — avec en partage : le SSO KA (ka_id émis par le hub), le framework cartographique Ka Maps, le design system « éditorial sharp » et le géocodage Adresses Québec.


Simon-Pierre Boucher — contact@spboucher.ai · git.spboucher.ai

© 2026 — tous droits réservés.

Un service Groupe Ka

Files · 8547737dcd4ea55035ba2794bb0382d8d65edd8a
Menu

Theme