200 lines
7.0 KiB
Python
200 lines
7.0 KiB
Python
#!/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='149fz'
|
||
)
|
||
|
||
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='149fz'
|
||
)
|
||
|
||
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()
|