]> de.git.xonotic.org Git - xonotic/xonstat.git/blob - xonstat/views.py
fd0ce6fe973af018321be8cdd27d5c8a48098694
[xonotic/xonstat.git] / xonstat / views.py
1 import datetime
2 import re
3 from pyramid.response import Response
4 from pyramid.view import view_config
5
6 from xonstat.models import *
7 from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound
8
9
10 import logging
11 log = logging.getLogger(__name__)
12
13 ##########################################################################
14 # This is the main index - the entry point to the entire site
15 ##########################################################################
16 @view_config(renderer='index.jinja2')
17 def main_index(request):
18     log.debug("testing logging; entered MainHandler.index()")
19     return {'project':'xonstat'}
20
21 ##########################################################################
22 # This is the player views area - only views pertaining to Xonotic players
23 # and their related information goes here
24 ##########################################################################
25 @view_config(renderer='player_index.mako')
26 def player_index(request):
27     players = DBSession.query(Player)
28     log.debug("testing logging; entered PlayerHandler.index()")
29     return {'players':players}
30
31 @view_config(renderer='player_info.mako')
32 def player_info(request):
33     player = DBSession.query(Player).filter_by(player_id=p_player_id)
34     log.debug("testing logging; entered PlayerHandler.info()")
35     return {'player':player}
36
37 ##########################################################################
38 # This is the stats views area - only views pertaining to Xonotic
39 # statistics and its related information goes here
40 ##########################################################################
41 def get_or_create_server(session=None, name=None):
42     try:
43         # find one by that name, if it exists
44         server = session.query(Server).filter_by(name=name).one()
45         log.debug("Found server id {0} with name {1}.".format(
46             server.server_id, server.name))
47     except NoResultFound, e:
48         server = Server(name=name)
49         session.add(server)
50         session.flush()
51         log.debug("Created server id {0} with name {1}".format(
52             server.server_id, server.name))
53     except MultipleResultsFound, e:
54         # multiple found, so use the first one but warn
55         log.debug(e)
56         servers = session.query(Server).filter_by(name=name).order_by(
57                 Server.server_id).all()
58         server = servers[0]
59         log.debug("Created server id {0} with name {1} but found \
60                 multiple".format(
61             server.server_id, server.name))
62
63     return server
64
65 def get_or_create_map(session=None, name=None):
66     try:
67         # find one by the name, if it exists
68         gmap = session.query(Map).filter_by(name=name).one()
69         log.debug("Found map id {0} with name {1}.".format(gmap.map_id, 
70             gmap.name))
71     except NoResultFound, e:
72         gmap = Map(name=name)
73         session.add(gmap)
74         session.flush()
75         log.debug("Created map id {0} with name {1}.".format(gmap.map_id,
76             gmap.name))
77     except MultipleResultsFound, e:
78         # multiple found, so use the first one but warn
79         log.debug(e)
80         gmaps = session.query(Map).filter_by(name=name).order_by(
81                 Map.map_id).all()
82         gmap = gmaps[0]
83         log.debug("Found map id {0} with name {1} but found \
84                 multiple.".format(gmap.map_id, gmap.name))
85
86     return gmap
87
88 def create_game(session=None, start_dt=None, game_type_cd=None, 
89         server_id=None, map_id=None, winner=None):
90     game = Game(start_dt=start_dt, game_type_cd=game_type_cd,
91                 server_id=server_id, map_id=map_id, winner=winner)
92     session.add(game)
93     session.flush()
94     log.debug("Created game id {0} on server {1}, map {2} at time \
95             {3} and on map {4}".format(game.game_id, 
96                 server_id, map_id, start_dt, map_id))
97
98     return game
99
100 # search for a player and if found, create a new one (w/ hashkey)
101 def get_or_create_player(session=None, hashkey=None):
102     # if we have a bot
103     if re.search('^bot#\d+$', hashkey):
104         player = session.query(Player).filter_by(player_id=1).one()
105     # if we have an untracked player
106     elif re.search('^player#\d+$', hashkey):
107         player = session.query(Player).filter_by(player_id=2).one()
108     # else it is a tracked player
109     else:
110         # see if the player is already in the database
111         # if not, create one and the hashkey along with it
112         try:
113             hashkey = session.query(Hashkey).filter_by(
114                     hashkey=hashkey).one()
115             player = session.query(Player).filter_by(
116                     player_id=hashkey.player_id).one()
117             log.debug("Found existing player {0} with hashkey {1}.".format(
118                 player.player_id, hashkey.hashkey))
119         except:
120             player = Player()
121             session.add(player)
122             session.flush()
123             hashkey = Hashkey(player_id=player.player_id, hashkey=hashkey)
124             session.add(hashkey)
125             log.debug("Created player {0} with hashkey {1}.".format(
126                 player.player_id, hashkey.hashkey))
127
128     return player
129
130 def create_player_game_stat(session=None, player=None, 
131         game=None, player_events=None):
132
133     # in here setup default values (e.g. if game type is CTF then
134     # set kills=0, score=0, captures=0, pickups=0, fckills=0, etc
135     # TODO: use game's create date here instead of now()
136     pgstat = PlayerGameStat(create_dt=datetime.datetime.now())
137
138     # set player id from player record
139     pgstat.player_id = player.player_id
140
141     #set game id from game record
142     pgstat.game_id = game.game_id
143
144     # all games have a score
145     pgstat.score = 0
146
147     if game.game_type_cd == 'dm':
148         pgstat.kills = 0
149         pgstat.deaths = 0
150         pgstat.suicides = 0
151     elif game.game_type_cd == 'ctf':
152         pgstat.kills = 0
153         pgstat.captures = 0
154         pgstat.pickups = 0
155         pgstat.drops = 0
156         pgstat.returns = 0
157         pgstat.carrier_frags = 0
158
159     for (key,value) in player_events.items():
160         if key == 'n': pgstat.nick = value
161         if key == 't': pgstat.team = value
162         if key == 'rank': pgstat.rank = value
163         if key == 'alivetime': 
164             pgstat.alivetime = datetime.timedelta(seconds=int(round(float(value))))
165         if key == 'scoreboard-drops': pgstat.drops = value
166         if key == 'scoreboard-returns': pgstat.returns = value
167         if key == 'scoreboard-fckills': pgstat.carrier_frags = value
168         if key == 'scoreboard-pickups': pgstat.pickups = value
169         if key == 'scoreboard-caps': pgstat.captures = value
170         if key == 'scoreboard-score': pgstat.score = value
171         if key == 'scoreboard-deaths': pgstat.deaths = value
172         if key == 'scoreboard-kills': pgstat.kills = value
173         if key == 'scoreboard-suicides': pgstat.suicides = value
174
175     # check to see if we had a name, and if 
176     # not use the name from the player id
177     if pgstat.nick == None:
178         pgstat.nick = player.nick
179
180     session.add(pgstat)
181     session.flush()
182
183     return pgstat
184
185
186 def create_player_weapon_stats(session=None, player=None, 
187         game=None, player_events=None):
188     pwstats = []
189
190     for (key,value) in player_events.items():
191         matched = re.search("acc-(.*?)-cnt-fired", key)
192         if matched:
193             log.debug("Matched key: {0}".format(key))
194             weapon_cd = matched.group(1)
195             pwstat = PlayerWeaponStat()
196             pwstat.player_id = player.player_id
197             pwstat.game_id = game.game_id
198             pwstat.weapon_cd = weapon_cd
199             try:
200                 pwstat.max = int(player_events['acc-' + weapon_cd + '-fired'])
201             except:
202                 pwstat.max = 0
203             try:
204                 pwstat.actual = int(player_events['acc-' + weapon_cd + '-hit'])
205             except:
206                 pwstat.actual = 0
207             try:
208                 pwstat.fired = int(player_events['acc-' + weapon_cd + '-cnt-fired'])
209             except:
210                 pwstat.fired = 0
211             try:
212                 pwstat.hit = int(player_events['acc-' + weapon_cd + '-cnt-hit'])
213             except:
214                 pwstat.hit = 0
215             try:
216                 pwstat.frags = int(player_events['acc-' + weapon_cd + '-frags'])
217             except:
218                 pwstat.frags = 0
219
220             session.add(pwstat)
221             pwstats.append(pwstat)
222
223     return pwstats
224
225
226 def parse_body(request):
227     # storage vars for the request body
228     game_meta = {}
229     player_events = {}
230     current_team = None
231     players = []
232     
233     log.debug(request.body)
234
235     for line in request.body.split('\n'):
236         try:
237             (key, value) = line.strip().split(' ', 1)
238     
239             if key in 'V' 'T' 'G' 'M' 'S' 'C' 'R' 'W':
240                 game_meta[key] = value
241
242             if key == 't':
243                 current_team = value
244     
245             if key == 'P':
246                 # if we were working on a player record already, append
247                 # it and work on a new one (only set team info)
248                 if len(player_events) != 0:
249                     players.append(player_events)
250                     player_events = {'t':current_team}
251     
252                 player_events[key] = value
253     
254             if key == 'e':
255                 (subkey, subvalue) = value.split(' ', 1)
256                 player_events[subkey] = subvalue
257
258             if key == 'n':
259                 player_events[key] = value
260         except:
261             # no key/value pair - move on to the next line
262             pass
263     
264     # add the last player we were working on
265     if len(player_events) > 0:
266         players.append(player_events)
267
268     return (game_meta, players)
269
270
271 @view_config(renderer='stats_submit.mako')
272 def stats_submit(request):
273     session = DBSession()
274
275     (game_meta, players) = parse_body(request)  
276     
277     # verify required metadata is present
278     if 'T' not in game_meta or\
279         'G' not in game_meta or\
280         'M' not in game_meta or\
281         'S' not in game_meta:
282         log.debug("Required game meta fields (T, G, M, or S) missing. "\
283                 "Can't continue.")
284         return {'msg':'Error processing the request.'}
285     
286     server = get_or_create_server(session=session, name=game_meta['S'])
287     gmap = get_or_create_map(session=session, name=game_meta['M'])
288
289     if 'W' in game_meta:
290         winner = game_meta['W']
291     else:
292         winner = None
293
294     # FIXME: don't use python now() here, convert from epoch T value
295     game = create_game(session=session, start_dt=datetime.datetime.now(), 
296             server_id=server.server_id, game_type_cd=game_meta['G'], 
297             map_id=gmap.map_id, winner=winner)
298     
299     # find or create a record for each player
300     # and add stats for each if they were present at the end
301     # of the game
302     has_real_players = False
303     for player_events in players:
304         if not player_events['P'].startswith('bot'):
305             has_real_players = True
306         player = get_or_create_player(session=session, 
307                 hashkey=player_events['P'])
308         if 'joins' in player_events and 'matches' in player_events\
309                  and 'scoreboardvalid' in player_events:
310             pgstat = create_player_game_stat(session=session, 
311                     player=player, game=game, player_events=player_events)
312             #pwstats = create_player_weapon_stats(session=session, 
313                     #player=player, game=game, player_events=player_events)
314     
315     if has_real_players:
316         session.commit()
317         log.debug('Success! Stats recorded.')
318         return {'msg':'Success! Stats recorded.'}
319     else:
320         session.rollback()
321         log.debug('No real players found. Stats ignored.')
322         return {'msg':'No real players found. Stats ignored.'}