fix(site-finder): smart_suggestions filter parcels above documented score threshold (#1503) #1692

Merged
lekss361 merged 1 commit from fix/smart-suggestions-threshold-1503 into main 2026-06-17 18:05:25 +00:00

View file

@ -130,6 +130,10 @@ AUDIENCES = {
# ---------- Scoring ----------
# Эмпирические пороги score для ЕКБ: средний диапазон 15-30, max редко >40.
# Зеркало SCORE_THRESHOLDS из backend/app/api/v1/parcels.py — менять синхронно.
SCORE_THRESHOLDS: dict[str, float] = {"плохо": 5.0, "средне": 15.0, "хорошо": 25.0, "отлично": 40.0}
EDU = [("kindergarten", 300, 1000, 1.0), ("school", 400, 1500, 1.0), ("university", 1000, 5000, 0.3)]
HEALTH = [("pharmacy", 300, 1000, 1.0), ("clinic", 500, 2000, 1.0), ("hospital", 1500, 5000, 0.5)]
TRANSIT_M = [("metro", 1000, 3000, 1.0)]
@ -1053,9 +1057,11 @@ def district_time_machine(district: str):
def smart_suggestions(lat: float, lon: float, audience: str = "balanced",
radius_km: float = 3.0, top_n: int = 5):
"""Top-N ЖК within radius_km that score higher than this point under selected audience.
Useful as 'nearby alternatives'."""
Useful as 'nearby alternatives'. Only parcels with weighted score >= SCORE_THRESHOLDS['хорошо']
(25.0) are surfaced below that threshold the suggestion has no meaningful quality signal."""
weights = AUDIENCES.get(audience, AUDIENCES["balanced"])
weights = {k: v for k, v in weights.items() if k != "label"}
min_weighted = SCORE_THRESHOLDS["хорошо"]
with _conn() as c:
rows = c.execute("""
SELECT s.site_id, s.name, s.developer, s.obj_class, s.lat, s.lon,
@ -1079,6 +1085,8 @@ def smart_suggestions(lat: float, lon: float, audience: str = "balanced",
"leisure": r["leisure"] or 0, "economic": r["economic"] or 0,
"market": r["market"] or 0}
weighted = sum(weights.get(k,0)*v for k,v in comps.items())
if weighted < min_weighted:
continue
out.append({
"site_id": r["site_id"], "name": r["name"], "developer": r["developer"],
"obj_class": r["obj_class"], "obj_district": r["obj_district"],