Compare commits
No commits in common. "01ccf4f133fb81a627f963b3abe1175db8c41a7a" and "993b44c7e0084076dedcfe1b55d35ce6c112878f" have entirely different histories.
01ccf4f133
...
993b44c7e0
3 changed files with 0 additions and 107 deletions
|
|
@ -1,17 +0,0 @@
|
||||||
-- 271_listings_pro_seller_from_agency.sql
|
|
||||||
-- #3063 п.2: is_pro_seller выводим из уже известного agency_name.
|
|
||||||
-- Прод-факт (замер владельца 23-24.08): 4 469 активных yandex-листингов имеют
|
|
||||||
-- заполненный agency_name (из detail-бэкфилла, где COALESCE-защита была), но
|
|
||||||
-- is_pro_seller у них NULL — признак эрозирован SERP-затиранием до PR #3067.
|
|
||||||
-- Починка разбора не чинит строки (ON CONFLICT/UPDATE прошлого не перезапишет
|
|
||||||
-- задним числом) — поэтому одноразовый бэкфилл.
|
|
||||||
--
|
|
||||||
-- Скоуп: ВСЕ источники, не только yandex — правило «есть имя агентства ⇒ продаёт
|
|
||||||
-- профи» источник-независимо. is_homeowner не трогаем: отсутствие агентства не
|
|
||||||
-- доказывает «частник» (п.3 задачи — отдельное решение).
|
|
||||||
-- Идемпотентно: повторный прогон обновит 0 строк.
|
|
||||||
UPDATE listings
|
|
||||||
SET is_pro_seller = TRUE
|
|
||||||
WHERE agency_name IS NOT NULL
|
|
||||||
AND agency_name <> ''
|
|
||||||
AND is_pro_seller IS DISTINCT FROM TRUE;
|
|
||||||
|
|
@ -1,82 +0,0 @@
|
||||||
"""#3063 п.2: is_pro_seller выводится из наличия агентского блока на детальной.
|
|
||||||
|
|
||||||
Прод-факт (23-24.08): 4 469 активных yandex-листингов с заполненным agency_name
|
|
||||||
(из detail-бэкфилла) имели is_pro_seller = NULL — признак эрозирован
|
|
||||||
SERP-затиранием до PR #3067, а detail-обогащение его и не выводило, хотя данных
|
|
||||||
достаточно. Признак идёт в оценщик trade-in.
|
|
||||||
|
|
||||||
Два конца:
|
|
||||||
- save_detail_enrichment пишет is_pro_seller=TRUE при известном agency_name
|
|
||||||
(fill-only COALESCE; отсутствие блока НЕ доказывает «частник» — п.3 задачи);
|
|
||||||
- миграция 271 бэкфиллит уже существующие строки («починка разбора ≠ починка
|
|
||||||
строк»: UPDATE прошлого задним числом никто не перезапишет).
|
|
||||||
|
|
||||||
Красный на origin/main по ЗНАЧЕНИЮ: в параметрах UPDATE нет ключа
|
|
||||||
is_pro_seller → .get() даёт None, не True.
|
|
||||||
"""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test")
|
|
||||||
|
|
||||||
from pathlib import Path
|
|
||||||
from typing import Any
|
|
||||||
from unittest.mock import MagicMock
|
|
||||||
|
|
||||||
from scraper_kit.providers.yandex.detail import DetailEnrichment, save_detail_enrichment
|
|
||||||
|
|
||||||
_MIGRATION = (
|
|
||||||
Path(__file__).resolve().parents[1] / "data" / "sql" / "271_listings_pro_seller_from_agency.sql"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class _CaptureDb:
|
|
||||||
def __init__(self) -> None:
|
|
||||||
self.params: list[dict[str, Any]] = []
|
|
||||||
|
|
||||||
def execute(self, _stmt: Any, params: dict[str, Any] | None = None) -> Any:
|
|
||||||
if params:
|
|
||||||
self.params.append(params)
|
|
||||||
return MagicMock(rowcount=1)
|
|
||||||
|
|
||||||
def commit(self) -> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
def _update_params(db: _CaptureDb) -> dict[str, Any]:
|
|
||||||
upd = [p for p in db.params if "listing_id" in p]
|
|
||||||
assert upd, "save_detail_enrichment не выполнил UPDATE"
|
|
||||||
return upd[-1]
|
|
||||||
|
|
||||||
|
|
||||||
def test_agency_block_derives_pro_seller_true() -> None:
|
|
||||||
db = _CaptureDb()
|
|
||||||
save_detail_enrichment(
|
|
||||||
db, 101, DetailEnrichment(offer_id="o1", source_url="https://x/1", agency_name="Этажи")
|
|
||||||
) # type: ignore[arg-type]
|
|
||||||
assert _update_params(db).get("is_pro_seller") is True, (
|
|
||||||
"агентский блок известен, а is_pro_seller не выводится — "
|
|
||||||
"4 469 строк на проде копились именно так"
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def test_no_agency_block_leaves_pro_seller_untouched() -> None:
|
|
||||||
"""Отсутствие блока — NULL в параметре: COALESCE сохранит ранее известное
|
|
||||||
значение, и «частник» из этого НЕ выводится (п.3 задачи — отдельно)."""
|
|
||||||
db = _CaptureDb()
|
|
||||||
save_detail_enrichment(
|
|
||||||
db, 102, DetailEnrichment(offer_id="o2", source_url="https://x/2", agency_name=None)
|
|
||||||
) # type: ignore[arg-type]
|
|
||||||
assert _update_params(db).get("is_pro_seller") is None
|
|
||||||
|
|
||||||
|
|
||||||
def test_backfill_migration_shape() -> None:
|
|
||||||
"""Миграция существует, идемпотентна (IS DISTINCT FROM TRUE), не трогает
|
|
||||||
is_homeowner и пустые имена агентств."""
|
|
||||||
sql = _MIGRATION.read_text()
|
|
||||||
assert "SET is_pro_seller = TRUE" in sql
|
|
||||||
assert "agency_name IS NOT NULL" in sql
|
|
||||||
assert "IS DISTINCT FROM TRUE" in sql
|
|
||||||
assert "is_homeowner" not in sql.split("--")[0] or "SET is_homeowner" not in sql
|
|
||||||
|
|
@ -612,10 +612,6 @@ def save_detail_enrichment(db: Session, listing_id: int, e: DetailEnrichment) ->
|
||||||
publish_date_relative
|
publish_date_relative
|
||||||
),
|
),
|
||||||
agency_name = COALESCE(CAST(:agency_name AS text), agency_name),
|
agency_name = COALESCE(CAST(:agency_name AS text), agency_name),
|
||||||
is_pro_seller = COALESCE(
|
|
||||||
CAST(:is_pro_seller AS boolean),
|
|
||||||
is_pro_seller
|
|
||||||
),
|
|
||||||
agency_founded_year = COALESCE(
|
agency_founded_year = COALESCE(
|
||||||
CAST(:agency_founded_year AS int),
|
CAST(:agency_founded_year AS int),
|
||||||
agency_founded_year
|
agency_founded_year
|
||||||
|
|
@ -659,10 +655,6 @@ def save_detail_enrichment(db: Session, listing_id: int, e: DetailEnrichment) ->
|
||||||
"views_total": e.views_total,
|
"views_total": e.views_total,
|
||||||
"pub_date_rel": e.publish_date_relative,
|
"pub_date_rel": e.publish_date_relative,
|
||||||
"agency_name": e.agency_name,
|
"agency_name": e.agency_name,
|
||||||
# #3063 п.2: агентский блок на детальной странице = продаёт профи.
|
|
||||||
# Вывод только в одну сторону (True или NULL): отсутствие блока НЕ
|
|
||||||
# доказывает «частник» — это п.3 задачи, отдельное решение.
|
|
||||||
"is_pro_seller": True if e.agency_name else None,
|
|
||||||
"agency_founded_year": e.agency_founded_year,
|
"agency_founded_year": e.agency_founded_year,
|
||||||
"agency_objects_count": e.agency_objects_count,
|
"agency_objects_count": e.agency_objects_count,
|
||||||
"metro_stations": metro_json,
|
"metro_stations": metro_json,
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue