diff --git a/src/submisi-tugas/submisi-tugas.controller.ts b/src/submisi-tugas/submisi-tugas.controller.ts index cb4f45a1b1cdb0d5535344ac9d8ccee62e4db8b9..c3619c22f465c82d97517c61261e7be94341e834 100644 --- a/src/submisi-tugas/submisi-tugas.controller.ts +++ b/src/submisi-tugas/submisi-tugas.controller.ts @@ -29,6 +29,7 @@ import { } from "./submisi-tugas.dto"; import { AuthDto } from "src/auth/auth.dto"; import { Request } from "express"; +import { SubmisiTugas } from "src/entities/submisiTugas.entity"; @ApiTags("Submisi Tugas") @ApiBearerAuth() @@ -38,13 +39,15 @@ import { Request } from "express"; export class SubmisiTugasController { constructor(private submisiTugasServ: SubmisiTugasService) {} + @ApiOperation({ + summary: "Create submisi tugas. Roles: S2_MAHASISWA", + }) @Roles(RoleEnum.S2_MAHASISWA) @Post() async createSubmisiTugas( @Body() createDto: CreateSubmisiTugasDto, @Req() req: Request, ) { - // TODO: More validation const { id } = req.user as AuthDto; return await this.submisiTugasServ.createSubmisiTugas(createDto, id); @@ -52,7 +55,7 @@ export class SubmisiTugasController { @ApiOperation({ summary: - "Get submisi tugas by sumbisi tugas id. Roles: S2_KULIAH, S2_MAHASISWA", + "Get submisi tugas by submisi tugas id. Roles: S2_KULIAH, S2_MAHASISWA", }) @ApiOkResponse({ type: GetSubmisiTugasByIdRespDto }) @Roles(RoleEnum.S2_KULIAH, RoleEnum.S2_MAHASISWA) @@ -101,4 +104,21 @@ export class SubmisiTugasController { query.isSubmitted, ); } + + @ApiOperation({ + summary: + "Get a specific submisi tugas by mahasiswa ID and tugas ID. Roles: S2_KULIAH, S2_MAHASISWA", + }) + @ApiOkResponse({ type: SubmisiTugas }) + @Roles(RoleEnum.S2_KULIAH, RoleEnum.S2_MAHASISWA) + @Get(":mahasiswaId/:tugasId") + async getSubmisiTugasByMahasiswaAndTugasId( + @Param("mahasiswaId") mahasiswaId: string, + @Param("tugasId") tugasId: string, + ): Promise<SubmisiTugas> { + return await this.submisiTugasServ.getSubmisiTugasByMahasiswaAndTugasId( + mahasiswaId, + tugasId, + ); + } } diff --git a/src/submisi-tugas/submisi-tugas.service.ts b/src/submisi-tugas/submisi-tugas.service.ts index d3c1c11a98b638aecbf96c5b67c1b8326073ef93..7c83cda2cb6e1df20c4fe6e9d0dcf77142690cd4 100644 --- a/src/submisi-tugas/submisi-tugas.service.ts +++ b/src/submisi-tugas/submisi-tugas.service.ts @@ -77,6 +77,38 @@ export class SubmisiTugasService { ); } + private async isTugasBeforeDeadlineOrFail(tugasId: string) { + const tugas = await this.tugasRepo.findOne({ + where: { id: tugasId }, + }); + + if (!tugas) { + throw new NotFoundException("Tugas tidak ditemukan"); + } + + const currDate = new Date(); + if (tugas.waktuSelesai < currDate) { + throw new ForbiddenException("Tugas sudah melewati deadline"); + } + } + + private async isDuplicateSubmissionOrFail( + tugasId: string, + mahasiswaId: string, + ) { + const submisiTugas = await this.submisiTugasRepo.findOne({ + where: { + id: tugasId, + mahasiswaId: mahasiswaId, + isSubmitted: true, + }, + }); + + if (submisiTugas) { + throw new ForbiddenException("Anda sudah mengumpulkan tugas ini"); + } + } + async createSubmisiTugas( createDto: CreateSubmisiTugasDto, mahasiswaId: string, @@ -86,6 +118,10 @@ export class SubmisiTugasService { createDto.tugasId, ); + await this.isTugasBeforeDeadlineOrFail(createDto.tugasId); + + await this.isDuplicateSubmissionOrFail(createDto.tugasId, mahasiswaId); + const tugas = await this.tugasRepo.findOneBy({ id: createDto.tugasId }); const mahasiswa = await this.penggunaRepo.findOneBy({ id: mahasiswaId }); @@ -254,4 +290,27 @@ export class SubmisiTugasService { return mappedResult; } + + async getSubmisiTugasByMahasiswaAndTugasId( + mahasiswaId: string, + tugasId: string, + ): Promise<SubmisiTugas> { + await this.tugasService.isMahasiswaTugasOrFail(mahasiswaId, tugasId); + + const submisiTugas = await this.submisiTugasRepo.findOne({ + where: { + mahasiswaId: mahasiswaId, + tugasId: tugasId, + }, + relations: ["berkasSubmisiTugas", "tugas"], + }); + + if (!submisiTugas) { + throw new NotFoundException( + `Submisi tugas tidak ditemukan untuk mahasiswa ID: ${mahasiswaId} dan tugas ID: ${tugasId}`, + ); + } + + return submisiTugas; + } } diff --git a/src/submisi-tugas/submisi.controller.ts b/src/submisi-tugas/submisi.controller.ts deleted file mode 100644 index 9e1a07521bb689811f96ac82c79b2e7856818187..0000000000000000000000000000000000000000 --- a/src/submisi-tugas/submisi.controller.ts +++ /dev/null @@ -1,67 +0,0 @@ -import { - Body, - Controller, - Get, - Param, - Post, - Req, - UseGuards, -} from "@nestjs/common"; -import { - ApiBearerAuth, - ApiBody, - ApiCookieAuth, - ApiOkResponse, - ApiResponse, - ApiTags, -} from "@nestjs/swagger"; -import { Request } from "express"; -import { AuthDto } from "src/auth/auth.dto"; -import { RoleEnum } from "src/entities/pengguna.entity"; -import { CustomAuthGuard } from "src/middlewares/custom-auth.guard"; -import { Roles } from "src/middlewares/roles.decorator"; -import { RolesGuard } from "src/middlewares/roles.guard"; -import { - CreateSubmisiReqDto, - CreateSubmisiResDto, - GetSubmisiResDto, -} from "./submisi.dto"; -import { SubmisiService } from "./submisi.service"; - -@ApiTags("Submisi Tugas") -@ApiCookieAuth() -@ApiBearerAuth() -@Controller("submisi") -@UseGuards(CustomAuthGuard, RolesGuard) -export class SubmisiController { - constructor(private readonly submisiService: SubmisiService) {} - - @ApiOkResponse({ type: GetSubmisiResDto }) - @Roles(RoleEnum.S2_MAHASISWA) - @Get("/:mahasiswaId/:tugasId") - async getSubmisiMahasiswaByTugasId( - @Param("mahasiswaId") mahasiswaId: string, - @Param("tugasId") tugasId: string, - @Req() request: Request, - ): Promise<GetSubmisiResDto> { - return this.submisiService.getSubmisiMahasiswaByTugasId( - mahasiswaId, - tugasId, - request.user as AuthDto, - ); - } - - @ApiResponse({ status: 201, type: CreateSubmisiResDto }) - @Roles(RoleEnum.S2_MAHASISWA) - @ApiBody({ type: CreateSubmisiReqDto }) - @Post("/") - async createSubmission( - @Req() request: Request, - @Body() createSubmisiReqDto: CreateSubmisiReqDto, - ): Promise<CreateSubmisiResDto> { - return this.submisiService.createSubmission( - createSubmisiReqDto, - request.user as AuthDto, - ); - } -} diff --git a/src/submisi-tugas/submisi.dto.ts b/src/submisi-tugas/submisi.dto.ts deleted file mode 100644 index 413a714d8888951d7ef547f732cefcbb00da39b8..0000000000000000000000000000000000000000 --- a/src/submisi-tugas/submisi.dto.ts +++ /dev/null @@ -1,74 +0,0 @@ -import { ApiProperty, PickType } from "@nestjs/swagger"; -import { SubmisiTugas } from "src/entities/submisiTugas"; - -class SubmisiRes extends PickType(SubmisiTugas, [ - "id", - "mahasiswa", - "jawaban", - "isSubmitted", - "berkasSubmisiTugas", - "submittedAt", -] as const) {} - -export class GetSubmisiResDto { - @ApiProperty({ - type: SubmisiRes, - example: { - id: "550e8400-e29b-41d4-a716-446655440000", - mahasiswa: { - id: "550e8400-e29b-41d4-a716-446655440000", - nama: "John Doe", - email: "johndoe@gmail.com", - }, - jawaban: "Lorem ipsum dolor sit amet", - isSubmitted: true, - berkasSubmisiTugas: [ - { - id: "550e8400-e29b-41d4-a716-446655440000", - nama: "Berkas Tugas 1", - url: "https://example.com/file.pdf", - }, - ], - submittedAt: "2021-08-17T07:45:00.000Z", - }, - }) - submisi: SubmisiRes; -} - -export class CreateBerkasSubmisiReqDto { - @ApiProperty({ - example: { - nama: "Berkas Tugas 1", - url: "https://example.com/file.pdf", - }, - }) - nama: string; - url: string; -} - -export class CreateSubmisiReqDto { - @ApiProperty({ - example: { - jawaban: "Lorem ipsum dolor sit amet", - berkasSubmisiTugas: [ - { - nama: "Berkas Tugas 1", - url: "https://example.com/file.pdf", - }, - ], - isSubmitted: true, - tugas: "550e8400-e29b-41d4-a716-446655440000", - }, - }) - jawaban: string; - berkasSubmisiTugas: CreateBerkasSubmisiReqDto[]; - isSubmitted: boolean; - tugas: string; -} - -export class CreateSubmisiResDto { - @ApiProperty({ - example: "Submisi tugas berhasil dibuat.", - }) - message: string; -} diff --git a/src/submisi-tugas/submisi.module.ts b/src/submisi-tugas/submisi.module.ts deleted file mode 100644 index 135b6e320e719c3912b5194871d9d6a1461f856d..0000000000000000000000000000000000000000 --- a/src/submisi-tugas/submisi.module.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { Module } from "@nestjs/common"; -import { TypeOrmModule } from "@nestjs/typeorm"; -import { SubmisiController } from "./submisi.controller"; -import { SubmisiService } from "./submisi.service"; -import { SubmisiTugas } from "src/entities/submisiTugas"; -import { Tugas } from "src/entities/tugas.entity"; -import { MahasiswaKelas } from "src/entities/mahasiswaKelas"; -import { Konfigurasi } from "src/entities/konfigurasi.entity"; -import { BerkasSubmisiTugas } from "src/entities/berkasSubmisiTugas"; - -@Module({ - imports: [ - TypeOrmModule.forFeature([ - SubmisiTugas, - Tugas, - MahasiswaKelas, - Konfigurasi, - BerkasSubmisiTugas, - ]), - ], - controllers: [SubmisiController], - providers: [SubmisiService], -}) -export class SubmisiModule {} diff --git a/src/submisi-tugas/submisi.service.ts b/src/submisi-tugas/submisi.service.ts deleted file mode 100644 index e2671f0968e0d018d779de79fa77b0e168d1389d..0000000000000000000000000000000000000000 --- a/src/submisi-tugas/submisi.service.ts +++ /dev/null @@ -1,160 +0,0 @@ -import { - BadRequestException, - ForbiddenException, - Injectable, - NotFoundException, -} from "@nestjs/common"; -import { InjectRepository } from "@nestjs/typeorm"; -import { Repository } from "typeorm"; -import { AuthDto } from "src/auth/auth.dto"; -import { - CreateSubmisiReqDto, - CreateSubmisiResDto, - GetSubmisiResDto, -} from "src/submisi-tugas/submisi.dto"; - -// Entities -import { SubmisiTugas } from "src/entities/submisiTugas"; -import { Tugas } from "src/entities/tugas.entity"; -import { MahasiswaKelas } from "src/entities/mahasiswaKelas"; -import { Konfigurasi } from "src/entities/konfigurasi.entity"; -import { RoleEnum } from "src/entities/pengguna.entity"; -import { BerkasSubmisiTugas } from "src/entities/berkasSubmisiTugas"; - -@Injectable() -export class SubmisiService { - constructor( - @InjectRepository(SubmisiTugas) - private submisiRepository: Repository<SubmisiTugas>, - @InjectRepository(Tugas) - private tugasRepository: Repository<Tugas>, - @InjectRepository(MahasiswaKelas) - private mahasiswaRepository: Repository<MahasiswaKelas>, - @InjectRepository(Konfigurasi) - private konfigurasiRepository: Repository<Konfigurasi>, - @InjectRepository(BerkasSubmisiTugas) - private berkasSubmisiTugasRepository: Repository<BerkasSubmisiTugas>, - ) {} - - async getSubmisiMahasiswaByTugasId( - mahasiswaId: string, - tugasId: string, - user: AuthDto, - ): Promise<GetSubmisiResDto> { - // Validate user - if (!user.roles.includes(RoleEnum.S2_MAHASISWA)) { - throw new ForbiddenException(); - } - - // Validate mahasiswa - const mahasiswa = await this.mahasiswaRepository.findOne({ - where: { id: mahasiswaId }, - }); - - if (!mahasiswa) { - throw new NotFoundException("Mahasiswa not found"); - } - - // Validate tugas - const currentPeriod = await this.konfigurasiRepository.findOne({ - where: { key: process.env.KONF_PERIODE_KEY }, - }); - - if (!currentPeriod) { - throw new BadRequestException("Periode belum dikonfigurasi."); - } - - // Get tugas where class is in current period - const tugas = await this.tugasRepository.findOne({ - where: { - id: tugasId, - kelas: { - periode: currentPeriod.value, - }, - }, - }); - - if (!tugas) { - throw new NotFoundException("Tugas kelas tidak ditemukan."); - } - - // Get submission - const submisi = await this.submisiRepository.findOne({ - where: { - mahasiswa: { id: mahasiswaId }, - tugas: { id: tugasId }, - }, - }); - - return { submisi }; - } - - async createSubmission( - createSubmisiDto: CreateSubmisiReqDto, - user: AuthDto, - ): Promise<CreateSubmisiResDto> { - // Validate user - if (!user.roles.includes(RoleEnum.S2_MAHASISWA)) { - throw new ForbiddenException(); - } - - // Validate tugas - const tugas = await this.tugasRepository.findOne({ - where: { id: createSubmisiDto.tugas }, - }); - - if (!tugas) { - throw new NotFoundException("Tugas tidak ditemukan."); - } - - // Validate mahasiswa - const mahasiswa = await this.mahasiswaRepository.findOne({ - where: { id: user.id }, - }); - - if (!mahasiswa) { - throw new NotFoundException("Mahasiswa tidak ditemukan."); - } - - // Validate submission if isSubmitted is true - const submisi = await this.submisiRepository.findOne({ - where: { - mahasiswa: { id: user.id }, - tugas: { id: createSubmisiDto.tugas }, - isSubmitted: true, - }, - }); - - if (submisi) { - throw new BadRequestException("Tugas sudah dikumpulkan."); - } - - // Create submission - // If isSubmitted is true, set submittedAt to current date - const submittedAt = createSubmisiDto.isSubmitted ? new Date() : null; - - // Create the berkasSubmisiTugas - const berkasSubmisiTugas = await Promise.all( - createSubmisiDto.berkasSubmisiTugas.map(async (berkas) => { - const newBerkas = this.berkasSubmisiTugasRepository.create({ - nama: berkas.nama, - url: berkas.url, - }); - - await this.berkasSubmisiTugasRepository.save(newBerkas); - return newBerkas; - }), - ); - - await this.submisiRepository.insert({ - mahasiswa: mahasiswa, - tugas: tugas, - jawaban: createSubmisiDto.jawaban, - isSubmitted: createSubmisiDto.isSubmitted, - berkasSubmisiTugas: berkasSubmisiTugas, - submittedAt: submittedAt, - }); - - return { message: "Submission created" }; - } -}