- avito.py: fetch_around(pages=N, delay_override_sec) — pagination 1..N (backward compat default=1)
- scrape_pipeline.py:
* EKB_ANCHORS const (5 точек ЕКБ)
* run_avito_pipeline: pages + request_delay_sec params (backward compat defaults)
* CitySweepCounters dataclass с anchors_done/total + aggregate counters
* run_avito_city_sweep(db, run_id, *, pages_per_anchor, detail_top_n, request_delay_sec)
* Cooperative cancel: checks scrape_runs.status каждый anchor
* Per-anchor heartbeat + counters refresh
- scrape_runs.py NEW: utility module (create_run/update_heartbeat/mark_done/failed/cancelled/list_recent/is_cancelled)
- admin.py: 3 NEW endpoints:
* POST /admin/scrape/avito-city-sweep — start (returns run_id, FastAPI BackgroundTasks)
* GET /admin/scrape/avito-city-sweep/runs?limit=N — recent runs list
* POST /admin/scrape/avito-city-sweep/{run_id}/cancel — cooperative cancel
- 051_scrape_runs_extend.sql: ADD COLUMN params/counters/error/finished_at + cancelled в status enum
- avito-city-sweep.sh: cron wrapper с random 0-3h delay (anti-pattern-detection)
- tests/test_city_sweep.py: 16 offline smoke tests (counters, scrape_runs utils, endpoints)
Crontab entry: 0 2 * * * /opt/.../avito-city-sweep.sh (runs 02:00-05:00 UTC daily)
57 lines
3.1 KiB
PL/PgSQL
57 lines
3.1 KiB
PL/PgSQL
-- 051_scrape_runs_extend.sql
|
||
-- Расширение таблицы scrape_runs для поддержки city sweep pipeline:
|
||
-- - params jsonb — параметры запуска (radius_m, pages_per_anchor, …)
|
||
-- - counters jsonb — агрегированные счётчики (aggregate anchors_done, lots_fetched, …)
|
||
-- - error text — краткое сообщение об ошибке (alias для error_text)
|
||
-- - finished_at — время завершения run (done/failed/cancelled)
|
||
-- - 'cancelled' в status CHECK
|
||
--
|
||
-- Dependencies: 015_scrape_runs.sql
|
||
-- Идемпотентно: безопасно запускать повторно (ALTER ADD COLUMN IF NOT EXISTS).
|
||
|
||
BEGIN;
|
||
|
||
-- 1. Добавить params jsonb — конфигурация запуска
|
||
ALTER TABLE scrape_runs
|
||
ADD COLUMN IF NOT EXISTS params jsonb;
|
||
|
||
-- 2. Добавить counters jsonb — агрегированные счётчики (city sweep)
|
||
ALTER TABLE scrape_runs
|
||
ADD COLUMN IF NOT EXISTS counters jsonb;
|
||
|
||
-- 3. Добавить finished_at (было только в error_text / heartbeat_at)
|
||
ALTER TABLE scrape_runs
|
||
ADD COLUMN IF NOT EXISTS finished_at timestamptz;
|
||
|
||
-- 4. Добавить error text (alias для краткого сообщения, совместимо с scrape_runs utility module)
|
||
-- error_text уже существует (из 015) — добавляем error как синоним для новых runs.
|
||
-- Utility module пишет в 'error' — перенаправляем на error_text через generated column
|
||
-- НЕ работает в PostGIS/PG16 для text → используем просто второй столбец.
|
||
ALTER TABLE scrape_runs
|
||
ADD COLUMN IF NOT EXISTS error text;
|
||
|
||
-- 5. Расширить CHECK constraint status чтобы допускал 'cancelled'
|
||
-- DROP + ADD (нельзя ALTER CHECK в Postgres без пересоздания)
|
||
ALTER TABLE scrape_runs
|
||
DROP CONSTRAINT IF EXISTS scrape_runs_status_check;
|
||
|
||
ALTER TABLE scrape_runs
|
||
ADD CONSTRAINT scrape_runs_status_check
|
||
CHECK (status IN ('running', 'done', 'failed', 'zombie', 'skipped', 'banned', 'cancelled'));
|
||
|
||
-- 6. run_type имеет NOT NULL в 015 — для city sweep run_type = 'city_sweep'
|
||
-- Убедимся что INSERT в scrape_runs.py покрывает это через DEFAULT
|
||
ALTER TABLE scrape_runs
|
||
ALTER COLUMN run_type SET DEFAULT 'city_sweep';
|
||
|
||
COMMENT ON COLUMN scrape_runs.params IS
|
||
'JSON-параметры запуска pipeline (pages_per_anchor, radius_m, detail_top_n, …)';
|
||
COMMENT ON COLUMN scrape_runs.counters IS
|
||
'JSON-счётчики прогресса (anchors_done, lots_fetched, houses_enriched, …). '
|
||
'Обновляется heartbeat''ом каждый anchor.';
|
||
COMMENT ON COLUMN scrape_runs.finished_at IS
|
||
'Время завершения run (status=done/failed/cancelled).';
|
||
COMMENT ON COLUMN scrape_runs.error IS
|
||
'Краткое сообщение об ошибке (для status=failed). Первые 1000 символов.';
|
||
|
||
COMMIT;
|