Skip to content

fix(be): fix contest leaderboard ranking malfunction#3419

Open
zero1177 wants to merge 8 commits intomainfrom
t2552-modify-contest-statistics-leaderboard
Open

fix(be): fix contest leaderboard ranking malfunction#3419
zero1177 wants to merge 8 commits intomainfrom
t2552-modify-contest-statistics-leaderboard

Conversation

@zero1177
Copy link
Copy Markdown
Contributor

@zero1177 zero1177 commented Feb 10, 2026

Description

Contest 리더보드 순위 오류 수정

변경사항

  • solvedProblemCount(1순위)가 동일할 경우, penalty(2순위)가 적은 user가 상위 rank에 오도록 정렬 로직 추가
  • lastAcceptedTime 기준으로 정답 제출이 더 빠른 유저가 상위 등수이도록 3순위 정렬 로직 추가

테스트

contest 7 기준
contestProblem 문제는 problemId 5,6,7,8 총 4개 (점수 각각 50, 60, 70, 80)

user02~user08 까지 7명의 user 대회 기록 생성 (seed.ts)

  • solvedProblemCount가 동일할 경우, penalty가 적은 user가 상위 rank
  • solvedProblemCount, penalty까지 모두 동일할 경우 lastAcceptedTime이 작은 (정답 제출이 더 빠른) 유저가 상위 rank
