- <!-- loop:users_loop --> внутри цикла ИС: {{user_fio}}, {{user_position}}
- администратор НЕ исключается (как просил Костя), убираются только
дубли по ФИО (нормализованное сравнение)
503 lines
23 KiB
Python
503 lines
23 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""DokoGen — Справочник переменных шаблонов и резолвер значений"""
|
||
|
||
from datetime import datetime
|
||
from typing import Dict, Any, List, Optional
|
||
import re
|
||
|
||
from . import declension as dc
|
||
from .models import Company, CommissionMember
|
||
|
||
|
||
def _norm_name(s):
|
||
"""Нормализация ФИО для сравнения: регистр, пробелы, точки, тире игнорируются."""
|
||
s = (s or '').lower().replace('ё', 'е')
|
||
return re.sub(r'[\s.,;:()«»"\'\-]+', '', s)
|
||
|
||
|
||
# ============================================================
|
||
# ОПИСАНИЕ ПЕРЕМЕННЫХ
|
||
# ============================================================
|
||
# Формат: (имя_переменной, группа, описание, ключ_резолвера)
|
||
# Группы: A — общие, B — 152-ФЗ
|
||
|
||
VARIABLE_DEFS = [
|
||
# === Полное название организации (6 падежей) ===
|
||
('{{company_full_name}}', 'A', 'Полное наименование организации (им.п.)', 'company:full_name'),
|
||
('{{company_full_name_genitive}}', 'A', 'Полное наименование (род.п.)', 'decl:full_name:gent'),
|
||
('{{company_full_name_dative}}', 'A', 'Полное наименование (дат.п.)', 'decl:full_name:datv'),
|
||
('{{company_full_name_accs}}', 'A', 'Полное наименование (вин.п.)', 'decl:full_name:accs'),
|
||
('{{company_full_name_ablt}}', 'A', 'Полное наименование (твор.п.)', 'decl:full_name:ablt'),
|
||
('{{company_full_name_loct}}', 'A', 'Полное наименование (пр.п.)', 'decl:full_name:loct'),
|
||
|
||
# === Краткое название организации (6 падежей) ===
|
||
('{{company_short_name}}', 'A', 'Краткое наименование (им.п.)', 'company:short_name'),
|
||
('{{company_short_name_genitive}}', 'A', 'Краткое наименование (род.п.)', 'decl:short_name:gent'),
|
||
('{{company_short_name_dative}}', 'A', 'Краткое наименование (дат.п.)', 'decl:short_name:datv'),
|
||
('{{company_short_name_accs}}', 'A', 'Краткое наименование (вин.п.)', 'decl:short_name:accs'),
|
||
('{{company_short_name_ablt}}', 'A', 'Краткое наименование (твор.п.)', 'decl:short_name:ablt'),
|
||
('{{company_short_name_loct}}', 'A', 'Краткое наименование (пр.п.)', 'decl:short_name:loct'),
|
||
|
||
# === Должностные лица ===
|
||
('{{chief_position}}', 'A', 'Должность руководителя', 'company:chief_position'),
|
||
('{{chief_fio_initials}}', 'A', 'И.О. Фамилия руководителя', 'initials:chief'),
|
||
('{{chief_fio_short}}', 'A', 'Фамилия И.О. руководителя', 'short_fio:chief'),
|
||
('{{responsible_position}}', 'A', 'Должность ответственного', 'company:responsible_position'),
|
||
('{{responsible_fio_initials}}', 'A', 'И.О. Фамилия ответственного', 'initials:responsible'),
|
||
('{{responsible_fio_short}}', 'A', 'Фамилия И.О. ответственного', 'short_fio:responsible'),
|
||
('{{admin_position}}', 'A', 'Должность администратора', 'company:admin_position'),
|
||
('{{admin_fio_initials}}', 'A', 'И.О. Фамилия администратора', 'initials:admin'),
|
||
('{{admin_fio_short}}', 'A', 'Фамилия И.О. администратора', 'short_fio:admin'),
|
||
|
||
# === Сведения об организации ===
|
||
('{{company_address}}', 'A', 'Адрес организации', 'company:address'),
|
||
('{{city_name}}', 'A', 'Город', 'company:city_name'),
|
||
('{{company_inn}}', 'A', 'ИНН', 'company:inn'),
|
||
('{{company_ogrn}}', 'A', 'ОГРН', 'company:ogrn'),
|
||
('{{company_ogrn_date}}', 'A', 'Дата ОГРН', 'company:ogrn_date'),
|
||
('{{company_kpp}}', 'A', 'КПП', 'company:kpp'),
|
||
('{{security_department}}', 'A', 'Наименование структурного подразделения по безопасности', 'company:security_department'),
|
||
('{{item.pd_actions}}', 'B', 'Действия с ПДн (в цикле ИС)', 'item:pd_actions'),
|
||
|
||
# === Комиссия ===
|
||
('{{commission_list}}', 'A', 'Список комиссии', 'commission_list'),
|
||
|
||
# === Специальные переменные ===
|
||
('{{date_year}}', 'A', 'Текущий год', 'date:year'),
|
||
('{{date}}', 'A', 'Текущая дата', 'date:full'),
|
||
('{{document_date}}', 'A', 'Дата документа', 'doc_date'),
|
||
('{{document_number}}', 'A', 'Номер документа', 'doc_number'),
|
||
|
||
# === 152-ФЗ ===
|
||
('{{is_security_requirements}}', 'B', 'Требования уровня защищённости', 'security_reqs'),
|
||
('{{is_list}}', 'B', 'Список всех ИС (нумерованный)', 'is_list'),
|
||
('{{is_name}}', 'B', 'Наименование первой ИС', 'is:name'),
|
||
('{{name}}', 'B', 'Название ИС (алиас)', 'is:name'),
|
||
('{{description}}', 'B', 'Описание ИС (алиас)', 'is:description'),
|
||
('{{category}}', 'B', 'Категория ИС (алиас)', 'is:category'),
|
||
('{{number}}', 'B', 'Номер (алиас)', 'doc_number'),
|
||
('{{pd_count}}', 'B', 'Количество записей ПДн (алиас)', 'is:pd_count'),
|
||
('{{pd_list}}', 'B', 'Список ПДн (алиас)', 'is:pd_list'),
|
||
('{{pd_subjects}}', 'B', 'Субъекты ПДн (алиас)', 'is:pd_subjects'),
|
||
('{{threat_type}}', 'B', 'Тип угроз (алиас)', 'is:threat_type'),
|
||
('{{users}}', 'B', 'Пользователи ИС (алиас)', 'is:users'),
|
||
('{{defence_level}}', 'B', 'Уровень защищённости (алиас)', 'is:defence_level'),
|
||
('{{full_name_genitive}}', 'A', 'Полное наименование (род.п., алиас)', 'decl:full_name:gent'),
|
||
('{{document}}', 'A', 'Название бумажного документа (алиас)', 'company:paper_documents'),
|
||
('{{storage}}', 'A', 'Место хранения (алиас)', 'company:paper_storage'),
|
||
('{{responsible_fio_accs}}', 'A', 'ФИО ответственного (вин.п.)', 'fio_acl:responsible'),
|
||
('{{commission1}}', 'A', 'Должность члена комиссии 1', 'commission_member:0:position'),
|
||
('{{commission1_fio}}', 'A', 'ФИО члена комиссии 1', 'commission_member:0:fio'),
|
||
('{{commission2}}', 'A', 'Должность члена комиссии 2', 'commission_member:1:position'),
|
||
('{{commission2_fio}}', 'A', 'ФИО члена комиссии 2', 'commission_member:1:fio'),
|
||
('{{commission3}}', 'A', 'Должность члена комиссии 3', 'commission_member:2:position'),
|
||
('{{commission3_fio}}', 'A', 'ФИО члена комиссии 3', 'commission_member:2:fio'),
|
||
]
|
||
|
||
# Маппинг старых переменных <...> → {{...}}
|
||
OLD_VAR_MAP = {
|
||
'<CompanyName1>': '{{company_full_name}}',
|
||
'<CompanyName2>': '{{company_short_name}}',
|
||
'<CompanyName1Gent>': '{{company_full_name_genitive}}',
|
||
'<CompanyName2Gent>': '{{company_short_name_genitive}}',
|
||
'<CompanyName1Datv>': '{{company_full_name_dative}}',
|
||
'<CompanyName2Datv>': '{{company_short_name_dative}}',
|
||
'<CompanyName1Accs>': '{{company_full_name_accs}}',
|
||
'<CompanyName2Accs>': '{{company_short_name_accs}}',
|
||
'<CompanyName1Ablt>': '{{company_full_name_ablt}}',
|
||
'<CompanyName2Ablt>': '{{company_short_name_ablt}}',
|
||
'<CompanyName1Loct>': '{{company_full_name_loct}}',
|
||
'<CompanyName2Loct>': '{{company_short_name_loct}}',
|
||
'<Address>': '{{company_address}}',
|
||
'<INN>': '{{company_inn}}',
|
||
'<OGRN>': '{{company_ogrn}}',
|
||
'<KPP>': '{{company_kpp}}',
|
||
'<ChiefFio>': '{{chief_fio_initials}}',
|
||
'<ChiefFioShort>': '{{chief_fio_short}}',
|
||
'<ChiefPosition>': '{{chief_position}}',
|
||
'<RspFio>': '{{responsible_fio_initials}}',
|
||
'<RspFioShort>': '{{responsible_fio_short}}',
|
||
'<RspPosition>': '{{responsible_position}}',
|
||
'<CommissionList>': '{{commission_list}}',
|
||
'<DocumentNumber>': '{{document_number}}',
|
||
'<DocumentDate>': '{{document_date}}',
|
||
'<Date>': '{{date}}',
|
||
'<SurveyDate>': '{{document_date}}',
|
||
}
|
||
|
||
VAR_PATTERN = re.compile(r'\{\{[^}]+\}\}')
|
||
OLD_VAR_PATTERN = re.compile(r'<[A-Za-z0-9_]+>')
|
||
|
||
|
||
# ============================================================
|
||
# ПОСТРОЕНИЕ СЛОВАРЯ ЗАМЕН
|
||
# ============================================================
|
||
|
||
def build_replacements(
|
||
company: Company,
|
||
is_list: List = None,
|
||
doc_number: str = "",
|
||
doc_date: str = "",
|
||
direction: str = "152fz",
|
||
) -> Dict[str, str]:
|
||
"""Строит полный словарь замен {{переменная}} → значение."""
|
||
if is_list is None:
|
||
is_list = []
|
||
|
||
# ---------- Комиссия ----------
|
||
commission_data = _build_commission_data(company)
|
||
|
||
# ---------- Контекст ----------
|
||
ctx = {
|
||
'company': company,
|
||
'is_list': is_list,
|
||
'commission_data': commission_data,
|
||
'doc_number': doc_number or '',
|
||
'doc_date': doc_date or '',
|
||
'direction': direction,
|
||
}
|
||
|
||
replacements = {}
|
||
for var_name, group, desc, source in VARIABLE_DEFS:
|
||
if direction == '152fz' and group not in ('A', 'B'):
|
||
continue
|
||
value = _resolve(source, ctx)
|
||
replacements[var_name] = str(value) if value else '—'
|
||
|
||
# Индексированные переменные ИС (is_name_1, is_pd_list_2 и т.д.)
|
||
for i, isys in enumerate(is_list, 1):
|
||
_add_is_indexed_vars(replacements, isys, i)
|
||
|
||
# Циклы
|
||
replacements['_loops'] = _build_loop_data(company, is_list)
|
||
return replacements
|
||
|
||
|
||
def _commission_role_display(members) -> List[str]:
|
||
"""Группирует роли комиссии для вывода:
|
||
- Председатель/Секретарь/Заместитель — отдельной строкой со своей ролью;
|
||
- обычные члены — первый получает «Члены комиссии», остальные — пустую роль
|
||
(чтобы не повторять «Член комиссии» в каждой строке)."""
|
||
result = []
|
||
member_used = False
|
||
for m in members:
|
||
role = (m.role or 'член комиссии').strip()
|
||
role_lower = role.lower()
|
||
if ('председател' in role_lower or 'секретар' in role_lower
|
||
or 'заместител' in role_lower or 'зам. председателя' in role_lower):
|
||
result.append(role)
|
||
continue
|
||
# обычный член комиссии
|
||
if not member_used:
|
||
result.append('Члены комиссии')
|
||
member_used = True
|
||
else:
|
||
result.append('')
|
||
return result
|
||
|
||
|
||
def _build_commission_data(company: Company) -> Dict[str, str]:
|
||
data = {}
|
||
members = company.commission or []
|
||
|
||
display_roles = _commission_role_display(members)
|
||
lines = []
|
||
for i, (m, role_disp) in enumerate(zip(members, display_roles), 1):
|
||
if role_disp:
|
||
line = (f"{i}. {role_disp}: {m.position} — {m.fio}" if m.position
|
||
else f"{i}. {role_disp}: {m.fio}")
|
||
else:
|
||
line = (f"{i}. {m.position} — {m.fio}" if m.position
|
||
else f"{i}. {m.fio}")
|
||
lines.append(line)
|
||
data['commission_list'] = '\n'.join(lines) if lines else '—'
|
||
|
||
return data
|
||
|
||
|
||
def _add_is_indexed_vars(replacements: Dict[str, str], isys, idx: int):
|
||
fields = {
|
||
'is_name': isys.name or '—',
|
||
'is_address': getattr(isys, 'address', '') or '—',
|
||
'is_description': isys.description or '—',
|
||
'is_has_internet': 'да' if isys.is_internet else 'нет',
|
||
'is_defence_level': str(isys.defence_level or '—'),
|
||
'is_category': ', '.join(isys.personal_data_category) or isys.category or '—',
|
||
'is_pd_list': ', '.join(isys.personal_data_list or []) or '—',
|
||
'is_users': ', '.join(isys.users_list or []) or '—',
|
||
'is_pd_count': str(isys.pd_count or '—'),
|
||
'is_purpose': isys.purpose or '—',
|
||
'is_structure': getattr(isys, 'structure', '') or '—',
|
||
}
|
||
for f, v in fields.items():
|
||
replacements[f'{{{{{f}_{idx}}}}}'] = str(v)
|
||
|
||
|
||
def _build_loop_data(company: Company, is_list: List) -> Dict[str, List[Dict]]:
|
||
loops = {}
|
||
|
||
# Комиссия
|
||
members = company.commission or []
|
||
if members:
|
||
display_roles = _commission_role_display(members)
|
||
items = []
|
||
for m, role_disp in zip(members, display_roles):
|
||
pos = m.position or '—'
|
||
fio = m.fio or '—'
|
||
if role_disp:
|
||
full = f"{role_disp} {pos} — {fio}" if pos != '—' else f"{role_disp} — {fio}"
|
||
else:
|
||
full = f"{pos} — {fio}" if pos != '—' else fio
|
||
# Строка подписи: «Роль: ___________ / » (рамка, ФИО — отдельной переменной)
|
||
if 'Председатель' in role_disp:
|
||
sig = f"{role_disp}: {'_' * 11} / "
|
||
elif role_disp:
|
||
sig = f"{role_disp}: {'_' * 17} / "
|
||
else:
|
||
sig = f"{'_' * 17} / "
|
||
initials = dc.get_initials(fio) if fio != '—' else ''
|
||
items.append({
|
||
'role': role_disp,
|
||
'position': pos,
|
||
'fio': fio,
|
||
'fio_initials': dc.get_initials(fio) if fio != '—' else '',
|
||
'fio_short': dc.get_short_fio(fio) if fio != '—' else '',
|
||
'full': full,
|
||
# Рамка подписи без ФИО: «Роль: ___________ / » (с пробелом — ФИО добавится)
|
||
'signature': sig,
|
||
# Полная подпись с ФИО: «Роль: ___________ / И.О. Фамилия»
|
||
'signature_full': (sig + initials).strip(),
|
||
})
|
||
loops['commission'] = items
|
||
|
||
# ИС
|
||
if is_list:
|
||
items = []
|
||
for isys in is_list:
|
||
# Пользователи ИС для вложенного цикла <!-- loop:users_loop -->
|
||
# ВСЕ пользователи, включая администратора (без дублей по ФИО)
|
||
seen = set()
|
||
users_loop = []
|
||
for u in (isys.users_list or []):
|
||
key = _norm_name(u)
|
||
if key and key in seen:
|
||
continue
|
||
seen.add(key)
|
||
users_loop.append({'fio': u, 'position': ''})
|
||
items.append({
|
||
'name': isys.name or '—',
|
||
'description': isys.description or '—',
|
||
'address': getattr(isys, 'address', '') or '—',
|
||
'is_has_lan': 'Да' if isys.is_local_network else 'Нет',
|
||
'has_internet': 'да' if isys.is_internet else 'нет',
|
||
'defence_level': str(isys.defence_level or '—'),
|
||
'threat_type': str(isys.threat_type or '3'),
|
||
'category': ', '.join(isys.personal_data_category) or isys.category or '—',
|
||
'pd_list': ', '.join(isys.personal_data_list or []) or '—',
|
||
'pd_count': str(isys.pd_count or '—'),
|
||
'users': ', '.join(isys.users_list or []) or '—',
|
||
'users_loop': users_loop,
|
||
'defense_tools_list': ', '.join(isys.defense_tools_list or []) or '—',
|
||
'processing_modes': isys.processing_modes or '—',
|
||
'pd_subjects': '; '.join(isys.pd_subjects_list or []) or '—',
|
||
'is_purpose': isys.purpose or '—',
|
||
'structure': getattr(isys, 'structure', '') or '—',
|
||
'software': getattr(isys, 'software', '') or '—',
|
||
'pd_actions': '; '.join(isys.pd_actions_list or []) or '—',
|
||
})
|
||
loops['information_systems'] = items
|
||
|
||
# Бумажные документы
|
||
def _storage_with_prep(storage: str) -> str:
|
||
"""«сейф» → «в сейфе» (предлог + предложный падеж).
|
||
Если уже начинается с предлога — оставить как есть."""
|
||
if not storage:
|
||
return ''
|
||
s = storage.strip()
|
||
low = s.lower()
|
||
if low.startswith(('в ', 'на ', 'под ', 'за ', 'у ', 'при ', 'из ', 'со ', 'во ', 'над ', 'перед ')):
|
||
return s
|
||
words = s.split()
|
||
out = []
|
||
# Исключения предложного падежа: «в шкафу», «в углу» (не «в шкафе»)
|
||
loct_exceptions = {'шкаф': 'шкафу', 'угол': 'углу', 'край': 'краю', 'рот': 'рту', 'мост': 'мосту'}
|
||
for w in words:
|
||
core = w.strip('.,;')
|
||
if not core or core.isdigit() or core.startswith('№'):
|
||
out.append(w)
|
||
continue
|
||
if core.lower() in loct_exceptions:
|
||
out.append(loct_exceptions[core.lower()])
|
||
continue
|
||
try:
|
||
p = dc.morph.parse(core)[0]
|
||
inf = p.inflect({'loct'})
|
||
if inf:
|
||
out.append(inf.word)
|
||
continue
|
||
except Exception:
|
||
pass
|
||
out.append(w)
|
||
return 'в ' + ' '.join(out)
|
||
|
||
paper_list = getattr(company, 'paper_documents_list', None) or []
|
||
if paper_list:
|
||
items = []
|
||
for p in paper_list:
|
||
storage = p.storage or ''
|
||
items.append({
|
||
'document': p.name or '',
|
||
'storage': _storage_with_prep(storage), # «в сейфе»
|
||
'storage_raw': storage, # «сейф» (как введено)
|
||
})
|
||
loops['paper_documents'] = items
|
||
|
||
# Сотрудники с доступом
|
||
employees = getattr(company, 'employees_access', None) or []
|
||
if employees:
|
||
items = []
|
||
for e in employees:
|
||
items.append({
|
||
'position': e.get('position', ''),
|
||
'fio': e.get('fio', ''),
|
||
})
|
||
loops['employees_access'] = items
|
||
|
||
return loops
|
||
|
||
|
||
# ============================================================
|
||
# РЕЗОЛВЕР ЗНАЧЕНИЙ
|
||
# ============================================================
|
||
|
||
def _resolve(source: str, ctx: Dict) -> str:
|
||
"""Преобразует ключ-источник в значение, подставляя из контекста."""
|
||
company = ctx.get('company')
|
||
is_list = ctx.get('is_list', [])
|
||
commission_data = ctx.get('commission_data', {})
|
||
doc_number = ctx.get('doc_number', '')
|
||
doc_date = ctx.get('doc_date', '')
|
||
|
||
# Прямые поля Company
|
||
if source.startswith('company:'):
|
||
field = source[8:]
|
||
return str(getattr(company, field, '') or '')
|
||
|
||
# Склонение названия
|
||
if source.startswith('decl:'):
|
||
_, field, case = source.split(':')
|
||
name = getattr(company, field, '') or ''
|
||
return dc.company_name_decline(name, case) if name else '—'
|
||
|
||
# Форматы ФИО
|
||
if source.startswith('short_fio:'):
|
||
who = source.split(':', 1)[1]
|
||
fio = _get_fio(company, who)
|
||
return dc.get_short_fio(fio) if fio else '—'
|
||
if source.startswith('initials:'):
|
||
who = source.split(':', 1)[1]
|
||
fio = _get_fio(company, who)
|
||
return dc.get_initials(fio) if fio else '—'
|
||
|
||
# Комиссия
|
||
if source in commission_data:
|
||
return str(commission_data[source])
|
||
|
||
# Даты
|
||
if source.startswith('date:'):
|
||
fmt = source.split(':', 1)[1]
|
||
now = datetime.now()
|
||
if fmt == 'year':
|
||
return str(now.year)
|
||
return now.strftime('%d.%m.%Y')
|
||
if source == 'doc_date':
|
||
return doc_date or datetime.now().strftime('%d.%m.%Y')
|
||
if source == 'doc_number':
|
||
num = str(doc_number).strip()
|
||
return re.sub(r'^[Nn]?[oо]?[№#]\s*', '', num) or '—'
|
||
|
||
# Требования защищённости
|
||
if source == 'security_reqs':
|
||
return _get_security_text(ctx)
|
||
|
||
# IS-алиасы (первая ИС)
|
||
if source.startswith('is:'):
|
||
field = source[3:]
|
||
if is_list:
|
||
first = is_list[0]
|
||
field_map = {
|
||
'name': 'name',
|
||
'description': 'description',
|
||
'category': 'category',
|
||
'defence_level': 'defence_level',
|
||
'threat_type': 'threat_type',
|
||
'pd_list': lambda: ', '.join(first.personal_data_list or []),
|
||
'pd_count': 'pd_count',
|
||
'pd_subjects': lambda: '; '.join(first.pd_subjects_list or []),
|
||
'users': lambda: ', '.join(first.users_list or []),
|
||
}
|
||
if field in field_map:
|
||
val = field_map[field]
|
||
if callable(val):
|
||
return val() or '—'
|
||
return str(getattr(first, val, '') or '—') if isinstance(val, str) else '—'
|
||
return '—'
|
||
|
||
# Список ИС
|
||
if source == 'is_list':
|
||
if is_list:
|
||
return '\n'.join(f"{i}. {isys.name}" for i, isys in enumerate(is_list, 1))
|
||
return '—'
|
||
if source == 'is_list_comma':
|
||
if is_list:
|
||
return ', '.join(isys.name for isys in is_list)
|
||
return '—'
|
||
|
||
# Члены комиссии
|
||
if source.startswith('commission_member:'):
|
||
parts = source.split(':')
|
||
idx, field = int(parts[1]), parts[2]
|
||
members = getattr(company, 'commission', None) or []
|
||
if idx < len(members):
|
||
return str(getattr(members[idx], field, '') or '—')
|
||
return '—'
|
||
|
||
# Склонение ФИО по падежам
|
||
if source.startswith('fio_acl:'):
|
||
who = source.split(':', 1)[1]
|
||
fio = _get_fio(company, who)
|
||
return dc.decline(fio, 'accs') if fio else '—'
|
||
|
||
return '—'
|
||
|
||
|
||
def _get_fio(company: Company, who: str) -> str:
|
||
mapping = {
|
||
'chief': 'chief_fio',
|
||
'responsible': 'responsible_fio',
|
||
'admin': 'admin_fio',
|
||
}
|
||
field = mapping.get(who, who + '_fio')
|
||
return getattr(company, field, '') or ''
|
||
|
||
|
||
def _get_security_text(ctx: Dict) -> str:
|
||
"""Возвращает текст требований для уровня защищённости."""
|
||
defence_level = ctx.get('extended_fields', {}).get('defence_level', '4')
|
||
is_list = ctx.get('is_list', [])
|
||
if not defence_level and is_list:
|
||
defence_level = str(getattr(is_list[0], 'defence_level', '4') or '4')
|
||
dl = str(defence_level).strip()[0]
|
||
|
||
TEXTS = {
|
||
'1': """Для обеспечения 1-го уровня защищённости...""",
|
||
'2': """Для обеспечения 2-го уровня защищённости...""",
|
||
'3': """Для обеспечения 3-го уровня защищённости...""",
|
||
'4': """Для обеспечения 4-го уровня защищённости персональных данных необходимо:
|
||
— режим безопасности помещений;
|
||
— сохранность носителей ПДн;
|
||
— утверждение перечня лиц с доступом к ПДн;
|
||
— использование сертифицированных СЗИ.""",
|
||
}
|
||
return TEXTS.get(dl, TEXTS['4'])
|