11const fetch = require ( "node-fetch" ) ;
22const Contest = require ( "../models/contest" ) ;
33const { IsLatestContest } = require ( "../helpers" ) ;
4+ const { BASE_CN_URL , BASE_URL } = require ( "./users" ) ;
5+
6+ const getContestParticipantsRankings = async ( contestSlug , dataRegion ) => {
7+ console . log ( `Fetching participant's rankings for ${ dataRegion } region...` ) ;
8+ const baseUrl = dataRegion === "CN" ? BASE_CN_URL : BASE_URL ;
9+ let resp = await fetch (
10+ `${ baseUrl } /contest/api/ranking/${ contestSlug } /?region=global`
11+ ) ;
12+ resp = await resp . json ( ) ;
13+ let pages = Math . ceil ( resp . user_num / 25 ) ;
14+ let all_rankings = [ ] ;
15+ let failed = [ ] ;
16+ let lastPage = Math . MAX_SAFE_INTEGER ;
17+ const fetchPageRankings = async (
18+ pageNo ,
19+ retries ,
20+ throwError = false
21+ ) => {
22+ if ( pageNo > lastPage ) {
23+ return ;
24+ }
25+ try {
26+ let res = await fetch (
27+ `${ baseUrl } /contest/api/ranking/${ contestSlug } /?pagination=${ pageNo } ®ion=${ dataRegion == "CN" ? "local" : "global" } `
28+ ) ;
29+ if ( res . status !== 200 ) {
30+ throw new Error ( res . statusText ) ;
31+ }
32+ res = await res . json ( ) ;
33+ rankings = res . total_rank
34+ . filter (
35+ ( ranks ) =>
36+ ! (
37+ ranks . score == 0 &&
38+ ranks . finish_time * 1000 ==
39+ contest . startTime . getTime ( )
40+ )
41+ ) ;
42+ if ( rankings . length < 25 ) {
43+ lastPage = Math . min ( lastPage , pageNo ) ;
44+ }
45+ all_rankings = all_rankings . concat ( rankings ) ;
46+ console . log (
47+ `Fetched rankings (${ contestSlug } page: ${ pageNo } )` ,
48+ ) ;
49+ } catch ( err ) {
50+ if ( retries > 0 ) {
51+ await fetchPageRankings ( pageNo , retries - 1 ) ;
52+ } else if ( throwError ) {
53+ throw err ;
54+ } else {
55+ failed . push ( pageNo ) ;
56+ }
57+ }
58+ } ;
59+ const limit = 5 ;
60+ const maxRetries = 5 ;
61+ for ( let i = 0 ; i < pages ; i += limit ) {
62+ let promises = [ ] ;
63+ for ( let j = 0 ; j < limit && i + j < pages ; j ++ ) {
64+ promises . push ( fetchPageRankings ( i + j + 1 , maxRetries ) ) ;
65+ }
66+ await Promise . all ( promises ) ;
67+ }
68+
69+ for ( let i = 0 ; i < failed . length ; i ++ ) {
70+ await fetchPageRankings ( failed [ i ] , maxRetries , true ) ;
71+ }
72+ console . log ( `(${ contestSlug } ) Rankings fetched from ${ baseUrl } ` ) ;
73+ return all_rankings ;
74+ } ;
75+
76+ const mergeRankings = ( us_rankings , cn_rankings ) => {
77+ us_rankings . sort ( ( a , b ) => ( a . rank > b . rank ? 1 : - 1 ) ) ;
78+ cn_rankings . sort ( ( a , b ) => ( a . rank > b . rank ? 1 : - 1 ) ) ;
79+ let totalUsRankings = us_rankings . length ;
80+ let totalCnRankings = cn_rankings . length ;
81+ let currRank = 0 ;
82+ let i = 0 , j = 0 ;
83+ all_rankings = [ ] ;
84+ while ( i < totalUsRankings || j < totalCnRankings ) {
85+ let currRanking ;
86+ if ( i == totalUsRankings ) {
87+ currRanking = cn_rankings [ j ++ ] ;
88+ } else if ( j == totalCnRankings ) {
89+ currRanking = us_rankings [ i ++ ] ;
90+ } else {
91+ if ( us_rankings [ i ] . score > cn_rankings [ j ] . score || ( us_rankings [ i ] . score === cn_rankings [ i ] . score && us_rankings [ i ] . finish_time <= cn_rankings [ j ] . finish_time ) ) {
92+ currRanking = us_rankings [ i ++ ] ;
93+ } else {
94+ currRanking = cn_rankings [ j ++ ] ;
95+ }
96+ }
97+ currRanking . rank = currRank ++ ;
98+ all_rankings . push ( currRanking ) ;
99+ }
100+ return all_rankings ;
101+ }
4102
5103const fetchContestRankings = async function ( contestSlug ) {
6104 try {
@@ -13,99 +111,33 @@ const fetchContestRankings = async function (contestSlug) {
13111 }
14112 contest . rankings = [ ] ;
15113 console . log ( `fetching ${ contestSlug } ...` ) ;
16- let resp = await fetch (
17- `https://leetcode.com/contest/api/ranking/${ contestSlug } /?region=global`
18- ) ;
19- resp = await resp . json ( ) ;
20- let pages = Math . ceil ( resp . user_num / 25 ) ;
21- let all_rankings = [ ] ;
22- let failed = [ ] ;
23- let lastPage = Math . MAX_SAFE_INTEGER ;
24- const fetchPageRankings = async (
25- pageNo ,
26- retries ,
27- throwError = false
28- ) => {
29- if ( pageNo > lastPage ) {
30- return ;
31- }
32- // console.log(`Fetching rankings (${contestSlug}): page: ${pageNo}`);
33- try {
34- let res = await fetch (
35- `https://leetcode.com/contest/api/ranking/${ contestSlug } /?pagination=${ pageNo } ®ion=global`
36- ) ;
37- if ( res . status !== 200 ) {
38- throw new Error ( res . statusText ) ;
39- }
40- res = await res . json ( ) ;
41- rankings = res . total_rank
42- . filter (
43- ( ranks ) =>
44- ! (
45- ranks . score == 0 &&
46- ranks . finish_time * 1000 ==
47- contest . startTime . getTime ( )
48- )
49- )
50- . map ( ( ranks ) => {
51- let {
52- username,
53- user_slug,
54- country_code,
55- country_name,
56- data_region,
57- rank,
58- } = ranks ;
59- if ( data_region === "CN" ) {
60- country_code = "CN" ;
61- }
62- let ranking = {
63- username,
64- user_slug,
65- country_code,
66- country_name,
67- data_region,
68- rank,
69- } ;
70- ranking [ "_id" ] = username ;
71- return ranking ;
72- } ) ;
73- if ( rankings . length < 25 ) {
74- lastPage = Math . min ( lastPage , pageNo ) ;
75- }
76- all_rankings = all_rankings . concat ( rankings ) ;
77- console . log (
78- `Fetched rankings (${ contestSlug } page: ${ pageNo } )`
79- ) ;
80- } catch ( err ) {
81- // console.log(
82- // `Failed to fetch rankings (${contestSlug} page: ${pageNo})`,
83- // err.message
84- // );
85- if ( retries > 0 ) {
86- await fetchPageRankings ( pageNo , retries - 1 ) ;
87- } else if ( throwError ) {
88- throw err ;
89- } else {
90- failed . push ( pageNo ) ;
91- }
92- }
93- } ;
94- const limit = 5 ;
95- const maxRetries = 5 ;
96- for ( let i = 0 ; i < pages ; i += limit ) {
97- let promises = [ ] ;
98- for ( let j = 0 ; j < limit && i + j < pages ; j ++ ) {
99- promises . push ( fetchPageRankings ( i + j + 1 , maxRetries ) ) ;
100- }
101- await Promise . all ( promises ) ;
102- }
103114
104- for ( let i = 0 ; i < failed . length ; i ++ ) {
105- await fetchPageRankings ( failed [ i ] , maxRetries , true ) ;
106- }
107- console . log ( `(${ contestSlug } ) Rankings fetched from leetcode!` ) ;
108- all_rankings . sort ( ( a , b ) => ( a . rank > b . rank ? 1 : - 1 ) ) ;
115+ us_rankings = await getContestParticipantsRankings ( contestSlug , "US" ) ;
116+ cn_rankings = await getContestParticipantsRankings ( contestSlug , "CN" ) ;
117+
118+ // Merged rankings sorted by rank
119+ all_rankings = mergeRankings ( us_rankings , cn_rankings ) . map ( ( ranking ) => {
120+ let {
121+ username,
122+ user_slug,
123+ country_code,
124+ country_name,
125+ data_region,
126+ rank,
127+ } = ranking ;
128+ if ( data_region === "CN" ) {
129+ country_code = "CN" ;
130+ }
131+ return {
132+ _id : username ,
133+ username,
134+ user_slug,
135+ country_code,
136+ country_name,
137+ data_region,
138+ rank,
139+ } ; ;
140+ } ) ;
109141
110142 contest . rankings = all_rankings ;
111143 contest . rankings_fetched = true ;
0 commit comments