chuvsu-js 0.3.0 → 1.1.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/README.md +90 -35
- package/dist/browser.d.ts +5 -0
- package/dist/browser.js +2 -0
- package/dist/index.d.ts +3 -2
- package/dist/index.js +2 -1
- package/dist/tt/client.d.ts +4 -27
- package/dist/tt/client.js +17 -107
- package/dist/tt/schedule.d.ts +34 -30
- package/dist/tt/schedule.js +97 -117
- package/dist/tt/types.d.ts +0 -8
- package/dist/tt/utils.d.ts +34 -0
- package/dist/tt/utils.js +124 -0
- package/package.json +5 -1
package/README.md
CHANGED
|
@@ -5,8 +5,6 @@ Node.js библиотека для работы с порталами ЧувГ
|
|
|
5
5
|
- **tt.chuvsu.ru** — расписание занятий (факультеты, группы, преподаватели)
|
|
6
6
|
- **lk.chuvsu.ru** — личный кабинет студента (персональные данные)
|
|
7
7
|
|
|
8
|
-
Пока что очень сырая, много что можно оптимизировать.
|
|
9
|
-
|
|
10
8
|
## Установка
|
|
11
9
|
|
|
12
10
|
```bash
|
|
@@ -27,19 +25,30 @@ await tt.loginAsGuest();
|
|
|
27
25
|
|
|
28
26
|
// Найти группу по названию
|
|
29
27
|
const groups = await tt.searchGroup({ name: "КТ-41-24" });
|
|
30
|
-
console.log(groups); // [{ id:
|
|
28
|
+
console.log(groups); // [{ id: 8919, name: "КТ-41-24", specialty: "...", profile: "..." }]
|
|
31
29
|
|
|
32
|
-
// Получить расписание
|
|
33
|
-
const
|
|
34
|
-
groupId: groups[0].id,
|
|
35
|
-
date: new Date(),
|
|
36
|
-
});
|
|
30
|
+
// Получить расписание группы
|
|
31
|
+
const schedule = await tt.getSchedule({ groupId: groups[0].id });
|
|
37
32
|
|
|
38
|
-
|
|
33
|
+
// Расписание на сегодня
|
|
34
|
+
const today = schedule.today();
|
|
35
|
+
for (const lesson of today) {
|
|
39
36
|
console.log(
|
|
40
37
|
`${lesson.start.hours}:${lesson.start.minutes} — ${lesson.subject} (${lesson.type})`,
|
|
41
38
|
);
|
|
42
39
|
}
|
|
40
|
+
|
|
41
|
+
// С фильтром по подгруппе
|
|
42
|
+
schedule.today({ subgroup: 1 });
|
|
43
|
+
|
|
44
|
+
// На завтра
|
|
45
|
+
schedule.tomorrow();
|
|
46
|
+
|
|
47
|
+
// На текущую неделю
|
|
48
|
+
schedule.thisWeek();
|
|
49
|
+
|
|
50
|
+
// Текущая пара
|
|
51
|
+
schedule.currentLesson();
|
|
43
52
|
```
|
|
44
53
|
|
|
45
54
|
### Личный кабинет (LkClient)
|
|
@@ -84,30 +93,13 @@ await tt.login({ email: "...", password: "..." });
|
|
|
84
93
|
await tt.loginAsGuest();
|
|
85
94
|
```
|
|
86
95
|
|
|
87
|
-
####
|
|
96
|
+
#### Получение расписания
|
|
88
97
|
|
|
89
98
|
```ts
|
|
90
|
-
|
|
91
|
-
const schedule = await tt.getGroupSchedule({ groupId, period? });
|
|
92
|
-
|
|
93
|
-
// Расписание на конкретную дату
|
|
94
|
-
const lessons = await tt.getScheduleForDate({ groupId, date, filter?, period? });
|
|
95
|
-
|
|
96
|
-
// Расписание на день недели (0 = воскресенье, 1 = понедельник, ...)
|
|
97
|
-
const lessons = await tt.getScheduleForDay({ groupId, weekday, filter?, period? });
|
|
98
|
-
|
|
99
|
-
// Расписание на неделю
|
|
100
|
-
const week = await tt.getScheduleForWeek({ groupId, week?, filter?, period? });
|
|
101
|
-
|
|
102
|
-
// Текущая пара
|
|
103
|
-
const lesson = await tt.getCurrentLesson({ groupId, filter? });
|
|
99
|
+
const schedule = await tt.getSchedule({ groupId, period? });
|
|
104
100
|
```
|
|
105
101
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
```ts
|
|
109
|
-
{ subgroup?: number; week?: number }
|
|
110
|
-
```
|
|
102
|
+
Возвращает объект `Schedule`, который позволяет получать расписание локально, без дополнительных запросов к серверу.
|
|
111
103
|
|
|
112
104
|
#### Поиск
|
|
113
105
|
|
|
@@ -125,6 +117,13 @@ const groups = await tt.searchGroup({ name: "ЗИ" });
|
|
|
125
117
|
const teachers = await tt.searchTeacher({ name: "Иванов" });
|
|
126
118
|
```
|
|
127
119
|
|
|
120
|
+
#### Период
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
// Текущий учебный период
|
|
124
|
+
const period = tt.getCurrentPeriod();
|
|
125
|
+
```
|
|
126
|
+
|
|
128
127
|
#### Кеш
|
|
129
128
|
|
|
130
129
|
```ts
|
|
@@ -137,10 +136,71 @@ const data = tt.exportCache();
|
|
|
137
136
|
tt.importCache(data);
|
|
138
137
|
```
|
|
139
138
|
|
|
140
|
-
Категории кеша: `schedule`, `faculties`, `groups
|
|
139
|
+
Категории кеша: `schedule`, `faculties`, `groups`.
|
|
140
|
+
|
|
141
|
+
### Schedule
|
|
142
|
+
|
|
143
|
+
Объект расписания группы. Все методы синхронные — данные уже загружены.
|
|
144
|
+
|
|
145
|
+
#### Свойства
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
schedule.groupId; // ID группы
|
|
149
|
+
schedule.period; // Учебный период
|
|
150
|
+
schedule.days; // Сырые данные (FullScheduleDay[])
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
#### Расписание по дате
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
// На сегодня
|
|
157
|
+
schedule.today({ subgroup?: number });
|
|
158
|
+
|
|
159
|
+
// На завтра
|
|
160
|
+
schedule.tomorrow({ subgroup?: number });
|
|
161
|
+
|
|
162
|
+
// На конкретную дату
|
|
163
|
+
schedule.forDate(date: Date, { subgroup?: number });
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
#### Расписание по неделе
|
|
167
|
+
|
|
168
|
+
```ts
|
|
169
|
+
// На текущую неделю
|
|
170
|
+
schedule.thisWeek({ subgroup?: number });
|
|
171
|
+
|
|
172
|
+
// На конкретную неделю
|
|
173
|
+
schedule.forWeek(week?: number, { subgroup?: number });
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### Расписание по дню недели
|
|
177
|
+
|
|
178
|
+
```ts
|
|
179
|
+
// По дню недели (0 = воскресенье, 1 = понедельник, ...)
|
|
180
|
+
schedule.forDay(weekday: number, { subgroup?: number, week?: number });
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
#### Текущая пара
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
const lesson = schedule.currentLesson({ subgroup?: number });
|
|
187
|
+
```
|
|
141
188
|
|
|
142
189
|
#### Утилиты семестра
|
|
143
190
|
|
|
191
|
+
```ts
|
|
192
|
+
// Номер текущей недели
|
|
193
|
+
schedule.getWeekNumber(date?: Date);
|
|
194
|
+
|
|
195
|
+
// Все недели семестра
|
|
196
|
+
schedule.getSemesterWeeks(weekCount?: number);
|
|
197
|
+
|
|
198
|
+
// Начало семестра
|
|
199
|
+
schedule.getSemesterStart();
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Утилиты также доступны как standalone функции:
|
|
203
|
+
|
|
144
204
|
```ts
|
|
145
205
|
import {
|
|
146
206
|
getSemesterStart,
|
|
@@ -149,13 +209,8 @@ import {
|
|
|
149
209
|
Period,
|
|
150
210
|
} from "chuvsu-js";
|
|
151
211
|
|
|
152
|
-
// Начало семестра
|
|
153
212
|
getSemesterStart({ period: Period.FallSemester, year: 2025 });
|
|
154
|
-
|
|
155
|
-
// Все недели семестра
|
|
156
213
|
getSemesterWeeks({ period: Period.SpringSemester });
|
|
157
|
-
|
|
158
|
-
// Номер текущей недели
|
|
159
214
|
getWeekNumber({ period: Period.SpringSemester });
|
|
160
215
|
```
|
|
161
216
|
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { Schedule } from "./tt/schedule.js";
|
|
2
|
+
export { getSemesterStart, getSemesterWeeks, getWeekNumber, getWeekdayName, } from "./tt/utils.js";
|
|
3
|
+
export { Period, EducationType } from "./common/types.js";
|
|
4
|
+
export type { Time, WeekRange, Teacher, } from "./common/types.js";
|
|
5
|
+
export type { Faculty, Group, ScheduleEntry, FullScheduleSlot, FullScheduleDay, LessonTimeSlot, Lesson, LessonTime, SemesterWeek, } from "./tt/types.js";
|
package/dist/browser.js
ADDED
package/dist/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { LkClient } from "./lk/client.js";
|
|
2
2
|
export { TtClient } from "./tt/client.js";
|
|
3
|
+
export { Schedule } from "./tt/schedule.js";
|
|
3
4
|
export type { CacheEntry } from "./common/cache.js";
|
|
4
|
-
export { getSemesterStart, getSemesterWeeks, getWeekNumber, getWeekdayName,
|
|
5
|
+
export { getSemesterStart, getSemesterWeeks, getWeekNumber, getWeekdayName, } from "./tt/utils.js";
|
|
5
6
|
export { Period, EducationType, AuthError, ParseError } from "./common/types.js";
|
|
6
7
|
export type { Time, WeekRange, Teacher, } from "./common/types.js";
|
|
7
8
|
export type { PersonalData, } from "./lk/types.js";
|
|
8
|
-
export type { Faculty, Group, ScheduleEntry, FullScheduleSlot, FullScheduleDay, LessonTimeSlot, Lesson, LessonTime,
|
|
9
|
+
export type { Faculty, Group, ScheduleEntry, FullScheduleSlot, FullScheduleDay, LessonTimeSlot, Lesson, LessonTime, SemesterWeek, TtClientOptions, CacheConfig, } from "./tt/types.js";
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { LkClient } from "./lk/client.js";
|
|
2
2
|
export { TtClient } from "./tt/client.js";
|
|
3
|
-
export {
|
|
3
|
+
export { Schedule } from "./tt/schedule.js";
|
|
4
|
+
export { getSemesterStart, getSemesterWeeks, getWeekNumber, getWeekdayName, } from "./tt/utils.js";
|
|
4
5
|
export { AuthError, ParseError } from "./common/types.js";
|
package/dist/tt/client.d.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { CacheEntry } from "../common/cache.js";
|
|
2
2
|
import { Period } from "../common/types.js";
|
|
3
|
-
import
|
|
3
|
+
import { Schedule } from "./schedule.js";
|
|
4
|
+
import type { Faculty, Group, TtClientOptions, CacheConfig } from "./types.js";
|
|
4
5
|
export declare class TtClient {
|
|
5
6
|
private http;
|
|
6
7
|
private educationType;
|
|
@@ -20,34 +21,10 @@ export declare class TtClient {
|
|
|
20
21
|
private relogin;
|
|
21
22
|
private authGet;
|
|
22
23
|
private authPost;
|
|
23
|
-
|
|
24
|
+
getSchedule(opts: {
|
|
24
25
|
groupId: number;
|
|
25
26
|
period?: Period;
|
|
26
|
-
}): Promise<
|
|
27
|
-
private getFilteredSlots;
|
|
28
|
-
private getDateForWeekday;
|
|
29
|
-
getScheduleForDay(opts: {
|
|
30
|
-
groupId: number;
|
|
31
|
-
weekday: number;
|
|
32
|
-
filter?: ScheduleFilter;
|
|
33
|
-
period?: Period;
|
|
34
|
-
}): Promise<Lesson[]>;
|
|
35
|
-
getScheduleForDate(opts: {
|
|
36
|
-
groupId: number;
|
|
37
|
-
date: Date;
|
|
38
|
-
subgroup?: number;
|
|
39
|
-
period?: Period;
|
|
40
|
-
}): Promise<Lesson[]>;
|
|
41
|
-
getScheduleForWeek(opts: {
|
|
42
|
-
groupId: number;
|
|
43
|
-
week?: number;
|
|
44
|
-
filter?: ScheduleFilter;
|
|
45
|
-
period?: Period;
|
|
46
|
-
}): Promise<ScheduleWeekDay[]>;
|
|
47
|
-
getCurrentLesson(opts: {
|
|
48
|
-
groupId: number;
|
|
49
|
-
subgroup?: number;
|
|
50
|
-
}): Promise<Lesson | null>;
|
|
27
|
+
}): Promise<Schedule>;
|
|
51
28
|
getCurrentPeriod(opts?: {
|
|
52
29
|
date?: Date;
|
|
53
30
|
}): Period;
|
package/dist/tt/client.js
CHANGED
|
@@ -2,7 +2,7 @@ import { HttpClient } from "../common/http.js";
|
|
|
2
2
|
import { Cache } from "../common/cache.js";
|
|
3
3
|
import { AuthError } from "../common/types.js";
|
|
4
4
|
import { parseGroupButtons, parseFacultyButtons, parseTeacherButtons, parseFullSchedule, } from "./parse.js";
|
|
5
|
-
import {
|
|
5
|
+
import { Schedule } from "./schedule.js";
|
|
6
6
|
const BASE = "https://tt.chuvsu.ru";
|
|
7
7
|
const AUTH_URL = `${BASE}/auth`;
|
|
8
8
|
export class TtClient {
|
|
@@ -91,117 +91,27 @@ export class TtClient {
|
|
|
91
91
|
return res;
|
|
92
92
|
}
|
|
93
93
|
// --- Schedule ---
|
|
94
|
-
async
|
|
95
|
-
const
|
|
94
|
+
async getSchedule(opts) {
|
|
95
|
+
const period = opts.period ?? this.getCurrentPeriod();
|
|
96
|
+
const cacheKey = `${opts.groupId}:${period}`;
|
|
96
97
|
const cached = this.cache?.get("schedule", cacheKey);
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
let body;
|
|
101
|
-
if (opts.period !== undefined) {
|
|
102
|
-
({ body } = await this.authPost(url, { htype: String(opts.period) }));
|
|
98
|
+
let days;
|
|
99
|
+
if (cached) {
|
|
100
|
+
days = cached;
|
|
103
101
|
}
|
|
104
102
|
else {
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const schedule = await this.getGroupSchedule({
|
|
113
|
-
groupId: opts.groupId,
|
|
114
|
-
period: opts.period,
|
|
115
|
-
});
|
|
116
|
-
const dayName = getWeekdayName(opts.weekday);
|
|
117
|
-
const day = schedule.find((d) => d.weekday.toLowerCase() === dayName.toLowerCase());
|
|
118
|
-
if (!day)
|
|
119
|
-
return [];
|
|
120
|
-
return filterSlots(day.slots, opts.filter);
|
|
121
|
-
}
|
|
122
|
-
getDateForWeekday(weekday, period, week) {
|
|
123
|
-
if (week != null) {
|
|
124
|
-
const semesterStart = getSemesterStart({ period });
|
|
125
|
-
const startMonday = getMonday(semesterStart);
|
|
126
|
-
const date = new Date(startMonday);
|
|
127
|
-
date.setDate(startMonday.getDate() +
|
|
128
|
-
(week - 1) * 7 +
|
|
129
|
-
(weekday === 0 ? 6 : weekday - 1));
|
|
130
|
-
return date;
|
|
131
|
-
}
|
|
132
|
-
const now = new Date();
|
|
133
|
-
const currentDay = now.getDay();
|
|
134
|
-
const diff = weekday - currentDay;
|
|
135
|
-
const date = new Date(now);
|
|
136
|
-
date.setDate(now.getDate() + diff);
|
|
137
|
-
date.setHours(0, 0, 0, 0);
|
|
138
|
-
return date;
|
|
139
|
-
}
|
|
140
|
-
async getScheduleForDay(opts) {
|
|
141
|
-
const period = opts.period ?? this.getCurrentPeriod();
|
|
142
|
-
const slots = await this.getFilteredSlots({
|
|
143
|
-
groupId: opts.groupId,
|
|
144
|
-
weekday: opts.weekday,
|
|
145
|
-
filter: opts.filter,
|
|
146
|
-
period,
|
|
147
|
-
});
|
|
148
|
-
const date = this.getDateForWeekday(opts.weekday, period, opts.filter?.week);
|
|
149
|
-
return slotsToLessons(slots, date);
|
|
150
|
-
}
|
|
151
|
-
async getScheduleForDate(opts) {
|
|
152
|
-
const weekday = opts.date.getDay();
|
|
153
|
-
const period = opts.period ?? this.getCurrentPeriod({ date: opts.date });
|
|
154
|
-
const effectiveFilter = { subgroup: opts.subgroup };
|
|
155
|
-
if (effectiveFilter.week == null) {
|
|
156
|
-
effectiveFilter.week = getWeekNumber({ period, date: opts.date });
|
|
157
|
-
}
|
|
158
|
-
const slots = await this.getFilteredSlots({
|
|
159
|
-
groupId: opts.groupId,
|
|
160
|
-
weekday,
|
|
161
|
-
filter: effectiveFilter,
|
|
162
|
-
period,
|
|
163
|
-
});
|
|
164
|
-
return slotsToLessons(slots, opts.date);
|
|
165
|
-
}
|
|
166
|
-
async getScheduleForWeek(opts) {
|
|
167
|
-
const period = opts.period ?? this.getCurrentPeriod();
|
|
168
|
-
const week = opts.week ?? getWeekNumber({ period });
|
|
169
|
-
const effectiveFilter = { ...opts.filter, week };
|
|
170
|
-
const semesterWeeks = getSemesterWeeks({ period, weekCount: week });
|
|
171
|
-
const weekData = semesterWeeks.find((w) => w.week === week);
|
|
172
|
-
const mondayDate = weekData ? weekData.start : new Date();
|
|
173
|
-
const result = [];
|
|
174
|
-
for (let i = 0; i < 7; i++) {
|
|
175
|
-
const date = new Date(mondayDate);
|
|
176
|
-
date.setDate(mondayDate.getDate() + i);
|
|
177
|
-
date.setHours(0, 0, 0, 0);
|
|
178
|
-
const weekday = i === 6 ? 0 : i + 1;
|
|
179
|
-
const slots = await this.getFilteredSlots({
|
|
180
|
-
groupId: opts.groupId,
|
|
181
|
-
weekday,
|
|
182
|
-
filter: effectiveFilter,
|
|
183
|
-
period,
|
|
184
|
-
});
|
|
185
|
-
result.push({ date, lessons: slotsToLessons(slots, date) });
|
|
186
|
-
}
|
|
187
|
-
return result;
|
|
188
|
-
}
|
|
189
|
-
async getCurrentLesson(opts) {
|
|
190
|
-
const now = new Date();
|
|
191
|
-
const lessons = await this.getScheduleForDate({
|
|
192
|
-
groupId: opts.groupId,
|
|
193
|
-
date: now,
|
|
194
|
-
subgroup: opts.subgroup,
|
|
195
|
-
});
|
|
196
|
-
const timeMinutes = now.getHours() * 60 + now.getMinutes();
|
|
197
|
-
for (const lesson of lessons) {
|
|
198
|
-
const start = lesson.start.hours * 60 + lesson.start.minutes;
|
|
199
|
-
const end = lesson.end.hours * 60 + lesson.end.minutes;
|
|
200
|
-
if (timeMinutes >= start && timeMinutes <= end) {
|
|
201
|
-
return lesson;
|
|
103
|
+
const url = `${BASE}/index/grouptt/gr/${opts.groupId}`;
|
|
104
|
+
let body;
|
|
105
|
+
if (opts.period !== undefined) {
|
|
106
|
+
({ body } = await this.authPost(url, { htype: String(opts.period) }));
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
({ body } = await this.authGet(url));
|
|
202
110
|
}
|
|
111
|
+
days = parseFullSchedule(body);
|
|
112
|
+
this.cache?.set("schedule", cacheKey, days);
|
|
203
113
|
}
|
|
204
|
-
return
|
|
114
|
+
return new Schedule(opts.groupId, period, days);
|
|
205
115
|
}
|
|
206
116
|
// --- Period ---
|
|
207
117
|
getCurrentPeriod(opts) {
|
package/dist/tt/schedule.d.ts
CHANGED
|
@@ -1,31 +1,35 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { FullScheduleDay, SemesterWeek, Lesson } from "./types.js";
|
|
2
2
|
import { Period } from "../common/types.js";
|
|
3
|
-
export declare
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
3
|
+
export declare class Schedule {
|
|
4
|
+
readonly groupId: number;
|
|
5
|
+
readonly period: Period;
|
|
6
|
+
readonly days: FullScheduleDay[];
|
|
7
|
+
constructor(groupId: number, period: Period, days: FullScheduleDay[]);
|
|
8
|
+
private getSlotsForWeekday;
|
|
9
|
+
private getDateForWeekday;
|
|
10
|
+
forDay(weekday: number, opts?: {
|
|
11
|
+
subgroup?: number;
|
|
12
|
+
week?: number;
|
|
13
|
+
}): Lesson[];
|
|
14
|
+
forDate(date: Date, opts?: {
|
|
15
|
+
subgroup?: number;
|
|
16
|
+
}): Lesson[];
|
|
17
|
+
forWeek(week?: number, opts?: {
|
|
18
|
+
subgroup?: number;
|
|
19
|
+
}): Lesson[];
|
|
20
|
+
today(opts?: {
|
|
21
|
+
subgroup?: number;
|
|
22
|
+
}): Lesson[];
|
|
23
|
+
tomorrow(opts?: {
|
|
24
|
+
subgroup?: number;
|
|
25
|
+
}): Lesson[];
|
|
26
|
+
thisWeek(opts?: {
|
|
27
|
+
subgroup?: number;
|
|
28
|
+
}): Lesson[];
|
|
29
|
+
currentLesson(opts?: {
|
|
30
|
+
subgroup?: number;
|
|
31
|
+
}): Lesson | null;
|
|
32
|
+
getWeekNumber(date?: Date): number;
|
|
33
|
+
getSemesterWeeks(weekCount?: number): SemesterWeek[];
|
|
34
|
+
getSemesterStart(): Date;
|
|
35
|
+
}
|
package/dist/tt/schedule.js
CHANGED
|
@@ -1,124 +1,104 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
return false;
|
|
27
|
-
}
|
|
28
|
-
if (filter?.week != null) {
|
|
29
|
-
if (e.weeks.from > 0 &&
|
|
30
|
-
(filter.week < e.weeks.from || filter.week > e.weeks.to)) {
|
|
31
|
-
return false;
|
|
32
|
-
}
|
|
33
|
-
if (e.weekParity) {
|
|
34
|
-
const isEven = filter.week % 2 === 0;
|
|
35
|
-
if (e.weekParity === "even" && !isEven)
|
|
36
|
-
return false;
|
|
37
|
-
if (e.weekParity === "odd" && isEven)
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
1
|
+
import { getWeekdayName, getMonday, getSemesterStart, getSemesterWeeks, getWeekNumber, filterSlots, slotsToLessons, } from "./utils.js";
|
|
2
|
+
export class Schedule {
|
|
3
|
+
groupId;
|
|
4
|
+
period;
|
|
5
|
+
days;
|
|
6
|
+
constructor(groupId, period, days) {
|
|
7
|
+
this.groupId = groupId;
|
|
8
|
+
this.period = period;
|
|
9
|
+
this.days = days;
|
|
10
|
+
}
|
|
11
|
+
getSlotsForWeekday(weekday, opts) {
|
|
12
|
+
const dayName = getWeekdayName(weekday);
|
|
13
|
+
const day = this.days.find((d) => d.weekday.toLowerCase() === dayName.toLowerCase());
|
|
14
|
+
if (!day)
|
|
15
|
+
return [];
|
|
16
|
+
return filterSlots(day.slots, opts);
|
|
17
|
+
}
|
|
18
|
+
getDateForWeekday(weekday, week) {
|
|
19
|
+
if (week != null) {
|
|
20
|
+
const startMonday = getMonday(getSemesterStart({ period: this.period }));
|
|
21
|
+
const date = new Date(startMonday);
|
|
22
|
+
date.setDate(startMonday.getDate() +
|
|
23
|
+
(week - 1) * 7 +
|
|
24
|
+
(weekday === 0 ? 6 : weekday - 1));
|
|
25
|
+
return date;
|
|
40
26
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
d.setDate(d.getDate() + diff);
|
|
49
|
-
d.setHours(0, 0, 0, 0);
|
|
50
|
-
return d;
|
|
51
|
-
}
|
|
52
|
-
/**
|
|
53
|
-
* Get the start date of a semester.
|
|
54
|
-
* Fall: September 1 of the given year.
|
|
55
|
-
* Spring: first Monday of February of the given year.
|
|
56
|
-
*/
|
|
57
|
-
export function getSemesterStart(opts) {
|
|
58
|
-
const year = opts.year ?? new Date().getFullYear();
|
|
59
|
-
if (opts.period === 1 /* Period.FallSemester */) {
|
|
60
|
-
return new Date(year, 8, 1); // September 1
|
|
27
|
+
const now = new Date();
|
|
28
|
+
const currentDay = now.getDay();
|
|
29
|
+
const diff = weekday - currentDay;
|
|
30
|
+
const date = new Date(now);
|
|
31
|
+
date.setDate(now.getDate() + diff);
|
|
32
|
+
date.setHours(0, 0, 0, 0);
|
|
33
|
+
return date;
|
|
61
34
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
const firstMonday = new Date(year, 1, 1 + daysToAdd);
|
|
67
|
-
firstMonday.setHours(0, 0, 0, 0);
|
|
68
|
-
return firstMonday;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Get all weeks in a semester with their start/end dates.
|
|
72
|
-
* Week 0 starts from the semester start date.
|
|
73
|
-
*/
|
|
74
|
-
export function getSemesterWeeks(opts) {
|
|
75
|
-
const weekCount = opts.weekCount ?? 17;
|
|
76
|
-
const semesterStart = getSemesterStart(opts);
|
|
77
|
-
const startMonday = getMonday(semesterStart);
|
|
78
|
-
const weeks = [];
|
|
79
|
-
for (let i = 0; i <= weekCount; i++) {
|
|
80
|
-
const start = new Date(startMonday);
|
|
81
|
-
start.setDate(startMonday.getDate() + i * 7);
|
|
82
|
-
const end = new Date(start);
|
|
83
|
-
end.setDate(start.getDate() + 6);
|
|
84
|
-
end.setHours(23, 59, 59, 999);
|
|
85
|
-
weeks.push({ week: i, start, end });
|
|
35
|
+
forDay(weekday, opts) {
|
|
36
|
+
const slots = this.getSlotsForWeekday(weekday, opts);
|
|
37
|
+
const date = this.getDateForWeekday(weekday, opts?.week);
|
|
38
|
+
return slotsToLessons(slots, date);
|
|
86
39
|
}
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
subject: entry.subject,
|
|
114
|
-
type: entry.type,
|
|
115
|
-
room: entry.room,
|
|
116
|
-
teacher: entry.teacher,
|
|
117
|
-
weeks: entry.weeks,
|
|
118
|
-
subgroup: entry.subgroup,
|
|
119
|
-
weekParity: entry.weekParity,
|
|
40
|
+
forDate(date, opts) {
|
|
41
|
+
const weekday = date.getDay();
|
|
42
|
+
const week = getWeekNumber({ period: this.period, date });
|
|
43
|
+
const slots = this.getSlotsForWeekday(weekday, {
|
|
44
|
+
subgroup: opts?.subgroup,
|
|
45
|
+
week,
|
|
46
|
+
});
|
|
47
|
+
return slotsToLessons(slots, date);
|
|
48
|
+
}
|
|
49
|
+
forWeek(week, opts) {
|
|
50
|
+
const effectiveWeek = week ?? getWeekNumber({ period: this.period });
|
|
51
|
+
const semesterWeeks = getSemesterWeeks({
|
|
52
|
+
period: this.period,
|
|
53
|
+
weekCount: effectiveWeek,
|
|
54
|
+
});
|
|
55
|
+
const weekData = semesterWeeks.find((w) => w.week === effectiveWeek);
|
|
56
|
+
const mondayDate = weekData ? weekData.start : new Date();
|
|
57
|
+
const lessons = [];
|
|
58
|
+
for (let i = 0; i < 7; i++) {
|
|
59
|
+
const date = new Date(mondayDate);
|
|
60
|
+
date.setDate(mondayDate.getDate() + i);
|
|
61
|
+
date.setHours(0, 0, 0, 0);
|
|
62
|
+
const weekday = i === 6 ? 0 : i + 1;
|
|
63
|
+
const slots = this.getSlotsForWeekday(weekday, {
|
|
64
|
+
subgroup: opts?.subgroup,
|
|
65
|
+
week: effectiveWeek,
|
|
120
66
|
});
|
|
67
|
+
lessons.push(...slotsToLessons(slots, date));
|
|
121
68
|
}
|
|
69
|
+
return lessons;
|
|
70
|
+
}
|
|
71
|
+
today(opts) {
|
|
72
|
+
return this.forDate(new Date(), opts);
|
|
73
|
+
}
|
|
74
|
+
tomorrow(opts) {
|
|
75
|
+
const date = new Date();
|
|
76
|
+
date.setDate(date.getDate() + 1);
|
|
77
|
+
return this.forDate(date, opts);
|
|
78
|
+
}
|
|
79
|
+
thisWeek(opts) {
|
|
80
|
+
return this.forWeek(undefined, opts);
|
|
81
|
+
}
|
|
82
|
+
currentLesson(opts) {
|
|
83
|
+
const now = new Date();
|
|
84
|
+
const lessons = this.forDate(now, opts);
|
|
85
|
+
const timeMinutes = now.getHours() * 60 + now.getMinutes();
|
|
86
|
+
for (const lesson of lessons) {
|
|
87
|
+
const start = lesson.start.hours * 60 + lesson.start.minutes;
|
|
88
|
+
const end = lesson.end.hours * 60 + lesson.end.minutes;
|
|
89
|
+
if (timeMinutes >= start && timeMinutes <= end) {
|
|
90
|
+
return lesson;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
getWeekNumber(date) {
|
|
96
|
+
return getWeekNumber({ period: this.period, date });
|
|
97
|
+
}
|
|
98
|
+
getSemesterWeeks(weekCount) {
|
|
99
|
+
return getSemesterWeeks({ period: this.period, weekCount });
|
|
100
|
+
}
|
|
101
|
+
getSemesterStart() {
|
|
102
|
+
return getSemesterStart({ period: this.period });
|
|
122
103
|
}
|
|
123
|
-
return lessons;
|
|
124
104
|
}
|
package/dist/tt/types.d.ts
CHANGED
|
@@ -50,14 +50,6 @@ export interface Lesson {
|
|
|
50
50
|
subgroup?: number;
|
|
51
51
|
weekParity?: "even" | "odd";
|
|
52
52
|
}
|
|
53
|
-
export interface ScheduleWeekDay {
|
|
54
|
-
date: Date;
|
|
55
|
-
lessons: Lesson[];
|
|
56
|
-
}
|
|
57
|
-
export interface ScheduleFilter {
|
|
58
|
-
subgroup?: number;
|
|
59
|
-
week?: number;
|
|
60
|
-
}
|
|
61
53
|
export interface SemesterWeek {
|
|
62
54
|
week: number;
|
|
63
55
|
start: Date;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { FullScheduleSlot, SemesterWeek, Lesson } from "./types.js";
|
|
2
|
+
import { Period } from "../common/types.js";
|
|
3
|
+
export declare function getWeekdayName(weekday: number): string;
|
|
4
|
+
export declare function getMonday(date: Date): Date;
|
|
5
|
+
/**
|
|
6
|
+
* Get the start date of a semester.
|
|
7
|
+
* Fall: September 1 of the given year.
|
|
8
|
+
* Spring: first Monday of February of the given year.
|
|
9
|
+
*/
|
|
10
|
+
export declare function getSemesterStart(opts: {
|
|
11
|
+
period: Period;
|
|
12
|
+
year?: number;
|
|
13
|
+
}): Date;
|
|
14
|
+
/**
|
|
15
|
+
* Get all weeks in a semester with their start/end dates.
|
|
16
|
+
* Week 0 starts from the semester start date.
|
|
17
|
+
*/
|
|
18
|
+
export declare function getSemesterWeeks(opts: {
|
|
19
|
+
period: Period;
|
|
20
|
+
year?: number;
|
|
21
|
+
weekCount?: number;
|
|
22
|
+
}): SemesterWeek[];
|
|
23
|
+
/**
|
|
24
|
+
* Get the current week number within a semester.
|
|
25
|
+
*/
|
|
26
|
+
export declare function getWeekNumber(opts: {
|
|
27
|
+
period: Period;
|
|
28
|
+
date?: Date;
|
|
29
|
+
}): number;
|
|
30
|
+
export declare function filterSlots(slots: FullScheduleSlot[], opts?: {
|
|
31
|
+
subgroup?: number;
|
|
32
|
+
week?: number;
|
|
33
|
+
}): FullScheduleSlot[];
|
|
34
|
+
export declare function slotsToLessons(slots: FullScheduleSlot[], date: Date): Lesson[];
|
package/dist/tt/utils.js
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
const WEEKDAY_NAMES = [
|
|
2
|
+
"Воскресенье",
|
|
3
|
+
"Понедельник",
|
|
4
|
+
"Вторник",
|
|
5
|
+
"Среда",
|
|
6
|
+
"Четверг",
|
|
7
|
+
"Пятница",
|
|
8
|
+
"Суббота",
|
|
9
|
+
];
|
|
10
|
+
export function getWeekdayName(weekday) {
|
|
11
|
+
return WEEKDAY_NAMES[weekday] ?? "";
|
|
12
|
+
}
|
|
13
|
+
export function getMonday(date) {
|
|
14
|
+
const d = new Date(date);
|
|
15
|
+
const day = d.getDay();
|
|
16
|
+
const diff = day === 0 ? -6 : 1 - day;
|
|
17
|
+
d.setDate(d.getDate() + diff);
|
|
18
|
+
d.setHours(0, 0, 0, 0);
|
|
19
|
+
return d;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get the start date of a semester.
|
|
23
|
+
* Fall: September 1 of the given year.
|
|
24
|
+
* Spring: first Monday of February of the given year.
|
|
25
|
+
*/
|
|
26
|
+
export function getSemesterStart(opts) {
|
|
27
|
+
const year = opts.year ?? new Date().getFullYear();
|
|
28
|
+
if (opts.period === 1 /* Period.FallSemester */) {
|
|
29
|
+
return new Date(year, 8, 1); // September 1
|
|
30
|
+
}
|
|
31
|
+
// Spring: first Monday of February
|
|
32
|
+
const feb1 = new Date(year, 1, 1);
|
|
33
|
+
const day = feb1.getDay();
|
|
34
|
+
const daysToAdd = day === 1 ? 0 : day === 0 ? 1 : 8 - day;
|
|
35
|
+
const firstMonday = new Date(year, 1, 1 + daysToAdd);
|
|
36
|
+
firstMonday.setHours(0, 0, 0, 0);
|
|
37
|
+
return firstMonday;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Get all weeks in a semester with their start/end dates.
|
|
41
|
+
* Week 0 starts from the semester start date.
|
|
42
|
+
*/
|
|
43
|
+
export function getSemesterWeeks(opts) {
|
|
44
|
+
const weekCount = opts.weekCount ?? 17;
|
|
45
|
+
const semesterStart = getSemesterStart(opts);
|
|
46
|
+
const startMonday = getMonday(semesterStart);
|
|
47
|
+
const weeks = [];
|
|
48
|
+
for (let i = 0; i <= weekCount; i++) {
|
|
49
|
+
const start = new Date(startMonday);
|
|
50
|
+
start.setDate(startMonday.getDate() + i * 7);
|
|
51
|
+
const end = new Date(start);
|
|
52
|
+
end.setDate(start.getDate() + 6);
|
|
53
|
+
end.setHours(23, 59, 59, 999);
|
|
54
|
+
weeks.push({ week: i, start, end });
|
|
55
|
+
}
|
|
56
|
+
return weeks;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Get the current week number within a semester.
|
|
60
|
+
*/
|
|
61
|
+
export function getWeekNumber(opts) {
|
|
62
|
+
const date = opts.date ?? new Date();
|
|
63
|
+
const semesterStart = getSemesterStart(opts);
|
|
64
|
+
const startMonday = getMonday(semesterStart);
|
|
65
|
+
const targetMonday = getMonday(date);
|
|
66
|
+
const diff = targetMonday.getTime() - startMonday.getTime();
|
|
67
|
+
return Math.floor(diff / (7 * 24 * 60 * 60 * 1000));
|
|
68
|
+
}
|
|
69
|
+
function filterEntries(entries, opts) {
|
|
70
|
+
return entries.filter((e) => {
|
|
71
|
+
if (opts?.subgroup && e.subgroup && e.subgroup !== opts.subgroup) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
if (opts?.week != null) {
|
|
75
|
+
if (e.weeks.from > 0 &&
|
|
76
|
+
(opts.week < e.weeks.from || opts.week > e.weeks.to)) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
if (e.weekParity) {
|
|
80
|
+
const isEven = opts.week % 2 === 0;
|
|
81
|
+
if (e.weekParity === "even" && !isEven)
|
|
82
|
+
return false;
|
|
83
|
+
if (e.weekParity === "odd" && isEven)
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return true;
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
export function filterSlots(slots, opts) {
|
|
91
|
+
if (opts?.subgroup == null && opts?.week == null)
|
|
92
|
+
return slots;
|
|
93
|
+
return slots
|
|
94
|
+
.map((slot) => ({
|
|
95
|
+
...slot,
|
|
96
|
+
entries: filterEntries(slot.entries, opts),
|
|
97
|
+
}))
|
|
98
|
+
.filter((slot) => slot.entries.length > 0);
|
|
99
|
+
}
|
|
100
|
+
function makeLessonTime(date, time) {
|
|
101
|
+
const d = new Date(date);
|
|
102
|
+
d.setHours(time.hours, time.minutes, 0, 0);
|
|
103
|
+
return { date: d, hours: time.hours, minutes: time.minutes };
|
|
104
|
+
}
|
|
105
|
+
export function slotsToLessons(slots, date) {
|
|
106
|
+
const lessons = [];
|
|
107
|
+
for (const slot of slots) {
|
|
108
|
+
for (const entry of slot.entries) {
|
|
109
|
+
lessons.push({
|
|
110
|
+
number: slot.number,
|
|
111
|
+
start: makeLessonTime(date, slot.timeStart),
|
|
112
|
+
end: makeLessonTime(date, slot.timeEnd),
|
|
113
|
+
subject: entry.subject,
|
|
114
|
+
type: entry.type,
|
|
115
|
+
room: entry.room,
|
|
116
|
+
teacher: entry.teacher,
|
|
117
|
+
weeks: entry.weeks,
|
|
118
|
+
subgroup: entry.subgroup,
|
|
119
|
+
weekParity: entry.weekParity,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return lessons;
|
|
124
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chuvsu-js",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
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",
|
|
@@ -9,6 +9,10 @@
|
|
|
9
9
|
".": {
|
|
10
10
|
"import": "./dist/index.js",
|
|
11
11
|
"types": "./dist/index.d.ts"
|
|
12
|
+
},
|
|
13
|
+
"./browser": {
|
|
14
|
+
"import": "./dist/browser.js",
|
|
15
|
+
"types": "./dist/browser.d.ts"
|
|
12
16
|
}
|
|
13
17
|
},
|
|
14
18
|
"files": [
|