Skip to content

Commit

Permalink
Merge branch 'main' into t1009-apply-search-sort-on-overall-apis
Browse files Browse the repository at this point in the history
  • Loading branch information
Jaehyeon1020 authored Nov 21, 2024
2 parents bb3d6ea + 0e09b52 commit cf6f055
Show file tree
Hide file tree
Showing 361 changed files with 6,542 additions and 5,031 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ jobs:
working-directory: ./apps/iris

- name: Lint (Node.js)
run: git diff --name-only --diff-filter=ACMRUXB origin/main | grep -E "(.ts$|.tsx$|.js$|.jsx$)" | xargs -r pnpm eslint
run: git diff --name-only --diff-filter=ACMRUXB origin/main | grep -E "(.ts$|.tsx$|.js$|.jsx$)" | grep -v 'next.config.js$' | xargs -r pnpm eslint

test-backend:
name: Test Backend
Expand Down
2 changes: 1 addition & 1 deletion apps/backend/apps/admin/src/contest/contest.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { ContestSubmissionSummaryForUser } from './model/contest-submission-summ
import { ContestWithParticipants } from './model/contest-with-participants.model'
import { CreateContestInput } from './model/contest.input'
import { UpdateContestInput } from './model/contest.input'
import { ContestsGroupedByStatus } from './model/contests-grouped-by-status'
import { ContestsGroupedByStatus } from './model/contests-grouped-by-status.output'
import { DuplicatedContestResponse } from './model/duplicated-contest-response.output'
import { ProblemScoreInput } from './model/problem-score.input'
import { PublicizingRequest } from './model/publicizing-request.model'
Expand Down
62 changes: 44 additions & 18 deletions apps/backend/apps/admin/src/contest/contest.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
UnprocessableDataException
} from '@libs/exception'
import { PrismaService } from '@libs/prisma'
import type { ContestWithScores } from './model/contest-with-scores.model'
import type { CreateContestInput } from './model/contest.input'
import type { UpdateContestInput } from './model/contest.input'
import type { ProblemScoreInput } from './model/problem-score.input'
Expand Down Expand Up @@ -379,11 +380,6 @@ export class ContestService {
if (!contest) {
throw new EntityNotExistException('contest')
}
if (contest.submission.length) {
throw new UnprocessableDataException(
'Cannot import problems if submission exists'
)
}

const contestProblems: ContestProblem[] = []

Expand Down Expand Up @@ -467,11 +463,6 @@ export class ContestService {
if (!contest) {
throw new EntityNotExistException('contest')
}
if (contest.submission.length) {
throw new UnprocessableDataException(
'Cannot delete problems if submission exists'
)
}

const contestProblems: ContestProblem[] = []

Expand Down Expand Up @@ -708,7 +699,7 @@ export class ContestService {
* 특정 user의 특정 Contest에 대한 총점, 통과한 문제 개수와 각 문제별 테스트케이스 통과 개수를 불러옵니다.
*/
async getContestScoreSummary(userId: number, contestId: number) {
const [contestProblems, submissions] = await Promise.all([
const [contestProblems, rawSubmissions] = await Promise.all([
this.prisma.contestProblem.findMany({
where: {
contestId
Expand All @@ -725,6 +716,14 @@ export class ContestService {
})
])

// 오직 현재 Contest에 남아있는 문제들의 제출에 대해서만 ScoreSummary 계산
const contestProblemIds = contestProblems.map(
(contestProblem) => contestProblem.problemId
)
const submissions = rawSubmissions.filter((submission) =>
contestProblemIds.includes(submission.problemId)
)

if (!submissions.length) {
return {
submittedProblemCount: 0,
Expand Down Expand Up @@ -856,7 +855,8 @@ export class ContestService {
select: {
realName: true
}
}
},
major: true
}
}
},
Expand All @@ -874,6 +874,7 @@ export class ContestService {
username: record.user?.username,
studentId: record.user?.studentId,
realName: record.user?.userProfile?.realName,
major: record.user?.major,
...(await this.getContestScoreSummary(
record.userId as number,
contestId
Expand All @@ -891,16 +892,25 @@ export class ContestService {
problemId
},
select: {
contest: true
contest: true,
score: true
}
})

if (!contestProblems.length) {
throw new EntityNotExistException('Problem or ContestProblem')
}

const contests = contestProblems.map(
(contestProblem) => contestProblem.contest
const contests = await Promise.all(
contestProblems.map(async (contestProblem) => {
return {
...contestProblem.contest,
problemScore: contestProblem.score,
totalScore: await this.getTotalScoreOfContest(
contestProblem.contest.id
)
}
})
)

const now = new Date()
Expand All @@ -919,12 +929,28 @@ export class ContestService {
return acc
},
{
upcoming: [] as Contest[],
ongoing: [] as Contest[],
finished: [] as Contest[]
upcoming: [] as ContestWithScores[],
ongoing: [] as ContestWithScores[],
finished: [] as ContestWithScores[]
}
)

return contestsGroupedByStatus
}

async getTotalScoreOfContest(contestId: number) {
const contestProblemScores = await this.prisma.contestProblem.findMany({
where: {
contestId
},
select: {
score: true
}
})

return contestProblemScores.reduce(
(total, problem) => total + problem.score,
0
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { Field, Int, ObjectType } from '@nestjs/graphql'
import { Contest } from '@admin/@generated'

@ObjectType()
export class ContestWithScores extends Contest {
@Field(() => Int)
problemScore: number

@Field(() => Int)
totalScore: number
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { Field, ObjectType } from '@nestjs/graphql'
import { ContestWithScores } from './contest-with-scores.model'

@ObjectType()
export class ContestsGroupedByStatus {
@Field(() => [ContestWithScores])
upcoming: ContestWithScores[]

@Field(() => [ContestWithScores])
ongoing: ContestWithScores[]

@Field(() => [ContestWithScores])
finished: ContestWithScores[]
}

This file was deleted.

3 changes: 3 additions & 0 deletions apps/backend/apps/admin/src/contest/model/score-summary.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ export class UserContestScoreSummaryWithUserInfo {
@Field(() => String, { nullable: true })
realName: string

@Field(() => String)
major: string

@Field(() => Int)
submittedProblemCount: number

Expand Down
15 changes: 15 additions & 0 deletions apps/backend/apps/admin/src/problem/problem.resolver.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import {
UnprocessableDataException
} from '@libs/exception'
import { CursorValidationPipe, GroupIDPipe, RequiredIntPipe } from '@libs/pipe'
import { ProblemScoreInput } from '@admin/contest/model/problem-score.input'
import { ImageSource } from './model/image.output'
import {
CreateProblemInput,
Expand Down Expand Up @@ -293,6 +294,20 @@ export class ContestProblemResolver {
}
}

@Mutation(() => [ContestProblem])
async updateContestProblemsScore(
@Args('groupId', { type: () => Int }, GroupIDPipe) groupId: number,
@Args('contestId', { type: () => Int }) contestId: number,
@Args('problemIdsWithScore', { type: () => [ProblemScoreInput] })
problemIdsWithScore: ProblemScoreInput[]
) {
return await this.problemService.updateContestProblemsScore(
groupId,
contestId,
problemIdsWithScore
)
}

@Mutation(() => [ContestProblem])
async updateContestProblemsOrder(
@Args(
Expand Down
26 changes: 26 additions & 0 deletions apps/backend/apps/admin/src/problem/problem.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
UnprocessableFileDataException
} from '@libs/exception'
import { PrismaService } from '@libs/prisma'
import type { ProblemScoreInput } from '@admin/contest/model/problem-score.input'
import { StorageService } from '@admin/storage/storage.service'
import { ImportedProblemHeader } from './model/problem.constants'
import type {
Expand Down Expand Up @@ -603,6 +604,31 @@ export class ProblemService {
return contestProblems
}

async updateContestProblemsScore(
groupId: number,
contestId: number,
problemIdsWithScore: ProblemScoreInput[]
): Promise<Partial<ContestProblem>[]> {
await this.prisma.contest.findFirstOrThrow({
where: { id: contestId, groupId }
})

const queries = problemIdsWithScore.map((record) => {
return this.prisma.contestProblem.update({
where: {
// eslint-disable-next-line @typescript-eslint/naming-convention
contestId_problemId: {
contestId,
problemId: record.problemId
}
},
data: { score: record.score }
})
})

return await this.prisma.$transaction(queries)
}

async updateContestProblemsOrder(
groupId: number,
contestId: number,
Expand Down
69 changes: 9 additions & 60 deletions apps/backend/apps/admin/src/user/user.resolver.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,6 @@
import {
BadRequestException,
InternalServerErrorException,
ConflictException,
Logger,
NotFoundException
} from '@nestjs/common'
import { Resolver, Query, Mutation, Args, Int } from '@nestjs/graphql'
import { UserGroup } from '@generated'
import { User } from '@generated'
import { PrismaClientKnownRequestError } from '@prisma/client/runtime/library'
import { OPEN_SPACE_ID } from '@libs/constants'
import { CursorValidationPipe, GroupIDPipe, RequiredIntPipe } from '@libs/pipe'
import { GroupMember } from './model/groupMember.model'
Expand All @@ -17,7 +9,6 @@ import { UserService } from './user.service'
@Resolver(() => User)
export class UserResolver {
constructor(private readonly userService: UserService) {}
private readonly logger = new Logger(UserResolver.name)

@Query(() => [GroupMember])
async getGroupMembers(
Expand Down Expand Up @@ -56,16 +47,7 @@ export class UserResolver {
@Args('userId', { type: () => Int }, new RequiredIntPipe('userId'))
userId: number
) {
try {
return await this.userService.getGroupMember(groupId, userId)
} catch (error) {
if (
error instanceof PrismaClientKnownRequestError &&
error.code == 'P2025'
) {
throw new NotFoundException(error.message)
}
}
return await this.userService.getGroupMember(groupId, userId)
}

@Mutation(() => UserGroup)
Expand All @@ -75,21 +57,11 @@ export class UserResolver {
@Args('groupId', { type: () => Int }, GroupIDPipe) groupId: number,
@Args('toGroupLeader') toGroupLeader: boolean
) {
try {
return await this.userService.updateGroupRole(
userId,
groupId,
toGroupLeader
)
} catch (error) {
if (error instanceof BadRequestException) {
throw new BadRequestException(error.message)
} else if (error instanceof NotFoundException) {
throw new NotFoundException(error.message)
}
this.logger.error(error)
throw new InternalServerErrorException()
}
return await this.userService.updateGroupRole(
userId,
groupId,
toGroupLeader
)
}

@Mutation(() => UserGroup)
Expand All @@ -98,29 +70,14 @@ export class UserResolver {
userId: number,
@Args('groupId', { type: () => Int }, GroupIDPipe) groupId: number
) {
try {
return await this.userService.deleteGroupMember(userId, groupId)
} catch (error) {
if (error instanceof BadRequestException) {
throw new BadRequestException(error.message)
} else if (error instanceof NotFoundException) {
throw new NotFoundException(error.message)
}
this.logger.error(error)
throw new InternalServerErrorException()
}
return await this.userService.deleteGroupMember(userId, groupId)
}

@Query(() => [User])
async getJoinRequests(
@Args('groupId', { type: () => Int }, GroupIDPipe) groupId: number
) {
try {
return await this.userService.getJoinRequests(groupId)
} catch (error) {
this.logger.error(error)
throw new InternalServerErrorException()
}
return await this.userService.getJoinRequests(groupId)
}

@Mutation(() => UserGroup)
Expand All @@ -130,14 +87,6 @@ export class UserResolver {
userId: number,
@Args('isAccept') isAccept: boolean
) {
try {
return await this.userService.handleJoinRequest(groupId, userId, isAccept)
} catch (error) {
if (error instanceof ConflictException) {
throw new ConflictException(error.message)
}
this.logger.error(error)
throw new InternalServerErrorException()
}
return await this.userService.handleJoinRequest(groupId, userId, isAccept)
}
}
Loading

0 comments on commit cf6f055

Please sign in to comment.