diff --git a/dokogen/ui.py b/dokogen/ui.py index e29b1e7..23b3798 100644 --- a/dokogen/ui.py +++ b/dokogen/ui.py @@ -22,7 +22,7 @@ from . import ( from .verify import ( check_declension_morpher, check_company_dadata, check_declension_ai, check_company_ai, - COMPANY_FIELDS, compare_company_values, + COMPANY_FIELDS, compare_company_values, normalize_threats, ) from .api_settings import load_api_settings, save_api_settings @@ -761,8 +761,9 @@ class DokoGenApp: 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) + from .verify import split_okved_extra _applied = 0 - for code in comp['okved_extra']: + for code in split_okved_extra(comp['okved_extra']): if str(code).strip(): self.okved_extra_listbox.insert(tk.END, resolve_okved(str(code).strip(), _tok3)) _applied += 1 @@ -4661,21 +4662,14 @@ class DokoGenApp: entry.insert(0, resolve_okved(data['okved'], _tok)) if data.get('okvedExtra'): if hasattr(self, 'okved_extra_listbox'): - from .verify import resolve_okved + from .verify import resolve_okved, split_okved_extra 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): - import re as _re - # Перенос строки внутри одной записи («27.32.2 Производство\nсиловых кабелей») - # не должен создавать вторую запись — сначала склеиваем переносы в пробел, - # затем режем только по запятой / точке с запятой. - extra = _re.sub(r'\s*[\n\r]+\s*', ' ', extra) - parts = [p.strip() for p in _re.split(r'[;]+|\s*,\s*', extra) if p.strip()] - else: - parts = [str(p).strip() for p in extra if str(p).strip()] - for p in parts: + # Доп. ОКВЭД могут быть склеены с переносами внутри названий + # («25.93 — Производство изделий из проволоки\nцепей и пружин 32.12.1 — …») — + # нарезаем по кодам ОКВЭД, затем нормализуем каждый в «код название». + for p in split_okved_extra(data['okvedExtra']): self.okved_extra_listbox.insert(tk.END, resolve_okved(p, _tok2)) # Объекты КИИ @@ -4713,8 +4707,8 @@ class DokoGenApp: attacker_category=is_data.get('attacker_category', ''), attacker_level=is_data.get('attacker_level', ''), attacker_types=is_data.get('attacker_types', ''), - threats=is_data.get('threats', ''), - incidents=is_data.get('incidents', ''), + threats=normalize_threats(is_data.get('threats', '')), + incidents=normalize_threats(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', ''), diff --git a/dokogen/verify.py b/dokogen/verify.py index 616afe0..2447374 100644 --- a/dokogen/verify.py +++ b/dokogen/verify.py @@ -544,6 +544,51 @@ def _norm_threat_text(text): return s.lower() +def normalize_threats(text): + """Склеить переносы внутри названий угроз. + + В опросниках встречается «УБИ.209 Угроза … памяти\r\nядра процессора» — + перенос строки внутри одного названия. Строка, не начинающаяся с «УБИ.», + считается продолжением предыдущей угрозы. + """ + if not text: + return '' + lines = [ln.strip() for ln in str(text).split('\n') if ln.strip()] + out = [] + for ln in lines: + if re.match(r'^уби\.?\s*\d+', ln, re.IGNORECASE): + out.append(ln) + elif out: + out[-1] = out[-1] + ' ' + ln + else: + out.append(ln) + return '\n'.join(out) + + +def split_okved_extra(value): + """Разбить okvedExtra (строка или список строк с переносами) на записи «код название». + + В опросниках доп. ОКВЭД часто идут склеенными: + «25.93 — Производство изделий из проволоки\nцепей и пружин 32.12.1 — …» + Здесь «цепей и пружин» — продолжение названия 25.93, а 32.12.1 — новый код. + Нарезаем по кодам ОКВЭД (двузначный код с точками в начале записи). + """ + if isinstance(value, (list, tuple)): + text = ' '.join(str(x) for x in value) + else: + text = str(value or '') + text = text.replace('\r', ' ').replace('\n', ' ') + # Нарезаем по началу кода ОКВЭД: «25.93», «32.12.1», «43.2» … + # Перед кодом не должно быть цифры или точки (чтобы не разрезать «25.93» на «25» и «93») + parts = re.split(r'(?