]> de.git.xonotic.org Git - xonotic/xonstat.git/blobdiff - xonstat/views/player.py
Python 2.6.x doesn't have total_seconds().
[xonotic/xonstat.git] / xonstat / views / player.py
index 02e3cd3360a9b5437f29c470e0f03d615ae0b2c5..b1fbd5d8ca643ef2f84613fc345dbace2126000f 100644 (file)
@@ -15,7 +15,7 @@ from xonstat.util import page_url
 log = logging.getLogger(__name__)
 
 
-def _player_index_data(request):
+def player_index_data(request):
     if request.params.has_key('page'):
         current_page = request.params['page']
     else:
@@ -42,7 +42,14 @@ def player_index(request):
     """
     Provides a list of all the current players.
     """
-    return _player_index_data(request)
+    return player_index_data(request)
+
+
+def player_index_json(request):
+    """
+    Provides a list of all the current players. JSON.
+    """
+    return [{'status':'not implemented'}]
 
 
 def _get_games_played(player_id):
@@ -108,6 +115,52 @@ def _get_total_stats(player_id):
     return total_stats
 
 
+def _get_fav_map(player_id):
+    """
+    Get the player's favorite map. The favorite map is defined
+    as the map that he or she has played the most in the past 
+    90 days.
+
+    Returns a dictionary with keys for the map's name and id.
+    """
+    # 90 day window
+    back_then = datetime.datetime.utcnow() - datetime.timedelta(days=90)
+
+    raw_fav_map = DBSession.query(Map.name, Map.map_id).\
+            filter(Game.game_id == PlayerGameStat.game_id).\
+            filter(Game.map_id == Map.map_id).\
+            filter(PlayerGameStat.player_id == player_id).\
+            filter(PlayerGameStat.create_dt > back_then).\
+            group_by(Map.name, Map.map_id).\
+            order_by(func.count().desc()).\
+            limit(1).one()
+
+    fav_map = {}
+    fav_map['name'] = raw_fav_map[0]
+    fav_map['id'] = raw_fav_map[1]
+
+    return fav_map
+
+
+def _get_rank(player_id):
+    """
+    Get the player's rank as well as the total number of ranks.
+    """
+    rank = DBSession.query("game_type_cd", "rank", "max_rank").\
+            from_statement(
+                "select pr.game_type_cd, pr.rank, overall.max_rank "
+                "from player_ranks pr,  "
+                   "(select game_type_cd, max(rank) max_rank "
+                    "from player_ranks  "
+                    "group by game_type_cd) overall "
+                "where pr.game_type_cd = overall.game_type_cd  "
+                "and player_id = :player_id "
+                "order by rank").\
+            params(player_id=player_id).all()
+
+    return rank;
+
+
 def get_accuracy_stats(player_id, weapon_cd, games):
     """
     Provides accuracy for weapon_cd by player_id for the past N games.
@@ -147,7 +200,49 @@ def get_accuracy_stats(player_id, weapon_cd, games):
     return (avg, accs)
 
 
-def _player_info_data(request):
+def get_damage_stats(player_id, weapon_cd, games):
+    """
+    Provides damage info for weapon_cd by player_id for the past N games.
+    """
+    try:
+        raw_avg = DBSession.query(func.sum(PlayerWeaponStat.actual),
+                func.sum(PlayerWeaponStat.hit)).\
+                filter(PlayerWeaponStat.player_id == player_id).\
+                filter(PlayerWeaponStat.weapon_cd == weapon_cd).\
+                one()
+
+        avg = round(float(raw_avg[0])/raw_avg[1], 2)
+
+        # Determine the damage efficiency (hit, fired) numbers for $games games
+        # This is then enumerated to create parameters for a flot graph
+        raw_dmgs = DBSession.query(PlayerWeaponStat.game_id, 
+            PlayerWeaponStat.actual, PlayerWeaponStat.hit).\
+                filter(PlayerWeaponStat.player_id == player_id).\
+                filter(PlayerWeaponStat.weapon_cd == weapon_cd).\
+                order_by(PlayerWeaponStat.game_id.desc()).\
+                limit(games).\
+                all()
+
+        # they come out in opposite order, so flip them in the right direction
+        raw_dmgs.reverse()
+
+        dmgs = []
+        for i in range(len(raw_dmgs)):
+            # try to derive, unless we've hit nothing then set to 0!
+            try:
+                dmg = round(float(raw_dmgs[i][1])/raw_dmgs[i][2], 2)
+            except:
+                dmg = 0.0
+
+            dmgs.append((raw_dmgs[i][0], dmg))
+    except Exception as e:
+        dmgs = []
+        avg = 0.0
+
+    return (avg, dmgs)
+
+
+def player_info_data(request):
     player_id = int(request.matchdict['id'])
     if player_id <= 2:
         player_id = -1;
@@ -162,6 +257,11 @@ def _player_info_data(request):
         # games breakdown - N games played (X ctf, Y dm) etc
         (total_games, games_breakdown) = _get_games_played(player.player_id)
 
+        # favorite map from the past 90 days
+        try:
+            fav_map = _get_fav_map(player.player_id)
+        except:
+            fav_map = None
 
         # friendly display of elo information and preliminary status
         elos = DBSession.query(PlayerElo).filter_by(player_id=player_id).\
@@ -178,6 +278,12 @@ def _player_info_data(request):
             elos_display.append(str.format(round(elo.elo, 3),
                 elo.game_type_cd))
 
+        # get current rank information
+        ranks = _get_rank(player_id)
+        ranks_display = ', '.join(["{1} of {2} ({0})".format(gtc, rank,
+            max_rank) for gtc, rank, max_rank in ranks])
+
+
         # which weapons have been used in the past 90 days
         # and also, used in 5 games or more?
         back_then = datetime.datetime.utcnow() - datetime.timedelta(days=90)
@@ -206,6 +312,8 @@ def _player_info_data(request):
         total_games = None
         games_breakdown = None
         recent_weapons = []
+        fav_map = None
+        ranks_display = None;
 
     return {'player':player,
             'elos_display':elos_display,
@@ -214,6 +322,8 @@ def _player_info_data(request):
             'total_games':total_games,
             'games_breakdown':games_breakdown,
             'recent_weapons':recent_weapons,
+            'fav_map':fav_map,
+            'ranks_display':ranks_display,
             }
 
 
@@ -221,10 +331,17 @@ def player_info(request):
     """
     Provides detailed information on a specific player
     """
-    return _player_info_data(request)
+    return player_info_data(request)
+
+
+def player_info_json(request):
+    """
+    Provides detailed information on a specific player. JSON.
+    """
+    return [{'status':'not implemented'}]
 
 
-def _player_game_index_data(request):
+def player_game_index_data(request):
     player_id = request.matchdict['player_id']
 
     if request.params.has_key('page'):
@@ -264,10 +381,19 @@ def player_game_index(request):
     player was involved. This is ordered by game_id, with
     the most recent game_ids first. Paginated.
     """
