zdu-student-api 1.1.6 → 1.1.8

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 (47) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +231 -18
  3. package/dist/cabinet/cabinet.d.ts +79 -79
  4. package/dist/cabinet/cabinet.js +162 -162
  5. package/dist/cabinet/data.d.ts +33 -4
  6. package/dist/cabinet/data.js +52 -6
  7. package/dist/cabinet/disciplines.d.ts +19 -19
  8. package/dist/cabinet/disciplines.js +87 -87
  9. package/dist/cabinet/index.d.ts +2 -5
  10. package/dist/cabinet/index.js +2 -5
  11. package/dist/cabinet/parsers.d.ts +22 -3
  12. package/dist/cabinet/parsers.js +118 -0
  13. package/dist/cabinet/scores.d.ts +12 -12
  14. package/dist/cabinet/scores.js +101 -101
  15. package/dist/cabinet/sesId.d.ts +10 -10
  16. package/dist/cabinet/sesId.js +40 -40
  17. package/dist/cabinet/session.d.ts +33 -0
  18. package/dist/cabinet/session.js +99 -0
  19. package/dist/cabinet/types.d.ts +0 -137
  20. package/dist/cabinet/utils.d.ts +6 -6
  21. package/dist/cabinet/utils.js +16 -19
  22. package/dist/cabinet/validSession.d.ts +8 -8
  23. package/dist/cabinet/validSession.js +29 -29
  24. package/dist/cabinetStudent/cabinetStudent.d.ts +87 -0
  25. package/dist/cabinetStudent/cabinetStudent.js +185 -0
  26. package/dist/cabinetStudent/disciplines.d.ts +19 -0
  27. package/dist/cabinetStudent/disciplines.js +86 -0
  28. package/dist/cabinetStudent/index.d.ts +4 -0
  29. package/dist/cabinetStudent/index.js +4 -0
  30. package/dist/cabinetStudent/scores.d.ts +12 -0
  31. package/dist/cabinetStudent/scores.js +103 -0
  32. package/dist/cabinetStudent/types.d.ts +137 -0
  33. package/dist/cabinetStudent/types.js +1 -0
  34. package/dist/cabinetTeacher/academicGroups.d.ts +10 -0
  35. package/dist/cabinetTeacher/academicGroups.js +36 -0
  36. package/dist/cabinetTeacher/cabinetTeacher.d.ts +71 -0
  37. package/dist/cabinetTeacher/cabinetTeacher.js +132 -0
  38. package/dist/cabinetTeacher/index.d.ts +3 -0
  39. package/dist/cabinetTeacher/index.js +3 -0
  40. package/dist/cabinetTeacher/types.d.ts +68 -0
  41. package/dist/cabinetTeacher/types.js +1 -0
  42. package/dist/examples.d.ts +1 -1
  43. package/dist/examples.js +50 -38
  44. package/dist/index.d.ts +2 -0
  45. package/dist/index.js +2 -0
  46. package/dist/types.d.ts +4 -3
  47. package/package.json +35 -34
