feat: окно критериев значимости — дословная таблица ПП РФ № 127

- criteria_127.json переписан точно по Постановлению Правительства РФ
  от 08.02.2019 № 127 (приложение): 5 критериев (социальная, политическая,
  экономическая, экологическая, значимость для обороны страны), 30 показателей
  (1-14 с подпунктами а/б/в/г), значения сразу привязаны к категориям III/II/I
- окно 980x700 со скроллом: у каждого показателя выбор значения из документа
  («III категория: …», «II категория: …», «I категория: …»)
- категория значимости по наивысшей выбранной: I→1-я, II→2-я, III→3-я, иначе без категории
- обоснование: автогенерация текста с перечнем выбранных показателей,
  если поле пустое; кнопки «Рассчитать категорию» и «Сохранить»
- категория сразу подставляется в поле категории значимости объекта
This commit is contained in:
2026-08-04 19:20:52 +04:00
parent 879ada5859
commit 8e647fc79a
2 changed files with 322 additions and 134 deletions
+83 -57
View File
@@ -1746,12 +1746,16 @@ class DokoGenApp:
self.users_listbox.insert(tk.END, user)
def _open_criteria_127_dialog(self):
"""Окно заполнения показателей критериев значимости по таблице ПП РФ № 127.
"""Окно заполнения показателей критериев значимости по ПП РФ № 127.
Показатели и обоснование объединены в одном окне:
для каждого показателя выбирается значение (1-4), внизу — общее обоснование.
По максимальному баллу автоматически рассчитывается категория значимости:
4 → 1-я, 3 → 2-я, 2 → 3-я, иначе «без категории».
Таблица перенесена дословно из Постановления Правительства РФ от 08.02.2019 № 127:
5 критериев (социальная, политическая, экономическая, экологическая,
значимость для обороны страны), показатели 1-14 с подпунктами,
значения сразу привязаны к категориям III / II / I.
Категория значимости объекта определяется по наибольшей категории,
которой соответствует хотя бы один выбранный показатель:
I → 1-я категория, II → 2-я, III → 3-я, иначе «без категории».
"""
import json as _json
dict_path = os.path.join(os.path.dirname(__file__), 'dictionaries', 'criteria_127.json')
@@ -1764,7 +1768,7 @@ class DokoGenApp:
dialog = tk.Toplevel(self.root)
dialog.title("Показатели критериев значимости (ПП РФ № 127)")
dialog.geometry("860x640")
dialog.geometry("980x700")
dialog.transient(self.root)
dialog.grab_set()
@@ -1780,39 +1784,46 @@ class DokoGenApp:
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
ttk.Label(body, text="Показатели критериев значимости объекта КИИ (ПП РФ № 127)",
font=('Arial', 10, 'bold')).pack(anchor=tk.W, pady=(0, 4))
ttk.Label(body, text="По каждому показателю выберите значение от 1 до 4 "
"(4 — максимальная значимость). Категория рассчитывается автоматически.",
foreground='gray', wraplength=780).pack(anchor=tk.W, pady=(0, 8))
ttk.Label(body, text="Показатели критериев значимости объекта КИИ",
font=('Arial', 11, 'bold')).pack(anchor=tk.W, pady=(0, 2))
ttk.Label(body, text="Постановление Правительства РФ от 08.02.2019 № 127 «Об утверждении Правил категорирования "
"объектов критической информационной инфраструктуры РФ» (приложение). "
"По каждому показателю выберите значение, которому соответствует объект. "
"Категория определяется по наивысшей из выбранных.",
foreground='gray', wraplength=920, justify=tk.LEFT).pack(anchor=tk.W, pady=(0, 8))
# Текущие значения из listbox (чтобы предзаполнить, если уже заполнено)
cur_values = set()
if hasattr(self, 'is_criteria_values_listbox'):
for i in range(self.is_criteria_values_listbox.size()):
cur_values.add(str(self.is_criteria_values_listbox.get(i)))
rows = [] # (num, name, values{III:..,II:..,I:..}, var)
rows = [] # (критерий, показатель, scale, var)
for crit in criteria:
grp = ttk.LabelFrame(body, text=crit.get('criteria', ''), padding=6)
grp.pack(fill=tk.X, pady=4)
for ind in crit.get('indicators', []):
num = ind.get('num', '')
name = ind.get('name', '')
scale = ind.get('scale', {})
values = ind.get('values', {})
row = ttk.Frame(grp)
row.pack(fill=tk.X, pady=2)
ttk.Label(row, text=name, wraplength=520, justify=tk.LEFT).pack(side=tk.LEFT)
# Номер показателя
ttk.Label(row, text=num, width=4, font=('Arial', 9, 'bold')).pack(side=tk.LEFT)
# Текст показателя
ttk.Label(row, text=name, wraplength=640, justify=tk.LEFT).pack(side=tk.LEFT)
# Выбор значения (категория)
var = tk.StringVar(value='')
combo = ttk.Combobox(row, textvariable=var, state='readonly', width=6,
values=['', '1', '2', '3', '4'])
options = ['']
for cat in ('III', 'II', 'I'):
v = values.get(cat, '')
if v:
options.append(f"{cat} категория: {v}")
combo = ttk.Combobox(row, textvariable=var, state='readonly', width=34,
values=options)
combo.pack(side=tk.RIGHT, padx=6)
rows.append((crit.get('criteria', ''), name, scale, var))
rows.append((num, name, values, var))
# Обоснование
ttk.Label(body, text="Обоснование полученных значений:", font=('Arial', 9, 'bold')).pack(
anchor=tk.W, pady=(10, 2))
justify_txt = tk.Text(body, width=90, height=5, wrap=tk.WORD)
justify_txt = tk.Text(body, width=110, height=5, wrap=tk.WORD)
justify_txt.pack(fill=tk.X, pady=(0, 6))
# предзаполнить обоснование из listbox, если есть
if hasattr(self, 'is_criteria_justification_listbox') and self.is_criteria_justification_listbox.size():
justify_txt.insert('1.0', '\n'.join(
self.is_criteria_justification_listbox.get(i)
@@ -1821,52 +1832,67 @@ class DokoGenApp:
result_lbl = ttk.Label(body, text="Категория значимости: —", font=('Arial', 10, 'bold'))
result_lbl.pack(anchor=tk.W, pady=(6, 0))
def _selected():
"""Список выбранных: [(num, name, категория, значение)]."""
out = []
for num, name, values, var in rows:
s = var.get().strip()
if not s:
continue
cat = s.split(' ')[0] # III/II/I
val = s.split(': ', 1)[1] if ': ' in s else ''
out.append((num, name, cat, val))
return out
def _calc_category(selected):
"""Категория по наивысшей из выбранных."""
cats = [c for _, _, c, _ in selected]
if 'I' in cats:
return '1'
if 'II' in cats:
return '2'
if 'III' in cats:
return '3'
return 'без категории'
def _calc():
vals = []
for crit_name, ind_name, scale, var in rows:
v = var.get().strip()
if v:
desc = scale.get(v, '')
vals.append(f"{crit_name}: {ind_name}{v}" + (f" ({desc})" if desc else ""))
if vals:
self.is_criteria_values_listbox.delete(0, tk.END)
for line in vals:
self.is_criteria_values_listbox.insert(tk.END, line)
# Категория по максимальному баллу
nums = [int(var.get()) for _, _, _, var in rows if var.get().strip().isdigit()]
if nums:
mx = max(nums)
cat = {4: '1', 3: '2', 2: '3'}.get(mx, 'без категории')
result_lbl.config(text=f"Категория значимости: {cat} (макс. балл {mx})")
else:
selected = _selected()
if not selected:
result_lbl.config(text="Категория значимости: — (ничего не выбрано)")
return
cat = _calc_category(selected)
result_lbl.config(text=f"Категория значимости: {cat} (выбрано показателей: {len(selected)})")
def _save():
vals = []
for crit_name, ind_name, scale, var in rows:
v = var.get().strip()
if v:
desc = scale.get(v, '')
vals.append(f"{crit_name}: {ind_name}{v}" + (f" ({desc})" if desc else ""))
selected = _selected()
# Заполняем список показателей
lines = []
for num, name, cat, val in selected:
lines.append(f"Показатель {num}. {name}{cat} категория: {val}")
if hasattr(self, 'is_criteria_values_listbox'):
self.is_criteria_values_listbox.delete(0, tk.END)
for line in vals:
for line in lines:
self.is_criteria_values_listbox.insert(tk.END, line)
# Обоснование
# Категория значимости
cat = _calc_category(selected)
if hasattr(self, 'is_category_var'):
self.is_category_var.set(cat)
# Обоснование: если поле пустое — сформируем автоматически
j = justify_txt.get('1.0', 'end-1c').strip()
auto_just = (
f"Категория значимости объекта КИИ определена в соответствии с Правилами категорирования "
f"объектов критической информационной инфраструктуры РФ (ПП РФ от 08.02.2019 № 127). "
f"Объект отнесён к {cat} категории значимости. Основания: "
+ "; ".join(f"показатель {num} ({cat} категория)" for num, _, cat, _ in selected) + "."
)
if not j:
j = auto_just
if hasattr(self, 'is_criteria_justification_listbox'):
self.is_criteria_justification_listbox.delete(0, tk.END)
for line in j.split('\n'):
if line.strip():
self.is_criteria_justification_listbox.insert(tk.END, line.strip())
# Категория → в поле категории объекта
nums = [int(var.get()) for _, _, _, var in rows if var.get().strip().isdigit()]
if nums:
mx = max(nums)
cat = {4: '1', 3: '2', 2: '3'}.get(mx, 'без категории')
if hasattr(self, 'is_category_var'):
self.is_category_var.set(cat)
self.log(f"📋 Показатели критериев значимости сохранены ({len(vals)} шт.)")
self.log(f"📋 Критерии значимости (ПП 127) сохранены: категория {cat}, показателей: {len(selected)}")
dialog.destroy()
btn_frame = ttk.Frame(body)