Implemented some (preliminary) JSON API to retrieve player data
[xonotic/xonstat.git] / xonstat / views / player.py
1 import datetime
2 import json
3 import logging
4 import re
5 import sqlalchemy as sa
6 import sqlalchemy.sql.functions as func
7 import time
8 from pyramid.response import Response
9 from pyramid.url import current_route_url
10 from sqlalchemy import desc, distinct
11 from webhelpers.paginate import Page, PageURL
12 from xonstat.models import *
13 from xonstat.util import page_url, datetime_seconds
14
15 log = logging.getLogger(__name__)
16
17
18 def player_index_data(request):
19     if request.params.has_key('page'):
20         current_page = request.params['page']
21     else:
22         current_page = 1
23
24     try:
25         player_q = DBSession.query(Player).\
26                 filter(Player.player_id > 2).\
27                 filter(Player.active_ind == True).\
28                 filter(sa.not_(Player.nick.like('Anonymous Player%'))).\
29                 order_by(Player.player_id.desc())
30
31         players = Page(player_q, current_page, items_per_page=10, url=page_url)
32
33     except Exception as e:
34         players = None
35         raise e
36
37     return {'players':players
38            }
39
40
41 def player_index(request):
42     """
43     Provides a list of all the current players.
44     """
45     return player_index_data(request)
46
47
48 def player_index_json(request):
49     """
50     Provides a list of all the current players. JSON.
51     """
52     return [{'status':'not implemented'}]
53
54
55 def _get_games_played(player_id):
56     """
57     DEPRECATED: Now included in _get_total_stats()
58     
59     Provides a breakdown by gametype of the games played by player_id.
60
61     Returns a tuple containing (total_games, games_breakdown), where
62     total_games is the absolute number of games played by player_id
63     and games_breakdown is an array containing (game_type_cd, # games)
64     """
65     games_played = DBSession.query(Game.game_type_cd, func.count()).\
66             filter(Game.game_id == PlayerGameStat.game_id).\
67             filter(PlayerGameStat.player_id == player_id).\
68             group_by(Game.game_type_cd).\
69             order_by(func.count().desc()).all()
70
71     total = 0
72     for (game_type_cd, games) in games_played:
73         total += games
74
75     return (total, games_played)
76
77
78 def _get_total_stats(player_id):
79     """
80     Provides aggregated stats by player_id.
81
82     Returns a dict with the keys 'kills', 'deaths', 'alivetime'.
83
84     games = how many games a player has played
85     games_breakdown = how many games of given type a player has played (dictionary)
86     games_alivetime = how many time a player has spent in a give game type (dictionary)
87     kills = how many kills a player has over all games
88     deaths = how many deaths a player has over all games
89     suicides = how many suicides a player has over all games
90     alivetime = how long a player has played over all games
91     alivetime_week = how long a player has played over all games in the last week
92     alivetime_month = how long a player has played over all games in the last month
93     wins = how many games a player has won
94
95     If any of the above are None, they are set to 0.
96     """
97     # 7 and 30 day windows
98     one_week_ago  = datetime.datetime.utcnow() - datetime.timedelta(days=7)
99     one_month_ago = datetime.datetime.utcnow() - datetime.timedelta(days=30)
100
101     total_stats = {}
102
103     games_played = DBSession.query(
104             Game.game_type_cd, func.count(), func.sum(PlayerGameStat.alivetime)).\
105             filter(Game.game_id == PlayerGameStat.game_id).\
106             filter(PlayerGameStat.player_id == player_id).\
107             group_by(Game.game_type_cd).\
108             order_by(func.count().desc()).\
109             all()
110
111     total_stats['games'] = 0
112     total_stats['games_breakdown'] = {}  # this is a dictionary inside a dictionary .. dictception?
113     total_stats['games_alivetime'] = {}
114     for (game_type_cd, games, alivetime) in games_played:
115         total_stats['games'] += games
116         total_stats['games_breakdown'][game_type_cd] = games
117         total_stats['games_alivetime'][game_type_cd] = alivetime
118
119      # more fields can be added here, e.g. 'collects' for kh games
120     (total_stats['kills'], total_stats['deaths'], total_stats['suicides'],
121      total_stats['alivetime'],) = DBSession.query(
122             func.sum(PlayerGameStat.kills),
123             func.sum(PlayerGameStat.deaths),
124             func.sum(PlayerGameStat.suicides),
125             func.sum(PlayerGameStat.alivetime)).\
126             filter(PlayerGameStat.player_id == player_id).\
127             one()
128
129     (total_stats['alivetime_week'],) = DBSession.query(
130             func.sum(PlayerGameStat.alivetime)).\
131             filter(PlayerGameStat.player_id == player_id).\
132             filter(PlayerGameStat.create_dt > one_week_ago).\
133             one()
134
135     (total_stats['alivetime_month'],) = DBSession.query(
136             func.sum(PlayerGameStat.alivetime)).\
137             filter(PlayerGameStat.player_id == player_id).\
138             filter(PlayerGameStat.create_dt > one_month_ago).\
139             one()
140
141     (total_stats['wins'],) = DBSession.\
142             query("total_wins").\
143             from_statement(
144                 "select count(*) total_wins "
145                 "from games g, player_game_stats pgs "
146                 "where g.game_id = pgs.game_id "
147                 "and player_id=:player_id "
148                 "and (g.winner = pgs.team or pgs.rank = 1)"
149             ).params(player_id=player_id).one()
150
151 #    (total_stats['wins'],) = DBSession.query(
152 #            func.count("*")).\
153 #            filter(Game.game_id == PlayerGameStat.game_id).\
154 #            filter(PlayerGameStat.player_id == player_id).\
155 #            filter(Game.winner == PlayerGameStat.team or PlayerGameStat.rank == 1).\
156 #            one()
157
158     (total_stats['duel_wins'],) = DBSession.query(
159             func.count("*")).\
160             filter(Game.game_id == PlayerGameStat.game_id).\
161             filter(Game.game_type_cd == "duel").\
162             filter(PlayerGameStat.player_id == player_id).\
163             filter(PlayerGameStat.rank == 1).\
164             one()
165
166     (total_stats['duel_kills'], total_stats['duel_deaths'], total_stats['duel_suicides'],) = DBSession.query(
167             func.sum(PlayerGameStat.kills),
168             func.sum(PlayerGameStat.deaths),
169             func.sum(PlayerGameStat.suicides)).\
170             filter(Game.game_id == PlayerGameStat.game_id).\
171             filter(Game.game_type_cd == "duel").\
172             filter(PlayerGameStat.player_id == player_id).\
173             one()
174
175     (total_stats['dm_wins'],) = DBSession.query(
176             func.count("*")).\
177             filter(Game.game_id == PlayerGameStat.game_id).\
178             filter(Game.game_type_cd == "dm").\
179             filter(PlayerGameStat.player_id == player_id).\
180             filter(PlayerGameStat.rank == 1).\
181             one()
182
183     (total_stats['dm_kills'], total_stats['dm_deaths'], total_stats['dm_suicides'],) = DBSession.query(
184             func.sum(PlayerGameStat.kills),
185             func.sum(PlayerGameStat.deaths),
186             func.sum(PlayerGameStat.suicides)).\
187             filter(Game.game_id == PlayerGameStat.game_id).\
188             filter(Game.game_type_cd == "dm").\
189             filter(PlayerGameStat.player_id == player_id).\
190             one()
191
192     (total_stats['tdm_kills'], total_stats['tdm_deaths'], total_stats['tdm_suicides'],) = DBSession.query(
193             func.sum(PlayerGameStat.kills),
194             func.sum(PlayerGameStat.deaths),
195             func.sum(PlayerGameStat.suicides)).\
196             filter(Game.game_id == PlayerGameStat.game_id).\
197             filter(Game.game_type_cd == "tdm").\
198             filter(PlayerGameStat.player_id == player_id).\
199             one()
200
201     (total_stats['tdm_wins'],) = DBSession.query(
202             func.count("*")).\
203             filter(Game.game_id == PlayerGameStat.game_id).\
204             filter(Game.game_type_cd == "tdm").\
205             filter(PlayerGameStat.player_id == player_id).\
206             filter(PlayerGameStat.rank == 1).\
207             one()
208
209     (total_stats['ctf_wins'],) = DBSession.query(
210             func.count("*")).\
211             filter(Game.game_id == PlayerGameStat.game_id).\
212             filter(Game.game_type_cd == "ctf").\
213             filter(PlayerGameStat.player_id == player_id).\
214             filter(PlayerGameStat.rank == 1).\
215             one()
216
217     (total_stats['ctf_caps'], total_stats['ctf_pickups'], total_stats['ctf_drops'],
218      total_stats['ctf_returns'], total_stats['ctf_fckills'],) = DBSession.query(
219             func.sum(PlayerGameStat.captures),
220             func.sum(PlayerGameStat.pickups),
221             func.sum(PlayerGameStat.drops),
222             func.sum(PlayerGameStat.returns),
223             func.sum(PlayerGameStat.carrier_frags)).\
224             filter(Game.game_id == PlayerGameStat.game_id).\
225             filter(Game.game_type_cd == "ctf").\
226             filter(PlayerGameStat.player_id == player_id).\
227             one()
228
229     for (key,value) in total_stats.items():
230         if value == None:
231             total_stats[key] = 0
232
233     return total_stats
234
235
236 def _get_fav_map(player_id):
237     """
238     Get the player's favorite map. The favorite map is defined
239     as the map that he or she has played the most in the past 
240     90 days.
241
242     Returns a dictionary with keys for the map's name and id.
243     """
244     # 90 day window
245     back_then = datetime.datetime.utcnow() - datetime.timedelta(days=90)
246
247     raw_fav_map = DBSession.query(Map.name, Map.map_id).\
248             filter(Game.game_id == PlayerGameStat.game_id).\
249             filter(Game.map_id == Map.map_id).\
250             filter(PlayerGameStat.player_id == player_id).\
251             filter(PlayerGameStat.create_dt > back_then).\
252             group_by(Map.name, Map.map_id).\
253             order_by(func.count().desc()).\
254             limit(5).all()
255
256     fav_map = []
257     for map_e in raw_fav_map:
258         entry = {}
259         entry['name'] = map_e[0]
260         entry['id']   = map_e[1]
261         fav_map.append(entry)
262
263     return fav_map
264
265
266 def _get_fav_weapon(player_id):
267     """
268     Get the player's favorite weapon. The favorite weapon is defined
269     as the weapon that he or she has employed the most in the past 
270     90 days.
271
272     Returns a sequence of dictionaries with keys for the weapon's name and id.
273     The sequence holds the most-used weapons in decreasing order.
274     """
275     # 90 day window
276     back_then = datetime.datetime.utcnow() - datetime.timedelta(days=90)
277
278     raw_fav_weapon = DBSession.query(Weapon.descr, Weapon.weapon_cd).\
279             filter(Game.game_id == PlayerWeaponStat.game_id).\
280             filter(PlayerWeaponStat.player_id == player_id).\
281             filter(PlayerWeaponStat.weapon_cd == Weapon.weapon_cd).\
282             filter(PlayerWeaponStat.create_dt > back_then).\
283             group_by(Weapon.descr, Weapon.weapon_cd).\
284             order_by(func.count().desc()).\
285             limit(5).all()
286
287     fav_weapon = []
288     for wpn_e in raw_fav_weapon:
289         entry = {}
290         entry['name'] = wpn_e[0]
291         entry['id']   = wpn_e[1]
292         fav_weapon.append(entry)
293
294     return fav_weapon
295
296
297 def _get_fav_server(player_id):
298     """
299     Get the player's favorite server. The favorite server is defined
300     as the server that he or she has played on the most in the past 
301     90 days.
302
303     Returns a sequence of dictionaries with keys for the server's name and id.
304     The sequence holds the most-used servers in decreasing order.
305     """
306     # 90 day window
307     back_then = datetime.datetime.utcnow() - datetime.timedelta(days=90)
308
309     raw_fav_server = DBSession.query(Server.name, Server.server_id).\
310             filter(Game.game_id == PlayerGameStat.game_id).\
311             filter(Game.server_id == Server.server_id).\
312             filter(PlayerGameStat.player_id == player_id).\
313             filter(PlayerGameStat.create_dt > back_then).\
314             group_by(Server.name, Server.server_id).\
315             order_by(func.count().desc()).\
316             limit(5).all()
317
318     fav_server = []
319     for srv_e in raw_fav_server:
320         entry = {}
321         entry['name'] = srv_e[0]
322         entry['id']   = srv_e[1]
323         fav_server.append(entry)
324
325     return fav_server
326
327
328 def _get_rank(player_id):
329     """
330     Get the player's rank as well as the total number of ranks.
331     """
332     rank = DBSession.query("game_type_cd", "rank", "max_rank").\
333             from_statement(
334                 "select pr.game_type_cd, pr.rank, overall.max_rank "
335                 "from player_ranks pr,  "
336                    "(select game_type_cd, max(rank) max_rank "
337                     "from player_ranks  "
338                     "group by game_type_cd) overall "
339                 "where pr.game_type_cd = overall.game_type_cd  "
340                 "and player_id = :player_id "
341                 "order by rank").\
342             params(player_id=player_id).all()
343
344     return rank;
345
346
347 def get_accuracy_stats(player_id, weapon_cd, games):
348     """
349     Provides accuracy for weapon_cd by player_id for the past N games.
350     """
351     # Reaching back 90 days should give us an accurate enough average
352     # We then multiply this out for the number of data points (games) to
353     # create parameters for a flot graph
354     try:
355         raw_avg = DBSession.query(func.sum(PlayerWeaponStat.hit),
356                 func.sum(PlayerWeaponStat.fired)).\
357                 filter(PlayerWeaponStat.player_id == player_id).\
358                 filter(PlayerWeaponStat.weapon_cd == weapon_cd).\
359                 one()
360
361         avg = round(float(raw_avg[0])/raw_avg[1]*100, 2)
362
363         # Determine the raw accuracy (hit, fired) numbers for $games games
364         # This is then enumerated to create parameters for a flot graph
365         raw_accs = DBSession.query(PlayerWeaponStat.game_id, 
366             PlayerWeaponStat.hit, PlayerWeaponStat.fired).\
367                 filter(PlayerWeaponStat.player_id == player_id).\
368                 filter(PlayerWeaponStat.weapon_cd == weapon_cd).\
369                 order_by(PlayerWeaponStat.game_id.desc()).\
370                 limit(games).\
371                 all()
372
373         # they come out in opposite order, so flip them in the right direction
374         raw_accs.reverse()
375
376         accs = []
377         for i in range(len(raw_accs)):
378             accs.append((raw_accs[i][0], round(float(raw_accs[i][1])/raw_accs[i][2]*100, 2)))
379     except:
380         accs = []
381         avg = 0.0
382
383     return (avg, accs)
384
385
386 def get_damage_stats(player_id, weapon_cd, games):
387     """
388     Provides damage info for weapon_cd by player_id for the past N games.
389     """
390     try:
391         raw_avg = DBSession.query(func.sum(PlayerWeaponStat.actual),
392                 func.sum(PlayerWeaponStat.hit)).\
393                 filter(PlayerWeaponStat.player_id == player_id).\
394                 filter(PlayerWeaponStat.weapon_cd == weapon_cd).\
395                 one()
396
397         avg = round(float(raw_avg[0])/raw_avg[1], 2)
398
399         # Determine the damage efficiency (hit, fired) numbers for $games games
400         # This is then enumerated to create parameters for a flot graph
401         raw_dmgs = DBSession.query(PlayerWeaponStat.game_id, 
402             PlayerWeaponStat.actual, PlayerWeaponStat.hit).\
403                 filter(PlayerWeaponStat.player_id == player_id).\
404                 filter(PlayerWeaponStat.weapon_cd == weapon_cd).\
405                 order_by(PlayerWeaponStat.game_id.desc()).\
406                 limit(games).\
407                 all()
408
409         # they come out in opposite order, so flip them in the right direction
410         raw_dmgs.reverse()
411
412         dmgs = []
413         for i in range(len(raw_dmgs)):
414             # try to derive, unless we've hit nothing then set to 0!
415             try:
416                 dmg = round(float(raw_dmgs[i][1])/raw_dmgs[i][2], 2)
417             except:
418                 dmg = 0.0
419
420             dmgs.append((raw_dmgs[i][0], dmg))
421     except Exception as e:
422         dmgs = []
423         avg = 0.0
424
425     return (avg, dmgs)
426
427
428 def player_info_data(request):
429     player_id = int(request.matchdict['id'])
430     if player_id <= 2:
431         player_id = -1;
432
433     try:
434         player = DBSession.query(Player).filter_by(player_id=player_id).\
435                 filter(Player.active_ind == True).one()
436
437         # games played, alivetime, wins, kills, deaths
438         total_stats = _get_total_stats(player.player_id)
439
440         # games breakdown - N games played (X ctf, Y dm) etc
441         # DEPRECATED: included in total_stats, see above
442         # (total_games, games_breakdown) = _get_games_played(player.player_id)
443
444         # favorite map from the past 90 days
445         try:
446             fav_map = _get_fav_map(player.player_id)
447         except:
448             fav_map = None
449
450         # favorite weapon from the past 90 days
451         try:
452             fav_weapon = _get_fav_weapon(player.player_id)
453         except:
454             fav_weapon = None
455
456         # favorite server from the past 90 days
457         try:
458             fav_server = _get_fav_server(player.player_id)
459         except:
460             fav_server = None
461
462         # friendly display of elo information and preliminary status
463         elos = DBSession.query(PlayerElo).filter_by(player_id=player_id).\
464                 filter(PlayerElo.game_type_cd.in_(['ctf','duel','dm'])).\
465                 order_by(PlayerElo.elo.desc()).all()
466
467         elos_display = []
468         elos_dict    = {}
469         for elo in elos:
470             if elo.games > 32:
471                 str = "{0} ({1})"
472             else:
473                 str = "{0}* ({1})"
474
475             elos_display.append(str.format(round(elo.elo, 3),
476                 elo.game_type_cd))
477             elos_dict[elo.game_type_cd] = round(elo.elo, 3)
478         elos_display = ', '.join(elos_display)
479
480         # get current rank information
481         ranks = _get_rank(player_id)
482         
483         ranks_display = []
484         ranks_dict    = {}
485         for gtc,rank,max_rank in ranks:
486             ranks_display.append("{1} of {2} ({0})".format(gtc, rank, max_rank))
487             ranks_dict[gtc] = (rank, max_rank)
488         ranks_display = ', '.join(ranks_display)
489
490
491         # which weapons have been used in the past 90 days
492         # and also, used in 5 games or more?
493         back_then = datetime.datetime.utcnow() - datetime.timedelta(days=90)
494         recent_weapons = []
495         for weapon in DBSession.query(PlayerWeaponStat.weapon_cd, func.count()).\
496                 filter(PlayerWeaponStat.player_id == player_id).\
497                 filter(PlayerWeaponStat.create_dt > back_then).\
498                 group_by(PlayerWeaponStat.weapon_cd).\
499                 having(func.count() > 4).\
500                 all():
501                     recent_weapons.append(weapon[0])
502
503         # recent games table, all data
504         recent_games = DBSession.query(PlayerGameStat, Game, Server, Map).\
505                 filter(PlayerGameStat.player_id == player_id).\
506                 filter(PlayerGameStat.game_id == Game.game_id).\
507                 filter(Game.server_id == Server.server_id).\
508                 filter(Game.map_id == Map.map_id).\
509                 order_by(Game.game_id.desc())[0:10]
510
511     except Exception as e:
512         player = None
513         elos = None
514         elos_display = None
515         total_stats = None
516         recent_games = None
517         # DEPRECATED: included in total_stats, see above
518         #total_games = None
519         #games_breakdown = None
520         recent_weapons = []
521         fav_map = None
522         fav_weapon = None
523         fav_server = None
524         ranks = None
525         ranks_display = None;
526
527     return {'player':player,
528             'elos':elos_dict,
529             'elos_display':elos_display,
530             'recent_games':recent_games,
531             'total_stats':total_stats,
532             # DEPRECATED: included in total_stats, see above
533             #'total_games':total_games,
534             #'games_breakdown':games_breakdown,
535             'recent_weapons':recent_weapons,
536             'fav_map':fav_map,
537             'fav_weapon':fav_weapon,
538             'fav_server':fav_server,
539             'ranks':ranks_dict,
540             'ranks_display':ranks_display,
541             }
542
543
544 def player_info(request):
545     """
546     Provides detailed information on a specific player
547     """
548     return player_info_data(request)
549
550
551 def player_info_json(request):
552     """
553     Provides detailed information on a specific player. JSON.
554     """
555     player_info = player_info_data(request)
556     json_result = {
557         'player': {
558             'id':               player_info['player'].player_id,
559             'nick':             player_info['player'].nick.encode('utf-8'),
560             'stripped_nick':    player_info['player'].nick_strip_colors(),
561             'joined':           player_info['player'].create_dt.isoformat(),
562             },
563         'elos':         player_info['elos'],
564         'ranks':        player_info['ranks'],
565         'total_stats':  {
566                 'games':            player_info['total_stats']['games'],
567                 'games_breakdown':  player_info['total_stats']['games_breakdown'],
568                 'alivetime':        datetime_seconds(player_info['total_stats']['alivetime']),
569                 'kills':            player_info['total_stats']['kills'],
570                 'deaths':           player_info['total_stats']['deaths'],
571                 'suicides':         player_info['total_stats']['suicides'],
572                 'wins':             player_info['total_stats']['wins'],
573                 # FIXME - current "wins" query is flawed!
574                 #'losses':           player_info['total_stats']['loses'],
575             },
576         'recent_games': [
577                 {
578                     'game_id':      game.game_id,
579                     'game_type':    game.game_type_cd,
580                     'server':       server.name,
581                     'map':          map.name,
582                     'team':         gamestat.team,
583                     'rank':         gamestat.rank,
584                     'win':          ((gamestat.team != None and gamestat.team == game.winner)
585                                         or (gamestat.team == None and gamestat.rank == 1)),
586                     'time':         game.create_dt.isoformat(),
587                 }
588                 for (gamestat, game, server, map) in player_info['recent_games'][:5]
589             ],
590         }
591     print json_result
592     return json_result
593
594
595 def player_game_index_data(request):
596     player_id = request.matchdict['player_id']
597
598     if request.params.has_key('page'):
599         current_page = request.params['page']
600     else:
601         current_page = 1
602
603     try:
604         games_q = DBSession.query(Game, Server, Map).\
605             filter(PlayerGameStat.game_id == Game.game_id).\
606             filter(PlayerGameStat.player_id == player_id).\
607             filter(Game.server_id == Server.server_id).\
608             filter(Game.map_id == Map.map_id).\
609             order_by(Game.game_id.desc())
610
611         games = Page(games_q, current_page, items_per_page=10, url=page_url)
612
613         pgstats = {}
614         for (game, server, map) in games:
615             pgstats[game.game_id] = DBSession.query(PlayerGameStat).\
616                     filter(PlayerGameStat.game_id == game.game_id).\
617                     order_by(PlayerGameStat.rank).\
618                     order_by(PlayerGameStat.score).all()
619
620     except Exception as e:
621         player = None
622         games = None
623
624     return {'player_id':player_id,
625             'games':games,
626             'pgstats':pgstats}
627
628
629 def player_game_index(request):
630     """
631     Provides an index of the games in which a particular
632     player was involved. This is ordered by game_id, with
633     the most recent game_ids first. Paginated.
634     """
635     return player_game_index_data(request)
636
637
638 def player_game_index_json(request):
639     """
640     Provides an index of the games in which a particular
641     player was involved. This is ordered by game_id, with
642     the most recent game_ids first. Paginated. JSON.
643     """
644     return [{'status':'not implemented'}]
645
646
647 def player_accuracy_data(request):
648     player_id = request.matchdict['id']
649     allowed_weapons = ['nex', 'rifle', 'shotgun', 'uzi', 'minstanex']
650     weapon_cd = 'nex'
651     games = 20
652
653     if request.params.has_key('weapon'):
654         if request.params['weapon'] in allowed_weapons:
655             weapon_cd = request.params['weapon']
656
657     if request.params.has_key('games'):
658         try:
659             games = request.params['games']
660
661             if games < 0:
662                 games = 20
663             if games > 50:
664                 games = 50
665         except:
666             games = 20
667
668     (avg, accs) = get_accuracy_stats(player_id, weapon_cd, games)
669
670     # if we don't have enough data for the given weapon
671     if len(accs) < games:
672         games = len(accs)
673
674     return {
675             'player_id':player_id, 
676             'player_url':request.route_url('player_info', id=player_id), 
677             'weapon':weapon_cd, 
678             'games':games, 
679             'avg':avg, 
680             'accs':accs
681             }
682
683
684 def player_accuracy(request):
685     """
686     Provides the accuracy for the given weapon. (JSON only)
687     """
688     return player_accuracy_data(request)
689
690
691 def player_accuracy_json(request):
692     """
693     Provides a JSON response representing the accuracy for the given weapon.
694
695     Parameters:
696        weapon = which weapon to display accuracy for. Valid values are 'nex',
697                 'shotgun', 'uzi', and 'minstanex'.
698        games = over how many games to display accuracy. Can be up to 50.
699     """
700     return player_accuracy_data(request)
701
702
703 def player_damage_data(request):
704     player_id = request.matchdict['id']
705     allowed_weapons = ['grenadelauncher', 'electro', 'crylink', 'hagar',
706             'rocketlauncher', 'laser']
707     weapon_cd = 'rocketlauncher'
708     games = 20
709
710     if request.params.has_key('weapon'):
711         if request.params['weapon'] in allowed_weapons:
712             weapon_cd = request.params['weapon']
713
714     if request.params.has_key('games'):
715         try:
716             games = request.params['games']
717
718             if games < 0:
719                 games = 20
720             if games > 50:
721                 games = 50
722         except:
723             games = 20
724
725     (avg, dmgs) = get_damage_stats(player_id, weapon_cd, games)
726
727     # if we don't have enough data for the given weapon
728     if len(dmgs) < games:
729         games = len(dmgs)
730
731     return {
732             'player_id':player_id, 
733             'player_url':request.route_url('player_info', id=player_id), 
734             'weapon':weapon_cd, 
735             'games':games, 
736             'avg':avg, 
737             'dmgs':dmgs
738             }
739
740
741 def player_damage_json(request):
742     """
743     Provides a JSON response representing the damage for the given weapon.
744
745     Parameters:
746        weapon = which weapon to display damage for. Valid values are
747          'grenadelauncher', 'electro', 'crylink', 'hagar', 'rocketlauncher',
748          'laser'.
749        games = over how many games to display damage. Can be up to 50.
750     """
751     return player_damage_data(request)