initial commit

This commit is contained in:
2026-10-16 22:08:45 +03:00
commit 19ee41411b
97 changed files with 15141 additions and 0 deletions

59
server/calendar.js Normal file
View File

@@ -0,0 +1,59 @@
import { readDocx } from "./parser.js";
const toIso = (d, m, y) =>
`${y}-${m.padStart(2, "0")}-${d.padStart(2, "0")}`;
const time = (h, m) => `${h.padStart(2, "0")}:${m}`;
// "Расписание белых и зеленых недель.docx": pairs of cells "номер | дд.мм.гггг-дд.мм.гггг".
export function parseWeeksDocx(buffer) {
const { tables } = readDocx(buffer);
const weeks = [];
for (const { rows } of tables) {
const header = rows[0] || [];
const colorOf = (col) => {
const label = header.find((c) => c.col === col)?.text.toLowerCase() || "";
return /зел[её]н/.test(label) ? "green" : /бел/.test(label) ? "white" : null;
};
for (const row of rows)
row.forEach((cell, i) => {
const range = cell.text.match(
/(\d{1,2})\.(\d{1,2})\.(\d{4})\s*[-–]\s*(\d{1,2})\.(\d{1,2})\.(\d{4})/,
);
const n = Number(row[i - 1]?.text.match(/^\d{1,2}$/)?.[0]);
if (!range || !n) return;
weeks.push({
n,
start: toIso(range[1], range[2], range[3]),
end: toIso(range[4], range[5], range[6]),
color: colorOf(cell.col) || (n % 2 ? "white" : "green"),
});
});
}
weeks.sort((a, b) => a.start.localeCompare(b.start));
if (!weeks.length)
throw new Error("Таблица недель не распознана.");
return weeks;
}
// "Расписание звонков.docx": "1 пара | 8.30 – 9.10 9.15 – 9.55".
export function parseBellsDocx(buffer) {
const { tables } = readDocx(buffer);
const bells = [];
for (const { rows } of tables)
for (const row of rows) {
const pair = Number(row[0]?.text.match(/^(\d)\s*пара/i)?.[1]);
const times = [
...row
.slice(1)
.map((c) => c.text)
.join(" ")
.matchAll(/([0-2]?\d)[.:]([0-5]\d)/g),
].map((m) => time(m[1], m[2]));
if (!pair || times.length < 2) continue;
const halves = [];
for (let i = 0; i + 1 < times.length; i += 2)
halves.push([times[i], times[i + 1]]);
bells.push({ pair, start: times[0], end: times.at(-1), halves });
}
return bells.sort((a, b) => a.pair - b.pair);
}