All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 9s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 52s
B1: avito/cian/yandex city-sweep по городам Свердловской обл. (Н.Тагил/Каменск/Первоуральск/В.Пышма/Серов). Schedules засеяны DISABLED — rollout вручную по одному городу (proxy = 1 живой прокси). - pipeline.py: CITY_ANCHORS (5) + get_city_anchors(slug) (None→EKB fallback). - scheduler.py: sweep-jobs читают params.city → анкоры; wildcard-хендлеры *_city_sweep_* (source UNIQUE → per-city source, как deactivate_stale_*). - geo.py: OBLAST66_BBOX + is_within_oblast66_bbox; avito/detail.py ingest-guard EKB→oblast66. - price_brackets.py: 5 city-seed (reuse EKB). migration 179: 15 rows enabled=false, staggered, idempotent. - +6 тестов. domclick (B2) не тронут.
51 lines
3 KiB
Python
51 lines
3 KiB
Python
"""Геометрические хелперы scraper_kit — чистая математика, без `app.*` / БД.
|
||
|
||
Strangler-копия (#2133) bbox-констант/предикатов из `app.services.geocoder`,
|
||
используемых скрапперами как ingest-guard (валидация координат detail-страниц
|
||
перед записью). Здесь — только чистые функции; продуктовый geocoder (Nominatim /
|
||
Yandex запросы, БД) НЕ копируется.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
# ЕКБ bbox = (lat_min, lat_max, lon_min, lon_max).
|
||
# TIGHT — центр агломерации; WIDE — с приграничьем, но всё ещё отсекает
|
||
# Питер/Тюмень/Уфу (#1871). WIDE строго содержит TIGHT.
|
||
EKB_BBOX_TIGHT = (56.65, 56.95, 60.40, 60.85)
|
||
EKB_BBOX_WIDE = (56.6, 57.1, 60.3, 60.9)
|
||
|
||
# Свердловская область целиком (region 66) — щедрый bbox под oblast-wide city-sweep
|
||
# (Н.Тагил/Каменск-Уральский/Первоуральск/В.Пышма/Серов, B1 rollout). Зеркалит
|
||
# `app.services.geocoder.OBLAST66_BBOX` дословно (та же (lat_min, lat_max, lon_min,
|
||
# lon_max)) — держим EKB_BBOX_TIGHT/WIDE отдельно для EKB-only fast-path (не заменяем).
|
||
OBLAST66_BBOX = (55.8, 62.2, 56.7, 66.6)
|
||
|
||
|
||
def is_within_ekb_bbox(
|
||
lat: float, lon: float, bbox: tuple[float, float, float, float] = EKB_BBOX_TIGHT
|
||
) -> bool:
|
||
"""True если (lat, lon) внутри bbox (inclusive). bbox = (lat_min, lat_max, lon_min, lon_max)."""
|
||
lat_min, lat_max, lon_min, lon_max = bbox
|
||
return lat_min <= lat <= lat_max and lon_min <= lon <= lon_max
|
||
|
||
|
||
def is_within_ekb_bbox_wide(lat: float, lon: float) -> bool:
|
||
"""Ingest-guard: True если координаты в широком ЕКБ-bbox (#1871).
|
||
|
||
Используется для валидации координат из avito detail-страниц перед записью в БД.
|
||
Шире geocoder-tight, поэтому не режет легитимное приграничье, но отсекает не-ЕКБ
|
||
(Питер/Тюмень/Уфа). EKB-only fast-path — для oblast-wide sweep см.
|
||
`is_within_oblast66_bbox`.
|
||
"""
|
||
return is_within_ekb_bbox(lat, lon, EKB_BBOX_WIDE)
|
||
|
||
|
||
def is_within_oblast66_bbox(lat: float, lon: float) -> bool:
|
||
"""Ingest-guard: True если координаты внутри bbox всей Свердловской области (region 66).
|
||
|
||
Oblast-wide аналог `is_within_ekb_bbox_wide` (B1 rollout — city-sweep за пределами ЕКБ:
|
||
Н.Тагил/Каменск-Уральский/Первоуральск/В.Пышма/Серов). Мирроит
|
||
`app.services.geocoder.is_within_oblast66_bbox` — щедрый bbox, всё ещё отсекает
|
||
кросс-региональные дубли (Тюмень/Курган и т.п. вне OBLAST66_BBOX).
|
||
"""
|
||
return is_within_ekb_bbox(lat, lon, OBLAST66_BBOX)
|