fix(tradein): guard listing_segment во всех оценочных читателях listings — novostroyki вне comps (#1186) #1192

Merged
lekss361 merged 2 commits from fix/tradein-segment-guards into main 2026-06-12 09:06:29 +00:00
Owner

Summary

Багфикс + шаг 1 плана «парсим все сегменты, фильтруем на чтении»: avito-sweep не фильтрует сегмент → novostroyki-строки уже в проде, а оценочные запросы не были защищены.

  • Канон-предикат во всех 13 местах: (listing_segment IS NULL OR listing_segment = 'vtorichka') — NULL = legacy-вторичка до м.011, выкидывать нельзя.
  • estimator.py — 6 запросов (Tier A/C — guard новый; Tier S/H через _COMMON_WHERE и Tier W — замена <> 'novostroyki', который молча отбрасывал NULL-строки (SQL three-valued logic) → канон возвращает legacy-вторичку в comps, задокументировано комментами).
  • asking_to_sold_ratio.py — 6 CTE (ask-сторона ratio), sber_index.py — reconciliation.
  • Refactor по review: убран dead-param listing_segment из _fetch_anchor_comps + вычисление target_segment в caller'е (Tier C теперь канон, не «самый частый сегмент»).
  • Тесты: test_segment_guard_1186.py — 16 шт., включая негативные (нигде нет голого = 'vtorichka' без NULL-ветки и нигде <> 'novostroyki').

Инвентаризация всех FROM listings (вердикты)

Guard добавлен (13): estimator Tier A/C/S/H/W (1468, 1511, 3336, 3387, 3470, 3583), asking_to_sold_ratio (93, 137, 214, 257, 319, 360), sber_index reconciliation (399).
Guard не нужен (обоснование): admin.py:439/455 + trade_in.py:669/1095 (операционная статистика/PK-lookup), house_imv_backfill:113 (параметры своего дома), scrape_pipeline/scrapers/matching (writing path + dedup всех сегментов — это правильно), geocode/price-history backfills (инфраструктура), search_query.py (catalog UI через matview — segment-параметр выдачи = scope #1188), data/sql 050/046/094/095 (display/diagnostic), 080/098 (one-shot seeds, re-apply не будет; daily refresh — Python task, исправлен).

Test plan

  • полный pytest tradein-backend: 1749 passed, 2 deselected
  • ruff чисто
  • post-merge: оценка тестового адреса стабильна; baseline-замер до включения полной загрузки — issue #1189 (D)

Pre-push review: ⚠️ MINOR (dead param, docstring, негативные asserts) — всё исправлено вторым коммитом a9a489b.

Refs #1186

## Summary Багфикс + шаг 1 плана «парсим все сегменты, фильтруем на чтении»: avito-sweep не фильтрует сегмент → novostroyki-строки уже в проде, а оценочные запросы не были защищены. - **Канон-предикат во всех 13 местах:** `(listing_segment IS NULL OR listing_segment = 'vtorichka')` — NULL = legacy-вторичка до м.011, выкидывать нельзя. - `estimator.py` — 6 запросов (Tier A/C — guard новый; Tier S/H через `_COMMON_WHERE` и Tier W — замена `<> 'novostroyki'`, который **молча отбрасывал NULL-строки** (SQL three-valued logic) → канон возвращает legacy-вторичку в comps, задокументировано комментами). - `asking_to_sold_ratio.py` — 6 CTE (ask-сторона ratio), `sber_index.py` — reconciliation. - Refactor по review: убран dead-param `listing_segment` из `_fetch_anchor_comps` + вычисление `target_segment` в caller'е (Tier C теперь канон, не «самый частый сегмент»). - Тесты: `test_segment_guard_1186.py` — 16 шт., включая негативные (нигде нет голого `= 'vtorichka'` без NULL-ветки и нигде `<> 'novostroyki'`). ## Инвентаризация всех `FROM listings` (вердикты) **Guard добавлен (13):** estimator Tier A/C/S/H/W (1468, 1511, 3336, 3387, 3470, 3583), asking_to_sold_ratio (93, 137, 214, 257, 319, 360), sber_index reconciliation (399). **Guard не нужен (обоснование):** admin.py:439/455 + trade_in.py:669/1095 (операционная статистика/PK-lookup), house_imv_backfill:113 (параметры своего дома), scrape_pipeline/scrapers/matching (writing path + dedup всех сегментов — это правильно), geocode/price-history backfills (инфраструктура), search_query.py (catalog UI через matview — segment-параметр выдачи = scope #1188), data/sql 050/046/094/095 (display/diagnostic), 080/098 (one-shot seeds, re-apply не будет; daily refresh — Python task, исправлен). ## Test plan - [x] полный pytest tradein-backend: **1749 passed, 2 deselected** - [x] ruff чисто - [ ] post-merge: оценка тестового адреса стабильна; baseline-замер до включения полной загрузки — issue #1189 (D) Pre-push review: ⚠️ MINOR (dead param, docstring, негативные asserts) — всё исправлено вторым коммитом a9a489b. Refs #1186
lekss361 added 2 commits 2026-06-12 09:01:59 +00:00
Канон-предикат: (listing_segment IS NULL OR listing_segment = 'vtorichka')
NULL = legacy вторичка до миграции 011 (sweep'ы были secondary-only) — отбрасывать нельзя.

Затронуты все SELECT-пути, влияющие на оценку и корректирующие коэффициенты:
- estimator.py: _COMMON_WHERE (Tier S/H), Tier W inline, Tier A anchor (добавлен впервые),
  Tier C anchor (заменён с параметрического CAST(:segment) на канон — прежний вариант
  отбрасывал NULL-строки при segment='vtorichka').
- asking_to_sold_ratio.py: ask_side, ask_global (_REDERIVE_SQL); bounds (_REDERIVE_BOUNDS_SQL);
  ask_tiered, ask_side_all, ask_global (_REDERIVE_TIERED_SQL) — 6 мест.
- sber_index.py: reconciliation-запрос (диагностический asking-median).

Не затронуты: скрейперы/scrape_pipeline, search matview (catalog display, не оценка),
admin stats, geocoding, cian price history, matching — обосновано в инвентаризации.

Тесты: новый test_segment_guard_1186.py (статика SQL + поведение mock); обновлён
test_asking_to_sold_ratio.py (subset-тест нормализует guard, добавленный в refresh но
отсутствующий в 080-seed).
refactor(tradein): убрать dead listing_segment param из _fetch_anchor_comps + негативные guard-asserts (review fixup, #1186)
All checks were successful
CI / frontend-tests (push) Has been skipped
CI / changes (pull_request) Successful in 6s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / changes (push) Successful in 6s
CI / backend-tests (push) Has been skipped
a9a489bba5
- Удалён параметр listing_segment: str | None = None из сигнатуры _fetch_anchor_comps
  (guard хардкожен в SQL-блоках Tier A/C, параметр не использовался)
- Убраны seg_counts / target_segment в caller-блоке SAME-BUILDING ANCHOR (~L2174)
- Docstring Tier C обновлён: «вторичка-канон guard (#1186): NULL = legacy вторичка»
- test_segment_guard_1186.py: добавлены 4 негативных assert'а:
  · no bare = 'vtorichka' без IS NULL OR
  · no deprecated <> / != 'novostroyki' form
  · _fetch_anchor_comps не содержит listing_segment в сигнатуре
  · Tier C не использует CAST(:segment AS text) (параметрическую форму)
lekss361 added the
bug
scope/backend
status/review
вторичка
labels 2026-06-12 09:02:10 +00:00
lekss361 merged commit d64a57cfcd into main 2026-06-12 09:06:29 +00:00
lekss361 deleted branch fix/tradein-segment-guards 2026-06-12 09:06:29 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#1192
No description provided.