Files
dokogen187/dokogen/variables.py
T
prog1764 29ba4b0532 feat: поля показателя 8 в GUI (доходы за 5 лет + суммарный ущерб)
- models: income_year_1..5 (доход за 5 лет назад ... за прошлый год),
  income_loss (суммарный экономический ущерб от нарушения критических
  процессов) — понятные названия без ΔДоходов
- вкладка «Организация» → «Финансовые показатели»: 6 новых полей
- авторасчёт п. 8: берёт доходы за 5 лет и ущерб из полей,
  считает №8 = ущерб / средний доход за 5 лет × 100% (проверено
  на примере методички: 33% → I категория)
- импорт из опросника: incomeYear1..5 / incomeLoss
- переменные шаблонов: {{company_income_year_1..5}},
  {{company_income_loss}}
2026-08-04 23:03:55 +04:00

574 lines
29 KiB
Python
Raw 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.
# -*- 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 — 187-ФЗ
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'),
('{{company_okved}}', 'A', 'Основной ОКВЭД', 'company:okved'),
('{{company_okved_extra}}', 'A', 'Дополнительные ОКВЭД', 'company:okved_extra'),
('{{company_profit}}', 'A', 'Сумма прибыли за предыдущий год (руб.)', 'company:profit'),
('{{company_profit_tax}}', 'A', 'Сумма налога с прибыли (руб.)', 'company:profit_tax'),
('{{company_income_year_1}}', 'A', 'Доход за 5 лет назад (руб.) — показатель 8', 'company:income_year_1'),
('{{company_income_year_2}}', 'A', 'Доход за 4 года назад (руб.) — показатель 8', 'company:income_year_2'),
('{{company_income_year_3}}', 'A', 'Доход за 3 года назад (руб.) — показатель 8', 'company:income_year_3'),
('{{company_income_year_4}}', 'A', 'Доход за 2 года назад (руб.) — показатель 8', 'company:income_year_4'),
('{{company_income_year_5}}', 'A', 'Доход за прошлый год (руб.) — показатель 8', 'company:income_year_5'),
('{{company_income_loss}}', 'A', 'Суммарный экономический ущерб от нарушения критических процессов (руб.) — показатель 8', 'company:income_loss'),
# === Комиссия ===
('{{commission_list}}', 'A', 'Список комиссии', 'commission_list'),
('{{fstek_district}}', 'A', 'Наименование управления ФСТЭК России (по адресу организации)', 'fstek_district'),
('{{fstek_recipient_address}}', 'A', 'Адрес управления ФСТЭК России для направления сведений', 'fstek_recipient_address'),
# === Специальные переменные ===
('{{date_year}}', 'A', 'Текущий год', 'date:year'),
('{{date}}', 'A', 'Текущая дата', 'date:full'),
('{{document_date}}', 'A', 'Дата документа', 'doc_date'),
('{{document_number}}', 'A', 'Номер документа', 'doc_number'),
# === 187-ФЗ ===
('{{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'),
('{{users}}', 'B', 'Пользователи ОКИИ (алиас)', 'is:users'),
('{{full_name_genitive}}', 'A', 'Полное наименование (род.п., алиас)', 'decl:full_name:gent'),
('{{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 = "187fz",
) -> 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 == '187fz' 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_category': isys.category or '—',
'is_users': ', '.join(isys.users_list or []) or '—',
'is_purpose': isys.purpose or '—',
'is_structure': getattr(isys, 'structure', '') or '—',
'is_software': getattr(isys, 'software', '') or '—',
'is_sphere': getattr(isys, 'sphere', '') or '—',
'is_object_type': getattr(isys, 'object_type', '') or '—',
'is_architecture': getattr(isys, 'architecture', '') or '—',
'is_typical_object': getattr(isys, 'typical_object', '') or '—',
'is_net_category': getattr(isys, 'net_category', '') or '—',
'is_net_operator': getattr(isys, 'net_operator', '') or '—',
'is_net_purpose': getattr(isys, 'net_purpose', '') or '—',
'is_net_method': getattr(isys, 'net_method', '') or '—',
'is_hw_sw': getattr(isys, 'hw_sw', '') or '—',
'is_os_sw': getattr(isys, 'os_sw', '') or '—',
'is_app_sw': getattr(isys, 'app_sw', '') 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
# Строка подписи: «___________ / » (только подчёркивание и слэш).
# Роль НЕ включаем — в шаблонах она выводится отдельной колонкой {{item.role}},
# иначе получается дубль «Председатель комиссии | Председатель комиссии: ___».
if 'Председатель' in role_disp:
sig = f"{'_' * 11} / "
sig_full = f"{role_disp}: {'_' * 11} / "
elif role_disp:
sig = f"{'_' * 17} / "
sig_full = f"{role_disp}: {'_' * 17} / "
else:
sig = f"{'_' * 17} / "
sig_full = 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_full + 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 'нет',
'category': isys.category or '—',
'users': ', '.join(isys.users_list or []) or '—',
'users_loop': users_loop,
'defense_tools_list': ', '.join(isys.defense_tools_list or []) or '—',
'is_purpose': isys.purpose or '—',
'structure': getattr(isys, 'structure', '') or '—',
'software': getattr(isys, 'software', '') or '—',
# 236 приказ
'purpose': isys.purpose or '—',
'sphere': getattr(isys, 'sphere', '') or '—',
'object_type': getattr(isys, 'object_type', '') or '—',
'architecture': getattr(isys, 'architecture', '') or '—',
'typical_object': getattr(isys, 'typical_object', '') or '—',
'subject_name': getattr(isys, 'subject_name', '') or '—',
'subject_address': getattr(isys, 'subject_address', '') or '—',
'subject_element': getattr(isys, 'subject_element', '') or '—',
'subject_inn': getattr(isys, 'subject_inn', '') or '—',
'subject_kpp': getattr(isys, 'subject_kpp', '') or '—',
'net_category': getattr(isys, 'net_category', '') or '—',
'net_operator': getattr(isys, 'net_operator', '') or '—',
'net_purpose': getattr(isys, 'net_purpose', '') or '—',
'net_method': getattr(isys, 'net_method', '') or '—',
'hw_sw': getattr(isys, 'hw_sw', '') or '—',
'os_sw': getattr(isys, 'os_sw', '') or '—',
'app_sw': getattr(isys, 'app_sw', '') or '—',
'balance_value': getattr(isys, 'balance_value', '') or '—',
'attacker_category': getattr(isys, 'attacker_category', '') or '—',
'attacker_level': getattr(isys, 'attacker_level', '') or '—',
'attacker_types': getattr(isys, 'attacker_types', '') or '—',
'threats': getattr(isys, 'threats', '') or '—',
# Алиасы под шаблон «Сведения о результатах категорирования» (ФСТЭК)
'objective': getattr(isys, 'purpose', '') or '—',
'standard_name': getattr(isys, 'typical_object', '') or '—',
'network_category': getattr(isys, 'net_category', '') or '—',
'provider': getattr(isys, 'net_operator', '') or '—',
'interaction_purpose': getattr(isys, 'net_purpose', '') or '—',
'interaction_method': getattr(isys, 'net_method', '') or '—',
'hardware': getattr(isys, 'hw_sw', '') or '—',
'system_software': getattr(isys, 'os_sw', '') or '—',
'defense_tools': ', '.join(isys.defense_tools_list or []) or '—',
'component': getattr(isys, 'subject_element', '') or '—',
'exploiting_org': getattr(isys, 'subject_name', '') or '—',
'exploiting_inn': getattr(isys, 'subject_inn', '') or '—',
'domain': getattr(isys, 'domain_name', '') or '—',
'threat_actor': getattr(isys, 'attacker_category', '') or '—',
'threat_justification': getattr(isys, 'threats', '') or '—',
'incidents': getattr(isys, 'incidents', '') or '—',
'criteria_values': getattr(isys, 'criteria_values', '') or '—',
'criteria_justification': getattr(isys, 'criteria_justification', '') or '—',
'org_measures': getattr(isys, 'org_measures', '') or '—',
'tech_measures': getattr(isys, 'tech_measures', '') or '—',
})
loops['information_systems'] = 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 _find_fstek_office(address_text: str):
"""Определяет управление ФСТЭК России по адресу организации.
Ищет город/регион из адреса в словаре fstek_offices.json.
Возвращает dict {'name': ..., 'address': ...} или None.
"""
import json as _json
import os as _os
if not address_text:
return None
dict_path = _os.path.join(_os.path.dirname(__file__), 'dictionaries', 'fstek_offices.json')
try:
with open(dict_path, encoding='utf-8') as _f:
offices = _json.load(_f)
except Exception:
return None
hay = address_text.lower()
# Нормализация сокращений: обл.→область, респ.→республика, г.о.→городской округ
hay = hay.replace('обл.', 'область').replace('обл,', 'область')
hay = hay.replace('респ.', 'республика').replace('респ,', 'республика')
hay = hay.replace('г.о.', 'городской округ').replace('г.о,', 'городской округ')
hay = hay.replace('ао', 'автономный округ')
best = None
best_len = 0
for off in offices:
# Ищем по городам
for city in off.get('cities', []):
if city in hay and len(city) > best_len:
best = off
best_len = len(city)
# Ищем по регионам (область/край/республика)
for region in off.get('regions', []):
if region in hay and len(region) > best_len:
best = off
best_len = len(region)
return best
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 ''
case_gr = dc._normalize_case(case)
# Сначала берём ГОТОВОЕ склонение из модели (пользователь мог
# подставить через Морфер/ИИ или ввести вручную), пересчитываем
# только если поле пустое
case_idx = {'nomn': 1, 'gent': 2, 'datv': 3, 'accs': 4, 'ablt': 5, 'loct': 6}.get(case_gr)
if case_idx:
if field == 'full_name':
ready = getattr(company, f'company_name_{case_idx}', '') or ''
if ready:
return ready
elif field == 'short_name':
ready = getattr(company, f'short_name_{case_idx}', '') or ''
if ready:
return ready
return dc.company_name_decline(name, case_gr) 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 in ('fstek_recipient_address', 'fstek_district', 'fstek_office_name'):
addr = getattr(company, 'address', '') or ''
off = _find_fstek_office(addr)
if off:
if source == 'fstek_recipient_address':
return off.get('address', '') or ''
return off.get('name', '') or ''
return ''
# Даты
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',
'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:
"""Возвращает текст требований к защите объекта КИИ по категории значимости."""
is_list = ctx.get('is_list', [])
category = ''
if is_list:
category = str(getattr(is_list[0], 'category', '') or '').strip()
TEXTS = {
'1': """Для значимого объекта КИИ 1-й категории значимости необходимо:
— создание системы безопасности значимого объекта КИИ;
— обеспечение защиты информации от компьютерных атак;
— категорирование объектов КИИ и контроль его результатов.""",
'2': """Для значимого объекта КИИ 2-й категории значимости необходимо:
— создание системы безопасности значимого объекта КИИ;
— обеспечение защиты информации от компьютерных атак.""",
'3': """Для значимого объекта КИИ 3-й категории значимости необходимо:
— обеспечение защиты информации от компьютерных атак.""",
}
if category in TEXTS:
return TEXTS[category]
return """Для объекта КИИ, не являющегося значимым, необходимо:
— принятие мер по защите информации, предусмотренных законодательством РФ о безопасности КИИ."""