gendesign/data/sql/180_connection_capacity.sql
bot-backend 4758ea5e2b
All checks were successful
Deploy / changes (push) Successful in 8s
Deploy / build-backend (push) Successful in 2m16s
Deploy / build-worker (push) Successful in 3m27s
Deploy / build-frontend (push) Successful in 3m47s
Deploy / deploy (push) Successful in 1m30s
feat(site-finder): резервы мощности для ТП — электро+вода на карте §3 (#2119 Фаза A) (#2120)
2026-07-02 08:36:51 +00:00

143 lines
9.6 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- 180_connection_capacity.sql
-- Forgejo #2119 Phase A — «точки подключения СО СВОБОДНОЙ МОЩНОСТЬЮ» для ПТИЦА §3.
--
-- Три таблицы верифицированных источников свободной мощности (электро + вода):
-- power_supply_centers — центры питания (ЦП: ПС 35/110 кВ) из Россети WFS +
-- резервы свободной мощности по ЦП (rosseti-ural.ru xlsx).
-- power_tp_rp_reserves — резервы ТП/РП напряжением ниже 35 кВ (10k+ строк,
-- в основном БЕЗ координат — Phase B геокод).
-- water_supply_reserves — резервы ЦСВ/ЦСК Водоканала ЕКБ (docx, city-level).
--
-- Источники гео-блокируют non-RU IP → загрузчики РАБОТАЮТ НА ПРОДЕ (Celery/manual
-- docker exec). Код не должен предполагать локальный сетевой доступ.
--
-- Refresh: weekly via tasks.connection_capacity_sync (WFS + reserves + water).
-- Порядок применения: миграция ПЕРЕД деплоем кода (schema-first, sql.md).
BEGIN;
-- ── power_supply_centers ─────────────────────────────────────────────────────
-- ЦП = центр питания (ПС 35/110 кВ). geom из Россети WFS (координаты округлены
-- ~100 м — для §3 «есть ли точка рядом» этого достаточно). Резервы свободной
-- мощности (installed/current/reserve) допихиваются из xlsx-раскрытия по
-- sc_name_norm-матчу. sc_name_norm — нормализованное имя для сопоставления
-- (lower, «ПС»/voltage-префикс срезан, ё→е, тире/пробелы схлопнуты).
CREATE TABLE IF NOT EXISTS power_supply_centers (
id SERIAL PRIMARY KEY,
source TEXT NOT NULL, -- 'rosseti_wfs'
external_id TEXT, -- feature id или stable hash
sc_name TEXT NOT NULL, -- исходное имя ЦП
sc_name_norm TEXT NOT NULL, -- нормализованное для матча
dzo_name TEXT, -- ДЗО (напр. «Россети Урал»)
org_name TEXT,
branch_name TEXT, -- филиал (br_name)
voltage_class TEXT, -- напр. '110/35/10'
load_index TEXT, -- 'open'|'limited'|'closed'|NULL
installed_capacity_mva NUMERIC, -- установленная, МВА
current_load_mva NUMERIC, -- текущая нагрузка, МВА
reserve_mva NUMERIC, -- свободная мощность, МВА
reserve_asof DATE, -- дата актуальности резерва
capacity_source TEXT, -- 'cp_35_110'|'tp_rp_sub35'|NULL
municipality TEXT,
place_city TEXT, -- sc_placecity
geom GEOMETRY(POINT, 4326),
raw JSONB, -- сырая WFS-фича целиком
fetched_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE (source, external_id)
);
CREATE INDEX IF NOT EXISTS gist_power_supply_centers_geom
ON power_supply_centers USING GIST (geom);
CREATE INDEX IF NOT EXISTS idx_power_supply_centers_name_norm
ON power_supply_centers (sc_name_norm);
COMMENT ON TABLE power_supply_centers IS
'Центры питания (ЦП, ПС 35/110 кВ) Россети + резервы свободной мощности по ЦП. '
'Source: Россети WFS (geom) + rosseti-ural.ru xlsx (reserve). '
'Refresh: weekly via tasks.connection_capacity_sync. Forgejo #2119 Phase A.';
COMMENT ON COLUMN power_supply_centers.sc_name_norm IS
'Нормализованное имя ЦП для матча с xlsx-резервом (lower, «ПС»/voltage-префикс '
'срезан, ё→е, тире/пробелы схлопнуты).';
COMMENT ON COLUMN power_supply_centers.geom IS
'Координаты Россети WFS округлены ~100 м — для §3 «есть ли ЦП рядом» достаточно.';
-- ── power_tp_rp_reserves ─────────────────────────────────────────────────────
-- Резервы ТП/РП напряжением ниже 35 кВ (10 741 строк в верифицированном xlsx).
-- В основном БЕЗ координат — geocode_status='pending', гео придёт в Phase B.
-- UNIQUE(name_norm, municipality, voltage_kv) — одно и то же имя ТП может
-- встречаться в разных МО / на разном напряжении.
CREATE TABLE IF NOT EXISTS power_tp_rp_reserves (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL, -- напр. «ТП-721»
name_norm TEXT NOT NULL, -- нормализованное для матча
res_unit TEXT, -- РЭС
municipality TEXT,
settlement TEXT,
voltage_kv TEXT, -- класс напряжения, кВ
installed_capacity_mva NUMERIC, -- установленная, МВА
current_load_mva NUMERIC, -- текущая нагрузка, МВА
reserve_mva NUMERIC, -- свободная мощность, МВА
reserve_asof DATE,
geom GEOMETRY(POINT, 4326), -- NULL до Phase B геокода
geocode_status TEXT DEFAULT 'pending',
raw JSONB,
fetched_at TIMESTAMPTZ DEFAULT NOW(),
-- NULLS NOT DISTINCT: municipality/voltage_kv бывают NULL (пустые ячейки xlsx);
-- без этого ON CONFLICT не матчится и weekly-прогон плодит дубли
-- (те же грабли, что 140_domrf_documents_unique_nulls_not_distinct.sql).
UNIQUE NULLS NOT DISTINCT (name_norm, municipality, voltage_kv)
);
CREATE INDEX IF NOT EXISTS gist_power_tp_rp_reserves_geom
ON power_tp_rp_reserves USING GIST (geom);
CREATE INDEX IF NOT EXISTS idx_power_tp_rp_reserves_name_norm
ON power_tp_rp_reserves (name_norm);
CREATE INDEX IF NOT EXISTS idx_power_tp_rp_reserves_geocode_status
ON power_tp_rp_reserves (geocode_status);
COMMENT ON TABLE power_tp_rp_reserves IS
'Резервы свободной мощности ТП/РП <35 кВ (rosseti-ural.ru xlsx, ~10.7k строк). '
'geom NULL до Phase B геокода (geocode_status). Forgejo #2119 Phase A.';
-- ── water_supply_reserves ────────────────────────────────────────────────────
-- Резервы ЦСВ (центр. система водоснабжения) / ЦСК (водоотведения) Водоканала
-- ЕКБ (docx-раскрытие). Городской уровень (без geom) — фронт показывает ЦСВ
-- «ЗФС, ГСВ» как атрибут города. reserve может быть ОТРИЦАТЕЛЬНЫМ (дефицит) —
-- знак сохраняем. UNIQUE(system_kind, system_name, period).
CREATE TABLE IF NOT EXISTS water_supply_reserves (
id SERIAL PRIMARY KEY,
system_kind TEXT NOT NULL, -- 'water'|'sewerage'
system_name TEXT NOT NULL, -- ЦСВ/ЦСК, напр. «ЗФС, ГСВ»
reserve_thousand_m3_day NUMERIC, -- резерв, тыс. м³/сут (может быть <0)
connections_requested INT, -- заявок на подключение
connections_refused INT, -- отказов
note TEXT,
period TEXT, -- напр. '2026-Q1'
source_url TEXT,
fetched_at TIMESTAMPTZ DEFAULT NOW(),
-- NULLS NOT DISTINCT: period может быть NULL при неудачном infer_period.
UNIQUE NULLS NOT DISTINCT (system_kind, system_name, period)
);
CREATE INDEX IF NOT EXISTS idx_water_supply_reserves_period
ON water_supply_reserves (period);
CREATE INDEX IF NOT EXISTS idx_water_supply_reserves_kind
ON water_supply_reserves (system_kind);
COMMENT ON TABLE water_supply_reserves IS
'Резервы ЦСВ/ЦСК Водоканала ЕКБ (docx-раскрытие, city-level, без geom). '
'reserve_thousand_m3_day может быть отрицательным (дефицит). Forgejo #2119 Phase A.';
COMMENT ON COLUMN water_supply_reserves.reserve_thousand_m3_day IS
'Свободная мощность ЦСВ/ЦСК, тыс. м³/сут. Отрицательное значение = дефицит.';
COMMIT;