- NSPD-skraper переехал в backend/app/services/scrapers/nspd_kn.py + Celery task scrape_nspd_region (beat: 20-е февраля/мая/авг/нояб). Redis lock 3h, WAF auto-retry, heartbeat в nspd_scrape_runs. - Recommend_mix Tier 3: per-bucket elasticity через регрессию по «доминирующему bucket» каждого ЖК. Weighted-elasticity для inverse-mode. UI показывает разброс эластичностей и переключение regression/fallback. - Cadastre vs market cross-check: spatial-join cad_buildings → ekb_districts_geom; cadastre_vs_market_pct в scope, аномалии (>+50% / <-30%) подсвечены в UI. - Sentry release tracking (#4): IMAGE_TAG → backend/.env.runtime → sentry_sdk.init(release=...). Compose v2 env_file optional path. Schemas: 63_schema_nspd_runs.sql (cad_buildings + nspd_scrape_runs/log формализуют то, что уже жило в проде через 61_import_nspd_batch.py), 64_v_zk_rosreestr_velocity.sql (refresh с cad_buildings).
94 lines
4.7 KiB
SQL
94 lines
4.7 KiB
SQL
-- NSPD scrape industrialization: schema baseline.
|
||
--
|
||
-- 1. cad_buildings — формальная DDL для таблицы, которая создавалась
|
||
-- напрямую в проде через 61_import_nspd_batch.py (10590 зданий ЕКБ
|
||
-- на момент 2026-04-30). IF NOT EXISTS — миграция идемпотентна.
|
||
-- 2. nspd_scrape_runs — журнал автоматизированных Celery-запусков
|
||
-- (по образцу kn_scrape_runs из 50_schema_kn_extensions.sql).
|
||
-- 3. nspd_scrape_log — построчный лог стадий (по образцу kn_scrape_log).
|
||
|
||
CREATE EXTENSION IF NOT EXISTS postgis;
|
||
|
||
-- ── 1. cad_buildings ─────────────────────────────────────────────────────────
|
||
CREATE TABLE IF NOT EXISTS cad_buildings (
|
||
cad_num TEXT PRIMARY KEY,
|
||
quarter_cad_num TEXT REFERENCES cad_quarters_geom(cad_number) ON DELETE SET NULL,
|
||
geom geometry(Geometry, 4326),
|
||
purpose TEXT,
|
||
building_name TEXT,
|
||
readable_address TEXT,
|
||
area NUMERIC,
|
||
floors INT,
|
||
year_built INT,
|
||
year_commisioning INT,
|
||
cost_value NUMERIC,
|
||
registration_date DATE,
|
||
status TEXT,
|
||
ownership_type TEXT,
|
||
cultural_heritage TEXT,
|
||
underground_floors INT,
|
||
build_record_area NUMERIC,
|
||
build_record_type TEXT,
|
||
common_data_status TEXT,
|
||
obj_type TEXT,
|
||
raw_props jsonb,
|
||
fetched_at timestamptz NOT NULL DEFAULT NOW()
|
||
);
|
||
|
||
CREATE INDEX IF NOT EXISTS cad_buildings_geom_gist
|
||
ON cad_buildings USING GIST (geom);
|
||
CREATE INDEX IF NOT EXISTS cad_buildings_quarter_idx
|
||
ON cad_buildings(quarter_cad_num);
|
||
CREATE INDEX IF NOT EXISTS cad_buildings_purpose_idx
|
||
ON cad_buildings(purpose);
|
||
|
||
COMMENT ON TABLE cad_buildings IS
|
||
'Здания (НСПД thematicSearchId=1) внутри cad_quarters_geom. '
|
||
'Источник cost_value для cross-check с sale_graph.';
|
||
|
||
-- ── 2. nspd_scrape_runs ──────────────────────────────────────────────────────
|
||
CREATE TABLE IF NOT EXISTS nspd_scrape_runs (
|
||
run_id BIGSERIAL PRIMARY KEY,
|
||
started_at timestamptz NOT NULL DEFAULT NOW(),
|
||
finished_at timestamptz,
|
||
heartbeat_at timestamptz,
|
||
region_code INT NOT NULL,
|
||
pending_count INT,
|
||
quarters_ok INT NOT NULL DEFAULT 0,
|
||
quarters_failed INT NOT NULL DEFAULT 0,
|
||
buildings_ok INT NOT NULL DEFAULT 0,
|
||
requests_count INT NOT NULL DEFAULT 0,
|
||
waf_429_count INT NOT NULL DEFAULT 0,
|
||
status TEXT NOT NULL DEFAULT 'running', -- running | done | failed | zombie | skipped
|
||
error TEXT,
|
||
triggered_by TEXT NOT NULL DEFAULT 'beat' -- beat | manual | resume
|
||
);
|
||
|
||
CREATE INDEX IF NOT EXISTS nspd_runs_status_idx
|
||
ON nspd_scrape_runs(status, started_at DESC);
|
||
CREATE INDEX IF NOT EXISTS nspd_runs_region_idx
|
||
ON nspd_scrape_runs(region_code, started_at DESC);
|
||
|
||
COMMENT ON TABLE nspd_scrape_runs IS
|
||
'Журнал NSPD-скрейпов кадастровых кварталов и зданий. Один прогон на регион. '
|
||
'Beat-расписание: 20-е число февраля/мая/августа/ноября (после кв. публикаций rosreestr).';
|
||
|
||
-- ── 3. nspd_scrape_log ───────────────────────────────────────────────────────
|
||
CREATE TABLE IF NOT EXISTS nspd_scrape_log (
|
||
log_id BIGSERIAL PRIMARY KEY,
|
||
run_id BIGINT REFERENCES nspd_scrape_runs(run_id) ON DELETE CASCADE,
|
||
ts timestamptz NOT NULL DEFAULT NOW(),
|
||
level TEXT NOT NULL DEFAULT 'info', -- debug | info | warn | error
|
||
stage TEXT, -- task_received | discover | quarter_fetch | buildings | commit | done | error
|
||
cad_number TEXT,
|
||
message TEXT
|
||
);
|
||
|
||
CREATE INDEX IF NOT EXISTS nspd_log_run_idx
|
||
ON nspd_scrape_log(run_id, ts DESC);
|
||
CREATE INDEX IF NOT EXISTS nspd_log_level_idx
|
||
ON nspd_scrape_log(level, ts DESC) WHERE level IN ('warn', 'error');
|
||
|
||
COMMENT ON TABLE nspd_scrape_log IS
|
||
'Построчный лог NSPD-скрейпа: какие cad-кварталы успешны/упали, '
|
||
'WAF-403 ретраи, фазы commit. Используется UI /scrape/nspd для мониторинга.';
|