docs: README v3 — chiffres live vérifiés, pastilles dynamiques, sections complètes
1 changed file +144 −60
modified
README.md
+144 −60
@@ -6,25 +6,46 @@ | |||||
| 6 | <h1 align="center">Lou·Ka</h1> | 6 | <h1 align="center">Lou·Ka</h1> | ||
| 7 | <p align="center"><b>Tous les logements à louer</b></p> | 7 | <p align="center"><b>Tous les logements à louer</b></p> | ||
| 8 | 8 | ||||
| 9 | −<p align="center"> | 9 | +<div align="center"> | ||
| 10 | − <a href="https://www.lou-ka.com"><img src="https://img.shields.io/website?url=https%3A%2F%2Fwww.lou-ka.com&style=flat-square&label=www.lou-ka.com&up_color=ff6a00&up_message=en%20ligne" alt="Site"></a> | 10 | + | ||
| 11 | − <a href="https://www.lou-ka.com/doc/"><img src="https://img.shields.io/badge/📖_documentation-%2Fdoc-ff6a00?style=flat-square" alt="Documentation"></a> | 11 | +<a href="https://www.lou-ka.com"><img src="https://img.shields.io/website?url=https%3A%2F%2Fwww.lou-ka.com&style=flat-square&label=www.lou-ka.com&up_color=ff6a00&up_message=en%20ligne" alt="Site"></a> | ||
| 12 | − <a href="https://www.lou-ka.com/doc/lou-ka-documentation.pdf"><img src="https://img.shields.io/badge/guide-PDF-ff6a00?style=flat-square" alt="PDF"></a> | 12 | +<a href="https://www.lou-ka.com/doc/"><img src="https://img.shields.io/badge/📖_documentation-%2Fdoc-ff6a00?style=flat-square" alt="Documentation"></a> | ||
| 13 | − <img src="https://img.shields.io/badge/n%C5%93ud-M3U96b-1f6feb?style=flat-square" alt="Nœud"> | 13 | +<a href="https://www.lou-ka.com/doc/lou-ka-documentation.pdf"><img src="https://img.shields.io/badge/guide-PDF-ff6a00?style=flat-square" alt="PDF"></a> | ||
| 14 | − <img src="https://img.shields.io/badge/port-8095-141814?style=flat-square" alt="Port"> | 14 | +<img src="https://img.shields.io/badge/n%C5%93ud-M3U96b-1f6feb?style=flat-square" alt="Nœud"> | ||
| 15 | − <img src="https://img.shields.io/badge/process-PM2-2b037a?style=flat-square" alt="PM2"> | 15 | +<img src="https://img.shields.io/badge/port-8095-141814?style=flat-square" alt="Port"> | ||
| 16 | −</p> | 16 | +<img src="https://img.shields.io/badge/process-PM2-2b037a?style=flat-square" alt="PM2"> | ||
| 17 | −<p align="center"> | 17 | + | ||
| 18 | − <img src="https://img.shields.io/badge/Python-3.14-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python"> | 18 | +</div> | ||
| 19 | − <img src="https://img.shields.io/badge/FastAPI-API_+_SSR-009688?style=flat-square&logo=fastapi&logoColor=white" alt="FastAPI"> | 19 | + | ||
| 20 | − <img src="https://img.shields.io/badge/React_18-Vite_+_TS-087EA4?style=flat-square&logo=react&logoColor=white" alt="React"> | 20 | +<div align="center"> | ||
| 21 | − <img src="https://img.shields.io/badge/SQLite-WAL-003B57?style=flat-square&logo=sqlite&logoColor=white" alt="SQLite"> | 21 | + | ||
| 22 | − <img src="https://img.shields.io/badge/Groupe-KA-ff6a00?style=flat-square" alt="Groupe KA"> | 22 | +<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.lou-ka.com%2Fapi%2Fstats&query=%24.total&label=annonces%20actives&color=ff6a00&style=flat-square" alt="Annonces actives (live)"> | ||
| 23 | −</p> | 23 | +<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.lou-ka.com%2Fapi%2Fstats&query=%24.sources&label=sources%20actives&color=ff6a00&style=flat-square" alt="Sources actives (live)"> | ||
| 24 | +<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.lou-ka.com%2Fapi%2Fstats%2Fdetailed&query=%24.totals.cities&label=villes&color=ff6a00&style=flat-square" alt="Villes (live)"> | ||||
| 25 | +<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.lou-ka.com%2Fapi%2Fstats%2Fdetailed&query=%24.totals.regions&label=r%C3%A9gions&color=ff6a00&style=flat-square" alt="Régions (live)"> | ||||
| 26 | +<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.lou-ka.com%2Fapi%2Fstats%2Fdetailed&query=%24.totals.median&label=loyer%20m%C3%A9dian&suffix=%20%24&color=ff6a00&style=flat-square" alt="Loyer médian (live)"> | ||||
| 27 | +<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.lou-ka.com%2Fapi%2Fstats%2Fdetailed&query=%24.totals.gps_pct&label=g%C3%A9olocalis%C3%A9es&suffix=%20%25&color=ff6a00&style=flat-square" alt="Géolocalisées (live)"> | ||||
| 28 | +<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.lou-ka.com%2Fapi%2Fkascores%2Fstats&query=%24.couverture_pct&label=couverture%20KA%20Scores&suffix=%20%25&color=ff6a00&style=flat-square" alt="Couverture KA Scores (live)"> | ||||
| 29 | +<img src="https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fwww.lou-ka.com%2Fapi%2Fct%2Fstats&query=%24.total&label=s%C3%A9jours%20court%20terme&color=ff6a00&style=flat-square" alt="Séjours court terme (live)"> | ||||
| 30 | + | ||||
| 31 | +</div> | ||||
| 32 | + | ||||
| 33 | +<div align="center"> | ||||
| 34 | + | ||||
| 35 | +<img src="https://img.shields.io/badge/Python-3.14-3776AB?style=flat-square&logo=python&logoColor=white" alt="Python"> | ||||
| 36 | +<img src="https://img.shields.io/badge/FastAPI-API_+_SSR-009688?style=flat-square&logo=fastapi&logoColor=white" alt="FastAPI"> | ||||
| 37 | +<img src="https://img.shields.io/badge/React_18-Vite_+_TS-087EA4?style=flat-square&logo=react&logoColor=white" alt="React"> | ||||
| 38 | +<img src="https://img.shields.io/badge/SQLite-WAL-003B57?style=flat-square&logo=sqlite&logoColor=white" alt="SQLite"> | ||||
| 39 | +<img src="https://img.shields.io/badge/Mapbox_GL-ka--maps-000000?style=flat-square&logo=mapbox&logoColor=white" alt="Mapbox GL"> | ||||
| 40 | +<img src="https://img.shields.io/badge/tunnel-ngrok-1F1E37?style=flat-square&logo=ngrok&logoColor=white" alt="ngrok"> | ||||
| 41 | +<img src="https://img.shields.io/badge/Groupe-KA-ff6a00?style=flat-square" alt="Groupe KA"> | ||||
| 42 | +<a href="https://git.spboucher.ai"><img src="https://img.shields.io/badge/remote--first-spbgit-8250df?style=flat-square&logo=git&logoColor=white" alt="spbgit"></a> | ||||
| 43 | + | ||||
| 44 | +</div> | ||||
| 24 | 45 | ||||
| 25 | Chercher un appartement au Québec, c'est ouvrir des dizaines de sites 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** (synchronisation périodique + hash de contenu → ajouts, mises à jour et retraits automatiques ; une annonce disparue répond `410 Gone`). Chaque fiche renvoie vers l'annonce originale du gestionnaire via une passerelle de sortie transparente (`/passerelle/{uid}`). | 46 | Chercher un appartement au Québec, c'est ouvrir des dizaines de sites 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** (synchronisation périodique + hash de contenu → ajouts, mises à jour et retraits automatiques ; une annonce disparue répond `410 Gone`). Chaque fiche renvoie vers l'annonce originale du gestionnaire via une passerelle de sortie transparente (`/passerelle/{uid}`). | ||
| 26 | 47 | ||||
| 27 | −Pour qui ? Les locataires qui cherchent (recherche filtrée, carte 3D, fiches complètes), les curieux du marché (observatoire des loyers, rapports PDF) et les gestionnaires (pages réclamables). **En chiffres** : 22 900+ annonces actives, 265 sources recensées, 212 connecteurs, 820 villes, 11 régions, ~70 % des annonces géolocalisées. | 48 | +Pour qui ? Les locataires qui cherchent (recherche filtrée, carte 3D, fiches complètes), les curieux du marché (observatoire des loyers, rapports PDF) et les gestionnaires (pages réclamables). **En chiffres (au 2026-08-24)** : 45 762 annonces actives publiées, 281 sources recensées, 250 connecteurs (221 location long terme + 29 court terme), 1 233 villes, 11 régions, 97,4 % des annonces géolocalisées, loyer médian 1 700 $ — plus **41 460 séjours court terme** (chalets, Airbnb…) dans la section `/court-terme`. | ||
| 28 | 49 | ||||
| 29 | ## Visite guidée | 50 | ## Visite guidée | ||
| 30 | 51 | ||||
@@ -82,33 +103,48 @@ Pour qui ? Les locataires qui cherchent (recherche filtrée, carte 3D, fiches co | |||||
| 82 | - **Recherche filtrée** — ville, quartier, type (3½, 4½…), loyer min/max, animaux, meublé, superficie, texte libre. | 103 | - **Recherche filtrée** — ville, quartier, type (3½, 4½…), loyer min/max, animaux, meublé, superficie, texte libre. | ||
| 83 | - **Vue carte 3D** — Lou-Ka Maps (framework `@groupe-ka/ka-maps`, moteur Mapbox GL JS) : clusters par prix moyen, « Rechercher dans cette zone », bascule 3D/2D, synchro liste ↔ carte. | 104 | - **Vue carte 3D** — Lou-Ka Maps (framework `@groupe-ka/ka-maps`, moteur Mapbox GL JS) : clusters par prix moyen, « Rechercher dans cette zone », bascule 3D/2D, synchro liste ↔ carte. | ||
| 84 | - **Fiches complètes** — toutes les photos, digest structuré de la description (text mining regex FR, aucun LLM), badge marché, historique de prix (`price_log`), fiche PDF avec QR code. | 105 | - **Fiches complètes** — toutes les photos, digest structuré de la description (text mining regex FR, aucun LLM), badge marché, historique de prix (`price_log`), fiche PDF avec QR code. | ||
| 85 | −- **Couches de données de quartier** — recensement 2021, registre des loyers, inondation (BDZI), criminalité (SPVM), qualité de l'air, prix de l'essence, commerces et transport en commun, estimation Hydro-Québec, **KA Scores**. | 106 | +- **Couches de données de quartier** — recensement 2021, registre des loyers (57 433 déclarations géocodées), inondation (BDZI), criminalité (SPVM, 12 mois par catégorie), qualité de l'air (RSQAQ/PST), prix de l'essence, commerces et transport en commun (métro/REM aux couleurs officielles), estimation Hydro-Québec live, **KA Scores** (95,5 % du parc couvert au 2026-08-24). | ||
| 107 | +- **Location court terme** — section isolée `/court-terme` (sous-système `louka/shortterm/`, API `/api/ct/*`) : chalets, Airbnb (quadrillage carte quadtree limité à la frontière du Québec), Vrbo, Booking… 41 460 séjours, 22 sources actives sur 38 recensées, 27 régions (au 2026-08-24) ; SEO complet (JSON-LD `VacationRental`, sitemaps `sitemap-ct-N.xml`). | ||||
| 86 | - **Pages villes SEO** — `/villes`, `/ville/{slug}`, `/ville/{slug}/{type}` en SSR léger, sitemaps dynamiques (chunks de 10 000), JSON-LD (`RealEstateListing` + `Offer` CAD), vrais `404`/`410`. | 108 | - **Pages villes SEO** — `/villes`, `/ville/{slug}`, `/ville/{slug}/{type}` en SSR léger, sitemaps dynamiques (chunks de 10 000), JSON-LD (`RealEstateListing` + `Offer` CAD), vrais `404`/`410`. | ||
| 87 | −- **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`). | 109 | +- **Observatoire du marché** — KPI temps réel, loyers médians par région/ville/taille, panneaux territoire (registre des loyers, population, essence), rapport de marché PDF multi-pages (`/api/stats/rapport.pdf`) et rapports PDF personnalisés (catalogue + `ReportBuilder`). | ||
| 88 | −- **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`. | 110 | +- **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` (281 entrées recensées). | ||
| 111 | +- **Qualité éditoriale** — porte de pertinence (quarantaine des stationnements, garages, locaux commerciaux, locations à la nuit) : 59 668 annonces publiées / 1 175 en quarantaine, complétude moyenne 91,4 % (au 2026-08-24). | ||||
| 89 | - **Comptes & SSO KA** — « Se connecter avec KA » via le hub groupe-ka.com (JWT HS256, `ka_id` émis par le hub) + Google OAuth direct ; favoris synchronisés vers « Mon univers Ka » ; pages gestionnaires réclamables (`/g/{source_id}`). | 112 | - **Comptes & SSO KA** — « Se connecter avec KA » via le hub groupe-ka.com (JWT HS256, `ka_id` émis par le hub) + Google OAuth direct ; favoris synchronisés vers « Mon univers Ka » ; pages gestionnaires réclamables (`/g/{source_id}`). | ||
| 90 | −- **Widget KA Agent** — bulle de chat IA du Groupe KA intégrée au site. | 113 | +- **Widget KA Agent** — bulle de chat IA du Groupe KA intégrée au site (déplaçable, position mémorisée). | ||
| 91 | - **PWA installable** + app iOS SwiftUI 100 % native (dépôt séparé `lou-ka-ios` sur spbgit). | 114 | - **PWA installable** + app iOS SwiftUI 100 % native (dépôt séparé `lou-ka-ios` sur spbgit). | ||
| 92 | −- **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. | 115 | +- **Robustesse** — garde-fou anti-dérive (`DRIFT_RATIO = 0.25`, `louka/db.py`) : si une source retourne soudainement beaucoup moins d'annonces, les retraits sont suspendus au lieu de vider l'inventaire ; chaîne de fetch anti-bot résiliente (Oxylabs → Scrapfly → Bright Data). | ||
| 93 | 116 | ||||
| 94 | ## API (endpoints principaux) | 117 | ## API (endpoints principaux) | ||
| 95 | 118 | ||||
| 96 | Servis par `louka/web.py` (FastAPI) sur le port 8095. | 119 | Servis par `louka/web.py` (FastAPI) sur le port 8095. | ||
| 97 | 120 | ||||
| 98 | −| Endpoint | Rôle | | 121 | +### Location long terme | ||
| 99 | −|---|---| | 122 | + | ||
| 100 | −| `GET /api/search` | recherche unifiée liste ↔ carte (mêmes filtres, même compteur) | | 123 | +| Méthode | Endpoint | Rôle | | ||
| 101 | −| `GET /api/listings` · `GET /api/listings/{uid}` | annonces paginées + fiche détaillée (`410 Gone` si retirée) | | 124 | +|---|---|---| | ||
| 102 | −| `GET /api/listings.geojson` | flux GeoJSON pour la carte 3D | | 125 | +| `GET` | `/api/search` | recherche unifiée liste ↔ carte (mêmes filtres, même compteur) | | ||
| 103 | −| `GET /api/listings/{uid}/pdf` | fiche PDF avec QR code | | 126 | +| `GET` | `/api/listings` · `/api/listings/{uid}` | annonces paginées + fiche détaillée (`410 Gone` si retirée) | | ||
| 104 | −| `GET /api/facets` · `GET /api/sources` | facettes de filtres, registre des sources | | 127 | +| `GET` | `/api/listings.geojson` | flux GeoJSON pour la carte 3D | | ||
| 105 | −| `GET /api/fairvalue/{uid}` | badge marché (juste prix) d'une annonce | | 128 | +| `GET` | `/api/listings/{uid}/pdf` | fiche PDF avec QR code | | ||
| 106 | −| `GET /api/rdl` · `/api/inondation` · `/api/air` · `/api/gaz` · `/api/commerces` · `/api/hydro` | couches de quartier : registre des loyers, BDZI, qualité de l'air, essence, commerces/transport, estimation Hydro-Québec | | 129 | +| `GET` | `/api/facets` · `/api/sources` | facettes de filtres, registre des sources | | ||
| 107 | −| `GET /api/kascores/stats` | statistiques du moteur KA Scores | | 130 | +| `GET` | `/api/fairvalue/{uid}` | badge marché (juste prix) d'une annonce | | ||
| 108 | −| `GET /api/stats` · `/detailed` · `/dashboard` · `/report` · `/catalog` · `POST /api/stats/report/custom` | observatoire + rapports PDF (catalogue et personnalisés) | | 131 | +| `GET` | `/api/rdl` · `/api/inondation` · `/api/air` · `/api/gaz` · `/api/commerces` · `/api/hydro` | couches de quartier : registre des loyers, BDZI, qualité de l'air, essence, commerces/transport, estimation Hydro-Québec | | ||
| 109 | −| `GET /api/stats/rapport.pdf` | rapport de marché PDF multi-pages | | 132 | +| `GET` | `/api/kascores/stats` | statistiques du moteur KA Scores | | ||
| 110 | −| `POST /api/sync` | déclenchement d'une synchronisation | | 133 | +| `GET` | `/api/stats` · `/detailed` · `/dashboard` · `/report` · `/catalog` | observatoire : KPI, détail, tableau de bord, rapports | | ||
| 111 | −| `GET /doc/` | guide d'utilisation en ligne | | 134 | +| `POST` | `/api/stats/report/custom` | rapport PDF personnalisé (ReportBuilder) | | ||
| 135 | +| `GET` | `/api/stats/rapport.pdf` | rapport de marché PDF multi-pages | | ||||
| 136 | +| `POST` | `/api/sync` | déclenchement d'une synchronisation | | ||||
| 137 | +| `GET` | `/doc/` | guide d'utilisation en ligne | | ||||
| 138 | + | ||||
| 139 | +### Location court terme (`/api/ct`, préfixe du sous-système `louka/shortterm/`) | ||||
| 140 | + | ||||
| 141 | +| Méthode | Endpoint | Rôle | | ||||
| 142 | +|---|---|---| | ||||
| 143 | +| `GET` | `/api/ct/listings` · `/api/ct/listings/{uid}` | séjours paginés + fiche détaillée | | ||||
| 144 | +| `GET` | `/api/ct/listings.geojson` | flux GeoJSON de la carte court terme | | ||||
| 145 | +| `GET` | `/api/ct/facets` · `/api/ct/sources` | facettes, registre des sources court terme | | ||||
| 146 | +| `GET` | `/api/ct/stats` | KPI court terme (total, sources, régions, prix/nuit) | | ||||
| 147 | +| `POST` | `/api/ct/sync` | déclenchement d'une synchronisation court terme | | ||||
| 112 | 148 | ||||
| 113 | ## Architecture | 149 | ## Architecture | ||
| 114 | 150 | ||||
@@ -116,36 +152,96 @@ Pipeline : **connecteurs → normalisation → déduplication → SQLite → API | |||||
| 116 | 152 | ||||
| 117 | - **Backend Python 3.14 / FastAPI / Uvicorn** (`louka/web.py`) : API JSON + service du build Vite + SSR SEO (`louka/seo.py`). | 153 | - **Backend Python 3.14 / FastAPI / Uvicorn** (`louka/web.py`) : API JSON + service du build Vite + SSR SEO (`louka/seo.py`). | ||
| 118 | - **SQLite (WAL)** (`louka/db.py`) : migrations auto, upsert par hash de contenu, anti-dérive ; bases annexes dans `data/` (quartier, air, gaz, hydro, commerces, inondation…). | 154 | - **SQLite (WAL)** (`louka/db.py`) : migrations auto, upsert par hash de contenu, anti-dérive ; bases annexes dans `data/` (quartier, air, gaz, hydro, commerces, inondation…). | ||
| 119 | −- **212 connecteurs** auto-découverts (`louka/connectors/`), extraction directe / Firecrawl / Scrapfly selon la source. | 155 | +- **250 connecteurs** auto-découverts : 221 long terme (`louka/connectors/`) + 29 court terme (`louka/shortterm/connectors/`), extraction directe / Firecrawl / Scrapfly / Bright Data / acteurs Apify maison selon la source. | ||
| 120 | - **Enrichissement** : géocodage (Nominatim + Adresses Québec), POI OSM, stats de quartier, KA Scores, estimation Hydro-Québec. | 156 | - **Enrichissement** : géocodage (Nominatim + Adresses Québec), POI OSM, stats de quartier, KA Scores, estimation Hydro-Québec. | ||
| 121 | - **Frontend React 18 + Vite + TypeScript** (`frontend/`), cartographie `@groupe-ka/ka-maps` (Mapbox GL). | 157 | - **Frontend React 18 + Vite + TypeScript** (`frontend/`), cartographie `@groupe-ka/ka-maps` (Mapbox GL). | ||
| 122 | −- **CLI `run.py`** : `sync` · `watch` · `serve` · `geocode` · `poi` · `quartier` · `record` (fixtures HTTP pour tests hors-ligne). | 158 | +- **CLI `run.py`** : `sync` · `watch` · `sync-ct` · `watch-ct` · `serve` · `geocode` · `quality` · `imgaudit` · `fairvalue` · `poi` · `quartier` · `kascores` · `rdl` · `air-refresh` · `gaz-refresh` · `hydro-precompute` · `record` (fixtures HTTP pour tests hors-ligne). | ||
| 123 | 159 | ||||
| 124 | Processus PM2 sur le nœud : | 160 | Processus PM2 sur le nœud : | ||
| 125 | 161 | ||||
| 126 | −| Processus | Rôle | Cadence | | 162 | +| Processus | Commande réelle | Rôle | Cadence | | ||
| 127 | −|---|---|---| | 163 | +|---|---|---|---| | ||
| 128 | −| `lou-ka-web` | serveur FastAPI/Uvicorn (API + frontend + SSR) sur le port **8095** | continu | | 164 | +| `lou-ka-web` | `.venv/bin/python run.py serve 8095` | serveur FastAPI/Uvicorn (API + frontend + SSR) sur le port **8095** | continu | | ||
| 129 | −| `lou-ka-sync` | boucle de resynchronisation des connecteurs (`run.py watch 60`) | cycle de 60 min | | 165 | +| `lou-ka-sync` | `.venv/bin/python run.py watch 60` | boucle de resynchronisation des connecteurs long terme | cycle de 60 min | | ||
| 130 | −| `lou-ka-ngrok` | tunnel ngrok vers **www.lou-ka.com** | continu | | 166 | +| `lou-ka-ngrok` | `ngrok http --url=www.lou-ka.com 8095` | tunnel ngrok vers **www.lou-ka.com** | continu | | ||
| 167 | + | ||||
| 168 | +La section court terme se resynchronise via `run.py watch-ct` (défaut 360 min) ou à la demande (`run.py sync-ct`, `POST /api/ct/sync`). | ||||
| 169 | + | ||||
| 170 | +## Démarrage rapide | ||||
| 131 | 171 | ||||
| 132 | −Points de configuration notables (variables d'environnement, sans secrets dans le repo) : clés Scrapfly/Firecrawl pour certains connecteurs, jeton Mapbox du frontend, secret SSO partagé avec le hub KA ID. Le registre des sources vit dans `data/sources.json` ; les bases SQLite de prod sont gitignorées. | 172 | +```bash | ||
| 173 | +# Backend (Python 3.14) | ||||
| 174 | +python3 -m venv .venv && .venv/bin/pip install -r requirements.txt | ||||
| 175 | + | ||||
| 176 | +# Frontend (nécessite le framework ka-maps bâti à côté : ../ka-maps) | ||||
| 177 | +cd frontend && npm install && npm run build && cd .. | ||||
| 178 | + | ||||
| 179 | +.venv/bin/python run.py sync # ingestion (toutes les sources ou une liste) | ||||
| 180 | +.venv/bin/python run.py sync-ct # ingestion court terme | ||||
| 181 | +.venv/bin/python run.py serve 8095 # API + frontend sur http://localhost:8095 | ||||
| 182 | +.venv/bin/python run.py watch 60 # resynchronisation en boucle (minutes) | ||||
| 183 | + | ||||
| 184 | +# Tests (pytest + fixtures HTTP rejouables hors-ligne) | ||||
| 185 | +.venv/bin/python -m pytest tests/ | ||||
| 186 | +``` | ||||
| 187 | + | ||||
| 188 | +En développement frontend : `cd frontend && npm run dev` (Vite sur `:5173`, proxy `/api`). | ||||
| 189 | + | ||||
| 190 | +## Variables d'environnement | ||||
| 191 | + | ||||
| 192 | +Aucun secret dans le repo — tout passe par l'environnement du nœud. Noms attendus : | ||||
| 193 | + | ||||
| 194 | +| Variable | Rôle | | ||||
| 195 | +|---|---| | ||||
| 196 | +| `SCRAPFLY_KEY` · `FIRECRAWL_API_KEY` · `APIFY_TOKEN` | extraction anti-bot / rendu / acteurs Apify maison | | ||||
| 197 | +| `BRIGHTDATA_API_KEY` · `BRIGHTDATA_ZONE` | Web Unlocker Bright Data (repli anti-bot) | | ||||
| 198 | +| `OXYLABS_PROXY` · `OXYLABS_PROXY_USER` · `OXYLABS_PROXY_PASS` | proxies résidentiels (1er maillon de la chaîne de fetch) | | ||||
| 199 | +| `GOOGLE_CLIENT_ID` · `GOOGLE_CLIENT_SECRET` | connexion Google OAuth directe | | ||||
| 200 | +| `KA_SSO_SECRET` · `KA_HUB_URL` · `SESSION_SECRET` | SSO KA ID (JWT HS256 partagé avec groupe-ka.com) + sessions | | ||||
| 201 | +| `HQ_CAPTCHA_PROVIDER` · `HQ_CAPTCHA_KEY` | résolution reCAPTCHA de l'estimation Hydro-Québec | | ||||
| 202 | +| `LOUKA_BASE_URL` | URL publique de base (liens PDF, sitemaps) | | ||||
| 203 | +| `LOUKA_<SOURCE>_*` (≈70 variables) | tuning par connecteur : limites de détail, TTL, pages max, cadence (ex. `LOUKA_KIJIJI_DETAIL_LIMIT`, `LOUKA_FBMP_CITIES`, `LOUKA_AIRBNB_BUDGET`) | | ||||
| 204 | +| `VITE_MAPBOX_TOKEN` (frontend) | jeton public Mapbox de la carte 3D | | ||||
| 205 | + | ||||
| 206 | +## Données & conformité | ||||
| 207 | + | ||||
| 208 | +- **Agrégateur indépendant** : aucune affiliation avec les gestionnaires immobiliers ; chaque fiche renvoie vers l'annonce originale via la passerelle de sortie transparente `/passerelle/{uid}` (mention affichée au pied du site). | ||||
| 209 | +- **Provenance** : sites publics des gestionnaires et portails d'annonces (281 sources recensées dans `data/sources.json`, statut et raison documentés pour chaque source non connectable) ; couches de quartier issues de données ouvertes (recensement 2021, registre des loyers, BDZI, SPVM, RSQAQ, Régie de l'énergie, OSM). | ||||
| 210 | +- **Cadence** : resynchronisation long terme en boucle de 60 min (`lou-ka-sync`), court terme par cycles de 360 min ; détection de changement par hash de contenu → ajouts, mises à jour et retraits automatiques. | ||||
| 211 | +- **Cycle de vie honnête** : une annonce retirée chez la source répond `410 Gone` et sort des sitemaps ; le garde-fou anti-dérive suspend les retraits massifs suspects plutôt que de vider l'inventaire. | ||||
| 212 | +- **Qualité** : porte de pertinence (quarantaine du hors-périmètre : stationnements, commerciaux, nuitées) et score de complétude par annonce (moyenne 91,4 %). | ||||
| 133 | 213 | ||||
| 134 | ## Structure du repo | 214 | ## Structure du repo | ||
| 135 | 215 | ||||
| 136 | ``` | 216 | ``` | ||
| 137 | lou-ka/ | 217 | lou-ka/ | ||
| 138 | −├── run.py # point d'entrée CLI (sync / watch / serve / geocode / poi / quartier / record) | 218 | +├── run.py # point d'entrée CLI (sync / watch / sync-ct / watch-ct / serve / geocode / poi / quartier / record) | ||
| 139 | −├── louka/ # paquet backend : web, seo, db, ingest, normalize, textmine, auth, kascores… + connectors/ (212) | 219 | +├── louka/ # paquet backend : web, seo, db, ingest, normalize, textmine, auth, kascores… | ||
| 220 | +│ ├── connectors/ # 221 connecteurs long terme auto-découverts | ||||
| 221 | +│ └── shortterm/ # sous-système court terme isolé (db, ingest, web + 29 connecteurs) | ||||
| 140 | ├── frontend/ # React 18 + Vite + TypeScript (pages, composants, kamaps/) + public/doc/ (guide) | 222 | ├── frontend/ # React 18 + Vite + TypeScript (pages, composants, kamaps/) + public/doc/ (guide) | ||
| 141 | −├── data/ # sources.json (registre) + bases SQLite de prod (gitignorées) | 223 | +├── data/ # sources.json (registre, 281 entrées) + bases SQLite de prod (gitignorées) | ||
| 142 | −├── scripts/ # build recensement/contexte/environnement, screenshots, check-order | 224 | +├── scripts/ # build recensement/contexte/environnement, docs connecteurs, screenshots, check-order | ||
| 143 | ├── tests/ # pytest + fixtures HTTP rejouables hors-ligne | 225 | ├── tests/ # pytest + fixtures HTTP rejouables hors-ligne | ||
| 144 | −├── actors/ # acteurs Apify d'appoint | 226 | +├── actors/ # acteurs Apify d'appoint (ka-fb-marketplace, ka-apartments-com…) | ||
| 145 | ├── reports/ # audits (fiches connecteurs, expansion par région, quartier…) | 227 | ├── reports/ # audits (fiches connecteurs, expansion par région, quartier…) | ||
| 146 | └── docs/ # captures d'écran + docs connecteurs + ka-maps | 228 | └── docs/ # captures d'écran + docs connecteurs + ka-maps | ||
| 147 | ``` | 229 | ``` | ||
| 148 | 230 | ||||
| 231 | +## Historique | ||||
| 232 | + | ||||
| 233 | +Repère des jalons (128 commits depuis la naissance) : | ||||
| 234 | + | ||||
| 235 | +| Date | Jalon | | ||||
| 236 | +|---|---| | ||||
| 237 | +| 2026-08-06 | naissance du projet : agrégateur Québec/Lévis/Grand Montréal, page Statistiques (`fb48938`, `ece6ed0`) | | ||||
| 238 | +| 2026-08-08 | Mission 1a — couche commune : schéma enrichi, normalisation centrale, robustesse, tests (`7323079`) | | ||||
| 239 | +| 2026-08-19 | KA Scores calculés sur 94,7 % du parc + standard « ordre des sections = ordre du DOM » (`747f631`, `a6e30ef`) | | ||||
| 240 | +| 2026-08-21→22 | porte de pertinence (quarantaine hors-périmètre) + chaîne de fetch anti-bot Oxylabs→Scrapfly→Bright Data + acteurs Apify maison FB Marketplace / Apartments.com (`8547737`, `8598785`, `e7f4876`) | | ||||
| 241 | +| 2026-08-22→23 | section « Location court terme » : sous-système isolé, Airbnb en quadrillage carte, 38 sources recensées, SEO complet (`38b708b`, `5c571bc`, `63456a0`) | | ||||
| 242 | +| 2026-08-23 | couches de quartier : registre des loyers (57 433 déclarations), inondation BDZI, criminalité SPVM, air, essence, commerces/transport, estimation Hydro-Québec live + rapports PDF personnalisés (`604455e` → `4d17c17`, `834c92c`) | | ||||
| 243 | +| 2026-08-24 | page documentation `/doc` + guide PDF + README v2/v3 (`963ba8f`, `4fa8714`) | | ||||
| 244 | + | ||||
| 149 | ## Documentation | 245 | ## Documentation | ||
| 150 | 246 | ||||
| 151 | - **Guide en ligne** : [www.lou-ka.com/doc/](https://www.lou-ka.com/doc/) — à quoi sert le site, le parcours en 4 étapes (recherche → résultats → fiche → /stats), d'où viennent les données, FAQ. | 247 | - **Guide en ligne** : [www.lou-ka.com/doc/](https://www.lou-ka.com/doc/) — à quoi sert le site, le parcours en 4 étapes (recherche → résultats → fiche → /stats), d'où viennent les données, FAQ. | ||
@@ -160,21 +256,9 @@ lou-ka/ | |||||
| 160 | - L'agent forwarding SSH est actif : le `git push origin main` fonctionne pendant une session SSH depuis le laptop. | 256 | - L'agent forwarding SSH est actif : le `git push origin main` fonctionne pendant une session SSH depuis le laptop. | ||
| 161 | 257 | ||||
| 162 | ```bash | 258 | ```bash | ||
| 163 | −# Backend | ||||
| 164 | −python3 -m venv .venv && .venv/bin/pip install -r requirements.txt | ||||
| 165 | − | ||||
| 166 | −# Frontend (nécessite le framework ka-maps bâti à côté : ../ka-maps) | ||||
| 167 | −cd frontend && npm install && npm run build && cd .. | ||||
| 168 | − | ||||
| 169 | −.venv/bin/python run.py sync # ingestion (toutes les sources ou une liste) | ||||
| 170 | −.venv/bin/python run.py serve 8095 # API + frontend | ||||
| 171 | −.venv/bin/python run.py watch 60 # resynchronisation en boucle (minutes) | ||||
| 172 | − | ||||
| 173 | pm2 restart lou-ka-web # après un changement en production | 259 | pm2 restart lou-ka-web # après un changement en production | ||
| 174 | ``` | 260 | ``` | ||
| 175 | 261 | ||||
| 176 | −En développement frontend : `cd frontend && npm run dev` (Vite sur `:5173`, proxy `/api`). | ||||
| 177 | − | ||||
| 178 | ## Déploiement | 262 | ## Déploiement | ||
| 179 | 263 | ||||
| 180 | - **Nœud** : M3U96b (Mac Studio, cluster MacLustr) — répertoire `~/apps/lou-ka` | 264 | - **Nœud** : M3U96b (Mac Studio, cluster MacLustr) — répertoire `~/apps/lou-ka` | ||
| 181 | 265 | ||||