fix(#112): competitors avg_price_per_m2 всегда None — WHERE status='sold' filter сломан #227

Closed
opened 2026-05-16 19:44:18 +00:00 by lekss361 · 2 comments
Owner

Симптом

avg_price_per_m2 возвращает null для всех конкурентов на любом участке.

Root cause

_AVG_PRICE_SQL в competitors.py фильтрует по f.status = 'sold', но поле status в domrf_kn_flats заполнено на ~0.2% строк (99.8% NULL). Результат: AVG(price_per_m2) всегда NULL — 0 строк проходят фильтр.

-- Текущий (сломанный) запрос:
WHERE f.obj_id = ANY(:obj_ids)
  AND f.price_per_m2 IS NOT NULL
  AND f.status = 'sold'  -- ← почти всегда NULL, 0 rows

Fix plan

Убрать фильтр status = 'sold' из _AVG_PRICE_SQL — оставить только price_per_m2 IS NOT NULL. Так AVG будет считаться по всем квартирам с известной ценой (что корректно для avg_price_per_m2 ЖК). Паттерн аналогичен velocity.py и objective_corpus_room_month — данные берутся по факту наличия, без broken status filter.

Файлы

  • backend/app/services/site_finder/competitors.py — убрать AND f.status = 'sold'
  • backend/tests/api/v1/test_parcel_competitors.py — обновить тест test_competitors_with_prices

Acceptance

  • avg_price_per_m2 не None на реальном кадастре (например 66:41:0204016:10)
  • uv run pytest backend/tests/api/v1/test_parcel_competitors.py — pass
## Симптом `avg_price_per_m2` возвращает `null` для всех конкурентов на любом участке. ## Root cause `_AVG_PRICE_SQL` в `competitors.py` фильтрует по `f.status = 'sold'`, но поле `status` в `domrf_kn_flats` заполнено на ~0.2% строк (99.8% NULL). Результат: `AVG(price_per_m2)` всегда NULL — 0 строк проходят фильтр. ```sql -- Текущий (сломанный) запрос: WHERE f.obj_id = ANY(:obj_ids) AND f.price_per_m2 IS NOT NULL AND f.status = 'sold' -- ← почти всегда NULL, 0 rows ``` ## Fix plan Убрать фильтр `status = 'sold'` из `_AVG_PRICE_SQL` — оставить только `price_per_m2 IS NOT NULL`. Так AVG будет считаться по всем квартирам с известной ценой (что корректно для avg_price_per_m2 ЖК). Паттерн аналогичен `velocity.py` и `objective_corpus_room_month` — данные берутся по факту наличия, без broken status filter. ## Файлы - `backend/app/services/site_finder/competitors.py` — убрать `AND f.status = 'sold'` - `backend/tests/api/v1/test_parcel_competitors.py` — обновить тест `test_competitors_with_prices` ## Acceptance - `avg_price_per_m2` не None на реальном кадастре (например `66:41:0204016:10`) - `uv run pytest backend/tests/api/v1/test_parcel_competitors.py` — pass
lekss361 added the
bug
feedback/max
site-finder
labels 2026-05-16 19:44:18 +00:00
Author
Owner

QA prod verification (2026-05-16):

Confirmed in prod via POST /api/v1/parcels/66:41:0303018:7/competitors:

  • competitors[0].avg_price_per_m2: null (for all 28 competitors)
  • domrf_kn_flats status distribution: 406990 total, 378578 NULL status (93%), 3122 sold (0.8%), 24649 free

Root cause confirmed: WHERE status='sold' filter returns <1% of rows. All competitors show avg_price: null.

QA prod verification (2026-05-16): Confirmed in prod via POST /api/v1/parcels/66:41:0303018:7/competitors: - competitors[0].avg_price_per_m2: null (for all 28 competitors) - domrf_kn_flats status distribution: 406990 total, 378578 NULL status (93%), 3122 sold (0.8%), 24649 free Root cause confirmed: WHERE status='sold' filter returns <1% of rows. All competitors show avg_price: null.
Author
Owner

Resolved earlier — commit 31581ce (2026-05-16) уже убрал status='sold' filter из _AVG_PRICE_SQL. В competitors.py:136 стоит комментарий:

# Фильтр status='sold' убран: поле status в domrf_kn_flats заполнено в ~0.2% строк
# (99.8% NULL) — фильтр давал 0 строк и avg_price_per_m2 всегда None (Issue #112/227).

AVG считается по всем price_per_m2 IS NOT NULL. Closing as stale.

Resolved earlier — commit `31581ce` (2026-05-16) уже убрал `status='sold'` filter из `_AVG_PRICE_SQL`. В `competitors.py:136` стоит комментарий: ```python # Фильтр status='sold' убран: поле status в domrf_kn_flats заполнено в ~0.2% строк # (99.8% NULL) — фильтр давал 0 строк и avg_price_per_m2 всегда None (Issue #112/227). ``` AVG считается по всем `price_per_m2 IS NOT NULL`. Closing as stale.
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#227
No description provided.