From 9489e6a37d05f3405cd414173ab0349eea52ff7d Mon Sep 17 00:00:00 2001 From: prog1764 Date: Tue, 4 Aug 2026 15:46:34 +0400 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=B8=D0=BC=D0=BF=D0=BE=D1=80=D1=82=20?= =?UTF-8?q?=D1=81=D1=80=D0=B5=D0=B4=D1=81=D1=82=D0=B2=20=D0=B7=D0=B0=D1=89?= =?UTF-8?q?=D0=B8=D1=82=D1=8B=20=E2=80=94=20=D0=BA=D0=B0=D0=B6=D0=B4=D0=B0?= =?UTF-8?q?=D1=8F=20=D0=BF=D1=80=D0=BE=D0=B3=D1=80=D0=B0=D0=BC=D0=BC=D0=B0?= =?UTF-8?q?=20=D0=BE=D1=82=D0=B4=D0=B5=D0=BB=D1=8C=D0=BD=D1=8B=D0=BC=20?= =?UTF-8?q?=D1=81=D1=80=D0=B5=D0=B4=D1=81=D1=82=D0=B2=D0=BE=D0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Проблема: clean_value заменял переносы строк на пробелы, из-за чего список программ в одной ячейке (каждая с новой строки) склеивался в одну строку. Теперь сырые значения ячеек сохраняются (raw_rows), разбивка по \n/;/, названия не режутся (KES ≠ KSC). --- dokogen/importer.py | 47 ++++++++++++++++++++++++++++++--------------- 1 file changed, 31 insertions(+), 16 deletions(-) diff --git a/dokogen/importer.py b/dokogen/importer.py index 7765e87..04f4104 100644 --- a/dokogen/importer.py +++ b/dokogen/importer.py @@ -268,12 +268,15 @@ def _import_187_is_list(wb, data, settings, log_fn): for sheet_name in is_sheets: ws = wb[sheet_name] rows = [] + raw_rows = [] for row in ws.iter_rows(min_row=1, values_only=True): + raw_vals = list(row) vals = [clean_value(c) for c in row] field = (vals[0] or '').strip() if vals else '' if not field: continue rows.append(vals) + raw_rows.append(raw_vals) if len(rows) < 2: continue @@ -313,28 +316,40 @@ def _import_187_is_list(wb, data, settings, log_fn): return False def _split_entries(text): - """Разбивает слипшиеся названия СЗИ: по переносам строк, ';' и по границе - 'цифра + заглавная буква' (напр. '15.1.0.22239Kaspersky' → два названия).""" - if not text: + """Разбивает список средств защиты на ОТДЕЛЬНЫЕ программы. + + В опроснике каждая программа записана с новой строки (разделитель \n), + также поддерживаются ';' и ','. Название программы при этом НЕ режется + (напр. 'Kaspersky Endpoint Security' остаётся одним средством, KES ≠ KSC). + """ + if text is None: return [] - parts = re.split(r'[\n;]+', text) + # 1) разбиваем по переносам строк и ';' + parts = re.split(r'[\n\r;]+', str(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) + # 2) дополнительно по запятой (если список через запятую) + for sub in re.split(r'\s*,\s*', p): + sub = sub.strip() + if not sub: + continue + # 3) запасной вариант: слипшиеся без разделителя «15.1.0.22239Kaspersky» + sub_parts = re.split(r'(?<=[0-9.])(?=[А-ЯЁA-Z])', sub) + for s in sub_parts: + s = s.strip() + if not s: + continue + cleaned = clean_value(s) + if cleaned and cleaned not in out: + out.append(cleaned) return out - for vals in rows: + for idx, vals in enumerate(rows): field = (vals[0] or '').strip() field_lower = field.lower() - val_b = clean_value(vals[1]) if len(vals) > 1 else '' + raw_row = raw_rows[idx] if idx < len(raw_rows) else vals + raw_b = raw_row[1] if len(raw_row) > 1 else None # сырое значение (с переносами строк!) + val_b = clean_value(raw_b) if raw_b is not None else '' is_checked = val_b.upper() in ('ДА', 'YES', '✓', '✔', '1', 'TRUE', 'ЕСТЬ') # --- Наименование объекта --- @@ -479,7 +494,7 @@ 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: - for tool in _split_entries(val_b): + for tool in _split_entries(raw_b if raw_b is not None else val_b): if tool not in is_obj['defense_tools']: is_obj['defense_tools'].append(tool) matched = True