Spaces:
Sleeping
Sleeping
File size: 7,195 Bytes
84818f7 3058aa3 84818f7 | 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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 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 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 | import {
Controller,
Get,
Post,
Put,
Delete,
Body,
Param,
Query,
Req,
UseGuards,
ParseIntPipe,
HttpCode,
HttpStatus,
} from '@nestjs/common';
import {
ApiTags,
ApiOperation,
ApiParam,
ApiBody,
ApiResponse,
ApiBearerAuth,
} from '@nestjs/swagger';
import { JwtAuthGuard } from '../../auth/guards/jwt-auth.guard';
import { RolesGuard } from '../../auth/guards/roles.guard';
import { Roles } from '../../auth/roles.decorator';
import { RoleName } from '../../auth/entities/role.entity';
import { ExamScheduleService } from '../services';
import {
CreateExamScheduleDto,
UpdateExamScheduleDto,
QueryExamScheduleDto,
} from '../dto';
@ApiTags('π Exam Schedules')
@ApiBearerAuth('JWT-auth')
@Controller('api/exams/schedule')
@UseGuards(JwtAuthGuard, RolesGuard)
export class ExamScheduleController {
constructor(private readonly examService: ExamScheduleService) {}
@Get()
@ApiOperation({
summary: 'List exam schedules',
description: `
## List Exam Schedules
Returns paginated list of exam schedules with optional filtering.
### Access Control
- **Authentication Required**: β
Yes (Bearer Token)
- **Roles**: ALL (filtered by role)
### Role-Based Filtering
- **Students**: See exams for enrolled courses only
- **Instructors**: See exams for courses they teach
- **TAs**: See exams for courses they assist
- **Admins**: See all exam schedules
### Query Parameters
- \`courseId\`: Filter by course
- \`semesterId\`: Filter by semester
- \`examType\`: Filter by type (midterm, final, quiz, makeup)
- \`fromDate\`: Filter from date
- \`toDate\`: Filter until date
`,
})
@ApiResponse({ status: 200, description: 'Paginated list of exam schedules' })
@ApiResponse({ status: 401, description: 'Unauthorized' })
async findAll(@Query() query: QueryExamScheduleDto, @Req() req: any) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
return this.examService.findAll(query, userId, roles);
}
@Get('conflicts')
@Roles(RoleName.ADMIN, RoleName.IT_ADMIN)
@ApiOperation({
summary: 'Check exam conflicts',
description: `
## Check Exam Conflicts
Identifies overlapping exam schedules that could affect students enrolled in multiple courses.
### Access Control
- **Authentication Required**: β
Yes (Bearer Token)
- **Roles**: ADMIN, IT_ADMIN only
### Use Cases
- Identify scheduling conflicts before finalizing exam dates
- Generate conflict reports for academic planning
`,
})
@ApiResponse({ status: 200, description: 'List of conflicting exam schedules' })
@ApiResponse({ status: 401, description: 'Unauthorized' })
@ApiResponse({ status: 403, description: 'Forbidden - Admin role required' })
async checkConflicts(
@Query('courseId') courseId?: number,
@Query('semesterId') semesterId?: number,
) {
return this.examService.checkExamConflicts(courseId, semesterId);
}
@Get(':id')
@ApiOperation({
summary: 'Get exam schedule by ID',
description: `
## Get Exam Details
Returns detailed information about a specific exam schedule.
### Access Control
- **Authentication Required**: β
Yes (Bearer Token)
- **Roles**: ALL
`,
})
@ApiParam({ name: 'id', description: 'Exam schedule ID', type: Number, example: 1 })
@ApiResponse({ status: 200, description: 'Exam schedule details' })
@ApiResponse({ status: 404, description: 'Exam schedule not found' })
async findById(@Param('id', ParseIntPipe) id: number, @Req() req: any) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
return this.examService.findById(id, userId, roles);
}
@Post()
@Roles(RoleName.INSTRUCTOR, RoleName.ADMIN, RoleName.IT_ADMIN)
@HttpCode(HttpStatus.CREATED)
@ApiOperation({
summary: 'Create exam schedule',
description: `
## Create New Exam Schedule
Creates a new exam schedule for a course.
### Access Control
- **Authentication Required**: β
Yes (Bearer Token)
- **Roles**: INSTRUCTOR, ADMIN only
### Conflict Detection
The system automatically checks for:
- Same location/time conflicts
- Overlapping exam times
### Required Fields
- \`courseId\`: Course ID
- \`semesterId\`: Semester ID
- \`examType\`: Type of exam
- \`examDate\`: Date of exam
- \`startTime\`: Start time
- \`durationMinutes\`: Duration in minutes
`,
})
@ApiBody({ type: CreateExamScheduleDto })
@ApiResponse({ status: 201, description: 'Exam schedule created successfully' })
@ApiResponse({ status: 400, description: 'Invalid input data' })
@ApiResponse({ status: 403, description: 'Forbidden - Instructor/Admin role required' })
@ApiResponse({ status: 409, description: 'Exam schedule conflict' })
async create(@Body() dto: CreateExamScheduleDto, @Req() req: any) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
return this.examService.create(dto, userId, roles);
}
@Put(':id')
@Roles(RoleName.INSTRUCTOR, RoleName.ADMIN, RoleName.IT_ADMIN)
@ApiOperation({
summary: 'Update exam schedule',
description: `
## Update Exam Schedule
Updates an existing exam schedule.
### Access Control
- **Authentication Required**: β
Yes (Bearer Token)
- **Roles**: INSTRUCTOR (own courses), ADMIN
### Conflict Detection
If date/time changes, conflict detection is re-run.
`,
})
@ApiParam({ name: 'id', description: 'Exam schedule ID', type: Number, example: 1 })
@ApiBody({ type: UpdateExamScheduleDto })
@ApiResponse({ status: 200, description: 'Exam schedule updated successfully' })
@ApiResponse({ status: 404, description: 'Exam schedule not found' })
@ApiResponse({ status: 403, description: 'Forbidden' })
@ApiResponse({ status: 409, description: 'Exam schedule conflict' })
async update(
@Param('id', ParseIntPipe) id: number,
@Body() dto: UpdateExamScheduleDto,
@Req() req: any,
) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
return this.examService.update(id, dto, userId, roles);
}
@Delete(':id')
@Roles(RoleName.INSTRUCTOR, RoleName.ADMIN, RoleName.IT_ADMIN)
@HttpCode(HttpStatus.OK)
@ApiOperation({
summary: 'Delete exam schedule',
description: `
## Delete Exam Schedule
Removes an exam schedule from the system.
### Access Control
- **Authentication Required**: β
Yes (Bearer Token)
- **Roles**: INSTRUCTOR (own courses), ADMIN
`,
})
@ApiParam({ name: 'id', description: 'Exam schedule ID', type: Number, example: 1 })
@ApiResponse({ status: 200, description: 'Exam schedule deleted successfully' })
@ApiResponse({ status: 404, description: 'Exam schedule not found' })
@ApiResponse({ status: 403, description: 'Forbidden' })
async delete(@Param('id', ParseIntPipe) id: number, @Req() req: any) {
const userId = req.user.userId || req.user.id;
const roles = this.extractRoles(req.user);
return this.examService.delete(id, userId, roles);
}
private extractRoles(user: any): string[] {
if (Array.isArray(user.roles)) {
return user.roles.map((r: any) => (typeof r === 'string' ? r : r.name || r.roleName));
}
return [];
}
}
|