From 416527fb33a87a7ff58b73ddfb6c51deef125a47 Mon Sep 17 00:00:00 2001 From: Zakhar Timoshenko Date: Fri, 23 Oct 2026 22:14:00 +0300 Subject: [PATCH] feat: Update parser to handle lesson type after teacher's name and add corresponding tests --- README.md | 1 + server/parser.js | 7 +++++-- test/formats.test.js | 9 +++++++++ 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index aa3f4e8..89a4d04 100644 --- a/README.md +++ b/README.md @@ -158,6 +158,7 @@ npm run sync - Строка с предметом и аудиторией над строками по видам — заголовок: её аудитория переходит к занятиям под ней. - **Совмещённый вид.** «(л./п.з.)» и «(л. и п.з.)» — вид «Лекция и практика». - **Подгруппа в тексте.** «12н.2п/гр. Экономика…» — занятие одной подгруппы. +- **Вид занятия после преподавателя** (парсер 18): «Русский язык как иностранный Козыренко А.Н.(п.з.) ауд.100» — название кончается там, где начинается преподаватель или вид, смотря что раньше. Раньше фамилия попадала в название (25‑ЭПи, 25‑ЭМ). - **Ячейка из одной точки** — не занятие. - **Время в начале ячейки:** - интервал с дефисом «17:00‑17:20‑информационный час»; diff --git a/server/parser.js b/server/parser.js index 08569c8..bbf1bb6 100644 --- a/server/parser.js +++ b/server/parser.js @@ -10,7 +10,7 @@ import { parseWeekRanges, parseTextDates, DATE_LIST, weekday } from "../shared/w import { roomMoves, onlyPlace, weekPlan, partsByWeek } from "./rooms.js"; import { placeNote, lessonPlace, placeOf } from "./places.js"; const run = promisify(execFile); -export const PARSER_VERSION = 17; +export const PARSER_VERSION = 18; const days = [ "понедельник", "вторник", @@ -98,8 +98,11 @@ export function details(text) { rest.match(/\b(\d{2,3}\s*[А-Яа-я]?)\s*$/u) || // A room with a name instead of a number: "ауд.Медиа". rest.match(/(?:ауд\.\s*)([А-ЯЁ][а-яё]{2,})(?![а-яё])/u); + // "Русский язык как иностранный Козыренко А.Н.(п.з.) ауд.100": the kind may follow the + // teacher, and the name ends where the first of them begins. + const end = [kind?.index, person?.index].filter((i) => i != null); let subject = (named ? named[1].trim() : rest - .slice(0, kind?.index ?? person?.index ?? room?.index ?? rest.length)) + .slice(0, end.length ? Math.min(...end) : room?.index ?? rest.length)) .trim() .replace(/[–—,\-]+$/, "") .trim(); diff --git a/test/formats.test.js b/test/formats.test.js index be94873..9139e20 100644 --- a/test/formats.test.js +++ b/test/formats.test.js @@ -152,6 +152,15 @@ test("Пары для иностранных студентов и для ост assert.equal(entries("Русский язык как иностранный (для иностранных студентов)\nХаткевич Т.В. ауд. 323н").length, 1); }); +test("Вид занятия после преподавателя: фамилия не входит в название", () => { + const after = details("Русский язык как иностранный Козыренко А.Н.(п.з.) ауд.100"); + assert.equal(after.subject, "Русский язык как иностранный"); + assert.equal(after.type, "Практика"); + assert.equal(details("Русский язык как иностранный\nХаткевич Т.В. (п.з.) ауд.504н").subject, "Русский язык как иностранный"); + // No subject at all: the cell's text stays visible. + assert.equal(details("(л.з.) Алексеева Т.А. ауд. 33н").subject, "(л.з.) Алексеева Т.А. ауд. 33н"); +}); + test("Старое имя разделённой группы: понятная ошибка, другое написание — та же группа", async () => { const { timetable } = await import("../server/timetable.js"); const schedule = { groups: ["24-ИЯ (РА)и-1", "24-ИЯ (РА)и-2"], lessons: [], notes: [], period: "Осенний семестр", source: {} };