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%
5.2 KB · 123 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 : entry point — `sync`, `watch`, `serve`6# -----------------------------------------------------------------------------7"""Usage:8    python run.py sync [source ...]     # sync the listings9    python run.py watch [minutes]       # sync loop (default 60 min)10    python run.py serve [port]          # start the API + frontend (default 8125)11    python run.py list                  # list the registered connectors12    python run.py record <source ...>   # record a source's test fixtures13    python run.py geocode [n]           # geocode listings missing coordinates14    python run.py quality [n]           # recompute completeness + publication15    python run.py imgaudit [n] [source] # image quality audit16    python run.py fairvalue             # recompute fair rental values17    python run.py poi [n]               # nearby amenities per building18    python run.py env [n]               # OSM environment tiles (KA Scores base)19    python run.py kascores [all]        # compute KA Scores (all = full recompute)20    python run.py buildings             # building passports (building_key + stats)21    python run.py managers [budget_s]   # managers: Google Maps profile + reviews22"""23from __future__ import annotations2425import os26import sys27from pathlib import Path2829# Load .env (SCRAPFLY_KEY, APIFY_TOKEN, etc.) with no external dependency30_env = Path(__file__).parent / ".env"31if _env.exists():32    for line in _env.read_text().splitlines():33        line = line.strip()34        if line and not line.startswith("#") and "=" in line:35            k, _, v = line.partition("=")36            os.environ.setdefault(k.strip(), v.strip())373839def main() -> None:40    cmd = sys.argv[1] if len(sys.argv) > 1 else "serve"41    if cmd == "sync":42        from rentka import ingest43        ingest.run(sys.argv[2:] or None)44    elif cmd == "watch":45        from rentka import ingest46        minutes = int(sys.argv[2]) if len(sys.argv) > 2 else 6047        ingest.watch(minutes * 60)48    elif cmd == "list":49        from rentka.connectors import CONNECTORS50        for sid in sorted(CONNECTORS):51            print(sid)52        print(f"-- {len(CONNECTORS)} connectors")53    elif cmd == "geocode1":54        from rentka import geocode55        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None56        geocode.run(limit)57    elif cmd == "geocode":58        from rentka import geocode59        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None60        geocode.run_batch(limit)61    elif cmd == "quality":62        # recompute the completeness score + publication/quarantine63        from rentka import quality64        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None65        quality.backfill(limit)66    elif cmd == "imgaudit":67        # image quality audit (dead links, tiny images, placeholders,68        # duplicates) — incremental: python run.py imgaudit [n] [source]69        from rentka import imgcheck70        limit = int(sys.argv[2]) if len(sys.argv) > 2 else 150071        src = sys.argv[3] if len(sys.argv) > 3 else None72        imgcheck.run(limit, source=src)73    elif cmd == "fairvalue":74        # recompute the fair rental value of every published listing75        from rentka import fairvalue76        fairvalue.compute_all()77    elif cmd == "poi":78        from rentka import poi79        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None80        poi.run(limit)81    elif cmd == "env":82        # OSM environment data per tile (roads, rails, cycling, bars, full83        # POI) — ~3-month cache, the KA Scores substrate84        from rentka import environment85        limit = int(sys.argv[2]) if len(sys.argv) > 2 else None86        print(environment.run(limit))87    elif cmd == "kascores":88        # recompute the KA Scores (Walk/Transit/Bike/Quiet/Services + global)89        # incremental; «python run.py kascores all» forces the whole stock90        from rentka import kascores91        force = len(sys.argv) > 2 and sys.argv[2] == "all"92        print(kascores.run(recompute_all=force))93    elif cmd == "buildings":94        # recompute every building passport (building_key + stats)95        from rentka import building96        print(building.rollup())97    elif cmd == "managers":98        # managers: seeding + Google Maps resolution + reviews (SerpApi)99        # «python run.py managers [budget_s]»100        from rentka import managers101        b = float(sys.argv[2]) if len(sys.argv) > 2 else 300102        print(managers.precompute(budget_s=b))103    elif cmd == "record":104        from rentka import fixtures105        from rentka.connectors import CONNECTORS106        targets = sys.argv[2:] or sorted(CONNECTORS)107        for sid in targets:108            try:109                print(f"[rent-ka] record {sid} ... {fixtures.record(sid)}")110            except Exception as exc:111                print(f"[rent-ka] record {sid} FAILED: {exc}", file=sys.stderr)112    elif cmd == "serve":113        import uvicorn114        port = int(sys.argv[2]) if len(sys.argv) > 2 else 8125115        uvicorn.run("rentka.web:app", host="0.0.0.0", port=port)116    else:117        print(__doc__)118        sys.exit(1)119120121if __name__ == "__main__":122    main()123