5 changed files with 319 additions and 6 deletions
|
|
@ -9,6 +9,11 @@ from __future__ import annotations
|
||||||
|
|
||||||
from typing import Literal, TypedDict
|
from typing import Literal, TypedDict
|
||||||
|
|
||||||
|
from app.services.site_finder.network_obremenenie import (
|
||||||
|
classify_network_zone,
|
||||||
|
network_kind_label,
|
||||||
|
)
|
||||||
|
|
||||||
# ── Zone matching ─────────────────────────────────────────────────────────────
|
# ── Zone matching ─────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
# Residential zone codes — точные ПЗЗ Свердловска (Ж-1..Ж-5)
|
# Residential zone codes — точные ПЗЗ Свердловска (Ж-1..Ж-5)
|
||||||
|
|
@ -155,7 +160,25 @@ def compute_gate_verdict(
|
||||||
# cad_zouit fallback path: classify by type_zone keywords (#232).
|
# cad_zouit fallback path: classify by type_zone keywords (#232).
|
||||||
# subcategory = NULL в cad_zouit, поэтому subcategory-based logic не применяется.
|
# subcategory = NULL в cad_zouit, поэтому subcategory-based logic не применяется.
|
||||||
type_zone_lower = (overlap.get("type_zone") or overlap.get("layer") or "").lower()
|
type_zone_lower = (overlap.get("type_zone") or overlap.get("layer") or "").lower()
|
||||||
if any(kw in type_zone_lower for kw in BLOCKER_TYPE_ZONE_KEYWORDS):
|
# #1070: «сетевое обременение» — охранная зона инж.сети — выделенный
|
||||||
|
# blocker-код с видом сети (тепло/электро/…) в детали. Классификация
|
||||||
|
# пришла из _get_cad_zouit_overlaps (is_network_zone/network_kind); если
|
||||||
|
# overlap пришёл из старого пути без флага — определяем здесь же.
|
||||||
|
net_kind = overlap.get("network_kind")
|
||||||
|
if net_kind is None and overlap.get("is_network_zone") is None:
|
||||||
|
net_kind = classify_network_zone(overlap.get("type_zone") or overlap.get("layer"))
|
||||||
|
if net_kind is not None:
|
||||||
|
kind_label = overlap.get("network_kind_label") or network_kind_label(net_kind)
|
||||||
|
blockers.append(
|
||||||
|
Blocker(
|
||||||
|
code="ZOUIT_NETWORK_OBREMENENIE",
|
||||||
|
detail=(
|
||||||
|
f"Сетевое обременение ({kind_label}): "
|
||||||
|
f"{overlap.get('type_zone', '')} {overlap.get('name', '')}".strip()
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
elif any(kw in type_zone_lower for kw in BLOCKER_TYPE_ZONE_KEYWORDS):
|
||||||
blockers.append(
|
blockers.append(
|
||||||
Blocker(
|
Blocker(
|
||||||
code="ZOUIT_CAD_BLOCKER",
|
code="ZOUIT_CAD_BLOCKER",
|
||||||
|
|
|
||||||
129
backend/app/services/site_finder/network_obremenenie.py
Normal file
129
backend/app/services/site_finder/network_obremenenie.py
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
"""Классификация «сетевых обременений» из ЗОУИТ (#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",
|
||||||
|
]
|
||||||
|
|
@ -23,6 +23,11 @@ from sqlalchemy import text
|
||||||
from sqlalchemy.exc import DataError, OperationalError, ProgrammingError
|
from sqlalchemy.exc import DataError, OperationalError, ProgrammingError
|
||||||
from sqlalchemy.orm import Session
|
from sqlalchemy.orm import Session
|
||||||
|
|
||||||
|
from app.services.site_finder.network_obremenenie import (
|
||||||
|
classify_network_zone,
|
||||||
|
network_kind_label,
|
||||||
|
)
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
# Порог свежести дампа — 180 дней. Совпадает с beat-расписанием (раз в квартал
|
# Порог свежести дампа — 180 дней. Совпадает с beat-расписанием (раз в квартал
|
||||||
|
|
@ -405,6 +410,10 @@ def _get_cad_zouit_overlaps(db: Session, parcel_wkt: str) -> list[dict[str, Any]
|
||||||
result: list[dict[str, Any]] = []
|
result: list[dict[str, Any]] = []
|
||||||
for r in rows:
|
for r in rows:
|
||||||
type_zone: str = r[0] or ""
|
type_zone: str = r[0] or ""
|
||||||
|
# #1070: выделяем «сетевые обременения» — охранные зоны инж.сетей
|
||||||
|
# (тепло/электро/газ/вода/связь/трубопровод/общие инж.коммуникации).
|
||||||
|
# Pure-function классификация по type_zone; None если зона не сетевая.
|
||||||
|
net_kind = classify_network_zone(type_zone)
|
||||||
result.append(
|
result.append(
|
||||||
{
|
{
|
||||||
"group_key": "cad_zouit",
|
"group_key": "cad_zouit",
|
||||||
|
|
@ -419,6 +428,12 @@ def _get_cad_zouit_overlaps(db: Session, parcel_wkt: str) -> list[dict[str, Any]
|
||||||
},
|
},
|
||||||
"source": "cad_zouit",
|
"source": "cad_zouit",
|
||||||
"type_zone": type_zone,
|
"type_zone": type_zone,
|
||||||
|
# #1070 «сетевые обременения»: флаг + вид сети + RU-подпись.
|
||||||
|
# is_network_zone=True → охранная зона инж.сети (обременение);
|
||||||
|
# network_kind ∈ NetworkKind | None; network_kind_label — RU.
|
||||||
|
"is_network_zone": net_kind is not None,
|
||||||
|
"network_kind": net_kind,
|
||||||
|
"network_kind_label": network_kind_label(net_kind),
|
||||||
# #255: geom для ZouitLayer на Leaflet (frontend JSON.parse → <GeoJSON>).
|
# #255: geom для ZouitLayer на Leaflet (frontend JSON.parse → <GeoJSON>).
|
||||||
# geom — GEOGRAPHY, ST_AsGeoJSON требует geometry → CAST(... AS geometry).
|
# geom — GEOGRAPHY, ST_AsGeoJSON требует geometry → CAST(... AS geometry).
|
||||||
"geom_geojson": r[5],
|
"geom_geojson": r[5],
|
||||||
|
|
|
||||||
|
|
@ -124,12 +124,17 @@ def test_pzz_unknown_warning_when_no_zoning():
|
||||||
|
|
||||||
|
|
||||||
def test_cad_zouit_ohranaya_zona_truboprovodov_blocks():
|
def test_cad_zouit_ohranaya_zona_truboprovodov_blocks():
|
||||||
"""cad_zouit overlap с 'охранная зона трубопроводов' → ZOUIT_CAD_BLOCKER → Нельзя."""
|
"""cad_zouit 'охранная зона трубопроводов' → сетевое обременение (газ) → Нельзя (#1070).
|
||||||
|
|
||||||
|
После #1070 трубопроводы/газ классифицируются как ZOUIT_NETWORK_OBREMENENIE
|
||||||
|
(более специфичный код, чем прежний ZOUIT_CAD_BLOCKER) — поведение «блокирует МКД»
|
||||||
|
сохранено.
|
||||||
|
"""
|
||||||
nspd_zoning = {"zone_code": "Ж-2", "zone_name": "Жилая"}
|
nspd_zoning = {"zone_code": "Ж-2", "zone_name": "Жилая"}
|
||||||
overlaps = [
|
overlaps = [
|
||||||
{
|
{
|
||||||
"source": "cad_zouit",
|
"source": "cad_zouit",
|
||||||
"type_zone": "Охранная зона трубопроводов",
|
"type_zone": "Охранная зона трубопроводов (газопроводов, нефтепроводов)",
|
||||||
"layer": "Охранная зона трубопроводов",
|
"layer": "Охранная зона трубопроводов",
|
||||||
"name": "Газопровод высокого давления",
|
"name": "Газопровод высокого давления",
|
||||||
}
|
}
|
||||||
|
|
@ -137,11 +142,11 @@ def test_cad_zouit_ohranaya_zona_truboprovodov_blocks():
|
||||||
verdict = compute_gate_verdict(nspd_zoning, overlaps, [], {"available": True})
|
verdict = compute_gate_verdict(nspd_zoning, overlaps, [], {"available": True})
|
||||||
assert verdict["can_build_mkd"] is False
|
assert verdict["can_build_mkd"] is False
|
||||||
assert verdict["verdict_label"] == "Нельзя"
|
assert verdict["verdict_label"] == "Нельзя"
|
||||||
assert any(b["code"] == "ZOUIT_CAD_BLOCKER" for b in verdict["blockers"])
|
assert any(b["code"] == "ZOUIT_NETWORK_OBREMENENIE" for b in verdict["blockers"])
|
||||||
|
|
||||||
|
|
||||||
def test_cad_zouit_elektr_blocks():
|
def test_cad_zouit_elektr_blocks():
|
||||||
"""cad_zouit overlap с 'электр' substring в type_zone → blocker."""
|
"""cad_zouit электросетевая охранная зона → сетевое обременение (электро) → blocker (#1070)."""
|
||||||
nspd_zoning = {"zone_code": "Ж-3", "zone_name": "Жилая"}
|
nspd_zoning = {"zone_code": "Ж-3", "zone_name": "Жилая"}
|
||||||
overlaps = [
|
overlaps = [
|
||||||
{
|
{
|
||||||
|
|
@ -153,7 +158,7 @@ def test_cad_zouit_elektr_blocks():
|
||||||
]
|
]
|
||||||
verdict = compute_gate_verdict(nspd_zoning, overlaps, [], {"available": True})
|
verdict = compute_gate_verdict(nspd_zoning, overlaps, [], {"available": True})
|
||||||
assert verdict["can_build_mkd"] is False
|
assert verdict["can_build_mkd"] is False
|
||||||
assert any(b["code"] == "ZOUIT_CAD_BLOCKER" for b in verdict["blockers"])
|
assert any(b["code"] == "ZOUIT_NETWORK_OBREMENENIE" for b in verdict["blockers"])
|
||||||
|
|
||||||
|
|
||||||
def test_cad_zouit_szz_is_warning_not_blocker():
|
def test_cad_zouit_szz_is_warning_not_blocker():
|
||||||
|
|
|
||||||
141
backend/tests/test_network_obremenenie.py
Normal file
141
backend/tests/test_network_obremenenie.py
Normal file
|
|
@ -0,0 +1,141 @@
|
||||||
|
"""Tests for network-obremenenie классификатор (#1070) — pure function, no DB.
|
||||||
|
|
||||||
|
type_zone-строки взяты из реальных distinct-значений cad_zouit (PROD, 2026-06-14).
|
||||||
|
"""
|
||||||
|
|
||||||
|
from app.services.site_finder.gate_verdict import compute_gate_verdict
|
||||||
|
from app.services.site_finder.network_obremenenie import (
|
||||||
|
classify_network_zone,
|
||||||
|
is_network_obremenenie,
|
||||||
|
network_kind_label,
|
||||||
|
)
|
||||||
|
|
||||||
|
# ── classify_network_zone: реальные type_zone из cad_zouit ────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
def test_thermal_zone():
|
||||||
|
assert classify_network_zone("Охранная зона тепловых сетей") == "thermal"
|
||||||
|
|
||||||
|
|
||||||
|
def test_electricity_zone():
|
||||||
|
tz = (
|
||||||
|
"Охранная зона объектов электроэнергетики (объектов электросетевого хозяйства "
|
||||||
|
"и объектов по производству электрической энергии)"
|
||||||
|
)
|
||||||
|
assert classify_network_zone(tz) == "electricity"
|
||||||
|
|
||||||
|
|
||||||
|
def test_pipeline_zone_gas_first():
|
||||||
|
# «трубопроводов (газопроводов, нефтепроводов …)» — газ ловится раньше pipeline.
|
||||||
|
tz = (
|
||||||
|
"Охранная зона трубопроводов (газопроводов, нефтепроводов "
|
||||||
|
"и нефтепродуктопроводов, аммиакопроводов)"
|
||||||
|
)
|
||||||
|
assert classify_network_zone(tz) == "gas"
|
||||||
|
|
||||||
|
|
||||||
|
def test_communication_zone():
|
||||||
|
assert classify_network_zone("Охранная зона линий и сооружений связи") == "communication"
|
||||||
|
|
||||||
|
|
||||||
|
def test_general_engineering_zone():
|
||||||
|
assert classify_network_zone("Охранная зона инженерных коммуникаций") == "general"
|
||||||
|
|
||||||
|
|
||||||
|
# ── негативные: НЕ сетевые ЗОУИТ ──────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
def test_servitude_not_network():
|
||||||
|
assert classify_network_zone("Зона публичного сервитута") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_szz_not_network():
|
||||||
|
assert classify_network_zone("Санитарно-защитная зона") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_natural_zouit_not_network():
|
||||||
|
assert classify_network_zone("Зоны с особыми условиями использования территории") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_none_and_empty():
|
||||||
|
assert classify_network_zone(None) is None
|
||||||
|
assert classify_network_zone("") is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_general_keyword_without_protection_marker_is_skipped():
|
||||||
|
# «инженерные коммуникации» без «охранная зона» → не ловим (избегаем ложных).
|
||||||
|
assert classify_network_zone("Размещение инженерных коммуникаций") is None
|
||||||
|
|
||||||
|
|
||||||
|
# ── helpers ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
def test_is_network_obremenenie():
|
||||||
|
assert is_network_obremenenie("Охранная зона тепловых сетей") is True
|
||||||
|
assert is_network_obremenenie("Зона публичного сервитута") is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_network_kind_label():
|
||||||
|
assert network_kind_label("thermal") == "теплоснабжение"
|
||||||
|
assert network_kind_label("general") == "инженерные коммуникации"
|
||||||
|
assert network_kind_label(None) is None
|
||||||
|
|
||||||
|
|
||||||
|
# ── gate_verdict: сетевое обременение → выделенный blocker ─────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
def _dump_available() -> dict:
|
||||||
|
return {"available": True, "stale": False}
|
||||||
|
|
||||||
|
|
||||||
|
def test_gate_verdict_thermal_network_blocker():
|
||||||
|
"""Теплосеть (#1070 ООО Брик) → ZOUIT_NETWORK_OBREMENENIE blocker."""
|
||||||
|
overlaps = [
|
||||||
|
{
|
||||||
|
"source": "cad_zouit",
|
||||||
|
"type_zone": "Охранная зона тепловых сетей",
|
||||||
|
"name": "ТС-123",
|
||||||
|
"is_network_zone": True,
|
||||||
|
"network_kind": "thermal",
|
||||||
|
"network_kind_label": "теплоснабжение",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
verdict = compute_gate_verdict(None, overlaps, None, _dump_available())
|
||||||
|
codes = [b["code"] for b in verdict["blockers"]]
|
||||||
|
assert "ZOUIT_NETWORK_OBREMENENIE" in codes
|
||||||
|
assert verdict["can_build_mkd"] is False
|
||||||
|
detail = next(
|
||||||
|
b["detail"] for b in verdict["blockers"] if b["code"] == "ZOUIT_NETWORK_OBREMENENIE"
|
||||||
|
)
|
||||||
|
assert "теплоснабжение" in detail
|
||||||
|
|
||||||
|
|
||||||
|
def test_gate_verdict_servitude_not_network_blocker():
|
||||||
|
"""Сервитут (не сеть) → НЕ ZOUIT_NETWORK_OBREMENENIE."""
|
||||||
|
overlaps = [
|
||||||
|
{
|
||||||
|
"source": "cad_zouit",
|
||||||
|
"type_zone": "Зона публичного сервитута",
|
||||||
|
"name": "",
|
||||||
|
"is_network_zone": False,
|
||||||
|
"network_kind": None,
|
||||||
|
"network_kind_label": None,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
verdict = compute_gate_verdict(None, overlaps, None, _dump_available())
|
||||||
|
codes = [b["code"] for b in verdict["blockers"]]
|
||||||
|
assert "ZOUIT_NETWORK_OBREMENENIE" not in codes
|
||||||
|
|
||||||
|
|
||||||
|
def test_gate_verdict_network_classified_when_flag_absent():
|
||||||
|
"""Back-compat: overlap без флага → gate_verdict классифицирует сам."""
|
||||||
|
overlaps = [
|
||||||
|
{
|
||||||
|
"source": "cad_zouit",
|
||||||
|
"type_zone": "Охранная зона объектов электроэнергетики",
|
||||||
|
"name": "ВЛ-10кВ",
|
||||||
|
}
|
||||||
|
]
|
||||||
|
verdict = compute_gate_verdict(None, overlaps, None, _dump_available())
|
||||||
|
codes = [b["code"] for b in verdict["blockers"]]
|
||||||
|
assert "ZOUIT_NETWORK_OBREMENENIE" in codes
|
||||||
Loading…
Add table
Reference in a new issue