From 5301a1a202809ba33f59338a795250469a5ba4ee Mon Sep 17 00:00:00 2001 From: Jaehyeon Kim <65964601+Jaehyeon1020@users.noreply.github.com> Date: Mon, 29 Jul 2024 07:36:50 +0000 Subject: [PATCH 01/19] feat(be): add contest-submission-result model --- .../model/contest-submission-result.model.ts | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 apps/backend/apps/admin/src/contest/model/contest-submission-result.model.ts diff --git a/apps/backend/apps/admin/src/contest/model/contest-submission-result.model.ts b/apps/backend/apps/admin/src/contest/model/contest-submission-result.model.ts new file mode 100644 index 0000000000..b52992b117 --- /dev/null +++ b/apps/backend/apps/admin/src/contest/model/contest-submission-result.model.ts @@ -0,0 +1,29 @@ +import { Field, Int, ObjectType } from '@nestjs/graphql' +import type { Language } from '@admin/@generated' + +/** + * 특정 User의 특정 Contest에 대한 Submission 정보 + */ +@ObjectType({ description: 'contestSubmissionResult' }) +export class ContestSubmissionResult { + @Field(() => Int) + contestId: number + + @Field(() => String) + problemTitle: string + + @Field(() => String) + submissionResult: string + + @Field(() => String) + language: Language + + @Field(() => String) + submissionTime: Date + + @Field(() => Int) + codeSize: number + + @Field(() => String) + ip: string +} From 952726ad367840bc79c0d2133d0f707161df3dd4 Mon Sep 17 00:00:00 2001 From: Jaehyeon Kim <65964601+Jaehyeon1020@users.noreply.github.com> Date: Mon, 29 Jul 2024 07:53:32 +0000 Subject: [PATCH 02/19] chore(be): rename contest-submission-result to contest-submission-information --- ...ult.model.ts => contest-submission-information.model.ts} | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) rename apps/backend/apps/admin/src/contest/model/{contest-submission-result.model.ts => contest-submission-information.model.ts} (68%) diff --git a/apps/backend/apps/admin/src/contest/model/contest-submission-result.model.ts b/apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts similarity index 68% rename from apps/backend/apps/admin/src/contest/model/contest-submission-result.model.ts rename to apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts index b52992b117..5cc1e73552 100644 --- a/apps/backend/apps/admin/src/contest/model/contest-submission-result.model.ts +++ b/apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts @@ -2,10 +2,10 @@ import { Field, Int, ObjectType } from '@nestjs/graphql' import type { Language } from '@admin/@generated' /** - * 특정 User의 특정 Contest에 대한 Submission 정보 + * 특정 User의 특정 Contest에 대한 Submission 정보 (!== model SubmissionResult) */ -@ObjectType({ description: 'contestSubmissionResult' }) -export class ContestSubmissionResult { +@ObjectType({ description: 'contestSubmissionInformation' }) +export class ContestSubmissionInformation { @Field(() => Int) contestId: number From 92d2875d9143c730c4cc9bff9f6a8dec6e18c7bf Mon Sep 17 00:00:00 2001 From: Jaehyeon Kim <65964601+Jaehyeon1020@users.noreply.github.com> Date: Mon, 29 Jul 2024 08:29:13 +0000 Subject: [PATCH 03/19] chore(be): set nullability of fields in contest-submission-information --- .../contest-submission-information.model.ts | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts b/apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts index 5cc1e73552..665d9e0d34 100644 --- a/apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts +++ b/apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts @@ -1,29 +1,29 @@ import { Field, Int, ObjectType } from '@nestjs/graphql' -import type { Language } from '@admin/@generated' +import { Language, SubmissionResult } from '@admin/@generated' /** * 특정 User의 특정 Contest에 대한 Submission 정보 (!== model SubmissionResult) */ @ObjectType({ description: 'contestSubmissionInformation' }) export class ContestSubmissionInformation { - @Field(() => Int) + @Field(() => Int, { nullable: false }) contestId: number - @Field(() => String) + @Field(() => String, { nullable: false }) problemTitle: string - @Field(() => String) - submissionResult: string + @Field(() => SubmissionResult, { nullable: false }) + submissionResult: SubmissionResult // Accepted, RuntimeError, ... - @Field(() => String) + @Field(() => Language, { nullable: false }) language: Language - @Field(() => String) + @Field(() => String, { nullable: false }) submissionTime: Date - @Field(() => Int) + @Field(() => Int, { nullable: false }) codeSize: number - @Field(() => String) + @Field(() => String, { nullable: false }) ip: string } From fb0c1e807e1ad51b33940daf5653fcb94b6a3c48 Mon Sep 17 00:00:00 2001 From: Jaehyeon Kim <65964601+Jaehyeon1020@users.noreply.github.com> Date: Mon, 29 Jul 2024 08:45:48 +0000 Subject: [PATCH 04/19] feat(be): add get-contest-submission-informations api --- .../admin/src/contest/contest.resolver.ts | 24 ++++++++++++- .../apps/admin/src/contest/contest.service.ts | 35 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/apps/backend/apps/admin/src/contest/contest.resolver.ts b/apps/backend/apps/admin/src/contest/contest.resolver.ts index d080adc0d3..e644893a2d 100644 --- a/apps/backend/apps/admin/src/contest/contest.resolver.ts +++ b/apps/backend/apps/admin/src/contest/contest.resolver.ts @@ -14,8 +14,14 @@ import { EntityNotExistException, UnprocessableDataException } from '@libs/exception' -import { CursorValidationPipe, GroupIDPipe, RequiredIntPipe } from '@libs/pipe' +import { + CursorValidationPipe, + GroupIDPipe, + IDValidationPipe, + RequiredIntPipe +} from '@libs/pipe' import { ContestService } from './contest.service' +import { ContestSubmissionInformation } from './model/contest-submission-information.model' import { ContestWithParticipants } from './model/contest-with-participants.model' import { CreateContestInput } from './model/contest.input' import { UpdateContestInput } from './model/contest.input' @@ -218,4 +224,20 @@ export class ContestResolver { throw new InternalServerErrorException() } } + + @Query(() => [ContestSubmissionInformation]) + async getContestSubmissionInformations( + @Args('contestId', { type: () => Int }, IDValidationPipe) contestId: number, + @Args('userId', { type: () => Int }, IDValidationPipe) userId: number + ) { + try { + return await this.contestService.getContestSubmissionInformations( + contestId, + userId + ) + } catch (error) { + this.logger.error(error) + throw new InternalServerErrorException() + } + } } diff --git a/apps/backend/apps/admin/src/contest/contest.service.ts b/apps/backend/apps/admin/src/contest/contest.service.ts index 6939bd9341..941d128720 100644 --- a/apps/backend/apps/admin/src/contest/contest.service.ts +++ b/apps/backend/apps/admin/src/contest/contest.service.ts @@ -371,4 +371,39 @@ export class ContestService { return contestProblems } + + async getContestSubmissionInformations(contestId: number, userId: number) { + const submissions = await this.prisma.submission.findMany({ + where: { + userId, + contestId + }, + include: { + problem: { + select: { + title: true + } + }, + user: { + select: { + username: true + } + } + } + }) + + return await Promise.all( + submissions.map(async (submission) => { + return { + contestId: submission.contestId, + problemTitle: submission.problem.title, + submissionResult: submission.result, + language: submission.language, + submissionTime: submission.createTime, + codeSize: submission.codeSize, + ip: '127.0.0.1' // TODO: submission.ip 사용 + } + }) + ) + } } From 58a2c9a9100f42da2122151a9c3a674818a5206d Mon Sep 17 00:00:00 2001 From: Jaehyeon Kim <65964601+Jaehyeon1020@users.noreply.github.com> Date: Thu, 1 Aug 2024 12:43:50 +0000 Subject: [PATCH 05/19] chore(be): rename --- apps/backend/apps/admin/src/contest/contest.resolver.ts | 8 ++++---- apps/backend/apps/admin/src/contest/contest.service.ts | 2 +- .../contest/model/contest-submission-information.model.ts | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/backend/apps/admin/src/contest/contest.resolver.ts b/apps/backend/apps/admin/src/contest/contest.resolver.ts index e644893a2d..5d7a29cc19 100644 --- a/apps/backend/apps/admin/src/contest/contest.resolver.ts +++ b/apps/backend/apps/admin/src/contest/contest.resolver.ts @@ -21,7 +21,7 @@ import { RequiredIntPipe } from '@libs/pipe' import { ContestService } from './contest.service' -import { ContestSubmissionInformation } from './model/contest-submission-information.model' +import { ContestSubmissionSummary } from './model/contest-submission-information.model' import { ContestWithParticipants } from './model/contest-with-participants.model' import { CreateContestInput } from './model/contest.input' import { UpdateContestInput } from './model/contest.input' @@ -225,13 +225,13 @@ export class ContestResolver { } } - @Query(() => [ContestSubmissionInformation]) - async getContestSubmissionInformations( + @Query(() => [ContestSubmissionSummary]) + async getContestSubmissionSummaries( @Args('contestId', { type: () => Int }, IDValidationPipe) contestId: number, @Args('userId', { type: () => Int }, IDValidationPipe) userId: number ) { try { - return await this.contestService.getContestSubmissionInformations( + return await this.contestService.getContestSubmissionSummaries( contestId, userId ) diff --git a/apps/backend/apps/admin/src/contest/contest.service.ts b/apps/backend/apps/admin/src/contest/contest.service.ts index 941d128720..64c06a4509 100644 --- a/apps/backend/apps/admin/src/contest/contest.service.ts +++ b/apps/backend/apps/admin/src/contest/contest.service.ts @@ -372,7 +372,7 @@ export class ContestService { return contestProblems } - async getContestSubmissionInformations(contestId: number, userId: number) { + async getContestSubmissionSummaries(contestId: number, userId: number) { const submissions = await this.prisma.submission.findMany({ where: { userId, diff --git a/apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts b/apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts index 665d9e0d34..355b305d8f 100644 --- a/apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts +++ b/apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts @@ -4,8 +4,8 @@ import { Language, SubmissionResult } from '@admin/@generated' /** * 특정 User의 특정 Contest에 대한 Submission 정보 (!== model SubmissionResult) */ -@ObjectType({ description: 'contestSubmissionInformation' }) -export class ContestSubmissionInformation { +@ObjectType({ description: 'contestSubmissionSummary' }) +export class ContestSubmissionSummary { @Field(() => Int, { nullable: false }) contestId: number From 07f231dc35eb77506cec5455add896612a9a5547 Mon Sep 17 00:00:00 2001 From: Jaehyeon Kim <65964601+Jaehyeon1020@users.noreply.github.com> Date: Thu, 1 Aug 2024 12:44:28 +0000 Subject: [PATCH 06/19] test(be): add test --- .../admin/src/contest/contest.service.spec.ts | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/apps/backend/apps/admin/src/contest/contest.service.spec.ts b/apps/backend/apps/admin/src/contest/contest.service.spec.ts index 218ef4d54e..f495e6e61f 100644 --- a/apps/backend/apps/admin/src/contest/contest.service.spec.ts +++ b/apps/backend/apps/admin/src/contest/contest.service.spec.ts @@ -134,6 +134,26 @@ const contestProblem: ContestProblem = { updateTime: faker.date.past() } +const submissionsWithProblemTitleAndUsername = { + id: 1, + userId: 1, + problemId: 1, + contestId: 1, + workbookId: 1, + code: [], + codeSize: 1, + language: 'C', + result: 'ACCEPTED', + createTime: '2000-01-01', + updateTime: '2000-01-02', + problem: { + title: 'submission' + }, + user: { + username: 'user01' + } +} + const publicizingRequest: PublicizingRequest = { contestId, userId, @@ -180,6 +200,9 @@ const db = { group: { findUnique: stub().resolves(Group) }, + submission: { + findMany: stub().resolves([submissionsWithProblemTitleAndUsername]) + }, $transaction: stub().callsFake(async () => { const updatedProblem = await db.problem.update() const newContestProblem = await db.contestProblem.create() @@ -342,4 +365,22 @@ describe('ContestService', () => { ).to.be.rejectedWith(EntityNotExistException) }) }) + + describe('getContestSubmissionSummaries', () => { + it('should return contest submission summaries', async () => { + const res = await service.getContestSubmissionSummaries(1, 1) + + expect(res).to.deep.equal([ + { + contestId: 1, + problemTitle: 'submission', + submissionResult: 'ACCEPTED', + language: 'C', + submissionTime: '2000-01-01', + codeSize: 1, + ip: '127.0.0.1' // TODO: submission.ip 사용 + } + ]) + }) + }) }) From a463a5a4e62397e1430746214820623471831ba1 Mon Sep 17 00:00:00 2001 From: Jaehyeon Kim <65964601+Jaehyeon1020@users.noreply.github.com> Date: Thu, 1 Aug 2024 13:14:10 +0000 Subject: [PATCH 07/19] docs(be): add docs --- .../contest-submission-information.model.ts | 10 +++--- .../Success.bru | 32 +++++++++++++++++++ 2 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 collection/admin/Contest/Get Contest Submission Summaries/Success.bru diff --git a/apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts b/apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts index 355b305d8f..719eb1711a 100644 --- a/apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts +++ b/apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts @@ -1,5 +1,5 @@ import { Field, Int, ObjectType } from '@nestjs/graphql' -import { Language, SubmissionResult } from '@admin/@generated' +import { Language, ResultStatus } from '@admin/@generated' /** * 특정 User의 특정 Contest에 대한 Submission 정보 (!== model SubmissionResult) @@ -12,8 +12,8 @@ export class ContestSubmissionSummary { @Field(() => String, { nullable: false }) problemTitle: string - @Field(() => SubmissionResult, { nullable: false }) - submissionResult: SubmissionResult // Accepted, RuntimeError, ... + @Field(() => ResultStatus, { nullable: false }) + submissionResult: ResultStatus // Accepted, RuntimeError, ... @Field(() => Language, { nullable: false }) language: Language @@ -21,8 +21,8 @@ export class ContestSubmissionSummary { @Field(() => String, { nullable: false }) submissionTime: Date - @Field(() => Int, { nullable: false }) - codeSize: number + @Field(() => Int, { nullable: true }) + codeSize?: number @Field(() => String, { nullable: false }) ip: string diff --git a/collection/admin/Contest/Get Contest Submission Summaries/Success.bru b/collection/admin/Contest/Get Contest Submission Summaries/Success.bru new file mode 100644 index 0000000000..bb8fb8d776 --- /dev/null +++ b/collection/admin/Contest/Get Contest Submission Summaries/Success.bru @@ -0,0 +1,32 @@ +meta { + name: Success + type: graphql + seq: 1 +} + +post { + url: {{gqlUrl}} + body: graphql + auth: none +} + +body:graphql { + query getContestSubmissionSummaries($contestId: Int!, $userId: Int!) { + getContestSubmissionSummaries(contestId: $contestId, userId: $userId) { + contestId + problemTitle + language + submissionTime + ip + codeSize + submissionResult + } + } +} + +body:graphql:vars { + { + "contestId": 1, + "userId": 4 + } +} From d9a9971d37668d87d5febfe217e91b3cf2901083 Mon Sep 17 00:00:00 2001 From: Jaehyeon Kim <65964601+Jaehyeon1020@users.noreply.github.com> Date: Wed, 7 Aug 2024 14:50:07 +0000 Subject: [PATCH 08/19] chore(be): rename files and add fields on contest-submission-summary-for-one --- .../backend/apps/admin/src/contest/contest.resolver.ts | 9 +++++---- .../apps/admin/src/contest/contest.service.spec.ts | 9 ++++++--- apps/backend/apps/admin/src/contest/contest.service.ts | 7 +++++-- ....ts => contest-submission-summary-for-one.model.ts} | 10 ++++++++-- 4 files changed, 24 insertions(+), 11 deletions(-) rename apps/backend/apps/admin/src/contest/model/{contest-submission-information.model.ts => contest-submission-summary-for-one.model.ts} (75%) diff --git a/apps/backend/apps/admin/src/contest/contest.resolver.ts b/apps/backend/apps/admin/src/contest/contest.resolver.ts index 5d7a29cc19..d6713c6afa 100644 --- a/apps/backend/apps/admin/src/contest/contest.resolver.ts +++ b/apps/backend/apps/admin/src/contest/contest.resolver.ts @@ -21,7 +21,7 @@ import { RequiredIntPipe } from '@libs/pipe' import { ContestService } from './contest.service' -import { ContestSubmissionSummary } from './model/contest-submission-information.model' +import { ContestSubmissionSummaryForOne } from './model/contest-submission-summary-for-one.model' import { ContestWithParticipants } from './model/contest-with-participants.model' import { CreateContestInput } from './model/contest.input' import { UpdateContestInput } from './model/contest.input' @@ -225,13 +225,14 @@ export class ContestResolver { } } - @Query(() => [ContestSubmissionSummary]) - async getContestSubmissionSummaries( + // TODO: 특정 User의 특정 Contest에 대한 점수만 요약해 불러오는 함수 필요 + @Query(() => [ContestSubmissionSummaryForOne]) + async getContestSubmissionSummaryByUserId( @Args('contestId', { type: () => Int }, IDValidationPipe) contestId: number, @Args('userId', { type: () => Int }, IDValidationPipe) userId: number ) { try { - return await this.contestService.getContestSubmissionSummaries( + return await this.contestService.getContestSubmissionSummaryByUserId( contestId, userId ) diff --git a/apps/backend/apps/admin/src/contest/contest.service.spec.ts b/apps/backend/apps/admin/src/contest/contest.service.spec.ts index f495e6e61f..33a03b771d 100644 --- a/apps/backend/apps/admin/src/contest/contest.service.spec.ts +++ b/apps/backend/apps/admin/src/contest/contest.service.spec.ts @@ -150,7 +150,8 @@ const submissionsWithProblemTitleAndUsername = { title: 'submission' }, user: { - username: 'user01' + username: 'user01', + studentId: '1234567890' } } @@ -366,14 +367,16 @@ describe('ContestService', () => { }) }) - describe('getContestSubmissionSummaries', () => { + describe('getContestSubmissionSummaryByUserId', () => { it('should return contest submission summaries', async () => { - const res = await service.getContestSubmissionSummaries(1, 1) + const res = await service.getContestSubmissionSummaryByUserId(1, 1) expect(res).to.deep.equal([ { contestId: 1, problemTitle: 'submission', + username: 'user01', + studentId: '1234567890', submissionResult: 'ACCEPTED', language: 'C', submissionTime: '2000-01-01', diff --git a/apps/backend/apps/admin/src/contest/contest.service.ts b/apps/backend/apps/admin/src/contest/contest.service.ts index 64c06a4509..c79717c97f 100644 --- a/apps/backend/apps/admin/src/contest/contest.service.ts +++ b/apps/backend/apps/admin/src/contest/contest.service.ts @@ -372,7 +372,7 @@ export class ContestService { return contestProblems } - async getContestSubmissionSummaries(contestId: number, userId: number) { + async getContestSubmissionSummaryByUserId(contestId: number, userId: number) { const submissions = await this.prisma.submission.findMany({ where: { userId, @@ -386,7 +386,8 @@ export class ContestService { }, user: { select: { - username: true + username: true, + studentId: true } } } @@ -397,6 +398,8 @@ export class ContestService { return { contestId: submission.contestId, problemTitle: submission.problem.title, + username: submission.user?.username, + studentId: submission.user?.studentId, submissionResult: submission.result, language: submission.language, submissionTime: submission.createTime, diff --git a/apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts b/apps/backend/apps/admin/src/contest/model/contest-submission-summary-for-one.model.ts similarity index 75% rename from apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts rename to apps/backend/apps/admin/src/contest/model/contest-submission-summary-for-one.model.ts index 719eb1711a..9c06757fc0 100644 --- a/apps/backend/apps/admin/src/contest/model/contest-submission-information.model.ts +++ b/apps/backend/apps/admin/src/contest/model/contest-submission-summary-for-one.model.ts @@ -4,14 +4,20 @@ import { Language, ResultStatus } from '@admin/@generated' /** * 특정 User의 특정 Contest에 대한 Submission 정보 (!== model SubmissionResult) */ -@ObjectType({ description: 'contestSubmissionSummary' }) -export class ContestSubmissionSummary { +@ObjectType({ description: 'ContestSubmissionSummaryForOne' }) +export class ContestSubmissionSummaryForOne { @Field(() => Int, { nullable: false }) contestId: number @Field(() => String, { nullable: false }) problemTitle: string + @Field(() => String, { nullable: false }) + username: string + + @Field(() => String, { nullable: false }) + studentId: string + @Field(() => ResultStatus, { nullable: false }) submissionResult: ResultStatus // Accepted, RuntimeError, ... From ff984807221738443f94e9a4398d187e089ff705 Mon Sep 17 00:00:00 2001 From: mnseok kang Date: Mon, 19 Aug 2024 05:39:31 +0000 Subject: [PATCH 09/19] feat(be): implement combine score summary and submissions --- .../admin/src/contest/contest.resolver.ts | 18 ++++++++++++---- .../apps/admin/src/contest/contest.service.ts | 21 ++++++++++++++++--- ...test-submission-summary-for-user.model.ts} | 10 +++++++++ 3 files changed, 42 insertions(+), 7 deletions(-) rename apps/backend/apps/admin/src/contest/model/{contest-submission-summary-for-one.model.ts => contest-submission-summary-for-user.model.ts} (70%) diff --git a/apps/backend/apps/admin/src/contest/contest.resolver.ts b/apps/backend/apps/admin/src/contest/contest.resolver.ts index 2920f5ca9e..c6db87139f 100644 --- a/apps/backend/apps/admin/src/contest/contest.resolver.ts +++ b/apps/backend/apps/admin/src/contest/contest.resolver.ts @@ -21,7 +21,7 @@ import { RequiredIntPipe } from '@libs/pipe' import { ContestService } from './contest.service' -import { ContestSubmissionSummaryForOne } from './model/contest-submission-summary-for-one.model' +import { ContestSubmissionSummaryForUser } from './model/contest-submission-summary-for-user.model' import { ContestWithParticipants } from './model/contest-with-participants.model' import { CreateContestInput } from './model/contest.input' import { UpdateContestInput } from './model/contest.input' @@ -228,15 +228,25 @@ export class ContestResolver { } // TODO: 특정 User의 특정 Contest에 대한 점수만 요약해 불러오는 함수 필요 - @Query(() => [ContestSubmissionSummaryForOne]) + @Query(() => ContestSubmissionSummaryForUser) async getContestSubmissionSummaryByUserId( @Args('contestId', { type: () => Int }, IDValidationPipe) contestId: number, - @Args('userId', { type: () => Int }, IDValidationPipe) userId: number + @Args('userId', { type: () => Int }, IDValidationPipe) userId: number, + @Args( + 'take', + { type: () => Int, defaultValue: 10 }, + new RequiredIntPipe('take') + ) + take: number, + @Args('cursor', { nullable: true, type: () => Int }, CursorValidationPipe) + cursor: number | null ) { try { return await this.contestService.getContestSubmissionSummaryByUserId( + take, contestId, - userId + userId, + cursor ) } catch (error) { this.logger.error(error) diff --git a/apps/backend/apps/admin/src/contest/contest.service.ts b/apps/backend/apps/admin/src/contest/contest.service.ts index f081adf6f7..6c0926ecd6 100644 --- a/apps/backend/apps/admin/src/contest/contest.service.ts +++ b/apps/backend/apps/admin/src/contest/contest.service.ts @@ -485,8 +485,16 @@ export class ContestService { return contestProblems } - async getContestSubmissionSummaryByUserId(contestId: number, userId: number) { + async getContestSubmissionSummaryByUserId( + take: number, + contestId: number, + userId: number, + cursor: number | null + ) { + const paginator = this.prisma.getPaginator(cursor) const submissions = await this.prisma.submission.findMany({ + ...paginator, + take, where: { userId, contestId @@ -506,7 +514,7 @@ export class ContestService { } }) - return await Promise.all( + const mappedSubmission = await Promise.all( submissions.map(async (submission) => { return { contestId: submission.contestId, @@ -517,10 +525,17 @@ export class ContestService { language: submission.language, submissionTime: submission.createTime, codeSize: submission.codeSize, - ip: '127.0.0.1' // TODO: submission.ip 사용 + ip: submission.userIp } }) ) + + const scoreSummary = await this.getContestScoreSummary(userId, contestId) + + return { + scoreSummary, + submissions: mappedSubmission + } } /** diff --git a/apps/backend/apps/admin/src/contest/model/contest-submission-summary-for-one.model.ts b/apps/backend/apps/admin/src/contest/model/contest-submission-summary-for-user.model.ts similarity index 70% rename from apps/backend/apps/admin/src/contest/model/contest-submission-summary-for-one.model.ts rename to apps/backend/apps/admin/src/contest/model/contest-submission-summary-for-user.model.ts index 9c06757fc0..a04a1846ee 100644 --- a/apps/backend/apps/admin/src/contest/model/contest-submission-summary-for-one.model.ts +++ b/apps/backend/apps/admin/src/contest/model/contest-submission-summary-for-user.model.ts @@ -1,5 +1,15 @@ import { Field, Int, ObjectType } from '@nestjs/graphql' import { Language, ResultStatus } from '@admin/@generated' +import { UserContestScoreSummary } from './score-summary' + +@ObjectType({ description: 'ContestSubmissionSummaryForUser' }) +export class ContestSubmissionSummaryForUser { + @Field(() => ContestSubmissionSummaryForOne, { nullable: false }) + submissions: ContestSubmissionSummaryForOne[] + + @Field(() => UserContestScoreSummary, { nullable: false }) + scoreSummary: UserContestScoreSummary +} /** * 특정 User의 특정 Contest에 대한 Submission 정보 (!== model SubmissionResult) From 4d3f99ffbf6432d5f94707b394e6d59987748bcd Mon Sep 17 00:00:00 2001 From: mnseok kang Date: Mon, 19 Aug 2024 06:30:25 +0000 Subject: [PATCH 10/19] chore(be): comment test --- .../admin/src/contest/contest.service.spec.ts | 72 +++++++++++++------ 1 file changed, 51 insertions(+), 21 deletions(-) diff --git a/apps/backend/apps/admin/src/contest/contest.service.spec.ts b/apps/backend/apps/admin/src/contest/contest.service.spec.ts index 72515f0eb2..6b3ae4491e 100644 --- a/apps/backend/apps/admin/src/contest/contest.service.spec.ts +++ b/apps/backend/apps/admin/src/contest/contest.service.spec.ts @@ -4,7 +4,7 @@ import { ContestProblem, Group, ContestRecord } from '@generated' import { Problem } from '@generated' import { Contest } from '@generated' import { faker } from '@faker-js/faker' -import { Prisma } from '@prisma/client' +import { Prisma, ResultStatus } from '@prisma/client' import type { Cache } from 'cache-manager' import { expect } from 'chai' import { stub } from 'sinon' @@ -141,13 +141,14 @@ const contestProblem: ContestProblem = { const submissionsWithProblemTitleAndUsername = { id: 1, userId: 1, + userIp: '127.0.0.1', problemId: 1, contestId: 1, workbookId: 1, code: [], codeSize: 1, language: 'C', - result: 'ACCEPTED', + result: ResultStatus.Accepted, createTime: '2000-01-01', updateTime: '2000-01-02', problem: { @@ -159,6 +160,19 @@ const submissionsWithProblemTitleAndUsername = { } } +// const submissionResults = [ +// { +// id: 1, +// submissionId: 1, +// problemTestcaseId: 1, +// result: ResultStatus.Accepted, +// cpuTime: BigInt(1), +// memory: 1, +// createTime: '2000-01-01', +// updateTime: '2000-01-02' +// } +// ] + const publicizingRequest: PublicizingRequest = { contestId, userId, @@ -198,7 +212,8 @@ const db = { }, contestProblem: { create: stub().resolves(ContestProblem), - findMany: stub().resolves([ContestProblem]) + findMany: stub().resolves([ContestProblem]), + findFirstOrThrow: stub().resolves(ContestProblem) }, contestRecord: { findMany: stub().resolves([ContestRecord]), @@ -214,6 +229,9 @@ const db = { submission: { findMany: stub().resolves([submissionsWithProblemTitleAndUsername]) }, + // submissionResult: { + // findMany: stub().resolves([submissionResults]) + // }, $transaction: stub().callsFake(async () => { const updatedProblem = await db.problem.update() const newContestProblem = await db.contestProblem.create() @@ -379,25 +397,37 @@ describe('ContestService', () => { }) }) - describe('getContestSubmissionSummaryByUserId', () => { - it('should return contest submission summaries', async () => { - const res = await service.getContestSubmissionSummaryByUserId(1, 1) + // describe('getContestSubmissionSummaryByUserId', () => { + // it('should return contest submission summaries', async () => { + // const res = await service.getContestSubmissionSummaryByUserId(10, 1, 1, 1) - expect(res).to.deep.equal([ - { - contestId: 1, - problemTitle: 'submission', - username: 'user01', - studentId: '1234567890', - submissionResult: 'ACCEPTED', - language: 'C', - submissionTime: '2000-01-01', - codeSize: 1, - ip: '127.0.0.1' // TODO: submission.ip 사용 - } - ]) - }) - }) + // expect(res.submissions).to.deep.equal([ + // { + // contestId: 1, + // problemTitle: 'submission', + // username: 'user01', + // studentId: '1234567890', + // submissionResult: ResultStatus.Accepted, + // language: 'C', + // submissionTime: '2000-01-01', + // codeSize: 1, + // ip: '127.0.0.1' // TODO: submission.ip 사용 + // } + // ]) + // expect(res.scoreSummary).to.deep.equal({ + // totalProblemCount: 1, + // submittedProblemCount: 1, + // totalScore: 1, + // acceptedTestcaseCountPerProblem: [ + // { + // acceptedTestcaseCount: 0, + // problemId: 1, + // totalTestcaseCount: 1 + // } + // ] + // }) + // }) + // }) // describe('duplicateContest', () => { // db['$transaction'] = stub().callsFake(async () => { From 02d11f5c8bde543c41876894a93cfdd48f1526ee Mon Sep 17 00:00:00 2001 From: Jaehyeon Kim <65964601+Jaehyeon1020@users.noreply.github.com> Date: Mon, 19 Aug 2024 08:00:22 +0000 Subject: [PATCH 11/19] feat(be): add problem-id option --- apps/backend/apps/admin/src/contest/contest.resolver.ts | 3 +++ apps/backend/apps/admin/src/contest/contest.service.ts | 4 +++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/backend/apps/admin/src/contest/contest.resolver.ts b/apps/backend/apps/admin/src/contest/contest.resolver.ts index c6db87139f..21ca34eedf 100644 --- a/apps/backend/apps/admin/src/contest/contest.resolver.ts +++ b/apps/backend/apps/admin/src/contest/contest.resolver.ts @@ -238,6 +238,8 @@ export class ContestResolver { new RequiredIntPipe('take') ) take: number, + @Args('problemId', { nullable: true, type: () => Int }, IDValidationPipe) + problemId: number, @Args('cursor', { nullable: true, type: () => Int }, CursorValidationPipe) cursor: number | null ) { @@ -246,6 +248,7 @@ export class ContestResolver { take, contestId, userId, + problemId, cursor ) } catch (error) { diff --git a/apps/backend/apps/admin/src/contest/contest.service.ts b/apps/backend/apps/admin/src/contest/contest.service.ts index 6c0926ecd6..7e3684e997 100644 --- a/apps/backend/apps/admin/src/contest/contest.service.ts +++ b/apps/backend/apps/admin/src/contest/contest.service.ts @@ -489,6 +489,7 @@ export class ContestService { take: number, contestId: number, userId: number, + problemId: number | null, cursor: number | null ) { const paginator = this.prisma.getPaginator(cursor) @@ -497,7 +498,8 @@ export class ContestService { take, where: { userId, - contestId + contestId, + problemId: problemId ?? undefined }, include: { problem: { From d13ccd520631747dcc18ededa822c197fd9884b5 Mon Sep 17 00:00:00 2001 From: Jaehyeon Kim <65964601+Jaehyeon1020@users.noreply.github.com> Date: Mon, 26 Aug 2024 07:55:47 +0000 Subject: [PATCH 12/19] fix(be): fix wrong type --- .../admin/src/contest/contest.resolver.ts | 4 +- .../apps/admin/src/contest/contest.service.ts | 28 +++++------ ...ntest-submission-summary-for-user.model.ts | 24 +++++----- .../Succeed.bru | 0 .../Success.bru | 48 +++++++++++++++++++ .../Success.bru | 32 ------------- 6 files changed, 75 insertions(+), 61 deletions(-) rename collection/admin/Contest/{Get Contest Score Summary of User => (Don't use) Get Contest Score Summary of User}/Succeed.bru (100%) create mode 100644 collection/admin/Contest/Get Contest Submission Summaries of User/Success.bru delete mode 100644 collection/admin/Contest/Get Contest Submission Summaries/Success.bru diff --git a/apps/backend/apps/admin/src/contest/contest.resolver.ts b/apps/backend/apps/admin/src/contest/contest.resolver.ts index e88268b04e..7b9c0e3615 100644 --- a/apps/backend/apps/admin/src/contest/contest.resolver.ts +++ b/apps/backend/apps/admin/src/contest/contest.resolver.ts @@ -235,14 +235,14 @@ export class ContestResolver { async getContestSubmissionSummaryByUserId( @Args('contestId', { type: () => Int }, IDValidationPipe) contestId: number, @Args('userId', { type: () => Int }, IDValidationPipe) userId: number, + @Args('problemId', { nullable: true, type: () => Int }, IDValidationPipe) + problemId: number, @Args( 'take', { type: () => Int, defaultValue: 10 }, new RequiredIntPipe('take') ) take: number, - @Args('problemId', { nullable: true, type: () => Int }, IDValidationPipe) - problemId: number, @Args('cursor', { nullable: true, type: () => Int }, CursorValidationPipe) cursor: number | null ) { diff --git a/apps/backend/apps/admin/src/contest/contest.service.ts b/apps/backend/apps/admin/src/contest/contest.service.ts index 8be5d32a04..6b64ab033d 100644 --- a/apps/backend/apps/admin/src/contest/contest.service.ts +++ b/apps/backend/apps/admin/src/contest/contest.service.ts @@ -518,21 +518,19 @@ export class ContestService { } }) - const mappedSubmission = await Promise.all( - submissions.map(async (submission) => { - return { - contestId: submission.contestId, - problemTitle: submission.problem.title, - username: submission.user?.username, - studentId: submission.user?.studentId, - submissionResult: submission.result, - language: submission.language, - submissionTime: submission.createTime, - codeSize: submission.codeSize, - ip: submission.userIp - } - }) - ) + const mappedSubmission = submissions.map((submission) => { + return { + contestId: submission.contestId, + problemTitle: submission.problem.title, + username: submission.user?.username, + studentId: submission.user?.studentId, + submissionResult: submission.result, + language: submission.language, + submissionTime: submission.createTime, + codeSize: submission.codeSize, + ip: submission.userIp + } + }) const scoreSummary = await this.getContestScoreSummary(userId, contestId) diff --git a/apps/backend/apps/admin/src/contest/model/contest-submission-summary-for-user.model.ts b/apps/backend/apps/admin/src/contest/model/contest-submission-summary-for-user.model.ts index a04a1846ee..8b10324555 100644 --- a/apps/backend/apps/admin/src/contest/model/contest-submission-summary-for-user.model.ts +++ b/apps/backend/apps/admin/src/contest/model/contest-submission-summary-for-user.model.ts @@ -4,11 +4,11 @@ import { UserContestScoreSummary } from './score-summary' @ObjectType({ description: 'ContestSubmissionSummaryForUser' }) export class ContestSubmissionSummaryForUser { - @Field(() => ContestSubmissionSummaryForOne, { nullable: false }) - submissions: ContestSubmissionSummaryForOne[] - @Field(() => UserContestScoreSummary, { nullable: false }) scoreSummary: UserContestScoreSummary + + @Field(() => [ContestSubmissionSummaryForOne], { nullable: false }) + submissions: ContestSubmissionSummaryForOne[] } /** @@ -16,30 +16,30 @@ export class ContestSubmissionSummaryForUser { */ @ObjectType({ description: 'ContestSubmissionSummaryForOne' }) export class ContestSubmissionSummaryForOne { - @Field(() => Int, { nullable: false }) + @Field(() => Int, { nullable: true }) contestId: number - @Field(() => String, { nullable: false }) + @Field(() => String, { nullable: true }) problemTitle: string - @Field(() => String, { nullable: false }) + @Field(() => String, { nullable: true }) username: string - @Field(() => String, { nullable: false }) + @Field(() => String, { nullable: true }) studentId: string - @Field(() => ResultStatus, { nullable: false }) + @Field(() => ResultStatus, { nullable: true }) submissionResult: ResultStatus // Accepted, RuntimeError, ... - @Field(() => Language, { nullable: false }) + @Field(() => Language, { nullable: true }) language: Language - @Field(() => String, { nullable: false }) + @Field(() => String, { nullable: true }) submissionTime: Date @Field(() => Int, { nullable: true }) codeSize?: number - @Field(() => String, { nullable: false }) - ip: string + @Field(() => String, { nullable: true }) + ip?: string } diff --git a/collection/admin/Contest/Get Contest Score Summary of User/Succeed.bru b/collection/admin/Contest/(Don't use) Get Contest Score Summary of User/Succeed.bru similarity index 100% rename from collection/admin/Contest/Get Contest Score Summary of User/Succeed.bru rename to collection/admin/Contest/(Don't use) Get Contest Score Summary of User/Succeed.bru diff --git a/collection/admin/Contest/Get Contest Submission Summaries of User/Success.bru b/collection/admin/Contest/Get Contest Submission Summaries of User/Success.bru new file mode 100644 index 0000000000..330b4d8190 --- /dev/null +++ b/collection/admin/Contest/Get Contest Submission Summaries of User/Success.bru @@ -0,0 +1,48 @@ +meta { + name: Success + type: graphql + seq: 1 +} + +post { + url: {{gqlUrl}} + body: graphql + auth: none +} + +body:graphql { + query getContestSubmissionSummariesByUserId($contestId: Int!, $userId: Int!) { + getContestSubmissionSummaryByUserId(contestId: $contestId, userId: $userId) { + scoreSummary { + contestPerfectScore + problemScores { + problemId + score + } + submittedProblemCount + totalProblemCount + userContestScore + } + submissions { + contestId + problemTitle + studentId + username + submissionResult + language + submissionTime + codeSize + ip + } + } + } + +} + +body:graphql:vars { + { + "contestId": 1, + "userId": 4 + // "problemId": 1 + } +} diff --git a/collection/admin/Contest/Get Contest Submission Summaries/Success.bru b/collection/admin/Contest/Get Contest Submission Summaries/Success.bru deleted file mode 100644 index bb8fb8d776..0000000000 --- a/collection/admin/Contest/Get Contest Submission Summaries/Success.bru +++ /dev/null @@ -1,32 +0,0 @@ -meta { - name: Success - type: graphql - seq: 1 -} - -post { - url: {{gqlUrl}} - body: graphql - auth: none -} - -body:graphql { - query getContestSubmissionSummaries($contestId: Int!, $userId: Int!) { - getContestSubmissionSummaries(contestId: $contestId, userId: $userId) { - contestId - problemTitle - language - submissionTime - ip - codeSize - submissionResult - } - } -} - -body:graphql:vars { - { - "contestId": 1, - "userId": 4 - } -} From cde2af942c88b24167eeb9883ccd635319d4871d Mon Sep 17 00:00:00 2001 From: Jaehyeon Kim <65964601+Jaehyeon1020@users.noreply.github.com> Date: Mon, 26 Aug 2024 08:40:19 +0000 Subject: [PATCH 13/19] docs(be): rename files --- .../Succeed.bru | 0 .../{Success.bru => Succeed.bru} | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename collection/admin/Contest/{(Don't use) Get Contest Score Summary of User => Get Contest Score Summary of User}/Succeed.bru (100%) rename collection/admin/Contest/Get Contest Submission Summaries of User/{Success.bru => Succeed.bru} (97%) diff --git a/collection/admin/Contest/(Don't use) Get Contest Score Summary of User/Succeed.bru b/collection/admin/Contest/Get Contest Score Summary of User/Succeed.bru similarity index 100% rename from collection/admin/Contest/(Don't use) Get Contest Score Summary of User/Succeed.bru rename to collection/admin/Contest/Get Contest Score Summary of User/Succeed.bru diff --git a/collection/admin/Contest/Get Contest Submission Summaries of User/Success.bru b/collection/admin/Contest/Get Contest Submission Summaries of User/Succeed.bru similarity index 97% rename from collection/admin/Contest/Get Contest Submission Summaries of User/Success.bru rename to collection/admin/Contest/Get Contest Submission Summaries of User/Succeed.bru index 330b4d8190..bf9fd6158c 100644 --- a/collection/admin/Contest/Get Contest Submission Summaries of User/Success.bru +++ b/collection/admin/Contest/Get Contest Submission Summaries of User/Succeed.bru @@ -1,5 +1,5 @@ meta { - name: Success + name: Succeed type: graphql seq: 1 } From f9b2cbfcc832ab9f7869d3bdf890fcdd3e50a593 Mon Sep 17 00:00:00 2001 From: Jaehyeon Kim <65964601+Jaehyeon1020@users.noreply.github.com> Date: Mon, 26 Aug 2024 08:42:36 +0000 Subject: [PATCH 14/19] docs(be): rename file --- .../Succeed.bru | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename collection/admin/Contest/{Get Contest Score Summary of User => Don't use - Get Contest Score Summary of User}/Succeed.bru (100%) diff --git a/collection/admin/Contest/Get Contest Score Summary of User/Succeed.bru b/collection/admin/Contest/Don't use - Get Contest Score Summary of User/Succeed.bru similarity index 100% rename from collection/admin/Contest/Get Contest Score Summary of User/Succeed.bru rename to collection/admin/Contest/Don't use - Get Contest Score Summary of User/Succeed.bru From 7d820ee5dd3dd3b2b3ad4eb4bc0920e1c7bd8c95 Mon Sep 17 00:00:00 2001 From: Jaehyeon Kim <65964601+Jaehyeon1020@users.noreply.github.com> Date: Mon, 26 Aug 2024 08:45:01 +0000 Subject: [PATCH 15/19] docs(be): rename files --- .../Succeed.bru | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename collection/admin/Contest/{Don't use - Get Contest Score Summary of User => Get Contest Score Summary of User}/Succeed.bru (100%) diff --git a/collection/admin/Contest/Don't use - Get Contest Score Summary of User/Succeed.bru b/collection/admin/Contest/Get Contest Score Summary of User/Succeed.bru similarity index 100% rename from collection/admin/Contest/Don't use - Get Contest Score Summary of User/Succeed.bru rename to collection/admin/Contest/Get Contest Score Summary of User/Succeed.bru From c10bcb3bc5a75499a78625ba92618188298e0e89 Mon Sep 17 00:00:00 2001 From: Jimin Ha Date: Tue, 27 Aug 2024 13:52:51 +0000 Subject: [PATCH 16/19] fix(be): fix ContestSubmissionSummaryForOne model --- .../contest-submission-summary-for-user.model.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/backend/apps/admin/src/contest/model/contest-submission-summary-for-user.model.ts b/apps/backend/apps/admin/src/contest/model/contest-submission-summary-for-user.model.ts index 8b10324555..598df5c5ae 100644 --- a/apps/backend/apps/admin/src/contest/model/contest-submission-summary-for-user.model.ts +++ b/apps/backend/apps/admin/src/contest/model/contest-submission-summary-for-user.model.ts @@ -16,25 +16,25 @@ export class ContestSubmissionSummaryForUser { */ @ObjectType({ description: 'ContestSubmissionSummaryForOne' }) export class ContestSubmissionSummaryForOne { - @Field(() => Int, { nullable: true }) + @Field(() => Int, { nullable: false }) contestId: number - @Field(() => String, { nullable: true }) + @Field(() => String, { nullable: false }) problemTitle: string - @Field(() => String, { nullable: true }) + @Field(() => String, { nullable: false }) username: string - @Field(() => String, { nullable: true }) + @Field(() => String, { nullable: false }) studentId: string - @Field(() => ResultStatus, { nullable: true }) + @Field(() => ResultStatus, { nullable: false }) submissionResult: ResultStatus // Accepted, RuntimeError, ... - @Field(() => Language, { nullable: true }) + @Field(() => Language, { nullable: false }) language: Language - @Field(() => String, { nullable: true }) + @Field(() => String, { nullable: false }) submissionTime: Date @Field(() => Int, { nullable: true }) From 7dadc1f7e75cf8c57d4ee0fcb3adc95917062e88 Mon Sep 17 00:00:00 2001 From: Jaehyeon Kim <65964601+Jaehyeon1020@users.noreply.github.com> Date: Tue, 27 Aug 2024 14:55:00 +0000 Subject: [PATCH 17/19] docs(be): remove resolved todo --- apps/backend/apps/admin/src/contest/contest.resolver.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/backend/apps/admin/src/contest/contest.resolver.ts b/apps/backend/apps/admin/src/contest/contest.resolver.ts index 7b9c0e3615..63a1430e43 100644 --- a/apps/backend/apps/admin/src/contest/contest.resolver.ts +++ b/apps/backend/apps/admin/src/contest/contest.resolver.ts @@ -230,7 +230,6 @@ export class ContestResolver { } } - // TODO: 특정 User의 특정 Contest에 대한 점수만 요약해 불러오는 함수 필요 @Query(() => ContestSubmissionSummaryForUser) async getContestSubmissionSummaryByUserId( @Args('contestId', { type: () => Int }, IDValidationPipe) contestId: number, From c2d584efa45b354954a64274857b55050ec5ded3 Mon Sep 17 00:00:00 2001 From: Jimin Ha Date: Wed, 28 Aug 2024 04:07:31 +0000 Subject: [PATCH 18/19] chore(be): lint contest.service.spec --- apps/backend/apps/admin/src/contest/contest.service.spec.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/backend/apps/admin/src/contest/contest.service.spec.ts b/apps/backend/apps/admin/src/contest/contest.service.spec.ts index e2046404f8..8a9601c94d 100644 --- a/apps/backend/apps/admin/src/contest/contest.service.spec.ts +++ b/apps/backend/apps/admin/src/contest/contest.service.spec.ts @@ -4,7 +4,7 @@ import { ContestProblem, Group, ContestRecord } from '@generated' import { Problem } from '@generated' import { Contest } from '@generated' import { faker } from '@faker-js/faker' -import { Prisma, ResultStatus } from '@prisma/client' +import { ResultStatus } from '@prisma/client' import type { Cache } from 'cache-manager' import { expect } from 'chai' import { stub } from 'sinon' From fb7922d772ed91283e9158d1c88ca48b20a228a2 Mon Sep 17 00:00:00 2001 From: Jimin Ha Date: Wed, 28 Aug 2024 04:36:33 +0000 Subject: [PATCH 19/19] fix(be): don't throw error when no submission --- .../apps/admin/src/contest/contest.service.ts | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/apps/backend/apps/admin/src/contest/contest.service.ts b/apps/backend/apps/admin/src/contest/contest.service.ts index 41ba74fd44..8961dd6263 100644 --- a/apps/backend/apps/admin/src/contest/contest.service.ts +++ b/apps/backend/apps/admin/src/contest/contest.service.ts @@ -661,15 +661,23 @@ export class ContestService { } }) ]) - - if (!submissions.length) { - throw new EntityNotExistException('Submissions') - } else if (!contestProblems.length) { + if (!contestProblems.length) { throw new EntityNotExistException('ContestProblems') } else if (!contestRecord) { throw new EntityNotExistException('contestRecord') } - + if (!submissions.length) { + return { + submittedProblemCount: 0, + totalProblemCount: contestProblems.length, + userContestScore: 0, + contestPerfectScore: contestProblems.reduce( + (total, { score }) => total + score, + 0 + ), + problemScores: [] + } + } // 하나의 Problem에 대해 여러 개의 Submission이 존재한다면, 마지막에 제출된 Submission만을 점수 계산에 반영함 const latestSubmissions: { [problemId: string]: {