From df71363d882d8ca9df32e359050bee06b0723d33 Mon Sep 17 00:00:00 2001 From: "Jan D. Behrens" Date: Thu, 20 Sep 2012 19:12:57 +0200 Subject: [PATCH] Implemented JSON API for retrieving player_info data --- xonstat/models.py | 20 +++++++--- xonstat/util.py | 20 ++++++++++ xonstat/views/player.py | 87 +++++++++++++++++++++++++++++++++++------ 3 files changed, 110 insertions(+), 17 deletions(-) diff --git a/xonstat/models.py b/xonstat/models.py index bbf8bf4..6b92e10 100644 --- a/xonstat/models.py +++ b/xonstat/models.py @@ -37,7 +37,11 @@ class Player(object): return "" % (self.player_id, self.nick.encode('utf-8')) def to_dict(self): - return {'player_id':self.player_id, 'name':self.nick.encode('utf-8')} + return {'player_id':self.player_id, 'name':self.nick.encode('utf-8'), + 'joined':self.create_dt.strftime('%Y-%m-%dT%H:%M:%SZ'), + 'active_ind':self.active_ind, 'location':self.location, + 'stripped_nick':self.stripped_nick.encode('utf-8'), + 'nick_html_colors':self.nick_html_colors().encode('utf-8')} def epoch(self): return timegm(self.create_dt.timetuple()) @@ -69,7 +73,8 @@ class Server(object): return "" % (self.server_id, self.name.encode('utf-8')) def to_dict(self): - return {'server_id':self.server_id, 'name':self.name.encode('utf-8')} + return {'server_id':self.server_id, 'name':self.name.encode('utf-8'), + 'ip_addr':self.ip_addr, 'location':self.location} def fuzzy_date(self): return pretty_date(self.create_dt) @@ -86,7 +91,8 @@ class Map(object): return "" % (self.map_id, self.name, self.version) def to_dict(self): - return {'map_id':self.map_id, 'name':self.name, 'version':self.version} + return {'map_id':self.map_id, 'name':self.name, 'version':self.version, + 'pk3_name':self.pk3_name, 'curl_url':self.curl_url} def fuzzy_date(self): return pretty_date(self.create_dt) @@ -127,7 +133,9 @@ class PlayerGameStat(object): return "" % (self.player_id, self.game_id, self.create_dt) def to_dict(self): - return {'player_id':self.player_id, 'game_id':self.game_id, 'create_dt':self.create_dt.strftime('%Y-%m-%dT%H:%M:%SZ')} + return {'player_id':self.player_id, 'game_id':self.game_id, + 'create_dt':self.create_dt.strftime('%Y-%m-%dT%H:%M:%SZ'), + 'alivetime':self.alivetime, 'rank':self.rank, 'score':self.score, 'team':self.team} def nick_stripped(self): if self.nick is None: @@ -215,10 +223,10 @@ class PlayerElo(object): self.games = 0 def __repr__(self): - return "" % (self.player_id, self.game_type_cd, self.elo) + return "" % (self.player_id, self.game_type_cd, self.elo, self.games) def to_dict(self): - return {'player_id':self.player_id, 'game_type_cd':self.game_type_cd, 'elo':self.elo} + return {'player_id':self.player_id, 'game_type_cd':self.game_type_cd, 'elo':self.elo, 'games':self.games} class PlayerRank(object): diff --git a/xonstat/util.py b/xonstat/util.py index 7c2692d..1f440ed 100644 --- a/xonstat/util.py +++ b/xonstat/util.py @@ -2,6 +2,7 @@ import re from colorsys import rgb_to_hls, hls_to_rgb from cgi import escape as html_escape from datetime import datetime, timedelta +from decimal import Decimal # Map of special chars to ascii from Darkplace's console.c. _qfont_table = [ @@ -155,3 +156,22 @@ def pretty_date(time=False): def datetime_seconds(td): return float(td.microseconds + (td.seconds + td.days * 24 * 3600) * 10**6) / 10**6 +def namedtuple_to_dict(tup): + result = {} + for key,value in tup._asdict().items(): + result[key] = value + return result + +def fix_json_types(data): + result = {} + for key,value in data.items(): + if type(value) == Decimal: + result[key] = float(value) + elif type(value) == datetime: + result[key] = value.strftime('%Y-%m-%dT%H:%M:%SZ') + elif type(value) == timedelta: + result[key] = datetime_seconds(value) + else: + result[key] = value + return result + diff --git a/xonstat/views/player.py b/xonstat/views/player.py index 14b793e..9cfb0db 100644 --- a/xonstat/views/player.py +++ b/xonstat/views/player.py @@ -11,7 +11,7 @@ from pyramid.url import current_route_url from sqlalchemy import desc, distinct from webhelpers.paginate import Page, PageURL from xonstat.models import * -from xonstat.util import page_url +from xonstat.util import page_url, namedtuple_to_dict, fix_json_types log = logging.getLogger(__name__) @@ -250,6 +250,8 @@ def get_fav_maps(player_id, game_type_cd=None): defined as the favorite map of the game type you've played the most. The input parameter game_type_cd is for this. """ + FavMap = namedtuple('FavMap', ['map_name', 'map_id', 'times_played', 'game_type_cd']) + raw_favs = DBSession.query('game_type_cd', 'map_name', 'map_id', 'times_played').\ from_statement( @@ -281,20 +283,25 @@ def get_fav_maps(player_id, game_type_cd=None): fav_maps = {} overall_fav = None for row in raw_favs: + fv = FavMap(map_name=row.map_name, + map_id=row.map_id, + times_played=row.times_played, + game_type_cd=row.game_type_cd) + # if we aren't given a favorite game_type_cd # then the overall favorite is the one we've # played the most if overall_fav is None: - fav_maps['overall'] = row - overall_fav = row.game_type_cd + fav_maps['overall'] = fv + overall_fav = fv.game_type_cd # otherwise it is the favorite map from the # favorite game_type_cd (provided as a param) # and we'll overwrite the first dict entry - if game_type_cd == row.game_type_cd: - fav_maps['overall'] = row + if game_type_cd == fv.game_type_cd: + fav_maps['overall'] = fv - fav_maps[row.game_type_cd] = row + fav_maps[row.game_type_cd] = fv return fav_maps @@ -310,7 +317,9 @@ def get_ranks(player_id): The key to the dictionary is the game type code. There is also an "overall" game_type_cd which is the overall best rank. - """ + """ + Rank = namedtuple('Rank', ['rank', 'max_rank', 'game_type_cd']) + raw_ranks = DBSession.query("game_type_cd", "rank", "max_rank").\ from_statement( "select pr.game_type_cd, pr.rank, overall.max_rank " @@ -326,11 +335,15 @@ def get_ranks(player_id): ranks = {} found_top_rank = False for row in raw_ranks: + rank = Rank(rank=row.rank, + max_rank=row.max_rank, + game_type_cd=row.game_type_cd) + if not found_top_rank: - ranks['overall'] = row + ranks['overall'] = rank found_top_rank = True - ranks[row.game_type_cd] = row + ranks[row.game_type_cd] = rank return ranks; @@ -370,6 +383,8 @@ def get_recent_games(player_id): Returns the full PlayerGameStat, Game, Server, Map objects for all recent games. """ + RecentGame = namedtuple('RecentGame', ['player_stats', 'game', 'server', 'map']) + # recent games table, all data recent_games = DBSession.query(PlayerGameStat, Game, Server, Map).\ filter(PlayerGameStat.player_id == player_id).\ @@ -378,7 +393,12 @@ def get_recent_games(player_id): filter(Game.map_id == Map.map_id).\ order_by(Game.game_id.desc())[0:10] - return recent_games + return [ + RecentGame(player_stats=row.PlayerGameStat, + game=row.Game, + server=row.Server, + map=row.Map) + for row in recent_games ] def get_recent_weapons(player_id): @@ -529,7 +549,52 @@ def player_info_json(request): """ Provides detailed information on a specific player. JSON. """ - return [{'status':'not implemented'}] + + # All player_info fields are converted into JSON-formattable dictionaries + player_info = player_info_data(request) + + player = player_info['player'].to_dict() + + games_played = {} + for game in player_info['games_played']: + games_played[game.game_type_cd] = namedtuple_to_dict(game) + + overall_stats = {} + for gt,stats in player_info['overall_stats'].items(): + overall_stats[gt] = fix_json_types(namedtuple_to_dict(stats)) + + elos = {} + for gt,elo in player_info['elos'].items(): + elos[gt] = fix_json_types(elo.to_dict()) + + ranks = {} + for gt,rank in player_info['ranks'].items(): + ranks[gt] = namedtuple_to_dict(rank) + + fav_maps = {} + for gt,stats in player_info['fav_maps'].items(): + fav_maps[gt] = namedtuple_to_dict(stats) + + recent_games = [] + for game in player_info['recent_games']: + entry = {} + for key,value in namedtuple_to_dict(game).items(): + entry[key] = fix_json_types(value.to_dict()) + recent_games.append(entry) + + recent_weapons = player_info['recent_weapons'] + + return [{ + 'player': player, + 'games_played': games_played, + 'overall_stats': overall_stats, + 'fav_maps': fav_maps, + 'elos': elos, + 'ranks': ranks, + 'recent_games': recent_games, + 'recent_weapons': recent_weapons, + }] + #return [{'status':'not implemented'}] def player_game_index_data(request): -- 2.39.2