Python 67%
TypeScript 18.2%
CSS 14.4%
1#!/usr/bin/env python32# -----------------------------------------------------------------------------3# House-Ka — Homes-for-sale aggregator (Canada outside 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 propriétés9 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 8090)11 python run.py list # liste les connecteurs enregistrés12 python run.py geocode [n] # géocode les annonces sans coordonnées (max n requêtes)13 python run.py geocode-audit [n] [dry] # audit coordonnées <-> ville (n = budget Nominatim)14 python run.py poi [n] # commodités de proximité par immeuble (max n requêtes)15 python run.py quartier [n] # rattache chaque annonce à son aire de diffusion (DAUID)16 python run.py mortgage-sync [slug…] # collecte les taux hypothécaires (toutes banques ou celles listées)17 python run.py mortgage-watch [min] # collecte en boucle (défaut IMMOKA_MORTGAGE_INTERVAL_MIN=180)18 python run.py mortgage-status # santé des providers de taux (OK/WARNING/ERROR)19"""20from __future__ import annotations2122import os23import sys24from pathlib import Path2526# Charger .env (FIRECRAWL_API_KEY, etc.) sans dépendance externe27_env = Path(__file__).parent / ".env"28if _env.exists():29 for line in _env.read_text().splitlines():30 line = line.strip()31 if line and not line.startswith("#") and "=" in line:32 k, _, v = line.partition("=")33 os.environ.setdefault(k.strip(), v.strip())343536def main() -> None:37 cmd = sys.argv[1] if len(sys.argv) > 1 else "serve"38 if cmd == "sync":39 from immoka import ingest40 ingest.run(sys.argv[2:] or None)41 elif cmd == "watch":42 from immoka import ingest43 minutes = int(sys.argv[2]) if len(sys.argv) > 2 else 6044 ingest.watch(minutes * 60)45 elif cmd == "list":46 from immoka.connectors import CONNECTORS47 for sid in sorted(CONNECTORS):48 print(sid)49 print(f"-- {len(CONNECTORS)} connecteur(s)")50 elif cmd == "geocode":51 from immoka import geocode52 geocode.run_batch(int(sys.argv[2]) if len(sys.argv) > 2 else None)53 elif cmd == "geocode1": # ancien mode 1-par-1 (+ repli Nominatim)54 from immoka import geocode55 geocode.run(int(sys.argv[2]) if len(sys.argv) > 2 else None)56 elif cmd == "geocode-audit": # cohérence coordonnées <-> ville annoncée57 from immoka import geocode58 budget = next((int(a) for a in sys.argv[2:] if a.isdigit()), None)59 geocode.run_audit(nominatim_budget=budget,60 apply="dry" not in sys.argv[2:])61 elif cmd == "poi":62 from immoka import poi63 poi.run(int(sys.argv[2]) if len(sys.argv) > 2 else None)64 elif cmd == "quartier":65 from immoka import quartier66 quartier.enrich(int(sys.argv[2]) if len(sys.argv) > 2 else None)67 elif cmd == "mortgage-sync":68 from immoka.mortgage import scheduler69 scheduler.run(sys.argv[2:] or None)70 elif cmd == "mortgage-watch":71 from immoka.mortgage import scheduler72 minutes = int(sys.argv[2]) if len(sys.argv) > 2 else None73 scheduler.watch(minutes)74 elif cmd == "mortgage-status":75 from immoka.mortgage import store76 con = store.connect()77 for h in store.provider_health(con):78 print(f"{h['level']:8s} {h['provider']:15s} "79 f"statut={h.get('status') or '-'} "80 f"produits_courants={h.get('current_products', 0)} "81 f"dernière_passe=il y a {h.get('age_minutes', '?')} min")82 elif cmd == "serve":83 import uvicorn84 port = int(sys.argv[2]) if len(sys.argv) > 2 else 809885 uvicorn.run("immoka.web:app", host="0.0.0.0", port=port)86 else:87 print(__doc__)88 sys.exit(1)899091if __name__ == "__main__":92 main()93