Fix formatting and correct instructor names in schedule snapshots
- Updated "Ф и з и ч е с к а я к у л ь т у р а" to "Физическая культура" in fkne-daily.txt - Corrected instructor name for "Лабораторный спецпрактикум: Объектно-ориентированное программирование" to "Солдатенко П.Н." in multiple entries in fkne-daily.txt - Changed the format of room numbers in "Иностранный язык (англ.)" entry in fkne.txt to use commas for clarity
This commit is contained in:
@@ -9,6 +9,7 @@ export const lessonLine = (l) =>
|
||||
[
|
||||
`${l.date || DAYS[l.day]} ${l.start}${l.end ? `–${l.end}` : ""}`,
|
||||
(l.date ? "один раз" : l.dates?.length ? `даты ${l.dates.join(",")}` : l.weekRanges ? `нед. ${weeks(l.weekRanges)}` : "каждую неделю") +
|
||||
(l.from ? ` с ${l.from}` : "") +
|
||||
(l.variant === "upper" ? ", верхняя строка" : l.variant === "lower" ? ", нижняя строка" : ""),
|
||||
l.subject,
|
||||
l.type,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { parseWeekRanges, weekday, DATE_LIST } from "../shared/weeks.js";
|
||||
import { roomMoves } from "./rooms.js";
|
||||
import { parseWeekRanges, weekday, lessonWeeks, DATE_LIST } from "../shared/weeks.js";
|
||||
import { roomMoves, PERSON, personName } from "./rooms.js";
|
||||
|
||||
// The parser never guesses: a cell it cannot read becomes a lesson "every week" or keeps its
|
||||
// whole text as the subject. These checks find such cells after parsing, so a new format or a
|
||||
@@ -23,8 +23,36 @@ const message = {
|
||||
subject_missing: "Название предмета не выделено.",
|
||||
time_invalid: "Время пары не распознано или выглядит неправдоподобно.",
|
||||
group_empty: "В заголовке есть группа, но ни одного её занятия.",
|
||||
weeks_color: "Пометка недель противоречит цвету строки: таких недель у строки этого цвета нет.",
|
||||
pair_rows: "У пары в Word больше двух строк, и занятия в ней совпали по времени: возможно, строка съехала из соседней пары.",
|
||||
cell_misaligned: "Ячейка заходит на столбцы соседней группы: занятие досталось обеим группам.",
|
||||
overlap: "В это время у группы стоит другое занятие в те же недели.",
|
||||
subject_brackets: "В названии незакрытая скобка или кавычка.",
|
||||
kind_unknown: "Вид занятия в скобках не распознан.",
|
||||
subject_variants: "Похожие названия одного предмета: возможно, опечатка, и в «Предметах» он двоится.",
|
||||
teacher_initials: "Инициалы преподавателя отличаются от других документов: возможно, опечатка.",
|
||||
};
|
||||
|
||||
// The weeks a weekly lesson takes place in, white rows on odd weeks and green on even.
|
||||
const LAST = 20;
|
||||
const weeksOf = (lesson) => new Set(lessonWeeks({ ...lesson, variant: lesson.variant || "all" }, LAST) || []);
|
||||
const norm = (s) => (s || "").toLowerCase().replace(/ё/g, "е").replace(/[^а-яa-z0-9]/g, "");
|
||||
// "(а.з.)" where a kind stands: a short abbreviation in brackets that none of the known ones is.
|
||||
const KIND_LIKE = /\(\s*([а-я]{1,3}\.\s*(?:[а-я]{1,3}\.?)?)\s*\)/iu;
|
||||
const KNOWN_KIND = /^(?:л|п\.?\s*з|л\.?\s*з|лаб|пр|лек|сем|экз|зач|конс|уст\.?\s*л|уст\.?\s*п\.?\s*з|англ|нем|франц|фр|исп|кит|рус|бел|ит|пол|(?:б|з|бел|зел)\.?\s*н(?:ед)?)\.?$/iu;
|
||||
/** Levenshtein distance up to `max`; more is reported as `max + 1`. */
|
||||
function distance(a, b, max = 2) {
|
||||
if (Math.abs(a.length - b.length) > max) return max + 1;
|
||||
let prev = Array.from({ length: b.length + 1 }, (_, j) => j);
|
||||
for (let i = 1; i <= a.length; i++) {
|
||||
const row = [i];
|
||||
for (let j = 1; j <= b.length; j++) row[j] = Math.min(prev[j] + 1, row[j - 1] + 1, prev[j - 1] + (a[i - 1] === b[j - 1] ? 0 : 1));
|
||||
prev = row;
|
||||
}
|
||||
return prev[b.length];
|
||||
}
|
||||
const subgroupsIn = (lesson, group) => lesson.subgroups.filter((s) => s.group === group).map((s) => s.label);
|
||||
|
||||
/** Problems found in one parsed document: `{code, message, groups, day, start, raw, …}`. */
|
||||
export function diagnose(schedule) {
|
||||
const found = [];
|
||||
@@ -72,31 +100,160 @@ export function diagnose(schedule) {
|
||||
// "3.12" or "24.11." is a date, "9.00", "17.00" or "11:10-11:30" a time.
|
||||
const date = sep === "." && whole === `${a}.${b}${dot}` && Number(b) >= 1 && Number(b) <= 12 && (dot || Number(a) < 7 || Number(a) > 23);
|
||||
add(date ? "date_in_subject" : "time_in_subject", lesson, { subject });
|
||||
} else if (!subject || /^[\d.,;:–-]/.test(subject) || (subject === raw.trim() && raw.length > 120))
|
||||
} else if (
|
||||
!subject ||
|
||||
/^[\d.,;:–-]/.test(subject) ||
|
||||
(subject === raw.trim() && raw.length > 120) ||
|
||||
// "8-14н. (л.з.) Алексеева Т.А. ауд. 33н": the kind, the teacher, the room and no subject.
|
||||
/^\(/.test(subject) ||
|
||||
new RegExp(`^${PERSON.source}`, "u").test(subject)
|
||||
)
|
||||
add("subject_missing", lesson, { subject });
|
||||
if ((subject.match(/\(/g) || []).length !== (subject.match(/\)/g) || []).length || (subject.match(/«/g) || []).length !== (subject.match(/»/g) || []).length)
|
||||
add("subject_brackets", lesson, { subject });
|
||||
const kindLike = raw.match(KIND_LIKE);
|
||||
if (kindLike && !KNOWN_KIND.test(kindLike[1].trim()) && lesson.type === "Занятие") add("kind_unknown", lesson, { kind: kindLike[0] });
|
||||
// "13н." in a green row: green rows are even weeks.
|
||||
if (!dated && lesson.weekRanges && (lesson.variant === "upper" || lesson.variant === "lower")) {
|
||||
const parity = lesson.variant === "upper" ? 1 : 0;
|
||||
const all = [];
|
||||
for (const [a, b] of lesson.weekRanges) for (let n = a; n <= Math.min(b ?? LAST, LAST); n++) all.push(n);
|
||||
if (all.length && !all.some((n) => n % 2 === parity)) add("weeks_color", lesson, { condition: lesson.condition });
|
||||
}
|
||||
if (lesson.misaligned) add("cell_misaligned", lesson);
|
||||
const start = lesson.start || "";
|
||||
if (!/^\d{2}:\d{2}$/.test(start) || start < "07:00" || start > "21:00" || (lesson.end && lesson.end <= start))
|
||||
add("time_invalid", lesson, { end: lesson.end || null });
|
||||
}
|
||||
for (const group of schedule.groups)
|
||||
if (!schedule.lessons.some((l) => l.groups.includes(group))) add("group_empty", null, { groups: [group] });
|
||||
overlaps(schedule.lessons, add);
|
||||
variants(schedule.lessons, add);
|
||||
return found;
|
||||
}
|
||||
|
||||
/**
|
||||
* Two lessons of one group (or of the same subgroup) at the same time in the same weeks.
|
||||
* Lessons to choose between (a second foreign language, a specialisation) and lessons for
|
||||
* foreign students alone are meant to be at the same time.
|
||||
*/
|
||||
function overlaps(lessons, add) {
|
||||
const free = (l) => l.option || l.elective || l.audience || /\(для\s/i.test(l.raw || "");
|
||||
const seen = new Set();
|
||||
for (let i = 0; i < lessons.length; i++) {
|
||||
const a = lessons[i];
|
||||
if (free(a)) continue;
|
||||
for (let j = i + 1; j < lessons.length; j++) {
|
||||
const b = lessons[j];
|
||||
if (free(b) || a.day !== b.day || a.start !== b.start || norm(a.subject) === norm(b.subject)) continue;
|
||||
if (a.date || b.date || a.dates?.length || b.dates?.length) {
|
||||
const da = a.date ? [a.date] : a.dates || [], db = b.date ? [b.date] : b.dates || [];
|
||||
if (!da.length || !db.length || !da.some((d) => db.includes(d))) continue;
|
||||
} else {
|
||||
const wb = weeksOf(b);
|
||||
if (![...weeksOf(a)].some((n) => wb.has(n))) continue;
|
||||
}
|
||||
const group = a.groups.find((g) => {
|
||||
if (!b.groups.includes(g)) return false;
|
||||
const sa = subgroupsIn(a, g), sb = subgroupsIn(b, g);
|
||||
return !sa.length || !sb.length || sa.some((x) => sb.includes(x));
|
||||
});
|
||||
if (!group || seen.has(j)) continue;
|
||||
seen.add(j);
|
||||
// A third row of a pair in Word (25-ПП): more likely a slip than a real clash.
|
||||
add(a.extraRow || b.extraRow ? "pair_rows" : "overlap", b, { with: a.subject, group });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** "Защита о вредоносного ПО" next to "Защита от вредоносного ПО" in one group's subjects. */
|
||||
function variants(lessons, add) {
|
||||
const byGroup = new Map();
|
||||
for (const l of lessons)
|
||||
for (const g of new Set(l.groups)) {
|
||||
if (!byGroup.has(g)) byGroup.set(g, new Map());
|
||||
const names = byGroup.get(g);
|
||||
const key = norm(l.subject);
|
||||
if (key.length > 8 && !names.has(key)) names.set(key, l);
|
||||
}
|
||||
const reported = new Set();
|
||||
for (const names of byGroup.values()) {
|
||||
const keys = [...names.keys()];
|
||||
for (let i = 0; i < keys.length; i++)
|
||||
for (let j = i + 1; j < keys.length; j++) {
|
||||
const [a, b] = [keys[i], keys[j]];
|
||||
// "Иностранный язык 1-й" and "… 2-й", "Физика 1" and "Физика 2" are different subjects.
|
||||
if (a.replace(/\d/g, "") === b.replace(/\d/g, "")) continue;
|
||||
if (distance(a, b) > 2 || reported.has(a + "|" + b)) continue;
|
||||
reported.add(a + "|" + b);
|
||||
add("subject_variants", names.get(b), { subject: names.get(b).subject, other: names.get(a).subject });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initials of a teacher that differ from the rest of the catalog: "Довгяло Д.В." teaching what
|
||||
* "Довгяло Д.А." teaches, or "Лагун А.Н." once against "Лагун А.М." nineteen times.
|
||||
*/
|
||||
function initialsAcross(docs) {
|
||||
const people = new Map();
|
||||
for (const { id, schedule } of docs)
|
||||
for (const lesson of schedule.lessons) {
|
||||
if (!/\s[А-ЯЁ]\.[А-ЯЁ]\.$/u.test(lesson.teacher || "")) continue;
|
||||
const name = personName(lesson.teacher);
|
||||
const surname = name.split(" ")[0];
|
||||
if (!people.has(surname)) people.set(surname, new Map());
|
||||
const forms = people.get(surname);
|
||||
if (!forms.has(name)) forms.set(name, { count: 0, subjects: new Set(), lessons: [] });
|
||||
const form = forms.get(name);
|
||||
form.count++;
|
||||
form.subjects.add(norm(lesson.subject));
|
||||
form.lessons.push({ id, lesson });
|
||||
}
|
||||
const out = new Map();
|
||||
for (const forms of people.values()) {
|
||||
if (forms.size < 2) continue;
|
||||
const list = [...forms].sort((x, y) => y[1].count - x[1].count);
|
||||
const [main, usual] = list[0];
|
||||
for (const [name, form] of list.slice(1)) {
|
||||
// One letter apart, and the same subject or a rare spelling of a frequent name.
|
||||
const [a, b] = [main.split(" ")[1], name.split(" ")[1]];
|
||||
const oneLetter = a.length === b.length && [...a].filter((c, k) => c !== b[k]).length === 1;
|
||||
const shared = [...form.subjects].some((s) => usual.subjects.has(s));
|
||||
if (!oneLetter || !(shared || (form.count <= 2 && usual.count >= 8))) continue;
|
||||
const { id, lesson } = form.lessons[0];
|
||||
if (!out.has(id)) out.set(id, []);
|
||||
out.get(id).push({
|
||||
code: "teacher_initials",
|
||||
message: message.teacher_initials,
|
||||
groups: [...new Set(lesson.groups)],
|
||||
day: lesson.day,
|
||||
start: lesson.start || null,
|
||||
raw: lesson.raw,
|
||||
teacher: name,
|
||||
usual: main,
|
||||
});
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
const reports = new WeakMap();
|
||||
/**
|
||||
* Every catalog document's problems, newest index only: computed once per index build.
|
||||
* Filters: `group` (as printed, "23-АРХ"), `doc` (catalog id) and `code`.
|
||||
*/
|
||||
export function catalogDiagnostics(idx, { group = "", doc = "", code = "" } = {}) {
|
||||
if (!reports.has(idx))
|
||||
if (!reports.has(idx)) {
|
||||
const across = initialsAcross(idx.docs);
|
||||
reports.set(
|
||||
idx,
|
||||
idx.docs.map(({ id, file, schedule }) => ({
|
||||
doc: { id, name: file.name, faculty: file.faculty || schedule.source.faculty || null, url: file.url || null },
|
||||
problems: diagnose(schedule),
|
||||
problems: [...diagnose(schedule), ...(across.get(id) || [])],
|
||||
})),
|
||||
);
|
||||
}
|
||||
const key = group.replace(/\s+/g, "").toLowerCase();
|
||||
const items = reports
|
||||
.get(idx)
|
||||
|
||||
240
server/parser.js
240
server/parser.js
@@ -7,10 +7,10 @@ import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { createHash } from "node:crypto";
|
||||
import { parseWeekRanges, parseTextDates, DATE_LIST, weekday } from "../shared/weeks.js";
|
||||
import { roomMoves, onlyPlace, weekPlan, partsByWeek } from "./rooms.js";
|
||||
import { roomMoves, onlyPlace, weekPlan, partsByWeek, personName } from "./rooms.js";
|
||||
import { placeNote, lessonPlace, placeOf } from "./places.js";
|
||||
const run = promisify(execFile);
|
||||
export const PARSER_VERSION = 20;
|
||||
export const PARSER_VERSION = 21;
|
||||
const days = [
|
||||
"понедельник",
|
||||
"вторник",
|
||||
@@ -23,9 +23,13 @@ const days = [
|
||||
export const clean = (value) =>
|
||||
value
|
||||
.replace(/\u00a0/g, " ")
|
||||
// "Ф и з и ч е с к а я к у л ь т у р а": letters spaced out for show, words apart by
|
||||
// several spaces. Joined back before the spaces collapse: "Физическая культура".
|
||||
.replace(/(?<![\p{L}\d])(?:\p{L} ){3,}\p{L}(?![\p{L}\d])/gu, (word) => word.replace(/ /g, ""))
|
||||
// "Ф и з и ч е с к а я к у л ь т у р а", "Ф И З И Ч Е С К А Я К У…":
|
||||
// letters spaced out for show, words apart by wider gaps. Joined back before the spaces
|
||||
// collapse: "Физическая культура".
|
||||
.replace(/(?<![\p{L}\d])\p{L}(?: +\p{L}(?![\p{L}\d])){3,}/gu, (run) => {
|
||||
const gap = Math.min(...run.match(/ +/g).map((g) => g.length));
|
||||
return run.split(/( +)/).map((piece) => (/^ +$/.test(piece) ? (piece.length > gap ? " " : "") : piece)).join("");
|
||||
})
|
||||
// "со 2 по 14 нед.": the usual "2-14 нед.".
|
||||
.replace(/(?<![\p{L}\d])(?:с|со)\s*(\d{1,2})\s*по\s*(\d{1,2})(?=\s*н(?:ед)?\.?)/giu, "$1-$2")
|
||||
// "C 4н." typed with a Latin C: the Russian "с" of "from week 4".
|
||||
@@ -76,14 +80,38 @@ function conditionIn(value) {
|
||||
);
|
||||
return range?.[0].trim() || "";
|
||||
}
|
||||
// The building after a room number: "207Г", "210м/к", "294 с/к" (the sports complex).
|
||||
const BLOCK = String.raw`[А-Яа-я]\s*\/\s*к(?![а-яё])|[А-Яа-я]`;
|
||||
// A teacher: "Иванов И.И." and "Иванов И.И" without the last dot, as some cells write it.
|
||||
// The building after a room number: "207Г", "210м/к", "294 с/к" (the sports complex). On one
|
||||
// line only: "ауд. 100 ⏎ Ректор…" is room 100, not "100Р".
|
||||
const BLOCK = String.raw`[А-Яа-я][ \t]*\/[ \t]*к(?![а-яё])|[А-Яа-я]{1,2}(?![а-яё])`;
|
||||
// A room number: "129", "21/28" (the discotheque hall).
|
||||
const ROOM_NUMBER = String.raw`\d{2,4}\/\d{2,4}|\d+`;
|
||||
// A teacher: "Иванов И.И.", "Иванов И.И" without the last dot, "КирилловаВ.С." glued together.
|
||||
const INITIALS = String.raw`[А-ЯЁ]\.\s*[А-ЯЁ](?:\.|(?![А-ЯЁа-яё]))`;
|
||||
const PERSON_TEXT = String.raw`[А-ЯЁ][а-яё-]+\s+${INITIALS}`;
|
||||
const PERSON_TEXT = String.raw`[А-ЯЁ][а-яё-]+\s*${INITIALS}`;
|
||||
const PERSON = new RegExp(PERSON_TEXT, "u");
|
||||
// "1-я п/группа", "2 подгр.", "(п.з., л.з. 1-я п/группа)": the lesson of one subgroup.
|
||||
const SUBGROUP_NOTE = /(?<!\d)([1-4])\s*-?\s*(?:а?я\s*)?(?:п\.?\s*\/\s*гр(?:упп[аы])?|подгр(?:упп[аы])?)\.?/iu;
|
||||
// A teacher with one initial or none ("Струй О.", "Хмельницкий 112м/к", "Бердашкевич104"): only
|
||||
// right after the kind or at the start of a line, and only before a room or the line's end.
|
||||
const SHORT_PERSON = String.raw`([А-ЯЁ][а-яё]{2,}(?:-[А-ЯЁ][а-яё]+)?)(?:[ \t]*([А-ЯЁ])\.)?(?=[ \t]*(?:ауд|а\.|\d|[А-ЯA-Z]\d-|$))`;
|
||||
const NOT_PERSON = /^(?:ауд|аудитория|лаб|медиа|дискозал|спортзал|бассейн|стадион|корпус|зал|каб|кабинет|онлайн|дистанционно)$/iu;
|
||||
// Rooms with a name, after the teacher: "Бровко Н.В. медиа", "стадион «Спартак»".
|
||||
const NAMED_ROOM = /(?<![А-Яа-яЁё])(медиа|дискозал|спортзал|бассейн|стадион\s*«[^»\n]+»)(?![а-яё])/iu;
|
||||
// "1-я п/группа", "2 подгр.", "(1 п/па)", "(п.з., л.з. 1-я п/группа)": the lesson of one subgroup.
|
||||
const SUBGROUP_NOTE = /(?<!\d)([1-4])\s*-?\s*(?:а?я\s*)?(?:п\.?\s*\/\s*(?:гр(?:упп[аы])?|па)|подгр(?:упп[аы])?)\.?/iu;
|
||||
// "ауд.32н (с 21.09)", "(с 5.10.)": the lesson begins on that date.
|
||||
const SINCE = /\(\s*с\s*(\d{1,2})\.(\d{1,2})\.?\s*\)/u;
|
||||
// "ауд.297 (каждую неделю)": on white and green weeks alike, whatever the row's colour.
|
||||
const EVERY_WEEK = /\(\s*каждую\s+неделю\s*\)/iu;
|
||||
/**
|
||||
* "медиа" right after the teacher, where the room stands: "Бровко Н.В. медиа", "Турович Л.С.,
|
||||
* Медель А.П. ⏎ стадион «Спартак»". Not after another lesson in between.
|
||||
*/
|
||||
function namedRoom(text, person) {
|
||||
if (!person) return null;
|
||||
const m = text.slice(person.index).match(NAMED_ROOM);
|
||||
if (!m) return null;
|
||||
const between = text.slice(person.index, person.index + m.index).replace(new RegExp(PERSON_TEXT, "gu"), "");
|
||||
return /^[\s,.;]*$/.test(between) ? Object.assign([m[0], m[1]], { index: person.index + m.index }) : null;
|
||||
}
|
||||
export function details(text) {
|
||||
// "Новополоцк" on a line of its own, "246(Новополоцк)": the town is no part of the subject or room.
|
||||
const { place, rest: raw } = lessonPlace(text);
|
||||
@@ -95,13 +123,20 @@ export function details(text) {
|
||||
condition = late[1];
|
||||
rest = rest.slice(0, late.index).trim();
|
||||
}
|
||||
const since = rest.match(SINCE);
|
||||
if (since) rest = rest.replace(SINCE, " ").trim();
|
||||
const everyWeek = EVERY_WEEK.test(rest);
|
||||
if (everyWeek) rest = rest.replace(EVERY_WEEK, " ").trim();
|
||||
// "Экзамен. Финансы …" or "Зачёт: …": the label is the kind of session event, not the subject.
|
||||
const leading = rest.match(
|
||||
/^(экзамен|зач[её]т|консультация|защита\s+курсов(?:ой|ого)\s+(?:работы|проекта))(?![а-яё])[\s.:–—-]*/i,
|
||||
);
|
||||
if (leading) rest = rest.slice(leading[0].length);
|
||||
// Correspondence sessions: "Экзамен по учебной дисциплине «Базы данных» Скуковская А.А. ауд.219Д".
|
||||
const named = rest.match(/^по\s+(?:учебной\s+)?дисциплине\s*«([^»]+)»\s*/i);
|
||||
// Correspondence sessions: "Экзамен по учебной дисциплине «Базы данных» Скуковская А.А. ауд.219Д",
|
||||
// credits: "Консультация по дисциплине: «Основы права» …" or without the quotes.
|
||||
const label = rest.match(/^по\s+(?:учебной\s+)?дисциплине\s*:?\s*/i);
|
||||
if (label) rest = rest.slice(label[0].length);
|
||||
const named = label && rest.match(/^«([^»]+)»\s*/);
|
||||
if (named) rest = named[1].trim() + " " + rest.slice(named[0].length);
|
||||
// "Дисциплина по выбору: Социология": the elective's label is no part of its name.
|
||||
rest = rest.replace(/^дисциплин[аы]\s+(?:по\s+выбору|с[пц]е[цш]иализаци[ийя])\s*:\s*/iu, "");
|
||||
@@ -111,31 +146,58 @@ export function details(text) {
|
||||
else if ((own = rest.match(SUBGROUP_NOTE))) {
|
||||
rest = (rest.slice(0, own.index) + rest.slice(own.index + own[0].length)).replace(/\(\s*\)/g, "").trim();
|
||||
}
|
||||
// "Алгоритмы компьютерной графики л.з.)": the kind lost its opening bracket.
|
||||
if ((rest.match(/\(/g) || []).length < (rest.match(/\)/g) || []).length)
|
||||
rest = rest.replace(/(\s)(л\.?\s*з|п\.?\s*з|лаб|пр|л)\.?\)/u, "$1($2.)");
|
||||
const kind = rest.match(KIND);
|
||||
// Whole words only: "консультирования" in a subject name is not a consultation.
|
||||
const sessionWord = (leading?.[1] ||
|
||||
rest.match(/(?<![а-яё])(экзамен|зач[её]т|консультация)(?![а-яё])/i)?.[1] ||
|
||||
"").toLowerCase();
|
||||
const person = rest.match(PERSON);
|
||||
// "Гигиена Апрасюхина Н.И. ауд. 429н (п.з., л.з.)": a kind after the room is no part of it.
|
||||
const roomText = kind ? rest.replace(kind[0], " ") : rest;
|
||||
// Blanked to the same length, so positions in it are positions in `rest`.
|
||||
const roomText = kind ? rest.replace(kind[0], " ".repeat(kind[0].length)) : rest;
|
||||
let person = rest.match(PERSON);
|
||||
if (!person) {
|
||||
// "(п.з.) Струй О. 537н", "(л.з.) Бердашкевич104", "… ⏎ Лемачко ауд.205".
|
||||
const after = kind && rest.slice(kind.index + kind[0].length).match(new RegExp(String.raw`^[ \t]*${SHORT_PERSON}`, "u"));
|
||||
const line = rest.match(new RegExp(String.raw`\n[ \t]*${SHORT_PERSON}`, "u"));
|
||||
const found = [after && { m: after, at: kind.index + kind[0].length }, line && { m: line, at: line.index }]
|
||||
.filter((f) => f && !NOT_PERSON.test(f.m[1]))[0];
|
||||
if (found) {
|
||||
const name = found.m[1] + (found.m[2] ? ` ${found.m[2]}.` : "");
|
||||
person = Object.assign([name], { index: found.at + found.m[0].indexOf(found.m[1]) });
|
||||
}
|
||||
}
|
||||
const room =
|
||||
// "ауд. А1-06", "ауд. С0-03": a building letter and a number.
|
||||
roomText.match(/(?:ауд\.?|(?<![А-Яа-яЁё])а\.)\s*([А-ЯA-Z]\d-\d{2})/u) ||
|
||||
roomText.match(new RegExp(String.raw`(?:ауд\.?\s*)(\d+\s*(?:${BLOCK})?(?:\s*\([^)]*\))?)`, "iu")) ||
|
||||
roomText.match(new RegExp(String.raw`(?<!\d)(\d{2,3}\s*(?:${BLOCK})?)\s*$`, "u")) ||
|
||||
roomText.match(new RegExp(String.raw`(?:ауд\.?[ \t]*)((?:${ROOM_NUMBER})[ \t]*(?:${BLOCK})?(?:[ \t]*\(\s*ДО\s*\))?)`, "iu")) ||
|
||||
// "А1-10 Ануфриенко Л.В.", "Лапацуева А.С.С0-03": the same style without "ауд.".
|
||||
roomText.match(/(?<![А-Яа-яЁё\d-])([А-ЯA-Z]\d-\d{2})(?!\d)/u) ||
|
||||
// "(л.з.) 403 Аврамёнок А.Н.", "(л.з.) 312 ⏎ Гришин П.Ф.": the room before the teacher.
|
||||
roomText.match(new RegExp(String.raw`(?<![\d.,:\/\-–])((?:${ROOM_NUMBER.replace("|\\d+", "|\\d{2,4}")})[ \t]*(?:${BLOCK})?)(?=\s*(?:\(\S*\))?\s*(?:${PERSON_TEXT}|\n\s*[А-ЯЁ][а-яё]+(?:[ \t]*[А-ЯЁ]\.)?\s*(?:\n|$)))`, "u")) ||
|
||||
// "… 301Г", "201 (Дом офицеров)", "252В.", "206м/к(202м/к)": the room closes the cell.
|
||||
roomText.match(new RegExp(String.raw`(?<!\d)((?:\d{2,4}\/\d{2,4}|\d{2,3})[ \t]*(?:${BLOCK})?)(?:[ \t]*\([^()\n]*\))?[ \t]*\.?\s*$`, "u")) ||
|
||||
// A room with a name instead of a number: "ауд.Медиа".
|
||||
roomText.match(/(?:ауд\.\s*)([А-ЯЁ][а-яё]{2,})(?![а-яё])/u);
|
||||
roomText.match(/(?:ауд\.\s*)([А-ЯЁ][а-яё]{2,})(?![а-яё])/u) ||
|
||||
namedRoom(roomText, person);
|
||||
// "Русский язык как иностранный Козыренко А.Н.(п.з.) ауд.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);
|
||||
// teacher, and the name ends where the first of them, or the room, begins.
|
||||
const end = [kind?.index, person?.index, room?.index || null].filter((i) => i != null);
|
||||
let subject = (named ? named[1].trim() : rest
|
||||
.slice(0, end.length ? Math.min(...end) : room?.index ?? rest.length))
|
||||
.slice(0, end.length ? Math.min(...end) : rest.length))
|
||||
.trim()
|
||||
.replace(/(?:\s|^)(?:ауд\.?|а\.)$/iu, "")
|
||||
.replace(/[–—,\-]+$/, "")
|
||||
.trim();
|
||||
// A typo'd date before the subject ("19,11, 03.12 Физика") leaves a stray comma behind.
|
||||
subject = subject.replace(/^[\s,;.]+/, "");
|
||||
// "Междуречье, ауд. А1-06 ⏎ Заведующий кафедрой …": the town and the room come first.
|
||||
if (room && /^(?:г\.\s*)?(?:Новополоцк|Полоцк|Междуречье|Витебск|Минск)[а-яё]?$/u.test(subject)) {
|
||||
const from = room.index + room[0].length;
|
||||
subject = rest.slice(from, person && person.index > from ? person.index : undefined).replace(/^[\s,.;]+/, "").trim() || subject;
|
||||
}
|
||||
if (!subject) subject = rest;
|
||||
// A long note under a capitalised heading ("ВВЕДЕНИЕ В ВЫСШЕЕ ОБРАЗОВАНИЕ / г. Полоцк, …"):
|
||||
// the heading names it; the whole text stays in the details.
|
||||
@@ -153,10 +215,11 @@ export function details(text) {
|
||||
? "конс"
|
||||
: undefined);
|
||||
return {
|
||||
subject,
|
||||
// "Забелендик О.Н" is the same teacher as "Забелендик О.Н.".
|
||||
teacher: person?.[0].replace(/([А-ЯЁ])$/u, "$1.") || "",
|
||||
room: room?.[1].trim().replace(/\s+/g, "") || "",
|
||||
// A line break in Word is no part of the name: "Инженерная ⏎ компьютерная графика".
|
||||
subject: subject.replace(/\s*\n\s*/g, " "),
|
||||
teacher: person ? personName(person[0]) : "",
|
||||
// "294 с/к" is "294с/к"; "стадион «Спартак»" keeps its space.
|
||||
room: room?.[1].trim().replace(/(\d)\s+(?=[А-Яа-я(\/])/gu, "$1").replace(/\s+/g, " ").replace(/\s+(?=\()/g, "") || "",
|
||||
type:
|
||||
token === "лпз" || token === "липз" || token === "пзл"
|
||||
? "Лекция и практика"
|
||||
@@ -188,11 +251,13 @@ export function details(text) {
|
||||
condition,
|
||||
...(own && { ownSubgroup: Number(own[1]) }),
|
||||
...(place && { place }),
|
||||
...(since && { since: { day: Number(since[1]), month: Number(since[2]) } }),
|
||||
...(everyWeek && { everyWeek }),
|
||||
};
|
||||
}
|
||||
// The kind of a lesson in brackets: "(л.)", "(п.з.)", "(уст.л.)", "(конс.)".
|
||||
const KIND =
|
||||
/\(\s*((?:л|п\.?\s*з|л\.?\s*з)\.?\s*(?:\/|и|,)\s*(?:л|п\.?\s*з|л\.?\s*з)\.?|уст\.?\s*л\.?|уст\.?\s*п\.?\s*з\.?|л\.?\s*з\.?|п\.*\s*з\.?|л\.?|лаб\.?|пр\.?|лек\.?|сем\.?|экз\.?|зач\.?|конс\.?)\s*\)/i;
|
||||
/\(\s*((?:л|п\.?\s*з|л\.?\s*з)\.?\s*(?:\/|и|,)?\s*(?:л|п\.?\s*з|л\.?\s*з)\.?|уст\.?\s*л\.?|уст\.?\s*п\.?\s*з\.?|л\.?\s*з\.?|п\.*\s*з\.?|л\.?|лаб\.?|пр\.?|лек\.?|сем\.?|экз\.?|зач\.?|конс\.?)\s*\)/i;
|
||||
// Split only at an explicit new week condition. Keep unfamiliar wording intact. A line of an
|
||||
// elective's choice may put its weeks after the subject ("Социология 1-10 н. (л.) …"): it is
|
||||
// a lesson of its own, read with the weeks first.
|
||||
@@ -229,10 +294,20 @@ const BY_KIND = new RegExp(
|
||||
String.raw`^(${WEEKS_TEXT})\s*[-–—]\s*(${KIND_TEXT})\.?\s*,\s*(${WEEKS_TEXT})\s*[-–—]\s*(${KIND_TEXT})\.?\s+([\s\S]+)$`,
|
||||
"iu",
|
||||
);
|
||||
// "Механика материалов (1-5н.-л.,7-17н.-п.з.) Дронченко В.А. а. 426": the same in brackets.
|
||||
const BY_KIND_BRACKETS = new RegExp(
|
||||
String.raw`^([^()]+?)\s*\(\s*(${WEEKS_TEXT})\s*[-–—]\s*(${KIND_TEXT})\.?\s*,\s*(${WEEKS_TEXT})\s*[-–—]\s*(${KIND_TEXT})\.?\s*\)\s*([\s\S]*)$`,
|
||||
"iu",
|
||||
);
|
||||
function byKind(text) {
|
||||
const weeks = (w) => (/н/i.test(w) ? w.trim() : `${w.trim()}н.`);
|
||||
const inside = text.match(BY_KIND_BRACKETS);
|
||||
if (inside) {
|
||||
const [, subject, w1, k1, w2, k2, rest] = inside;
|
||||
return [`${weeks(w1)} ${subject} (${k1}.) ${rest}`.trim(), `${weeks(w2)} ${subject} (${k2}.) ${rest}`.trim()];
|
||||
}
|
||||
const m = text.match(BY_KIND);
|
||||
if (!m) return [text];
|
||||
const weeks = (w) => (/н/i.test(w) ? w.trim() : `${w.trim()}н.`);
|
||||
// The kind goes right after the subject, before the teacher and the room.
|
||||
const rest = m[5];
|
||||
const cut = [rest.search(PERSON), rest.search(/(?:ауд\.?|(?<![А-Яа-яЁё])а\.)\s*\S/u)]
|
||||
@@ -320,9 +395,27 @@ export function parallel(text) {
|
||||
* other weeks. A stray "." is no lesson. Lessons side by side for parts of the group ([parallel])
|
||||
* also carry `choice`: the subjects to choose from.
|
||||
*/
|
||||
// "Профилизация: Техническая эксплуатация автомобилей ⏎ 2-5 н. Конструкция автомобиля (л.) …":
|
||||
// the track's name heads the lesson below it; it is none itself.
|
||||
const TRACK = /^(?:профилизация|профиль|специализация|модуль)\s*:[^\n]*\n(?=[\s\S]*\()/iu;
|
||||
/**
|
||||
* "Консультация по дисциплине: ⏎ «Основы права» Валевко А.В. ауд. 402н. ⏎ «Политология» Шидловский
|
||||
* С.О. ауд.412 н": one consultation per subject in quotes.
|
||||
*/
|
||||
function bySubject(text) {
|
||||
const m = text.match(/^((?:экзамен|зач[её]т|консультация)\s+по\s+(?:учебной\s+)?дисциплин[еам]\s*:?)\s*\n([\s\S]+)$/iu);
|
||||
if (!m) return [text];
|
||||
const lines = m[2].split("\n").filter((l) => l.trim());
|
||||
if (lines.length < 2 || !lines.every((l) => /^\s*«/.test(l))) return [text];
|
||||
return lines.map((l) => `${m[1]} ${l.trim()}`);
|
||||
}
|
||||
export function entries(raw) {
|
||||
const out = [];
|
||||
for (const part of alternatives(raw).flatMap(byKind)) {
|
||||
raw = raw.replace(TRACK, "");
|
||||
// "Дисциплины специализаций: 2-11 н. Цифровое право … 2-12 н. Цифровые технологии …": lessons at
|
||||
// the same time that students choose between.
|
||||
const elective = /^\s*дисциплин[аы]\s+(?:по\s+выбору|с[пц]е[цш]иализаци)/iu.test(raw);
|
||||
for (const part of alternatives(raw).flatMap(bySubject).flatMap(byKind)) {
|
||||
// "…Третьяк З.И. 331Е,\n2-13н. …": the comma before the next lesson is not part of the room.
|
||||
const alternative = part.replace(/[,;]\s*$/, "");
|
||||
if (!/[\p{L}\d]/u.test(alternative)) continue;
|
||||
@@ -342,7 +435,10 @@ export function entries(raw) {
|
||||
}
|
||||
} else {
|
||||
prev.text += "\n" + alternative;
|
||||
prev.plan.push(...weekPlan(alternative));
|
||||
const more = weekPlan(alternative);
|
||||
prev.plan.push(...more);
|
||||
// "ауд.403" under every week's teacher: the room of the weeks above as well.
|
||||
if (more.common) for (const p of prev.plan) if (!p.rooms.length) p.rooms = [...more.common];
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -356,7 +452,7 @@ export function entries(raw) {
|
||||
}
|
||||
const split = byAudience(alternative);
|
||||
if (split) {
|
||||
for (const p of split) out.push({ text: p.text, main: p.text, plan: weekPlan(p.text).filter((w) => w.color), audience: p.audience });
|
||||
for (const p of split) out.push({ text: p.text, main: p.text, plan: weekPlan(p.text, { main: true }), audience: p.audience });
|
||||
continue;
|
||||
}
|
||||
if (options.length > 1) {
|
||||
@@ -364,10 +460,11 @@ export function entries(raw) {
|
||||
for (const o of options) out.push({ text: o, main: o, plan: [], choice });
|
||||
continue;
|
||||
}
|
||||
// "бел.н.504н/зел.н.524н": a room for white weeks and one for green ones.
|
||||
out.push({ text: alternative, main: alternative, plan: weekPlan(alternative).filter((p) => p.color) });
|
||||
// "бел.н.504н/зел.н.524н": a room for white weeks and one for green ones; "ауд. 125
|
||||
// (14,16н-332)": another room in some weeks.
|
||||
out.push({ text: alternative, main: alternative, plan: weekPlan(alternative, { main: true }) });
|
||||
}
|
||||
return out.filter((e) => !e.heading);
|
||||
return out.filter((e) => !e.heading).map((e) => (elective ? { ...e, elective } : e));
|
||||
}
|
||||
export function readDocx(buffer) {
|
||||
let zip;
|
||||
@@ -629,6 +726,7 @@ export function parseDocx(buffer, source = {}) {
|
||||
time = null,
|
||||
pair = null,
|
||||
timeCol = null,
|
||||
slotRow = 0,
|
||||
// The day's first lesson time: a town note further down holds from its own row on.
|
||||
dayStart = null;
|
||||
// Session schedules use separate room, subject and teacher columns.
|
||||
@@ -675,11 +773,15 @@ export function parseDocx(buffer, source = {}) {
|
||||
if (!time) continue;
|
||||
dayStart ??= time.start;
|
||||
seenRows++;
|
||||
const continuedTime =
|
||||
row.find((c) => c.col === timeCol)?.merge === "continue";
|
||||
const ownTime = row.find((c) => c.col === timeCol);
|
||||
const continuedTime = ownTime?.merge === "continue";
|
||||
// An empty time cell over a green row ("18.35. Таможенный контроль…" in 24-ТД) still
|
||||
// makes its row the white one.
|
||||
const pairedTime =
|
||||
timeCell?.merge === "restart" &&
|
||||
ownTime?.merge === "restart" &&
|
||||
rows[i + 1]?.find((c) => c.col === timeCol)?.merge === "continue";
|
||||
// Rows of one pair: white and green. A third one (25-ПП) is a slip in Word.
|
||||
slotRow = continuedTime ? slotRow + 1 : 0;
|
||||
const variant = continuedTime ? "lower" : pairedTime ? "upper" : "all";
|
||||
const emit = (cell, targetGroups, raw, overrides = {}) => {
|
||||
if (!raw.trim()) return;
|
||||
@@ -710,7 +812,7 @@ export function parseDocx(buffer, source = {}) {
|
||||
raw = note.rest;
|
||||
if (!raw.trim()) return;
|
||||
}
|
||||
for (const { text: whole, main, plan, choice, audience } of entries(raw)) {
|
||||
for (const { text: whole, main, plan, choice, audience, elective } of entries(raw)) {
|
||||
if (choice) choices.push({ groups: targetGroups, options: choice });
|
||||
// "24-КФ: Русский язык как иностранный": one of the cell's groups.
|
||||
let alternative = main;
|
||||
@@ -730,10 +832,15 @@ export function parseDocx(buffer, source = {}) {
|
||||
if (credits && data.type === "Занятие") data.type = "Зачёт";
|
||||
return data;
|
||||
};
|
||||
// "… ауд. 411н ⏎ 15.20-16.45": a time of its own on the cell's last line.
|
||||
const tail = alternative.match(/\n\s*([0-2]?\d)[.:]([0-5]\d)\s*[-–]\s*([0-2]?\d)[.:]([0-5]\d)\s*$/);
|
||||
if (tail) alternative = alternative.slice(0, tail.index);
|
||||
let data = read(alternative);
|
||||
// "20.10" is 20 October or the eighth pair at 20:10. A date on another weekday than
|
||||
// the row, or followed by a week mark, is the time.
|
||||
let lead = leadingTime(alternative) || leadingDate(alternative, yearStart);
|
||||
if (!lead && tail)
|
||||
lead = { starts: [`${tail[1].padStart(2, "0")}:${tail[2]}`], end: `${tail[3].padStart(2, "0")}:${tail[4]}`, length: 0 };
|
||||
if (lead?.date) {
|
||||
const after = alternative.slice(lead.length).trim();
|
||||
const time = leadingTime(alternative, true);
|
||||
@@ -748,7 +855,15 @@ export function parseDocx(buffer, source = {}) {
|
||||
const subgroups = [...columnSubgroups];
|
||||
// A subgroup named in the cell ("2п/гр.") when the column does not tell one.
|
||||
const own = data.ownSubgroup;
|
||||
// "(с 21.09)": the first date; "(каждую неделю)": both colours of weeks.
|
||||
const { since, everyWeek } = data;
|
||||
delete data.ownSubgroup;
|
||||
delete data.since;
|
||||
delete data.everyWeek;
|
||||
const from =
|
||||
since && yearStart
|
||||
? `${since.month >= 8 ? Number(yearStart) : Number(yearStart) + 1}-${String(since.month).padStart(2, "0")}-${String(since.day).padStart(2, "0")}`
|
||||
: null;
|
||||
if (own && !subgroups.length) subgroups.push(...targetGroups.map((group) => ({ group, label: `Подгруппа ${own}` })));
|
||||
// "17.00 и 18.35 Русский язык": one lesson at each time.
|
||||
for (const [n, start] of (lead?.starts || [null]).entries())
|
||||
@@ -763,10 +878,14 @@ export function parseDocx(buffer, source = {}) {
|
||||
...time,
|
||||
...(start && { start, end: lead.end }),
|
||||
...data,
|
||||
...overrides,
|
||||
...Object.fromEntries(Object.entries(overrides).filter(([k]) => k !== "misaligned")),
|
||||
groups: lessonGroups,
|
||||
subgroups,
|
||||
variant: spansBoth ? "all" : variant,
|
||||
variant: spansBoth || everyWeek ? "all" : variant,
|
||||
...(from && !date && !oneDate && { from }),
|
||||
// For the diagnostics: a cell over part of another group's columns, a third row of a pair.
|
||||
...(overrides.misaligned && { misaligned: true }),
|
||||
...(slotRow >= 2 && { extraRow: true }),
|
||||
weekRanges:
|
||||
date || oneDate ? null : parseWeekRanges(data.condition),
|
||||
dates:
|
||||
@@ -780,6 +899,7 @@ export function parseDocx(buffer, source = {}) {
|
||||
: null,
|
||||
raw: whole,
|
||||
...(choice && { option: data.subject }),
|
||||
...(elective && { elective }),
|
||||
// Groups of foreign students alone ("25-ДОи") have Russian as everyone's lesson.
|
||||
...((audience || audienceOf(whole)) &&
|
||||
!lessonGroups.every(FOREIGN_GROUP.test.bind(FOREIGN_GROUP)) && { audience: audience || audienceOf(whole) }),
|
||||
@@ -794,11 +914,17 @@ export function parseDocx(buffer, source = {}) {
|
||||
const room = row.find((c) => c.col === sessionColumns.room)?.text || "";
|
||||
const teacher =
|
||||
row.find((c) => c.col === sessionColumns.teacher)?.text || "";
|
||||
// "Кузнечик Е.В. ⏎ Струй О.М. ⏎ Филимончик Н.А." and "526н ⏎ 325н ⏎ 502н": one teacher
|
||||
// per subgroup; the first stands for the lesson, the cell keeps them all.
|
||||
const first = teacher.match(PERSON);
|
||||
emit(
|
||||
cell,
|
||||
ranges.map((r) => r.group),
|
||||
[cell.text, teacher, room].filter(Boolean).join("\n"),
|
||||
{ room, teacher },
|
||||
{
|
||||
room: room.split(/\s*[\n;]\s*/).map((r) => r.replace(/,\s*$/, "")).filter(Boolean).join(", "),
|
||||
teacher: first ? personName(first[0]) : teacher,
|
||||
},
|
||||
);
|
||||
} else {
|
||||
let count = 0;
|
||||
@@ -809,11 +935,13 @@ export function parseDocx(buffer, source = {}) {
|
||||
cell.col < ranges[0].col
|
||||
)
|
||||
continue;
|
||||
const targetGroups = ranges
|
||||
.filter((r) => cell.col < r.end && cell.col + cell.span > r.col)
|
||||
.map((r) => r.group);
|
||||
const covered = ranges.filter((r) => cell.col < r.end && cell.col + cell.span > r.col);
|
||||
const targetGroups = covered.map((r) => r.group);
|
||||
if (!targetGroups.length) continue;
|
||||
emit(cell, targetGroups, cell.text);
|
||||
// "Информационный час" over 23-ПР-1 and the first column of 23-ПР-2: shifted in Word.
|
||||
const misaligned =
|
||||
covered.length > 1 && covered.some((r) => r.col < cell.col || r.end > cell.col + cell.span);
|
||||
emit(cell, targetGroups, cell.text, misaligned ? { misaligned: true } : {});
|
||||
count++;
|
||||
}
|
||||
if (!count) skippedRows++;
|
||||
@@ -842,6 +970,30 @@ export function parseDocx(buffer, source = {}) {
|
||||
warnings.add(
|
||||
"Номера подгрупп обозначают столбцы внутри группы в исходном Word.",
|
||||
);
|
||||
// "Техническая гидроме-ханика": a hyphen Word left where it broke the line. Joined back when
|
||||
// the document writes the word whole elsewhere, so the subject is one, not two.
|
||||
const words = new Set(
|
||||
lessons.flatMap((l) => l.subject.toLowerCase().split(/[^а-яё]+/u)).filter((w) => w.length > 3),
|
||||
);
|
||||
const unbreak = (text) =>
|
||||
text.replace(/([А-Яа-яЁё]+)-\s?([а-яё]+)/gu, (whole, a, b) =>
|
||||
// "Объектно- ориентированные": a compound with a stray space after its hyphen.
|
||||
words.has((a + b).toLowerCase()) ? a + b : `${a}-${b}`,
|
||||
);
|
||||
for (const l of lessons) {
|
||||
l.subject = unbreak(l.subject);
|
||||
if (l.option) l.option = unbreak(l.option);
|
||||
}
|
||||
// "Емельянова а. 403", "Струй О.": the teacher the document names in full elsewhere, when only
|
||||
// one of that surname fits.
|
||||
const full = [...new Set(lessons.map((l) => l.teacher).filter((t) => /\s[А-ЯЁ]\.[А-ЯЁ]\.$/u.test(t)))];
|
||||
for (const l of lessons) {
|
||||
const short = l.teacher.match(/^([А-ЯЁ][а-яё-]+)(?:\s([А-ЯЁ])\.)?$/u);
|
||||
if (!short) continue;
|
||||
const fits = full.filter((t) => t.startsWith(`${short[1]} ${short[2] || ""}`));
|
||||
if (fits.length === 1) l.teacher = fits[0];
|
||||
}
|
||||
for (const c of choices) c.options = c.options.map(unbreak);
|
||||
// A lesson on a day the group spends in another town is there too, unless its cell says otherwise.
|
||||
const labels = new Map();
|
||||
for (const l of lessons)
|
||||
|
||||
@@ -4,9 +4,17 @@ import { parseWeekRanges } from "../shared/weeks.js";
|
||||
// diagnostics: room tokens, dated moves ("26.10.2026-333") and plans by week ("1-9н. 502н,
|
||||
// с 10н. – 359", "бел.н.504н/зел.н.524н", "2-9н.-Клос О.Ю., 10-14н.-Конторов В.Э.").
|
||||
|
||||
// "Клос О.Ю." and "Забелендик О.Н", the last dot left out.
|
||||
export const PERSON = /[А-ЯЁ][а-яё]+(?:-[А-ЯЁ][а-яё]+)?\s+[А-ЯЁ]\.\s*[А-ЯЁ](?:\.|(?![А-ЯЁа-яё]))/gu;
|
||||
export const personName = (s) => s.replace(/\s+/g, " ").replace(/\.\s+/g, ".").trim().replace(/([А-ЯЁ])$/u, "$1.");
|
||||
// "Клос О.Ю.", "Забелендик О.Н" with the last dot left out, "КирилловаВ.С." glued together.
|
||||
export const PERSON = /[А-ЯЁ][а-яё]+(?:-[А-ЯЁ][а-яё]+)?\s*[А-ЯЁ]\.\s*[А-ЯЁ](?:\.|(?![А-ЯЁа-яё]))/gu;
|
||||
// One way to write a teacher: "Кириллова В.С." for "КирилловаВ.С", "Хмельницкий Р. С." and "… В.С".
|
||||
export const personName = (s) =>
|
||||
s
|
||||
.replace(/\s+/g, " ")
|
||||
.replace(/([а-яё])\s*(?=[А-ЯЁ]\.)/u, "$1 ")
|
||||
.replace(/\.\s+(?=[А-ЯЁ](?:\.|$))/gu, ".")
|
||||
.trim()
|
||||
.replace(/(\s[А-ЯЁ])$/u, "$1.")
|
||||
.replace(/(\.[А-ЯЁ])$/u, "$1.");
|
||||
// "129", "207Г", "202МК", "504Н", and the "В0-03" style. Rooms have at least two digits.
|
||||
export const ROOM_TOKEN = /[А-ЯA-Z]\d-\d{2}|\d{2,4}\/\d{2,4}|\d{2,4}[А-ЯA-Z]{0,2}/gu;
|
||||
const ROOM_TEXT = "[А-ЯA-Z]\\d-\\d{2}|\\d{2,4}(?:\\s?[А-Яа-я]\\s?\\/\\s?к(?![а-яё])|\\s?(?!и(?![а-яё]))[А-Яа-я]{1,2}(?![а-яё]))?";
|
||||
@@ -57,7 +65,7 @@ export function roomMoves(text, year) {
|
||||
|
||||
// A week mark opening a part of a line: "1-9н.", "с 10н", "14-17н.", "7,9,11н.". Not a room
|
||||
// with its week's colour: "32н-з.н." is room 32Н in green weeks.
|
||||
const WEEK_MARK = /(?<=^|[\s,;])(?:(?:с|со)\s*)?\d{1,2}(?:\s*[-–,]\s*\d{1,2})*\s*н(?:ед)?(?![а-яё])(?!\.?\s*[-–]?\s*[бз](?:ел)?\.?\s*н)\.?/giu;
|
||||
const WEEK_MARK = /(?<=^|[\s,;(])(?:(?:с|со)\s*)?\d{1,2}(?:\s*[-–,]\s*\d{1,2})*\s*н(?:ед)?(?![а-яё])(?!\.?\s*[-–]?\s*[бз](?:ел)?\.?\s*н)\.?/giu;
|
||||
|
||||
/** A line cut where each week mark begins: "1-13н-332, 14-17н.-502н" → two parts. */
|
||||
export function partsByWeek(text) {
|
||||
@@ -68,8 +76,19 @@ export function partsByWeek(text) {
|
||||
const PART_MARK = /^(?:(?:с|со)\s*)?\d{1,2}(?:\s*[-–,]\s*\d{1,2})*\s*н(?:ед)?\.?/iu;
|
||||
// "бел.н.504н/зел.н.524н", "б.н.301Г/з.н.317Д": a room for white weeks and one for green ones.
|
||||
const BY_COLOR = /б(?:ел)?\.?\s*н(?:ед)?\.?\s*([^/\s]+)\s*\/\s*з(?:ел)?\.?\s*н(?:ед)?\.?\s*([^\s,;]+)/iu;
|
||||
// "ауд.533н-б.н.,420н-з.н.": the colour after each room.
|
||||
const ROOM_COLOR = new RegExp(`(${ROOM_TEXT})\\s*[-–]?\\s*(б|з)(?:ел)?\\.?\\s*н(?:ед)?\\.?(?![а-яё])`, "giu");
|
||||
// "ауд.533н-б.н.,420н-з.н.", "331 (бел.нед.), 125 (зел.нед.)", "252В (бел.) 247Ж (зел.)": the
|
||||
// colour after each room.
|
||||
const ROOM_COLOR = new RegExp(
|
||||
`(${ROOM_TEXT})\\s*(?:[-–]?\\s*(б|з)(?:ел)?\\.?\\s*н(?:ед)?\\.?(?![а-яё])|\\(\\s*(б|з)(?:ел)?\\.?\\s*(?:н(?:ед)?\\.?)?\\s*\\))`,
|
||||
"giu",
|
||||
);
|
||||
// "Парфенова Л.М. (1-9н.); Лазаренко О.В. (10-16н.)": each teacher's weeks after the name.
|
||||
const PERSON_WEEKS = new RegExp(
|
||||
`(${PERSON.source})\\s*\\(\\s*((?:(?:с|со)\\s*)?\\d{1,2}(?:\\s*[-–,]\\s*\\d{1,2})*\\s*н(?:ед)?\\.?)\\s*\\)`,
|
||||
"gu",
|
||||
);
|
||||
// "ауд.403" on the last line of a cell: the room of every week the lines above name.
|
||||
const COMMON_ROOM = /\n\s*((?:ауд\.?|а\.)\s*[^\n]+)$/iu;
|
||||
|
||||
const roomsIn = (text) =>
|
||||
tokens(text.replace(PERSON, " ").replace(/\([^)]*\)/g, " "))
|
||||
@@ -79,11 +98,25 @@ const roomsIn = (text) =>
|
||||
/**
|
||||
* Rooms and teachers that depend on the week, from a line of a cell: entries
|
||||
* `{weeks: [[a, b]] | null, color: "white" | "green" | null, rooms, teachers}`. Empty when the
|
||||
* line names none of them by week.
|
||||
* line names none of them by week. With `main`, the line is the lesson's own: its leading week
|
||||
* mark is the lesson's weeks, not a plan ("1-9н. Химия … 403").
|
||||
* A room on the last line without weeks ("13-17н. Иванов В.П. ⏎ ауд.403") is every week's: it
|
||||
* comes back as `plan.common`.
|
||||
*/
|
||||
export function weekPlan(text) {
|
||||
export function weekPlan(text, { main = false } = {}) {
|
||||
const plan = [];
|
||||
for (const part of partsByWeek(text || "")) {
|
||||
let line = text || "";
|
||||
for (const m of line.matchAll(PERSON_WEEKS)) {
|
||||
const weeks = parseWeekRanges(m[2]);
|
||||
if (weeks) plan.push({ weeks, color: null, rooms: [], teachers: [personName(m[1])] });
|
||||
}
|
||||
if (plan.length) line = line.replace(PERSON_WEEKS, " ");
|
||||
const common = line.match(COMMON_ROOM);
|
||||
if (common && !PART_MARK.test(common[1]) && !line.slice(common.index).match(PERSON)) line = line.slice(0, common.index);
|
||||
let before = "";
|
||||
for (const part of partsByWeek(line)) {
|
||||
const lead = main && !/[А-Яа-яЁё]/u.test(before);
|
||||
before += part;
|
||||
const mark = part.match(PART_MARK)?.[0] || "";
|
||||
const weeks = mark ? parseWeekRanges(mark) : null;
|
||||
if (mark && !weeks) continue;
|
||||
@@ -96,10 +129,11 @@ export function weekPlan(text) {
|
||||
plan.push({ weeks, color: "green", rooms: roomsIn(color[2]), teachers });
|
||||
} else if (byRoom.length) {
|
||||
for (const [name, letter] of [["white", "б"], ["green", "з"]]) {
|
||||
const rooms = byRoom.filter((m) => m[2].toLowerCase() === letter).map((m) => roomKey(m[1]));
|
||||
const rooms = byRoom.filter((m) => (m[2] || m[3]).toLowerCase() === letter).map((m) => roomKey(m[1]));
|
||||
if (rooms.length) plan.push({ weeks, color: name, rooms, teachers });
|
||||
}
|
||||
} else if (mark) {
|
||||
} else if (mark && !lead) {
|
||||
// Not the lesson's own weeks ("1-9н. Химия", "17.00 4 нед. Теория вероятностей").
|
||||
const rooms = roomsIn(rest);
|
||||
if (rooms.length || teachers.length) plan.push({ weeks, color: null, rooms, teachers });
|
||||
}
|
||||
@@ -108,6 +142,11 @@ export function weekPlan(text) {
|
||||
const last = plan.at(-1);
|
||||
if (plan.length > 1 && last.rooms.length && plan.slice(0, -1).every((p) => !p.rooms.length && p.teachers.length && !p.color))
|
||||
for (const p of plan.slice(0, -1)) p.rooms = [...last.rooms];
|
||||
if (common && line.length < (text || "").length) {
|
||||
const rooms = roomsIn(common[1]);
|
||||
for (const p of plan) if (!p.rooms.length) p.rooms = [...rooms];
|
||||
plan.common = rooms;
|
||||
}
|
||||
return plan;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,6 +18,14 @@ const CODES = {
|
||||
subject_missing: "Нет названия",
|
||||
time_invalid: "Странное время",
|
||||
group_empty: "Группа без пар",
|
||||
weeks_color: "Недели против цвета",
|
||||
pair_rows: "Лишняя строка пары",
|
||||
cell_misaligned: "Ячейка на две группы",
|
||||
overlap: "Два занятия сразу",
|
||||
subject_brackets: "Скобка в названии",
|
||||
kind_unknown: "Вид не распознан",
|
||||
subject_variants: "Похожие названия",
|
||||
teacher_initials: "Инициалы",
|
||||
};
|
||||
const EVENTS = {
|
||||
new: ["Новый документ", "ok"],
|
||||
|
||||
2
shared/weeks.d.ts
vendored
2
shared/weeks.d.ts
vendored
@@ -50,6 +50,8 @@ interface WeekLesson {
|
||||
condition: string;
|
||||
variant: "all" | "upper" | "lower";
|
||||
weekRanges?: WeekRange[] | null;
|
||||
/** The first date ("ауд.32н (с 21.09)"): no earlier ones. */
|
||||
from?: string;
|
||||
}
|
||||
export function parseWeekRanges(condition: string): WeekRange[] | null;
|
||||
export function parseTextDates(raw: string, year: string | number): string[];
|
||||
|
||||
@@ -134,11 +134,15 @@ export function lessonDates(lesson, calendar) {
|
||||
if (lesson.dates?.length) return lesson.dates;
|
||||
const numbers = lessonWeeks(lesson, calendar.weeks.at(-1).n);
|
||||
if (!numbers) return null;
|
||||
return numbers
|
||||
const dates = numbers
|
||||
.map((n) => calendar.weeks.find((w) => w.n === n))
|
||||
.filter(Boolean)
|
||||
.map((w) => dateInWeek(w, lesson.day))
|
||||
.filter(Boolean);
|
||||
// "ауд.32н (с 21.09)": not before its first date. A first date after all its weeks ("2н …
|
||||
// (с 5.10.)") contradicts them and is kept literally, like a mark against its row's colour.
|
||||
const later = lesson.from ? dates.filter((date) => date >= lesson.from) : dates;
|
||||
return later.length ? later : dates;
|
||||
}
|
||||
|
||||
/** The academic year starts on 1 September: 2026-08-31 → 2025, 2026-09-01 → 2026. */
|
||||
|
||||
@@ -23,7 +23,7 @@ test("Диагностика: обычные занятия без замеча
|
||||
assert.deepEqual(
|
||||
codes(
|
||||
lesson("Химия (л.) Иванов И.И. 101", { subject: "Химия" }),
|
||||
lesson("1-8н. Физика", { subject: "Физика", condition: "1-8н.", weekRanges: [[1, 8]] }),
|
||||
lesson("1-8н. Физика", { subject: "Физика", condition: "1-8н.", weekRanges: [[1, 8]], start: "10:35", end: "11:55" }),
|
||||
lesson("Единый день 07.09;14.09", { subject: "Единый день", dates: ["2026-09-07", "2026-09-14"] }),
|
||||
),
|
||||
[],
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
# Осенний семестр · 24-ИТ-1, 24-ИТ-2
|
||||
2026-09-03 15:20–16:45 | один раз, верхняя строка | Разработка и анализ требований к программному обеспечению | Лабораторная | 218Д | Струк Т.М. | 24-ИТ-1,24-ИТ-1/п3
|
||||
2026-09-03 15:20–16:45 | один раз, верхняя строка | Разработка и анализ требований к программному обеспечению | Лабораторная | 218Д | Струк Т.М. | 24-ИТ-2,24-ИТ-2/п3
|
||||
2026-09-07 08:30–09:55 | один раз, нижняя строка | Разработка и анализ требований к програм-мному обеспечению | Лабораторная | 213А | Струк Т.М. | 24-ИТ-1,24-ИТ-1/п5
|
||||
2026-09-07 08:30–09:55 | один раз, нижняя строка | Разработка и анализ требований к програм-мному обеспечению | Лабораторная | 213А | Струк Т.М. | 24-ИТ-2,24-ИТ-2/п5
|
||||
2026-09-07 08:30–09:55 | один раз, нижняя строка | Разработка и анализ требований к программному обеспечению | Лабораторная | 213А | Струк Т.М. | 24-ИТ-1,24-ИТ-1/п5
|
||||
2026-09-07 08:30–09:55 | один раз, нижняя строка | Разработка и анализ требований к программному обеспечению | Лабораторная | 213А | Струк Т.М. | 24-ИТ-2,24-ИТ-2/п5
|
||||
2026-09-07 15:20–16:45 | один раз, нижняя строка | Разработка и анализ требований к программному обеспечению | Лабораторная | 218Д | Струк Т.М. | 24-ИТ-1,24-ИТ-1/п1
|
||||
2026-09-07 15:20–16:45 | один раз, нижняя строка | Разработка и анализ требований к программному обеспечению | Лабораторная | 218Д | Струк Т.М. | 24-ИТ-2,24-ИТ-2/п1
|
||||
2026-09-21 15:20–16:45 | один раз, нижняя строка | Разработка и анализ требований к программному обеспечению | Лабораторная | 218Д | Струк Т.М. | 24-ИТ-1,24-ИТ-1/п2
|
||||
@@ -20,8 +20,8 @@
|
||||
вт 13:40–15:05 | каждую неделю, верхняя строка | Метрология, стандартизация и сертификация (в информационных технологиях | Практика | 321Д | Михнович О.В. | 24-ИТ-1,24-ИТ-2
|
||||
вт 13:40–15:05 | каждую неделю, нижняя строка | Стандартизация программного обеспечения | Практика | 321Д | Михнович О.В. | 24-ИТ-1,24-ИТ-2
|
||||
вт 15:20–16:45 | каждую неделю | ФИЗИЧЕСКАЯ КУЛЬТУРА | Занятие | | | 24-ИТ-1,24-ИТ-2
|
||||
пн 08:30–09:55 | каждую неделю, верхняя строка | Объектно-ориентированные технологии програм-мирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п3
|
||||
пн 08:30–09:55 | каждую неделю, верхняя строка | Объектно-ориентированные технологии програм-мирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п3
|
||||
пн 08:30–09:55 | каждую неделю, верхняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п3
|
||||
пн 08:30–09:55 | каждую неделю, верхняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п3
|
||||
пн 08:30–09:55 | каждую неделю, нижняя строка | Системное программирование | Лабораторная | 207Г | Юрчишко Е.И. | 24-ИТ-1,24-ИТ-1/п4
|
||||
пн 08:30–09:55 | каждую неделю, нижняя строка | Системное программирование | Лабораторная | 207Г | Юрчишко Е.И. | 24-ИТ-2,24-ИТ-2/п4
|
||||
пн 10:05–11:30 | каждую неделю | Системное программирование | Лабораторная | 217Д | Юрчишко Е.И. | 24-ИТ-1,24-ИТ-1/п5
|
||||
@@ -29,31 +29,31 @@
|
||||
пн 10:05–11:30 | каждую неделю, верхняя строка | Разработка и анализ требований к программному обеспечению | Лабораторная | 213А | Струк Т.М. | 24-ИТ-1,24-ИТ-1/п3
|
||||
пн 10:05–11:30 | каждую неделю, верхняя строка | Разработка и анализ требований к программному обеспечению | Лабораторная | 213А | Струк Т.М. | 24-ИТ-2,24-ИТ-2/п3
|
||||
пн 10:05–11:30 | каждую неделю, верхняя строка | Системное программирование | Лабораторная | 207Г | Юрчишко Е.И. | 24-ИТ-2,24-ИТ-2/п4
|
||||
пн 10:05–11:30 | каждую неделю, нижняя строка | Разработка и анализ требований к програм-мному обеспечению | Лабораторная | 320Д | Струк Т.М. | 24-ИТ-1,24-ИТ-1/п4
|
||||
пн 10:05–11:30 | каждую неделю, нижняя строка | Разработка и анализ требований к програм-мному обеспечению | Лабораторная | 320Д | Струк Т.М. | 24-ИТ-2,24-ИТ-2/п4
|
||||
пн 10:05–11:30 | каждую неделю, нижняя строка | Разработка и анализ требований к программному обеспечению | Лабораторная | 320Д | Струк Т.М. | 24-ИТ-1,24-ИТ-1/п4
|
||||
пн 10:05–11:30 | каждую неделю, нижняя строка | Разработка и анализ требований к программному обеспечению | Лабораторная | 320Д | Струк Т.М. | 24-ИТ-2,24-ИТ-2/п4
|
||||
пн 11:45–13:10 | нед. 1-14 | Объектно-ориентированные технологии программирования и стандарты проектирования | Лекция | 220Д | Глухова Т.М. | 24-ИТ-1,24-ИТ-2
|
||||
пн 11:45–13:10 | нед. 15-16 | Веб-технологии | Лекция | 220Д | Дьякова А.С. | 24-ИТ-1,24-ИТ-2
|
||||
пн 13:40–15:05 | каждую неделю | Разработка и анализ требований к программному обеспечению | Лекция | 220Д | Струк Т.М. | 24-ИТ-1,24-ИТ-2
|
||||
пн 15:20–16:45 | каждую неделю, верхняя строка | Веб-технологии | Лабораторная | 302Г | Аврусевич К.Д. | 24-ИТ-1,24-ИТ-1/п5
|
||||
пн 15:20–16:45 | каждую неделю, верхняя строка | Веб-технологии | Лабораторная | 302Г | Аврусевич К.Д. | 24-ИТ-2,24-ИТ-2/п5
|
||||
пн 15:20–16:45 | каждую неделю, верхняя строка | Разработка и анализ требований к програм-мному обеспечению | Лабораторная | 218Д | Струк Т.М. | 24-ИТ-1,24-ИТ-1/п2
|
||||
пн 15:20–16:45 | каждую неделю, верхняя строка | Разработка и анализ требований к програм-мному обеспечению | Лабораторная | 218Д | Струк Т.М. | 24-ИТ-2,24-ИТ-2/п2
|
||||
пн 15:20–16:45 | каждую неделю, верхняя строка | Разработка и анализ требований к программному обеспечению | Лабораторная | 218Д | Струк Т.М. | 24-ИТ-1,24-ИТ-1/п2
|
||||
пн 15:20–16:45 | каждую неделю, верхняя строка | Разработка и анализ требований к программному обеспечению | Лабораторная | 218Д | Струк Т.М. | 24-ИТ-2,24-ИТ-2/п2
|
||||
пн 15:20–16:45 | каждую неделю, верхняя строка | Системное программирование | Лабораторная | 217Д | Юрчишко Е.И. | 24-ИТ-1,24-ИТ-1/п4
|
||||
пн 15:20–16:45 | каждую неделю, верхняя строка | Системное программирование | Лабораторная | 217Д | Юрчишко Е.И. | 24-ИТ-2,24-ИТ-2/п4
|
||||
пн 15:20–16:45 | каждую неделю, нижняя строка | Системное программирование | Лабораторная | 217Д | Юрчишко Е.И. | 24-ИТ-1,24-ИТ-1/п3
|
||||
пн 15:20–16:45 | каждую неделю, нижняя строка | Системное программирование | Лабораторная | 217Д | Юрчишко Е.И. | 24-ИТ-2,24-ИТ-2/п3
|
||||
пн 17:00–18:25 | нед. 2, нижняя строка | Объектно-ориентированные технологии програм-мирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п3
|
||||
пн 17:00–18:25 | нед. 2, нижняя строка | Объектно-ориентированные технологии програм-мирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п3
|
||||
пн 17:00–18:25 | нед. 2, нижняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п3
|
||||
пн 17:00–18:25 | нед. 2, нижняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п3
|
||||
пт 08:30–09:55 | каждую неделю, верхняя строка | Системное программирование | Лабораторная | 207Г | Скуковская А.А. | 24-ИТ-1,24-ИТ-1/п1
|
||||
пт 08:30–09:55 | каждую неделю, верхняя строка | Системное программирование | Лабораторная | 207Г | Скуковская А.А. | 24-ИТ-2,24-ИТ-2/п1
|
||||
пт 08:30–09:55 | каждую неделю, нижняя строка | Математическое программирование | Лабораторная | 303Г | Пастухов Д.Ф. | 24-ИТ-1,24-ИТ-1/п3
|
||||
пт 08:30–09:55 | каждую неделю, нижняя строка | Математическое программирование | Лабораторная | 303Г | Пастухов Д.Ф. | 24-ИТ-2,24-ИТ-2/п3
|
||||
пт 08:30–09:55 | нед. 1, верхняя строка | Математическое программирование | Лабораторная | 302Г | Пастухов Д.Ф. | 24-ИТ-1,24-ИТ-1/п2
|
||||
пт 08:30–09:55 | нед. 1, верхняя строка | Математическое программирование | Лабораторная | 302Г | Пастухов Д.Ф. | 24-ИТ-2,24-ИТ-2/п2
|
||||
пт 08:30–09:55 | нед. 2, нижняя строка | Объектно-ориентированные технологии програм-мирования и стандарты проектирования | Лабораторная | 302Г | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п1
|
||||
пт 08:30–09:55 | нед. 2, нижняя строка | Объектно-ориентированные технологии програм-мирования и стандарты проектирования | Лабораторная | 302Г | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п1
|
||||
пт 08:30–09:55 | нед. 4, нижняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 302Г | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п2
|
||||
пт 08:30–09:55 | нед. 4, нижняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 302Г | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п2
|
||||
пт 08:30–09:55 | нед. 2, нижняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 302Г | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п1
|
||||
пт 08:30–09:55 | нед. 2, нижняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 302Г | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п1
|
||||
пт 08:30–09:55 | нед. 4, нижняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 302Г | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п2
|
||||
пт 08:30–09:55 | нед. 4, нижняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 302Г | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п2
|
||||
пт 10:05–11:30 | каждую неделю, верхняя строка | Веб-технологии | Лабораторная | 302Г | Аврусевич К.Д. | 24-ИТ-1,24-ИТ-1/п2
|
||||
пт 10:05–11:30 | каждую неделю, верхняя строка | Веб-технологии | Лабораторная | 302Г | Аврусевич К.Д. | 24-ИТ-2,24-ИТ-2/п2
|
||||
пт 10:05–11:30 | каждую неделю, верхняя строка | Математическое программирование | Лабораторная | 217Д | Пастухов Д.Ф. | 24-ИТ-1,24-ИТ-1/п4
|
||||
@@ -62,14 +62,14 @@
|
||||
пт 10:05–11:30 | каждую неделю, верхняя строка | Системное программирование | Лабораторная | 207Г | Скуковская А.А. | 24-ИТ-2,24-ИТ-2/п1
|
||||
пт 10:05–11:30 | каждую неделю, нижняя строка | Веб-технологии | Лабораторная | 303Г | Аврусевич К.Д. | 24-ИТ-1,24-ИТ-1/п3
|
||||
пт 10:05–11:30 | каждую неделю, нижняя строка | Веб-технологии | Лабораторная | 303Г | Аврусевич К.Д. | 24-ИТ-2,24-ИТ-2/п3
|
||||
пт 10:05–11:30 | каждую неделю, нижняя строка | Математическое программирование | Лабораторная | | Пастухов Д.Ф. | 24-ИТ-1,24-ИТ-1/п1
|
||||
пт 10:05–11:30 | каждую неделю, нижняя строка | Математическое программирование | Лабораторная | | Пастухов Д.Ф. | 24-ИТ-2,24-ИТ-2/п1
|
||||
пт 10:05–11:30 | каждую неделю, нижняя строка | Математическое программирование | Лабораторная | медиа | Пастухов Д.Ф. | 24-ИТ-1,24-ИТ-1/п1
|
||||
пт 10:05–11:30 | каждую неделю, нижняя строка | Математическое программирование | Лабораторная | медиа | Пастухов Д.Ф. | 24-ИТ-2,24-ИТ-2/п1
|
||||
пт 10:05–11:30 | каждую неделю, нижняя строка | Системное программирование | Лабораторная | 207Г | Скуковская А.А. | 24-ИТ-1,24-ИТ-1/п2
|
||||
пт 10:05–11:30 | каждую неделю, нижняя строка | Системное программирование | Лабораторная | 207Г | Скуковская А.А. | 24-ИТ-2,24-ИТ-2/п2
|
||||
пт 10:05–11:30 | нед. 2, нижняя строка | Объектно-ориентированне технологии программи-рования и стандарты проектирования | Лабораторная | 217Д | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п4
|
||||
пт 10:05–11:30 | нед. 2, нижняя строка | Объектно-ориентированне технологии программи-рования и стандарты проектирования | Лабораторная | 217Д | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п4
|
||||
пт 10:05–11:30 | нед. 4, нижняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 217Д | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п5
|
||||
пт 10:05–11:30 | нед. 4, нижняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 217Д | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п5
|
||||
пт 10:05–11:30 | нед. 2, нижняя строка | Объектно-ориентированне технологии программирования и стандарты проектирования | Лабораторная | 217Д | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п4
|
||||
пт 10:05–11:30 | нед. 2, нижняя строка | Объектно-ориентированне технологии программирования и стандарты проектирования | Лабораторная | 217Д | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п4
|
||||
пт 10:05–11:30 | нед. 4, нижняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 217Д | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п5
|
||||
пт 10:05–11:30 | нед. 4, нижняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 217Д | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п5
|
||||
пт 11:45–13:10 | нед. 1-13 | Математическое программирование | Лекция | 321Д | Пастухов Д.Ф. | 24-ИТ-1,24-ИТ-2
|
||||
пт 11:45–13:10 | нед. 14-15 | Базы данных | Лекция | 321Д | Скуковская А.А. | 24-ИТ-1,24-ИТ-2
|
||||
пт 13:40–15:05 | каждую неделю | Системное программирование | Лекция | 254В | Скуковская А.А. | 24-ИТ-1,24-ИТ-2
|
||||
@@ -87,8 +87,8 @@
|
||||
чт 11:45–13:10 | каждую неделю, верхняя строка | Базы данных | Лабораторная | 218Д | Дьякова А.С. | 24-ИТ-2,24-ИТ-2/п2
|
||||
чт 11:45–13:10 | каждую неделю, верхняя строка | Веб-технологии | Лабораторная | 207Г | Аврусевич К.Д. | 24-ИТ-1,24-ИТ-1/п4
|
||||
чт 11:45–13:10 | каждую неделю, верхняя строка | Веб-технологии | Лабораторная | 207Г | Аврусевич К.Д. | 24-ИТ-2,24-ИТ-2/п4
|
||||
чт 11:45–13:10 | каждую неделю, верхняя строка | Математическое программирование | Лабораторная | | Пастухов Д.Ф. | 24-ИТ-1,24-ИТ-1/п5
|
||||
чт 11:45–13:10 | каждую неделю, верхняя строка | Математическое программирование | Лабораторная | | Пастухов Д.Ф. | 24-ИТ-2,24-ИТ-2/п5
|
||||
чт 11:45–13:10 | каждую неделю, верхняя строка | Математическое программирование | Лабораторная | медиа | Пастухов Д.Ф. | 24-ИТ-1,24-ИТ-1/п5
|
||||
чт 11:45–13:10 | каждую неделю, верхняя строка | Математическое программирование | Лабораторная | медиа | Пастухов Д.Ф. | 24-ИТ-2,24-ИТ-2/п5
|
||||
чт 11:45–13:10 | каждую неделю, верхняя строка | Разработка и анализ требований к программному обеспечению | Лабораторная | 217Д | Струк Т.М. | 24-ИТ-1,24-ИТ-1/п1
|
||||
чт 11:45–13:10 | каждую неделю, верхняя строка | Разработка и анализ требований к программному обеспечению | Лабораторная | 217Д | Струк Т.М. | 24-ИТ-2,24-ИТ-2/п1
|
||||
чт 11:45–13:10 | каждую неделю, верхняя строка | Системное программирование | Лабораторная | 219Д | Юрчишко Е.И. | 24-ИТ-1,24-ИТ-1/п3
|
||||
@@ -99,8 +99,8 @@
|
||||
чт 11:45–13:10 | каждую неделю, нижняя строка | Базы данных | Лабораторная | 219Д | Юрчишко Е.И. | 24-ИТ-2,24-ИТ-2/п4
|
||||
чт 11:45–13:10 | каждую неделю, нижняя строка | Веб-технологии | Лабораторная | 207Г | Аврусевич К.Д. | 24-ИТ-1,24-ИТ-1/п1
|
||||
чт 11:45–13:10 | каждую неделю, нижняя строка | Веб-технологии | Лабораторная | 207Г | Аврусевич К.Д. | 24-ИТ-2,24-ИТ-2/п1
|
||||
чт 11:45–13:10 | каждую неделю, нижняя строка | Математическое программирование | Лабораторная | | Пастухов Д.Ф. | 24-ИТ-1,24-ИТ-1/п2
|
||||
чт 11:45–13:10 | каждую неделю, нижняя строка | Математическое программирование | Лабораторная | | Пастухов Д.Ф. | 24-ИТ-2,24-ИТ-2/п2
|
||||
чт 11:45–13:10 | каждую неделю, нижняя строка | Математическое программирование | Лабораторная | медиа | Пастухов Д.Ф. | 24-ИТ-1,24-ИТ-1/п2
|
||||
чт 11:45–13:10 | каждую неделю, нижняя строка | Математическое программирование | Лабораторная | медиа | Пастухов Д.Ф. | 24-ИТ-2,24-ИТ-2/п2
|
||||
чт 11:45–13:10 | каждую неделю, нижняя строка | Разработка и анализ требований к программному обеспечению | Лабораторная | 217Д | Струк Т.М. | 24-ИТ-1,24-ИТ-1/п5
|
||||
чт 11:45–13:10 | каждую неделю, нижняя строка | Разработка и анализ требований к программному обеспечению | Лабораторная | 217Д | Струк Т.М. | 24-ИТ-2,24-ИТ-2/п5
|
||||
чт 13:40–15:05 | даты 2026-09-10,2026-10-15,2026-11-19,2026-12-17 | Единый день информирования: 10 сентября, 15 октября, 19 ноября, 17 декабря | Занятие | | | 24-ИТ-1,24-ИТ-2
|
||||
@@ -110,35 +110,39 @@
|
||||
чт 13:40–15:05 | нед. 1, верхняя строка | Базы данных | Лабораторная | 219Д | Юрчишко Е.И. | 24-ИТ-2,24-ИТ-2/п4
|
||||
чт 13:40–15:05 | нед. 1, верхняя строка | Математическое программирование | Лабораторная | 207Г | Пастухов Д.Ф. | 24-ИТ-1,24-ИТ-1/п5
|
||||
чт 13:40–15:05 | нед. 1, верхняя строка | Математическое программирование | Лабораторная | 207Г | Пастухов Д.Ф. | 24-ИТ-2,24-ИТ-2/п5
|
||||
чт 13:40–15:05 | нед. 1, верхняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п2
|
||||
чт 13:40–15:05 | нед. 1, верхняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п2
|
||||
чт 13:40–15:05 | нед. 1, верхняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п2
|
||||
чт 13:40–15:05 | нед. 1, верхняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п2
|
||||
чт 13:40–15:05 | нед. 2, нижняя строка | Базы данных | Лабораторная | 217Д | Дьякова А.С. | 24-ИТ-1,24-ИТ-1/п1
|
||||
чт 13:40–15:05 | нед. 2, нижняя строка | Базы данных | Лабораторная | 217Д | Дьякова А.С. | 24-ИТ-2,24-ИТ-2/п1
|
||||
чт 13:40–15:05 | нед. 2, нижняя строка | Математическое программирование | Лабораторная | 219Д | Пастухов Д.Ф. | 24-ИТ-1,24-ИТ-1/п4
|
||||
чт 13:40–15:05 | нед. 2, нижняя строка | Математическое программирование | Лабораторная | 219Д | Пастухов Д.Ф. | 24-ИТ-2,24-ИТ-2/п4
|
||||
чт 13:40–15:05 | нед. 2, нижняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п5
|
||||
чт 13:40–15:05 | нед. 2, нижняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п5
|
||||
чт 13:40–15:05 | нед. 2, нижняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п5
|
||||
чт 13:40–15:05 | нед. 2, нижняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п5
|
||||
чт 13:40–15:05 | нед. 3, верхняя строка | Базы данных | Лабораторная | 217Д | Дьякова А.С. | 24-ИТ-1,24-ИТ-1/п2
|
||||
чт 13:40–15:05 | нед. 3, верхняя строка | Базы данных | Лабораторная | 217Д | Дьякова А.С. | 24-ИТ-2,24-ИТ-2/п2
|
||||
чт 13:40–15:05 | нед. 3, верхняя строка | Базы данных | Лабораторная | 219Д | Юрчишко Е.И. | 24-ИТ-1,24-ИТ-1/п5
|
||||
чт 13:40–15:05 | нед. 3, верхняя строка | Базы данных | Лабораторная | 219Д | Юрчишко Е.И. | 24-ИТ-2,24-ИТ-2/п5
|
||||
чт 13:40–15:05 | нед. 3, верхняя строка | Математическое программирование | Лабораторная | 207Г | Пастухов Д.Ф. | 24-ИТ-1,24-ИТ-1/п3
|
||||
чт 13:40–15:05 | нед. 3, верхняя строка | Математическое программирование | Лабораторная | 207Г | Пастухов Д.Ф. | 24-ИТ-2,24-ИТ-2/п3
|
||||
чт 13:40–15:05 | нед. 3, верхняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п1
|
||||
чт 13:40–15:05 | нед. 3, верхняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п1
|
||||
чт 13:40–15:05 | нед. 3, верхняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п1
|
||||
чт 13:40–15:05 | нед. 3, верхняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п1
|
||||
чт 13:40–15:05 | нед. 4, нижняя строка | Математическое программирование | Лабораторная | 217Д | Пастухов Д.Ф. | 24-ИТ-1,24-ИТ-1/п1
|
||||
чт 13:40–15:05 | нед. 4, нижняя строка | Математическое программирование | Лабораторная | 217Д | Пастухов Д.Ф. | 24-ИТ-2,24-ИТ-2/п1
|
||||
чт 13:40–15:05 | нед. 4, нижняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п4
|
||||
чт 13:40–15:05 | нед. 4, нижняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п4
|
||||
чт 13:40–15:05 | нед. 4, нижняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п4
|
||||
чт 13:40–15:05 | нед. 4, нижняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п4
|
||||
чт 15:20–16:45 | каждую неделю, верхняя строка | Базы данных | Лабораторная | 219Д | Юрчишко Е.И. | 24-ИТ-1,24-ИТ-1/п5
|
||||
чт 15:20–16:45 | каждую неделю, верхняя строка | Базы данных | Лабораторная | 219Д | Юрчишко Е.И. | 24-ИТ-2,24-ИТ-2/п5
|
||||
чт 15:20–16:45 | каждую неделю, нижняя строка | Базы данных | Лабораторная | 207Д | Дьякова А.С. | 24-ИТ-1,24-ИТ-1/п1
|
||||
чт 15:20–16:45 | каждую неделю, нижняя строка | Базы данных | Лабораторная | 207Д | Дьякова А.С. | 24-ИТ-2,24-ИТ-2/п1
|
||||
чт 15:20–16:45 | нед. 1, верхняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п2
|
||||
чт 15:20–16:45 | нед. 1, верхняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п2
|
||||
чт 15:20–16:45 | нед. 2, нижняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п5
|
||||
чт 15:20–16:45 | нед. 2, нижняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п5
|
||||
чт 15:20–16:45 | нед. 3, верхняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п1
|
||||
чт 15:20–16:45 | нед. 3, верхняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п1
|
||||
чт 15:20–16:45 | нед. 4, нижняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п4
|
||||
чт 15:20–16:45 | нед. 4, нижняя строка | Объектно- ориентированные технологии программи-рования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п4
|
||||
чт 15:20–16:45 | нед. 1, верхняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п2
|
||||
чт 15:20–16:45 | нед. 1, верхняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п2
|
||||
чт 15:20–16:45 | нед. 2, нижняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п5
|
||||
чт 15:20–16:45 | нед. 2, нижняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п5
|
||||
чт 15:20–16:45 | нед. 3, верхняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п1
|
||||
чт 15:20–16:45 | нед. 3, верхняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п1
|
||||
чт 15:20–16:45 | нед. 4, нижняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-1,24-ИТ-1/п4
|
||||
чт 15:20–16:45 | нед. 4, нижняя строка | Объектно-ориентированные технологии программирования и стандарты проектирования | Лабораторная | 311А | Гордевич Д.А. | 24-ИТ-2,24-ИТ-2/п4
|
||||
! subject_brackets | 2 нед. Метрология, стандартизация и сертификация (в информационных технологиях (л.) Михнович О.В. 211А
|
||||
! subject_brackets | Метрология, стандартизация и сертификация (в информационных технологиях (л.) Михнович О.В. 321Д
|
||||
! subject_brackets | Метрология, стандартизация и сертификация (в информационных технологиях (пр.) Михнович О.В. 321Д
|
||||
! subject_variants | 2 нед. Объектно-ориентированне технологии программи-рования и стандарты проектирования (л.з.) Гордевич Д.А. 217Д
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
вт 10:05–11:30 | каждую неделю, нижняя строка | Методы оптимизации и исследования операций | Лабораторная | 333н | Скоромник О.В. | 24-КБ,24-КБ/п2
|
||||
вт 10:05–11:30 | каждую неделю, нижняя строка | Надежность программного обеспечения | Лабораторная | 330 | Карабец Д.П. | 24-КБ,24-КБ/п1
|
||||
вт 10:05–11:30 | нед. 1-16 | Электродинамика | Практика | 220н | Вабищевич С.А. | 24-КФ
|
||||
вт 11:45–13:10 | каждую неделю | Ф и з и ч е с к а я к у л ь т у р а | Занятие | | | 24-КБ,24-КФ
|
||||
вт 11:45–13:10 | каждую неделю | Физическая культура | Занятие | | | 24-КБ,24-КФ
|
||||
вт 13:40–15:05 | каждую неделю, верхняя строка | Методы численного анализа | Лекция | 418н | Сковородко М.А. | 24-КБ
|
||||
вт 13:40–15:05 | каждую неделю, верхняя строка | Физика атома и введение в квантовую механику | Лекция | 220н | Окунев Д.О. | 24-КФ
|
||||
вт 13:40–15:05 | каждую неделю, нижняя строка | Надежность программного обеспечения | Лабораторная | 330 | Карабец Д.П. | 24-КБ,24-КБ/п2
|
||||
@@ -28,14 +28,14 @@
|
||||
пн 15:20–16:45 | каждую неделю, нижняя строка | Физика атома и введение в квантовую механику | Практика | 230н | Окунев Д.О. | 24-КФ
|
||||
пн 17:00–18:25 | каждую неделю, верхняя строка | Лабораторный спецпрактикум: Объектно-ориентированное программирование | Занятие | 215н | Жусель Д.В. | 24-КФ,24-КФ/п2
|
||||
пн 17:00–18:25 | каждую неделю, верхняя строка | Надежность программного обеспечения | Лабораторная | 330 | Карабец Д.П. | 24-КБ,24-КБ/п1
|
||||
пн 17:00–18:25 | каждую неделю, нижняя строка | Лабораторный спецпрактикум: Объектно-ориентированное программирование Солдатенко | Занятие | 215н | | 24-КФ,24-КФ/п1
|
||||
пн 17:00–18:25 | каждую неделю, нижняя строка | Лабораторный спецпрактикум: Объектно-ориентированное программирование | Занятие | 215н | Солдатенко П.Н. | 24-КФ,24-КФ/п1
|
||||
пн 17:00–18:25 | нед. 14,16, нижняя строка | Основы радиоэлектроники | Лабораторная | 406н | Грозберг Ю.Г. | 24-КФ,24-КФ/п2
|
||||
пн 17:00–18:25 | нед. 15,17, верхняя строка | Основы радиоэлектроники | Лабораторная | 406н | Грозберг Ю.Г. | 24-КФ,24-КФ/п1
|
||||
пт 08:30–09:55 | каждую неделю, верхняя строка | Лабораторный спецпрактикум: Объектно-ориентированное программирование Солдатенко | Занятие | 215н | | 24-КФ,24-КФ/п1
|
||||
пт 08:30–09:55 | каждую неделю, верхняя строка | Лабораторный спецпрактикум: Объектно-ориентированное программирование | Занятие | 215н | Солдатенко П.Н. | 24-КФ,24-КФ/п1
|
||||
пт 08:30–09:55 | каждую неделю, верхняя строка | Физический практикум: физика атома | Занятие | 210н | Окунев Д.О. | 24-КФ,24-КФ/п2
|
||||
пт 08:30–09:55 | каждую неделю, нижняя строка | Лабораторный спецпрактикум: Объектно-ориентированное программирование | Занятие | 215н | Жусель Д.В. | 24-КФ,24-КФ/п2
|
||||
пт 08:30–09:55 | каждую неделю, нижняя строка | Физический практикум: физика атома | Занятие | 210н | Окунев Д.О. | 24-КФ,24-КФ/п1
|
||||
пт 10:05–11:30 | каждую неделю, верхняя строка | Лабораторный спецпрактикум: Объектно-ориентированное программирование Солдатенко | Занятие | 215н | | 24-КФ,24-КФ/п1
|
||||
пт 10:05–11:30 | каждую неделю, верхняя строка | Лабораторный спецпрактикум: Объектно-ориентированное программирование | Занятие | 215н | Солдатенко П.Н. | 24-КФ,24-КФ/п1
|
||||
пт 10:05–11:30 | каждую неделю, верхняя строка | Физический практикум: физика атома | Занятие | 210н | Окунев Д.О. | 24-КФ,24-КФ/п2
|
||||
пт 10:05–11:30 | каждую неделю, нижняя строка | Лабораторный спецпрактикум: Объектно-ориентированное программирование | Занятие | 215н | Жусель Д.В. | 24-КФ,24-КФ/п2
|
||||
пт 10:05–11:30 | каждую неделю, нижняя строка | Физический практикум: физика атома | Занятие | 210н | Окунев Д.О. | 24-КФ,24-КФ/п1
|
||||
@@ -61,7 +61,7 @@
|
||||
сб 11:45–13:10 | каждую неделю, нижняя строка | Компьютерные сети | Лабораторная | 215н | Филиппов Ю.И. | 24-КБ,24-КБ/п2
|
||||
ср 10:05–11:30 | нед. 2-14 | Модуль «Майнор» | Занятие | | | 24-КБ,24-КФ
|
||||
чт 08:30–09:55 | каждую неделю | Русский язык как иностранный | Занятие | 537н | Кириллова В.С. | 24-КФ · иностранцам
|
||||
чт 08:30–09:55 | каждую неделю | Ф и з и ч е с к а я к у л ь т у р а | Занятие | | | 24-КБ,24-КФ · кроме иностранцев
|
||||
чт 08:30–09:55 | каждую неделю | Физическая культура | Занятие | | | 24-КБ,24-КФ · кроме иностранцев
|
||||
чт 10:05–11:30 | каждую неделю, верхняя строка | Криптографические методы защиты информации | Лекция | 329н | Кухта С.В. | 24-КБ
|
||||
чт 10:05–11:30 | каждую неделю, верхняя строка | Основы математического моделирования | Лабораторная | 215н | Сковородко М.А. | 24-КФ,24-КФ/п1
|
||||
чт 10:05–11:30 | каждую неделю, верхняя строка | Основы радиоэлектроники | Лабораторная | 406н | Грозберг Ю.Г. | 24-КФ,24-КФ/п2
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
2026-09-17 11:00–11:40 | один раз | Организационное собрание | Занятие | 515н | Бословяк С.В. | 26-ЭЭТзс
|
||||
2026-09-17 11:45–13:10 | один раз | Инженерная графика | Лекция | 518н | Малаховская В.В. | 26-ЭЭТзс
|
||||
2026-09-17 13:40–15:05 | один раз | Химия | Лекция | 427н | Фонин М.Ф. | 26-ЭЭТзс
|
||||
2026-09-17 15:20–16:45 | один раз | Иностранный язык (англ.) | Практика | 526н 325н 502н | Кузнечик Е.В. Струй О.М. Филимончик Н.А. | 26-ЭЭТзс
|
||||
2026-09-17 15:20–16:45 | один раз | Иностранный язык (англ.) | Практика | 526н, 325н, 502н | Кузнечик Е.В. | 26-ЭЭТзс
|
||||
2026-09-18 15:20–16:45 | один раз | Математика | Лекция | 336н | Козлов А.А. | 26-ЭЭТзс
|
||||
2026-09-18 17:00–18:25 | один раз | Конструкционные материалы | Лекция | 412н | Молодечкина Т.В. | 26-ЭЭТзс
|
||||
2026-09-18 18:35–20:00 | один раз | Информатика | Лекция | 412н | Иванова К.И. | 26-ЭЭТзс
|
||||
|
||||
Reference in New Issue
Block a user