Add a new summary statistics byline below the main logo.
authorAnt Zucaro <azucaro@gmail.com>
Sun, 16 Dec 2012 00:14:59 +0000 (19:14 -0500)
committerJan Behrens <zykure@web.de>
Sun, 23 Dec 2012 14:18:35 +0000 (15:18 +0100)
Summary statistics shown and refreshed hourly via cache
invalidation (Beaker):

- total games
- total dm, duel, and ctf games
- total players
- total servers

If for some reason the summary statistics can't be retrieved, the
old one is shown instead.

development.ini
production.ini
xonstat/__init__.py
xonstat/static/css/style.css
xonstat/templates/main_index.mako
xonstat/views/main.py

index 4ba4338..8f0d819 100755 (executable)
@@ -15,6 +15,13 @@ session.secret = CHANGEMECHANGEMECHANGEME
 jinja2.directories = xonstat:templates
 mako.directories = xonstat:templates
 
+# Beaker cache settings
+# Default is to use one region with hourly cache invalidation
+cache.regions = hourly_term
+cache.type = memory
+cache.hourly_term.expire = 3600
+
+
 ##### XONSTAT CONFIG SETTINGS #####
 
 # how many "real" players are required before the data
index 94c751f..e36da4b 100644 (file)
@@ -10,6 +10,12 @@ sqlalchemy.url = sqlite:///%(here)s/XonStat.db
 jinja2.directories = xonstat:templates
 mako.directories = xonstat:templates
 
+# Beaker cache settings
+# Default is to use one region with hourly cache invalidation
+cache.regions = hourly_term
+cache.type = memory
+cache.hourly_term.expire = 3600
+
 xonstat.minimum_required_players = 2
 xonstat.leaderboard_lifetime = 7
 xonstat.verify_requests = true
index 2c02f99..e334af5 100644 (file)
@@ -1,4 +1,5 @@
 import sqlahelper
+from pyramid_beaker import set_cache_regions_from_settings
 from pyramid.config import Configurator
 from pyramid.renderers import JSONP
 from sqlalchemy import engine_from_config
@@ -15,6 +16,9 @@ def main(global_config, **settings):
     # initialize database structures
     initialize_db(engine)
 
+    # set up beaker cache
+    set_cache_regions_from_settings(settings)
+
     config = Configurator(settings=settings)
 
     config.add_static_view('static', 'xonstat:static')
index 58aa20d..a270f64 100755 (executable)
@@ -3399,7 +3399,7 @@ table {
 }
 #statline {
   font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
-  font-size: 12px;
+  font-size: 13px;
   position: relative;
   top: -25px;
 }
index e26bddf..6ed4e17 100644 (file)
@@ -12,8 +12,11 @@ Leaderboard
 <%block name="hero_unit">
       <div class="hero-unit">
         <img src="/static/css/img/web_background_l2.png" />
-        #####<p id="statline">Tracking <a href="#">12345</a> players, <a href="#">12345</a> games (<a href="#">123</a> duels, <a href="#">123</a> ctfs, <a href="#">123</a> dms), <a href="#">12345</a> servers, and <a href="#">12345</a> maps since November 2011.</p>
+        % if summary_stats is None:
         <p id="statline">Tracking Xonotic statistics since October 2011.</p>
+        % else:
+        <p id="statline">Tracking <a href="${request.route_url('player_index')}">${summary_stats.total_players}</a> players, <a href="${request.route_url('game_index')}">${summary_stats.total_games}</a> games (${summary_stats.duel_games} duel, ${summary_stats.ctf_games} ctf, ${summary_stats.dm_games} dm), and <a href="${request.route_url('server_index')}">${summary_stats.total_servers}</a> servers since October 2011.</p>
+        % endif
       </div>
 </%block>
 
index 10505b4..d22b594 100644 (file)
@@ -1,6 +1,7 @@
 import logging
 import sqlalchemy.sql.functions as func
 import sqlalchemy.sql.expression as expr
+from beaker.cache import cache_regions, cache_region
 from datetime import datetime, timedelta
 from pyramid.response import Response
 from xonstat.models import *
@@ -11,6 +12,58 @@ from xonstat.views.helpers import RecentGame, recent_games_q
 log = logging.getLogger(__name__)
 
 
+@cache_region('hourly_term')
+def get_summary_stats():
+    """
+    Gets the following aggregate or "summary" statistics about stats:
+        - the total number of players (total_players)
+        - the total number of servers (total_servers)
+        - the total number of games (total_games)
+        - the total number of dm games (dm_games)
+        - the total number of duel games (duel_games)
+        - the total number of ctf games (ctf_games)
+
+    It is worth noting that there is also a table built to house these
+    stats in case the query in this function becomes too long for the
+    one time it runs per hour. In that case there is a script in the
+    xonstatdb repo - update_summary_stats.sql - that can be used via
+    cron to update the data offline.
+    """
+    summary_stats = DBSession.query("total_players", "total_servers",
+            "total_games", "dm_games", "duel_games", "ctf_games").\
+        from_statement(
+        """
+        with total_games as (
+            select game_type_cd, count(*) total_games
+            from games
+            where game_type_cd in ('duel', 'dm', 'ctf')
+            group by game_type_cd
+        ),
+        total_players as (
+            select count(*) total_players
+            from players
+            where active_ind = true
+        ),
+        total_servers as (
+            select count(*) total_servers
+            from servers
+            where active_ind = true
+        )
+        select tp.total_players, ts.total_servers, dm.total_games+
+               duel.total_games+ctf.total_games total_games,
+               dm.total_games dm_games, duel.total_games duel_games,
+               ctf.total_games ctf_games
+        from   total_games dm, total_games duel, total_games ctf,
+               total_players tp, total_servers ts
+        where  dm.game_type_cd = 'dm'
+        and    ctf.game_type_cd = 'ctf'
+        and    duel.game_type_cd = 'duel'
+        """
+        ).one()
+
+    return summary_stats
+
+
 def _main_index_data(request):
     try:
         leaderboard_lifetime = int(
@@ -21,6 +74,12 @@ def _main_index_data(request):
     leaderboard_count = 10
     recent_games_count = 20
 
+    # summary statistics for the tagline
+    try:
+        summary_stats = get_summary_stats()
+    except:
+        summary_stats = None
+
     # top ranked duelers
     duel_ranks = DBSession.query(PlayerRank.player_id, PlayerRank.nick, 
             PlayerRank.elo).\
@@ -96,6 +155,7 @@ def _main_index_data(request):
             'duel_ranks':duel_ranks,
             'ctf_ranks':ctf_ranks,
             'dm_ranks':dm_ranks,
+            'summary_stats':summary_stats,
             }