Skip to content

Commit c3d441d

Browse files
author
sunny chen
committed
created routes for user timetables
1 parent 39e1bdd commit c3d441d

File tree

3 files changed

+273
-3
lines changed

3 files changed

+273
-3
lines changed

server/src/user/types.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,11 @@ export class UserSettings {
1515
unscheduleClassesByDefault: boolean;
1616
hideExamClasses: boolean;
1717
}
18+
19+
export class UserTimetable {
20+
id: string;
21+
name: string;
22+
year: number;
23+
term: string;
24+
primary: boolean;
25+
}

server/src/user/user.controller.ts

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,15 @@
1-
import { Body, Controller, Get, Post, Req, UseGuards } from '@nestjs/common';
1+
import {
2+
Body,
3+
Controller,
4+
Delete,
5+
Get,
6+
Param,
7+
Patch,
8+
Post,
9+
Query,
10+
Req,
11+
UseGuards,
12+
} from '@nestjs/common';
213
import { UserService } from './user.service';
314
import { AuthenticatedGuard } from 'src/auth/authenticated.guard';
415
import { Request } from 'express';
@@ -49,4 +60,84 @@ export class UserController {
4960
await this.userService.setSettings(req.user.id, settings);
5061
return;
5162
}
63+
64+
@Get('timetables/:id')
65+
@UseGuards(AuthenticatedGuard)
66+
async getTimetable(
67+
@Req() req: AuthenticatedRequest,
68+
@Param('id') timetableId: string,
69+
) {
70+
const timetable = await this.userService.getTimetable(
71+
req.user.id,
72+
timetableId,
73+
);
74+
return timetable;
75+
}
76+
77+
@Get('timetables')
78+
@UseGuards(AuthenticatedGuard)
79+
async getUserTimetables(
80+
@Req() req: AuthenticatedRequest,
81+
@Query('year') year: string,
82+
@Query('term') term: string,
83+
) {
84+
const timetables = await this.userService.getUserTimetables(
85+
req.user.id,
86+
Number(year),
87+
term,
88+
);
89+
return timetables;
90+
}
91+
92+
@Post('timetables')
93+
@UseGuards(AuthenticatedGuard)
94+
async createTimetable(
95+
@Req() req: AuthenticatedRequest,
96+
@Body() data: { name: string; year: number; term: string },
97+
) {
98+
const timetable = await this.userService.createTimetable(
99+
req.user!.id,
100+
data,
101+
);
102+
return timetable;
103+
}
104+
105+
@Delete('timetables/:id')
106+
@UseGuards(AuthenticatedGuard)
107+
async deleteTimetable(
108+
@Req() req: AuthenticatedRequest,
109+
@Param('id') timetableId: string,
110+
@Query('year') year: string,
111+
@Query('term') term: string,
112+
) {
113+
await this.userService.deleteTimetable(
114+
req.user!.id,
115+
timetableId,
116+
Number(year),
117+
term,
118+
);
119+
return;
120+
}
121+
122+
@Patch('timetables/:id/rename')
123+
@UseGuards(AuthenticatedGuard)
124+
async renameTimetable(
125+
@Req() req: AuthenticatedRequest,
126+
@Param('id') timetableId: string,
127+
@Body('name') newName: string,
128+
) {
129+
await this.userService.renameTimetable(req.user!.id, timetableId, newName);
130+
return;
131+
}
132+
133+
@Patch('timetables/:id/change-primary')
134+
@UseGuards(AuthenticatedGuard)
135+
async makePrimary(
136+
@Req() req: AuthenticatedRequest,
137+
@Param('id') timetableId: string,
138+
@Body() data: { year: number; term: string },
139+
) {
140+
await this.userService.makePrimary(req.user!.id, timetableId, data);
141+
return;
142+
}
52143
}

server/src/user/user.service.ts

Lines changed: 173 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
1-
import { Injectable } from '@nestjs/common';
1+
import {
2+
ForbiddenException,
3+
Injectable,
4+
NotFoundException,
5+
} from '@nestjs/common';
26
import { PrismaService } from 'src/prisma/prisma.service';
3-
import { UserInfo, UserSettings } from './types';
7+
import { UserInfo, UserSettings, UserTimetable } from './types';
48

