Исследование системы составления расписания

Анализ связей и качества данных расписания§

Отчет получен скриптом research/analyze_relations.py из 19 JSON-экспортов активных строк в exports/. Исходные DB не менялись. Первичные ключи взяты из схемы формата; внешние связи проверены по значениям, но не объявляются найденными физическими FK. Содержимое комментариев, причин ограничений, ФИО и заметок не публикуется.

Объем и первичные ключи§

Таблица Активные строки PK
CHAIRS 47 ChairNo
CLASSINF 0 ClassNo, RoomNo
Classes 15111 ClassNo
ClassesInf 15111 не задан
Courses 12 CourseNo
DAYS 6 DayNo
FACULTY 6 FacultyNo
GROUPS 554 GroupNo
GRPPLAN 21419 GroupNo, Subgroup, PlanNo
PLANITEM 7098 PlanNo, PlanType
PLANROOM 0 PlanNo, PlanType, RoomNo
Plan 4748 PlanNo
ROOMRSTR 990 RoomNo, DayNo, TimeNo, Week
Rooms 184 RoomNo
STREAMS 140 StreamNo
SUBJECTS 5068 SubjectNo
TCHRRSTR 4541 TeacherNo, DayNo, TimeNo, Week
TEACHERS 866 TeacherNo
TIMES 8 TimeNo

Во всех 18 таблицах, для которых в схеме задан PK, нет повторов или NULL в ключе; проверка двух пустых таблиц тривиальна. ClassesInf не имеет PK. В ClassesInf счетчик заголовка 15 124, активных строк 15 111 (разница 13); для прикладного анализа использован экспорт связного списка активных блоков.

Модель и проверенные связи§

FACULTY → CHAIRS → TEACHERS описывает организацию преподавателей; FACULTY → GROUPS ← Courses — организацию групп; SUBJECTS → Plan ← CHAIRS — дисциплину и кафедру в плане. Plan → PLANITEM задает виды занятий и объем, Plan ← GRPPLAN → GROUPS связывает план с группами и подгруппами. Classes содержит отдельные записи занятий с планом, видом, группой/подгруппой, преподавателем, аудиторией и позицией.

Полностью совпадают ссылки Plan.SubjectNo, Plan.ChairNo, GRPPLAN.GroupNo, GRPPLAN.PlanNo, PLANITEM.PlanNo, Classes.PlanNo, GROUPS.FacultyNo, GROUPS.CourseNo. Все 15 111 сочетаний (Classes.PlanNo, Classes.PlanType) существуют в PLANITEM; у всех 7 098 элементов PLANITEM есть хотя бы одна запись Classes. Все явные группы/подгруппы Classes входят в GRPPLAN соответствующего плана. Все подгруппы GRPPLAN и Classes укладываются в GROUPS.NSubgroups.

Все ссылки преподавателя/аудитории/времени в Classes корректны после отдельного учета -1 и NULL. Ограничения TCHRRSTR и ROOMRSTR целиком ссылаются на существующие ресурсы, дни 0–5 и пары 1–8.

Служебные значения и неполные связи§

Поле Наблюдение Безопасная трактовка при чтении
Classes.GroupNo -1: 3 458 Получать список групп через GRPPLAN данного PlanNo
Classes.Subgroup -1: 11 238; 1–5: 3 873 -1 означает отсутствие конкретной подгруппы в записи
Classes.TeacherNo -1: 2 352 Преподаватель не указан
Classes.RoomNo -1: 5 665 Аудитория не указана
Classes.DayNo/TimeNo/OddWeek NULL у одних и тех же 3 293 записей Позиция/четность не заполнена
GROUPS.StreamNo -1 у всех 554 групп Прямые ссылки на STREAMS не используются в этом снимке
TEACHERS.ChairNo -1: 3 Кафедра не указана
Rooms.ChairNo 0: 66, -1: 85, корректный ID: 33 Не смешивать два служебных значения без проверки приложения
CHAIRS.FacultyNo NULL: 5, ID 46 и 48: по 1 Два положительных ID отсутствуют в FACULTY; требуется сверка

Нельзя глобально заменять 0 на NULL: DAYS.DayNo=0 — понедельник, а Week=0 присутствует в ограничениях как нормальное значение.

Группы, подгруппы и потоки§

GRPPLAN содержит 21 419 связей; все 4 748 планов связаны хотя бы с одной группой. С одной группой связаны 3 074 плана, с несколькими — 1674; максимум — 21 группа. Состав объединенного занятия следует выводить из GRPPLAN, а не из названия или комментария.

Все 3 458 записей Classes.GroupNo=-1 имеют Subgroup=-1, GrpSplit=False; это 3 258 записей типа л и 200 типа э. Среди них есть планы как с одной, так и с несколькими группами. Следовательно, GroupNo=-1 нельзя переводить в интерфейсе как «ошибка/группа потеряна» или исключительно «поток из нескольких групп».

Все 3 873 записи с конкретной подгруппой имеют PLANITEM.GrpSplit=True. 22 записи с GrpSplit=True и Subgroup=-1 относятся только к группам с одной подгруппой, поэтому сами по себе не выглядят ошибкой. В STREAMS есть 140 названий, но в GROUPS нет ни одной ссылки на них: справочник сохранен, однако реальные объединения для расписания видны через план.

При раскрытии объединенных занятий получается 17 727 отображений «занятие–группа», при этом физических записей Classes по-прежнему 15 111. Фильтр расписания группы должен брать как прямые записи этой группы, так и записи GroupNo=-1 планов, связанных с группой через GRPPLAN; после соединения нужна дедупликация по ClassNo, поскольку у группы может быть несколько подгрупп.

Позиция в расписании и флаги§

DAYS задает 0=понедельник … 5=суббота. TIMES задает восемь начал пар: 08:00, 09:40, 11:25, 13:20, 15:05, 16:50, 18:30, 20:05. Концы пар в этой таблице не указаны.

Classes.DayNo нельзя напрямую связывать с DAYS.DayNo: только 4 018 записей имеют значения 0–5. Еще 7 800 имеют значения 14–299, а 3 293 — NULL. Для всех 11 818 непустых значений выполнено DayNo % 14 ∈ {0,1,2,3,4,5}. Это сильное свидетельство упаковки идентификатора периода и дня недели в одном числе. Арифметическое разложение quotient=DayNo//14, weekday=DayNo%14 проверено на всех строках, но названия и порядок периодов требуют конфигурации или проверки в приложении. Число 294 нельзя объявлять 294-м календарным днем или 42-й неделей только по базе.

DayNo//14 Диапазон DayNo Записей Assigned=True
0 0–5 4018 3870
1 14–19 556 554
2 28–33 725 722
3 42–47 109 107
4 56–61 149 147
5 70–75 42 42
10 140–145 25 0
11 158–158 6 0
13 182–187 741 731
15 210–215 339 329
19 266–271 266 263
20 280–285 400 387
21 294–299 4442 4378

Assigned=True у 11 530 записей, False у 3 581. Все True имеют позицию, но 288 False также имеют заполненные DayNo, TimeNo и OddWeek; остальные 3 293 False не имеют позиции. Кроме того, среди Assigned=True есть 2 339 без аудитории, в том числе 23 также без преподавателя. Значение флага не эквивалентно ни наличию координат, ни полноте ресурсов; точную команду/состояние приложения, которому он соответствует, следует проверить отдельно.

EachWeek=True у 23 записей, False у 15 088. OddWeek=True у 6 035, False у 5 783, NULL у 3 293. Среди EachWeek=True значение OddWeek также сохранено (True: 19, False: 4); поэтому при отображении нельзя независимо выводить обе метки как два разных ограничения. Названия полей предполагают еженедельность/четность, но календарная привязка первой недели из самих таблиц не следует.

