добавил денение на подгруппы для лабораторных работ
This commit is contained in:
41
docs/API.md
41
docs/API.md
@@ -101,7 +101,7 @@
|
||||
|
||||
## Динамическое расписание
|
||||
|
||||
Новая модель расписания строится из правил (`schedule_rules`) и слотов (`schedule_rule_slots`). В правиле отдельно хранятся часы и стартовые недели для лекций, лабораторных и практик. Фактические занятия рендерятся на диапазон дат только для дней, где календарный учебный график группы имеет код, разрешающий обычные пары.
|
||||
Новая модель расписания строится из правил (`schedule_rules`) и слотов (`schedule_rule_slots`). В правиле отдельно хранятся часы и стартовые недели для лекций, лабораторных и практик. Лабораторные слоты можно назначать на подгруппы, лекции и практики всегда проводятся для всей выбранной группы или потока. Фактические занятия рендерятся на диапазон дат только для дней, где календарный учебный график группы имеет код, разрешающий обычные пары.
|
||||
|
||||
### `GET /api/schedule`
|
||||
|
||||
@@ -148,6 +148,7 @@ GET /api/schedule?groupId=1&startDate=2026-04-27&endDate=2026-05-03
|
||||
"lessonTypeName": "Лекция",
|
||||
"lessonFormat": "Очно",
|
||||
"subgroupId": null,
|
||||
"subgroupName": null,
|
||||
"groupIds": [1],
|
||||
"groupNames": ["ИВТ-21-1"],
|
||||
"activityType": "Т",
|
||||
@@ -291,7 +292,7 @@ GET /api/schedule?groupId=1&startDate=2026-04-27&endDate=2026-05-03
|
||||
"dayOfWeek": 1,
|
||||
"parity": "BOTH",
|
||||
"timeSlotId": 3,
|
||||
"subgroupId": null,
|
||||
"subgroupId": null,
|
||||
"teacherId": 2,
|
||||
"classroomId": 1,
|
||||
"lessonTypeId": 1,
|
||||
@@ -313,6 +314,8 @@ CRUD доступен по:
|
||||
|
||||
`timeSlotId` должен ссылаться на базовый слот (`scopeApplyMode = DEFAULT`). Субботняя и ручные сетки не выбираются в правиле напрямую.
|
||||
|
||||
`subgroupId` можно передавать только для лабораторного слота. Подгруппа должна относиться к одной из групп правила. Для лекций и практик поле должно быть `null`, иначе API вернёт ошибку валидации.
|
||||
|
||||
Часы и недели начала задаются отдельно для лекций, лабораторных и практик. Если для типа занятий указан ненулевой лимит часов, в правиле должен быть хотя бы один слот этого типа; если слот типа есть, его лимит часов должен быть больше нуля.
|
||||
|
||||
### `GET /api/lesson-types`
|
||||
@@ -323,7 +326,8 @@ CRUD доступен по:
|
||||
```json
|
||||
[
|
||||
{ "id": 1, "name": "Лекция" },
|
||||
{ "id": 2, "name": "Практика" }
|
||||
{ "id": 2, "name": "Практика" },
|
||||
{ "id": 3, "name": "Лабораторная работа" }
|
||||
]
|
||||
```
|
||||
|
||||
@@ -454,6 +458,37 @@ CRUD доступен по:
|
||||
|
||||
Удаление группы.
|
||||
|
||||
### Подгруппы группы
|
||||
|
||||
Подгруппы используются только для деления лабораторных занятий. Лекции и практики не принимают `subgroupId`.
|
||||
|
||||
| Метод | URL | Назначение |
|
||||
|-------|-----|------------|
|
||||
| `GET` | `/api/subgroups` | Список всех подгрупп |
|
||||
| `GET` | `/api/groups/{groupId}/subgroups` | Подгруппы конкретной группы |
|
||||
| `POST` | `/api/groups/{groupId}/subgroups` | Создать подгруппу |
|
||||
| `PUT` | `/api/groups/{groupId}/subgroups/{id}` | Обновить подгруппу |
|
||||
| `DELETE` | `/api/groups/{groupId}/subgroups/{id}` | Удалить подгруппу, если она не используется в расписании |
|
||||
|
||||
**Тело создания/обновления:**
|
||||
```json
|
||||
{
|
||||
"name": "Подгруппа 1",
|
||||
"studentCapacity": 12
|
||||
}
|
||||
```
|
||||
|
||||
**Ответ:**
|
||||
```json
|
||||
{
|
||||
"id": 1,
|
||||
"groupId": 1,
|
||||
"groupName": "ИВТ-21-1",
|
||||
"name": "Подгруппа 1",
|
||||
"studentCapacity": 12
|
||||
}
|
||||
```
|
||||
|
||||
### Календарные графики группы
|
||||
|
||||
| Метод | URL | Назначение |
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
| `time_slots` | Настраиваемая сетка пар для тенанта |
|
||||
| `schedule_rules` | Лимиты часов и недели начала по лекциям, лабораторным и практикам |
|
||||
| `schedule_rule_groups` | Группы правила, включая потоковые лекции |
|
||||
| `schedule_rule_slots` | День, чётность, слот, преподаватель, аудитория, тип и формат занятия |
|
||||
| `schedule_rule_slots` | День, чётность, слот, преподаватель, аудитория, тип, формат и опциональная лабораторная подгруппа |
|
||||
|
||||
Генератор `ScheduleGeneratorService` рендерит расписание по запросу:
|
||||
1. Определяет семестр для каждой даты диапазона.
|
||||
@@ -95,9 +95,11 @@
|
||||
6. Загружает правила группы или преподавателя.
|
||||
7. Для каждого типа занятия проверяет свою неделю начала в семестре.
|
||||
8. Подставляет эффективную сетку времени даты: ручную, субботнюю или базовую.
|
||||
9. Считает уже проведённые часы отдельно для лекций, лабораторных и практик.
|
||||
9. Считает уже проведённые часы отдельно для лекций, практик и каждой лабораторной подгруппы.
|
||||
10. Останавливает вывод слотов конкретного типа, когда достигнут его лимит часов.
|
||||
|
||||
Лабораторные работы могут делиться на подгруппы через `schedule_rule_slots.subgroup_id`. Если подгруппа выбрана, занятие выводится только для родительской группы этой подгруппы, а лимит лабораторных часов списывается отдельно по этой подгруппе. Лекции и практики не делятся на подгруппы: такие слоты сохраняются только с `subgroup_id = NULL`.
|
||||
|
||||
Обычные пары генерируются только на коде `Т` (`allow_schedule = true`). Экзамены, каникулы, практики, нерабочие дни, праздники `*` и дни вне учебного года `=` считаются пропуском: занятие не переносится и не списывает академические часы. Если у группы нет назначения графика на учебный год, `GET /api/schedule` возвращает пустой список для этой группы без ошибки.
|
||||
|
||||
### Временные слоты
|
||||
@@ -124,6 +126,7 @@
|
||||
- **Покрытие типов:** если для лекций, лабораторных или практик указан лимит часов, должен быть хотя бы один слот этого типа; слот типа не сохраняется с нулевым лимитом часов.
|
||||
- **Слот:** день недели должен быть от 1 до 7, чётность недели обязательна.
|
||||
- **Связанные сущности:** базовый временной слот, преподаватель, аудитория и тип занятия должны существовать в БД.
|
||||
- **Подгруппы:** `subgroupId` разрешён только для лабораторных слотов и должен относиться к одной из групп правила.
|
||||
- **Формат:** `lessonFormat` обязателен и хранится в слоте правила.
|
||||
|
||||
---
|
||||
|
||||
@@ -348,6 +348,8 @@ erDiagram
|
||||
| `name` | VARCHAR(100) | Название подгруппы |
|
||||
| `student_capacity` | INT | Количество студентов |
|
||||
|
||||
Уникальность задаётся парой `(group_id, name)`: в разных группах могут быть подгруппы с одинаковым названием. Подгруппы применяются только для лабораторных занятий.
|
||||
|
||||
#### `subjects` — Дисциплины
|
||||
| Колонка | Тип | Описание |
|
||||
|---------|-----|----------|
|
||||
@@ -532,12 +534,14 @@ Seed создаёт `Базовая сетка` (`DEFAULT`) и `Субботня
|
||||
| `day_of_week` | INT CHECK(1–7) | День недели: 1 — понедельник |
|
||||
| `parity` | VARCHAR(10) | `BOTH`, `EVEN`, `ODD` |
|
||||
| `time_slot_id` | BIGINT FK → time_slots | Базовый временной слот |
|
||||
| `subgroup_id` | BIGINT FK → subgroups, NULL | Подгруппа |
|
||||
| `subgroup_id` | BIGINT FK → subgroups, NULL | Подгруппа лабораторной работы |
|
||||
| `teacher_id` | BIGINT FK → users | Преподаватель |
|
||||
| `classroom_id` | BIGINT FK → classrooms | Аудитория |
|
||||
| `lesson_type_id` | BIGINT FK → lesson_types | Тип занятия |
|
||||
| `lesson_format` | VARCHAR(30) | `Очно` или `Онлайн` |
|
||||
|
||||
`subgroup_id` заполняется только для лабораторных слотов. Лекции и практики не делятся на подгруппы и должны хранить `NULL`. Ограничение дополнительно проверяется триггером `validate_schedule_rule_slot_subgroup`; для быстрых выборок есть индекс `idx_schedule_rule_slots_subgroup`.
|
||||
|
||||
---
|
||||
|
||||
## Flyway миграции
|
||||
|
||||
@@ -25,7 +25,7 @@
|
||||
| `time_slots` | Настраиваемая сетка пар |
|
||||
| `schedule_rules` | Правила проведения дисциплин с отдельными часами и стартовыми неделями лекций, лабораторных и практик |
|
||||
| `schedule_rule_groups` | Группы правила |
|
||||
| `schedule_rule_slots` | Шаблонные слоты правила |
|
||||
| `schedule_rule_slots` | Шаблонные слоты правила, включая опциональную лабораторную подгруппу |
|
||||
|
||||
Праздничные и неучебные дни больше не хранятся отдельной сущностью: они задаются кодами `*` и `=` в дневной сетке.
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
| `AcademicDateService` | Семестр по дате, номер недели, чётность, курс группы, активность дня по назначенному графику |
|
||||
| `ScheduleGeneratorService` | Рендер расписания группы или преподавателя на диапазон дат |
|
||||
|
||||
`ScheduleGeneratorService` пропускает день, если код активности не разрешает обычные пары. Для лекций, лабораторных и практик генератор отдельно проверяет стартовую неделю и отдельно списывает академические часы. Если у группы нет назначенного графика на учебный год, расписание этой группы возвращается пустым списком.
|
||||
`ScheduleGeneratorService` пропускает день, если код активности не разрешает обычные пары. Для лекций, лабораторных и практик генератор отдельно проверяет стартовую неделю и отдельно списывает академические часы. Лабораторные подгруппы имеют отдельный счётчик лабораторных часов; лекции и практики на подгруппы не делятся. Если у группы нет назначенного графика на учебный год, расписание этой группы возвращается пустым списком.
|
||||
|
||||
## API
|
||||
|
||||
@@ -64,6 +64,7 @@ GET /api/schedule?teacherId=2&startDate=2026-04-27&endDate=2026-05-03
|
||||
| `/api/admin/academic-calendars/{id}/grid` | Получение и сохранение дневной сетки |
|
||||
| `/api/specialties/{id}/profiles` | CRUD профилей специальности |
|
||||
| `/api/groups/{id}/calendar-assignments` | Назначения графиков группе |
|
||||
| `/api/subgroups`, `/api/groups/{id}/subgroups` | Управление подгруппами для лабораторных |
|
||||
| `/api/admin/schedule-rules` | CRUD правил расписания |
|
||||
|
||||
## Frontend
|
||||
@@ -72,12 +73,13 @@ GET /api/schedule?teacherId=2&startDate=2026-04-27&endDate=2026-05-03
|
||||
|
||||
- конструктор правил динамического расписания;
|
||||
- отдельные поля часов и недель начала для лекций, лабораторных и практик;
|
||||
- выбор подгруппы только для лабораторного слота;
|
||||
- управление временными слотами;
|
||||
- учебные годы и семестры;
|
||||
- CRUD календарных графиков;
|
||||
- Excel-подобный редактор дневной сетки по курсам, датам и кодам активностей.
|
||||
|
||||
Экран `groups` создаёт и редактирует группы с профилем обучения, а также назначает календарный график группе на учебный год.
|
||||
Экран `groups` создаёт и редактирует группы с профилем обучения, управляет подгруппами для лабораторных и назначает календарный график группе на учебный год.
|
||||
|
||||
Экран `departments-data` управляет кафедрами, специальностями и профилями специальностей.
|
||||
|
||||
|
||||
@@ -106,13 +106,13 @@ frontend/
|
||||
| Tab | Описание | API |
|
||||
|-----|----------|-----|
|
||||
| `users` | CRUD пользователей | `/api/users` |
|
||||
| `groups` | CRUD групп | `/api/groups` |
|
||||
| `groups` | CRUD групп, подгруппы лабораторных и назначения графиков | `/api/groups`, `/api/subgroups` |
|
||||
| `edu-forms` | Формы обучения | `/api/education-forms` |
|
||||
| `profiles` | Создание, редактирование и удаление профилей обучения специальностей | `/api/specialties/{id}/profiles` |
|
||||
| `equipments` | Оборудование | `/api/equipments` |
|
||||
| `classrooms` | Аудитории | `/api/classrooms` |
|
||||
| `subjects` | Дисциплины | `/api/subjects` |
|
||||
| `schedule` | Конструктор правил динамического расписания | `/api/admin/schedule-rules`, `/api/admin/time-slots`, `/api/admin/calendar/years`, `/api/lesson-types` |
|
||||
| `schedule` | Конструктор правил динамического расписания | `/api/admin/schedule-rules`, `/api/admin/time-slots`, `/api/admin/calendar/years`, `/api/lesson-types`, `/api/subgroups` |
|
||||
| `academic-calendar` | Учебные годы, семестры, создание календарных графиков и Excel-подобный редактор дневной сетки | `/api/admin/calendar`, `/api/admin/academic-calendars`, `/api/admin/calendar/activity-types`, `/api/education-forms` |
|
||||
| `auditorium-workload` | Динамическая загруженность аудиторий: аудитории в строках, временные слоты в столбцах | `/api/classrooms`, `/api/admin/time-slots`, `/api/equipments`, `/api/groups`, `/api/schedule` |
|
||||
| `database` | Тенанты | `/api/database` |
|
||||
@@ -120,10 +120,10 @@ frontend/
|
||||
|
||||
### Особенности админских вкладок
|
||||
|
||||
- Вкладка `groups` загружает кафедры, специальности, профили, учебные годы и календарные графики. Группа создаётся через `/api/groups` с `specialtyId` и `specialtyProfileId`, а блок назначений использует `/api/groups/{id}/calendar-assignments`.
|
||||
- Вкладка `groups` загружает кафедры, специальности, профили, учебные годы и календарные графики. Группа создаётся через `/api/groups` с `specialtyId` и `specialtyProfileId`, блок подгрупп использует `/api/subgroups` и `/api/groups/{id}/subgroups`, а блок назначений использует `/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}`.
|
||||
@@ -218,7 +218,7 @@ export function isAuthenticatedAsAdmin() {
|
||||
- навигация по неделям: предыдущая, текущая, следующая;
|
||||
- выбор даты через `input[type="date"]`;
|
||||
- запрос `GET /api/schedule?teacherId={userId}&startDate={YYYY-MM-DD}&endDate={YYYY-MM-DD}`;
|
||||
- отображение дисциплины, времени, типа занятия, аудитории и всех групп правила.
|
||||
- отображение дисциплины, времени, типа занятия, лабораторной подгруппы, аудитории и всех групп правила.
|
||||
|
||||
Если пользователь вошёл до появления поля `userId`, страница попросит выполнить вход заново.
|
||||
|
||||
@@ -230,7 +230,7 @@ export function isAuthenticatedAsAdmin() {
|
||||
- селект группы с сохранением выбора в `localStorage.studentGroupId`;
|
||||
- недельная сетка по дням;
|
||||
- запрос `GET /api/schedule?groupId={groupId}&startDate={YYYY-MM-DD}&endDate={YYYY-MM-DD}`;
|
||||
- отображение дисциплины, времени, преподавателя, аудитории, формата и типа занятия.
|
||||
- отображение дисциплины, времени, преподавателя, аудитории, формата, типа занятия и лабораторной подгруппы.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
- календарный учебный график по специальности, профилю, форме обучения и учебному году;
|
||||
- правила расписания дисциплин;
|
||||
- слоты правил: день недели, чётность, пара, преподаватель, аудитория, тип занятия и формат;
|
||||
- слоты правил: день недели, чётность, пара, преподаватель, аудитория, тип занятия, формат и опциональная подгруппа для лабораторных;
|
||||
- назначение конкретного календарного графика учебной группе на учебный год.
|
||||
|
||||
При запросе `GET /api/schedule` backend рендерит расписание на диапазон дат. Если дата не является днём теоретического обучения по назначенному группе графику, обычные пары не выводятся и академические часы не списываются.
|
||||
@@ -29,7 +29,7 @@
|
||||
- `time_slots` задаёт сетку пар для тенанта.
|
||||
- `schedule_rules` хранит дисциплину, семестр, отдельные лимиты часов и стартовые недели для лекций, лабораторных и практик.
|
||||
- `schedule_rule_groups` связывает правило с одной или несколькими группами.
|
||||
- `schedule_rule_slots` хранит шаблонные занятия правила.
|
||||
- `schedule_rule_slots` хранит шаблонные занятия правила; `subgroup_id` разрешён только для лабораторных работ.
|
||||
|
||||
## Алгоритм генерации
|
||||
|
||||
@@ -39,7 +39,9 @@
|
||||
4. По курсу и дате находится код активности.
|
||||
5. Обычные пары генерируются только для кода, где `allow_schedule = true`; в seed-данных это `Т`.
|
||||
6. Для разрешённого дня выбираются активные правила и слоты.
|
||||
7. Лимит часов считается только по реально проведённым занятиям.
|
||||
7. Лимит часов считается только по реально проведённым занятиям; для лабораторных подгрупп счётчик ведётся отдельно по каждой подгруппе.
|
||||
|
||||
Лекции и практики нельзя делить на подгруппы. Если лабораторный слот привязан к подгруппе, он выводится только для родительской группы этой подгруппы.
|
||||
|
||||
Если назначение графика отсутствует, расписание группы возвращается пустым списком без ошибки.
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
- [x] Обновлён `AcademicDateService`: активность дня определяется по назначенному группе графику.
|
||||
- [x] Обновлён `ScheduleGeneratorService`: обычные пары генерируются только на разрешённых днях.
|
||||
- [x] В `V1__init.sql` добавлены отдельные часы и недели начала для лекций, лабораторных и практик.
|
||||
- [x] Добавлены подгруппы для лабораторных слотов; лекции и практики не делятся на подгруппы.
|
||||
- [x] Добавлены API профилей, календарей, дневной сетки, кодов активностей и назначений группам.
|
||||
- [x] Обновлены админские экраны специальностей, групп и расписания.
|
||||
- [x] Добавлена документация `docs/ACADEMIC_CALENDAR.md`.
|
||||
|
||||
Reference in New Issue
Block a user