Пользовательская половина разбора #2574: витрины читают listings без сегмента
и без свежести, поэтому показывают числа, посчитанные не по тому пулу.
1. Миграция 211 — гард #1186 в window_listings у street_sales_vs_listings().
27.3% кандидатов на пару «ДКП ↔ объявление» были новостройками, и
девелоперский прайс (который не торгуется) формировал показываемый процент
торга. is_active здесь по-прежнему НЕ фильтруется — осознанно: функция
намеренно смотрит и снятые объявления, иначе к сделке нечего подставить.
Сигнатура не меняется, значит CREATE OR REPLACE — замена, а не вторая
перегрузка (грабли #2627 закрыты тестом-сравнением сигнатур с м.205).
2. location_index — предикат свежести + сегментный гард в обоих запросах
медианы, симметрично _COMMON_WHERE эстиматора. Витрина обязана смотреть на
тот же пул, на котором считается цена; окно свежести берётся импортом
LISTINGS_FRESH_DAYS, второго определения константы не заводим.
3. /scraper/data-quality и /cache-stats — «активно» не прячем, а разделяем:
рядом отдаётся «из них не виделись N дней» (+ сам порог N в ответе).
Именно слепой count(*) WHERE is_active заставлял #2574 месяц выглядеть
как «всё собирается».
Refs #2660
- browser/server.py: GET /pacing и PUT /pacing — read/write _MIN_PAGE_INTERVAL_BY_PROVIDER
in-memory; валидация source in PROVIDERS, interval_s >= 0; логирует изменения; сбрасывается
к env-дефолту на рестарте by design
- backend/admin.py: GET /scraper/pacing — прокси к browser /pacing, PacingResponse pydantic;
PUT /scraper/pacing/{source} — прокси PUT с валидацией ge=0 le=120; 502/503 при недоступности
- backend/admin.py: GET /scraper/data-quality — single-pass FILTER-агрегаты по listings WHERE
is_active GROUP BY source (description/photo_urls/address/lat/lon/kitchen_area_m2/living_area_m2/
ceiling_height/ceiling_height_m/metro_stations); houses (total/avito_validated_at%/rating_score%/
house_type%); house_reviews count
- browser/test_server_pacing.py: 10 новых тестов GET+PUT /pacing (16 total, все зелёные)
- backend/tests/test_scraper_admin_apis.py: тесты pacing-прокси + data-quality shape/pct-range
(21 total, все зелёные)
- pyproject.toml: httpx[socks]>=0.27.0 — тянет socksio, socks5:// прокси в _probe_current_ip работает прозрачно
- admin.py list_scrape_runs_unified: status Literal["done","running","banned","zombie","failed","cancelled"] вместо str|None — невалидные значения → 422
- avito_houses.py save_house_catalog_enrichment: гард ext_id=0 + нет адреса → skip without DB touch; два id-less novostroyka URL не схлопываются в bogus avito:0 в house_sources
- тесты: test_unified_runs_invalid_status_422 + test_unified_runs_valid_statuses_200; test_save_house_catalog_enrichment_skips_zero_ext_id_no_address + *_with_address_persists