Index: controllers/query.py
===================================================================
--- controllers/query.py	(revision 242)
+++ controllers/query.py	(working copy)
@@ -26,7 +26,7 @@
     if request.params.get('do_query', None):
       c.params.setFromParams(request.params)
       c.reports = c.params.query_topcrashes()
-      if c.reports.rowcount == 1:
+      if len(c.reports) == 1:
         h.redirect_to('/report/list', signature=c.reports.fetchone().signature,
                       **c.params.getURLDict())
 
Index: lib/queryparams.py
===================================================================
--- lib/queryparams.py	(revision 242)
+++ lib/queryparams.py	(working copy)
@@ -4,7 +4,7 @@
 from pylons.database import create_engine
 from sqlalchemy import sql, func, select, types
 from sqlalchemy.databases.postgres import PGInterval
-import re
+import re, sys
 from socorro.lib.platforms import count_platforms, platformList
 import socorro
 from pylons import h
@@ -179,7 +179,7 @@
 
   def filterByPlatform(self, q):
     if len(self.platforms):
-      q = q.filter(sql.or_(*[platform.condition()
+      q = q.filter(sql.or_(*[platform.condition(Report.c)
                              for platform in self.platforms]))
     return q
   
@@ -205,9 +205,10 @@
 
     return q
 
-  def filter(self, q, use_query=True):
+  def filter(self, q, use_query=True, use_dates=True):
     q = q.filter(Report.c.signature != None)
-    q = self.filterByDate(q)
+    if use_dates:
+      q = self.filterByDate(q)
     q = self.filterByProduct(q)
     q = self.filterByBranch(q)
     q = self.filterByVersion(q)
@@ -264,25 +265,50 @@
     return s.execute()
 
   def query_topcrashes(self):
-    total = func.count(Report.c.id)
-    selects = [Report.c.signature, total]
+    crashcount = func.count(Report.c.id)
+    selects = [Report.c.signature, crashcount.label('crashcount')]
     selects.extend(count_platforms())
-    s = select(selects,
-               group_by=[Report.c.signature],
-               order_by=sql.desc(func.count(Report.c.id)),
-               limit=100,
-               engine=create_engine())
-    s.append_whereclause(Report.c.signature != None)
-
+    current = select(selects,
+                     group_by=[Report.c.signature],
+                     order_by=sql.desc(crashcount),
+                     limit=100,
+                     engine=create_engine())
     def FilterToAppend(clause):
-      s.append_whereclause(clause)
-      return s
+      current.append_whereclause(clause)
+      return current
+    current.filter = FilterToAppend
+    current = self.filter(current)
 
-    s.filter = FilterToAppend
+    oldrange = Report.c.date.between(
+      self.getSQLDateStart() - self.getSQLRange(),
+      self.getSQLDateStart())
 
-    s = self.filter(s)
-    return s.execute()
+    old = select([Report.c.signature, crashcount],
+                 group_by=[Report.c.signature],
+                 order_by=sql.desc(crashcount),
+                 limit=200,
+                 whereclause=oldrange,
+                 engine=create_engine())
+    def OldFilterToAppend(clause):
+      old.append_whereclause(clause)
+      return old
+    old.filter = OldFilterToAppend
+    old = self.filter(old, use_dates=False)
 
+    oldranks = {}
+    rank = 1
+    for topcrash in old.execute():
+      oldranks[topcrash.signature] = rank
+      rank += 1
+    
+    topcrashes = [c for c in current.execute()]
+    rank = 1
+    for topcrash in topcrashes:
+      topcrash.rank = rank
+      topcrash.oldrank = oldranks.get(topcrash.signature, None)
+      rank += 1
+    return topcrashes
+
   def __str__(self):
     if self.date is None:
       enddate = 'now'
Index: public/css/style.css
===================================================================
--- public/css/style.css	(revision 242)
+++ public/css/style.css	(working copy)
@@ -73,3 +73,12 @@
 .record th {
   text-align: right;
 }
+
+em.warning {
+  color: red;
+  font-style: normal;
+}
+
+.oldrank {
+  color: #666;
+}
Index: templates/includes/common.html
===================================================================
--- templates/includes/common.html	(revision 242)
+++ templates/includes/common.html	(working copy)
@@ -8,6 +8,7 @@
  <thead>
   <tr>
    <th>Rank</th>
+   <th title="Change">&#8593;&#8595;</th>
    <th>Signature</th>
    <th py:if="len(c.params.platforms) != 1">#</th>
    <th py:for="platform in platformList"
@@ -17,19 +18,32 @@
   </tr>
  </thead>
  <tbody>
-  <?python row=1 ?>
-  <tr py:for="signature in signatures" class="${h.get_row_class(row)}">
-   <td>${str(row)}</td>
+  <tr py:for="signature in signatures" class="${h.get_row_class(signature.rank)}">
    <td>
+    ${signature.rank}
+   </td>
+   <td py:choose="">
+    <em py:when="signature.oldrank is None" class="warning">New</em>
+    <py:when test="signature.rank == signature.oldrank">-</py:when>
+    <em py:when="signature.oldrank - signature.rank &gt;= 5" class="warning">
+     &#8593;&nbsp;<span class="oldrank">${signature.oldrank}</span>
+    </em>
+    <py:when test="signature.rank &lt; signature.oldrank">
+     &#8593;&nbsp;<span class="oldrank">${signature.oldrank}</span>
+    </py:when>
+    <py:otherwise>
+     &#8595;&nbsp;<span class="oldrank">${signature.oldrank}</span>
+    </py:otherwise>
+   </td>
+   <td>
     <a href="${h.url_for('/report/list', signature=signature.signature, **params.getURLDict())}"
        title="View reports with this signature.">${signature.signature}</a>
    </td>
-   <td py:if="len(c.params.platforms) != 1">${signature.count}</td>
+   <td py:if="len(c.params.platforms) != 1">${signature.crashcount}</td>
    <td py:for="platform in platformList"
        py:if="len(c.params.platforms) == 0 or platform in c.params.platforms">
     ${getattr(signature, platform.count_name)}
    </td>
-  <?python row+=1 ?>
   </tr>
  </tbody>
 </table>
