DotaNoobs main site.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

101 lines
4.2 KiB

  1. import requests
  2. from time import sleep, mktime
  3. from bs4 import BeautifulSoup
  4. from datetime import datetime, timedelta
  5. from app import app, db, models
  6. MODES_TO_SKIP = ['Ability Draft', 'Greeviling', 'Diretide']
  7. def collect_match_results(dotabuff_id, num_matches):
  8. results = []
  9. page = 0
  10. while True:
  11. page += 1
  12. url = "http://dotabuff.com/players/{}/matches/?page={}".format(dotabuff_id, page)
  13. data = requests.get(url).text
  14. try:
  15. soup = BeautifulSoup(data).article.table.tbody
  16. except:
  17. break
  18. else:
  19. # Catch last page
  20. if 'sorry' in soup.tr.td.text.lower():
  21. break
  22. # Parse the matches on current page
  23. for row in soup.find_all('tr'):
  24. # Pass over bot matches and other 'inactive' games
  25. if 'inactive' in row.get('class', ''): continue
  26. cells = row.find_all('td')
  27. result_cell = cells[2]
  28. match_cell = cells[3]
  29. match_id = int(result_cell.a['href'].split('/')[-1])
  30. match_type = match_cell.div.text
  31. if match_type in MODES_TO_SKIP: continue
  32. result = True if 'won' in result_cell.a['class'] else False
  33. dt = datetime.strptime(result_cell.time['datetime'], '%Y-%m-%dT%H:%M:%S+00:00')
  34. results.append({'match_id':match_id, 'win':result, 'datetime':dt, 'game_mode':match_type})
  35. if len(results) > num_matches:
  36. break
  37. if len(results) > num_matches:
  38. break
  39. sleep(60)
  40. results.reverse()
  41. return results
  42. def apply_window(results, window_size=50):
  43. windows = []
  44. # Compute the initial window
  45. win_rate = 0.00
  46. for idx in range(0, window_size-1):
  47. win_rate += 1 if results[idx]['win'] else 0
  48. win_rate /= window_size
  49. windows.append(win_rate)
  50. # From here on, modify based on leave/enter data points
  51. fractional_change = 1. / window_size
  52. for idx in range(window_size, len(results)):
  53. if results[idx-window_size]['win'] == results[idx]['win']:
  54. pass
  55. elif results[idx]['win']:
  56. win_rate += fractional_change
  57. else:
  58. win_rate -= fractional_change
  59. windows.append(win_rate)
  60. return windows
  61. # Single user alternative for testing/calculating on demand
  62. def calculate_winrate(user_id):
  63. user = models.User.query.get(user_id)
  64. db_id = requests.get("http://dotabuff.com/search?q="+user.steam_id).url.split("/")[-1]
  65. result = collect_match_results(db_id, app.config['ANALYTICS_WINRATE_NUM_MATCHES'])
  66. windowed = apply_window(result, app.config['ANALYTICS_WINRATE_WINDOW'])
  67. date_nums = map(lambda x: mktime(x['datetime'].timetuple()),\
  68. result[app.config['ANALYTICS_WINRATE_WINDOW']-1:])
  69. winrate = {'total_games': len(result), 'data': zip(date_nums, windowed) }
  70. user.winrate_data = winrate
  71. db.session.commit()
  72. def calculate_winrates():
  73. users_analyzed = 0
  74. delta = datetime.utcnow() - timedelta(weeks=4)
  75. print "Starting winrate calculation"
  76. for user in models.User.query.filter(models.User.last_seen > delta).all():
  77. print "Begin calculating winrate for {}".format(user.nickname.encode('utf-8'))
  78. db_id = requests.get("http://dotabuff.com/search?q="+user.steam_id).url.split("/")[-1]
  79. result = collect_match_results(db_id, app.config['ANALYTICS_WINRATE_NUM_MATCHES'])
  80. if len(result):
  81. windowed = apply_window(result, app.config['ANALYTICS_WINRATE_WINDOW'])
  82. date_nums = map(lambda x: mktime(x['datetime'].timetuple()),\
  83. result[app.config['ANALYTICS_WINRATE_WINDOW']-1:])
  84. winrate = {'total_games': len(result), 'data': zip(date_nums, windowed) }
  85. user.winrate_data = winrate
  86. db.session.commit()
  87. users_analyzed += 1
  88. print "Finished winrate calculation for {} ({} total games)".format(user.nickname.encode('utf-8'), winrate['total_games'])
  89. sleep(60)
  90. else:
  91. print "DotaBuff unable to access match statistics for {}".format(user.nickname.encode('utf-8'))
  92. app.logger.info("Calculated win rate numbers for {} doobs.".format(users_analyzed))
  93. print "Calculated win rate numbers for {} doobs.".format(users_analyzed)
  94. return users_analyzed