Веб-приложение для составления и ведения расписания

Техническое задание: веб-система составления и ведения учебного расписания§

Идентификатор: ТЗ-RSP-WEB-001
Версия: 0.1 — проект для редактирования заказчиком
Дата: 9 сентября 2026 года
Назначение: основа для обсуждения, оценки и разработки новой системы с возможностью последующего расширения.
Целевая установка: автономное веб-приложение на сервере заказчика, основная СУБД — PostgreSQL.
Основание: исследование файлов и экранов существующей системы «Расписание занятий и экзаменов», выполненное в проекте newrasp.

Документ задаёт поведение будущего продукта. Формулировка «система должна» относится к новой реализации. Подтверждённые возможности старой программы, проектные решения и ещё не выясненные правила обозначены отдельно. ТЗ не утверждает, что все предлагаемые операции были выполнены или проверены в старой программе.

Версия 0.1 предназначена для дополнений заказчика. Все требования базового объёма действуют как рабочая редакция до изменения документа; открытые вопросы не дают исполнителю права молча выбирать способ преобразования неоднозначных данных. Сроки, стоимость, состав команды и окончательный перечень расширений определяются после согласования объёма. Создание этого ТЗ само по себе не означает начало разработки или изменение работающего сайта.

Оглавление§

Назначение и ожидаемый результат§

Цель продукта§

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

Результат — устанавливаемая на инфраструктуре заказчика система: исходный код, PostgreSQL со схемой и миграциями, интерфейс, API, механизм импорта, автоматические проверки, установочный комплект, инструкции и проверенная процедура восстановления. Рабочие действия выполняются через браузер; установка Delphi, BDE, Paradox или клиентского SQL-приложения пользователям не требуется.

Что означает «такая же система»§

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

Побайтовая совместимость с Paradox, повторение всех пиктограмм, цветов и окон Windows, а также воспроизведение недоступного алгоритма старой программы в базовый объём не входят. Новый интерфейс должен решать те же задачи понятными веб-формами. Там, где старое поведение не установлено, ниже задано самостоятельное проектное правило или указан вопрос, который нужно разрешить.

Разграничение трёх существующих материалов§

Материал Роль в проекте
Исходная настольная программа и её Paradox-файлы Источник предметной области, данных и наблюдаемых интерфейсных возможностей
Технический отчёт и текущий просмотрщик SQLite на rsp.nsuemdev.ru Исследовательский инструмент: помогает изучать снимок, искать записи и сверять импорт
Веб-приложение по настоящему ТЗ Новый продукт для многопользовательского редактирования и эксплуатации на PostgreSQL

Нынешний просмотрщик не является готовым редактором расписания. Его API, общая HTTP Basic-учётная запись и файловая SQLite не должны автоматически становиться архитектурой нового продукта. Допускается повторное использование документированных правил чтения и проверки источников, русских описаний полей и проверочных наборов данных.

PostgreSQL и файловое хранение§

SYS-001. Основной источник рабочих данных — отдельная серверная PostgreSQL. Это не один файл, который приложение открывает как SQLite: доступ идёт по протоколу СУБД, а данные сохраняются в управляемом PostgreSQL каталоге/томе. Браузер обращается только к API приложения.

Файл exports/schedule.sqlite используется для первоначального импорта и сверки. Переносимость установки обеспечивается резервной копией PostgreSQL, архивом приватных файлов и конфигурацией приложения. Простое копирование работающего каталога PostgreSQL средствами файлового менеджера не является предусмотренным способом резервного копирования.

Приёмка SYS-001: после удаления контейнеров приложения и их повторного создания рабочие данные сохраняются; новая установка восстанавливается из документированного комплекта резервной копии без доступа к старой машине.

Основания, исходные данные и границы достоверности§

Материалы исследования§

Код источника Материал Назначение
SRC-01 Технический отчёт Сводная архитектура, словарь 88 полей, 59 интерфейсных возможностей, оговорки
SRC-02 Анализ экранов и screen/Screenshot_1.jpgScreenshot_15.jpg Наблюдаемые формы, поля и команды
SRC-03 Анализ связей Проверенные отношения, значения -1/0/NULL, групповые выборки
SRC-04 Схема экспорта (локальный материал), SQLite Точные имена и типы полей, доступные строки
SRC-05 Анализ форматов, анализ индексов Paradox, CP866, индексы и ограничения источников
SRC-06 Проверка групповых выборок (локальный материал), независимая сверка pxlib (локальный материал) Контрольные данные для миграции
SRC-07 Проверка текущего просмотрщика (локальный материал) Проверка чтения снимка через HTTPS; не подтверждает работу нового редактора

Исходники основной настольной программы и её основной исполняемый файл в исследованном наборе отсутствуют. Функциональность исследовалась по файлам и скриншотам; не проводилось выполнение всех команд старой программы. Связанная утилита Reorder.EXE подтверждает Windows x86, Delphi/VCL и BDE для этой утилиты; использование того же стека всей программой остаётся обоснованной гипотезой.

Контрольный объём снимка§

Показатель Значение
Основные таблицы Paradox 19
Все доступные строки 19 таблиц, включая вспомогательные 75 909
Строки Classes 15 111
Группы 554
Преподаватели 866
Аудитории 184
Дисциплины 5 068
Позиции плана 4 748
Связи групп с планами 21 419
Объекты текущей SQLite 19 исходных таблиц + 2 представления + _source_manifest

Контрольная SHA-256 SQLite: c9754cd457d4902429ccfcbf85cdf54c7307b0cfdf74f3ed6b6ead0f8ca207b0. Для следующего снимка значения и хеш должны вычисляться заново. 15 111 исходных записей нельзя заранее считать 15 111 отдельными календарными событиями: одна запись может кодировать повторение, и смысл периода ещё нужно подтвердить.

Особенности, влияющие на реализацию§

  1. Скриншоты относятся к сентябрю 2024 года; имя папки — 1_sem_2026_2027. Название каталога не устанавливает дату начала семестра.
  2. Classes.DayNo содержит 72 непустых кода в диапазоне 0–299. Формула даты, начало отсчёта, сочетание EachWeek и OddWeek не подтверждены.
  3. У 3 458 строк занятий GroupNo=-1; участники определяются по соответствующей позиции плана через GRPPLAN. Объединение не должно размножать одно занятие в нескольких физических копиях.
  4. ClassesInf имеет 15 111 доступных строк при счётчике 15 124 в заголовке источника. CLASSINF — другая, пустая таблица. Расхождение не разрешается добавлением 13 фиктивных записей.
  5. Assigned=True не означает полноту ресурсов: 2 339 таких записей не имеют аудитории. В новой модели временное размещение, полнота сведений и разрешение на публикацию разделяются.
  6. CourseNo и CourseCount нельзя превращать в номер фактического года обучения. Все прямые GROUPS.StreamNo равны -1; состав объединённых занятий не восстанавливается из справочника STREAMS.
  7. Свободные заметки могут содержать внутренние сведения и реквизиты онлайн-занятий. Для внешнего просмотра необходим отдельный разрешённый состав полей; значения заметок в это ТЗ не копируются.
  8. В двух кафедрах встречаются ссылки на факультеты 46 и 48, отсутствующие в справочнике. Импорт должен показывать такие связи как требующие сопоставления, а не создавать вымышленные факультеты.

Состав базовой версии и порядок изменения ТЗ§

Приоритеты и происхождение требований§

Обозначение Значение
B / базовое Обязательное требование первой полноценной рабочей версии
E / расширение Кандидат дальнейшего развития; не включается в стоимость и приёмку B автоматически
Q / уточнение Неопределённость источника или решение заказчика; указано допустимое поведение до её разрешения
Подтверждено данными Сущность, поле или связь действительно обнаружены в файлах
Наблюдается на экране Форма или команда видна; её выполнение не проверено
Проектное решение Требование к новой системе, независимо от устройства старой

Требования SYS, FR, DATA, CAL, MIG, API, SEC, NFR, OPS и ACC, если явно не помечены E, относятся к базовому объёму. Идентификаторы LEG обозначают пункты сопоставления старого и нового интерфейсов, а не дополнительные требования. Пункт Q не отменяет необходимость сохранить исходные данные и дать пользователю доступ к их исследованию.

Базовый объём B§

Направление Результат
Пользователи и права Индивидуальные учётные записи, роли, области ответственности, журнал действий
Учебная организация Справочники, группы и подгруппы, аудитории, преподаватели, дисциплины
Планирование Учебные периоды, календарь, звонки, планы, виды и объёмы нагрузки
Расписание Ручной редактор событий и повторений, неназначенные занятия, изменения ресурсов, обмен местами
Проверка Пересечения участников и ресурсов, жёсткие ограничения, полнота и предупреждения
Совместная работа Защита от перезаписи чужих изменений, разграничение кафедральных предложений и общего плана
Выдача результата Утверждённые версии, просмотр по группе/преподавателю/аудитории, печатный вид и контролируемая выгрузка
Перенос Воспроизводимый импорт SQLite, сохранение исходных значений, протокол расхождений и сопоставлений
Эксплуатация Автономная установка, резервное копирование, восстановление, мониторинг и обновления

Рабочие решения для версии 0.1§

  • Одна образовательная организация в одной установке; её подразделения и несколько учебных периодов. Полноценный коммерческий SaaS с арендаторами — расширение.
  • Русский интерфейс; часовой пояс организации по умолчанию Asia/Novosibirsk. Фактические даты нового расписания задаются явно.
  • Единственный редактируемый черновик в пределах учебного периода. История публикаций неизменяема. Несколько параллельных веток расписания — расширение.
  • Один основной преподаватель и одна аудитория на экземпляр занятия в B; отсутствие ресурса в черновике допускается с замечанием о неполноте. Несколько одновременно занятых аудиторий/преподавателей требуют отдельного расширения и проверки.
  • Состояния экземпляра: unplaced, scheduled, cancelled. «Опубликовано» относится к версии расписания, а «передано кафедре» — к отдельному процессу согласования.
  • Жёсткие пересечения ресурсов и участников внутри одной редакции нельзя сохранить как нормальное размещение. Черновики разных периодов независимы; пересечения с действующими публикациями других периодов отмечаются и блокируют новую публикацию, но не сохранение черновика. Несовпадение вместимости или назначения помещения — предупреждение, требующее осмысленного разрешения по правилам ниже.
  • Читатель по умолчанию входит в систему. Анонимное опубликованное расписание — явно включаемая настройка с отдельным составом выдаваемых полей.
  • Ручное составление является обязательным. Автоматический оптимизатор, интеграции с вузовскими системами, мессенджерами и внешней идентификацией в B не входят.

Как дополнять документ§

Изменять требования следует по идентификаторам: сохранять старый ID при уточнении смысла, выдавать новый при добавлении самостоятельной функции, а исключённое требование помечать как снятое с причиной. Для каждой новой функции обязательно определить роль, входные данные, влияние на существующие занятия и версии, ошибки, права, критерий приёмки и необходимость миграции схемы. Одной строки «добавить интеграцию» или «сделать автосоставление» недостаточно для оценки разработки.

Если новое решение конфликтует с рабочим правилом версии 0.1, менять нужно также связанные разделы данных, API, миграции и приёмки. Список открытых решений и шаблон карточки расширения приведены в конце документа.

Функциональные требования к новой веб-системе расписания§

Этот раздел задает поведение нового продукта, а не утверждает, что перечисленные проверки выполнялись в настольной программе. Основание: техническое описание (локальный материал), анализ 15 экранов, исходные таблицы и подготовленная SQLite-выгрузка.

Для прослеживаемости используются обозначения: ИД — соответствующие данные подтверждены в файлах; ИЭ — экран, поле или подписанная команда видны на скриншоте; НП — новое проектное правило, нужное веб-приложению. Наличие ИД или ИЭ подтверждает источник функции, но не весь алгоритм ее выполнения. Например, Stable найден в данных, а запрет редактирования закрепленного занятия вводится настоящим ТЗ. B — обязательная базовая поставка; E — последующее расширение, не являющееся условием приемки базовой поставки. Все требования ниже имеют приоритет B, кроме явно отмеченных E.

Система предназначена для учебных диспетчеров: вести справочники, формировать учебную потребность, вручную размещать занятия и экзамены, проверять занятость ресурсов, согласовывать изменения и выпускать версии расписания. Автоматическое построение полного расписания не включается в обязательную поставку. Не требуется воспроизведение визуального оформления Windows: требуется функциональная преемственность и понятная работа в браузере.

Термины и границы поведения§

  • Позиция плана — отдельная учебная потребность по дисциплине, подразделению и составу участников. Одна дисциплина может иметь несколько позиций плана.
  • Вид нагрузки — лекция, практика/семинар, лабораторная, зачет, экзамен или консультация. Код и название согласуются отдельно от старых буквенных обозначений.
  • Экземпляр занятия — одно конкретное занятие, которое после размещения имеет календарную дату и интервал начала/окончания. В базовой поставке у экземпляра один основной преподаватель и, для очного занятия, одна аудитория.
  • Серия — связанный набор экземпляров, созданных по правилу повторения. Серия не заменяет даты отдельных экземпляров.
  • Учебный поток занятия — явно выбранный набор групп/подгрупп, совместно участвующих в нагрузке. Административный поток — именованный классификационный набор групп; его членство само по себе не назначает группы на занятия.
  • Рабочее расписание — изменяемый вариант семестра. Публикация — отдельная неизменяемая версия, которую видят читатели. Редактирование рабочего варианта не изменяет опубликованный.
  • Назначение — активное размещение с состоянием scheduled; неназначенностьunplaced; отмена проведенияcancelled. Закрепление, кафедральное согласование и опубликованность не являются альтернативными значениями этого статуса.

Пользователи, доступ и рабочее пространство§

FR-001. Вход и завершение сеанса — B, НП§

Внутренние разделы доступны после входа в личную учетную запись. Пользователь может завершить сеанс; администратор — заблокировать учетную запись и отозвать ее активные сеансы. Пароль общего доступа уже развернутого просмотрщика не переносится в качестве системы прав нового продукта. При наличии публичного просмотра он действует только для опубликованных безопасных полей.

Приемка: пользователь входит и выходит; после блокировки ранее открытый сеанс перестает выполнять защищенные запросы. Прямой URL карточки или API без действующего сеанса не раскрывает внутренние данные.

FR-002. Роли и совмещение полномочий — B, НП§

Предусмотреть роли: администратор системы, центральный диспетчер, сотрудник кафедры, утверждающий/публикатор и читатель. Роли можно совмещать. Администратор управляет учетными записями, правами и техническими настройками; изменение учебных данных требует дополнительно назначенной роли диспетчера. Публикатор выпускает версии, но без роли диспетчера не меняет занятия. Читатель не изменяет данные.

Приемка: одна учетная запись с двумя ролями получает объединение разрешенных действий; снятие роли убирает соответствующие команды и API-права. Технический администратор без предметной роли не сохраняет изменение расписания.

FR-003. Область полномочий — B, НП§

Каждая предметная роль получает явно заданную область: все подразделения либо определенные кафедры/факультеты. Центральный диспетчер работает со всем назначенным ему контуром. Сотрудник кафедры ведет потребность своей кафедры, предлагает преподавателей и ограничения, обрабатывает переданные заявки; окончательное размещение и межкафедральные изменения выполняет центральный диспетчер. Для проверки занятости можно показывать занятые интервалы ресурсов вне области без закрытых комментариев.

Приемка: сотрудник кафедры A не изменяет план кафедры B, подменив идентификатор в запросе. Участие группы другого факультета в общем занятии не дает сотруднику прав редактирования всей этой группы.

FR-004. Матрица основных действий — B, НП§

Следующая матрица является обязательной исходной политикой; доступ дополнительно ограничивается областью FR-003.

Действие Администратор Диспетчер Кафедра Публикатор Читатель
Учетные записи и права Да Нет Нет Нет Нет
Общие справочники и семестры Только с ролью диспетчера Да Нет Нет Нет
Планы кафедры и предложения преподавателей Нет Да В своей области Просмотр Нет
Назначение, перенос, обмен, отмена Нет Да Заявка Нет Нет
Ограничения преподавателей Нет Да Предложение в своей области Просмотр Нет
Закрепление и снятие закрепления Нет Да Нет Нет Нет
Утверждение и публикация Нет Только при совмещении Нет Да Нет
Закрытые заметки Отдельное предметное право По области По области По области при предоставленном праве Нет
Просмотр опубликованного расписания Да Да Да Да Да
Технический просмотр данных Отдельное право Отдельное право Нет Нет Нет

Приемка: для каждой строки выполняются положительный тест разрешенной роли и отрицательный тест запрещенной; скрытие кнопки без серверного запрета не считается выполнением требования.

FR-005. Контроль доступа к выгрузкам и журналам — B, НП§

Те же права действуют для таблиц, карточек, поиска, выгрузок, печати, фоновых заданий и истории. Журнал действий доступен диспетчеру по его области и специально уполномоченному аудитору; чтение журнала не должно открывать закрытые старые значения пользователю без права чтения заметок. Объем прав экспортирующего проверяется при формировании файла и получении результата.

Приемка: пользователь без доступа к кафедре не получает ее закрытые строки через CSV или поиск; после снятия права не скачивает ранее созданную защищенную выгрузку по сохраненной ссылке.

FR-006. Навигация и контекст — B, ИЭ + НП§

Основные разделы: обзор, расписание, неназначенные занятия, учебный план, справочники, ограничения, согласование, публикации, импорт/диагностика и просмотр данных. В заголовке постоянно видны семестр и режим «рабочий вариант»/«опубликованная версия». Фильтры списка и выбранная неделя сохраняются при возврате из карточки; пустая выдача отличается от ошибки загрузки.

Приемка: переход из расписания в преподавателя и обратно сохраняет неделю и группу. При переключении семестра интерфейс очищает несовместимые фильтры и не показывает данные старого семестра под новым заголовком.

Справочники и учебные ресурсы§

FR-007. Единое поведение справочников — B, ИД + ИЭ + НП§

Справочники имеют поиск без учета регистра русских букв, фильтры, сортировку, страницы результатов, карточку, создание, изменение и архивирование. Использованные записи не удаляются физически через обычный интерфейс. У каждой записи есть устойчивый идентификатор; переименование не разрывает связи. Повторяющиеся названия допускаются только там, где это содержательно возможно, и различаются дополнительными реквизитами.

Приемка: поиск «ИВАН» находит «Иванов»; архивная аудитория сохраняется в старой версии, но недоступна для нового назначения. Удаление используемого справочника отклоняется с перечнем зависимостей.

FR-008. Факультеты — B, ИД + ИЭ§

Вести полное и краткое название факультета, действительность и внешний код при наличии. Факультет используется для группировки кафедр, групп и фильтров. Пустые названия и буквальные артефакты импорта вроде HTML-сущностей отмечаются для проверки; система не создает подразделение из такого значения молча.

Приемка: переименование меняет актуальное отображение связанных карточек без потери связей. Архивирование факультета с активными группами требует предварительного решения о них; автоматического переноса в произвольный факультет нет.

FR-009. Кафедры — B, ИД + ИЭ§

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

Приемка: кафедра с неизвестной ссылкой на факультет при импорте попадает в диагностику. Изменение факультета не дает прежним кафедральным пользователям права на новое подразделение и не меняет автоматически владельца исторической публикации.

FR-010. Курсы и уровни обучения — B, ИД + ИЭ§

Вести название курса, уровень обучения и, если заполнен, фактический год обучения как отдельное значение. Числовой идентификатор не показывать вместо курса. В старых данных CourseNo=6 может соответствовать первому курсу магистратуры; такие записи отображаются по названию и согласованному сопоставлению.

Приемка: группа с указанным классификатором показывается как «1-й курс магистратуры», а не «6-й курс». Отсутствие подтвержденного фактического года не заполняется номером старого ключа.

FR-011. Учебные группы — B, ИД + ИЭ§

Карточка группы включает обозначение, факультет, курс/уровень, учебный период действительности, численность и состав подгрупп. Предусмотреть просмотр плана и расписания группы. Численность — целое неотрицательное число либо отдельное состояние «неизвестна»; неизвестность отличается от нуля. Для совпадающих обозначений показывать период и факультет.

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

FR-012. Подгруппы и пересечения участников — B, ИД + ИЭ + НП§

В базовой версии группа имеет одно разбиение на подгруппы с явным признаком подтверждения непересекаемости. Для подгруппы задаются обозначение и численность либо неизвестное значение. Занятие всей группы пересекается с любой ее подгруппой; занятия разных подгрупп могут идти одновременно только внутри подтвержденного непересекающегося разбиения. Если непересекаемость неизвестна, система консервативно считает такие подгруппы пересекающимися. Индивидуальные списки студентов и несколько пересекающихся схем деления — отдельное расширение.

Приемка: параллельные занятия подгрупп 1 и 2 разрешаются при подтвержденной непересекаемости и разных преподавателях/аудиториях; при неизвестной непересекаемости либо участии всей группы в том же интервале сохранение блокируется. Сумма известных численностей подгрупп не превышает численность группы без явной диагностики.

FR-013. Преподаватели — B, ИД + ИЭ§

