From 57dd989e580116e25fe27643e5ba7d3308516f28 Mon Sep 17 00:00:00 2001 From: lekss361 Date: Sun, 24 May 2026 18:27:02 +0300 Subject: [PATCH] fix(tradein-phase-c): prepend region prefix to addresses for IMV geocoder MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Avito /coords/by_address mis-resolves ambiguous addresses to wrong regions. Examples: 'улица Академика Королева, 8Г' -> Беларусь, Могилёв (404); 'р-н Орджоникидзевский, мкр. Уралмаш...' -> Хакасия (404). Add _REGION_BBOX lookup (lat/lon -> region name) + _enrich_address_for_imv() that prepends region prefix if address doesn't already have one. Default EKB fallback when coords are NULL. --- tradein-mvp/scripts/backfill-house-imv.py | 48 ++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/tradein-mvp/scripts/backfill-house-imv.py b/tradein-mvp/scripts/backfill-house-imv.py index 48b8a38b..a9da11d3 100644 --- a/tradein-mvp/scripts/backfill-house-imv.py +++ b/tradein-mvp/scripts/backfill-house-imv.py @@ -70,6 +70,46 @@ def _map_house_type(raw: str | None) -> str: return _HOUSE_TYPE_DEFAULT return _HOUSE_TYPE_TO_IMV.get(raw.lower().strip(), _HOUSE_TYPE_DEFAULT) + +# Bounding boxes for known regions in our DB. +# When house.lat/lon falls inside one, we prepend the region name to address +# before sending to Avito (resolves ambiguity — e.g. "Орджоникидзевский район" exists +# in EKB AND in Khakasia republic). +_REGION_BBOX = [ + # (region_name, lat_min, lat_max, lon_min, lon_max) + ("Свердловская область, Екатеринбург", 56.5, 57.1, 59.9, 61.0), + ("Свердловская область", 56.0, 61.0, 58.0, 65.0), # rest of region + ("Кировская область, Киров", 58.4, 58.8, 49.4, 49.9), + ("Ставропольский край, Ставрополь", 44.9, 45.2, 41.7, 42.2), + ("Тюменская область, Тюмень", 57.0, 57.3, 65.3, 65.8), + ("Челябинская область, Челябинск", 55.0, 55.4, 61.2, 61.7), +] +_REGION_DEFAULT = "Свердловская область, Екатеринбург" # bulk of our data is EKB + + +def _detect_region_prefix(lat: float | None, lon: float | None) -> str: + if lat is None or lon is None: + return _REGION_DEFAULT + for name, lat_min, lat_max, lon_min, lon_max in _REGION_BBOX: + if lat_min <= lat <= lat_max and lon_min <= lon <= lon_max: + return name + return _REGION_DEFAULT + + +def _enrich_address_for_imv(raw: str, lat: float | None, lon: float | None) -> str: + """Prepend region prefix if address lacks one (ambiguity fix for Avito geocoder).""" + addr = (raw or "").strip() + # Already has region marker? skip + addr_lower = addr.lower() + if any( + m in addr_lower + for m in ("область,", " обл.,", " обл,", "край,", "респ.,", "республика,") + ): + return addr + prefix = _detect_region_prefix(lat, lon) + return f"{prefix}, {addr}" + + DATABASE_URL = os.environ.get("DATABASE_URL") if not DATABASE_URL: raise SystemExit( @@ -317,8 +357,14 @@ async def process_one(db: Session, house: dict) -> str: mark_status(db, house["id"], "no_address", "house.address is NULL") return "no_address" + # Enrich with region prefix to prevent Avito geocoder mis-resolving ambiguous + # street names to wrong regions (e.g. "улица Победы" exists in EKB and Khakasia). + enriched = _enrich_address_for_imv(address, house.get("lat"), house.get("lon")) + if enriched != address: + logger.debug("enriched address house=%d %r -> %r", house["id"], address, enriched) + try: - result = await evaluate_via_imv(address=address, **params) + result = await evaluate_via_imv(address=enriched, **params) except IMVAddressNotFoundError as e: mark_status(db, house["id"], "not_found", str(e)[:200]) return "not_found" -- 2.45.3