2 import sqlalchemy as sa
3 import sqlalchemy.sql.functions as func
4 import sqlalchemy.sql.expression as expr
5 from beaker.cache import cache_regions, cache_region
6 from collections import namedtuple
7 from datetime import datetime, timedelta
8 from pyramid.response import Response
9 from xonstat.models import *
10 from xonstat.util import *
11 from xonstat.views.helpers import RecentGame, recent_games_q
12 from webhelpers.paginate import Page
15 log = logging.getLogger(__name__)
18 @cache_region('hourly_term')
19 def get_summary_stats(cutoff_days=None):
21 Gets the following aggregate statistics about the past cutoff_days days:
22 - the number of active players
23 - the number of games per game type
24 If cutoff_days is None, the above stats are calculated for all time.
26 This information is then summarized into a string which is passed
27 directly to the template.
30 if cutoff_days is not None:
31 # only games played during this range are considered
32 right_now = datetime.now()
33 cutoff_dt = right_now - timedelta(days=cutoff_days)
35 games = DBSession.query(Game.game_type_cd, func.count()).\
36 filter(expr.between(Game.create_dt, cutoff_dt, right_now)).\
37 group_by(Game.game_type_cd).\
38 order_by(expr.desc(func.count())).all()
40 active_players = DBSession.query(func.count(sa.distinct(PlayerGameStat.player_id))).\
41 filter(PlayerGameStat.player_id > 2).\
42 filter(expr.between(PlayerGameStat.create_dt, cutoff_dt, right_now)).\
45 games = DBSession.query(Game.game_type_cd, func.count()).\
46 group_by(Game.game_type_cd).\
47 order_by(expr.desc(func.count())).all()
49 active_players = DBSession.query(func.count(sa.distinct(PlayerGameStat.player_id))).\
50 filter(PlayerGameStat.player_id > 2).\
55 total_games += total[1]
61 other_games += total[1]
64 # don't send anything if we don't have any activity
68 # This is ugly because we're doing template-like stuff within the
69 # view code. The alternative isn't any better, though: we would
70 # have to assemble the string inside the template by using a Python
71 # code block. For now I'll leave it like this since it is the lesser
73 # Also we need to hard-code the URL structure in here to allow caching,
75 in_paren = "; ".join(["{:2,d} {}".format(
77 "<a href='/games?type={0}'>{0}</a>".format(g[0])
78 ) for g in games[:5]])
81 in_paren += "; {:2,d} other".format(other_games)
83 stat_line = "{:2,d} active players and {:2,d} games ({})".format(
89 except Exception as e:
95 @cache_region('hourly_term')
96 def get_ranks(game_type_cd):
98 Gets a set number of the top-ranked people for the specified game_type_cd.
100 The game_type_cd parameter is the type to fetch. Currently limited to
101 duel, dm, ctf, and tdm.
103 # how many ranks we want to fetch
104 leaderboard_count = 10
106 # only a few game modes are actually ranked
107 if game_type_cd not in 'duel' 'dm' 'ctf' 'tdm':
110 ranks = DBSession.query(PlayerRank).\
111 filter(PlayerRank.game_type_cd==game_type_cd).\
112 order_by(PlayerRank.rank).\
113 limit(leaderboard_count).all()
118 def top_players_by_time_q(cutoff_days):
120 Query for the top players by the amount of time played during a date range.
122 Games older than cutoff_days days old are ignored.
125 # only games played during this range are considered
126 right_now = datetime.utcnow()
127 cutoff_dt = right_now - timedelta(days=cutoff_days)
129 top_players_q = DBSession.query(Player.player_id, Player.nick,
130 func.sum(PlayerGameStat.alivetime)).\
131 filter(Player.player_id == PlayerGameStat.player_id).\
132 filter(Player.player_id > 2).\
133 filter(expr.between(PlayerGameStat.create_dt, cutoff_dt, right_now)).\
134 order_by(expr.desc(func.sum(PlayerGameStat.alivetime))).\
135 group_by(Player.nick).\
136 group_by(Player.player_id)
141 @cache_region('hourly_term')
142 def get_top_players_by_time(cutoff_days):
144 The top players by the amount of time played during a date range.
146 Games older than cutoff_days days old are ignored.
148 # how many to retrieve
151 # only games played during this range are considered
152 right_now = datetime.utcnow()
153 cutoff_dt = right_now - timedelta(days=cutoff_days)
155 top_players_q = top_players_by_time_q(cutoff_days)
157 top_players = top_players_q.limit(count).all()
159 top_players = [(player_id, html_colors(nick), score) \
160 for (player_id, nick, score) in top_players]
165 def top_servers_by_players_q(cutoff_days):
167 Query to get the top servers by the amount of players active
170 Games older than cutoff_days days old are ignored.
172 # only games played during this range are considered
173 right_now = datetime.utcnow()
174 cutoff_dt = right_now - timedelta(days=cutoff_days)
176 top_servers_q = DBSession.query(Server.server_id, Server.name,
178 filter(Game.server_id==Server.server_id).\
179 filter(expr.between(Game.create_dt, cutoff_dt, right_now)).\
180 order_by(expr.desc(func.count(Game.game_id))).\
181 group_by(Server.server_id).\
182 group_by(Server.name)
187 @cache_region('hourly_term')
188 def get_top_servers_by_players(cutoff_days):
190 The top servers by the amount of players active during a date range.
192 Games older than cutoff_days days old are ignored.
194 # how many to retrieve
197 top_servers = top_servers_by_players_q(cutoff_days).limit(count).all()
202 def top_maps_by_times_played_q(cutoff_days):
204 Query to retrieve the top maps by the amount of times it was played
207 Games older than cutoff_days days old are ignored.
209 # only games played during this range are considered
210 right_now = datetime.utcnow()
211 cutoff_dt = right_now - timedelta(days=cutoff_days)
213 top_maps_q = DBSession.query(Game.map_id, Map.name,
215 filter(Map.map_id==Game.map_id).\
216 filter(expr.between(Game.create_dt, cutoff_dt, right_now)).\
217 order_by(expr.desc(func.count())).\
218 group_by(Game.map_id).\
224 @cache_region('hourly_term')
225 def get_top_maps_by_times_played(cutoff_days):
227 The top maps by the amount of times it was played during a date range.
229 Games older than cutoff_days days old are ignored.
231 # how many to retrieve
234 top_maps = top_maps_by_times_played_q(cutoff_days).limit(count).all()
239 def _main_index_data(request):
241 leaderboard_lifetime = int(
242 request.registry.settings['xonstat.leaderboard_lifetime'])
244 leaderboard_lifetime = 30
246 leaderboard_count = 10
247 recent_games_count = 20
249 # summary statistics for the tagline
250 stat_line = get_summary_stats()
251 day_stat_line = get_summary_stats(1)
254 # the three top ranks tables
256 for gtc in ['duel', 'ctf', 'dm', 'tdm']:
257 rank = get_ranks(gtc)
261 right_now = datetime.utcnow()
262 back_then = datetime.utcnow() - timedelta(days=leaderboard_lifetime)
264 # top players by playing time
265 top_players = get_top_players_by_time(leaderboard_lifetime)
267 # top servers by number of total players played
268 top_servers = get_top_servers_by_players(leaderboard_lifetime)
270 # top maps by total times played
271 top_maps = get_top_maps_by_times_played(leaderboard_lifetime)
273 # recent games played in descending order
274 rgs = recent_games_q(cutoff=back_then).limit(recent_games_count).all()
275 recent_games = [RecentGame(row) for row in rgs]
277 return {'top_players':top_players,
278 'top_servers':top_servers,
280 'recent_games':recent_games,
282 'stat_line':stat_line,
283 'day_stat_line':day_stat_line,
287 def main_index(request):
289 Display the main page information.
291 mainindex_data = _main_index_data(request)
293 # FIXME: code clone, should get these from _main_index_data
294 leaderboard_count = 10
295 recent_games_count = 20
297 for i in range(leaderboard_count-len(mainindex_data['top_players'])):
298 mainindex_data['top_players'].append(('-', '-', '-'))
300 for i in range(leaderboard_count-len(mainindex_data['top_servers'])):
301 mainindex_data['top_servers'].append(('-', '-', '-'))
303 for i in range(leaderboard_count-len(mainindex_data['top_maps'])):
304 mainindex_data['top_maps'].append(('-', '-', '-'))
306 return mainindex_data
309 def main_index_json(request):
311 JSON output of the main page information.
313 return [{'status':'not implemented'}]
316 def top_players_by_time(request):
317 current_page = request.params.get('page', 1)
319 cutoff_days = int(request.registry.settings.\
320 get('xonstat.leaderboard_lifetime', 30))
322 top_players_q = top_players_by_time_q(cutoff_days)
324 top_players = Page(top_players_q, current_page, items_per_page=25, url=page_url)
326 top_players.items = [(player_id, html_colors(nick), score) \
327 for (player_id, nick, score) in top_players.items]
329 return {'top_players':top_players}
332 def top_servers_by_players(request):
333 current_page = request.params.get('page', 1)
335 cutoff_days = int(request.registry.settings.\
336 get('xonstat.leaderboard_lifetime', 30))
338 top_servers_q = top_servers_by_players_q(cutoff_days)
340 top_servers = Page(top_servers_q, current_page, items_per_page=25, url=page_url)
342 return {'top_servers':top_servers}
345 def top_maps_by_times_played(request):
346 current_page = request.params.get('page', 1)
348 cutoff_days = int(request.registry.settings.\
349 get('xonstat.leaderboard_lifetime', 30))
351 top_maps_q = top_maps_by_times_played_q(cutoff_days)
353 top_maps = Page(top_maps_q, current_page, items_per_page=25, url=page_url)
355 return {'top_maps':top_maps}