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 == "inondation-build":97 from louka import inondation98 inondation.build()99 elif cmd == "air-refresh":100 from louka import air101 air.refresh()102 elif cmd == "gaz-refresh":103 from louka import gaz104 print(f"[gaz] {gaz.refresh()} stations")105 elif cmd == "hydro-precompute":106 from louka import hydro107 n = int(sys.argv[2]) if len(sys.argv) > 2 else 100108 b = int(sys.argv[3]) if len(sys.argv) > 3 else 40109 hydro.precompute(limit=n, budget=b)110 elif cmd == "rdl":111 from louka import rdl112 rdl.refresh()113 elif cmd == "record":114 from louka import fixtures115 from louka.connectors import CONNECTORS116 targets = sys.argv[2:] or sorted(CONNECTORS)117 for sid in targets:118 try:119 print(f"[lou-ka] record {sid} ... {fixtures.record(sid)}")120 except Exception as exc:121 print(f"[lou-ka] record {sid} ÉCHEC : {exc}", file=sys.stderr)122 elif cmd == "serve":123 import uvicorn124 port = int(sys.argv[2]) if len(sys.argv) > 2 else 8080125 uvicorn.run("louka.web:app", host="0.0.0.0", port=port)126 else:127 print(__doc__)128 sys.exit(1)129130131if __name__ == "__main__":132 main()133