SPB Git

spb/lou-ka

Public

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

History of louka/db.py Clear filter
page 1
  1. Tue, Nov 3, 2026 1

    1. [ka2] fix site down louka: saturation du threadpool par les agrégats plein-table de l'accueil et de /api/stats — cache TTL single-flight. Panne du 2026-11-03 21:02 (502 public, localhost:8095 muet, process pm2 « online ») : py-spy montre les ~40 threads anyio TOUS bloqués dans home_ssr (seo.py) et stats (web.py), le sample natif les montre moulinant dans des lectures SQLite (pages overflow + contention du mutex pcache). Cause racine : chaque GET / exécutait 4 scans plein-table sur listings (louka.db = 5 Go, 254 k lignes aux gros blobs JSON) et chaque /api/stats 2 de plus — rapides à chaud (<1 s) mais >30 s à froid ; après le reboot du nœud (17:14) puis le gros sync de 21:05 qui évince le cache de pages OS, healthchecks + bot (via ngrok) empilent ces requêtes plus vite qu'elles ne finissent, le threadpool se vide et le site devient muet (le redémarrage pm2 de 21:06 n'a tenu que ~2 min, le temps que la pile se reforme). Fix : agrégats de l'accueil (_home_data, TTL 600 s) et de /api/stats (_stats_aggregates, TTL 300 s) calculés en single-flight — un seul thread recalcule, les appels concurrents servent la version précédente — et plus jamais N fois en parallèle ; recent_syncs/disabled_sources de /api/stats restent live (contrat moniteur api-ka inchangé). Bonus : index idx_sync_log_ts (sync_log = 206 k lignes triées par ts à chaque /api/stats), créé sur la base live + ajouté au schéma db.py. Testé : TestClient / et /api/stats → 200 (1er remplissage ~2 s, hits cachés ~1 ms, payload stats identique), pm2 restart lou-ka-web, puis 2 mesures à 30 s+ d'intervalle : localhost:8095 200 (0,001-0,003 s), https://www.lou-ka.com 200 (0,09-0,20 s), /api/stats 200 — stable à travers un restart du veilleur à 21:16.
      …
      Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
      Simon-Pierre Boucher · 1 file +1
  2. Thu, Aug 27, 2026 2

    1. pause(ontario): expansion ON suspendue temporairement — rien d'effacé
      …
      - .env : LOUKA_ONTARIO=1 commenté (les 56 connecteurs gatés sortent du registre)
      - sources.json : 65 sources ON -> status pause + note_pause
      - quality.evaluate : quarantaine pause-ontario (published=0) pour province=ON
        tant que le flag n'est pas reposé ; province ajoutée au backfill et à
        l'évaluation à l'ingestion (db.py)
      - docs/ONTARIO-PAUSE.md : quoi/pourquoi/comment réactiver
      
      5 851 annonces ON conservées en base, dépubliées. QC intact (62 412 publiées).
      Simon-Pierre Boucher · 1 file +2 −1
    2. feat(ontario): 20 nouveaux connecteurs ON + socle province, tout gaté LOUKA_ONTARIO=1 (zéro impact prod QC)
      …
      Socle : Listing.province + bbox coordonnées ON (schema.py), migration
      province TEXT DEFAULT 'QC' (db.py), géocodage ON via Nominatim sans repli
      Adresses Québec (geocode.py), townhouse/bungalow résidentiels (quality.py).
      
      Nouveaux connecteurs (disabled hors gate) : liftsystem.py (13 clients
      dynamiques lift_* via data/liftsystem_clients.json — Greenwin, Park,
      Williams & McDaniel, Briarlane, DMS, Signet, Shelter, York, Terracorp,
      RentSarnia, First North, TVM, Urbandale ; 922 propriétés résidentielles),
      rentcafe.py (4 clients rc_* via data/rentcafe_clients.json — Effort Trust,
      Osgoode ON, GWLRA, O'Shanter ; Scrapfly ASP), homestead.py + skyline.py
      (passerelle publique website-gateway.rentsync.com), killam.py (blob
      #available_prop RentCafe).
      
      Connecteurs QC élargis sous gate (uids QC vérifiés identiques hors gate) :
      capreit +189, boardwalk +74, minto +81, hazelview +14, interrent +83,
      morguard +258, akelius +95, metcap +60, realstar +12, cogir +38,
      centurion +52 annonces ON potentielles.
      
      sources.json : +19 entrées ON (status inactif) + skyline recyclé.
      Registre : 365 connecteurs sans gate (inchangé), 385 avec LOUKA_ONTARIO=1.
      Potentiel à l'activation : ~1 200-1 600 annonces ON (~30 sources).
      
      Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
      Simon-Pierre Boucher · 1 file +8 −4
  3. Mon, Aug 24, 2026 1

    1. Simon-Pierre Boucher · 1 file +140 −1
  4. Fri, Aug 21, 2026 1

    1. quality: porte de pertinence — quarantaine des stationnements, garages, locaux commerciaux et locations à la nuit
      …
      Certaines sources mêlent aux logements des cases de stationnement (~200 $),
      garages/entreposage, locaux commerciaux/bureaux, objets divers et tarifs à la
      nuit. relevance_reason() les détecte (titre + description + seuils de prix,
      sans faux positif sur les logements « avec stationnement inclus ») et evaluate()
      les met en quarantaine (published=0). Backfill appliqué : 78 annonces retirées.
      
      Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
      Simon-Pierre Boucher · 1 file +1
  5. Tue, Aug 18, 2026 3

    1. KA Scores — suite géographique maison (Walk/Transit/Bike/Calme/Services)
      …
      · environment.py : inventaire OSM par tuile (Overpass) — routes majeures,
        rails, aéroports, zones industrielles, bars, pistes cyclables (géométrie
        densifiée) et TOUS les POI par catégorie ; cache env_tiles ~3 mois.
      · kascores.py : 5 scores 0-100 par immeuble + global pondéré, barème
        versionné (2026.08-v1), détails honnêtes par score, « Données
        insuffisantes »/« Non desservi » plutôt que des 0 trompeurs ; Transit =
        proximité des arrêts × percentile PMD StatCan ; calcul incrémental
        (7 900 immeubles en ~8 s), distribution de calibration en sortie.
      · API : ks_* joints à /api/listings et /api/search (tri « ka », filtre
        kascore_min), détail complet sur la fiche, /api/kascores/stats.
      · Frontend : pastille KA sur les cartes d annonces, section KA Scores de
        la fiche (5 jauges + détail dépliable + date/version), page /ka-scores
        (méthodologie publique + pondérations personnelles en curseurs), tri
        « Meilleur KA Score » dans la vue carte, filtre « KA Score minimal ».
      · Boucle watch : env (4 tuiles max) + kascores incrémental à chaque sync.
      · Tests : test_kascores.py (barème, honnêteté, rendement décroissant,
        cohérence spatiale sur le parc réel).
      
      Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
      Simon-Pierre Boucher · 1 file +23
    2. Qualité des données + solidité mobile + framework de juste valeur (fair value)
      …
      Mission 1 — enrichissement des connecteurs et qualité :
      - quality.py : score de complétude 0-100 + seuil de publication (quarantaine
        des annonces sans prix plausible / localisation / contenu exploitable)
      - imgcheck.py : audit des images (liens morts 404/410, minuscules, placeholders
        multi-adresses, doublons) -> galeries nettoyées images_ok, cache image_checks,
        politesse 3 requêtes/hôte, écriture hors transaction réseau
      - 26 connecteurs enrichis (galeries complètes, descriptions, prix, adresses) :
        kangalou, royal_lepage, medway, remax_quebec, groupe_maurice, mellem, sutton,
        lespac, heafey, kijiji, fb_marketplace, zumper, equinoxe_batimo, logis_experts,
        quartier_sila, msi, summum, quartier_plus, urban_services, cosoltec, garic,
        lakle, matinale, huma, jutras, groupe_theoret, ferrovia
      - normalize : clean_city/clean_sector (rues et entités HTML retirées des menus),
        kijiji : adresses françaises « 89, rue X » correctement découpées
      - geocode : virgule après le n° civique corrigée (lot AQ), base en WAL
      - dedup : une annonce en quarantaine ne masque jamais un doublon publié
      
      Mission 2 — mobile :
      - /api/img : miniatures WebP servies à la taille de l écran (cache disque)
      - SmartImg : miniature -> original -> visuel de secours Lou-Ka (jamais d icône cassée)
      - lightbox tactile : balayage scroll-snap + pincement + double-tape
      - bannière témoins : ne recouvre plus le bouton Filtres (--consent-h)
      - cibles tactiles >= 44 px (pointer: coarse) ; KA Agent plein écran à l ouverture
      - stats : complétude moyenne, quarantaine par source
      
      Mission 3 — juste valeur locative (fairvalue.py, fv-logement-1.0) :
      - 3 niveaux : segments médians (repli en échelle) + coefficients de
        caractéristiques appris et centrés + comparables kNN géographiques (médiane
        pondérée) ; couloir de sécurité, fourchette, indice de confiance
      - prudence : pas de verdict si confiance faible, prix « à partir de »,
        prix non mensuel, écart hors plausible ; chambres/coloc = segment dédié
      - pastilles sous/dans/au-dessus du marché (cartes + fiche), bloc Analyse de
        prix avec histogramme, filtre « Sous le marché » + tri, page /juste-valeur
      - API : champs fv_* + deal/sort, /api/fairvalue/{uid} ; stats + PDF ;
        historisation fv_segments ; tests synthétiques ; docs/FAIRVALUE*.md
      
      Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
      Simon-Pierre Boucher · 1 file +49 −5
    3. Vague 2 : colonnes bedrooms/bathrooms, perf fb_marketplace/logisquebec, connecteurs Kangalou + Rentals.ca, visites virtuelles
      …
      - Schéma : colonnes bedrooms/bathrooms REAL (additives) dérivées dans
        finalize() — details explicites > type d'unité (n½ -> n-2, convention QC)
        > extraction texte ; backfill one-shot : 96,7 % bedrooms / 51 % bathrooms
        sur le parc actif.
      - fb_marketplace : recherche et détails Scrapfly EN PARALLÈLE (6 fils,
        mêmes requêtes, aucun volume perdu), TTL détail 10 -> 30 j, annonces
        louées filtrées avant enrichissement : 43-47 min -> 7,3 min/cycle.
      - logisquebec : budget détail 1200 -> 400 (dimensionné TTL 7 j / ~10
        passes/j), fiches jamais vues servies en premier, délai 0,7 -> 0,5 s :
        19 min -> 7,6 min/cycle, parc complet (6 393 fiches).
      - Kangalou (nouveau) : découverte en 1 requête via les pins de la carte
        (/fr/recherche/ajax/search-map/, loués/commerciaux exclus), fiches SSR
        (adresse, chambres/SDB, GPS, galerie, dispo, superficie) : 7 916 annonces.
      - Rentals.ca (nouveau) : même GraphQL que Louer.ca mais inventaire ~+15 %,
        rejoué via Scrapfly ASP sans rendu JS (Cloudflare), détails PAR LOTS de
        40 via nodes(ids), villes/volumes plafonnés : 3 452 annonces (les
        recoupements Louer.ca sont résorbés par la dédup, déjà dans PORTALS).
      - Visites virtuelles : details.virtual_tour — tours{} Matterport/YouTube
        du réseau Rentals.ca (louer_ca clé cache v2 + rentals_ca) + détection
        générique Matterport/iGUIDE/Klapty dans les fiches logisquebec,
        duproprio, kangalou. 1 107 annonces actives en ont déjà une.
      - TtlDetailCache : peek()/put() pour la planification de lots parallèles.
      - sources.json : +kangalou, +rentals_ca (275 sources, 212 connecteurs).
      
      Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
      Simon-Pierre Boucher · 1 file +11 −3
  6. Mon, Aug 17, 2026 1

    1. Nouvelles sources + déduplication inter-sources
      …
      8 connecteurs (208 → 212), testés en live :
      - logisquebec — portail (~9 800 fiches) : sitemaps + JSON-LD, expiration HTTP 410
      - duproprio — location par particuliers (~4 200, 0 % chevauchement) : sitemaps gz + JSON-LD, retrait = 302
      - louer_ca — réseau Rentals.ca (~1 700) : API GraphQL interne (JWT), 1 annonce par plan d'étage
      - roomies — chambres/colocation : JSON-LD Room
      - groupe_maurice — 37 résidences pour aînés (RPA) : API REST + grilles de prix
      - bons_locataires — agence Montréal : carte MapListPro + fiches WordPress
      - omhm — logements abordables ENHARMONIE
      - fb_marketplace — Scrapfly ASP, pagination par tranches de prix (scroll bloqué hors login)
      
      Déduplication inter-sources (louka/dedup.py) :
      - colonnes dup_of / dup_sources (migration db.py), recalcul idempotent dans ingest.watch() après géocodage
      - blocage par clé d'adresse (civique + rue normalisée FR/EN + ville), confirmation type + prix ±4 %
      - contrainte « ≤1 annonce par source par groupe » (évite de fusionner des unités distinctes d'un immeuble)
      - canonique = source la plus autoritaire (gestionnaire direct > portail > petites annonces)
      - web.py n'affiche que les annonces canoniques (dup_of IS NULL) ; dup_sources exposé pour le badge « aussi publiée sur »
      - résultat : 147 groupes, 154 doublons masqués sur 18 536 annonces
      
      Utilitaire _detailutil.TtlDetailCache : cache de pages détail avec TTL + budget par sync.
      
      Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
      Simon-Pierre Boucher · 1 file +2
  7. Thu, Aug 13, 2026 5

    1. Lou-Ka Maps — carte propulsée par Ka Maps (framework Groupe Ka, moteur Mapbox 3D)
      …
      Frontend : MapView réécrit sur @groupe-ka/ka-maps (file:../../ka-maps) —
      style Mapbox Standard 3D par défaut + bouton 2D, pastilles de prix encre/
      blanc, grappes compte + loyer moyen (clamp anti-prix aberrants),
      « Rechercher dans cette zone », aperçu React (fin du popup HTML injecté),
      sync liste↔carte (sélection + survol), caméra dans l'URL, badge Lou-Ka
      Maps · Groupe Ka, jeton public Mapbox (kamaps/config.ts). Recherche
      debouncée (fin des requêtes à chaque frappe).
      Backend : /api/listings.geojson accepte bbox + limit et renvoie
      totalGeocoded/totalMatching ; index idx_listings_geo(lat,lng) ; PRAGMA
      optimize au cycle d'ingestion (le planificateur choisissait le mauvais
      index).
      
      Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
      Simon-Pierre Boucher · 1 file +1
    2. Profil enrichi — photo téléversée, bio, ville, coordonnées, réseaux sociaux
      …
      - louka/profile.py : PUT /api/me/profile (nom affiché, bio 280, ville,
        téléphone, site web, réseaux instagram/facebook/x/linkedin/tiktok/youtube),
        POST /api/me/avatar (Pillow : EXIF, recadrage carré centré, 512 px, JPEG,
        purge des anciennes photos), DELETE /api/me/avatar (retour avatar Google)
      - /uploads/ monté (data/uploads/), colonnes users (display_name, avatar_url,
        bio, city, phone, website, socials JSON)
      - /api/me : nom et avatar « effectifs » (display_name > Google, upload >
        Google) + tous les champs ; profil public /u/{ka_id} : bio, ville, rôle,
        site, réseaux (jamais courriel/téléphone)
      - /profil : éditeur complet (photo avec téléversement, nom affiché, bio avec
        compteur, ville, téléphone, site, 6 réseaux avec icônes maison)
      - /u/{ka_id} : bio, chips ville/rôle/site, boutons réseaux sociaux
      - python-multipart ajouté (formulaires multipart FastAPI)
      Simon-Pierre Boucher · 1 file +7
    3. Comptes v2 — KA-ID, profil « carte de membre », rôles, favoris, pages gestionnaires
      …
      - KA-ID : identifiant membre unique « ka- » + 10 chiffres, attribué à la
        création du compte (index unique, backfill au premier /api/me)
      - /profil : carte de membre Groupe KA (encre + lime, KA-ID en vedette, avatar,
        ancienneté, bande décorative), infos du compte, copie du KA-ID
      - Profil PUBLIC opt-in : /u/{ka_id} (nom, avatar, ancienneté — jamais le
        courriel ; 404 indistinguable si non public), toggle + lien copiable
      - Rôles à l'intégration (/bienvenue après le premier login Google) :
        · locataire — favoris (cœur sur les cartes, page /favoris, toggle optimiste)
        · gestionnaire — réclame la page de SA source (1/compte, 1 compte/source)
          et la personnalise (/gestion : accroche, description, contact, logo) ;
          page publique /g/{source} avec inventaire relié + pagination
      - louka/accounts.py (rôles, favoris, source_profiles), tables favorites +
        source_profiles + colonnes users (ka_id, public, role, org_source)
      - Contexte de compte partagé (account.tsx) : header, cartes et pages reliés
      Simon-Pierre Boucher · 1 file +30
    4. Comptes utilisateurs — connexion Google (OAuth 2.0 / OpenID Connect)
      …
      - louka/auth.py : flux authorization-code serveur (login -> Google -> callback),
        state signé, upsert table users, session HMAC-SHA256 en cookie httpOnly 30 j
        (stdlib seulement), /api/me, /api/auth/logout, /api/auth/config
      - db.py : table users (google_sub unique, email, name, picture)
      - Frontend : bouton « Connexion » (logo Google) dans le header, avatar + menu
        compte (courriel, se déconnecter) — masqué tant que l'OAuth n'est pas configuré
      - .env : SESSION_SECRET généré ; GOOGLE_CLIENT_ID/SECRET à coller
        (redirect URI : https://www.lou-ka.com/api/auth/google/callback)
      Simon-Pierre Boucher · 1 file +10
  8. Sat, Aug 8, 2026 4

    1. Fiche v2 mobile-first : essentiel d'abord, badge marché, CTA sticky, historique de prix
      …
      - Ordre mobile : galerie (balayage natif + compteur) -> prix + badge
        « vs le secteur » (seuils -15 %/+10 %) -> chips clés défilantes -> ancres ->
        description -> inclusions (confirmées ✓ vs mentionnées, badge 0 $ de frais
        cachés) -> détails pratiques -> quartier -> à proximité groupé repliable
        avec temps de marche estimés
      - Desktop : 2 colonnes (logement | synthèse+quartier) via display:contents
      - CTA source sticky en bas d'écran mobile (toujours visible)
      - Criminalité reformulée (« 15 % sous la moyenne canadienne ✅ »)
      - Backend : table price_log (historique de prix aux changements), colonne
        digest (description structurée, pipeline en cours), « en ligne depuis X
        jours », plomberie schema/db/web
      
      Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
      simon-pierre boucher · 1 file +21 −5
    2. Stats de quartier (à la Centris) — runtime + fiche « Le quartier »
      …
      - louka/quartier.py : jointure lat/lng -> aire de diffusion 2021 (point-dans-
        polygone local, préfiltre bbox), assemblage fiche (démographie recensement,
        scores de proximité StatCan, défavorisation INSPQ, îlot de chaleur,
        criminalité SPVM <500 m sur l'île / indice IGC ailleurs), enrichissement
        listings.dauid dans la boucle watch
      - data/quartier.db : base statique construite par scripts/ (fusion via
        scripts/merge_quartier.py), défensif si absente
      - fiche : section « Le quartier » (tuiles démographiques, barres de proximité
        0-100, badges chaleur/criminalité, attributions licences ouvertes)
      
      Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
      simon-pierre boucher · 1 file +1
    3. Commodités de proximité par immeuble (Overpass/OSM) sur chaque fiche
      …
      - louka/poi.py : une requête Overpass par immeuble (13 catégories : épicerie,
        dépanneur, pharmacie, école, garderie, parc, bus, métro, gym, café,
        clinique, hôpital, bibliothèque), plus proche par catégorie avec distance,
        cache permanent par coordonnées arrondies (~11 m), politesse 1 req/s,
        rafraîchissement aux 3 mois
      - run.py poi [n] + intégration à la boucle watch (80 nouveaux immeubles/cycle)
      - /api/listings/{uid} : champ poi[] joint depuis le cache
      - fiche : section « À proximité » (icône, nom réel, distance formatée),
        mention données OpenStreetMap
      
      Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
      simon-pierre boucher · 1 file +8
    4. Mission 1a — couche commune : schéma enrichi, normalisation centrale, robustesse, tests
      …
      - schema.Listing : availability_date (ISO/now), area_sqft, pets, furnished,
        details (JSON structuré) + finalize() appliqué à toutes les sources
      - normalize.py : dates FR/EN → ISO, superficies pi²/m², prix robuste,
        types d'unité (chambres→½, 6½+, MDV…), commodités → tags canoniques,
        stationnement/animaux/fumeur/contact extraits du texte
      - db.py : migration auto, délai de grâce (2 syncs) avant désactivation,
        détection de dérive (volume + taux de prix null) avec retraits suspendus,
        cache des pages détail, cache de géocodage (préparé pour la carte)
      - fixtures.py + run.py record : enregistrement/rejeu des réponses HTTP,
        tests de régression pytest par connecteur (snapshot + validité schéma)
      - web.py : nouveaux filtres pets/furnished/available_by/area_min
      
      Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
      simon-pierre boucher · 1 file +202 −39
  9. Thu, Aug 6, 2026 1

    1. Lou-Ka — agrégateur de logements à louer (Québec, Lévis, Grand Montréal)
      …
      Plateforme complète : 68 connecteurs de gestionnaires immobiliers, schéma
      standardisé avec détection de changements (hash), API FastAPI, frontend
      React/Vite PWA (design éditorial sharp, thème clair), déployée sur
      www.lou-ka.com avec resynchronisation horaire.
      
      Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
      Simon-Pierre Boucher · 1 file +148
Menu

Theme