1- import { includes , isEmpty , sortBy , find , camelCase , groupBy } from 'lodash' ;
1+ import {
2+ includes ,
3+ isEmpty ,
4+ sortBy ,
5+ find ,
6+ camelCase ,
7+ groupBy ,
8+ orderBy ,
9+ } from 'lodash' ;
210import { Injectable } from '@nestjs/common' ;
311import { ENV_CONFIG } from 'src/config' ;
412import { Logger } from 'src/shared/global' ;
@@ -11,18 +19,14 @@ import { WinningsCategory, WinningsType } from 'src/dto/winning.dto';
1119import { WinningsRepository } from '../repository/winnings.repo' ;
1220
1321const placeToOrdinal = ( place : number ) => {
14- if ( place === 1 ) return " 1st" ;
15- if ( place === 2 ) return " 2nd" ;
16- if ( place === 3 ) return " 3rd" ;
22+ if ( place === 1 ) return ' 1st' ;
23+ if ( place === 2 ) return ' 2nd' ;
24+ if ( place === 3 ) return ' 3rd' ;
1725
1826 return `${ place } th` ;
19- }
20-
21- const {
22- TOPCODER_API_V6_BASE_URL ,
23- TGBillingAccounts,
24- } = ENV_CONFIG ;
27+ } ;
2528
29+ const { TOPCODER_API_V6_BASE_URL , TGBillingAccounts } = ENV_CONFIG ;
2630
2731@Injectable ( )
2832export class ChallengesService {
@@ -42,30 +46,45 @@ export class ChallengesService {
4246 const challenge = await this . m2MService . m2mFetch < Challenge > ( requestUrl ) ;
4347 this . logger . log ( JSON . stringify ( challenge , null , 2 ) ) ;
4448 return challenge ;
45- } catch ( e ) {
46- this . logger . error ( `Challenge ${ challengeId } details couldn't be fetched!` , e ) ;
49+ } catch ( e ) {
50+ this . logger . error (
51+ `Challenge ${ challengeId } details couldn't be fetched!` ,
52+ e ,
53+ ) ;
4754 }
4855 }
4956
5057 async getChallengeResources ( challengeId : string ) {
5158 try {
52- const resources = await this . m2MService . m2mFetch < ChallengeResource [ ] > ( `${ TOPCODER_API_V6_BASE_URL } /resources?challengeId=${ challengeId } ` ) ;
53- const resourceRoles = await this . m2MService . m2mFetch < ResourceRole [ ] > ( `${ TOPCODER_API_V6_BASE_URL } /resource-roles` ) ;
54-
55- const rolesMap = resourceRoles . reduce ( ( map , role ) => {
56- map [ role . id ] = camelCase ( role . name ) ;
57- return map ;
58- } , { } as { [ key : string ] : string } ) ;
59-
60- return groupBy ( resources , ( r ) => rolesMap [ r . roleId ] ) as { [ role : string ] : ChallengeResource [ ] } ;
61- } catch ( e ) {
62- this . logger . error ( `Challenge resources for challenge ${ challengeId } couldn\'t be fetched!` , e ) ;
59+ const resources = await this . m2MService . m2mFetch < ChallengeResource [ ] > (
60+ `${ TOPCODER_API_V6_BASE_URL } /resources?challengeId=${ challengeId } ` ,
61+ ) ;
62+ const resourceRoles = await this . m2MService . m2mFetch < ResourceRole [ ] > (
63+ `${ TOPCODER_API_V6_BASE_URL } /resource-roles` ,
64+ ) ;
65+
66+ const rolesMap = resourceRoles . reduce (
67+ ( map , role ) => {
68+ map [ role . id ] = camelCase ( role . name ) ;
69+ return map ;
70+ } ,
71+ { } as { [ key : string ] : string } ,
72+ ) ;
73+
74+ return groupBy ( resources , ( r ) => rolesMap [ r . roleId ] ) as {
75+ [ role : string ] : ChallengeResource [ ] ;
76+ } ;
77+ } catch ( e ) {
78+ this . logger . error (
79+ `Challenge resources for challenge ${ challengeId } couldn\'t be fetched!` ,
80+ e ,
81+ ) ;
6382 }
6483 }
6584
6685 async getChallengePayments ( challenge : Challenge ) {
6786 this . logger . log (
68- `Generating payments for challenge ${ challenge . name } (${ challenge . id } ).`
87+ `Generating payments for challenge ${ challenge . name } (${ challenge . id } ).` ,
6988 ) ;
7089 const challengeResources = await this . getChallengeResources ( challenge . id ) ;
7190
@@ -87,25 +106,37 @@ export class ChallengesService {
87106 ChallengeStatuses . CancelledFailedReview . toLowerCase ( ) ;
88107
89108 // generate placement payments
90- const placementPrizes = sortBy ( find ( prizeSets , { type : 'PLACEMENT' } ) ?. prizes , 'value' ) ;
109+ const placementPrizes = orderBy (
110+ find ( prizeSets , { type : 'PLACEMENT' } ) ?. prizes ,
111+ 'value' ,
112+ 'desc' ,
113+ ) ;
114+
91115 if ( ! isCancelledFailedReview ) {
92116 if ( placementPrizes . length < winners . length ) {
93- throw new Error ( 'Task has incorrect number of placement prizes! There are more winners than prizes!' ) ;
117+ throw new Error (
118+ 'Task has incorrect number of placement prizes! There are more winners than prizes!' ,
119+ ) ;
94120 }
95121
96122 winners . forEach ( ( winner ) => {
97123 payments . push ( {
98124 handle : winner . handle ,
99125 amount : placementPrizes [ winner . placement - 1 ] . value ,
100126 userId : winner . userId . toString ( ) ,
101- type : challenge . task . isTask ? WinningsCategory . TASK_PAYMENT : WinningsCategory . CONTEST_PAYMENT ,
102- description : challenge . type === 'Task' ? challenge . name : `${ challenge . name } - ${ placeToOrdinal ( winner . placement ) } Place` ,
127+ type : challenge . task . isTask
128+ ? WinningsCategory . TASK_PAYMENT
129+ : WinningsCategory . CONTEST_PAYMENT ,
130+ description :
131+ challenge . type === 'Task'
132+ ? challenge . name
133+ : `${ challenge . name } - ${ placeToOrdinal ( winner . placement ) } Place` ,
103134 } ) ;
104135 } ) ;
105136 }
106137
107138 // generate copilot payments
108- const copilotPrizes = find ( prizeSets , { type : 'COPILOT' } ) ?. prizes ?? [ ] ;
139+ const copilotPrizes = find ( prizeSets , { type : 'COPILOT' } ) ?. prizes ?? [ ] ;
109140 if ( copilotPrizes . length && ! isCancelledFailedReview ) {
110141 const copilots = challengeResources . copilot ;
111142
@@ -119,8 +150,8 @@ export class ChallengesService {
119150 amount : copilotPrizes [ 0 ] . value ,
120151 userId : copilot . memberId . toString ( ) ,
121152 type : WinningsCategory . COPILOT_PAYMENT ,
122- } )
123- } )
153+ } ) ;
154+ } ) ;
124155 }
125156
126157 // generate reviewer payments
@@ -132,32 +163,45 @@ export class ChallengesService {
132163 payments . push ( {
133164 handle : reviewer . memberHandle ,
134165 userId : reviewer . memberId . toString ( ) ,
135- amount : Math . round ( ( challengeReviewer . basePayment ?? 0 ) + ( ( challengeReviewer . incrementalPayment ?? 0 ) * challenge . numOfSubmissions ) * firstPlacePrize ) ,
166+ amount : Math . round (
167+ ( challengeReviewer . basePayment ?? 0 ) +
168+ ( ( challengeReviewer . incrementalPayment ?? 0 ) / 100 ) *
169+ challenge . numOfSubmissions *
170+ firstPlacePrize ,
171+ ) ,
136172 type : WinningsCategory . REVIEW_BOARD_PAYMENT ,
137- } )
173+ } ) ;
138174 } ) ;
139175 }
140176
141- const totalAmount = payments . reduce ( ( sum , payment ) => sum + payment . amount , 0 ) ;
177+ const totalAmount = payments . reduce (
178+ ( sum , payment ) => sum + payment . amount ,
179+ 0 ,
180+ ) ;
142181 return payments . map ( ( payment ) => ( {
143182 winnerId : payment . userId . toString ( ) ,
144183 type : WinningsType . PAYMENT ,
145- origin : " Topcoder" ,
184+ origin : ' Topcoder' ,
146185 category : payment . type ,
147186 title : challenge . name ,
148187 description : payment . description || challenge . name ,
149188 externalId : challenge . id ,
150- details : [ {
151- totalAmount : payment . amount ,
152- grossAmount : payment . amount ,
153- installmentNumber : 1 ,
154- currency : "USD" ,
155- billingAccount : `${ challenge . billing . billingAccountId } ` ,
156- challengeFee : totalAmount * challenge . billing . markup ,
157- } ] ,
189+ details : [
190+ {
191+ totalAmount : payment . amount ,
192+ grossAmount : payment . amount ,
193+ installmentNumber : 1 ,
194+ currency : 'USD' ,
195+ billingAccount : `${ challenge . billing . billingAccountId } ` ,
196+ challengeFee : totalAmount * challenge . billing . markup ,
197+ } ,
198+ ] ,
158199 attributes : {
159200 billingAccountId : challenge . billing . billingAccountId ,
160- payroll : includes ( TGBillingAccounts , challenge . billing . billingAccountId ) ,
201+ payroll : includes (
202+ TGBillingAccounts ,
203+ challenge . billing . billingAccountId ,
204+ ) ,
161205 } ,
162206 } ) ) ;
163207 }
@@ -175,17 +219,26 @@ export class ChallengesService {
175219 ] ;
176220
177221 if ( ! allowedStatuses . includes ( challenge . status . toLowerCase ( ) ) ) {
178- throw new Error ( ' Challenge isn\ 't in a payable status!' ) ;
222+ throw new Error ( " Challenge isn't in a payable status!" ) ;
179223 }
180224
181- const existingPayments = ( await this . winningsRepo . searchWinnings ( { externalIds : [ challengeId ] } ) ) ?. data ?. winnings ;
225+ const existingPayments = (
226+ await this . winningsRepo . searchWinnings ( { externalIds : [ challengeId ] } )
227+ ) ?. data ?. winnings ;
182228 if ( existingPayments ?. length > 0 ) {
183- this . logger . log ( `Payments already exist for challenge ${ challengeId } , skipping payment generation` ) ;
184- throw new Error ( `Payments already exist for challenge ${ challengeId } , skipping payment generation` ) ;
229+ this . logger . log (
230+ `Payments already exist for challenge ${ challengeId } , skipping payment generation` ,
231+ ) ;
232+ throw new Error (
233+ `Payments already exist for challenge ${ challengeId } , skipping payment generation` ,
234+ ) ;
185235 }
186236
187237 const payments = await this . getChallengePayments ( challenge ) ;
188- const totalAmount = payments . reduce ( ( sum , payment ) => sum + payment . details [ 0 ] . totalAmount , 0 ) ;
238+ const totalAmount = payments . reduce (
239+ ( sum , payment ) => sum + payment . details [ 0 ] . totalAmount ,
240+ 0 ,
241+ ) ;
189242
190243 const baValidation = {
191244 challengeId : challenge . id ,
@@ -199,9 +252,13 @@ export class ChallengesService {
199252 baValidation . markup = challenge . billing . clientBillingRate ;
200253 }
201254
202- await Promise . all ( payments . map ( p => this . winningsService . createWinningWithPayments ( p , userId ) ) ) ;
255+ await Promise . all (
256+ payments . map ( ( p ) =>
257+ this . winningsService . createWinningWithPayments ( p , userId ) ,
258+ ) ,
259+ ) ;
203260
204- this . logger . log ( " Task Completed. locking consumed budget" , baValidation ) ;
261+ this . logger . log ( ' Task Completed. locking consumed budget' , baValidation ) ;
205262 await this . baService . lockConsumeAmount ( baValidation ) ;
206263 }
207264}
0 commit comments