SPB Git

spb/lou-ka

Public

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

6.5 KB · 151 lines python
1#!/usr/bin/env python32# -----------------------------------------------------------------------------3# Lou-Ka — Agrégateur de logements à louer (province de Québec)4# Auteur : Simon-Pierre Boucher — contact@spboucher.ai5# run.py : point d'entrée — `sync`, `watch`, `serve`6# -----------------------------------------------------------------------------7"""Utilisation :8    python run.py sync [source ...]     # synchronise les annonces9    python run.py watch [minutes]       # synchronise en boucle (défaut 60 min)10    python run.py sync-ct [source ...]  # synchronise la section court terme11    python run.py watch-ct [minutes]    # boucle court terme (défaut 360 min)12    python run.py serve [port]          # démarre l'API + le frontend (défaut 8080)13    python run.py record <source ...>   # enregistre les fixtures de test d'une source14    python run.py geocode [n]           # géocode les annonces sans coordonnées (max n requêtes)15    python run.py poi [n]               # commodités de proximité par immeuble (max n requêtes)16    python run.py env [n]               # environnement OSM par tuile (socle des KA Scores)17    python run.py kascores [all]        # calcule les KA Scores du parc (all = tout recalculer)18    python run.py buildings             # passeport des immeubles (building_key + stats)19    python run.py managers [budget_s]   # gestionnaires : fiche Google Maps + avis (SerpApi)20"""21from __future__ import annotations2223import os24import sys25from pathlib import Path2627# Charger .env (FIRECRAWL_API_KEY, etc.) sans dépendance externe28_env = Path(__file__).parent / ".env"29if _env.exists():30    for line in _env.read_text().splitlines():31        line = line.strip()32        if line and not line.startswith("#") and "=" in line:33            k, _, v = line.partition("=")34            os.environ.setdefault(k.strip(), v.strip())353637def main() -> None:38    cmd = sys.argv[1] if len(sys.argv) > 1 else "serve"39    if cmd == "sync":40        from louka import ingest41        ingest.run(sys.argv[2:] or None)42    elif cmd == "watch":43        from louka import ingest44        minutes = int(sys.argv[2]) if len(sys.argv) > 2 else 6045        ingest.watch(minutes * 60)46    elif cmd == "sync-ct":47        from louka.shortterm import ingest as ct_ingest48        ct_ingest.run(sys.argv[2:] or None)49    elif cmd == "watch-ct":50        from louka.shortterm import ingest as ct_ingest51        minutes = int(sys.argv[2]) if len(sys.argv) > 2 else 36052        ct_ingest.watch(minutes * 60)53    elif cmd == "geocode1":54        from louka import geocode55        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None56        geocode.run(limit)57    elif cmd == "geocode":58        # lot Adresses Québec (rapide) ; « geocode1 » = ancien mode 1-par-159        from louka import geocode60        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None61        geocode.run_batch(limit)62    elif cmd == "quality":63        # (re)calcule le score de complétude + publication/quarantaine64        from louka import quality65        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None66        quality.backfill(limit)67    elif cmd == "imgaudit":68        # contrôle qualité des images (liens morts, minuscules, placeholders,69        # doublons) — incrémental : python run.py imgaudit [n_annonces] [source]70        from louka import imgcheck71        limit = int(sys.argv[2]) if len(sys.argv) > 2 else 150072        src = sys.argv[3] if len(sys.argv) > 3 else None73        imgcheck.run(limit, source=src)74    elif cmd == "fairvalue":75        # (re)calcule la juste valeur locative de toutes les annonces publiées76        from louka import fairvalue77        fairvalue.compute_all()78    elif cmd == "poi":79        from louka import poi80        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None81        poi.run(limit)82    elif cmd == "quartier":83        from louka import quartier84        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None85        quartier.enrich(limit)86    elif cmd == "env":87        # données d'environnement OSM par tuile (routes, rails, cyclable,88        # bars, POI complets) — cache ~3 mois, socle des KA Scores89        from louka import environment90        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None91        print(environment.run(limit))92    elif cmd == "kascores":93        # (re)calcule les KA Scores (Walk/Transit/Bike/Calme/Services + global)94        # incrémental ; « python run.py kascores all » force tout le parc95        from louka import kascores96        force = len(sys.argv) > 2 and sys.argv[2] == "all"97        print(kascores.run(recompute_all=force))98    elif cmd == "inondation-build":99        from louka import inondation100        inondation.build()101    elif cmd == "air-refresh":102        from louka import air103        air.refresh()104    elif cmd == "gaz-refresh":105        from louka import gaz106        print(f"[gaz] {gaz.refresh()} stations")107    elif cmd == "hydro-precompute":108        from louka import hydro109        n = int(sys.argv[2]) if len(sys.argv) > 2 else 100110        b = int(sys.argv[3]) if len(sys.argv) > 3 else 40111        hydro.precompute(limit=n, budget=b)112    elif cmd == "rdl":113        from louka import rdl114        rdl.refresh()115    elif cmd == "tal":116        # historique TAL (décisions SOQUIJ) par adresse, par lots117        from louka import tal118        n = int(sys.argv[2]) if len(sys.argv) > 2 else 60119        b = int(sys.argv[3]) if len(sys.argv) > 3 else 600120        tal.precompute(limit=n, budget=b)121    elif cmd == "buildings":122        # (re)calcule le passeport de tous les immeubles (building_key + stats)123        from louka import building124        print(building.rollup())125    elif cmd == "managers":126        # gestionnaires : amorçage + résolution Google Maps + avis (SerpApi)127        # « python run.py managers [budget_s] »128        from louka import managers129        b = float(sys.argv[2]) if len(sys.argv) > 2 else 300130        print(managers.precompute(budget_s=b))131    elif cmd == "record":132        from louka import fixtures133        from louka.connectors import CONNECTORS134        targets = sys.argv[2:] or sorted(CONNECTORS)135        for sid in targets:136            try:137                print(f"[lou-ka] record {sid} ... {fixtures.record(sid)}")138            except Exception as exc:139                print(f"[lou-ka] record {sid} ÉCHEC : {exc}", file=sys.stderr)140    elif cmd == "serve":141        import uvicorn142        port = int(sys.argv[2]) if len(sys.argv) > 2 else 8080143        uvicorn.run("louka.web:app", host="0.0.0.0", port=port)144    else:145        print(__doc__)146        sys.exit(1)147148149if __name__ == "__main__":150    main()151
Files · 7e29e36e77f56b030d27452ba8c7ee8cc0061d8b
Menu

Theme