From 703b781012019cf93096c297c9417bc0e1d6e19c Mon Sep 17 00:00:00 2001 From: Vasilica Olariu Date: Fri, 24 Oct 2025 09:58:41 +0300 Subject: [PATCH] PM-1105 - Checkpoint winners --- src/api/challenges/challenges.service.ts | 76 +++++++++++++++++++----- src/api/challenges/models/challenge.ts | 3 +- 2 files changed, 62 insertions(+), 17 deletions(-) diff --git a/src/api/challenges/challenges.service.ts b/src/api/challenges/challenges.service.ts index 9a9bad5..b08d372 100644 --- a/src/api/challenges/challenges.service.ts +++ b/src/api/challenges/challenges.service.ts @@ -14,7 +14,9 @@ import { Challenge, ChallengeResource, ChallengeReview, + Prize, ResourceRole, + Winner, } from './models'; import { BillingAccountsService } from 'src/shared/topcoder/billing-accounts.service'; import { TopcoderM2MService } from 'src/shared/topcoder/topcoder-m2m.service'; @@ -117,9 +119,12 @@ export class ChallengesService { } } - generateWinnersPayments(challenge: Challenge): PaymentPayload[] { - const { prizeSets, winners } = challenge; - + generateWinnersPayments( + challenge: Challenge, + winners: Winner[], + prizes: Prize[], + type?: WinningsCategory, + ): PaymentPayload[] { const isCancelledFailedReview = challenge.status.toLowerCase() === ChallengeStatuses.CancelledFailedReview.toLowerCase(); @@ -128,6 +133,53 @@ export class ChallengesService { return []; } + return winners.map((winner) => ({ + handle: winner.handle, + amount: prizes[winner.placement - 1].value, + userId: winner.userId.toString(), + type: + type ?? + (challenge.task.isTask + ? WinningsCategory.TASK_PAYMENT + : WinningsCategory.CONTEST_PAYMENT), + description: + challenge.type === 'Task' + ? challenge.name + : `${challenge.name} - ${placeToOrdinal(winner.placement)} Place`, + })); + } + + generateCheckpointWinnersPayments(challenge: Challenge): PaymentPayload[] { + const { prizeSets, checkpointWinners } = challenge; + + // generate placement payments + const checkpointPrizes = orderBy( + find(prizeSets, { type: 'CHECKPOINT' })?.prizes, + 'value', + 'desc', + ); + + if ((checkpointPrizes?.length ?? 0) < (checkpointWinners?.length ?? 0)) { + throw new Error( + 'Task has incorrect number of checkpoint prizes! There are more checkpoint winners than checkpoint prizes!', + ); + } + + if (!checkpointPrizes?.length) { + return []; + } + + return this.generateWinnersPayments( + challenge, + checkpointWinners, + checkpointPrizes, + WinningsCategory.CONTEST_CHECKPOINT_PAYMENT, + ); + } + + generatePlacementWinnersPayments(challenge: Challenge): PaymentPayload[] { + const { prizeSets, winners } = challenge; + // generate placement payments const placementPrizes = orderBy( find(prizeSets, { type: 'PLACEMENT' })?.prizes, @@ -141,18 +193,7 @@ export class ChallengesService { ); } - return winners.map((winner) => ({ - handle: winner.handle, - amount: placementPrizes[winner.placement - 1].value, - userId: winner.userId.toString(), - type: challenge.task.isTask - ? WinningsCategory.TASK_PAYMENT - : WinningsCategory.CONTEST_PAYMENT, - description: - challenge.type === 'Task' - ? challenge.name - : `${challenge.name} - ${placeToOrdinal(winner.placement)} Place`, - })); + return this.generateWinnersPayments(challenge, winners, placementPrizes); } generateCopilotPayment( @@ -284,7 +325,9 @@ export class ChallengesService { throw new Error('Missing challenge resources!'); } - const winnersPayments = this.generateWinnersPayments(challenge); + const winnersPayments = this.generatePlacementWinnersPayments(challenge); + const checkpointPayments = + this.generateCheckpointWinnersPayments(challenge); const copilotPayments = this.generateCopilotPayment( challenge, challengeResources.copilot, @@ -315,6 +358,7 @@ export class ChallengesService { const payments: PaymentPayload[] = [ ...winnersPayments, + ...checkpointPayments, ...copilotPayments, ...reviewersPayments, ]; diff --git a/src/api/challenges/models/challenge.ts b/src/api/challenges/models/challenge.ts index 945140a..635b057 100644 --- a/src/api/challenges/models/challenge.ts +++ b/src/api/challenges/models/challenge.ts @@ -36,7 +36,8 @@ export interface Challenge { created: string; updated: string; overview: PrizeOverview; - winners: Winner[]; // Replace with type if needed + winners: Winner[]; + checkpointWinners: Winner[]; numOfSubmissions: number; numOfCheckpointSubmissions: number; numOfRegistrants: number;