From 904bc48138b55394dba64ed7c1aa83b58bec7305 Mon Sep 17 00:00:00 2001 From: Dmitry <124861781+ada-dmitry@users.noreply.github.com> Date: Sun, 28 Dec 2025 17:40:25 +0300 Subject: [PATCH] =?UTF-8?q?=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=B8=D1=82?= =?UTF-8?q?=D1=8C=20=D0=BD=D0=B0=D1=87=D0=B0=D0=BB=D1=8C=D0=BD=D1=83=D1=8E?= =?UTF-8?q?=20=D1=81=D1=82=D1=80=D1=83=D0=BA=D1=82=D1=83=D1=80=D1=83=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B5=D0=BA=D1=82=D0=B0=20=D1=81=20=D0=BE?= =?UTF-8?q?=D1=81=D0=BD=D0=BE=D0=B2=D0=BD=D1=8B=D0=BC=D0=B8=20=D1=84=D0=B0?= =?UTF-8?q?=D0=B9=D0=BB=D0=B0=D0=BC=D0=B8=20=D0=B8=20=D0=B7=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D1=81=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D1=8F=D0=BC=D0=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 13 ++ .python-version | 1 + README.md | 0 main.ipynb | 311 +++++++++++++++++++++++++++++++++++++++++++++++ main.py | 54 ++++++++ pyproject.toml | 7 ++ requirements.txt | 4 + uv.lock | 8 ++ 8 files changed, 398 insertions(+) create mode 100644 .gitignore create mode 100644 .python-version create mode 100644 README.md create mode 100644 main.ipynb create mode 100644 main.py create mode 100644 pyproject.toml create mode 100644 requirements.txt create mode 100644 uv.lock diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..90c83cf --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +# Python-generated files +__pycache__/ +*.py[oc] +build/ +dist/ +wheels/ +*.egg-info + +# Virtual environments +.venv + +# Files +files/ \ No newline at end of file diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..24ee5b1 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.13 diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/main.ipynb b/main.ipynb new file mode 100644 index 0000000..d643c79 --- /dev/null +++ b/main.ipynb @@ -0,0 +1,311 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 70, + "id": "c7f0feca", + "metadata": {}, + "outputs": [], + "source": [ + "import pandas as pd\n", + "import openpyxl\n", + "import os\n", + "from tqdm import tqdm\n", + "from datetime import datetime" + ] + }, + { + "cell_type": "code", + "execution_count": 71, + "id": "c3d2a034", + "metadata": {}, + "outputs": [], + "source": [ + "DB_FILE = 'files/source.xlsx'\n", + "TEMPLATE_FILE = 'files/template.xlsx'\n", + "OUTPUT_FOLDER = 'files/output/'\n", + "\n", + "\n", + "MAPPING = {\n", + " 'Дата МО' : 'Q1',\n", + " 'Время МО' : 'Q2',\n", + " 'Номер направления': 'A13',\n", + " 'Фамилия, имя, отчество': 'D21',\n", + " 'Дата рождения': 'D22',\n", + " 'Структурное подразделение': 'F26',\n", + " 'Должность работника ': 'F27',\n", + " '''Наименование вредных производственных факторов или фидов работ (приложение к приказу Минздрава РФ от 28.01.2021 №29Н)''': 'F30'\n", + "}\n", + "\n", + "if not os.path.exists(OUTPUT_FOLDER):\n", + " os.makedirs(OUTPUT_FOLDER)" + ] + }, + { + "cell_type": "code", + "execution_count": 72, + "id": "753709e8", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Всего сотрудников в списке: 1482\n" + ] + }, + { + "data": { + "text/plain": [ + "Index(['Дата МО', 'Время МО', 'Номер направления', 'Фамилия, имя, отчество',\n", + " 'Должность работника ', 'Дата рождения', 'Структурное подразделение',\n", + " 'Наименование вредных производственных факторов или фидов работ (приложение к приказу Минздрава РФ от 28.01.2021 №29Н)',\n", + " 'Примечания'],\n", + " dtype='object')" + ] + }, + "execution_count": 72, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df = pd.read_excel(DB_FILE)\n", + "print(f\"Всего сотрудников в списке: {len(df)}\")\n", + "# print(df.columns.tolist())\n", + "\n", + "df.columns" + ] + }, + { + "cell_type": "code", + "execution_count": 73, + "id": "dca336b7", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
Дата МОВремя МОНомер направленияФамилия, имя, отчествоДолжность работникаДата рожденияСтруктурное подразделениеНаименование вредных производственных факторов или фидов работ (приложение к приказу Минздрава РФ от 28.01.2021 №29Н)Примечания
02026-02-0209:301Орлова Ксения НиколаевнаДоцент17.07.1985кафедра радиационной физики и безопасности ато...4.1NaN
12026-02-0209:352Камнев Владимир АлександровичСпециалист по учебно-методической работе 1 кат...24.01.1955кафедра радиационной физики и безопасности ато...4.1NaN
22026-02-0209:403Стогов Юрий ВладимировичДоцент05.01.1959кафедра теоретической и экспериментальной физи...4.1NaN
32026-02-0209:454Алферов Владимир ПетровичЗаместитель главного инженера исследовательско...08.11.1948исследовательский реактор типовой атомного центра4.1NaN
42026-02-0209:505Савостиков Сергей АлексеевичСлесарь по ремонту и обслуживанию систем венти...20.05.1988теплоэнергетический отдел службы главного инже...5.1, 6.2NaN
\n", + "
" + ], + "text/plain": [ + " Дата МО Время МО Номер направления Фамилия, имя, отчество \\\n", + "0 2026-02-02 09:30 1 Орлова Ксения Николаевна \n", + "1 2026-02-02 09:35 2 Камнев Владимир Александрович \n", + "2 2026-02-02 09:40 3 Стогов Юрий Владимирович \n", + "3 2026-02-02 09:45 4 Алферов Владимир Петрович \n", + "4 2026-02-02 09:50 5 Савостиков Сергей Алексеевич \n", + "\n", + " Должность работника Дата рождения \\\n", + "0 Доцент 17.07.1985 \n", + "1 Специалист по учебно-методической работе 1 кат... 24.01.1955 \n", + "2 Доцент 05.01.1959 \n", + "3 Заместитель главного инженера исследовательско... 08.11.1948 \n", + "4 Слесарь по ремонту и обслуживанию систем венти... 20.05.1988 \n", + "\n", + " Структурное подразделение \\\n", + "0 кафедра радиационной физики и безопасности ато... \n", + "1 кафедра радиационной физики и безопасности ато... \n", + "2 кафедра теоретической и экспериментальной физи... \n", + "3 исследовательский реактор типовой атомного центра \n", + "4 теплоэнергетический отдел службы главного инже... \n", + "\n", + " Наименование вредных производственных факторов или фидов работ (приложение к приказу Минздрава РФ от 28.01.2021 №29Н) \\\n", + "0 4.1 \n", + "1 4.1 \n", + "2 4.1 \n", + "3 4.1 \n", + "4 5.1, 6.2 \n", + "\n", + " Примечания \n", + "0 NaN \n", + "1 NaN \n", + "2 NaN \n", + "3 NaN \n", + "4 NaN " + ] + }, + "execution_count": 73, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "df.head()" + ] + }, + { + "cell_type": "code", + "execution_count": 74, + "id": "94ab472b", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "100%|██████████| 1482/1482 [00:54<00:00, 27.41it/s]" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + "Файлы сохранены в 'files/output/'\n" + ] + }, + { + "name": "stderr", + "output_type": "stream", + "text": [ + "\n" + ] + } + ], + "source": [ + "for index, row in tqdm(df.iterrows(), total=len(df)):\n", + " wb = openpyxl.load_workbook(TEMPLATE_FILE)\n", + " ws = wb.active\n", + "\n", + " # Заполняем поля по словарю MAPPING\n", + " for excel_col, cell_address in MAPPING.items():\n", + " value = row.get(excel_col, \"\")\n", + "\n", + " if pd.isna(value):\n", + " value = \"\"\n", + "\n", + " if cell_address == 'A13':\n", + " ws[cell_address] = f\"НАПРАВЛЕНИЕ № {value}\"\n", + "\n", + " elif isinstance(value, (datetime, pd.Timestamp)):\n", + " ws[cell_address] = value.strftime('%d.%m.%Y')\n", + "\n", + " else:\n", + " ws[cell_address] = str(value)\n", + "\n", + " num = str(row.get('Номер направления', index + 1))\n", + " fio = str(row.get('Фамилия, имя, отчество')).strip().replace(' ', '_')\n", + " file_name = f\"Направление_№{num}_{fio}.xlsx\"\n", + "\n", + " save_path = os.path.join(OUTPUT_FOLDER, file_name)\n", + " wb.save(save_path)\n", + "\n", + "print(f\"\\nФайлы сохранены в '{OUTPUT_FOLDER}'\")" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "med-check-generator", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/main.py b/main.py new file mode 100644 index 0000000..8bf69ae --- /dev/null +++ b/main.py @@ -0,0 +1,54 @@ +import pandas as pd +import openpyxl +import os +from tqdm import tqdm +from datetime import datetime + +DB_FILE = 'files/source.xlsx' +TEMPLATE_FILE = 'files/template.xlsx' +OUTPUT_FOLDER = 'files/output/' + +MAPPING = { + 'Дата МО': 'Q1', + 'Время МО': 'Q2', + 'Номер направления': 'A13', + 'Фамилия, имя, отчество': 'D21', + 'Дата рождения': 'D22', + 'Структурное подразделение': 'F26', + 'Должность работника ': 'F27', + '''Наименование вредных производственных факторов или фидов работ (приложение к приказу Минздрава РФ от 28.01.2021 №29Н)''': 'F30' +} + +if not os.path.exists(OUTPUT_FOLDER): + os.makedirs(OUTPUT_FOLDER) + +df = pd.read_excel(DB_FILE) +print(f"Всего сотрудников в списке: {len(df)}") + +for index, row in tqdm(df.iterrows(), total=len(df), desc="Обработка"): + wb = openpyxl.load_workbook(TEMPLATE_FILE) + ws = wb.active + + for excel_col, cell_address in MAPPING.items(): + value = row.get(excel_col, "") + + if pd.isna(value): + value = "" + + if cell_address == 'A13': + ws[cell_address] = f"НАПРАВЛЕНИЕ № {value}" # type: ignore + + elif isinstance(value, (datetime, pd.Timestamp)): + ws[cell_address] = value.strftime('%d.%m.%Y') # type: ignore + + else: + ws[cell_address] = str(value) # type: ignore + + num = str(row.get('Номер направления', index + 1)) # type: ignore + fio = str(row.get('Фамилия, имя, отчество')).strip().replace(' ', '_') + file_name = f"Направление_№{num}_{fio}.xlsx" + + save_path = os.path.join(OUTPUT_FOLDER, file_name) + wb.save(save_path) + +print(f"\nГотово! Файлы сохранены в '{OUTPUT_FOLDER}'") diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..4329753 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,7 @@ +[project] +name = "med-check-generator" +version = "0.1.0" +description = "Add your description here" +readme = "README.md" +requires-python = ">=3.13" +dependencies = [] diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..d66de00 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,4 @@ +pandas +docxtpl +openpyxl +tqdm \ No newline at end of file diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000..4fe04a4 --- /dev/null +++ b/uv.lock @@ -0,0 +1,8 @@ +version = 1 +revision = 3 +requires-python = ">=3.13" + +[[package]] +name = "med-check-generator" +version = "0.1.0" +source = { virtual = "." }