-    return _player_game_index_data(request)
+    return player_game_index_data(request)
 
 
-def _player_accuracy_data(request):
+def player_game_index_json(request):
+    """
+    Provides an index of the games in which a particular
+    player was involved. This is ordered by game_id, with
+    the most recent game_ids first. Paginated. JSON.
+    """
+    return [{'status':'not implemented'}]
+
+
+def player_accuracy_data(request):
     player_id = request.matchdict['id']
     allowed_weapons = ['nex', 'rifle', 'shotgun', 'uzi', 'minstanex']
     weapon_cd = 'nex'
@@ -304,6 +430,13 @@ def _player_accuracy_data(request):
             }
 
 
+def player_accuracy(request):
+    """
+    Provides the accuracy for the given weapon. (JSON only)
+    """
+    return player_accuracy_data(request)
+
+
 def player_accuracy_json(request):
     """
     Provides a JSON response representing the accuracy for the given weapon.
@@ -313,4 +446,55 @@ def player_accuracy_json(request):
                 'shotgun', 'uzi', and 'minstanex'.
        games = over how many games to display accuracy. Can be up to 50.
     """
-    return _player_accuracy_data(request)
+    return player_accuracy_data(request)
+
+
+def player_damage_data(request):
+    player_id = request.matchdict['id']
+    allowed_weapons = ['grenadelauncher', 'electro', 'crylink', 'hagar',
+            'rocketlauncher', 'laser']
+    weapon_cd = 'rocketlauncher'
+    games = 20
+
+    if request.params.has_key('weapon'):
+        if request.params['weapon'] in allowed_weapons:
+            weapon_cd = request.params['weapon']
+
+    if request.params.has_key('games'):
+        try:
+            games = request.params['games']
+
+            if games < 0:
+                games = 20
+            if games > 50:
+                games = 50
+        except:
+            games = 20
+
+    (avg, dmgs) = get_damage_stats(player_id, weapon_cd, games)
+
+    # if we don't have enough data for the given weapon
+    if len(dmgs) < games:
+        games = len(dmgs)
+
+    return {
+            'player_id':player_id, 
+            'player_url':request.route_url('player_info', id=player_id), 
+            'weapon':weapon_cd, 
+            'games':games, 
+            'avg':avg, 
+            'dmgs':dmgs
+            }
+
+
+def player_damage_json(request):
+    """
+    Provides a JSON response representing the damage for the given weapon.
+
+    Parameters:
+       weapon = which weapon to display damage for. Valid values are
+         'grenadelauncher', 'electro', 'crylink', 'hagar', 'rocketlauncher',
+         'laser'.
+       games = over how many games to display damage. Can be up to 50.
+    """
+    return player_damage_data(request)