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:
+83
-57
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user