diff --git a/README.md b/README.md index 64cb587..929bb9c 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,9 @@ nginx (https://schedule.our-place.ru) → 127.0.0.1:8090 столбцы 2–3 даты «знаменателя» столбец 4 день недели (указан один раз на блок дня) столбец 5 номер пары и время (ячейка объединена на две строки пары) -столбцы 6+ группы — по одному столбцу на группу (скрытые столбцы пропускаются) +столбцы 6+ группы — по одному столбцу на группу, порядок столбцов = порядок + групп на сайте (скрытые столбцы пропускаются; имена вида + «ИВТ-11БО», «ПИЭ-13МО», «ИВТ-ТОП-11БО») ``` Каждый день занимает блок строк, и строки идут парами: «числитель» / @@ -168,7 +170,7 @@ tests/ | Метод | Путь | Описание | |---|---|---| -| GET | `/groups` | список групп (сначала БО, потом МО) | +| GET | `/groups` | группы в том порядке, в каком идут столбцы в таблице | | GET | `/schedule/{group}` | расписание группы с разбором на занятия | | GET | `/schedule/{group}/{day}` | расписание группы на конкретный день | | GET | `/today?group={group}` | расписание группы на сегодня | diff --git a/src/backend/main.py b/src/backend/main.py index 276f7b8..9ed4301 100644 --- a/src/backend/main.py +++ b/src/backend/main.py @@ -165,11 +165,9 @@ def create_app( @app.get("/groups") def groups() -> list[str]: + """Группы в том порядке, в каком идут столбцы в таблице.""" data, _ = _data() - return sorted( - data.keys(), - key=lambda g: (0 if g.endswith("БО") else (1 if g.endswith("МО") else 2), g), - ) + return list(data.keys()) @app.get("/teachers") def teachers() -> list[str]: diff --git a/src/backend/schedule/parsing.py b/src/backend/schedule/parsing.py index e3ea942..8d14091 100644 --- a/src/backend/schedule/parsing.py +++ b/src/backend/schedule/parsing.py @@ -35,7 +35,9 @@ _COL_DAY = 4 # день недели _COL_PERIOD = 5 # пара и время _FIRST_GROUP_COL = 6 -_GROUP_RE = re.compile(r"^[А-ЯЁ]{2,4}-\d") +# Имя группы: буквенные части через дефис, номер потока и необязательный +# суффикс: «ИВТ-11БО», «ПИЭ-13МО», «ИВТ-ТОП-11БО», «ИТ-ТОП-12БО». +_GROUP_RE = re.compile(r"^[А-ЯЁ]+(?:-[А-ЯЁ]+)*-\d{1,2}[А-ЯЁ]{0,3}$") _PERIOD_RE = re.compile(r"^\s*(\d{1,2})\s*(.*)$") _ROOM_RE = re.compile(r"ауд\.\s*([\w\-]+)", re.I) _SEMESTER_DATE_RE = re.compile(r"(\d{1,2}\.\d{1,2}\.\d{2})") @@ -108,13 +110,16 @@ def current_parity(week_ranges: list[dict], today: date | None = None) -> str | def _find_group_columns(header: list[str], hidden_cols: set[int]) -> dict[int, str]: + """Столбцы групп: имя из заголовка, порядок — как столбцы в таблице.""" columns: dict[int, str] = {} seen: set[str] = set() for idx, cell in enumerate(header): if idx in hidden_cols: continue name = _normalize_header(cell) - if name and _GROUP_RE.match(name) and name not in seen: + if not name or name in seen or len(name) > 24: + continue + if _GROUP_RE.match(name): columns[idx] = name seen.add(name) return columns diff --git a/tests/test_api.py b/tests/test_api.py index a00eebc..dc81d68 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -46,6 +46,21 @@ def test_groups(client): assert client.get("/groups").json() == ["ИВТ-11БО", "ИТ-12БО"] +def test_groups_keep_sheet_order(monkeypatch): + """GET /groups отдаёт группы в порядке столбцов таблицы, а не по алфавиту.""" + from fixtures import fixture_sheets + from xlsx_builder import build_workbook + + sheets = fixture_sheets() + header = sheets["Бакалавры и магистры"].rows[0] + header[6] = "ЯЯ-ТОП-11БО\nсеместр до 11.01.27" + header[7] = "АА-11БО\nсеместр до 29.12.26" + + cache, _, _ = make_cache(monkeypatch, build_workbook(sheets)) + with TestClient(main.create_app(cache)) as client: + assert client.get("/groups").json() == ["ЯЯ-ТОП-11БО", "АА-11БО"] + + def test_teachers(client): assert client.get("/teachers").json() == [ "Иванов И.И.", "Петров П.П.", "Сидорова С.С.", "Смирнов А.В.", diff --git a/tests/test_parsing.py b/tests/test_parsing.py index 64ca6a7..5cc3d2d 100644 --- a/tests/test_parsing.py +++ b/tests/test_parsing.py @@ -23,6 +23,28 @@ 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"