2 from datetime import datetime, timedelta
4 from beaker.cache import cache_region
5 from xonstat.models import DBSession, PlayerRank, ActivePlayer, ActiveServer, ActiveMap
6 from xonstat.views.helpers import RecentGame, recent_games_q
8 log = logging.getLogger(__name__)
11 @cache_region('hourly_term')
12 def get_summary_stats(scope="all"):
14 Gets the following aggregate statistics according to the provided scope:
16 - the number of active players
17 - the number of games per game type
19 Scope can be "all" or "day".
21 The fetched information is summarized into a string which is passed
22 directly to the template.
24 if scope not in ["all", "day"]:
28 ss = DBSession.query("num_players", "game_type_cd", "num_games").\
30 "SELECT num_players, game_type_cd, num_games "
31 "FROM summary_stats_mv "
32 "WHERE scope = :scope "
33 "ORDER BY sort_order "
34 ).params(scope=scope).all()
40 # the number of players is constant in each row
41 total_players = row.num_players
43 total_games += row.num_games
45 # we can't show all game types on the single summary line, so any
46 # past the fifth one will get bundled in to an "other" count
48 other_games += row.num_games
52 # don't send anything if we don't have any activity
56 # This is ugly because we're doing template-like stuff within the
57 # view code. The alternative isn't any better, though: we would
58 # have to assemble the string inside the template by using a Python
59 # code block. For now I'll leave it like this since it is the lesser
61 # Also we need to hard-code the URL structure in here to allow caching,
63 in_paren = "; ".join(["{:2,d} {}".format(
65 "<a href='/games?type={0}'>{0}</a>".format(g.game_type_cd)
69 in_paren += "; {:2,d} other".format(other_games)
71 stat_line = "{:2,d} players and {:2,d} games ({})".format(
77 except Exception as e:
84 @cache_region('hourly_term')
85 def get_ranks(game_type_cd):
87 Gets a set number of the top-ranked people for the specified game_type_cd.
89 The game_type_cd parameter is the type to fetch. Currently limited to
90 duel, dm, ctf, and tdm.
92 # how many ranks we want to fetch
93 leaderboard_count = 10
95 # only a few game modes are actually ranked
96 if game_type_cd not in 'duel' 'dm' 'ctf' 'tdm':
99 ranks = DBSession.query(PlayerRank).\
100 filter(PlayerRank.game_type_cd==game_type_cd).\
101 order_by(PlayerRank.rank).\
102 limit(leaderboard_count).all()
107 @cache_region('hourly_term')
108 def get_top_players_by_time(limit=None, start=None):
110 The top players by the amount of time played during a date range.
112 q = DBSession.query(ActivePlayer)
114 if start is not None:
115 q = q.filter(ActivePlayer.sort_order >= start)
117 q = q.order_by(ActivePlayer.sort_order)
119 if limit is not None:
125 @cache_region('hourly_term')
126 def get_top_servers_by_games(limit=None, start=None):
128 The top servers by the number of games played during a date range.
130 q = DBSession.query(ActiveServer)
132 if start is not None:
133 q = q.filter(ActiveServer.sort_order >= start)
135 q = q.order_by(ActiveServer.sort_order)
137 if limit is not None:
143 @cache_region('hourly_term')
144 def get_top_maps_by_games(limit=None, start=None):
146 The top maps by the number of games played during a date range.
148 q = DBSession.query(ActiveMap)
150 if start is not None:
151 q = q.filter(ActiveMap.sort_order >= start)
153 q = q.order_by(ActiveMap.sort_order)
155 if limit is not None:
161 def _main_index_data(request):
163 leaderboard_lifetime = int(
164 request.registry.settings['xonstat.leaderboard_lifetime'])
166 leaderboard_lifetime = 30
168 leaderboard_count = 10
169 recent_games_count = 20
171 # summary statistics for the tagline
172 stat_line = get_summary_stats("all")
173 day_stat_line = get_summary_stats("day")
176 # the three top ranks tables
178 for gtc in ['duel', 'ctf', 'dm', 'tdm']:
179 rank = get_ranks(gtc)
183 right_now = datetime.utcnow()
184 back_then = datetime.utcnow() - timedelta(days=leaderboard_lifetime)
186 # top players by playing time
187 top_players = get_top_players_by_time(10)
189 # top servers by number of games
190 top_servers = get_top_servers_by_games(10)
192 # top maps by total times played
193 top_maps = get_top_maps_by_games(10)
195 # recent games played in descending order
196 rgs = recent_games_q(cutoff=back_then).limit(recent_games_count).all()
197 recent_games = [RecentGame(row) for row in rgs]
199 return {'top_players':top_players,
200 'top_servers':top_servers,
202 'recent_games':recent_games,
204 'stat_line':stat_line,
205 'day_stat_line':day_stat_line,
209 def main_index(request):
211 Display the main page information.
213 return _main_index_data(request)
216 def main_index_json(request):
218 JSON output of the main page information.
220 return [{'status':'not implemented'}]
223 def top_players_index(request):
225 start = int(request.params.get('start', None))
229 top_players = get_top_players_by_time(20, start)
231 # building a query string
233 if len(top_players) > 1:
234 query['start'] = top_players[-1].sort_order + 1
237 'top_players':top_players,
243 def top_servers_index(request):
245 start = int(request.params.get('start', None))
249 top_servers = get_top_servers_by_games(20, start)
251 # building a query string
253 if len(top_servers) > 1:
254 query['start'] = top_servers[-1].sort_order + 1
257 'top_servers':top_servers,
263 def top_maps_index(request):
265 start = int(request.params.get('start', None))
269 top_maps = get_top_maps_by_games(20, start)
271 # building a query string
273 if len(top_maps) > 1:
274 query['start'] = top_maps[-1].sort_order + 1