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.

76 lines
2.9 KiB

  1. import requests
  2. from time import sleep, mktime
  3. from bs4 import BeautifulSoup
  4. from datetime import datetime
  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. soup = BeautifulSoup(data).article.table.tbody
  15. # Catch last page
  16. if 'sorry' in soup.tr.td.text.lower():
  17. break
  18. # Parse the matches on current page
  19. for row in soup.find_all('tr'):
  20. # Pass over bot matches and other 'inactive' games
  21. if 'inactive' in row.get('class', ''): continue
  22. cells = row.find_all('td')
  23. result_cell = cells[2]
  24. match_cell = cells[3]
  25. match_id = int(result_cell.a['href'].split('/')[-1])
  26. match_type = match_cell.div.text
  27. if match_type in MODES_TO_SKIP: continue
  28. result = True if 'won' in result_cell.a['class'] else False
  29. dt = datetime.strptime(result_cell.time['datetime'], '%Y-%m-%dT%H:%M:%S+00:00')
  30. results.append({'match_id':match_id, 'win':result, 'datetime':dt, 'game_mode':match_type})
  31. if len(results) > num_matches:
  32. break
  33. if len(results) > num_matches:
  34. break
  35. sleep(60)
  36. results.reverse()
  37. return results
  38. def apply_window(results, window_size=50):
  39. windows = []
  40. # Compute the initial window
  41. win_rate = 0.00
  42. for idx in range(0, window_size):
  43. win_rate += 1 if results[idx]['win'] else 0
  44. win_rate /= window_size
  45. windows.append(win_rate)
  46. # From here on, modify based on leave/enter data points
  47. fractional_change = 1. / window_size
  48. for idx in range(window_size, len(results)):
  49. if results[idx-window_size]['win'] == results[idx]['win']:
  50. pass
  51. elif results[idx]['win']:
  52. win_rate += fractional_change
  53. else:
  54. win_rate -= fractional_change
  55. windows.append(win_rate)
  56. return windows
  57. def calculate_winrates():
  58. users_analyzed = 0
  59. for user in models.User.query.all():
  60. db_id = requests.get("http://dotabuff.com/search?q="+user.steam_id).url.split("/")[-1]
  61. result = collect_match_results(db_id, app.config['ANALYTICS_WINRATE_NUM_MATCHES'])
  62. windowed = apply_window(result, app.config['ANALYTICS_WINRATE_WINDOW'])
  63. date_nums = map(lambda x: mktime(x['datetime'].timetuple()),\
  64. result[app.config['ANALYTICS_WINRATE_WINDOW']-1:])
  65. winrate = {'total_games': len(result), 'data': zip(date_nums, windowed) }
  66. user.winrate_data = winrate
  67. db.session.commit()
  68. users_analyzed += 1
  69. sleep(60)
  70. app.logger.info("Calculated win rate numbers for {} doobs.".format(users_analyzed))
  71. return users_analyzed