SPB Git forge

spb/rent-ka

Public
8commits 1branches 0releases
7.4 MBsize
maindefault branch
19 days agolast push
Python 68.8% TypeScript 18.6% CSS 8.7% JavaScript 3.3% HTML 0.6%
4.6 KB · 107 lines python
Raw Blame History
1#!/usr/bin/env python32# -----------------------------------------------------------------------------3# Rent-Ka — Rental listings aggregator (Canada, outside Québec)4# Author: 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 rentka import ingest37        ingest.run(sys.argv[2:] or None)38    elif cmd == "watch":39        from rentka import ingest40        minutes = int(sys.argv[2]) if len(sys.argv) > 2 else 6041        ingest.watch(minutes * 60)42    elif cmd == "geocode1":43        from rentka 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 rentka 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 rentka 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 rentka 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 rentka import fairvalue66        fairvalue.compute_all()67    elif cmd == "poi":68        from rentka import poi69        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None70        poi.run(limit)71    elif cmd == "quartier":72        from rentka 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 rentka 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 rentka 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 rentka import fixtures89        from rentka.connectors import CONNECTORS90        targets = sys.argv[2:] or sorted(CONNECTORS)91        for sid in targets:92            try:93                print(f"[rent-ka] record {sid} ... {fixtures.record(sid)}")94            except Exception as exc:95                print(f"[rent-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("rentka.web:app", host="0.0.0.0", port=port)100    else:101        print(__doc__)102        sys.exit(1)103104105if __name__ == "__main__":106    main()107