129 lines
8.7 KiB
Python
129 lines
8.7 KiB
Python
"""Классификация «сетевых обременений» из ЗОУИТ (#1070).
|
||
|
||
Pure-function классификатор: по тексту ``type_zone`` ЗОУИТ (cad_zouit / НСПД-дамп)
|
||
определяет, является ли зона *охранной зоной инженерной сети* — и если да, к какому
|
||
виду сети относится (тепло/электро/газ/водоснабжение/связь/общая инженерная).
|
||
|
||
Контекст (#1070 ООО «Брик»): теплосеть 66:41:0000000:90310 проходит через пятно
|
||
застройки → реальное обременение. Эпик #1067 (слой ИРД для analyze) НЕ содержал явного
|
||
среза «сетевые обременения». ЗОУИТ-данные cad_zouit уже текут в analyze
|
||
(``quarter_dump_lookup._get_cad_zouit_overlaps``, #232/#243) и классифицируются в
|
||
gate_verdict, но как ОБЩИЕ ЗОУИТ-overlaps без выделенного «сетевого» среза.
|
||
|
||
Этот модуль выделяет сетевые охранные зоны в отдельную типизированную категорию, чтобы
|
||
analyze/фронт могли показать «сетевое обременение (теплосеть)» как самостоятельный флаг.
|
||
|
||
VERIFY против PROD (cad_zouit, 2026-06-14): сетевые охранные зоны РЕАЛЬНО присутствуют —
|
||
~1930 строк из 3485:
|
||
• «Охранная зона инженерных коммуникаций» — 1281
|
||
• «Охранная зона тепловых сетей» — 548
|
||
• «Охранная зона объектов электроэнергетики …» — 69
|
||
• «Охранная зона трубопроводов (газопроводов …)» — 23
|
||
• «Охранная зона линий и сооружений связи» — 9
|
||
|
||
Зачем pure-function (без БД): тестируемо без фикстур, переиспользуемо и в
|
||
``_get_cad_zouit_overlaps`` (теггинг overlap-строк), и в ``gate_verdict`` (выделенный
|
||
warning-код), без дублирования keyword-таблиц.
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from typing import Literal
|
||
|
||
# Виды сетевого обременения. 'general' — «инженерные коммуникации» без указания вида сети.
|
||
NetworkKind = Literal[
|
||
"thermal", # теплоснабжение
|
||
"electricity", # электросети / объекты электроэнергетики
|
||
"gas", # газопроводы (входит в «трубопроводы …»)
|
||
"water", # водоснабжение / водоотведение / канализация
|
||
"communication", # линии и сооружения связи
|
||
"pipeline", # трубопроводы (нефте-/нефтепродукто-/аммиако- — не газ, но сетевое)
|
||
"general", # «охранная зона инженерных коммуникаций» (вид сети не уточнён)
|
||
]
|
||
|
||
# Человекочитаемые подписи (RU) для UI/детали обременения.
|
||
NETWORK_KIND_LABELS: dict[str, str] = {
|
||
"thermal": "теплоснабжение",
|
||
"electricity": "электроснабжение",
|
||
"gas": "газоснабжение",
|
||
"water": "водоснабжение/водоотведение",
|
||
"communication": "связь",
|
||
"pipeline": "трубопровод",
|
||
"general": "инженерные коммуникации",
|
||
}
|
||
|
||
# Упорядоченные правила «подстрока type_zone → вид сети». Порядок ВАЖЕН: более
|
||
# специфичные виды проверяются раньше «general» (инженерные коммуникации).
|
||
# Все подстроки lowercase; матч по `substring in type_zone.lower()`.
|
||
# Сверено с distinct type_zone из cad_zouit PROD (2026-06-14).
|
||
_NETWORK_RULES: tuple[tuple[NetworkKind, tuple[str, ...]], ...] = (
|
||
("thermal", ("тепл",)), # «… тепловых сетей»
|
||
("electricity", ("электроэнерг", "электросет", "лэп", "линий электропередач")),
|
||
("gas", ("газопров", "газоснаб")),
|
||
("water", ("водопров", "водоснаб", "водоотвед", "канализ")),
|
||
("communication", ("линий и сооружений связи", "сооружений связи", "линий связи")),
|
||
# «Охранная зона трубопроводов (газопроводов, нефтепроводов …)» — если газ не
|
||
# сматчился выше, относим к pipeline (нефте-/нефтепродукто-/аммиако-проводы).
|
||
("pipeline", ("трубопров", "нефтепров", "нефтепродуктопров", "аммиакопров")),
|
||
# Самое общее — «охранная зона инженерных коммуникаций» (вид сети не уточнён).
|
||
("general", ("инженерных коммуникаций", "инженерной инфраструктур", "инженерных сет")),
|
||
)
|
||
|
||
# Зона должна быть именно ОХРАННОЙ (обременение), а не, например, «зона публичного
|
||
# сервитута» без сетевой привязки. Требуем маркер охранной/защитной зоны ИЛИ явный
|
||
# сетевой вид в тексте — иначе «инженерные коммуникации» в произвольной зоне дало бы
|
||
# ложные срабатывания. На практике cad_zouit использует «Охранная зона …».
|
||
_PROTECTION_MARKERS: tuple[str, ...] = ("охранная зона", "охранн")
|
||
|
||
|
||
def classify_network_zone(type_zone: str | None) -> NetworkKind | None:
|
||
"""Определить вид сетевого обременения по тексту ``type_zone`` ЗОУИТ.
|
||
|
||
Args:
|
||
type_zone: текст типа зоны (cad_zouit.type_zone / НСПД-дамп). None/пусто → None.
|
||
|
||
Returns:
|
||
``NetworkKind`` если зона — охранная зона инженерной сети; иначе ``None``
|
||
(зона не сетевая: СЗЗ, ЗРЗ, сервитут без сети, природные ЗОУИТ и т.п.).
|
||
"""
|
||
if not type_zone:
|
||
return None
|
||
tz = type_zone.lower()
|
||
is_protection = any(m in tz for m in _PROTECTION_MARKERS)
|
||
# ЗСО (зона санитарной охраны ИСТОЧНИКА водоснабжения) — это зона вокруг водозабора,
|
||
# НЕ охранная зона водопроводной СЕТИ → не сетевое обременение. Иначе water-needles
|
||
# («водоснаб») ложно отнесли бы ЗСО к сети и флипнули verdict на blocker.
|
||
is_sanitary_source = "санитарн" in tz
|
||
for kind, needles in _NETWORK_RULES:
|
||
if any(n in tz for n in needles):
|
||
# «general» (инженерные коммуникации) принимаем только при маркере охранной
|
||
# зоны — иначе слишком широкая ловля. Специфичные виды (тепло/газ/…) сами по
|
||
# себе достаточно сетевые, но в cad_zouit они тоже всегда «Охранная зона …».
|
||
if kind == "general" and not is_protection:
|
||
continue
|
||
# water-зона санитарной охраны источника — не сеть, пропускаем.
|
||
if kind == "water" and is_sanitary_source:
|
||
continue
|
||
return kind
|
||
return None
|
||
|
||
|
||
def network_kind_label(kind: NetworkKind | None) -> str | None:
|
||
"""RU-подпись вида сети для UI/детали (None → None)."""
|
||
if kind is None:
|
||
return None
|
||
return NETWORK_KIND_LABELS.get(kind, kind)
|
||
|
||
|
||
def is_network_obremenenie(type_zone: str | None) -> bool:
|
||
"""True если ЗОУИТ — сетевое обременение (любой вид сети)."""
|
||
return classify_network_zone(type_zone) is not None
|
||
|
||
|
||
__all__ = [
|
||
"NETWORK_KIND_LABELS",
|
||
"NetworkKind",
|
||
"classify_network_zone",
|
||
"is_network_obremenenie",
|
||
"network_kind_label",
|
||
]
|