Add a plain text hashkey view for menu integration.
authorantzucaro <azucaro@gmail.com>
Sun, 30 Dec 2012 16:40:55 +0000 (11:40 -0500)
committerantzucaro <azucaro@gmail.com>
Sun, 30 Dec 2012 16:40:55 +0000 (11:40 -0500)
This adds a plain text response view at the URL /player/<hashkey>,
where hashkey is the idfp of a given player. The response is quite
lengthy and won't be specified in this message, but suffice to say
that the response roughly corresponds to the player_info JSON view
currently at /player/<player_id>.json.

xonstat/__init__.py
xonstat/templates/player_hashkey_info_text.mako [new file with mode: 0644]
xonstat/views/__init__.py
xonstat/views/player.py

index a18a6a12e038b9393f67cf5e0b500adbe0335d70..d4ccfde2e5590fa158414ff2d3e721e279daafb8 100644 (file)
@@ -44,8 +44,11 @@ def main(global_config, **settings):
     config.add_view(player_info,      route_name="player_info",      renderer="player_info.mako")
     config.add_view(player_info_json, route_name="player_info_json", renderer="jsonp")
 
-    config.add_route("player_hashkey_info_json", "/hashkey/{hashkey}")
-    config.add_view(player_hashkey_info_json, route_name="player_hashkey_info_json", renderer="jsonp")
+    config.add_route("player_hashkey_info_text", "/hashkey/{hashkey}")
+    config.add_view(player_hashkey_info_text, route_name="player_hashkey_info_text", renderer="player_hashkey_info_text.mako")
+
+    #config.add_route("player_hashkey_info_json", "/hashkey/{hashkey}.json")
+    #config.add_view(player_hashkey_info_json, route_name="player_hashkey_info_json", renderer="jsonp")
 
     config.add_route("player_elo_info_json", "/elo/{hashkey}")
     config.add_view(player_elo_info_json, route_name="player_elo_info_json", renderer="jsonp")
diff --git a/xonstat/templates/player_hashkey_info_text.mako b/xonstat/templates/player_hashkey_info_text.mako
new file mode 100644 (file)
index 0000000..6d47e44
--- /dev/null
@@ -0,0 +1,61 @@
+V 1
+R XonStat/1.0
+T ${now}
+S ${request.route_url('player_info', id=player.player_id)}
+P ${hashkey}
+n ${player.nick}
+i ${player.player_id}
+# NOTE: using unixtime here
+e joined ${player_joined}
+% if player.active_ind == True:
+e active-ind 1
+% else:
+e active-ind 0
+% endif
+e location
+% if 'overall' in elos.keys():
+# NOTE: need game type here to specify which elo is being shown as "best"
+e elo ${elos['overall'].elo} ${elos['overall'].game_type_cd}
+% endif
+% if 'overall' in ranks.keys():
+# NOTE: need game type here to specify which percentile is being shown as "best"
+e percentile ${ranks['overall'].percentile} ${ranks['overall'].game_type_cd}
+% endif
+e matches ${games_played[0].games}
+% if 'overall' in ranks.keys():
+# NOTE: need game type here to specify which rank is being shown as "best"
+e rank ${ranks['overall'].rank} ${ranks['overall'].game_type_cd}
+% endif
+e total-deaths ${overall_stats['overall'].total_deaths}
+e total-fckills ${overall_stats['overall'].total_carrier_frags}
+e alivetime ${overall_stats['overall'].total_playing_time_secs}
+e total-kills ${overall_stats['overall'].total_kills}
+e wins ${games_played[0].wins}
+e favorite-map ${fav_maps['overall'].map_name} ${fav_maps['overall'].times_played} ${fav_maps['overall'].game_type_cd}
+% for game_type_cd in overall_stats.keys():
+{ G ${game_type_cd}
+% if game_type_cd in elos.keys():
+e elo ${elos[game_type_cd].elo}
+% endif
+% if game_type_cd in ranks.keys():
+e percentile ${ranks[game_type_cd].percentile}
+% endif
+% for gp in games_played:
+% if gp.game_type_cd == game_type_cd:
+e matches ${gp.games}
+% endif
+% endfor
+% if game_type_cd in ranks.keys():
+e rank ${ranks[game_type_cd].rank}
+% endif
+e total-deaths ${overall_stats[game_type_cd].total_deaths}
+e alivetime ${overall_stats[game_type_cd].total_playing_time_secs}
+e total-kills ${overall_stats[game_type_cd].total_kills}
+% for gp in games_played:
+% if gp.game_type_cd == game_type_cd:
+e wins ${gp.wins}
+% endif
+% endfor
+e favorite-map ${fav_maps[game_type_cd].map_name} ${fav_maps[game_type_cd].times_played}
+}
+% endfor
index ac029dbd44584bb5cee566a4e490d970228ab578..0d9fd13bf8075a88b794a778c6aa76d7c796faee 100644 (file)
@@ -4,7 +4,7 @@ from xonstat.views.player import player_accuracy
 from xonstat.views.player import player_index_json, player_info_json
 from xonstat.views.player import player_game_index_json, player_accuracy_json
 from xonstat.views.player import player_damage_json, player_hashkey_info_json
