fix: 8 багов — город, IS поля, ПО, авторасчёт, скролл, прогресc, комиссия, замены
This commit is contained in:
+56
-8
@@ -57,16 +57,19 @@ def process_template(
|
||||
buf.seek(0)
|
||||
doc = Document(buf)
|
||||
|
||||
# 5. Плоская замена переменных (2 прохода)
|
||||
for _ in range(2):
|
||||
# 5. Плоская замена переменных (3 прохода)
|
||||
for _ in range(3):
|
||||
_replace_in_paragraphs(doc.paragraphs, replacements)
|
||||
_replace_in_tables(doc.tables, replacements)
|
||||
_replace_in_headers_footers(doc, replacements)
|
||||
|
||||
# 6. Подсветка незаполненных
|
||||
# 6. Логирование незаменённых переменных
|
||||
_log_unmatched(doc, replacements, log_func if log_func else lambda x: None)
|
||||
|
||||
# 7. Подсветка незаполненных
|
||||
_highlight_unmatched(doc)
|
||||
|
||||
# 7. Сохранение
|
||||
# 8. Сохранение
|
||||
doc.save(output_path)
|
||||
|
||||
if log_func:
|
||||
@@ -338,12 +341,57 @@ def _find_element_bounds(xml_str: str, pos: int):
|
||||
return None, None
|
||||
|
||||
|
||||
# ==================== ЛОГИРОВАНИЕ НЕЗАМЕНЁННЫХ ====================
|
||||
|
||||
UNMATCHED_PATTERN = re.compile(r'\{\{[^}]+\}\}|<[A-Za-z0-9_]+>')
|
||||
|
||||
|
||||
def _log_unmatched(doc, replacements: Dict[str, str], log_func):
|
||||
"""Логирует оставшиеся незаменённые переменные."""
|
||||
unmatched = set()
|
||||
|
||||
def _scan_text(text):
|
||||
for m in UNMATCHED_PATTERN.finditer(text):
|
||||
var = m.group(0)
|
||||
# Проверяем, не является ли это уже обработанной переменной
|
||||
if var.startswith('{{'):
|
||||
if var not in replacements:
|
||||
unmatched.add(var)
|
||||
elif var.startswith('<'):
|
||||
if var not in replacements:
|
||||
unmatched.add(var)
|
||||
|
||||
for para in doc.paragraphs:
|
||||
_scan_text(para.text)
|
||||
for table in doc.tables:
|
||||
for row in table.rows:
|
||||
for cell in row.cells:
|
||||
for para in cell.paragraphs:
|
||||
_scan_text(para.text)
|
||||
for section in doc.sections:
|
||||
for hf in [section.header, section.footer,
|
||||
section.first_page_header, section.first_page_footer]:
|
||||
if hf:
|
||||
for para in hf.paragraphs:
|
||||
_scan_text(para.text)
|
||||
|
||||
if unmatched:
|
||||
log_func(f"⚠️ Незаменённые переменные ({len(unmatched)}): {', '.join(sorted(unmatched)[:20])}")
|
||||
if len(unmatched) > 20:
|
||||
log_func(f" ... и ещё {len(unmatched) - 20}")
|
||||
else:
|
||||
log_func("✅ Все переменные успешно заменены")
|
||||
|
||||
|
||||
# ==================== ПОДСВЕТКА НЕЗАПОЛНЕННЫХ ====================
|
||||
|
||||
def _highlight_unmatched(doc):
|
||||
"""Подсвечивает незаполненные переменные красным."""
|
||||
"""Подсвечивает незаполненные переменные красным ({{...}} и старые <...>)."""
|
||||
ns = 'http://schemas.openxmlformats.org/wordprocessingml/2006/main'
|
||||
|
||||
def _has_unmatched(text):
|
||||
return bool(UNMATCHED_PATTERN.search(text))
|
||||
|
||||
def _highlight_run(run):
|
||||
rpr = run._element.find(f'{{{ns}}}rPr')
|
||||
if rpr is None:
|
||||
@@ -358,7 +406,7 @@ def _highlight_unmatched(doc):
|
||||
|
||||
for para in doc.paragraphs:
|
||||
for run in para.runs:
|
||||
if '{{' in run.text and '}}' in run.text:
|
||||
if _has_unmatched(run.text):
|
||||
_highlight_run(run)
|
||||
|
||||
for table in doc.tables:
|
||||
@@ -366,7 +414,7 @@ def _highlight_unmatched(doc):
|
||||
for cell in row.cells:
|
||||
for para in cell.paragraphs:
|
||||
for run in para.runs:
|
||||
if '{{' in run.text and '}}' in run.text:
|
||||
if _has_unmatched(run.text):
|
||||
_highlight_run(run)
|
||||
|
||||
for section in doc.sections:
|
||||
@@ -374,5 +422,5 @@ def _highlight_unmatched(doc):
|
||||
if hf:
|
||||
for para in hf.paragraphs:
|
||||
for run in para.runs:
|
||||
if '{{' in run.text and '}}' in run.text:
|
||||
if _has_unmatched(run.text):
|
||||
_highlight_run(run)
|
||||
|
||||
Reference in New Issue
Block a user