Виды занятий и объем плана§

Код PlanType PLANITEM Classes
з 154 358
к 100 101
л 2694 3258
п 261 621
с 3789 10573
э 100 200

База хранит коды, а не отдельный справочник расшифровок типов. Расшифровки л/с/п/з/э/к следует подтверждать настройками или надписями интерфейса. PLANITEM.PlanQty принимает 1, 2, 4, 6, 8, 10, 12, 14, 15, 16, 18; это не счетчик физических строк Classes: он взаимодействует с видом занятия и разделением групп. Например, в 374 элементах типа л значение PlanQty=2 соответствует одной строке Classes, а в 71 элементе того же типа PlanQty=4 — двум строкам. Объем не стоит пересчитывать или изменять без проверки единицы измерения и алгоритма создания занятий.

ClassesInf и CLASSINF — разные таблицы§

CLASSINF пуста и имеет составной PK (ClassNo, RoomNo) плюс TeacherNo: структура позволяет связывать занятие с дополнительными ресурсами, однако фактическое использование в снимке отсутствует.

ClassesInf содержит почти точную проекцию Classes по полям PlanNo, RoomNo, EachWeek, OddWeek, Assigned, DayNo, TimeNo и добавляет Plan.Stable. Значение Stable совпадает с соответствующим Plan во всех 15 111 строках. Таблица похожа на производную служебную выборку/кэш, но ее роль при запуске программы требует проверки. В ней нет ClassNo, группы, подгруппы, вида занятия и преподавателя, поэтому она не заменяет Classes.

Сравнение выполнено как сравнение мультимножеств (порядок строк не использован). Выявлена одна разница: для PlanNo=226555, DayNo=16, TimeNo=3, EachWeek=False, OddWeek=True, Assigned=True в Classes стоит RoomNo=10141, а в ClassesInfRoomNo=10151. Оба ID существуют. Это конкретный сигнал рассогласования копии/кэша; без запуска программы нельзя установить, какая аудитория будет фактически показана и какой источник она считает главным.

Качество, полнота и рекомендации для чтения§

  • Приоритетные проверки: расхождение одной аудитории между Classes и ClassesInf; счетчик ClassesInf на 13 строк больше активных; две кафедры с отсутствующими FacultyNo 46 и 48.
  • Незаданные ресурсы и позиции — состояния данных, а не автоматически повреждение файла. Их стоит показывать отдельными фильтрами и сохранять исходные значения.
  • Нет дублей PK. Есть повторяющиеся названия дисциплин (5 068 строк, 5 028 уникальных полных названий; 4 667 уникальных сокращений) и кафедр (47 строк, 33 уникальных названия). Соединения по имени небезопасны; используйте ID.
  • Без ссылок из других экспортированных таблиц остаются 4 066 дисциплин, 82 преподавателя, 19 аудиторий, 32 группы и все 140 потоков. Это может быть запас/наследие справочника; автоматически удалять такие строки нельзя.
  • Comment заполнен у 2 915 занятий; Reason — у всех 4 541 ограничений преподавателей и 990 ограничений аудиторий. Эти тексты и заметки могут содержать служебную или персональную информацию; в публичной выдаче лучше исключить их по умолчанию.
  • Поля Alpha ограничены байтовой длиной. Например, встречаются тексты длиной ровно 64 символа в полях на 64 байта; это повод проверять новые вводимые значения, но не доказательство уже состоявшегося обрезания.
  • Фильтрация расписания группы требует раскрытия GRPPLAN; фильтрация периода требует декодирования DayNo. Для проверки пересечений нужны подтвержденные значения Assigned, EachWeek/OddWeek, периоды и состав групп. Простое совпадение DayNo/TimeNo без этих правил не является доказанным конфликтом.

Полные агрегаты, распределения, результаты каждой кандидатной связи и SHA-256 входных JSON находятся в research/metrics.json. Повторный запуск скрипта перезаписывает только этот отчет и metrics.json.