# Календарный учебный график

## Назначение

Календарный учебный график хранит правила учебного года в той же логике, что и исходные Excel-таблицы: для каждого курса и каждой даты указывается код активности. Динамическое расписание использует этот график как фильтр: обычные пары выводятся только в дни теоретического обучения.

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

## Связи данных

Основная цепочка выглядит так:

```text
специальность -> профиль обучения -> календарный учебный график -> назначение группе
```

- `specialties` хранит код и название специальности, например `09.03.04`.
- `specialty_profiles` хранит профили внутри специальности. У одной специальности может быть несколько профилей.
- `academic_calendars` хранит график профиля, формы обучения и учебного года.
- Форма обучения графика берётся из общего справочника `education_forms`; отдельной таблицы форм для календаря нет.
- `academic_calendar_days` хранит дневную сетку графика по курсу и дате.
- `academic_calendar_subjects` хранит дисциплины графика по номерам учебных семестров; дисциплины выбираются из справочника `subjects`.
- `student_groups` связана со специальностью, профилем и той же таблицей форм обучения.
- `student_group_calendar_assignments` назначает группе конкретный график на учебный год.

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

## Коды активностей

| Код | Значение | Обычные пары |
|-----|----------|--------------|
| `Т` | Теоретическое обучение | Да |
| `Э` | Экзаменационная сессия | Нет |
| `К` | Каникулы | Нет |
| `У` | Учебная практика | Нет |
| `П` | Производственная практика | Нет |
| `Пд` | Преддипломная практика | Нет |
| `Н` | Научно-исследовательская работа | Нет |
| `Г` | Государственная итоговая аттестация | Нет |
| `Д` | Подготовка выпускной квалификационной работы | Нет |
| `ПА` | Промежуточная аттестация | Нет |
| `С` | Самостоятельная работа или специальный период | Нет |
| `*` | Праздничный или нерабочий день | Нет |
| `=` | День вне учебного года или пустая ячейка графика | Нет |

Технически разрешение генерации задаётся полем `academic_calendar_activity_types.allow_schedule`. В seed-данных `true` установлен только для `Т`.

## Правило генерации расписания

`ScheduleGeneratorService` при запросе `GET /api/schedule` проверяет календарный график до вывода занятия:

1. Определяется учебный год и семестр даты.
2. Для группы находится назначение графика на этот учебный год.
3. По году начала обучения группы вычисляется текущий курс.
4. В `academic_calendar_days` ищется ячейка для курса и даты.
5. Если код ячейки разрешает обычные пары, применяются правила `schedule_rules`.
6. Если код не разрешает пары, день пропускается и академические часы не списываются.

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

## Работа администратора

1. В разделе кафедр и специальностей администратор создаёт специальность и её профили.
2. В разделе групп администратор создаёт группу, выбирая специальность и профиль обучения.
3. В разделе календарного графика администратор создаёт учебный год и семестры.
4. Там же создаётся календарный учебный график: администратор выбирает учебный год, специальность, профиль, форму обучения из общего справочника и количество курсов. Название вручную не вводится — frontend формирует его в порядке `код специальности — профиль обучения — форма обучения — учебный год`.
5. Администратор открывает сетку графика, заполняет дни вручную или массово через диапазон дат.
6. На вкладке дисциплин графика администратор вручную привязывает дисциплины к учебным семестрам: например, для четырёх курсов доступны семестры `1..8`.
7. В разделе групп администратор назначает календарный график группе на нужный учебный год; вместе с назначением виден список дисциплин по семестрам.

После сохранения графика или назначения группы кэш динамического расписания сбрасывается. Следующий запрос `GET /api/schedule` сразу использует новые правила календаря.

## Особенности модели

- Старый отдельный механизм праздников не используется: праздник хранится кодом `*` в дневной сетке.
- Дни вне учебного года или пустые периоды Excel хранятся кодом `=`.
- График привязан к профилю обучения, а не только к коду специальности.
- Форма обучения графика должна совпадать с формой обучения группы при назначении графика.
- Название группы не является уникальным ключом; идентичные названия допустимы, если различаются другие параметры.
- Excel-импорт в первой реализации не предусмотрен: редактор заполняет данные через сайт.
