02.06.2026 Технический разбор (алгоритм) RU

От Excel-ведомости до XML: как сервис собирает приложение к диплому по шагам

Пошаговый разбор алгоритма: разбор Excel, сопоставление дисциплин, подсветка проблемных ячеек и сборка XML.

Никита Конкин

Проект Репозиторий Демо
Языковые версии: RU EN

В предыдущем разборе архитектура сервиса рассматривалась в целом — три модуля, разделение по языкам, запуск через Docker. Здесь разбирается сам алгоритм обработки: что происходит с данными на пути от загруженного Excel до готового XML. Изложение следует порядку конвейера.

Шаг 0. Что на входе

Сервису требуются два Excel-файла:

  1. Ведомость с оценками — отдельный лист на каждого студента (выгрузка из деканата).
  2. Список дисциплин — эталонный учебный план («обязательная часть»), задающий правильный порядок и названия предметов.

Задача алгоритма — наложить оценки из первого файла на структуру второго и привести результат к строгому формату.

Шаг 1. Разбор ведомости по листам

Каждый лист книги соответствует одному студенту. Функция process_student_workbook проходит по листам и для каждого:

  • берёт имя студента из пятого столбца;
  • очищает названия колонок и отсекает «шапку» — реальные данные начинаются с седьмой строки;
  • отбрасывает служебные строки: Всего, ПГТУ -, пустые наименования предметов;
  • переводит часы в зачётные единицы по правилу зач.ед = часы / 36;
  • нормализует зачёт: символ V интерпретируется как оценка 6.

На выходе формируется единая таблица df_stud_scores, где строки — это предметы, а столбцы — студенты.

# часы в зачётные единицы
df['часы учр'] = (df['часы учр'].astype(int) / 36).astype(int)
df.rename(columns={"часы учр": "зач ед"}, inplace=True)

# зачёт 'V' трактуем как оценку 6
df['зачет'] = df['зачет'].apply(lambda x: 6 if x == 'V' else x)

Шаг 2. Кодирование типа контроля в имя строки

Это ключевой приём алгоритма. Вместо отдельной колонки «тип» сервис записывает тип контроля прямо в индекс строки, добавляя суффикс (функция parse_rating):

  • зачёт или экзамен → _дисциплина_
  • практика → _практика_
  • курсовой → _курсовая_

Итоговый ключ строки выглядит так:

Математика_дисциплина_6
   ^предмет    ^тип    ^зач.ед

Дальше по всему конвейеру достаточно вызвать split('_'), чтобы определить предмет, его тип и объём, не обращаясь к дополнительным таблицам. Простое решение, которое удерживает целостность всего конвейера.

Шаг 3. Сопоставление с учебным планом

Названия в ведомости и в плане почти никогда не совпадают дословно, поэтому match_row представляет собой набор эвристик, применяемых по порядку:

  • прямое совпадение базового имени;
  • элективы вида Дисциплина (модуль) — берётся часть до скобки;
  • коды с точкой Б1.В.01 — сравнение по части до точки;
  • курсовые и факультативы — по ключевым словам;
  • префиксные группы вида Дисциплина * 3 — означают, что далее идут три дисциплины этой группы; счётчик count_of_prefix распределяет их по очереди.

То, что не удалось сопоставить, помечается пустым значением. Так план остаётся полным, а пропуски сразу заметны проверяющему.

Шаг 4. Подсветка проблемных ячеек

Перед тем как доверять данным, сервис выделяет подозрительные значения (highlight_problematic_cells) двумя цветами:

if cell_value is None or str(cell_value).strip() == '':
    cell.fill = yellow_fill      # пропущенное значение
elif '!' in str(cell_value) or '?' in str(cell_value):
    cell.fill = red_fill         # спорное значение

Жёлтый отмечает пустую ячейку, красный — значение со знаком ! или ?. Проверяющий работает только с выделенными ячейками, а не со всей таблицей.

Шаг 5. Сборка XML в формате «КиберДиплом 3.5.1»

Финальный этап выполняет DiplomaXMLGenerator. Здесь используется суффикс из шага 2: ключ предмет_тип_зач.ед разбирается, и запись помещается в нужную секцию XML.

Тип в индексе Секция XML
дисциплина <Дисциплины>
практика <Практики>
курсовая <Курсовые>
факультатив <Факультативы>
госэкзамен <Госэкзамены>

Параллельно разбирается ФИО с аккуратной обработкой составных отчеств (оглы, кызы, углы), а общие параметры — квалификация, срок обучения, председатель ГЭК — берутся из конфигурации. Дерево документа собирается через xml.etree.ElementTree и возвращается готовой строкой.

Конвейер целиком

2 Excel-файла
   ↓ разбор листов, очистка, часы → з.е.
df_stud_scores (предметы × студенты)
   ↓ кодирование типа в индекс (предмет_тип_з.е.)
   ↓ сопоставление с учебным планом (эвристики match_row)
df_final
   ↓ подсветка проблемных ячеек
   ↓ раскладка по секциям → ElementTree
XML «КиберДиплом 3.5.1»

Выводы

  1. Кодирование метаданных в ключ строки (предмет_тип_з.е.) избавляет от лишних структур данных на всём пути обработки.
  2. Нечёткое сопоставление названий решается не «магией», а упорядоченным списком простых эвристик.
  3. Подсветка перед доверием к данным — отдельный осознанный шаг валидации.
  4. Нормализация на входе (часы → з.е., V → 6) делает последующие шаги предсказуемыми.

Доступность

Проект открыт под лицензией MIT. Можно запустить собственный файл, увидеть подсветку и разобрать алгоритм по исходному коду.

Теги

программирование python pandas openpyxl алгоритмы обработкаданных

Другие обновления по проекту

01.06.2026 Технический разбор Сервис автоматизации приложений к дипломам

Как собрать сервис автоматизации документов из трёх языков: практический разбор

Разбор архитектуры сервиса автоматизации дипломных приложений: Java API, Python-обработка Excel, XML-генерация и Docker Compose.