написал обработку количества часов лекций лабораторных и практик по отдельности

This commit is contained in:
Zuev
2026-05-06 21:54:17 +03:00
parent 44cfe8ab6e
commit b222f3adac
17 changed files with 540 additions and 113 deletions

View File

@@ -101,7 +101,7 @@
## Динамическое расписание
Новая модель расписания строится из правил (`schedule_rules`) и слотов (`schedule_rule_slots`). Фактические занятия рендерятся на диапазон дат только для дней, где календарный учебный график группы имеет код, разрешающий обычные пары.
Новая модель расписания строится из правил (`schedule_rules`) и слотов (`schedule_rule_slots`). В правиле отдельно хранятся часы и стартовые недели для лекций, лабораторных и практик. Фактические занятия рендерятся на диапазон дат только для дней, где календарный учебный график группы имеет код, разрешающий обычные пары.
### `GET /api/schedule`
@@ -151,9 +151,9 @@ GET /api/schedule?groupId=1&startDate=2026-04-27&endDate=2026-05-03
"groupIds": [1],
"groupNames": ["ИВТ-21-1"],
"activityType": "Т",
"totalAcademicHours": 72,
"consumedAcademicHoursBeforeLesson": 24,
"remainingAcademicHoursAfterLesson": 46
"lessonTypeAcademicHours": 32,
"consumedLessonTypeAcademicHoursBeforeLesson": 12,
"remainingLessonTypeAcademicHoursAfterLesson": 18
}
]
```
@@ -279,8 +279,12 @@ GET /api/schedule?groupId=1&startDate=2026-04-27&endDate=2026-05-03
{
"subjectId": 1,
"semesterId": 1,
"activeFromDate": "2026-02-01",
"totalAcademicHours": 72,
"lectureAcademicHours": 32,
"laboratoryAcademicHours": 16,
"practiceAcademicHours": 24,
"lectureStartWeek": 1,
"laboratoryStartWeek": 3,
"practiceStartWeek": 2,
"groupIds": [1, 2],
"slots": [
{
@@ -309,6 +313,8 @@ CRUD доступен по:
`timeSlotId` должен ссылаться на базовый слот (`scopeApplyMode = DEFAULT`). Субботняя и ручные сетки не выбираются в правиле напрямую.
Часы и недели начала задаются отдельно для лекций, лабораторных и практик. Если для типа занятий указан ненулевой лимит часов, в правиле должен быть хотя бы один слот этого типа; если слот типа есть, его лимит часов должен быть больше нуля.
### `GET /api/lesson-types`
Справочник типов занятий для конструктора правил расписания.

View File

@@ -82,7 +82,7 @@
| `academic_calendar_days` | Дневная сетка по курсу и дате |
| `student_group_calendar_assignments` | Назначение конкретного графика группе на учебный год |
| `time_slots` | Настраиваемая сетка пар для тенанта |
| `schedule_rules` | Лимит часов дисциплины в семестре |
| `schedule_rules` | Лимиты часов и недели начала по лекциям, лабораторным и практикам |
| `schedule_rule_groups` | Группы правила, включая потоковые лекции |
| `schedule_rule_slots` | День, чётность, слот, преподаватель, аудитория, тип и формат занятия |
@@ -93,9 +93,10 @@
4. Определяет код активности по курсу группы и конкретной дате.
5. Пропускает день, если код активности не разрешает обычные пары.
6. Загружает правила группы или преподавателя.
7. Симулирует уже проведённые занятия от `active_from_date`.
7. Для каждого типа занятия проверяет свою неделю начала в семестре.
8. Подставляет эффективную сетку времени даты: ручную, субботнюю или базовую.
9. Останавливает вывод правила, когда достигнут `total_academic_hours`.
9. Считает уже проведённые часы отдельно для лекций, лабораторных и практик.
10. Останавливает вывод слотов конкретного типа, когда достигнут его лимит часов.
Обычные пары генерируются только на коде `Т` (`allow_schedule = true`). Экзамены, каникулы, практики, нерабочие дни, праздники `*` и дни вне учебного года `=` считаются пропуском: занятие не переносится и не списывает академические часы. Если у группы нет назначения графика на учебный год, `GET /api/schedule` возвращает пустой список для этой группы без ошибки.
@@ -119,7 +120,8 @@
### Валидация правил расписания
- **Правило:** обязательны дисциплина, семестр, дата начала, положительный лимит академических часов и хотя бы одна группа.
- **Правило:** обязательны дисциплина, семестр, хотя бы один положительный лимит часов по типу занятий, положительные недели начала и хотя бы одна группа.
- **Покрытие типов:** если для лекций, лабораторных или практик указан лимит часов, должен быть хотя бы один слот этого типа; слот типа не сохраняется с нулевым лимитом часов.
- **Слот:** день недели должен быть от 1 до 7, чётность недели обязательна.
- **Связанные сущности:** базовый временной слот, преподаватель, аудитория и тип занятия должны существовать в БД.
- **Формат:** `lessonFormat` обязателен и хранится в слоте правила.

View File

@@ -207,8 +207,12 @@ erDiagram
BIGSERIAL id PK
BIGINT subject_id FK
BIGINT semester_id FK
DATE active_from_date
INT total_academic_hours
INT lecture_academic_hours
INT laboratory_academic_hours
INT practice_academic_hours
INT lecture_start_week
INT laboratory_start_week
INT practice_start_week
}
schedule_rule_groups {
@@ -507,8 +511,12 @@ Seed создаёт `Базовая сетка` (`DEFAULT`) и `Субботня
| `id` | BIGSERIAL PK | ID |
| `subject_id` | BIGINT FK → subjects | Дисциплина |
| `semester_id` | BIGINT FK → semesters | Семестр |
| `active_from_date` | DATE | Дата начала действия правила |
| `total_academic_hours` | INT | Лимит академических часов |
| `lecture_academic_hours` | INT | Лимит академических часов лекций |
| `laboratory_academic_hours` | INT | Лимит академических часов лабораторных работ |
| `practice_academic_hours` | INT | Лимит академических часов практик |
| `lecture_start_week` | INT | Неделя семестра, с которой начинаются лекции |
| `laboratory_start_week` | INT | Неделя семестра, с которой начинаются лабораторные |
| `practice_start_week` | INT | Неделя семестра, с которой начинаются практики |
#### `schedule_rule_groups` — Группы правила
| Колонка | Тип | Описание |
@@ -548,11 +556,11 @@ Seed создаёт `Базовая сетка` (`DEFAULT`) и `Субботня
| Файл | Описание |
|------|----------|
| `V1__init.sql` | Инициализация: справочники, профили специальностей, календарные учебные графики, динамическое расписание, тестовые правила, триггеры, комментарии |
| `V1__init.sql` | Инициализация: справочники, профили специальностей, календарные учебные графики, динамическое расписание с раздельными часами и стартовыми неделями по типам занятий, тестовые правила, триггеры, комментарии |
### Накатывание на существующих тенантов
Для применения новой миграции к уже существующим тенантам необходимо перезапустить backend:
Для применения новой базовой схемы к уже существующим тенантам нужен полный сброс БД. Перезапуск backend без сброса не изменит уже применённую `V1__init.sql`.
```bash
# Kubernetes

