Brandon Cornejo
11 years ago
14 changed files with 352 additions and 92 deletions
-
1.gitignore
-
24app.py
-
27app/__init__.py
-
76app/analytics.py
-
6app/models.py
-
13app/static/css/app.css
-
35app/teamspeak.py
-
10app/templates/edit_event.html
-
8app/templates/layout.html
-
108app/templates/profile.html
-
2app/templates/sidenav.html
-
2app/templates/teamspeak.html
-
26migrations/versions/1c90e0fd276a_.py
-
102run.py
@ -1,24 +0,0 @@ |
|||||
#!venv/bin/python |
|
||||
from flask import Flask |
|
||||
from flask.ext.script import Manager, Server |
|
||||
from flask.ext.migrate import Migrate, MigrateCommand |
|
||||
|
|
||||
from app import * |
|
||||
|
|
||||
SQLALCHEMY_DATABASE_URI = 'mysql://root:$perwePP@localhost/dotanoobs' |
|
||||
|
|
||||
migrate = Migrate(app, db) |
|
||||
manager = Manager(app) |
|
||||
manager.add_command('db', MigrateCommand) |
|
||||
|
|
||||
|
|
||||
@manager.command |
|
||||
def admin(name): |
|
||||
u = models.User.query.filter_by(nickname=name).first() |
|
||||
if u and not u.admin: |
|
||||
u.admin = True |
|
||||
db.session.commit() |
|
||||
print "User {} has been granted admin access.".format(name) |
|
||||
|
|
||||
if __name__ == '__main__': |
|
||||
manager.run() |
|
@ -0,0 +1,76 @@ |
|||||
|
import requests |
||||
|
from time import sleep, mktime |
||||
|
from bs4 import BeautifulSoup |
||||
|
from datetime import datetime |
||||
|
|
||||
|
from app import app, db, models |
||||
|
|
||||
|
MODES_TO_SKIP = ['Ability Draft', 'Greeviling', 'Diretide'] |
||||
|
|
||||
|
def collect_match_results(dotabuff_id, num_matches): |
||||
|
results = [] |
||||
|
page = 0 |
||||
|
while True: |
||||
|
page += 1 |
||||
|
url = "http://dotabuff.com/players/{}/matches/?page={}".format(dotabuff_id, page) |
||||
|
data = requests.get(url).text |
||||
|
soup = BeautifulSoup(data).article.table.tbody |
||||
|
# Catch last page |
||||
|
if 'sorry' in soup.tr.td.text.lower(): |
||||
|
break |
||||
|
# Parse the matches on current page |
||||
|
for row in soup.find_all('tr'): |
||||
|
# Pass over bot matches and other 'inactive' games |
||||
|
if 'inactive' in row.get('class', ''): continue |
||||
|
cells = row.find_all('td') |
||||
|
result_cell = cells[2] |
||||
|
match_cell = cells[3] |
||||
|
match_id = int(result_cell.a['href'].split('/')[-1]) |
||||
|
match_type = match_cell.div.text |
||||
|
if match_type in MODES_TO_SKIP: continue |
||||
|
result = True if 'won' in result_cell.a['class'] else False |
||||
|
dt = datetime.strptime(result_cell.time['datetime'], '%Y-%m-%dT%H:%M:%S+00:00') |
||||
|
results.append({'match_id':match_id, 'win':result, 'datetime':dt, 'game_mode':match_type}) |
||||
|
if len(results) > num_matches: |
||||
|
break |
||||
|
if len(results) > num_matches: |
||||
|
break |
||||
|
sleep(60) |
||||
|
results.reverse() |
||||
|
return results |
||||
|
|
||||
|
def apply_window(results, window_size=50): |
||||
|
windows = [] |
||||
|
# Compute the initial window |
||||
|
win_rate = 0.00 |
||||
|
for idx in range(0, window_size): |
||||
|
win_rate += 1 if results[idx]['win'] else 0 |
||||
|
win_rate /= window_size |
||||
|
windows.append(win_rate) |
||||
|
# From here on, modify based on leave/enter data points |
||||
|
fractional_change = 1. / window_size |
||||
|
for idx in range(window_size, len(results)): |
||||
|
if results[idx-window_size]['win'] == results[idx]['win']: |
||||
|
pass |
||||
|
elif results[idx]['win']: |
||||
|
win_rate += fractional_change |
||||
|
else: |
||||
|
win_rate -= fractional_change |
||||
|
windows.append(win_rate) |
||||
|
return windows |
||||
|
|
||||
|
def calculate_winrates(): |
||||
|
users_analyzed = 0 |
||||
|
for user in models.User.query.all(): |
||||
|
db_id = requests.get("http://dotabuff.com/search?q="+user.steam_id).url.split("/")[-1] |
||||
|
result = collect_match_results(db_id, app.config['ANALYTICS_WINRATE_NUM_MATCHES']) |
||||
|
windowed = apply_window(result, app.config['ANALYTICS_WINRATE_WINDOW']) |
||||
|
date_nums = map(lambda x: mktime(x['datetime'].timetuple()),\ |
||||
|
result[app.config['ANALYTICS_WINRATE_WINDOW']-1:]) |
||||
|
winrate = {'total_games': len(result), 'data': zip(date_nums, windowed) } |
||||
|
user.winrate_data = winrate |
||||
|
db.session.commit() |
||||
|
users_analyzed += 1 |
||||
|
sleep(60) |
||||
|
app.logger.info("Calculated win rate numbers for {} doobs.".format(users_analyzed)) |
||||
|
return users_analyzed |
@ -0,0 +1,26 @@ |
|||||
|
"""empty message |
||||
|
|
||||
|
Revision ID: 1c90e0fd276a |
||||
|
Revises: a6f7dd522b7 |
||||
|
Create Date: 2014-06-24 19:01:39.358682 |
||||
|
|
||||
|
""" |
||||
|
|
||||
|
# revision identifiers, used by Alembic. |
||||
|
revision = '1c90e0fd276a' |
||||
|
down_revision = 'a6f7dd522b7' |
||||
|
|
||||
|
from alembic import op |
||||
|
import sqlalchemy as sa |
||||
|
|
||||
|
|
||||
|
def upgrade(): |
||||
|
### commands auto generated by Alembic - please adjust! ### |
||||
|
op.add_column('user', sa.Column('winrate_data', sa.Json(), nullable=True)) |
||||
|
### end Alembic commands ### |
||||
|
|
||||
|
|
||||
|
def downgrade(): |
||||
|
### commands auto generated by Alembic - please adjust! ### |
||||
|
op.drop_column('user', 'winrate_data') |
||||
|
### end Alembic commands ### |
@ -0,0 +1,102 @@ |
|||||
|
#!venv/bin/python |
||||
|
from flask import Flask |
||||
|
from flask.ext.script import Manager, Server |
||||
|
from flask.ext.migrate import Migrate, MigrateCommand |
||||
|
|
||||
|
from app import app, db, models |
||||
|
|
||||
|
#SQLALCHEMY_DATABASE_URI = 'mysql://root:$perwePP@localhost/dotanoobs' |
||||
|
|
||||
|
migrate = Migrate(app, db) |
||||
|
manager = Manager(app) |
||||
|
manager.add_command('db', MigrateCommand) |
||||
|
|
||||
|
def createTeamspeakInstance(): |
||||
|
import ts3 |
||||
|
s = ts3.TS3Server(app.config['TS3_HOST'], app.config['TS3_PORT']) |
||||
|
s.login(app.config['TS3_USERNAME'], app.config['TS3_PASSWORD']) |
||||
|
s.use(1) |
||||
|
return s |
||||
|
|
||||
|
@manager.command |
||||
|
def install_cronjobs(): |
||||
|
from os import path |
||||
|
from crontab import CronTab |
||||
|
cron = CronTab(user=True) |
||||
|
|
||||
|
# Clear out existing jobs |
||||
|
cron.remove_all(comment='DOOBSAUTO') |
||||
|
|
||||
|
def make_job(job): |
||||
|
p = path.realpath(__file__) |
||||
|
c = cron.new(command='{}/venv/bin/python {} {}'.format(path.split(p)[0],\ |
||||
|
p, job), comment='DOOBSAUTO') |
||||
|
return c |
||||
|
|
||||
|
# Create the jobs |
||||
|
winrate = make_job('calc_winrates') |
||||
|
ts3_move_afk = make_job('ts3_move_afk') |
||||
|
ts3_snapshot = make_job('ts3_snapshot') |
||||
|
ts3_award_points = make_job('ts3_award_points') |
||||
|
ts3_process_events = make_job('ts3_process_events') |
||||
|
|
||||
|
# Set their frequency to run |
||||
|
winrate.every(1).day() |
||||
|
ts3_move_afk.every(app.config['MOVE_AFK_FREQUENCY']).minute() |
||||
|
ts3_snapshot.every(app.config['SNAPSHOT_FREQUENCY']).hour() |
||||
|
ts3_award_points.every(app.config['AWARD_POINTS_FREQUENCY']).minute() |
||||
|
ts3_process_events.every(app.config['PROCESS_EVENTS_FREQUENCY']).hour() |
||||
|
|
||||
|
try: |
||||
|
assert True == winrate.is_valid() |
||||
|
assert True == ts3_move_afk.is_valid() |
||||
|
assert True == ts3_snapshot.is_valid() |
||||
|
assert True == ts3_award_points.is_valid() |
||||
|
assert True == ts3_process_events.is_valid() |
||||
|
except AssertionError as e: |
||||
|
print "Problem installing cronjobs: {}".format(e) |
||||
|
else: |
||||
|
cron.write() |
||||
|
print "Cron jobs written succesfully" |
||||
|
|
||||
|
@manager.command |
||||
|
def admin(name): |
||||
|
u = models.User.query.filter_by(nickname=name).first() |
||||
|
if u and not u.admin: |
||||
|
u.admin = True |
||||
|
db.session.commit() |
||||
|
print "User {} has been granted admin access.".format(name) |
||||
|
|
||||
|
@manager.command |
||||
|
def calc_winrates(): |
||||
|
from app.analytics import calculate_winrates |
||||
|
tsServer = createTeamspeakInstance() |
||||
|
calculate_winrates() |
||||
|
|
||||
|
@manager.command |
||||
|
def ts3_move_afk(): |
||||
|
from app.teamspeak import idle_mover |
||||
|
tsServer = createTeamspeakInstance() |
||||
|
idle_mover(tsServer) |
||||
|
|
||||
|
@manager.command |
||||
|
def ts3_snapshot(): |
||||
|
from app.teamspeak import store_active_data |
||||
|
tsServer = createTeamspeakInstance() |
||||
|
store_active_data(tsServer) |
||||
|
|
||||
|
@manager.command |
||||
|
def ts3_award_points(): |
||||
|
from app.teamspeak import award_idle_ts3_points |
||||
|
tsServer = createTeamspeakInstance() |
||||
|
award_idle_ts3_points(tsServer) |
||||
|
|
||||
|
@manager.command |
||||
|
def ts3_process_events(): |
||||
|
from app.teamspeak import process_ts3_events |
||||
|
tsServer = createTeamspeakInstance() |
||||
|
process_ts3_events(tsServer) |
||||
|
|
||||
|
|
||||
|
if __name__ == '__main__': |
||||
|
manager.run() |
Write
Preview
Loading…
Cancel
Save
Reference in new issue