feat: ребрендинг под dokogen149FZ (основа из dokogen187)
This commit is contained in:
+1
-1
@@ -11,7 +11,7 @@ from PyInstaller.utils.hooks import collect_data_files, collect_submodules
|
|||||||
datas = [
|
datas = [
|
||||||
# Справочники (defense_tools.json, pd_actions.json, pd_items.json, pd_categories.json)
|
# Справочники (defense_tools.json, pd_actions.json, pd_items.json, pd_categories.json)
|
||||||
('dokogen/dictionaries', 'dokogen/dictionaries'),
|
('dokogen/dictionaries', 'dokogen/dictionaries'),
|
||||||
# Пустой опросник 187-ФЗ (для «Справка → Пустой опросник»)
|
# Пустой опросник 149-ФЗ (117 ПР ФСТЭК) (для «Справка → Пустой опросник»)
|
||||||
('dokogen/data', 'dokogen/data'),
|
('dokogen/data', 'dokogen/data'),
|
||||||
# Иконка приложения (для окна программы в exe)
|
# Иконка приложения (для окна программы в exe)
|
||||||
('dokogen/icon.ico', 'dokogen'),
|
('dokogen/icon.ico', 'dokogen'),
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""DokoGen — Пакет генератора документов 187-ФЗ"""
|
"""DokoGen — Пакет генератора документов 149-ФЗ (117 ПР ФСТЭК)"""
|
||||||
|
|
||||||
from .models import Company, CommissionMember, InformationSystem, PaperDocument
|
from .models import Company, CommissionMember, InformationSystem, PaperDocument
|
||||||
from .declension import inflect_name, decline, company_name_decline, get_short_fio, get_initials
|
from .declension import inflect_name, decline, company_name_decline, get_short_fio, get_initials
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
рядом с программой (при сборке .exe — рядом с exe):
|
рядом с программой (при сборке .exe — рядом с exe):
|
||||||
- Инструкция_пользователя.docx
|
- Инструкция_пользователя.docx
|
||||||
- Инструкция_администратора.docx
|
- Инструкция_администратора.docx
|
||||||
- Опросный_лист_187_ФЗ.xlsx (пустой шаблон)
|
- Опросный_лист_149_FZ.xlsx (пустой шаблон)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
@@ -45,7 +45,7 @@ def variables_reference_path():
|
|||||||
|
|
||||||
|
|
||||||
def survey_path():
|
def survey_path():
|
||||||
return os.path.join(docs_dir(), 'Опросный_лист_187_ФЗ.xlsx')
|
return os.path.join(docs_dir(), 'Опросный_лист_149_FZ.xlsx')
|
||||||
|
|
||||||
|
|
||||||
def _h(doc, text, size=14):
|
def _h(doc, text, size=14):
|
||||||
@@ -83,7 +83,7 @@ def _generate_variables_reference(path):
|
|||||||
_p(doc, 'Список переменных для шаблонов Word. Переменная подставляется в текст '
|
_p(doc, 'Список переменных для шаблонов Word. Переменная подставляется в текст '
|
||||||
'как есть: {{имя_переменной}}. Циклы: <!-- loop:имя --> ... <!-- loop_end -->.')
|
'как есть: {{имя_переменной}}. Циклы: <!-- loop:имя --> ... <!-- loop_end -->.')
|
||||||
|
|
||||||
groups = {'A': 'Общие переменные', 'B': '187-ФЗ (персональные данные)'}
|
groups = {'A': 'Общие переменные', 'B': '149-ФЗ (117 ПР ФСТЭК) (персональные данные)'}
|
||||||
by_group = {'A': [], 'B': []}
|
by_group = {'A': [], 'B': []}
|
||||||
for var, group, desc, _ in VARIABLE_DEFS:
|
for var, group, desc, _ in VARIABLE_DEFS:
|
||||||
by_group.setdefault(group, []).append((var, desc))
|
by_group.setdefault(group, []).append((var, desc))
|
||||||
@@ -108,11 +108,11 @@ def _generate_variables_reference(path):
|
|||||||
def _generate_user_manual(path):
|
def _generate_user_manual(path):
|
||||||
"""Инструкция пользователя: программа, циклы, переменные, проверки."""
|
"""Инструкция пользователя: программа, циклы, переменные, проверки."""
|
||||||
doc = docx.Document()
|
doc = docx.Document()
|
||||||
_h(doc, 'ДоКоГеНеРаТоР — 187-ФЗ')
|
_h(doc, 'ДоКоГеНеРаТоР — 149-ФЗ (117 ПР ФСТЭК)')
|
||||||
_h(doc, 'Инструкция пользователя', 13)
|
_h(doc, 'Инструкция пользователя', 13)
|
||||||
|
|
||||||
_h(doc, '1. Что это за программа', 12)
|
_h(doc, '1. Что это за программа', 12)
|
||||||
_p(doc, 'Программа автоматически заполняет шаблоны документов по 187-ФЗ '
|
_p(doc, 'Программа автоматически заполняет шаблоны документов по 149-ФЗ (117 ПР ФСТЭК) '
|
||||||
'(акты обследования, политики, инструкции, приказы и т.д.) данными '
|
'(акты обследования, политики, инструкции, приказы и т.д.) данными '
|
||||||
'об организации, информационных системах, комиссии и сотрудниках. '
|
'об организации, информационных системах, комиссии и сотрудниках. '
|
||||||
'Данные можно ввести вручную на вкладках или импортировать из '
|
'Данные можно ввести вручную на вкладках или импортировать из '
|
||||||
@@ -173,7 +173,7 @@ def _generate_user_manual(path):
|
|||||||
def _generate_admin_manual(path):
|
def _generate_admin_manual(path):
|
||||||
"""Инструкция администратора: словари, API-ключи, лимиты."""
|
"""Инструкция администратора: словари, API-ключи, лимиты."""
|
||||||
doc = docx.Document()
|
doc = docx.Document()
|
||||||
_h(doc, 'ДоКоГеНеРаТоР — 187-ФЗ')
|
_h(doc, 'ДоКоГеНеРаТоР — 149-ФЗ (117 ПР ФСТЭК)')
|
||||||
_h(doc, 'Инструкция администратора', 13)
|
_h(doc, 'Инструкция администратора', 13)
|
||||||
|
|
||||||
_h(doc, '1. Файлы настроек рядом с программой', 12)
|
_h(doc, '1. Файлы настроек рядом с программой', 12)
|
||||||
@@ -240,8 +240,8 @@ def _ensure_survey():
|
|||||||
return target
|
return target
|
||||||
# Ищем шаблон опросника: рядом с программой или встроенный
|
# Ищем шаблон опросника: рядом с программой или встроенный
|
||||||
candidates = [
|
candidates = [
|
||||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data', 'Опросный_лист_187_ФЗ.xlsx'),
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data', 'Опросный_лист_149_FZ.xlsx'),
|
||||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Опросный_лист_187_ФЗ.xlsx'),
|
os.path.join(os.path.dirname(os.path.abspath(__file__)), 'Опросный_лист_149_FZ.xlsx'),
|
||||||
]
|
]
|
||||||
for c in candidates:
|
for c in candidates:
|
||||||
if os.path.exists(c):
|
if os.path.exists(c):
|
||||||
|
|||||||
+9
-9
@@ -1,5 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""DokoGen — Модуль импорта из Excel (опросный лист 187-ФЗ)"""
|
"""DokoGen — Модуль импорта из Excel (опросный лист 149-ФЗ (117 ПР ФСТЭК))"""
|
||||||
|
|
||||||
import re
|
import re
|
||||||
import traceback
|
import traceback
|
||||||
@@ -122,8 +122,8 @@ def clean_value(val):
|
|||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
def import_187fz(filepath: str, log_fn: Optional[Callable] = None, settings_path: Optional[str] = None) -> dict:
|
def import_149fz(filepath: str, log_fn: Optional[Callable] = None, settings_path: Optional[str] = None) -> dict:
|
||||||
"""Импорт данных из опросного листа Excel (187-ФЗ).
|
"""Импорт данных из опросного листа Excel (149-ФЗ (117 ПР ФСТЭК)).
|
||||||
|
|
||||||
settings_path — путь к JSON-файлу настроек импорта (что и откуда брать).
|
settings_path — путь к JSON-файлу настроек импорта (что и откуда брать).
|
||||||
Если None — файл import_settings.json рядом с программой; если его нет —
|
Если None — файл import_settings.json рядом с программой; если его нет —
|
||||||
@@ -154,9 +154,9 @@ def import_187fz(filepath: str, log_fn: Optional[Callable] = None, settings_path
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
try:
|
try:
|
||||||
_import_187_common(wb, data, settings, log_fn)
|
_import_149_common(wb, data, settings, log_fn)
|
||||||
_import_187_is_list(wb, data, settings, log_fn)
|
_import_149_is_list(wb, data, settings, log_fn)
|
||||||
_import_187_employees(wb, data, settings, log_fn)
|
_import_149_employees(wb, data, settings, log_fn)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log_fn(f"❌ Ошибка при импорте: {e}")
|
log_fn(f"❌ Ошибка при импорте: {e}")
|
||||||
log_fn(traceback.format_exc())
|
log_fn(traceback.format_exc())
|
||||||
@@ -179,7 +179,7 @@ def import_187fz(filepath: str, log_fn: Optional[Callable] = None, settings_path
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def _import_187_common(wb, data, settings, log_fn):
|
def _import_149_common(wb, data, settings, log_fn):
|
||||||
"""Импорт общих сведений по словарю настроек.
|
"""Импорт общих сведений по словарю настроек.
|
||||||
|
|
||||||
Для каждого поля: сначала пробуем конкретную ячейку (cell),
|
Для каждого поля: сначала пробуем конкретную ячейку (cell),
|
||||||
@@ -247,7 +247,7 @@ def _import_187_common(wb, data, settings, log_fn):
|
|||||||
break
|
break
|
||||||
|
|
||||||
|
|
||||||
def _import_187_is_list(wb, data, settings, log_fn):
|
def _import_149_is_list(wb, data, settings, log_fn):
|
||||||
"""Импорт информационных систем из остальных листов."""
|
"""Импорт информационных систем из остальных листов."""
|
||||||
exclude_kws = settings.get('is_sheet_exclude_keywords', ['общие сведения', 'сотрудники'])
|
exclude_kws = settings.get('is_sheet_exclude_keywords', ['общие сведения', 'сотрудники'])
|
||||||
is_sheets = []
|
is_sheets = []
|
||||||
@@ -447,7 +447,7 @@ def _import_187_is_list(wb, data, settings, log_fn):
|
|||||||
log_fn(f"\n✅ Всего импортировано ИС: {len(is_list)}")
|
log_fn(f"\n✅ Всего импортировано ИС: {len(is_list)}")
|
||||||
|
|
||||||
|
|
||||||
def _import_187_employees(wb, data, settings, log_fn):
|
def _import_149_employees(wb, data, settings, log_fn):
|
||||||
"""Импорт комиссии и сотрудников из листа «Сотрудники»."""
|
"""Импорт комиссии и сотрудников из листа «Сотрудники»."""
|
||||||
sheet_name = None
|
sheet_name = None
|
||||||
sheet_kw = settings.get('employees_sheet_keyword', 'сотруд')
|
sheet_kw = settings.get('employees_sheet_keyword', 'сотруд')
|
||||||
|
|||||||
+2
-2
@@ -1,5 +1,5 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""DokoGen — Модели данных (только 187-ФЗ, только нужные поля)"""
|
"""DokoGen — Модели данных (только 149-ФЗ (117 ПР ФСТЭК), только нужные поля)"""
|
||||||
|
|
||||||
from dataclasses import dataclass, field, asdict
|
from dataclasses import dataclass, field, asdict
|
||||||
from typing import List, Dict, Any
|
from typing import List, Dict, Any
|
||||||
@@ -49,7 +49,7 @@ class PaperDocument:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Company:
|
class Company:
|
||||||
"""Организация — основной объект (187-ФЗ)"""
|
"""Организация — основной объект (149-ФЗ (117 ПР ФСТЭК))"""
|
||||||
# Полное название
|
# Полное название
|
||||||
full_name: str = ""
|
full_name: str = ""
|
||||||
company_name_1: str = "" # именительный
|
company_name_1: str = "" # именительный
|
||||||
|
|||||||
+8
-8
@@ -31,7 +31,7 @@ class DokoGenApp:
|
|||||||
|
|
||||||
def __init__(self, root: tk.Tk):
|
def __init__(self, root: tk.Tk):
|
||||||
self.root = root
|
self.root = root
|
||||||
root.title("ДоКоГеНеРаТоР — 187-ФЗ (КИИ)")
|
root.title("ДоКоГеНеРаТоР — 149-ФЗ (117 ПР ФСТЭК)")
|
||||||
root.geometry("1200x800")
|
root.geometry("1200x800")
|
||||||
root.minsize(900, 600)
|
root.minsize(900, 600)
|
||||||
self._set_app_icon()
|
self._set_app_icon()
|
||||||
@@ -44,7 +44,7 @@ class DokoGenApp:
|
|||||||
self._last_template_dir: str = ""
|
self._last_template_dir: str = ""
|
||||||
self._autosave_path = os.path.join(
|
self._autosave_path = os.path.join(
|
||||||
os.path.dirname(os.path.abspath(__file__)),
|
os.path.dirname(os.path.abspath(__file__)),
|
||||||
'.autosave', 'autosave_187.json'
|
'.autosave', 'autosave_149fz.json'
|
||||||
)
|
)
|
||||||
|
|
||||||
# Путь к файлу настроек импорта (словарь «что и откуда брать»)
|
# Путь к файлу настроек импорта (словарь «что и откуда брать»)
|
||||||
@@ -1757,7 +1757,7 @@ class DokoGenApp:
|
|||||||
is_list=self.model.information_systems,
|
is_list=self.model.information_systems,
|
||||||
doc_number=self.doc_number_var.get(),
|
doc_number=self.doc_number_var.get(),
|
||||||
doc_date=self.doc_date_var.get(),
|
doc_date=self.doc_date_var.get(),
|
||||||
direction='187fz',
|
direction='149fz',
|
||||||
)
|
)
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
@@ -1846,7 +1846,7 @@ class DokoGenApp:
|
|||||||
|
|
||||||
def import_from_survey(self):
|
def import_from_survey(self):
|
||||||
path = filedialog.askopenfilename(
|
path = filedialog.askopenfilename(
|
||||||
title="Выберите опросный лист (187-ФЗ)",
|
title="Выберите опросный лист (149-ФЗ (117 ПР ФСТЭК))",
|
||||||
filetypes=[("Excel", "*.xlsx *.xls")]
|
filetypes=[("Excel", "*.xlsx *.xls")]
|
||||||
)
|
)
|
||||||
if not path:
|
if not path:
|
||||||
@@ -1856,8 +1856,8 @@ class DokoGenApp:
|
|||||||
# Импорт в отдельном потоке
|
# Импорт в отдельном потоке
|
||||||
def _run():
|
def _run():
|
||||||
try:
|
try:
|
||||||
from .importer import import_187fz
|
from .importer import import_149fz
|
||||||
data = import_187fz(path, self.log, settings_path=self.import_settings_path)
|
data = import_149fz(path, self.log, settings_path=self.import_settings_path)
|
||||||
if data:
|
if data:
|
||||||
self._apply_imported_data(data)
|
self._apply_imported_data(data)
|
||||||
except ImportError:
|
except ImportError:
|
||||||
@@ -2203,7 +2203,7 @@ class DokoGenApp:
|
|||||||
|
|
||||||
def show_about(self):
|
def show_about(self):
|
||||||
messagebox.showinfo("О программе",
|
messagebox.showinfo("О программе",
|
||||||
"ДоКоГеНеРаТоР — 187-ФЗ\n"
|
"ДоКоГеНеРаТоР — 149-ФЗ (117 ПР ФСТЭК)\n"
|
||||||
"Версия 1.0\n"
|
"Версия 1.0\n"
|
||||||
"Генератор документов из шаблонов")
|
"Генератор документов из шаблонов")
|
||||||
|
|
||||||
@@ -2251,7 +2251,7 @@ class DokoGenApp:
|
|||||||
def download_survey(self):
|
def download_survey(self):
|
||||||
from .help_docs import ensure_docs, survey_path
|
from .help_docs import ensure_docs, survey_path
|
||||||
ensure_docs()
|
ensure_docs()
|
||||||
self._save_resource(survey_path(), "Опросный_лист_187_ФЗ.xlsx", "Сохранить пустой опросник")
|
self._save_resource(survey_path(), "Опросный_лист_149_FZ.xlsx", "Сохранить пустой опросник")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ def _norm_name(s):
|
|||||||
# ОПИСАНИЕ ПЕРЕМЕННЫХ
|
# ОПИСАНИЕ ПЕРЕМЕННЫХ
|
||||||
# ============================================================
|
# ============================================================
|
||||||
# Формат: (имя_переменной, группа, описание, ключ_резолвера)
|
# Формат: (имя_переменной, группа, описание, ключ_резолвера)
|
||||||
# Группы: A — общие, B — 187-ФЗ
|
# Группы: A — общие, B — 149-ФЗ (117 ПР ФСТЭК)
|
||||||
|
|
||||||
VARIABLE_DEFS = [
|
VARIABLE_DEFS = [
|
||||||
# === Полное название организации (6 падежей) ===
|
# === Полное название организации (6 падежей) ===
|
||||||
@@ -68,7 +68,7 @@ VARIABLE_DEFS = [
|
|||||||
('{{document_date}}', 'A', 'Дата документа', 'doc_date'),
|
('{{document_date}}', 'A', 'Дата документа', 'doc_date'),
|
||||||
('{{document_number}}', 'A', 'Номер документа', 'doc_number'),
|
('{{document_number}}', 'A', 'Номер документа', 'doc_number'),
|
||||||
|
|
||||||
# === 187-ФЗ ===
|
# === 149-ФЗ (117 ПР ФСТЭК) ===
|
||||||
('{{is_security_requirements}}', 'B', 'Требования уровня защищённости', 'security_reqs'),
|
('{{is_security_requirements}}', 'B', 'Требования уровня защищённости', 'security_reqs'),
|
||||||
('{{is_list}}', 'B', 'Список всех ИС (нумерованный)', 'is_list'),
|
('{{is_list}}', 'B', 'Список всех ИС (нумерованный)', 'is_list'),
|
||||||
('{{is_name}}', 'B', 'Наименование первой ИС', 'is:name'),
|
('{{is_name}}', 'B', 'Наименование первой ИС', 'is:name'),
|
||||||
@@ -138,7 +138,7 @@ def build_replacements(
|
|||||||
is_list: List = None,
|
is_list: List = None,
|
||||||
doc_number: str = "",
|
doc_number: str = "",
|
||||||
doc_date: str = "",
|
doc_date: str = "",
|
||||||
direction: str = "187fz",
|
direction: str = "149fz",
|
||||||
) -> Dict[str, str]:
|
) -> Dict[str, str]:
|
||||||
"""Строит полный словарь замен {{переменная}} → значение."""
|
"""Строит полный словарь замен {{переменная}} → значение."""
|
||||||
if is_list is None:
|
if is_list is None:
|
||||||
@@ -159,7 +159,7 @@ def build_replacements(
|
|||||||
|
|
||||||
replacements = {}
|
replacements = {}
|
||||||
for var_name, group, desc, source in VARIABLE_DEFS:
|
for var_name, group, desc, source in VARIABLE_DEFS:
|
||||||
if direction == '187fz' and group not in ('A', 'B'):
|
if direction == '149fz' and group not in ('A', 'B'):
|
||||||
continue
|
continue
|
||||||
value = _resolve(source, ctx)
|
value = _resolve(source, ctx)
|
||||||
replacements[var_name] = str(value) if value else '—'
|
replacements[var_name] = str(value) if value else '—'
|
||||||
|
|||||||
+1
-1
@@ -399,7 +399,7 @@ def check_company_ai(company_data, api_key, model='deepseek-chat', base_url=None
|
|||||||
if not api_key:
|
if not api_key:
|
||||||
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
|
return None, "Не задан API-ключ ИИ (Файл → Настройки → API)"
|
||||||
system = (
|
system = (
|
||||||
"Ты — юрист по защите персональных данных (187-ФЗ). Проверь реквизиты организации. "
|
"Ты — юрист по защите персональных данных (149-ФЗ (117 ПР ФСТЭК)). Проверь реквизиты организации. "
|
||||||
"Верни ТОЛЬКО JSON: "
|
"Верни ТОЛЬКО JSON: "
|
||||||
'{"оценка": "ok|warning|error", '
|
'{"оценка": "ok|warning|error", '
|
||||||
'"замечания": ["..."], '
|
'"замечания": ["..."], '
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""DokoGen — Генератор документов 187-ФЗ
|
"""DokoGen — Генератор документов 149-ФЗ (117 ПР ФСТЭК)
|
||||||
Точка входа: python main.py
|
Точка входа: python main.py
|
||||||
"""
|
"""
|
||||||
import sys
|
import sys
|
||||||
|
|||||||
+2
-2
@@ -87,7 +87,7 @@ def test_zip_loop_expansion():
|
|||||||
is_list=company.information_systems,
|
is_list=company.information_systems,
|
||||||
doc_number='001',
|
doc_number='001',
|
||||||
doc_date='31.07.2026',
|
doc_date='31.07.2026',
|
||||||
direction='187fz'
|
direction='149fz'
|
||||||
)
|
)
|
||||||
|
|
||||||
loops = replacements.get('_loops', {})
|
loops = replacements.get('_loops', {})
|
||||||
@@ -135,7 +135,7 @@ def test_zip_loop_expansion():
|
|||||||
is_list=company.information_systems,
|
is_list=company.information_systems,
|
||||||
doc_number='001',
|
doc_number='001',
|
||||||
doc_date='31.07.2026',
|
doc_date='31.07.2026',
|
||||||
direction='187fz'
|
direction='149fz'
|
||||||
)
|
)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
Reference in New Issue
Block a user