Objective API (api.objctv.ru) интегрирован как новый source of truth для
per-flat данных по новостройкам УрФО. Заменяет промежуточный Anton-SQLite
(legacy bootstrap-ETL остался как fallback в свёрнутом блоке UI).
Schema (data/sql/68_v2 — applied на проде, 6 таблиц):
- objective_lots (UPSERT по lot_id; 303 677 rows)
- objective_corpus_room_month (long-формат месяц×корпус×room_bucket; 19 738 rows)
- objective_lots_history (append-only weekly snapshots для elasticity)
- objective_complex_mapping (Objective ComplexName ↔ domrf_kn_objects.obj_id)
- objective_raw_reports (jsonb страховка на смену схемы API)
- objective_scrape_runs (журнал прогонов)
+ data/sql/72_objective_sync_config (single-row динамический конфиг)
Backend:
- services/scrapers/objective.py: ObjectiveClient — Bearer-токен (Redis +
in-memory fallback), retry на 401/429/5xx, Retry-After header support
- services/objective_etl.py: ETL SQLite Антона → PG (legacy)
- services/objective_sync_config.py: read/update single-row config
- workers/tasks/scrape_objective.py:
* sync_objective_group: 2 рабочих отчёта (corp_sum, lots_pf), inline-парсинг
* sync_all_groups: wrapper, перебирает группы из БД-конфига с
inter-group паузой; PATCH-merge explicit args > DB config
- workers/tasks/objective_etl.py: Celery task для legacy bootstrap
- workers/celery_app.py: beat читает cron из БД при старте (fallback на env)
- api/v1/admin_scrape.py: 5 новых endpoints для /objective/*
Frontend (frontend/src/app/admin/scrape/objective/page.tsx):
- PRIMARY blue блок «🌐 Наш sync» с input для override групп
- Collapsible «⚙️ Настройки» с формой (cron + 8 параметров) → PUT в БД
- Coverage-панель с PG counts + строка про SQLite Антона как legacy
- Collapsible «🛠 Bootstrap ETL» — legacy-инструмент
Beat schedule: вторник 06:00 МСК, ~10-15 мин на 4 группы (Свердл.обл +
Челябинск + Тюмень + Пермь = ~700K квартир УрФО). Расписание и параметры
меняются через админку без редеплоя (cron требует restart beat).
Эмпирические находки об API (probe 2026-05-10):
- 13 из 21 проверенных group_name доступны на тарифе (включая «Свердловская
область», «Челябинск», «Тюмень», «Пермь», но НЕ «Свердловская обл» —
формат имени строгий)
- ComplexName требует БЕЗ префикса «ЖК» и БЕЗ кавычек
- Поле «Банк» для всех 303k = NULL (тариф не отдаёт), но «Тип обременения»
работает (36% строк = ипотека) → ipoteka_share возможен, банковская
атрибуция — нет
Docker:
- bind-mount /opt/gendesign/site-finder:/data/anton-sqlite:ro в worker
GitHub backlog: добавлены #22-25 (recommend_mix v3 — 4 сабтаска по
улучшению алгоритма на основе фидбэка про POI / границы районов /
конкурентов / окно данных / success-driven mix).
Knowledge graph (memory/memory-gendesign.jsonl): обновлены entities
Objective_Integration_May07_2026, Schema_Objective_v2_May07,
Objective_API_Findings_May07, Module_Objective_Client + новый
Session_End_May07_2026.
TODO для прода: прописать OBJECTIVE_API_KEY=<key> в backend/.env +
docker compose restart worker beat.
46 lines
3.4 KiB
SQL
46 lines
3.4 KiB
SQL
-- Динамическая конфигурация Objective sync — single-row settings.
|
||
--
|
||
-- Зачем нужна:
|
||
-- 1. В админке /admin/scrape/objective можно менять параметры запроса
|
||
-- (groups, use_ddu/dkp, period_months_back, inter_group_delay) без редеплоя.
|
||
-- 2. Cron-расписание тоже хранится здесь — Celery beat читает его при старте.
|
||
-- Изменение требует restart beat-контейнера (это указано в UI как hint).
|
||
--
|
||
-- Pattern: single-row (id=1), UPSERT для безопасных обновлений.
|
||
-- Если строки нет — task/beat падают на settings.* env как fallback.
|
||
|
||
CREATE TABLE IF NOT EXISTS objective_sync_config (
|
||
id SMALLINT PRIMARY KEY DEFAULT 1 CHECK (id = 1),
|
||
-- Cron-расписание для Celery beat (5-полевой формат: 'minute hour dom month dow').
|
||
-- Celery beat перечитывает это поле только при старте — после изменения
|
||
-- нужен `docker compose restart beat`.
|
||
cron_schedule TEXT NOT NULL DEFAULT '0 6 * * tue',
|
||
-- Группы для sync (csv). Эмпирически проверены на тарифе:
|
||
-- 'Свердловская область', 'Челябинск', 'Тюмень', 'Пермь',
|
||
-- 'Екатеринбург', 'Верхняя Пышма', 'Берёзовский', 'Нижний Тагил',
|
||
-- 'Каменск-Уральский', 'Первоуральск', 'Новоуральск', 'Сысерть'.
|
||
groups_csv TEXT NOT NULL DEFAULT 'Свердловская область,Челябинск,Тюмень,Пермь',
|
||
-- Параметры запроса /v2/Report/GetReport.
|
||
use_ddu BOOLEAN NOT NULL DEFAULT TRUE,
|
||
use_dkp BOOLEAN NOT NULL DEFAULT TRUE,
|
||
-- Сколько месяцев назад тянуть для Сводных отчётов (corp_sum).
|
||
-- 1 = только последний полный месяц (default). 12 = весь год.
|
||
-- Поквартирные/Лоты не имеют дат — всегда snapshot.
|
||
period_months_back SMALLINT NOT NULL DEFAULT 1 CHECK (period_months_back BETWEEN 1 AND 24),
|
||
-- Пауза между группами в одном task'е (секунды). Защита от 429.
|
||
inter_group_delay_s SMALLINT NOT NULL DEFAULT 30 CHECK (inter_group_delay_s BETWEEN 0 AND 300),
|
||
-- Пауза между HTTP-запросами в client (мс). Передаётся в ObjectiveClient(rate_ms=…).
|
||
rate_ms SMALLINT NOT NULL DEFAULT 3000 CHECK (rate_ms BETWEEN 0 AND 10000),
|
||
-- Сколько ретраев на 429/5xx.
|
||
retries SMALLINT NOT NULL DEFAULT 2 CHECK (retries BETWEEN 0 AND 5),
|
||
updated_at timestamptz NOT NULL DEFAULT NOW(),
|
||
updated_by TEXT
|
||
);
|
||
|
||
COMMENT ON TABLE objective_sync_config IS
|
||
'Single-row динамическая конфигурация Objective sync. Все task-ы и beat '
|
||
'читают отсюда. Если строки нет — fallback на settings.* env.';
|
||
|
||
-- Засеваем единственную строку с дефолтами (если ещё не создана).
|
||
INSERT INTO objective_sync_config (id) VALUES (1)
|
||
ON CONFLICT (id) DO NOTHING;
|