chuvsu-js 2.8.2 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/README.md +3 -2
  2. package/dist/browser.d.ts +1 -5
  3. package/dist/browser.js +1 -2
  4. package/dist/index.d.ts +1 -8
  5. package/dist/index.js +2 -3
  6. package/dist/shared.d.ts +9 -0
  7. package/dist/shared.js +7 -0
  8. package/dist/tt/client.js +1 -1
  9. package/dist/tt/parse/audience.d.ts +3 -0
  10. package/dist/tt/parse/audience.js +150 -0
  11. package/dist/tt/parse/full-schedule.d.ts +4 -0
  12. package/dist/tt/parse/full-schedule.js +190 -0
  13. package/dist/tt/parse/groups.d.ts +15 -0
  14. package/dist/tt/parse/groups.js +28 -0
  15. package/dist/tt/parse/index.d.ts +5 -0
  16. package/dist/tt/parse/index.js +5 -0
  17. package/dist/tt/parse/lists.d.ts +11 -0
  18. package/dist/tt/parse/lists.js +80 -0
  19. package/dist/tt/parse/overlays.d.ts +10 -0
  20. package/dist/tt/parse/overlays.js +104 -0
  21. package/dist/tt/parse/teacher.d.ts +4 -0
  22. package/dist/tt/parse/teacher.js +166 -0
  23. package/dist/tt/schedule.d.ts +1 -2
  24. package/dist/tt/schedule.js +2 -8
  25. package/dist/tt/types.d.ts +9 -4
  26. package/dist/tt/utils/date.d.ts +5 -0
  27. package/dist/tt/utils/date.js +27 -0
  28. package/dist/tt/{utils.d.ts → utils/holidays.d.ts} +5 -55
  29. package/dist/tt/utils/holidays.js +197 -0
  30. package/dist/tt/utils/index.d.ts +7 -0
  31. package/dist/tt/utils/index.js +6 -0
  32. package/dist/tt/utils/lessons.d.ts +14 -0
  33. package/dist/tt/utils/lessons.js +123 -0
  34. package/dist/tt/utils/period.d.ts +6 -0
  35. package/dist/tt/utils/period.js +24 -0
  36. package/dist/tt/utils/semester.d.ts +30 -0
  37. package/dist/tt/utils/semester.js +64 -0
  38. package/dist/tt/utils/time-slots.d.ts +5 -0
  39. package/dist/tt/utils/time-slots.js +41 -0
  40. package/package.json +1 -1
  41. package/dist/tt/parse.d.ts +0 -16
  42. package/dist/tt/parse.js +0 -671
  43. package/dist/tt/utils.js +0 -521