59
@Injectable({})
610
export class UserService {
@@ -64,4 +68,171 @@ export class UserService {
6468
},
6569
});
6670
}
71+
72+
async getTimetable(
73+
userId: string,
74+
timetableId: string,
75+
): Promise<UserTimetable> {
76+
const data = await this.prisma.timetable.findFirstOrThrow({
77+
where: {
78+
userId,
79+
id: timetableId,
80+
},
81+
});
82+
83+
return {
84+
id: data.id,
85+
name: data.name,
86+
year: data.year,
87+
term: data.term,
88+
primary: data.primary,
89+
};
90+
}
91+
92+
async getUserTimetables(
93+
userId: string,
94+
year: number,
95+
term: string,
96+
): Promise<UserTimetable[]> {
97+
const timetables = await this.prisma.timetable.findMany({
98+
where: {
99+
userId,
100+
year,
101+
term,
102+
},
103+
select: {
104+
id: true,
105+
name: true,
106+
year: true,
107+
term: true,
108+
primary: true,
109+
},
110+
});
111+
112+
return timetables;
113+
}
114+
115+
// get term timetable
116+
117+
async createTimetable(
118+
userId: string,
119+
data: { name: string; year: number; term: string },
120+
): Promise<string> {
121+
// check if user has existing timetables
122+
const numTimetables = await this.prisma.timetable.count({
123+
where: {
124+
userId,
125+
year: data.year,
126+
term: data.term,
127+
},
128+
});
129+
130+
// add timetable and return id from prisma
131+
const timetable = await this.prisma.timetable.create({
132+
data: {
133+
userId,
134+
name: data.name,
135+
year: data.year,
136+
term: data.term,
137+
primary: numTimetables === 0,
138+
},
139+
select: {
140+
id: true,
141+
},
142+
});
143+
144+
return timetable.id;
145+
}
146+
147+
async deleteTimetable(
148+
userId: string,
149+
timetableId: string,
150+
year: number,
151+
term: string,
152+
): Promise<void> {
153+
const numTimetables = await this.prisma.timetable.count({
154+
where: {
155+
userId,
156+
year,
157+
term,
158+
},
159+
});
160+
161+
if (numTimetables <= 1) {
162+
throw new ForbiddenException('Cannot delete the last timetable.');
163+
}
164+
165+
const timetable = await this.prisma.timetable.findFirst({
166+
where: { id: timetableId, userId },
167+
});
168+
169+
if (!timetable) {
170+
throw new ForbiddenException('Timetable does not belong to this user.');
171+
}
172+
173+
if (timetable.primary) {
174+
throw new ForbiddenException('Cannot delete the primary timetable.');
175+
}
176+
177+
await this.prisma.timetable.delete({
178+
where: {
179+
id: timetableId,
180+
},
181+
});
182+
}
183+
184+
async renameTimetable(
185+
userId: string,
186+
timetableId: string,
187+
newName: string,
188+
): Promise<void> {
189+
await this.prisma.timetable.updateMany({
190+
where: {
191+
userId,
192+
id: timetableId,
193+
},
194+
data: {
195+
name: newName,
196+
},
197+
});
198+
}
199+
200+
async makePrimary(
201+
userId: string,
202+
timetableId: string,
203+
data: { year: number; term: string },
204+
): Promise<void> {
205+
const timetable = await this.prisma.timetable.findFirst({
206+
where: {
207+
id: timetableId,
208+
userId,
209+
},
210+
});
211+
212+
if (!timetable) {
213+
throw new ForbiddenException('Timetable does not belong to this user.');
214+
}
215+
216+
await this.prisma.$transaction([
217+
this.prisma.timetable.updateMany({
218+
where: {
219+
userId,
220+
primary: true,
221+
year: data.year,
222+
term: data.term,
223+
},
224+
data: {
225+
primary: false,
226+
},
227+
}),
228+
this.prisma.timetable.update({
229+
where: {
230+
id: timetableId,
231+
},
232+
data: {
233+
primary: true,
234+
},
235+
}),
236+
]);
237+
}
67238
}

0 commit comments

Comments
 (0)