@@ -320,6 +320,74 @@ async function GetChallengeInfo(data) {
320320 }
321321}
322322
323+ // if a player is not in a team calculate collected points and display their username
324+ // otherwise calculate points collected by the team and display the team name
325+ async function GetLeaderboardData ( ) {
326+ // { name: STRING, points: NUMBER }
327+ // sort by point desending and if points match
328+ // sort based on time of recent completion (time attribute (time | timestamp) on last completion)
329+ let leaderboardData = [ ]
330+
331+ const soloUsers = await UserCollection . find ( { team_id : "None" } , { username : 1 , completions : 1 } )
332+ const teams = await TeamCollection . find ( { } , { name : 1 , completions : 1 } )
333+
334+ let readableSoloUsers = [ ] ;
335+ let readableTeams = [ ] ;
336+
337+ // for each user we need to calculate accumulated points
338+ for ( let userDoc of soloUsers ) {
339+ const user = userDoc . toObject ( ) ; // Make it modifiable
340+
341+ user . points = 0 ;
342+ user . name = user . username ;
343+
344+ for ( const completion of user . completions ) {
345+ const challengeProfile = await ChallengeCollection . findOne ( { _id : completion . id } ) ;
346+ if ( challengeProfile ) {
347+ user . points += challengeProfile . points ;
348+ }
349+ }
350+
351+ user . recent = user . completions . at ( - 1 ) ?. time ?? 0 ;
352+ delete user . completions ;
353+ delete user . username ;
354+ delete user . _id ;
355+
356+ readableSoloUsers . push ( user ) ; // Save modified copy
357+ }
358+
359+ // for each tean we need to calculate accumulated points
360+ for ( let teamDoc of teams ) {
361+ const team = teamDoc . toObject ( ) ; // Convert Mongoose document to plain object
362+
363+ team . points = 0 ;
364+
365+ for ( const completion of team . completions ) {
366+ team . points += completion . points ;
367+ }
368+
369+ team . recent = team . completions . at ( - 1 ) ?. timestamp ?? 0 ;
370+ delete team . completions ;
371+ delete team . _id ;
372+
373+ readableTeams . push ( team ) ; // Store modified team object
374+ }
375+
376+ // merge both lists (readableSoloUsers & readableTeams) in sorted fashion
377+ const merged = [ ...readableSoloUsers , ...readableTeams ] ;
378+ merged . sort ( ( a , b ) => {
379+ if ( b . points !== a . points ) {
380+ // Sort by points descending
381+ return b . points - a . points ;
382+ } else {
383+ // If points are equal, sort by recent (epoch) ascending
384+ return a . recent - b . recent ;
385+ }
386+ } ) ;
387+
388+ return merged ;
389+ }
390+
323391async function DoesExist ( username , email ) {
324392 username = SanitizeString ( username ) ;
325393 email = SanitizeString ( email ) ;
@@ -1719,4 +1787,4 @@ export { LoginUser, LoginAdmin, RegisterUser, GetUserProfile, UpdateUserProfile,
17191787 GetChallengeInfo , GetAllUsers , GetAllTeams , RemoveTeam ,
17201788 RemoveUser , UpdateChallenge , AdminGetChallenges ,
17211789 CreateChallenge , DeleteChallenge , RegisterAdmin , RemoveAdmin ,
1722- GetAdmins , ValidateAdmin } ;
1790+ GetAdmins , ValidateAdmin , GetLeaderboardData } ;
0 commit comments