#!/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('') 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('') doc.add_paragraph(' - {{item.role}}: {{item.position}} {{item.fio}}') doc.add_paragraph('') doc.add_paragraph('') doc.add_paragraph('--- end of IS ---') doc.add_paragraph('') doc.add_paragraph('') 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='kzifz' ) 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='kzifz' ) 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()