commit 904bc48138b55394dba64ed7c1aa83b58bec7305
Author: Dmitry <124861781+ada-dmitry@users.noreply.github.com>
Date: Sun Dec 28 17:40:25 2025 +0300
Добавить начальную структуру проекта с основными файлами и зависимостями
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",
+ " Наименование вредных производственных факторов или фидов работ (приложение к приказу Минздрава РФ от 28.01.2021 №29Н) | \n",
+ " Примечания | \n",
+ "
\n",
+ " \n",
+ " \n",
+ " \n",
+ " | 0 | \n",
+ " 2026-02-02 | \n",
+ " 09:30 | \n",
+ " 1 | \n",
+ " Орлова Ксения Николаевна | \n",
+ " Доцент | \n",
+ " 17.07.1985 | \n",
+ " кафедра радиационной физики и безопасности ато... | \n",
+ " 4.1 | \n",
+ " NaN | \n",
+ "
\n",
+ " \n",
+ " | 1 | \n",
+ " 2026-02-02 | \n",
+ " 09:35 | \n",
+ " 2 | \n",
+ " Камнев Владимир Александрович | \n",
+ " Специалист по учебно-методической работе 1 кат... | \n",
+ " 24.01.1955 | \n",
+ " кафедра радиационной физики и безопасности ато... | \n",
+ " 4.1 | \n",
+ " NaN | \n",
+ "
\n",
+ " \n",
+ " | 2 | \n",
+ " 2026-02-02 | \n",
+ " 09:40 | \n",
+ " 3 | \n",
+ " Стогов Юрий Владимирович | \n",
+ " Доцент | \n",
+ " 05.01.1959 | \n",
+ " кафедра теоретической и экспериментальной физи... | \n",
+ " 4.1 | \n",
+ " NaN | \n",
+ "
\n",
+ " \n",
+ " | 3 | \n",
+ " 2026-02-02 | \n",
+ " 09:45 | \n",
+ " 4 | \n",
+ " Алферов Владимир Петрович | \n",
+ " Заместитель главного инженера исследовательско... | \n",
+ " 08.11.1948 | \n",
+ " исследовательский реактор типовой атомного центра | \n",
+ " 4.1 | \n",
+ " NaN | \n",
+ "
\n",
+ " \n",
+ " | 4 | \n",
+ " 2026-02-02 | \n",
+ " 09:50 | \n",
+ " 5 | \n",
+ " Савостиков Сергей Алексеевич | \n",
+ " Слесарь по ремонту и обслуживанию систем венти... | \n",
+ " 20.05.1988 | \n",
+ " теплоэнергетический отдел службы главного инже... | \n",
+ " 5.1, 6.2 | \n",
+ " NaN | \n",
+ "
\n",
+ " \n",
+ "
\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 = "." }