From b6f595fd32ad5c4ce34e194df8c19e0c5abc6f4a Mon Sep 17 00:00:00 2001 From: prog1764 Date: Tue, 4 Aug 2026 17:34:17 +0400 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=B8=D0=BC=D0=BF=D0=BE=D1=80=D1=82=20?= =?UTF-8?q?=D0=BE=D0=B4=D0=BD=D0=BE=D0=B3=D0=BE=20=D1=87=D0=B5=D0=BB=D0=BE?= =?UTF-8?q?=D0=B2=D0=B5=D0=BA=D0=B0=20=D0=B2=20=D0=BF=D0=BE=D0=BB=D0=B5=20?= =?UTF-8?q?=D0=A4=D0=98=D0=9E=20(=D1=81=D1=82=D0=B0=D1=80=D1=8B=D0=B5=20?= =?UTF-8?q?=D0=BE=D0=BF=D1=80=D0=BE=D1=81=D0=BD=D0=B8=D0=BA=D0=B8)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - если в ячейке ФИО несколько человек (через запятую/точку с запятой/ перевод строки или просто подряд) — остаётся первое полное ФИО - применяется к должностным лицам (chief/ответственный/админ), комиссии и сотрудникам при импорте из опросника - то же при открытии сохранённого файла (from_dict): «Коростелев Павел Сергеевич Хрящев Сергей Владимирович …» → «Коростелев Павел Сергеевич» --- dokogen/importer.py | 29 +++++++++++++++++++++++++++-- dokogen/models.py | 23 ++++++++++++++++++++++- 2 files changed, 49 insertions(+), 3 deletions(-) diff --git a/dokogen/importer.py b/dokogen/importer.py index 12f051a..2a343c9 100644 --- a/dokogen/importer.py +++ b/dokogen/importer.py @@ -179,6 +179,27 @@ def import_187fz(filepath: str, log_fn: Optional[Callable] = None, settings_path return data +def _extract_single_fio(text): + """Из строки, где может быть несколько ФИО (через запятую, точку с запятой, + перевод строки или просто подряд), выделяет одно первое полное ФИО. + + Пример: «Коростелев Павел Сергеевич Хрящев Сергей Владимирович …» → «Коростелев Павел Сергеевич» + """ + if not text: + return text + text = str(text).strip() + # 1) Явные разделители — берём первую часть + for sep in (',', ';', '\n', '\r'): + if sep in text: + text = text.split(sep)[0].strip() + break + # 2) Если осталось больше 3 слов — это несколько ФИО подряд, оставляем первые 3 + words = [w for w in text.split() if w] + if len(words) > 3: + text = ' '.join(words[:3]) + return text.strip() + + def _import_187_common(wb, data, settings, log_fn): """Импорт общих сведений по словарю настроек. @@ -228,6 +249,8 @@ def _import_187_common(wb, data, settings, log_fn): break val = clean_value(cell_value(target_ws, coord)) if val: + if key in ('chiefFio', 'responsibleFio', 'ispdnFio', 'adminFio'): + val = _extract_single_fio(val) data[key] = val log_fn(f" {name} [ячейка {coord}]: {val}") continue @@ -242,6 +265,8 @@ def _import_187_common(wb, data, settings, log_fn): for field_lower, val in row_cache.items(): if rx.search(field_lower): if val: + if key in ('chiefFio', 'responsibleFio', 'ispdnFio', 'adminFio'): + val = _extract_single_fio(val) data[key] = val log_fn(f" {name} [по ключу]: {val}") break @@ -619,7 +644,7 @@ def _import_187_employees(wb, data, settings, log_fn): commission.append({ 'role': role, 'position': col_b, - 'fio': col_c.replace('\n', ' '), + 'fio': _extract_single_fio(col_c.replace('\n', ' ')), }) if in_employees: @@ -628,7 +653,7 @@ def _import_187_employees(wb, data, settings, log_fn): continue employee = { 'position': col_b, - 'fio': fio.replace('\n', ' '), + 'fio': _extract_single_fio(fio.replace('\n', ' ')), } if col_e: employee['is_list'] = [s.strip() for s in col_e.split('\n') if s.strip()] diff --git a/dokogen/models.py b/dokogen/models.py index 2e4a1af..86cf92b 100644 --- a/dokogen/models.py +++ b/dokogen/models.py @@ -5,6 +5,21 @@ from dataclasses import dataclass, field, asdict from typing import List, Dict, Any +def _clean_single_fio(value): + """Оставить одно первое ФИО, если в строке их несколько.""" + if not value: + return value + value = str(value).strip() + for sep in (',', ';', '\n', '\r'): + if sep in value: + value = value.split(sep)[0].strip() + break + words = [w for w in value.split() if w] + if len(words) > 3: + value = ' '.join(words[:3]) + return value.strip() + + @dataclass class CommissionMember: """Член комиссии по категорированию объектов КИИ""" @@ -171,7 +186,13 @@ class Company: if "okved_extra" in d and not isinstance(d.get("okved_extra"), list): d["okved_extra"] = [] valid = {f.name for f in cls.__dataclass_fields__.values()} - return cls(**{k: v for k, v in d.items() if k in valid}) + obj = cls(**{k: v for k, v in d.items() if k in valid}) + # Если в поле ФИО несколько человек — оставить одного (старые опросники) + for fio_field in ('chief_fio', 'responsible_fio', 'admin_fio'): + val = getattr(obj, fio_field, '') + if val: + setattr(obj, fio_field, _clean_single_fio(val)) + return obj def get_folder_name(self) -> str: if self.short_name: