Files
magistr/DYNAMIC_SCHEDULE_IMPLEMENTATION.md

15 KiB
Raw Blame History

Реализация динамического расписания

Этот файл фиксирует текущий результат внедрения динамической системы расписания: что уже добавлено в проект, как связаны база данных, backend, API и frontend, как работает генератор расписания и какие ограничения остаются для следующего этапа.

Статус внедрения

Реализован первый рабочий срез динамического расписания. Новая модель теперь является базовой схемой проекта и создаётся сразу в V1__init.sql.

Старые таблицы lessons и schedule_data, старые контроллеры, DTO, модели, репозитории и админские вызовы старых API удалены. Новая модель расписания является единственным базовым способом хранения и генерации расписания.

Что добавлено

База данных

Динамическая схема встроена в базовую Flyway-миграцию V1__init.sql.

Она создаёт следующие таблицы:

Таблица Назначение
time_slots Настраиваемая сетка пар: номер, время начала, время окончания, длительность
academic_years Учебные годы
semesters Семестры учебного года, от start_date считается первая неделя
holidays Праздники и исключённые даты, когда занятия не проводятся
academic_calendar_matrix Матрица учебного графика по семестру, курсу, специальности и неделе
schedule_rules Правила проведения дисциплины с лимитом академических часов
schedule_rule_groups Привязка правила к одной или нескольким группам
schedule_rule_slots Конкретные шаблонные слоты правила: день, чётность, пара, преподаватель, аудитория, тип и формат

Миграция также создаёт тестовые данные сразу в новой модели:

  1. Создаёт базовые временные слоты.
  2. Создаёт учебные годы 2024-2025 и 2025-2026.
  3. Заполняет матрицу учебного графика базовым статусом THEORY.
  4. Создаёт тестовые schedule_rules для групп и дисциплин.
  5. Создаёт привязки групп в schedule_rule_groups, включая потоковую лекцию.
  6. Создаёт шаблонные занятия в schedule_rule_slots.

Backend

Добавлены JPA-модели для новой схемы:

  • TimeSlot
  • AcademicYear
  • Semester
  • Holiday
  • AcademicCalendarMatrix
  • ScheduleRule
  • ScheduleRuleSlot
  • AcademicActivityType
  • ScheduleParity

Добавлены DTO для API:

  • TimeSlotDto
  • AcademicYearDto
  • SemesterDto
  • HolidayDto
  • AcademicCalendarMatrixDto
  • ScheduleRuleDto
  • ScheduleRuleSlotDto
  • RenderedLessonDto

Добавлены репозитории для новых таблиц:

  • TimeSlotRepository
  • AcademicYearRepository
  • SemesterRepository
  • HolidayRepository
  • AcademicCalendarMatrixRepository
  • ScheduleRuleRepository
  • ScheduleRuleSlotRepository

Добавлены сервисы:

Сервис Назначение
AcademicDateService Календарная математика: семестр по дате, номер недели, чётность, праздник, курс группы, тип активности
ScheduleGeneratorService Генерация расписания группы или преподавателя на диапазон дат

Старый ручной слой занятий удалён: в backend больше нет LessonsController, ScheduleDataController, сущностей Lesson/ScheduleData, их DTO, репозиториев и валидаторов старых строк расписания.

API

Добавлен новый endpoint просмотра расписания:

GET /api/schedule?groupId=1&startDate=2026-04-27&endDate=2026-05-03
GET /api/schedule?teacherId=2&startDate=2026-04-27&endDate=2026-05-03

Правило запроса: нужно передать ровно один идентификатор — groupId или teacherId.

Ответ — массив RenderedLessonDto, где у каждого занятия есть:

  • полная дата занятия;
  • номер дня недели;
  • название дня;
  • номер недели семестра;
  • чётность;
  • временной слот;
  • дисциплина;
  • преподаватель;
  • аудитория;
  • тип и формат занятия;
  • группы;
  • лимит часов правила;
  • уже списанные часы перед занятием;
  • остаток часов после занятия.

Добавлены административные API:

API Назначение
/api/admin/time-slots CRUD временных слотов
/api/admin/calendar/years CRUD учебных годов
/api/admin/calendar/years/{academicYearId}/semesters Семестры учебного года
/api/admin/calendar/semesters/{id} Обновление семестра
/api/admin/calendar/holidays CRUD праздников
/api/admin/calendar/matrix Получение и массовое сохранение матрицы учебного графика
/api/admin/schedule-rules CRUD правил расписания

Авторизация

Ответ POST /api/auth/login расширен полем userId.

Frontend сохраняет его в localStorage, чтобы кабинет преподавателя мог запрашивать личное расписание через:

GET /api/schedule?teacherId={userId}&startDate=...&endDate=...

Frontend

Заглушки кабинетов заменены на базовые экраны просмотра расписания.

Админский экран schedule показывает текущие schedule_rules и сетку time_slots через новые admin API.

Кабинет студента

Файл: frontend/student/index.html.

Текущая модель пользователя-студента не содержит прямой связи с учебной группой, поэтому экран работает через выбор группы:

  1. Загружает список групп из /api/groups.
  2. Сохраняет выбранную группу в localStorage.studentGroupId.
  3. Строит недельный диапазон дат.
  4. Запрашивает расписание через GET /api/schedule?groupId=....
  5. Показывает занятия по дням недели.