Вести отображаемое ФИО, кафедру, должность, степень, звание, состояние действительности и закрытую служебную заметку. Из карточки открываются нагрузка, расписание и ограничения. Полные тезки допустимы; различаются кафедрой и устойчивым идентификатором. Учетная запись преподавателя, если появится позднее, не заменяет его предметную карточку.

Приемка: можно создать двух тезок без слияния. Архивный преподаватель остается в истории, но не назначается на новое занятие; заметка не появляется в публичной карточке расписания.

FR-014. Дисциплины — B, ИД + ИЭ§

Вести полное и краткое название дисциплины, внешний код и признак действительности. Сетка использует краткое название с доступным полным названием в карточке. Несколько позиций плана одной дисциплины допустимы; их объединение по совпадению текста запрещено без специальной операции с предварительным просмотром последствий.

Приемка: две позиции одной дисциплины для разных кафедр сохраняются независимо; изменение сокращения не меняет объем и участников. Пустое полное название отклоняется.

FR-015. Аудитории — B, ИД + ИЭ + НП§

Вести обозначение, корпус/адрес как реквизит, вместимость, назначение, закрепленную кафедру, действительность и закрытую заметку. Различать тип аудитории и вид занятия. Поддержать как минимум лекционные, практические, лаборатории, компьютерные и лингафонные аудитории; соответствие старым кодам согласовать при импорте. Для ресурса доступны расписание и ограничения.

Приемка: аудитория с отсутствующей вместимостью имеет пометку «неизвестна», а не нулевую вместимость. Одинаковые номера в разных корпусах различимы; выбор «Любая» существует в фильтре и не сохраняется как тип помещения.

FR-016. Административные потоки — B, ИД + ИЭ + НП§

Разрешить создать именованный набор групп, добавить/исключить группу и использовать набор для фильтрации или первоначального выбора участников. Изменение административного потока не изменяет состав уже созданных позиций плана и занятий. Название с учебным годом является текстом, а не источником календарных дат.

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

FR-017. Классификаторы видов нагрузки — B, ИД + ИЭ + НП§

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

Приемка: неизвестный код попадает в очередь сопоставления; сходный код аудитории не используется для автоматической расшифровки вида занятия. Архивирование вида не меняет уже опубликованное расписание.

FR-018. Дни и сетки звонков — B, ИД + ИЭ + НП§

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

Приемка: слот 08:00–09:30 сохраняется, окончание раньше начала отклоняется. Время окончания не вычисляется как начало следующей пары; изменение сетки не сдвигает существующие датированные занятия молча.

Семестр и учебная потребность§

FR-019. Параметры семестра — B, ИЭ + НП§

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

Приемка: нельзя сохранить окончание раньше начала. Сокращение периода не удаляет занятия за новыми границами: показывает их и блокирует подтверждение до согласованного решения.

FR-020. Позиция учебного плана — B, ИД + ИЭ§

Создавать отдельную позицию по дисциплине, ответственной кафедре, семестру и описанию. Карточка содержит участников, виды/объемы нагрузки, требования к аудиториям, закрепление и связи с экземплярами. Копирование позиции допускается с предварительным выбором переносимых полей; исторические назначения, публикации и согласования не копируются как завершенные.

Приемка: две позиции с одинаковой дисциплиной, но разными участниками сохраняются. Копия имеет новый идентификатор и не делит изменяемые занятия с оригиналом.

FR-021. Состав учебного потока — B, ИД + ИЭ + НП§

Для каждого вида нагрузки явно выбирать группы целиком либо отдельные подгруппы. Общая лекция нескольких групп является одним занятием с несколькими участниками. Выбор всей группы и ее подгруппы одновременно нормализуется до всей группы после предупреждения; численность не суммируется повторно. Состав не выводится автоматически из административного потока после создания.

Приемка: общая лекция двух групп отображается у каждой группы по одному разу и занимает одного преподавателя один раз. Повторный выбор той же подгруппы не удваивает состав и объем нагрузки.

FR-022. Виды, объемы и деление нагрузки — B, ИД + ИЭ + НП§

Для каждого вида указывать общий объем за период, при необходимости недельную норму, число/состав подгрупп и ожидаемую длительность одного занятия. Недельная норма служит плановым ориентиром, а не скрытым правилом автоматического умножения. Экзамены, зачеты и консультации используют тот же механизм явного объема с собственными длительностями.

Приемка: план может иметь лекции без лабораторных и отдельный экзамен. Отрицательные объемы запрещены; несовместимый набор «проводить по подгруппам» без выбранных подгрупп не проходит сохранение.

FR-023. Единицы учета — B, НП§

В настройках семестра указать длительность академического часа, например 45 минут, и допустимый шаг объема. Отличать академические часы, астрономическую длительность и число экземпляров. В карточке явно показывать используемую единицу. Механизм расчета объема экземпляра определяется длительностью учебной части; перерыв не засчитывается автоматически.

Приемка: два академических часа по 45 минут дают 90 минут учебного времени; наличие перерыва между слотами не увеличивает нагрузку. Импортированное PlanQty с невыясненной единицей не объявляется подтвержденным количеством пар.

FR-024. Формирование неназначенных занятий из плана — B, НП§

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

Приемка: из 18 академических часов при длительности 2 часа формируются 9 экземпляров; неподелившийся остаток требует решения и не округляется молча. Повтор одного подтвержденного запроса не создает дубликаты.

FR-025. Изменение плана с существующим расписанием — B, НП§

Изменение объема, участников или длительности сначала показывает затронутые экземпляры, конфликты и опубликованные ссылки. Можно применить изменение к будущим выбранным экземплярам либо оставить их и зафиксировать расхождение. Сокращение объема не удаляет проведенные/прошедшие или опубликованные экземпляры; избыток решается отдельными отменами или корректировкой плана.

Приемка: уменьшение с 20 до 16 часов при 20 размещенных часах дает предупреждение об избытке и перечень занятий. Без подтвержденного выбора четыре часа не исчезают автоматически.

FR-026. Требования к помещениям и ресурсам — B, ИД + ИЭ + НП§

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

Приемка: план без перечня помещений позволяет обычный поиск по типу/вместимости. При заданном обязательном перечне аудитория вне него не назначается; правило не подменяется вероятным смыслом пустой старой PLANROOM.

FR-027. Баланс учебной нагрузки — B, НП§

Показывать по плану, виду и участнику: требуемый объем, созданный, размещенный, неназначенный, отмененный и отклонение. Общий поток считается одним проведением для преподавателя, но участием для каждой включенной группы; эти показатели подписываются раздельно. Отмененные экземпляры не входят в обеспеченный объем, а публикация сама по себе не означает проведение занятия.

Приемка: отмена одной двухчасовой лекции уменьшает размещенный объем на два часа и показывает дефицит; не удваивает преподавательскую нагрузку из-за двух групп. Статистика содержит переход к исходным занятиям.

Календарь, состояния и ограничения§

FR-028. Явные даты и интервалы — B, НП§

Размещенное занятие имеет конкретную календарную дату, время начала и окончания в часовом поясе семестра. Интервалы рассматриваются как [начало, окончание): соседние занятия без пересечения допустимы. Переход через полночь в базовом редакторе запрещен; нестандартное вечернее занятие должно завершаться в те же локальные сутки. Старый DayNo не является датой нового события.

Приемка: 08:00–09:30 и 09:30–11:00 не конфликтуют; 08:00–09:30 и 09:20–10:50 конфликтуют. Неоднозначная импортированная дата не появляется на календаре по предположению.

FR-029. Повторяющиеся серии — B, ИЭ + НП§

Поддержать еженедельное повторение, повторение через неделю от выбранной опорной даты и явный набор дат в пределах семестра. До сохранения показывать все создаваемые экземпляры, исключения и конфликты. Метки «четная/нечетная» вычисляются из настроек учебных недель, а не из номера ISO-недели без соглашения.

Приемка: правило «каждые две недели от 7 сентября» дает проверяемый список дат; изменение опорной даты обновляет предпросмотр. Система не создает экземпляры после окончания семестра и не размещает только бесконечное абстрактное правило.

FR-030. Исключения и области изменения серии — B, НП§

При редактировании серии выбирать «только это занятие», «это и последующие» либо «вся серия в выбранном диапазоне». Праздник и нерабочий день исключаются по календарю; восстановление отдельного дня выполняется явно. Индивидуально измененный экземпляр помечается исключением и не теряет изменения при последующем редактировании шаблона без подтвержденного решения.

Приемка: перенос одной лекции не переносит остальные. Массовое изменение показывает ранее измененные исключения и предлагает сохранить их либо включить; молчаливого сброса исключений нет.

FR-031. Состояния экземпляра — B, ИД + ИЭ + НП§

unplaced означает отсутствие активного размещения и может содержать отдельные пожелания даты/ресурсов; занятость не создает. scheduled в рабочем варианте требует участников, даты, интервала и формата; преподаватель и аудитория могут временно отсутствовать. Размещение занимает только известные ресурсы. Перед публикацией для обычного учебного занятия обязателен преподаватель, для очного — также аудитория; для дистанционного аудитория не требуется. Иные исключения допускаются только утвержденной политикой вида занятия, без произвольного обхода. cancelled сохраняет прежнее размещение для истории, причину и автора, но не занимает ресурсы. Состояние не определяется заполненностью одного поля.

Приемка: неназначенная запись с желаемым временем не блокирует аудиторию. Черновое размещение без преподавателя сохраняется с замечанием, занимает участвующие группы и известную аудиторию, но блокирует публикацию. Отсутствие аудитории в старой назначенной записи не объявляется доказанной ошибкой исходной системы; импорт проверяет достаточность для новой политики.

FR-032. Жесткие конфликты — B, НП§

Перед размещением, переносом, обменом и изменением участников проверять пересечения одного преподавателя, аудитории и пересекающихся групп/подгрупп, обязательные ограничения ресурса, действительность справочников и границы семестра. Жесткое пересечение внутри той же рабочей редакции блокирует сохранение и показывает оба объекта и причину. Другие рабочие черновики независимы и не блокируют сохранение. Пересечение с активной публикацией другого учебного периода показывается как замечание, блокирующее публикацию, но допускающее сохранение черновика. Перед активацией публикации выполняется повторная атомарная проверка жестких пересечений по кандидату и всем действующим публикациям других периодов.

Приемка: два диспетчера не сохраняют разные занятия одному преподавателю на один интервал в одной редакции даже при одновременной отправке. Независимый черновик другого периода не мешает сохранению. Пересечение с опубликованным занятием другого периода видно вне зависимости от фильтра, допускает сохранение черновика и блокирует его публикацию; одновременная активация конфликтующих публикаций не проходит. Закрытые примечания конфликтующего занятия не раскрываются.

FR-033. Предупреждения и обоснованные исключения — B, НП§

Недостаточная или неизвестная вместимость, нежелательный тип помещения, отклонение от недельной нормы и мягкое пожелание преподавателя показываются как отдельные предупреждения. Если предупреждение допускает обход, диспетчер вводит обоснование, сохраняемое в истории. Неизвестная численность не считается нулем. Для обязательного требования из FR-026 обход не предлагается.

Приемка: размещение группы 30 человек в аудитории на 24 требует причины; отказ от ввода оставляет данные без изменения. Одно подтверждение мягкого предупреждения не отключает проверку жестких пересечений.

FR-034. Недоступность и пожелания — B, ИД + НП§

Для преподавателя и аудитории создавать разовый или повторяющийся интервал недоступности с периодом действия, видом «жесткий запрет»/«пожелание» и закрытой причиной. После изменения ограничения показывать затронутые занятия; жесткое ограничение, пересекающееся с размещением, не активируется до решения конфликтов. Предложения кафедры требуют принятия диспетчером для включения в действующие ограничения.

Приемка: принятый жесткий запрет исключает помещение из свободных; пожелание дает предупреждение. Просмотр занятости другим подразделением сообщает факт недоступности без служебной причины.

Ручное составление и изменение расписания§

FR-035. Основной редактор — B, ИЭ + НП§

Все операции доступны через кнопки и форму карточки: дата, слот/интервал, участники, преподаватель, формат, аудитория, комментарии и область изменения серии. Форма показывает текущие значения, несохраненные изменения и результат проверки. Сохранение завершается явным подтверждением факта записи; сетка обновляется после успешного ответа.

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

FR-036. Назначение из списка — B, ИЭ + НП§

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

Приемка: число экземпляров не меняется при назначении; баланс перемещает объем из неназначенного в размещенный. Двойное нажатие «Назначить» не создает две записи.

FR-037. Перенос занятия — B, ИЭ + НП§

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

Приемка: успешный перенос освобождает старый интервал и занимает новый в одной операции; при конфликте сохраняется старый. История связывает обе позиции с одним экземпляром.

FR-038. Обмен местами — B, ИЭ + НП§

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

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

FR-039. Смена аудитории — B, ИЭ + НП§

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

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

FR-040. Замена преподавателя — B, ИЭ + НП§

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

Приемка: новый преподаватель получает занятие в своем рабочем расписании, прежний перестает быть занят этим экземпляром. Занятый или архивный кандидат не сохраняется в scheduled.

FR-041. Снять назначение и отменить проведение — B, ИЭ + НП§

Разделить две команды. «Снять назначение» переводит экземпляр в unplaced, освобождает ресурсы и возвращает объем в очередь планирования; прежние значения остаются в истории. «Отменить проведение» переводит в cancelled, сохраняет карточку с причиной и не создает автоматически замену. «Восстановить» заново проходит обычные проверки; физического удаления использованного занятия нет.

Приемка: после снятия экземпляр есть в неназначенных; после отмены — в отмененных, но не в обычной очереди. Восстановление на уже занятый интервал отклоняется.

FR-042. Закрепление — новое правило для Stable — B, ИД + ИЭ + НП§

В новом продукте «Закреплено» означает защиту от изменения времени, преподавателя, аудитории, участников, формата и состояния без предварительного снятия закрепления диспетчером с причиной. Закрепление позиции плана распространяется на выбранные связанные экземпляры через явный предпросмотр. Оно не означает опубликованность или факт проведения. Старый Stable до согласования хранится как исходный признак и не включает эту блокировку автоматически.

Приемка: закрепленный экземпляр нельзя перенести ни из формы, ни массовым API. После снятия закрепления с причиной перенос разрешен при отсутствии других конфликтов; оба действия видны в истории.

FR-043. История и восстановление — B, НП§

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

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

FR-044. Одновременная и массовая работа — B, НП§

Форма сохраняется только относительно версии записи, которую пользователь открыл. Если запись изменил коллега, показывать различия и предлагать перечитать/повторить операцию; молчаливое сохранение поверх запрещено. Массовое действие сначала показывает состав и результат проверки, затем по умолчанию выполняется целиком. Альтернативное частичное применение допускается только после явного выбора с протоколом успешных и отклоненных строк.

Приемка: две открытые формы не теряют первое сохранение. Устаревший предварительный просмотр массовой операции перепроверяется при подтверждении; возникший конфликт не приводит к частично скрытому результату.

FR-045. Публичные комментарии и закрытые заметки — B, ИД + ИЭ + НП§

В карточке разделить публичный комментарий расписания и закрытую служебную заметку. Причины ограничений, заметки преподавателей/аудиторий и импортированные свободные тексты по умолчанию закрыты. Участник с правом заметок видит все исходные поля в защищенном просмотре; перенос текста в публичный комментарий выполняется явным отдельным действием. HTML и код из текста не исполняются.

Приемка: закрытый текст отсутствует в публичной версии, печати, подсказках и сетевых ответах читателя. Вставка HTML-разметки выводится безопасным текстом; изменение публичности записывается в историю.

Просмотры и поиск§

FR-046. Общее расписание — B, ИЭ + НП§

Предусмотреть недельную сетку и альтернативный список с фильтрами по периоду, факультету, кафедре, курсу, группе, преподавателю, аудитории, дисциплине, виду и состоянию. Ячейка показывает время, сокращение дисциплины, вид, преподавателя, аудиторию и обозначение участников. Цвет дополнен текстом/значком, чтобы смысл не зависел только от цвета. Детали доступны нажатием и клавиатурой.

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

FR-047. Расписания группы, преподавателя и аудитории — B, ИЭ + НП§

Из карточки занятия открывать три ресурсных представления на той же неделе. Группа видит занятия всей группы и своих подгрупп с обозначением состава; преподаватель — личные занятия и доступную ему недоступность; аудитория — занятые интервалы и ограничения без закрытых причин для неуполномоченных. Доступны неделя, диапазон и список.

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

FR-048. Поиск свободной аудитории — B, ИЭ + НП§

Поиск принимает конкретную дату или набор дат, интервал/слот, вместимость, назначение, корпус и при необходимости кафедру. На полный интервал отдельно проверяются занятость в своей рабочей редакции, жесткая недоступность и пересечения с активными публикациями других учебных периодов; другие независимые черновики не считаются занятостью этой редакции. Выдача явно различает «свободна в рабочей редакции», «пересекается с действующей публикацией другого периода», «подходит требованиям» и предупреждения. Пересечение с чужой действующей публикацией допускает предварительное назначение в черновике с блокирующим публикацию замечанием по FR-032. Показ результата не резервирует помещение.

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

FR-049. Очередь неназначенных — B, ИЭ + НП§

Вывести отдельный список unplaced с фильтрами по подразделению, группе, дисциплине, виду, ответственному и причине отсутствия назначения. Показывать недостающие реквизиты, желательные параметры и общий объем. Исключенные при импорте неоднозначные строки имеют отдельную очередь диагностики; они не смешиваются с корректной учебной потребностью без метки.

Приемка: снятое назначение сразу появляется в очереди, отмененное проведение не появляется. Счетчик списка совпадает с числом экземпляров по фильтру; ошибка импорта не превращается в автоматически готовое к размещению занятие.

FR-050. Защищенный просмотр данных — B, НП§

Предоставить технический раздел чтения: каталог разрешенных сущностей/импортных таблиц, описание полей, количество строк, поиск, фильтры, сортировку, страницы и карточку строки со ссылками на связанные объекты. Показывать все предметные исходные поля пользователю с соответствующим правом, различая NULL, пустую строку, ноль, ложь и старый -1. Секреты аутентификации и технические служебные таблицы не входят в этот каталог.

Приемка: можно открыть пустую таблицу, найти текст кириллицей и увидеть исходные DayNo/флаги без выдуманных дат. Раздел не содержит SQL-консоли, команд записи или незащищенной ссылки на весь файл базы.

Согласование и публикация§

FR-051. Передача на кафедру — новое правило — B, ИЭ + НП§

Команда создает заявку кафедре по позиции плана или набору экземпляров: причина, требуемое действие, ответственный и срок при необходимости. Состояния заявки: «открыта», «предложение подготовлено», «принята», «возвращена», «закрыта без изменения». Кафедра предлагает преподавателя, условия или исправление потребности; диспетчер принимает изменения с обычными проверками. Передача сама по себе не снимает назначение и не освобождает ресурс.

Приемка: после передачи существующее занятие остается в расписании; ответ кафедры не меняет его до принятия. Сотрудник другой кафедры не отвечает на заявку путем прямого обращения к ее идентификатору.

FR-052. Рабочий вариант и подготовка выпуска — B, НП§

Для семестра в базовой версии используется один действующий рабочий вариант. Перед выпуском формируется кандидат версии с зафиксированным составом и отчетом проверки: конфликты, неназначенный объем, неполные данные, предупреждения и изменения относительно предыдущей публикации. Неназначенные занятия не выдаются за опубликованные; отчет об остатке обязателен для утверждающего.

Приемка: исправление в рабочем варианте не меняет предыдущую публикацию. Кандидат с жестким конфликтом или отсутствующими обязательными ресурсами размещенного занятия не утверждается. Выпуск с согласованным остатком неназначенного объема допускается с явным решением и комментарием утверждающего; этот остаток не публикуется как полноценное расписание.

FR-053. Утверждение и неизменяемая публикация — B, НП§

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

Приемка: одновременное чтение во время выпуска не смешивает старые и новые занятия. Измененный после проверки рабочий вариант не публикуется под прежним неподтвержденным кандидатом; требуется новая проверка.

FR-054. Доступ читателей и отмены в выпуске — B, НП§

По умолчанию просмотр требует авторизации; публичное чтение включается отдельно для выбранного семестра/версии. В публикацию входят только безопасные поля расписания. Отмены и переносы после предыдущего выпуска видны как изменения; нерасписанная потребность, заявки кафедр и внутренние причины не выдаются читателям. В заголовке присутствуют версия и время публикации.

Приемка: после новой публикации перенесенное занятие отображается в новой позиции с доступным указанием изменения. Публичный URL рабочего варианта не открывает черновик; отключение публичного режима закрывает и API той же версии.

Обмен, вывод и справка§

FR-055. CSV-выгрузка — B, НП§

Выгружать текущую разрешенную выборку справочника, плана или расписания в CSV с понятными заголовками, UTF-8, указанием версии/семестра и единиц измерения. Экспорт сохраняет фильтры и использует безопасную обработку значений, которые табличные редакторы могут интерпретировать как формулы. В базовую поставку не входит воспроизведение неизвестного старого формата обмена.

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

