Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

issue/91/share-timetable-image #92

Merged
merged 11 commits into from
Mar 27, 2024
1,280 changes: 1,134 additions & 146 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,11 @@
"@types/morgan": "^1.9.4",
"@types/passport-jwt": "^3.0.6",
"@types/passport-local": "^1.0.34",
"@types/sharp": "^0.32.0",
"axios": "^1.4.0",
"bcrypt": "^5.1.0",
"cached-prisma": "^1.2.1",
"canvas": "^2.11.2",
"class-transformer": "^0.5.1",
"class-validator": "^0.14.0",
"cookie-parser": "^1.4.6",
Expand Down
2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { TracksModule } from './modules/tracks/tracks.module';
import { UserModule } from './modules/user/user.module';
import { WishlistModule } from './modules/wishlist/wishlist.module';
import { PrismaModule } from './prisma/prisma.module';
import { ShareModule } from './modules/share/share.module';

@Module({
imports: [
Expand All @@ -42,6 +43,7 @@ import { PrismaModule } from './prisma/prisma.module';
DepartmentsModule,
PlannersModule,
TracksModule,
ShareModule,
],
controllers: [AppController],
providers: [
Expand Down
17 changes: 17 additions & 0 deletions src/common/entities/ETimetabls.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { Prisma } from '@prisma/client';

export namespace ETimetable {
export const WithLectureClasstimes =
Prisma.validator<Prisma.timetable_timetable_lecturesArgs>()({
include: {
subject_lecture: {
include: {
subject_classtime: true,
},
},
},
});

export type WithLectureClasstimes =
Prisma.timetable_timetable_lecturesGetPayload<typeof WithLectureClasstimes>;
}
45 changes: 45 additions & 0 deletions src/common/interfaces/ILecture.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Type } from 'class-transformer';
import { IsInt, IsString } from 'class-validator';
import { ITimetable } from './ITimetable';

export namespace ILecture {
export class AutocompleteDto {
Expand All @@ -14,4 +15,48 @@ export namespace ILecture {
@IsString()
keyword!: string;
}
export interface Basic {
id: number;
code: string;
old_code: string;
year: number;
semester: number;
department_id: number;
class_no: string;
title: string;
title_en: string;
type: string;
type_en: string;
audience: number;
credit: number;
title_en_no_space: string;
title_no_space: string;
num_classes: number;
num_labs: number;
credit_au: number;
limit: number;
num_people: number | null; // Allow num_people to be null
is_english: boolean;
deleted: boolean;
course_id: number;
grade_sum: number;
load_sum: number;
speech_sum: number;
grade: number;
load: number;
speech: number;
review_total_weight: number;
class_title: string | null;
class_title_en: string | null;
common_title: string | null;
common_title_en: string | null;
subject_classtime: ITimetable.IClasstime[];
// professor_names: string[] | null; // 교수 이름 목록
// professor_names_en: string[] | null; // 교수 영문 이름 목록
// classroom_str: string | null; // 강의실 문자열
// classroom_str_en: string | null; // 강의실 영문 문자열
// classroom_short: string | null; // 강의실 축약 문자열
// classroom_short_en: string | null; // 강의실 영문 축약 문자열
// Additional properties as needed
}
}
47 changes: 47 additions & 0 deletions src/common/interfaces/IShare.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { CanvasRenderingContext2D } from 'canvas';
import { ILecture } from './ILecture';

export namespace IShare {
export interface RoundedRectangleOptions {
ctx: CanvasRenderingContext2D;
x: number;
y: number;
width: number;
height: number;
radius: number;
color: string;
}

export interface TextOptions {
ctx: CanvasRenderingContext2D;
x: number;
y: number;
text: string;
font: string;
fontSize: number;
color: string;
align?: 'right' | 'left' | 'center'; // Optional parameter
}

export interface DrawTileOptions {
ctx: CanvasRenderingContext2D;
x: number;
y: number;
width: number;
height: number;
title: string;
professor: string;
location: string;
font: string;
fontSize: number;
}

export interface drawTimetableDatas {
lectures: ILecture.Basic[];
timetableType: string;
semesterName: string;
isEnglish: boolean;
semesterFontSize: number;
tileFontSize: number;
}
}
17 changes: 17 additions & 0 deletions src/common/interfaces/ITimetable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
export namespace ITimetable {
export interface IClasstime {
id: number;
day: number;
begin: Date;
end: Date;
type: string;
building_id: string | null;
building_full_name: string | null;
building_full_name_en: string | null;
room_name: string | null;
unit_time: number | null;
lecture_id: number | null;
// Additional properties as needed
}
// Other interfaces as needed...
}
20 changes: 20 additions & 0 deletions src/common/interfaces/dto/share/share.request.dto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Type } from 'class-transformer';
import { IsInt, IsNumber, IsOptional, IsString } from 'class-validator';

Check warning on line 2 in src/common/interfaces/dto/share/share.request.dto.ts

View workflow job for this annotation

GitHub Actions / Lint

'IsInt' is defined but never used

export class TimetableImageQueryDto {
@Type(() => Number)
@IsNumber()
timetable!: number;

@Type(() => Number)
@IsNumber()
year!: number;

@Type(() => Number)
@IsNumber()
semester!: number;

@IsString()
@IsOptional()
language?: string;
}
3 changes: 3 additions & 0 deletions src/common/interfaces/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export * from './IAuth';
export * from './ICourse';
export * from './IFeed';
export * from './ITimetable';
export * from './ILecture';
export * from './IShare';
76 changes: 76 additions & 0 deletions src/modules/lectures/lectures.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ import { toJsonReview } from 'src/common/interfaces/serializer/review.serializer
import { ReviewsRepository } from 'src/prisma/repositories/review.repository';
import { LectureDetails } from '../../common/schemaTypes/types';
import { LectureRepository } from './../../prisma/repositories/lecture.repository';
import { PrismaService } from 'src/prisma/prisma.service';

@Injectable()
export class LecturesService {
constructor(
private LectureRepository: LectureRepository,
private reviewsRepository: ReviewsRepository,
private prisma: PrismaService,
) {}

public async getLectureByFilter(
Expand Down Expand Up @@ -104,4 +106,78 @@ export class LecturesService {
return professor.professor.professor_name_en;
}
}

async getProfessorShortStr(
lectureId: number,
isEnglish: boolean = false,
): Promise<string> {
const lectureWithProfessors =
await this.LectureRepository.getProfessorsByLectureId(lectureId);

if (!lectureWithProfessors) {
throw new Error(`Lecture with ID ${lectureId} not found.`);
}

const profNameList = lectureWithProfessors.subject_lecture_professors.map(
(lp) =>
isEnglish
? lp.professor.professor_name_en
: lp.professor.professor_name,
);

if (profNameList.length <= 2) {
return profNameList.join(', ');
}
return isEnglish
? `${profNameList[0]} and ${profNameList.length - 1} others`
: `${profNameList[0]} 외 ${profNameList.length - 1} 명`;
}

async getClassroomShortStr(
lectureId: number,
isEnglish: boolean = false,
): Promise<string> {
const lectureWithClassTimes =
await this.LectureRepository.getClassroomByLectureId(lectureId);

if (
!lectureWithClassTimes ||
!lectureWithClassTimes.subject_classtime ||
lectureWithClassTimes.subject_classtime.length === 0
) {
throw new Error(`Lecture with ID ${lectureId} not found.`);
}

// 첫 번째 classtime 요소에서 정보를 가져옵니다.
const classtime = lectureWithClassTimes.subject_classtime[0];
const { building_full_name, building_full_name_en, room_name } = classtime;

if (!building_full_name) {
return isEnglish ? 'Unknown' : '정보 없음';
}

let classroomShort = '';
if (building_full_name.startsWith('(')) {
const buildingCode = building_full_name.substring(
1,
building_full_name.indexOf(')'),
);
classroomShort = `(${buildingCode}) ${room_name || ''}`;
} else {
classroomShort = `${building_full_name} ${room_name || ''}`;
}

let classroomShortEn = '';
if (building_full_name_en && building_full_name_en.startsWith('(')) {
const buildingCodeEn = building_full_name_en.substring(
1,
building_full_name_en.indexOf(')'),
);
classroomShortEn = `(${buildingCodeEn}) ${room_name || ''}`;
} else {
classroomShortEn = `${building_full_name_en} ${room_name || ''}`;
}

return isEnglish ? classroomShortEn : classroomShort;
}
}
1 change: 1 addition & 0 deletions src/modules/semesters/semesters.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ import { SemestersService } from './semesters.service';
imports: [PrismaModule],
controllers: [SemestersController],
providers: [SemestersService],
exports: [SemestersService],
})
export class SemestersModule {}
13 changes: 13 additions & 0 deletions src/modules/semesters/semesters.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Injectable } from '@nestjs/common';
import { SemesterRepository } from 'src/prisma/repositories/semester.repository';
import { SemesterQueryDto } from '../../common/interfaces/dto/semester/semester.request.dto';
import { orderFilter } from '../../common/utils/search.utils';
import { subject_semester } from '@prisma/client';

