# Концепция динамической генерации расписания

Документ фиксирует актуальную концепцию после внедрения календарных учебных графиков. Подробная продуктовая модель описана в [`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` хранит шаблонные занятия правила, а `schedule_rule_slot_subgroups` связывает лабораторный слот с одной или несколькими подгруппами.

## Алгоритм генерации

1. Для даты определяется семестр.
2. Для группы находится назначенный календарный учебный график.
3. По году начала обучения вычисляется текущий курс группы.
4. По курсу и дате находится код активности.
5. Обычные пары генерируются только для кода, где `allow_schedule = true`; в seed-данных это `Т`.
6. Для разрешённого дня выбираются активные правила и слоты.
7. Лимит часов считается только по реально проведённым занятиям; для лабораторных подгрупп счётчик ведётся отдельно по каждой подгруппе.

Лекции и практики нельзя делить на подгруппы. Если лабораторный слот привязан к нескольким подгруппам разных групп, он выводится как совместное занятие этих групп, а часы списываются отдельно по каждой подгруппе.

Если назначение графика отсутствует, расписание группы возвращается пустым списком без ошибки.

## Административный сценарий

Администратор создаёт специальность и профиль, затем создаёт группу с этой связкой. В разделе расписания он создаёт учебный год, семестры и календарный график, заполняет дневную сетку кодами активностей и назначает график группе. После сохранения графика или назначения кэш генератора очищается, поэтому следующие запросы расписания используют новые правила сразу.