Кабинет преподавателя

Файл: frontend/teacher/index.html.

Экран использует localStorage.userId, который появляется после нового ответа авторизации:

  1. Определяет текущую неделю.
  2. Позволяет переключать неделю стрелками или через выбор даты.
  3. Запрашивает расписание через GET /api/schedule?teacherId=....
  4. Показывает занятия преподавателя в недельной сетке.
  5. В занятии отображаются дисциплина, время, тип, аудитория и группы.

Как работает генерация расписания

Генерация выполняется на backend по запросу клиента. Фактические занятия не хранятся отдельными строками на каждую дату, а вычисляются из правил.

Расписание группы

Алгоритм buildScheduleForGroup(groupId, startDate, endDate):

  1. Проверяет корректность диапазона дат.
  2. Находит группу.
  3. Для каждой даты диапазона определяет семестр.
  4. Вычисляет номер недели семестра.
  5. Вычисляет текущий курс группы по year_start_study.
  6. Проверяет матрицу учебного графика.
  7. Пропускает дату, если это праздник или не THEORY.
  8. Загружает правила группы для найденного семестра.
  9. Для каждого правила считает, сколько академических часов уже проведено до текущей даты.
  10. Если лимит total_academic_hours ещё не исчерпан, проецирует подходящие слоты правила на текущую дату.

Расписание преподавателя

Алгоритм buildScheduleForTeacher(teacherId, startDate, endDate) похож, но правила ищутся не по группе, а по преподавателю в schedule_rule_slots.teacher_id.

В ответе занятие дополнительно содержит все группы, привязанные к правилу. Это поддерживает потоковые лекции.

Чётность недели

Чётность вычисляется от semesters.start_date.

Настройка:

app.schedule.even-week-is-upper=false

По умолчанию:

  • нечётная неделя семестра соответствует ODD;
  • чётная неделя семестра соответствует EVEN.

Если app.schedule.even-week-is-upper=true, соответствие инвертируется.

Праздники

Праздник считается пропуском занятия:

  • занятие не отображается в расписании;
  • академические часы не списываются;
  • дисциплина фактически продолжается дольше, пока не будет исчерпан лимит часов.

Лимит часов

Каждый schedule_rules.total_academic_hours задаёт общий лимит академических часов дисциплины.

Генератор симулирует период от active_from_date до запрошенной даты и считает только реально проведённые слоты:

  • один слот = 2 академических часа;
  • праздники не считаются;
  • недели с активностью не THEORY не считаются;
  • после достижения лимита правило перестаёт выводиться.

Поток данных

Новая базовая БД сразу создаёт рабочую модель так:

V1__init.sql
    -> time_slots
    -> academic_years / semesters / holidays / academic_calendar_matrix
    -> schedule_rules
    -> schedule_rule_groups
    -> schedule_rule_slots

Для просмотра:

frontend
    -> GET /api/schedule
    -> ScheduleController
    -> ScheduleGeneratorService
    -> schedule_rules + schedule_rule_slots + calendar tables
    -> RenderedLessonDto[]

Что проверено

Проверка backend:

docker run --rm -v /mnt/HDD/magistr/magistr/backend:/app -w /app maven:3.9-eclipse-temurin-17 mvn -q -DskipTests compile

Результат: компиляция backend прошла успешно.

Проверка миграции:

  1. Поднят временный PostgreSQL-контейнер на образе postgres:alpine3.23.
  2. Применён V1__init.sql.
  3. Проверены контрольные количества строк новой модели.

Контрольный результат:

Объект Количество
schedule_rules 4
schedule_rule_groups 5
schedule_rule_slots 4
time_slots 7
academic_calendar_matrix 132
lessons таблица отсутствует
schedule_data таблица отсутствует

Также выполнен:

git diff --check

Результат: замечаний по whitespace нет.

Ограничения текущего среза

Этот срез создаёт рабочее ядро динамического расписания, но ещё не закрывает весь продуктовый объём.

Ограничения:

  • строгая проверка конфликтов преподавателей, аудиторий и групп при сохранении правил ещё не реализована;
  • студент пока выбирает группу вручную, потому что в модели пользователя нет связи student -> group.

Следующий этап

Рекомендуемый следующий этап:

  1. Реализовать backend-валидацию конфликтов:
    • преподаватель не может вести разные занятия одновременно;
    • аудитория не может быть занята двумя разными занятиями одновременно;
    • группа не может быть на двух занятиях одновременно, кроме корректного деления по подгруппам.
  2. Добавить модель связи студента с учебной группой, чтобы кабинет студента не требовал ручного выбора группы.
  3. Добавить отдельное управление подгруппами и подключить выбор subgroupId в конструкторе правил.

Связанные документы

  • SCHEDULE_PROPOSAL.md — исходная концепция динамического расписания.
  • SCHEDULE_TASKS.md — исходная декомпозиция задач.
  • docs/API.md — описание REST API.
  • docs/DATABASE.md — описание схемы БД.
  • docs/BUSINESS_LOGIC.md — бизнес-правила.
  • docs/FRONTEND.md — frontend-архитектура.