# -*- coding: utf-8 -*- """DokoGen — расчёт уровня защищённости ПДн по ПП РФ № 1119 от 01.11.2012. Эталонная таблица (п. 9–12 ПП-1119), сверено с текстом постановления: Специальные категории: - угрозы 1 типа → УЗ-1 (любое количество, п.9а) - угрозы 2 типа: >100к не-сотрудников → УЗ-1 (п.9б); сотрудники или ≤100к → УЗ-2 (п.10б) - угрозы 3 типа: >100к не-сотрудников → УЗ-2 (п.10е); сотрудники или ≤100к → УЗ-3 (п.11в) Биометрические: - угрозы 1 типа → УЗ-1 (п.9а); угрозы 2 типа → УЗ-2 (п.10в); угрозы 3 типа → УЗ-3 (п.11г) (без разбивки по количеству) Общедоступные: - угрозы 1 типа → УЗ-2 (п.10а) - угрозы 2 типа: >100к → УЗ-2 (п.10г); сотрудники или ≤100к → УЗ-3 (п.11а) - угрозы 3 типа → УЗ-4 (п.12а) Иные категории: - угрозы 1 типа → УЗ-1 (п.9а) - угрозы 2 типа: >100к не-сотрудников → УЗ-2 (п.10д); сотрудники или ≤100к → УЗ-3 (п.11б) - угрозы 3 типа: >100к не-сотрудников → УЗ-3 (п.11д); сотрудники или ≤100к → УЗ-4 (п.12б) Используется и в авторасчёте (ui.py), и при импорте (importer.py). """ from typing import List def calc_defence_level( categories: List[str], is_employees: bool, is_large: bool, threat_type: str, ) -> int: """Расчёт уровня защищённости ПДн (1–4) по ПП РФ № 1119. categories — список категорий ПДн ('специальные', 'биометрические', 'общедоступные', 'иные'). is_employees — True, если обрабатываются ПДн только сотрудников оператора. is_large — True, если количество субъектов > 100 000. threat_type — тип актуальных угроз: '1', '2' или '3'. """ threat = str(threat_type or '3').strip() has = lambda *names: any(n in categories for n in names) # Приоритет категорий: специальные > биометрические > общедоступные > иные if has('специальные'): return _special(threat, is_employees, is_large) if has('биометрические'): return _bio(threat) if has('общедоступные') and not has('иные'): return _public(threat, is_employees, is_large) return _other(threat, is_employees, is_large) def _special(threat: str, employees: bool, large: bool) -> int: if threat == '1': return 1 if threat == '2': return 1 if (not employees and large) else 2 # threat == '3' return 2 if (not employees and large) else 3 def _bio(threat: str) -> int: return {'1': 1, '2': 2, '3': 3}.get(threat, 3) def _public(threat: str, employees: bool, large: bool) -> int: if threat == '1': return 2 if threat == '2': return 2 if (not employees and large) else 3 # threat == '3' return 4 def _other(threat: str, employees: bool, large: bool) -> int: if threat == '1': return 1 if threat == '2': return 2 if (not employees and large) else 3 # threat == '3' return 3 if (not employees and large) else 4