fix: ОКВЭД импорт/DaData/словарь+интернет, доменное имя, вкладка ОКИИ → вложенные вкладки, словарики, СЗИ разбивка, адрес отдельно
- ОКВЭД: resolve_okved (словарь okved.json + DaData suggest okved2), применяется при импорте и DaData; галочки «ОКВЭД осн./доп.» в «что применять» - DaData: показ и подстановка доп. ОКВЭД - доменное имя: поле во вкладке «Организация» + импорт - вкладка «Объекты КИИ»: сверху Наименование/Описание/Категория, остальное вкладками: Основное (назначение, сфера, тип, архитектура, типовой отраслевой объект — со словариками и выпадающими списками), Субъект, Сети и ПО, Безопасность (236), СЗИ, Пользователи ОКИИ - новые словари: spheres.json, object_types.json, architectures.json, typical_objects.json - адрес объекта — в отдельное поле (не в описание) - СЗИ: разбивка слипшихся названий (Kaspersky...)
This commit is contained in:
@@ -0,0 +1,9 @@
|
||||
[
|
||||
"одноранговая сеть",
|
||||
"клиент-серверная система",
|
||||
"технология \"тонкий клиент\"",
|
||||
"сеть передачи данных",
|
||||
"система диспетчерского управления и контроля",
|
||||
"распределенная система управления",
|
||||
"иная архитектура"
|
||||
]
|
||||
@@ -0,0 +1,5 @@
|
||||
[
|
||||
"Информационная система",
|
||||
"Автоматизированная система управления",
|
||||
"Информационно-телекоммуникационная сеть"
|
||||
]
|
||||
@@ -0,0 +1,17 @@
|
||||
[
|
||||
"Здравоохранение",
|
||||
"Наука",
|
||||
"Транспорт",
|
||||
"Связь",
|
||||
"Энергетика",
|
||||
"Государственная регистрация прав",
|
||||
"Банковская сфера и финансовый рынок",
|
||||
"Топливно-энергетический комплекс",
|
||||
"Атомная энергетика",
|
||||
"Оборонная промышленность",
|
||||
"Ракетно-космическая промышленность",
|
||||
"Горнодобывающая промышленность",
|
||||
"Металлургическая промышленность",
|
||||
"Химическая промышленность",
|
||||
"Иная сфера деятельности"
|
||||
]
|
||||
@@ -0,0 +1,13 @@
|
||||
[
|
||||
"Информационная система управления",
|
||||
"Автоматизированная система управления технологическим процессом",
|
||||
"Информационно-телекоммуникационная сеть",
|
||||
"Автоматизированная система управления производством",
|
||||
"Система диспетчерского управления",
|
||||
"Система управления базами данных",
|
||||
"Автоматизированная система научных исследований",
|
||||
"Система электронного документооборота",
|
||||
"Информационная система расчетов",
|
||||
"Система управления финансами",
|
||||
"Иной типовой отраслевой объект КИИ"
|
||||
]
|
||||
+28
-2
@@ -279,7 +279,7 @@ def _import_187_is_list(wb, data, settings, log_fn):
|
||||
continue
|
||||
|
||||
is_obj = {
|
||||
'name': '', 'description': '', 'usersList': [],
|
||||
'name': '', 'description': '', 'address': '', 'usersList': [],
|
||||
'isInternet': False, 'defense_tools': [], 'software': '',
|
||||
# 236 приказ
|
||||
'sphere': '', 'purpose': '', 'object_type': '', 'architecture': '',
|
||||
@@ -315,6 +315,25 @@ def _import_187_is_list(wb, data, settings, log_fn):
|
||||
return True
|
||||
return False
|
||||
|
||||
def _split_entries(text):
|
||||
"""Разбивает слипшиеся названия СЗИ: по переносам строк, ';' и по границе
|
||||
'цифра + заглавная буква' (напр. '15.1.0.22239Kaspersky' → два названия)."""
|
||||
if not text:
|
||||
return []
|
||||
parts = re.split(r'[\n;]+', text)
|
||||
out = []
|
||||
for p in parts:
|
||||
p = p.strip()
|
||||
if not p:
|
||||
continue
|
||||
# граница: цифра/точка → заглавная буква без пробела
|
||||
sub = re.split(r'(?<=[0-9.])(?=[А-ЯЁA-Z])', p)
|
||||
for s in sub:
|
||||
s = s.strip()
|
||||
if s and s not in out:
|
||||
out.append(s)
|
||||
return out
|
||||
|
||||
for vals in rows:
|
||||
field = (vals[0] or '').strip()
|
||||
field_lower = field.lower()
|
||||
@@ -326,6 +345,11 @@ def _import_187_is_list(wb, data, settings, log_fn):
|
||||
is_obj['name'] = val_b
|
||||
log_fn(f" Объект КИИ: {val_b}")
|
||||
continue
|
||||
# --- Адрес размещения объекта (в отдельное поле, не в описание) ---
|
||||
if 'адрес размещения' in field_lower or 'адрес расположения' in field_lower or 'местонахождение объекта' in field_lower:
|
||||
is_obj['address'] = val_b
|
||||
log_fn(f" Адрес объекта: {val_b[:60]}")
|
||||
continue
|
||||
# --- Описание ---
|
||||
if 'описание окки' in field_lower or 'описание объекта' in field_lower:
|
||||
if val_b:
|
||||
@@ -458,7 +482,9 @@ def _import_187_is_list(wb, data, settings, log_fn):
|
||||
for kw, label in defense_keywords.items():
|
||||
if re.search(kw, field_lower):
|
||||
if val_b and 'выбрать' not in field_lower:
|
||||
is_obj['defense_tools'].append(val_b)
|
||||
for tool in _split_entries(val_b):
|
||||
if tool not in is_obj['defense_tools']:
|
||||
is_obj['defense_tools'].append(tool)
|
||||
matched = True
|
||||
break
|
||||
if matched:
|
||||
|
||||
+121
-65
@@ -359,6 +359,8 @@ class DokoGenApp:
|
||||
'address': tk.BooleanVar(value=True),
|
||||
'requisites': tk.BooleanVar(value=True),
|
||||
'chief': tk.BooleanVar(value=True),
|
||||
'okved': tk.BooleanVar(value=True),
|
||||
'okved_extra': tk.BooleanVar(value=True),
|
||||
}
|
||||
apply_row = ttk.Frame(right)
|
||||
apply_row.pack(fill=tk.X, pady=(4, 0))
|
||||
@@ -368,6 +370,11 @@ class DokoGenApp:
|
||||
ttk.Checkbutton(apply_row, text="адрес", variable=self._apply_flags['address']).pack(side=tk.LEFT)
|
||||
ttk.Checkbutton(apply_row, text="ИНН/КПП/ОГРН", variable=self._apply_flags['requisites']).pack(side=tk.LEFT)
|
||||
ttk.Checkbutton(apply_row, text="руковод.", variable=self._apply_flags['chief']).pack(side=tk.LEFT)
|
||||
apply_row2 = ttk.Frame(right)
|
||||
apply_row2.pack(fill=tk.X, pady=(2, 0))
|
||||
ttk.Label(apply_row2, text="", font=('', 8), width=10).pack(side=tk.LEFT)
|
||||
ttk.Checkbutton(apply_row2, text="ОКВЭД осн.", variable=self._apply_flags['okved']).pack(side=tk.LEFT)
|
||||
ttk.Checkbutton(apply_row2, text="ОКВЭД доп.", variable=self._apply_flags['okved_extra']).pack(side=tk.LEFT)
|
||||
self._verify_result = None
|
||||
|
||||
# ---------- ПРОВЕРКА ДАННЫХ (Морфер / DaData / ИИ) ----------
|
||||
@@ -704,15 +711,18 @@ 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:
|
||||
# ОКВЭД из DaData (с названием из словаря/интернета)
|
||||
from .verify import resolve_okved
|
||||
from .api_settings import load_api_settings as _las3
|
||||
_tok3 = (_las3().get('dadata_token') or '')
|
||||
if want('okved') and 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._company_entries['okved'].insert(0, resolve_okved(comp['okved'], _tok3))
|
||||
if want('okved_extra') and 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())
|
||||
self.okved_extra_listbox.insert(tk.END, resolve_okved(str(code).strip(), _tok3))
|
||||
if want('chief'):
|
||||
if comp.get('chief_position') and 'chief_position' in self._official_entries:
|
||||
self._official_entries['chief_position'].delete(0, tk.END)
|
||||
@@ -748,61 +758,87 @@ class DokoGenApp:
|
||||
fields_frame = ttk.Frame(self._is_edit_panel, padding=10)
|
||||
fields_frame.pack(fill=tk.X)
|
||||
|
||||
# Основные поля
|
||||
# Основные поля (сверху — только название, описание, категория)
|
||||
ttk.Label(fields_frame, text="Наименование:").grid(row=0, column=0, sticky=tk.W, pady=2)
|
||||
self.is_name_entry = ttk.Entry(fields_frame, width=55)
|
||||
self.is_name_entry = ttk.Entry(fields_frame, width=60)
|
||||
self.is_name_entry.grid(row=0, column=1, padx=10, sticky=tk.EW)
|
||||
|
||||
ttk.Label(fields_frame, text="Описание:").grid(row=1, column=0, sticky=tk.W, pady=2)
|
||||
self.is_description_entry = ttk.Entry(fields_frame, width=55)
|
||||
self.is_description_entry = ttk.Entry(fields_frame, width=60)
|
||||
self.is_description_entry.grid(row=1, column=1, padx=10, sticky=tk.EW)
|
||||
|
||||
# Чекбоксы
|
||||
chk_frame = ttk.Frame(fields_frame)
|
||||
chk_frame.grid(row=2, column=1, sticky=tk.W, pady=5)
|
||||
self.is_lan_var = tk.BooleanVar()
|
||||
ttk.Checkbutton(chk_frame, text="ЛВС", variable=self.is_lan_var).pack(side=tk.LEFT, padx=5)
|
||||
self.is_internet_var = tk.BooleanVar()
|
||||
ttk.Checkbutton(chk_frame, text="Интернет", variable=self.is_internet_var).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# Категория значимости объекта КИИ
|
||||
self.is_category_var = tk.StringVar(value="без категории")
|
||||
ttk.Label(fields_frame, text="Категория значимости:").grid(row=3, column=0, sticky=tk.W, pady=2)
|
||||
ttk.Label(fields_frame, text="Категория значимости:").grid(row=2, column=0, sticky=tk.W, pady=2)
|
||||
cat_frame = ttk.Frame(fields_frame)
|
||||
cat_frame.grid(row=3, column=1, sticky=tk.W, padx=10)
|
||||
cat_frame.grid(row=2, column=1, sticky=tk.W, padx=10)
|
||||
for val in ["1", "2", "3", "без категории"]:
|
||||
ttk.Radiobutton(cat_frame, text=val, variable=self.is_category_var,
|
||||
value=val).pack(side=tk.LEFT, padx=3)
|
||||
|
||||
# Программное обеспечение
|
||||
ttk.Label(fields_frame, text="Программное обеспечение:").grid(row=4, column=0, sticky=tk.W, pady=2)
|
||||
self.is_software_entry = ttk.Entry(fields_frame, width=55)
|
||||
self.is_software_entry.grid(row=4, column=1, padx=10, sticky=tk.EW)
|
||||
|
||||
fields_frame.columnconfigure(1, weight=1)
|
||||
|
||||
# Вложенные списки
|
||||
# ---- Вложенные вкладки ----
|
||||
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)
|
||||
# ===== Вкладка «Основное» =====
|
||||
main_tab = ttk.Frame(nb2)
|
||||
nb2.add(main_tab, text="Основное")
|
||||
main_frame = ttk.Frame(main_tab, padding=8)
|
||||
main_frame.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# Назначение объекта
|
||||
ttk.Label(main_frame, text="Назначение объекта:").grid(row=0, column=0, sticky=tk.W, pady=2)
|
||||
self.is_purpose_entry = ttk.Entry(main_frame, width=70)
|
||||
self.is_purpose_entry.grid(row=0, column=1, padx=8, sticky=tk.EW, pady=1)
|
||||
|
||||
# Сфера деятельности — словарик + выпадающий список
|
||||
ttk.Label(main_frame, text="Сфера деятельности:").grid(row=1, column=0, sticky=tk.W, pady=2)
|
||||
self.is_sphere_combo = ttk.Combobox(main_frame, width=68)
|
||||
self.is_sphere_combo.grid(row=1, column=1, padx=8, sticky=tk.EW, pady=1)
|
||||
try:
|
||||
import json as _json
|
||||
_sp = os.path.join(os.path.dirname(__file__), 'dictionaries', 'spheres.json')
|
||||
with open(_sp, encoding='utf-8') as _f:
|
||||
self.is_sphere_combo['values'] = _json.load(_f)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Тип объекта — словарик + выпадающий список
|
||||
ttk.Label(main_frame, text="Тип объекта:").grid(row=2, column=0, sticky=tk.W, pady=2)
|
||||
self.is_object_type_combo = ttk.Combobox(main_frame, width=68)
|
||||
self.is_object_type_combo.grid(row=2, column=1, padx=8, sticky=tk.EW, pady=1)
|
||||
try:
|
||||
_ot = os.path.join(os.path.dirname(__file__), 'dictionaries', 'object_types.json')
|
||||
with open(_ot, encoding='utf-8') as _f:
|
||||
self.is_object_type_combo['values'] = _json.load(_f)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Архитектура объекта — словарик + выпадающий список
|
||||
ttk.Label(main_frame, text="Архитектура объекта:").grid(row=3, column=0, sticky=tk.W, pady=2)
|
||||
self.is_architecture_combo = ttk.Combobox(main_frame, width=68)
|
||||
self.is_architecture_combo.grid(row=3, column=1, padx=8, sticky=tk.EW, pady=1)
|
||||
try:
|
||||
_ar = os.path.join(os.path.dirname(__file__), 'dictionaries', 'architectures.json')
|
||||
with open(_ar, encoding='utf-8') as _f:
|
||||
self.is_architecture_combo['values'] = _json.load(_f)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
# Типовой отраслевой объект КИИ — словарик + выпадающий список
|
||||
ttk.Label(main_frame, text="Типовой отраслевой объект КИИ:").grid(row=4, column=0, sticky=tk.W, pady=2)
|
||||
self.is_typical_object_combo = ttk.Combobox(main_frame, width=68)
|
||||
self.is_typical_object_combo.grid(row=4, column=1, padx=8, sticky=tk.EW, pady=1)
|
||||
try:
|
||||
_to = os.path.join(os.path.dirname(__file__), 'dictionaries', 'typical_objects.json')
|
||||
with open(_to, encoding='utf-8') as _f:
|
||||
self.is_typical_object_combo['values'] = _json.load(_f)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
main_frame.columnconfigure(1, weight=1)
|
||||
|
||||
# ===== Вкладка «Субъект» =====
|
||||
subj_tab = ttk.Frame(nb2)
|
||||
@@ -835,19 +871,29 @@ class DokoGenApp:
|
||||
nb2.add(net_tab, text="Сети и ПО")
|
||||
net_frame = ttk.Frame(net_tab, padding=8)
|
||||
net_frame.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# ЛВС / Интернет
|
||||
chk = ttk.Frame(net_frame)
|
||||
chk.grid(row=0, column=0, columnspan=2, sticky=tk.W, pady=2)
|
||||
self.is_lan_var = tk.BooleanVar()
|
||||
ttk.Checkbutton(chk, text="ЛВС", variable=self.is_lan_var).pack(side=tk.LEFT, padx=5)
|
||||
self.is_internet_var = tk.BooleanVar()
|
||||
ttk.Checkbutton(chk, text="Интернет", variable=self.is_internet_var).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
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"),
|
||||
("Категория сети электросвязи:", "is_net_category_entry", 1),
|
||||
("Оператор связи/провайдер хостинга:", "is_net_operator_entry", 2),
|
||||
("Цель взаимодействия с сетью:", "is_net_purpose_entry", 3),
|
||||
("Способ взаимодействия/тип доступа:", "is_net_method_entry", 4),
|
||||
("ПО и аппаратные средства:", "is_hw_sw_entry", 5),
|
||||
("Общесистемное ПО (ОС, виртуализация):", "is_os_sw_entry", 6),
|
||||
("Прикладные программы:", "is_app_sw_entry", 7),
|
||||
("Программное обеспечение:", "is_software_entry", 8),
|
||||
]
|
||||
for i, (label, attr) in enumerate(net_fields):
|
||||
ttk.Label(net_frame, text=label).grid(row=i, column=0, sticky=tk.W, pady=2)
|
||||
for label, attr, row in net_fields:
|
||||
ttk.Label(net_frame, text=label).grid(row=row, 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)
|
||||
entry.grid(row=row, column=1, padx=8, sticky=tk.EW, pady=1)
|
||||
setattr(self, attr, entry)
|
||||
net_frame.columnconfigure(1, weight=1)
|
||||
|
||||
@@ -872,7 +918,7 @@ class DokoGenApp:
|
||||
setattr(self, attr, entry)
|
||||
sec_frame.columnconfigure(1, weight=1)
|
||||
|
||||
# СЗИ
|
||||
# ===== Вкладка «Средства защиты» =====
|
||||
def_tab = ttk.Frame(nb2)
|
||||
nb2.add(def_tab, text="Средства защиты")
|
||||
def_btn_frame = ttk.Frame(def_tab)
|
||||
@@ -883,7 +929,7 @@ class DokoGenApp:
|
||||
self.defense_tools_listbox = tk.Listbox(def_tab, height=6)
|
||||
self.defense_tools_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
|
||||
# Пользователи ОКИИ
|
||||
# ===== Вкладка «Пользователи ОКИИ» =====
|
||||
user_tab = ttk.Frame(nb2)
|
||||
nb2.add(user_tab, text="Пользователи ОКИИ")
|
||||
self.users_listbox = tk.Listbox(user_tab, height=6)
|
||||
@@ -1124,10 +1170,10 @@ class DokoGenApp:
|
||||
# Поля 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_sphere_combo', 'sphere'),
|
||||
('is_object_type_combo', 'object_type'),
|
||||
('is_architecture_combo', 'architecture'),
|
||||
('is_typical_object_combo', 'typical_object'),
|
||||
('is_subject_name_entry', 'subject_name'),
|
||||
('is_subject_address_entry', 'subject_address'),
|
||||
('is_subject_chief_position_entry', 'subject_chief_position'),
|
||||
@@ -1364,10 +1410,10 @@ class DokoGenApp:
|
||||
# Поля 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_sphere_combo', 'sphere'),
|
||||
('is_object_type_combo', 'object_type'),
|
||||
('is_architecture_combo', 'architecture'),
|
||||
('is_typical_object_combo', 'typical_object'),
|
||||
('is_subject_name_entry', 'subject_name'),
|
||||
('is_subject_address_entry', 'subject_address'),
|
||||
('is_subject_chief_position_entry', 'subject_chief_position'),
|
||||
@@ -1397,8 +1443,12 @@ class DokoGenApp:
|
||||
]:
|
||||
w = getattr(self, attr, None)
|
||||
if w is not None:
|
||||
val = getattr(isys, model_field, '') or ''
|
||||
if isinstance(w, ttk.Combobox):
|
||||
w.set(val)
|
||||
else:
|
||||
w.delete(0, tk.END)
|
||||
w.insert(0, getattr(isys, model_field, '') or '')
|
||||
w.insert(0, val)
|
||||
|
||||
# Списки
|
||||
for lb, items in [
|
||||
@@ -1970,10 +2020,16 @@ class DokoGenApp:
|
||||
if data.get('okved'):
|
||||
entry = self._company_entries.get('okved')
|
||||
if entry:
|
||||
from .verify import resolve_okved
|
||||
from .api_settings import load_api_settings as _las
|
||||
_tok = (_las().get('dadata_token') or '')
|
||||
entry.delete(0, tk.END)
|
||||
entry.insert(0, data['okved'])
|
||||
entry.insert(0, resolve_okved(data['okved'], _tok))
|
||||
if data.get('okvedExtra'):
|
||||
if hasattr(self, 'okved_extra_listbox'):
|
||||
from .verify import resolve_okved
|
||||
from .api_settings import load_api_settings as _las2
|
||||
_tok2 = (_las2().get('dadata_token') or '')
|
||||
self.okved_extra_listbox.delete(0, tk.END)
|
||||
extra = data['okvedExtra']
|
||||
if isinstance(extra, str):
|
||||
@@ -1982,7 +2038,7 @@ class DokoGenApp:
|
||||
else:
|
||||
parts = [str(p).strip() for p in extra if str(p).strip()]
|
||||
for p in parts:
|
||||
self.okved_extra_listbox.insert(tk.END, p)
|
||||
self.okved_extra_listbox.insert(tk.END, resolve_okved(p, _tok2))
|
||||
|
||||
# Объекты КИИ
|
||||
if data.get('informationSystems'):
|
||||
|
||||
@@ -148,6 +148,49 @@ def normalize_address(text):
|
||||
return ' '.join(fixed)
|
||||
|
||||
|
||||
def resolve_okved(code, dadata_token=None):
|
||||
"""Код ОКВЭД → «код - название».
|
||||
|
||||
1) Ищем в словаре dictionaries/okved.json (строки вида «27.32.2 Название»).
|
||||
2) Если не нашли — запрашиваем DaData suggest okved2 (если есть токен).
|
||||
3) Иначе возвращаем код как есть.
|
||||
"""
|
||||
code = (code or '').strip()
|
||||
if not code:
|
||||
return ''
|
||||
# 1) Словарь
|
||||
try:
|
||||
import os, json
|
||||
dict_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'dictionaries', 'okved.json')
|
||||
with open(dict_path, encoding='utf-8') as f:
|
||||
items = json.load(f)
|
||||
for item in items:
|
||||
item = str(item).strip()
|
||||
# совпадение по началу: «27.32.2» или «27.32.2 - ...» / «27.32.2 ...»
|
||||
if item.split()[0].rstrip('.') == code.rstrip('.'):
|
||||
# нормализуем: «код - название»
|
||||
rest = item[len(item.split()[0]):].strip().lstrip('- ').strip()
|
||||
if rest:
|
||||
return f"{code} - {rest}"
|
||||
return item
|
||||
except Exception:
|
||||
pass
|
||||
# 2) DaData suggest okved2
|
||||
if dadata_token:
|
||||
try:
|
||||
js, err = _http_json('https://suggestions.dadata.ru/suggestions/api/4_1/rs/suggest/okved2',
|
||||
data={'query': code, 'count': 1},
|
||||
headers={'Content-Type': 'application/json',
|
||||
'Authorization': f'Token {dadata_token}'})
|
||||
if not err and js:
|
||||
sugg = (js.get('suggestions') or [])
|
||||
if sugg and sugg[0].get('value'):
|
||||
return sugg[0]['value']
|
||||
except Exception:
|
||||
pass
|
||||
return code
|
||||
|
||||
|
||||
def _fmt_date(value):
|
||||
"""Дата: '2005-02-09' → '09.02.2005'; миллисекунды 1107907200000 → дата."""
|
||||
if not value:
|
||||
|
||||
Reference in New Issue
Block a user