FR-056. Управляемый импорт — B, ИД + НП§

Интерфейс импорта принимает подготовленный пакет миграции и согласованные CSV-шаблоны справочников/планов. До записи показываются сопоставление столбцов, новые/измененные строки, ошибки ссылок, неоднозначные значения и итоговые количества. Пользователь подтверждает конкретный предварительный результат; исходный файл и протокол доступны уполномоченному для сверки. Повторный импорт того же пакета не создает дубликаты.

Приемка: строки с неизвестным видом занятия, неясной единицей часов или нерасшифрованным DayNo остаются в диагностике. Импорт не выбирает дату из имени каталога и не публикует данные автоматически.

FR-057. Печать и сохранение PDF браузером — B, НП§

Предоставить печатное представление списка/недельной сетки группы, преподавателя или аудитории. Включать заголовок, диапазон, часовой пояс, номер версии, обозначения отмен и дату формирования. Не включать панели редактирования и закрытые заметки. Базовый механизм — печать браузера, включая пользовательское сохранение в PDF; отдельный серверный генератор сложных форм не обязателен.

Приемка: согласованные недельные примеры печатаются на A4 без обрезанных строк, с повторяемыми заголовками таблицы. Печать черновика содержит заметную пометку, исключающую смешение с утвержденным выпуском.

FR-058. Встроенная справка и объяснение ошибок — B, ИЭ + НП§

Предоставить краткое руководство по пути «справочники → план → неназначенные → назначение → проверка → публикация». Для статусов, закрепления, потоков, единиц нагрузки и областей изменения серии дать контекстные пояснения. Ошибка сообщает, что не выполнено и какие объекты мешают, без технической трассировки. Раздел «О системе» показывает версию приложения и ссылку на руководство.

Приемка: новый диспетчер по руководству размещает тестовую лекцию и передает ее к выпуску. Сообщение о конфликте открывает доступную карточку/интервал, а ошибка сервера не показывает пароль, SQL или закрытые данные.

FR-059. Перетаскивание в сетке — E, НП§

Дополнительный способ переносить или назначать занятия мышью/касанием. Перетаскивание использует те же права, выбор области серии, проверки, историю и подтверждение, что форма FR-035. Визуальный предварительный перенос не становится сохраненным до успешного ответа. Обязательная базовая поставка выполняет эти операции формами без этого механизма.

Приемка расширения: перенос закрепленного или конфликтующего занятия возвращает карточку в исходную позицию с объяснением. Все действия остаются доступны клавиатурой; особого обхода проверок через перетаскивание нет.

FR-060. Помощник автоматического размещения — E, НП§

Возможное расширение формирует предложения размещения неназначенных занятий для выбранного контура с объяснением непримененных ограничений и невозможных случаев. Предложение сохраняется отдельно и принимается диспетчером после просмотра; не меняет опубликованное расписание и не снимает закрепления. Конкретный оптимизатор, целевая функция и гарантии качества определяются отдельным ТЗ после проверки ручного процесса.

Приемка расширения: задаче без допустимого размещения соответствует отчет о препятствиях, а не расписание с нарушением жестких запретов. Принятие предложения повторяет актуальные проверки; изменения коллег после расчета не перезаписываются.

Сквозные сценарии обязательной приемки§

  1. Общая лекция. Диспетчер создает позицию плана для двух групп, задает 18 академических часов, получает девять неназначенных двухчасовых экземпляров, формирует календарную серию, выбирает преподавателя и аудиторию. Каждая группа видит девять занятий; нагрузка преподавателя равна 18 часам, не 36. Попытка одновременно поставить другой предмет всей первой группе блокируется.
  2. Деление на подгруппы. Для одной группы создаются две практики по подгруппам в одно время с разными преподавателями/помещениями. Они разрешаются; размещение общей лекции группы в тот же интервал запрещается. Неизвестная численность одной подгруппы вызывает отдельное предупреждение при подборе помещения.
  3. Перенос опубликованного занятия. Диспетчер переносит один экземпляр серии и указывает причину. Активная публикация остается прежней. Публикатор видит различия, утверждает новый кандидат; после выпуска читатель получает новую версию целиком и видит изменение.
  4. Отмена и восстановление. Отмена освобождает ресурсы, сохраняет причину и показывает дефицит плана. Другой диспетчер занимает интервал. Восстановление отмененного занятия на прежнее место блокируется, пока не выбрано допустимое новое размещение.
  5. Кафедральная заявка. Диспетчер передает запрос о замене преподавателя. Сотрудник нужной кафедры предлагает кандидата, сотрудник другой кафедры не может изменить заявку. После принятия меняется преподаватель; до принятия расписание и занятость остаются прежними.
  6. Параллельная работа. Два диспетчера открывают один экземпляр. Первый сохраняет перенос, второй пытается сменить аудиторию по старой версии. Система сообщает об изменении коллегой и не теряет перенос; повторное сохранение после перечитывания проходит новые проверки.
  7. Неоднозначный импорт. Загружается набор со старым кодом дня, неполной ссылкой на факультет, отрицательным служебным ключом и закрытыми текстовыми полями. Подтвержденные справочники проходят проверку; неоднозначные строки доступны в защищенной диагностике, на календарь и в публикацию не попадают. Исходные значения сохранены для сверки.
  8. Безопасная публикация. В карточке существуют публичный комментарий и закрытая заметка. Публикатор выпускает расписание; читатель и неавторизованный пользователь при включенном публичном режиме получают только публичный комментарий. Закрытая заметка отсутствует также в API, CSV, печати и старых значениях истории, доступных читателю.

Решения для обсуждения владельцем продукта§

Эти вопросы не отменяют определенные выше исходные правила и предназначены для следующей редакции ТЗ: подтвердить длительность академического часа и единицы старого PlanQty; точное соответствие буквенных видов; календарную расшифровку старых занятий; возможность одновременного участия студента в нескольких схемах подгрупп; потребность в комиссиях/нескольких помещениях; обязательность двух разных людей для редактирования и публикации; отдельные роли преподавателя/студента; кампусы и время перехода между корпусами. По умолчанию применяется описанная базовая модель, а неподтвержденные исторические значения остаются в диагностике.

Модель данных PostgreSQL§

Принципы и границы модели§

DATA-01. Рабочая база нового приложения — PostgreSQL. Файлы Paradox и schedule.sqlite служат источниками миграции и контрольными снимками. Новые действия пользователя не изменяют исходные файлы. SQLite не становится второй рабочей базой: параллельная запись в две системы не входит в базовый объем.

DATA-02. Основной объект планирования — учебный период организации: семестр, экзаменационная сессия либо другой явно заданный диапазон дат. Общие справочники имеют устойчивые идентификаторы; планы, состав участников, серии, экземпляры занятий и правила ограничений принадлежат конкретной редакции расписания периода. Ссылка из занятия на план другой редакции запрещена составным внешним ключом. Проверять это только на уровне формы недостаточно.

DATA-03. Для бизнес-сущностей использовать uuid как PK; исходные числовые ключи хранить отдельно в реестре происхождения. Для записей аудита и технических журналов допустим bigint GENERATED ... AS IDENTITY. Совпадение названия дисциплины, кафедры, группы или ФИО не является основанием для объединения объектов. Все технические даты создания и изменения — timestamptz; текст — UTF-8. Денежные величины не требуются; объем нагрузки хранится точным numeric, а не float.

Общие поля изменяемой сущности: id, organization_id, created_at, updated_at, created_by_id, updated_by_id, row_version bigint NOT NULL DEFAULT 1. У архивируемых справочников дополнительно archived_at; у сущностей редакции — revision_id и logical_id, сохраняющий идентичность объекта при создании следующей редакции. Уникальность (revision_id, logical_id) обязательна. Значения row_version изменяются сервером.

DATA-04. Удаление используемого справочника запрещено через ON DELETE RESTRICT; пользователь архивирует запись. Она исчезает из списков нового выбора, но сохраняется в существующих планах и истории. Физическое каскадное удаление допустимо только для технических дочерних строк непубличного временного набора, например бронирований внутри полностью откатываемой транзакции. Публикации и завершенные журналы импорта не удаляются обычными операциями интерфейса.

Справочники и учебная структура§

Упрощенная карта основных связей; таблицы участников, аудита и бронирований раскрыты ниже:

Сокращённая модель данных
Сокращённая модель данныхВсе 13 связей исходной ER-схемы. Стрелка идёт от одной родительской записи к нулю или нескольким дочерним; полный исходный код доступен под схемой. containsversionscontainsrequiresincludesparticipatesgeneratesexpandsattendsparticipatesreservesoccupiedfrozen_sourceorganizationОрганизацияacademic_periodУчебный периодschedule_revisionРедакция расписанияcurriculum_planУчебная потребностьplan_componentВид и объём нагрузкиlesson_seriesСерия занятийlesson_occurrenceЭкземпляр занятияresource_bookingЗанятость ресурсаcomponent_participantУчастник вида нагрузкиacademic_groupУчебная группаoccurrence_participantУчастник занятияschedule_publicationПубликация расписанияschedulable_resourceРесурс расписания

Все связи: одна родительская запись → 0…n дочерних. Названия отношений сохранены из исходной схемы. На узком экране схему можно прокручивать по горизонтали.

