# ----------------------------------------------------------------------------- # Home-Ka — US real-estate aggregator (Groupe KA) # Author: Simon-Pierre Boucher — contact@spboucher.ai # brokerages.py : brokerage registry — seed loading + scoring refresh. # The seed (data/brokerages_seed.json) is the initial base of 500+ US # brokerages worth pursuing for direct feeds; the discovery pipeline # (discovery.py) then inspects, classifies and scores them. # ----------------------------------------------------------------------------- from __future__ import annotations import json import time from pathlib import Path from . import db from .discovery import priority SEED_PATH = Path(__file__).resolve().parent.parent / "data" / "brokerages_seed.json" def load_seed(path: Path | str | None = None) -> dict: """Upsert the seed file into the brokerages table (idempotent, keyed on website). Never overwrites discovery results or partnership statuses.""" p = Path(path) if path else SEED_PATH data = json.loads(p.read_text(encoding="utf-8")) rows = data["brokerages"] if isinstance(data, dict) else data con = db.connect() now = time.time() added = updated = 0 for b in rows: website = (b.get("website") or "").strip().rstrip("/") if not website or not b.get("name"): continue states = b.get("states") or [] prio = priority(b.get("estimated_listings"), b.get("estimated_agents"), 30, len(states)) # 30 = unknown feed prob. before inspection existing = con.execute("SELECT id FROM brokerages WHERE website=?", (website,)).fetchone() if existing is None: con.execute( """INSERT INTO brokerages (name, website, states, cities, estimated_agents, estimated_listings, mls_affiliations, possible_feed_type, priority_score, partnership_status, created, updated) VALUES (?,?,?,?,?,?,?,?,?,'prospect',?,?)""", (b["name"], website, json.dumps(states, ensure_ascii=False), json.dumps(b.get("cities") or [], ensure_ascii=False), b.get("estimated_agents"), b.get("estimated_listings"), json.dumps(b.get("mls_affiliations") or [], ensure_ascii=False), b.get("possible_feed_type") or "crawl", prio, now, now)) added += 1 else: con.execute( """UPDATE brokerages SET name=?, states=CASE WHEN states='[]' THEN ? ELSE states END, cities=CASE WHEN cities='[]' THEN ? ELSE cities END, estimated_agents=COALESCE(estimated_agents, ?), estimated_listings=COALESCE(estimated_listings, ?), updated=? WHERE id=?""", (b["name"], json.dumps(states, ensure_ascii=False), json.dumps(b.get("cities") or [], ensure_ascii=False), b.get("estimated_agents"), b.get("estimated_listings"), now, existing["id"])) updated += 1 con.commit() n = con.execute("SELECT COUNT(*) c FROM brokerages").fetchone()["c"] con.close() return {"added": added, "updated": updated, "total": n} def rescore(con=None) -> int: """Recompute priority scores from current inspection data.""" own = con is None con = con or db.connect() n = 0 for r in con.execute("SELECT id, estimated_listings, estimated_agents," " feed_probability_score, states FROM brokerages"): states = json.loads(r["states"] or "[]") prio = priority(r["estimated_listings"], r["estimated_agents"], r["feed_probability_score"] or 30, len(states)) con.execute("UPDATE brokerages SET priority_score=? WHERE id=?", (prio, r["id"])) n += 1 con.commit() if own: con.close() return n