9
9
from selenium import webdriver
10
10
from selenium .webdriver .common .action_chains import ActionChains
11
11
12
+ from util import get_safe_nested_key
13
+
12
14
13
15
class UsernameError (Exception ):
14
16
pass
@@ -294,6 +296,7 @@ def __interviewbit(self):
294
296
295
297
return details
296
298
299
+ # DEPRECATED
297
300
def __leetcode (self ):
298
301
url = 'https://leetcode.com/{}' .format (self .__username )
299
302
@@ -309,52 +312,56 @@ def __leetcode(self):
309
312
# driver = webdriver.PhantomJS(executable_path='./phantomjs')
310
313
311
314
driver = webdriver .Chrome (options = options , executable_path = os .environ .get ("CHROMEDRIVER_PATH" ))
312
- driver .get (url )
315
+ try :
316
+ driver .get (url )
313
317
314
- driver .implicitly_wait (10 )
318
+ driver .implicitly_wait (10 )
315
319
316
- hover_ranking = driver .find_element_by_xpath (
317
- '/html/body/div[1]/div[2]/div/div[1]/div[1]/div[2]/div/div[1]/div[3]/div' )
318
- ActionChains (driver ).move_to_element (to_element = hover_ranking ).perform ()
320
+ hover_ranking = driver .find_element_by_xpath (
321
+ '/html/body/div[1]/div[2]/div/div[1]/div[1]/div[2]/div/div[1]/div[3]/div' )
322
+ ActionChains (driver ).move_to_element (to_element = hover_ranking ).perform ()
319
323
320
- ranking = driver .find_element_by_xpath ('/html/body/div[4]/div/div/div/div[2]' ).text
321
- print ('rank: ' , ranking )
324
+ ranking = driver .find_element_by_xpath ('/html/body/div[4]/div/div/div/div[2]' ).text
325
+ print ('rank: ' , ranking )
322
326
323
- total_problems_solved = driver .find_element_by_xpath (
324
- '/html/body/div[1]/div[2]/div/div[1]/div[2]/div/div[1]/div[1]/div[2]' ).text
327
+ total_problems_solved = driver .find_element_by_xpath (
328
+ '/html/body/div[1]/div[2]/div/div[1]/div[2]/div/div[1]/div[1]/div[2]' ).text
325
329
326
- acceptance_rate_span_1 = driver .find_element_by_xpath (
327
- '/html/body/div[1]/div[2]/div/div[1]/div[2]/div/div[1]/div[2]/div[2]/div/div[1]/span[1]' ).text
328
- acceptance_rate_span_2 = driver .find_element_by_xpath (
329
- '/html/body/div[1]/div[2]/div/div[1]/div[2]/div/div[1]/div[2]/div[2]/div/div[1]/span[2]' ).text
330
- acceptance_rate = str (acceptance_rate_span_1 ) + str (acceptance_rate_span_2 )
330
+ acceptance_rate_span_1 = driver .find_element_by_xpath (
331
+ '/html/body/div[1]/div[2]/div/div[1]/div[2]/div/div[1]/div[2]/div[2]/div/div[1]/span[1]' ).text
332
+ acceptance_rate_span_2 = driver .find_element_by_xpath (
333
+ '/html/body/div[1]/div[2]/div/div[1]/div[2]/div/div[1]/div[2]/div[2]/div/div[1]/span[2]' ).text
334
+ acceptance_rate = str (acceptance_rate_span_1 ) + str (acceptance_rate_span_2 )
331
335
332
- easy_questions_solved = driver .find_element_by_xpath (
333
- '//*[@id="profile-root"]/div[2]/div/div[1]/div[2]/div/div[2]/div/div[1]/div[2]/span[1]' ).text
334
- total_easy_questions = driver .find_element_by_xpath (
335
- '//*[@id="profile-root"]/div[2]/div/div[1]/div[2]/div/div[2]/div/div[1]/div[2]/span[2]' ).text
336
+ easy_questions_solved = driver .find_element_by_xpath (
337
+ '//*[@id="profile-root"]/div[2]/div/div[1]/div[2]/div/div[2]/div/div[1]/div[2]/span[1]' ).text
338
+ total_easy_questions = driver .find_element_by_xpath (
339
+ '//*[@id="profile-root"]/div[2]/div/div[1]/div[2]/div/div[2]/div/div[1]/div[2]/span[2]' ).text
336
340
337
- medium_questions_solved = driver .find_element_by_xpath (
338
- '//*[@id="profile-root"]/div[2]/div/div[1]/div[2]/div/div[2]/div/div[2]/div[2]/span[1]' ).text
339
- total_medium_questions = driver .find_element_by_xpath (
340
- '//*[@id="profile-root"]/div[2]/div/div[1]/div[2]/div/div[2]/div/div[2]/div[2]/span[2]' ).text
341
+ medium_questions_solved = driver .find_element_by_xpath (
342
+ '//*[@id="profile-root"]/div[2]/div/div[1]/div[2]/div/div[2]/div/div[2]/div[2]/span[1]' ).text
343
+ total_medium_questions = driver .find_element_by_xpath (
344
+ '//*[@id="profile-root"]/div[2]/div/div[1]/div[2]/div/div[2]/div/div[2]/div[2]/span[2]' ).text
341
345
342
- hard_questions_solved = driver .find_element_by_xpath (
343
- '//*[@id="profile-root"]/div[2]/div/div[1]/div[2]/div/div[2]/div/div[3]/div[2]/span[1]' ).text
344
- total_hard_questions = driver .find_element_by_xpath (
345
- '//*[@id="profile-root"]/div[2]/div/div[1]/div[2]/div/div[2]/div/div[3]/div[2]/span[2]' ).text
346
+ hard_questions_solved = driver .find_element_by_xpath (
347
+ '//*[@id="profile-root"]/div[2]/div/div[1]/div[2]/div/div[2]/div/div[3]/div[2]/span[1]' ).text
348
+ total_hard_questions = driver .find_element_by_xpath (
349
+ '//*[@id="profile-root"]/div[2]/div/div[1]/div[2]/div/div[2]/div/div[3]/div[2]/span[2]' ).text
346
350
347
- contribution_points = driver .find_element_by_xpath (
348
- '/html/body/div[1]/div[2]/div/div[1]/div[3]/div[2]/div/div/div/li[1]/span' ).text
351
+ contribution_points = driver .find_element_by_xpath (
352
+ '/html/body/div[1]/div[2]/div/div[1]/div[3]/div[2]/div/div/div/li[1]/span' ).text
349
353
350
- contribution_problems = driver .find_element_by_xpath (
351
- '/html/body/div[1]/div[2]/div/div[1]/div[3]/div[2]/div/div/div/li[2]/span' ).text
354
+ contribution_problems = driver .find_element_by_xpath (
355
+ '/html/body/div[1]/div[2]/div/div[1]/div[3]/div[2]/div/div/div/li[2]/span' ).text
352
356
353
- contribution_testcases = driver .find_element_by_xpath (
354
- '/html/body/div[1]/div[2]/div/div[1]/div[3]/div[2]/div/div/div/li[3]/span' ).text
357
+ contribution_testcases = driver .find_element_by_xpath (
358
+ '/html/body/div[1]/div[2]/div/div[1]/div[3]/div[2]/div/div/div/li[3]/span' ).text
355
359
356
- reputation = driver .find_element_by_xpath (
357
- '/html/body/div[1]/div[2]/div/div[1]/div[4]/div[2]/div/div/div/li/span' ).text
360
+ reputation = driver .find_element_by_xpath (
361
+ '/html/body/div[1]/div[2]/div/div[1]/div[4]/div[2]/div/div/div/li/span' ).text
362
+ finally :
363
+ driver .close ()
364
+ driver .quit ()
358
365
359
366
details = {'status' : 'Success' , 'ranking' : ranking [9 :],
360
367
'total_problems_solved' : total_problems_solved ,
@@ -372,6 +379,98 @@ def __leetcode(self):
372
379
373
380
return details
374
381
382
+ def __leetcode_v2 (self ):
383
+
384
+ def __parse_response (response ):
385
+ total_submissions_count = 0
386
+ ac_submissions_count = 0
387
+ total_problems_solved = 0
388
+ total_easy_questions = 0
389
+ total_medium_questions = 0
390
+ total_hard_questions = 0
391
+ easy_questions_solved = 0
392
+ medium_questions_solved = 0
393
+ hard_questions_solved = 0
394
+
395
+ ranking = get_safe_nested_key (['data' , 'matchedUser' , 'profile' , 'ranking' ], response )
396
+ if ranking > 100000 :
397
+ ranking = '~100000'
398
+
399
+ reputation = get_safe_nested_key (['data' , 'matchedUser' , 'profile' , 'reputation' ], response )
400
+
401
+ total_questions_stats = get_safe_nested_key (['data' , 'allQuestionsCount' ], response )
402
+ for item in total_questions_stats :
403
+ if item ['difficulty' ] == "Easy" :
404
+ total_easy_questions = item ['count' ]
405
+ if item ['difficulty' ] == "Medium" :
406
+ total_medium_questions = item ['count' ]
407
+ if item ['difficulty' ] == "Hard" :
408
+ total_hard_questions = item ['count' ]
409
+
410
+ ac_submissions = get_safe_nested_key (['data' , 'matchedUser' , 'submitStats' , 'acSubmissionNum' ], response )
411
+ for submission in ac_submissions :
412
+ if submission ['difficulty' ] == "All" :
413
+ ac_submissions_count = submission ['submissions' ]
414
+
415
+ total_submissions = get_safe_nested_key (['data' , 'matchedUser' , 'submitStats' , 'totalSubmissionNum' ],
416
+ response )
417
+ for submission in total_submissions :
418
+ if submission ['difficulty' ] == "All" :
419
+ total_problems_solved = submission ['count' ]
420
+ total_submissions_count = submission ['submissions' ]
421
+ if submission ['difficulty' ] == "Easy" :
422
+ easy_questions_solved = submission ['count' ]
423
+ if submission ['difficulty' ] == "Medium" :
424
+ medium_questions_solved = submission ['count' ]
425
+ if submission ['difficulty' ] == "Hard" :
426
+ hard_questions_solved = submission ['count' ]
427
+
428
+ if total_submissions_count > 0 :
429
+ acceptance_rate = round (ac_submissions_count * 100 / total_submissions_count , 2 )
430
+ else :
431
+ acceptance_rate = 0
432
+
433
+ contribution_points = get_safe_nested_key (['data' , 'matchedUser' , 'contributions' , 'points' ],
434
+ response )
435
+ contribution_problems = get_safe_nested_key (['data' , 'matchedUser' , 'contributions' , 'questionCount' ],
436
+ response )
437
+ contribution_testcases = get_safe_nested_key (['data' , 'matchedUser' , 'contributions' , 'testcaseCount' ],
438
+ response )
439
+
440
+ return {
441
+ 'status' : 'Success' ,
442
+ 'ranking' : str (ranking ),
443
+ 'total_problems_solved' : str (total_problems_solved ),
444
+ 'acceptance_rate' : f"{ acceptance_rate } %" ,
445
+ 'easy_questions_solved' : str (easy_questions_solved ),
446
+ 'total_easy_questions' : str (total_easy_questions ),
447
+ 'medium_questions_solved' : str (medium_questions_solved ),
448
+ 'total_medium_questions' : str (total_medium_questions ),
449
+ 'hard_questions_solved' : str (hard_questions_solved ),
450
+ 'total_hard_questions' : str (total_hard_questions ),
451
+ 'contribution_points' : str (contribution_points ),
452
+ 'contribution_problems' : str (contribution_problems ),
453
+ 'contribution_testcases' : str (contribution_testcases ),
454
+ 'reputation' : str (reputation )
455
+ }
456
+
457
+ url = f'https://leetcode.com/{ self .__username } '
458
+ if requests .get (url ).status_code != 200 :
459
+ raise UsernameError ('User not Found' )
460
+ payload = {
461
+ "operationName" : "getUserProfile" ,
462
+ "variables" : {
463
+ "username" : self .__username
464
+ },
465
+ "query" : "query getUserProfile($username: String!) { allQuestionsCount { difficulty count } matchedUser(username: $username) { contributions { points questionCount testcaseCount } profile { reputation ranking } submitStats { acSubmissionNum { difficulty count submissions } totalSubmissionNum { difficulty count submissions } } }}"
466
+ }
467
+ res = requests .post (url = 'https://leetcode.com/graphql' ,
468
+ json = payload ,
469
+ headers = {'referer' : f'https://leetcode.com/{ self .__username } /' })
470
+ res .raise_for_status ()
471
+ res = res .json ()
472
+ return __parse_response (res )
473
+
375
474
def get_details (self , platform ):
376
475
if platform == 'codechef' :
377
476
return self .__codechef ()
@@ -389,14 +488,22 @@ def get_details(self, platform):
389
488
return self .__interviewbit ()
390
489
391
490
if platform == 'leetcode' :
392
- return self .__leetcode ()
491
+ return self .__leetcode_v2 ()
393
492
394
493
raise PlatformError ('Platform not Found' )
395
494
396
495
397
496
if __name__ == '__main__' :
398
- ud = UserData ('abhijeet_ar' )
399
-
400
- ans = ud .get_details ('codeforces' )
497
+ ud = UserData ('uwi' )
498
+ ans = ud .get_details ('leetcode' )
401
499
402
500
print (ans )
501
+
502
+ # leetcode backward compatibility test. Commenting it out as it will fail in future
503
+ # leetcode_ud = UserData('saurabhprakash')
504
+ # leetcode_ans = leetcode_ud.get_details('leetcode')
505
+ # assert leetcode_ans == dict(status='Success', ranking='~100000', total_problems_solved='10',
506
+ # acceptance_rate='56.0%', easy_questions_solved='3', total_easy_questions='457',
507
+ # medium_questions_solved='5', total_medium_questions='901', hard_questions_solved='2',
508
+ # total_hard_questions='365', contribution_points='58', contribution_problems='0',
509
+ # contribution_testcases='0', reputation='0')
0 commit comments