Spaces:
Sleeping
Sleeping
| import { | |
| Body, | |
| Controller, | |
| Delete, | |
| Get, | |
| Param, | |
| ParseIntPipe, | |
| Patch, | |
| Post, | |
| Query, | |
| Req, | |
| UploadedFile, | |
| UseGuards, | |
| UseInterceptors, | |
| } from '@nestjs/common'; | |
| import { FileInterceptor } from '@nestjs/platform-express'; | |
| import { ApiBearerAuth, ApiTags } from '@nestjs/swagger'; | |
| import { RoleName } from '../auth/entities/role.entity'; | |
| import { JwtAuthGuard } from '../auth/guards/jwt-auth.guard'; | |
| import { RolesGuard } from '../auth/guards/roles.guard'; | |
| import { Roles } from '../auth/roles.decorator'; | |
| import { | |
| AddDraftItemDto, | |
| DuplicateDraftDto, | |
| ReorderDraftItemsDto, | |
| ReplacementCheckDto, | |
| } from './dto/exam-draft-item.dto'; | |
| import { ExportExamDto } from './dto/exam-export.dto'; | |
| import { | |
| ApplyExamPaperTemplateDto, | |
| SaveExamPaperTemplateDto, | |
| } from './dto/exam-paper-template.dto'; | |
| import { | |
| BatchDeleteExamRecordsDto, | |
| BatchSaveExamDraftsDto, | |
| } from './dto/exam-batch.dto'; | |
| import { | |
| ArchiveExamDto, | |
| PublishExamDto, | |
| UnpublishExamDto, | |
| } from './dto/exam-lifecycle.dto'; | |
| import { | |
| CreateExamSectionDto, | |
| NormalizeSectionMarksDto, | |
| ReorderExamSectionsDto, | |
| UpdateExamSectionDto, | |
| } from './dto/exam-section.dto'; | |
| import { ExamDraftListQueryDto, ExamListQueryDto } from './dto/exam-query.dto'; | |
| import { ExamResponseDto } from './dto/exam-response.dto'; | |
| import { | |
| GenerateExamPreviewDto, | |
| UpdateDraftItemDto, | |
| } from './dto/generate-exam.dto'; | |
| import { ExamDraftItem } from './entities/exam-draft-item.entity'; | |
| import { ExamDraftSection } from './entities/exam-draft-section.entity'; | |
| import { ExamsService } from './exams.service'; | |
| type AuthenticatedRequest = { | |
| user: { | |
| userId: number; | |
| }; | |
| }; | |
| ('Exams') | |
| ('JWT-auth') | |
| (JwtAuthGuard, RolesGuard) | |
| ('api/exams') | |
| export class ExamsController { | |
| constructor(private readonly examsService: ExamsService) {} | |
| () | |
| (RoleName.INSTRUCTOR) | |
| getExams(() req: AuthenticatedRequest, () query: ExamListQueryDto) { | |
| return this.examsService.findExams( | |
| req.user.userId, | |
| query.page, | |
| query.limit, | |
| query, | |
| ); | |
| } | |
| ('list') | |
| (RoleName.INSTRUCTOR) | |
| listExams( | |
| () req: AuthenticatedRequest, | |
| () query: ExamListQueryDto, | |
| ) { | |
| return this.examsService.findExams( | |
| req.user.userId, | |
| query.page, | |
| query.limit, | |
| query, | |
| ); | |
| } | |
| ('drafts') | |
| (RoleName.INSTRUCTOR) | |
| getDrafts( | |
| () req: AuthenticatedRequest, | |
| () query: ExamDraftListQueryDto, | |
| ) { | |
| return this.examsService.findDrafts( | |
| req.user.userId, | |
| query.page, | |
| query.limit, | |
| query, | |
| ); | |
| } | |
| ('drafts/list') | |
| (RoleName.INSTRUCTOR) | |
| listDrafts( | |
| () req: AuthenticatedRequest, | |
| () query: ExamDraftListQueryDto, | |
| ) { | |
| return this.examsService.findDrafts( | |
| req.user.userId, | |
| query.page, | |
| query.limit, | |
| query, | |
| ); | |
| } | |
| ('drafts/:draftId') | |
| (RoleName.INSTRUCTOR) | |
| getDraftById( | |
| ('draftId', ParseIntPipe) draftId: number, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.findDraftById(draftId, req.user.userId); | |
| } | |
| ('drafts/:draftId') | |
| (RoleName.INSTRUCTOR) | |
| async deleteDraft( | |
| ('draftId', ParseIntPipe) draftId: number, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| await this.examsService.deleteDraft(draftId, req.user.userId); | |
| return { message: 'Draft deleted successfully', deletedDraftId: draftId }; | |
| } | |
| ('drafts/save/batch') | |
| (RoleName.INSTRUCTOR) | |
| async batchSaveDrafts( | |
| () dto: BatchSaveExamDraftsDto, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.batchSaveDrafts(dto.draftIds, req.user.userId); | |
| } | |
| ('records/delete/batch') | |
| (RoleName.INSTRUCTOR) | |
| async batchDeleteRecords( | |
| () dto: BatchDeleteExamRecordsDto, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.batchDeleteRecords(dto, req.user.userId); | |
| } | |
| ('stats') | |
| (RoleName.INSTRUCTOR) | |
| getStats( | |
| () req: AuthenticatedRequest, | |
| ('courseId') courseId?: string, | |
| ) { | |
| return this.examsService.getExamStats( | |
| req.user.userId, | |
| courseId ? Number(courseId) : undefined, | |
| ); | |
| } | |
| ('generation-readiness') | |
| (RoleName.INSTRUCTOR) | |
| getGenerationReadiness( | |
| () req: AuthenticatedRequest, | |
| ('courseId', ParseIntPipe) courseId: number, | |
| ) { | |
| return this.examsService.getGenerationReadiness(req.user.userId, courseId); | |
| } | |
| ('generate-preview') | |
| (RoleName.INSTRUCTOR) | |
| generatePreview( | |
| () dto: GenerateExamPreviewDto, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.generatePreview(dto, req.user.userId); | |
| } | |
| ('generation-availability') | |
| (RoleName.INSTRUCTOR) | |
| generationAvailability( | |
| () dto: GenerateExamPreviewDto, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.checkGenerationAvailability(dto, req.user.userId); | |
| } | |
| ('paper-templates') | |
| (RoleName.INSTRUCTOR) | |
| listPaperTemplates( | |
| () req: AuthenticatedRequest, | |
| ('courseId') courseId?: string, | |
| ) { | |
| return this.examsService.listPaperTemplates( | |
| req.user.userId, | |
| courseId ? Number(courseId) : undefined, | |
| ); | |
| } | |
| ('paper-templates') | |
| (RoleName.INSTRUCTOR) | |
| createPaperTemplate( | |
| () dto: SaveExamPaperTemplateDto, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.createPaperTemplate(req.user.userId, dto); | |
| } | |
| ('paper-templates/:templateId') | |
| (RoleName.INSTRUCTOR) | |
| updatePaperTemplate( | |
| ('templateId', ParseIntPipe) templateId: number, | |
| () dto: SaveExamPaperTemplateDto, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.updatePaperTemplate( | |
| templateId, | |
| req.user.userId, | |
| dto, | |
| ); | |
| } | |
| ('paper-templates/:templateId') | |
| (RoleName.INSTRUCTOR) | |
| async deletePaperTemplate( | |
| ('templateId', ParseIntPipe) templateId: number, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| await this.examsService.deletePaperTemplate(templateId, req.user.userId); | |
| return { message: 'Paper template deleted successfully' }; | |
| } | |
| ('drafts/:draftId/sections') | |
| (RoleName.INSTRUCTOR) | |
| createDraftSection( | |
| ('draftId', ParseIntPipe) draftId: number, | |
| () dto: CreateExamSectionDto, | |
| () req: AuthenticatedRequest, | |
| ): Promise<ExamDraftSection> { | |
| return this.examsService.createDraftSection(draftId, dto, req.user.userId); | |
| } | |
| ('drafts/:draftId/validation') | |
| (RoleName.INSTRUCTOR) | |
| validateDraft( | |
| ('draftId', ParseIntPipe) draftId: number, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.validateDraft(draftId, req.user.userId); | |
| } | |
| ('drafts/:draftId/regenerate') | |
| (RoleName.INSTRUCTOR) | |
| regenerateDraft( | |
| ('draftId', ParseIntPipe) draftId: number, | |
| () dto: { seed?: string; keepManualEdits?: boolean }, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.regenerateDraft(draftId, req.user.userId, dto); | |
| } | |
| ('drafts/:draftId/duplicate') | |
| (RoleName.INSTRUCTOR) | |
| duplicateDraft( | |
| ('draftId', ParseIntPipe) draftId: number, | |
| () dto: DuplicateDraftDto, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.duplicateDraft(draftId, req.user.userId, dto); | |
| } | |
| ('drafts/:draftId/sections/:sectionId/reshuffle') | |
| (RoleName.INSTRUCTOR) | |
| reshuffleDraftSection( | |
| ('draftId', ParseIntPipe) draftId: number, | |
| ('sectionId', ParseIntPipe) sectionId: number, | |
| () dto: { seed?: string; keepManualEdits?: boolean }, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.reshuffleDraftSection( | |
| draftId, | |
| sectionId, | |
| req.user.userId, | |
| dto, | |
| ); | |
| } | |
| ('drafts/:draftId/sections/:sectionId/normalize-marks') | |
| (RoleName.INSTRUCTOR) | |
| normalizeSectionMarks( | |
| ('draftId', ParseIntPipe) draftId: number, | |
| ('sectionId', ParseIntPipe) sectionId: number, | |
| () dto: NormalizeSectionMarksDto, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.normalizeSectionMarks( | |
| draftId, | |
| sectionId, | |
| req.user.userId, | |
| dto, | |
| ); | |
| } | |
| ('drafts/:draftId/sections/reorder') | |
| (RoleName.INSTRUCTOR) | |
| reorderDraftSections( | |
| ('draftId', ParseIntPipe) draftId: number, | |
| () dto: ReorderExamSectionsDto, | |
| () req: AuthenticatedRequest, | |
| ): Promise<ExamDraftSection[]> { | |
| return this.examsService.reorderDraftSections( | |
| draftId, | |
| dto, | |
| req.user.userId, | |
| ); | |
| } | |
| ('drafts/:draftId/sections/:sectionId') | |
| (RoleName.INSTRUCTOR) | |
| updateDraftSection( | |
| ('draftId', ParseIntPipe) draftId: number, | |
| ('sectionId', ParseIntPipe) sectionId: number, | |
| () dto: UpdateExamSectionDto, | |
| () req: AuthenticatedRequest, | |
| ): Promise<ExamDraftSection> { | |
| return this.examsService.updateDraftSection( | |
| draftId, | |
| sectionId, | |
| dto, | |
| req.user.userId, | |
| ); | |
| } | |
| ('drafts/:draftId/sections/:sectionId') | |
| (RoleName.INSTRUCTOR) | |
| async deleteDraftSection( | |
| ('draftId', ParseIntPipe) draftId: number, | |
| ('sectionId', ParseIntPipe) sectionId: number, | |
| () req: AuthenticatedRequest, | |
| ): Promise<{ message: string }> { | |
| await this.examsService.deleteDraftSection( | |
| draftId, | |
| sectionId, | |
| req.user.userId, | |
| ); | |
| return { message: 'Draft section deleted successfully' }; | |
| } | |
| ('drafts/:draftId/items') | |
| (RoleName.INSTRUCTOR) | |
| addDraftItem( | |
| ('draftId', ParseIntPipe) draftId: number, | |
| () dto: AddDraftItemDto, | |
| () req: AuthenticatedRequest, | |
| ): Promise<ExamDraftItem> { | |
| return this.examsService.addDraftItem(draftId, dto, req.user.userId); | |
| } | |
| ('drafts/:draftId/items/reorder') | |
| (RoleName.INSTRUCTOR) | |
| reorderDraftItems( | |
| ('draftId', ParseIntPipe) draftId: number, | |
| () dto: ReorderDraftItemsDto, | |
| () req: AuthenticatedRequest, | |
| ): Promise<ExamDraftItem[]> { | |
| return this.examsService.reorderDraftItems(draftId, dto, req.user.userId); | |
| } | |
| ('drafts/:draftId/items/:itemId') | |
| (RoleName.INSTRUCTOR) | |
| updateDraftItem( | |
| ('draftId', ParseIntPipe) draftId: number, | |
| ('itemId', ParseIntPipe) itemId: number, | |
| () dto: UpdateDraftItemDto, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.updateDraftItem( | |
| draftId, | |
| itemId, | |
| dto, | |
| req.user.userId, | |
| ); | |
| } | |
| ('drafts/:draftId/items/:itemId/replacement-check') | |
| (RoleName.INSTRUCTOR) | |
| replacementCheck( | |
| ('draftId', ParseIntPipe) draftId: number, | |
| ('itemId', ParseIntPipe) itemId: number, | |
| () dto: ReplacementCheckDto, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.checkReplacement( | |
| draftId, | |
| itemId, | |
| dto, | |
| req.user.userId, | |
| ); | |
| } | |
| ('drafts/:draftId/items/:itemId') | |
| (RoleName.INSTRUCTOR) | |
| async removeDraftItem( | |
| ('draftId', ParseIntPipe) draftId: number, | |
| ('itemId', ParseIntPipe) itemId: number, | |
| () req: AuthenticatedRequest, | |
| ): Promise<{ message: string }> { | |
| await this.examsService.removeDraftItem(draftId, itemId, req.user.userId); | |
| return { message: 'Draft item removed successfully' }; | |
| } | |
| ('drafts/:draftId/save') | |
| (RoleName.INSTRUCTOR) | |
| saveDraft( | |
| ('draftId', ParseIntPipe) draftId: number, | |
| () req: AuthenticatedRequest, | |
| ): Promise<ExamResponseDto> { | |
| return this.examsService | |
| .saveDraft(draftId, req.user.userId) | |
| .then((exam) => this.examsService.toExamResponse(exam)); | |
| } | |
| (':id/full') | |
| (RoleName.INSTRUCTOR) | |
| getFullExam( | |
| ('id', ParseIntPipe) id: number, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.getFullExamDetail(id, req.user.userId); | |
| } | |
| (':id/paper-template') | |
| (RoleName.INSTRUCTOR) | |
| applyPaperTemplate( | |
| ('id', ParseIntPipe) id: number, | |
| () dto: ApplyExamPaperTemplateDto, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.applyPaperTemplate(id, req.user.userId, dto); | |
| } | |
| (':id') | |
| (RoleName.INSTRUCTOR) | |
| getExam( | |
| ('id', ParseIntPipe) id: number, | |
| () req: AuthenticatedRequest, | |
| ): Promise<ExamResponseDto> { | |
| return this.examsService | |
| .findExamById(id, req.user.userId) | |
| .then((exam) => this.examsService.toExamResponse(exam)); | |
| } | |
| (':id') | |
| (RoleName.INSTRUCTOR) | |
| async deleteExam( | |
| ('id', ParseIntPipe) id: number, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| await this.examsService.deleteExam(id, req.user.userId); | |
| return { message: 'Exam deleted successfully', deletedExamId: id }; | |
| } | |
| (':id/publish') | |
| (RoleName.INSTRUCTOR) | |
| publishExam( | |
| ('id', ParseIntPipe) id: number, | |
| () dto: PublishExamDto, | |
| () req: AuthenticatedRequest, | |
| ): Promise<ExamResponseDto> { | |
| return this.examsService | |
| .publishExam(id, dto, req.user.userId) | |
| .then((exam) => this.examsService.toExamResponse(exam)); | |
| } | |
| (':id/unpublish') | |
| (RoleName.INSTRUCTOR) | |
| unpublishExam( | |
| ('id', ParseIntPipe) id: number, | |
| () dto: UnpublishExamDto, | |
| () req: AuthenticatedRequest, | |
| ): Promise<ExamResponseDto> { | |
| return this.examsService | |
| .unpublishExam(id, dto, req.user.userId) | |
| .then((exam) => this.examsService.toExamResponse(exam)); | |
| } | |
| (':id/archive') | |
| (RoleName.INSTRUCTOR) | |
| archiveExam( | |
| ('id', ParseIntPipe) id: number, | |
| () dto: ArchiveExamDto, | |
| () req: AuthenticatedRequest, | |
| ): Promise<ExamResponseDto> { | |
| return this.examsService | |
| .archiveExam(id, dto, req.user.userId) | |
| .then((exam) => this.examsService.toExamResponse(exam)); | |
| } | |
| (':id/export-word') | |
| (RoleName.INSTRUCTOR) | |
| exportWord( | |
| ('id', ParseIntPipe) id: number, | |
| () dto: ExportExamDto, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.exportExamAsWord(id, dto, req.user.userId); | |
| } | |
| (':id/client-export') | |
| (RoleName.INSTRUCTOR) | |
| (FileInterceptor('file')) | |
| registerClientExport( | |
| ('id', ParseIntPipe) id: number, | |
| () file: Express.Multer.File, | |
| () body: Record<string, unknown>, | |
| () req: AuthenticatedRequest, | |
| ) { | |
| return this.examsService.registerClientPdfExport( | |
| id, | |
| file, | |
| body, | |
| req.user.userId, | |
| ); | |
| } | |
| } | |