Загрузка данных
import os
import re
import glob
import pandas as pd
# ================= НАСТРОЙКИ ПУТЕЙ =================
MAIN_FILE = r"result_updated.xlsx" # Ваша готовая таблица (куда больше не надо добавлять строки)
REF_FILE = r"справочник_групп.xlsx" # НОВАЯ таблица (где группа, уровень, форма, факультет)
DUMP_FOLDER = r"C:\Users\n.o.ishanhodzhaeva\Desktop\Выгрузка по кафедрам\zv" # Папка с 40+ файлами
OUTPUT_FILE = r"result_perfect.xlsx" # Имя итогового файла
# ===================================================
def find_col(columns, keywords):
"""Находит столбец по ключевым словам"""
for col in columns:
col_low = str(col).lower().strip()
if any(k in col_low for k in keywords):
return col
return None
def clean_val(val):
"""Очищает значение от лишних пробелов и пустых 'nan'"""
if pd.isna(val):
return ""
s = re.sub(r'\s+', ' ', str(val)).strip()
return "" if s.lower() in ['nan', 'none', 'null'] else s
def clean_teacher_name(name):
"""Убирает старые скобки с кафедрой из конца ФИО, чтобы не задвоить: 'Иванов И.И. (ИМП)' -> 'Иванов И.И.'"""
name = clean_val(name)
return re.sub(r'\s*\([^)]+\)\s*$', '', name).strip()
def make_abbreviation(full_dept_name, known_abbrevs):
"""Превращает полное название кафедры в аббревиатуру или находит её в базе ваших 4000 строк"""
dept = clean_val(full_dept_name)
if not dept:
return ""
# Убираем слово "кафедра" в начале, если оно есть
dept_clean = re.sub(r'^(кафедра|каф\.?)\s+', '', dept, flags=re.IGNORECASE).strip()
# 1. Если это уже готовая аббревиатура из ваших 4к строк (без учета регистра)
for k_low, k_orig in known_abbrevs.items():
if dept_clean.lower() == k_low:
return k_orig
# 2. Если название короткое (до 15 символов, 1-2 слова) — возвращаем как есть
if len(dept_clean) <= 15 and len(dept_clean.split()) <= 2:
return dept_clean
# 3. Если это длинное название из нескольких слов — собираем аббревиатуру по первым буквам
stop_words = {'и', 'в', 'на', 'с', 'по', 'для', 'от', 'при', 'о', 'у', 'за', 'над', 'под'}
words = re.findall(r'[А-Яа-яЁёA-Za-z0-9]+', dept_clean)
abbr_letters = []
for w in words:
if w.lower() in stop_words:
abbr_letters.append(w.lower())
else:
abbr_letters.append(w[0].upper())
generated = "".join(abbr_letters)
# Проверяем, есть ли похожая аббревиатура в ваших 4000 строках
if generated.lower() in known_abbrevs:
return known_abbrevs[generated.lower()]
return generated
# --- ШАГ 1. Загружаем вашу готовую таблицу ---
print("1. Загрузка вашей основной таблицы...")
all_sheets = pd.read_excel(MAIN_FILE, sheet_name=None)
target_sheet = list(all_sheets.keys())[0]
df_main = all_sheets[target_sheet].copy()
df_main.columns = df_main.columns.astype(str).str.strip()
c_inst = find_col(df_main.columns, ['факультет', 'институт'])
c_grp = find_col(df_main.columns, ['групп'])
c_form = find_col(df_main.columns, ['форма'])
c_lvl = find_col(df_main.columns, ['уровень', 'подготовк'])
c_subj = find_col(df_main.columns, ['наименование', 'дисциплин', 'предмет'])
c_teach = find_col(df_main.columns, ['преподавател', 'фио', 'ппс'])
c_dept = find_col(df_main.columns, ['кафедр'])
# Собираем эталонные аббревиатуры кафедр и связку (Преподаватель -> Кафедра) из ваших 4000 строк
known_abbrevs = {} # {нижний_регистр: ПравильноеНаписание}
teacher_to_dept = {} # {чистое_фио_нижний_регистр: Кафедра}
for _, row in df_main.iterrows():
dept_val = clean_val(row[c_dept]) if c_dept else ""
teach_val = clean_val(row[c_teach]) if c_teach else ""
# Если в ФИО уже есть (Кафедра), вытаскиваем её тоже как эталон
match = re.search(r'\(([^)]+)\)\s*$', teach_val)
dept_in_brackets = match.group(1).strip() if match else ""
actual_dept = dept_val or dept_in_brackets
if actual_dept:
known_abbrevs[actual_dept.lower()] = actual_dept
if teach_val:
clean_t = clean_teacher_name(teach_val).lower()
if clean_t:
teacher_to_dept[clean_t] = actual_dept
# --- ШАГ 2. Читаем НОВУЮ таблицу для сверки групп (Факультет, Форма, Уровень) ---
print("2. Чтение новой таблицы-справочника групп...")
ref_dict = {} # {номер_группы: {'inst': ..., 'form': ..., 'lvl': ...}}
if os.path.exists(REF_FILE):
ref_sheets = pd.read_excel(REF_FILE, sheet_name=None)
for _, r_df in ref_sheets.items():
if r_df.empty:
continue
r_df.columns = r_df.columns.astype(str).str.strip()
r_grp = find_col(r_df.columns, ['групп'])
r_inst = find_col(r_df.columns, ['факультет', 'институт'])
r_form = find_col(r_df.columns, ['форма'])
r_lvl = find_col(r_df.columns, ['уровень', 'подготовк'])
if not r_grp:
continue
for _, r_row in r_df.iterrows():
# На случай если в справочнике группы тоже через "|"
raw_g = clean_val(r_row[r_grp])
for g in raw_g.split('|'):
g = g.strip()
if not g:
continue
ref_dict[g] = {
'inst': clean_val(r_row[r_inst]) if r_inst else "",
'form': clean_val(r_row[r_form]) if r_form else "",
'lvl': clean_val(r_row[r_lvl]) if r_lvl else ""
}
print(f" Загружено уникальных групп для сверки: {len(ref_dict)}")
else:
print(f" [ВНИМАНИЕ] Файл справочника '{REF_FILE}' не найден! Проверьте имя файла.")
# --- ШАГ 3. Сканируем 40+ файлов выгрузки для поиска кафедр ---
print("3. Поиск аббревиатур кафедр в 40+ файлах выгрузки...")
dump_files = glob.glob(os.path.join(DUMP_FOLDER, '*.xls*'))
# База из выгрузки: {(группа, предмет, фио): кафедра} и {(предмет, фио): кафедра}
dump_dept_map = {}
for fpath in dump_files:
fname = os.path.basename(fpath)
if '~$' in fname or os.path.abspath(fpath) in [os.path.abspath(MAIN_FILE), os.path.abspath(REF_FILE)]:
continue
# Часто название файла выгрузки само содержит аббревиатуру кафедры (например, "Нагрузка_ИМП.xlsx")
file_stem = os.path.splitext(fname)[0]
file_abbr_candidate = ""
for k_low, k_orig in known_abbrevs.items():
# Ищем точное совпадение аббревиатуры в имени файла
if re.search(r'(^|[^а-яa-z0-9])' + re.escape(k_low) + r'($|[^а-яa-z0-9])', file_stem.lower()):
file_abbr_candidate = k_orig
break
try:
xls = pd.read_excel(fpath, sheet_name=None, header=None)
for s_name, raw_df in xls.items():
if raw_df.empty:
continue
# Проверяем первые строки над таблицей (там часто пишут "Кафедра: ...")
sheet_dept = file_abbr_candidate
header_idx = None
for idx, row in raw_df.head(20).iterrows():
row_text = ' '.join(row.dropna().astype(str)).strip()
row_low = row_text.lower()
# Если в шапке листа написана кафедра
if 'кафедр' in row_low and 'групп' not in row_low:
m = re.search(r'кафедр[аы]?\s*[:\-]?\s*([А-Яа-яЁёA-Za-z0-9\s\-]+)', row_text, re.IGNORECASE)
if m and not sheet_dept:
sheet_dept = make_abbreviation(m.group(1), known_abbrevs)
if 'групп' in row_low and any(w in row_low for w in ['дисциплин', 'предмет', 'наименование']):
header_idx = idx
break
if header_idx is None:
continue
df_s = raw_df.iloc[header_idx + 1:].copy()
df_s.columns = raw_df.iloc[header_idx].astype(str).str.strip()
s_grp = find_col(df_s.columns, ['групп'])
s_subj = find_col(df_s.columns, ['наименование', 'дисциплин', 'предмет'])
s_teach = find_col(df_s.columns, ['преподавател', 'фио', 'ппс'])
s_dept = find_col(df_s.columns, ['кафедр'])
for _, d_row in df_s.iterrows():
t_raw = clean_val(d_row[s_teach]) if s_teach else ""
s_raw = clean_val(d_row[s_subj]).lower() if s_subj else ""
g_raw = clean_val(d_row[s_grp]) if s_grp else ""
# Ищем кафедру: 1) в столбце Кафедра, 2) в скобках у ФИО, 3) из шапки листа/имени файла
d_val = clean_val(d_row[s_dept]) if s_dept else ""
if not d_val and '(' in t_raw and ')' in t_raw:
m = re.search(r'\(([^)]+)\)\s*$', t_raw)
if m:
d_val = m.group(1).strip()
if not d_val:
d_val = sheet_dept
abbr = make_abbreviation(d_val, known_abbrevs)
if not abbr:
continue
t_clean = clean_teacher_name(t_raw).lower()
if t_clean and t_clean not in teacher_to_dept:
teacher_to_dept[t_clean] = abbr
if s_raw and t_clean:
dump_dept_map[(s_raw, t_clean)] = abbr
for g_item in g_raw.split('|'):
g_clean = g_item.strip().lower()
if g_clean and s_raw:
dump_dept_map[(g_clean, s_raw, t_clean)] = abbr
except Exception as e:
pass
# --- ШАГ 4. Сверяем и обновляем вашу основную таблицу (БЕЗ добавления строк) ---
print("4. Сверка и исправление данных в вашей таблице...")
code_map = {
'1': ('Очная', 'Бакалавриат'),
'2': ('Очно-заочная', 'Бакалавриат'),
'3': ('Заочная', 'Бакалавриат'),
'4': ('Очная', 'Магистратура'),
'5': ('Очно-заочная', 'Магистратура'),
'6': ('Заочная', 'Магистратура')
}
updated_groups_count = 0
updated_depts_count = 0
def update_main_row(row):
global updated_groups_count, updated_depts_count
grp = clean_val(row[c_grp]) if c_grp else ""
subj = clean_val(row[c_subj]).lower() if c_subj else ""
teach_raw = clean_val(row[c_teach]) if c_teach else ""
dept_raw = clean_val(row[c_dept]) if c_dept else ""
# --- А. СВЕРКА ФАКУЛЬТЕТА, ФОРМЫ И УРОВНЯ ПО НОВОЙ ТАБЛИЦЕ ---
if grp in ref_dict:
ref_info = ref_dict[grp]
if c_inst and ref_info['inst'] and clean_val(row[c_inst]) != ref_info['inst']:
row[c_inst] = ref_info['inst']
updated_groups_count += 1
if c_form and ref_info['form'] and clean_val(row[c_form]) != ref_info['form']:
row[c_form] = ref_info['form']
if c_lvl and ref_info['lvl'] and clean_val(row[c_lvl]) != ref_info['lvl']:
row[c_lvl] = ref_info['lvl']
else:
# Если группы вдруг нет в справочнике — страхуем проверкой по 3-й цифре
prefix = grp.split('-')[0]
if len(prefix) >= 3 and prefix[:3].isdigit():
c_digit = prefix[2]
if c_digit in code_map:
if c_form: row[c_form] = code_map[c_digit][0]
if c_lvl: row[c_lvl] = code_map[c_digit][1]
# Жесткие правила для ИГРИК -> ФГРИК и ПШ ИД
if c_inst:
if clean_val(row[c_inst]) == 'ИГРИК':
row[c_inst] = 'ФГРИК'
if grp in ['261-1011', '264-1011']:
row[c_inst] = 'ПШ ИД'
# Особое правило для физкультуры: кафедра всегда ФВ (если на 1-м фото у вас "(ФВ)")
# --- Б. ОПРЕДЕЛЕНИЕ АББРЕВИАТУРЫ КАФЕДРЫ И ЗАПИСЬ В СКОБКАХ У ПРЕПОДАВАТЕЛЯ ---
t_base = clean_teacher_name(teach_raw)
t_low = t_base.lower()
# Проверяем, есть ли уже скобки в ФИО
m_br = re.search(r'\(([^)]+)\)\s*$', teach_raw)
dept_from_br = m_br.group(1).strip() if m_br else ""
# Ищем правильную аббревиатуру по приоритету:
# 1) Уже стоит в столбце Кафедра или в скобках у ФИО
# 2) Найдена в файлах выгрузки по (Группа + Предмет + ФИО) или (Предмет + ФИО)
# 3) Найдена в базе преподавателей из ваших 4000 строк
final_dept = dept_raw or dept_from_br
if not final_dept:
final_dept = (
dump_dept_map.get((grp.lower(), subj, t_low)) or
dump_dept_map.get((subj, t_low)) or
teacher_to_dept.get(t_low, "")
)
if not final_dept and 'физическая культура' in subj:
final_dept = 'ФВ' # На вашем 1-м фото у преподавателей физкультуры стоит (ФВ)
final_dept = make_abbreviation(final_dept, known_abbrevs)
# Записываем кафедру в столбец "Кафедра"
if c_dept and final_dept:
if clean_val(row[c_dept]) != final_dept:
updated_depts_count += 1
row[c_dept] = final_dept
# Записываем ФИО в формате: "Фамилия Имя Отчество (Кафедра)"
if c_teach and t_base:
if final_dept:
row[c_teach] = f"{t_base} ({final_dept})"
else:
row[c_teach] = t_base
return row
df_main = df_main.apply(update_main_row, axis=1)
# --- ШАГ 5. Сохранение результата ---
all_sheets[target_sheet] = df_main
with pd.ExcelWriter(OUTPUT_FILE, engine='openpyxl') as writer:
for s_name, s_df in all_sheets.items():
s_df.to_excel(writer, sheet_name=s_name, index=False)
print(f"\nГОТОВО! Количество строк не изменилось: {len(df_main)}")
print(f"Исправлено несовпадений по группам (факультет/форма/уровень): {updated_groups_count}")
print(f"Обновлено/добавлено аббревиатур кафедр: {updated_depts_count}")
print(f"Результат сохранен в файл: {OUTPUT_FILE}")