Files
dokogen187/test_generator.py
LocoAgent 5af23ae525 fix: аудит dokogen187 — критические и высокие замечания
- api_settings: убраны реальные API-ключи из кода (env/файл) — 1.1
- verify: промт ИИ для проверки реквизитов больше не ссылается на 187-ФЗ
  как на закон о ПДн («юрист по ПДн (187-ФЗ)» → специалист по реквизитам)
- ui: дата ПП РФ №127 исправлена 08.02.2019 → 08.02.2018 (3 места)
- help_docs: группа B переименована «187-ФЗ (персональные данные)» →
  «187-ФЗ (критическая информационная инфраструктура)»
- generator: убран дубль хвоста параграфа в _highlight_unmatched
- importer: «магия дат» (числа 10001-79999 → дата Excel) теперь только
  для полей-дат (as_date), финансовые поля не ломаются
- ui (_collect_is): убраны дубли пар маппинга attacker_level/attacker_types
- ui (импорт): убрано наследие 152-ФЗ ispdnPosition/ispdnFio из маппинга
- test_generator: переписан под модель 187-ФЗ (category/sphere/object_type
  вместо pd_count/threat_type/defence_level)
- criteria_justifications: п.9 для здравоохранения согласован с кодом
  (убран «критерий неприменим» — противоречие с «применим ко всем»)
- criteria_applicability: Транспорт (добавлены п.1, п.2; убран п.10),
  Банковская сфера (добавлены п.8, п.9) — требуется экспертная сверка
- ui: Бусредн вынесен в константу DEFAULT_BUDGET_AVG (данные Минфина,
  обновлять ежегодно); автозаполнение п.9 унифицировано с авторасчётом
  (ΔБ = ΔНП+ΔНДПИ+ΔДИВ+ΔВСО+ΔНС)
- ui: п.8 — предупреждение при неполных данных (менее 5 лет доходов)
- models: поля ndpi/div/vso/ns для п.9; комментарий income_loss уточнён
- .gitignore: api_settings.json/.env не попадают в git
2026-08-05 09:16:16 +04:00

200 lines
6.9 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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('Sphere: {{item.sphere}}')
doc.add_paragraph('Object type: {{item.object_type}}')
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='1',
sphere='Банковская сфера и финансовый рынок', object_type='АСУ'),
InformationSystem(name='ОКИИ-2 "Кадры"', category='2',
sphere='Наука', object_type='ИС'),
InformationSystem(name='ОКИИ-3 "Склад"', category='3',
sphere='Здравоохранение', object_type='ИС'),
]
)
# Build replacements
replacements = build_replacements(
company=company,
is_list=company.information_systems,
doc_number='001',
doc_date='31.07.2026',
direction='187fz'
)
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='187fz'
)
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()