feat: применение предложений ИИ по безопасности по галочкам (нарушитель/угрозы/инциденты/показатели/обоснование/меры)
This commit is contained in:
+111
-30
@@ -3043,47 +3043,128 @@ class DokoGenApp:
|
||||
self.log(f"❌ Проверка безопасности: {err}")
|
||||
return
|
||||
d = result['data']
|
||||
lines = [f"Источник: {result['source']}"]
|
||||
lines.append(f"Оценка: {d.get('rating', 'warning')}")
|
||||
notes = d.get('notes') or []
|
||||
if notes:
|
||||
lines.append("\nЗамечания:")
|
||||
for n in notes:
|
||||
lines.append(f" • {n}")
|
||||
props = d.get('proposals') or {}
|
||||
if any(props.values()):
|
||||
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._verify_log(f"🤖 ИИ: оценка {d.get('rating', 'warning')}, замечаний: {len(notes)}")
|
||||
self.log(f"🤖 ИИ: оценка {d.get('rating', 'warning')}, замечаний: {len(notes)}")
|
||||
# Показываем результат отдельным окном (панель проверки на вкладке «Организация» может быть не видна)
|
||||
self._show_security_ai_result(lines)
|
||||
self._show_security_ai_result(notes, props, d.get('rating', 'warning'), result.get('source', 'ИИ'))
|
||||
|
||||
def _show_security_ai_result(self, lines):
|
||||
"""Отдельное окно с результатом ИИ-проверки безопасности."""
|
||||
def _show_security_ai_result(self, lines, proposals=None, rating='', source='ИИ'):
|
||||
"""Окно с результатом ИИ-проверки безопасности.
|
||||
|
||||
Если переданы proposals — показываем каждый раздел с галочкой
|
||||
и кнопкой «Применить отмеченное» (применение по галочкам, ИИ может ошибаться).
|
||||
Иначе — просто текстовый результат (для остальных вызовов)."""
|
||||
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.title("🤖 Проверка безопасности — результат ИИ")
|
||||
win.geometry("760x560")
|
||||
win.title("🤖 Проверка безопасности — предложения ИИ")
|
||||
win.geometry("960x760")
|
||||
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))
|
||||
frm = ttk.Frame(win, padding=8)
|
||||
frm.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
header = f"Оценка ИИ: {rating}. Отметьте галочками, что применить в объект (ИИ может ошибаться — проверяйте)."
|
||||
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()
|
||||
except Exception:
|
||||
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):
|
||||
"""Заполнить поля субъекта данными организации (если объект в той же организации)."""
|
||||
if self._current_is_idx is None:
|
||||
|
||||
Reference in New Issue
Block a user