All checks were successful
Deploy Trade-In / changes (push) Successful in 11s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / build-browser (push) Has been skipped
Deploy Trade-In / test (push) Successful in 3m5s
Deploy Trade-In / build-backend (push) Successful in 1m43s
Deploy Trade-In / deploy (push) Successful in 2m26s
46 lines
2.8 KiB
Python
46 lines
2.8 KiB
Python
"""Единый гейт правдоподобия для высоты потолков (#2699).
|
||
|
||
Канон — колонка ``listings.ceiling_height_m`` (метры). Единицы вынесены в имя,
|
||
как и во всём остальном коде проекта (``domrf_kn_flats.ceiling_height_m``,
|
||
``domrf_kn_objects.ceiling_height_m``, фронтовый ``ceiling_height_m``);
|
||
``listings.ceiling_height`` (миграция 019, numeric(3,2)) — единственное
|
||
исключение, помеченное DEPRECATED в миграции 238.
|
||
|
||
Гейт был только у yandex SERP (инлайн, #2007) — остальные писатели гнали в БД
|
||
что распарсили. Прод 2026-08-07: в ``ceiling_height_m`` 26 строк > 6 м (максимум
|
||
29.90) и 84 строки < 2 м (83 из них — ровно 0.00), все из avito detail. Теперь
|
||
гейт один на всех писателей.
|
||
|
||
Диапазон 2.0–6.0 м: снизу — ниже жилой потолок не бывает (СП 54.13330 требует
|
||
от 2.5 м; 2.0 оставлено с запасом на мис-парс округлений), сверху — сталинки и
|
||
лофты редко переваливают за 5 м, 6.0 покрывает двухуровневые карточки, где
|
||
продавец указывает высоту помещения целиком. Тот же диапазон уже год работает у
|
||
yandex SERP; прод-подтверждение: все 7 675 yandex-значений лежат в [2.00, 6.00].
|
||
|
||
Второй эффект гейта: ``ceiling_height`` — numeric(3,2), max 9.99, и запись
|
||
out-of-range роняла бы весь батч DataError'ом (per-lot SAVEPOINT ловит только
|
||
IntegrityError). Гейт снимает и этот класс отказа.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
CEILING_MIN_M = 2.0
|
||
CEILING_MAX_M = 6.0
|
||
|
||
|
||
def plausible_ceiling_m(value: float | int | str | None) -> float | None:
|
||
"""Вернуть высоту потолка в метрах, если она физически возможна, иначе None.
|
||
|
||
Нечисловой вход (``None``, пустая строка, мусор) → ``None`` без исключения:
|
||
вызывающие — парсеры чужого HTML/JSON, для них «не разобралось» это норма,
|
||
а не ошибка. Сырое значение остаётся в ``raw_payload`` карточки.
|
||
"""
|
||
if value is None:
|
||
return None
|
||
try:
|
||
height = float(value)
|
||
except (TypeError, ValueError):
|
||
return None
|
||
if CEILING_MIN_M <= height <= CEILING_MAX_M:
|
||
return height
|
||
return None
|