@@ -0,0 +1,6 @@
1
+ export { getMonday, getWeekdayName, isSameDay } from "./date.js";
2
+ export { getAdjacentSemester, getCurrentPeriod, isSessionPeriod, } from "./period.js";
3
+ export { getSemesterStart, getSemesterWeeks, getWeekNumber, } from "./semester.js";
4
+ export { getLessonNumber, getTimeSlots } from "./time-slots.js";
5
+ export { collectTransfers, filterSlots, slotsToLessons, sortLessons, suppressTransferredLessons, } from "./lessons.js";
6
+ export { getCompensatingWorkDays, getEffectiveHolidays, getHolidayTransfers, isHoliday, RUSSIAN_HOLIDAYS, } from "./holidays.js";
@@ -0,0 +1,14 @@
1
+ import type { FullScheduleDay, FullScheduleSlot, Lesson, ScheduleEntry } from "../types.js";
2
+ export declare function sortLessons(a: Lesson, b: Lesson): number;
3
+ export declare function filterSlots(slots: FullScheduleSlot[], opts?: {
4
+ subgroup?: number;
5
+ week?: number;
6
+ date?: Date;
7
+ }): FullScheduleSlot[];
8
+ export declare function slotsToLessons(slots: FullScheduleSlot[], date: Date, opts?: {
9
+ isTeacherSchedule?: boolean;
10
+ }): Lesson[];
11
+ /** All transfer entries from the given schedule days. */
12
+ export declare function collectTransfers(days: FullScheduleDay[]): ScheduleEntry[];
13
+ /** Remove lessons whose source date/slot match a transfer. */
14
+ export declare function suppressTransferredLessons(lessons: Lesson[], transfers: ScheduleEntry[], date: Date): Lesson[];
@@ -0,0 +1,123 @@
1
+ import { isSameDay } from "./date.js";
2
+ export function sortLessons(a, b) {
3
+ return a.start.date.getTime() - b.start.date.getTime();
4
+ }
5
+ function filterEntries(entries, opts) {
6
+ return entries.filter((e) => {
7
+ // Subgroup filter applies to all entry types
8
+ if (opts?.subgroup && e.subgroup && e.subgroup !== opts.subgroup) {
9
+ return false;
10
+ }
11
+ // Transfer entries: only include when the query date matches the target date
12
+ if (e.transfer) {
13
+ if (!opts?.date)
14
+ return false;
15
+ return isSameDay(e.transfer.targetDate, opts.date);
16
+ }
17
+ // Substitute-for entries: only include when the query date matches
18
+ if (e.substituteFor) {
19
+ if (!opts?.date)
20
+ return false;
21
+ return isSameDay(e.substituteFor.date, opts.date);
22
+ }
23
+ if (opts?.week != null) {
24
+ if (e.weeks.from > 0 &&
25
+ (opts.week < e.weeks.from || opts.week > e.weeks.to)) {
26
+ return false;
27
+ }
28
+ if (e.weekParity) {
29
+ const isEven = opts.week % 2 === 0;
30
+ if (e.weekParity === "even" && !isEven)
31
+ return false;
32
+ if (e.weekParity === "odd" && isEven)
33
+ return false;
34
+ }
35
+ }
36
+ return true;
37
+ });
38
+ }
39
+ export function filterSlots(slots, opts) {
40
+ if (opts?.subgroup == null && opts?.week == null && opts?.date == null)
41
+ return slots;
42
+ return slots
43
+ .map((slot) => ({
44
+ ...slot,
45
+ entries: filterEntries(slot.entries, opts),
46
+ }))
47
+ .filter((slot) => slot.entries.length > 0);
48
+ }
49
+ function makeLessonTime(date, time) {
50
+ const d = new Date(date);
51
+ d.setHours(time.hours, time.minutes, 0, 0);
52
+ return { date: d, hours: time.hours, minutes: time.minutes };
53
+ }
54
+ export function slotsToLessons(slots, date, opts) {
55
+ const lessons = [];
56
+ for (const slot of slots) {
57
+ for (const entry of slot.entries) {
58
+ let room = entry.room;
59
+ let teacher = entry.teacher;
60
+ let originalRoom;
61
+ let originalTeacher;
62
+ // Apply date-specific substitutions
63
+ if (entry.substitutions) {
64
+ const sub = entry.substitutions.find((s) => isSameDay(s.date, date));
65
+ if (sub) {
66
+ if (sub.room) {
67
+ originalRoom = room;
68
+ room = sub.room;
69
+ }
70
+ if (sub.teacher) {
71
+ // On teacher schedules, a teacher substitution means another teacher
72
+ // is taking over — exclude the lesson entirely.
73
+ if (opts?.isTeacherSchedule)
74
+ continue;
75
+ originalTeacher = teacher;
76
+ teacher = sub.teacher;
77
+ }
78
+ }
79
+ }
80
+ lessons.push({
81
+ number: slot.number,
82
+ start: makeLessonTime(date, slot.timeStart),
83
+ end: makeLessonTime(date, slot.timeEnd),
84
+ subject: entry.subject,
85
+ type: entry.type,
86
+ room,
87
+ teacher,
88
+ groups: entry.groups,
89
+ weeks: entry.weeks,
90
+ subgroup: entry.subgroup,
91
+ weekParity: entry.weekParity,
92
+ originalRoom,
93
+ originalTeacher,
94
+ transfer: entry.transfer,
95
+ substituteFor: entry.substituteFor,
96
+ possibleChanges: entry.possibleChanges,
97
+ });
98
+ }
99
+ }
100
+ return lessons;
101
+ }
102
+ /** All transfer entries from the given schedule days. */
103
+ export function collectTransfers(days) {
104
+ const transfers = [];
105
+ for (const day of days) {
106
+ for (const slot of day.slots) {
107
+ for (const entry of slot.entries) {
108
+ if (entry.transfer)
109
+ transfers.push(entry);
110
+ }
111
+ }
112
+ }
113
+ return transfers;
114
+ }
115
+ /** Remove lessons whose source date/slot match a transfer. */
116
+ export function suppressTransferredLessons(lessons, transfers, date) {
117
+ return lessons.filter((lesson) => {
118
+ return !transfers.some((t) => t.transfer &&
119
+ isSameDay(t.transfer.fromDate, date) &&
120
+ t.transfer.fromSlot === lesson.number &&
121
+ t.transfer.subject === lesson.subject);
122
+ });
123
+ }
@@ -0,0 +1,6 @@
1
+ import { Period } from "../../common/types.js";
2
+ export declare function getCurrentPeriod(opts?: {
3
+ date?: Date;
4
+ }): Period;
5
+ export declare function isSessionPeriod(period: Period): boolean;
6
+ export declare function getAdjacentSemester(session: Period): Period;
@@ -0,0 +1,24 @@
1
+ export function getCurrentPeriod(opts) {
2
+ const date = opts?.date ?? new Date();
3
+ const month = date.getMonth();
4
+ const day = date.getDate();
5
+ // Dec 25+ and Jan -> Winter session (зимняя сессия)
6
+ if (month === 0 || (month === 11 && day >= 25))
7
+ return 2 /* Period.WinterSession */;
8
+ // Feb-May -> Spring semester (весенний семестр)
9
+ if (month >= 1 && month <= 4)
10
+ return 3 /* Period.SpringSemester */;
11
+ // Jun-Aug -> Summer session (летняя сессия)
12
+ if (month >= 5 && month <= 7)
13
+ return 4 /* Period.SummerSession */;
14
+ // Sep - Dec 24 -> Fall semester (осенний семестр)
15
+ return 1 /* Period.FallSemester */;
16
+ }
17
+ export function isSessionPeriod(period) {
18
+ return period === 2 /* Period.WinterSession */ || period === 4 /* Period.SummerSession */;
19
+ }
20
+ export function getAdjacentSemester(session) {
21
+ return session === 2 /* Period.WinterSession */
22
+ ? 1 /* Period.FallSemester */
23
+ : 3 /* Period.SpringSemester */;
24
+ }
@@ -0,0 +1,30 @@
1
+ import { Period } from "../../common/types.js";
2
+ import type { SemesterWeek } from "../types.js";
3
+ /**
4
+ * Start date of a semester.
5
+ * Fall: September 1 of the semester year.
6
+ * Spring: first Monday of February of the semester year.
7
+ * If year is omitted, the semester year is derived from the current
8
+ * academic year instead of the calendar year.
9
+ */
10
+ export declare function getSemesterStart(opts: {
11
+ period: Period;
12
+ year?: number;
13
+ date?: Date;
14
+ }): Date;
15
+ /**
16
+ * All weeks in a semester with their start/end dates.
17
+ * Week 0 starts from the semester start date.
18
+ */
19
+ export declare function getSemesterWeeks(opts: {
20
+ period: Period;
21
+ year?: number;
22
+ date?: Date;
23
+ weekCount?: number;
24
+ }): SemesterWeek[];
25
+ /** Current week number within a semester. */
26
+ export declare function getWeekNumber(opts: {
27
+ period: Period;
28
+ year?: number;
29
+ date?: Date;
30
+ }): number;
@@ -0,0 +1,64 @@
1
+ import { getMonday } from "./date.js";
2
+ function getAcademicYearStartYear(date) {
3
+ const year = date.getFullYear();
4
+ const month = date.getMonth();
5
+ // Academic year starts in September.
6
+ return month >= 8 ? year : year - 1;
7
+ }
8
+ function resolveSemesterYear(opts) {
9
+ if (opts.year != null)
10
+ return opts.year;
11
+ const baseDate = opts.date ?? new Date();
12
+ const academicYearStart = getAcademicYearStartYear(baseDate);
13
+ return opts.period === 1 /* Period.FallSemester */
14
+ ? academicYearStart
15
+ : academicYearStart + 1;
16
+ }
17
+ /**
18
+ * Start date of a semester.
19
+ * Fall: September 1 of the semester year.
20
+ * Spring: first Monday of February of the semester year.
21
+ * If year is omitted, the semester year is derived from the current
22
+ * academic year instead of the calendar year.
23
+ */
24
+ export function getSemesterStart(opts) {
25
+ const year = resolveSemesterYear(opts);
26
+ if (opts.period === 1 /* Period.FallSemester */) {
27
+ return new Date(year, 8, 1); // September 1
28
+ }
29
+ // Spring: first Monday of February
30
+ const feb1 = new Date(year, 1, 1);
31
+ const day = feb1.getDay();
32
+ const daysToAdd = day === 1 ? 0 : day === 0 ? 1 : 8 - day;
33
+ const firstMonday = new Date(year, 1, 1 + daysToAdd);
34
+ firstMonday.setHours(0, 0, 0, 0);
35
+ return firstMonday;
36
+ }
37
+ /**
38
+ * All weeks in a semester with their start/end dates.
39
+ * Week 0 starts from the semester start date.
40
+ */
41
+ export function getSemesterWeeks(opts) {
42
+ const weekCount = opts.weekCount ?? 17;
43
+ const semesterStart = getSemesterStart(opts);
44
+ const startMonday = getMonday(semesterStart);
45
+ const weeks = [];
46
+ for (let i = 0; i <= weekCount; i++) {
47
+ const start = new Date(startMonday);
48
+ start.setDate(startMonday.getDate() + i * 7);
49
+ const end = new Date(start);
50
+ end.setDate(start.getDate() + 6);
51
+ end.setHours(23, 59, 59, 999);
52
+ weeks.push({ week: i, start, end });
53
+ }
54
+ return weeks;
55
+ }
56
+ /** Current week number within a semester. */
57
+ export function getWeekNumber(opts) {
58
+ const date = opts.date ?? new Date();
59
+ const semesterStart = getSemesterStart({ ...opts, date });
60
+ const startMonday = getMonday(semesterStart);
61
+ const targetMonday = getMonday(date);
62
+ const diff = targetMonday.getTime() - startMonday.getTime();
63
+ return Math.floor(diff / (7 * 24 * 60 * 60 * 1000));
64
+ }
@@ -0,0 +1,5 @@
1
+ import { EducationType } from "../../common/types.js";
2
+ import type { Time } from "../../common/types.js";
3
+ import type { LessonTimeSlot } from "../types.js";
4
+ export declare function getTimeSlots(educationType: EducationType): LessonTimeSlot[];
5
+ export declare function getLessonNumber(time: Time, educationType: EducationType): number;
@@ -0,0 +1,41 @@
1
+ const VO_TIME_SLOTS = [
2
+ { number: 1, start: { hours: 8, minutes: 20 }, end: { hours: 9, minutes: 40 } },
3
+ { number: 2, start: { hours: 9, minutes: 50 }, end: { hours: 11, minutes: 10 } },
4
+ { number: 3, start: { hours: 11, minutes: 40 }, end: { hours: 13, minutes: 0 } },
5
+ { number: 4, start: { hours: 13, minutes: 30 }, end: { hours: 14, minutes: 50 } },
6
+ { number: 5, start: { hours: 15, minutes: 0 }, end: { hours: 16, minutes: 20 } },
7
+ { number: 6, start: { hours: 16, minutes: 40 }, end: { hours: 18, minutes: 0 } },
8
+ { number: 7, start: { hours: 18, minutes: 10 }, end: { hours: 19, minutes: 30 } },
9
+ { number: 8, start: { hours: 19, minutes: 40 }, end: { hours: 21, minutes: 0 } },
10
+ ];
11
+ const SPO_TIME_SLOTS = [
12
+ { number: 1, start: { hours: 8, minutes: 10 }, end: { hours: 9, minutes: 40 } },
13
+ { number: 2, start: { hours: 9, minutes: 55 }, end: { hours: 11, minutes: 25 } },
14
+ { number: 3, start: { hours: 11, minutes: 55 }, end: { hours: 13, minutes: 25 } },
15
+ { number: 4, start: { hours: 13, minutes: 40 }, end: { hours: 15, minutes: 10 } },
16
+ { number: 5, start: { hours: 15, minutes: 25 }, end: { hours: 16, minutes: 55 } },
17
+ { number: 6, start: { hours: 17, minutes: 10 }, end: { hours: 18, minutes: 40 } },
18
+ { number: 7, start: { hours: 18, minutes: 55 }, end: { hours: 20, minutes: 25 } },
19
+ ];
20
+ export function getTimeSlots(educationType) {
21
+ return educationType === 2 /* EducationType.VocationalEducation */
22
+ ? SPO_TIME_SLOTS
23
+ : VO_TIME_SLOTS;
24
+ }
25
+ function timeToMinutes(t) {
26
+ return t.hours * 60 + t.minutes;
27
+ }
28
+ export function getLessonNumber(time, educationType) {
29
+ const slots = getTimeSlots(educationType);
30
+ const target = timeToMinutes(time);
31
+ let closest = slots[0];
32
+ let minDiff = Math.abs(timeToMinutes(closest.start) - target);
33
+ for (let i = 1; i < slots.length; i++) {
34
+ const diff = Math.abs(timeToMinutes(slots[i].start) - target);
35
+ if (diff < minDiff) {
36
+ minDiff = diff;
37
+ closest = slots[i];
38
+ }
39
+ }
40
+ return closest.number;
41
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chuvsu-js",
3
- "version": "2.8.2",
3
+ "version": "3.0.1",
4
4
  "description": "Node.js library for ChuvSU student portal (lk.chuvsu.ru) and schedule (tt.chuvsu.ru)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -1,16 +0,0 @@
1
- import { type Period, EducationType } from "../common/types.js";
2
- import type { Audience, AudienceInfo, Faculty, Group, FullScheduleDay, TeacherInfo } from "./types.js";
3
- export declare function parsePeriodFromPage(html: string): Period | null;
4
- export declare function parseGroupButtons(html: string): Group[];
5
- export declare function parseFacultyButtons(html: string): Faculty[];
6
- export declare function parseAudienceButtons(html: string): Audience[];
7
- export declare function parseAudienceName(html: string): string | null;
8
- export declare function parseTeacherButtons(html: string): {
9
- id: number;
10
- name: string;
11
- }[];
12
- export declare function parseFullSchedule(html: string, educationType?: EducationType): FullScheduleDay[];
13
- export declare function parseAudienceInfo(html: string): AudienceInfo | null;
14
- export declare function parseAudienceFullSchedule(html: string): FullScheduleDay[];
15
- export declare function parseTeacherFullSchedule(html: string, educationType?: EducationType): FullScheduleDay[];
16
- export declare function parseTeacherInfo(html: string): TeacherInfo | null;