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 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"""15from __future__ import annotations1617import os18import sys19from pathlib import Path2021# Charger .env (FIRECRAWL_API_KEY, etc.) sans dépendance externe22_env = Path(__file__).parent / ".env"23if _env.exists():24 for line in _env.read_text().splitlines():25 line = line.strip()26 if line and not line.startswith("#") and "=" in line:27 k, _, v = line.partition("=")28 os.environ.setdefault(k.strip(), v.strip())293031def main() -> None:32 cmd = sys.argv[1] if len(sys.argv) > 1 else "serve"33 if cmd == "sync":34 from louka import ingest35 ingest.run(sys.argv[2:] or None)36 elif cmd == "watch":37 from louka import ingest38 minutes = int(sys.argv[2]) if len(sys.argv) > 2 else 6039 ingest.watch(minutes * 60)40 elif cmd == "geocode1":41 from louka import geocode42 limit = int(sys.argv[2]) if len(sys.argv) > 2 else None43 geocode.run(limit)44 elif cmd == "geocode":45 # lot Adresses Québec (rapide) ; « geocode1 » = ancien mode 1-par-146 from louka import geocode47 limit = int(sys.argv[2]) if len(sys.argv) > 2 else None48 geocode.run_batch(limit)49 elif cmd == "quality":50 # (re)calcule le score de complétude + publication/quarantaine51 from louka import quality52 limit = int(sys.argv[2]) if len(sys.argv) > 2 else None53 quality.backfill(limit)54 elif cmd == "imgaudit":55 # contrôle qualité des images (liens morts, minuscules, placeholders,56 # doublons) — incrémental : python run.py imgaudit [n_annonces] [source]57 from louka import imgcheck58 limit = int(sys.argv[2]) if len(sys.argv) > 2 else 150059 src = sys.argv[3] if len(sys.argv) > 3 else None60 imgcheck.run(limit, source=src)61 elif cmd == "fairvalue":62 # (re)calcule la juste valeur locative de toutes les annonces publiées63 from louka import fairvalue64 fairvalue.compute_all()65 elif cmd == "poi":66 from louka import poi67 limit = int(sys.argv[2]) if len(sys.argv) > 2 else None68 poi.run(limit)69 elif cmd == "quartier":70 from louka import quartier71 limit = int(sys.argv[2]) if len(sys.argv) > 2 else None72 quartier.enrich(limit)73 elif cmd == "record":74 from louka import fixtures75 from louka.connectors import CONNECTORS76 targets = sys.argv[2:] or sorted(CONNECTORS)77 for sid in targets:78 try:79 print(f"[lou-ka] record {sid} ... {fixtures.record(sid)}")80 except Exception as exc:81 print(f"[lou-ka] record {sid} ÉCHEC : {exc}", file=sys.stderr)82 elif cmd == "serve":83 import uvicorn84 port = int(sys.argv[2]) if len(sys.argv) > 2 else 808085 uvicorn.run("louka.web:app", host="0.0.0.0", port=port)86 else:87 print(__doc__)88 sys.exit(1)899091if __name__ == "__main__":92 main()93