chuvsu-js 2.5.0 → 2.6.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.
- package/dist/index.d.ts +1 -1
- package/dist/tt/client.js +2 -2
- package/dist/tt/parse.js +62 -3
- package/dist/tt/schedule.d.ts +3 -1
- package/dist/tt/schedule.js +8 -5
- package/dist/tt/types.d.ts +11 -0
- package/dist/tt/utils.d.ts +3 -1
- package/dist/tt/utils.js +15 -3
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -7,4 +7,4 @@ export type { Holiday, HolidayTransfer } from "./tt/utils.js";
|
|
|
7
7
|
export { Period, EducationType, AuthError, ParseError, } from "./common/types.js";
|
|
8
8
|
export type { Time, WeekRange, Teacher } from "./common/types.js";
|
|
9
9
|
export type { PersonalData } from "./lk/types.js";
|
|
10
|
-
export type { Faculty, Group, ScheduleEntry, FullScheduleSlot, FullScheduleDay, LessonTimeSlot, Lesson, LessonTime, SemesterWeek, Substitution, TransferInfo, TeacherInfo, TtClientOptions, CacheConfig, } from "./tt/types.js";
|
|
10
|
+
export type { Faculty, Group, ScheduleEntry, FullScheduleSlot, FullScheduleDay, LessonTimeSlot, Lesson, LessonTime, SemesterWeek, Substitution, SubstituteForInfo, TransferInfo, TeacherInfo, TtClientOptions, CacheConfig, } from "./tt/types.js";
|
package/dist/tt/client.js
CHANGED
|
@@ -203,13 +203,13 @@ export class TtClient {
|
|
|
203
203
|
for (const { period, days } of results) {
|
|
204
204
|
schedules.set(period, days);
|
|
205
205
|
}
|
|
206
|
-
return new Schedule(teacherId, schedules, undefined, this.educationType);
|
|
206
|
+
return new Schedule(teacherId, schedules, undefined, this.educationType, undefined, undefined, true);
|
|
207
207
|
}
|
|
208
208
|
async getTeacherScheduleForPeriod(opts) {
|
|
209
209
|
const days = await this.fetchTeacherSchedule(opts.teacherId, opts.period);
|
|
210
210
|
const schedules = new Map();
|
|
211
211
|
schedules.set(opts.period, days);
|
|
212
|
-
return new Schedule(opts.teacherId, schedules, opts.period, this.educationType);
|
|
212
|
+
return new Schedule(opts.teacherId, schedules, opts.period, this.educationType, undefined, undefined, true);
|
|
213
213
|
}
|
|
214
214
|
async getTeacherInfo(teacherId) {
|
|
215
215
|
const url = `${BASE}/index/techtt/tech/${teacherId}`;
|
package/dist/tt/parse.js
CHANGED
|
@@ -130,10 +130,18 @@ function parseTransferDiv(div) {
|
|
|
130
130
|
return null;
|
|
131
131
|
const roomMatch = divHtml.match(/([А-Яа-яA-Za-z]-\d+)/);
|
|
132
132
|
const typeMatch = divText.match(/\((лк|пр|лб|зач|экз|зчО|кр|конс)\)/);
|
|
133
|
-
// Teacher: last text line
|
|
133
|
+
// Teacher: last text line that isn't a subgroup marker
|
|
134
134
|
const parts = divHtml.split(/<br\s*\/?>/);
|
|
135
|
-
|
|
135
|
+
let teacherPart = "";
|
|
136
|
+
for (let i = parts.length - 1; i >= 0; i--) {
|
|
137
|
+
const clean = parts[i].replace(/<[^>]*>/g, "").trim();
|
|
138
|
+
if (clean && !/подгруппа/.test(clean)) {
|
|
139
|
+
teacherPart = clean;
|
|
140
|
+
break;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
136
143
|
const transfer = { targetDate, fromDate, fromSlot, subject };
|
|
144
|
+
const subgroupMatch = divText.match(/(\d+)\s*подгруппа/);
|
|
137
145
|
return {
|
|
138
146
|
transfer,
|
|
139
147
|
entry: {
|
|
@@ -141,7 +149,8 @@ function parseTransferDiv(div) {
|
|
|
141
149
|
subject,
|
|
142
150
|
type: typeMatch?.[1] ?? "",
|
|
143
151
|
weeks: { from: 0, to: 0 },
|
|
144
|
-
teacher: parseTeacher(
|
|
152
|
+
teacher: parseTeacher(teacherPart),
|
|
153
|
+
subgroup: subgroupMatch ? parseInt(subgroupMatch[1]) : undefined,
|
|
145
154
|
transfer,
|
|
146
155
|
},
|
|
147
156
|
};
|
|
@@ -163,6 +172,47 @@ function parseSubstitutionDiv(div) {
|
|
|
163
172
|
teacher = parseTeacher(teacherMatch[1].trim());
|
|
164
173
|
return { date, room, teacher };
|
|
165
174
|
}
|
|
175
|
+
function parseSubstituteForDiv(div) {
|
|
176
|
+
const divText = text(div);
|
|
177
|
+
const divHtml = div.innerHTML ?? "";
|
|
178
|
+
const m = divText.match(/(\d{2})\.(\d{2})\.(\d{4})\s*замена\s*вместо:/);
|
|
179
|
+
if (!m)
|
|
180
|
+
return null;
|
|
181
|
+
const date = parseDate(m[1], m[2], m[3]);
|
|
182
|
+
// Original teacher: first blue span (right after "замена вместо:")
|
|
183
|
+
const origTeacherMatch = divHtml.match(/замена\s*вместо:\s*<\/b><\/span>\s*<span[^>]*>([^<]+)<\/span>/);
|
|
184
|
+
const originalTeacher = origTeacherMatch
|
|
185
|
+
? parseTeacher(origTeacherMatch[1].trim())
|
|
186
|
+
: { name: "" };
|
|
187
|
+
// Subject: second blue span
|
|
188
|
+
const subjectEl = div.querySelectorAll('span[style*="color: blue"]');
|
|
189
|
+
let subject = "";
|
|
190
|
+
for (const el of subjectEl) {
|
|
191
|
+
const t = text(el);
|
|
192
|
+
if (t && t !== origTeacherMatch?.[1]?.trim()) {
|
|
193
|
+
subject = t;
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
if (!subject)
|
|
198
|
+
return null;
|
|
199
|
+
const roomMatch = divHtml.match(/(?:<br\s*\/?>)\s*([А-Яа-яA-Za-z]-\d+)/);
|
|
200
|
+
const typeMatch = divText.match(/\((лк|пр|лб|зач|экз|зчО|кр|конс)\)/);
|
|
201
|
+
const groupsMatch = divHtml.match(/\((?:лк|пр|лб|зач|экз|зчО|кр|конс)\)\s*(?:<br\s*\/?>)\s*([^<]+?)(?:\s*<i|$)/);
|
|
202
|
+
const subgroupMatch = divText.match(/(\d+)\s*подгруппа/);
|
|
203
|
+
return {
|
|
204
|
+
entry: {
|
|
205
|
+
room: roomMatch?.[1] ?? "",
|
|
206
|
+
subject,
|
|
207
|
+
type: typeMatch?.[1] ?? "",
|
|
208
|
+
weeks: { from: 0, to: 0 },
|
|
209
|
+
teacher: { name: "" },
|
|
210
|
+
groups: groupsMatch?.[1]?.trim() ?? "",
|
|
211
|
+
subgroup: subgroupMatch ? parseInt(subgroupMatch[1]) : undefined,
|
|
212
|
+
substituteFor: { date, originalTeacher },
|
|
213
|
+
},
|
|
214
|
+
};
|
|
215
|
+
}
|
|
166
216
|
function parseSemesterEntry(el) {
|
|
167
217
|
const td = el.querySelector("td") ?? el;
|
|
168
218
|
const fullHtml = td.innerHTML ?? "";
|
|
@@ -319,6 +369,15 @@ function parseTeacherSemesterEntry(el) {
|
|
|
319
369
|
return result.entry;
|
|
320
370
|
}
|
|
321
371
|
}
|
|
372
|
+
// Check for "замена вместо:" (substitute lesson for another teacher)
|
|
373
|
+
for (const div of redDivs) {
|
|
374
|
+
const result = parseSubstituteForDiv(div);
|
|
375
|
+
if (result) {
|
|
376
|
+
if (possibleChanges)
|
|
377
|
+
result.entry.possibleChanges = true;
|
|
378
|
+
return result.entry;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
322
381
|
const substitutions = [];
|
|
323
382
|
for (const div of redDivs) {
|
|
324
383
|
const sub = parseSubstitutionDiv(div);
|
package/dist/tt/schedule.d.ts
CHANGED
|
@@ -5,12 +5,14 @@ export declare class Schedule {
|
|
|
5
5
|
readonly groupId: number;
|
|
6
6
|
readonly scheduleMap: Map<number, FullScheduleDay[]>;
|
|
7
7
|
readonly educationType: EducationType;
|
|
8
|
+
/** Whether this is a teacher schedule (affects substitution handling). */
|
|
9
|
+
readonly isTeacherSchedule: boolean;
|
|
8
10
|
/** List of holidays to exclude from schedule queries. Pass `[]` to disable. */
|
|
9
11
|
readonly holidays: Holiday[];
|
|
10
12
|
/** Government decree day-off transfers (Постановление Правительства). */
|
|
11
13
|
readonly holidayTransfers: HolidayTransfer[];
|
|
12
14
|
private _period?;
|
|
13
|
-
constructor(groupId: number, scheduleMap: Map<number, FullScheduleDay[]>, period?: Period, educationType?: EducationType, holidays?: Holiday[] | null, holidayTransfers?: HolidayTransfer[]);
|
|
15
|
+
constructor(groupId: number, scheduleMap: Map<number, FullScheduleDay[]>, period?: Period, educationType?: EducationType, holidays?: Holiday[] | null, holidayTransfers?: HolidayTransfer[], isTeacherSchedule?: boolean);
|
|
14
16
|
/** Current (or fixed) period for this schedule. */
|
|
15
17
|
get period(): Period;
|
|
16
18
|
/** Days for the current period. */
|
package/dist/tt/schedule.js
CHANGED
|
@@ -3,15 +3,18 @@ export class Schedule {
|
|
|
3
3
|
groupId;
|
|
4
4
|
scheduleMap;
|
|
5
5
|
educationType;
|
|
6
|
+
/** Whether this is a teacher schedule (affects substitution handling). */
|
|
7
|
+
isTeacherSchedule;
|
|
6
8
|
/** List of holidays to exclude from schedule queries. Pass `[]` to disable. */
|
|
7
9
|
holidays;
|
|
8
10
|
/** Government decree day-off transfers (Постановление Правительства). */
|
|
9
11
|
holidayTransfers;
|
|
10
12
|
_period;
|
|
11
|
-
constructor(groupId, scheduleMap, period, educationType, holidays, holidayTransfers) {
|
|
13
|
+
constructor(groupId, scheduleMap, period, educationType, holidays, holidayTransfers, isTeacherSchedule) {
|
|
12
14
|
this.groupId = groupId;
|
|
13
15
|
this.scheduleMap = scheduleMap;
|
|
14
16
|
this.educationType = educationType ?? 1 /* EducationType.HigherEducation */;
|
|
17
|
+
this.isTeacherSchedule = isTeacherSchedule ?? false;
|
|
15
18
|
this._period = period;
|
|
16
19
|
this.holidays = holidays ?? RUSSIAN_HOLIDAYS;
|
|
17
20
|
this.holidayTransfers = holidayTransfers ?? [];
|
|
@@ -73,14 +76,14 @@ export class Schedule {
|
|
|
73
76
|
const dayName = getWeekdayName(weekday);
|
|
74
77
|
for (const d of days) {
|
|
75
78
|
if (d.weekday.toLowerCase() === dayName.toLowerCase() && d.date) {
|
|
76
|
-
lessons.push(...slotsToLessons(d.slots, d.date));
|
|
79
|
+
lessons.push(...slotsToLessons(d.slots, d.date, { isTeacherSchedule: this.isTeacherSchedule }));
|
|
77
80
|
}
|
|
78
81
|
}
|
|
79
82
|
return lessons.sort(sortLessons);
|
|
80
83
|
}
|
|
81
84
|
const slots = this.getSlotsForWeekday(weekday, days, opts);
|
|
82
85
|
const date = this.getDateForWeekday(weekday, period, opts?.week);
|
|
83
|
-
return slotsToLessons(slots, date);
|
|
86
|
+
return slotsToLessons(slots, date, { isTeacherSchedule: this.isTeacherSchedule });
|
|
84
87
|
}
|
|
85
88
|
forDate(date, opts) {
|
|
86
89
|
if (isHoliday(date, this.holidays, this.holidayTransfers))
|
|
@@ -91,7 +94,7 @@ export class Schedule {
|
|
|
91
94
|
for (const [, d] of this.scheduleMap) {
|
|
92
95
|
for (const day of d) {
|
|
93
96
|
if (day.date && Schedule.isSameDay(day.date, date)) {
|
|
94
|
-
lessons.push(...slotsToLessons(day.slots, date));
|
|
97
|
+
lessons.push(...slotsToLessons(day.slots, date, { isTeacherSchedule: this.isTeacherSchedule }));
|
|
95
98
|
}
|
|
96
99
|
}
|
|
97
100
|
}
|
|
@@ -109,7 +112,7 @@ export class Schedule {
|
|
|
109
112
|
week,
|
|
110
113
|
date,
|
|
111
114
|
});
|
|
112
|
-
lessons.push(...slotsToLessons(slots, date));
|
|
115
|
+
lessons.push(...slotsToLessons(slots, date, { isTeacherSchedule: this.isTeacherSchedule }));
|
|
113
116
|
}
|
|
114
117
|
// 3. Suppress lessons that were transferred away from this date
|
|
115
118
|
const transfers = collectTransfers(semesterDays);
|
package/dist/tt/types.d.ts
CHANGED
|
@@ -18,6 +18,13 @@ export interface Substitution {
|
|
|
18
18
|
/** New teacher, if changed. */
|
|
19
19
|
teacher?: Teacher;
|
|
20
20
|
}
|
|
21
|
+
/** Info about a lesson this teacher is substituting for another teacher. */
|
|
22
|
+
export interface SubstituteForInfo {
|
|
23
|
+
/** The date this substitute lesson takes place. */
|
|
24
|
+
date: Date;
|
|
25
|
+
/** The original teacher being replaced. */
|
|
26
|
+
originalTeacher: Teacher;
|
|
27
|
+
}
|
|
21
28
|
/** Info about a lesson transferred from another date/slot. */
|
|
22
29
|
export interface TransferInfo {
|
|
23
30
|
/** Date when this lesson takes place (target). */
|
|
@@ -43,6 +50,8 @@ export interface ScheduleEntry {
|
|
|
43
50
|
substitutions?: Substitution[];
|
|
44
51
|
/** If this entry is a transferred lesson (перенос). */
|
|
45
52
|
transfer?: TransferInfo;
|
|
53
|
+
/** If this entry is a substitute lesson (замена вместо). */
|
|
54
|
+
substituteFor?: SubstituteForInfo;
|
|
46
55
|
/** Whether this entry is marked as potentially changing (class="want"). */
|
|
47
56
|
possibleChanges?: boolean;
|
|
48
57
|
}
|
|
@@ -86,6 +95,8 @@ export interface Lesson {
|
|
|
86
95
|
originalTeacher?: Teacher;
|
|
87
96
|
/** Transfer info if this lesson was moved from another date/slot. */
|
|
88
97
|
transfer?: TransferInfo;
|
|
98
|
+
/** If this lesson is a substitute (замена вместо), the original teacher. */
|
|
99
|
+
substituteFor?: SubstituteForInfo;
|
|
89
100
|
/** Whether this lesson is marked as potentially changing. */
|
|
90
101
|
possibleChanges?: boolean;
|
|
91
102
|
}
|
package/dist/tt/utils.d.ts
CHANGED
|
@@ -38,7 +38,9 @@ export declare function filterSlots(slots: FullScheduleSlot[], opts?: {
|
|
|
38
38
|
week?: number;
|
|
39
39
|
date?: Date;
|
|
40
40
|
}): FullScheduleSlot[];
|
|
41
|
-
export declare function slotsToLessons(slots: FullScheduleSlot[], date: Date
|
|
41
|
+
export declare function slotsToLessons(slots: FullScheduleSlot[], date: Date, opts?: {
|
|
42
|
+
isTeacherSchedule?: boolean;
|
|
43
|
+
}): Lesson[];
|
|
42
44
|
/** Collect all transfer entries from schedule days. */
|
|
43
45
|
export declare function collectTransfers(days: FullScheduleDay[]): ScheduleEntry[];
|
|
44
46
|
/** Remove lessons whose source date/slot match a transfer. */
|
package/dist/tt/utils.js
CHANGED
|
@@ -95,14 +95,21 @@ function isSameDay(a, b) {
|
|
|
95
95
|
}
|
|
96
96
|
function filterEntries(entries, opts) {
|
|
97
97
|
return entries.filter((e) => {
|
|
98
|
+
// Subgroup filter applies to all entry types
|
|
99
|
+
if (opts?.subgroup && e.subgroup && e.subgroup !== opts.subgroup) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
98
102
|
// Transfer entries: only include when the query date matches the target date
|
|
99
103
|
if (e.transfer) {
|
|
100
104
|
if (!opts?.date)
|
|
101
105
|
return false;
|
|
102
106
|
return isSameDay(e.transfer.targetDate, opts.date);
|
|
103
107
|
}
|
|
104
|
-
|
|
105
|
-
|
|
108
|
+
// Substitute-for entries: only include when the query date matches
|
|
109
|
+
if (e.substituteFor) {
|
|
110
|
+
if (!opts?.date)
|
|
111
|
+
return false;
|
|
112
|
+
return isSameDay(e.substituteFor.date, opts.date);
|
|
106
113
|
}
|
|
107
114
|
if (opts?.week != null) {
|
|
108
115
|
if (e.weeks.from > 0 &&
|
|
@@ -135,7 +142,7 @@ function makeLessonTime(date, time) {
|
|
|
135
142
|
d.setHours(time.hours, time.minutes, 0, 0);
|
|
136
143
|
return { date: d, hours: time.hours, minutes: time.minutes };
|
|
137
144
|
}
|
|
138
|
-
export function slotsToLessons(slots, date) {
|
|
145
|
+
export function slotsToLessons(slots, date, opts) {
|
|
139
146
|
const lessons = [];
|
|
140
147
|
for (const slot of slots) {
|
|
141
148
|
for (const entry of slot.entries) {
|
|
@@ -152,6 +159,10 @@ export function slotsToLessons(slots, date) {
|
|
|
152
159
|
room = sub.room;
|
|
153
160
|
}
|
|
154
161
|
if (sub.teacher) {
|
|
162
|
+
// On teacher schedules, a teacher substitution means another teacher
|
|
163
|
+
// is taking over — exclude the lesson entirely.
|
|
164
|
+
if (opts?.isTeacherSchedule)
|
|
165
|
+
continue;
|
|
155
166
|
originalTeacher = teacher;
|
|
156
167
|
teacher = sub.teacher;
|
|
157
168
|
}
|
|
@@ -172,6 +183,7 @@ export function slotsToLessons(slots, date) {
|
|
|
172
183
|
originalRoom,
|
|
173
184
|
originalTeacher,
|
|
174
185
|
transfer: entry.transfer,
|
|
186
|
+
substituteFor: entry.substituteFor,
|
|
175
187
|
possibleChanges: entry.possibleChanges,
|
|
176
188
|
});
|
|
177
189
|
}
|