fix(tradein/domklik): пробросить isRosreestrApproved и is_pro_seller в ScrapedLot

BFF-ответ Домклика уже парсит isRosreestrApproved и seller.company/agent
(agency_name), но клал их только в raw_payload jsonb — на проде обе колонки
is_rosreestr_checked и is_pro_seller у всех 1338 активных domklik-листингов
были 100% NULL, хотя данные для их заполнения были в наличии всегда.

- is_rosreestr_checked: прямой passthrough isRosreestrApproved (mirror
  cian serp.py: offer.get("isRosreestrChecked") -> is_rosreestr_checked).
  НЕ то же самое, что listings.registry_match (Avito-специфичная сверка
  "площадь/адрес/этаж совпадают" из detail-страницы, заполняется только
  avito/detail.py) — поэтому registry_match не трогаем.
- is_pro_seller выводится из agency_name: agency_name is None -> None
  (нет данных о продавце), иначе bool(agency_name). agency_name="" на
  практике не возникает (_extract_agency_name уже схлопывает whitespace-only
  в None), но проверка сделана через `is not None`, а не `!= ""`, чтобы
  случайно не превратить None в True.

Refs #3064
This commit is contained in:
bot-backend 2026-08-24 00:32:54 +03:00
parent ccdd7553ba
commit db31473611
2 changed files with 49 additions and 0 deletions

View file

@ -90,6 +90,40 @@ def test_map_item_basic_mapping() -> None:
assert lot.lon == pytest.approx(60.612)
def test_map_item_promotes_rosreestr_and_pro_seller_from_bff_fixture() -> None:
"""isRosreestrApproved / seller.company из BFF-фикстуры пробрасываются в
ScrapedLot.is_rosreestr_checked / is_pro_seller (#3064 п.1-2).
На проде 1338 активных domklik-листингов имели ОБЕ колонки 100% NULL, хотя
isRosreestrApproved и seller уже парсились и оседали только в raw_payload.
"""
import json
from pathlib import Path
fixture_path = Path(__file__).parent / "fixtures" / "domclick_bff_offers_sample.json"
items = json.loads(fixture_path.read_text(encoding="utf-8"))["result"]["items"]
config = SimpleNamespace(browser_http_endpoint="http://tradein-browser:9000")
scraper = DomClickScraper(config, delay_provider=lambda _name: 8.0)
# item 1001: isRosreestrApproved=true, seller.company="АН Этажи" (непустая
# строка) -> ЕГРН-сверка пройдена и продавец агентство/агент.
lot_checked = scraper._map_item(items[0])
assert lot_checked is not None
assert lot_checked.is_rosreestr_checked is True
assert lot_checked.agency_name == "АН Этажи"
assert lot_checked.is_pro_seller is True
# item 1003: isRosreestrApproved отсутствует в payload, seller=None ->
# оба поля неизвестны (None), а НЕ False/True — отсутствие данных не
# то же самое, что "проверено — не сходится" / "не агентство".
lot_unknown = scraper._map_item(items[2])
assert lot_unknown is not None
assert lot_unknown.is_rosreestr_checked is None
assert lot_unknown.agency_name is None
assert lot_unknown.is_pro_seller is None
# ── fetch_city: report_ban на QRATOR-блок (#2600 п.1) ───────────────────────────

View file

@ -661,6 +661,19 @@ class DomClickScraper(BaseScraper):
if not isinstance(seller, dict):
seller = {}
agency_name = _extract_agency_name(seller)
# is_pro_seller выводим из agency_name (тот же паттерн промоута, что
# у cian/yandex #2008): непустое имя агентства/агента -> pro-продавец.
# None остаётся None (нет данных о продавце) — не путать с "точно
# частник" (False). agency_name="" не бывает (_extract_agency_name
# уже схлопывает whitespace-only в None), но проверяем `is not None`
# явно, а не `!= ""`, чтобы не превратить None в True по ошибке.
is_pro_seller: bool | None = None if agency_name is None else bool(agency_name)
# ── ЕГРН-проверка (Layer A promote, #3064) ─────────────────────────
# isRosreestrApproved — прямое 1:1 соответствие ScrapedLot.is_rosreestr_checked
# (mirror cian serp.py: offer.get("isRosreestrChecked")). Оставляем ключ и
# в raw_payload (как cian) — совместимость с существующими consumers.
is_rosreestr_checked: bool | None = item.get("isRosreestrApproved")
flat_complex = item.get("flatComplex") or {}
raw_payload: dict[str, Any] = {
@ -714,6 +727,8 @@ class DomClickScraper(BaseScraper):
repair_state=repair_state,
description_minhash=description_minhash,
agency_name=agency_name,
is_pro_seller=is_pro_seller,
is_rosreestr_checked=is_rosreestr_checked,
raw_payload=raw_payload,
)
except Exception: