mirror of
https://github.com/n08i40k/schedule-parser-next.git
synced 2025-12-06 09:47:46 +03:00
Добавлено получение расписания по имени преподавателя.
This commit is contained in:
3881
package-lock.json
generated
3881
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -65,7 +65,7 @@
|
||||
"eslint": "^8.42.0",
|
||||
"eslint-config-prettier": "^9.0.0",
|
||||
"eslint-plugin-prettier": "^5.0.0",
|
||||
"jest": "^29.5.0",
|
||||
"jest": "^30.0.0-alpha.6",
|
||||
"prettier": "^3.0.0",
|
||||
"prisma": "^5.19.1",
|
||||
"source-map-support": "^0.5.21",
|
||||
|
||||
@@ -10,7 +10,7 @@ import { V2LessonDto } from "./v2-lesson.dto";
|
||||
export class V2DayDto {
|
||||
/**
|
||||
* День недели
|
||||
* @example Понедельник
|
||||
* @example "Понедельник"
|
||||
*/
|
||||
@IsString()
|
||||
name: string;
|
||||
|
||||
10
src/schedule/dto/v2/v2-schedule-teacher-names.dto.ts
Normal file
10
src/schedule/dto/v2/v2-schedule-teacher-names.dto.ts
Normal file
@@ -0,0 +1,10 @@
|
||||
import { IsArray } from "class-validator";
|
||||
|
||||
export class V2ScheduleTeacherNamesDto {
|
||||
/**
|
||||
* Группы
|
||||
* @example ["Хомченко Н.Е."]
|
||||
*/
|
||||
@IsArray()
|
||||
names: Array<string>;
|
||||
}
|
||||
15
src/schedule/dto/v2/v2-teacher-day.dto.ts
Normal file
15
src/schedule/dto/v2/v2-teacher-day.dto.ts
Normal file
@@ -0,0 +1,15 @@
|
||||
import { V2DayDto } from "./v2-day.dto";
|
||||
import { IsArray, ValidateNested } from "class-validator";
|
||||
import { Type } from "class-transformer";
|
||||
import { OmitType } from "@nestjs/swagger";
|
||||
import { V2TeacherLessonDto } from "./v2-teacher-lesson.dto";
|
||||
|
||||
export class V2TeacherDayDto extends OmitType(V2DayDto, ["lessons"]) {
|
||||
/**
|
||||
* Занятия
|
||||
*/
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => V2TeacherLessonDto)
|
||||
lessons: Array<V2TeacherLessonDto>;
|
||||
}
|
||||
18
src/schedule/dto/v2/v2-teacher-lesson.dto.ts
Normal file
18
src/schedule/dto/v2/v2-teacher-lesson.dto.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import { V2LessonDto } from "./v2-lesson.dto";
|
||||
import { IsOptional, IsString } from "class-validator";
|
||||
import { NullIf } from "../../../utility/class-validators/conditional-field";
|
||||
import { V2LessonType } from "../../enum/v2-lesson-type.enum";
|
||||
|
||||
export class V2TeacherLessonDto extends V2LessonDto {
|
||||
/**
|
||||
* Название группы
|
||||
* @example "ИС-214/23"
|
||||
* @optional
|
||||
*/
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@NullIf((self: V2TeacherLessonDto) => {
|
||||
return self.type === V2LessonType.BREAK;
|
||||
})
|
||||
group: string | null;
|
||||
}
|
||||
24
src/schedule/dto/v2/v2-teacher-schedule.dto.ts
Normal file
24
src/schedule/dto/v2/v2-teacher-schedule.dto.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { PickType } from "@nestjs/swagger";
|
||||
import { V2ScheduleDto } from "./v2-schedule.dto";
|
||||
import { IsArray, IsObject, ValidateNested } from "class-validator";
|
||||
import { Type } from "class-transformer";
|
||||
import { V2TeacherDto } from "./v2-teacher.dto";
|
||||
|
||||
export class V2TeacherScheduleDto extends PickType(V2ScheduleDto, [
|
||||
"updatedAt",
|
||||
]) {
|
||||
/**
|
||||
* Расписание преподавателя
|
||||
*/
|
||||
@IsObject()
|
||||
teacher: V2TeacherDto;
|
||||
|
||||
/**
|
||||
* Обновлённые дни с последнего изменения расписания
|
||||
* @example [5, 6]
|
||||
*/
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => Number)
|
||||
updated: Array<number>;
|
||||
}
|
||||
20
src/schedule/dto/v2/v2-teacher.dto.ts
Normal file
20
src/schedule/dto/v2/v2-teacher.dto.ts
Normal file
@@ -0,0 +1,20 @@
|
||||
import { IsArray, IsString, ValidateNested } from "class-validator";
|
||||
import { Type } from "class-transformer";
|
||||
import { V2TeacherDayDto } from "./v2-teacher-day.dto";
|
||||
|
||||
export class V2TeacherDto {
|
||||
/**
|
||||
* ФИО преподавателя
|
||||
* @example "Хомченко Н.Е."
|
||||
*/
|
||||
@IsString()
|
||||
name: string;
|
||||
|
||||
/**
|
||||
* Расписание каждого дня
|
||||
*/
|
||||
@IsArray()
|
||||
@ValidateNested({ each: true })
|
||||
@Type(() => V2TeacherDayDto)
|
||||
days: Array<V2TeacherDayDto>;
|
||||
}
|
||||
@@ -13,6 +13,9 @@ import { V2DayDto } from "../../dto/v2/v2-day.dto";
|
||||
import { V2GroupDto } from "../../dto/v2/v2-group.dto";
|
||||
import * as assert from "node:assert";
|
||||
import { ScheduleReplacerService } from "../../schedule-replacer.service";
|
||||
import { V2TeacherDto } from "../../dto/v2/v2-teacher.dto";
|
||||
import { V2TeacherDayDto } from "../../dto/v2/v2-teacher-day.dto";
|
||||
import { V2TeacherLessonDto } from "../../dto/v2/v2-teacher-lesson.dto";
|
||||
|
||||
type InternalId = {
|
||||
/**
|
||||
@@ -80,11 +83,23 @@ export class V2ScheduleParseResult {
|
||||
*/
|
||||
groups: Array<V2GroupDto>;
|
||||
|
||||
/**
|
||||
* Расписание преподавателей в виде списка.
|
||||
* Ключ - ФИО преподавателя
|
||||
*/
|
||||
teachers: Array<V2TeacherDto>;
|
||||
|
||||
/**
|
||||
* Список групп у которых было обновлено расписание с момента последнего обновления файла.
|
||||
* Ключ - название группы.
|
||||
*/
|
||||
updatedGroups: Array<Array<number>>;
|
||||
|
||||
/**
|
||||
* Список преподавателей у которых было обновлено расписание с момента последнего обновления файла.
|
||||
* Ключ - ФИО преподавателя.
|
||||
*/
|
||||
updatedTeachers: Array<Array<number>>;
|
||||
}
|
||||
|
||||
export class V2ScheduleParser {
|
||||
@@ -323,6 +338,76 @@ export class V2ScheduleParser {
|
||||
return this.xlsDownloader;
|
||||
}
|
||||
|
||||
private static convertGroupsToTeachers(
|
||||
groups: Array<V2GroupDto>,
|
||||
): Array<V2TeacherDto> {
|
||||
const result: Array<V2TeacherDto> = [];
|
||||
|
||||
for (const groupName in groups) {
|
||||
const group = groups[groupName];
|
||||
|
||||
for (const day of group.days) {
|
||||
for (const lesson of day.lessons) {
|
||||
if (lesson.type !== V2LessonType.DEFAULT) continue;
|
||||
|
||||
for (const subGroup of lesson.subGroups) {
|
||||
let teacherDto: V2TeacherDto = result[subGroup.teacher];
|
||||
|
||||
if (!teacherDto) {
|
||||
teacherDto = result[subGroup.teacher] =
|
||||
new V2TeacherDto();
|
||||
|
||||
teacherDto.name = subGroup.teacher;
|
||||
teacherDto.days = [];
|
||||
}
|
||||
|
||||
let teacherDay: V2TeacherDayDto =
|
||||
teacherDto.days[day.name];
|
||||
|
||||
if (!teacherDay) {
|
||||
teacherDay = teacherDto.days[day.name] =
|
||||
new V2TeacherDayDto();
|
||||
|
||||
// TODO: Что это блять такое?
|
||||
// noinspection JSConstantReassignment
|
||||
teacherDay.name = day.name;
|
||||
teacherDay.date = day.date;
|
||||
teacherDay.lessons = [];
|
||||
}
|
||||
|
||||
const teacherLesson = structuredClone(
|
||||
lesson,
|
||||
) as V2TeacherLessonDto;
|
||||
teacherLesson.group = groupName;
|
||||
|
||||
teacherDay.lessons.push(teacherLesson);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (const teacherName in result) {
|
||||
const teacher = result[teacherName];
|
||||
|
||||
const days = teacher.days;
|
||||
|
||||
for (const dayName in days) {
|
||||
const day = days[dayName];
|
||||
delete days[dayName];
|
||||
|
||||
day.lessons.sort(
|
||||
(a, b) => a.time.start.valueOf() - b.time.start.valueOf(),
|
||||
);
|
||||
|
||||
days.push(day);
|
||||
}
|
||||
|
||||
days.sort((a, b) => a.date.valueOf() - b.date.valueOf());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает текущее расписание
|
||||
* @returns {V2ScheduleParseResult} - расписание
|
||||
@@ -462,11 +547,6 @@ export class V2ScheduleParser {
|
||||
}
|
||||
|
||||
for (const time of dayTimes) {
|
||||
// if (day.name === "Четверг" && group.name === "ИС-214/23") {
|
||||
// console.log("-------------------");
|
||||
// console.log(groupSkeleton.column);
|
||||
// console.log(time.xlsxRange);
|
||||
// }
|
||||
const lessons = V2ScheduleParser.parseLesson(
|
||||
workSheet,
|
||||
day,
|
||||
@@ -491,17 +571,32 @@ export class V2ScheduleParser {
|
||||
groups,
|
||||
);
|
||||
|
||||
const teachers = V2ScheduleParser.convertGroupsToTeachers(groups);
|
||||
|
||||
const updatedTeachers = V2ScheduleParser.getUpdatedTeachers(
|
||||
this.lastResult?.teachers,
|
||||
teachers,
|
||||
);
|
||||
|
||||
return (this.lastResult = {
|
||||
downloadedAt: headData.requestedAt,
|
||||
uploadedAt: headData.uploadedAt,
|
||||
|
||||
etag: headData.etag,
|
||||
replacerId: replacer?.id,
|
||||
|
||||
groups: groups,
|
||||
teachers: teachers,
|
||||
|
||||
updatedGroups:
|
||||
updatedGroups.length === 0
|
||||
? (this.lastResult?.updatedGroups ?? [])
|
||||
: updatedGroups,
|
||||
|
||||
updatedTeachers:
|
||||
updatedTeachers.length === 0
|
||||
? (this.lastResult?.updatedTeachers ?? [])
|
||||
: updatedTeachers,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -646,9 +741,9 @@ export class V2ScheduleParser {
|
||||
|
||||
const updatedGroups = [];
|
||||
|
||||
for (const groupName in cachedGroups) {
|
||||
const cachedGroup = cachedGroups[groupName];
|
||||
const currentGroup = currentGroups[groupName];
|
||||
for (const name in cachedGroups) {
|
||||
const cachedGroup = cachedGroups[name];
|
||||
const currentGroup = currentGroups[name];
|
||||
|
||||
const affectedGroupDays: Array<number> = [];
|
||||
|
||||
@@ -657,12 +752,40 @@ export class V2ScheduleParser {
|
||||
objectHash.sha1(currentGroup.days[dayIdx]) !==
|
||||
objectHash.sha1(cachedGroup.days[dayIdx])
|
||||
)
|
||||
affectedGroupDays.push(Number.parseInt(dayIdx));
|
||||
affectedGroupDays.push(+dayIdx);
|
||||
}
|
||||
|
||||
updatedGroups[groupName] = affectedGroupDays;
|
||||
updatedGroups[name] = affectedGroupDays;
|
||||
}
|
||||
|
||||
return updatedGroups;
|
||||
}
|
||||
|
||||
private static getUpdatedTeachers(
|
||||
cachedTeachers: Array<V2TeacherDto> | null,
|
||||
currentTeachers: Array<V2TeacherDto>,
|
||||
): Array<Array<number>> {
|
||||
if (!cachedTeachers) return [];
|
||||
|
||||
const updatedTeachers = [];
|
||||
|
||||
for (const name in cachedTeachers) {
|
||||
const cachedTeacher = cachedTeachers[name];
|
||||
const currentTeacher = currentTeachers[name];
|
||||
|
||||
const affectedTeacherDays: Array<number> = [];
|
||||
|
||||
for (const dayIdx in currentTeacher.days) {
|
||||
if (
|
||||
objectHash.sha1(currentTeacher.days[dayIdx]) !==
|
||||
objectHash.sha1(cachedTeacher.days[dayIdx])
|
||||
)
|
||||
affectedTeacherDays.push(+dayIdx);
|
||||
}
|
||||
|
||||
updatedTeachers[name] = affectedTeacherDays;
|
||||
}
|
||||
|
||||
return updatedTeachers;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
Controller,
|
||||
Get,
|
||||
HttpCode,
|
||||
HttpStatus,
|
||||
HttpStatus, Param,
|
||||
Patch,
|
||||
UseGuards,
|
||||
UseInterceptors,
|
||||
@@ -31,6 +31,8 @@ import { V2UpdateDownloadUrlDto } from "./dto/v2/v2-update-download-url.dto";
|
||||
import { V2GroupScheduleByNameDto } from "./dto/v2/v2-group-schedule-by-name.dto";
|
||||
import { V2GroupScheduleDto } from "./dto/v2/v2-group-schedule.dto";
|
||||
import { V2ScheduleGroupNamesDto } from "./dto/v2/v2-schedule-group-names.dto";
|
||||
import { V2TeacherScheduleDto } from "./dto/v2/v2-teacher-schedule.dto";
|
||||
import { V2ScheduleTeacherNamesDto } from "./dto/v2/v2-schedule-teacher-names.dto";
|
||||
|
||||
@ApiTags("v2/schedule")
|
||||
@ApiBearerAuth()
|
||||
@@ -85,10 +87,6 @@ export class V2ScheduleController {
|
||||
description: "Список получен успешно",
|
||||
type: V2ScheduleGroupNamesDto,
|
||||
})
|
||||
@ApiResponse({
|
||||
status: HttpStatus.NOT_FOUND,
|
||||
description: "Требуемая группа не найдена",
|
||||
})
|
||||
@ResultDto(V2ScheduleGroupNamesDto)
|
||||
@CacheKey("v2-schedule-group-names")
|
||||
@UseInterceptors(CacheInterceptor)
|
||||
@@ -99,6 +97,42 @@ export class V2ScheduleController {
|
||||
return await this.scheduleService.getGroupNames();
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "Получение расписания преподавателя" })
|
||||
@ApiBody({ type: V2GroupScheduleByNameDto })
|
||||
@ApiResponse({
|
||||
status: HttpStatus.OK,
|
||||
description: "Расписание получено успешно",
|
||||
type: V2TeacherScheduleDto,
|
||||
})
|
||||
@ApiResponse({
|
||||
status: HttpStatus.NOT_FOUND,
|
||||
description: "Требуемый преподаватель не найден",
|
||||
})
|
||||
@ResultDto(V2TeacherScheduleDto)
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Get("teacher/:name")
|
||||
async getTeacherSchedule(
|
||||
@Param("name") name: string,
|
||||
): Promise<V2TeacherScheduleDto> {
|
||||
return await this.scheduleService.getTeacher(name);
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "Получение списка ФИО преподавателей" })
|
||||
@ApiResponse({
|
||||
status: HttpStatus.OK,
|
||||
description: "Список получен успешно",
|
||||
type: V2ScheduleTeacherNamesDto,
|
||||
})
|
||||
@ResultDto(V2ScheduleTeacherNamesDto)
|
||||
@CacheKey("v2-schedule-teacher-names")
|
||||
@UseInterceptors(CacheInterceptor)
|
||||
@AuthUnauthorized()
|
||||
@HttpCode(HttpStatus.OK)
|
||||
@Get("teacher-names")
|
||||
async getTeacherNames(): Promise<V2ScheduleTeacherNamesDto> {
|
||||
return await this.scheduleService.getTeacherNames();
|
||||
}
|
||||
|
||||
@ApiOperation({ summary: "Обновление основной страницы политехникума" })
|
||||
@ApiResponse({
|
||||
status: HttpStatus.OK,
|
||||
|
||||
@@ -20,6 +20,8 @@ import * as objectHash from "object-hash";
|
||||
import { V2CacheStatusDto } from "./dto/v2/v2-cache-status.dto";
|
||||
import { V2GroupScheduleDto } from "./dto/v2/v2-group-schedule.dto";
|
||||
import { V2ScheduleGroupNamesDto } from "./dto/v2/v2-schedule-group-names.dto";
|
||||
import { V2TeacherScheduleDto } from "./dto/v2/v2-teacher-schedule.dto";
|
||||
import { V2ScheduleTeacherNamesDto } from "./dto/v2/v2-schedule-teacher-names.dto";
|
||||
|
||||
@Injectable()
|
||||
export class V2ScheduleService {
|
||||
@@ -107,14 +109,14 @@ export class V2ScheduleService {
|
||||
return {
|
||||
updatedAt: this.cacheUpdatedAt,
|
||||
groups: sourceSchedule.groups,
|
||||
updatedGroups: sourceSchedule.updatedGroups,
|
||||
updatedGroups: sourceSchedule.updatedGroups ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
async getGroup(group: string): Promise<V2GroupScheduleDto> {
|
||||
async getGroup(name: string): Promise<V2GroupScheduleDto> {
|
||||
const schedule = await this.getSourceSchedule();
|
||||
|
||||
if (schedule.groups[group] === undefined) {
|
||||
if (schedule.groups[name] === undefined) {
|
||||
throw new NotFoundException(
|
||||
"Группы с таким названием не существует!",
|
||||
);
|
||||
@@ -122,8 +124,8 @@ export class V2ScheduleService {
|
||||
|
||||
return {
|
||||
updatedAt: this.cacheUpdatedAt,
|
||||
group: schedule.groups[group],
|
||||
updated: schedule.updatedGroups[group] ?? [],
|
||||
group: schedule.groups[name],
|
||||
updated: schedule.updatedGroups[name] ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
@@ -131,13 +133,40 @@ export class V2ScheduleService {
|
||||
const schedule = await this.getSourceSchedule();
|
||||
const names: Array<string> = [];
|
||||
|
||||
for (const groupName in schedule.groups) names.push(groupName);
|
||||
for (const name in schedule.groups) names.push(name);
|
||||
|
||||
return plainToInstance(V2ScheduleGroupNamesDto, {
|
||||
names: names,
|
||||
});
|
||||
}
|
||||
|
||||
async getTeacher(name: string): Promise<V2TeacherScheduleDto> {
|
||||
const schedule = await this.getSourceSchedule();
|
||||
|
||||
if (schedule.teachers[name] === undefined) {
|
||||
throw new NotFoundException(
|
||||
"Преподавателя с таким ФИО не существует!",
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
updatedAt: this.cacheUpdatedAt,
|
||||
teacher: schedule.teachers[name],
|
||||
updated: schedule.updatedGroups[name] ?? [],
|
||||
};
|
||||
}
|
||||
|
||||
async getTeacherNames(): Promise<V2ScheduleTeacherNamesDto> {
|
||||
const schedule = await this.getSourceSchedule();
|
||||
const names: Array<string> = [];
|
||||
|
||||
for (const name in schedule.teachers) names.push(name);
|
||||
|
||||
return plainToInstance(V2ScheduleTeacherNamesDto, {
|
||||
names: names,
|
||||
});
|
||||
}
|
||||
|
||||
async updateDownloadUrl(
|
||||
url: string,
|
||||
silent: boolean = false,
|
||||
|
||||
Reference in New Issue
Block a user