competitors.py:491-502 — ценовой fallback покрывает только objective_complex_mapping, игнорируя nearest_cx gap-fill: конкурент с velocity>0 из спатиального матча получает avg_price=None #1615

Closed
opened 2026-06-16 17:19:55 +00:00 by bot-backend · 0 comments
Collaborator

Severity: low · Категория: inconsistency / coverage-gap · Файл: backend/app/services/site_finder/competitors.py:491-502, 643-664

Файл: backend/app/services/site_finder/competitors.py: velocity (CTE mapped, 378-393) обогащается из ДВУХ источников — objective_complex_mapping (380-382) И спатиально-именного nearest_cx gap-fill (387-392; nearest_cx по строке 371 берёт ровно те obj_id, которых нет в objective_complex_mapping). Ценовой fallback _OBJECTIVE_PRICE_FALLBACK_SQL (491-502) джойнит ТОЛЬКО objective_complex_mapping cm (496), не учитывая nearest_cx. В результате конкурент, чья velocity пришла из gap-fill, при отсутствии domrf-цены (на последнем snapshot domrf_kn_flats.price_per_m2 = 0 строк, коммент 474-476) получает avg_price_per_m2=None, хотя у спатиально-сматченного complex'а есть objective_lots.price_per_m2_rub. Эффект: price_similarity падает в нейтраль 0.5 (_price_similarity, 209-210), avg_price_per_m2/price_source не показываются (728-729), тогда как velocity у этого же конкурента посчитан → несогласованность relevance_weight и полноты отчёта. Не порча данных, velocity отображается, цена просто отсутствует → severity low. Фикс: расширить ценовой fallback на nearest_cx-маппинг — добавить ветку, которая через nearest_cx.complex_id → objective_lots.project_name берёт median(price_per_m2_rub) для obj_id из gap-fill (зеркало того, как mapped UNION'ит nearest_cx для velocity), либо переиспользовать единый CTE mapped как мост и для цены.

Почему баг: Конкурент, чья velocity пришла из спатиального gap-fill (его нет в objective_complex_mapping), при пустой domrf-цене (а на последнем snapshot price_per_m2 = 0 строк, см. комментарий #307) получит avg_price_per_m2=None, хотя у спатиально-сматченного complex'а есть objective_lots.price_per_m2_rub. Итог: price_similarity падает в нейтраль 0.5 и avg_price не показывается, при том что velocity у этого же конкурента посчитан. Несогласованность обогащения; влияет на relevance_weight и на полноту отчёта. Не порча данных — отсюда low.

Как чинить: Либо использовать общий маппинг (UNION objective_complex_mapping + nearest_cx→objective_lots.project_name) и для цены, и для velocity, либо явно задокументировать, что ценовой fallback намеренно покрывает только явные маппинги, чтобы это не выглядело как баг при росте velocity-покрытия.


deep-audit backend v2 (после PR #1543) · verify+harden, confidence 0.78 · unit B36

**Severity:** low · **Категория:** inconsistency / coverage-gap · **Файл:** `backend/app/services/site_finder/competitors.py:491-502, 643-664` Файл: backend/app/services/site_finder/competitors.py: velocity (CTE mapped, 378-393) обогащается из ДВУХ источников — objective_complex_mapping (380-382) И спатиально-именного nearest_cx gap-fill (387-392; nearest_cx по строке 371 берёт ровно те obj_id, которых нет в objective_complex_mapping). Ценовой fallback _OBJECTIVE_PRICE_FALLBACK_SQL (491-502) джойнит ТОЛЬКО objective_complex_mapping cm (496), не учитывая nearest_cx. В результате конкурент, чья velocity пришла из gap-fill, при отсутствии domrf-цены (на последнем snapshot domrf_kn_flats.price_per_m2 = 0 строк, коммент 474-476) получает avg_price_per_m2=None, хотя у спатиально-сматченного complex'а есть objective_lots.price_per_m2_rub. Эффект: price_similarity падает в нейтраль 0.5 (_price_similarity, 209-210), avg_price_per_m2/price_source не показываются (728-729), тогда как velocity у этого же конкурента посчитан → несогласованность relevance_weight и полноты отчёта. Не порча данных, velocity отображается, цена просто отсутствует → severity low. Фикс: расширить ценовой fallback на nearest_cx-маппинг — добавить ветку, которая через nearest_cx.complex_id → objective_lots.project_name берёт median(price_per_m2_rub) для obj_id из gap-fill (зеркало того, как mapped UNION'ит nearest_cx для velocity), либо переиспользовать единый CTE mapped как мост и для цены. **Почему баг:** Конкурент, чья velocity пришла из спатиального gap-fill (его нет в objective_complex_mapping), при пустой domrf-цене (а на последнем snapshot price_per_m2 = 0 строк, см. комментарий #307) получит avg_price_per_m2=None, хотя у спатиально-сматченного complex'а есть objective_lots.price_per_m2_rub. Итог: price_similarity падает в нейтраль 0.5 и avg_price не показывается, при том что velocity у этого же конкурента посчитан. Несогласованность обогащения; влияет на relevance_weight и на полноту отчёта. Не порча данных — отсюда low. **Как чинить:** Либо использовать общий маппинг (UNION objective_complex_mapping + nearest_cx→objective_lots.project_name) и для цены, и для velocity, либо явно задокументировать, что ценовой fallback намеренно покрывает только явные маппинги, чтобы это не выглядело как баг при росте velocity-покрытия. --- <sub>deep-audit backend v2 (после PR #1543) · verify+harden, confidence 0.78 · unit B36</sub>
bot-backend added the
week ревью 1
label 2026-06-16 17:19:55 +00:00
Sign in to join this conversation.
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#1615
No description provided.