@Injectable()
export class SemestersService {
Expand All @@ -14,4 +15,16 @@ export class SemestersService {
});
return semesters;
}

public getSemesterName(
semester: subject_semester,
language: string = 'kr',
): string {
const seasons = language.includes('en')
? ['spring', 'summer', 'fall', 'winter']
: ['봄', '여름', '가을', '겨울'];

const seasonName = seasons[semester.semester - 1];
return `${semester.year} ${seasonName}`;
}
}
35 changes: 32 additions & 3 deletions src/modules/share/share.controller.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,33 @@
import { Controller } from '@nestjs/common';
import {
Controller,
Get,
HttpException,
HttpStatus,
Query,
Res,
} from '@nestjs/common';
import { session_userprofile } from '@prisma/client';
import { GetUser } from '../../common/decorators/get-user.decorator';
import { ShareService } from './share.service';
import { TimetableRepository } from 'src/prisma/repositories/timetable.repository';
import { Response } from 'express';
import { TimetableImageQueryDto } from 'src/common/interfaces/dto/share/share.request.dto';

@Controller('share')
export class ShareController {}
@Controller('/api/share')
export class ShareController {
constructor(
private readonly shareService: ShareService,
private readonly timetableRepository: TimetableRepository,
) {}

@Get('timetable/image')
async getTimetableImage(
@Query() query: TimetableImageQueryDto,
@GetUser() user: session_userprofile,
@Res() res: Response,
) {
const imageBuffer = await this.shareService.createTimetableImage(query);
res.setHeader('Content-Type', 'image/png');
res.send(imageBuffer);
}
}
5 changes: 5 additions & 0 deletions src/modules/share/share.module.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
import { Module } from '@nestjs/common';
import { ShareController } from './share.controller';
import { ShareService } from './share.service';
import { TimetablesModule } from '../timetables/timetables.module';
import { PrismaModule } from 'src/prisma/prisma.module';
import { SemestersModule } from '../semesters/semesters.module';
import { LecturesModule } from '../lectures/lectures.module';

@Module({
imports: [PrismaModule, TimetablesModule, SemestersModule, LecturesModule],
controllers: [ShareController],
providers: [ShareService],
})
Expand Down
Loading
Loading