@@ -170,6 +170,70 @@ async function computeRankChanges(currentSorted, filename) {
170170 } ) ;
171171}
172172
173+ /**
174+ * Processes a timeframe-based leaderboard (daily/weekly/monthly) by computing
175+ * per-user deltas from a previous snapshot, sorting, ranking, and writing output.
176+ *
177+ * @param {Array } sourceData - The overall dataset to deep-clone and process
178+ * @param {string } DATA_DIR - Path to the data directory
179+ * @param {string } periodName - Label for the timeframe ("daily", "weekly", "monthly")
180+ * @param {number } daysAgo - Number of days to look back for the snapshot file
181+ */
182+ async function processTimeframe ( sourceData , DATA_DIR , periodName , daysAgo ) {
183+ const data = JSON . parse ( JSON . stringify ( sourceData ) ) ;
184+ console . log ( " " ) ;
185+ console . log ( `Loading previous ${ periodName } 's file...` ) ;
186+ const previousFilepath = path . join ( DATA_DIR , "daily" , getFileName ( daysAgo ) ) ;
187+ let previousData = [ ] ;
188+ try {
189+ const rawData = fs . readFileSync ( previousFilepath , "utf8" ) ;
190+ previousData = JSON . parse ( rawData ) ;
191+ console . log ( `Previous ${ periodName } 's data loaded successfully` ) ;
192+ } catch ( err ) {
193+ console . error ( `Failed to load previous file: ` , err . message ) ;
194+ process . exit ( 1 ) ;
195+ }
196+
197+ console . log ( " " ) ;
198+ console . log ( `Calculating ${ periodName } progress...` ) ;
199+ for ( let i = 0 ; i < data . length ; i ++ ) {
200+ const previousIndex = previousData . findIndex (
201+ ( obj ) => obj . id === data [ i ] . id ,
202+ ) ;
203+ if ( previousIndex == - 1 ) {
204+ data . splice ( i -- , 1 ) ;
205+ continue ;
206+ }
207+ data [ i ] . data . easySolved -= previousData [ previousIndex ] . data . easySolved ;
208+ data [ i ] . data . mediumSolved -= previousData [ previousIndex ] . data . mediumSolved ;
209+ data [ i ] . data . hardSolved -= previousData [ previousIndex ] . data . hardSolved ;
210+ data [ i ] . score =
211+ data [ i ] . data . easySolved +
212+ data [ i ] . data . mediumSolved * 3 +
213+ data [ i ] . data . hardSolved * 5 ;
214+ data [ i ] . data . totalSolved =
215+ data [ i ] . data . easySolved +
216+ data [ i ] . data . mediumSolved +
217+ data [ i ] . data . hardSolved ;
218+ }
219+ console . log ( "Calculation done" ) ;
220+ console . log ( "" ) ;
221+
222+ console . log ( "Sorting calculated data..." ) ;
223+ stableSortByScore ( data ) ;
224+ assignCompetitionRanks ( data ) ;
225+ console . log ( `Writing sorted ${ periodName } data to ${ periodName } .json...` ) ;
226+ const filepath = path . join ( DATA_DIR , `${ periodName } .json` ) ;
227+ await computeRankChanges ( data , `${ periodName } .json` ) ;
228+ try {
229+ atomicWrite ( filepath , data ) ;
230+ console . log ( `${ periodName } data saved successfully` ) ;
231+ } catch ( err ) {
232+ console . error ( `Failed to write json file: ` , err . message ) ;
233+ process . exit ( 1 ) ;
234+ }
235+ }
236+
173237( async ( ) => {
174238 const DATA_DIR = process . env . DATA_DIR || path . join ( __dirname , ".." , "data" ) ;
175239 console . log ( `Using data directory: ${ DATA_DIR } ` ) ;
@@ -322,173 +386,10 @@ async function computeRankChanges(currentSorted, filename) {
322386 process . exit ( 1 ) ;
323387 }
324388
325- let dailyData = JSON . parse ( JSON . stringify ( overallData ) ) ;
326- console . log ( " " ) ;
327- console . log ( "Loading previous day's file..." ) ;
328- const previousDayFilepath = path . join ( DATA_DIR , "daily" , getFileName ( 1 ) ) ;
329- let previousData = [ ] ;
330- try {
331- const rawData = fs . readFileSync ( previousDayFilepath , "utf8" ) ;
332- previousData = JSON . parse ( rawData ) ;
333- console . log ( "Previous day's data loaded successfully" ) ;
334- } catch ( err ) {
335- console . error ( `Failed to load previous file: ` , err . message ) ;
336- process . exit ( 1 ) ;
337- }
338-
339- console . log ( " " ) ;
340- console . log ( "Calculating daily progress..." ) ;
341- for ( let i = 0 ; i < dailyData . length ; i ++ ) {
342- const previousIndex = previousData . findIndex (
343- ( obj ) => obj . id === dailyData [ i ] . id ,
344- ) ;
345- if ( previousIndex == - 1 ) {
346- dailyData . splice ( i -- , 1 ) ;
347- continue ;
348- }
349-
350- dailyData [ i ] . data . easySolved -= previousData [ previousIndex ] . data . easySolved ;
351- dailyData [ i ] . data . mediumSolved -=
352- previousData [ previousIndex ] . data . mediumSolved ;
353- dailyData [ i ] . data . hardSolved -= previousData [ previousIndex ] . data . hardSolved ;
354-
355- dailyData [ i ] . score =
356- dailyData [ i ] . data . easySolved +
357- dailyData [ i ] . data . mediumSolved * 3 +
358- dailyData [ i ] . data . hardSolved * 5 ;
359- dailyData [ i ] . data . totalSolved =
360- dailyData [ i ] . data . easySolved +
361- dailyData [ i ] . data . mediumSolved +
362- dailyData [ i ] . data . hardSolved ;
363- }
364- console . log ( "Calculation done" ) ;
365- console . log ( "" ) ;
366-
367- console . log ( "Sorting calculated data..." ) ;
368- stableSortByScore ( dailyData ) ;
369- assignCompetitionRanks ( dailyData ) ;
370- console . log ( "Writing sorted daily data to daily.json..." ) ;
371- const dailyFilepath = path . join ( DATA_DIR , "daily.json" ) ;
372- await computeRankChanges ( dailyData , "daily.json" ) ;
373- try {
374- atomicWrite ( dailyFilepath , dailyData ) ;
375- console . log ( "Daily data saved successfully" ) ;
376- } catch ( err ) {
377- console . error ( `Failed to write json file: ` , err . message ) ;
378- process . exit ( 1 ) ;
379- }
380-
381- let weeklyData = JSON . parse ( JSON . stringify ( overallData ) ) ;
382- console . log ( " " ) ;
383- console . log ( "Loading previous week's file..." ) ;
384- const previousWeekFilepath = path . join ( DATA_DIR , "daily" , getFileName ( 7 ) ) ;
385- previousData = [ ] ;
386- try {
387- const rawData = fs . readFileSync ( previousWeekFilepath , "utf8" ) ;
388- previousData = JSON . parse ( rawData ) ;
389- console . log ( "Previous week's data loaded successfully" ) ;
390- } catch ( err ) {
391- console . error ( `Failed to load previous file: ` , err . message ) ;
392- process . exit ( 1 ) ;
393- }
394-
395- console . log ( " " ) ;
396- console . log ( "Calculating weekly progress..." ) ;
397- for ( let i = 0 ; i < weeklyData . length ; i ++ ) {
398- const previousIndex = previousData . findIndex (
399- ( obj ) => obj . id === weeklyData [ i ] . id ,
400- ) ;
401- if ( previousIndex == - 1 ) {
402- weeklyData . splice ( i -- , 1 ) ;
403- continue ;
404- }
405- weeklyData [ i ] . data . easySolved -=
406- previousData [ previousIndex ] . data . easySolved ;
407- weeklyData [ i ] . data . mediumSolved -=
408- previousData [ previousIndex ] . data . mediumSolved ;
409- weeklyData [ i ] . data . hardSolved -=
410- previousData [ previousIndex ] . data . hardSolved ;
411- weeklyData [ i ] . score =
412- weeklyData [ i ] . data . easySolved +
413- weeklyData [ i ] . data . mediumSolved * 3 +
414- weeklyData [ i ] . data . hardSolved * 5 ;
415- weeklyData [ i ] . data . totalSolved =
416- weeklyData [ i ] . data . easySolved +
417- weeklyData [ i ] . data . mediumSolved +
418- weeklyData [ i ] . data . hardSolved ;
419- }
420- console . log ( "Calculation done" ) ;
421- console . log ( "" ) ;
422-
423- console . log ( "Sorting calculated data..." ) ;
424- stableSortByScore ( weeklyData ) ;
425- assignCompetitionRanks ( weeklyData ) ;
426- console . log ( "Writing sorted weekly data to weekly.json..." ) ;
427- const weeklyFilepath = path . join ( DATA_DIR , "weekly.json" ) ;
428- await computeRankChanges ( weeklyData , "weekly.json" ) ;
429- try {
430- atomicWrite ( weeklyFilepath , weeklyData ) ;
431- console . log ( "Weekly data saved successfully" ) ;
432- } catch ( err ) {
433- console . error ( `Failed to write json file: ` , err . message ) ;
434- process . exit ( 1 ) ;
435- }
436-
437- let monthlyData = JSON . parse ( JSON . stringify ( overallData ) ) ;
438- console . log ( " " ) ;
439- console . log ( "Loading previous month's file..." ) ;
440- const previousMonthFilepath = path . join ( DATA_DIR , "daily" , getFileName ( 30 ) ) ;
441- previousData = [ ] ;
442- try {
443- const rawData = fs . readFileSync ( previousMonthFilepath , "utf8" ) ;
444- previousData = JSON . parse ( rawData ) ;
445- console . log ( "Previous week's data loaded successfully" ) ;
446- } catch ( err ) {
447- console . error ( `Failed to load previous file: ` , err . message ) ;
448- process . exit ( 1 ) ;
449- }
450-
451- console . log ( " " ) ;
452- console . log ( "Calculating monthly progress..." ) ;
453- for ( let i = 0 ; i < monthlyData . length ; i ++ ) {
454- const previousIndex = previousData . findIndex (
455- ( obj ) => obj . id === monthlyData [ i ] . id ,
456- ) ;
457- if ( previousIndex == - 1 ) {
458- monthlyData . splice ( i -- , 1 ) ;
459- continue ;
460- }
461- monthlyData [ i ] . data . easySolved -=
462- previousData [ previousIndex ] . data . easySolved ;
463- monthlyData [ i ] . data . mediumSolved -=
464- previousData [ previousIndex ] . data . mediumSolved ;
465- monthlyData [ i ] . data . hardSolved -=
466- previousData [ previousIndex ] . data . hardSolved ;
467- monthlyData [ i ] . score =
468- monthlyData [ i ] . data . easySolved +
469- monthlyData [ i ] . data . mediumSolved * 3 +
470- monthlyData [ i ] . data . hardSolved * 5 ;
471- monthlyData [ i ] . data . totalSolved =
472- monthlyData [ i ] . data . easySolved +
473- monthlyData [ i ] . data . mediumSolved +
474- monthlyData [ i ] . data . hardSolved ;
475- }
476- console . log ( "Calculation done" ) ;
477- console . log ( "" ) ;
478-
479- console . log ( "Sorting calculated data..." ) ;
480- stableSortByScore ( monthlyData ) ;
481- assignCompetitionRanks ( monthlyData ) ;
482- console . log ( "Writing sorted monthly data to monthly.json..." ) ;
483- const monthlyFilepath = path . join ( DATA_DIR , "monthly.json" ) ;
484- await computeRankChanges ( monthlyData , "monthly.json" ) ;
485- try {
486- atomicWrite ( monthlyFilepath , monthlyData ) ;
487- console . log ( "Monthly data saved successfully" ) ;
488- } catch ( err ) {
489- console . error ( `Failed to write json file: ` , err . message ) ;
490- process . exit ( 1 ) ;
491- }
389+ // Process timeframe-based leaderboards using the shared function
390+ await processTimeframe ( overallData , DATA_DIR , "daily" , 1 ) ;
391+ await processTimeframe ( overallData , DATA_DIR , "weekly" , 7 ) ;
392+ await processTimeframe ( overallData , DATA_DIR , "monthly" , 30 ) ;
492393
493394 console . log ( "Generating changes.json..." ) ;
494395 const changesFilepath = path . join ( DATA_DIR , "changes.json" ) ;
0 commit comments