View File

@@ -0,0 +1,107 @@
# Реализация динамического расписания
Файл фиксирует текущий результат внедрения динамического расписания и календарных учебных графиков.
## Статус
Новая модель является базовой схемой проекта и создаётся в `backend/src/main/resources/db/migration/V1__init.sql`. Раздельные часы и недели начала по типам занятий уже включены в эту базовую схему; применение рассчитано на полный сброс БД.
Старый ручной слой занятий удалён. Динамическое расписание строится из правил `schedule_rules`, слотов `schedule_rule_slots` и календарного учебного графика группы.
## База данных
`V1__init.sql` создаёт и заполняет:
| Таблица | Назначение |
|---------|------------|
| `specialties` | Коды и названия специальностей |
| `specialty_profiles` | Профили обучения внутри специальности |
| `student_groups` | Группы со ссылками на специальность и профиль |
| `education_forms` | Общий справочник форм обучения для групп и календарных графиков |
| `academic_calendar_activity_types` | Коды активностей из Excel-графика |
| `academic_calendars` | Календарные графики профиля, формы обучения и учебного года |
| `academic_calendar_days` | Дневная сетка графика по курсу и дате |
| `student_group_calendar_assignments` | Назначения графиков группам по учебному году |
| `time_slots` | Настраиваемая сетка пар |
| `schedule_rules` | Правила проведения дисциплин с отдельными часами и стартовыми неделями лекций, лабораторных и практик |
| `schedule_rule_groups` | Группы правила |
| `schedule_rule_slots` | Шаблонные слоты правила |
Праздничные и неучебные дни больше не хранятся отдельной сущностью: они задаются кодами `*` и `=` в дневной сетке.
## Backend
Добавлены сущности и репозитории для профилей, календарей, кодов активностей, дневной сетки и назначений группам.
Ключевые сервисы:
| Сервис | Назначение |
|--------|------------|
| `AcademicDateService` | Семестр по дате, номер недели, чётность, курс группы, активность дня по назначенному графику |
| `ScheduleGeneratorService` | Рендер расписания группы или преподавателя на диапазон дат |
`ScheduleGeneratorService` пропускает день, если код активности не разрешает обычные пары. Для лекций, лабораторных и практик генератор отдельно проверяет стартовую неделю и отдельно списывает академические часы. Если у группы нет назначенного графика на учебный год, расписание этой группы возвращается пустым списком.
## API
Просмотр расписания:
```http
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
```
Административные 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/activity-types` | CRUD кодов активностей |
| `/api/admin/academic-calendars` | CRUD календарных учебных графиков |
| `/api/admin/academic-calendars/{id}/grid` | Получение и сохранение дневной сетки |
| `/api/specialties/{id}/profiles` | CRUD профилей специальности |
| `/api/groups/{id}/calendar-assignments` | Назначения графиков группе |
| `/api/admin/schedule-rules` | CRUD правил расписания |
## Frontend
Админский экран `schedule` содержит:
- конструктор правил динамического расписания;
- отдельные поля часов и недель начала для лекций, лабораторных и практик;
- управление временными слотами;
- учебные годы и семестры;
- CRUD календарных графиков;
- Excel-подобный редактор дневной сетки по курсам, датам и кодам активностей.
Экран `groups` создаёт и редактирует группы с профилем обучения, а также назначает календарный график группе на учебный год.
Экран `departments-data` управляет кафедрами, специальностями и профилями специальностей.
## Проверки
Доступные локальные проверки:
```bash
node --check frontend/admin/js/views/schedule.js
node --check frontend/admin/js/views/groups.js
node --check frontend/admin/js/views/departments-data.js
git diff --check
```
Полная проверка требует окружения с Maven и доступом к Docker:
```bash
mvn -DskipTests compile
docker compose down -v
docker compose up -d --build
```
## Следующие этапы
- Добавить интеграционные тесты генерации по кодам `Т`, `Э`, `К`, `*`, `=`.
- Добавить строгую backend-валидацию конфликтов преподавателей, аудиторий и групп при сохранении правил.
- Добавить связь пользователя-студента с учебной группой, чтобы кабинет студента не требовал ручного выбора группы.

View File

@@ -123,7 +123,7 @@ frontend/
- Вкладка `groups` загружает кафедры, специальности, профили, учебные годы и календарные графики. Группа создаётся через `/api/groups` с `specialtyId` и `specialtyProfileId`, а блок назначений использует `/api/groups/{id}/calendar-assignments`.
- Вкладка `auditorium-workload` показывает матрицу загруженности по выбранной дате: строки — реальные аудитории из `/api/classrooms`, столбцы — эффективные временные слоты выбранного дня из `/api/admin/time-slots/effective`, занятость собирается из динамического расписания `/api/schedule` по группам. Фильтры корпуса, вместимости и оборудования заполняются из API.
- Вкладка `profiles` выделена под профили обучения: администратор выбирает специальность, создаёт профиль, редактирует описание и удаляет неиспользуемые профили.
- Вкладка `schedule` не обращается к старым `lessons` API. Создание и редактирование расписания выполняется через правила `/api/admin/schedule-rules`, где каждое правило содержит группы и набор базовых слотов. Из календарной системы здесь используется только список семестров для выбора периода действия правила.
- Вкладка `schedule` не обращается к старым `lessons` API. Создание и редактирование расписания выполняется через правила `/api/admin/schedule-rules`, где каждое правило содержит группы, отдельные часы и недели начала для лекций, лабораторных и практик, а также набор базовых слотов. Из календарной системы здесь используется список семестров для выбора периода действия правила.
- Вкладка `academic-calendar` полностью отделяет календарную систему от расписания занятий: администратор создаёт учебные годы и семестры, заводит календарные графики, выбирает форму обучения из общего справочника `/api/education-forms`, заполняет дневную сетку по курсам и кодам активностей, назначает ручную временную сетку на конкретную дату, а сохранение сетки идёт через `/api/admin/academic-calendars/{id}/grid`.
- Редактор годового графика во вкладке `academic-calendar` показывает мелкую табличную сетку: каждый курс разделён на семестры (`1`/`2`, `3`/`4` и далее), внутри семестра столбцы подписаны номерами недель учебного года, строки — днями недели, а ячейки содержат буквенный код активности. Подробная расшифровка и изменение кода открываются в модальном окне по клику на ячейку.
- Вкладка `departments-data` использует модальные формы редактирования и маршруты `PUT/DELETE /api/departments/{id}` и `PUT/DELETE /api/specialties/{id}`.

48
docs/SCHEDULE_PROPOSAL.md Normal file
View File

@@ -0,0 +1,48 @@
# Концепция динамической генерации расписания
Документ фиксирует актуальную концепцию после внедрения календарных учебных графиков. Подробная продуктовая модель описана в [`docs/ACADEMIC_CALENDAR.md`](docs/ACADEMIC_CALENDAR.md), REST API — в [`docs/API.md`](docs/API.md), схема БД — в [`docs/DATABASE.md`](docs/DATABASE.md).
## Базовая идея
Фактические занятия не хранятся отдельными строками на каждую дату. Система хранит:
- календарный учебный график по специальности, профилю, форме обучения и учебному году;
- правила расписания дисциплин;
- слоты правил: день недели, чётность, пара, преподаватель, аудитория, тип занятия и формат;
- назначение конкретного календарного графика учебной группе на учебный год.
При запросе `GET /api/schedule` backend рендерит расписание на диапазон дат. Если дата не является днём теоретического обучения по назначенному группе графику, обычные пары не выводятся и академические часы не списываются.
## Слои модели
### Календарный слой
- `academic_years` и `semesters` задают границы учебного года и семестров.
- `specialties` и `specialty_profiles` определяют образовательную программу группы.
- `academic_calendars` хранит график профиля и формы обучения.
- `academic_calendar_activity_types` хранит коды из Excel-графика: `Т`, `Э`, `К`, `У`, `П`, `Пд`, `Н`, `Г`, `Д`, `ПА`, `С`, `*`, `=`.
- `academic_calendar_days` хранит дневную сетку по курсу и дате.
- `student_group_calendar_assignments` выбирает график для группы на учебный год.
### Слой правил расписания
- `time_slots` задаёт сетку пар для тенанта.
- `schedule_rules` хранит дисциплину, семестр, отдельные лимиты часов и стартовые недели для лекций, лабораторных и практик.
- `schedule_rule_groups` связывает правило с одной или несколькими группами.
- `schedule_rule_slots` хранит шаблонные занятия правила.
## Алгоритм генерации
1. Для даты определяется семестр.
2. Для группы находится назначенный календарный учебный график.
3. По году начала обучения вычисляется текущий курс группы.
4. По курсу и дате находится код активности.
5. Обычные пары генерируются только для кода, где `allow_schedule = true`; в seed-данных это `Т`.
6. Для разрешённого дня выбираются активные правила и слоты.
7. Лимит часов считается только по реально проведённым занятиям.
Если назначение графика отсутствует, расписание группы возвращается пустым списком без ошибки.
## Административный сценарий
Администратор создаёт специальность и профиль, затем создаёт группу с этой связкой. В разделе расписания он создаёт учебный год, семестры и календарный график, заполняет дневную сетку кодами активностей и назначает график группе. После сохранения графика или назначения кэш генератора очищается, поэтому следующие запросы расписания используют новые правила сразу.

33
docs/SCHEDULE_TASKS.md Normal file
View File

@@ -0,0 +1,33 @@
# Задачи: динамическое расписание и календарный учебный график
Актуальная декомпозиция относится к модели, где календарный учебный график хранится дневной сеткой и назначается группе на учебный год.
## Выполнено
- [x] Переписана базовая схема `V1__init.sql` под полный сброс БД.
- [x] Добавлены специальности, профили обучения и связь группы с профилем.
- [x] Добавлены формы обучения календарного графика.
- [x] Добавлен справочник кодов активностей Excel-графика.
- [x] Добавлены календарные графики, дневная сетка и назначения графиков группам.
- [x] Обновлён `AcademicDateService`: активность дня определяется по назначенному группе графику.
- [x] Обновлён `ScheduleGeneratorService`: обычные пары генерируются только на разрешённых днях.
- [x] В `V1__init.sql` добавлены отдельные часы и недели начала для лекций, лабораторных и практик.
- [x] Добавлены API профилей, календарей, дневной сетки, кодов активностей и назначений группам.
- [x] Обновлены админские экраны специальностей, групп и расписания.
- [x] Добавлена документация `docs/ACADEMIC_CALENDAR.md`.
## Оставшиеся задачи
- [ ] Добавить интеграционные тесты генерации расписания для кодов `Т`, `Э`, `К`, `*`, `=`.
- [ ] Добавить проверку конфликтов преподавателей, аудиторий и групп при сохранении правил.
- [ ] Добавить модель связи пользователя-студента с учебной группой.
- [ ] Добавить импорт календарного графика из Excel, если он понадобится после ручного редактора.
## Проверочный сценарий
1. Полностью сбросить локальную БД.
2. Запустить приложение и применить `V1__init.sql`.
3. Создать профиль специальности.
4. Создать календарный график и заполнить дневную сетку.
5. Назначить график группе.
6. Проверить `GET /api/schedule` на дне теории и на днях, где пары запрещены.