fix(ptica): HTML-экспортёр перестаёт выбрасывать легаси-зонирование, которое сам же проверил (#2464)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 9s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 1m57s
CI / backend-tests (pull_request) Successful in 17m20s
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 9s
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 1m57s
CI / backend-tests (pull_request) Successful in 17m20s
_build_zoning при пустом nspd_zoning берёт легаси result["zoning"], проверяет, что там есть zone_code, — и дальше читает поля из так и оставшегося ПУСТЫМ nspd_zoning. Все пары выходят None, отбрасываются фильтром, и §1 печатает «нет данных» ПОВЕРХ имеющихся данных. То есть это не пустая таблица, а ложное утверждение об отсутствии. Соседний full_report_docx._build_zoning — та же структура слово в слово — закрывает эту ветку одной строкой `nspd_zoning = zoning`. В HTML её не было. Достижимость называю честно: сегодня ветка на проде не срабатывает. Легаси-зонирование заполняется из pzz_zones_ekb, а она пуста намеренно — задача pzz-sync отключена в beat-расписании, потому что Росреестр закрыл PKK6 (#233, #259). Дефект латентный. Чиню всё равно по двум причинам. HTML-сборка — основа полного PDF-отчёта (full_report_pdf импортирует build_full_report_html), так что в день, когда таблицу наполнят, зонирование молча пропадёт из главного документа. И правка — ровно одна строка, приводящая функцию к её же соседу. Тест против origin/main: код зоны Ж-4 не попал в таблицу — вывод '<div class="no-data">нет данных</div>' заглушка «нет данных» рядом с данными — то же самое, отдельным утверждением приоритет nspd_zoning сохранён — контроль, зелёный на обеих сторонах отсутствие данных честно названо — контроль, зелёный на обеих сторонах Контроль «приоритет nspd_zoning» стоит не для симметрии: он ловит «починку», которая слила бы два источника вместо того, чтобы оставить НСПД главным. Прогоны: tests/services/exporters 224 passed rc=0; tests/services rc=0. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
feff8214f7
commit
ed13f0af31
2 changed files with 96 additions and 0 deletions
|
|
@ -482,6 +482,12 @@ def _build_zoning(result: dict[str, Any]) -> str:
|
|||
note = zoning.get("note")
|
||||
note_html = f'<p class="alt-meta">{_esc(note)}</p>' if note else ""
|
||||
return _no_data() + note_html
|
||||
# Без этой строки годное легаси-зонирование признавалось пригодным выше и тут же
|
||||
# терялось: ниже всё читается из nspd_zoning, а он в этой ветке пустой — все пары
|
||||
# выходили None, отбрасывались фильтром, и §1 печатал «нет данных» ПОВЕРХ
|
||||
# имеющихся данных. Соседний full_report_docx._build_zoning делает ровно это же
|
||||
# присваивание (#2464).
|
||||
nspd_zoning = zoning
|
||||
|
||||
zone_code = nspd_zoning.get("zone_code") or nspd_zoning.get("regulation_zone_index")
|
||||
pairs: list[tuple[str, Any]] = [
|
||||
|
|
|
|||
|
|
@ -0,0 +1,90 @@
|
|||
"""HTML-экспортёр не должен выбрасывать легаси-зонирование, которое сам же проверил (#2464).
|
||||
|
||||
`_build_zoning` при пустом `nspd_zoning` берёт легаси `result["zoning"]`, проверяет, что
|
||||
там есть `zone_code`, — и дальше читает поля из так и оставшегося ПУСТЫМ `nspd_zoning`.
|
||||
То есть данные признаны годными и тут же потеряны: все пары получаются None, отбрасываются
|
||||
фильтром, и таблица уходит в отчёт без единой строки.
|
||||
|
||||
Соседний экспортёр `full_report_docx._build_zoning` — та же структура слово в слово — эту
|
||||
ветку закрывает одной строкой `nspd_zoning = zoning`. Здесь её не было.
|
||||
|
||||
Достижимость. Сегодня ветка не срабатывает на проде: легаси-зонирование заполняется из
|
||||
`pzz_zones_ekb`, а та пуста намеренно — задача `pzz-sync` отключена в beat-расписании,
|
||||
потому что Росреестр закрыл PKK6 (#233, #259). Так что это латентный дефект, а не
|
||||
наблюдаемый отказ. Чинится он всё равно: HTML-сборка — основа полного PDF-отчёта
|
||||
(`full_report_pdf` импортирует `build_full_report_html`), и в день, когда таблицу
|
||||
наполнят, зонирование молча пропадёт из главного документа.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
|
||||
os.environ.setdefault("DATABASE_URL", "postgresql+psycopg://test:test@localhost:5432/test")
|
||||
|
||||
from typing import Any
|
||||
|
||||
# Легаси-зонирование в той форме, в какой его собирает parcels.py из pzz_zones_ekb.
|
||||
_LEGACY_ZONING: dict[str, Any] = {
|
||||
"zone_code": "Ж-4",
|
||||
"zone_name": "Зона многоэтажной жилой застройки",
|
||||
"description": "Многоэтажные жилые дома",
|
||||
"data_available": True,
|
||||
"source": "rosreestr-pkk6-cached",
|
||||
}
|
||||
|
||||
|
||||
def _zoning_html(result: dict[str, Any]) -> str:
|
||||
from app.services.exporters.full_report_html import _build_zoning
|
||||
|
||||
return _build_zoning(result)
|
||||
|
||||
|
||||
def test_legacy_zoning_reaches_the_table() -> None:
|
||||
"""Код зоны из легаси-источника обязан попасть в таблицу отчёта."""
|
||||
out = _zoning_html({"nspd_zoning": None, "zoning": dict(_LEGACY_ZONING)})
|
||||
|
||||
assert "Ж-4" in out, (
|
||||
"код зоны из легаси-зонирования не попал в таблицу: ветка признала данные "
|
||||
"годными и продолжила читать из пустого nspd_zoning"
|
||||
)
|
||||
assert "Зона многоэтажной жилой застройки" in out, "наименование зоны потеряно"
|
||||
|
||||
|
||||
def test_legacy_zoning_does_not_render_as_no_data() -> None:
|
||||
"""Таблица с данными не должна выглядеть как «данных нет».
|
||||
|
||||
Отдельно от предыдущего: даже если код зоны когда-нибудь попадёт в вывод другим
|
||||
путём, заглушка «нет данных» рядом с реальными данными — сама по себе дефект.
|
||||
"""
|
||||
out = _zoning_html({"nspd_zoning": None, "zoning": dict(_LEGACY_ZONING)})
|
||||
|
||||
from app.services.exporters.full_report_html import _NO_DATA
|
||||
|
||||
assert _NO_DATA not in out, f"в таблице с данными стоит заглушка {_NO_DATA!r}"
|
||||
|
||||
|
||||
def test_nspd_zoning_still_wins_when_present() -> None:
|
||||
"""Контроль: при живом nspd_zoning легаси не подмешивается.
|
||||
|
||||
Ловит «починку», которая слила бы два источника: приоритет НСПД должен остаться.
|
||||
"""
|
||||
out = _zoning_html(
|
||||
{
|
||||
"nspd_zoning": {"zone_code": "ЦС-1", "zone_name": "Зона делового назначения"},
|
||||
"zoning": dict(_LEGACY_ZONING),
|
||||
}
|
||||
)
|
||||
|
||||
assert "ЦС-1" in out
|
||||
assert "Ж-4" not in out, "легаси-зона подмешалась поверх живого nspd_zoning"
|
||||
|
||||
|
||||
def test_absent_zoning_still_says_no_data() -> None:
|
||||
"""Контроль: когда данных нет НИГДЕ, отчёт по-прежнему честно говорит об этом."""
|
||||
from app.services.exporters.full_report_html import _NO_DATA
|
||||
|
||||
out = _zoning_html({"nspd_zoning": None, "zoning": {"zone_code": None, "note": "PKK6 закрыт"}})
|
||||
|
||||
assert _NO_DATA in out, "исчезла честная заглушка «данных нет»"
|
||||
assert "PKK6 закрыт" in out, "потеряна пояснительная сноска к отсутствию данных"
|
||||
Loading…
Add table
Reference in a new issue