def GET(self): web.header("Content-Type", "text/html; charset=utf-8") query_dict = dict(urlparse.parse_qsl(web.ctx.env['QUERY_STRING'])) target_player = query_dict['player'].decode('utf-8') db = utils.get_mongo_database() games = db.games norm_target_player = norm_name(target_player) games_coll = games.find({'players': norm_target_player}) leaderboard_history_result = db.leaderboard_history.find_one( {'_id': norm_target_player}) leaderboard_history = None if leaderboard_history_result: leaderboard_history = leaderboard_history_result['history'] game_list = [] aliases = set() overall_record = RecordSummary() rec_by_game_size = collections.defaultdict(RecordSummary) rec_by_date = collections.defaultdict(RecordSummary) rec_by_turn_order = collections.defaultdict(RecordSummary) expansion_dist = collections.defaultdict(float) expansion_win_points = collections.defaultdict(float) date_buckets = [1, 3, 5, 10] for g in games_coll: game_val = game.Game(g) if game_val.dubious_quality(): continue all_player_names = game_val.all_player_names() norm_names = map(norm_name, all_player_names) if len(set(norm_names)) != len(all_player_names): continue target_player_cur_name_cand = [ n for n in all_player_names if norm_name(n) == norm_target_player] if len(target_player_cur_name_cand) != 1: continue game_list.append(game_val) target_player_cur_name = target_player_cur_name_cand[0] aliases.add(target_player_cur_name) pd = game_val.get_player_deck(target_player_cur_name) wp = pd.WinPoints() res = game_val.win_loss_tie(target_player_cur_name) overall_record.record_result(res, wp) game_len = len(game_val.get_player_decks()) rec_by_game_size[game_len].record_result(res, wp) _ord = pd.TurnOrder() rec_by_turn_order[_ord].record_result(res, wp) for delta in date_buckets: _padded = (game_val.date() + datetime.timedelta(days = delta)) delta_padded_date = _padded.date() today = datetime.datetime.now().date() if delta_padded_date >= today: rec_by_date[delta].record_result(res, wp) for (ex, wt) in game_val.get_expansion_weight().items(): expansion_dist[ex] += wt expansion_win_points[ex] += wt * wp #TODO: a good choice for a template like jinja2 ret = standard_heading("CouncilRoom.com: Dominion Stats: %s" % target_player) ret += '<form action="/player" method="get">' ret += '<span class="subhead">Profile for %s</span>' % target_player leaderboard_history_most_recent = (leaderboard_history[-1] if leaderboard_history else None) if leaderboard_history_most_recent: level = (leaderboard_history_most_recent[1] - leaderboard_history_most_recent[2]) level = int(max(math.floor(level), 0)) ret += '<span class="level">Level ' + str(level) + '</span>' ret += '<span class="search2">' ret += """ Search for another player: <input type="text" name="player" style="width:100px;" /> <input type="submit" value="View Stats!" /> </span></form><br><br> """ if len(aliases) > 1: ret += 'Aliases: ' + ', '.join(aliases) + '\n' ret += render_record_table('Record by game size', overall_record, rec_by_game_size, lambda game_size: '%d players' % game_size) ret += render_record_table('Recent Record', overall_record, rec_by_date, lambda num_days: 'Last %d days' % num_days) ret += render_record_table('Record by turn order', overall_record, rec_by_turn_order, lambda pos: 'Table position %d' % pos) ret += '<div style="clear: both;"> </div>' ret += '<div class="cardborder yellow"><h3>Expansion Data</h3><table class="stats">' ret += '<tr><th>Card Set<th>Avg. Cards<br/> Per Kingdom<th>Weighted<br/> Win Points<th>Favor' for (ex, weight) in sorted(expansion_dist.iteritems(), key=operator.itemgetter(1), reverse=True): if ex == 'Fan': continue wp = expansion_win_points[ex] / weight average = overall_record.average_win_points() ret += '<tr><th>%s</th>'%ex ret += '<td>%.2f</td>'% (weight * 10. / len(game_list)) ret += '<td>%.2f<td>' % wp if average > 0: ret += '<td>%.2f%%</td>'% ( (wp - average) * 100. / average ) else: ret += '<td>0</td>' ret += '</table></div>' ret += '<div style="clear: both;"> </div>' ret += goals.MaybeRenderGoals(db, norm_target_player) ret += '<A HREF="/popular_buys?player=%s"><h2>Stats by card</h2></A>\n' % target_player ret += '<A HREF="/games_by_opponent?player=%s"><h2>Record by opponent</h2></A>\n' % target_player if leaderboard_history: render = web.template.render('') ret += str(render.player_page_leaderboard_history_template( json.dumps(leaderboard_history))) ret += '<h2>Most recent games</h2>\n' game_list.sort(key = game.Game.get_id, reverse = True) qm = query_matcher.QueryMatcher(p1_name=target_player) for g in game_list[:3]: ret += (query_matcher.GameMatcher(g, qm).display_game_snippet() + '<br>') ret += ('<A HREF="/search_result?p1_name=%s">(See more)</A>' % target_player) ret += '</body></html>' return ret
def GET(self): web.header("Content-Type", "text/html; charset=utf-8") query_dict = dict(urlparse.parse_qsl(web.ctx.env['QUERY_STRING'])) target_player = query_dict['player'].decode('utf-8') db = utils.get_mongo_database() game_stats = db.game_stats norm_target_player = norm_name(target_player) games_coll = game_stats.find({compkey('_id', NAME): norm_target_player}) leaderboard_history_result = db.leaderboard_history.find_one( {'_id': norm_target_player}) leaderboard_history = None if leaderboard_history_result: leaderboard_history = leaderboard_history_result['history'] game_list = [] aliases = set() overall_record = RecordSummary() rec_by_game_size = collections.defaultdict(RecordSummary) rec_by_date = collections.defaultdict(RecordSummary) rec_by_turn_order = collections.defaultdict(RecordSummary) expansion_dist = collections.defaultdict(float) expansion_win_points = collections.defaultdict(float) date_buckets = [1, 3, 5, 10] cutoffs = {} for delta in date_buckets: cutoff = datetime.datetime.now().date() + datetime.timedelta(days = -delta) cutoffs[delta] = cutoff.strftime("%Y%m%d") # NOTE: This assumes that game IDs can be lexically sorted # into temporal order for g in games_coll.sort('_id', pymongo.DESCENDING): g_id = g['_id']['game_id'] game_list.append(g_id) name = g['_id'][NAME] # TODO: Turn this back. The concept of aliases only comes #into play when two different "real" player names both #normalize to the same "normalized" player name. # aliases.add(target_player_cur_name) wp = g[WIN_POINTS] res = g[RESULT] overall_record.record_result(res, wp) game_len = len( g[PLAYERS] ) + 1 rec_by_game_size[game_len].record_result(res, wp) _ord = g[ORDER] rec_by_turn_order[_ord].record_result(res, wp) for delta in date_buckets: if g['game_date'] >= cutoffs[delta]: rec_by_date[delta].record_result(res, wp) supply = [dominioncards.index_to_card(i) for i in g[SUPPLY]] for (ex, wt) in dominioncards.get_expansion_weight(supply).items(): expansion_dist[ex] += wt expansion_win_points[ex] += wt * wp #TODO: a good choice for a template like jinja2 ret = standard_heading("CouncilRoom.com: Dominion Stats: %s" % target_player) ret += '<form action="/player" method="get">' ret += '<span class="subhead">Profile for %s</span>' % target_player leaderboard_history_most_recent = (leaderboard_history[-1] if leaderboard_history else None) if leaderboard_history_most_recent: level = (leaderboard_history_most_recent[1] - leaderboard_history_most_recent[2]) level = int(max(math.floor(level), 0)) ret += '<span class="level">Level ' + str(level) + '</span>' ret += '<span class="search2">' ret += """ Search for another player: <input type="text" name="player" style="width:100px;" /> <input type="submit" value="View Stats!" /> </span></form><br><br> """ if len(aliases) > 1: ret += 'Aliases: ' + ', '.join(aliases) + '\n' ret += render_record_table('Record by game size', overall_record, rec_by_game_size, lambda game_size: '%d players' % game_size) ret += render_record_table('Recent Record', overall_record, rec_by_date, lambda num_days: 'Last %d days' % num_days) ret += render_record_table('Record by turn order', overall_record, rec_by_turn_order, lambda pos: 'Table position %d' % pos) ret += '<div style="clear: both;"> </div>' ret += '<div class="cardborder yellow"><h3>Expansion Data</h3><table class="stats">' ret += '<tr><th>Card Set<th>Avg. Cards<br/> Per Kingdom<th>Weighted<br/> Win Points<th>Favor' for (ex, weight) in sorted(expansion_dist.iteritems(), key=operator.itemgetter(1), reverse=True): if ex == 'Fan': continue wp = expansion_win_points[ex] / weight average = overall_record.average_win_points() ret += '<tr><th>%s</th>'%ex ret += '<td>%.2f</td>'% (weight * 10. / len(game_list)) ret += '<td>%.2f<td>' % wp if average > 0: ret += '<td>%.2f%%</td>'% ( (wp - average) * 100. / average ) else: ret += '<td>0</td>' ret += '</table></div>' ret += '<div style="clear: both;"> </div>' ret += goals.MaybeRenderGoals(db, norm_target_player) ret += '<A HREF="/popular_buys?player=%s"><h2>Stats by card</h2></A>\n' % target_player ret += '<A HREF="/games_by_opponent?player=%s"><h2>Record by opponent</h2></A>\n' % target_player if leaderboard_history: render = web.template.render('') ret += str(render.player_page_leaderboard_history_template( json.dumps(leaderboard_history))) ret += '<h2>Most recent games</h2>\n' qm = query_matcher.QueryMatcher(p1_name=target_player) goko_games = [g for g in game_list if '.txt' in game_list] if len(goko_games) > 2: goko_games.sort(reverse=True) most_recent = goko_games[:3] else: most_recent = game_list[:3] for g_id in most_recent: g = db.games.find_one({'_id': g_id}) game_val = game.Game(g) ret += (query_matcher.GameMatcher(game_val, qm).display_game_snippet() + '<br>') ret += ('<A HREF="/search_result?p1_name=%s">(See more)</A>' % target_player) ret += '</body></html>' return ret
def GET(self): web.header("Content-Type", "text/html; charset=utf-8") query_dict = dict(urlparse.parse_qsl(web.ctx.env['QUERY_STRING'])) target_player = query_dict['player'].decode('utf-8') db = utils.get_mongo_database() games = db.games norm_target_player = norm_name(target_player) games_coll = games.find({'players': norm_target_player}) keyed_by_opp = collections.defaultdict(list) real_name_usage = collections.defaultdict( lambda: collections.defaultdict(int)) game_list = [] aliases = set() overall_record = RecordSummary() rec_by_game_size = collections.defaultdict(RecordSummary) rec_by_date = collections.defaultdict(RecordSummary) rec_by_turn_order = collections.defaultdict(RecordSummary) date_buckets = ( 1, 3, 5, 10 ) for g in games_coll: game_val = game.Game(g) if game_val.dubious_quality(): continue all_player_names = game_val.all_player_names() norm_names = map(norm_name, all_player_names) if len(set(norm_names)) != len(all_player_names): continue target_player_cur_name_cand = [ n for n in all_player_names if norm_name(n) == norm_target_player] if len(target_player_cur_name_cand) != 1: continue game_list.append(game_val) target_player_cur_name = target_player_cur_name_cand[0] aliases.add(target_player_cur_name) for p in game_val.get_player_decks(): if p.name() != target_player_cur_name: other_norm_name = norm_name(p.name()) keyed_by_opp[other_norm_name].append( (p.name(), target_player_cur_name, game_val)) real_name_usage[other_norm_name][p.name()] += 1 else: #this is getting fidgety about 80 chars, which sometimes #can mean that it's getting too nested and could use a #rethink res = game_val.win_loss_tie(p.name()) overall_record.record_result(res, p.WinPoints()) game_len = len(game_val.get_player_decks()) rec_by_game_size[game_len].record_result(res, p.WinPoints()) _ord = p.TurnOrder() rec_by_turn_order[_ord].record_result(res, p.WinPoints()) for delta in date_buckets: _padded = (game_val.date() + datetime.timedelta(days = delta)) delta_padded_date = _padded.date() today = datetime.datetime.now().date() if delta_padded_date >= today: rec_by_date[delta].record_result(res, p.WinPoints()) keyed_by_opp_list = keyed_by_opp.items() keyed_by_opp_list.sort(key = lambda x: (-len(x[1]), x[0])) #TODO: a good choice for a template like jinja2 ret = ('<html><head><title>CouncilRoom.com: Dominion Stats: ' '%s</title></head>\n' % target_player) ret += '<body><A HREF="/">Back to CouncilRoom.com</A><BR><BR>' ret += """ Search for another player: <form action='/player' method='get'> <input type="text" name="player" style="width:100px;" /> <input type="submit" value="Submit" /> </form><hr> """ ret += '<h2>CouncilRoom Profile for %s</h2><BR>' % target_player if len(aliases) > 1: ret += 'Aliases: ' + ', '.join(aliases) + '<br>\n' ret += render_record_table('Record by game size', overall_record, rec_by_game_size, lambda game_size: '%d players' % game_size) ret += render_record_table('Recent Record', overall_record, rec_by_date, lambda num_days: 'Last %d days' % num_days) ret += render_record_table('Record by turn order', overall_record, rec_by_turn_order, lambda pos: 'Table position %d' % pos) ret += '<div style="clear: both;"> </div>' ret += goals.MaybeRenderGoals(db, norm_target_player) ret += '<A HREF="/popular_buys?player=%s"><h2>Stats by card</h2></A><BR>\n' % target_player ret += '<h2>Most recent games</h2>\n' game_list.sort(key = game.Game.get_id, reverse = True) qm = query_matcher.QueryMatcher(p1_name=target_player) for g in game_list[:3]: ret += (query_matcher.GameMatcher(g, qm).display_game_snippet() + '<br>') ret += ('<A HREF="/search_result?p1_name=%s">(See more)</A>' % target_player) ret += '<h2>Record by opponent</h2>' ret += '<table border=1>' ret += '<tr><td>Opponent</td><td>Record</td></tr>' for opp_norm_name, game_list in keyed_by_opp_list: record = [0, 0, 0] for opp_name, tgt_player_curname, g in game_list: record[g.win_loss_tie(tgt_player_curname, opp_name)] += 1 ret += '<tr>' # Get most freq used name for opponent #TODO: lambdas can be switched to itemgetters opp_cannon_name = max(real_name_usage[opp_norm_name].iteritems(), key=lambda x: x[1])[0] row_span = (len(game_list) - 1) / 10 + 1 ret += '<td rowspan=%d>%s</td>' % ( row_span, game.PlayerDeck.PlayerLink(opp_cannon_name)) ret += '<td rowspan=%d>%d-%d-%d</td>' % (row_span, record[0], record[1], record[2]) for idx, (opp_name, tgt_player_curname, g) in enumerate( game_list): if idx % 10 == 0 and idx > 0: ret += '</tr><tr>' ret += g.short_render_cell_with_perspective(tgt_player_curname, opp_name) ret += '</tr>\n' ret += '</table></body></html>' return ret
def GET(self): web.header("Content-Type", "text/html; charset=utf-8") query_dict = dict(urlparse.parse_qsl(web.ctx.env['QUERY_STRING'])) target_player = query_dict['player'].decode('utf-8') db = utils.get_mongo_database() games = db.games norm_target_player = NormName(target_player) games_coll = games.find({'players': norm_target_player}) keyed_by_opp = collections.defaultdict(list) real_name_usage = collections.defaultdict( lambda: collections.defaultdict(int)) game_list = [] aliases = set() overall_record = RecordSummary() rec_by_game_size = collections.defaultdict(RecordSummary) rec_by_date = collections.defaultdict(RecordSummary) rec_by_turn_order = collections.defaultdict(RecordSummary) date_buckets = (1, 3, 5, 10) for g in games_coll: game_val = game.Game(g) if game_val.DubiousQuality(): continue all_player_names = game_val.AllPlayerNames() norm_names = map(NormName, all_player_names) if len(set(norm_names)) != len(all_player_names): continue target_player_cur_name_cand = [ n for n in all_player_names if NormName(n) == norm_target_player ] if len(target_player_cur_name_cand) != 1: continue game_list.append(game_val) target_player_cur_name = target_player_cur_name_cand[0] aliases.add(target_player_cur_name) for p in game_val.PlayerDecks(): if p.Name() != target_player_cur_name: other_norm_name = NormName(p.Name()) keyed_by_opp[other_norm_name].append( (p.Name(), target_player_cur_name, game_val)) real_name_usage[other_norm_name][p.Name()] += 1 else: res = game_val.WinLossTie(p.Name()) overall_record.RecordResult(res, p.WinPoints()) game_len = len(game_val.PlayerDecks()) rec_by_game_size[game_len].RecordResult(res, p.WinPoints()) rec_by_turn_order[p.TurnOrder()].RecordResult( res, p.WinPoints()) for delta in date_buckets: delta_padded_date = ( game_val.Date() + datetime.timedelta(days=delta)).date() today = datetime.datetime.now().date() if (delta_padded_date >= today): rec_by_date[delta].RecordResult(res, p.WinPoints()) keyed_by_opp_list = keyed_by_opp.items() keyed_by_opp_list.sort(key=lambda x: (-len(x[1]), x[0])) ret = ('<html><head><title>CouncilRoom.com: Dominion Stats: ' '%s</title></head>\n' % target_player) ret += '<body><A HREF="/">Back to CouncilRoom.com</A><BR><BR>' ret += """ Player: <form action='/player' method='get'> <input type="text" name="player" style="width:100px;" /> <input type="submit" value="Submit" /> </form> """ if len(aliases) > 1: ret += 'Player aliases: ' + ', '.join(aliases) + '<br>\n' ret += RenderRecordTable('Record by game size', overall_record, rec_by_game_size, lambda game_size: '%d players' % game_size) ret += RenderRecordTable('Recent Record', overall_record, rec_by_date, lambda num_days: 'Last %d days' % num_days) ret += RenderRecordTable('Record by turn order', overall_record, rec_by_turn_order, lambda pos: 'Table position %d' % pos) ret += '<div style="clear: both;"> </div>' ret += goals.MaybeRenderGoals(db, norm_target_player) ret += '<h2>Most recent games</h2>\n' game_list.sort(key=game.Game.Id, reverse=True) qm = query_matcher.QueryMatcher(p1_name=target_player) for g in game_list[:3]: ret += (query_matcher.GameMatcher(g, qm).DisplayGameSnippet() + '<br>') ret += ('<A HREF="/search_result?p1_name=%s">(See more)</A>' % target_player) ret += '<h2>Record by opponent</h2>' ret += '<table border=1>' ret += '<tr><td>Opponent</td><td>Record</td></tr>' for opp_norm_name, game_list in keyed_by_opp_list: record = [0, 0, 0] for opp_name, targ_player_cur_name, g in game_list: record[g.WinLossTie(targ_player_cur_name, opp_name)] += 1 ret += '<tr>' opp_cannon_name = max( # Get most freq used name for opponent real_name_usage[opp_norm_name].iteritems(), key=lambda x: x[1])[0] row_span = (len(game_list) - 1) / 10 + 1 ret += '<td rowspan=%d>%s</td>' % ( row_span, game.PlayerDeck.PlayerLink(opp_cannon_name)) ret += '<td rowspan=%d>%d-%d-%d</td>' % (row_span, record[0], record[1], record[2]) for idx, (opp_name, targ_player_cur_name, g) in enumerate(game_list): if idx % 10 == 0 and idx > 0: ret += '</tr><tr>' ret += g.ShortRenderCellWithPerspective( targ_player_cur_name, opp_name) ret += '</tr>\n' ret += '</table></body></html>' return ret
def GET(self): web.header("Content-Type", "text/html; charset=utf-8") query_dict = dict(urlparse.parse_qsl(web.ctx.env['QUERY_STRING'])) target_player = query_dict['player'].decode('utf-8') db = utils.get_mongo_database() games = db.games norm_target_player = norm_name(target_player) games_coll = games.find({'players': norm_target_player}) keyed_by_opp = collections.defaultdict(list) real_name_usage = collections.defaultdict( lambda: collections.defaultdict(int)) game_list = [] aliases = set() overall_record = RecordSummary() rec_by_game_size = collections.defaultdict(RecordSummary) rec_by_date = collections.defaultdict(RecordSummary) rec_by_turn_order = collections.defaultdict(RecordSummary) date_buckets = ( 1, 3, 5, 10 ) for g in games_coll: game_val = game.Game(g) if game_val.dubious_quality(): continue all_player_names = game_val.all_player_names() norm_names = map(norm_name, all_player_names) if len(set(norm_names)) != len(all_player_names): continue target_player_cur_name_cand = [ n for n in all_player_names if norm_name(n) == norm_target_player] if len(target_player_cur_name_cand) != 1: continue game_list.append(game_val) target_player_cur_name = target_player_cur_name_cand[0] aliases.add(target_player_cur_name) for p in game_val.get_player_decks(): if p.name() != target_player_cur_name: other_norm_name = norm_name(p.name()) keyed_by_opp[other_norm_name].append( (p.name(), target_player_cur_name, game_val)) real_name_usage[other_norm_name][p.name()] += 1 else: #this is getting fidgety about 80 chars, which sometimes #can mean that it's getting too nested and could use a #rethink res = game_val.win_loss_tie(p.name()) overall_record.record_result(res, p.WinPoints()) game_len = len(game_val.get_player_decks()) rec_by_game_size[game_len].record_result(res, p.WinPoints()) _ord = p.TurnOrder() rec_by_turn_order[_ord].record_result(res, p.WinPoints()) for delta in date_buckets: _padded = (game_val.date() + datetime.timedelta(days = delta)) delta_padded_date = _padded.date() today = datetime.datetime.now().date() if delta_padded_date >= today: rec_by_date[delta].record_result(res, p.WinPoints()) keyed_by_opp_list = keyed_by_opp.items() keyed_by_opp_list.sort(key = lambda x: (-len(x[1]), x[0])) #TODO: a good choice for a template like jinja2 ret = standard_heading("CouncilRoom.com: Dominion Stats: %s" % target_player) ret += '<form action="/player" method="get">' ret += '<span class="subhead">Profile for %s</span>' % target_player ret += '<span class="search2">' ret += """ Search for another player: <input type="text" name="player" style="width:100px;" /> <input type="submit" value="View Stats!" /> </span></form><br><br> """ if len(aliases) > 1: ret += 'Aliases: ' + ', '.join(aliases) + '\n' ret += render_record_table('Record by game size', overall_record, rec_by_game_size, lambda game_size: '%d players' % game_size) ret += render_record_table('Recent Record', overall_record, rec_by_date, lambda num_days: 'Last %d days' % num_days) ret += render_record_table('Record by turn order', overall_record, rec_by_turn_order, lambda pos: 'Table position %d' % pos) ret += '<div style="clear: both;"> </div>' ret += goals.MaybeRenderGoals(db, norm_target_player) ret += '<A HREF="/popular_buys?player=%s"><h2>Stats by card</h2></A><BR>\n' % target_player ret += '<h2>Most recent games</h2>\n' game_list.sort(key = game.Game.get_id, reverse = True) qm = query_matcher.QueryMatcher(p1_name=target_player) for g in game_list[:3]: ret += (query_matcher.GameMatcher(g, qm).display_game_snippet() + '<br>') ret += ('<A HREF="/search_result?p1_name=%s">(See more)</A>' % target_player) ret += '<h2>Record by opponent</h2>' ret += '<table border=1>' ret += '<tr><td>Opponent</td><td>Record</td></tr>' for opp_norm_name, game_list in keyed_by_opp_list: record = [0, 0, 0] for opp_name, tgt_player_curname, g in game_list: record[g.win_loss_tie(tgt_player_curname, opp_name)] += 1 ret += '<tr>' # Get most freq used name for opponent #TODO: lambdas can be switched to itemgetters opp_cannon_name = max(real_name_usage[opp_norm_name].iteritems(), key=lambda x: x[1])[0] row_span = (len(game_list) - 1) / 10 + 1 ret += '<td rowspan=%d>%s</td>' % ( row_span, game.PlayerDeck.PlayerLink(opp_cannon_name)) ret += '<td rowspan=%d>%d-%d-%d</td>' % (row_span, record[0], record[1], record[2]) for idx, (opp_name, tgt_player_curname, g) in enumerate( game_list): if idx % 10 == 0 and idx > 0: ret += '</tr><tr>' ret += g.short_render_cell_with_perspective(tgt_player_curname, opp_name) ret += '</tr>\n' ret += '</table></body></html>' return ret
def GET(self): web.header("Content-Type", "text/html; charset=utf-8") query_dict = dict(urlparse.parse_qsl(web.ctx.env['QUERY_STRING'])) target_player = query_dict['player'].decode('utf-8') db = utils.get_mongo_database() game_stats = db.game_stats norm_target_player = norm_name(target_player) games_coll = game_stats.find({compkey('_id', NAME): norm_target_player}) leaderboard_history_result = db.leaderboard_history.find_one( {'_id': norm_target_player}) leaderboard_history = None if leaderboard_history_result: leaderboard_history = leaderboard_history_result['history'] game_list = [] aliases = set() overall_record = RecordSummary() rec_by_game_size = collections.defaultdict(RecordSummary) rec_by_date = collections.defaultdict(RecordSummary) rec_by_turn_order = collections.defaultdict(RecordSummary) expansion_dist = collections.defaultdict(float) expansion_win_points = collections.defaultdict(float) date_buckets = [1, 3, 5, 10] cutoffs = {} for delta in date_buckets: cutoff = datetime.datetime.now().date() + datetime.timedelta(days = -delta) cutoffs[delta] = cutoff.strftime("%Y%m%d") # NOTE: This assumes that game IDs can be lexically sorted # into temporal order for g in games_coll.sort('_id', pymongo.DESCENDING): g_id = g['_id']['game_id'] game_list.append(g_id) name = g['_id'][NAME] # TODO: Turn this back. The concept of aliases only comes #into play when two different "real" player names both #normalize to the same "normalized" player name. # aliases.add(target_player_cur_name) wp = g[WIN_POINTS] res = g[RESULT] overall_record.record_result(res, wp) game_len = len( g[PLAYERS] ) + 1 rec_by_game_size[game_len].record_result(res, wp) _ord = g[ORDER] rec_by_turn_order[_ord].record_result(res, wp) for delta in date_buckets: if g['game_date'] >= cutoffs[delta]: rec_by_date[delta].record_result(res, wp) supply = [dominioncards.index_to_card(i) for i in g[SUPPLY]] for (ex, wt) in dominioncards.get_expansion_weight(supply).items(): expansion_dist[ex] += wt expansion_win_points[ex] += wt * wp #TODO: a good choice for a template like jinja2 ret = standard_heading("CouncilRoom.com: Dominion Stats: %s" % target_player) ret += '<form action="/player" method="get">' ret += '<span class="subhead">Profile for %s</span>' % target_player leaderboard_history_most_recent = (leaderboard_history[-1] if leaderboard_history else None) if leaderboard_history_most_recent: level = (leaderboard_history_most_recent[1] - leaderboard_history_most_recent[2]) level = int(max(math.floor(level), 0)) ret += '<span class="level">Level ' + str(level) + '</span>' ret += '<span class="search2">' ret += """ Search for another player: <input type="text" name="player" style="width:100px;" /> <input type="submit" value="View Stats!" /> </span></form><br><br> """ if len(aliases) > 1: ret += 'Aliases: ' + ', '.join(aliases) + '\n' ret += render_record_table('Record by game size', overall_record, rec_by_game_size, lambda game_size: '%d players' % game_size) ret += render_record_table('Recent Record', overall_record, rec_by_date, lambda num_days: 'Last %d days' % num_days) ret += render_record_table('Record by turn order', overall_record, rec_by_turn_order, lambda pos: 'Table position %d' % pos) ret += '<div style="clear: both;"> </div>' ret += '<div class="cardborder yellow"><h3>Expansion Data</h3><table class="stats">' ret += '<tr><th>Card Set<th>Avg. Cards<br/> Per Kingdom<th>Weighted<br/> Win Points<th>Favor' for (ex, weight) in sorted(expansion_dist.iteritems(), key=operator.itemgetter(1), reverse=True): if ex == 'Fan': continue wp = expansion_win_points[ex] / weight average = overall_record.average_win_points() ret += '<tr><th>%s</th>'%ex ret += '<td>%.2f</td>'% (weight * 10. / len(game_list)) ret += '<td>%.2f<td>' % wp if average > 0: ret += '<td>%.2f%%</td>'% ( (wp - average) * 100. / average ) else: ret += '<td>0</td>' ret += '</table></div>' ret += '<div style="clear: both;"> </div>' ret += goals.MaybeRenderGoals(db, norm_target_player) ret += '<A HREF="/popular_buys?player=%s"><h2>Stats by card</h2></A>\n' % target_player ret += '<A HREF="/games_by_opponent?player=%s"><h2>Record by opponent</h2></A>\n' % target_player if leaderboard_history: render = web.template.render('') ret += str(render.player_page_leaderboard_history_template( json.dumps(leaderboard_history))) ret += '<h2>Most recent games</h2>\n' qm = query_matcher.QueryMatcher(p1_name=target_player) for g_id in game_list[:3]: g = db.games.find_one({'_id': g_id}) game_val = game.Game(g) ret += (query_matcher.GameMatcher(game_val, qm).display_game_snippet() + '<br>') ret += ('<A HREF="/search_result?p1_name=%s">(See more)</A>' % target_player) ret += '</body></html>' return ret