feat: применение предложений ИИ по безопасности по галочкам (нарушитель/угрозы/инциденты/показатели/обоснование/меры)

This commit is contained in:
2026-08-05 01:35:30 +04:00
parent b3ae4c22cf
commit 638e2446d5
+111 -30
View File
@@ -3043,47 +3043,128 @@ class DokoGenApp:
self.log(f"❌ Проверка безопасности: {err}") self.log(f"❌ Проверка безопасности: {err}")
return return
d = result['data'] d = result['data']
lines = [f"Источник: {result['source']}"]
lines.append(f"Оценка: {d.get('rating', 'warning')}")
notes = d.get('notes') or [] notes = d.get('notes') or []
if notes:
lines.append("\nЗамечания:")
for n in notes:
lines.append(f"{n}")
props = d.get('proposals') or {} props = d.get('proposals') or {}
if any(props.values()): self._verify_log(f"🤖 ИИ: оценка {d.get('rating', 'warning')}, замечаний: {len(notes)}")
lines.append("\nПредложения ИИ:")
for key, label in [('attacker_category', 'Нарушитель'),
('threats', 'Угрозы'),
('incidents', 'Инциденты'),
('criteria_values', 'Показатели'),
('criteria_justification', 'Обоснование'),
('org_measures', 'Орг. меры'),
('tech_measures', 'Тех. меры')]:
v = props.get(key)
if v:
lines.append(f" {label}: {v}")
self._verify_log('\n'.join(lines))
self.log(f"🤖 ИИ: оценка {d.get('rating', 'warning')}, замечаний: {len(notes)}") self.log(f"🤖 ИИ: оценка {d.get('rating', 'warning')}, замечаний: {len(notes)}")
# Показываем результат отдельным окном (панель проверки на вкладке «Организация» может быть не видна) self._show_security_ai_result(notes, props, d.get('rating', 'warning'), result.get('source', 'ИИ'))
self._show_security_ai_result(lines)
def _show_security_ai_result(self, lines): def _show_security_ai_result(self, lines, proposals=None, rating='', source='ИИ'):
"""Отдельное окно с результатом ИИ-проверки безопасности.""" """Окно с результатом ИИ-проверки безопасности.
Если переданы proposals — показываем каждый раздел с галочкой
и кнопкой «Применить отмеченное» (применение по галочкам, ИИ может ошибаться).
Иначе — просто текстовый результат (для остальных вызовов)."""
try: try:
if proposals is None:
win = tk.Toplevel(self.root)
win.title("🤖 Проверка безопасности — результат ИИ")
win.geometry("760x560")
win.transient(self.root)
txt = scrolledtext.ScrolledText(win, wrap=tk.WORD, font=('', 10), state=tk.NORMAL)
txt.pack(fill=tk.BOTH, expand=True, padx=10, pady=(10, 4))
txt.insert('1.0', '\n'.join(lines))
txt.config(state=tk.DISABLED)
ttk.Button(win, text="Закрыть", command=win.destroy).pack(pady=(0, 10))
win.grab_set()
return
# Режим с галочками: предложения ИИ по разделам безопасности
win = tk.Toplevel(self.root) win = tk.Toplevel(self.root)
win.title("🤖 Проверка безопасности — результат ИИ") win.title("🤖 Проверка безопасности — предложения ИИ")
win.geometry("760x560") win.geometry("960x760")
win.transient(self.root) win.transient(self.root)
txt = scrolledtext.ScrolledText(win, wrap=tk.WORD, font=('', 10), state=tk.NORMAL) frm = ttk.Frame(win, padding=8)
txt.pack(fill=tk.BOTH, expand=True, padx=10, pady=(10, 4)) frm.pack(fill=tk.BOTH, expand=True)
txt.insert('1.0', '\n'.join(lines))
txt.config(state=tk.DISABLED) header = f"Оценка ИИ: {rating}. Отметьте галочками, что применить в объект (ИИ может ошибаться — проверяйте)."
ttk.Button(win, text="Закрыть", command=win.destroy).pack(pady=(0, 10)) ttk.Label(frm, text=header, font=('Arial', 10, 'bold'),
wraplength=900, justify=tk.LEFT).pack(anchor=tk.W)
if notes_lines := [l for l in (lines or []) if l]:
ttk.Label(frm, text="Замечания:", font=('', 9, 'bold')).pack(anchor=tk.W, pady=(6, 0))
t_notes = scrolledtext.ScrolledText(frm, height=4, wrap=tk.WORD, font=('', 9))
t_notes.pack(fill=tk.X)
t_notes.insert('1.0', '\n'.join(notes_lines))
t_notes.config(state=tk.DISABLED)
sections = [
('attacker_category', 'Нарушитель (категория/уровень)'),
('threats', 'Угрозы'),
('incidents', 'Инциденты'),
('criteria_values', 'Показатели критериев'),
('criteria_justification', 'Обоснование значений'),
('org_measures', 'Орг. меры'),
('tech_measures', 'Тех. меры'),
]
entries, checks = {}, {}
for key, label in sections:
v = proposals.get(key)
if not v:
continue
row = ttk.Frame(frm)
row.pack(fill=tk.X, pady=(4, 0))
var = tk.BooleanVar(value=True)
ttk.Checkbutton(row, text=f"{label}:", variable=var).pack(side=tk.LEFT)
checks[key] = var
e = scrolledtext.ScrolledText(frm, height=2, wrap=tk.WORD, font=('', 9))
e.pack(fill=tk.X)
e.insert('1.0', v)
entries[key] = e
btn_row = ttk.Frame(frm)
btn_row.pack(fill=tk.X, pady=(8, 0))
ttk.Button(btn_row, text="✔️ Применить отмеченное",
command=lambda: self._apply_security_ai_proposals(entries, checks, win)).pack(side=tk.LEFT, padx=2)
ttk.Button(btn_row, text="Отмена", command=win.destroy).pack(side=tk.LEFT, padx=2)
win.grab_set() win.grab_set()
except Exception: except Exception:
pass pass
def _apply_security_ai_proposals(self, entries, checks, win):
"""Применить отмеченные предложения ИИ к полям безопасности объекта."""
def _wanted(key):
return checks.get(key, tk.BooleanVar(value=True)).get()
def _text(key):
e = entries.get(key)
return e.get('1.0', 'end-1c').strip() if e else ''
def _fill_listbox(lb, text):
if not lb:
return
lb.delete(0, tk.END)
for line in (text or '').split('\n'):
line = line.strip()
if line:
lb.insert(tk.END, line)
try:
if _wanted('attacker_category'):
val = _text('attacker_category')
if val and hasattr(self, 'is_attacker_category_combo'):
values = list(self.is_attacker_category_combo['values'])
exact = next((v for v in values if v.lower() == val.lower()), None)
self.is_attacker_category_combo.set(exact or val)
if _wanted('threats'):
_fill_listbox(getattr(self, 'is_threats_listbox', None), _text('threats'))
if _wanted('incidents'):
_fill_listbox(getattr(self, 'is_incidents_listbox', None), _text('incidents'))
if _wanted('criteria_values'):
_fill_listbox(getattr(self, 'is_criteria_values_listbox', None), _text('criteria_values'))
if _wanted('criteria_justification'):
_fill_listbox(getattr(self, 'is_criteria_justification_listbox', None), _text('criteria_justification'))
if _wanted('org_measures'):
_fill_listbox(getattr(self, 'is_org_measures_listbox', None), _text('org_measures'))
if _wanted('tech_measures'):
_fill_listbox(getattr(self, 'is_tech_measures_listbox', None), _text('tech_measures'))
self._collect_is()
self.log("🤖 Предложения ИИ по безопасности применены (по галочкам)")
win.destroy()
messagebox.showinfo("Безопасность", "Отмеченные предложения ИИ применены к объекту.\nПроверьте результат и при необходимости поправьте вручную.")
except Exception as e:
messagebox.showerror("Ошибка", f"Не удалось применить: {e}")
def _fill_subject_from_company(self): def _fill_subject_from_company(self):
"""Заполнить поля субъекта данными организации (если объект в той же организации).""" """Заполнить поля субъекта данными организации (если объект в той же организации)."""
if self._current_is_idx is None: if self._current_is_idx is None: