3 from pyramid.response import Response
4 from pyramid.view import view_config
6 from xonstat.models import *
7 from sqlalchemy.orm.exc import MultipleResultsFound, NoResultFound
11 log = logging.getLogger(__name__)
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'}
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}
31 @view_config(renderer='player_info.mako')
32 def player_info(request):
33 player_id = request.matchdict['id']
35 player = DBSession.query(Player).filter_by(player_id=player_id).one()
38 return {'player':player}
41 ##########################################################################
42 # This is the game views area - only views pertaining to Xonotic
43 # games and their related information goes here
44 ##########################################################################
45 def game_info(request):
46 game_id = request.matchdict['id']
48 game = DBSession.query(Game).filter_by(game_id=game_id).one()
54 ##########################################################################
55 # This is the server views area - only views pertaining to Xonotic
56 # servers and their related information goes here
57 ##########################################################################
58 def server_info(request):
59 server_id = request.matchdict['id']
61 server = DBSession.query(Server).filter_by(server_id=server_id).one()
64 return {'server':server}
67 ##########################################################################
68 # This is the map views area - only views pertaining to Xonotic
69 # maps and their related information goes here
70 ##########################################################################
71 def map_info(request):
72 map_id = request.matchdict['id']
74 gmap = DBSession.query(Map).filter_by(map_id=map_id).one()
80 ##########################################################################
81 # This is the stats views area - only views pertaining to Xonotic
82 # statistics and its related information goes here
83 ##########################################################################
84 def get_or_create_server(session=None, name=None):
86 # find one by that name, if it exists
87 server = session.query(Server).filter_by(name=name).one()
88 log.debug("Found server id {0} with name {1}.".format(
89 server.server_id, server.name))
90 except NoResultFound, e:
91 server = Server(name=name)
94 log.debug("Created server id {0} with name {1}".format(
95 server.server_id, server.name))
96 except MultipleResultsFound, e:
97 # multiple found, so use the first one but warn
99 servers = session.query(Server).filter_by(name=name).order_by(
100 Server.server_id).all()
102 log.debug("Created server id {0} with name {1} but found \
104 server.server_id, server.name))
108 def get_or_create_map(session=None, name=None):
110 # find one by the name, if it exists
111 gmap = session.query(Map).filter_by(name=name).one()
112 log.debug("Found map id {0} with name {1}.".format(gmap.map_id,
114 except NoResultFound, e:
115 gmap = Map(name=name)
118 log.debug("Created map id {0} with name {1}.".format(gmap.map_id,
120 except MultipleResultsFound, e:
121 # multiple found, so use the first one but warn
123 gmaps = session.query(Map).filter_by(name=name).order_by(
126 log.debug("Found map id {0} with name {1} but found \
127 multiple.".format(gmap.map_id, gmap.name))
131 def create_game(session=None, start_dt=None, game_type_cd=None,
132 server_id=None, map_id=None, winner=None):
133 game = Game(start_dt=start_dt, game_type_cd=game_type_cd,
134 server_id=server_id, map_id=map_id, winner=winner)
137 log.debug("Created game id {0} on server {1}, map {2} at time \
138 {3} and on map {4}".format(game.game_id,
139 server_id, map_id, start_dt, map_id))
143 # search for a player and if found, create a new one (w/ hashkey)
144 def get_or_create_player(session=None, hashkey=None):
146 if re.search('^bot#\d+$', hashkey):
147 player = session.query(Player).filter_by(player_id=1).one()
148 # if we have an untracked player
149 elif re.search('^player#\d+$', hashkey):
150 player = session.query(Player).filter_by(player_id=2).one()
151 # else it is a tracked player
153 # see if the player is already in the database
154 # if not, create one and the hashkey along with it
156 hashkey = session.query(Hashkey).filter_by(
157 hashkey=hashkey).one()
158 player = session.query(Player).filter_by(
159 player_id=hashkey.player_id).one()
160 log.debug("Found existing player {0} with hashkey {1}.".format(
161 player.player_id, hashkey.hashkey))
166 hashkey = Hashkey(player_id=player.player_id, hashkey=hashkey)
168 log.debug("Created player {0} with hashkey {1}.".format(
169 player.player_id, hashkey.hashkey))
173 def create_player_game_stat(session=None, player=None,
174 game=None, player_events=None):
176 # in here setup default values (e.g. if game type is CTF then
177 # set kills=0, score=0, captures=0, pickups=0, fckills=0, etc
178 # TODO: use game's create date here instead of now()
179 pgstat = PlayerGameStat(create_dt=datetime.datetime.now())
181 # set player id from player record
182 pgstat.player_id = player.player_id
184 #set game id from game record
185 pgstat.game_id = game.game_id
187 # all games have a score
190 if game.game_type_cd == 'dm':
194 elif game.game_type_cd == 'ctf':
200 pgstat.carrier_frags = 0
202 for (key,value) in player_events.items():
203 if key == 'n': pgstat.nick = value
204 if key == 't': pgstat.team = value
205 if key == 'rank': pgstat.rank = value
206 if key == 'alivetime':
207 pgstat.alivetime = datetime.timedelta(seconds=int(round(float(value))))
208 if key == 'scoreboard-drops': pgstat.drops = value
209 if key == 'scoreboard-returns': pgstat.returns = value
210 if key == 'scoreboard-fckills': pgstat.carrier_frags = value
211 if key == 'scoreboard-pickups': pgstat.pickups = value
212 if key == 'scoreboard-caps': pgstat.captures = value
213 if key == 'scoreboard-score': pgstat.score = value
214 if key == 'scoreboard-deaths': pgstat.deaths = value
215 if key == 'scoreboard-kills': pgstat.kills = value
216 if key == 'scoreboard-suicides': pgstat.suicides = value
218 # check to see if we had a name, and if
219 # not use the name from the player id
220 if pgstat.nick == None:
221 pgstat.nick = player.nick
229 def create_player_weapon_stats(session=None, player=None,
230 game=None, player_events=None):
233 for (key,value) in player_events.items():
234 matched = re.search("acc-(.*?)-cnt-fired", key)
236 log.debug("Matched key: {0}".format(key))
237 weapon_cd = matched.group(1)
238 pwstat = PlayerWeaponStat()
239 pwstat.player_id = player.player_id
240 pwstat.game_id = game.game_id
241 pwstat.weapon_cd = weapon_cd
243 pwstat.max = int(player_events['acc-' + weapon_cd + '-fired'])
247 pwstat.actual = int(player_events['acc-' + weapon_cd + '-hit'])
251 pwstat.fired = int(player_events['acc-' + weapon_cd + '-cnt-fired'])
255 pwstat.hit = int(player_events['acc-' + weapon_cd + '-cnt-hit'])
259 pwstat.frags = int(player_events['acc-' + weapon_cd + '-frags'])
264 pwstats.append(pwstat)
269 def parse_body(request):
270 # storage vars for the request body
276 log.debug(request.body)
278 for line in request.body.split('\n'):
280 (key, value) = line.strip().split(' ', 1)
282 if key in 'V' 'T' 'G' 'M' 'S' 'C' 'R' 'W':
283 game_meta[key] = value
289 # if we were working on a player record already, append
290 # it and work on a new one (only set team info)
291 if len(player_events) != 0:
292 players.append(player_events)
293 player_events = {'t':current_team}
295 player_events[key] = value
298 (subkey, subvalue) = value.split(' ', 1)
299 player_events[subkey] = subvalue
302 player_events[key] = value
304 # no key/value pair - move on to the next line
307 # add the last player we were working on
308 if len(player_events) > 0:
309 players.append(player_events)
311 return (game_meta, players)
314 @view_config(renderer='stats_submit.mako')
315 def stats_submit(request):
317 session = DBSession()
319 (game_meta, players) = parse_body(request)
321 # verify required metadata is present
322 if 'T' not in game_meta or\
323 'G' not in game_meta or\
324 'M' not in game_meta or\
325 'S' not in game_meta:
326 log.debug("Required game meta fields (T, G, M, or S) missing. "\
330 server = get_or_create_server(session=session, name=game_meta['S'])
331 gmap = get_or_create_map(session=session, name=game_meta['M'])
334 winner = game_meta['W']
338 # FIXME: don't use python now() here, convert from epoch T value
339 game = create_game(session=session, start_dt=datetime.datetime.now(),
340 server_id=server.server_id, game_type_cd=game_meta['G'],
341 map_id=gmap.map_id, winner=winner)
343 # find or create a record for each player
344 # and add stats for each if they were present at the end
346 has_real_players = False
347 for player_events in players:
348 if not player_events['P'].startswith('bot'):
349 has_real_players = True
350 player = get_or_create_player(session=session,
351 hashkey=player_events['P'])
352 if 'joins' in player_events and 'matches' in player_events\
353 and 'scoreboardvalid' in player_events:
354 pgstat = create_player_game_stat(session=session,
355 player=player, game=game, player_events=player_events)
356 #pwstats = create_player_weapon_stats(session=session,
357 #player=player, game=game, player_events=player_events)
361 log.debug('Success! Stats recorded.')
362 return Response('200 OK')
365 log.debug('No real players found. Stats ignored.')
366 return {'msg':'No real players found. Stats ignored.'}
367 except Exception as e: