SPB Git

spb/home-ka

Public

quality: plausible price is a hard publication requirement; jsonld: parse address from Product.name (Real Geeks, @properties platforms); probe www-normalization fix

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Simon-Pierre Boucher committed (Aug 28, 2026) parent 83a3f0d Browse files

3 changed files +25 −3

modified homeka/connectors/json/jsonld_site.py +18 −1
@@ -43,6 +43,11 @@ _LISTING_TYPES = {
43 # URL slug fallback: ".../7039-s-straight-avenue-homosassa-fl-34446/" 43 # URL slug fallback: ".../7039-s-straight-avenue-homosassa-fl-34446/"
44 _SLUG_ADDR_RE = re.compile(r"/([\w-]+)-([a-z]{2})-(\d{5})/?$", re.I) 44 _SLUG_ADDR_RE = re.compile(r"/([\w-]+)-([a-z]{2})-(\d{5})/?$", re.I)
45 45
46+# `name` fallback: several platforms (Real Geeks, @properties/Christie's)
47+# put the full address in Product.name — "123 Main St #4, Austin, TX 78701"
48+_NAME_ADDR_RE = re.compile(
49+ r"^(.+?),\s*([^,]+?),\s*([A-Za-z]{2})[,\s]+(\d{5})(?:-\d{4})?\s*$")
50+
46 51
47 # Next.js "flight" streaming: some SSR sites (Douglas Elliman) embed the 52 # Next.js "flight" streaming: some SSR sites (Douglas Elliman) embed the
48 # JSON-LD as self.__next_s.push([0,{"type":"application/ld+json", 53 # JSON-LD as self.__next_s.push([0,{"type":"application/ld+json",
@@ -116,9 +121,16 @@ class JSONLDSiteConnector(BaseConnector):
116 if cfg.get("sitemap_exclude") else None 121 if cfg.get("sitemap_exclude") else None
117 depth = 0 122 depth = 0
118 max_pages = int(cfg.get("max_pages", 400)) 123 max_pages = int(cfg.get("max_pages", 400))
124+ max_children = int(cfg.get("sitemap_max_children", 80))
125+ _PRIORITY = re.compile(r"active|listing|residential|propert|homes",
126+ re.I)
119 while sitemaps and depth < 3 and len(urls) < max_pages: 127 while sitemaps and depth < 3 and len(urls) < max_pages:
128+ # likely-listing shards first — big indexes (@properties: 69
129+ # children) would otherwise exhaust the budget on agent/office maps
130+ sitemaps.sort(key=lambda u: 0 if _PRIORITY.search(
131+ u.rsplit("/", 1)[-1]) else 1)
120 nxt: list[str] = [] 132 nxt: list[str] = []
121− for sm in sitemaps[:30]: 133+ for sm in sitemaps[:max_children]:
122 if len(urls) >= max_pages: 134 if len(urls) >= max_pages:
123 break 135 break
124 if sm_exclude and sm_exclude.search(sm): 136 if sm_exclude and sm_exclude.search(sm):
@@ -308,6 +320,11 @@ class JSONLDSiteConnector(BaseConnector):
308 city = str(addr.get("addressLocality") or "") 320 city = str(addr.get("addressLocality") or "")
309 state = str(addr.get("addressRegion") or "") 321 state = str(addr.get("addressRegion") or "")
310 zip_code = str(addr.get("postalCode") or "") 322 zip_code = str(addr.get("postalCode") or "")
323+ if not street: # name fallback: "123 Main St, Austin, TX 78701"
324+ m = _NAME_ADDR_RE.match(str(merged.get("name") or "").strip())
325+ if m:
326+ street, city = m.group(1), city or m.group(2)
327+ state, zip_code = state or m.group(3).upper(), zip_code or m.group(4)
311 if not street: # slug fallback: .../123-main-street-austin-tx-78701/ 328 if not street: # slug fallback: .../123-main-street-austin-tx-78701/
312 m = _SLUG_ADDR_RE.search(url) 329 m = _SLUG_ADDR_RE.search(url)
313 if m: 330 if m:
modified homeka/quality.py +5 −1
@@ -50,7 +50,11 @@ def refresh(con: sqlite3.Connection) -> dict:
50 " property_type, description, features, images" 50 " property_type, description, features, images"
51 " FROM listings WHERE active=1"): 51 " FROM listings WHERE active=1"):
52 q = score_row(r) 52 q = score_row(r)
53− pub = 1 if q >= PUBLISH_THRESHOLD else 0 53+ # a plausible asking price is a HARD requirement for publication —
54+ # crawled sold/off-market shells must never reach the grid
55+ price_ok = (r["list_price"] is not None
56+ and PRICE_MIN <= r["list_price"] <= PRICE_MAX)
57+ pub = 1 if (q >= PUBLISH_THRESHOLD and price_ok) else 0
54 published += pub 58 published += pub
55 quarantined += 1 - pub 59 quarantined += 1 - pub
56 con.execute("UPDATE listings SET quality=?, published=? WHERE uid=?", 60 con.execute("UPDATE listings SET quality=?, published=? WHERE uid=?",
modified scripts/probe_sources.py +2 −1
@@ -95,7 +95,8 @@ def main() -> None:
95 existing_sites = set() 95 existing_sites = set()
96 for src in db.get_sources(con): 96 for src in db.get_sources(con):
97 u = (src["config"].get("base_url") or "").replace("https://", "") 97 u = (src["config"].get("base_url") or "").replace("https://", "")
98− existing_sites.add(u.replace("http://", "").rstrip("/").lower()) 98+ u = u.replace("http://", "").rstrip("/").lower()
99+ existing_sites.add(u.removeprefix("www."))
99 rows = con.execute( 100 rows = con.execute(
100 "SELECT id, name, website, states FROM brokerages" 101 "SELECT id, name, website, states FROM brokerages"
101 " WHERE website<>'' AND (inspect_error IS NULL OR inspect_error='')" 102 " WHERE website<>'' AND (inspect_error IS NULL OR inspect_error='')"
102 103
Menu

Theme