Add the 'topactive' view to maps as well. Work-in-progress.
authorAnt Zucaro <azucaro@gmail.com>
Sat, 8 Jul 2017 14:00:24 +0000 (10:00 -0400)
committerAnt Zucaro <azucaro@gmail.com>
Sat, 8 Jul 2017 14:00:24 +0000 (10:00 -0400)
xonstat/__init__.py
xonstat/templates/map_top_active.mako [new file with mode: 0644]
xonstat/views/__init__.py
xonstat/views/map.py

index f72e5b9..72e849d 100644 (file)
@@ -167,6 +167,12 @@ def main(global_config, **settings):
     config.add_view(view=MapTopScorers, route_name="map_top_scorers", attr="json",
                     renderer="json", accept="application/json")
 
+    config.add_route("map_top_active", "/map/{id:\d+}/topactive")
+    config.add_view(view=MapTopPlayers, route_name="map_top_active", attr="html",
+                    renderer="map_top_active.mako", accept="text/html")
+    config.add_view(view=MapTopPlayers, route_name="map_top_active", attr="json",
+                    renderer="json", accept="application/json")
+
     config.add_route("map_info",      "/map/{id:\d+}")
     config.add_view(map_info,      route_name="map_info",      renderer="map_info.mako")
 
diff --git a/xonstat/templates/map_top_active.mako b/xonstat/templates/map_top_active.mako
new file mode 100644 (file)
index 0000000..291f7e4
--- /dev/null
@@ -0,0 +1,57 @@
+<%inherit file="base.mako"/>
+<%namespace name="nav" file="nav.mako" />
+
+<%block name="navigation">
+  ${nav.nav('maps')}
+</%block>
+
+<%block name="title">
+  Map Active Players Index
+</%block>
+
+% if not top_players and last is not None:
+  <h2 class="text-center">Sorry, no more active players!</h2>
+
+% elif not top_players and last is None:
+  <h2 class="text-center">No active players found. Yikes, get playing!</h2>
+
+% else:
+  ##### ACTIVE PLAYERS #####
+  <div class="row">
+    <div class="small-12 large-6 large-offset-3 columns">
+      <table class="table-hover table-condensed">
+        <thead>
+          <tr>
+            <th class="small-2">#</th>
+            <th class="small-7">Nick</th>
+            <th class="small-3">Play Time</th>
+          </tr>
+        </thead>
+
+        <tbody>
+        % for tp in top_players:
+          <tr>
+            <td>${tp.rank}</td>
+            <td class="no-stretch"><a href="${request.route_url('player_info', id=tp.player_id)}" title="Go to the player info page for this player">${tp.nick|n}</a></td>
+            <td>${tp.alivetime}</td>
+          </tr>
+        % endfor
+        </tbody>
+      </table>
+      <p class="text-center"><small>Note: these figures are from the past ${lifetime} days</small>
+    </div>
+  </div>
+
+  % if len(top_players) == 20:
+    <div class="row">
+      <div class="small-12 large-6 large-offset-3 columns">
+        <ul class="pagination">
+          <li>
+            <a  href="${request.route_url('map_top_active', id=map_id, _query=query)}" name="Next Page">Next <i class="fa fa-arrow-right"></i></a>
+          </li>
+        </ul>
+      </div>
+    </div>
+  % endif
+
+% endif
index e947cb0..13564e0 100644 (file)
@@ -13,7 +13,7 @@ from xonstat.views.game   import game_info, rank_index
 from xonstat.views.game   import game_info_json, rank_index_json
 from xonstat.views.game   import game_finder, game_finder_json
 
-from xonstat.views.map import MapIndex, MapTopScorers
+from xonstat.views.map import MapIndex, MapTopScorers, MapTopPlayers
 from xonstat.views.map import map_info, map_info_json
 from xonstat.views.map import map_captimes, map_captimes_json
 
index 702d280..9642ac9 100644 (file)
@@ -25,7 +25,6 @@ class MapIndex(object):
     def __init__(self, request):
         """Common parameter parsing."""
         self.request = request
-        self.page = request.params.get("page", 1)
         self.last = request.params.get("last", None)
 
         # all views share this data, so we'll pre-calculate
@@ -96,7 +95,6 @@ class MapTopScorers(MapInfoBase):
     def get_top_scorers(self):
         """Top players by score. Shared by all renderers."""
         cutoff = self.now - timedelta(days=self.lifetime)
-        cutoff = self.now - timedelta(days=120)
 
         top_scorers_q = DBSession.query(
             fg.row_number().over(order_by=expr.desc(func.sum(PlayerGameStat.score))).label("rank"),
@@ -154,6 +152,75 @@ class MapTopScorers(MapInfoBase):
         }
 
 
+class MapTopPlayers(MapInfoBase):
+    """Returns the top players by time on a given map."""
+
+    def __init__(self, request, limit=INDEX_COUNT, last=None):
+        """Common parameter parsing."""
+        super(MapTopPlayers, self).__init__(request, limit, last)
+        self.top_players = self.get_top_players()
+
+    def get_top_players(self):
+        """Top players by score. Shared by all renderers."""
+        cutoff = self.now - timedelta(days=self.lifetime)
+
+        top_players_q = DBSession.query(
+            fg.row_number().over(order_by=expr.desc(func.sum(PlayerGameStat.alivetime))).label("rank"),
+            Player.player_id, Player.nick, func.sum(PlayerGameStat.alivetime).label("alivetime"))\
+            .filter(Player.player_id == PlayerGameStat.player_id)\
+            .filter(Game.game_id == PlayerGameStat.game_id)\
+            .filter(Game.map_id == self.map_id)\
+            .filter(Player.player_id > 2)\
+            .filter(PlayerGameStat.create_dt > cutoff)\
+            .order_by(expr.desc(func.sum(PlayerGameStat.alivetime)))\
+            .group_by(Player.nick)\
+            .group_by(Player.player_id)
+
+        if self.last:
+            top_players_q = top_players_q.offset(self.last)
+
+        if self.limit:
+            top_players_q = top_players_q.limit(self.limit)
+
+        top_players = top_players_q.all()
+
+        return top_players
+
+    def html(self):
+        """Returns the HTML-ready representation."""
+        TopPlayer = namedtuple("TopPlayer", ["rank", "player_id", "nick", "alivetime"])
+
+        top_players = [TopPlayer(tp.rank, tp.player_id, html_colors(tp.nick), tp.alivetime)
+                       for tp in self.top_players]
+
+        # build the query string
+        query = {}
+        if len(top_players) > 1:
+            query['last'] = top_players[-1].rank
+
+        return {
+            "map_id": self.map_id,
+            "top_players": top_players,
+            "lifetime": self.lifetime,
+            "last": query.get("last", None),
+            "query": query,
+        }
+
+    def json(self):
+        """For rendering this data using JSON."""
+        top_players = [{
+            "rank": ts.rank,
+            "player_id": ts.player_id,
+            "nick": ts.nick,
+            "time": ts.alivetime.total_seconds(),
+        } for ts in self.top_players]
+
+        return {
+            "map_id": self.map_id,
+            "top_players": top_players,
+        }
+
+
 def _map_info_data(request):
     map_id = int(request.matchdict['id'])