forked from doocs/leetcode
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrating.py
164 lines (148 loc) · 6.61 KB
/
rating.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
from string import Template
import requests
import urllib3
urllib3.disable_warnings()
class Ranking:
def __init__(self, region='CN', retry=3):
self.retry = retry
self.region = region
if region == 'CN':
self.url = 'https://leetcode.cn/graphql'
self.page_query = Template(
"{\n localRankingV2(page:$page) {\nmyRank {\nattendedContestCount\n"
"currentRatingRanking\ndataRegion\nisDeleted\nuser {\nrealName\n"
"userAvatar\nuserSlug\n__typename\n}\n__typename\n}\npage\ntotalUsers\n"
"userPerPage\nrankingNodes {\nattendedContestCount\ncurrentRatingRanking\n"
"dataRegion\nisDeleted\nuser {\nrealName\nuserAvatar\nuserSlug\n__typename\n}\n__"
"typename\n}\n__typename\n }\n}\n"
)
else:
self.url = 'https://leetcode.com/graphql'
self.page_query = Template(
"{\nglobalRanking(page:$page){\ntotalUsers\nuserPerPage\nmyRank{\nranking\n"
"currentGlobalRanking\ncurrentRating\ndataRegion\nuser{\nnameColor\nactiveBadge{\n"
"displayName\nicon\n__typename\n}\n__typename\n}\n__typename\n}\nrankingNodes{\n"
"ranking\ncurrentRating\ncurrentGlobalRanking\ndataRegion\nuser{\nusername\nnameColor\n"
"activeBadge{\ndisplayName\nicon\n__typename\n}\nprofile{\nuserAvatar\ncountryCode\n"
"countryName\nrealName\n__typename\n}\n__typename\n}\n__typename\n}\n__typename\n}\n}\n"
)
def load_page(self, page):
query = self.page_query.substitute(page=page)
for _ in range(self.retry):
resp = requests.post(url=self.url, json={'query': query}, verify=False)
if resp.status_code != 200:
continue
nodes = resp.json()['data'][
'localRankingV2' if self.region == 'CN' else 'globalRanking'
]['rankingNodes']
if self.region == 'CN':
return [
(int(v['currentRatingRanking']), v['user']['userSlug'])
for v in nodes
]
else:
return [
(int(v['currentGlobalRanking']), v['user']['username'])
for v in nodes
]
return []
def _user_ranking(self, region, uid):
if region == 'CN':
key = 'userSlug'
url = 'https://leetcode.cn/graphql/noj-go/'
query = (
"\nquery userContestRankingInfo($userSlug:String!){\nuserContestRanking"
"(userSlug:$userSlug){\nattendedContestsCount\nrating\nglobalRanking\nlocalRanking\n"
"globalTotalParticipants\nlocalTotalParticipants\ntopPercentage\n}\n"
"userContestRankingHistory(userSlug:$userSlug){\nattended\ntotalProblems\n"
"trendingDirection\nfinishTimeInSeconds\nrating\nscore\nranking\ncontest{\n"
"title\ntitleCn\nstartTime\n}\n}\n}\n"
)
else:
key = 'username'
url = 'https://leetcode.com/graphql'
query = (
"\nquery userContestRankingInfo($username:String!){\nuserContestRanking"
"(username:$username){\nattendedContestsCount\nrating\nglobalRanking\n"
"totalParticipants\ntopPercentage\nbadge{\nname\n}\n}\nuserContestRankingHistory"
"(username:$username){\nattended\ntrendDirection\nproblemsSolved\n"
"totalProblems\nfinishTimeInSeconds\nrating\nranking\ncontest{\n"
"title\nstartTime\n}\n}\n}\n "
)
variables = {key: uid}
for _ in range(self.retry):
resp = requests.post(
url=url, json={'query': query, 'variables': variables}, verify=False
)
if resp.status_code != 200:
continue
res = resp.json()
if 'errors' in res:
break
ranking = res['data']['userContestRanking']
if ranking and 'rating' in ranking:
score = float(ranking['rating'])
if 'localRanking' in ranking:
return int(ranking['localRanking']), score
if 'globalRanking' in ranking:
return int(ranking['globalRanking']), score
return None, None
def get_user_ranking(self, uid):
for region in ['CN', 'US']:
# 美国站会有国服用户,这里最多需要查询两次
ranking, score = self._user_ranking(region, uid)
if score is not None:
return ranking, score
return None
def get_1600_count(self):
left, right = 1, 4000
while left < right:
mid = (left + right + 1) >> 1
page = self.load_page(mid)
print(f'第 {mid} 页:', page)
if not page:
return 0
ranking, score = self.get_user_ranking(page[0][1])
if score >= 1600:
left = mid
else:
right = mid - 1
page = [uid for _, uid in self.load_page(left) if uid]
print('校准中...')
left, right = 0, len(page) - 1
while left < right:
mid = (left + right + 1) >> 1
ranking, score = self.get_user_ranking(page[mid])
if score >= 1600:
left = mid
else:
right = mid - 1
return self.get_user_ranking(page[left])[0]
def get_user(self, rank):
p = (rank - 1) // 25 + 1
offset = (rank - 1) % 25
page = self.load_page(p)
_, score = self.get_user_ranking(page[offset][1])
return score, page[offset][1]
def fetch_ranking_data(self):
total = self.get_1600_count()
if not total:
return
print(f'[{self.region}] 1600 分以上共计 {total} 人')
guardian = int(total * 0.05)
knight = int(total * 0.25)
g_first, g_last = self.get_user(1), self.get_user(guardian)
print(
f'Guardian(top 5%): 共 {guardian} 名,守门员 {g_last[0]} 分(uid: {g_last[1]}),最高 {g_first[0]} 分(uid: {g_first[1]})'
)
k_first, k_last = self.get_user(guardian + 1), self.get_user(knight)
print(
f'Knight(top 25%): 共 {knight} 名,守门员 {k_last[0]} 分(uid: {k_last[1]}),最高 {k_first[0]} 分(uid: {k_first[1]})'
)
# 国服竞赛排名
lk = Ranking(region='CN')
lk.fetch_ranking_data()
print('\n------------------------------\n')
# 全球竞赛排名
gk = Ranking(region='US')
gk.fetch_ranking_data()