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