From 9f305d15b660d9424e72bc44d3e4a1c9fbf4a074 Mon Sep 17 00:00:00 2001 From: Chaim Date: Sat, 25 Apr 2026 15:04:32 +0000 Subject: [PATCH] fix(ui): use Govmap resolved address text for street inference, not frequency heuristic MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Previous approach: pick most common streetNameHeb among street-source deals. Problem: a busier nearby street wins even when the searched parcel is on a quieter street (e.g. רב צעיר beats הילדסהיימר despite being a different street). Fix: backend now returns resolved_address from Govmap autocomplete (e.g. "הילדסהיימר 14, ירושלים"). Frontend parses the leading non-numeric tokens as the street name and uses that as ground truth for proximity labeling. Co-Authored-By: Claude Opus 4.7 (1M context) --- nadlan_mcp/web_app.py | 5 ++++- web/src/App.tsx | 5 ++++- web/src/api/types.ts | 1 + web/src/components/DealsTable.tsx | 24 +++++++++++++----------- 4 files changed, 22 insertions(+), 13 deletions(-) diff --git a/nadlan_mcp/web_app.py b/nadlan_mcp/web_app.py index 1731d94..7d91382 100644 --- a/nadlan_mcp/web_app.py +++ b/nadlan_mcp/web_app.py @@ -165,13 +165,15 @@ def create_app() -> FastAPI: logger.exception("find_recent_deals_for_address failed") raise HTTPException(status_code=502, detail=str(e)) - # Search-center coords for the map (best-effort). + # Search-center coords + resolved address text for the UI (best-effort). search_coords = None + resolved_address = None try: ar = govmap_client.autocomplete_address(req.address) if ar.results and ar.results[0].coordinates: c = ar.results[0].coordinates search_coords = {"longitude": c.longitude, "latitude": c.latitude} + resolved_address = ar.results[0].text except Exception: pass @@ -198,6 +200,7 @@ def create_app() -> FastAPI: return { "search": { "address": req.address, + "resolved_address": resolved_address, "coordinates": search_coords, "years_back": req.years_back, "radius_meters": req.radius_meters, diff --git a/web/src/App.tsx b/web/src/App.tsx index e28dae3..dfda205 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -204,7 +204,10 @@ export default function App() { )} {deals.data && !deals.isFetching && ( - + )} {!deals.isFetching && !deals.error && !deals.data && (
diff --git a/web/src/api/types.ts b/web/src/api/types.ts index aca5923..fb653d3 100644 --- a/web/src/api/types.ts +++ b/web/src/api/types.ts @@ -50,6 +50,7 @@ export interface Deal { export interface DealsSearchResponse { search: { address: string; + resolved_address: string | null; coordinates: Coordinates | null; years_back: number; radius_meters: number; diff --git a/web/src/components/DealsTable.tsx b/web/src/components/DealsTable.tsx index 41d2a0c..b2f42de 100644 --- a/web/src/components/DealsTable.tsx +++ b/web/src/components/DealsTable.tsx @@ -3,18 +3,20 @@ import { formatCurrencyILS, formatDate, formatNumber } from "../lib/utils"; interface Props { deals: Deal[]; + resolvedAddress?: string; } -function inferStreetName(deals: Deal[]): string | null { - const freq: Record = {}; - for (const d of deals) { - if (d.deal_source === "street" && d.streetNameHeb) { - freq[d.streetNameHeb] = (freq[d.streetNameHeb] ?? 0) + 1; - } +// Extract street name from a Govmap address string like "הילדסהיימר 14, ירושלים". +// Takes all leading tokens that contain no digits. +function streetFromAddress(address: string | undefined): string | null { + if (!address) return null; + const tokens = address.split(/[\s,]+/); + const parts: string[] = []; + for (const t of tokens) { + if (/\d/.test(t)) break; + if (t) parts.push(t); } - const entries = Object.entries(freq); - if (!entries.length) return null; - return entries.reduce((a, b) => (b[1] > a[1] ? b : a))[0]; + return parts.length ? parts.join(" ") : null; } function proximityLabel(deal: Deal, inferredStreet: string | null): string { @@ -34,7 +36,7 @@ function proximityClass(deal: Deal, inferredStreet: string | null): string { return "bg-slate-100 text-slate-700"; } -export function DealsTable({ deals }: Props) { +export function DealsTable({ deals, resolvedAddress }: Props) { if (!deals.length) { return (
@@ -43,7 +45,7 @@ export function DealsTable({ deals }: Props) { ); } - const inferredStreet = inferStreetName(deals); + const inferredStreet = streetFromAddress(resolvedAddress); return (