rewrite: чистый генератор с ZIP-уровневой обработкой циклов
This commit is contained in:
@@ -0,0 +1,199 @@
|
||||
#!/usr/bin/env python3
|
||||
"""DokoGen — test: verify generator with nested loops"""
|
||||
|
||||
import sys, os, json, tempfile, zipfile, shutil, re
|
||||
from datetime import datetime
|
||||
|
||||
# Add to path
|
||||
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
from dokogen.models import Company, CommissionMember, InformationSystem
|
||||
from dokogen.variables import build_replacements
|
||||
from dokogen.generator import process_template, _expand_loops_in_zip
|
||||
|
||||
def create_test_template():
|
||||
"""Creates a test .docx with nested loops for verification."""
|
||||
from docx import Document
|
||||
from docx.shared import Pt
|
||||
|
||||
doc = Document()
|
||||
|
||||
# Title
|
||||
doc.add_heading('Test Document with Nested Loops', 0)
|
||||
|
||||
# Simple variable
|
||||
doc.add_paragraph('Company: {{company_full_name}}')
|
||||
doc.add_paragraph('Date: {{date}}')
|
||||
doc.add_paragraph('')
|
||||
|
||||
# Outer loop: information_systems
|
||||
doc.add_paragraph('<!-- loop:information_systems -->')
|
||||
doc.add_paragraph('=== IS: {{item.name}} ===')
|
||||
doc.add_paragraph('Category: {{item.category}}')
|
||||
doc.add_paragraph('Threat type: {{item.threat_type}}')
|
||||
doc.add_paragraph('Defence level: {{item.defence_level}}')
|
||||
doc.add_paragraph('')
|
||||
|
||||
# Inner loop: commission
|
||||
doc.add_paragraph('Commission members:')
|
||||
doc.add_paragraph('<!-- loop:commission -->')
|
||||
doc.add_paragraph(' - {{item.role}}: {{item.position}} {{item.fio}}')
|
||||
doc.add_paragraph('<!-- loop_end -->')
|
||||
|
||||
doc.add_paragraph('')
|
||||
doc.add_paragraph('--- end of IS ---')
|
||||
doc.add_paragraph('')
|
||||
doc.add_paragraph('<!-- loop_end -->')
|
||||
|
||||
doc.add_paragraph('')
|
||||
doc.add_paragraph('Footer: Generated automatically')
|
||||
|
||||
path = '/tmp/test_template_nested.docx'
|
||||
doc.save(path)
|
||||
print(f'✅ Test template created: {path}')
|
||||
return path
|
||||
|
||||
def test_zip_loop_expansion():
|
||||
"""Test ZIP-level loop expansion with mock data."""
|
||||
print('\n' + '='*60)
|
||||
print('TEST: ZIP-level loop expansion')
|
||||
print('='*60)
|
||||
|
||||
# Create test data
|
||||
company = Company(
|
||||
full_name='ООО "Ромашка"',
|
||||
short_name='Ромашка',
|
||||
inn='7701234567',
|
||||
chief_fio='Иванов Иван Иванович',
|
||||
chief_position='Генеральный директор',
|
||||
commission=[
|
||||
CommissionMember(role='Председатель', position='Директор', fio='Иванов И.И.'),
|
||||
CommissionMember(role='Секретарь', position='Секретарь', fio='Петров П.П.'),
|
||||
CommissionMember(role='Член комиссии', position='Бухгалтер', fio='Сидорова А.А.'),
|
||||
],
|
||||
information_systems=[
|
||||
InformationSystem(name='ИС-1 "Бухгалтерия"', category='специальные',
|
||||
pd_count='менее 100 000', threat_type='1', defence_level='2'),
|
||||
InformationSystem(name='ИС-2 "Кадры"', category='иные',
|
||||
pd_count='более 100 000', threat_type='3', defence_level='4'),
|
||||
InformationSystem(name='ИС-3 "Склад"', category='общедоступные',
|
||||
pd_count='менее 100 000', threat_type='2', defence_level='3'),
|
||||
]
|
||||
)
|
||||
|
||||
# Build replacements
|
||||
replacements = build_replacements(
|
||||
company=company,
|
||||
is_list=company.information_systems,
|
||||
doc_number='001',
|
||||
doc_date='31.07.2026',
|
||||
direction='152fz'
|
||||
)
|
||||
|
||||
loops = replacements.get('_loops', {})
|
||||
print(f'\nLoops found: {list(loops.keys())}')
|
||||
print(f'IS items: {len(loops.get("information_systems", []))}')
|
||||
print(f'Commission items: {len(loops.get("commission", []))}')
|
||||
|
||||
# Create test template
|
||||
template_path = create_test_template()
|
||||
|
||||
# Verify template has markers
|
||||
with zipfile.ZipFile(template_path, 'r') as z:
|
||||
doc_xml = z.read('word/document.xml').decode('utf-8')
|
||||
|
||||
start_count = doc_xml.count('loop:')
|
||||
end_count = doc_xml.count('loop_end')
|
||||
print(f'\nMarkers in template: {start_count} starts, {end_count} ends')
|
||||
|
||||
# Process with ZIP-level loop expansion
|
||||
print('\nProcessing loops via ZIP...')
|
||||
result_path = _expand_loops_in_zip(template_path, loops)
|
||||
|
||||
# Verify result
|
||||
with zipfile.ZipFile(result_path, 'r') as z:
|
||||
result_xml = z.read('word/document.xml').decode('utf-8')
|
||||
|
||||
remaining_starts = len(re.findall(r'loop:(\w+)', result_xml))
|
||||
remaining_ends = result_xml.count('loop_end')
|
||||
|
||||
print(f'Markers after expansion: {remaining_starts} starts, {remaining_ends} ends')
|
||||
|
||||
if remaining_starts == 0 and remaining_ends == 0:
|
||||
print('✅ ALL LOOPS EXPANDED SUCCESSFULLY')
|
||||
else:
|
||||
print(f'⚠️ {remaining_starts} loop markers remaining')
|
||||
|
||||
# Now test full process_template
|
||||
print('\n' + '='*60)
|
||||
print('TEST: Full process_template with all replacements')
|
||||
print('='*60)
|
||||
|
||||
# Rebuild replacements (they were consumed)
|
||||
replacements2 = build_replacements(
|
||||
company=company,
|
||||
is_list=company.information_systems,
|
||||
doc_number='001',
|
||||
doc_date='31.07.2026',
|
||||
direction='152fz'
|
||||
)
|
||||
|
||||
try:
|
||||
output_path = process_template(
|
||||
template_path=template_path,
|
||||
replacements=replacements2,
|
||||
output_path='/tmp/test_output.docx',
|
||||
log_func=print
|
||||
)
|
||||
print(f'\n✅ Output generated: {output_path}')
|
||||
|
||||
# Verify output
|
||||
with zipfile.ZipFile(output_path, 'r') as z:
|
||||
out_xml = z.read('word/document.xml').decode('utf-8')
|
||||
|
||||
# Check no remaining variables
|
||||
remaining_vars = re.findall(r'\{\{[^}]+\}\}', out_xml)
|
||||
remaining_old = re.findall(r'<[A-Za-z_]+>', out_xml)
|
||||
|
||||
if remaining_vars:
|
||||
print(f'⚠️ Remaining {{...}} vars: {remaining_vars[:5]}')
|
||||
else:
|
||||
print('✅ No remaining {{...}} variables')
|
||||
|
||||
if remaining_old:
|
||||
print(f'⚠️ Remaining <...> vars: {remaining_old[:5]}')
|
||||
else:
|
||||
print('✅ No remaining <...> variables')
|
||||
|
||||
# Check IS names appear
|
||||
for isys in company.information_systems:
|
||||
if isys.name in out_xml:
|
||||
print(f'✅ Found IS name: {isys.name}')
|
||||
else:
|
||||
print(f'⚠️ MISSING IS name: {isys.name}')
|
||||
|
||||
# Check commission members appear
|
||||
for m in company.commission:
|
||||
if m.fio in out_xml:
|
||||
print(f'✅ Found commission: {m.fio}')
|
||||
else:
|
||||
print(f'⚠️ MISSING commission: {m.fio}')
|
||||
|
||||
except Exception as e:
|
||||
print(f'❌ Error: {e}')
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
|
||||
# Cleanup
|
||||
os.unlink(template_path)
|
||||
if os.path.exists(result_path):
|
||||
os.unlink(result_path)
|
||||
if os.path.exists('/tmp/test_output.docx'):
|
||||
os.unlink('/tmp/test_output.docx')
|
||||
|
||||
print('\n' + '='*60)
|
||||
print('TEST COMPLETE')
|
||||
print('='*60)
|
||||
|
||||
if __name__ == '__main__':
|
||||
test_zip_loop_expansion()
|
||||
Reference in New Issue
Block a user