2 from datetime import datetime, timedelta
4 from beaker.cache import cache_region
5 from sqlalchemy import text
6 from xonstat.models import DBSession, PlayerRank, ActivePlayer, ActiveServer, ActiveMap
7 from xonstat.views.helpers import RecentGame, recent_games_q
9 log = logging.getLogger(__name__)
12 @cache_region('hourly_term')
13 def summary_stats_data(scope="all"):
15 Gets the summary stats (number of active players, the game type, and the number of games)
18 :param scope: The scope to fetch from the table. May be "all" or "day".
21 sql = text("SELECT num_players, game_type_cd, num_games, create_dt refresh_dt "
22 "FROM summary_stats_mv "
23 "WHERE scope = :scope "
24 "ORDER BY sort_order ")
27 ss = DBSession.query("num_players", "game_type_cd", "num_games", "refresh_dt").\
28 from_statement(sql).params(scope=scope).all()
31 except Exception as e:
36 def summary_stats_json(request):
37 scope = request.params.get("scope", "all")
38 if scope not in ["all", "day"]:
41 ss = summary_stats_data(scope)
45 last_refreshed = "unknown"
49 players = ss[0].num_players
50 last_refreshed = ss[0].refresh_dt.isoformat()
51 games = [{"game_type_cd": r.game_type_cd, "num_games": r.num_games} for r in ss]
56 "last_refreshed": last_refreshed,
61 @cache_region('hourly_term')
62 def summary_stats_string(scope="all"):
64 Assembles the summary stats data into a readable line for direct inclusion in templates.
67 ss = summary_stats_data(scope)
73 # the number of players is constant in each row
74 total_players = row.num_players
76 total_games += row.num_games
78 # we can't show all game types on the single summary line, so any
79 # past the fifth one will get bundled in to an "other" count
81 other_games += row.num_games
85 # don't send anything if we don't have any activity
89 # This is ugly because we're doing template-like stuff within the
90 # view code. The alternative isn't any better, though: we would
91 # have to assemble the string inside the template by using a Python
92 # code block. For now I'll leave it like this since it is the lesser
94 # Also we need to hard-code the URL structure in here to allow caching,
96 in_paren = "; ".join(["{:2,d} {}".format(
98 "<a href='/games?type={0}'>{0}</a>".format(g.game_type_cd)
102 in_paren += "; {:2,d} other".format(other_games)
104 stat_line = "{:2,d} players and {:2,d} games ({})".format(
110 except Exception as e:
117 @cache_region('hourly_term')
118 def get_ranks(game_type_cd):
120 Gets a set number of the top-ranked people for the specified game_type_cd.
122 The game_type_cd parameter is the type to fetch. Currently limited to
123 duel, dm, ctf, and tdm.
125 # how many ranks we want to fetch
126 leaderboard_count = 10
128 # only a few game modes are actually ranked
129 if game_type_cd not in 'duel' 'dm' 'ctf' 'tdm':
132 ranks = DBSession.query(PlayerRank).\
133 filter(PlayerRank.game_type_cd==game_type_cd).\
134 order_by(PlayerRank.rank).\
135 limit(leaderboard_count).all()
140 @cache_region('hourly_term')
141 def get_top_players_by_time(limit=None, start=None):
143 The top players by the amount of time played during a date range.
145 q = DBSession.query(ActivePlayer)
147 if start is not None:
148 q = q.filter(ActivePlayer.sort_order >= start)
150 q = q.order_by(ActivePlayer.sort_order)
152 if limit is not None:
158 @cache_region('hourly_term')
159 def get_top_servers_by_play_time(limit=None, start=None):
161 The top servers by the cumulative amount of time played on them during a given interval.
163 q = DBSession.query(ActiveServer)
165 if start is not None:
166 q = q.filter(ActiveServer.sort_order >= start)
168 q = q.order_by(ActiveServer.sort_order)
170 if limit is not None:
176 @cache_region('hourly_term')
177 def get_top_maps_by_games(limit=None, start=None):
179 The top maps by the number of games played during a date range.
181 q = DBSession.query(ActiveMap)
183 if start is not None:
184 q = q.filter(ActiveMap.sort_order >= start)
186 q = q.order_by(ActiveMap.sort_order)
188 if limit is not None:
194 def _main_index_data(request):
196 leaderboard_lifetime = int(
197 request.registry.settings['xonstat.leaderboard_lifetime'])
199 leaderboard_lifetime = 30
201 leaderboard_count = 10
202 recent_games_count = 20
204 # summary statistics for the tagline
205 stat_line = summary_stats_string("all")
206 day_stat_line = summary_stats_string("day")
209 # the three top ranks tables
211 for gtc in ['duel', 'ctf', 'dm', 'tdm']:
212 rank = get_ranks(gtc)
216 right_now = datetime.utcnow()
217 back_then = datetime.utcnow() - timedelta(days=leaderboard_lifetime)
219 # top players by playing time
220 top_players = get_top_players_by_time(10)
222 # top servers by number of games
223 top_servers = get_top_servers_by_play_time(10)
225 # top maps by total times played
226 top_maps = get_top_maps_by_games(10)
228 # recent games played in descending order
229 rgs = recent_games_q(cutoff=back_then).limit(recent_games_count).all()
230 recent_games = [RecentGame(row) for row in rgs]
232 return {'top_players':top_players,
233 'top_servers':top_servers,
235 'recent_games':recent_games,
237 'stat_line':stat_line,
238 'day_stat_line':day_stat_line,
242 def main_index(request):
244 Display the main page information.
246 return _main_index_data(request)
249 def main_index_json(request):
251 JSON output of the main page information.
253 return [{'status':'not implemented'}]
256 def top_players_index(request):
258 start = int(request.params.get('start', None))
262 top_players = get_top_players_by_time(20, start)
264 # building a query string
266 if len(top_players) > 1:
267 query['start'] = top_players[-1].sort_order + 1
270 'top_players':top_players,
276 def top_servers_index(request):
278 start = int(request.params.get('start', None))
282 top_servers = get_top_servers_by_play_time(20, start)
284 # building a query string
286 if len(top_servers) > 1:
287 query['start'] = top_servers[-1].sort_order + 1
290 'top_servers':top_servers,
296 def top_maps_index(request):
298 start = int(request.params.get('start', None))
302 top_maps = get_top_maps_by_games(20, start)
304 # building a query string
306 if len(top_maps) > 1:
307 query['start'] = top_maps[-1].sort_order + 1