feat: переделка под ОКИИ (187-ФЗ) — объекты КИИ вместо ИС, категории 1-3, ОКВЭД, убраны ПДн/бумажные носители
- вкладка «Информационные системы» → «Объекты КИИ», убрана «Структура объекта» - «Тип угроз»/«УЗ» → категория значимости 1/2/3/без категории - удалены Категории ПДн, кол-во записей, перечень/субъекты/действия с ПДн + словари - «Пользователи ИС» → «Пользователи ОКИИ», средства защиты оставлены - вкладка «Бумажные документы» удалена полностью (GUI + логика + модели) - ОКВЭД: поля во вкладке «Организация», словарь okved.json, выбор галочками, импорт - нормализация регистра адреса DaData (КАПС → нормальный) - importer: объекты КИИ (наименование объекта, ПО, СЗИ, пользователи)
This commit is contained in:
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
"""DokoGen — Пакет генератора документов 187-ФЗ"""
|
"""DokoGen — Пакет генератора документов 187-ФЗ"""
|
||||||
|
|
||||||
from .models import Company, CommissionMember, InformationSystem, PaperDocument
|
from .models import Company, CommissionMember, InformationSystem
|
||||||
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
|
||||||
from .variables import build_replacements, VARIABLE_DEFS
|
from .variables import build_replacements, VARIABLE_DEFS
|
||||||
from .generator import process_template
|
from .generator import process_template
|
||||||
|
|||||||
@@ -0,0 +1,268 @@
|
|||||||
|
[
|
||||||
|
"01.1 Выращивание однолетних культур",
|
||||||
|
"01.2 Выращивание многолетних культур",
|
||||||
|
"01.3 Выращивание рассады",
|
||||||
|
"01.4 Животноводство",
|
||||||
|
"01.5 Смешанное сельское хозяйство",
|
||||||
|
"01.6 Деятельность вспомогательная в области производства сельскохозяйственных культур и послеуборочной обработки сельхозпродукции",
|
||||||
|
"01.7 Охота, отлов и отстрел диких животных",
|
||||||
|
"01.8 Лесоводство и прочая деятельность в области лесозаготовок",
|
||||||
|
"02.1 Лесоводство и прочая деятельность в области лесозаготовок",
|
||||||
|
"02.2 Предоставление услуг в области лесоводства и лесозаготовок",
|
||||||
|
"02.3 Сбор и заготовка пищевых лесных ресурсов, недревесных лесных ресурсов и лекарственных растений",
|
||||||
|
"03.1 Рыболовство",
|
||||||
|
"03.2 Рыбоводство",
|
||||||
|
"05.1 Добыча угля",
|
||||||
|
"05.2 Добыча и обогащение угля",
|
||||||
|
"06.1 Добыча сырой нефти и природного газа",
|
||||||
|
"06.2 Добыча природного газа и газового конденсата",
|
||||||
|
"07.1 Добыча железных руд",
|
||||||
|
"07.2 Добыча руд цветных металлов",
|
||||||
|
"08.1 Добыча камня, песка и глины",
|
||||||
|
"08.9 Добыча полезных ископаемых, не включенных в другие группировки",
|
||||||
|
"09.1 Деятельность вспомогательная в сфере добычи нефти и природного газа",
|
||||||
|
"10.1 Переработка и консервирование мяса и мясной пищевой продукции",
|
||||||
|
"10.2 Переработка и консервирование рыбы, ракообразных и моллюсков",
|
||||||
|
"10.3 Переработка и консервирование фруктов и овощей",
|
||||||
|
"10.4 Производство растительных и животных масел и жиров",
|
||||||
|
"10.5 Производство молочной продукции",
|
||||||
|
"10.6 Производство мукомольной и крупяной промышленности, крахмала и крахмалосодержащих продуктов",
|
||||||
|
"10.7 Производство хлебобулочных и мучных кондитерских изделий",
|
||||||
|
"10.8 Производство прочих пищевых продуктов",
|
||||||
|
"10.9 Производство готовых кормов для животных",
|
||||||
|
"11.0 Производство напитков",
|
||||||
|
"12.0 Производство табачных изделий",
|
||||||
|
"13.1 Прядение текстильных волокон",
|
||||||
|
"13.2 Ткацкое производство",
|
||||||
|
"13.3 Отделка тканей и текстильных изделий",
|
||||||
|
"13.9 Производство прочих текстильных изделий",
|
||||||
|
"14.1 Производство одежды, кроме одежды из меха",
|
||||||
|
"14.2 Производство меховых изделий",
|
||||||
|
"14.3 Производство вязаных и трикотажных изделий одежды",
|
||||||
|
"15.1 Дубление и отделка кожи, производство чемоданов, сумок и т.п.",
|
||||||
|
"15.2 Производство обуви",
|
||||||
|
"16.1 Распиловка и строгание древесины",
|
||||||
|
"16.2 Производство изделий из дерева, пробки, соломки",
|
||||||
|
"17.1 Производство целлюлозы, древесной массы, бумаги и картона",
|
||||||
|
"17.2 Производство изделий из бумаги и картона",
|
||||||
|
"18.1 Деятельность полиграфическая и предоставление услуг в этой области",
|
||||||
|
"18.2 Копирование записанных носителей информации",
|
||||||
|
"19.1 Производство кокса",
|
||||||
|
"19.2 Производство нефтепродуктов",
|
||||||
|
"20.1 Производство основных химических веществ, удобрений, пластмасс и синтетического каучука",
|
||||||
|
"20.2 Производство пестицидов и прочей агрохимической продукции",
|
||||||
|
"20.3 Производство красок, лаков и аналогичных покрытий",
|
||||||
|
"20.4 Производство мыла и моющих, чистящих и полирующих средств",
|
||||||
|
"20.5 Производство прочих химических продуктов",
|
||||||
|
"21.1 Производство фармацевтических субстанций",
|
||||||
|
"21.2 Производство лекарственных препаратов и материалов",
|
||||||
|
"22.1 Производство резиновых изделий",
|
||||||
|
"22.2 Производство пластмассовых изделий",
|
||||||
|
"23.1 Производство стекла и изделий из стекла",
|
||||||
|
"23.2 Производство огнеупорных изделий",
|
||||||
|
"23.3 Производство строительных керамических материалов",
|
||||||
|
"23.4 Производство прочих фарфоровых и керамических изделий",
|
||||||
|
"23.5 Производство цемента, извести и гипса",
|
||||||
|
"23.6 Производство изделий из бетона, цемента и гипса",
|
||||||
|
"23.7 Резка, обработка и отделка камня",
|
||||||
|
"23.9 Производство абразивных и неметаллических минеральных изделий",
|
||||||
|
"24.1 Производство чугуна, стали и ферросплавов",
|
||||||
|
"24.2 Производство стальных труб, полых профилей и фитингов",
|
||||||
|
"24.3 Производство прочих стальных изделий первичной обработки",
|
||||||
|
"24.4 Производство основных драгоценных металлов и цветных металлов",
|
||||||
|
"24.5 Литье металлов",
|
||||||
|
"25.1 Производство строительных металлических конструкций и изделий",
|
||||||
|
"25.2 Производство металлических цистерн, резервуаров и емкостей",
|
||||||
|
"25.3 Производство паровых котлов",
|
||||||
|
"25.4 Производство оружия и боеприпасов",
|
||||||
|
"25.5 Ковка, прессование, штамповка и профилирование; порошковая металлургия",
|
||||||
|
"25.6 Обработка металлических изделий механическая",
|
||||||
|
"25.7 Производство ножевых изделий и столовых приборов",
|
||||||
|
"25.9 Производство прочих готовых металлических изделий",
|
||||||
|
"26.1 Производство элементов электронной аппаратуры и печатных схем",
|
||||||
|
"26.2 Производство компьютеров и периферийного оборудования",
|
||||||
|
"26.3 Производство коммуникационного оборудования",
|
||||||
|
"26.4 Производство бытовой электроники",
|
||||||
|
"26.5 Производство контрольно-измерительных приборов и навигационных приборов",
|
||||||
|
"26.6 Производство облучающего и электротерапевтического оборудования",
|
||||||
|
"26.7 Производство оптических приборов, фото- и кинооборудования",
|
||||||
|
"26.8 Производство незаписанных магнитных и оптических носителей",
|
||||||
|
"27.1 Производство электродвигателей, генераторов, трансформаторов",
|
||||||
|
"27.2 Производство электрических аккумуляторов и аккумуляторных батарей",
|
||||||
|
"27.3 Производство кабелей и кабельной арматуры",
|
||||||
|
"27.4 Производство электрического осветительного оборудования",
|
||||||
|
"27.5 Производство бытовых приборов",
|
||||||
|
"27.9 Производство прочего электрического оборудования",
|
||||||
|
"28.1 Производство машин и оборудования общего назначения",
|
||||||
|
"28.2 Производство прочих машин и оборудования общего назначения",
|
||||||
|
"28.3 Производство машин и оборудования для сельского и лесного хозяйства",
|
||||||
|
"28.4 Производство станков, машин и оборудования для обработки металлов",
|
||||||
|
"28.9 Производство прочих машин и оборудования специального назначения",
|
||||||
|
"29.1 Производство автотранспортных средств",
|
||||||
|
"29.2 Производство кузовов, прицепов и полуприцепов",
|
||||||
|
"29.3 Производство комплектующих и принадлежностей для автотранспортных средств",
|
||||||
|
"30.1 Строительство кораблей, судов и лодок",
|
||||||
|
"30.2 Производство железнодорожных локомотивов и подвижного состава",
|
||||||
|
"30.3 Производство летательных аппаратов и космических аппаратов",
|
||||||
|
"30.4 Производство военных боевых машин",
|
||||||
|
"30.9 Производство прочих транспортных средств и оборудования",
|
||||||
|
"31.0 Производство мебели",
|
||||||
|
"32.1 Производство ювелирных изделий, бижутерии",
|
||||||
|
"32.2 Производство музыкальных инструментов",
|
||||||
|
"32.3 Производство спортивных товаров",
|
||||||
|
"32.4 Производство игр и игрушек",
|
||||||
|
"32.5 Производство медицинских инструментов и оборудования",
|
||||||
|
"32.9 Производство изделий, не включенных в другие группировки",
|
||||||
|
"33.1 Ремонт и монтаж металлических изделий и машин",
|
||||||
|
"33.2 Монтаж промышленных машин и оборудования",
|
||||||
|
"35.1 Производство, передача и распределение электроэнергии",
|
||||||
|
"35.2 Производство и распределение газообразного топлива",
|
||||||
|
"35.3 Производство, передача и распределение пара и горячей воды",
|
||||||
|
"36.0 Забор, очистка и распределение воды",
|
||||||
|
"37.0 Сбор и обработка сточных вод",
|
||||||
|
"38.1 Сбор отходов",
|
||||||
|
"38.2 Обработка и утилизация отходов",
|
||||||
|
"38.3 Деятельность по обработке вторичного сырья",
|
||||||
|
"39.0 Предоставление услуг в области ликвидации последствий загрязнений",
|
||||||
|
"41.1 Разработка строительных проектов",
|
||||||
|
"41.2 Строительство жилых и нежилых зданий",
|
||||||
|
"42.1 Строительство автомобильных и железных дорог",
|
||||||
|
"42.2 Строительство инженерных коммуникаций",
|
||||||
|
"42.9 Строительство прочих инженерных сооружений",
|
||||||
|
"43.1 Разборка и снос зданий, подготовка строительного участка",
|
||||||
|
"43.2 Производство электромонтажных, санитарно-технических и прочих строительно-монтажных работ",
|
||||||
|
"43.3 Работы строительные отделочные",
|
||||||
|
"43.9 Работы строительные специализированные прочие",
|
||||||
|
"45.1 Торговля автотранспортными средствами",
|
||||||
|
"45.2 Техническое обслуживание и ремонт автотранспортных средств",
|
||||||
|
"45.3 Торговля автомобильными деталями и принадлежностями",
|
||||||
|
"45.4 Торговля мотоциклами и их обслуживание",
|
||||||
|
"46.1 Торговля оптовая за вознаграждение или на договорной основе",
|
||||||
|
"46.2 Торговля оптовая сельскохозяйственным сырьем и живыми животными",
|
||||||
|
"46.3 Торговля оптовая пищевыми продуктами, напитками и табачными изделиями",
|
||||||
|
"46.4 Торговля оптовая непродовольственными потребительскими товарами",
|
||||||
|
"46.5 Торговля оптовая информационным и коммуникационным оборудованием",
|
||||||
|
"46.6 Торговля оптовая прочими машинами и оборудованием",
|
||||||
|
"46.7 Торговля оптовая специализированная прочая",
|
||||||
|
"46.9 Торговля оптовая неспециализированная",
|
||||||
|
"47.1 Торговля розничная в неспециализированных магазинах",
|
||||||
|
"47.2 Торговля розничная пищевыми продуктами в специализированных магазинах",
|
||||||
|
"47.3 Торговля розничная моторным топливом",
|
||||||
|
"47.4 Торговля розничная информационным и коммуникационным оборудованием",
|
||||||
|
"47.5 Торговля розничная прочими бытовыми изделиями",
|
||||||
|
"47.6 Торговля розничная товарами культурно-развлекательного назначения",
|
||||||
|
"47.7 Торговля розничная прочими товарами в специализированных магазинах",
|
||||||
|
"47.8 Торговля розничная в палатках и на рынках",
|
||||||
|
"47.9 Торговля розничная вне магазинов, палаток, рынков",
|
||||||
|
"49.1 Деятельность железнодорожного пассажирского транспорта",
|
||||||
|
"49.2 Деятельность железнодорожного грузового транспорта",
|
||||||
|
"49.3 Деятельность прочего сухопутного пассажирского транспорта",
|
||||||
|
"49.4 Деятельность автомобильного грузового транспорта",
|
||||||
|
"49.5 Деятельность трубопроводного транспорта",
|
||||||
|
"50.1 Деятельность морского пассажирского транспорта",
|
||||||
|
"50.2 Деятельность морского грузового транспорта",
|
||||||
|
"50.3 Деятельность внутреннего водного пассажирского транспорта",
|
||||||
|
"50.4 Деятельность внутреннего водного грузового транспорта",
|
||||||
|
"51.1 Деятельность пассажирского воздушного транспорта",
|
||||||
|
"51.2 Деятельность грузового воздушного транспорта",
|
||||||
|
"52.1 Деятельность по складированию и хранению",
|
||||||
|
"52.2 Деятельность транспортная вспомогательная",
|
||||||
|
"53.1 Деятельность почтовой связи общего пользования",
|
||||||
|
"53.2 Деятельность почтовой связи прочая и курьерская",
|
||||||
|
"55.1 Деятельность гостиниц и прочих мест временного проживания",
|
||||||
|
"55.2 Деятельность по предоставлению мест для краткосрочного проживания",
|
||||||
|
"55.3 Деятельность по предоставлению мест для кемпингов и стоянок",
|
||||||
|
"56.1 Деятельность ресторанов и услуги по доставке продуктов питания",
|
||||||
|
"56.2 Деятельность предприятий общественного питания",
|
||||||
|
"56.3 Подача напитков",
|
||||||
|
"58.1 Издание книг, периодических изданий и другие виды издательской деятельности",
|
||||||
|
"58.2 Издание программного обеспечения",
|
||||||
|
"59.1 Производство кинофильмов, видеофильмов и телевизионных программ",
|
||||||
|
"59.2 Деятельность в области звукозаписи и издания музыкальных произведений",
|
||||||
|
"60.1 Деятельность в области радиовещания",
|
||||||
|
"60.2 Деятельность в области телевизионного вещания",
|
||||||
|
"61.1 Деятельность в области проводной связи",
|
||||||
|
"61.2 Деятельность в области беспроводной связи",
|
||||||
|
"61.3 Деятельность в области спутниковой связи",
|
||||||
|
"61.9 Деятельность в области телекоммуникаций прочая",
|
||||||
|
"62.0 Разработка компьютерного программного обеспечения, консультационные услуги в данной области",
|
||||||
|
"63.1 Деятельность по обработке данных, предоставление услуг по размещению информации",
|
||||||
|
"63.9 Деятельность в области информационных услуг прочая",
|
||||||
|
"64.1 Денежное посредничество",
|
||||||
|
"64.2 Деятельность холдинговых компаний",
|
||||||
|
"64.3 Деятельность инвестиционных фондов",
|
||||||
|
"64.9 Деятельность по финансовому посредничеству прочая",
|
||||||
|
"65.1 Страхование жизни",
|
||||||
|
"65.2 Перестрахование",
|
||||||
|
"65.3 Страхование и перестрахование, негосударственное пенсионное обеспечение",
|
||||||
|
"66.1 Деятельность вспомогательная в сфере финансовых услуг",
|
||||||
|
"66.2 Деятельность вспомогательная в сфере страхования",
|
||||||
|
"66.3 Деятельность по управлению фондами",
|
||||||
|
"68.1 Покупка и продажа собственного недвижимого имущества",
|
||||||
|
"68.2 Аренда и управление собственным или арендованным недвижимым имуществом",
|
||||||
|
"68.3 Операции с недвижимым имуществом за вознаграждение или на договорной основе",
|
||||||
|
"69.1 Деятельность в области права",
|
||||||
|
"69.2 Деятельность по оказанию услуг в области бухгалтерского учета и аудита",
|
||||||
|
"70.1 Деятельность головных офисов",
|
||||||
|
"70.2 Консультирование по вопросам управления",
|
||||||
|
"70.3 Деятельность по связям с общественностью",
|
||||||
|
"71.1 Деятельность в области архитектуры, инженерных изысканий",
|
||||||
|
"71.2 Технические испытания, исследования, анализ и сертификация",
|
||||||
|
"72.1 Научные исследования и разработки в области естественных и технических наук",
|
||||||
|
"72.2 Научные исследования и разработки в области общественных и гуманитарных наук",
|
||||||
|
"73.1 Деятельность рекламная",
|
||||||
|
"73.2 Исследование конъюнктуры рынка и опросы общественного мнения",
|
||||||
|
"74.1 Деятельность специализированная в области дизайна",
|
||||||
|
"74.2 Деятельность в области фотографии",
|
||||||
|
"74.3 Деятельность по письменному и устному переводу",
|
||||||
|
"74.9 Деятельность профессиональная, научная и техническая прочая",
|
||||||
|
"75.0 Деятельность ветеринарная",
|
||||||
|
"77.1 Аренда и лизинг автотранспортных средств",
|
||||||
|
"77.2 Прокат и аренда предметов личного пользования и хозяйственно-бытового назначения",
|
||||||
|
"77.3 Аренда и лизинг машин и оборудования",
|
||||||
|
"77.4 Аренда и лизинг интеллектуальной собственности",
|
||||||
|
"78.1 Деятельность агентств по подбору персонала",
|
||||||
|
"78.2 Деятельность агентств по временному трудоустройству",
|
||||||
|
"78.3 Деятельность по обеспечению безопасности в чрезвычайных ситуациях; деятельность по обеспечению безопасности в области информатизации",
|
||||||
|
"79.1 Деятельность туристических агентств и туроператоров",
|
||||||
|
"79.9 Услуги по бронированию прочие",
|
||||||
|
"80.1 Деятельность частных охранных служб",
|
||||||
|
"80.2 Деятельность систем обеспечения безопасности",
|
||||||
|
"80.3 Деятельность по расследованию",
|
||||||
|
"81.1 Деятельность по комплексному обслуживанию помещений",
|
||||||
|
"81.2 Деятельность по чистке и уборке",
|
||||||
|
"81.3 Деятельность по благоустройству ландшафта",
|
||||||
|
"82.1 Деятельность административно-хозяйственная комплексная",
|
||||||
|
"82.2 Деятельность центров обработки телефонных вызовов",
|
||||||
|
"82.3 Деятельность по организации конференций и выставок",
|
||||||
|
"82.9 Деятельность по предоставлению вспомогательных услуг для бизнеса",
|
||||||
|
"84.1 Деятельность органов государственного управления",
|
||||||
|
"84.2 Предоставление государственных услуг обществу",
|
||||||
|
"84.3 Деятельность в области обязательного социального обеспечения",
|
||||||
|
"85.1 Образование общее",
|
||||||
|
"85.2 Образование профессиональное",
|
||||||
|
"85.3 Обучение профессиональное",
|
||||||
|
"85.4 Образование дополнительное",
|
||||||
|
"86.1 Деятельность больничных организаций",
|
||||||
|
"86.2 Медицинская и стоматологическая практика",
|
||||||
|
"86.9 Деятельность в области медицины прочая",
|
||||||
|
"87.1 Деятельность по уходу с обеспечением проживания",
|
||||||
|
"87.2 Деятельность по уходу с обеспечением проживания для умственно отсталых и психически больных",
|
||||||
|
"87.9 Деятельность по уходу с обеспечением проживания прочая",
|
||||||
|
"88.1 Предоставление социальных услуг без обеспечения проживания престарелым и инвалидам",
|
||||||
|
"88.9 Предоставление прочих социальных услуг без обеспечения проживания",
|
||||||
|
"90.0 Деятельность творческая, деятельность в области искусства и организации развлечений",
|
||||||
|
"91.0 Деятельность библиотек, архивов, музеев и прочих объектов культуры",
|
||||||
|
"92.0 Деятельность по организации и проведению азартных игр и пари",
|
||||||
|
"93.1 Деятельность в области спорта",
|
||||||
|
"93.2 Деятельность в области отдыха и развлечений",
|
||||||
|
"94.1 Деятельность профессиональных союзов",
|
||||||
|
"94.2 Деятельность религиозных организаций",
|
||||||
|
"94.9 Деятельность общественных организаций прочая",
|
||||||
|
"95.1 Ремонт компьютеров и коммуникационного оборудования",
|
||||||
|
"95.2 Ремонт предметов личного потребления и хозяйственно-бытового назначения",
|
||||||
|
"96.0 Деятельность по предоставлению прочих персональных услуг",
|
||||||
|
"97.0 Деятельность домашних хозяйств с наемными работниками",
|
||||||
|
"98.0 Деятельность домашних хозяйств по производству товаров для собственного использования",
|
||||||
|
"99.0 Деятельность экстерриториальных организаций и органов"
|
||||||
|
]
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
[
|
|
||||||
"сбор",
|
|
||||||
"систематизация",
|
|
||||||
"хранение",
|
|
||||||
"извлечение",
|
|
||||||
"передача (предоставление доступа)",
|
|
||||||
"блокирование",
|
|
||||||
"уничтожение",
|
|
||||||
"запись",
|
|
||||||
"накопление",
|
|
||||||
"уточнение (обновление, изменение)",
|
|
||||||
"использование",
|
|
||||||
"обезличивание",
|
|
||||||
"удаление",
|
|
||||||
"распространение",
|
|
||||||
"иные действия"
|
|
||||||
]
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
[
|
|
||||||
"Сотрудникам организации",
|
|
||||||
"Соискателям",
|
|
||||||
"Родственникам сотрудников",
|
|
||||||
"Уволенным сотрудникам",
|
|
||||||
"Контрагентам",
|
|
||||||
"Представителям контрагентов",
|
|
||||||
"Клиентам",
|
|
||||||
"Посетителям сайта",
|
|
||||||
"Выгодоприобретателям по договорам",
|
|
||||||
"Субъект персональных данных, предоставивший согласие на трансграничную передачу персональных данных",
|
|
||||||
"Учащимя",
|
|
||||||
"Студентам",
|
|
||||||
"Законным представителям",
|
|
||||||
"Иным категориям субъектов персональных данных, персональные данные которых обрабатываются"
|
|
||||||
]
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
[
|
|
||||||
"Фамилия, Имя, Отчество",
|
|
||||||
"Год рождения",
|
|
||||||
"Месяц рождения",
|
|
||||||
"Дата рождения",
|
|
||||||
"Место рождения",
|
|
||||||
"Семейное положение",
|
|
||||||
"Социальное положение",
|
|
||||||
"Имущественное положение",
|
|
||||||
"Сведения о доходах",
|
|
||||||
"Пол",
|
|
||||||
"Адрес электронной почты",
|
|
||||||
"Адрес места жительства",
|
|
||||||
"Адрес регистрации",
|
|
||||||
"Номер телефона",
|
|
||||||
"Страховой номер индивидуального лицевого счета (СНИЛС)",
|
|
||||||
"Идентификационный номер налогоплательщика (ИНН);",
|
|
||||||
"Сведения о гражданстве",
|
|
||||||
"Данные документа, удостоверяющего личность",
|
|
||||||
"Данные водительского удостоверения",
|
|
||||||
"Данные документа, удостоверяющего личность за пределами Российской Федерации",
|
|
||||||
"Данные документа, содержащиеся в свидетельстве о рождении",
|
|
||||||
"Реквизиты банковских карт",
|
|
||||||
"Номер расчетного счета",
|
|
||||||
"Номер лицевого счета",
|
|
||||||
"Профессия",
|
|
||||||
"Должность",
|
|
||||||
"Сведения о трудовой деятельности (в том числе стаж работы, данные о трудовой занятости на текущее время с указанием наименования и расчетного счета организации)",
|
|
||||||
"Сведения об отношении к воинской обязанности, о воинском учете",
|
|
||||||
"Иные персональные данные",
|
|
||||||
"Сведения, собираемые посредством метрических программ (например, Яндекс.Метрика)",
|
|
||||||
"Сведения об образовании",
|
|
||||||
"Фото-видео изображение лица",
|
|
||||||
"Данные голоса человека",
|
|
||||||
"Сведения о состоянии здоровья",
|
|
||||||
"Национальная принадлежность",
|
|
||||||
"Политические взгляды",
|
|
||||||
"Религиозные или философские убеждения",
|
|
||||||
"Сведения о судимости",
|
|
||||||
"Данные изображения лица, полученные с помощью фото- видео устройств, на основании которых можно установить его личность и которые используются оператором для установления личности субъекта персональных данных (биометрия)",
|
|
||||||
"Данные голоса человека, на основании которых можно установить его личность и которые используются оператором для установления личности субъекта персональных данных (биометрия)",
|
|
||||||
"Изображение папиллярных узоров пальцев рук",
|
|
||||||
"Иные биометрические персональные данные"
|
|
||||||
]
|
|
||||||
+10
-10
@@ -97,10 +97,10 @@ def _generate_variables_reference(path):
|
|||||||
_bullet(doc, f'{var} — {desc}')
|
_bullet(doc, f'{var} — {desc}')
|
||||||
|
|
||||||
_h(doc, 'Циклы', 12)
|
_h(doc, 'Циклы', 12)
|
||||||
_bullet(doc, 'information_systems — ИС ({{item.name}}, {{item.description}}, {{item.structure}}, {{item.pd_actions}}, {{item.defense_tools_list}}, {{item.users}}, {{item.category}}, {{item.pd_count}} и др.).')
|
_bullet(doc, 'information_systems — объекты КИИ ({{item.name}}, {{item.description}}, {{item.category}}, {{item.defense_tools_list}}, {{item.users}}, {{item.software}} и др.).')
|
||||||
_bullet(doc, 'commission — комиссия ({{item.role}}, {{item.position}}, {{item.fio}}, {{item.fio_initials}}, {{item.fio_short}}, {{item.signature}}).')
|
_bullet(doc, 'commission — комиссия ({{item.role}}, {{item.position}}, {{item.fio}}, {{item.fio_initials}}, {{item.fio_short}}, {{item.signature}}).')
|
||||||
_bullet(doc, 'employees_access — сотрудники с доступом ({{item.position}}, {{item.fio}}).')
|
_bullet(doc, 'employees_access — сотрудники с доступом ({{item.position}}, {{item.fio}}).')
|
||||||
_bullet(doc, 'paper_documents — бумажные документы ({{item.document}}, {{item.storage}}).')
|
|
||||||
|
|
||||||
doc.save(path)
|
doc.save(path)
|
||||||
|
|
||||||
@@ -120,10 +120,10 @@ def _generate_user_manual(path):
|
|||||||
|
|
||||||
_h(doc, '2. Основные вкладки', 12)
|
_h(doc, '2. Основные вкладки', 12)
|
||||||
_bullet(doc, 'Организация — реквизиты, адреса, руководитель, склонения названий, проверка данных (Морфер / DaData / ИИ).')
|
_bullet(doc, 'Организация — реквизиты, адреса, руководитель, склонения названий, проверка данных (Морфер / DaData / ИИ).')
|
||||||
_bullet(doc, 'Информационные системы — ИС, категории ПДн, действия с ПДн, средства защиты, пользователи.')
|
_bullet(doc, 'Объекты КИИ — наименование, описание, категория значимости, средства защиты, пользователи ОКИИ.')
|
||||||
_bullet(doc, 'Комиссия — состав комиссии по классификации ИСПДн.')
|
_bullet(doc, 'Комиссия — состав комиссии по категорированию объектов КИИ.')
|
||||||
_bullet(doc, 'Пользователи — сотрудники с доступом к ПДн.')
|
_bullet(doc, 'Пользователи — сотрудники с доступом к объектам КИИ.')
|
||||||
_bullet(doc, 'Бумажные документы — бумажные носители ПДн и места хранения.')
|
|
||||||
_bullet(doc, 'Генерация — выбор шаблонов и запуск генерации.')
|
_bullet(doc, 'Генерация — выбор шаблонов и запуск генерации.')
|
||||||
_bullet(doc, 'Лог — журнал действий, экспорт в файл.')
|
_bullet(doc, 'Лог — журнал действий, экспорт в файл.')
|
||||||
|
|
||||||
@@ -137,13 +137,13 @@ def _generate_user_manual(path):
|
|||||||
|
|
||||||
_h(doc, '4. Циклы (повторяющиеся блоки)', 12)
|
_h(doc, '4. Циклы (повторяющиеся блоки)', 12)
|
||||||
_p(doc, 'Если в документе нужно повторить блок для каждой ИС, комиссии, '
|
_p(doc, 'Если в документе нужно повторить блок для каждой ИС, комиссии, '
|
||||||
'сотрудника или бумажного документа — используются маркеры цикла:')
|
'сотрудника или объекта КИИ — используются маркеры цикла:')
|
||||||
_p(doc, '<!-- loop:information_systems --> ... {{item.name}} ... <!-- loop_end -->', bold=True)
|
_p(doc, '<!-- loop:information_systems --> ... {{item.name}} ... <!-- loop_end -->', bold=True)
|
||||||
_p(doc, 'Доступные циклы:')
|
_p(doc, 'Доступные циклы:')
|
||||||
_bullet(doc, 'information_systems — ИС ({{item.name}}, {{item.description}}, {{item.structure}}, {{item.pd_actions}}, {{item.defense_tools_list}}, {{item.users}}, {{item.category}}, {{item.pd_count}} и др.).')
|
_bullet(doc, 'information_systems — объекты КИИ ({{item.name}}, {{item.description}}, {{item.category}}, {{item.defense_tools_list}}, {{item.users}}, {{item.software}} и др.).')
|
||||||
_bullet(doc, 'commission — комиссия ({{item.role}}, {{item.position}}, {{item.fio}}, {{item.fio_initials}}, {{item.fio_short}}, {{item.signature}}).')
|
_bullet(doc, 'commission — комиссия ({{item.role}}, {{item.position}}, {{item.fio}}, {{item.fio_initials}}, {{item.fio_short}}, {{item.signature}}).')
|
||||||
_bullet(doc, 'employees_access — сотрудники с доступом ({{item.position}}, {{item.fio}}).')
|
_bullet(doc, 'employees_access — сотрудники с доступом ({{item.position}}, {{item.fio}}).')
|
||||||
_bullet(doc, 'paper_documents — бумажные документы ({{item.document}}, {{item.storage}}).')
|
|
||||||
_p(doc, 'Внутри цикла переменные пишутся через точку: {{item.поле}}. '
|
_p(doc, 'Внутри цикла переменные пишутся через точку: {{item.поле}}. '
|
||||||
'Допускается и синтаксис без точки: {{item_поле}}.')
|
'Допускается и синтаксис без точки: {{item_поле}}.')
|
||||||
|
|
||||||
@@ -179,7 +179,7 @@ def _generate_admin_manual(path):
|
|||||||
_h(doc, '1. Файлы настроек рядом с программой', 12)
|
_h(doc, '1. Файлы настроек рядом с программой', 12)
|
||||||
_bullet(doc, 'api_settings.json — API-ключи (DaData, ИИ).')
|
_bullet(doc, 'api_settings.json — API-ключи (DaData, ИИ).')
|
||||||
_bullet(doc, 'import_settings.json — словарь импорта: что и откуда брать из опросника.')
|
_bullet(doc, 'import_settings.json — словарь импорта: что и откуда брать из опросника.')
|
||||||
_bullet(doc, 'dictionaries/ — справочники: defense_tools.json, pd_actions.json, pd_items.json, pd_categories.json.')
|
_bullet(doc, 'dictionaries/ — справочники: defense_tools.json, okved.json.')
|
||||||
_bullet(doc, 'dokogen.log — журнал работы (пишется автоматически).')
|
_bullet(doc, 'dokogen.log — журнал работы (пишется автоматически).')
|
||||||
_bullet(doc, 'docs/ — инструкции и пустой опросник (создаются автоматически).')
|
_bullet(doc, 'docs/ — инструкции и пустой опросник (создаются автоматически).')
|
||||||
|
|
||||||
|
|||||||
+53
-150
@@ -141,8 +141,8 @@ def import_187fz(filepath: str, log_fn: Optional[Callable] = None, settings_path
|
|||||||
data = {
|
data = {
|
||||||
'fullName': '', 'shortName': '', 'addressLegal': '', 'addressActual': '',
|
'fullName': '', 'shortName': '', 'addressLegal': '', 'addressActual': '',
|
||||||
'email': '', 'phone': '', 'inn': '', 'ogrn': '', 'kpp': '',
|
'email': '', 'phone': '', 'inn': '', 'ogrn': '', 'kpp': '',
|
||||||
'okved': '', 'chiefPosition': '', 'chiefFio': '',
|
'okved': '', 'okvedExtra': '', 'chiefPosition': '', 'chiefFio': '',
|
||||||
'ispdnFio': '', 'ispdnPosition': '', 'paperDocuments': '', 'paperStorage': '',
|
'ispdnFio': '', 'ispdnPosition': '',
|
||||||
'ispdnDepartment': '', 'ispdnEmail': '', 'ispdnPhone': '',
|
'ispdnDepartment': '', 'ispdnEmail': '', 'ispdnPhone': '',
|
||||||
'informationSystems': [], 'commission': [], 'employeesAccess': [],
|
'informationSystems': [], 'commission': [], 'employeesAccess': [],
|
||||||
}
|
}
|
||||||
@@ -248,7 +248,7 @@ def _import_187_common(wb, data, settings, log_fn):
|
|||||||
|
|
||||||
|
|
||||||
def _import_187_is_list(wb, data, settings, log_fn):
|
def _import_187_is_list(wb, data, settings, log_fn):
|
||||||
"""Импорт информационных систем из остальных листов."""
|
"""Импорт объектов КИИ из остальных листов (ОБЪЕКТ №1, №2, ...)."""
|
||||||
exclude_kws = settings.get('is_sheet_exclude_keywords', ['общие сведения', 'сотрудники'])
|
exclude_kws = settings.get('is_sheet_exclude_keywords', ['общие сведения', 'сотрудники'])
|
||||||
is_sheets = []
|
is_sheets = []
|
||||||
for sn in wb.sheetnames:
|
for sn in wb.sheetnames:
|
||||||
@@ -257,7 +257,7 @@ def _import_187_is_list(wb, data, settings, log_fn):
|
|||||||
continue
|
continue
|
||||||
is_sheets.append(sn)
|
is_sheets.append(sn)
|
||||||
if not is_sheets:
|
if not is_sheets:
|
||||||
log_fn("⚠ Листы ИС не найдены")
|
log_fn("⚠ Листы объектов КИИ не найдены")
|
||||||
return
|
return
|
||||||
|
|
||||||
is_list = []
|
is_list = []
|
||||||
@@ -275,16 +275,20 @@ def _import_187_is_list(wb, data, settings, log_fn):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
is_obj = {
|
is_obj = {
|
||||||
'name': '', 'description': '', 'room': '', 'employee': '', 'position': '',
|
'name': '', 'description': '', 'usersList': [],
|
||||||
'personalDataList': [], 'pd_subjects_list': [], 'pd_actions': [],
|
'isInternet': False, 'defense_tools': [], 'software': '',
|
||||||
'usersList': [], 'isInternet': False, 'personalDataCount': 'менее 100 000',
|
|
||||||
'defense_tools': [], 'processing_mode': '',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
current_section = 'header'
|
defense_keywords = {
|
||||||
pd_items_raw = []
|
'антивирус': 'Антивирус',
|
||||||
subjects_raw = []
|
'крипто': 'СКЗИ',
|
||||||
actions_raw = []
|
'межсетев': 'Межсетевой экран',
|
||||||
|
'сзи.*от несанкционирован': 'СЗИ от НСД',
|
||||||
|
'обнаружен.*вторжен': 'СОВ',
|
||||||
|
'программные.*средств.*модул': 'Доверенная загрузка',
|
||||||
|
'доверен.*загрузк': 'Доверенная загрузка',
|
||||||
|
'гарантирован': 'Доверенная загрузка',
|
||||||
|
}
|
||||||
|
|
||||||
for vals in rows:
|
for vals in rows:
|
||||||
field = (vals[0] or '').strip()
|
field = (vals[0] or '').strip()
|
||||||
@@ -292,159 +296,58 @@ def _import_187_is_list(wb, data, settings, log_fn):
|
|||||||
val_b = clean_value(vals[1]) if len(vals) > 1 else ''
|
val_b = clean_value(vals[1]) if len(vals) > 1 else ''
|
||||||
is_checked = val_b.upper() in ('ДА', 'YES', '✓', '✔', '1', 'TRUE', 'ЕСТЬ')
|
is_checked = val_b.upper() in ('ДА', 'YES', '✓', '✔', '1', 'TRUE', 'ЕСТЬ')
|
||||||
|
|
||||||
if 'цель обработки пд' in field_lower or ('цель обработки' in field_lower and ':' in field):
|
if 'наименование объекта' in field_lower or 'название объекта' in field_lower:
|
||||||
current_section = 'purpose'
|
is_obj['name'] = val_b
|
||||||
|
log_fn(f" Объект КИИ: {val_b}")
|
||||||
continue
|
continue
|
||||||
if 'перечисленные пдн принадлежат' in field_lower or 'принадлеж' in field_lower:
|
if 'описание объекта' in field_lower or 'описание окки' in field_lower:
|
||||||
current_section = 'subjects'
|
|
||||||
continue
|
|
||||||
if 'структура ис' in field_lower or 'структура информационной' in field_lower:
|
|
||||||
if val_b:
|
if val_b:
|
||||||
is_obj['structure'] = val_b
|
is_obj['description'] = val_b
|
||||||
elif ':' in field:
|
|
||||||
# Значение может быть записано в той же ячейке после двоеточия
|
|
||||||
tail = field.split(':', 1)[1].strip()
|
|
||||||
if tail and 'выбрать' not in tail.lower():
|
|
||||||
is_obj['structure'] = tail
|
|
||||||
continue
|
continue
|
||||||
if 'названия программ' in field_lower or ('программ' in field_lower and 'ос взаимодейств' in field_lower):
|
if 'операционная' in field_lower or 'прикладные программы' in field_lower or 'наименования программно' in field_lower:
|
||||||
if val_b:
|
if val_b:
|
||||||
is_obj['software'] = val_b
|
is_obj['software'] = (is_obj['software'] + '; ' + val_b) if is_obj['software'] else val_b
|
||||||
continue
|
|
||||||
if 'локальную сеть' in field_lower:
|
|
||||||
is_obj['is_has_lan'] = is_checked
|
|
||||||
continue
|
|
||||||
if 'количество записей' in field_lower or 'количество субъект' in field_lower:
|
|
||||||
if val_b:
|
|
||||||
is_obj['personalDataCount'] = val_b
|
|
||||||
continue
|
|
||||||
if 'перечень действий с пд' in field_lower or ('действи' in field_lower and 'пд' in field_lower):
|
|
||||||
current_section = 'actions'
|
|
||||||
continue
|
|
||||||
if 'способ обработки пд' in field_lower:
|
|
||||||
if val_b:
|
|
||||||
is_obj['processing_mode'] = val_b
|
|
||||||
continue
|
continue
|
||||||
if 'интернет' in field_lower:
|
if 'интернет' in field_lower:
|
||||||
is_obj['isInternet'] = is_checked
|
is_obj['isInternet'] = is_checked
|
||||||
continue
|
continue
|
||||||
|
if 'сотрудник' in field_lower and 'должност' not in field_lower:
|
||||||
if current_section == 'header':
|
if val_b:
|
||||||
if 'перечень пд' in field_lower:
|
parts = [p.strip() for p in val_b.replace('\n', ';').split(';') if p.strip()]
|
||||||
current_section = 'pd_items'
|
for part in parts:
|
||||||
elif 'название ис' in field_lower or 'наименование ис' in field_lower or 'наименование информационной' in field_lower or 'наименование объекта' in field_lower or 'название объекта' in field_lower:
|
cleaned = part.strip()
|
||||||
is_obj['name'] = val_b
|
if ':' in cleaned and len(cleaned.split(':')[0]) < 20:
|
||||||
log_fn(f" ИС: {val_b}")
|
cleaned = cleaned.split(':', 1)[1].strip()
|
||||||
elif 'описание ис' in field_lower or 'описание информационной' in field_lower:
|
if cleaned and cleaned not in is_obj['usersList']:
|
||||||
if val_b:
|
is_obj['usersList'].append(cleaned)
|
||||||
is_obj['description'] = val_b
|
continue
|
||||||
log_fn(f" Описание: {val_b[:60]}")
|
# Средства защиты
|
||||||
continue
|
matched = False
|
||||||
elif 'сотрудник' in field_lower and 'должност' not in field_lower:
|
for kw, label in defense_keywords.items():
|
||||||
is_obj['employee'] = val_b
|
if re.search(kw, field_lower):
|
||||||
if val_b:
|
if val_b and 'выбрать' not in field_lower:
|
||||||
parts = [p.strip() for p in val_b.replace('\n', ';').split(';') if p.strip()]
|
is_obj['defense_tools'].append(val_b)
|
||||||
for part in parts:
|
matched = True
|
||||||
cleaned = part.strip()
|
break
|
||||||
if ':' in cleaned and len(cleaned.split(':')[0]) < 20:
|
if matched:
|
||||||
cleaned = cleaned.split(':', 1)[1].strip()
|
continue
|
||||||
if cleaned and cleaned not in is_obj['usersList']:
|
if val_b and 'выбрать' not in field_lower and field_lower not in ('значение', 'примечание'):
|
||||||
is_obj['usersList'].append(cleaned)
|
# Остальные заполненные поля — в описание (адрес, сфера, тип и т.д.)
|
||||||
elif 'должность сотрудника' in field_lower:
|
if not is_obj['description']:
|
||||||
is_obj['position'] = val_b
|
is_obj['description'] = f"{field}: {val_b}"
|
||||||
elif 'наименование отдела' in field_lower:
|
else:
|
||||||
pass
|
is_obj['description'] += f"\n{field}: {val_b}"
|
||||||
elif 'номер кабинета' in field_lower:
|
|
||||||
is_obj['room'] = val_b
|
|
||||||
elif 'значение' in field_lower or 'примечание' in field_lower:
|
|
||||||
continue
|
|
||||||
elif val_b and 'выбрать' not in field_lower:
|
|
||||||
if not is_obj['description']:
|
|
||||||
is_obj['description'] = f"{field}: {val_b}"
|
|
||||||
else:
|
|
||||||
is_obj['description'] += f"\n{field}: {val_b}"
|
|
||||||
elif current_section == 'pd_items':
|
|
||||||
if is_checked and 'выбрать' not in field_lower and field_lower != 'значение':
|
|
||||||
pd_items_raw.append(field)
|
|
||||||
if 'цель обработки' in field_lower:
|
|
||||||
current_section = 'purpose'
|
|
||||||
elif current_section == 'purpose':
|
|
||||||
purpose_val = val_b or field or ''
|
|
||||||
if purpose_val and 'выбрать' not in purpose_val.lower() and ':' not in purpose_val[:40]:
|
|
||||||
is_obj['purpose'] = purpose_val
|
|
||||||
log_fn(f" Цель: {purpose_val[:80]}")
|
|
||||||
current_section = 'header'
|
|
||||||
elif current_section == 'subjects':
|
|
||||||
if is_checked and 'выбрать' not in field_lower:
|
|
||||||
subjects_raw.append(field)
|
|
||||||
if 'количество записей' in field_lower or 'действи' in field_lower:
|
|
||||||
continue
|
|
||||||
elif current_section == 'actions':
|
|
||||||
if is_checked and 'выбрать' not in field_lower and 'защита' not in field_lower[:20]:
|
|
||||||
actions_raw.append(field)
|
|
||||||
if 'защита' in field_lower and 'действи' not in field_lower:
|
|
||||||
current_section = 'defense'
|
|
||||||
elif current_section == 'defense':
|
|
||||||
defense_keywords = {
|
|
||||||
'антивирус': 'Антивирус',
|
|
||||||
'крипто': 'СКЗИ',
|
|
||||||
'межсетев': 'Межсетевой экран',
|
|
||||||
'сзи.*от несанкционирован': 'СЗИ от НСД',
|
|
||||||
'обнаружен.*вторжен': 'СОВ',
|
|
||||||
'программные.*средств.*модул': 'Доверенная загрузка',
|
|
||||||
'доверен.*загрузк': 'Доверенная загрузка',
|
|
||||||
'гарантирован': 'Доверенная загрузка',
|
|
||||||
}
|
|
||||||
matched_label = None
|
|
||||||
for kw, label in defense_keywords.items():
|
|
||||||
if re.search(kw, field_lower):
|
|
||||||
matched_label = label
|
|
||||||
break
|
|
||||||
if matched_label and val_b and 'выбрать' not in field_lower:
|
|
||||||
# Без префикса категории: просто название средства (Kaspersky, КриптоПро)
|
|
||||||
is_obj['defense_tools'].append(val_b)
|
|
||||||
elif matched_label:
|
|
||||||
pass
|
|
||||||
elif 'куда' in field_lower or 'передаются' in field_lower:
|
|
||||||
current_section = 'header'
|
|
||||||
elif 'способ обработки' in field_lower or 'локальную сеть' in field_lower or 'интернет' in field_lower:
|
|
||||||
current_section = 'header'
|
|
||||||
|
|
||||||
if pd_items_raw:
|
|
||||||
is_obj['personalDataList'] = pd_items_raw
|
|
||||||
log_fn(f" ПДн: {len(pd_items_raw)}")
|
|
||||||
if subjects_raw:
|
|
||||||
is_obj['pd_subjects_list'] = subjects_raw
|
|
||||||
log_fn(f" Субъектов: {len(subjects_raw)}")
|
|
||||||
if actions_raw:
|
|
||||||
is_obj['pd_actions'] = actions_raw
|
|
||||||
log_fn(f" Действий: {len(actions_raw)}")
|
|
||||||
|
|
||||||
if not is_obj['name']:
|
if not is_obj['name']:
|
||||||
is_obj['name'] = sheet_name.strip()
|
is_obj['name'] = sheet_name.strip()
|
||||||
log_fn(f" ⚠ ИС без названия — использовано имя листа: {is_obj['name']}")
|
log_fn(f" ⚠ Объект без названия — использовано имя листа: {is_obj['name']}")
|
||||||
|
|
||||||
# Автоопределение категорий ПДн
|
|
||||||
pd_cats = []
|
|
||||||
all_pd_text = ' '.join(p.lower() for p in pd_items_raw)
|
|
||||||
special_kw = ['состоян.*здоров', 'национальн', 'политическ', 'религиозн', 'философ', 'судимост', 'интимн']
|
|
||||||
if any(re.search(kw, all_pd_text) for kw in special_kw):
|
|
||||||
pd_cats.append('специальные')
|
|
||||||
bio_kw = ['биометрическ', 'изображен.*лиц', 'голос.*человек', 'папилляр', 'дактилоскоп', 'фото.*изображен']
|
|
||||||
if any(re.search(kw, all_pd_text) for kw in bio_kw):
|
|
||||||
pd_cats.append('биометрические')
|
|
||||||
if pd_items_raw:
|
|
||||||
pd_cats.append('иные')
|
|
||||||
if pd_cats:
|
|
||||||
is_obj['personalDataCategory'] = pd_cats
|
|
||||||
log_fn(f" Категории ПДн: {', '.join(pd_cats)}")
|
|
||||||
|
|
||||||
is_list.append(is_obj)
|
is_list.append(is_obj)
|
||||||
log_fn(f" ✅ {is_obj['name']} — импортирована")
|
log_fn(f" ✅ {is_obj['name']} — импортирован")
|
||||||
|
|
||||||
if is_list:
|
if is_list:
|
||||||
data['informationSystems'] = is_list
|
data['informationSystems'] = is_list
|
||||||
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_187_employees(wb, data, settings, log_fn):
|
||||||
@@ -497,7 +400,7 @@ def _import_187_employees(wb, data, settings, log_fn):
|
|||||||
if 'перечень сотрудников' in a_lower:
|
if 'перечень сотрудников' in a_lower:
|
||||||
in_commission = False
|
in_commission = False
|
||||||
in_employees = True
|
in_employees = True
|
||||||
log_fn(" Секция: сотрудники с доступом к ПДн")
|
log_fn(" Секция: сотрудники с доступом к объектам КИИ")
|
||||||
continue
|
continue
|
||||||
if a_lower in ('№ п/п', '№', 'номер', ''):
|
if a_lower in ('№ п/п', '№', 'номер', ''):
|
||||||
continue
|
continue
|
||||||
|
|||||||
+10
-31
@@ -7,7 +7,7 @@ from typing import List, Dict, Any
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class CommissionMember:
|
class CommissionMember:
|
||||||
"""Член комиссии по ПДн"""
|
"""Член комиссии по категорированию объектов КИИ"""
|
||||||
role: str = "член комиссии"
|
role: str = "член комиссии"
|
||||||
position: str = ""
|
position: str = ""
|
||||||
fio: str = ""
|
fio: str = ""
|
||||||
@@ -15,38 +15,19 @@ class CommissionMember:
|
|||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class InformationSystem:
|
class InformationSystem:
|
||||||
"""Информационная система (обработка ПДн)"""
|
"""Объект КИИ (вместо информационной системы 152-ФЗ)"""
|
||||||
name: str = ""
|
name: str = ""
|
||||||
description: str = ""
|
description: str = ""
|
||||||
is_local_network: bool = False
|
is_local_network: bool = False
|
||||||
is_internet: bool = False
|
is_internet: bool = False
|
||||||
defence_level: str = ""
|
category: str = "" # Категория значимости объекта КИИ: 1 / 2 / 3 / без категории
|
||||||
threat_type: str = "3"
|
users_list: List[str] = field(default_factory=list) # Пользователи ОКИИ
|
||||||
category: str = ""
|
defense_tools_list: List[str] = field(default_factory=list) # Средства защиты
|
||||||
pd_list: str = ""
|
|
||||||
pd_count: str = "менее 100 000"
|
|
||||||
users: str = ""
|
|
||||||
defense_tools_list: List[str] = field(default_factory=list)
|
|
||||||
processing_modes: str = ""
|
|
||||||
subject_type: str = "работники организации"
|
|
||||||
pd_actions_list: List[str] = field(default_factory=list)
|
|
||||||
pd_subjects_list: List[str] = field(default_factory=list)
|
|
||||||
personal_data_list: List[str] = field(default_factory=list)
|
|
||||||
users_list: List[str] = field(default_factory=list)
|
|
||||||
personal_data_category: List[str] = field(default_factory=list)
|
|
||||||
purpose: str = ""
|
purpose: str = ""
|
||||||
room: str = ""
|
structure: str = "" # Структура объекта КИИ
|
||||||
structure: str = "" # Структура информационной системы
|
|
||||||
software: str = "" # Программное обеспечение
|
software: str = "" # Программное обеспечение
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
|
||||||
class PaperDocument:
|
|
||||||
"""Бумажный документ с ПДн"""
|
|
||||||
name: str = ""
|
|
||||||
storage: str = ""
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Company:
|
class Company:
|
||||||
"""Организация — основной объект (187-ФЗ)"""
|
"""Организация — основной объект (187-ФЗ)"""
|
||||||
@@ -75,6 +56,8 @@ class Company:
|
|||||||
ogrn: str = ""
|
ogrn: str = ""
|
||||||
ogrn_date: str = ""
|
ogrn_date: str = ""
|
||||||
kpp: str = ""
|
kpp: str = ""
|
||||||
|
okved: str = "" # Основной ОКВЭД
|
||||||
|
okved_extra: List[str] = field(default_factory=list) # Дополнительные ОКВЭД
|
||||||
|
|
||||||
# Должностные лица
|
# Должностные лица
|
||||||
chief_position: str = ""
|
chief_position: str = ""
|
||||||
@@ -91,7 +74,6 @@ class Company:
|
|||||||
# Списки
|
# Списки
|
||||||
commission: List[CommissionMember] = field(default_factory=list)
|
commission: List[CommissionMember] = field(default_factory=list)
|
||||||
information_systems: List[InformationSystem] = field(default_factory=list)
|
information_systems: List[InformationSystem] = field(default_factory=list)
|
||||||
paper_documents_list: List[PaperDocument] = field(default_factory=list)
|
|
||||||
employees_access: List[Dict[str, str]] = field(default_factory=list)
|
employees_access: List[Dict[str, str]] = field(default_factory=list)
|
||||||
|
|
||||||
# Наименование структурного подразделения по безопасности
|
# Наименование структурного подразделения по безопасности
|
||||||
@@ -135,13 +117,10 @@ class Company:
|
|||||||
InformationSystem(**isys) if isinstance(isys, dict) else isys
|
InformationSystem(**isys) if isinstance(isys, dict) else isys
|
||||||
for isys in d["information_systems"]
|
for isys in d["information_systems"]
|
||||||
]
|
]
|
||||||
if "paper_documents_list" in d and isinstance(d["paper_documents_list"], list):
|
|
||||||
d["paper_documents_list"] = [
|
|
||||||
PaperDocument(**p) if isinstance(p, dict) else p
|
|
||||||
for p in d["paper_documents_list"]
|
|
||||||
]
|
|
||||||
if "employees_access" in d and not isinstance(d["employees_access"], list):
|
if "employees_access" in d and not isinstance(d["employees_access"], list):
|
||||||
d["employees_access"] = []
|
d["employees_access"] = []
|
||||||
|
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()}
|
valid = {f.name for f in cls.__dataclass_fields__.values()}
|
||||||
return cls(**{k: v for k, v in d.items() if k in valid})
|
return cls(**{k: v for k, v in d.items() if k in valid})
|
||||||
|
|
||||||
|
|||||||
+158
-362
@@ -11,7 +11,7 @@ from datetime import datetime
|
|||||||
from typing import List, Optional
|
from typing import List, Optional
|
||||||
|
|
||||||
from . import (
|
from . import (
|
||||||
Company, CommissionMember, InformationSystem, PaperDocument,
|
Company, CommissionMember, InformationSystem,
|
||||||
inflect_name,
|
inflect_name,
|
||||||
build_replacements, VARIABLE_DEFS,
|
build_replacements, VARIABLE_DEFS,
|
||||||
process_template,
|
process_template,
|
||||||
@@ -157,7 +157,6 @@ class DokoGenApp:
|
|||||||
self._build_is_tab()
|
self._build_is_tab()
|
||||||
self._build_commission_tab()
|
self._build_commission_tab()
|
||||||
self._build_users_tab()
|
self._build_users_tab()
|
||||||
self._build_paper_tab()
|
|
||||||
self._build_generation_tab()
|
self._build_generation_tab()
|
||||||
self._build_log_tab()
|
self._build_log_tab()
|
||||||
|
|
||||||
@@ -270,6 +269,33 @@ class DokoGenApp:
|
|||||||
|
|
||||||
f2.columnconfigure(1, weight=1)
|
f2.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
|
# --- ОКВЭД ---
|
||||||
|
f_okved = ttk.LabelFrame(left, text="ОКВЭД", padding=10)
|
||||||
|
f_okved.pack(fill=tk.X, pady=5)
|
||||||
|
|
||||||
|
# Основной ОКВЭД — один, из словаря или вручную
|
||||||
|
ttk.Label(f_okved, text="Основной ОКВЭД:").grid(row=0, column=0, sticky=tk.W, pady=2)
|
||||||
|
okved_entry = ttk.Entry(f_okved, width=50)
|
||||||
|
okved_entry.grid(row=0, column=1, padx=10, sticky=tk.EW, pady=1)
|
||||||
|
self._company_entries['okved'] = okved_entry
|
||||||
|
ttk.Button(f_okved, text="Выбрать из словаря",
|
||||||
|
command=self._select_main_okved).grid(row=0, column=2, padx=5)
|
||||||
|
|
||||||
|
# Дополнительные ОКВЭД — сколько угодно
|
||||||
|
ttk.Label(f_okved, text="Дополнительные ОКВЭД:").grid(row=1, column=0, sticky=tk.NW, pady=2)
|
||||||
|
self.okved_extra_listbox = tk.Listbox(f_okved, height=4, width=50)
|
||||||
|
self.okved_extra_listbox.grid(row=1, column=1, padx=10, sticky=tk.EW, pady=1)
|
||||||
|
okved_btn = ttk.Frame(f_okved)
|
||||||
|
okved_btn.grid(row=1, column=2, padx=5, sticky=tk.N)
|
||||||
|
ttk.Button(okved_btn, text="Выбрать из словаря",
|
||||||
|
command=self._select_extra_okved).pack(fill=tk.X, pady=1)
|
||||||
|
ttk.Button(okved_btn, text="+ Добавить",
|
||||||
|
command=self._add_okved_manual).pack(fill=tk.X, pady=1)
|
||||||
|
ttk.Button(okved_btn, text="- Удалить",
|
||||||
|
command=self._delete_okved_extra).pack(fill=tk.X, pady=1)
|
||||||
|
|
||||||
|
f_okved.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
# --- Должностные лица ---
|
# --- Должностные лица ---
|
||||||
f3 = ttk.LabelFrame(left, text="Должностные лица", padding=10)
|
f3 = ttk.LabelFrame(left, text="Должностные лица", padding=10)
|
||||||
f3.pack(fill=tk.X, pady=5)
|
f3.pack(fill=tk.X, pady=5)
|
||||||
@@ -686,12 +712,12 @@ class DokoGenApp:
|
|||||||
# ---------- ВКЛАДКА "ИНФОРМАЦИОННЫЕ СИСТЕМЫ" ----------
|
# ---------- ВКЛАДКА "ИНФОРМАЦИОННЫЕ СИСТЕМЫ" ----------
|
||||||
def _build_is_tab(self):
|
def _build_is_tab(self):
|
||||||
tab = ttk.Frame(self.notebook)
|
tab = ttk.Frame(self.notebook)
|
||||||
self.notebook.add(tab, text="Информационные системы")
|
self.notebook.add(tab, text="Объекты КИИ")
|
||||||
|
|
||||||
# Левая панель — список ИС
|
# Левая панель — список объектов КИИ
|
||||||
left = ttk.Frame(tab)
|
left = ttk.Frame(tab)
|
||||||
left.pack(side=tk.LEFT, fill=tk.Y, padx=5, pady=5)
|
left.pack(side=tk.LEFT, fill=tk.Y, padx=5, pady=5)
|
||||||
ttk.Label(left, text="Информационные системы:").pack()
|
ttk.Label(left, text="Объекты КИИ:").pack()
|
||||||
self.is_listbox = tk.Listbox(left, width=35, height=18, exportselection=False)
|
self.is_listbox = tk.Listbox(left, width=35, height=18, exportselection=False)
|
||||||
self.is_listbox.pack(fill=tk.BOTH, expand=True, pady=5)
|
self.is_listbox.pack(fill=tk.BOTH, expand=True, pady=5)
|
||||||
self.is_listbox.bind('<<ListboxSelect>>', self._on_is_select)
|
self.is_listbox.bind('<<ListboxSelect>>', self._on_is_select)
|
||||||
@@ -703,7 +729,7 @@ class DokoGenApp:
|
|||||||
ttk.Button(btn_frame, text="- Удалить", command=self._delete_is).pack(side=tk.LEFT, padx=2)
|
ttk.Button(btn_frame, text="- Удалить", command=self._delete_is).pack(side=tk.LEFT, padx=2)
|
||||||
|
|
||||||
# Правая панель — редактирование
|
# Правая панель — редактирование
|
||||||
self._is_edit_panel = ttk.LabelFrame(tab, text="Редактирование ИС")
|
self._is_edit_panel = ttk.LabelFrame(tab, text="Редактирование объекта КИИ")
|
||||||
self._is_edit_panel.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
|
self._is_edit_panel.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=5, pady=5)
|
||||||
|
|
||||||
fields_frame = ttk.Frame(self._is_edit_panel, padding=10)
|
fields_frame = ttk.Frame(self._is_edit_panel, padding=10)
|
||||||
@@ -726,51 +752,19 @@ class DokoGenApp:
|
|||||||
self.is_internet_var = tk.BooleanVar()
|
self.is_internet_var = tk.BooleanVar()
|
||||||
ttk.Checkbutton(chk_frame, text="Интернет", variable=self.is_internet_var).pack(side=tk.LEFT, padx=5)
|
ttk.Checkbutton(chk_frame, text="Интернет", variable=self.is_internet_var).pack(side=tk.LEFT, padx=5)
|
||||||
|
|
||||||
# Тип угроз
|
# Категория значимости объекта КИИ
|
||||||
self.is_threat_var = tk.StringVar(value="3")
|
self.is_category_var = tk.StringVar(value="без категории")
|
||||||
self.is_threat_var.trace_add("write", lambda *_: self._on_categories_or_count_changed())
|
ttk.Label(fields_frame, text="Категория значимости:").grid(row=3, column=0, sticky=tk.W, pady=2)
|
||||||
ttk.Label(fields_frame, text="Тип угроз:").grid(row=3, column=0, sticky=tk.W, pady=2)
|
cat_frame = ttk.Frame(fields_frame)
|
||||||
threat_frame = ttk.Frame(fields_frame)
|
cat_frame.grid(row=3, column=1, sticky=tk.W, padx=10)
|
||||||
threat_frame.grid(row=3, column=1, sticky=tk.W, padx=10)
|
for val in ["1", "2", "3", "без категории"]:
|
||||||
for val, text in [("1", "1 (НДВ в СПО)"), ("2", "2 (НДВ в ППО)"), ("3", "3 (НДВ нет)")]:
|
ttk.Radiobutton(cat_frame, text=val, variable=self.is_category_var,
|
||||||
ttk.Radiobutton(threat_frame, text=text, variable=self.is_threat_var,
|
|
||||||
value=val).pack(side=tk.LEFT, padx=3)
|
value=val).pack(side=tk.LEFT, padx=3)
|
||||||
|
|
||||||
# Уровень защищённости
|
# Программное обеспечение
|
||||||
self.is_defence_var = tk.StringVar(value="4")
|
ttk.Label(fields_frame, text="Программное обеспечение:").grid(row=4, column=0, sticky=tk.W, pady=2)
|
||||||
ttk.Label(fields_frame, text="Ур. защищённости:").grid(row=4, column=0, sticky=tk.W, pady=2)
|
self.is_software_entry = ttk.Entry(fields_frame, width=55)
|
||||||
def_frame = ttk.Frame(fields_frame)
|
self.is_software_entry.grid(row=4, column=1, padx=10, sticky=tk.EW)
|
||||||
def_frame.grid(row=4, column=1, sticky=tk.W, padx=10)
|
|
||||||
for lvl in ["1", "2", "3", "4"]:
|
|
||||||
ttk.Radiobutton(def_frame, text=f"{lvl} ур.",
|
|
||||||
variable=self.is_defence_var, value=lvl).pack(side=tk.LEFT, padx=3)
|
|
||||||
|
|
||||||
|
|
||||||
# Категории ПДн
|
|
||||||
ttk.Label(fields_frame, text="Категории ПДн:").grid(row=5, column=0, sticky=tk.NW, pady=2)
|
|
||||||
cat_frame = ttk.Frame(fields_frame)
|
|
||||||
cat_frame.grid(row=5, column=1, sticky=tk.W, padx=10)
|
|
||||||
self.category_vars = []
|
|
||||||
for cat in ["специальные", "биометрические", "общедоступные", "иные"]:
|
|
||||||
var = tk.BooleanVar()
|
|
||||||
var.trace_add('write', lambda *_: self._on_categories_or_count_changed())
|
|
||||||
cb = ttk.Checkbutton(cat_frame, text=cat, variable=var)
|
|
||||||
cb.pack(anchor=tk.W)
|
|
||||||
self.category_vars.append((cat, var))
|
|
||||||
|
|
||||||
# Количество записей ПДн (выпадающий список)
|
|
||||||
ttk.Label(fields_frame, text="Кол-во записей:").grid(row=6, column=0, sticky=tk.W, pady=2)
|
|
||||||
self.is_pd_count_var = tk.StringVar(value="менее 100 000")
|
|
||||||
self.is_pd_count_combo = ttk.Combobox(fields_frame, textvariable=self.is_pd_count_var,
|
|
||||||
values=["менее 100 000", "более 100 000"],
|
|
||||||
state='readonly', width=28)
|
|
||||||
self.is_pd_count_combo.grid(row=6, column=1, padx=10, sticky=tk.W)
|
|
||||||
self.is_pd_count_combo.bind('<<ComboboxSelected>>', lambda e: self._on_categories_or_count_changed())
|
|
||||||
|
|
||||||
# Структура ИС
|
|
||||||
ttk.Label(fields_frame, text="Структура ИС:").grid(row=7, column=0, sticky=tk.W, pady=2)
|
|
||||||
self.is_structure_entry = ttk.Entry(fields_frame, width=55)
|
|
||||||
self.is_structure_entry.grid(row=7, column=1, padx=10, sticky=tk.EW)
|
|
||||||
|
|
||||||
fields_frame.columnconfigure(1, weight=1)
|
fields_frame.columnconfigure(1, weight=1)
|
||||||
|
|
||||||
@@ -778,39 +772,6 @@ class DokoGenApp:
|
|||||||
nb2 = ttk.Notebook(self._is_edit_panel)
|
nb2 = ttk.Notebook(self._is_edit_panel)
|
||||||
nb2.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
nb2.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
|
||||||
|
|
||||||
# Список ПДн
|
|
||||||
pd_tab = ttk.Frame(nb2)
|
|
||||||
nb2.add(pd_tab, text="Перечень ПДн")
|
|
||||||
pd_btn_frame = ttk.Frame(pd_tab)
|
|
||||||
pd_btn_frame.pack(fill=tk.X)
|
|
||||||
ttk.Button(pd_btn_frame, text="+ Добавить", command=self._add_pd_item).pack(side=tk.LEFT, padx=2)
|
|
||||||
ttk.Button(pd_btn_frame, text="- Удалить", command=lambda: self._del_from_listbox(self.pd_listbox)).pack(side=tk.LEFT, padx=2)
|
|
||||||
ttk.Button(pd_btn_frame, text="Выбрать из списка", command=self._select_from_pd_list).pack(side=tk.LEFT, padx=2)
|
|
||||||
self.pd_listbox = tk.Listbox(pd_tab, height=6)
|
|
||||||
self.pd_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
|
||||||
|
|
||||||
# Субъекты ПДн
|
|
||||||
subj_tab = ttk.Frame(nb2)
|
|
||||||
nb2.add(subj_tab, text="Субъекты ПДн")
|
|
||||||
subj_btn_frame = ttk.Frame(subj_tab)
|
|
||||||
subj_btn_frame.pack(fill=tk.X)
|
|
||||||
ttk.Button(subj_btn_frame, text="+ Добавить", command=self._add_pd_subjects_item).pack(side=tk.LEFT, padx=2)
|
|
||||||
ttk.Button(subj_btn_frame, text="- Удалить", command=lambda: self._del_from_listbox(self.pd_subjects_listbox)).pack(side=tk.LEFT, padx=2)
|
|
||||||
ttk.Button(subj_btn_frame, text="Выбрать из списка", command=self._select_from_subjects_list).pack(side=tk.LEFT, padx=2)
|
|
||||||
self.pd_subjects_listbox = tk.Listbox(subj_tab, height=6)
|
|
||||||
self.pd_subjects_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
|
||||||
|
|
||||||
# Действия с ПДн
|
|
||||||
act_tab = ttk.Frame(nb2)
|
|
||||||
nb2.add(act_tab, text="Действия с ПДн")
|
|
||||||
act_btn_frame = ttk.Frame(act_tab)
|
|
||||||
act_btn_frame.pack(fill=tk.X)
|
|
||||||
ttk.Button(act_btn_frame, text="+ Добавить", command=self._add_pd_action_item).pack(side=tk.LEFT, padx=2)
|
|
||||||
ttk.Button(act_btn_frame, text="- Удалить", command=lambda: self._del_from_listbox(self.pd_actions_listbox)).pack(side=tk.LEFT, padx=2)
|
|
||||||
ttk.Button(act_btn_frame, text="Выбрать из списка", command=self._select_from_actions_list).pack(side=tk.LEFT, padx=2)
|
|
||||||
self.pd_actions_listbox = tk.Listbox(act_tab, height=6)
|
|
||||||
self.pd_actions_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
|
||||||
|
|
||||||
# СЗИ
|
# СЗИ
|
||||||
def_tab = ttk.Frame(nb2)
|
def_tab = ttk.Frame(nb2)
|
||||||
nb2.add(def_tab, text="Средства защиты")
|
nb2.add(def_tab, text="Средства защиты")
|
||||||
@@ -822,9 +783,9 @@ class DokoGenApp:
|
|||||||
self.defense_tools_listbox = tk.Listbox(def_tab, height=6)
|
self.defense_tools_listbox = tk.Listbox(def_tab, height=6)
|
||||||
self.defense_tools_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
self.defense_tools_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
# Пользователи
|
# Пользователи ОКИИ
|
||||||
user_tab = ttk.Frame(nb2)
|
user_tab = ttk.Frame(nb2)
|
||||||
nb2.add(user_tab, text="Пользователи ИС")
|
nb2.add(user_tab, text="Пользователи ОКИИ")
|
||||||
self.users_listbox = tk.Listbox(user_tab, height=6)
|
self.users_listbox = tk.Listbox(user_tab, height=6)
|
||||||
self.users_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
self.users_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||||
btnf = ttk.Frame(user_tab)
|
btnf = ttk.Frame(user_tab)
|
||||||
@@ -886,7 +847,7 @@ class DokoGenApp:
|
|||||||
frame = ttk.Frame(scrollable, padding=10)
|
frame = ttk.Frame(scrollable, padding=10)
|
||||||
frame.pack(fill=tk.BOTH, expand=True)
|
frame.pack(fill=tk.BOTH, expand=True)
|
||||||
|
|
||||||
ttk.Label(frame, text="Сотрудники, имеющие доступ к ПДн",
|
ttk.Label(frame, text="Сотрудники, имеющие доступ к объектам КИИ",
|
||||||
font=('Arial', 11, 'bold')).pack(anchor=tk.W, pady=(0, 10))
|
font=('Arial', 11, 'bold')).pack(anchor=tk.W, pady=(0, 10))
|
||||||
|
|
||||||
# Заголовки
|
# Заголовки
|
||||||
@@ -971,102 +932,6 @@ class DokoGenApp:
|
|||||||
if emp['position'] or emp['fio']:
|
if emp['position'] or emp['fio']:
|
||||||
self.model.employees_access.append(emp)
|
self.model.employees_access.append(emp)
|
||||||
|
|
||||||
# ---------- ВКЛАДКА "БУМАЖНЫЕ ДОКУМЕНТЫ" ----------
|
|
||||||
def _build_paper_tab(self):
|
|
||||||
tab = ttk.Frame(self.notebook)
|
|
||||||
self.notebook.add(tab, text="Бумажные документы")
|
|
||||||
|
|
||||||
canvas = tk.Canvas(tab, highlightthickness=0)
|
|
||||||
scrollbar = ttk.Scrollbar(tab, orient=tk.VERTICAL, command=canvas.yview)
|
|
||||||
scrollable = ttk.Frame(canvas)
|
|
||||||
scrollable.bind("<Configure>",
|
|
||||||
lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
|
|
||||||
canvas.create_window((0, 0), window=scrollable, anchor="nw")
|
|
||||||
canvas.configure(yscrollcommand=scrollbar.set)
|
|
||||||
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
|
||||||
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
|
||||||
|
|
||||||
frame = ttk.Frame(scrollable, padding=10)
|
|
||||||
frame.pack(fill=tk.BOTH, expand=True)
|
|
||||||
|
|
||||||
ttk.Label(frame, text="Бумажные носители ПДн (документы)",
|
|
||||||
font=('Arial', 11, 'bold')).pack(anchor=tk.W, pady=(0, 10))
|
|
||||||
|
|
||||||
# Заголовки
|
|
||||||
header = ttk.Frame(frame)
|
|
||||||
header.pack(fill=tk.X)
|
|
||||||
for w, col in [(45, "Название документа"), (45, "Место хранения")]:
|
|
||||||
ttk.Label(header, text=col, font=('', 9, 'bold'),
|
|
||||||
width=w, anchor=tk.W).pack(side=tk.LEFT, padx=2)
|
|
||||||
|
|
||||||
self._paper_frame = ttk.Frame(frame)
|
|
||||||
self._paper_frame.pack(fill=tk.X, pady=5)
|
|
||||||
|
|
||||||
btn_frame = ttk.Frame(frame)
|
|
||||||
btn_frame.pack(fill=tk.X, pady=5)
|
|
||||||
ttk.Button(btn_frame, text="+ Добавить документ",
|
|
||||||
command=self._add_paper_row_ui).pack(side=tk.LEFT, padx=2)
|
|
||||||
ttk.Button(btn_frame, text="- Удалить выбранный",
|
|
||||||
command=self._delete_paper_selected).pack(side=tk.LEFT, padx=2)
|
|
||||||
|
|
||||||
self._paper_count_label = ttk.Label(frame, text="Добавлено: 0")
|
|
||||||
self._paper_count_label.pack(anchor=tk.W, pady=5)
|
|
||||||
|
|
||||||
self._paper_entries = []
|
|
||||||
# Загрузка существующих
|
|
||||||
for p in self.model.paper_documents_list:
|
|
||||||
self._add_paper_row(p.name, p.storage)
|
|
||||||
self._update_paper_count()
|
|
||||||
|
|
||||||
def _add_paper_row(self, name="", storage=""):
|
|
||||||
"""Добавляет одну строку документа."""
|
|
||||||
row = ttk.Frame(self._paper_frame)
|
|
||||||
row.pack(fill=tk.X, pady=2)
|
|
||||||
|
|
||||||
name_entry = ttk.Entry(row, width=45)
|
|
||||||
name_entry.insert(0, name)
|
|
||||||
name_entry.pack(side=tk.LEFT, padx=2)
|
|
||||||
|
|
||||||
storage_entry = ttk.Entry(row, width=45)
|
|
||||||
storage_entry.insert(0, storage)
|
|
||||||
storage_entry.pack(side=tk.LEFT, padx=2)
|
|
||||||
|
|
||||||
del_btn = ttk.Button(row, text="✖", width=3,
|
|
||||||
command=lambda: self._delete_paper_row(row))
|
|
||||||
del_btn.pack(side=tk.LEFT, padx=5)
|
|
||||||
|
|
||||||
self._paper_entries.append((row, name_entry, storage_entry))
|
|
||||||
self._update_paper_count()
|
|
||||||
|
|
||||||
def _add_paper_row_ui(self):
|
|
||||||
self._add_paper_row()
|
|
||||||
|
|
||||||
def _delete_paper_row(self, row_frame):
|
|
||||||
self._paper_entries = [e for e in self._paper_entries if e[0] != row_frame]
|
|
||||||
row_frame.destroy()
|
|
||||||
self._update_paper_count()
|
|
||||||
|
|
||||||
def _delete_paper_selected(self):
|
|
||||||
if self._paper_entries:
|
|
||||||
row_frame = self._paper_entries[-1][0]
|
|
||||||
self._delete_paper_row(row_frame)
|
|
||||||
|
|
||||||
def _update_paper_count(self):
|
|
||||||
count = len(self._paper_entries)
|
|
||||||
if hasattr(self, '_paper_count_label'):
|
|
||||||
self._paper_count_label.config(text=f"Добавлено: {count}")
|
|
||||||
|
|
||||||
def _collect_paper_documents(self):
|
|
||||||
"""Собирает данные бумажных документов из GUI в модель."""
|
|
||||||
self.model.paper_documents_list.clear()
|
|
||||||
for row, name_e, storage_e in self._paper_entries:
|
|
||||||
name = name_e.get().strip()
|
|
||||||
storage = storage_e.get().strip()
|
|
||||||
if name or storage:
|
|
||||||
self.model.paper_documents_list.append(
|
|
||||||
PaperDocument(name=name, storage=storage)
|
|
||||||
)
|
|
||||||
|
|
||||||
# ---------- ВКЛАДКА "ГЕНЕРАЦИЯ" ----------
|
# ---------- ВКЛАДКА "ГЕНЕРАЦИЯ" ----------
|
||||||
def _build_generation_tab(self):
|
def _build_generation_tab(self):
|
||||||
tab = ttk.Frame(self.notebook)
|
tab = ttk.Frame(self.notebook)
|
||||||
@@ -1142,29 +1007,21 @@ class DokoGenApp:
|
|||||||
setattr(c, key, entry.get().strip())
|
setattr(c, key, entry.get().strip())
|
||||||
c.contract_number = self.doc_number_var.get()
|
c.contract_number = self.doc_number_var.get()
|
||||||
c.contract_date = self.doc_date_var.get()
|
c.contract_date = self.doc_date_var.get()
|
||||||
|
if hasattr(self, 'okved_extra_listbox'):
|
||||||
|
c.okved_extra = list(self.okved_extra_listbox.get(0, tk.END))
|
||||||
|
|
||||||
def _collect_is(self):
|
def _collect_is(self):
|
||||||
"""Собирает данные ИС из GUI в модель."""
|
"""Собирает данные объекта КИИ из GUI в модель."""
|
||||||
if self._current_is_idx is not None and self._current_is_idx < len(self.model.information_systems):
|
if self._current_is_idx is not None and self._current_is_idx < len(self.model.information_systems):
|
||||||
isys = self.model.information_systems[self._current_is_idx]
|
isys = self.model.information_systems[self._current_is_idx]
|
||||||
isys.name = self.is_name_entry.get().strip()
|
isys.name = self.is_name_entry.get().strip()
|
||||||
isys.description = self.is_description_entry.get().strip()
|
isys.description = self.is_description_entry.get().strip()
|
||||||
if hasattr(self, 'is_structure_entry'):
|
|
||||||
isys.structure = self.is_structure_entry.get().strip()
|
|
||||||
if hasattr(self, 'is_software_entry'):
|
if hasattr(self, 'is_software_entry'):
|
||||||
isys.software = self.is_software_entry.get().strip()
|
isys.software = self.is_software_entry.get().strip()
|
||||||
isys.is_local_network = self.is_lan_var.get()
|
isys.is_local_network = self.is_lan_var.get()
|
||||||
isys.is_internet = self.is_internet_var.get()
|
isys.is_internet = self.is_internet_var.get()
|
||||||
isys.threat_type = self.is_threat_var.get()
|
isys.category = self.is_category_var.get()
|
||||||
isys.defence_level = self.is_defence_var.get()
|
|
||||||
cats = [c for c, v in self.category_vars if v.get()]
|
|
||||||
isys.personal_data_category = cats
|
|
||||||
if hasattr(self, 'is_pd_count_entry'):
|
|
||||||
isys.pd_count = self.is_pd_count_entry.get().strip() or 'менее 100 000'
|
|
||||||
# Списки
|
# Списки
|
||||||
isys.personal_data_list = list(self.pd_listbox.get(0, tk.END))
|
|
||||||
isys.pd_subjects_list = list(self.pd_subjects_listbox.get(0, tk.END))
|
|
||||||
isys.pd_actions_list = list(self.pd_actions_listbox.get(0, tk.END))
|
|
||||||
isys.defense_tools_list = list(self.defense_tools_listbox.get(0, tk.END))
|
isys.defense_tools_list = list(self.defense_tools_listbox.get(0, tk.END))
|
||||||
isys.users_list = list(self.users_listbox.get(0, tk.END))
|
isys.users_list = list(self.users_listbox.get(0, tk.END))
|
||||||
|
|
||||||
@@ -1199,6 +1056,12 @@ class DokoGenApp:
|
|||||||
self.doc_number_var.set(c.contract_number or "")
|
self.doc_number_var.set(c.contract_number or "")
|
||||||
self.doc_date_var.set(c.contract_date or "")
|
self.doc_date_var.set(c.contract_date or "")
|
||||||
|
|
||||||
|
# Дополнительные ОКВЭД
|
||||||
|
if hasattr(self, 'okved_extra_listbox'):
|
||||||
|
self.okved_extra_listbox.delete(0, tk.END)
|
||||||
|
for item in (c.okved_extra or []):
|
||||||
|
self.okved_extra_listbox.insert(tk.END, item)
|
||||||
|
|
||||||
# Склонения
|
# Склонения
|
||||||
idx_map = {'nomn': 1, 'gent': 2, 'datv': 3, 'accs': 4, 'ablt': 5, 'loct': 6}
|
idx_map = {'nomn': 1, 'gent': 2, 'datv': 3, 'accs': 4, 'ablt': 5, 'loct': 6}
|
||||||
for code, entry in self.decl_entries.items():
|
for code, entry in self.decl_entries.items():
|
||||||
@@ -1236,15 +1099,6 @@ class DokoGenApp:
|
|||||||
)
|
)
|
||||||
self._update_emp_count()
|
self._update_emp_count()
|
||||||
|
|
||||||
# Бумажные документы
|
|
||||||
if hasattr(self, '_paper_entries'):
|
|
||||||
for row_frame, _, _ in self._paper_entries:
|
|
||||||
row_frame.destroy()
|
|
||||||
self._paper_entries.clear()
|
|
||||||
for p in c.paper_documents_list:
|
|
||||||
self._add_paper_row(p.name, p.storage)
|
|
||||||
self._update_paper_count()
|
|
||||||
|
|
||||||
# Шаблоны
|
# Шаблоны
|
||||||
self._on_templates_changed()
|
self._on_templates_changed()
|
||||||
|
|
||||||
@@ -1296,64 +1150,6 @@ class DokoGenApp:
|
|||||||
self.log(f"🏙 Город определён: {city}")
|
self.log(f"🏙 Город определён: {city}")
|
||||||
|
|
||||||
# ==================== АВТОРАСЧЁТ УРОВНЯ ЗАЩИЩЁННОСТИ ====================
|
# ==================== АВТОРАСЧЁТ УРОВНЯ ЗАЩИЩЁННОСТИ ====================
|
||||||
def _auto_calc_defence_level(self):
|
|
||||||
"""Автоматический расчёт УЗ по ПП №1119: категории ПДн, тип субъектов,
|
|
||||||
кол-во записей, тип угроз."""
|
|
||||||
if self._current_is_idx is None:
|
|
||||||
return
|
|
||||||
|
|
||||||
categories = [cat for cat, var in self.category_vars if var.get()]
|
|
||||||
pd_count = self.is_pd_count_var.get() if hasattr(self, 'is_pd_count_var') else 'менее 100 000'
|
|
||||||
threat_type = self.is_threat_var.get() if hasattr(self, 'is_threat_var') else '3'
|
|
||||||
is_large = 'более' in pd_count.lower()
|
|
||||||
# Автоопределение типа субъектов: если в Субъектах ПДн выбраны только "Сотрудники" → работники, иначе → иные
|
|
||||||
subj_items = self.pd_subjects_listbox.get(0, tk.END) if hasattr(self, 'pd_subjects_listbox') else []
|
|
||||||
has_only_employees = len(subj_items) > 0 and all('сотрудник' in s.lower() for s in subj_items)
|
|
||||||
is_employees = has_only_employees or len(subj_items) == 0
|
|
||||||
|
|
||||||
# Таблица ПП №1119: (категория, тип_субъектов, объём, тип_угроз) → УЗ
|
|
||||||
def lookup(cat_group, employees, large, threat):
|
|
||||||
# Общедоступные → всегда 4
|
|
||||||
if cat_group == 'public':
|
|
||||||
return 4
|
|
||||||
# Специальные/биометрические
|
|
||||||
if cat_group == 'special_bio':
|
|
||||||
tbl = {
|
|
||||||
('emp', False, '1'): 2, ('emp', True, '1'): 1,
|
|
||||||
('other', False, '1'): 1, ('other', True, '1'): 1,
|
|
||||||
('emp', False, '2'): 2, ('emp', True, '2'): 1,
|
|
||||||
('other', False, '2'): 2, ('other', True, '2'): 1,
|
|
||||||
('emp', False, '3'): 3, ('emp', True, '3'): 2,
|
|
||||||
('other', False, '3'): 3, ('other', True, '3'): 2,
|
|
||||||
}
|
|
||||||
else: # иные
|
|
||||||
tbl = {
|
|
||||||
('emp', False, '1'): 3, ('emp', True, '1'): 2,
|
|
||||||
('other', False, '1'): 2, ('other', True, '1'): 1,
|
|
||||||
('emp', False, '2'): 4, ('emp', True, '2'): 3,
|
|
||||||
('other', False, '2'): 3, ('other', True, '2'): 2,
|
|
||||||
('emp', False, '3'): 4, ('emp', True, '3'): 4,
|
|
||||||
('other', False, '3'): 4, ('other', True, '3'): 3,
|
|
||||||
}
|
|
||||||
key = ('emp' if employees else 'other', large, threat)
|
|
||||||
return tbl.get(key, 4)
|
|
||||||
|
|
||||||
# Определяем группу категорий
|
|
||||||
if 'общедоступные' in categories and not any(c in categories for c in ['специальные', 'биометрические', 'иные']):
|
|
||||||
cat_group = 'public'
|
|
||||||
elif any(c in categories for c in ['специальные', 'биометрические']):
|
|
||||||
cat_group = 'special_bio'
|
|
||||||
else:
|
|
||||||
cat_group = 'other'
|
|
||||||
|
|
||||||
level = lookup(cat_group, is_employees, is_large, threat_type)
|
|
||||||
self.is_defence_var.set(str(level))
|
|
||||||
|
|
||||||
def _on_categories_or_count_changed(self):
|
|
||||||
"""Обработчик изменения категорий ПДн или количества записей."""
|
|
||||||
if self._current_is_idx is not None:
|
|
||||||
self._auto_calc_defence_level()
|
|
||||||
|
|
||||||
# ==================== КОМИССИЯ ====================
|
# ==================== КОМИССИЯ ====================
|
||||||
def _add_commission_row(self, role="Член комиссии", position="", fio=""):
|
def _add_commission_row(self, role="Член комиссии", position="", fio=""):
|
||||||
row = ttk.Frame(self._comm_members_frame)
|
row = ttk.Frame(self._comm_members_frame)
|
||||||
@@ -1421,28 +1217,15 @@ class DokoGenApp:
|
|||||||
self.is_name_entry.insert(0, isys.name or "")
|
self.is_name_entry.insert(0, isys.name or "")
|
||||||
self.is_description_entry.delete(0, tk.END)
|
self.is_description_entry.delete(0, tk.END)
|
||||||
self.is_description_entry.insert(0, isys.description or "")
|
self.is_description_entry.insert(0, isys.description or "")
|
||||||
if hasattr(self, 'is_structure_entry'):
|
|
||||||
self.is_structure_entry.delete(0, tk.END)
|
|
||||||
self.is_structure_entry.insert(0, isys.structure or "")
|
|
||||||
if hasattr(self, 'is_software_entry'):
|
if hasattr(self, 'is_software_entry'):
|
||||||
self.is_software_entry.delete(0, tk.END)
|
self.is_software_entry.delete(0, tk.END)
|
||||||
self.is_software_entry.insert(0, isys.software or "")
|
self.is_software_entry.insert(0, isys.software or "")
|
||||||
self.is_lan_var.set(isys.is_local_network)
|
self.is_lan_var.set(isys.is_local_network)
|
||||||
self.is_internet_var.set(isys.is_internet)
|
self.is_internet_var.set(isys.is_internet)
|
||||||
self.is_threat_var.set(isys.threat_type or "3")
|
self.is_category_var.set(isys.category or "без категории")
|
||||||
self.is_defence_var.set(isys.defence_level or "4")
|
|
||||||
for cat, var in self.category_vars:
|
|
||||||
var.set(cat in isys.personal_data_category)
|
|
||||||
# Заполняем кол-во записей ПДн
|
|
||||||
if hasattr(self, 'is_pd_count_entry'):
|
|
||||||
self.is_pd_count_entry.delete(0, tk.END)
|
|
||||||
self.is_pd_count_entry.insert(0, isys.pd_count or 'менее 100 000')
|
|
||||||
|
|
||||||
# Списки
|
# Списки
|
||||||
for lb, items in [
|
for lb, items in [
|
||||||
(self.pd_listbox, isys.personal_data_list or []),
|
|
||||||
(self.pd_subjects_listbox, isys.pd_subjects_list or []),
|
|
||||||
(self.pd_actions_listbox, isys.pd_actions_list or []),
|
|
||||||
(self.defense_tools_listbox, isys.defense_tools_list or []),
|
(self.defense_tools_listbox, isys.defense_tools_list or []),
|
||||||
(self.users_listbox, isys.users_list or []),
|
(self.users_listbox, isys.users_list or []),
|
||||||
]:
|
]:
|
||||||
@@ -1471,13 +1254,7 @@ class DokoGenApp:
|
|||||||
description=src.description,
|
description=src.description,
|
||||||
is_local_network=src.is_local_network,
|
is_local_network=src.is_local_network,
|
||||||
is_internet=src.is_internet,
|
is_internet=src.is_internet,
|
||||||
defence_level=src.defence_level,
|
category=src.category,
|
||||||
subject_type=src.subject_type,
|
|
||||||
threat_type=src.threat_type,
|
|
||||||
personal_data_category=list(src.personal_data_category),
|
|
||||||
personal_data_list=list(src.personal_data_list),
|
|
||||||
pd_actions_list=list(src.pd_actions_list),
|
|
||||||
pd_subjects_list=list(src.pd_subjects_list),
|
|
||||||
defense_tools_list=list(src.defense_tools_list),
|
defense_tools_list=list(src.defense_tools_list),
|
||||||
users_list=list(src.users_list),
|
users_list=list(src.users_list),
|
||||||
)
|
)
|
||||||
@@ -1497,16 +1274,6 @@ class DokoGenApp:
|
|||||||
self.is_listbox.selection_set(0)
|
self.is_listbox.selection_set(0)
|
||||||
self._on_is_select()
|
self._on_is_select()
|
||||||
|
|
||||||
def _add_pd_item(self):
|
|
||||||
item = simpledialog.askstring("Добавить ПДн", "Введите персональные данные:")
|
|
||||||
if item:
|
|
||||||
self.pd_listbox.insert(tk.END, item)
|
|
||||||
|
|
||||||
def _add_pd_subjects_item(self):
|
|
||||||
item = simpledialog.askstring("Добавить категорию", "Введите категорию субъектов:")
|
|
||||||
if item:
|
|
||||||
self.pd_subjects_listbox.insert(tk.END, item)
|
|
||||||
|
|
||||||
def _add_defense_item(self):
|
def _add_defense_item(self):
|
||||||
item = simpledialog.askstring("Добавить СЗИ", "Введите средство защиты:")
|
item = simpledialog.askstring("Добавить СЗИ", "Введите средство защиты:")
|
||||||
if item:
|
if item:
|
||||||
@@ -1533,7 +1300,7 @@ class DokoGenApp:
|
|||||||
if not names:
|
if not names:
|
||||||
messagebox.showinfo(
|
messagebox.showinfo(
|
||||||
"Выбор пользователей",
|
"Выбор пользователей",
|
||||||
"Список пуст.\nЗаполните вкладку «Пользователи» (сотрудники с доступом к ПДн) "
|
"Список пуст.\nЗаполните вкладку «Пользователи» (сотрудники с доступом к объектам КИИ) "
|
||||||
"или добавьте пользователей в другой ИС."
|
"или добавьте пользователей в другой ИС."
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
@@ -1607,44 +1374,6 @@ class DokoGenApp:
|
|||||||
|
|
||||||
dialog.wait_window()
|
dialog.wait_window()
|
||||||
|
|
||||||
def _add_pd_action_item(self):
|
|
||||||
item = simpledialog.askstring("Добавить действие", "Введите действие с ПДн:")
|
|
||||||
if item:
|
|
||||||
self.pd_actions_listbox.insert(tk.END, item)
|
|
||||||
|
|
||||||
def _select_from_actions_list(self):
|
|
||||||
"""Выбор действий с ПДн из JSON словаря."""
|
|
||||||
import json
|
|
||||||
dict_path = os.path.join(os.path.dirname(__file__), 'dictionaries', 'pd_actions.json')
|
|
||||||
try:
|
|
||||||
with open(dict_path, 'r', encoding='utf-8') as f:
|
|
||||||
items = json.load(f)
|
|
||||||
self._select_from_dialog("Выбор действий с персональными данными", items, self.pd_actions_listbox)
|
|
||||||
except Exception as e:
|
|
||||||
messagebox.showerror("Ошибка", f"Не удалось загрузить словарь: {e}")
|
|
||||||
|
|
||||||
def _select_from_pd_list(self):
|
|
||||||
"""Выбор ПДн из JSON словаря."""
|
|
||||||
import json
|
|
||||||
dict_path = os.path.join(os.path.dirname(__file__), 'dictionaries', 'pd_items.json')
|
|
||||||
try:
|
|
||||||
with open(dict_path, 'r', encoding='utf-8') as f:
|
|
||||||
items = json.load(f)
|
|
||||||
self._select_from_dialog("Выбор персональных данных", items, self.pd_listbox)
|
|
||||||
except Exception as e:
|
|
||||||
messagebox.showerror("Ошибка", f"Не удалось загрузить словарь: {e}")
|
|
||||||
|
|
||||||
def _select_from_subjects_list(self):
|
|
||||||
"""Выбор категорий субъектов из JSON словаря."""
|
|
||||||
import json
|
|
||||||
dict_path = os.path.join(os.path.dirname(__file__), 'dictionaries', 'pd_categories.json')
|
|
||||||
try:
|
|
||||||
with open(dict_path, 'r', encoding='utf-8') as f:
|
|
||||||
items = json.load(f)
|
|
||||||
self._select_from_dialog("Выбор категорий субъектов ПДн", items, self.pd_subjects_listbox)
|
|
||||||
except Exception as e:
|
|
||||||
messagebox.showerror("Ошибка", f"Не удалось загрузить словарь: {e}")
|
|
||||||
|
|
||||||
def _select_from_defense_list(self):
|
def _select_from_defense_list(self):
|
||||||
"""Выбор СЗИ из JSON словаря."""
|
"""Выбор СЗИ из JSON словаря."""
|
||||||
import json
|
import json
|
||||||
@@ -1656,6 +1385,84 @@ class DokoGenApp:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
messagebox.showerror("Ошибка", f"Не удалось загрузить словарь: {e}")
|
messagebox.showerror("Ошибка", f"Не удалось загрузить словарь: {e}")
|
||||||
|
|
||||||
|
# ==================== ОКВЭД ====================
|
||||||
|
def _load_okved_dict(self):
|
||||||
|
"""Загружает словарь ОКВЭД (dictionaries/okved.json)."""
|
||||||
|
import json
|
||||||
|
dict_path = os.path.join(os.path.dirname(__file__), 'dictionaries', 'okved.json')
|
||||||
|
try:
|
||||||
|
with open(dict_path, 'r', encoding='utf-8') as f:
|
||||||
|
return json.load(f)
|
||||||
|
except Exception as e:
|
||||||
|
messagebox.showerror("Ошибка", f"Не удалось загрузить словарь ОКВЭД:\n{e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
def _select_main_okved(self):
|
||||||
|
"""Выбор основного ОКВЭД из словаря (всегда один)."""
|
||||||
|
items = self._load_okved_dict()
|
||||||
|
if not items:
|
||||||
|
return
|
||||||
|
current = self._company_entries.get('okved').get().strip() if 'okved' in self._company_entries else ''
|
||||||
|
dialog = tk.Toplevel(self.root)
|
||||||
|
dialog.title("Основной ОКВЭД")
|
||||||
|
dialog.geometry("500x500")
|
||||||
|
dialog.transient(self.root)
|
||||||
|
dialog.grab_set()
|
||||||
|
|
||||||
|
frame = ttk.Frame(dialog, padding=10)
|
||||||
|
frame.pack(fill=tk.BOTH, expand=True)
|
||||||
|
ttk.Label(frame, text="Выберите основной ОКВЭД (один):",
|
||||||
|
font=('Arial', 10, 'bold')).pack(anchor=tk.W, pady=(0, 5))
|
||||||
|
|
||||||
|
canvas = tk.Canvas(frame, highlightthickness=0)
|
||||||
|
scrollbar = ttk.Scrollbar(frame, orient=tk.VERTICAL, command=canvas.yview)
|
||||||
|
check_frame = ttk.Frame(canvas)
|
||||||
|
check_frame.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))
|
||||||
|
canvas.create_window((0, 0), window=check_frame, anchor="nw")
|
||||||
|
canvas.configure(yscrollcommand=scrollbar.set)
|
||||||
|
canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||||
|
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||||
|
|
||||||
|
# Радио-кнопки: основной всегда один
|
||||||
|
var = tk.StringVar(value=current)
|
||||||
|
for item in items:
|
||||||
|
ttk.Radiobutton(check_frame, text=item, variable=var,
|
||||||
|
value=item).pack(anchor=tk.W, pady=1)
|
||||||
|
|
||||||
|
btn_frame = ttk.Frame(dialog, padding=10)
|
||||||
|
btn_frame.pack(fill=tk.X)
|
||||||
|
|
||||||
|
def _apply():
|
||||||
|
val = var.get()
|
||||||
|
if val:
|
||||||
|
entry = self._company_entries.get('okved')
|
||||||
|
if entry:
|
||||||
|
entry.delete(0, tk.END)
|
||||||
|
entry.insert(0, val)
|
||||||
|
dialog.destroy()
|
||||||
|
|
||||||
|
ttk.Button(btn_frame, text="✅ Применить", command=_apply).pack(side=tk.RIGHT, padx=2)
|
||||||
|
ttk.Button(btn_frame, text="Отмена", command=dialog.destroy).pack(side=tk.RIGHT, padx=2)
|
||||||
|
dialog.wait_window()
|
||||||
|
|
||||||
|
def _select_extra_okved(self):
|
||||||
|
"""Выбор дополнительных ОКВЭД из словаря (можно несколько)."""
|
||||||
|
items = self._load_okved_dict()
|
||||||
|
if not items:
|
||||||
|
return
|
||||||
|
self._select_from_dialog("Выбор дополнительных ОКВЭД", items, self.okved_extra_listbox)
|
||||||
|
|
||||||
|
def _add_okved_manual(self):
|
||||||
|
"""Ручное добавление дополнительного ОКВЭД."""
|
||||||
|
item = simpledialog.askstring("Добавить ОКВЭД", "Введите код ОКВЭД:")
|
||||||
|
if item:
|
||||||
|
self.okved_extra_listbox.insert(tk.END, item.strip())
|
||||||
|
|
||||||
|
def _delete_okved_extra(self):
|
||||||
|
sel = self.okved_extra_listbox.curselection()
|
||||||
|
if sel:
|
||||||
|
self.okved_extra_listbox.delete(sel[0])
|
||||||
|
|
||||||
# ==================== ШАБЛОНЫ ====================
|
# ==================== ШАБЛОНЫ ====================
|
||||||
def select_templates(self):
|
def select_templates(self):
|
||||||
folder = filedialog.askdirectory(title="Папка шаблонов")
|
folder = filedialog.askdirectory(title="Папка шаблонов")
|
||||||
@@ -1720,7 +1527,6 @@ class DokoGenApp:
|
|||||||
self._collect_commission()
|
self._collect_commission()
|
||||||
self._collect_is()
|
self._collect_is()
|
||||||
self._collect_employees()
|
self._collect_employees()
|
||||||
self._collect_paper_documents()
|
|
||||||
|
|
||||||
# Проверка
|
# Проверка
|
||||||
if not self._validate_before_generate():
|
if not self._validate_before_generate():
|
||||||
@@ -1939,7 +1745,24 @@ class DokoGenApp:
|
|||||||
if data.get('ispdnDepartment'):
|
if data.get('ispdnDepartment'):
|
||||||
self.model.security_department = data['ispdnDepartment']
|
self.model.security_department = data['ispdnDepartment']
|
||||||
|
|
||||||
# Информационные системы
|
# ОКВЭД (основной и дополнительные)
|
||||||
|
if data.get('okved'):
|
||||||
|
entry = self._company_entries.get('okved')
|
||||||
|
if entry:
|
||||||
|
entry.delete(0, tk.END)
|
||||||
|
entry.insert(0, data['okved'])
|
||||||
|
if data.get('okvedExtra'):
|
||||||
|
if hasattr(self, 'okved_extra_listbox'):
|
||||||
|
self.okved_extra_listbox.delete(0, tk.END)
|
||||||
|
extra = data['okvedExtra']
|
||||||
|
if isinstance(extra, str):
|
||||||
|
parts = [p.strip() for p in extra.replace(';', '\n').split('\n') if p.strip()]
|
||||||
|
else:
|
||||||
|
parts = [str(p).strip() for p in extra if str(p).strip()]
|
||||||
|
for p in parts:
|
||||||
|
self.okved_extra_listbox.insert(tk.END, p)
|
||||||
|
|
||||||
|
# Объекты КИИ
|
||||||
if data.get('informationSystems'):
|
if data.get('informationSystems'):
|
||||||
self.model.information_systems.clear()
|
self.model.information_systems.clear()
|
||||||
for is_data in data['informationSystems']:
|
for is_data in data['informationSystems']:
|
||||||
@@ -1948,17 +1771,12 @@ class DokoGenApp:
|
|||||||
description=is_data.get('description', ''),
|
description=is_data.get('description', ''),
|
||||||
is_local_network=is_data.get('is_has_lan', is_data.get('is_local_network', False)),
|
is_local_network=is_data.get('is_has_lan', is_data.get('is_local_network', False)),
|
||||||
is_internet=is_data.get('isInternet', False),
|
is_internet=is_data.get('isInternet', False),
|
||||||
pd_count=is_data.get('personalDataCount', 'менее 100 000'),
|
category=is_data.get('category', ''),
|
||||||
personal_data_list=is_data.get('personalDataList', []),
|
|
||||||
pd_subjects_list=is_data.get('pd_subjects_list', []),
|
|
||||||
defense_tools_list=is_data.get('defense_tools', []),
|
defense_tools_list=is_data.get('defense_tools', []),
|
||||||
users_list=is_data.get('usersList', []),
|
users_list=is_data.get('usersList', []),
|
||||||
personal_data_category=is_data.get('personalDataCategory', []),
|
purpose=is_data.get('purpose', is_data.get('description', '')),
|
||||||
processing_modes=is_data.get('processing_mode', ''),
|
|
||||||
purpose=is_data.get('description', ''),
|
|
||||||
room=is_data.get('room', ''),
|
|
||||||
structure=is_data.get('structure', ''),
|
structure=is_data.get('structure', ''),
|
||||||
pd_actions_list=is_data.get('pd_actions', []),
|
software=is_data.get('software', ''),
|
||||||
)
|
)
|
||||||
self.model.information_systems.append(isys)
|
self.model.information_systems.append(isys)
|
||||||
self._refresh_is_listbox()
|
self._refresh_is_listbox()
|
||||||
@@ -2005,26 +1823,6 @@ class DokoGenApp:
|
|||||||
)
|
)
|
||||||
self._update_emp_count()
|
self._update_emp_count()
|
||||||
|
|
||||||
# Бумажные документы (из «Названия документов» / «Место хранения»)
|
|
||||||
if data.get('paperDocuments'):
|
|
||||||
self.model.paper_documents_list.clear()
|
|
||||||
names = [n.strip() for n in data['paperDocuments'].replace('\n', ';').split(';') if n.strip()]
|
|
||||||
storages = [s.strip() for s in (data.get('paperStorage') or '').replace('\n', ';').split(';') if s.strip()]
|
|
||||||
for i, name in enumerate(names):
|
|
||||||
storage = storages[i] if i < len(storages) else (storages[0] if storages else '')
|
|
||||||
self.model.paper_documents_list.append(
|
|
||||||
PaperDocument(name=name, storage=storage)
|
|
||||||
)
|
|
||||||
# Обновляем GUI
|
|
||||||
if hasattr(self, '_paper_entries'):
|
|
||||||
for row_frame, _, _ in self._paper_entries:
|
|
||||||
row_frame.destroy()
|
|
||||||
self._paper_entries.clear()
|
|
||||||
for p in self.model.paper_documents_list:
|
|
||||||
self._add_paper_row(p.name, p.storage)
|
|
||||||
self._update_paper_count()
|
|
||||||
self.log(f" Бумажные документы: {len(self.model.paper_documents_list)} шт.")
|
|
||||||
|
|
||||||
self.log("✅ Импорт применён")
|
self.log("✅ Импорт применён")
|
||||||
self.root.after(0, _apply)
|
self.root.after(0, _apply)
|
||||||
|
|
||||||
@@ -2069,7 +1867,6 @@ class DokoGenApp:
|
|||||||
self._collect_commission()
|
self._collect_commission()
|
||||||
self._collect_is()
|
self._collect_is()
|
||||||
self._collect_employees()
|
self._collect_employees()
|
||||||
self._collect_paper_documents()
|
|
||||||
try:
|
try:
|
||||||
with open(filename, 'w', encoding='utf-8') as f:
|
with open(filename, 'w', encoding='utf-8') as f:
|
||||||
json.dump(self.model.to_dict(), f, ensure_ascii=False, indent=2)
|
json.dump(self.model.to_dict(), f, ensure_ascii=False, indent=2)
|
||||||
@@ -2095,7 +1892,6 @@ class DokoGenApp:
|
|||||||
self._collect_commission()
|
self._collect_commission()
|
||||||
self._collect_is()
|
self._collect_is()
|
||||||
self._collect_employees()
|
self._collect_employees()
|
||||||
self._collect_paper_documents()
|
|
||||||
try:
|
try:
|
||||||
os.makedirs(os.path.dirname(self._autosave_path), exist_ok=True)
|
os.makedirs(os.path.dirname(self._autosave_path), exist_ok=True)
|
||||||
with open(self._autosave_path, 'w', encoding='utf-8') as f:
|
with open(self._autosave_path, 'w', encoding='utf-8') as f:
|
||||||
|
|||||||
+30
-91
@@ -57,7 +57,8 @@ VARIABLE_DEFS = [
|
|||||||
('{{company_ogrn_date}}', 'A', 'Дата ОГРН', 'company:ogrn_date'),
|
('{{company_ogrn_date}}', 'A', 'Дата ОГРН', 'company:ogrn_date'),
|
||||||
('{{company_kpp}}', 'A', 'КПП', 'company:kpp'),
|
('{{company_kpp}}', 'A', 'КПП', 'company:kpp'),
|
||||||
('{{security_department}}', 'A', 'Наименование структурного подразделения по безопасности', 'company:security_department'),
|
('{{security_department}}', 'A', 'Наименование структурного подразделения по безопасности', 'company:security_department'),
|
||||||
('{{item.pd_actions}}', 'B', 'Действия с ПДн (в цикле ИС)', 'item:pd_actions'),
|
('{{company_okved}}', 'A', 'Основной ОКВЭД', 'company:okved'),
|
||||||
|
('{{company_okved_extra}}', 'A', 'Дополнительные ОКВЭД', 'company:okved_extra'),
|
||||||
|
|
||||||
# === Комиссия ===
|
# === Комиссия ===
|
||||||
('{{commission_list}}', 'A', 'Список комиссии', 'commission_list'),
|
('{{commission_list}}', 'A', 'Список комиссии', 'commission_list'),
|
||||||
@@ -69,22 +70,15 @@ VARIABLE_DEFS = [
|
|||||||
('{{document_number}}', 'A', 'Номер документа', 'doc_number'),
|
('{{document_number}}', 'A', 'Номер документа', 'doc_number'),
|
||||||
|
|
||||||
# === 187-ФЗ ===
|
# === 187-ФЗ ===
|
||||||
('{{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'),
|
||||||
('{{name}}', 'B', 'Название ИС (алиас)', 'is:name'),
|
('{{name}}', 'B', 'Название объекта КИИ (алиас)', 'is:name'),
|
||||||
('{{description}}', 'B', 'Описание ИС (алиас)', 'is:description'),
|
('{{description}}', 'B', 'Описание объекта КИИ (алиас)', 'is:description'),
|
||||||
('{{category}}', 'B', 'Категория ИС (алиас)', 'is:category'),
|
('{{category}}', 'B', 'Категория значимости объекта КИИ (алиас)', 'is:category'),
|
||||||
('{{number}}', 'B', 'Номер (алиас)', 'doc_number'),
|
('{{number}}', 'B', 'Номер (алиас)', 'doc_number'),
|
||||||
('{{pd_count}}', 'B', 'Количество записей ПДн (алиас)', 'is:pd_count'),
|
('{{users}}', 'B', 'Пользователи ОКИИ (алиас)', 'is:users'),
|
||||||
('{{pd_list}}', 'B', 'Список ПДн (алиас)', 'is:pd_list'),
|
|
||||||
('{{pd_subjects}}', 'B', 'Субъекты ПДн (алиас)', 'is:pd_subjects'),
|
|
||||||
('{{threat_type}}', 'B', 'Тип угроз (алиас)', 'is:threat_type'),
|
|
||||||
('{{users}}', 'B', 'Пользователи ИС (алиас)', 'is:users'),
|
|
||||||
('{{defence_level}}', 'B', 'Уровень защищённости (алиас)', 'is:defence_level'),
|
|
||||||
('{{full_name_genitive}}', 'A', 'Полное наименование (род.п., алиас)', 'decl:full_name:gent'),
|
('{{full_name_genitive}}', 'A', 'Полное наименование (род.п., алиас)', 'decl:full_name:gent'),
|
||||||
('{{document}}', 'A', 'Название бумажного документа (алиас)', 'company:paper_documents'),
|
|
||||||
('{{storage}}', 'A', 'Место хранения (алиас)', 'company:paper_storage'),
|
|
||||||
('{{responsible_fio_accs}}', 'A', 'ФИО ответственного (вин.п.)', 'fio_acl:responsible'),
|
('{{responsible_fio_accs}}', 'A', 'ФИО ответственного (вин.п.)', 'fio_acl:responsible'),
|
||||||
('{{commission1}}', 'A', 'Должность члена комиссии 1', 'commission_member:0:position'),
|
('{{commission1}}', 'A', 'Должность члена комиссии 1', 'commission_member:0:position'),
|
||||||
('{{commission1_fio}}', 'A', 'ФИО члена комиссии 1', 'commission_member:0:fio'),
|
('{{commission1_fio}}', 'A', 'ФИО члена комиссии 1', 'commission_member:0:fio'),
|
||||||
@@ -221,13 +215,11 @@ def _add_is_indexed_vars(replacements: Dict[str, str], isys, idx: int):
|
|||||||
'is_address': getattr(isys, 'address', '') or '—',
|
'is_address': getattr(isys, 'address', '') or '—',
|
||||||
'is_description': isys.description or '—',
|
'is_description': isys.description or '—',
|
||||||
'is_has_internet': 'да' if isys.is_internet else 'нет',
|
'is_has_internet': 'да' if isys.is_internet else 'нет',
|
||||||
'is_defence_level': str(isys.defence_level or '—'),
|
'is_category': isys.category or '—',
|
||||||
'is_category': ', '.join(isys.personal_data_category) or isys.category or '—',
|
|
||||||
'is_pd_list': ', '.join(isys.personal_data_list or []) or '—',
|
|
||||||
'is_users': ', '.join(isys.users_list or []) or '—',
|
'is_users': ', '.join(isys.users_list or []) or '—',
|
||||||
'is_pd_count': str(isys.pd_count or '—'),
|
|
||||||
'is_purpose': isys.purpose or '—',
|
'is_purpose': isys.purpose or '—',
|
||||||
'is_structure': getattr(isys, 'structure', '') or '—',
|
'is_structure': getattr(isys, 'structure', '') or '—',
|
||||||
|
'is_software': getattr(isys, 'software', '') or '—',
|
||||||
}
|
}
|
||||||
for f, v in fields.items():
|
for f, v in fields.items():
|
||||||
replacements[f'{{{{{f}_{idx}}}}}'] = str(v)
|
replacements[f'{{{{{f}_{idx}}}}}'] = str(v)
|
||||||
@@ -295,68 +287,16 @@ def _build_loop_data(company: Company, is_list: List) -> Dict[str, List[Dict]]:
|
|||||||
'address': getattr(isys, 'address', '') or '—',
|
'address': getattr(isys, 'address', '') or '—',
|
||||||
'is_has_lan': 'Да' if isys.is_local_network else 'Нет',
|
'is_has_lan': 'Да' if isys.is_local_network else 'Нет',
|
||||||
'has_internet': 'да' if isys.is_internet else 'нет',
|
'has_internet': 'да' if isys.is_internet else 'нет',
|
||||||
'defence_level': str(isys.defence_level or '—'),
|
'category': isys.category or '—',
|
||||||
'threat_type': str(isys.threat_type or '3'),
|
|
||||||
'category': ', '.join(isys.personal_data_category) or isys.category or '—',
|
|
||||||
'pd_list': ', '.join(isys.personal_data_list or []) or '—',
|
|
||||||
'pd_count': str(isys.pd_count or '—'),
|
|
||||||
'users': ', '.join(isys.users_list or []) or '—',
|
'users': ', '.join(isys.users_list or []) or '—',
|
||||||
'users_loop': users_loop,
|
'users_loop': users_loop,
|
||||||
'defense_tools_list': ', '.join(isys.defense_tools_list or []) or '—',
|
'defense_tools_list': ', '.join(isys.defense_tools_list or []) or '—',
|
||||||
'processing_modes': isys.processing_modes or '—',
|
|
||||||
'pd_subjects': '; '.join(isys.pd_subjects_list or []) or '—',
|
|
||||||
'is_purpose': isys.purpose or '—',
|
'is_purpose': isys.purpose or '—',
|
||||||
'structure': getattr(isys, 'structure', '') or '—',
|
'structure': getattr(isys, 'structure', '') or '—',
|
||||||
'software': getattr(isys, 'software', '') or '—',
|
'software': getattr(isys, 'software', '') or '—',
|
||||||
'pd_actions': '; '.join(isys.pd_actions_list or []) or '—',
|
|
||||||
})
|
})
|
||||||
loops['information_systems'] = items
|
loops['information_systems'] = items
|
||||||
|
|
||||||
# Бумажные документы
|
|
||||||
def _storage_with_prep(storage: str) -> str:
|
|
||||||
"""«сейф» → «в сейфе» (предлог + предложный падеж).
|
|
||||||
Если уже начинается с предлога — оставить как есть."""
|
|
||||||
if not storage:
|
|
||||||
return ''
|
|
||||||
s = storage.strip()
|
|
||||||
low = s.lower()
|
|
||||||
if low.startswith(('в ', 'на ', 'под ', 'за ', 'у ', 'при ', 'из ', 'со ', 'во ', 'над ', 'перед ')):
|
|
||||||
return s
|
|
||||||
words = s.split()
|
|
||||||
out = []
|
|
||||||
# Исключения предложного падежа: «в шкафу», «в углу» (не «в шкафе»)
|
|
||||||
loct_exceptions = {'шкаф': 'шкафу', 'угол': 'углу', 'край': 'краю', 'рот': 'рту', 'мост': 'мосту'}
|
|
||||||
for w in words:
|
|
||||||
core = w.strip('.,;')
|
|
||||||
if not core or core.isdigit() or core.startswith('№'):
|
|
||||||
out.append(w)
|
|
||||||
continue
|
|
||||||
if core.lower() in loct_exceptions:
|
|
||||||
out.append(loct_exceptions[core.lower()])
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
p = dc.morph.parse(core)[0]
|
|
||||||
inf = p.inflect({'loct'})
|
|
||||||
if inf:
|
|
||||||
out.append(inf.word)
|
|
||||||
continue
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
out.append(w)
|
|
||||||
return 'в ' + ' '.join(out)
|
|
||||||
|
|
||||||
paper_list = getattr(company, 'paper_documents_list', None) or []
|
|
||||||
if paper_list:
|
|
||||||
items = []
|
|
||||||
for p in paper_list:
|
|
||||||
storage = p.storage or ''
|
|
||||||
items.append({
|
|
||||||
'document': p.name or '',
|
|
||||||
'storage': _storage_with_prep(storage), # «в сейфе»
|
|
||||||
'storage_raw': storage, # «сейф» (как введено)
|
|
||||||
})
|
|
||||||
loops['paper_documents'] = items
|
|
||||||
|
|
||||||
# Сотрудники с доступом
|
# Сотрудники с доступом
|
||||||
employees = getattr(company, 'employees_access', None) or []
|
employees = getattr(company, 'employees_access', None) or []
|
||||||
if employees:
|
if employees:
|
||||||
@@ -448,11 +388,6 @@ def _resolve(source: str, ctx: Dict) -> str:
|
|||||||
'name': 'name',
|
'name': 'name',
|
||||||
'description': 'description',
|
'description': 'description',
|
||||||
'category': 'category',
|
'category': 'category',
|
||||||
'defence_level': 'defence_level',
|
|
||||||
'threat_type': 'threat_type',
|
|
||||||
'pd_list': lambda: ', '.join(first.personal_data_list or []),
|
|
||||||
'pd_count': 'pd_count',
|
|
||||||
'pd_subjects': lambda: '; '.join(first.pd_subjects_list or []),
|
|
||||||
'users': lambda: ', '.join(first.users_list or []),
|
'users': lambda: ', '.join(first.users_list or []),
|
||||||
}
|
}
|
||||||
if field in field_map:
|
if field in field_map:
|
||||||
@@ -501,21 +436,25 @@ def _get_fio(company: Company, who: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
def _get_security_text(ctx: Dict) -> str:
|
def _get_security_text(ctx: Dict) -> str:
|
||||||
"""Возвращает текст требований для уровня защищённости."""
|
"""Возвращает текст требований к защите объекта КИИ по категории значимости."""
|
||||||
defence_level = ctx.get('extended_fields', {}).get('defence_level', '4')
|
|
||||||
is_list = ctx.get('is_list', [])
|
is_list = ctx.get('is_list', [])
|
||||||
if not defence_level and is_list:
|
category = ''
|
||||||
defence_level = str(getattr(is_list[0], 'defence_level', '4') or '4')
|
if is_list:
|
||||||
dl = str(defence_level).strip()[0]
|
category = str(getattr(is_list[0], 'category', '') or '').strip()
|
||||||
|
|
||||||
TEXTS = {
|
TEXTS = {
|
||||||
'1': """Для обеспечения 1-го уровня защищённости...""",
|
'1': """Для значимого объекта КИИ 1-й категории значимости необходимо:
|
||||||
'2': """Для обеспечения 2-го уровня защищённости...""",
|
— создание системы безопасности значимого объекта КИИ;
|
||||||
'3': """Для обеспечения 3-го уровня защищённости...""",
|
— обеспечение защиты информации от компьютерных атак;
|
||||||
'4': """Для обеспечения 4-го уровня защищённости персональных данных необходимо:
|
— категорирование объектов КИИ и контроль его результатов.""",
|
||||||
— режим безопасности помещений;
|
'2': """Для значимого объекта КИИ 2-й категории значимости необходимо:
|
||||||
— сохранность носителей ПДн;
|
— создание системы безопасности значимого объекта КИИ;
|
||||||
— утверждение перечня лиц с доступом к ПДн;
|
— обеспечение защиты информации от компьютерных атак.""",
|
||||||
— использование сертифицированных СЗИ.""",
|
'3': """Для значимого объекта КИИ 3-й категории значимости необходимо:
|
||||||
|
— обеспечение защиты информации от компьютерных атак.""",
|
||||||
}
|
}
|
||||||
return TEXTS.get(dl, TEXTS['4'])
|
if category in TEXTS:
|
||||||
|
return TEXTS[category]
|
||||||
|
return """Для объекта КИИ, не являющегося значимым, необходимо:
|
||||||
|
— принятие мер по защите информации, предусмотренных законодательством РФ о безопасности КИИ."""
|
||||||
|
|
||||||
|
|||||||
+32
-2
@@ -65,6 +65,17 @@ _ADDRESS_ABBR = {
|
|||||||
'СТ': 'ст.', 'Ш': 'ш.', 'Р-Н': 'р-н.', 'Б-Р': 'б-р.', 'МКР': 'мкр.',
|
'СТ': 'ст.', 'Ш': 'ш.', 'Р-Н': 'р-н.', 'Б-Р': 'б-р.', 'МКР': 'мкр.',
|
||||||
'НАБ': 'наб.', 'ТУП': 'туп.', 'ПР-Д': 'пр-д.', 'КРП': 'крп.',
|
'НАБ': 'наб.', 'ТУП': 'туп.', 'ПР-Д': 'пр-д.', 'КРП': 'крп.',
|
||||||
'Г.': 'г.', 'УЛ.': 'ул.', 'Д.': 'д.', 'Р-Н.': 'р-н.',
|
'Г.': 'г.', 'УЛ.': 'ул.', 'Д.': 'д.', 'Р-Н.': 'р-н.',
|
||||||
|
'Г.О': 'г.о.', 'Г.О.': 'г.о.', 'ГО': 'г.о.', 'М.О': 'м.о.', 'М.О.': 'м.о.',
|
||||||
|
}
|
||||||
|
|
||||||
|
# Слова, которые в адресах пишутся со строчной буквы (кроме случаев,
|
||||||
|
# когда стоят первым словом после точки — «г. Электроугли» и т.п.)
|
||||||
|
_ADDRESS_LOWERCASE = {
|
||||||
|
'область', 'край', 'республика', 'округ', 'район', 'город', 'поселок',
|
||||||
|
'посёлок', 'деревня', 'село', 'станица', 'хутор', 'аул', 'улица',
|
||||||
|
'проспект', 'переулок', 'бульвар', 'шоссе', 'набережная', 'площадь',
|
||||||
|
'проезд', 'тупик', 'аллея', 'линия', 'дом', 'строение', 'корпус',
|
||||||
|
'квартира', 'офис', 'помещение', 'этаж', 'комната', 'участок',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -105,7 +116,12 @@ def _normalize_text(text):
|
|||||||
|
|
||||||
|
|
||||||
def normalize_address(text):
|
def normalize_address(text):
|
||||||
"""«Г Крымск» → «г. Крымск», «Р-Н» → «р-н.» и т.п."""
|
"""«Г Крымск» → «г. Крымск», «Р-Н» → «р-н.», «МОСКОВСКАЯ ОБЛАСТЬ» → «Московская область».
|
||||||
|
|
||||||
|
Приводит адрес к нормальному регистру: каждое слово с заглавной буквы,
|
||||||
|
адресные сокращения (г., ул., д., обл., г.о.) и административные термины
|
||||||
|
(область, район, город, дом и т.п.) — со строчной.
|
||||||
|
"""
|
||||||
if not text:
|
if not text:
|
||||||
return ''
|
return ''
|
||||||
parts = re.split(r'(\s+|[,\;])', text)
|
parts = re.split(r'(\s+|[,\;])', text)
|
||||||
@@ -115,7 +131,21 @@ def normalize_address(text):
|
|||||||
out.append(_ADDRESS_ABBR[p.strip().upper()])
|
out.append(_ADDRESS_ABBR[p.strip().upper()])
|
||||||
else:
|
else:
|
||||||
out.append(p)
|
out.append(p)
|
||||||
return ''.join(out)
|
res = ''.join(out)
|
||||||
|
# КАПС → нормальный регистр по словам
|
||||||
|
res = _normalize_text(res)
|
||||||
|
# Адресные термины — со строчной буквы (сохраняя пунктуацию)
|
||||||
|
words = res.split(' ')
|
||||||
|
fixed = []
|
||||||
|
for w in words:
|
||||||
|
m = re.match(r'^([^\w]*)([\w\-]+)([^\w]*)$', w, re.UNICODE)
|
||||||
|
if m:
|
||||||
|
pre, core, post = m.group(1), m.group(2), m.group(3)
|
||||||
|
if core.lower() in _ADDRESS_LOWERCASE:
|
||||||
|
fixed.append(pre + core.lower() + post)
|
||||||
|
continue
|
||||||
|
fixed.append(w)
|
||||||
|
return ' '.join(fixed)
|
||||||
|
|
||||||
|
|
||||||
def _fmt_date(value):
|
def _fmt_date(value):
|
||||||
|
|||||||
Reference in New Issue
Block a user