Merge branch 'master' into zykure/wip
[xonotic/xonstat.git] / xonstat / views / game.py
1 import datetime
2 import logging
3 import re
4 import time
5 from collections import OrderedDict
6 from pyramid.response import Response
7 from sqlalchemy import desc, func, over
8 from collections import namedtuple
9 from webhelpers.paginate import Page, PageURL
10 from xonstat.models import *
11 from xonstat.util import page_url
12 from xonstat.views.helpers import RecentGame, recent_games_q
13
14 import random
15
16 log = logging.getLogger(__name__)
17
18
19 # DEPRECATED
20 def _game_index_data(request):
21     game_type_cd = None
22     game_type_descr = None
23
24     if request.params.has_key('game_type_cd'):
25         game_type_cd = request.params['game_type_cd']
26         try:
27             game_type_descr = DBSession.query(GameType.descr).\
28                 filter(GameType.game_type_cd == game_type_cd).\
29                 one()[0]
30         except Exception as e:
31             game_type_cd = None
32
33     if request.params.has_key('page'):
34         current_page = request.params['page']
35     else:
36         current_page = 1
37
38     try:
39         rgs_q = recent_games_q(game_type_cd=game_type_cd)
40
41         games = Page(rgs_q, current_page, items_per_page=10, url=page_url)
42
43         # replace the items in the canned pagination class with more rich ones
44         games.items = [RecentGame(row) for row in games.items]
45
46         pgstats = {}
47         for game in games.items:
48             pgstats[game.game_id] = DBSession.query(PlayerGameStat).\
49                     filter(PlayerGameStat.game_id == game.game_id).\
50                     order_by(PlayerGameStat.scoreboardpos).\
51                     order_by(PlayerGameStat.score).all()
52
53     except Exception as e:
54         games           = None
55         pgstats         = None
56         game_type_cd    = None
57         game_type_descr = None
58
59     return {'games':games,
60             'pgstats':pgstats,
61             'game_type_cd':game_type_cd,
62             'game_type_descr':game_type_descr,
63             }
64
65
66 def game_index(request):
67     """
68     Provides a list of current games, with the associated game stats.
69     These games are ordered by game_id, with the most current ones first.
70     Paginated.
71     """
72     return _game_index_data(request)
73
74
75 def game_index_json(request):
76     """
77     Provides a list of current games, with the associated game stats.
78     These games are ordered by game_id, with the most current ones first.
79     Paginated. JSON.
80     """
81     return [{'status':'not implemented'}]
82
83
84 def _game_info_data(request):
85     game_id = request.matchdict['id']
86
87     if request.params.has_key('show_elo'):
88         show_elo = True
89     else:
90         show_elo = False
91
92     show_latency = False
93
94     try:
95         notfound = False
96
97         (game, server, map, gametype) = DBSession.query(Game, Server, Map, GameType).\
98                 filter(Game.game_id == game_id).\
99                 filter(Game.server_id == Server.server_id).\
100                 filter(Game.map_id == Map.map_id).\
101                 filter(Game.game_type_cd == GameType.game_type_cd).one()
102
103         pgstats = DBSession.query(PlayerGameStat).\
104                 filter(PlayerGameStat.game_id == game_id).\
105                 order_by(PlayerGameStat.scoreboardpos).\
106                 order_by(PlayerGameStat.score).\
107                 all()
108
109         # if at least one player has a valid latency, we'll show the column
110         for pgstat in pgstats:
111             if pgstat.avg_latency is not None:
112                 show_latency = True
113
114         q = DBSession.query(TeamGameStat).\
115                 filter(TeamGameStat.game_id == game_id)
116         if game.game_type_cd == 'ctf':
117             q = q.order_by(TeamGameStat.caps.desc())
118         elif game.game_type_cd == 'ca':
119             q = q.order_by(TeamGameStat.rounds.desc())
120         # dom -> ticks, rc -> laps, nb -> goals, as -> objectives
121
122         q = q.order_by(TeamGameStat.score.desc())
123
124         tgstats = q.all()
125
126         stats_by_team = OrderedDict()
127         for pgstat in pgstats:
128             if pgstat.team not in stats_by_team.keys():
129                 stats_by_team[pgstat.team] = []
130             stats_by_team[pgstat.team].append(pgstat)
131
132         captimes = []
133         if game.game_type_cd == 'ctf':
134             for pgstat in pgstats:
135                 if pgstat.fastest is not None:
136                     captimes.append(pgstat)
137             captimes = sorted(captimes, key=lambda x:x.fastest)
138
139         teamscores = {}
140         for pgstat in pgstats:
141             if pgstat.team in [5,14,13,10]:
142                 team = pgstat.team_html_color()
143                 if pgstat.teamscore is not None:
144                     if not teamscores.has_key(team):
145                         teamscores[team] = pgstat.teamscore
146                     else:
147                         if teamscores[team] != pgstat.teamscore:  # this should not happen!
148                             teamscores[team] = None
149         if len(teamscores) == 0:
150             teamscores = None
151             
152         ### RANDOM SCORES FOR TESTING
153         teams = ["red","blue","yellow","pink"]
154         random.shuffle(teams)
155         teamscores = {}
156         for k in range(random.randint(2,4)):
157             team = teams[k-1]
158             teamscores[team] = random.randint(-5,150)
159         ### END
160         
161         #TeamInfo = namedtuple('TeamInfo', ['team','scoreboardpos','playercount','teamscore'])
162         #
163         #teams = {}
164         #last_pgs = pgstats[0]
165         #for pgstat in pgstats:
166         #    if pgstat.team != last_pgs.team:
167         #        teams[last_pgs.scoreboardpos] = TeamInfo(
168         #                team=last_pgs.team,
169         #                scoreboardpos=last_pgs.scoreboardpos,
170         #                playercount=pgstat.scoreboardpos-last_pgs.scoreboardpos,
171         #                teamscore=last_pgs.teamscore)
172         #        last_pgs = pgstat
173         #teams[last_pgs.scoreboardpos] = TeamInfo(
174         #        team=last_pgs.team,
175         #        scoreboardpos=last_pgs.scoreboardpos,
176         #        playercount=pgstat.scoreboardpos-last_pgs.scoreboardpos,
177         #        teamscore=last_pgs.teamscore)
178         #print teams
179
180         pwstats = {}
181         for (pwstat, pgstat, weapon) in DBSession.query(PlayerWeaponStat, PlayerGameStat, Weapon).\
182                 filter(PlayerWeaponStat.game_id == game_id).\
183                 filter(PlayerWeaponStat.weapon_cd == Weapon.weapon_cd).\
184                 filter(PlayerWeaponStat.player_game_stat_id == \
185                     PlayerGameStat.player_game_stat_id).\
186                 order_by(PlayerGameStat.scoreboardpos).\
187                 order_by(PlayerGameStat.score).\
188                 order_by(Weapon.descr).\
189                 all():
190                     if pgstat.player_game_stat_id not in pwstats:
191                         pwstats[pgstat.player_game_stat_id] = []
192
193                     # NOTE adding pgstat to position 6 in order to display nick.
194                     # You have to use a slice [0:5] to pass to the accuracy
195                     # template
196                     pwstats[pgstat.player_game_stat_id].append((weapon.descr,
197                         weapon.weapon_cd, pwstat.actual, pwstat.max,
198                         pwstat.hit, pwstat.fired, pgstat))
199
200     except Exception as inst:
201         game = None
202         server = None
203         map = None
204         gametype = None
205         pgstats = None
206         tgstats = None
207         pwstats = None
208         captimes = None
209         teams = None
210         show_elo = False
211         show_latency = False
212         stats_by_team = None
213         raise inst
214
215     return {'game':game,
216             'server':server,
217             'map':map,
218             'gametype':gametype,
219             'pgstats':pgstats,
220             'tgstats':tgstats,
221             'pwstats':pwstats,
222             'captimes':captimes,
223             'teams':teams,
224             'teamscores':teamscores,
225             'show_elo':show_elo,
226             'show_latency':show_latency,
227             'stats_by_team':stats_by_team,
228             }
229
230
231 def game_info(request):
232     """
233     List the game stats (scoreboard) for a particular game. Paginated.
234     """
235     return _game_info_data(request)
236
237
238 def game_info_json(request):
239     """
240     List the game stats (scoreboard) for a particular game. Paginated. JSON.
241     """
242     return [{'status':'not implemented'}]
243
244
245 def _rank_index_data(request):
246     if request.params.has_key('page'):
247         current_page = request.params['page']
248     else:
249         current_page = 1
250
251     game_type_cd = request.matchdict['game_type_cd']
252
253     ranks_q = DBSession.query(PlayerRank).\
254             filter(PlayerRank.game_type_cd==game_type_cd).\
255             order_by(PlayerRank.rank)
256
257     ranks = Page(ranks_q, current_page, url=page_url)
258
259     if len(ranks) == 0:
260         ranks = None
261
262     return {
263             'ranks':ranks,
264             'game_type_cd':game_type_cd,
265            }
266
267
268 def rank_index(request):
269     """
270     Provide a list of gametype ranks, paginated.
271     """
272     return _rank_index_data(request)
273
274
275 def rank_index_json(request):
276     """
277     Provide a list of gametype ranks, paginated. JSON.
278     """
279     return [{'status':'not implemented'}]
280
281
282 def game_finder_data(request):
283     if request.params.has_key('page'):
284         current_page = request.params['page']
285     else:
286         current_page = 1
287
288     query = {}
289
290     server_id, map_id, player_id = None, None, None
291     range_start, range_end, game_type_cd = None, None, None
292     game_type_descr = None
293
294     # these become WHERE clauses when present
295     if request.params.has_key('server_id'):
296         server_id = request.params['server_id']
297         query['server_id'] = server_id
298
299     if request.params.has_key('map_id'):
300         map_id = request.params['map_id']
301         query['map_id'] = map_id
302
303     if request.params.has_key('player_id'):
304         player_id = request.params['player_id']
305         query['player_id'] = player_id
306
307     if request.params.has_key('range_start'):
308         range_start = request.params['range_start']
309         query['range_start'] = range_start
310
311     if request.params.has_key('range_end'):
312         range_end = request.params['range_end']
313         query['range_end'] = range_end
314
315     if request.params.has_key('type'):
316         game_type_cd = request.params['type']
317         query['type'] = game_type_cd
318         try:
319             game_type_descr = DBSession.query(GameType.descr).\
320                 filter(GameType.game_type_cd == game_type_cd).\
321                 one()[0]
322         except Exception as e:
323             game_type_cd = None
324
325     rgs_q = recent_games_q(server_id=server_id, map_id=map_id,
326             player_id=player_id, game_type_cd=game_type_cd)
327
328     recent_games = Page(rgs_q, current_page, url=page_url)
329
330     recent_games.items = [RecentGame(row) for row in recent_games.items]
331
332     return {
333             'recent_games':recent_games,
334             'query':query,
335             'game_type_cd':game_type_cd,
336            }
337
338 def game_finder(request):
339     """
340     Provide a list of recent games with an advanced filter.
341     """
342     return game_finder_data(request)