chuvsu-js 2.7.0 → 2.8.0
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.d.ts +43 -1
- package/dist/tt/client.js +117 -1
- package/dist/tt/parse.d.ts +5 -1
- package/dist/tt/parse.js +147 -0
- package/dist/tt/types.d.ts +20 -0
- 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, SubstituteForInfo, TransferInfo, TeacherInfo, TtClientOptions, CacheConfig, } from "./tt/types.js";
|
|
10
|
+
export type { Audience, AudienceInfo, Faculty, Group, ScheduleEntry, FullScheduleSlot, FullScheduleDay, LessonTimeSlot, Lesson, LessonTime, SemesterWeek, Substitution, SubstituteForInfo, TransferInfo, TeacherInfo, TtClientOptions, CacheConfig, } from "./tt/types.js";
|
package/dist/tt/client.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { CacheEntry } from "../common/cache.js";
|
|
2
2
|
import { Period } from "../common/types.js";
|
|
3
3
|
import { Schedule } from "./schedule.js";
|
|
4
|
-
import type { Faculty, Group, TeacherInfo, TtClientOptions, CacheConfig } from "./types.js";
|
|
4
|
+
import type { Audience, AudienceInfo, Faculty, Group, TeacherInfo, TtClientOptions, CacheConfig } from "./types.js";
|
|
5
5
|
export declare class TtClient {
|
|
6
6
|
private http;
|
|
7
7
|
private educationType;
|
|
@@ -42,6 +42,48 @@ export declare class TtClient {
|
|
|
42
42
|
searchGroup(opts: {
|
|
43
43
|
name: string;
|
|
44
44
|
}): Promise<Group[]>;
|
|
45
|
+
/**
|
|
46
|
+
* Search audiences by name (substring match). The server requires at
|
|
47
|
+
* least 3 characters in the query.
|
|
48
|
+
*/
|
|
49
|
+
searchAudience(opts: {
|
|
50
|
+
name: string;
|
|
51
|
+
}): Promise<Audience[]>;
|
|
52
|
+
/**
|
|
53
|
+
* Get every audience known to the system in a single request.
|
|
54
|
+
*
|
|
55
|
+
* The site exposes only a search form ("at least 3 characters") and no
|
|
56
|
+
* listing endpoint. However the query is passed to a SQL LIKE, so the
|
|
57
|
+
* 3-character wildcard `%%%` matches every audience at once and returns
|
|
58
|
+
* the full list of (id, name) pairs.
|
|
59
|
+
*/
|
|
60
|
+
getAudiences(): Promise<Audience[]>;
|
|
61
|
+
/**
|
|
62
|
+
* Resolve an audience id from its exact name by searching and
|
|
63
|
+
* selecting the button whose `value` equals the given name.
|
|
64
|
+
*/
|
|
65
|
+
findAudienceByName(opts: {
|
|
66
|
+
name: string;
|
|
67
|
+
}): Promise<Audience | null>;
|
|
68
|
+
/** Fetch the audience's display name from its schedule page. */
|
|
69
|
+
getAudienceName(audienceId: number): Promise<string | null>;
|
|
70
|
+
/**
|
|
71
|
+
* Fetch detailed info about an audience (building, floor, usage,
|
|
72
|
+
* image URLs for the audience photo, building photo and floor plan).
|
|
73
|
+
*/
|
|
74
|
+
getAudienceInfo(audienceId: number): Promise<AudienceInfo | null>;
|
|
75
|
+
private fetchAudienceSchedule;
|
|
76
|
+
getAudienceSchedule(audienceId: number): Promise<Schedule>;
|
|
77
|
+
getAudienceScheduleForPeriod(opts: {
|
|
78
|
+
audienceId: number;
|
|
79
|
+
period: Period;
|
|
80
|
+
}): Promise<Schedule>;
|
|
81
|
+
/** Get the audience photo (audimage). Returns null if missing. */
|
|
82
|
+
getAudienceImage(audienceId: number): Promise<Buffer | null>;
|
|
83
|
+
/** Get the building exterior image (blockimage). Returns null if missing. */
|
|
84
|
+
getAudienceBlockImage(audienceId: number): Promise<Buffer | null>;
|
|
85
|
+
/** Get the floor plan image for the audience. Returns null if missing. */
|
|
86
|
+
getAudienceFloorplan(audienceId: number): Promise<Buffer | null>;
|
|
45
87
|
searchTeacher(opts: {
|
|
46
88
|
name: string;
|
|
47
89
|
}): Promise<{
|
package/dist/tt/client.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { HttpClient } from "../common/http.js";
|
|
2
2
|
import { Cache } from "../common/cache.js";
|
|
3
3
|
import { AuthError } from "../common/types.js";
|
|
4
|
-
import { parseGroupButtons, parseFacultyButtons, parseTeacherButtons, parseFullSchedule, parseTeacherFullSchedule, parseTeacherInfo, } from "./parse.js";
|
|
4
|
+
import { parseAudienceButtons, parseAudienceFullSchedule, parseAudienceInfo, parseAudienceName, parseGroupButtons, parseFacultyButtons, parseTeacherButtons, parseFullSchedule, parseTeacherFullSchedule, parseTeacherInfo, } from "./parse.js";
|
|
5
5
|
import { Schedule } from "./schedule.js";
|
|
6
6
|
const BASE = "https://tt.chuvsu.ru";
|
|
7
7
|
const AUTH_URL = `${BASE}/auth`;
|
|
@@ -167,6 +167,122 @@ export class TtClient {
|
|
|
167
167
|
});
|
|
168
168
|
return parseGroupButtons(body);
|
|
169
169
|
}
|
|
170
|
+
/**
|
|
171
|
+
* Search audiences by name (substring match). The server requires at
|
|
172
|
+
* least 3 characters in the query.
|
|
173
|
+
*/
|
|
174
|
+
async searchAudience(opts) {
|
|
175
|
+
const { body } = await this.authPost(`${BASE}/`, {
|
|
176
|
+
audname: opts.name,
|
|
177
|
+
findaud: "найти",
|
|
178
|
+
hfac: "0",
|
|
179
|
+
pertt: this.pertt,
|
|
180
|
+
});
|
|
181
|
+
return parseAudienceButtons(body);
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Get every audience known to the system in a single request.
|
|
185
|
+
*
|
|
186
|
+
* The site exposes only a search form ("at least 3 characters") and no
|
|
187
|
+
* listing endpoint. However the query is passed to a SQL LIKE, so the
|
|
188
|
+
* 3-character wildcard `%%%` matches every audience at once and returns
|
|
189
|
+
* the full list of (id, name) pairs.
|
|
190
|
+
*/
|
|
191
|
+
async getAudiences() {
|
|
192
|
+
const { body } = await this.authPost(`${BASE}/`, {
|
|
193
|
+
audname: "%%%",
|
|
194
|
+
findaud: "найти",
|
|
195
|
+
hfac: "0",
|
|
196
|
+
pertt: this.pertt,
|
|
197
|
+
});
|
|
198
|
+
return parseAudienceButtons(body);
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Resolve an audience id from its exact name by searching and
|
|
202
|
+
* selecting the button whose `value` equals the given name.
|
|
203
|
+
*/
|
|
204
|
+
async findAudienceByName(opts) {
|
|
205
|
+
const q = opts.name.length >= 3 ? opts.name : "%%%";
|
|
206
|
+
const list = await this.searchAudience({ name: q });
|
|
207
|
+
return list.find((a) => a.name === opts.name) ?? null;
|
|
208
|
+
}
|
|
209
|
+
/** Fetch the audience's display name from its schedule page. */
|
|
210
|
+
async getAudienceName(audienceId) {
|
|
211
|
+
const { body } = await this.authGet(`${BASE}/index/audtt/aud/${audienceId}`);
|
|
212
|
+
return parseAudienceName(body);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Fetch detailed info about an audience (building, floor, usage,
|
|
216
|
+
* image URLs for the audience photo, building photo and floor plan).
|
|
217
|
+
*/
|
|
218
|
+
async getAudienceInfo(audienceId) {
|
|
219
|
+
const cached = this.cache?.get("audienceInfo", String(audienceId));
|
|
220
|
+
if (cached)
|
|
221
|
+
return cached;
|
|
222
|
+
const { body } = await this.authGet(`${BASE}/index/audtt/aud/${audienceId}`);
|
|
223
|
+
const info = parseAudienceInfo(body);
|
|
224
|
+
if (info)
|
|
225
|
+
this.cache?.set("audienceInfo", String(audienceId), info);
|
|
226
|
+
return info;
|
|
227
|
+
}
|
|
228
|
+
async fetchAudienceSchedule(audienceId, period) {
|
|
229
|
+
const cacheKey = `audience:${audienceId}:${period}`;
|
|
230
|
+
const cached = this.cache?.get("schedule", cacheKey);
|
|
231
|
+
if (cached)
|
|
232
|
+
return cached;
|
|
233
|
+
const url = `${BASE}/index/audtt/aud/${audienceId}`;
|
|
234
|
+
const { body } = await this.authPost(url, { htype: String(period) });
|
|
235
|
+
const days = parseAudienceFullSchedule(body);
|
|
236
|
+
this.cache?.set("schedule", cacheKey, days);
|
|
237
|
+
// Cache audience info from the same page to avoid an extra request.
|
|
238
|
+
if (!this.cache?.get("audienceInfo", String(audienceId))) {
|
|
239
|
+
const info = parseAudienceInfo(body);
|
|
240
|
+
if (info)
|
|
241
|
+
this.cache?.set("audienceInfo", String(audienceId), info);
|
|
242
|
+
}
|
|
243
|
+
return days;
|
|
244
|
+
}
|
|
245
|
+
async getAudienceSchedule(audienceId) {
|
|
246
|
+
const schedules = new Map();
|
|
247
|
+
const results = await Promise.all(ALL_PERIODS.map(async (period) => {
|
|
248
|
+
const days = await this.fetchAudienceSchedule(audienceId, period);
|
|
249
|
+
return { period, days };
|
|
250
|
+
}));
|
|
251
|
+
for (const { period, days } of results) {
|
|
252
|
+
schedules.set(period, days);
|
|
253
|
+
}
|
|
254
|
+
return new Schedule(audienceId, schedules, undefined, this.educationType);
|
|
255
|
+
}
|
|
256
|
+
async getAudienceScheduleForPeriod(opts) {
|
|
257
|
+
const days = await this.fetchAudienceSchedule(opts.audienceId, opts.period);
|
|
258
|
+
const schedules = new Map();
|
|
259
|
+
schedules.set(opts.period, days);
|
|
260
|
+
return new Schedule(opts.audienceId, schedules, opts.period, this.educationType);
|
|
261
|
+
}
|
|
262
|
+
/** Get the audience photo (audimage). Returns null if missing. */
|
|
263
|
+
async getAudienceImage(audienceId) {
|
|
264
|
+
const info = await this.getAudienceInfo(audienceId);
|
|
265
|
+
if (!info?.audImageUrl)
|
|
266
|
+
return null;
|
|
267
|
+
const buf = await this.authGetBuffer(`${BASE}${info.audImageUrl}`);
|
|
268
|
+
return buf.length > 0 ? buf : null;
|
|
269
|
+
}
|
|
270
|
+
/** Get the building exterior image (blockimage). Returns null if missing. */
|
|
271
|
+
async getAudienceBlockImage(audienceId) {
|
|
272
|
+
const info = await this.getAudienceInfo(audienceId);
|
|
273
|
+
if (!info?.blockImageUrl)
|
|
274
|
+
return null;
|
|
275
|
+
const buf = await this.authGetBuffer(`${BASE}${info.blockImageUrl}`);
|
|
276
|
+
return buf.length > 0 ? buf : null;
|
|
277
|
+
}
|
|
278
|
+
/** Get the floor plan image for the audience. Returns null if missing. */
|
|
279
|
+
async getAudienceFloorplan(audienceId) {
|
|
280
|
+
const info = await this.getAudienceInfo(audienceId);
|
|
281
|
+
if (!info?.floorplanUrl)
|
|
282
|
+
return null;
|
|
283
|
+
const buf = await this.authGetBuffer(`${BASE}${info.floorplanUrl}`);
|
|
284
|
+
return buf.length > 0 ? buf : null;
|
|
285
|
+
}
|
|
170
286
|
async searchTeacher(opts) {
|
|
171
287
|
const { body } = await this.authPost(`${BASE}/`, {
|
|
172
288
|
techname: opts.name,
|
package/dist/tt/parse.d.ts
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
import { type Period, EducationType } from "../common/types.js";
|
|
2
|
-
import type { Faculty, Group, FullScheduleDay, TeacherInfo } from "./types.js";
|
|
2
|
+
import type { Audience, AudienceInfo, Faculty, Group, FullScheduleDay, TeacherInfo } from "./types.js";
|
|
3
3
|
export declare function parsePeriodFromPage(html: string): Period | null;
|
|
4
4
|
export declare function parseGroupButtons(html: string): Group[];
|
|
5
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;
|
|
6
8
|
export declare function parseTeacherButtons(html: string): {
|
|
7
9
|
id: number;
|
|
8
10
|
name: string;
|
|
9
11
|
}[];
|
|
10
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[];
|
|
11
15
|
export declare function parseTeacherFullSchedule(html: string, educationType?: EducationType): FullScheduleDay[];
|
|
12
16
|
export declare function parseTeacherInfo(html: string): TeacherInfo | null;
|
package/dist/tt/parse.js
CHANGED
|
@@ -40,6 +40,31 @@ export function parseFacultyButtons(html) {
|
|
|
40
40
|
}
|
|
41
41
|
return faculties;
|
|
42
42
|
}
|
|
43
|
+
export function parseAudienceButtons(html) {
|
|
44
|
+
const results = [];
|
|
45
|
+
const seen = new Set();
|
|
46
|
+
const re = /<button[^>]*\bname="aud(\d+)"[^>]*\bvalue="([^"]*)"/g;
|
|
47
|
+
let m;
|
|
48
|
+
while ((m = re.exec(html)) !== null) {
|
|
49
|
+
const id = parseInt(m[1]);
|
|
50
|
+
if (seen.has(id))
|
|
51
|
+
continue;
|
|
52
|
+
seen.add(id);
|
|
53
|
+
results.push({ id, name: m[2] });
|
|
54
|
+
}
|
|
55
|
+
return results;
|
|
56
|
+
}
|
|
57
|
+
export function parseAudienceName(html) {
|
|
58
|
+
const m = html.match(/id="path"[\s\S]*?findaud[^>]*>[^<]*<\/a>([\s\S]*?)<\/div>/);
|
|
59
|
+
if (!m)
|
|
60
|
+
return null;
|
|
61
|
+
const tail = m[1]
|
|
62
|
+
.replace(/ /g, " ")
|
|
63
|
+
.replace(/<[^>]*>/g, "")
|
|
64
|
+
.replace(/^[\s/]+/, "")
|
|
65
|
+
.trim();
|
|
66
|
+
return tail || null;
|
|
67
|
+
}
|
|
43
68
|
export function parseTeacherButtons(html) {
|
|
44
69
|
const doc = parseHtml(html);
|
|
45
70
|
const results = [];
|
|
@@ -344,6 +369,128 @@ function parseSessionEntry(td) {
|
|
|
344
369
|
timeEnd: parseTime(timeMatch[2]),
|
|
345
370
|
};
|
|
346
371
|
}
|
|
372
|
+
// --- Audience schedule & info parsing ---
|
|
373
|
+
export function parseAudienceInfo(html) {
|
|
374
|
+
const doc = parseHtml(html);
|
|
375
|
+
// Name: <span class="htext"><nobr>Аудитория <span style="color: blue;">NAME</span></nobr></span>
|
|
376
|
+
const nameEl = doc.querySelector('.htext span[style*="color: blue"]');
|
|
377
|
+
const name = nameEl ? text(nameEl).trim() : "";
|
|
378
|
+
if (!name)
|
|
379
|
+
return null;
|
|
380
|
+
// Details: <span class="htextb"> (Корпус Б; 3 этаж - Учебная лаборатория)</span>
|
|
381
|
+
const detailsEl = doc.querySelector(".htextb");
|
|
382
|
+
const details = detailsEl ? text(detailsEl).trim() : "";
|
|
383
|
+
let building;
|
|
384
|
+
let floor;
|
|
385
|
+
let usage;
|
|
386
|
+
if (details) {
|
|
387
|
+
const buildingMatch = details.match(/Корпус\s+([^\s;,)]+)/i);
|
|
388
|
+
if (buildingMatch)
|
|
389
|
+
building = buildingMatch[1];
|
|
390
|
+
const floorMatch = details.match(/(\d+)\s*этаж/i);
|
|
391
|
+
if (floorMatch)
|
|
392
|
+
floor = parseInt(floorMatch[1]);
|
|
393
|
+
const usageMatch = details.match(/этаж\s*-\s*([^)]+?)\s*\)?\s*$/i);
|
|
394
|
+
if (usageMatch)
|
|
395
|
+
usage = usageMatch[1].trim();
|
|
396
|
+
}
|
|
397
|
+
const audImg = doc.querySelector("#audsrc");
|
|
398
|
+
const blockImg = doc.querySelector("#blocksrc");
|
|
399
|
+
const floorImg = doc.querySelector("#floorsrc");
|
|
400
|
+
return {
|
|
401
|
+
name,
|
|
402
|
+
building,
|
|
403
|
+
floor,
|
|
404
|
+
usage,
|
|
405
|
+
audImageUrl: audImg?.getAttribute("src") || undefined,
|
|
406
|
+
blockImageUrl: blockImg?.getAttribute("src") || undefined,
|
|
407
|
+
floorplanUrl: floorImg?.getAttribute("src") || undefined,
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
function parseAudienceSemesterEntry(el) {
|
|
411
|
+
const td = el.querySelector("td") ?? el;
|
|
412
|
+
const fullHtml = td.innerHTML ?? "";
|
|
413
|
+
const plainText = text(td);
|
|
414
|
+
if (!plainText)
|
|
415
|
+
return null;
|
|
416
|
+
const possibleChanges = (td.getAttribute("class") ?? "").includes("want") || undefined;
|
|
417
|
+
const redDivs = td.querySelectorAll('div[style*="border: 2px solid red"]');
|
|
418
|
+
for (const div of redDivs) {
|
|
419
|
+
const result = parseTransferDiv(div);
|
|
420
|
+
if (result) {
|
|
421
|
+
if (possibleChanges)
|
|
422
|
+
result.entry.possibleChanges = true;
|
|
423
|
+
return result.entry;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
for (const div of redDivs) {
|
|
427
|
+
const result = parseSubstituteForDiv(div);
|
|
428
|
+
if (result) {
|
|
429
|
+
if (possibleChanges)
|
|
430
|
+
result.entry.possibleChanges = true;
|
|
431
|
+
return result.entry;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
const substitutions = [];
|
|
435
|
+
for (const div of redDivs) {
|
|
436
|
+
const sub = parseSubstitutionDiv(div);
|
|
437
|
+
if (sub)
|
|
438
|
+
substitutions.push(sub);
|
|
439
|
+
}
|
|
440
|
+
let cleanHtml = fullHtml;
|
|
441
|
+
let cleanText = plainText;
|
|
442
|
+
for (const div of redDivs) {
|
|
443
|
+
cleanHtml = cleanHtml.replace(div.outerHTML ?? "", "");
|
|
444
|
+
cleanText = cleanText.replace(text(div), "");
|
|
445
|
+
}
|
|
446
|
+
const subjectEl = td.querySelector('span[style*="color: blue"]');
|
|
447
|
+
const subject = subjectEl ? text(subjectEl) : "";
|
|
448
|
+
if (!subject)
|
|
449
|
+
return null;
|
|
450
|
+
const typeMatch = cleanText.match(/\((лк|пр|лб|зач|экз|зчО|кр|конс)\)/);
|
|
451
|
+
const weeksMatch = cleanText.match(/\(([^)]*нед\.?[^)]*)\)/);
|
|
452
|
+
const subgroupMatch = cleanText.match(/(\d+)\s*подгруппа/);
|
|
453
|
+
const weekParity = parseWeekParity(cleanHtml);
|
|
454
|
+
// Audience entries layout:
|
|
455
|
+
// <span blue>SUBJ</span> (TYPE) (WEEKS) <br>TEACHER<br>GROUPS
|
|
456
|
+
// Teacher is the first line after </span>...<br>, groups is the next line.
|
|
457
|
+
// We split on <br> after the blue subject span.
|
|
458
|
+
const afterSubject = cleanHtml.split(/<\/span>/i).slice(1).join("</span>");
|
|
459
|
+
const parts = afterSubject
|
|
460
|
+
.split(/<br\s*\/?>/i)
|
|
461
|
+
.map((p) => p.replace(/<[^>]*>/g, "").trim())
|
|
462
|
+
.filter((p) => p.length > 0);
|
|
463
|
+
// parts[0] = " (лк) (1 - 16 нед.) " — trailing metadata; drop tokens that
|
|
464
|
+
// look like (type)/(weeks)/(N подгруппа). First real text line = teacher.
|
|
465
|
+
const textLines = [];
|
|
466
|
+
for (const p of parts) {
|
|
467
|
+
const cleaned = p
|
|
468
|
+
.replace(/\((лк|пр|лб|зач|экз|зчО|кр|конс)\)/g, "")
|
|
469
|
+
.replace(/\([^)]*нед\.?[^)]*\)/g, "")
|
|
470
|
+
.replace(/\(\d+\s*подгруппа\)/g, "")
|
|
471
|
+
.trim();
|
|
472
|
+
if (cleaned)
|
|
473
|
+
textLines.push(cleaned);
|
|
474
|
+
}
|
|
475
|
+
const teacherLine = textLines[0] ?? "";
|
|
476
|
+
const groupsLine = textLines.slice(1).join(" ").trim();
|
|
477
|
+
return {
|
|
478
|
+
room: "",
|
|
479
|
+
subject,
|
|
480
|
+
type: typeMatch?.[1] ?? "",
|
|
481
|
+
weeks: parseWeeks(weeksMatch?.[1] ?? ""),
|
|
482
|
+
teacher: parseTeacher(teacherLine),
|
|
483
|
+
groups: groupsLine || undefined,
|
|
484
|
+
subgroup: subgroupMatch ? parseInt(subgroupMatch[1]) : undefined,
|
|
485
|
+
weekParity,
|
|
486
|
+
substitutions: substitutions.length > 0 ? substitutions : undefined,
|
|
487
|
+
possibleChanges,
|
|
488
|
+
};
|
|
489
|
+
}
|
|
490
|
+
export function parseAudienceFullSchedule(html) {
|
|
491
|
+
const doc = parseHtml(html);
|
|
492
|
+
return parseSemesterScheduleWith(doc, parseAudienceSemesterEntry);
|
|
493
|
+
}
|
|
347
494
|
// --- Teacher schedule parsing ---
|
|
348
495
|
export function parseTeacherFullSchedule(html, educationType) {
|
|
349
496
|
const doc = parseHtml(html);
|
package/dist/tt/types.d.ts
CHANGED
|
@@ -9,6 +9,25 @@ export interface Group {
|
|
|
9
9
|
specialty?: string;
|
|
10
10
|
profile?: string;
|
|
11
11
|
}
|
|
12
|
+
export interface Audience {
|
|
13
|
+
id: number;
|
|
14
|
+
name: string;
|
|
15
|
+
}
|
|
16
|
+
export interface AudienceInfo {
|
|
17
|
+
name: string;
|
|
18
|
+
/** Building letter/name, e.g. "Б". */
|
|
19
|
+
building?: string;
|
|
20
|
+
/** Floor number, e.g. 3. */
|
|
21
|
+
floor?: number;
|
|
22
|
+
/** Free-form usage description, e.g. "Учебная лаборатория". */
|
|
23
|
+
usage?: string;
|
|
24
|
+
/** Relative URL of the audience photo (/index/audimage/...). */
|
|
25
|
+
audImageUrl?: string;
|
|
26
|
+
/** Relative URL of the building image (/index/blockimage/...). */
|
|
27
|
+
blockImageUrl?: string;
|
|
28
|
+
/** Relative URL of the floor plan image (/index/floorplan/...). */
|
|
29
|
+
floorplanUrl?: string;
|
|
30
|
+
}
|
|
12
31
|
/** A date-specific substitution (room and/or teacher change). */
|
|
13
32
|
export interface Substitution {
|
|
14
33
|
/** The date this substitution applies to. */
|
|
@@ -120,6 +139,7 @@ export interface CacheConfig {
|
|
|
120
139
|
teachers?: number;
|
|
121
140
|
teacherInfo?: number;
|
|
122
141
|
teacherPhotos?: number;
|
|
142
|
+
audienceInfo?: number;
|
|
123
143
|
}
|
|
124
144
|
export interface TtClientOptions {
|
|
125
145
|
educationType?: EducationType;
|