@@ -1,87 +1,87 @@
1
- import fetch from 'cross-fetch';
2
- import iconv from 'iconv-lite';
3
- import { parseDisciplinesPageN6, parseDisciplinesPageN7 } from './parsers.js';
4
- import { generateCookieString, isLoginPage } from './utils.js';
5
- /**
6
- * Отримати всі дисципліни студента
7
- * @category Cabinet
8
- * @param sesId - ID сесії користувача
9
- * @param sessGUID - GUID сесії з cookie
10
- * @throws {Error} Якщо виникають проблеми з запитом або дані некоректні.
11
- * @returns Масив дисциплін {@link Disciplines}
12
- */
13
- export async function getDisciplines(sesId, sessGUID) {
14
- try {
15
- const result = { ok: false, disciplines: [] };
16
- const cookieString = generateCookieString(sessGUID);
17
- const response1 = await fetch(`https://dekanat.zu.edu.ua/cgi-bin/classman.cgi?n=3&sesID=${sesId}`, { headers: { Cookie: cookieString } });
18
- const buffer1 = await response1.arrayBuffer();
19
- const html1 = iconv.decode(Buffer.from(buffer1), 'windows-1251');
20
- if (isLoginPage(html1))
21
- return result;
22
- let data = extractStrongValue(html1, 'Семестрові бали');
23
- if (data === undefined)
24
- return result;
25
- const response2 = await fetch(`https://dekanat.zu.edu.ua/cgi-bin/${data.slice(2)}`, {
26
- headers: { Cookie: cookieString },
27
- });
28
- const buffer2 = await response2.arrayBuffer();
29
- const html2 = iconv.decode(Buffer.from(buffer2), 'windows-1251');
30
- if (isLoginPage(html2))
31
- return result;
32
- result.disciplines = parseDisciplinesPageN6(html2);
33
- result.ok = true;
34
- return result;
35
- }
36
- catch (e) {
37
- console.error('Error in getDisciplines:', e);
38
- throw e;
39
- }
40
- }
41
- /**
42
- * Витягує значення з HTML після label до кінця li тегу
43
- */
44
- function extractStrongValue(html, label) {
45
- const textIdx = html.indexOf(label);
46
- if (textIdx === -1)
47
- return undefined;
48
- const liStart = html.lastIndexOf('<li', textIdx);
49
- if (liStart === -1)
50
- return undefined;
51
- const liEnd = html.indexOf('</li>', textIdx);
52
- if (liEnd === -1)
53
- return undefined;
54
- const liContent = html.substring(liStart, liEnd);
55
- const hrefMatch = liContent.match(/href\s*=\s*["']([^"']+)["']/i);
56
- if (!hrefMatch)
57
- return undefined;
58
- const href = hrefMatch[1].trim();
59
- return href || undefined;
60
- }
61
- /**
62
- * Отримати поточні дисципліни студента
63
- * @category Cabinet
64
- * @param sesId - ID сесії користувача
65
- * @param sessGUID - GUID сесії з cookie
66
- * @throws {Error} Якщо виникають проблеми з запитом або дані некоректні.
67
- * @returns Масив дисциплін {@link Disciplines}
68
- */
69
- export async function getСurrentDisciplines(sesId, sessGUID) {
70
- try {
71
- const result = { ok: false, disciplines: [] };
72
- const cookieString = generateCookieString(sessGUID);
73
- const response1 = await fetch(`https://dekanat.zu.edu.ua/cgi-bin/classman.cgi?n=7&sesID=${sesId}`, { headers: { Cookie: cookieString } });
74
- const buffer1 = await response1.arrayBuffer();
75
- const html1 = iconv.decode(Buffer.from(buffer1), 'windows-1251');
76
- if (isLoginPage(html1))
77
- return result;
78
- result.disciplines = parseDisciplinesPageN7(html1);
79
- //console.log(html1);
80
- result.ok = true;
81
- return result;
82
- }
83
- catch (e) {
84
- console.error('Error in getСurrentDisciplines:', e);
85
- throw e;
86
- }
87
- }
1
+ import fetch from 'cross-fetch';
2
+ import iconv from 'iconv-lite';
3
+ import { parseDisciplinesPageN6, parseDisciplinesPageN7 } from './parsers.js';
4
+ import { generateCookieString, isLoginPage } from './utils.js';
5
+ /**
6
+ * Отримати всі дисципліни студента
7
+ * @category Cabinet
8
+ * @param sesId - ID сесії користувача
9
+ * @param sessGUID - GUID сесії з cookie
10
+ * @throws {Error} Якщо виникають проблеми з запитом або дані некоректні.
11
+ * @returns Масив дисциплін {@link Disciplines}
12
+ */
13
+ export async function getDisciplines(sesId, sessGUID) {
14
+ try {
15
+ const result = { ok: false, disciplines: [] };
16
+ const cookieString = generateCookieString(sessGUID);
17
+ const response1 = await fetch(`https://dekanat.zu.edu.ua/cgi-bin/classman.cgi?n=3&sesID=${sesId}`, { headers: { Cookie: cookieString } });
18
+ const buffer1 = await response1.arrayBuffer();
19
+ const html1 = iconv.decode(Buffer.from(buffer1), 'windows-1251');
20
+ if (isLoginPage(html1))
21
+ return result;
22
+ let data = extractStrongValue(html1, 'Семестрові бали');
23
+ if (data === undefined)
24
+ return result;
25
+ const response2 = await fetch(`https://dekanat.zu.edu.ua/cgi-bin/${data.slice(2)}`, {
26
+ headers: { Cookie: cookieString },
27
+ });
28
+ const buffer2 = await response2.arrayBuffer();
29
+ const html2 = iconv.decode(Buffer.from(buffer2), 'windows-1251');
30
+ if (isLoginPage(html2))
31
+ return result;
32
+ result.disciplines = parseDisciplinesPageN6(html2);
33
+ result.ok = true;
34
+ return result;
35
+ }
36
+ catch (e) {
37
+ console.error('Error in getDisciplines:', e);
38
+ throw e;
39
+ }
40
+ }
41
+ /**
42
+ * Витягує значення з HTML після label до кінця li тегу
43
+ */
44
+ function extractStrongValue(html, label) {
45
+ const textIdx = html.indexOf(label);
46
+ if (textIdx === -1)
47
+ return undefined;
48
+ const liStart = html.lastIndexOf('<li', textIdx);
49
+ if (liStart === -1)
50
+ return undefined;
51
+ const liEnd = html.indexOf('</li>', textIdx);
52
+ if (liEnd === -1)
53
+ return undefined;
54
+ const liContent = html.substring(liStart, liEnd);
55
+ const hrefMatch = liContent.match(/href\s*=\s*["']([^"']+)["']/i);
56
+ if (!hrefMatch)
57
+ return undefined;
58
+ const href = hrefMatch[1].trim();
59
+ return href || undefined;
60
+ }
61
+ /**
62
+ * Отримати поточні дисципліни студента
63
+ * @category Cabinet
64
+ * @param sesId - ID сесії користувача
65
+ * @param sessGUID - GUID сесії з cookie
66
+ * @throws {Error} Якщо виникають проблеми з запитом або дані некоректні.
67
+ * @returns Масив дисциплін {@link Disciplines}
68
+ */
69
+ export async function getСurrentDisciplines(sesId, sessGUID) {
70
+ try {
71
+ const result = { ok: false, disciplines: [] };
72
+ const cookieString = generateCookieString(sessGUID);
73
+ const response1 = await fetch(`https://dekanat.zu.edu.ua/cgi-bin/classman.cgi?n=7&sesID=${sesId}`, { headers: { Cookie: cookieString } });
74
+ const buffer1 = await response1.arrayBuffer();
75
+ const html1 = iconv.decode(Buffer.from(buffer1), 'windows-1251');
76
+ if (isLoginPage(html1))
77
+ return result;
78
+ result.disciplines = parseDisciplinesPageN7(html1);
79
+ //console.log(html1);
80
+ result.ok = true;
81
+ return result;
82
+ }
83
+ catch (e) {
84
+ console.error('Error in getСurrentDisciplines:', e);
85
+ throw e;
86
+ }
87
+ }
@@ -1,6 +1,3 @@
1
- export * from './sesId.js';
2
1
  export * from './types.js';
3
- export * from './data.js';
4
- export * from './disciplines.js';
5
- export * from './scores.js';
6
- export * from './cabinet.js';
2
+ export * from './session.js';
3
+ export * from './utils.js';
@@ -1,6 +1,3 @@
1
- export * from './sesId.js';
2
1
  export * from './types.js';
3
- export * from './data.js';
4
- export * from './disciplines.js';
5
- export * from './scores.js';
6
- export * from './cabinet.js';
2
+ export * from './session.js';
3
+ export * from './utils.js';
@@ -1,12 +1,13 @@
1
- import { Discipline, Data } from './types.js';
1
+ import { Discipline, DataStudent } from '../cabinetStudent/types.js';
2
+ import { DataTeacher, AcademicGroup } from '../cabinetTeacher/types.js';
2
3
  /**
3
4
  * Парсить дані з сторінки n=31 (анкетні дані)
4
5
  */
5
- export declare function parseDataPageN31(html: string): Partial<Data>;
6
+ export declare function parseDataPageN31(html: string): Partial<DataStudent>;
6
7
  /**
7
8
  * Парсить дані з сторінки n=3 (загальна інформація)
8
9
  */
9
- export declare function parseDataPageN3(html: string): Partial<Data>;
10
+ export declare function parseDataPageN3(html: string): Partial<DataStudent>;
10
11
  /**
11
12
  * Парсить сторінку "Семестрові бали" (n=6) і повертає список дисциплін
12
13
  */
@@ -16,3 +17,21 @@ export declare function parseDisciplinesPageN6(html: string): Discipline[];
16
17
  * @param html - HTML сторінки з select[id="prt"]
17
18
  */
18
19
  export declare function parseDisciplinesPageN7(html: string): Discipline[];
20
+ /**
21
+ * Парсить головну сторінку кабінету викладача
22
+ * @param html - HTML сторінки кабінету викладача
23
+ * @returns Дані викладача
24
+ */
25
+ export declare function parseTeacherData(html: string): DataTeacher;
26
+ /**
27
+ * Парсить сторінку зі списком академічних груп викладача
28
+ * @param html - HTML сторінки "Академічні групи"
29
+ * @returns Список груп викладача
30
+ */
31
+ export declare function parseGroupsPage(html: string, semester: number): AcademicGroup[];
32
+ /**
33
+ * Декодує назву групи з URL-encoded формату
34
+ * @param encodedName - URL-encoded назва групи
35
+ * @returns Декодована назва групи
36
+ */
37
+ export declare function decodeGroupName(encodedName: string): string;
@@ -164,3 +164,121 @@ export function parseDisciplinesPageN7(html) {
164
164
  }
165
165
  return disciplines;
166
166
  }
167
+ /**
168
+ * Парсить головну сторінку кабінету викладача
169
+ * @param html - HTML сторінки кабінету викладача
170
+ * @returns Дані викладача
171
+ */
172
+ export function parseTeacherData(html) {
173
+ const data = { ok: false };
174
+ try {
175
+ const fullName = extractTeacherFullName(html);
176
+ if (fullName) {
177
+ data.fullName = fullName;
178
+ const nameParts = parseFullName(fullName);
179
+ Object.assign(data, nameParts);
180
+ }
181
+ data.department = extractDepartment(html);
182
+ data.partTimeHours = extractHoursValue(html, /за сумісництвом\s*:\s*<strong>[^0-9]*\((\d+)[^)]*\)<\/strong>/);
183
+ data.workDurationMonths = extractNumericValue(html, /Тривалість роботи в навч\. році \(місяців\):\s*<strong>(\d+)<\/strong>/);
184
+ data.totalPositionHours = extractHoursValue(html, /загалом\s*<strong>(\d+)\s*год\.<\/strong>\s*за ставками/);
185
+ data.workloadByStaff = extractHoursValue(html, /за штатом\s*-\s*<strong>(\d+)\s*год\.<\/strong>/);
186
+ const workloadSectionIndex = html.indexOf('Розподілене навчальне навантаження:');
187
+ if (workloadSectionIndex !== -1) {
188
+ const workloadSection = html.substring(workloadSectionIndex);
189
+ data.totalWorkload = extractHoursValue(workloadSection, /загалом\s*<strong>(\d+)\s*год\.<\/strong>/);
190
+ }
191
+ data.ok = true;
192
+ }
193
+ catch (error) {
194
+ data.ok = false;
195
+ }
196
+ return data;
197
+ }
198
+ /**
199
+ * Витягує повне ім'я викладача з h2 тегу
200
+ */
201
+ function extractTeacherFullName(html) {
202
+ const h2Match = html.match(/<h2>Викладач:\s*([^<]+)<\/h2>/);
203
+ if (!h2Match)
204
+ return undefined;
205
+ return h2Match[1].trim();
206
+ }
207
+ /**
208
+ * Витягує назву кафедри з h3 тегу
209
+ */
210
+ function extractDepartment(html) {
211
+ const h3Match = html.match(/<h3>Кафедра:\s*([^<]+)<\/h3>/);
212
+ if (!h3Match)
213
+ return undefined;
214
+ return h3Match[1].trim();
215
+ }
216
+ /**
217
+ * Витягує числове значення годин за регулярним виразом
218
+ */
219
+ function extractHoursValue(html, regex) {
220
+ const match = html.match(regex);
221
+ if (!match)
222
+ return undefined;
223
+ const value = parseInt(match[1], 10);
224
+ return isNaN(value) ? undefined : value;
225
+ }
226
+ /**
227
+ * Витягує числове значення за регулярним виразом
228
+ */
229
+ function extractNumericValue(html, regex) {
230
+ const match = html.match(regex);
231
+ if (!match)
232
+ return undefined;
233
+ const value = parseInt(match[1], 10);
234
+ return isNaN(value) ? undefined : value;
235
+ }
236
+ /**
237
+ * Парсить сторінку зі списком академічних груп викладача
238
+ * @param html - HTML сторінки "Академічні групи"
239
+ * @returns Список груп викладача
240
+ */
241
+ export function parseGroupsPage(html, semester) {
242
+ try {
243
+ const groups = [];
244
+ const rowRegex = /<tr><td><a[^>]+href="\.\/teachers\.cgi\?sesID=[^"]+&n=1&grp=([^"&]+)&teacher=(\d+)"[^>]*>([^<]+)<\/a><\/td><td[^>]*>(\d+)<\/td><td>([^<]+?)<br\s*\/>\s*<em>([^<]+)<\/em><\/td><\/tr>/gi;
245
+ let match;
246
+ while ((match = rowRegex.exec(html)) !== null) {
247
+ const encodedName = match[1].trim();
248
+ const teacherId = match[2].trim();
249
+ const name = match[3].trim();
250
+ const course = parseInt(match[4].trim(), 10);
251
+ const specialty = match[5].trim();
252
+ const faculty = match[6].trim();
253
+ // Формуємо повний URL журналу
254
+ const journalUrl = `./teachers.cgi?sesID={sesID}&n=1&grp=${encodedName}&teacher=${teacherId}`;
255
+ groups.push({
256
+ name,
257
+ semester,
258
+ encodedName,
259
+ course,
260
+ specialty,
261
+ faculty,
262
+ teacherId,
263
+ journalUrl,
264
+ });
265
+ }
266
+ return groups;
267
+ }
268
+ catch {
269
+ return [];
270
+ }
271
+ }
272
+ /**
273
+ * Декодує назву групи з URL-encoded формату
274
+ * @param encodedName - URL-encoded назва групи
275
+ * @returns Декодована назва групи
276
+ */
277
+ export function decodeGroupName(encodedName) {
278
+ try {
279
+ return decodeURIComponent(encodedName);
280
+ }
281
+ catch (error) {
282
+ return encodedName;
283
+ }
284
+ }
@@ -1,12 +1,12 @@
1
- import { Scores } from './types.js';
2
- /**
3
- * Отримати оцінки пвибраного предмета студента
4
- * @category Cabinet
5
- * @param sesId - ID сесії користувача
6
- * @param sessGUID - GUID сесії з cookie
7
- * @param prId - ID дисципліни
8
- * @param semester - Семестр
9
- * @throws {Error} Якщо виникають проблеми з запитом або дані некоректні.
10
- * @returns Масив дисциплін {@link Disciplines}
11
- */
12
- export declare function getScores(sesId: string, sessGUID: string, prId: string, semester: 0 | 1): Promise<Scores>;
1
+ import { Scores } from './types.js';
2
+ /**
3
+ * Отримати оцінки пвибраного предмета студента
4
+ * @category Cabinet
5
+ * @param sesId - ID сесії користувача
6
+ * @param sessGUID - GUID сесії з cookie
7
+ * @param prId - ID дисципліни
8
+ * @param semester - Семестр
9
+ * @throws {Error} Якщо виникають проблеми з запитом або дані некоректні.
10
+ * @returns Масив дисциплін {@link Disciplines}
11
+ */
12
+ export declare function getScores(sesId: string, sessGUID: string, prId: string, semester: 0 | 1): Promise<Scores>;
@@ -1,101 +1,101 @@
1
- import fetch from 'cross-fetch';
2
- import iconv from 'iconv-lite';
3
- import { generateCookieString, isLoginPage } from './utils.js';
4
- /**
5
- * Отримати оцінки пвибраного предмета студента
6
- * @category Cabinet
7
- * @param sesId - ID сесії користувача
8
- * @param sessGUID - GUID сесії з cookie
9
- * @param prId - ID дисципліни
10
- * @param semester - Семестр
11
- * @throws {Error} Якщо виникають проблеми з запитом або дані некоректні.
12
- * @returns Масив дисциплін {@link Disciplines}
13
- */
14
- export async function getScores(sesId, sessGUID, prId, semester) {
15
- try {
16
- const result = {
17
- ok: false,
18
- prId: prId,
19
- studentId: '',
20
- scheduleItem: [],
21
- studentScores: [],
22
- };
23
- const cookieString = generateCookieString(sessGUID);
24
- const formData = `n=7&sesID=${sesId}&teacher=0&irc=0&tid=0&CYKLE=-1&prt=${prId}&hlf=${semester}&grade=0&m=-1`;
25
- const encodedFormData = iconv.encode(formData, 'windows-1251');
26
- const response1 = await fetch(`https://dekanat.zu.edu.ua/cgi-bin/classman.cgi?n=7&sesID=${sesId}`, {
27
- method: 'POST',
28
- body: new Uint8Array(encodedFormData),
29
- redirect: 'manual',
30
- headers: { Cookie: cookieString },
31
- });
32
- const buffer1 = await response1.arrayBuffer();
33
- let html1 = iconv.decode(Buffer.from(buffer1), 'windows-1251');
34
- if (isLoginPage(html1))
35
- return result;
36
- result.scheduleItem = parseSchedule(html1);
37
- result.studentScores = parseScores(html1);
38
- result.studentId = result.studentScores[0].id;
39
- result.studentScores.sort((a, b) => a.id.localeCompare(b.id));
40
- result.ok = true;
41
- return result;
42
- }
43
- catch (e) {
44
- console.error('Error in getQuestionnaireData:', e);
45
- throw e;
46
- }
47
- }
48
- /**
49
- * Витягує список пар з HTML
50
- */
51
- function parseSchedule(html) {
52
- let idx = html.indexOf('<th></th>');
53
- html = idx === -1 ? html : html.slice(idx + '<th></th>'.length);
54
- idx = html.indexOf('<th class="dh">');
55
- html = idx === -1 ? html : html.slice(0, idx);
56
- const regex = /<th[^>]*data-hth="([^"]+)"[^>]*>[\s\S]*?<a[^>]*data-ind="([^"]+)"[^>]*>[^<]+<\/a>[\s\S]*?<br>([^<]+)<\/th>/g;
57
- const result = [];
58
- let match;
59
- while ((match = regex.exec(html)) !== null) {
60
- const [_, dataHth, index, time] = match;
61
- const parts = dataHth.split(',');
62
- const teacher = parts[0].trim();
63
- const dateType = parts[1]?.trim().split(' ') || [];
64
- const date = dateType[0] || '';
65
- const type = dateType[1] || '';
66
- result.push({ teacher, date, type, time: time.trim(), index });
67
- }
68
- return result;
69
- }
70
- /**
71
- * Витягує список оцінок з HTML
72
- */
73
- function parseScores(html) {
74
- const studentRows = html.match(/<tr\s+[^>]*id="s\d+"[\s\S]*?<\/tr>/g) || [];
75
- const result = [];
76
- for (const row of studentRows) {
77
- const idMatch = row.match(/id="(s\d+)"/);
78
- const id = idMatch ? idMatch[1] : '';
79
- const tdMatches = [...row.matchAll(/<td[^>]*data-item="(\d+)"[^>]*>([\s\S]*?)<\/td>/g)];
80
- const scoresMap = new Map();
81
- for (const td of tdMatches) {
82
- const dataItem = Number(td[1]);
83
- const score = td[2].trim() || '';
84
- if (!scoresMap.has(dataItem)) {
85
- scoresMap.set(dataItem, [score]);
86
- }
87
- else {
88
- scoresMap.get(dataItem).push(score);
89
- }
90
- }
91
- const scores = Array.from(scoresMap.values());
92
- scores.shift();
93
- const finalMatch = row.match(/<td[^>]*class="f f1"[^>]*>([\s\S]*?)<\/td>/);
94
- const finalScore = finalMatch ? finalMatch[1].trim() : '';
95
- const absenceMatches = [...row.matchAll(/<td[^>]*class="f f2"[^>]*>([\s\S]*?)<\/td>/g)];
96
- const absences = absenceMatches[0] ? Number(absenceMatches[0][1].trim() || 0) : 0;
97
- const uabsences = absenceMatches[1] ? Number(absenceMatches[1][1].trim() || 0) : 0;
98
- result.push({ id, scores, absences, uabsences, finalScore });
99
- }
100
- return result;
101
- }
1
+ import fetch from 'cross-fetch';
2
+ import iconv from 'iconv-lite';
3
+ import { generateCookieString, isLoginPage } from './utils.js';
4
+ /**
5
+ * Отримати оцінки пвибраного предмета студента
6
+ * @category Cabinet
7
+ * @param sesId - ID сесії користувача
8
+ * @param sessGUID - GUID сесії з cookie
9
+ * @param prId - ID дисципліни
10
+ * @param semester - Семестр
11
+ * @throws {Error} Якщо виникають проблеми з запитом або дані некоректні.
12
+ * @returns Масив дисциплін {@link Disciplines}
13
+ */
14
+ export async function getScores(sesId, sessGUID, prId, semester) {
15
+ try {
16
+ const result = {
17
+ ok: false,
18
+ prId: prId,
19
+ studentId: '',
20
+ scheduleItem: [],
21
+ studentScores: [],
22
+ };
23
+ const cookieString = generateCookieString(sessGUID);
24
+ const formData = `n=7&sesID=${sesId}&teacher=0&irc=0&tid=0&CYKLE=-1&prt=${prId}&hlf=${semester}&grade=0&m=-1`;
25
+ const encodedFormData = iconv.encode(formData, 'windows-1251');
26
+ const response1 = await fetch(`https://dekanat.zu.edu.ua/cgi-bin/classman.cgi?n=7&sesID=${sesId}`, {
27
+ method: 'POST',
28
+ body: new Uint8Array(encodedFormData),
29
+ redirect: 'manual',
30
+ headers: { Cookie: cookieString },
31
+ });
32
+ const buffer1 = await response1.arrayBuffer();
33
+ let html1 = iconv.decode(Buffer.from(buffer1), 'windows-1251');
34
+ if (isLoginPage(html1))
35
+ return result;
36
+ result.scheduleItem = parseSchedule(html1);
37
+ result.studentScores = parseScores(html1);
38
+ result.studentId = result.studentScores[0].id;
39
+ result.studentScores.sort((a, b) => a.id.localeCompare(b.id));
40
+ result.ok = true;
41
+ return result;
42
+ }
43
+ catch (e) {
44
+ console.error('Error in getQuestionnaireData:', e);
45
+ throw e;
46
+ }
47
+ }
48
+ /**
49
+ * Витягує список пар з HTML
50
+ */
51
+ function parseSchedule(html) {
52
+ let idx = html.indexOf('<th></th>');
53
+ html = idx === -1 ? html : html.slice(idx + '<th></th>'.length);
54
+ idx = html.indexOf('<th class="dh">');
55
+ html = idx === -1 ? html : html.slice(0, idx);
56
+ const regex = /<th[^>]*data-hth="([^"]+)"[^>]*>[\s\S]*?<a[^>]*data-ind="([^"]+)"[^>]*>[^<]+<\/a>[\s\S]*?<br>([^<]+)<\/th>/g;
57
+ const result = [];
58
+ let match;
59
+ while ((match = regex.exec(html)) !== null) {
60
+ const [_, dataHth, index, time] = match;
61
+ const parts = dataHth.split(',');
62
+ const teacher = parts[0].trim();
63
+ const dateType = parts[1]?.trim().split(' ') || [];
64
+ const date = dateType[0] || '';
65
+ const type = dateType[1] || '';
66
+ result.push({ teacher, date, type, time: time.trim(), index });
67
+ }
68
+ return result;
69
+ }
70
+ /**
71
+ * Витягує список оцінок з HTML
72
+ */
73
+ function parseScores(html) {
74
+ const studentRows = html.match(/<tr\s+[^>]*id="s\d+"[\s\S]*?<\/tr>/g) || [];
75
+ const result = [];
76
+ for (const row of studentRows) {
77
+ const idMatch = row.match(/id="(s\d+)"/);
78
+ const id = idMatch ? idMatch[1] : '';
79
+ const tdMatches = [...row.matchAll(/<td[^>]*data-item="(\d+)"[^>]*>([\s\S]*?)<\/td>/g)];
80
+ const scoresMap = new Map();
81
+ for (const td of tdMatches) {
82
+ const dataItem = Number(td[1]);
83
+ const score = td[2].trim() || '';
84
+ if (!scoresMap.has(dataItem)) {
85
+ scoresMap.set(dataItem, [score]);
86
+ }
87
+ else {
88
+ scoresMap.get(dataItem).push(score);
89
+ }
90
+ }
91
+ const scores = Array.from(scoresMap.values());
92
+ scores.shift();
93
+ const finalMatch = row.match(/<td[^>]*class="f f1"[^>]*>([\s\S]*?)<\/td>/);
94
+ const finalScore = finalMatch ? finalMatch[1].trim() : '';
95
+ const absenceMatches = [...row.matchAll(/<td[^>]*class="f f2"[^>]*>([\s\S]*?)<\/td>/g)];
96
+ const absences = absenceMatches[0] ? Number(absenceMatches[0][1].trim() || 0) : 0;
97
+ const uabsences = absenceMatches[1] ? Number(absenceMatches[1][1].trim() || 0) : 0;
98
+ result.push({ id, scores, absences, uabsences, finalScore });
99
+ }
100
+ return result;
101
+ }
@@ -1,10 +1,10 @@
1
- import { SessionData } from './types.js';
2
- /**
3
- * Отримати sesID та sessGUID користувача
4
- * @category Cabinet
5
- * @param login - Прізвище користувача
6
- * @param password - Пароль
7
- * @throws {Error} Якщо виникають проблеми з запитом або дані некоректні.
8
- * @returns Об'єкт { sesID, sessGUID }
9
- */
10
- export declare function getSesId(login: string, password: string): Promise<SessionData>;
1
+ import { SessionData } from './types.js';
2
+ /**
3
+ * Отримати sesID та sessGUID користувача
4
+ * @category Cabinet
5
+ * @param login - Прізвище користувача
6
+ * @param password - Пароль
7
+ * @throws {Error} Якщо виникають проблеми з запитом або дані некоректні.
8
+ * @returns Об'єкт { sesID, sessGUID }
9
+ */
10
+ export declare function getSesId(login: string, password: string): Promise<SessionData>;