Исходный код схемы
erDiagram
    organization ||--o{ academic_period : contains
    academic_period ||--o{ schedule_revision : versions
    schedule_revision ||--o{ curriculum_plan : contains
    curriculum_plan ||--o{ plan_component : requires
    plan_component ||--o{ component_participant : includes
    academic_group ||--o{ component_participant : participates
    plan_component ||--o{ lesson_series : generates
    lesson_series ||--o{ lesson_occurrence : expands
    lesson_occurrence ||--o{ occurrence_participant : attends
    academic_group ||--o{ occurrence_participant : participates
    lesson_occurrence ||--o{ resource_booking : reserves
    schedulable_resource ||--o{ resource_booking : occupied
    schedule_revision ||--o{ schedule_publication : frozen_source

Названия таблиц ниже являются целевой логической схемой. Допускается другое физическое именование при сохранении полей, связей и проверок. ? обозначает разрешенный NULL; поля связи без ? обязательны.

Таблица Ключевые поля и типы Обязательные связи и ограничения
organization id uuid, name text, default_timezone text Часовой пояс — идентификатор IANA; для текущей организации первоначально Asia/Novosibirsk
academic_period id, organization_id, name text, starts_on date, ends_on date, timezone text, week_anchor date, first_week_kind text, active_publication_id uuid? starts_on <= ends_on; период и правила отсчета задаются пользователем; имя папки не задает календарь
faculty id, organization_id, name text, short_name text? Организация 1:N факультетов; название не уникальный ключ
chair id, faculty_id uuid?, name text, short_name text? Факультет 1:N кафедр; отсутствие факультета допустимо и явно отображается
teacher id, chair_id uuid?, full_name text, degree text?, position text?, title text?, private_note text? Кафедра 1:N преподавателей; ФИО не уникально; заметка доступна по отдельному праву
subject id, name text, short_name text?, legacy_character text? Сохранять полное и сокращенное названия раздельно; неизвестный смысл исходного SubjectChar не заменять новым правилом
course_category id, name text, sort_order integer, study_year smallint?, education_level text? study_year > 0 при заполнении; числовой исходный ключ и CourseCount не переносить в год обучения без подтверждения
academic_group id, faculty_id, course_category_id, name text, student_count integer? Численность >= 0, неизвестная численность — NULL; одинаковые отображаемые названия разных сущностей допустимы
group_partition id, revision_id, group_id, name text, verification_status text В базовой версии одна действующая схема непересекающихся подгрупп группы в редакции; статусы unverified/confirmed
subgroup id, partition_id, ordinal smallint, name text?, student_count integer? Уникальность (partition_id, ordinal), положительный номер; подгруппа принадлежит ровно одной группе через схему
named_cohort id, name text, private_note text? Именованное административное объединение; само по себе не задает участников занятия
cohort_group revision_id, cohort_id, group_id M:N; уникальная тройка; создается только при существующей или явно введенной связи
building id, name text, address text?, timezone text? Новый нормализованный справочник для кабинетов; неизвестный корпус не угадывать из номера аудитории
room_type id, code text, name text, description text? Отдельный классификатор, не объединяемый с видами занятий
room id, building_id uuid?, chair_id uuid?, name text, capacity integer?, room_type_id uuid?, private_note text? Вместимость >= 0; неизвестная вместимость — NULL; полный идентификатор комнаты не зависит от имени
equipment_feature, room_feature Признак id/code/name; связь room_id/feature_id M:N, уникальная пара; исходный RoomSpec остается сырым значением до расшифровки
lesson_type id, code text, name text, description text? Кириллический исходный код хранится отдельно; подтверждение названий обязательно перед бизнес-расчетами
bell_schedule, bell_slot Сетка: id/name; слот: id/schedule_id/ordinal/start_time time/end_time time Уникальная позиция внутри сетки; начало меньше окончания; конец задается явно, не вычисляется разностью соседних начал
period_calendar_day period_id, date, kind text, teaching_week_number integer?, week_kind text?, comment text? Уникальность (period_id,date); учебный, выходной, праздничный или исключенный день; правила переноса задаются явно

Составы подгрупп и административных объединений фиксируются для редакции. Изменение общей численности либо переименование справочника не должно менять уже опубликованное представление: при публикации сохраняется снимок используемых названий, численности и параметров. Ссылки UUID остаются доступны для перехода к текущей карточке, а экран опубликованной версии использует ее исторические значения.

План, занятия, редакции и публикации§

Таблица Ключевые поля и типы Обязательные связи и ограничения
schedule_revision id, period_id, number integer, state text, based_on_id uuid?, catalog_snapshot jsonb? Состояния draft/frozen; один изменяемый draft на период, обеспеченный частичным уникальным индексом; замороженная редакция неизменяема
curriculum_plan id, revision_id, logical_id, subject_id, chair_id, comment text? Редакция 1:N планов; исходный Stable не трактуется как блокировка; проверенное новое закрепление хранить отдельно
plan_component id, revision_id, plan_id, lesson_type_id, planned_quantity numeric(10,2)?, quantity_unit text?, split_policy text, participant_mode text Уникальность (revision_id,plan_id,lesson_type_id); объем >= 0; единица — явный код academic_hour/clock_minute/session/unknown
plan_participant id, revision_id, plan_id, group_id, subgroup_id uuid? M:N между планами и группами/подгруппами; не более одной одинаковой связи с учетом NULL; подгруппа обязана принадлежать выбранной группе
component_participant id, revision_id, component_id, group_id, subgroup_id uuid? Состав по конкретному виду работы; participant_mode=inherit использует состав плана, override использует полный явно заданный набор компонента; те же проверки группы/подгруппы и уникальности
plan_room_preference revision_id, component_id, room_id, preference text M:N; уникальная пара компонента и комнаты; предпочтение не означает фактическое бронирование
lesson_series id, revision_id, component_id, logical_id, recurrence_kind text, recurrence_rule jsonb?, default_duration_minutes integer?, default_teacher_id uuid?, default_room_id uuid?, delivery_mode text Серия описывает повторение одного учебного события; допустимо одиночное занятие; recurrence_rule проверяется по схеме и не исполняется как код
series_participant revision_id, series_id, group_id, subgroup_id uuid? Фактический состав участников серии; копируется из эффективного состава компонента с учетом деления, затем изменяется явной операцией
lesson_occurrence id, revision_id, series_id, logical_id, origin_date date?, starts_at timestamptz?, ends_at timestamptz?, status text, teacher_id uuid?, room_id uuid?, delivery_mode text, public_comment text?, private_comment text?, is_fixed boolean Конкретное занятие или неразмещенная потребность; статусы unplaced/scheduled/cancelled; одно основное назначение преподавателя и аудитории в базовой версии; публичный и внутренний комментарии раздельны
occurrence_participant revision_id, occurrence_id, group_id, subgroup_id uuid? Материализованный фактический состав занятия; изменение участников серии пересчитывает будущие экземпляры только после предпросмотра
recurrence_exception revision_id, series_id, origin_date, action text, occurrence_id uuid?, reason text Уникальная исходная дата серии; действия cancel/replace/add; исключения не теряются при повторной генерации
workflow_task id, revision_id, occurrence_id uuid?, plan_id uuid?, assigned_chair_id uuid?, state text, reason text Передача работы кафедре — отдельный процесс; не изменяет смысл даты, отмены и публикации занятия
schedule_publication id, period_id, revision_id, number integer, published_at, published_by_id, content_sha256 char(64), comment text? Уникальная пара периода и номера; публикация ссылается на неизменяемую редакцию, включает сохраненные справочные подписи

Эффективный состав компонента определяется режимом inherit/override: отсутствие переопределения не стирает участников плана, а переопределение задает полный набор конкретного вида работы. Переключение режима и генерация серий показывают сравнение участников. Исходный GRPPLAN заполняет plan_participant; наличие в старом снимке только этой связи не запрещает новому приложению отдельный состав лекций, практик и экзаменов. Генератор берет эффективный состав компонента, а не безусловно все группы плана.

Одинаковая группа вправе входить в два разных плана по одной дисциплине: связи различаются по plan_id. Нельзя делать upsert участников по одному group_id, заменяя ранее существовавшие связи. Если несколько строк состава указывают на одну группу, список групп в карточке дедуплицируется по идентификатору, но состав подгрупп сохраняется. Для соединений с разрешенным NULL использовать корректную уникальность с учетом NULL, например UNIQUE NULLS NOT DISTINCT, либо эквивалентные частичные индексы.

Все изменяемые поля редакции защищаются серверной проверкой state=draft. Любая транзакция изменения самой редакции или ее дочерних сущностей сначала блокирует строку schedule_revision через SELECT ... FOR UPDATE, проверяет состояние и увеличивает агрегатный row_version редакции в той же транзакции. Публикация и заморозка получают ту же блокировку и сверяют ожидаемую агрегатную версию, поэтому новая дочерняя запись не может появиться между последней проверкой и заморозкой. Фоновая подготовка работает с зафиксированным снимком и перед активацией сверяет захваченный row_version; устаревший результат пересчитывается, а не публикуется. После публикации эта же редакция замораживается, а следующий рабочий черновик создается отдельной операцией копирования. Редактирование старой публикации через идентификатор объекта, API, административную форму или фоновую задачу должно отклоняться. Исправление выполняется в новом черновике и выпускается новым номером публикации. Предыдущая версия остается доступна уполномоченным пользователям.

Дополнительные поля и технические сущности§

Схема выше задает основные связи, но не исключает явно требуемые функциональностью поля. Для academic_group предусмотреть диапазон действительности, для справочников — архивирование, для lesson_type — явную политику обязательности преподавателя и допустимых форматов. У lesson_occurrence отдельно хранятся пожелания/прежнее размещение, не создающие занятости, причина отмены и причина закрепления; отсутствие этих сведений в колонках старой БД не отменяет новых требований. Учебные периоды, календарь, звонки, доступность и используемые параметры ресурсов включаются в проверяемый снимок публикации; последующее редактирование общих настроек не меняет исторический вид и смысл уже опубликованной версии.

Для учета объема в academic_period обязательно определить academic_hour_minutes; в plan_component — необязательную weekly_target_quantity с единицей и основанием расчета, отдельную от общего объема. У серии и экземпляра хранить contact_minutes — учебные минуты без перерыва, отличные от полного временного интервала занятости. При известном интервале учебные минуты не превышают его длительность; значение не вычисляется как вся разница между началом и концом, если внутри есть перерыв. Настройки расчета входят в снимок публикации.

Общий объем компонента имеет явную область quantity_scope: рабочее правило для новых планов — потребность каждого выбранного учебного состава. Совместная лекция двух групп засчитывает ее учебное время каждой группе, но преподавателю — один раз; раздельное проведение для двух подгрупп создает отдельную потребность каждой и может увеличить суммарную нагрузку преподавателя. Показывать отдельно выполнение плана по участникам и суммарное время уникальных проведений. Для импортированного объема область остается unknown до подтверждения; генерация с ней запрещена. Недельный ориентир, общий объем и число учебных недель не должны взаимно перезаписывать друг друга без показанного расчета.

Сущность Обязательное содержание
user, серверная сессия Поддерживаемая модель аутентификации Django; индивидуальная учетная запись, состояние, безопасный хеш пароля и отзыв сеансов
role_assignment, permission_scope Пользователь, роль, организация/подразделения, действительность назначения; права внутренней заметки и исходников импорта отдельно
audit_event Автор, время UTC, действие, тип и ID/logical_id объекта, редакция, причина, безопасная разница до/после, общий ID команды; неизменяемая запись
background_job, job_attempt, outbox_message Статус, владелец, параметры без секретов, прогресс, попытки, lease/heartbeat, результат и гарантированная повторная доставка
idempotency_record Ключ, владелец/область, вид команды, хеш входа, статус и ссылка на сохраненный результат
private_file, export_artifact Владелец, права, назначение, размер, SHA-256, безопасный путь хранения вне webroot, срок удаления

Публичное API-представление может использовать другую терминологию: /departments соответствует логической chair, /plan-entriescurriculum_plan, /eventslesson_occurrence, а /event-serieslesson_series. Это не дополнительные дублирующие сущности. Соответствия должны быть включены в словарь новой системы.

Календарь и правила размещения§

Серия и конкретное занятие§

CAL-01. Пользователь должен различать объем учебного плана, правило повторения и конкретное занятие. plan_component задает потребность; lesson_series — правило; lesson_occurrence — фактический экземпляр. Число строк плана и число календарных занятий не обязаны совпадать. Генерация по объему допускается только после задания единицы нагрузки, продолжительности и правил деления группы. Система выводит расчет до сохранения: исходный объем, полученное число занятий, общий объем минут и остаток.

CAL-02. Базовая версия поддерживает одиночное занятие, каждую учебную неделю и через одну учебную неделю. Правило включает начальную и конечную даты, дни недели, локальное время, продолжительность и опорную неделю периода. Понятия «четная/нечетная» определяются календарем учебного периода, а не автоматически номером ISO-недели года. Пользователь видит конкретные получившиеся даты. Правила с неизвестной точкой отсчета не создают экземпляров.

CAL-03. Конкретные интервалы хранятся в timestamptz и передаются API как ISO 8601 со смещением. Часовой пояс периода хранится отдельно: он необходим для повторений в локальном времени и не восстанавливается из UTC-смещения. Для неожиданно неоднозначного или несуществующего локального времени операция требует явного выбора либо отклоняется с объяснением. Время сервера и часовой пояс браузера не переопределяют учебный календарь.

CAL-04. Интервал занятия — полуоткрытый [starts_at, ends_at), с конечными границами и starts_at < ends_at. Занятие 08:00–09:30 не пересекается с занятием 09:30–11:00. Интервалы сравниваются по реальному времени, независимо от номера пары; занятие может занимать несколько слотов либо иметь индивидуальную продолжительность. Отображение номера пары — дополнительное представление. Нельзя считать, что разность двух начал звонков является длительностью пары.

CAL-05. Для изменения повторяющегося занятия интерфейс предлагает область: один экземпляр, этот и следующие либо вся серия. Предпросмотр показывает добавляемые, переносимые, отменяемые экземпляры, затрагиваемые исключения и конфликты. Изменение одного экземпляра сохраняет его origin_date и создает исключение, а не лишний дубль. Повторная генерация с тем же правилом и ключом запроса идемпотентна. Откат не должен восстанавливать уже исключенное занятие незаметно для пользователя.

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

Статусы и полнота назначения§

Статус экземпляра Интервал Бронирование ресурсов Смысл
unplaced Оба поля времени NULL Отсутствует Занятие существует как потребность, место в календаре не выбрано
scheduled Начало и окончание обязательны Создается для известных участников, преподавателя и аудитории Занятие размещено в календаре черновика; полнота обязательных ресурсов проверяется отдельно
cancelled Допускается сохраненный прежний интервал либо NULL; пара полей согласована Отсутствует Отмененный экземпляр остается в истории; причина отмены обязательна

CAL-06. В черновике размещенное занятие может временно не иметь преподавателя или аудитории. Известные ресурсы при этом бронируются, неизвестные — нет; карточка явно показывает недостающие назначения. Участники размещенного занятия обязательны; пустой набор не считается свободной группой. Для публикации применяются требования выбранного режима: очное занятие требует аудиторию; дистанционное может обходиться без нее; преподаватель обязателен, если соответствующий вид работы предполагает преподавание. Эти правила задаются явно и не выводятся из наличия поля.

CAL-07. Публикация — состояние версии всего расписания, а не четвертый статус экземпляра. Передача занятия «на кафедру», согласование и возврат — состояния задания workflow_task. Новый признак фиксации is_fixed запрещает перемещение без отдельного права и указанной причины; он не выводится автоматически из старого Stable. Старый Assigned сохраняется в данных происхождения, но не отождествляется с scheduled: в снимке 288 неназначенных строк имеют координаты, а 2 339 назначенных не имеют аудитории.

Участники, занятость и защита от конфликтов§

Атомарные ресурсы групп§

CON-01. Проверка занятости опирается на единый справочник schedulable_resource: преподаватель, аудитория либо атомарная часть группы. Преподаватель, аудитория и группа имеют глобальный устойчивый UUID в организации, одинаковый во всех периодах и редакциях. Атомы также имеют глобальные устойчивые UUID; версии состава хранятся отдельными снимками и не подменяют идентичность ресурса. UUID атома нельзя повторно использовать для другого состава участников. Ресурс идентифицируется UUID, не отображаемым названием. Таблицы resource_group_atom и participant_atom связывают группу/подгруппу с набором атомарных ресурсов в редакции. Занятие всей группы занимает все ее атомы; занятие конкретной подгруппы — атомы этой подгруппы. Благодаря этому «вся группа» конфликтует с любой своей подгруппой, а две подтвержденно непересекающиеся подгруппы могут заниматься одновременно.

Наличие двух разных номеров подгрупп само по себе не доказывает независимость, если используются разные схемы деления либо состав неизвестен. В базовой версии допускается одна подтвержденная непересекающаяся схема группы. До подтверждения все участники этой группы используют общий атом: параллельное размещение блокируется консервативно с сообщением «Состав подгрупп не подтвержден». Если схема подтверждена, но для отдельной записи не удалось сопоставить подгруппу, запись занимает все атомы группы. Учет студентов поименно не требуется; подтверждение схемы является отдельным действием уполномоченного пользователя.

Для объединенного занятия нескольких групп набор ресурсов строится как объединение атомов без повторов. Одна и та же группа не удваивает занятость и численность из-за нескольких строк связи. Если численность подгрупп неизвестна, интерфейс показывает неизвестное значение либо явно обозначенную верхнюю оценку; делить NStudents поровну без согласованного правила нельзя.

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

Проверки и транзакционная гарантия§

CON-02. Жесткие конфликты: пересекающиеся интервалы одного преподавателя, аудитории или атома группы; попадание в жесткий запрет ресурса. Они запрещают фиксацию операции независимо от полномочий диспетчера. Недостаточная вместимость, несоответствие рекомендованного типа помещения, неизвестная численность и мягкое предпочтение — предупреждения. Диспетчер может сохранить допустимое размещение после указания причины принятия предупреждения; причина входит в аудит. Предупреждение не отменяет жесткую проверку занятости.

Целевые технические таблицы:

Таблица Назначение и существенные поля
schedulable_resource id uuid, organization_id, kind, ссылка на преподавателя, комнату либо атом; ограничение ровно одного подходящего владельца
availability_rule id, revision_id, resource_id, severity=hard/soft, календарное правило либо конкретный интервал, reason, created_by_id
resource_booking id, revision_id, resource_id, during tstzrange, occurrence_id?, block_id?; ровно один источник: занятие или материализованный жесткий запрет
booking_block_source Связь объединенного интервала запрета с исходными правилами и причинами; позволяет корректно удалить одно из перекрывающихся правил
active_publication_booking resource_id, during tstzrange, publication_id, occurrence_id; действующая занятость опубликованных расписаний всех периодов, с глобальными UUID ресурсов
active_publication_participant group_id, partition_snapshot_id, subgroup_id?, during, publication_id, occurrence_id; снимок участников для межпериодной проверки разных схем деления
partition_intersection Два снимка схем одной глобальной группы, две подгруппы, результат overlap/disjoint/unknown, автор и основание подтверждения; матрица не переносится на измененный состав автоматически
conflict_decision revision_id, occurrence_id, тип предупреждения, данные проверки, accepted_by_id, reason, created_at; только для разрешенных предупреждений

Перед записью API выполняет предварительную проверку для объяснимого сообщения, но окончательную защиту реализует PostgreSQL. Использовать GiST exclusion constraint для пересечения диапазонов и расширение btree_gist для равенства UUID. Это поддерживаемый механизм PostgreSQL для запрета пересекающихся резервирований. Типы диапазонов PostgreSQL, btree_gist.

Принцип ограничения в миграции БД:

CREATE EXTENSION IF NOT EXISTS btree_gist;

ALTER TABLE resource_booking
  ADD CONSTRAINT resource_booking_no_overlap
  EXCLUDE USING gist (
    revision_id WITH =,
    resource_id WITH =,
    during WITH &&
  ) DEFERRABLE INITIALLY IMMEDIATE;

Дополнительно обязательны CHECK: диапазон непустой, обе границы конечные, нижняя включена, верхняя исключена. В этой таблице отсутствуют бронирования отмененных и неразмещенных занятий. Жесткие запреты предварительно объединяются по ресурсу в непересекающиеся интервалы с сохранением всех причин: два перекрывающихся запрета не должны мешать друг другу, но оба препятствуют занятию. Новый запрет, затрагивающий уже назначенные занятия, не применяется молча — система показывает перечень и требует сначала разрешить ситуацию согласованными операциями.

CON-03. Любое размещение, перемещение, замена ресурса, изменение участников и массовая операция выполняются одной транзакцией: заблокировать редакцию и проверить ее состояние; проверить версию объекта и права; заблокировать затронутые записи в устойчивом порядке; пересчитать интервалы и атомы; заменить бронирования; записать новое состояние и аудит; подтвердить ограничения перед commit. Обмен двух занятий может временно отложить exclusion constraint внутри транзакции, но к commit итог обязан быть корректным. Ошибка ограничения отменяет все изменения операции; API возвращает 409 Conflict с актуальным конфликтующим ресурсом и интервалом, если пользователь имеет право их видеть.

Общий порядок блокировок: организация, если операция требует координации публикаций/общих правил; затем редакции по UUID; затем глобальные группы/ресурсы по UUID; затем конкретные изменяемые строки по UUID. Отсутствующие уровни пропускаются, обратный порядок в другой команде не допускается. Публикатор также получает блокировку редакции до блокировок ее групп. Deadlock/serialization failure допускает ограниченный безопасный повтор только всей идемпотентной транзакции; частичный повтор ее SQL-шагов запрещен.

Оптимистическая блокировка по row_version предотвращает потерю изменения одного объекта. Ограничение занятости предотвращает другой случай: два пользователя изменяют разные занятия и одновременно выбирают свободную аудиторию. Проверка вида «SELECT свободно → INSERT» без ограничения БД не считается реализацией требования. Идемпотентный ключ клиентской команды предотвращает повторное выполнение после сетевого повтора.

CON-04. Жесткая защита записи действует внутри одной редакции. Изолированные черновики не конфликтуют между собой: это варианты расписания. Пересечение с действующей публикацией другого периода может сохраняться в черновике как препятствие публикации, но не обходить собственные жесткие ограничения редакции. При публикации проверяется также совместимость с действующими публикациями других периодов организации, если их даты пересекаются. Для active_publication_booking применяется аналогичный exclusion constraint по resource_id и during, без revision_id. Публикация атомарно заменяет бронирования предыдущей действующей версии своего периода; при конфликте с другим периодом прежняя публикация остается действующей. Операции публикации сериализуются блокировкой строки организации и захватывают блокировки всех затронутых глобальных групп в устойчивом порядке UUID. Публикация проверяет ожидаемую версию редакции под ее блокировкой. Активация бронирований, участников и указателя действующей публикации выполняется одной транзакцией. Предварительная проверка черновика может предупреждать о таких межпериодных пересечениях заранее.

Одного exclusion constraint по атомам недостаточно, если разные периоды используют разные схемы деления одной группы. Перед активацией дополнительно сравниваются пересекающиеся интервалы active_publication_participant по глобальному group_id: вся группа пересекается с любой своей подгруппой; одинаковая подтвержденная схема использует атомы; разные схемы используют подтвержденную матрицу partition_intersection. Результат unknown трактуется как возможное пересечение и блокирует публикацию. Проверка и запись удерживают блокировки глобальных групп до commit, поэтому две параллельные публикации не могут одновременно подтвердить отсутствие друг друга. Эту гарантию реализовать общей транзакционной службой и обязательным проверочным триггером активации либо эквивалентным механизмом БД, исключающим запись в обход проверки.

Опубликованный состав и его матрица пересечений неизменяемы. Изменение схемы в черновике создает новый снимок; переименование ресурса не меняет UUID. Изменения глобальных правил, влияющие на совместимость действующих публикаций, используют те же блокировки организации и групп, повторную проверку и явный результат; они не пересчитывают действующую занятость молча.

Перенос существующих данных§

Полный охват источников§

MIG-01. Импортер принимает подготовленный exports/schedule.sqlite вместе со схемой и манифестом. Прямое чтение Paradox допускается отдельной утилитой подготовки, воспроизводящей проверенный экспорт. Исходники сохраняются неизменяемыми с SHA-256, а пользователь видит источник каждого импортированного объекта. Не требуется переносить Paradox-индексы в бизнес-таблицы PostgreSQL.

Источник Активных строк Целевая обработка
FACULTY 6 faculty, исходный ключ в реестре соответствий
CHAIRS 47 chair; FacultyNo=46/48 направляются на сверку отсутствующей связи
TEACHERS 866 teacher, отдельные квалификационные поля и закрытая заметка
SUBJECTS 5 068 subject; полное/короткое названия и исходный SubjectChar сохраняются
Courses 12 course_category; CourseCount — исходный атрибут, не год обучения
GROUPS 554 academic_group, неподтвержденная схема подгрупп по NSubgroups; факультет и классификатор курса по ID
STREAMS 140 named_cohort; никаких связей с группами по сходству названий
Rooms 184 room и отдельный room_type; исходный RoomSpec сохраняется без догадки о назначении
DAYS 6 Таблица соответствия исходных дней 0–5; для новой модели — ISO-день 1–7 и реальные даты
TIMES 8 Начала bell_slot; окончания требуют ввода/подтверждения до генерации календаря
Plan 4 748 curriculum_plan; Stable сохраняется как исходный признак без присвоения is_fixed
PLANITEM 7 098 plan_component; PlanQty и GrpSplit сохраняются, объем имеет единицу unknown до подтверждения
GRPPLAN 21 419 plan_participant; полная комбинация плана, группы и подгруппы, без потери повторных членств
PLANROOM 0 Поддержать схему связи с plan_room_preference; пустота не доказывает работавший механизм предпочтений
Classes 15 111 Основной источник занятий; импортируемая потребность и исходные координаты, календарные экземпляры только после подтверждения декодирования
CLASSINF 0 Самостоятельный источник дополнительных ресурсов; схема и пустой набор сохраняются, функция нескольких ресурсов — расширение
ClassesInf 15 111 Служебный снимок для сверки; не создает дополнительные занятия и не перезаписывает Classes автоматически
TCHRRSTR 4 541 Исходные ограничения преподавателей; нормализованные availability_rule после подтверждения смысла Week, привязки и длительности
ROOMRSTR 990 Аналогично ограничениям преподавателей, с ресурсом комнаты
class_details Представление Воспроизвести как чтение/DTO с обогащенными названиями; не импортировать как новые строки занятий
plan_group_membership Представление Воспроизвести запросом связей участников плана; не считать отдельным источником данных
_source_manifest Служебная таблица Перенести метаданные происхождения файлов; не включать в количество бизнес-строк

Итого в 19 исходных таблицах 75 909 активных строк, включая связи и вспомогательную копию; фактических строк Classes15 111. Количество объектов в нормализованной модели может отличаться из-за разложения связей, но отчет объясняет каждое отличие. Названия источников регистрозависимы: CLASSINF и ClassesInf — две разные таблицы. Импортер не нормализует их к одному имени.

Происхождение, промежуточное хранение и разрешение неопределенностей§

Таблица Обязательные данные
import_batch UUID; SHA-256 пакета и SQLite; дата; пользователь; версия импортера; версия и hash правил; целевой период/редакция; статусы uploaded/validated/needs_resolution/ready/committed/failed/rolled_back; агрегированные счетчики
import_source_file Пакет, точное имя и регистр файла, размер, SHA-256, исходная кодировка, схема, счетчик заголовка и активных строк
import_raw_row Файл/таблица, исходный составной ключ JSON, порядковый номер, hash содержимого, raw_payload jsonb, статус разбора
import_issue Пакет, исходная строка/поле, код проблемы, уровень, текст объяснения, возможные решения, принятое решение, автор и время
legacy_identity Пространство имен источника, точное имя таблицы, канонический исходный ключ, UUID целевой сущности, тип сущности; уникальность исходной идентичности
import_entity_change Пакет, целевая сущность, действие создания/изменения, версии до/после, исходная строка; база для проверки и безопасного отката

В legacy_identity для справочника целевая идентичность соответствует устойчивому UUID, а для сущности редакции — ее logical_id; конкретный UUID строки и редакция фиксируются в import_entity_change. Копирование черновика не меняет исходное соответствие и не создает нового исходного ключа. Реестр идентичности либо отдельные типизированные связи обеспечивают FK до целевых сущностей; полиморфный идентификатор без проверки существования не считается достаточным.

Для ClassesInf, не имеющей PK, сохранять порядковый номер в конкретном исходном файле и hash строки; проверять мультимножество, не превращая порядок строк в бизнес-идентификатор. Повторяющиеся одинаковые строки этой вспомогательной таблицы не теряются. raw_payload сохраняет NULL, 0, -1, логические значения, пробелы и полный текст; значения не публикуются в технических логах. Все 88 исходных полей доступны пользователю с правом просмотра исходных данных, в том числе если их нормализация заблокирована.

MIG-02. Импорт обязан иметь отдельную страницу «Требует сопоставления». Нерасшифрованная строка не исчезает и не объявляется отсутствующей: доступны исходные поля, связанные записи, проблема и ввод решения. Загрузка исходного архива может быть успешно завершена, даже если календарная нормализация части записей не готова. Это не дает права объявлять завершенной миграцию редактируемого расписания. Пользователь видит раздельные счетчики «сохранено исходных», «нормализовано», «ожидает решения», «отклонено».

Обязательные правила для данного снимка:

  1. Classes.GroupNo=-1 раскрывается через GRPPLAN того же PlanNo; это 3 458 записей, в том числе с единственной группой. Прямая группа раскрывается только как указанная группа/подгруппа, а не как все участники плана. Исходная форма связи сохраняется.
  2. TeacherNo=-1 и RoomNo=-1 в Classes преобразуются в отсутствие назначения только по явному правилу соответствующего поля. Для Rooms.ChairNo=0 и -1 сохраняются разные исходные значения до объяснения их различия. Глобальная замена нуля на NULL запрещена: исходный день 0 — понедельник, а Week=0 — наблюдаемое значение ограничения.
  3. Положительные CHAIRS.FacultyNo=46/48, отсутствующие в FACULTY, создают две проблемы связи. Допускается сопоставить существующий факультет, создать подтвержденный недостающий либо явно оставить кафедру без факультета с причиной. Создавать фиктивный факультет только ради FK нельзя.
  4. Все 554 значения GROUPS.StreamNo=-1 не создают членства в 140 строках STREAMS. Участники объединенных занятий определяются планом; название старого потока не заменяет эти связи.
  5. CourseNo и равный ему в этом снимке CourseCount являются исходными числовыми атрибутами классификатора. Например, код 6 соответствует первому курсу магистратуры. Реальный год обучения оставляется пустым до подтверждения.
  6. Classes.DayNo сохраняется целиком. Проверенное арифметическое свойство DayNo % 14 ∈ 0..5 не является утвержденным календарным алгоритмом. До подтверждения точки отсчета, периодов, EachWeek/OddWeek, статуса Assigned и окончания пар даты не рассчитываются, бронирования не создаются. Неразмещенная новая потребность и исходная строка с неизвестной интерпретацией различаются в интерфейсе.
  7. Для ClassesInf журнал фиксирует 15 124 строки в заголовке против 15 111 активных. Не добавлять 13 пустых строк и не считать их восстановленными. Зафиксировать также одно расхождение комнаты с основной Classes; выбор значения допускается только явным решением. По умолчанию основной источник занятий — Classes.
  8. PlanQty, Stable, коды видов занятий, RoomSpec, SubjectChar и смысл Week в ограничениях не получают новой бизнес-семантики по созвучию. Исходные значения сохраняются; согласованные трактовки оформляются версионированным набором правил. Заполнение правил выполняется через интерфейс, а не скрытой правкой SQL.

Порядок и повторяемость импорта§

MIG-03. Импорт состоит из отдельных шагов: загрузка и проверка пакета; сохранение исходных строк; проверка схемы, PK и связей; предварительное сопоставление; решение неопределенностей; предпросмотр изменений; подтверждение переноса; сверка результата. После загрузки все исходные таблицы доступны в защищенном браузере данных. Предпросмотр показывает новые и измененные сущности, неразрешенные вопросы, конфликты, отличия от действующего черновика и ожидаемые счетчики.

MIG-04. Повтор того же пакета с теми же правилами и целевой областью возвращает уже полученный результат без дублей. Ключ идентичности импорта включает hash пакета, hash правил, версию импортера и целевую область. Новый снимок того же источника сопоставляется по исходным ключам, но автоматически не затирает пользовательские изменения: конфликт версии требует выбора или импорта в новый подготовительный набор. Исчезновение строки из нового снимка не означает команду удаления.

MIG-05. Подтверждение переноса выполняется транзакционно в разрешенный черновик. Для больших наборов разрешено заранее формировать изолированный подготовительный набор и транзакционно переносить его в рабочий черновик после полной проверки. Публикация остается отдельной явной командой. Нельзя «успешно импортировать» первую часть, оставив половину ссылок незаписанной, или очищать рабочие таблицы перед проверкой пакета.

MIG-06. До подтверждения отмена импорта не меняет рабочий черновик. После подтверждения откат восстанавливает предыдущую редакцию или создает компенсирующую редакцию по журналу, сохраняя пакет и аудит. Если появились последующие ручные изменения, система показывает различия и блокирует разрушительный автоматический откат. Опубликованный импорт исправляется новой публикацией; удаление истории не является допустимым способом отката.

Приемка модели, конфликтов и миграции§

Проверка Ожидаемый результат
AC-D01. Полнота исходников Для контрольного SQLite сохранены 19 таблиц, все 88 полей и 75 909 активных строк; две views и манифест учтены отдельно; исходный SHA-256 не изменился
AC-D02. Повторный импорт Два запуска одного пакета с теми же правилами дают одинаковые счетчики и UUID соответствий, без новых копий справочников и занятий
AC-D03. Вспомогательные таблицы CLASSINF остается отдельным пустым источником; 15 111 строк ClassesInf не увеличивают число исходных занятий; расхождение счетчика 13 и одна аудитория видны в отчете
AC-D04. Нерасшифрованный календарь При неподтвержденном DayNo все исходные записи доступны для просмотра, но ни одна дата не придумана и занятость не создана на основании имени папки
AC-D05. Полнота расписания группы Для группы с GroupNo=434109 исходный просмотр возвращает 79 уникальных занятий: 39 прямых и 40 через план. Для всех 554 групп результат совпадает с проверенным раскрытием связей; суммарно 17 727 пар «занятие–группа» при 15 111 исходных занятиях
AC-D06. Состав участников Одна группа в двух планах сохраняет оба членства; несколько ее подгрупп в одном плане не дублируют само занятие в выдаче группы и не удваивают полную численность
AC-D07. Служебные значения Преподаватель/комната -1 отображаются как неназначенные с сохранением исходного числа; день 0 остается понедельником; кафедры с FacultyNo 46/48 требуют отдельного решения
AC-D08. Ресурсы в черновике Размещенное по известной дате занятие без комнаты сохраняется в черновике с отметкой неполноты; режим, требующий аудиторию, блокирует публикацию, а дистанционный режим по утвержденным правилам — нет
AC-D09. Граница интервалов [08:00,09:30) и [09:30,11:00) одного ресурса допустимы; [08:00,09:31) и [09:30,11:00) отклоняются
AC-D10. Группа и подгруппы Занятие всей группы конфликтует с каждой ее подгруппой; подтвержденные разные атомы допускают параллельность; неподтвержденная схема блокирует ее с понятной причиной
AC-D11. Гонка двух пользователей Два независимых соединения одновременно бронируют один ресурс на пересекающееся время: ровно одна операция завершается, другая получает 409; в БД нет двойной занятости
AC-D12. Обмен и откат Обмен двух слотов фиксируется целиком; ошибка третьего ресурса откатывает оба переноса и связанные бронирования, оставляя прежнее расписание
AC-D13. Запреты ресурсов Перекрывающиеся жесткие запреты объединяются без потери причин; занятие внутри них отклоняется; снятие одного правила не снимает другое
AC-D14. Повторение и исключение Отмена одного экземпляра сохраняется после повторной генерации; перенос одного экземпляра не создает дубль на исходную дату; область «следующие» не меняет прошедшие экземпляры
AC-D15. Версии и публикация Старый номер публикации сохраняет занятия, подписи и состав участников после правки справочника и выпуска новой версии; прямое изменение frozen-редакции отклоняется
AC-D16. Несколько периодов Конкурирующие публикации разных периодов с одним преподавателем либо пересекающимися участниками одной группы не могут обе стать действующими, включая разные UUID атомов двух схем; неизвестное пересечение схем блокирует активацию; предыдущая публикация сохраняется при неуспехе
AC-D17. Безопасный откат импорта Откат не удаляет последующие ручные изменения и исходный пакет; при их наличии требуется разрешение конкретных различий либо новая компенсирующая редакция

Численные проверки исходного снимка являются обязательными регрессионными фикстурами миграции. Проверки гонок должны выполняться на PostgreSQL с отдельными соединениями и реальными транзакциями; тест на SQLite или последовательный вызов одной функции не подтверждает защиту от одновременного редактирования.

Архитектура и технологический стек§

Общая схема§

SYS-002. Реализовать модульное серверное приложение с единой предметной моделью и одной основной PostgreSQL. Отдельные процессы API и фонового обработчика используют один и тот же прикладной код и правила. Бизнес-операции не дублируются в React, SQL-скриптах импорта и служебной административной панели.

Архитектура веб-приложения
Архитектура веб-приложенияБраузер обращается к Nginx по HTTPS, Nginx — к Django API. API связан с PostgreSQL и приватными файлами. PostgreSQL содержит фоновые задания. Celery worker обменивается сообщениями с Redis и работает с PostgreSQL и файлами. Для PostgreSQL и файлов предусмотрены резервные копии. HTTPSБраузер пользователяNginx: статика и проксиDjango REST APIPostgreSQLПриватные файлы импорта иэкспортаСохранённые фоновые заданияCelery worker и диспетчерзаданийRedis: доставка задачРезервные копии

На узком экране схему можно прокручивать по горизонтали.

Исходный код схемы
flowchart LR
    U[Браузер пользователя] -->|HTTPS| N[Nginx: статика и прокси]
    N --> A[Django REST API]
    A --> P[(PostgreSQL)]
    A --> F[Приватные файлы импорта и экспорта]
    P --> O[Сохранённые фоновые задания]
    O --> W[Celery worker и диспетчер заданий]
    W <--> R[Redis: доставка задач]
    W --> P
    W --> F
    P --> B[Резервные копии]
    F --> B

В схеме PostgreSQL хранит предметные данные, версии, журнал и состояния заданий. Redis используется для доставки небольших сообщений с идентификатором задания. Файлы базы и полные наборы строк не отправляются через очередь. Копия утверждённой выдачи не получает прав редактирования черновика.

Приёмка SYS-002: одинаковый перенос через интерфейс, API и пакетную операцию вызывает один механизм правил и даёт одинаковые конфликты; администратор Django не может обойти эти правила редактированием бронирований.

Рекомендуемый базовый стек§

Выбор стека ниже — проектная рекомендация для автономной серверной работы. Совместимость веток проверена по официальной документации на дату ТЗ; конкретные исправленные версии и образы фиксируются исполнителем при старте разработки и обновляются контролируемо.

Слой Базовый выбор Назначение и ограничение
Клиент React 19, TypeScript в строгом режиме Сетка расписания, формы, таблицы, клиентское состояние; сервер остаётся источником правил
Сборка клиента Vite 8, Node.js 24 LTS Сборка статических файлов в CI/установочном комплекте; Node не требуется как отдельный production-сервер для этой SPA
Сервер Python 3.13, Django 5.2 LTS ORM, миграции, пользователи, сессии, транзакции, административные операции
API Django REST Framework 3.16 или совместимая поддерживаемая версия Сериализация, объектные права, единый REST-контракт, JSON и OpenAPI
СУБД PostgreSQL 18 с актуальным исправляющим выпуском FK, транзакции, история, поиск, диапазоны времени и защита бронирований
Драйвер БД Psycopg 3, совместимая версия Доступ Django к PostgreSQL; управляемые параметры соединений
Фоновые задачи Celery 5.6, Redis 8 как брокер Импорт, формирование больших экспортов, подготовка снимков; надёжность обеспечивается также записью задания в PostgreSQL
HTTP-процесс Python Gunicorn, поддерживаемая совместимая версия Запуск Django за Nginx; число процессов/потоков выбирается по измерениям
Входящий HTTP Nginx и TLS Статические файлы, проксирование, ограничения запросов, защищённая выдача экспортов
Поставка Docker Engine и Docker Compose Воспроизводимый запуск API, worker, PostgreSQL, Redis и Nginx на одной машине
Проверки pytest/pytest-django, Playwright, средства проверки TypeScript и кода Тестирование на настоящем PostgreSQL, пользовательские сценарии и проверки сборки

React 19.2 указан в актуальной линейке документации; Vite 8 предъявляет требования к Node, которым выбранная LTS-ветка 24 соответствует. Эти сведения не являются требованием использовать устаревший исправляющий выпуск. React: версии, Vite 8, Node.js: график поддержки.

Django 5.2 — LTS-ветка с расширенной поддержкой до апреля 2028 года; DRF 3.16 документирует совместимость с Django 5.2 и Python 3.13. До окончания поддержки должен быть выполнен плановый переход на поддерживаемую ветку. Django: поддерживаемые версии, DRF 3.16.

Для PostgreSQL 18 предусмотрена поддержка до ноября 2030 года; эксплуатация должна использовать актуальные исправления выбранной основной версии. PostgreSQL: политика версий. Redis входит в документированные стабильные брокеры Celery; состояние выполнения важных операций следует сохранять в PostgreSQL независимо от брокера. Celery: брокеры и хранилища результатов.

Правила выбора зависимостей§

SYS-003. Поставить pyproject.toml и lock-файл Python, package.json и lock-файл JavaScript, Dockerfile и Compose-файлы. Образы выпуска закрепляются тегом версии и digest; тег latest не считается воспроизводимой поставкой. В документации выпуска перечислить фактически проверенные версии.

SYS-004. В B не требуются Kubernetes, микросервисы, отдельный поисковый кластер, обязательная коммерческая облачная БД или платная библиотека календаря. Визуальную сетку разрешено строить на проверенной библиотеке с подходящей лицензией или собственных компонентах; предметные ограничения расписания должны оставаться в серверном домене.

SYS-005. Клиентские шрифты, скрипты и стили поставляются локально. После установки система должна выполнять основные функции при недоступности внешнего Интернета, если пользователь имеет сетевой доступ к серверу. Для установки пакетов, обновлений, внешнего резервного хранения и продления публичного TLS-сертификата могут потребоваться отдельные сетевые соединения. Внутрисетевая установка должна поддерживать сертификат заказчика.

Приёмка SYS-003–005: зафиксированный выпуск собирается на чистой машине; после сборки запросы браузера не требуют CDN, API лицензирования или стороннего аккаунта; отключение брокера не повреждает сохранённые занятия и не блокирует обычное чтение и ручное редактирование.

Прикладные модули§

Модуль Ответственность
identity Пользователи, сессии, роли, области доступа
catalogs Подразделения, группы, подгруппы, преподаватели, дисциплины, аудитории
academic_calendar Учебные периоды, даты, календарные исключения, сетки звонков
curriculum Позиции плана, виды и объёмы потребности, состав участников
scheduling Серии и экземпляры, назначения, перестановки, замены, блокировки
constraints Пересечения, запреты, вместимость, качество и полнота
publication Согласование, неизменяемые снимки и текущая опубликованная выдача
migration Приём SQLite, исходные записи, сопоставления, протоколы импорта
audit Автор, причина, до/после, связь действий в одну операцию
jobs / exports Фоновые задания, печать/экспорты, временные файлы

Границы модулей — границы ответственности внутри одного репозитория. Общие модели и транзакции не заменяются сетевыми запросами между «микросервисами». Служебная панель Django предназначена прежде всего для технических справочников и пользователей; пользовательский редактор должен быть полноценной частью веб-интерфейса.

Рекомендуемая структура поставки:

backend/                 Django-приложения, доменные операции, миграции
frontend/                React/TypeScript, формы, сетка, клиент API
tests/                   интеграционные, конкурентные, нагрузочные сценарии
fixtures/                обезличенные и синтетические тестовые данные
deploy/                  Dockerfile, Compose, Nginx, резервное копирование
docs/                    OpenAPI, ER-схема, инструкции, решения по архитектуре
scripts/                 импорт, диагностика и контролируемое обслуживание

Фоновые задания и отказоустойчивость операций§

SYS-006. Импорт, большой экспорт и формирование объёмного снимка выполняются асинхронно. HTTP-запрос регистрирует задание и возвращает его ID. Пользователь видит статус queued/running/succeeded/failed/cancelled, фазу, счётчики обработанных строк, ошибки и ссылку на разрешённый результат. Процент показывается только при известном знаменателе.

Запись задания и сообщение outbox создаются в одной транзакции PostgreSQL. Диспетчер отправляет ожидающие сообщения в Celery и умеет повторять отправку. Повторная доставка не должна повторно применять импорт или создавать две публикации. Worker фиксирует попытку, lease/heartbeat и результат в PostgreSQL; задача с истёкшей арендой может быть безопасно повторена.

Служебный диспетчер разрешается запускать отдельным лёгким процессом или системным расписанием. В одной установке должен быть один активный диспетчер либо БД-блокировка, исключающая конфликтующее управление. Простого on_commit(send_task) без восстанавливаемой записи задания недостаточно: необходимо обработать отказ между фиксацией данных и доставкой сообщения.

Приёмка SYS-006: остановка worker во время подготовки импорта не оставляет частично активированный семестр; повторный запуск доводит задание до однозначного состояния. Очистка брокера не удаляет предметные данные и не теряет задания, зарегистрированные в PostgreSQL. Повторная доставка одной команды не изменяет итог второй раз.

Требования к HTTP API§

Общие правила контракта§

API-001. Предоставить версионированный API /api/v1/ и спецификацию OpenAPI. Для публичного представления при его включении использовать отдельный набор маршрутов и сериализаторов, например /api/public/v1/. Авторизация обязательна для каждого внутреннего маршрута, а не только для страницы клиента.

API-002. Использовать JSON UTF-8. Идентификаторы сущностей — UUID в строковом виде; старые числовые ID доступны только как сведения происхождения. Время события передавать ISO 8601 с явным смещением, дополнительно возвращать часовой пояс периода. Числовые объёмы с дробной частью передавать десятичной строкой, если требуется точность; NULL, пустая строка и ноль имеют разные значения.

API-003. Списки поддерживают ограниченную пагинацию, поиск и белый список сортировок. По умолчанию 50, максимум 200 записей на страницу. Календарная выдача ограничивается периодом запроса и числом событий; при превышении лимита возвращается требование сузить фильтр, а не непомеченная усечённая сетка. Для всех выборок задаётся стабильный дополнительный порядок по ID.

API-004. Проверка прав и фильтра области ответственности выполняются на сервере до выдачи объектов. Подмена department_id, period_id, UUID или параметров экспорта не должна расширять доступ. Общий просмотрщик данных использует такой же контроль; произвольного SQL, загрузки серверного файла по пути и прямого доступа к таблицам пользователей через него нет.

Основные группы маршрутов§

Названия маршрутов ниже задают рабочий контракт для согласования; окончательный OpenAPI должен содержать поля, примеры, права, ошибки и ссылки на требования. Префикс /api/v1 в таблице опущен.

Метод и маршрут Назначение
POST /auth/login, POST /auth/logout, GET /auth/me Вход, завершение сессии и собственные права
GET/POST /users, PATCH /users/{id} Управление пользователями в пределах административных полномочий
GET/POST /faculties, /departments, /course-labels Организационные справочники и классификатор обучения
GET/POST /groups, GET/PATCH /groups/{id}, GET /groups/{id}/subgroups Группы и подгруппы
GET/POST /teachers, /subjects, /rooms Справочники преподавателей, дисциплин и аудиторий
POST /{catalog}/{id}/archive Контролируемое архивирование справочной записи
GET/POST /academic-periods, GET/PATCH /academic-periods/{id} Учебный период и его рабочие параметры
GET/POST /academic-periods/{id}/calendar-rules Календарь, рабочие даты и исключения
GET/POST /bell-schedules, PATCH /bell-schedules/{id} Начала и окончания временных слотов
GET/POST /plan-entries, GET/PATCH /plan-entries/{id} Учебные потребности, виды нагрузки и участники
POST /plan-entries/{id}/generation-preview Предварительный расчёт экземпляров по подтверждённым правилам
POST /plan-entries/{id}/generate Идемпотентное создание подтверждённого набора экземпляров
GET /schedule-revisions/{id}/events Датированные занятия черновика или разрешённой версии
GET /schedule-revisions/{id}/unplaced Неразмещённые занятия
GET/PATCH /events/{id} Карточка и изменение разрешённых простых полей
POST /events, POST /event-series Отдельное занятие и серия повторений
POST /events/{id}/move-preview, POST /events/{id}/move Проверка и применение переноса
POST /events/swap Атомарный обмен местами двух занятий
POST /events/{id}/replace-teacher, /replace-room Замена ресурса с повторной проверкой
POST /events/{id}/unplace, /cancel, /restore Размещение снять, занятие отменить либо восстановить по правилам
POST /event-series/{id}/change-preview, /apply-change Изменение одного, будущих либо всех экземпляров серии
POST /events/{id}/lock, /unlock Управление явно определённым закреплением
GET/POST /resource-restrictions, PATCH /resource-restrictions/{id} Ограничения доступности
GET /rooms/availability Поиск помещений на дату и интервал с выбранными требованиями
POST /schedule-revisions/{id}/validate Проверка полноты, предупреждений и готовности к публикации
POST /department-requests, POST /department-requests/{id}/submit Передача потребности и отправка предложения кафедры
POST /department-requests/{id}/accept, /reject Принятие или возврат предложения уполномоченным сотрудником
POST /schedule-revisions/{id}/publish Публикация проверенной версии с защитой от гонок
GET /publications, GET /publications/{id}, POST /publications/{id}/activate История, чтение и контролируемое повторное назначение активной версии
POST /imports, GET /imports/{id}, GET /imports/{id}/issues Загрузка снимка, анализ и ошибки
POST /imports/{id}/mapping, /preview, /commit Сопоставления, предварительная проверка и подтверждённое применение
GET /data/catalog, GET /data/{entity}/rows Просмотр разрешённых нормализованных данных и исходных записей импорта
POST /exports, GET /jobs/{id}, GET /exports/{id}/download Фоновая выгрузка и защищённое получение результата
GET /audit-events Журнал в пределах прав пользователя
GET /health/live, GET /health/ready Минимальная проверка процесса и готовности, без секретов и структуры БД

Изменения, повторные запросы и ошибки§

API-005. Карточка изменяемой сущности содержит version: bigint и ETag. Изменение существующей сущности требует If-Match её версии; отсутствие обязательного условия — 428 Precondition Required, устаревшая версия — 412 Precondition Failed. Создание нового объекта не требует несуществующего ETag: для дочернего объекта передаётся expected_revision_version родительской редакции, для нового независимого справочника действуют проверки уникальности и Idempotency-Key. Обмен и массовая операция передают expected_revision_version и версии всех изменяемых объектов; сервер проверяет их совместно в одной транзакции. Ответ включает доступную пользователю текущую версию и позволяет обновить карточку. Автоматическая перезапись последнего изменения другого диспетчера запрещена.

API-006. Команды создания, переноса, обмена, массового изменения, импорта и публикации принимают Idempotency-Key. Ключ связан с пользователем, видом операции, областью и хешем нормализованного тела. Повтор одного ключа с тем же запросом возвращает исходный результат; с другим телом — 409. Для обычных команд результат хранится не менее 24 часов; для импорта и публикации сохраняется постоянный идентификатор операции и её происхождения.

API-007. Предварительная проверка возвращает проект результата, список затронутых записей, ошибки/предупреждения и версии входных данных. Предпросмотр не резервирует аудиторию навсегда. При применении сервер повторяет проверку и фиксирует изменения в транзакции. Если ресурсы или правила изменились, прежний результат не считается разрешением обойти конфликт.

Пример команды переноса; UUID условные, дата относится к новому тестовому расписанию, а не к восстановленной дате старого DayNo:

POST /api/v1/events/11111111-1111-4111-8111-111111111111/move
If-Match: "17"
Idempotency-Key: 7f2f9d79-d7b8-492d-b99f-1a839d6d1111
Content-Type: application/json
X-CSRFToken: <токен сессии>

{
  "start_at": "2026-09-14T09:40:00+07:00",
  "end_at": "2026-09-14T11:10:00+07:00",
  "room_id": "22222222-2222-4222-8222-222222222222",
  "scope": "occurrence",
  "reason": "Изменение доступности помещения"
}

Пример структурированной ошибки:

{
  "code": "RESOURCE_CONFLICT",
  "message": "Аудитория занята в выбранное время",
  "request_id": "46d75d83-923f-48f6-a082-95c0cd7d1111",
  "conflicts": [{"resource_type": "room", "start_at": "2026-09-14T09:00:00+07:00", "end_at": "2026-09-14T10:30:00+07:00"}],
  "field_errors": {}
}

API-008. Установить общую семантику кодов: 200/201 — выполнено, 202 — задание зарегистрировано, 400 — неверный формат, 401 — нет действующей сессии, 403 — нет права, 404 — объект отсутствует или скрыт областью доступа, 409 — предметный конфликт/повтор ключа с иным телом, 412/428 — версия, 422 — невозможное значение или бизнес-правило, 429 — ограничение частоты, 503 — временная неготовность зависимости. Обёртка ошибки одна для всех маршрутов. Сведения о конфликтующих занятиях за пределами прав пользователя заменяются нейтральным сообщением о занятости.

API-009. Зафиксировать лимиты входных списков и максимальное число изменяемых событий. Рабочий предел одной синхронной команды — 200 экземпляров; больший набор обрабатывается заданием с предпросмотром. Публикация большого снимка подготавливается в фоне и активируется атомарно. Ни один лимит не допускает частичного молчаливого применения.

Приёмка API-001–009: спецификация проверяется в CI; клиент строится против этого контракта; неизвестные поля, недопустимые ID, повторы команд, устаревшие версии и попытки доступа к чужому подразделению проверяются автоматическими сценариями.

Безопасность, доступ и защита данных§

Учётные записи и сессии§

SEC-001. Каждый сотрудник работает под индивидуальной учётной записью. Общий пароль текущего исследовательского просмотрщика не переносится в новый продукт. При установке создаётся первый администратор безопасной командой; в поставке отсутствуют заранее заданные рабочие пароли.

SEC-002. В B использовать серверные сессии Django, защищённые cookies HttpOnly, Secure, SameSite=Lax и CSRF-защиту изменяющих запросов. Серверные сессии хранятся в PostgreSQL; отказ Redis не должен разлогинивать всех пользователей. Долгоживущие токены в localStorage не используются. Авторизация для API проверяется явно и тестируется отдельно от middleware HTML-страниц. Публичный и внутренний API разделены.

SEC-003. Пароли сохраняются стандартным поддерживаемым адаптивным хешированием Django. Базовая политика: не менее 12 символов, проверка очевидных/распространённых паролей, ограничение частоты неудачных входов с нарастающей задержкой. Политика не должна позволять злоумышленнику навсегда заблокировать другого пользователя несколькими запросами. Выход и административное отключение аккаунта прекращают его сессии. По умолчанию: 8 часов абсолютной длительности и 60 минут неактивности; при приближении тайм-аута показать предупреждение без потери текста формы.

SEC-004. Сброс пароля в автономном профиле выполняет уполномоченный администратор через одноразовый временный секрет с принудительной сменой. Почтовое восстановление и единый вход подключаются отдельной настройкой/расширением. Отсутствие SMTP не должно блокировать эксплуатацию.

Поля, объекты и внешняя выдача§

SEC-005. Применять разрешения к спискам, карточкам, поиску, счётчикам, журналу, заданиям, экспорту и файловым ссылкам. По одному UUID нельзя получить чужое задание импорта или приватные поля недоступного подразделения. Возможность посмотреть занятия преподавателя не тождественна праву читать его внутреннюю заметку.

SEC-006. Зафиксировать классификацию полей: публичные поля опубликованного расписания; внутренние рабочие поля; ограниченные заметки и исходные данные импорта; технические секреты. Внешний сериализатор выдаёт только разрешённые поля: дата/интервал, дисциплина, вид занятия, группы/подгруппы, разрешённое отображаемое имя преподавателя, аудитория и публичный комментарий. Внутренний комментарий, причина ограничений, служебные метаданные и реквизиты подключения в него не попадают.

SEC-007. Разделить публичный комментарий и внутреннюю заметку в модели и форме. Не переносить текст из старого Comment/Reason/Memo в публичный комментарий автоматически. При включении анонимной выдачи выполнить предпросмотр точного состава полей. По умолчанию она выключена. Учётные данные приложения, SSH и СУБД вообще не являются пользовательскими полями расписания.

SEC-008. Все строки из импорта выводить как текст, с экранированием. Буквальное &nbsp; хранить как исходный текст до отдельного подтверждённого исправления. Произвольный HTML, JavaScript и SQL в заметках не выполняются. Для CSV учитывать формульные префиксы ячеек, чтобы выгрузка при открытии в табличном редакторе не выполняла формулы из введённого текста; изменение представления в CSV не меняет значение в БД.

Инфраструктурная защита§

SEC-009. Рабочий доступ — по HTTPS. PostgreSQL и Redis не публикуются в Интернет; API доступен через Nginx. Сервисная роль приложения в PostgreSQL не имеет SUPERUSER, права создавать роли и менять кластер; учётная запись миграций отделена от runtime. Процессы контейнеров выполняются без root, кроме минимальной необходимой подготовки окружения.

SEC-010. SQLite и архивы импорта считаются недоверенными входными файлами. Проверять сигнатуру, разрешённую схему/типы, размер, счётчики и целостность. Рабочий предел загрузки — 100 МБ с контролируемым изменением настройки; неизвестные таблицы сохраняются в диагностике, но не выполняются как схема PostgreSQL. Запрещены загрузка SQLite-расширений и выполнение SQL из входных данных. Импорт выполняется ограниченным worker с тайм-аутом и ограничением памяти. Поддержка архивов Paradox, если будет добавлена, требует защиты от выхода путей из каталога распаковки и архивных бомб.

SEC-011. Приватные файлы располагаются вне статического веб-каталога. Скачивание экспорта происходит после проверки владельца и области прав; неподписанная постоянная ссылка на файл не даёт доступ. Временные экспорты удаляются по расписанию, базовый срок — 24 часа. Удаление временного файла не удаляет запись о выполненном задании.

SEC-012. Пароли, токены, cookies, строки подключения и полные тела приватных запросов исключаются из access/error-логов. Журнал предметных действий сохраняет автора, время, вид операции, объект, причину и разрешённую разницу значений. Для технических секретов сохраняется факт изменения, а не прежний/новый секрет. Подробные приватные изменения доступны только специальному разрешению; общий аудит не является способом прочитать закрытые заметки.

SEC-013. В конфигурации выпуска: DEBUG=False, ограниченные допустимые Host, доверенные reverse proxy, ограниченный CORS или единый origin, защитные HTTP-заголовки. Применить поддерживаемый проверочный режим Django к production-настройкам. Django: проверка конфигурации развёртывания.

Приёмка SEC: негативные тесты доступа по прямым UUID и API, попытки CSRF, XSS и SQL-инъекций, выгрузки с формульными префиксами, просроченная сессия, отключённый аккаунт, приватная заметка в публичном ответе, неподтверждённый импорт и отсутствие открытых портов БД. Необработанная критическая уязвимость авторизации или раскрытие секретов блокирует выпуск.

Нефункциональные требования§

Проверочный профиль нагрузки§

Числа ниже — проектные целевые показатели для приёмки, а не результаты измерения ещё не разработанного приложения. Исполнитель должен воспроизвести профиль на выделенном стенде и приложить отчёт. Профиль изменяется вместе с бюджетом ресурсов при согласовании ТЗ.

Параметр стенда Рабочая величина
Ресурсы стенда без посторонней нагрузки 4 vCPU, 8 ГиБ RAM, SSD
Группы / преподаватели / аудитории 1 000 / 1 500 / 300
Учебные периоды 10 в истории; не менее 2 с пересекающимися рабочими датами
Датированные экземпляры 250 000 в БД, включая историю; не менее 50 000 в проверяемой редакции
Журнал 1 000 000 предметных изменений
Одновременные сессии 50 читателей и 10 редакторов
Длительность прогона 30 минут после отдельного прогрева
Базовый поток 20 запросов чтения/сек. и 2 изменения/сек.; пакетные задания проверяются отдельным сценарием

NFR-001. На указанном стенде p95 API-поиска справочника и выдачи страницы из 50 записей — не более 500 мс; p95 календарной выдачи выбранной недели до 100 групп и до 2 000 событий — не более 1,5 с; p95 одиночного изменения с повторной проверкой конфликтов — не более 1 с. Измерять успешные запросы отдельно от намеренно ошибочных. Доля неожиданных 5xx — менее 0,1%; намеренно полученный 409/412 не считается сбоем сервера.

NFR-002. На контрольном рабочем месте с современным браузером и каналом 20 Мбит/с, RTT до сервера 50 мс, первая полезная отрисовка выбранного расписания — до 3 с в 95% измерений. Сетка должна ограничивать объём DOM виртуализацией/постраничной загрузкой колонок и строк. Нельзя выдавать десятки тысяч событий в браузер для локальной фильтрации.

NFR-003. Предварительный разбор текущей SQLite и построение отчёта импорта укладываются в 60 с на контрольном стенде; счётчики и ошибки доступны независимо от возможности расшифровать даты. Для больших согласованных наборов допустимо фоновое выполнение с видимым состоянием. Полное время конвертации неизвестной календарной семантики в этот норматив не входит: оно зависит от принятия решения, а не от скорости кода.

NFR-004. Публикация 50 000 экземпляров подготавливается заданием; финальная блокирующая транзакция активации должна быть измерена и обоснована. Рабочий предел недоступности изменений выбранной редакции — 5 с; если он не достигается, требуется изменить реализацию подготовки/индексов и повторить измерение, а не скрыть продолжительную блокировку под анимацией. До переключения читатели видят целиком прежнюю версию, после — целиком новую.

Надёжность и эксплуатационные свойства§

NFR-005. Любая предметная команда атомарна: занятие, участники, занятость ресурсов и запись аудита фиксируются совместно. Ошибка последнего участника массовой операции откатывает всю заявленную единицу изменения. Явно разделённые независимые задания показывают отдельный результат каждого, а не называются атомарной операцией.

NFR-006. Подтверждённые сервером изменения не теряются после перезапуска API/worker. Для PostgreSQL сохраняются настройки надёжной фиксации WAL; отключение fsync ради выполнения нагрузочного теста запрещено. Потеря всей машины восстанавливается в пределах отдельно заданных RPO/RTO, а не обещается как нулевая потеря.

NFR-007. Сбой брокера приостанавливает фоновые задачи, но сохраняет чтение, обычное редактирование и очередь зарегистрированных заданий. При недоступности PostgreSQL интерфейс сообщает о невозможности сохранения и не показывает ложное «Сохранено». Повторный запрос использует прежний ключ операции.

NFR-008. Для production установлен целевой месячный показатель доступности 99,5% на согласованном окне обслуживания. Одна серверная машина не обеспечивает автоматический переход на резервный узел; это ограничение базовой архитектуры. Расчёт доступности, плановых окон и ответственных за инфраструктурный простой фиксируется перед вводом в эксплуатацию. Высокая доступность с репликацией и failover относится к расширению.

Интерфейс и сопровождаемость§

NFR-009. Поддерживать актуальную и предыдущую стабильные версии Chrome, Firefox и Edge на момент приёмки. Просмотр расписания и карточек доступен в Safari и мобильном браузере; таблицы на ширине 390 px имеют контролируемую прокрутку. Основной плотный редактор должен быть удобен от 1280×720; необходимые изменения через форму выполняются и без перетаскивания мышью.

NFR-010. Основные операции доступны с клавиатуры. Диалоги удерживают и возвращают фокус; поля имеют подписи; ошибки связаны с полями; статусы различаются текстом/значком, а не только цветом. Масштаб 200% не скрывает кнопку сохранения и объяснение конфликта. Для screen reader предоставить табличное/списочное представление расписания вместо единственной графической сетки.

NFR-011. Интерфейс показывает отдельные состояния загрузки, пустой таблицы, отсутствия результатов, недоступности сервера и отказа в правах. Во время незавершённого изменения нельзя незаметно сменить семестр или закрыть карточку с потерей ввода. Несохранённый приватный текст не сохраняется бессрочно в общедоступном хранилище браузера.

NFR-012. Числа и даты отображаются в русской локали, внутри API формат однозначен. Поиск не зависит от регистра кириллицы. Политику е/ё, Unicode-нормализации и пробелов закрепить тестами; по умолчанию е и ё различаются, поиск по подстроке регистронезависим. Имена людей и групп не преобразуются автоматически в уникальные идентификаторы.

NFR-013. Исключить SQL N+1 на календарных выдачах, индексировать FK и основные фильтры, профилировать запросы на приёмочном наборе. Настройки индексов и поиск выбираются по EXPLAIN, а не по числу таблиц в старой Paradox. PostgreSQL-поиск и необходимые расширения достаточны для B; отдельный поисковый сервис не требуется.

NFR-014. Поставлять автоматические тесты правил повторений, участников, конкуренции, прав и импорта. Проверки БД выполняются на PostgreSQL той же основной версии, что production; SQLite не подменяет тесты диапазонных ограничений и блокировок. Сборка клиента, типизация, линтеры и миграции проверяются в CI.

Матрица покрытия возможностей исходной системы§

Матрица связывает целевое веб-приложение с полным каталогом из 59 пунктов анализа экранов. Номера LEG-01LEG-59 соответствуют номерам исходного каталога. Источник S03 означает Screenshot_3.jpg; остальные изображения именуются аналогично и находятся в каталоге экранов. Скриншоты календаря относятся к сентябрю 2024 года; их принадлежность снимку данных 1_sem_2026_2027 не подтверждена.

Доказательность: «Экран», «Поле» и «Команда» подтверждают наличие соответствующего элемента интерфейса, но не успешное выполнение операции; «Гипотеза» означает вывод по неподписанной пиктограмме или предполагаемому переходу. Ни одна строка не должна трактоваться как протокол испытания исходной программы. Колонка «Целевое поведение» задает проектное решение для новой реализации.

Области: REF — справочники; PLAN — учебная потребность и виды нагрузки; STREAM — именованные наборы групп; CAL — календарные представления; EDIT — изменения занятий; AVAIL — доступность ресурсов; ADMIN — данные и настройки; HELP — справка; AUTH — доступ.

Объем: «База» — обязательное покрытие первой рабочей версии; «Уточнить» — возможность учитывается в проекте, но воспроизведение исторической семантики требует решения из реестра ниже. «Уточнить» не означает разрешение убрать возможность без согласования. Проектные расширения, не доказанные для исходной системы, перечислены отдельно после матрицы.

ID Возможность исходного каталога Источник и доказательность Область Целевое поведение Объем
LEG-01 Открыть список групп S01, S03; экран REF Таблица групп с переходом к карточке и связанному расписанию База
LEG-02 Найти/отфильтровать группы S03; поле, алгоритм не показан REF Поиск по названию; отбор по факультету, курсу и активности База
LEG-03 Вести факультет, численность, подгруппы и курс группы S03; поля, сохранение не показано REF Карточка с проверкой ссылок и неотрицательной численности; курс отображается названием База
LEG-04 Добавить/изменить/удалить группу S03; гипотеза по пиктограммам REF Создание и редактирование; связанная с расписанием группа архивируется, удаление не разрушает историю База
LEG-05 Открыть список преподавателей S01, S04; экран REF Таблица преподавателей с карточками База
LEG-06 Найти/отфильтровать преподавателя S04; поле REF Поиск по имени, отбор по кафедре и активности База
LEG-07 Вести кафедру, должность, звание, степень, замечания преподавателя S04; поля REF Сохранение всех перечисленных атрибутов; доступ к заметкам определяется ролью База
LEG-08 Добавить/изменить/удалить преподавателя S04; гипотеза по пиктограммам REF Создание, редактирование, архивирование; запрет потери связей с проведенными и планируемыми занятиями База
LEG-09 Открыть список дисциплин S01, S05; экран REF Таблица дисциплин и переход к использующим их планам База
LEG-10 Найти/отфильтровать дисциплину S05; поле REF Поиск по полному и краткому названию База
LEG-11 Вести полное и краткое название дисциплины S05; поля REF Редактирование обоих названий; краткое используется в плотной сетке, полное доступно в карточке База
LEG-12 Добавить/изменить/удалить дисциплину S05; гипотеза по пиктограммам REF Создание, изменение и архивирование; названия не используются как ключи База
LEG-13 Открыть список аудиторий S01, S06; экран REF Таблица аудиторий с вместимостью, назначением и переходом к занятости База
LEG-14 Найти/отфильтровать аудиторию S06; поле REF Поиск по названию, отбор по типу, кафедре и вместимости База
LEG-15 Вести вместимость, назначение, кафедру, замечания аудитории S06; поля REF Карточка перечисленных атрибутов; тип помещения отделен от типа занятия База
LEG-16 Добавить/изменить/удалить аудиторию S06; гипотеза по пиктограммам REF Создание, изменение, архивирование; действующие назначения сохраняют ссылки База
LEG-17 Открыть список кафедр S01, S07; экран REF Таблица кафедр и их организационных связей База
LEG-18 Найти/отфильтровать кафедру S07; поле REF Поиск по названию, фильтр по факультету База
LEG-19 Связать кафедру с факультетом S07; поле REF Выбор существующего факультета; несогласованные ссылки импорта требуют исправления База
LEG-20 Добавить/изменить кафедру S07; гипотеза по пиктограммам REF Создание и изменение кафедры; поведение удаления в старой программе не заявляется База
LEG-21 Просмотреть факультеты S01, S08; экран REF Таблица факультетов; управление справочником предоставляется администратору как проектное дополнение База
LEG-22 Просмотреть учебный план выбранной группы S01, S09; экран PLAN Список позиций плана по группе и учебному периоду, включая общие занятия нескольких групп База
LEG-23 Указать дисциплину и кафедру в плане S09; поля PLAN Позиция плана с отдельными ссылками на дисциплину и ответственную кафедру База
LEG-24 Указать недельную нагрузку по лекциям, практикам, лабораторным S09; поля PLAN Явное указание вида, объема, единицы измерения и периода действия; исторический PlanQty не пересчитывается без правила Уточнить
LEG-25 Учитывать проведение практик/лабораторных по подгруппам S09; флажки PLAN Участники задаются группой целиком либо конкретными подгруппами; учитываются при генерации и конфликтах База
LEG-26 Учитывать экзамены, консультации, зачеты S09; блоки, поведение ввода не показано PLAN Отдельные виды учебной потребности и занятий; объем и правила порождения согласуются отдельно База
LEG-27 Задать признак «Стабильное» S09; поле, смысл неизвестен PLAN Сохранение исходного Stable как наследуемого атрибута; блокировка изменений вводится отдельным явным действием Уточнить
LEG-28 Просмотреть группы, объединенные для записи плана S09; поле «Поток» PLAN Уникальный состав групп и подгрупп конкретной позиции плана; не подменяется справочником STREAMS База
LEG-29 Добавить/изменить/удалить запись учебного плана S09; гипотеза по пиктограммам PLAN Управление потребностью с предварительным показом влияния на существующие занятия База
LEG-30 Просмотреть классификатор курсов и уровней обучения S01, S10; экран REF Названия курсов/уровней, отдельные от технических идентификаторов; исходный CourseCount не трактуется как год обучения База
LEG-31 Просмотреть потоки и состав групп S01, S11; экран STREAM Именованные наборы групп для отбора и просмотра; членство отделено от состава участников плана База
LEG-32 Создать поток S11; команда STREAM Создание именованного набора групп в учебном периоде База
LEG-33 Удалить поток S11; команда STREAM Удаление либо архивирование набора без удаления групп и занятий База
LEG-34 Включить/исключить группу из потока S11; гипотеза по стрелкам STREAM Явное редактирование членства набора с отображением результата до сохранения База
LEG-35 Выбрать «Полное»/«Нечетная»/«Четная»/«Экзамены» для потока S11; неактивные команды, результат неизвестен STREAM, CAL В новой версии — фильтры состава показываемого расписания; печать/экспорт старых кнопок не предполагаются Уточнить
LEG-36 Просмотреть начала занятий S01, S12; экран REF Сетка звонков с номером, началом и явно заданным окончанием; импорт не вычисляет окончание по следующему началу База
LEG-37 Открыть общее расписание S14, S15; экран; S02 — гипотеза перехода CAL Основной календарь групп, дней и временных интервалов База
LEG-38 Выбрать неделю S14, S15; поле CAL Выбор фактической недели и учебного периода; старые коды переводятся в даты только после подтверждения правил База
LEG-39 Просмотреть занятия по дням, позициям и группам S14, S15; экран CAL Календарная сетка и табличный вид с согласованным составом занятий; легенда цветов и статусов База
LEG-40 Просмотреть детали занятия при наведении S14; экран CAL Краткая подсказка и доступная по нажатию/клавиатуре карточка дисциплины, участников, преподавателя и аудитории База
LEG-41 Просмотреть неназначенные занятия S14, S15; экран CAL Отдельный список занятий, ожидающих размещения; исходный Assigned=False не заменяется проверкой пустых координат База
LEG-42 Обновить представление расписания S14, S15; команда CAL Получение актуальной версии данных с сохранением фильтров и уведомлением об изменениях База
LEG-43 Отменить выбранный элемент S15; команда, семантика неизвестна EDIT Раздельные действия «Снять назначение» и «Отменить проведение»; историческая команда не считается эквивалентной обоим Уточнить
LEG-44 Переставить занятие S15; команда EDIT Перенос выбранного проведения в другую дату/время после проверки участников, ресурсов и ограничений База
LEG-45 Сменить аудиторию занятия S15; команда EDIT Замена аудитории с проверкой доступности и совместимости, журналом прежнего и нового значения База
LEG-46 Заменить преподавателя S15; команда EDIT Замена преподавателя с проверкой занятости, границ полномочий и журналом изменений База
LEG-47 Отдать на кафедру S15; команда, семантика неизвестна EDIT До уточнения — отдельное состояние/задание ответственной кафедре; не очищает преподавателя и помещение неявно Уточнить
LEG-48 Открыть расписание группы S15; команда CAL Календарь всех занятий группы, включая общие занятия через участников плана и ее подгруппы База
LEG-49 Открыть расписание преподавателя S15; команда CAL Календарь назначений преподавателя и доступных пользователю ограничений База
LEG-50 Открыть расписание аудитории S15; неактивная команда CAL Календарь занятости аудитории с переходом к занятиям База
LEG-51 Работать с комментарием занятия S15; команда EDIT Чтение и изменение комментария согласно роли; внутренние сведения исключаются из публичного представления База
LEG-52 Открыть поиск свободных аудиторий S02, S13; команда и экран AVAIL Поиск по фактической дате и временному интервалу с учетом занятий и ограничений База
LEG-53 Выбрать день поиска аудитории S13; команды AVAIL Выбор даты, а также шаблона недели для предварительного планирования База
LEG-54 Отобрать аудитории по назначению S13; варианты фильтра AVAIL Отбор по отдельному справочнику типов помещений; «Любая» является только состоянием фильтра База
LEG-55 Просмотреть свободные аудитории по времени S13; экран, выдача пуста AVAIL Результаты по слотам/интервалам с объяснением недоступности и временем актуальности расчета База
LEG-56 Выполнить действие «База данных» S02; команда, содержание неизвестно ADMIN В новой версии — выбор учебного периода и раздел импорта/диагностики по правам; PostgreSQL не выбирается как локальный файл в браузере Уточнить
LEG-57 Открыть параметры S02, S14, S15; команды, состав неизвестен ADMIN Явные настройки календаря, звонков и отображения, разграниченные по полномочиям; произвольная настройка соединения обычным пользователем не требуется Уточнить
LEG-58 Открыть помощь S01, S02; меню, содержание неизвестно HELP Встроенное руководство по сценариям, обозначениям, ограничениям и версии приложения База
LEG-59 Выйти из приложения S01; команда AUTH Завершить серверную сессию и вернуться на экран входа; браузерное окно закрывать не требуется База

Требования, которые нельзя обосновывать только этими 59 пунктами§

Следующие возможности должны иметь собственные требования и приемочные проверки; их нельзя записывать как доказанные алгоритмы старого приложения:

  • Ограничения преподавателей и аудиторий: хранение подтверждено таблицами TCHRRSTR и ROOMRSTR, но редактор и последовательность применения не показаны на скриншотах.
  • Проверка пересечений групп, подгрупп, преподавателей и аудиторий, вместимости и совместимости помещений: это необходимые проектные правила корректности новой системы, а не проверенное поведение прежней.
  • Порождение неназначенных занятий из нагрузки: связь модели правдоподобна, однако алгоритм, округление и единицы объема не установлены. Порождение перечня занятий и автоматический выбор для них времени/ресурсов — разные функции.
  • Автоматическое составление или оптимизация расписания не продемонстрированы. Такая подсистема должна оформляться отдельным расширением с входными ограничениями, целевой функцией, пределом времени расчета и критериями качества.
  • Совместная работа, роли, серверные сессии, аудит, контроль конкурентных изменений, резервное копирование и восстановление — требования новой серверной реализации. Они не следуют из наличия файлов блокировок Paradox.
  • Публикация утвержденного расписания и отделение внутренних заметок от внешнего представления — проектные возможности веб-продукта; текущий защищенный просмотрщик данных не является полноценным редактором расписания.
  • Печать, выгрузки, интеграции, уведомления, согласование и универсальная отмена/повтор изменений не подтверждены показанными экранами. Каждый включаемый сценарий требует отдельного описания; подписи «Полное» и «Отменить» не заменяют такого описания.
  • Перестановка групп и аудиторий обнаружена в ресурсах утилиты Reorder.EXE, а не в каталоге 59 функций. Если пользовательский порядок необходим в новом календаре, его следует принять отдельным требованием, сохранив числовые идентификаторы исходных объектов.

Реестр предметных решений и неопределенностей§

Рабочие допущения ниже позволяют проектировать новую систему. Они не подтверждают исторические правила и не разрешают без проверки преобразовывать неоднозначные данные в рабочее расписание. «До уточнения» означает сохранение исходного значения в слое импорта и в отчете расхождений; записи не теряются и не получают придуманных дат, участников или статусов. Подтверждение решения оформляется в журнале решений проекта со ссылкой на ID и примером до/после. Указанные роли обозначают представителей заказчика, а не конкретных людей.

ID Требуется определить Рабочее правило новой версии / действие до уточнения Влияние на импорт и ввод в эксплуатацию Кто подтверждает и на каком примере
DEC-01 Кодирование Classes.DayNo, начало отсчета и соотношение OddWeek/EachWeek Для новых данных используются реальные даты и явное повторение; исходные коды сохраняются. Формула DayNo // 14 — гипотеза, автоматическое преобразование по ней запрещено Блокирует достоверный импорт дат, календарную проверку конфликтов и расчет свободных ресурсов по старым занятиям Старший диспетчер и сопровождающий прежнюю программу: минимум по одному занятию каждой четности, еженедельное занятие, переход двухнедельного блока
DEC-02 Учебный период, его даты, каникулы, праздничные исключения, часовой пояс и опорная неделя Период и календарь задаются явно; рекомендуемый часовой пояс развертывания — Asia/Novosibirsk. Название каталога не определяет дату первого учебного дня Блокирует публикацию перенесенного расписания на фактические даты; отдельно фиксируется несоответствие экранов 2024 года и имени снимка 2026–2027 Учебное управление: утвержденный календарный график и параметры рабочей программы
DEC-03 Значение Week=0/1 в ограничениях и применение ограничения к периоду Для новых ограничений указываются даты действия и повторение. Исходный вариант недели сохраняется без переименования в четный/нечетный Неопознанные ограничения требуют проверки до допуска автоматического назначения ресурсов Диспетчер: по одному известному запрету на каждой неделе с сопоставлением экранов
DEC-04 Единица PlanQty, длительность академического часа, понятие пары, периодичность и округление Новая нагрузка имеет явную единицу и область действия. Импорт сохраняет число и код вида без автоматического умножения на недели или деления на два Блокирует эквивалентную генерацию занятий и контроль выполнения нагрузки; чтение исходного значения возможно Учебно-методическое управление и диспетчер: один план с расчетом вручную и фактическим количеством созданных занятий
DEC-05 Время окончания слотов и занятия длительностью больше одного слота Начало и окончание задаются независимо, интервалы учитывают фактическое пересечение. Конец не выводится из начала следующей пары Начала всех восьми слотов импортируются; окончания требуют заполнения до точной проверки занятости Учебное управление: утвержденное расписание звонков, примеры экзамена и удлиненного занятия
DEC-06 Значение Plan.Stable Сохраняется отдельный исторический признак; блокировка переноса, фиксация и факт проведения имеют самостоятельные статусы Не применять импортированный флаг как запрет изменения или признак проведенности Диспетчер: изменение флага на копии и наблюдение за разрешенными операциями
DEC-07 Разница между снятием назначения, отменой проведения, удалением и Assigned=False с заполненной позицией В новой версии это раздельные операции. Снятие освобождает ресурсы и возвращает потребность к размещению; отмена сохраняет историю и причину. Старая позиция сохраняется как исходный атрибут 288 неназначенных записей с координатами не должны занимать ресурсы только из-за наличия координат; требуется подтвердить их отображение Диспетчер: отмена одного занятия на рабочей копии и сравнение до/после
DEC-08 Значение «Отдать на кафедру» и полномочия кафедрального пользователя Предварительное проектное значение — передать ответственность за уточнение преподавателя/условий. Операция не меняет ресурсы и статус размещения неявно Нет надежного признака для автоматического восстановления исторических передач; импорт не создает вымышленные задания Руководитель расписания и представитель кафедры: полный сценарий передачи и возврата
DEC-09 Состав и пересечение подгрупп, нумерация, неизвестная численность, возможность совместного участия Подгруппы представляются явно; занятие целой группы пересекается со всеми ее подгруппами. При неизвестном составе разных схем подгрупп система не объявляет отсутствие конфликта Нельзя делить NStudents на NSubgroups и считать результат подтвержденной численностью; GRPPLAN сохраняется без схлопывания строк подгрупп Диспетчер и учебное управление: план с разными подгруппами и две параллельные записи
DEC-10 Участники занятия при GroupNo=-1, связи с GrpSplit и общими экзаменами Отсутствие прямой группы разбирается в контексте позиции плана и вида занятия; справочник STREAMS не используется как замена участникам 3 458 записей требуют восстановления через GRPPLAN; повторяющаяся группа не увеличивает численность повторно Диспетчер: общий потоковый курс, общий экзамен и план с единственной группой
DEC-11 Значения видов занятий с/л/п/з/э/к, типов помещений и RoomSpec Два отдельных справочника. Неоднозначные коды сохраняются с исходной подписью до утверждения таблицы соответствий Блокирует корректное автоматическое сопоставление типа занятия и аудитории; особенно проверяются с/п, к и отсутствующий код лингафонного кабинета Диспетчер и администратор помещений: карточки каждого типа и по одному сохраненному изменению
DEC-12 Допустимость назначенного занятия без аудитории/преподавателя, дистанционные и внешние площадки Неполное назначение явно помечается; новый формат проведения задается отдельным полем, отсутствие аудитории само по себе не означает онлайн-формат 2 339 строк Assigned=True без аудитории и 23 из них без преподавателя требуют классификации до признания готовыми к публикации Руководитель расписания: примеры допустимых исключений и правила выпуска расписания
DEC-13 Являются ли ограничения запретами, пожеланиями или справочными заметками; нужны ли исключения на конкретную дату Для новых ограничений явно задаются жесткость, ресурс и интервал; исключение имеет причину и полномочие. До классификации исходные строки показываются как неуточненные ограничения Нельзя считать произвольный текст Reason однозначным машинным запретом; требуется согласовать влияние 4 541 ограничения преподавателей и 990 ограничений аудиторий Диспетчер и ответственные за ресурсы: перечень типов ограничений и приоритетов
DEC-14 Возможность использовать одну аудиторию/преподавателя несколькими занятиями одновременно По умолчанию пересечение одного ресурса запрещается; совместное занятие задается одним проведением с несколькими участниками. Исключение требует отдельного правила Исторические пересечения импортируются с диагностикой, а не автоматически удаляются или объявляются допустимыми Руководитель расписания: примеры законного совместного использования и недопустимого конфликта
DEC-15 Смысл именованных потоков, их привязка к периоду и кнопки «Полное»/«Нечетная»/«Четная»/«Экзамены» Рабочее назначение — именованный набор групп и режимы просмотра. Объединение участников плана остается отдельным механизмом Все 140 строк STREAMS сохраняются; при StreamNo=-1 у всех 554 групп членство не восстанавливается по похожим названиям Диспетчер: создать поток, включить группу и выполнить каждую из четырех команд на копии
DEC-16 Авторитетный источник при расхождении Classes и ClassesInf; роль CLASSINF/PLANROOM Основным кандидатом считается Classes; вспомогательные данные сохраняются для сверки, не порождают дубликаты занятий. Пустые таблицы не доказывают востребованность нескольких ресурсов Расхождение счетчика 15 124/15 111 и одной аудитории оформляется отдельным результатом сверки; решение фиксируется до утверждения миграции Сопровождающий старую систему и диспетчер: отображение спорного занятия и сохранение на тестовой копии
DEC-17 Исправление отсутствующих факультетов и буквальных HTML-сущностей в справочниках Сохраняется исходный текст и ссылка; исправления делаются по согласованной карте соответствий. Похожие названия не сливаются автоматически Две ссылки кафедр на факультеты 46 и 48 остаются в отчете проблем; импорт не создает фиктивный действующий факультет без решения Владелец организационных справочников: корректные названия и принадлежность кафедр
DEC-18 Допустимый доступ к персональным сведениям и свободному тексту Авторизованные пользователи видят поля по ролям; публичное представление, если оно включено, не содержит внутренних заметок и реквизитов онлайн-занятий. Пароли доступа не включаются в документацию и журналы Все исходные поля сохраняются в защищенном контуре, но не копируются автоматически в публичные API, журналы и экспорт для посетителей Владелец продукта и ответственный за доступ: утвержденная матрица полей/ролей; действующий пароль просмотрщика не является моделью ролей будущего приложения
DEC-19 Редактирование серии: одно проведение, следующие либо все; влияние на уже опубликованные занятия По умолчанию изменяется выбранное проведение. Массовая операция явно показывает диапазон, количество и конфликты; прошедшие записи не переписываются молча Импортированный шаблон повторения не должен приводить к изменению несвязанных строк только по совпадающему PlanNo Руководитель расписания: перенос одной пары и изменение всей оставшейся серии
DEC-20 Генерация занятий из плана и поведение повторной генерации Только после определения единиц, периода и повторения; повторный запуск показывает разницу, не создает дубликаты и не стирает ручные изменения Существующие 15 111 записей не заменяются результатом недоказанной формулы; создается сверка план/занятия Учебное управление и диспетчер: ручной расчет трех типов нагрузки, повторный запуск после переноса занятия
DEC-21 Граница запуска новой системы и дальнейшие изменения старой Рабочая схема — согласованный финальный снимок, сверка, переключение на PostgreSQL как единственный источник записи. Обратная запись в Paradox и двусторонняя синхронизация не предполагаются Требуется дата прекращения изменений старой копии и процедура повторного импорта до переключения; автономные изменения двух систем не сливаются автоматически Владелец продукта и руководитель эксплуатации: план перехода, ответственное лицо и критерии отката
DEC-22 Кто выпускает расписание, допустимы ли черновики и неполные назначения Редактирование выполняется в черновике; выпуск требует отсутствия блокирующих ошибок. Правила согласования кафедрами не добавляются автоматически из команды «Отдать» Исходный Assigned не считается признаком публикации. Импортированный период требует отдельного решения о выпуске Руководитель расписания: полномочия, перечень блокирующих ошибок и порядок уведомления об изменениях

Условия закрытия неопределенностей§

  1. Для DEC-01DEC-05 готовится воспроизводимый пример с исходной строкой, настройками старой программы, ожидаемой датой/длительностью и утвержденным результатом преобразования. Без этого импорт может быть завершен как загрузка исходных данных, но не как проверенная миграция действующего календаря.
  2. Для спорной операции фиксируются исходная запись, действие в тестовой копии прежней программы и результат после сохранения. Сведения из скриншота без выполнения не закрывают вопрос семантики.
  3. Если проверить старое поведение невозможно, заказчик утверждает новое правило как проектное отклонение. Исходные значения остаются доступны для сверки; критерий приемки проверяет утвержденное новое правило, а не заявленное полное совпадение неизвестного алгоритма.
  4. Автоматическое размещение/оптимизация не является условием признания ручного веб-редактора эквивалентным показанным функциям. Включение оптимизатора в первую версию требует самостоятельного решения об объеме и приемке.

Установка и автономная эксплуатация§

Профили окружения§

OPS-001. Предусмотреть три независимых окружения: разработка, приёмочный стенд и production. У них разные базы, пароли, домены/порты, тома и каталоги приватных файлов. Тесты и демонстрационные данные не запускаются на рабочей БД. Данные production для разработки обезличиваются либо используются только в специально защищённом стенде по принятому порядку доступа.

OPS-002. Поставка запускается на поддерживаемом Linux x86_64 с Docker Engine/Compose и постоянными томами. Рекомендуемый начальный профиль для всего приложения — 4 vCPU, 8 ГиБ RAM и 80 ГБ SSD; это плановый ориентир, который подтверждается нагрузочной приёмкой. Объём хранения учитывает БД, историю, WAL, исходники импорта, экспорты и резерв свободного места; он не равен размеру исходной SQLite.

На общем сервере Gintoft ресурсы уже используют другие приложения. Размещение новой системы на нём требует проверки доступной памяти, CPU, диска, портов и совместного нагрузочного испытания. Настоящее ТЗ не предписывает останавливать другие приложения, очищать их данные или менять действующее расписание. Для изоляции допустим отдельный сервер или отдельная согласованная квота ресурсов.

OPS-003. Основные сервисы Compose: web (Nginx и собранный клиент), api, worker, postgres, redis. Диспетчер сохранённых заданий имеет отдельный однозначный способ запуска. API и worker используют один образ приложения; разработческие серверы React/Django в production не запускаются. Для каждого процесса задать restart policy, healthcheck, ограничения памяти, разумный предел CPU и размер логов. Нехватка памяти одного процесса не должна вытеснять все соседние сервисы.

Предусмотреть два сетевых профиля: отдельная машина, где Nginx приложения принимает 80/443, и размещение за уже существующим Nginx заказчика, где сервис приложения слушает только свободный loopback-порт. Внешними остаются только нужные HTTP(S)-порты. Предметная PostgreSQL не должна использовать ту же роль/БД, что другие приложения на сервере.

Docker Compose поддерживает установку на одной машине и отдельные production-настройки; параметры эксплуатации необходимо поставлять явно. Docker: использование Compose в production.

Первичная установка и конфигурация§

OPS-004. Инструкция установки описывает: подготовку каталогов и секретов; создание томов; запуск PostgreSQL; применение миграций; создание администратора; настройку организации, часового пояса и домена; запуск сервисов; настройку HTTPS; проверку health; пробный вход; загрузку контрольного снимка и проверку восстановления. Чистая установка должна воспроизводиться человеком, который не разрабатывал систему, по этой инструкции.

OPS-005. Поставить .env.example с названиями параметров и безопасными примерами без реальных секретов. Рабочие секреты хранятся в закрытых файлах/секретах окружения вне репозитория. В обязательных параметрах: URL/реквизиты PostgreSQL, Django secret key, allowed hosts, trusted origins, режим TLS/proxy, часовой пояс, каталог приватных файлов, предел импорта, параметры брокера, лимиты заданий и срок экспорта.

OPS-006. Локальные Docker-тома PostgreSQL и приватных файлов переживают пересоздание контейнеров. Обычная команда обновления не содержит удаления томов. Для установки в закрытой сети предоставить архив образов/артефактов и необходимые зависимости; после запуска основная работа не требует входа в облачный сервис разработчика.

Резервное копирование и восстановление§

OPS-007. В B задать RPO не более 24 часов и RTO не более 4 часов для контрольного объёма. RPO означает допустимую потерю изменений при утрате всей установки; RTO — время восстановления доступности из подготовленной копии. Эти значения подтверждаются испытанием и согласуются с ответственным за инфраструктуру.

OPS-008. Не реже одного раза в сутки автоматически формировать согласованный комплект: PostgreSQL-дамп, необходимые роли/параметры восстановления, приватные исходники импорта, файлы, на которые ссылается БД, версии образов и схемы. Секреты восстанавливаются из отдельного защищённого источника. Проверять код завершения, размер, контрольную сумму и возможность чтения копии; один факт наличия файла не считается успешным backup.

OPS-009. Сохранять не менее 7 ежедневных и 4 еженедельных копий, как минимум одну вне основного сервера. Копии и передача защищаются шифрованием и отдельными правами. Раз в месяц выполнять восстановление в изолированном окружении с проверкой контрольных счетчиков и входа в систему. Перед изменением схемы — дополнительная копия и подтверждение возможности её восстановления. Не удалять последнюю исправную копию из-за ошибки очередной.

Для B предусмотрен логический backup PostgreSQL подходящими штатными средствами. Непрерывное архивирование WAL/PITR и меньший RPO могут быть расширением; если они понадобятся, задаётся отдельный профиль хранения и регулярно проверяется восстановление на выбранный момент. PostgreSQL: способы резервного копирования.

Обновления и откат§

OPS-010. Каждый выпуск имеет номер, хеш исходников/образов, список миграций, изменения конфигурации и инструкцию обновления. Обновление сначала проходит на стенде с копией структуры production. Миграции выполняет один процесс под отдельной ролью; одновременно запущенные API не пытаются мигрировать БД независимо друг от друга.

OPS-011. При изменении схемы предпочитать последовательность расширить → перенести → переключить → удалить устаревшее в последующем выпуске. План отката должен различать возврат образа и восстановление данных. Откат только приложения допустим, если предыдущий код совместим с новой схемой; иначе требуется заранее предусмотренная обратная миграция либо восстановление копии с явным учётом возможной потери новых изменений. Обещание «просто вернуть предыдущий контейнер» недостаточно.

OPS-012. Во время обслуживания показать понятное состояние, корректно завершить активные операции и не оборвать транзакцию с ложным успешным ответом. После обновления проверить вход, чтение расписания, тестовую команду в отдельной проверочной области, очередь, права и резервное копирование. Действующая публикация сохраняется до атомарного переключения.

Наблюдаемость§

OPS-013. Собирать структурированные логи с временем UTC, уровнем, request_id/job_id, идентификатором операции и безопасным кодом ошибки. Предметный аудит хранить отдельно от технического лога. Для каждой ошибки из интерфейса должен существовать диагностический идентификатор без раскрытия SQL, внутреннего пути и секрета.

OPS-014. Контролировать доступность API и PostgreSQL, CPU/RAM/свободный диск, длительность SQL и блокировок, очередь и время последнего heartbeat worker, ошибки публикации/импорта, возраст последнего исправного backup и срок TLS-сертификата. Выбранный канал уведомлений настраивается заказчиком; базовая диагностика и журнал доступны без внешнего мессенджера.

OPS-015. По умолчанию технические логи сохраняются 30 дней с ротацией; предметный аудит — 365 дней. Учебные периоды, опубликованные версии и исходники принятых импортов автоматически не удаляются: срок их хранения устанавливает заказчик, а удаление выполняется отдельной контролируемой процедурой с проверкой ссылок и резервных копий. Изменение политики хранения само фиксируется в аудите.

Порядок разработки и комплект поставки§

Этапы с проверяемым результатом§

Этап Работы Условие завершения
0. Уточнение предметных правил Выбрать правила нового календаря, единицы нагрузки, подгруппы и публикацию; оформить решения Q/DEC Реестр решений с примерами входа/выхода; оставшиеся неизвестные отделены в импортном карантине
1. Основа и перенос Окружение, пользователи, PostgreSQL, справочники, raw/staging-импорт и контрольный просмотр Все 19 источников сохранены; нет потерь полей; права и повторный импорт проверены
2. План и календарь Учебные периоды, звонки, состав групп по видам нагрузки, серии и генерация Синтетическое новое расписание имеет явные даты, корректную нагрузку и исключения
3. Редактор и ограничения Назначение, перенос, обмен, снятие, отмена, ресурсы, подгруппы, конфликты и конкуренция Сквозные операции и конкурентные тесты не создают двойную занятость и не теряют изменения
4. Согласование и выдача Кафедральные предложения, версии, публикация, печать и экспорт Опубликованный снимок неизменяем и соответствует проверенному состоянию
5. Производственная готовность Нагрузка, резервное копирование, восстановление, обновление, документация Воспроизведены NFR и RPO/RTO, выполнена установка на чистом стенде
6. Пилот и ввод Работа реального диспетчера на ограниченном периоде, устранение дефектов, подтверждение переноса Заказчик выполнил согласованные сценарии и утвердил рабочую редакцию данных

Этапы 1–5 можно частично выполнять параллельно. Неизвестная формула старого DayNo не блокирует создание нового расписания на явных датах, но блокирует утверждение, что импортированные записи уже имеют достоверное положение в календаре. Этап просмотра данных не считается завершением всего редактора.

Обязательные результаты передачи§

DEL-001. Передать заказчику:

  1. Репозиторий исходного кода клиента, сервера, миграций и инфраструктуры; инструкции сборки и запуска, перечень зависимостей и лицензий.
  2. OpenAPI, ER-схему PostgreSQL, словарь полей новой системы, правила состояний, повторений, конфликтов и публикаций.
  3. Зафиксированное соответствие всех 88 исходных полей и всех 59 пунктов каталога; список принятых преобразований и оставшихся ошибок импорта.
  4. Контрольный импорт с манифестом, хешами, исходными счётчиками, нормализованными результатами и причинами отклонений.
  5. Автоматические тесты и протоколы функциональных, конкурентных, нагрузочных и негативных проверок.
  6. Инструкции диспетчера, сотрудника кафедры, публикатора и администратора с конкретными сценариями и снимками новой версии интерфейса.
  7. Compose-конфигурации, шаблоны секретов без значений, проверенный сценарий обновления, резервного копирования и восстановления.
  8. Паспорта production и приёмочного окружений без секретов; список известных ограничений, миграционные решения и журнал изменений.
  9. Рабочие административные реквизиты через согласованный защищённый канал; никаких универсальных паролей разработчика в коде или документации.

Что считается готовым требованием§

DEL-002. Для каждого базового требования выполнены: реализация; проверка положительного и существенного отрицательного сценария; объектные права; нужные миграции; доступное сообщение об ошибке; документация; связь с тестом или ручным протоколом. Для операции записи также обязательны аудит, транзакционность и защита от повторного/конкурентного запроса. Запись «экран нарисован» не закрывает требование сохранения данных.

Общая программа приёмки§

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

ID Проверка Ожидаемый результат
ACC-01 Чистая автономная установка из поставки Вход, создание справочника и занятия без инструментов разработчика и внешнего SaaS
ACC-02 Импорт контрольного SQLite 75 909 исходных строк сохранены в raw-слое; 19 таблиц различимы; доступна сверка 88 полей
ACC-03 Неизвестные даты и единицы нагрузки Никаких вымышленных дат или автоматически созданных «пар»; строки видны в импорте и имеют причину ожидания
ACC-04 Повтор того же снимка и повтор команды commit Ни дублей предметных сущностей, ни второй активации; результат операции однозначен
ACC-05 Полный путь диспетчера Созданы период, группы, план, серия; занятие назначено, перенесено, ресурсы изменены; история доступна
ACC-06 Подгруппы и объединённое занятие Подтверждённые непересекающиеся части могут идти параллельно; целая группа конфликтует с любой своей частью; общий урок не дублируется по числу групп
ACC-07 Часы и фактические минуты Изменение длительности/повторений даёт объяснимый пересчёт по выбранной единице; 1 час не приравнивается к 1 паре неявно
ACC-08 Пустые ресурсы в черновике Сохранено датированное предварительное занятие с замечанием; публикация отклонена при невыполненных обязательных требованиях
ACC-09 Одновременное занятие одной аудитории Два независимых соединения пытаются занять пересекающиеся интервалы: не более одного commit в одном черновике
ACC-10 Одновременное изменение одной карточки Второй запрос получает конфликт версии; первое изменение не теряется
ACC-11 Атомарный обмен местами При успехе изменены оба события; при внешнем конфликте сохранены прежние значения обоих
ACC-12 Конфликт при изменении серии Указан конкретный конфликтующий экземпляр; заявленная атомарная операция не применена частично
ACC-13 Публикация одновременно с редактированием Активируется точно проверенная версия либо запрос отклоняется/пересчитывается; изменившийся кандидат не выдаётся как ранее проверенный
ACC-14 Общие ресурсы разных периодов Конфликтующие активные публикации не появляются даже при одновременной активации; смена схемы подгрупп не обходит проверку
ACC-15 Изменение справочника после публикации Исторический опубликованный вид сохраняет зафиксированные названия и состав; новый черновик получает изменения контролируемо
ACC-16 Права по API и экспортам Кафедра не меняет чужую область, читатель не редактирует, закрытые заметки не выдаются в публичный API или экспорт
ACC-17 Снятие и отмена Снятие возвращает в список размещения, отмена не считается проведённым занятием; занятость освобождена, причина и история сохранены
ACC-18 Недоступность worker/Redis/API Нет ложного успеха или двойной операции; после восстановления задача имеет корректное состояние
ACC-19 Отказ загрузки/транзакции импорта Рабочий черновик и действующая публикация не становятся наполовину импортированными
ACC-20 Резервная копия и утрата установки Изолированное восстановление укладывается в RPO/RTO; восстановлены данные, происхождение, права и история
ACC-21 Обновление и откат Следование инструкции сохраняет совместимую схему и данные либо явно восстанавливает согласованную копию
ACC-22 Нагрузка и удобство Приложены измерения NFR; формы, ошибки, таблицы и печать работают в заявленных браузерах и с клавиатуры
ACC-23 Публичный режим выключен/включен Без включения доступ закрыт; после включения выдаётся только текущий утверждённый разрешённый набор полей
ACC-24 Матрица старого и нового Все LEG-01…LEG-59 имеют реализацию, явно согласованное уточнение или статус расширения, без молчаливого пропуска

ACC-25. Условия выпуска. Закрыты все обязательные требования B либо оформлено явное изменение состава версии. Нет дефектов потери данных, обхода прав, незаметного перезаписывания и двойного бронирования. Согласованы решения, необходимые для рабочего календаря. Наличие нерешённых исторических записей допустимо лишь при их полном сохранении, объяснимом статусе и согласованном плане обработки; их нельзя выдавать за успешно восстановленное опубликованное расписание.

Сроки пилотной эксплуатации, поддержки и исправления дефектов определяются отдельным соглашением после оценки объёма; в этом документе они не подменяются произвольными обещаниями исполнителя.

Развитие продукта после базовой версии§

Эти возможности представлены как кандидаты. Они не являются установленными функциями старой программы и не включаются в B только из-за упоминания в списке.

ID Расширение Что требуется определить до разработки
EXT-01 Перетаскивание занятий по сетке Жесты, доступная альтернатива, выбор области серии, ошибка конфликта, подтверждение переноса
EXT-02 Автоматический подбор/оптимизация расписания Целевая функция, жёсткие и мягкие ограничения, веса, время поиска, фиксация неизменяемых элементов и объяснение невозможности
EXT-03 Несколько преподавателей/аудиторий на занятие Роли ресурсов, одновременность, распределение участников, правила занятости и оплаты/нагрузки
EXT-04 Разные схемы подгрупп по дисциплинам Сведения о пересечении составов, источник членства, проверка конфликтов и версионирование схем
EXT-05 Личные кабинеты студентов и преподавателей Способ идентификации, связь с персоной, источники данных, доступ к изменениям и личные предпочтения
EXT-06 SSO и многофакторный вход Провайдер, OIDC/SAML-контракт, правила связывания и отключения локальных аккаунтов
EXT-07 Интеграция с учебной системой/1С/кадрами Владельцы полей, направления обмена, внешние ключи, ошибки, повторные доставки и разрешение конфликтов
EXT-08 iCalendar, подписки и уведомления Состав событий, отмены/переносы, UID, приватность ссылок, частота и гарантии доставки
EXT-09 Несколько площадок и переходы между корпусами Справочник зданий, матрица времени перехода, дистанционный формат и временные буферы
EXT-10 Ограничения преподавателя и пожелания нагрузки Приоритеты, владельцы согласования, пределы занятий/окон и способ разрешения предупреждений
EXT-11 Фактическое проведение и замены по дням Разделение планового и фактического расписания, подтверждение проведения и учёт нагрузки
EXT-12 Несколько организаций/SaaS Изоляция арендаторов, квоты, администрирование, хранение и миграции каждого арендатора
EXT-13 Высокая доступность и RPO меньше суток Репликация PostgreSQL, резервный узел, WAL/PITR, мониторинг и испытание переключения
EXT-14 Обратный экспорт в старую программу Точные форматы и правила BDE/Paradox, проверка основной программы, тестовые копии и риск повреждения индексов
EXT-15 Сравнение нескольких альтернативных черновиков Ветки, общий план, независимые бронирования, сравнение, слияние и выбор публикации

Для оптимизатора базовый критерий будущего ТЗ должен включать воспроизводимый набор данных, запрет нарушения жёстких правил, статус «решение не найдено за отведённое время», возможность остановки и предпросмотр до применения. Нельзя обещать оптимальное расписание для любого набора ограничений без определения критерия и допустимого времени.

Шаблон карточки нового требования§

### NEW-001. Название функции

- Статус: предложение / согласовано / отложено / снято.
- Приоритет и целевая версия:
- Проблема пользователя и ожидаемый результат:
- Роли и область доступа:
- Основание: новая потребность / источник / действующее требование:
- Входные данные и предварительные условия:
- Основной сценарий:
- Ошибки и альтернативные сценарии:
- Изменяемые сущности, публикации и история:
- Влияние на календарь, занятость и ограничения:
- Новые поля, миграция данных и совместимость API:
- События аудита и приватные данные:
- Массовые операции, повторный запрос и конкуренция:
- Ограничения ресурсов и времени выполнения:
- Критерии приёмки (дано / действие / ожидаемый результат):
- Какие существующие требования меняются:
- Открытые вопросы и кто принимает решение:

Журнал редакций ТЗ§

Версия Дата Изменение Статус
0.1 2026-09-09 Исходное ТЗ на веб-версию, PostgreSQL, миграцию и автономную эксплуатацию по исследованной системе Подготовлено для правок заказчика
0.2 Дополнения заказчика, выбор расширений и уточнение предметных правил Заполняется после редактирования

Приложение: сопоставление всех 88 исходных полей§

Каждое поле дополнительно сохраняется в import_raw_row.raw_payload независимо от выбранной нормализованной цели. «Только исходное» означает доступность в защищенном браузере источника и отсутствие неподтвержденного влияния на расчет расписания. Указанные ниже типы — типы исходного Paradox; типы новой PostgreSQL-модели определены выше. Отсутствующие в старой системе данные, включая дату окончания периода, концы пар, часовой пояс и правила публикации, задаются в интерфейсе настройки.

Исходное поле Исходный тип Перенос и правило
CHAIRS.ChairNo AutoIncrement legacy_identitychair.id; не рабочий PK PostgreSQL
CHAIRS.ChairName Alpha(50 байт) chair.name
CHAIRS.FacultyNo Long chair.faculty_id; отсутствующие 46/48 — задача сопоставления
CLASSINF.ClassNo Long Только исходная связь дополнительного ресурса до включения расширения; самостоятельный источник
CLASSINF.RoomNo Long Только исходная связь дополнительной аудитории; часть составного ключа
CLASSINF.TeacherNo Long Только исходная связь дополнительного преподавателя
Classes.ClassNo AutoIncrement legacy_identity → импортируемая lesson_series; порожденные экземпляры сохраняют ссылку происхождения
Classes.GroupNo Long series_participant.group_id / occurrence_participant.group_id; для −1 раскрыть GRPPLAN
Classes.Subgroup Short series_participant.subgroup_id / occurrence_participant.subgroup_id; −1 не создает фиктивную подгруппу
Classes.PlanNo Long Найти plan_component через исходную пару (PlanNo,PlanType), установить lesson_series.component_id
Classes.PlanType Alpha(1 байт) Та же исходная пара определяет plan_component.lesson_type_id; название кода подтвердить
Classes.EachWeek Logical Исходное значение; lesson_series.recurrence_rule только после подтверждения календарных правил
Classes.Assigned Logical Исходное значение; не присваивать напрямую lesson_occurrence.status
Classes.OddWeek Logical Исходное значение; правило чередования только после утверждения точки отсчета и приоритета EachWeek
Classes.DayNo Short Исходный составной код; даты lesson_occurrence только по подтвержденному декодеру
Classes.TimeNo Short Сопоставление bell_slot; время экземпляра после утверждения даты и окончания/длительности
Classes.TeacherNo Long lesson_series.default_teacher_id и lesson_occurrence.teacher_id; −1 → NULL с сохранением оригинала
Classes.RoomNo Long lesson_series.default_room_id и lesson_occurrence.room_id; −1 → NULL с сохранением оригинала
Classes.Comment Alpha(64 байт) lesson_occurrence.private_comment после генерации экземпляров; полный текст в исходной строке
ClassesInf.PlanNo Long Только служебная сверка по исходному плану; не создает план или занятие
ClassesInf.RoomNo Long Только служебная сверка; одно расхождение с Classes требует решения
ClassesInf.EachWeek Logical Только служебная сверка исходного флага
ClassesInf.OddWeek Logical Только служебная сверка исходного флага
ClassesInf.Assigned Logical Только служебная сверка исходного флага
ClassesInf.DayNo Short Только служебная сверка исходного кода позиции
ClassesInf.TimeNo Short Только служебная сверка исходного номера времени
ClassesInf.Stable Logical Только сверка с Plan.Stable; не задает фиксацию занятия
Courses.CourseNo AutoIncrement legacy_identitycourse_category.id; не study_year
Courses.CourseCount Short Только исходное число до расшифровки; не study_year
Courses.CourseName Alpha(255 байт) course_category.name
DAYS.DayNo Short Таблица соответствия день 0–5 → ISO-день 1–6; не FK для всех Classes.DayNo
DAYS.DayName Alpha(11 байт) Исходная подпись дня для просмотра/проверки локализации
DAYS.ShortDayName Alpha(2 байт) Исходная короткая подпись дня для просмотра/проверки локализации
FACULTY.FacultyNo AutoIncrement legacy_identityfaculty.id
FACULTY.FacultyName Alpha(32 байт) faculty.name
GROUPS.GroupNo AutoIncrement legacy_identityacademic_group.id
GROUPS.GroupName Alpha(8 байт) academic_group.name
GROUPS.FacultyNo Long academic_group.faculty_id по исходному ключу
GROUPS.NSubgroups Short Число исходных подгрупп; подготовить group_partition и subgroup, требующие подтверждения независимости
GROUPS.NStudents Short academic_group.student_count; число подгруппы не вычислять равным делением
GROUPS.StreamNo Long cohort_group только при подтвержденной положительной ссылке; в этом снимке −1 у всех групп
GROUPS.CourseNo Long academic_group.course_category_id по ключу классификатора
GRPPLAN.GroupNo Long plan_participant.group_id; часть исходного составного ключа
GRPPLAN.Subgroup Short plan_participant.subgroup_id; исходную −1 трактовать в контексте состава
GRPPLAN.PlanNo Long plan_participant.plan_id; часть исходного составного ключа
PLANITEM.PlanNo Long plan_component.plan_id; часть исходного составного ключа
PLANITEM.PlanType Alpha(1 байт) plan_component.lesson_type_id через подтвержденный справочник кодов
PLANITEM.PlanQty Short plan_component.planned_quantity, первоначально quantity_unit=unknown; не счетчик занятий
PLANITEM.GrpSplit Logical Исходный флаг и предлагаемый split_policy; способ генерации подтверждается до применения
PLANROOM.PlanNo Long plan_room_preference.component_id через пару (PlanNo,PlanType), если появятся строки
PLANROOM.PlanType Alpha(1 байт) Та же пара определяет компонент; смысл предпочтения требует подтверждения
PLANROOM.RoomNo Long plan_room_preference.room_id по исходному ключу; не фактическое бронирование
Plan.PlanNo AutoIncrement legacy_identitycurriculum_plan.id
Plan.SubjectNo Long curriculum_plan.subject_id
Plan.ChairNo Long curriculum_plan.chair_id
Plan.Stable Logical Только исходный флаг до проверки действия; не lesson_occurrence.is_fixed
ROOMRSTR.RoomNo Long availability_rule.resource_id ресурса комнаты после нормализации ограничения
ROOMRSTR.DayNo Short Исходный день 0–5; календарное правило доступности после привязки периода
ROOMRSTR.TimeNo Short Ссылка на исходный слот; конкретный интервал требует времени окончания
ROOMRSTR.Week Short Исходное значение 0/1; смысл и приоритет чередования должны быть утверждены
ROOMRSTR.Reason Alpha(64 байт) availability_rule.reason; закрытое текстовое поле, полный текст сохраняется
Rooms.RoomNo AutoIncrement legacy_identityroom.id
Rooms.RoomName Alpha(8 байт) room.name; не разбирать автоматически на корпус/кабинет
Rooms.RoomCap Short room.capacity
Rooms.RoomSpec Logical Только исходный флаг; не создавать оборудование по догадке
Rooms.RoomType Alpha(1 байт) room.room_type_id через отдельный подтвержденный классификатор
Rooms.RoomMemo Alpha(64 байт) room.private_note
Rooms.ChairNo Long room.chair_id; 0 и −1 сохраняются раздельно до решения
STREAMS.StreamNo AutoIncrement legacy_identitynamed_cohort.id
STREAMS.StreamName Alpha(255 байт) named_cohort.name; имя не определяет участников занятия
SUBJECTS.SubjectNo AutoIncrement legacy_identitysubject.id
SUBJECTS.SubjectName Alpha(35 байт) subject.name
SUBJECTS.SubjectChar Alpha(1 байт) subject.legacy_character; без новой бизнес-логики до расшифровки
SUBJECTS.SubjectShortName Alpha(10 байт) subject.short_name
TCHRRSTR.TeacherNo Long availability_rule.resource_id ресурса преподавателя после нормализации ограничения
TCHRRSTR.DayNo Short Исходный день 0–5; календарное правило доступности после привязки периода
TCHRRSTR.TimeNo Short Ссылка на исходный слот; конкретный интервал требует времени окончания
TCHRRSTR.Week Short Исходное значение 0/1; трактовка требует подтверждения
TCHRRSTR.Reason Alpha(64 байт) availability_rule.reason; закрытое текстовое поле, полный текст сохраняется
TEACHERS.TeacherNo AutoIncrement legacy_identityteacher.id
TEACHERS.TeacherName Alpha(30 байт) teacher.full_name; не уникальный ключ
TEACHERS.ChairNo Long teacher.chair_id; −1 → NULL только по правилу этого поля
TEACHERS.TeacherDegree Alpha(16 байт) teacher.degree
TEACHERS.TeacherPosition Alpha(16 байт) teacher.position
TEACHERS.TeacherTitle Alpha(16 байт) teacher.title
TEACHERS.TeacherMemo Alpha(40 байт) teacher.private_note
TIMES.TimeNo Short legacy_identitybell_slot.id; исходный номер → bell_slot.ordinal
TIMES.TimeName Alpha(10 байт) Разбор в bell_slot.start_time; оригинальная строка сохраняется, окончания нет в источнике