показатели значимости: автогенерация таблицы 1-14 в шаблон категорирования (обоснование применимости по сфере + авторасчёт 8/9 из данных организации)
This commit is contained in:
@@ -373,9 +373,204 @@ def _build_loop_data(company: Company, is_list: List) -> Dict[str, List[Dict]]:
|
||||
})
|
||||
loops['employees_access'] = items
|
||||
|
||||
# --- Шаблон «Сведения о результатах категорирования» (ФСТЭК) ---
|
||||
# Цикл объектов КИИ — тот же список, что information_systems.
|
||||
if is_list:
|
||||
loops['kii_objects'] = loops.get('information_systems', [])
|
||||
|
||||
# Цикл показателей критериев значимости (ПП-127): автоматически
|
||||
# формируем строки таблицы с обоснованием «применим/не применим» по сфере
|
||||
# и расчётом для показателей 8 и 9 (как в примере заказчика).
|
||||
loops['categorization_indicators'] = _build_categorization_indicators(company, is_list)
|
||||
|
||||
return loops
|
||||
|
||||
|
||||
# ============================================================
|
||||
# ПОКАЗАТЕЛИ КРИТЕРИЕВ ЗНАЧИМОСТИ (ПП-127)
|
||||
# ============================================================
|
||||
|
||||
def _num(s):
|
||||
"""«121 118 270 руб», «1 000», «2 212.5» → число (float) или None."""
|
||||
s = str(s or '').strip()
|
||||
if not s:
|
||||
return None
|
||||
s = s.replace('\u00a0', ' ').replace(' ', '')\
|
||||
.replace('руб', '').replace('₽', '').replace('р.', '').replace(',', '.')
|
||||
try:
|
||||
return float(s)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
def _load_json(name):
|
||||
import json as _json
|
||||
import os as _os
|
||||
p = _os.path.join(_os.path.dirname(_os.path.abspath(__file__)), 'dictionaries', name)
|
||||
try:
|
||||
with open(p, encoding='utf-8') as f:
|
||||
return _json.load(f)
|
||||
except Exception:
|
||||
return {}
|
||||
|
||||
|
||||
def _calc_criterion_8_9(company, isys, num):
|
||||
"""Расчёт показателя 8 (ущерб субъекту КИИ) или 9 (ущерб бюджету РФ).
|
||||
|
||||
Возвращает (текст_обоснования, категория или None).
|
||||
Логика — как в авторасчёте программы (окно ПП-127), текст — по образцу
|
||||
заполнения заказчика для ОПК.
|
||||
"""
|
||||
# ΔДоходов: ущерб из вкладки «Организация» или балансовая стоимость объекта
|
||||
d = _num(getattr(company, 'income_loss', '') or '')
|
||||
if d is None:
|
||||
d = _num(getattr(isys, 'balance_value', '') or '')
|
||||
# Доходы за 5 лет
|
||||
incs = [_num(getattr(company, f'income_year_{i}', '') or '') for i in range(1, 6)]
|
||||
incs = [x for x in incs if x is not None]
|
||||
r_avg = (sum(incs) / len(incs)) if incs else None
|
||||
|
||||
if num == '8':
|
||||
if d is None or not r_avg:
|
||||
return ("Показатель применим. Для расчёта заполните данные организации: "
|
||||
"«Снижение уровня дохода» и доходы за 5 лет, или балансовую стоимость объекта КИИ.", None)
|
||||
val_pct = d / r_avg * 100
|
||||
if val_pct > 20:
|
||||
cat = 'I'
|
||||
elif val_pct > 10:
|
||||
cat = 'II'
|
||||
elif val_pct >= 1:
|
||||
cat = 'III'
|
||||
else:
|
||||
cat = None
|
||||
text = (f"Реализация компьютерных атак на объект не приведет к снижению доходов субъекта КИИ. "
|
||||
f"Объем среднегодового дохода субъекта КИИ (с учетом НДС), усредненный за прошедший "
|
||||
f"5-летний период, составляет {r_avg:,.2f} руб. В случае выхода из строя объекта КИИ "
|
||||
f"потребуется замена, установка и настройка, стоимость которых составит около {d:,.2f} руб. "
|
||||
f"Абсолютный ущерб субъекта КИИ: U={d:,.2f} руб. "
|
||||
f"Показатель ущерба субъекту КИИ: U_8 = U / R_год = {d:,.2f} / {r_avg:,.2f} "
|
||||
f"= {val_pct:.6f} %.")
|
||||
if cat:
|
||||
text += (f" Так как {val_pct:.6f} % более 1% (пороговое значение для 3-й категории "
|
||||
f"значимости согласно Постановлению № 127), по данному показателю объекту "
|
||||
f"присваивается {cat}-я категория значимости.")
|
||||
else:
|
||||
text += (f" Так как {val_pct:.6f} % менее 1% (пороговое значение для 3-й категории "
|
||||
f"значимости согласно Постановлению № 127), по данному показателю объекту "
|
||||
f"категория значимости не присваивается.")
|
||||
return text, cat
|
||||
|
||||
# num == '9'
|
||||
if d is None:
|
||||
return ("Показатель применим. Для расчёта заполните данные организации: "
|
||||
"«Снижение уровня дохода» или балансовую стоимость объекта КИИ.", None)
|
||||
profit = _num(getattr(company, 'profit', '') or '')
|
||||
tax = _num(getattr(company, 'profit_tax', '') or '')
|
||||
if profit and tax is not None and profit > 0:
|
||||
knp = tax / profit
|
||||
dnp = d * knp
|
||||
if d > profit:
|
||||
dnp = tax
|
||||
else:
|
||||
knp = 0.0
|
||||
dnp = 0.0
|
||||
DEFAULT_BUDGET_AVG = 21228053357700.0 # Бусредн — прогноз годового дохода федбюджета, усреднённый за 3 года
|
||||
b = DEFAULT_BUDGET_AVG
|
||||
val_pct = dnp / b * 100
|
||||
if val_pct > 0.001:
|
||||
cat = 'I'
|
||||
elif val_pct > 0.0006:
|
||||
cat = 'II'
|
||||
elif val_pct > 0.0003:
|
||||
cat = 'III'
|
||||
else:
|
||||
cat = None
|
||||
text = (f"Реализация компьютерных атак на объект не приведет к снижению доходов федерального бюджета. "
|
||||
f"Объем прогнозируемого годового дохода федерального бюджета Российской Федерации, "
|
||||
f"усредненный за планируемый трехлетний период, составляет {b:,.2f} руб. "
|
||||
f"Коэффициент выплат (отчислений) от налога на прибыль субъекта КИИ = {knp:.4f}. "
|
||||
f"В случае выхода из строя объекта КИИ потребуется замена, установка и настройка, "
|
||||
f"стоимость которых составит около {d:,.2f} руб. "
|
||||
f"Снижение выплат (отчислений) от налога на прибыль субъекта КИИ в бюджеты Российской Федерации "
|
||||
f"Уабс = {d:,.2f} × {knp:.4f} = {dnp:,.2f} руб. (приближённо). "
|
||||
f"Показатель ущерба бюджетам = {val_pct:.6f} %.")
|
||||
if cat:
|
||||
text += (f" Так как {val_pct:.6f} % более 0,0003% (пороговое значение для 3-й категории "
|
||||
f"значимости согласно Постановлению № 127), по данному показателю объекту "
|
||||
f"присваивается {cat}-я категория значимости.")
|
||||
else:
|
||||
text += (f" Так как {val_pct:.6f} % менее 0,0003% (пороговое значение для 3-й категории "
|
||||
f"значимости согласно Постановлению № 127), по данному показателю объекту "
|
||||
f"категория значимости не присваивается.")
|
||||
return text, cat
|
||||
|
||||
|
||||
def _build_categorization_indicators(company, is_list):
|
||||
"""Строки таблицы показателей критериев значимости для шаблона
|
||||
«Сведения о результатах категорирования» (цикл categorization_indicators).
|
||||
|
||||
Для каждого показателя ПП-127 (5 групп, показатели 1–14 с подпунктами):
|
||||
section, name, number, value, justification, category.
|
||||
- Если показатель не применим для сферы объекта — value = «Показатель не применим.»,
|
||||
justification — текст из словаря обоснований по сфере (или заготовка).
|
||||
- Если применим (8/9) — автоматический расчёт из данных организации.
|
||||
"""
|
||||
import re as _re
|
||||
criteria = _load_json('criteria_127.json')
|
||||
applicability = _load_json('criteria_applicability.json')
|
||||
justifications = _load_json('criteria_justifications.json')
|
||||
if not isinstance(criteria, list):
|
||||
criteria = []
|
||||
|
||||
sphere = getattr(is_list[0], 'sphere', '') if is_list else ''
|
||||
sphere = (sphere or '').strip()
|
||||
appl = applicability.get(sphere) if isinstance(applicability, dict) else None
|
||||
just = (justifications.get(sphere) or {}) if isinstance(justifications, dict) else {}
|
||||
|
||||
isys = is_list[0] if is_list else None
|
||||
|
||||
rows = []
|
||||
for grp in criteria:
|
||||
section = grp.get('criteria', '')
|
||||
for ind in grp.get('indicators', []):
|
||||
num = str(ind.get('num', ''))
|
||||
name = ind.get('name', '')
|
||||
m = _re.match(r'(\d+)', num)
|
||||
grp_num = m.group(1) if m else num
|
||||
applicable = appl is None or grp_num in appl
|
||||
|
||||
value = '—'
|
||||
justification = '—'
|
||||
category = '—'
|
||||
if not applicable:
|
||||
value = 'Показатель не применим.'
|
||||
justification = (just.get(num) or just.get(grp_num)
|
||||
or f"Показатель не применим к объекту КИИ организации сферы «{sphere or '—'}».")
|
||||
elif grp_num in ('8', '9'):
|
||||
text, cat = _calc_criterion_8_9(company, isys, grp_num)
|
||||
if text:
|
||||
value = text
|
||||
if cat:
|
||||
category = f"{cat}-я категория"
|
||||
else:
|
||||
category = '—'
|
||||
else:
|
||||
# Применимый показатель без авторасчёта — оператор заполняет значение
|
||||
value = '—'
|
||||
justification = (just.get(num) or just.get(grp_num)
|
||||
or 'Показатель применим к объекту КИИ; значение определяется по данным объекта.')
|
||||
|
||||
rows.append({
|
||||
'section': section,
|
||||
'name': name,
|
||||
'number': num,
|
||||
'value': value,
|
||||
'justification': justification,
|
||||
'category': category,
|
||||
})
|
||||
return rows
|
||||
|
||||
|
||||
# ============================================================
|
||||
# РЕЗОЛВЕР ЗНАЧЕНИЙ
|
||||
# ============================================================
|
||||
|
||||
Reference in New Issue
Block a user