показатели значимости: автогенерация таблицы 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
|
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
|
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