Lou·Ka — tous les logements à louer du Québec, un seul endroit.
- 264commits
- 1branches
- 0releases
- 180.7 MBon disk
- maindefault branch
- 5 days agolast push
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"""19from __future__ import annotations2021import os22import sys23from pathlib import Path2425# Charger .env (FIRECRAWL_API_KEY, etc.) sans dépendance externe26_env = Path(__file__).parent / ".env"27if _env.exists():28 for line in _env.read_text().splitlines():29 line = line.strip()30 if line and not line.startswith("#") and "=" in line:31 k, _, v = line.partition("=")32 os.environ.setdefault(k.strip(), v.strip())333435def main() -> None:36 cmd = sys.argv[1] if len(sys.argv) > 1 else "serve"37 if cmd == "sync":38 from louka import ingest39 ingest.run(sys.argv[2:] or None)40 elif cmd == "watch":41 from louka import ingest42 minutes = int(sys.argv[2]) if len(sys.argv) > 2 else 6043 ingest.watch(minutes * 60)44 elif cmd == "sync-ct":45 from louka.shortterm import ingest as ct_ingest46 ct_ingest.run(sys.argv[2:] or None)47 elif cmd == "watch-ct":48 from louka.shortterm import ingest as ct_ingest49 minutes = int(sys.argv[2]) if len(sys.argv) > 2 else 36050 ct_ingest.watch(minutes * 60)51 elif cmd == "geocode1":52 from louka import geocode53 limit = int(sys.argv[2]) if len(sys.argv) > 2 else None54 geocode.run(limit)55 elif cmd == "geocode":56 # lot Adresses Québec (rapide) ; « geocode1 » = ancien mode 1-par-157 from louka import geocode58 limit = int(sys.argv[2]) if len(sys.argv) > 2 else None59 geocode.run_batch(limit)60 elif cmd == "quality":61 # (re)calcule le score de complétude + publication/quarantaine62 from louka import quality63 limit = int(sys.argv[2]) if len(sys.argv) > 2 else None64 quality.backfill(limit)65 elif cmd == "imgaudit":66 # contrôle qualité des images (liens morts, minuscules, placeholders,67 # doublons) — incrémental : python run.py imgaudit [n_annonces] [source]68 from louka import imgcheck69 limit = int(sys.argv[2]) if len(sys.argv) > 2 else 150070 src = sys.argv[3] if len(sys.argv) > 3 else None71 imgcheck.run(limit, source=src)72 elif cmd == "fairvalue":73 # (re)calcule la juste valeur locative de toutes les annonces publiées74 from louka import fairvalue75 fairvalue.compute_all()76 elif cmd == "poi":77 from louka import poi78 limit = int(sys.argv[2]) if len(sys.argv) > 2 else None79 poi.run(limit)80 elif cmd == "quartier":81 from louka import quartier82 limit = int(sys.argv[2]) if len(sys.argv) > 2 else None83 quartier.enrich(limit)84 elif cmd == "env":85 # données d'environnement OSM par tuile (routes, rails, cyclable,86 # bars, POI complets) — cache ~3 mois, socle des KA Scores87 from louka import environment88 limit = int(sys.argv[2]) if len(sys.argv) > 2 else None89 print(environment.run(limit))90 elif cmd == "kascores":91 # (re)calcule les KA Scores (Walk/Transit/Bike/Calme/Services + global)92 # incrémental ; « python run.py kascores all » force tout le parc93 from louka import kascores94 force = len(sys.argv) > 2 and sys.argv[2] == "all"95 print(kascores.run(recompute_all=force))96 elif cmd == "record":97 from louka import fixtures98 from louka.connectors import CONNECTORS99 targets = sys.argv[2:] or sorted(CONNECTORS)100 for sid in targets:101 try:102 print(f"[lou-ka] record {sid} ... {fixtures.record(sid)}")103 except Exception as exc:104 print(f"[lou-ka] record {sid} ÉCHEC : {exc}", file=sys.stderr)105 elif cmd == "serve":106 import uvicorn107 port = int(sys.argv[2]) if len(sys.argv) > 2 else 8080108 uvicorn.run("louka.web:app", host="0.0.0.0", port=port)109 else:110 print(__doc__)111 sys.exit(1)112113114if __name__ == "__main__":115 main()116