-from xonstat.views.player import player_elo_info_json
+from xonstat.views.player import player_hashkey_info_text, player_elo_info_json
 
 from xonstat.views.game   import game_index, game_info, rank_index
 from xonstat.views.game   import game_index_json, game_info_json, rank_index_json
index d773e134efd849017869e2c4069b0e65e19f7b80..85142604b09fdef3731a63876d764c4b27f35d7c 100644 (file)
@@ -12,7 +12,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, to_json, pretty_date
+from xonstat.util import page_url, to_json, pretty_date, datetime_seconds
 from xonstat.views.helpers import RecentGame, recent_games_q
 
 log = logging.getLogger(__name__)
@@ -135,6 +135,7 @@ def get_overall_stats(player_id):
         - last_played_epoch (same as above, but in seconds since epoch)
         - last_played_fuzzy (same as above, but in relative date)
         - total_playing_time (total amount of time played the game type)
+        - total_playing_time_secs (same as the above, but in seconds)
         - total_pickups (ctf only)
         - total_captures (ctf only)
         - cap_ratio (ctf only)
@@ -146,7 +147,7 @@ def get_overall_stats(player_id):
     """
     OverallStats = namedtuple('OverallStats', ['total_kills', 'total_deaths',
         'k_d_ratio', 'last_played', 'last_played_epoch', 'last_played_fuzzy',
-        'total_playing_time', 'total_pickups', 'total_captures', 'cap_ratio',
+        'total_playing_time', 'total_playing_time_secs', 'total_pickups', 'total_captures', 'cap_ratio',
         'total_carrier_frags', 'game_type_cd'])
 
     raw_stats = DBSession.query('game_type_cd', 'total_kills',
@@ -166,28 +167,25 @@ def get_overall_stats(player_id):
                 "WHERE  g.game_id = pgs.game_id "
                   "AND  pgs.player_id = :player_id "
                 "GROUP  BY g.game_type_cd "
+                "UNION "
+                "SELECT 'overall' game_type_cd, "
+                       "Sum(pgs.kills)         total_kills, "
+                       "Sum(pgs.deaths)        total_deaths, "
+                       "Max(pgs.create_dt)     last_played, "
+                       "Sum(pgs.alivetime)     total_playing_time, "
+                       "Sum(pgs.pickups)       total_pickups, "
+                       "Sum(pgs.captures)      total_captures, "
+                       "Sum(pgs.carrier_frags) total_carrier_frags "
+                "FROM   games g, "
+                       "player_game_stats pgs "
+                "WHERE  g.game_id = pgs.game_id "
+                  "AND  pgs.player_id = :player_id "
             ).params(player_id=player_id).all()
 
     # to be indexed by game_type_cd
     overall_stats = {}
 
-    # sums for the "overall" game type (which is fake)
-    overall_kills = 0
-    overall_deaths = 0
-    overall_last_played = None
-    overall_playing_time = datetime.timedelta(seconds=0)
-    overall_carrier_frags = 0
-
     for row in raw_stats:
-        # running totals or mins
-        overall_kills += row.total_kills or 0
-        overall_deaths += row.total_deaths or 0
-
-        if overall_last_played is None or row.last_played > overall_last_played:
-            overall_last_played = row.last_played
-
-        overall_playing_time += row.total_playing_time
-
         # individual gametype ratio calculations
         try:
             k_d_ratio = float(row.total_kills)/row.total_deaths
@@ -199,8 +197,6 @@ def get_overall_stats(player_id):
         except:
             cap_ratio = None
 
-        overall_carrier_frags += row.total_carrier_frags or 0
-
         # everything else is untouched or "raw"
         os = OverallStats(total_kills=row.total_kills,
                 total_deaths=row.total_deaths,
@@ -209,6 +205,7 @@ def get_overall_stats(player_id):
                 last_played_epoch=timegm(row.last_played.timetuple()),
                 last_played_fuzzy=pretty_date(row.last_played),
                 total_playing_time=row.total_playing_time,
+                total_playing_time_secs=int(datetime_seconds(row.total_playing_time)),
                 total_pickups=row.total_pickups,
                 total_captures=row.total_captures,
                 cap_ratio=cap_ratio,
@@ -217,27 +214,6 @@ def get_overall_stats(player_id):
 
         overall_stats[row.game_type_cd] = os
 
-    # and lastly, the overall stuff
-    try:
-        overall_k_d_ratio = float(overall_kills)/overall_deaths
-    except:
-        overall_k_d_ratio = None
-
-    os = OverallStats(total_kills=overall_kills,
-            total_deaths=overall_deaths,
-            k_d_ratio=overall_k_d_ratio,
-            last_played=overall_last_played,
-            last_played_epoch=timegm(overall_last_played.timetuple()),
-            last_played_fuzzy=pretty_date(overall_last_played),
-            total_playing_time=overall_playing_time,
-            total_pickups=None,
-            total_captures=None,
-            cap_ratio=None,
-            total_carrier_frags=overall_carrier_frags,
-            game_type_cd='overall')
-
-    overall_stats['overall'] = os
-
     return overall_stats
 
 
@@ -773,6 +749,7 @@ def player_hashkey_info_data(request):
         ranks          = None
 
     return {'player':player,
+            'hashkey':hashkey,
             'games_played':games_played,
             'overall_stats':overall_stats,
             'fav_maps':fav_maps,
@@ -822,6 +799,47 @@ def player_hashkey_info_json(request):
     }]
 
 
+def player_hashkey_info_text(request):
+    """
+    Provides detailed information on a specific player. Plain text.
+    """
+    # UTC epoch
+    now = timegm(datetime.datetime.utcnow().timetuple())
+
+    # All player_info fields are converted into JSON-formattable dictionaries
+    player_info = player_hashkey_info_data(request)
+
+    # gather all of the data up into aggregate structures
+    player = player_info['player']
+    games_played = player_info['games_played']
+    overall_stats = player_info['overall_stats']
+    elos = player_info['elos']
+    ranks = player_info['ranks']
+    fav_maps = player_info['fav_maps']
+
+    # one-offs for things needing conversion for text/plain
+    player_joined = timegm(player.create_dt.timetuple())
+    alivetime = int(datetime_seconds(overall_stats['overall'].total_playing_time))
+
+    # this is a plain text response, if we don't do this here then
+    # Pyramid will assume html
+    request.response.content_type = 'text/plain'
+
+    return {
+        'version':          1,
+        'now':              now,
+        'player':           player,
+        'hashkey':          player_info['hashkey'],
+        'player_joined':    player_joined,
+        'games_played':     games_played,
+        'overall_stats':    overall_stats,
+        'alivetime':        alivetime,
+        'fav_maps':         fav_maps,
+        'elos':             elos,
+        'ranks':            ranks,
+    }
+
+
 def player_elo_info_data(request):
     """
     Provides elo information on a specific player. Raw data is returned.