Анализ связей и качества данных расписания§
Отчет получен скриптом 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, а в ClassesInf — RoomNo=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.