SPB Git forge

spb/lou-ka

Public

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

232commits 1branches 0releases
172.9 MBsize
maindefault branch
2 days agolast push
HTML 98.9% Python 0.6%
4.5 KB · 107 lines python
Raw Blame History
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 serve [port]          # démarre l'API + le frontend (défaut 8080)11    python run.py record <source ...>   # enregistre les fixtures de test d'une source12    python run.py geocode [n]           # géocode les annonces sans coordonnées (max n requêtes)13    python run.py poi [n]               # commodités de proximité par immeuble (max n requêtes)14    python run.py env [n]               # environnement OSM par tuile (socle des KA Scores)15    python run.py kascores [all]        # calcule les KA Scores du parc (all = tout recalculer)16"""17from __future__ import annotations1819import os20import sys21from pathlib import Path2223# Charger .env (FIRECRAWL_API_KEY, etc.) sans dépendance externe24_env = Path(__file__).parent / ".env"25if _env.exists():26    for line in _env.read_text().splitlines():27        line = line.strip()28        if line and not line.startswith("#") and "=" in line:29            k, _, v = line.partition("=")30            os.environ.setdefault(k.strip(), v.strip())313233def main() -> None:34    cmd = sys.argv[1] if len(sys.argv) > 1 else "serve"35    if cmd == "sync":36        from louka import ingest37        ingest.run(sys.argv[2:] or None)38    elif cmd == "watch":39        from louka import ingest40        minutes = int(sys.argv[2]) if len(sys.argv) > 2 else 6041        ingest.watch(minutes * 60)42    elif cmd == "geocode1":43        from louka import geocode44        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None45        geocode.run(limit)46    elif cmd == "geocode":47        # lot Adresses Québec (rapide) ; « geocode1 » = ancien mode 1-par-148        from louka import geocode49        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None50        geocode.run_batch(limit)51    elif cmd == "quality":52        # (re)calcule le score de complétude + publication/quarantaine53        from louka import quality54        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None55        quality.backfill(limit)56    elif cmd == "imgaudit":57        # contrôle qualité des images (liens morts, minuscules, placeholders,58        # doublons) — incrémental : python run.py imgaudit [n_annonces] [source]59        from louka import imgcheck60        limit = int(sys.argv[2]) if len(sys.argv) > 2 else 150061        src = sys.argv[3] if len(sys.argv) > 3 else None62        imgcheck.run(limit, source=src)63    elif cmd == "fairvalue":64        # (re)calcule la juste valeur locative de toutes les annonces publiées65        from louka import fairvalue66        fairvalue.compute_all()67    elif cmd == "poi":68        from louka import poi69        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None70        poi.run(limit)71    elif cmd == "quartier":72        from louka import quartier73        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None74        quartier.enrich(limit)75    elif cmd == "env":76        # données d'environnement OSM par tuile (routes, rails, cyclable,77        # bars, POI complets) — cache ~3 mois, socle des KA Scores78        from louka import environment79        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None80        print(environment.run(limit))81    elif cmd == "kascores":82        # (re)calcule les KA Scores (Walk/Transit/Bike/Calme/Services + global)83        # incrémental ; « python run.py kascores all » force tout le parc84        from louka import kascores85        force = len(sys.argv) > 2 and sys.argv[2] == "all"86        print(kascores.run(recompute_all=force))87    elif cmd == "record":88        from louka import fixtures89        from louka.connectors import CONNECTORS90        targets = sys.argv[2:] or sorted(CONNECTORS)91        for sid in targets:92            try:93                print(f"[lou-ka] record {sid} ... {fixtures.record(sid)}")94            except Exception as exc:95                print(f"[lou-ka] record {sid} ÉCHEC : {exc}", file=sys.stderr)96    elif cmd == "serve":97        import uvicorn98        port = int(sys.argv[2]) if len(sys.argv) > 2 else 808099        uvicorn.run("louka.web:app", host="0.0.0.0", port=port)100    else:101        print(__doc__)102        sys.exit(1)103104105if __name__ == "__main__":106    main()107