From 5af23ae5252394d01db224f7c4008550e5abd565 Mon Sep 17 00:00:00 2001 From: LocoAgent Date: Wed, 5 Aug 2026 09:16:16 +0400 Subject: [PATCH] =?UTF-8?q?fix:=20=D0=B0=D1=83=D0=B4=D0=B8=D1=82=20dokogen?= =?UTF-8?q?187=20=E2=80=94=20=D0=BA=D1=80=D0=B8=D1=82=D0=B8=D1=87=D0=B5?= =?UTF-8?q?=D1=81=D0=BA=D0=B8=D0=B5=20=D0=B8=20=D0=B2=D1=8B=D1=81=D0=BE?= =?UTF-8?q?=D0=BA=D0=B8=D0=B5=20=D0=B7=D0=B0=D0=BC=D0=B5=D1=87=D0=B0=D0=BD?= =?UTF-8?q?=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - api_settings: убраны реальные API-ключи из кода (env/файл) — 1.1 - verify: промт ИИ для проверки реквизитов больше не ссылается на 187-ФЗ как на закон о ПДн («юрист по ПДн (187-ФЗ)» → специалист по реквизитам) - ui: дата ПП РФ №127 исправлена 08.02.2019 → 08.02.2018 (3 места) - help_docs: группа B переименована «187-ФЗ (персональные данные)» → «187-ФЗ (критическая информационная инфраструктура)» - generator: убран дубль хвоста параграфа в _highlight_unmatched - importer: «магия дат» (числа 10001-79999 → дата Excel) теперь только для полей-дат (as_date), финансовые поля не ломаются - ui (_collect_is): убраны дубли пар маппинга attacker_level/attacker_types - ui (импорт): убрано наследие 152-ФЗ ispdnPosition/ispdnFio из маппинга - test_generator: переписан под модель 187-ФЗ (category/sphere/object_type вместо pd_count/threat_type/defence_level) - criteria_justifications: п.9 для здравоохранения согласован с кодом (убран «критерий неприменим» — противоречие с «применим ко всем») - criteria_applicability: Транспорт (добавлены п.1, п.2; убран п.10), Банковская сфера (добавлены п.8, п.9) — требуется экспертная сверка - ui: Бусредн вынесен в константу DEFAULT_BUDGET_AVG (данные Минфина, обновлять ежегодно); автозаполнение п.9 унифицировано с авторасчётом (ΔБ = ΔНП+ΔНДПИ+ΔДИВ+ΔВСО+ΔНС) - ui: п.8 — предупреждение при неполных данных (менее 5 лет доходов) - models: поля ndpi/div/vso/ns для п.9; комментарий income_loss уточнён - .gitignore: api_settings.json/.env не попадают в git --- .gitignore | 5 ++ dokogen/api_settings.py | 14 +++-- .../dictionaries/criteria_applicability.json | 9 ++-- .../dictionaries/criteria_justifications.json | 44 +++++++-------- dokogen/generator.py | 3 -- dokogen/help_docs.py | 2 +- dokogen/importer.py | 17 ++++-- dokogen/models.py | 8 ++- dokogen/ui.py | 54 ++++++++++++------- dokogen/verify.py | 3 +- test_generator.py | 16 +++--- 11 files changed, 107 insertions(+), 68 deletions(-) diff --git a/.gitignore b/.gitignore index 01aac8c..5b3fbcc 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,8 @@ __pycache__/ .autosave/ Сгенерированные_документы/ *.result.docx + +# Секреты и локальные настройки — НЕ коммитить +api_settings.json +.env +.env.local diff --git a/dokogen/api_settings.py b/dokogen/api_settings.py index 33ed2d7..179f3b6 100644 --- a/dokogen/api_settings.py +++ b/dokogen/api_settings.py @@ -13,11 +13,17 @@ import os import sys import json +# ВНИМАНИЕ: реальные ключи НЕ хранятся в коде (утечка в git-историю — см. аудит). +# Источники ключей (по приоритету): +# 1) переменные окружения DADATA_TOKEN / DEEPSEEK_API_KEY / DEEPSEEK_MODEL / DEEPSEEK_BASE_URL +# 2) файл api_settings.json рядом с программой +# 3) DEFAULT_SETTINGS ниже (пустые значения) + DEFAULT_SETTINGS = { - 'dadata_token': '431aa11706bf83f571cef0319259289aa750bcb1', - 'ai_api_key': 'sk-3c7e6673f245412697d0e14b6096435f', - 'ai_model': 'deepseek-chat', - 'ai_base_url': 'https://api.deepseek.com', + 'dadata_token': os.environ.get('DADATA_TOKEN', ''), + 'ai_api_key': os.environ.get('DEEPSEEK_API_KEY', ''), + 'ai_model': os.environ.get('DEEPSEEK_MODEL', 'deepseek-chat'), + 'ai_base_url': os.environ.get('DEEPSEEK_BASE_URL', 'https://api.deepseek.com'), } # Старые ключи (для совместимости со старым файлом api_settings.json) diff --git a/dokogen/dictionaries/criteria_applicability.json b/dokogen/dictionaries/criteria_applicability.json index 6aee230..461ef2f 100644 --- a/dokogen/dictionaries/criteria_applicability.json +++ b/dokogen/dictionaries/criteria_applicability.json @@ -23,9 +23,10 @@ "9" ], "Транспорт": [ - "10", - "11", - "3" + "1", + "2", + "3", + "11" ], "Связь": [ "4" @@ -61,6 +62,8 @@ "14" ], "Банковская сфера и финансовый рынок": [ + "8", + "9", "10" ], "Топливно-энергетический комплекс": [ diff --git a/dokogen/dictionaries/criteria_justifications.json b/dokogen/dictionaries/criteria_justifications.json index 93c224f..2fcb57c 100644 --- a/dokogen/dictionaries/criteria_justifications.json +++ b/dokogen/dictionaries/criteria_justifications.json @@ -1,23 +1,23 @@ { - "Здравоохранение": { - "1": "Рассматриваемый объект КИИ не участвует в обработке информации, необходимой для оказания медицинской помощи, проведения медицинских экспертиз, медицинских осмотров и медицинских освидетельствований, санитарно-противоэпидемических (профилактических) мероприятий и не осуществляет управление, контроль или мониторинг данных бизнес-процессов. Объект является вспомогательной информационной системой, целью которой является автоматизация процессов ведения бухгалтерского учета. Объект не осуществляет мониторинг, контроль или управление другими объектами КИИ, обеспечивающими жизненно важные функции организма. Утрата или иное повреждение объекта КИИ не может повлиять на жизненно важные функции организма.", - "2": "Объект КИИ не относится к объектам обеспечения жизнедеятельности населения, в том числе объектам водоснабжения и канализации, очистки сточных вод, тепло- и электроснабжения, гидротехнических сооружений.", - "3": "Объекты КИИ организации сферы здравоохранения не относятся к объектам транспортной инфраструктуры, и субъект КИИ не имеет в своем составе подразделения транспортной инфраструктуры.", - "4": "Субъект КИИ не является оператором связи, объекты КИИ организации сферы здравоохранения не относятся к объектам транспортной инфраструктуры.", - "5": "Организация сферы здравоохранения является юридическим лицом, обеспечивающим эксплуатацию информационной системы, задействованной в процессе оказания государственной услуги, однако нарушение функционирования информационной системы (рассматриваемого объекта КИИ), не участвующей в обработке информации, необходимой для оказания государственной услуги, не влечет за собой нарушение и (или) прекращение доступа к государственной услуге. Рассматриваемый объект КИИ не участвует в обработке информации, необходимой для оказания государственной услуги.", - "6": "Организация сферы здравоохранения не является органом государственной власти, в связи с этим критерий неприменим.", - "7": "Организация сферы здравоохранения не является субъектом политической сферы деятельности, в связи с этим критерий неприменим.", - "8": "Организация сферы здравоохранения не является государственной корпорацией, государственным унитарным предприятием, государственной компанией, стратегическим акционерным обществом, стратегическим предприятием, в связи с этим критерий неприменим.", - "9": "Объект КИИ задействован только в предоставлении медицинских услуг, освобожденных от налогообложения, в связи с этим критерий неприменим.", - "10": "Организация сферы здравоохранения не является субъектом банковской сферы деятельности.", - "10.1": "Организация сферы здравоохранения не является центральным контрагентом.", - "10.2": "Организация сферы здравоохранения не является центральным депозитарием.", - "10.3": "Организация сферы здравоохранения не является негосударственным пенсионным фондом.", - "10.4": "Организация сферы здравоохранения не является страховой организацией.", - "10.5": "Организация сферы здравоохранения не является оператором услуг информационного обмена.", - "11": "Объекты КИИ организации сферы здравоохранения не относятся к опасным производственным объектам в соответствии с законодательством Российской Федерации.", - "12": "Организация сферы здравоохранения не выполняет функции пункта управления (ситуационного центра) государственного органа власти или государственной корпорации.", - "13": "Организация сферы здравоохранения не является субъектом обеспечения обороны страны, безопасности государства и правопорядка.", - "14": "Организация сферы здравоохранения не имеет информационных систем, функционирующих в области обеспечения обороны страны, безопасности государства и правопорядка." - } -} + "Здравоохранение": { + "1": "Рассматриваемый объект КИИ не участвует в обработке информации, необходимой для оказания медицинской помощи, проведения медицинских экспертиз, медицинских осмотров и медицинских освидетельствований, санитарно-противоэпидемических (профилактических) мероприятий и не осуществляет управление, контроль или мониторинг данных бизнес-процессов. Объект является вспомогательной информационной системой, целью которой является автоматизация процессов ведения бухгалтерского учета. Объект не осуществляет мониторинг, контроль или управление другими объектами КИИ, обеспечивающими жизненно важные функции организма. Утрата или иное повреждение объекта КИИ не может повлиять на жизненно важные функции организма.", + "2": "Объект КИИ не относится к объектам обеспечения жизнедеятельности населения, в том числе объектам водоснабжения и канализации, очистки сточных вод, тепло- и электроснабжения, гидротехнических сооружений.", + "3": "Объекты КИИ организации сферы здравоохранения не относятся к объектам транспортной инфраструктуры, и субъект КИИ не имеет в своем составе подразделения транспортной инфраструктуры.", + "4": "Субъект КИИ не является оператором связи, объекты КИИ организации сферы здравоохранения не относятся к объектам транспортной инфраструктуры.", + "5": "Организация сферы здравоохранения является юридическим лицом, обеспечивающим эксплуатацию информационной системы, задействованной в процессе оказания государственной услуги, однако нарушение функционирования информационной системы (рассматриваемого объекта КИИ), не участвующей в обработке информации, необходимой для оказания государственной услуги, не влечет за собой нарушение и (или) прекращение доступа к государственной услуге. Рассматриваемый объект КИИ не участвует в обработке информации, необходимой для оказания государственной услуги.", + "6": "Организация сферы здравоохранения не является органом государственной власти, в связи с этим критерий неприменим.", + "7": "Организация сферы здравоохранения не является субъектом политической сферы деятельности, в связи с этим критерий неприменим.", + "8": "Организация сферы здравоохранения не является государственной корпорацией, государственным унитарным предприятием, государственной компанией, стратегическим акционерным обществом, стратегическим предприятием, в связи с этим критерий неприменим.", + "9": "Объект КИИ задействован в предоставлении медицинских услуг. Показатель 9 применим ко всем субъектам КИИ; при расчёте учитывается, что медицинские услуги освобождены от налогообложения (значение показателя может быть минимальным).", + "10": "Организация сферы здравоохранения не является субъектом банковской сферы деятельности.", + "10.1": "Организация сферы здравоохранения не является центральным контрагентом.", + "10.2": "Организация сферы здравоохранения не является центральным депозитарием.", + "10.3": "Организация сферы здравоохранения не является негосударственным пенсионным фондом.", + "10.4": "Организация сферы здравоохранения не является страховой организацией.", + "10.5": "Организация сферы здравоохранения не является оператором услуг информационного обмена.", + "11": "Объекты КИИ организации сферы здравоохранения не относятся к опасным производственным объектам в соответствии с законодательством Российской Федерации.", + "12": "Организация сферы здравоохранения не выполняет функции пункта управления (ситуационного центра) государственного органа власти или государственной корпорации.", + "13": "Организация сферы здравоохранения не является субъектом обеспечения обороны страны, безопасности государства и правопорядка.", + "14": "Организация сферы здравоохранения не имеет информационных систем, функционирующих в области обеспечения обороны страны, безопасности государства и правопорядка." + } +} \ No newline at end of file diff --git a/dokogen/generator.py b/dokogen/generator.py index 4afffb6..8214cad 100644 --- a/dokogen/generator.py +++ b/dokogen/generator.py @@ -692,9 +692,6 @@ def _highlight_unmatched(doc): pos = m.end() if pos < len(full): _add_run_with_tabs(para, full[pos:], first) - run = para.add_run(full[pos:]) - if first is not None: - _copy_run_style(first, run) for para in doc.paragraphs: _process_para(para) diff --git a/dokogen/help_docs.py b/dokogen/help_docs.py index 6fd98ff..d8b2442 100644 --- a/dokogen/help_docs.py +++ b/dokogen/help_docs.py @@ -83,7 +83,7 @@ def _generate_variables_reference(path): _p(doc, 'Список переменных для шаблонов Word. Переменная подставляется в текст ' 'как есть: {{имя_переменной}}. Циклы: ... .') - groups = {'A': 'Общие переменные', 'B': '187-ФЗ (персональные данные)'} + groups = {'A': 'Общие переменные', 'B': '187-ФЗ (критическая информационная инфраструктура)'} by_group = {'A': [], 'B': []} for var, group, desc, _ in VARIABLE_DEFS: by_group.setdefault(group, []).append((var, desc)) diff --git a/dokogen/importer.py b/dokogen/importer.py index 273d94e..9449319 100644 --- a/dokogen/importer.py +++ b/dokogen/importer.py @@ -97,13 +97,18 @@ def normalize_address(text: str) -> str: return ''.join(out) -def clean_value(val): - """Очистка и нормализация значения ячейки Excel.""" +def clean_value(val, as_date=False): + """Очистка и нормализация значения ячейки Excel. + + as_date=True — конвертировать серийный номер Excel в дату (только для + полей-дат, напр. ogrnDate). Для финансовых полей НЕ вызывается, + иначе число 50 000 руб. превратится в «дату» (см. аудит importer.py:105-109). + """ if val is None: return '' if isinstance(val, (int, float)): - # Попытка распознать дату (серийный номер Excel) - if 10000 < val < 80000: + # Попытка распознать дату (серийный номер Excel) — только для полей-дат + if as_date and 10000 < val < 80000: serial = int(val) if serial > 60: serial -= 1 @@ -253,7 +258,7 @@ def _import_187_common(wb, data, settings, log_fn): if sn.lower() == sheet_part.lower(): target_ws = wb[sn] break - val = clean_value(cell_value(target_ws, coord)) + val = clean_value(cell_value(target_ws, coord), as_date=('дата' in name.lower() or 'date' in key.lower())) if val: if key in ('chiefFio', 'responsibleFio', 'ispdnFio', 'adminFio'): val = _extract_single_fio(val) @@ -273,6 +278,8 @@ def _import_187_common(wb, data, settings, log_fn): if val: if key in ('chiefFio', 'responsibleFio', 'ispdnFio', 'adminFio'): val = _extract_single_fio(val) + elif 'дата' in name.lower() or 'date' in key.lower(): + val = clean_value(val, as_date=True) data[key] = val log_fn(f" {name} [по ключу]: {val}") break diff --git a/dokogen/models.py b/dokogen/models.py index 9c94f17..19bee90 100644 --- a/dokogen/models.py +++ b/dokogen/models.py @@ -135,9 +135,15 @@ class Company: income_year_3: str = "" # Доход за 3 года назад (руб.) income_year_4: str = "" # Доход за 2 года назад (руб.) income_year_5: str = "" # Доход за прошлый год (руб.) - income_loss: str = "" # Суммарный экономический ущерб от нарушения + income_loss: str = "" # Снижение уровня дохода (суммарный ущерб) от нарушения # критических процессов (руб.) — ΔДоходов для п. 8 + # Показатель 9: компоненты ΔБ (унификация автозаполнения и авторасчёта) + ndpi: str = "" # Снижение выплат от налога на добычу ПИ ΔНДПИ (руб.) + div: str = "" # Снижение выплат от дивидендов ΔДИВ (руб.) + vso: str = "" # Снижение выплат сторонним организациям ΔВСО (руб.) + ns: str = "" # Снижение сборов ΔНС (руб.) + # Списки commission: List[CommissionMember] = field(default_factory=list) information_systems: List[InformationSystem] = field(default_factory=list) diff --git a/dokogen/ui.py b/dokogen/ui.py index f6c8b56..686e1e1 100644 --- a/dokogen/ui.py +++ b/dokogen/ui.py @@ -26,6 +26,11 @@ from .verify import ( ) from .api_settings import load_api_settings, save_api_settings +# Бусредн — средний доход федерального бюджета за 3 года (данные Минфина). +# ВАЖНО: значение устаревает — обновлять ежегодно по данным Минфина или +# запрашивать у оператора в диалоге авторасчёта (поле редактируется). +DEFAULT_BUDGET_AVG = 21228053357700.0 + class DokoGenApp: """Главное окно приложения.""" @@ -353,7 +358,7 @@ class DokoGenApp: ("Доход за 3 года назад (руб.):", "income_year_3"), ("Доход за 2 года назад (руб.):", "income_year_4"), ("Доход за прошлый год (руб.):", "income_year_5"), - ("Суммарный экономический ущерб от нарушения критических процессов (руб.):", "income_loss"), + ("Снижение уровня дохода от нарушения критических процессов (руб.):", "income_loss"), ] for j, (label, key) in enumerate(income_fields): ttk.Label(f_fin, text=label).grid(row=len(fin_fields) + 1 + j, column=0, sticky=tk.W, pady=2) @@ -1418,8 +1423,6 @@ class DokoGenApp: ('is_os_sw_listbox', 'os_sw'), ('is_app_sw_listbox', 'app_sw'), ('is_attacker_category_combo', 'attacker_category'), - ('is_attacker_level_combo', 'attacker_level'), - ('is_attacker_types_listbox', 'attacker_types'), ('is_attacker_level_combo', 'attacker_level'), ('is_attacker_types_listbox', 'attacker_types'), ('is_threats_listbox', 'threats'), @@ -2079,7 +2082,7 @@ class DokoGenApp: def _open_criteria_127_dialog(self): """Окно заполнения показателей критериев значимости по ПП РФ № 127. - Таблица перенесена дословно из Постановления Правительства РФ от 08.02.2019 № 127: + Таблица перенесена дословно из Постановления Правительства РФ от 08.02.2018 № 127: 5 критериев (социальная, политическая, экономическая, экологическая, значимость для обороны страны), показатели 1-14 с подпунктами, значения сразу привязаны к категориям III / II / I. @@ -2169,7 +2172,7 @@ class DokoGenApp: ttk.Label(body, text="Показатели критериев значимости объекта КИИ", font=('Arial', 11, 'bold')).pack(anchor=tk.W, pady=(0, 2)) - ttk.Label(body, text="Постановление Правительства РФ от 08.02.2019 № 127 «Об утверждении Правил категорирования " + ttk.Label(body, text="Постановление Правительства РФ от 08.02.2018 № 127 «Об утверждении Правил категорирования " "объектов критической информационной инфраструктуры РФ» (приложение). " "По каждому показателю выберите значение, которому соответствует объект. " "Категория определяется по наивысшей из выбранных.", @@ -2247,7 +2250,7 @@ class DokoGenApp: "нарушения (в опроснике её нет, вводится вручную; можно приравнять к ущербу из показателя 8).", foreground='#8a6d00', wraplength=900, justify=tk.LEFT).pack(anchor=tk.W) e_budget = _row("Доход федерального бюджета, усреднённый за 3 года Бусредн (руб.):", 'budget', - '21228053357700') + f'{DEFAULT_BUDGET_AVG:.0f}') ttk.Label(frm, text="⚠️ Бусредн — средний доход федерального бюджета за 3 года (данные Минфина, " "подставлено значение по умолчанию; при необходимости уточните).", foreground='#8a6d00', wraplength=900, justify=tk.LEFT).pack(anchor=tk.W) @@ -2275,19 +2278,22 @@ class DokoGenApp: e_profit = e_tax = None # Доходы за 5 лет — из данных организации (поля «Доходы за 5 лет») - _incs = [_num(getattr(self.model, f'income_year_{i}', '') or '') for i in range(1, 6)] - _incs = [x for x in _incs if x is not None] - _d_avg = (sum(_incs) / len(_incs)) if _incs else None + _incs_raw = [getattr(self.model, f'income_year_{i}', '') or '' for i in range(1, 6)] + _incs = [_num(x) for x in _incs_raw] + _incs_filled = [x for x in _incs if x is not None] + _d_avg = (sum(_incs_filled) / len(_incs_filled)) if _incs_filled else None _d_loss = _num(getattr(self.model, 'income_loss', '') or '') - e_dincome = _row("Суммарный экономический ущерб от нарушения критических процессов (руб.):", + e_dincome = _row("Снижение уровня дохода от нарушения критических процессов (руб.):", 'dincome', f"{_d_loss:,.0f}" if _d_loss else '') - ttk.Label(frm, text="⚠️ Ущерб — поле «Суммарный ущерб (показатель 8)» из вкладки «Организация» " + ttk.Label(frm, text="⚠️ ΔДоходов — поле «Снижение уровня дохода (показатель 8)» из вкладки «Организация» " "(подставлено автоматически, можно поправить).", foreground='#8a6d00', wraplength=900, justify=tk.LEFT).pack(anchor=tk.W) e_budget = _row("Доходы субъекта КИИ, усреднённые за 5 лет (руб.):", 'budget', f"{_d_avg:,.0f}" if _d_avg else '') - ttk.Label(frm, text="⚠️ Доходы за 5 лет — среднее по полям «Доход за …» из вкладки «Организация» " - "(подставлено автоматически, можно поправить).", + _n_years = len(_incs_filled) + ttk.Label(frm, text=f"⚠️ Доходы за 5 лет — среднее по заполненным годам ({_n_years} из 5). " + "ПП-127 требует усреднение за прошедший пятилетний период: " + "при неполных данных результат методологически некорректен — заполните все 5 лет.", foreground='#8a6d00', wraplength=900, justify=tk.LEFT).pack(anchor=tk.W) result = tk.Text(frm, height=8, wrap=tk.WORD, state=tk.NORMAL) result.pack(fill=tk.BOTH, expand=True, pady=(8, 0)) @@ -2302,7 +2308,7 @@ class DokoGenApp: result.insert('1.0', "Заполните оба поля (ущерб и доходы за 5 лет).") return val = d / b * 100 - lines.append(f"Суммарный экономический ущерб от нарушения критических процессов: {d:,.0f} руб.") + lines.append(f"Снижение уровня дохода от нарушения критических процессов: {d:,.0f} руб.") lines.append(f"Доходы субъекта КИИ, усреднённые за 5 лет: {b:,.0f} руб.") lines.append(f"№8 = ущерб / доходы за 5 лет × 100% = {d:,.0f} / {b:,.0f} × 100 = {val:.2f} %") if val > 20: @@ -2585,7 +2591,7 @@ class DokoGenApp: if chosen: auto_parts.append( "Категория значимости объекта КИИ определена в соответствии с Правилами категорирования " - "объектов критической информационной инфраструктуры РФ (ПП РФ от 08.02.2019 № 127). " + "объектов критической информационной инфраструктуры РФ (ПП РФ от 08.02.2018 № 127). " f"Объект отнесён к {cat} категории значимости. Основания: " + "; ".join(f"показатель {num} ({c} категория)" for num, _, c, _ in chosen) + "." ) @@ -2685,12 +2691,18 @@ class DokoGenApp: parent=dialog, minvalue=0) if dincome is None: continue - budget = 21228053357700.0 # Бусредн (доход федерального бюджета за 3 года) + budget = DEFAULT_BUDGET_AVG # Бусредн (доход федерального бюджета за 3 года) knp = tax / profit dnp = dincome * knp if dincome > profit: dnp = tax - val = dnp / budget * 100 + # Унификация с авторасчётом: ΔБ = ΔНП + ΔНДПИ + ΔДИВ + ΔВСО + ΔНС + ndpi = _num2(getattr(self.model, 'ndpi', '') or '') or 0 + div = _num2(getattr(self.model, 'div', '') or '') or 0 + vso = _num2(getattr(self.model, 'vso', '') or '') or 0 + ns = _num2(getattr(self.model, 'ns', '') or '') or 0 + db = dnp + ndpi + div + vso + ns + val = db / budget * 100 if val > 0.001: cat = 'I' elif val > 0.0006: @@ -2708,8 +2720,10 @@ class DokoGenApp: calc_txt = ( f"Коэффициент КНП = налог/прибыль = {tax:,.0f} / {profit:,.0f} = {knp:.4f}\n" f"ΔНП = ΔДоходов × КНП = {dincome:,.0f} × {knp:.4f} = {dnp:,.1f} руб.\n" + f"ΔНДПИ = {ndpi:,.0f}, ΔДИВ = {div:,.0f}, ΔВСО = {vso:,.0f}, ΔНС = {ns:,.0f} руб.\n" + f"ΔБ = ΔНП + ΔНДПИ + ΔДИВ + ΔВСО + ΔНС = {db:,.1f} руб.\n" f"Бусредн (доход федерального бюджета за 3 года) = {budget:,.0f} руб.\n" - f"№9 = ΔБ / Бусредн × 100% = {dnp:,.1f} / {budget:,.0f} × 100 = {val:.6f} %\n" + f"№9 = ΔБ / Бусредн × 100% = {db:,.1f} / {budget:,.0f} × 100 = {val:.6f} %\n" f"Категория значимости: {cat or '— (не присваивается)'}" ) prefix = f"Показатель {num}. {name} — рекомендация по расчёту:" @@ -4136,8 +4150,8 @@ class DokoGenApp: 'chief_fio': 'chiefFio', 'responsible_position': 'responsiblePosition', 'responsible_fio': 'responsibleFio', - 'admin_position': ('administratorPosition', 'ispdnPosition'), - 'admin_fio': ('administratorFio', 'ispdnFio'), + 'admin_position': 'administratorPosition', + 'admin_fio': 'administratorFio', } for entry_key, data_keys in officials_map.items(): entry = self._official_entries.get(entry_key) diff --git a/dokogen/verify.py b/dokogen/verify.py index 9c7bc8f..2b28c81 100644 --- a/dokogen/verify.py +++ b/dokogen/verify.py @@ -694,7 +694,8 @@ def check_company_ai(company_data, api_key, model='deepseek-chat', base_url=None if not api_key: return None, "Не задан API-ключ ИИ (Файл → Настройки → API)" system = ( - "Ты — юрист по защите персональных данных (187-ФЗ). Проверь реквизиты организации. " + "Ты — специалист по проверке реквизитов организации (ИНН/КПП/ОГРН). " + "Проверь реквизиты организации. " "Верни ТОЛЬКО JSON: " '{"оценка": "ok|warning|error", ' '"замечания": ["..."], ' diff --git a/test_generator.py b/test_generator.py index a010332..d3e7ae9 100644 --- a/test_generator.py +++ b/test_generator.py @@ -30,8 +30,8 @@ def create_test_template(): doc.add_paragraph('') doc.add_paragraph('=== IS: {{item.name}} ===') doc.add_paragraph('Category: {{item.category}}') - doc.add_paragraph('Threat type: {{item.threat_type}}') - doc.add_paragraph('Defence level: {{item.defence_level}}') + doc.add_paragraph('Sphere: {{item.sphere}}') + doc.add_paragraph('Object type: {{item.object_type}}') doc.add_paragraph('') # Inner loop: commission @@ -72,12 +72,12 @@ def test_zip_loop_expansion(): CommissionMember(role='Член комиссии', position='Бухгалтер', fio='Сидорова А.А.'), ], information_systems=[ - InformationSystem(name='ИС-1 "Бухгалтерия"', category='специальные', - pd_count='менее 100 000', threat_type='1', defence_level='2'), - InformationSystem(name='ИС-2 "Кадры"', category='иные', - pd_count='более 100 000', threat_type='3', defence_level='4'), - InformationSystem(name='ИС-3 "Склад"', category='общедоступные', - pd_count='менее 100 000', threat_type='2', defence_level='3'), + InformationSystem(name='ОКИИ-1 "Платёжный шлюз"', category='1', + sphere='Банковская сфера и финансовый рынок', object_type='АСУ'), + InformationSystem(name='ОКИИ-2 "Кадры"', category='2', + sphere='Наука', object_type='ИС'), + InformationSystem(name='ОКИИ-3 "Склад"', category='3', + sphere='Здравоохранение', object_type='ИС'), ] )