]> de.git.xonotic.org Git - xonotic/xonstat.git/blob - xonstat/views/server.py
More server refactoring...still not sure if this is the right way to go.
[xonotic/xonstat.git] / xonstat / views / server.py
1 import logging
2 import sqlalchemy.sql.functions as func
3 import sqlalchemy.sql.expression as expr
4 from datetime import datetime, timedelta
5 from pyramid.httpexceptions import HTTPNotFound
6 from webhelpers.paginate import Page
7 from xonstat.models import DBSession, Player, Server, Map, Game, PlayerGameStat
8 from xonstat.util import page_url, html_colors
9 from xonstat.views.helpers import RecentGame, recent_games_q
10
11 log = logging.getLogger(__name__)
12
13
14 # Defaults
15 LEADERBOARD_LIFETIME = 30
16 LEADERBOARD_COUNT = 10
17 RECENT_GAMES_COUNT = 20
18
19
20 class ServerIndex(object):
21     """Returns a list of servers."""
22
23     def __init__(self, request):
24         """Common parameter parsing."""
25         self.request = request
26         self.page = request.params.get("page", 1)
27         self.servers = self.raw()
28
29     def raw(self):
30         """Returns the raw data shared by all renderers."""
31         try:
32             server_q = DBSession.query(Server).order_by(Server.server_id.desc())
33             servers = Page(server_q, self.page, items_per_page=25, url=page_url)
34
35         except:
36             servers = None
37
38         return servers
39
40     def html(self):
41         """For rendering this data using something HTML-based."""
42         return {
43             'servers': self.servers,
44         }
45
46     def json(self):
47         """For rendering this data using JSON."""
48         return {
49             'servers': [s.to_dict() for s in self.servers],
50         }
51
52
53 class ServerInfoBase(object):
54     """Baseline parameter parsing for Server URLs with a server_id in them."""
55
56     def __init__(self, request):
57         """Common parameter parsing."""
58         self.request = request
59         self.server_id = request.matchdict.get("id", None)
60
61         raw_lifetime = request.registry.settings.get('xonstat.leaderboard_lifetime',
62                                                      LEADERBOARD_LIFETIME)
63         self.lifetime = int(raw_lifetime)
64
65         self.now = datetime.utcnow()
66
67
68 class ServerTopMaps(ServerInfoBase):
69     """Returns the top maps played on a given server."""
70
71     def __init__(self, request):
72         """Common parameter parsing."""
73         super(ServerTopMaps, self).__init__(request)
74         self.top_maps = self.raw()
75
76     def raw(self):
77         """Returns the raw data shared by all renderers."""
78         try:
79             top_maps = DBSession.query(Game.map_id, Map.name, func.count())\
80                 .filter(Map.map_id==Game.map_id)\
81                 .filter(Game.server_id==self.server_id)\
82                 .filter(Game.create_dt > (self.now - timedelta(days=self.lifetime)))\
83                 .group_by(Game.map_id)\
84                 .group_by(Map.name) \
85                 .order_by(expr.desc(func.count()))\
86                 .limit(LEADERBOARD_COUNT)\
87                 .all()
88         except:
89             top_maps = None
90
91         return top_maps
92
93     def json(self):
94         """For rendering this data using JSON."""
95         top_maps = [{
96             "map_id": tm.map_id,
97             "map_name": tm.name,
98             "times_played": tm[2],
99         } for tm in self.top_maps]
100
101         return top_maps
102
103
104 class ServerTopScorers(ServerInfoBase):
105     """Returns the top scorers on a given server."""
106
107     def __init__(self, request):
108         """Common parameter parsing."""
109
110         super(ServerTopScorers, self).__init__(request)
111         self.top_scorers = self.raw()
112
113     def raw(self):
114         """Top scorers on this server by total score."""
115         try:
116             top_scorers = DBSession.query(Player.player_id, Player.nick,
117                                           func.sum(PlayerGameStat.score))\
118                 .filter(Player.player_id == PlayerGameStat.player_id)\
119                 .filter(Game.game_id == PlayerGameStat.game_id)\
120                 .filter(Game.server_id == self.server_id)\
121                 .filter(Player.player_id > 2)\
122                 .filter(PlayerGameStat.create_dt >
123                         (self.now - timedelta(days=LEADERBOARD_LIFETIME)))\
124                 .order_by(expr.desc(func.sum(PlayerGameStat.score)))\
125                 .group_by(Player.nick)\
126                 .group_by(Player.player_id)\
127                 .limit(LEADERBOARD_COUNT)
128
129         except:
130             top_scorers = None
131
132         return top_scorers
133
134     def json(self):
135         """For rendering this data using JSON."""
136         top_scorers = [{
137             "player_id": ts.player_id,
138             "nick": ts.nick,
139             "score": ts[2],
140         } for ts in self.top_scorers]
141
142         return top_scorers
143
144
145 class ServerTopPlayers(ServerInfoBase):
146     """Returns the top players by playing time on a given server."""
147
148     def __init__(self, request):
149         """Common parameter parsing."""
150
151         super(ServerTopPlayers, self).__init__(request)
152         self.top_players = self.raw()
153
154     def raw(self):
155         """Top players on this server by total playing time."""
156         try:
157             top_players = DBSession.query(Player.player_id, Player.nick,
158                                           func.sum(PlayerGameStat.alivetime))\
159                 .filter(Player.player_id == PlayerGameStat.player_id)\
160                 .filter(Game.game_id == PlayerGameStat.game_id)\
161                 .filter(Game.server_id == self.server_id)\
162                 .filter(Player.player_id > 2)\
163                 .filter(PlayerGameStat.create_dt > (self.now - timedelta(days=self.lifetime)))\
164                 .order_by(expr.desc(func.sum(PlayerGameStat.alivetime)))\
165                 .group_by(Player.nick)\
166                 .group_by(Player.player_id)\
167                 .limit(LEADERBOARD_COUNT)
168
169         except:
170             top_players = None
171
172         return top_players
173
174     def json(self):
175         """For rendering this data using JSON."""
176         top_players = [{
177             "player_id": ts.player_id,
178             "nick": ts.nick,
179             "time": ts[2].total_seconds(),
180         } for ts in self.top_players]
181
182         return top_players
183
184
185 class ServerInfo(ServerInfoBase):
186     """Returns detailed information about a particular server."""
187
188     def __init__(self, request):
189         """Common parameter parsing."""
190
191         super(ServerInfo, self).__init__(request)
192         self.server_info = self.raw()
193
194     def raw(self):
195         """Returns the raw data shared by all renderers."""
196         try:
197             server = DBSession.query(Server).filter_by(server_id=self.server_id).one()
198
199             top_maps = ServerTopMaps(self.request).top_maps
200
201             top_scorers_raw = ServerTopScorers(self.request).top_scorers
202             top_scorers = [(player_id, html_colors(nick), score)
203                            for (player_id, nick, score) in top_scorers_raw]
204
205             top_players_raw = ServerTopPlayers(self.request).top_players
206             top_players = [(player_id, html_colors(nick), score)
207                            for (player_id, nick, score) in top_players_raw]
208
209             rgs = recent_games_q(server_id=self.server_id).limit(RECENT_GAMES_COUNT).all()
210             recent_games = [RecentGame(row) for row in rgs]
211         except:
212             raise HTTPNotFound
213
214         return {
215             'server': server,
216             'recent_games': recent_games,
217             'top_players': top_players,
218             'top_scorers': top_scorers,
219             'top_maps': top_maps,
220         }
221
222     def html(self):
223         """For rendering this data using something HTML-based."""
224         return self.server_info
225
226     def json(self):
227         """For rendering this data using JSON."""
228         return {"status": "Not implemented"}