Загрузка данных
import difflib
import os
import re
import openpyxl
from openpyxl.utils import get_column_letter
# ================= ФАЙЛЫ =================
FILE_MAIN = "result_perfect.xlsx"
FILE_NAGRUZKA = "Svodnaya_uchebnaya_nagruzka_12_09_2026_-_krasnym_izmenenia.xlsx"
FILE_PODRAZDEL = "Подразделения.xlsx"
FILE_OUTPUT = "result_ready.xlsx" # Имя готового файла
# =========================================
def resolve_file(filename, keyword=None):
"""Находит файл в папке, даже если в названии дважды написано .xlsx"""
if os.path.exists(filename):
return filename
if os.path.exists(filename + ".xlsx"):
return filename + ".xlsx"
if filename.endswith(".xlsx.xlsx") and os.path.exists(filename[:-5]):
return filename[:-5]
if keyword:
for f in os.listdir("."):
if keyword.lower() in f.lower() and f.endswith(".xlsx") and not f.startswith("~$"):
return f
return filename
def clean_str(val):
if val is None:
return ""
s = str(val).replace('"', "").replace("'", "").replace("«", "").replace("»", "")
s = s.lower().replace("ё", "е")
s = re.sub(r"\s+", " ", s)
return s.strip()
# 1. Читаем Подразделения (Факультеты/Кафедры -> Шифр)
path_podr = resolve_file(FILE_PODRAZDEL, "подразделения")
print(f"1. Читаем Подразделения: {path_podr}")
wb_podr = openpyxl.load_workbook(path_podr, data_only=True)
kaf_to_shifr = {}
for sheet in wb_podr.worksheets:
for row in sheet.iter_rows(values_only=True):
if len(row) >= 3 and row[1] is not None and row[2] is not None:
shifr = str(row[1]).strip()
kaf_full = clean_str(row[2])
if kaf_full and kaf_full != "факультеты/кафедры" and shifr.lower() != "шифр":
kaf_to_shifr[kaf_full] = shifr
print(f" Найдено шифров кафедр: {len(kaf_to_shifr)}")
# 2. Читаем Сводную нагрузку (Дисциплина -> Кафедра)
path_nagr = resolve_file(FILE_NAGRUZKA, "svodnaya")
print(f"2. Читаем Сводную нагрузку: {path_nagr}")
wb_nagr = openpyxl.load_workbook(path_nagr, data_only=True)
subj_to_kaf = {}
for sheet in wb_nagr.worksheets:
for row in sheet.iter_rows(values_only=True):
if len(row) >= 3 and row[1] is not None and row[2] is not None:
kaf_name = clean_str(row[1])
subj_name = clean_str(row[2])
if subj_name and subj_name != "дисциплина" and kaf_name != "кафедра":
subj_to_kaf[subj_name] = kaf_name
print(f" Найдено дисциплин: {len(subj_to_kaf)}")
# 3. Открываем основную таблицу и АВТОМАТИЧЕСКИ находим нужные столбцы
path_main = resolve_file(FILE_MAIN, "result")
print(f"3. Открываем основную таблицу: {path_main}")
wb_main = openpyxl.load_workbook(path_main)
ws_main = wb_main.active
col_fio = None
col_kaf = None
col_subj = None
# Известные фамилии со скрина для 100% точного поиска колонки ФИО
known_surnames = ("пулькова", "шалина", "шадрин", "филонов", "филиппович", "салихова", "бригида", "гулина", "ефремов")
subj_matches_per_col = {}
fio_matches_per_col = {}
kaf_matches_per_col = {}
for col_idx in range(1, ws_main.max_column + 1):
subj_matches = 0
fio_matches = 0
kaf_matches = 0
for row_idx in range(1, min(ws_main.max_row + 1, 300)):
val = clean_str(ws_main.cell(row=row_idx, column=col_idx).value)
if not val:
continue
if val in subj_to_kaf:
subj_matches += 1
if any(val.startswith(sur) for sur in known_surnames):
fio_matches += 1
if val == "цпд" or val.upper() in kaf_to_shifr.values():
kaf_matches += 1
subj_matches_per_col[col_idx] = subj_matches
fio_matches_per_col[col_idx] = fio_matches
kaf_matches_per_col[col_idx] = kaf_matches
col_fio = max(fio_matches_per_col, key=fio_matches_per_col.get)
# Колонка кафедры — та, где написано ЦПД (обычно сразу справа от ФИО)
if kaf_matches_per_col.get(col_fio + 1, 0) > 0:
col_kaf = col_fio + 1
else:
col_kaf = max(kaf_matches_per_col, key=kaf_matches_per_col.get)
# Исключаем колонки ФИО и Кафедры при поиске колонки Предмета
subj_matches_per_col[col_fio] = -1
subj_matches_per_col[col_kaf] = -1
col_subj = max(subj_matches_per_col, key=subj_matches_per_col.get)
print(
f" Автоопределение колонок: Предмет = {get_column_letter(col_subj)} ({col_subj}), "
f"ФИО = {get_column_letter(col_fio)} ({col_fio}), "
f"Кафедра = {get_column_letter(col_kaf)} ({col_kaf})"
)
# Предварительно связываем названия кафедр из нагрузки с шифрами из подразделений
all_podr_names = list(kaf_to_shifr.keys())
def get_shifr_by_kaf_name(kaf_name):
if not kaf_name:
return None
if kaf_name in kaf_to_shifr:
return kaf_to_shifr[kaf_name]
for k_podr, s_podr in kaf_to_shifr.items():
if kaf_name in k_podr or k_podr in kaf_name:
return s_podr
close = difflib.get_close_matches(kaf_name, all_podr_names, n=1, cutoff=0.75)
if close:
return kaf_to_shifr[close[0]]
return None
filled_kaf = 0
updated_fio = 0
for row_idx in range(1, ws_main.max_row + 1):
cell_subj = ws_main.cell(row=row_idx, column=col_subj)
cell_fio = ws_main.cell(row=row_idx, column=col_fio)
cell_kaf = ws_main.cell(row=row_idx, column=col_kaf)
subj_clean = clean_str(cell_subj.value)
fio_raw = str(cell_fio.value).strip() if cell_fio.value is not None else ""
kaf_raw = str(cell_kaf.value).strip() if cell_kaf.value is not None else ""
# Пропускаем строку шапки, если она есть
if subj_clean in ("дисциплина", "предмет", "наименование дисциплины") or fio_raw.lower() in ("фио", "преподаватель"):
continue
# ШАГ 1: Если кафедра пустая — ищем её по предмету и дозаполняем
if not kaf_raw and subj_clean:
kaf_full = subj_to_kaf.get(subj_clean)
shifr = get_shifr_by_kaf_name(kaf_full)
if shifr:
kaf_raw = shifr
cell_kaf.value = shifr
filled_kaf += 1
# ШАГ 2: Если есть ФИО и есть кафедра (старая или только что заполненная) — добавляем (кафедра) к ФИО
if fio_raw and kaf_raw and fio_raw.lower() not in ("вакансия", "нет", "-"):
# Убираем старые скобки на конце, если вдруг они там уже были от прошлых запусков, чтобы не было ФИО(ЦПД)(ЦПД)
fio_base = re.sub(r"\s*\([^)]*\)\s*$", "", fio_raw).strip()
cell_fio.value = f"{fio_base}({kaf_raw})"
updated_fio += 1
wb_main.save(FILE_OUTPUT)
print(f"\nГотово! Сохранено в файл: {FILE_OUTPUT}")
print(f"— Дозаполнено пустых кафедр: {filled_kaf}")
print(f"— Обновлено ФИО со скобками: {updated_fio}")