feat: ОКИИ — ОКВЭД (импорт+DaData), доменное имя, поля 236 приказа ФСТЭК

- ОКВЭД: починен импорт (split по запятым/точкам с запятой), добавлен okvedExtra и domainName в DEFAULT_SETTINGS + merge полей по var в load_settings (старый import_settings.json теперь дополняется)
- DaData: проверка и подстановка основного/доп. ОКВЭД
- Вкладка «Организация»: поле «Доменное имя и внешний IP» + импорт из опросника
- Вкладка «Объекты КИИ»: поля 236 приказа — сведения (назначение, сфера, тип, архитектура, типовой отраслевой объект), субъект (12 полей), сети и ПО (категория сети, оператор, цель/способ взаимодействия, ПО/аппаратные средства, ОС, прикладные), безопасность (нарушитель, угрозы, инциденты, показатели критериев, обоснование, орг./тех. меры)
- importer: распознавание полей 236 из опросника, variables: новые переменные шаблонов
This commit is contained in:
2026-08-04 14:42:33 +04:00
parent ce1b56066a
commit 2037925986
7 changed files with 502 additions and 14 deletions
+254 -2
View File
@@ -255,6 +255,7 @@ class DokoGenApp:
("КПП:", "kpp"),
("ОГРН:", "ogrn"),
("Дата ОГРН:", "ogrn_date"),
("Доменное имя и внешний IP:", "domain_name"),
]
for i, (label, key) in enumerate(addr_fields):
ttk.Label(f2, text=label).grid(row=i, column=0, sticky=tk.W, pady=2)
@@ -589,9 +590,12 @@ class DokoGenApp:
for key, label in [('full_name', 'Наименование'), ('short_name', 'Краткое'),
('address', 'Адрес'), ('inn', 'ИНН'), ('kpp', 'КПП'),
('ogrn', 'ОГРН'), ('ogrn_date', 'Дата ОГРН'),
('okved', 'Основной ОКВЭД'),
('chief_position', 'Руководитель'), ('chief_fio', 'ФИО')]:
if d.get(key):
lines.append(f"{label}: {d[key]}")
if d.get('okved_extra'):
lines.append(f"Доп. ОКВЭД: {', '.join(d['okved_extra'])}")
else:
# ИИ-рецензия: показываем снимок полей (что ИИ увидел), замечания и предложения
lines.append(f"Оценка: {d.get('rating', '')}")
@@ -700,6 +704,15 @@ class DokoGenApp:
if comp.get(data_key) and ui_key in self._company_entries:
self._company_entries[ui_key].delete(0, tk.END)
self._company_entries[ui_key].insert(0, comp[data_key])
# ОКВЭД из DaData
if comp.get('okved') and 'okved' in self._company_entries:
self._company_entries['okved'].delete(0, tk.END)
self._company_entries['okved'].insert(0, comp['okved'])
if comp.get('okved_extra') and hasattr(self, 'okved_extra_listbox'):
self.okved_extra_listbox.delete(0, tk.END)
for code in comp['okved_extra']:
if str(code).strip():
self.okved_extra_listbox.insert(tk.END, str(code).strip())
if want('chief'):
if comp.get('chief_position') and 'chief_position' in self._official_entries:
self._official_entries['chief_position'].delete(0, tk.END)
@@ -772,6 +785,93 @@ class DokoGenApp:
nb2 = ttk.Notebook(self._is_edit_panel)
nb2.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
# ===== Вкладка «Сведения (236 приказ)» =====
info_tab = ttk.Frame(nb2)
nb2.add(info_tab, text="Сведения (236)")
info_frame = ttk.Frame(info_tab, padding=8)
info_frame.pack(fill=tk.BOTH, expand=True)
info_fields = [
("Назначение объекта:", "is_purpose_entry"),
("Сфера деятельности:", "is_sphere_entry"),
("Тип объекта:", "is_object_type_entry"),
("Архитектура объекта:", "is_architecture_entry"),
("Типовой отраслевой объект КИИ:", "is_typical_object_entry"),
]
for i, (label, attr) in enumerate(info_fields):
ttk.Label(info_frame, text=label).grid(row=i, column=0, sticky=tk.W, pady=2)
entry = ttk.Entry(info_frame, width=70)
entry.grid(row=i, column=1, padx=8, sticky=tk.EW, pady=1)
setattr(self, attr, entry)
info_frame.columnconfigure(1, weight=1)
# ===== Вкладка «Субъект» =====
subj_tab = ttk.Frame(nb2)
nb2.add(subj_tab, text="Субъект")
subj_frame = ttk.Frame(subj_tab, padding=8)
subj_frame.pack(fill=tk.BOTH, expand=True)
subj_fields = [
("Наименование субъекта:", "is_subject_name_entry"),
("Адрес местонахождения:", "is_subject_address_entry"),
("Должность руководителя:", "is_subject_chief_position_entry"),
("ФИО руководителя:", "is_subject_chief_fio_entry"),
("Должность отв. за безопасность:", "is_subject_security_position_entry"),
("ФИО отв. за безопасность:", "is_subject_security_fio_entry"),
("Структурное подразделение:", "is_subject_department_entry"),
("Руководитель подразделения:", "is_subject_department_chief_entry"),
("Телефон:", "is_subject_phone_entry"),
("Эл. почта:", "is_subject_email_entry"),
("ИНН субъекта:", "is_subject_inn_entry"),
("КПП обособленных подразделений:", "is_subject_kpp_entry"),
]
for i, (label, attr) in enumerate(subj_fields):
ttk.Label(subj_frame, text=label).grid(row=i, column=0, sticky=tk.W, pady=2)
entry = ttk.Entry(subj_frame, width=70)
entry.grid(row=i, column=1, padx=8, sticky=tk.EW, pady=1)
setattr(self, attr, entry)
subj_frame.columnconfigure(1, weight=1)
# ===== Вкладка «Сети и ПО» =====
net_tab = ttk.Frame(nb2)
nb2.add(net_tab, text="Сети и ПО")
net_frame = ttk.Frame(net_tab, padding=8)
net_frame.pack(fill=tk.BOTH, expand=True)
net_fields = [
("Категория сети электросвязи:", "is_net_category_entry"),
("Оператор связи/провайдер хостинга:", "is_net_operator_entry"),
("Цель взаимодействия с сетью:", "is_net_purpose_entry"),
("Способ взаимодействия/тип доступа:", "is_net_method_entry"),
("ПО и аппаратные средства:", "is_hw_sw_entry"),
("Общесистемное ПО (ОС, виртуализация):", "is_os_sw_entry"),
("Прикладные программы:", "is_app_sw_entry"),
]
for i, (label, attr) in enumerate(net_fields):
ttk.Label(net_frame, text=label).grid(row=i, column=0, sticky=tk.W, pady=2)
entry = ttk.Entry(net_frame, width=70)
entry.grid(row=i, column=1, padx=8, sticky=tk.EW, pady=1)
setattr(self, attr, entry)
net_frame.columnconfigure(1, weight=1)
# ===== Вкладка «Безопасность (236)» =====
sec_tab = ttk.Frame(nb2)
nb2.add(sec_tab, text="Безопасность (236)")
sec_frame = ttk.Frame(sec_tab, padding=8)
sec_frame.pack(fill=tk.BOTH, expand=True)
sec_fields = [
("Категория нарушителя:", "is_attacker_category_entry"),
("Основные угрозы БИ:", "is_threats_entry"),
("Типы компьютерных инцидентов:", "is_incidents_entry"),
("Показатели критериев значимости:", "is_criteria_values_entry"),
("Обоснование значений показателей:", "is_criteria_justification_entry"),
("Организационные меры:", "is_org_measures_entry"),
("Технические меры:", "is_tech_measures_entry"),
]
for i, (label, attr) in enumerate(sec_fields):
ttk.Label(sec_frame, text=label).grid(row=i, column=0, sticky=tk.W, pady=2)
entry = ttk.Entry(sec_frame, width=70)
entry.grid(row=i, column=1, padx=8, sticky=tk.EW, pady=1)
setattr(self, attr, entry)
sec_frame.columnconfigure(1, weight=1)
# СЗИ
def_tab = ttk.Frame(nb2)
nb2.add(def_tab, text="Средства защиты")
@@ -1021,6 +1121,43 @@ class DokoGenApp:
isys.is_local_network = self.is_lan_var.get()
isys.is_internet = self.is_internet_var.get()
isys.category = self.is_category_var.get()
# Поля 236 приказа
for attr, model_field in [
('is_purpose_entry', 'purpose'),
('is_sphere_entry', 'sphere'),
('is_object_type_entry', 'object_type'),
('is_architecture_entry', 'architecture'),
('is_typical_object_entry', 'typical_object'),
('is_subject_name_entry', 'subject_name'),
('is_subject_address_entry', 'subject_address'),
('is_subject_chief_position_entry', 'subject_chief_position'),
('is_subject_chief_fio_entry', 'subject_chief_fio'),
('is_subject_security_position_entry', 'subject_security_position'),
('is_subject_security_fio_entry', 'subject_security_fio'),
('is_subject_department_entry', 'subject_department'),
('is_subject_department_chief_entry', 'subject_department_chief'),
('is_subject_phone_entry', 'subject_phone'),
('is_subject_email_entry', 'subject_email'),
('is_subject_inn_entry', 'subject_inn'),
('is_subject_kpp_entry', 'subject_kpp'),
('is_net_category_entry', 'net_category'),
('is_net_operator_entry', 'net_operator'),
('is_net_purpose_entry', 'net_purpose'),
('is_net_method_entry', 'net_method'),
('is_hw_sw_entry', 'hw_sw'),
('is_os_sw_entry', 'os_sw'),
('is_app_sw_entry', 'app_sw'),
('is_attacker_category_entry', 'attacker_category'),
('is_threats_entry', 'threats'),
('is_incidents_entry', 'incidents'),
('is_criteria_values_entry', 'criteria_values'),
('is_criteria_justification_entry', 'criteria_justification'),
('is_org_measures_entry', 'org_measures'),
('is_tech_measures_entry', 'tech_measures'),
]:
w = getattr(self, attr, None)
if w is not None:
setattr(isys, model_field, w.get().strip())
# Списки
isys.defense_tools_list = list(self.defense_tools_listbox.get(0, tk.END))
isys.users_list = list(self.users_listbox.get(0, tk.END))
@@ -1224,6 +1361,45 @@ class DokoGenApp:
self.is_internet_var.set(isys.is_internet)
self.is_category_var.set(isys.category or "без категории")
# Поля 236 приказа
for attr, model_field in [
('is_purpose_entry', 'purpose'),
('is_sphere_entry', 'sphere'),
('is_object_type_entry', 'object_type'),
('is_architecture_entry', 'architecture'),
('is_typical_object_entry', 'typical_object'),
('is_subject_name_entry', 'subject_name'),
('is_subject_address_entry', 'subject_address'),
('is_subject_chief_position_entry', 'subject_chief_position'),
('is_subject_chief_fio_entry', 'subject_chief_fio'),
('is_subject_security_position_entry', 'subject_security_position'),
('is_subject_security_fio_entry', 'subject_security_fio'),
('is_subject_department_entry', 'subject_department'),
('is_subject_department_chief_entry', 'subject_department_chief'),
('is_subject_phone_entry', 'subject_phone'),
('is_subject_email_entry', 'subject_email'),
('is_subject_inn_entry', 'subject_inn'),
('is_subject_kpp_entry', 'subject_kpp'),
('is_net_category_entry', 'net_category'),
('is_net_operator_entry', 'net_operator'),
('is_net_purpose_entry', 'net_purpose'),
('is_net_method_entry', 'net_method'),
('is_hw_sw_entry', 'hw_sw'),
('is_os_sw_entry', 'os_sw'),
('is_app_sw_entry', 'app_sw'),
('is_attacker_category_entry', 'attacker_category'),
('is_threats_entry', 'threats'),
('is_incidents_entry', 'incidents'),
('is_criteria_values_entry', 'criteria_values'),
('is_criteria_justification_entry', 'criteria_justification'),
('is_org_measures_entry', 'org_measures'),
('is_tech_measures_entry', 'tech_measures'),
]:
w = getattr(self, attr, None)
if w is not None:
w.delete(0, tk.END)
w.insert(0, getattr(isys, model_field, '') or '')
# Списки
for lb, items in [
(self.defense_tools_listbox, isys.defense_tools_list or []),
@@ -1257,6 +1433,37 @@ class DokoGenApp:
category=src.category,
defense_tools_list=list(src.defense_tools_list),
users_list=list(src.users_list),
purpose=src.purpose,
sphere=src.sphere,
object_type=src.object_type,
architecture=src.architecture,
typical_object=src.typical_object,
subject_name=src.subject_name,
subject_address=src.subject_address,
subject_chief_position=src.subject_chief_position,
subject_chief_fio=src.subject_chief_fio,
subject_security_position=src.subject_security_position,
subject_security_fio=src.subject_security_fio,
subject_department=src.subject_department,
subject_department_chief=src.subject_department_chief,
subject_phone=src.subject_phone,
subject_email=src.subject_email,
subject_inn=src.subject_inn,
subject_kpp=src.subject_kpp,
net_category=src.net_category,
net_operator=src.net_operator,
net_purpose=src.net_purpose,
net_method=src.net_method,
hw_sw=src.hw_sw,
os_sw=src.os_sw,
app_sw=src.app_sw,
attacker_category=src.attacker_category,
threats=src.threats,
incidents=src.incidents,
criteria_values=src.criteria_values,
criteria_justification=src.criteria_justification,
org_measures=src.org_measures,
tech_measures=src.tech_measures,
)
self.model.information_systems.append(copy)
self._refresh_is_listbox()
@@ -1745,6 +1952,20 @@ class DokoGenApp:
if data.get('ispdnDepartment'):
self.model.security_department = data['ispdnDepartment']
# Доменное имя и внешний сетевой адрес
if data.get('domainName'):
entry = self._company_entries.get('domain_name')
if entry:
entry.delete(0, tk.END)
entry.insert(0, data['domainName'])
# Доменное имя и внешний сетевой адрес
if data.get('domainName'):
entry = self._company_entries.get('domain_name')
if entry:
entry.delete(0, tk.END)
entry.insert(0, data['domainName'])
# ОКВЭД (основной и дополнительные)
if data.get('okved'):
entry = self._company_entries.get('okved')
@@ -1756,7 +1977,8 @@ class DokoGenApp:
self.okved_extra_listbox.delete(0, tk.END)
extra = data['okvedExtra']
if isinstance(extra, str):
parts = [p.strip() for p in extra.replace(';', '\n').split('\n') if p.strip()]
import re as _re
parts = [p.strip() for p in _re.split(r'[;,\n]+', extra) if p.strip()]
else:
parts = [str(p).strip() for p in extra if str(p).strip()]
for p in parts:
@@ -1774,9 +1996,39 @@ class DokoGenApp:
category=is_data.get('category', ''),
defense_tools_list=is_data.get('defense_tools', []),
users_list=is_data.get('usersList', []),
purpose=is_data.get('purpose', is_data.get('description', '')),
purpose=is_data.get('purpose', ''),
structure=is_data.get('structure', ''),
software=is_data.get('software', ''),
sphere=is_data.get('sphere', ''),
object_type=is_data.get('object_type', ''),
architecture=is_data.get('architecture', ''),
typical_object=is_data.get('typical_object', ''),
subject_name=is_data.get('subject_name', ''),
subject_address=is_data.get('subject_address', ''),
subject_chief_position=is_data.get('subject_chief_position', ''),
subject_chief_fio=is_data.get('subject_chief_fio', ''),
subject_security_position=is_data.get('subject_security_position', ''),
subject_security_fio=is_data.get('subject_security_fio', ''),
subject_department=is_data.get('subject_department', ''),
subject_department_chief=is_data.get('subject_department_chief', ''),
subject_phone=is_data.get('subject_phone', ''),
subject_email=is_data.get('subject_email', ''),
subject_inn=is_data.get('subject_inn', ''),
subject_kpp=is_data.get('subject_kpp', ''),
net_category=is_data.get('net_category', ''),
net_operator=is_data.get('net_operator', ''),
net_purpose=is_data.get('net_purpose', ''),
net_method=is_data.get('net_method', ''),
hw_sw=is_data.get('hw_sw', ''),
os_sw=is_data.get('os_sw', ''),
app_sw=is_data.get('app_sw', ''),
attacker_category=is_data.get('attacker_category', ''),
threats=is_data.get('threats', ''),
incidents=is_data.get('incidents', ''),
criteria_values=is_data.get('criteria_values', ''),
criteria_justification=is_data.get('criteria_justification', ''),
org_measures=is_data.get('org_measures', ''),
tech_measures=is_data.get('tech_measures', ''),
)
self.model.information_systems.append(isys)
self._refresh_is_listbox()