const testData = [
    {
      // user02 (2등) 3솔
      scores: [50, 60, 70, 0], //180점
      penalties: [30, 30, 40, 0], //100
      finishTime: [
        new Date('2026-03-01T09:20:00Z'),
        new Date('2026-03-01T09:35:00Z'),
        new Date('2026-03-01T10:20:00Z'),
        null
      ]
    },
    {
      // user03 (1등) 4솔
      scores: [50, 60, 70, 80], //260
      penalties: [15, 100, 55, 200], //370
      finishTime: [
        new Date('2026-03-01T09:15:00Z'),
        new Date('2026-03-01T10:15:00Z'),
        new Date('2026-03-01T10:00:00Z'),
        new Date('2026-03-01T10:50:00Z')
      ]
    },
    {
      // user04 (3등) 3솔
      scores: [0, 60, 70, 80], //210
      penalties: [0, 150, 20, 300], //470
      finishTime: [
        null,
        new Date('2026-03-01T10:30:00Z'),
        new Date('2026-03-01T09:30:00Z'),
        new Date('2026-03-01T11:30:00Z')
      ]
    },
    {
      // user05 (6등) 2솔
      scores: [0, 0, 70, 80], //150
      penalties: [0, 0, 150, 100], //250
      finishTime: [
        null,
        null,
        new Date('2026-03-01T10:40:00Z'),
        new Date('2026-03-01T10:10:00Z')
      ]
    },
    {
      //user06 (7등) 1솔
      scores: [0, 0, 70, 0], //70
      penalties: [0, 0, 150, 0], //150
      finishTime: [null, null, new Date('2026-03-01T10:30:00Z'), null]
    },
    {
      //user07 (4등) 2솔 - user08보다 lastAcceptedTime이 빠름
      scores: [50, 60, 0, 0], //110
      penalties: [20, 35, 0, 0], //55
      finishTime: [
        new Date('2026-03-01T09:20:00Z'),
        new Date('2026-03-01T09:40:00Z'), // lastAccepted = 09:40
        null,
        null
      ]
    },
    {
      //user08 (5등) 2솔 - user07보다 lastAcceptedTime이 늦음
      scores: [0, 60, 70, 0], //130
      penalties: [0, 35, 20, 0], //55
      finishTime: [
        null,
        new Date('2026-03-01T09:55:00Z'),
        new Date('2026-03-01T10:05:00Z'), // lastAccepted = 10:05
        null
      ]
    }

Before submitting the PR, please make sure you do the following

@zero1177 zero1177 requested a review from hjkim24 February 10, 2026 15:08
@zero1177 zero1177 self-assigned this Feb 10, 2026
@zero1177 zero1177 added ⛳️ team-backend 🍊squad-유자차 스쿼드 유자차 작업물입니다 labels Feb 10, 2026
@skkuding-bot
Copy link
Copy Markdown

skkuding-bot Bot commented Feb 10, 2026

Syncing Preview App Succeeded

Application: frontend
Revision: 2e9ff4cc85cd6d061fdc1c3c9755bfcc3712cd32
Health Status: Healthy

Open Preview | View in Argo CD

@hjkim24
Copy link
Copy Markdown
Contributor

hjkim24 commented Mar 25, 2026

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the contest leaderboard calculation logic in ContestService, introducing custom sorting and ranking based on solved problems and penalties, and adds new seeding data for contestId7 to test these changes. The review highlights a performance regression in ContestService due to sequential database queries that should be parallelized using Promise.all. Additionally, the new ranking logic in ContestService does not correctly handle ties, and a related comment is misleading. In prisma/seed.ts, the newly created contest7Records are not included in the function's return value, and there's a typo in a comment describing the number of solved problems for a test user.

Comment thread apps/backend/apps/client/src/contest/contest.service.ts Outdated
Comment thread apps/backend/apps/client/src/contest/contest.service.ts Outdated
Comment thread apps/backend/apps/client/src/contest/contest.service.ts Outdated
Comment thread apps/backend/prisma/seed.ts Outdated
Comment thread apps/backend/prisma/seed.ts Outdated
@hjkim24
Copy link
Copy Markdown
Contributor

hjkim24 commented Apr 26, 2026

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the leaderboard calculation logic in ContestService, moving sorting and ranking from the database query to application memory. It introduces manual calculation of scores, penalties, and solved problem counts, and implements a ranking algorithm to handle ties. Additionally, seed data for testing these changes was added. The review feedback suggests enhancing the sorting and ranking logic by incorporating totalScore and lastAcceptedTime as tie-breakers to ensure fairness and accuracy.

Comment thread apps/backend/apps/client/src/contest/contest.service.ts
Comment thread apps/backend/apps/client/src/contest/contest.service.ts
Comment thread apps/backend/apps/client/src/contest/contest.service.ts
Comment thread apps/backend/apps/client/src/contest/contest.service.ts
Comment thread apps/backend/apps/client/src/contest/contest.service.ts
Copilot AI review requested due to automatic review settings April 27, 2026 12:59
@zero1177
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the contest leaderboard logic to calculate rankings based on the number of solved problems, total penalty, and the last accepted time. It also introduces new seed data for testing these ranking criteria. Feedback highlights a logic mismatch between the sorting and ranking assignment steps regarding totalScore, potential inconsistencies with other statistics methods in the service, and missing finishTime data in the new test seeds.

Comment thread apps/backend/apps/client/src/contest/contest.service.ts
Comment thread apps/backend/apps/client/src/contest/contest.service.ts
Comment thread apps/backend/prisma/seed.ts
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes incorrect contest leaderboard ranking by changing how leaderboard entries are aggregated and ranked (with additional seed data to reproduce/test the issue).

Changes:

  • Reworked /contest/:id/leaderboard ranking to derive solved/penalty from per-problem records and sort accordingly.
  • Added seed data for contest 7 to reproduce leaderboard ranking scenarios.
  • Updated the Bruno collection request to call contest 7’s leaderboard.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 8 comments.

File Description
collection/client/Contest/Get Leaderboard/Succeed.bru Points the sample leaderboard request to contest id 7 for testing.
apps/backend/prisma/seed.ts Adds contest 7 seed records intended for leaderboard ranking validation.
apps/backend/apps/client/src/contest/contest.service.ts Recomputes leaderboard totals and implements in-memory sorting + rank assignment.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread apps/backend/prisma/seed.ts
Comment thread apps/backend/prisma/seed.ts
Comment thread apps/backend/prisma/seed.ts Outdated
Comment thread apps/backend/prisma/seed.ts
Comment thread apps/backend/prisma/seed.ts Outdated
Comment thread apps/backend/apps/client/src/contest/contest.service.ts Outdated
Comment thread apps/backend/apps/client/src/contest/contest.service.ts
Comment thread apps/backend/apps/client/src/contest/contest.service.ts
Co-authored-by: Copilot <copilot@github.com>
@zero1177
Copy link
Copy Markdown
Contributor Author

/gemini review

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the contest leaderboard logic to include lastAcceptedTime as a tie-breaking criterion and transitions to manual rank calculation within the service. It also adds comprehensive seed data to test these ranking scenarios and updates the associated API test collection. Feedback suggests centralizing the new sorting logic within the calculateRanks helper to ensure consistency across different statistics views and updating the API documentation in the .bru file to reflect the new response fields.

Comment on lines +595 to +610
leaderboard.sort((a, b) => {
//1순위: 맞춘 문제 개수 많을수록 상위
if (b.solvedProblemCount !== a.solvedProblemCount) {
return b.solvedProblemCount - a.solvedProblemCount
}

//2순위: 패널티 작을수록 상위
if (a.totalPenalty !== b.totalPenalty) {
return a.totalPenalty - b.totalPenalty
}

//3순위: 맞춘문제, 패널티 모두 동점일 경우 마지막 정답 제출시간 빠를수록 상위
const timeA = a.lastAcceptedTime?.getTime() ?? Number.MAX_SAFE_INTEGER
const timeB = b.lastAcceptedTime?.getTime() ?? Number.MAX_SAFE_INTEGER
return timeA - timeB
})
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

The sorting logic for the leaderboard has been updated to include lastAcceptedTime as a third-tier criterion. However, this logic is now inlined here, while other methods like getContestUsersStatistics and getContestUserStatistics still rely on the calculateRanks helper method which does not yet account for lastAcceptedTime. This will result in inconsistent rankings across different views (e.g., the leaderboard vs. the statistics page). It is recommended to update the calculateRanks method to include all three criteria and use it consistently throughout the service to maintain a single source of truth for ranking logic.


params:path {
id: 1
id: 7
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

While updating the contest ID for the test, the documentation for the response format (lines 37-79) was not updated to reflect the new fields added to the leaderboard records (solvedProblemCount, lastAcceptedTime). Please update the documentation to ensure it remains accurate and helpful for API consumers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🍊squad-유자차 스쿼드 유자차 작업물입니다 ⛳️ team-backend

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants