From 7801ac6cc1c9951bbda98355435d087ae8c8312d Mon Sep 17 00:00:00 2001 From: bot-backend Date: Sun, 12 Jul 2026 20:36:08 +0300 Subject: [PATCH 1/2] =?UTF-8?q?fix(tradein/estimator):=20=D1=80=D0=B5?= =?UTF-8?q?=D0=B7=D0=BE=D0=BB=D0=B2=D0=B8=D1=82=D1=8C=20target-=D0=B3?= =?UTF-8?q?=D0=BE=D1=80=D0=BE=D0=B4=20=D0=BA=D0=BE=D1=80=D0=B8=D0=B4=D0=BE?= =?UTF-8?q?=D1=80=D0=B0=20=D0=B8=D0=B7=20payload.address=20(C2=20hotfix)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit C2 city-scope коридора был NO-OP для не-ЕКБ: estimate_quality резолвил город из geo.full_address, но геокодер РОНЯЕТ город для не-ЕКБ («Ленина, 1» вместо «Нижний Тагил, Ленина, 1») → target_city=None → коридор либо unscoped, либо None. Verified на проде: Н.Тагил corridor=None. Фикс: резолвить город из payload.address (город есть) с fallback на canonical/geo; коридор матчить тоже по payload.address. GET: row.address приоритетнее canonical. Verified прод-диагностикой: corridor(address=payload.address, city=resolve) → Н.Тагил=9, ЕКБ=15 (было: Н.Тагил=None). EKB regression-гейт байт-зелёный. geocoder full_address city-strip (display «Ленина,1») → cosmetic, E. --- tradein-mvp/backend/app/api/v1/trade_in.py | 8 ++++---- tradein-mvp/backend/app/services/estimator.py | 9 +++++++-- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/tradein-mvp/backend/app/api/v1/trade_in.py b/tradein-mvp/backend/app/api/v1/trade_in.py index 88a7c33d..439f7052 100644 --- a/tradein-mvp/backend/app/api/v1/trade_in.py +++ b/tradein-mvp/backend/app/api/v1/trade_in.py @@ -284,10 +284,10 @@ def get_estimate( else None ) - # (oblast C2): city-scope корридора на GET-rehydrate тоже — тот же - # resolver, что и на POST (estimate_quality), над персистированным адресом - # (canonical_address приоритетнее сырого row.address, если DaData отработал). - target_city = _resolve_target_city(row.canonical_address or row.address) + # (oblast C2 + c2fix): city-scope корридора на GET-rehydrate. row.address + # (исходный ввод) приоритетнее canonical для резолва города — canonical/geo + # роняют город для не-ЕКБ («Ленина, 1»). Corridor уже матчит по row.address. + target_city = _resolve_target_city(row.address) or _resolve_target_city(row.canonical_address) dkp_raw = _fetch_dkp_corridor( db, address=row.address, rooms=row.rooms, area=area_f, city=target_city ) diff --git a/tradein-mvp/backend/app/services/estimator.py b/tradein-mvp/backend/app/services/estimator.py index c7c71931..afe8adc4 100644 --- a/tradein-mvp/backend/app/services/estimator.py +++ b/tradein-mvp/backend/app/services/estimator.py @@ -3207,13 +3207,18 @@ async def estimate_quality( # (oblast C2): city-scope корридора — та же precedence, что и для # match_house_readonly выше (dadata.canonical_address приоритетнее сырого # geo.full_address, если DaData отработал). - target_city = _resolve_target_city( + # payload.address (пользовательский ввод) — САМЫЙ надёжный источник города: + # geocoder.full_address/canonical часто роняют город для не-ЕКБ («Ленина, 1» + # вместо «Нижний Тагил, Ленина, 1») → без этого city-scope коридора (C2) был + # бы no-op для всей области (verified прод: tc=None → corridor=None). Fallback + # на canonical/geo для bare-адресов без города. + target_city = _resolve_target_city(payload.address) or _resolve_target_city( (dadata.canonical_address if dadata else None) or geo.full_address ) dkp_raw = await asyncio.to_thread( _fetch_dkp_corridor, db, - address=geo.full_address, + address=payload.address, rooms=payload.rooms, area=payload.area_m2, city=target_city, From 69f8b50934db63949de51a8ede3348d562c19ed6 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Sun, 12 Jul 2026 20:48:25 +0300 Subject: [PATCH 2/2] =?UTF-8?q?docs(tradein):=20=D0=B8=D1=81=D0=BF=D1=80?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D1=82=D1=8C=20GET-rehydrate=20comment=20?= =?UTF-8?q?=E2=80=94=20row.address=3Dgeo.full=5Faddress=20(city-stripped),?= =?UTF-8?q?=20corridor=20unscoped=20=D0=B4=D0=BB=D1=8F=20=D0=BD=D0=B5-?= =?UTF-8?q?=D0=95=D0=9A=D0=91=20(known=20limitation)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tradein-mvp/backend/app/api/v1/trade_in.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/tradein-mvp/backend/app/api/v1/trade_in.py b/tradein-mvp/backend/app/api/v1/trade_in.py index 439f7052..094c5ca4 100644 --- a/tradein-mvp/backend/app/api/v1/trade_in.py +++ b/tradein-mvp/backend/app/api/v1/trade_in.py @@ -284,9 +284,12 @@ def get_estimate( else None ) - # (oblast C2 + c2fix): city-scope корридора на GET-rehydrate. row.address - # (исходный ввод) приоритетнее canonical для резолва города — canonical/geo - # роняют город для не-ЕКБ («Ленина, 1»). Corridor уже матчит по row.address. + # (oblast C2): city-scope корридора на GET-rehydrate. NB: row.address здесь = + # geo.full_address (персистится на estimate-time), который РОНЯЕТ город для + # не-ЕКБ («Ленина, 1») → target_city=None → corridor unscoped/None для не-ЕКБ + # (KNOWN LIMITATION: POST-path чинит через payload.address; GET получит паритет + # когда raw payload.address начнёт персиститься — follow-up). Для ЕКБ ок; reorder + # ниже безвреден (оба source city-stripped для не-ЕКБ, оба екб для ЕКБ). target_city = _resolve_target_city(row.address) or _resolve_target_city(row.canonical_address) dkp_raw = _fetch_dkp_corridor( db, address=row.address, rooms=row.rooms, area=area_f, city=target_city