fix: 6 замечаний по генератору

1) нормализация адреса: «Р-Н Крымский» → «р-н. Крымский»,
   «Г Крымск» → «г. Крымск», «УЛ» → «ул.», «Д.» → «д.»
2) убрана строчка «ПО» на вкладке ИС
3) СЗИ без категорий: «Kaspersky, КриптоПро» (через запятую)
4) кнопка «Выбрать из списка» в подвкладке «Пользователи ИС»
5) бумажные документы: «хранятся в сейфе» (авто-предлог+падеж)
6) незаполненные данные «—» подсвечиваются жёлтым (незаменённые
   переменные — красным, как было)
This commit is contained in:
2026-08-03 13:23:52 +04:00
parent 2f4a08b2e4
commit 9d2690e3e0
4 changed files with 138 additions and 31 deletions
+47 -21
View File
@@ -449,32 +449,58 @@ def expand_loops_in_zip(docx_path: str, loops: Dict[str, List[Dict]]) -> str:
# 3. ЗАМЕНА ПЕРЕМЕННЫХ В ЭЛЕМЕНТАХ DOCX
# ============================================================
def _apply_highlight_color(run_el, color: str):
"""Добавляет маркер highlight указанного цвета к run."""
ns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'
rpr = run_el.find(f'{{{ns}}}rPr')
if rpr is None:
rpr = OxmlElement('w:rPr')
run_el.insert(0, rpr)
hl = OxmlElement('w:highlight')
hl.set(f'{{{ns}}}val', color)
rpr.append(hl)
def _replace_text_in_para(para, replacements: Dict[str, str]) -> bool:
"""Заменяет {{var}} в параграфе. Возвращает True, если были замены."""
"""Заменяет {{var}} в параграфе. Возвращает True, если были замены.
Значения «—» (незаполненная информация) подсвечиваются жёлтым маркером.
"""
full = para.text
matches = VAR_PATTERN.findall(full)
matches = list(VAR_PATTERN.finditer(full))
if not matches:
return False
new_text = full
for var in matches:
# Собираем параграф заново: обычный текст + значения переменных
first = para.runs[0] if para.runs else None
para.clear()
pos = 0
changed = False
for m in matches:
var = m.group(0)
val = replacements.get(var)
if val is not None:
new_text = new_text.replace(var, str(val))
if new_text == full:
return False
# Сохраняем форматирование первого run
if para.runs:
first = para.runs[0]
para.clear()
run = para.add_run(new_text)
_copy_run_style(first, run)
else:
para.clear()
para.add_run(new_text)
return True
if val is None:
continue
# текст до переменной
if m.start() > pos:
run = para.add_run(full[pos:m.start()])
if first is not None:
_copy_run_style(first, run)
# значение переменной
val_str = str(val)
run = para.add_run(val_str)
if first is not None:
_copy_run_style(first, run)
if val_str == '' or val_str == '':
_apply_highlight_color(run._element, 'yellow')
changed = True
pos = m.end()
# хвост после последней переменной
if pos < len(full):
run = para.add_run(full[pos:])
if first is not None:
_copy_run_style(first, run)
return changed
def _copy_run_style(source, target):