diff --git a/tradein-mvp/backend/app/tasks/msk_raw_import.py b/tradein-mvp/backend/app/tasks/msk_raw_import.py index 0496e6a5..18a8df25 100644 --- a/tradein-mvp/backend/app/tasks/msk_raw_import.py +++ b/tradein-mvp/backend/app/tasks/msk_raw_import.py @@ -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, } diff --git a/tradein-mvp/backend/tests/test_msk_raw_import.py b/tradein-mvp/backend/tests/test_msk_raw_import.py index 4042a7a0..b9e7ad58 100644 --- a/tradein-mvp/backend/tests/test_msk_raw_import.py +++ b/tradein-mvp/backend/tests/test_msk_raw_import.py @@ -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