ДомКлик как четвёртый источник переливки московского сырья в listings #3497

Merged
lekss361 merged 1 commit from feat/msk-domclick-import into main 2026-09-12 12:26:10 +00:00
2 changed files with 53 additions and 0 deletions

View file

@ -77,10 +77,21 @@ source_id) считает сам кит (`ScrapedLot.compute_dedup_hash`), це
значения, «Москва» 10 610 и «Московская область» 10 783. Координаты у Яндекса
заполнены у 100% карточек, поэтому ни геокод, ни `geocode_missing` ему не нужны.
Отбор Москвы (source=domclick) тоже стоит ноль вызовов, но по другой причине:
сырьё собрано запросом с GUID-ом Москвы и отфильтровано по bbox ещё на стороне
сборщика, поэтому областных карточек в нём нет по построению. Регион читается
ПЕРВЫМ компонентом адреса («Москва, улица Нижние Мнёвники, 16 к2»), и фильтр
оставлен второй линией на случай, если в ту же таблицу когда-нибудь приедет
второй город. Замер по 5 024 карточкам первого прохода: единственное значение
первого компонента «Москва». Новая Москва приходит как «Москва, пос. Птичное,
», то есть посёлок стоит вторым компонентом и регион 77 не ломает. Координаты
заполнены у 100% карточек.
Запуск:
python -m app.tasks.msk_raw_import --dry-run
python -m app.tasks.msk_raw_import --limit 500
python -m app.tasks.msk_raw_import --source yandex
python -m app.tasks.msk_raw_import --source domclick
python -m app.tasks.msk_raw_import --source avito --geocode --geocode-limit 9000
python -m app.tasks.msk_raw_import --source avito --allow-unfiltered # аварийный
"""
@ -160,6 +171,7 @@ SOURCE_VIEWS = {
"cian": "msk_raw.cian_latest",
"avito": "msk_raw.avito_latest",
"yandex": "msk_raw.yandex_latest",
"domclick": "msk_raw.domclick_latest",
}
_PAGE_SQL = """
@ -485,11 +497,30 @@ def is_moscow_yandex_address(address: str | None) -> bool:
return len(parts) > 1 and parts[1] == "Москва"
def is_moscow_domclick_address(address: str | None) -> bool:
"""У ДомКлика регион — ПЕРВЫЙ компонент адреса: «Москва, улица …».
Источник собран запросом с GUID-ом Москвы в параметре address и дополнительно
отфильтрован по bbox на стороне сборщика, так что областных карточек в сырье
и не должно быть. Фильтр здесь вторая линия: сменится GUID в сборщике или
появится второй город в той же таблице импорт не потащит его в Москву
молча. Замер по 5 024 карточкам первого прохода: первый компонент имеет ровно
одно значение, «Москва», областных нет ни одной.
Новая Москва отдельным значением НЕ приходит: «Москва, x. Ильичевка, »,
«Москва, пос. Птичное, » посёлок стоит ВТОРЫМ компонентом, первый всегда
город, что совпадает с кодом региона 77.
"""
parts = [part.strip() for part in (address or "").split(",")]
return bool(parts) and parts[0] == "Москва"
# Источники, у которых город виден в самой карточке. Ключ отсутствует —
# источник про город молчит, и без пред-геокода писать его нельзя (avito).
CITY_FILTERS = {
"cian": is_moscow_address,
"yandex": is_moscow_yandex_address,
"domclick": is_moscow_domclick_address,
}

View file

@ -461,3 +461,25 @@ def test_geocode_is_refused_for_yandex(captured: list[dict[str, Any]]) -> None:
mod.import_msk_raw(
_FakeSession([{"id": 1, "payload": _yandex()}]), source="yandex", geocode=True
)
def test_domclick_moscow_is_recognised_without_geocode() -> None:
"""Регион ДомКлика — первый компонент адреса; Новая Москва тоже Москва."""
assert mod.is_moscow_domclick_address("Москва, улица Нижние Мнёвники, 16 к2")
assert mod.is_moscow_domclick_address("Москва, пос. Птичное, Лесная улица, 82")
assert mod.is_moscow_domclick_address("Москва, x. Ильичевка, хутор Ильичёвка, 1")
def test_domclick_non_moscow_is_rejected() -> None:
"""Вторая линия обороны: GUID в сборщике сменится — импорт не смолчит."""
assert not mod.is_moscow_domclick_address("Московская область, Химки, 9 Мая, 21к3")
assert not mod.is_moscow_domclick_address("Екатеринбург, улица Ясная, 2")
# «Москва» должна быть отдельным компонентом, а не префиксом другого слова.
assert not mod.is_moscow_domclick_address("Московский, 1-я улица, 5")
assert not mod.is_moscow_domclick_address("")
assert not mod.is_moscow_domclick_address(None)
def test_domclick_view_is_wired_into_source_views() -> None:
assert mod.SOURCE_VIEWS["domclick"] == "msk_raw.domclick_latest"
assert mod.CITY_FILTERS["domclick"] is mod.is_moscow_domclick_address