В таблице появились группы с «ТОП» в имени (ИВТ-ТОП-11БО, ИТ-ТОП-12БО): шаблон требовал цифру сразу после первого дефиса, поэтому такие столбцы пропускались целиком. Теперь имя группы — буквенные части через дефис, номер потока и необязательный суффикс, т.е. распознаются и «ИВТ-11БО», и «ПИЭ-13МО», и «ИВТ-ТОП-11БО» (похожие заголовки с пробелами — нет). GET /groups больше не сортирует по алфавиту с приоритетом БО/МО, а отдаёт группы в порядке столбцов таблицы — как просили, «как в расписании». Состав групп (появление/переименование/скрытие) подхватывается сам при следующем обновлении, потому что источник истины — заголовок листа. Тесты: ТОП-имена + порядок столбцов, отсев похожих не-групповых заголовков.
196 lines
7.6 KiB
Python
196 lines
7.6 KiB
Python
"""Тесты разбора листов: группы, дни/пары/чётность, индексы, вспомогательные листы."""
|
||
|
||
from datetime import date
|
||
|
||
import pytest
|
||
|
||
from fixtures import fixture_sheets, make_sheets
|
||
from schedule import parsing
|
||
from schedule.errors import ScheduleError
|
||
|
||
|
||
@pytest.fixture()
|
||
def sheets():
|
||
return make_sheets(fixture_sheets())
|
||
|
||
|
||
@pytest.fixture()
|
||
def data(sheets):
|
||
return parsing.parse_schedule(sheets)
|
||
|
||
|
||
def test_hidden_group_excluded(data):
|
||
assert set(data) == {"ИВТ-11БО", "ИТ-12БО"}
|
||
|
||
|
||
def test_group_names_with_top_and_sheet_order():
|
||
"""ТОП-группы распознаются, а порядок групп — как столбцы в таблице."""
|
||
sheets = fixture_sheets()
|
||
header = sheets[parsing.MAIN_SHEET].rows[0]
|
||
header[6] = "ИТ-ТОП-11БО\nсеместр до 11.01.27"
|
||
header[7] = "АА-11БО\nсеместр до 29.12.26"
|
||
|
||
data = parsing.parse_schedule(make_sheets(sheets))
|
||
assert list(data) == ["ИТ-ТОП-11БО", "АА-11БО"] # не по алфавиту, а по столбцам
|
||
assert data["ИТ-ТОП-11БО"]["days"]["понедельник"][1]["ч"]
|
||
|
||
|
||
def test_not_a_group_headers_ignored():
|
||
"""Похожие, но не групповые заголовки столбцами групп не становятся."""
|
||
sheets = fixture_sheets()
|
||
header = sheets[parsing.MAIN_SHEET].rows[0]
|
||
header[9] = "ИВТ 11БО" # пробел вместо дефиса — это не имя группы
|
||
|
||
data = parsing.parse_schedule(make_sheets(sheets))
|
||
assert set(data) == {"ИВТ-11БО", "ИТ-12БО"}
|
||
|
||
|
||
def test_semester(data):
|
||
assert data["ИВТ-11БО"]["semester_until"] == "11.01.27"
|
||
assert data["ИТ-12БО"]["semester_until"] == "29.12.26"
|
||
|
||
|
||
def test_day_period_parity(data):
|
||
mon = data["ИВТ-11БО"]["days"]["понедельник"]
|
||
p1 = mon[1]
|
||
assert p1["time"] == "9:00-10:35"
|
||
assert p1["ч"] is not None and p1["з"] is not None
|
||
assert "Математика" in p1["ч"]
|
||
assert "216" in p1["з"]
|
||
|
||
|
||
def test_empty_parity_slot_kept(data):
|
||
mon = data["ИТ-12БО"]["days"]["понедельник"]
|
||
assert mon[1]["з"] is None
|
||
# время на пустой слот подставлено
|
||
assert mon[1]["time"] == "9:00-10:35"
|
||
|
||
|
||
def test_lessons_preparsed(data):
|
||
mon = data["ИВТ-11БО"]["days"]["понедельник"]
|
||
lesson = mon[1]["lessons"]["ч"][0]
|
||
assert lesson["subject"] == "Математика"
|
||
assert lesson["teacher"] == "Смирнов А.В."
|
||
assert lesson["room"] == "ауд. 215"
|
||
assert lesson["note"] == "пара 14.09 не будет"
|
||
# пустое parity -> None
|
||
assert data["ИТ-12БО"]["days"]["понедельник"][1]["lessons"]["з"] is None
|
||
|
||
|
||
def test_marker_parsed(data):
|
||
mon_g1 = data["ИВТ-11БО"]["days"]["понедельник"]
|
||
mon_g2 = data["ИТ-12БО"]["days"]["понедельник"]
|
||
assert mon_g1[2]["lessons"]["ч"][0]["marker"] == "к.в. - 2"
|
||
assert mon_g2[2]["lessons"]["з"][0]["marker"] == "к.в."
|
||
|
||
|
||
def test_tuesday_only_ch(data):
|
||
tue = data["ИВТ-11БО"]["days"]["вторник"]
|
||
assert tue[1]["ч"] and "Биология" in tue[1]["ч"]
|
||
assert tue[1]["з"] is None
|
||
|
||
|
||
def test_missing_main_sheet(sheets):
|
||
del sheets[parsing.MAIN_SHEET]
|
||
with pytest.raises(ScheduleError):
|
||
parsing.parse_schedule(sheets)
|
||
|
||
|
||
def test_odd_day_block_warns(caplog):
|
||
sheets = make_sheets(fixture_sheets())
|
||
# уберём вторую строку понедельника (пара 1, з) -> нечётное число строк
|
||
rows = sheets[parsing.MAIN_SHEET].rows
|
||
# строки: 0 header, 1 с/по, 2..5 понедельник, 6..7 вторник
|
||
del rows[3]
|
||
with caplog.at_level("WARNING"):
|
||
result = parsing.parse_schedule(sheets)
|
||
assert any("сдвинуться" in r.message for r in caplog.records)
|
||
# разбор всё равно завершается
|
||
assert "ИВТ-11БО" in result
|
||
|
||
|
||
def test_week_ranges(sheets):
|
||
weeks = parsing.extract_week_ranges(sheets)
|
||
assert len(weeks) == 8
|
||
assert weeks[0] == {"parity": "ч", "start": date(2026, 8, 31), "end": date(2026, 9, 6)}
|
||
assert weeks[1]["parity"] == "з"
|
||
assert weeks[-2]["parity"] == "ч"
|
||
|
||
|
||
def test_current_parity():
|
||
sheets = make_sheets(fixture_sheets())
|
||
weeks = parsing.extract_week_ranges(sheets)
|
||
assert parsing.current_parity(weeks, date(2026, 9, 1)) == "ч"
|
||
assert parsing.current_parity(weeks, date(2026, 9, 9)) == "з"
|
||
assert parsing.current_parity(weeks, date(2030, 1, 1)) is None
|
||
|
||
|
||
def test_teacher_room_index(data):
|
||
teachers, rooms = parsing.build_teacher_room_index(data)
|
||
assert set(teachers) == {"Смирнов А.В.", "Иванов И.И.", "Петров П.П.", "Сидорова С.С."}
|
||
|
||
smirnov = teachers["Смирнов А.В."]["понедельник"]["1"]
|
||
assert len(smirnov["ч"]) == 1 and len(smirnov["з"]) == 1
|
||
assert smirnov["ч"][0]["subject"] == "Математика"
|
||
assert smirnov["ч"][0]["room"] == "ауд. 215"
|
||
assert smirnov["ч"][0]["groups"] == ["ИВТ-11БО"]
|
||
|
||
petrov_zn = teachers["Петров П.П."]["понедельник"]["2"]["з"]
|
||
assert petrov_zn[0]["marker"] == "к.в."
|
||
|
||
assert set(rooms) == {"ауд. 215", "ауд. 216", "ауд. 301", "ауд. 220", "ауд. 201"}
|
||
room_301 = rooms["ауд. 301"]["понедельник"]
|
||
assert room_301["1"]["ч"][0]["subject"] == "Физика"
|
||
assert room_301["1"]["ч"][0]["teacher"] == "Иванов И.И."
|
||
assert room_301["2"]["ч"][0]["groups"] == ["ИВТ-11БО"]
|
||
|
||
|
||
def test_day_times(data):
|
||
times = parsing.build_day_times(data)
|
||
assert times["понедельник"] == {"1": "9:00-10:35", "2": "10:45-12:20"}
|
||
assert times["вторник"] == {"1": "9:00-10:35"}
|
||
|
||
|
||
def test_group_schedule_shape(data):
|
||
out = parsing.build_group_schedule(data, "ИВТ-11БО")
|
||
assert out["group"] == "ИВТ-11БО"
|
||
assert out["semester_until"] == "11.01.27"
|
||
p1 = out["days"]["понедельник"]["1"]
|
||
assert p1["time"] == "9:00-10:35"
|
||
assert "raw" in p1["ч"] and "lessons" in p1["ч"]
|
||
assert p1["з"]["lessons"][0]["room"] == "ауд. 216"
|
||
assert parsing.build_group_schedule(data, "НЕТ-11БО") is None
|
||
|
||
|
||
def test_rooms_info(sheets):
|
||
info = parsing.parse_rooms_info(sheets)
|
||
assert info == {
|
||
"ауд. 215": {"room": "ауд. 215", "info": "комп.класс, 12 мест"},
|
||
"ауд. 301": {"room": "ауд. 301", "info": "интерактивная панель, 24 места"},
|
||
"ауд. 220": {"room": "ауд. 220", "info": "20 мест"},
|
||
}
|
||
|
||
|
||
def test_teachers_full(sheets):
|
||
full = parsing.parse_teachers_full(sheets)
|
||
assert full == {
|
||
"Смирнов А.В.": "Смирнов Александр Викторович",
|
||
"Иванов И.И.": "Иванов Иван Иванович",
|
||
}
|
||
|
||
|
||
def test_sort_rooms():
|
||
assert parsing.sort_rooms(["ауд. 10", "ауд. 9", "ауд. 100", "зал"]) == [
|
||
"ауд. 9", "ауд. 10", "ауд. 100", "зал",
|
||
]
|
||
|
||
|
||
def test_build_room_schedule(data):
|
||
_, rooms = parsing.build_teacher_room_index(data)
|
||
times = parsing.build_day_times(data)
|
||
out = parsing.build_room_schedule(rooms, "ауд. 301", times)
|
||
# дни с расписанием плюс дни, где есть время пар (даже без занятий)
|
||
assert set(out["days"]) == {"понедельник", "вторник"}
|
||
assert out["days"]["понедельник"]["1"]["ч"][0]["subject"] == "Физика"
|
||
assert parsing.build_room_schedule(rooms, "ауд. 999", times) is None
|