#167 Render legends correctly.
#167 Render legends correctly.

--- a/ckanext/ga_report/controller.py
+++ b/ckanext/ga_report/controller.py
@@ -192,24 +192,17 @@
                 filter(GA_Stat.stat_name==k).\
                 order_by(GA_Stat.period_name)
             # Run the query on all months to gather graph data
-            series = {}
-            x_axis = set()
+            graph = {}
             for stat in q:
-                x_val = _get_unix_epoch(stat.period_name)
-                series[ stat.key ] = series.get(stat.key,{})
-                series[ stat.key ][x_val] = float(stat.value)
-                x_axis.add(x_val)
-            # Common x-axis for all series. Exclude this month (incomplete data)
-            x_axis = sorted(list(x_axis))[:-1]
-            # Buffer a rickshaw dataset from the series
-            def create_graph(series_name, series_data):
-                return { 
-                    'name':series_name, 
-                    'data':[ {'x':x,'y':series_data.get(x,0)} for x in x_axis ]
-                    }
-            rickshaw = [ create_graph(name,data) for name, data in series.items() ]
-            rickshaw = sorted(rickshaw,key=lambda x:x['data'][-1]['y'])
-            setattr(c, v+'_graph', json.dumps(rickshaw))
+                graph[ stat.key ] = graph.get(stat.key,{
+                    'name':stat.key, 
+                    'data': []
+                    })
+                graph[ stat.key ]['data'].append({
+                    'x':_get_unix_epoch(stat.period_name),
+                    'y':float(stat.value)
+                    })
+            setattr(c, v+'_graph', json.dumps( _to_rickshaw(graph.values(),percentageMode=True) ))
 
             # Buffer the tabular data
             if c.month:
@@ -253,7 +246,9 @@
         writer = csv.writer(response)
         writer.writerow(["Publisher Title", "Publisher Name", "Views", "Visits", "Period Name"])
 
-        for publisher,view,visit in _get_top_publishers(None):
+        top_publishers, top_publishers_graph = _get_top_publishers(None)
+
+        for publisher,view,visit in top_publishers:
             writer.writerow([publisher.title.encode('utf-8'),
                              publisher.name.encode('utf-8'),
                              view,
@@ -273,7 +268,7 @@
             if not c.publisher:
                 abort(404, 'A publisher with that name could not be found')
 
-        packages, graph_data = self._get_packages(c.publisher)
+        packages = self._get_packages(c.publisher)
         response.headers['Content-Type'] = "text/csv; charset=utf-8"
         response.headers['Content-Disposition'] = \
             str('attachment; filename=datasets_%s_%s.csv' % (c.publisher_name, month,))
@@ -302,7 +297,9 @@
         if c.month:
             c.month_desc = ''.join([m[1] for m in c.months if m[0]==c.month])
 
-        c.top_publishers = _get_top_publishers()
+        c.top_publishers, graph_data = _get_top_publishers()
+        c.top_publishers_graph = json.dumps( _to_rickshaw(graph_data.values()) )
+
         return render('ga_report/publisher/index.html')
 
     def _get_packages(self, publisher=None, count=-1):
@@ -319,7 +316,6 @@
             q = q.filter(GA_Url.department_id==publisher.name)
         q = q.filter(GA_Url.period_name==month)
         q = q.order_by('ga_url.pageviews::int desc')
-        graph_data = []
         top_packages = []
         if count == -1:
             entries = q.all()
@@ -328,7 +324,6 @@
 
         for entry,package in entries:
             if package:
-                graph = []
                 # Downloads ....
                 if have_download_data:
                     dls = model.Session.query(GA_Stat).\
@@ -338,16 +333,14 @@
                         dls = dls.filter(GA_Stat.period_name==month)
                     downloads = 0
                     for x in dls:
-                        graph.append({ 'x': _get_unix_epoch(d.period_name), 'y': int(d.value)})
-                        downloads += int(d.value)
+                        downloads += int(x.value)
                 else:
                     downloads = 'No data'
-                top_packages.append((package, entry.pageviews, entry.visits, downloads, graph_data))
-                graph_data.append({'name':package.title, 'data':graph})
+                top_packages.append((package, entry.pageviews, entry.visits, downloads))
             else:
                 log.warning('Could not find package associated package')
 
-        return top_packages,graph_data
+        return top_packages
 
     def read(self):
         '''
@@ -389,10 +382,76 @@
         entry = q.filter(GA_Url.period_name==c.month).first()
         c.publisher_page_views = entry.pageviews if entry else 0
 
-        c.top_packages, graph_data = self._get_packages(c.publisher, 20)
-        c.graph_data = json.dumps(graph_data)
+        c.top_packages = self._get_packages(c.publisher, 20)
+
+        # Graph query
+        top_package_names = [ x[0].name for x in c.top_packages ]
+        graph_query = model.Session.query(GA_Url,model.Package)\
+            .filter(model.Package.name==GA_Url.package_id)\
+            .filter(GA_Url.url.like('/dataset/%'))\
+            .filter(GA_Url.package_id.in_(top_package_names))
+        graph_data = {}
+        for entry,package in graph_query:
+            if not package: continue
+            if entry.period_name=='All': continue
+            graph_data[package.id] = graph_data.get(package.id,{
+                'name':package.title,
+                'data':[]
+                })
+            graph_data[package.id]['data'].append({
+                'x':_get_unix_epoch(entry.period_name),
+                'y':int(entry.pageviews),
+                })
+                    
+        c.graph_data = json.dumps( _to_rickshaw(graph_data.values()) )
 
         return render('ga_report/publisher/read.html')
+
+def _to_rickshaw(data, percentageMode=False):
+    if data==[]:
+        return data
+    # Create a consistent x-axis
+    num_points = [ len(package['data']) for package in data ]
+    ideal_index = num_points.index( max(num_points) )
+    x_axis = [ point['x'] for point in data[ideal_index]['data'] ]
+    for package in data:
+        xs = [ point['x'] for point in package['data'] ]
+        assert set(xs).issubset( set(x_axis) ), (xs, x_axis)
+        # Zero pad any missing values
+        for x in set(x_axis).difference(set(xs)):
+            package['data'].append( {'x':x, 'y':0} )
+        assert len(package['data'])==len(x_axis), (len(package['data']),len(x_axis),package['data'],x_axis,set(x_axis).difference(set(xs)))
+    if percentageMode:
+        # Transform data into percentage stacks
+        totals = {}
+        for x in x_axis:
+            for package in data:
+                for point in package['data']:
+                    totals[ point['x'] ] = totals.get(point['x'],0) + point['y']
+        # Roll insignificant series into a catch-all
+        THRESHOLD = 0.01
+        significant_series = []
+        for package in data:
+            for point in package['data']:
+                fraction = float(point['y']) / totals[point['x']]
+                if fraction>THRESHOLD and not (package in significant_series):
+                    significant_series.append(package)
+        temp = {}
+        for package in data:
+            if package in significant_series: continue
+            for point in package['data']:
+                temp[point['x']] = temp.get(point['x'],0) + point['y']
+        catch_all = { 'name':'Other','data': [ {'x':x,'y':y} for x,y in temp.items() ] }
+        # Roll insignificant series into one
+        data = significant_series
+        data.append(catch_all)
+    # Sort the points
+    for package in data:
+        package['data'] = sorted( package['data'], key=lambda x:x['x'] )
+        # Strip the latest month's incomplete analytics
+        package['data'] = package['data'][:-1]
+    return data
+
 
 def _get_top_publishers(limit=20):
     '''
@@ -415,11 +474,35 @@
 
     top_publishers = []
     res = connection.execute(q, month)
+    department_ids = []
     for row in res:
         g = model.Group.get(row[0])
         if g:
+            department_ids.append(row[0])
             top_publishers.append((g, row[1], row[2]))
-    return top_publishers
+
+    graph = {}
+    if limit is not None:
+        # Query for a history graph of these publishers
+        q = model.Session.query(
+                GA_Url.department_id, 
+                GA_Url.period_name, 
+                func.sum(cast(GA_Url.pageviews,sqlalchemy.types.INT)))\
+            .filter( GA_Url.department_id.in_(department_ids) )\
+            .filter( GA_Url.period_name!='All' )\
+            .filter( GA_Url.url.like('/dataset/%') )\
+            .filter( GA_Url.package_id!='' )\
+            .group_by( GA_Url.department_id, GA_Url.period_name )
+        for dept_id,period_name,views in q:
+            graph[dept_id] = graph.get( dept_id, {
+                'name' : model.Group.get(dept_id).title,
+                'data' : []
+                })
+            graph[dept_id]['data'].append({
+                'x': _get_unix_epoch(period_name),
+                'y': views
+                })
+    return top_publishers, graph
 
 
 def _get_publishers():

--- a/ckanext/ga_report/public/css/ga_report.css
+++ b/ckanext/ga_report/public/css/ga_report.css
@@ -5,7 +5,7 @@
 }
 .rickshaw_chart_container {
   position: relative;
-  height: 300px;
+  height: 350px;
   margin: 0 auto 20px auto;
 }
 .rickshaw_chart {
@@ -20,9 +20,12 @@
   right: 0;
   top: 0;
   margin-left: 15px;
+  padding: 0 5px;
   background: transparent;
   max-width: 150px;
   overflow: hidden;
+  background: rgba(0,0,0,0.05);
+  border-radius:5px;
 }
 .rickshaw_y_axis {
   position: absolute;
@@ -30,4 +33,9 @@
   bottom: 0;
   width: 40px;
 }
+.rickshaw_legend .label {
+  background: transparent !important;
+  color: #000000 !important;
+  font-weight: normal !important;
+}
 

--- /dev/null
+++ b/ckanext/ga_report/public/scripts/ckanext_ga_reports.js
@@ -1,1 +1,42 @@
 
+var CKAN = CKAN || {};
+CKAN.GA_Reports = {};
+
+CKAN.GA_Reports.render_rickshaw = function( css_name, data, mode, colorscheme ) {
+    var palette = new Rickshaw.Color.Palette( { scheme: colorscheme } );
+    $.each(data, function(i, object) {
+        object['color'] = palette.color();
+    });
+
+    var graphElement =  document.querySelector("#chart_"+css_name);
+
+    var graph = new Rickshaw.Graph( {
+        element: document.querySelector("#chart_"+css_name),
+        renderer: mode,
+        series: data ,
+        height: 328
+    });
+    var x_axis = new Rickshaw.Graph.Axis.Time( { graph: graph } );
+    var y_axis = new Rickshaw.Graph.Axis.Y( {
+        graph: graph,
+        orientation: 'left',
+        tickFormat: Rickshaw.Fixtures.Number.formatKMBT,
+        element: document.getElementById('y_axis_'+css_name),
+    } );
+    var legend = new Rickshaw.Graph.Legend( {
+        element: document.querySelector('#legend_'+css_name),
+        graph: graph
+    } );
+    var hoverDetail = new Rickshaw.Graph.HoverDetail( {
+      graph: graph,
+      formatter: function(series, x, y) {
+        var date = '<span class="date">' + new Date(x * 1000).toUTCString() + '</span>';
+        var swatch = '<span class="detail_swatch" style="background-color: ' + series.color + '"></span>';
+        var content = swatch + series.name + ": " + parseInt(y) + '<br>' + date;
+        return content;
+      }
+    } );
+    graph.render();
+};
+
+

--- a/ckanext/ga_report/templates/ga_report/ga_util.html
+++ b/ckanext/ga_report/templates/ga_report/ga_util.html
@@ -30,36 +30,14 @@
  </table>
 
 
-<div py:def="rickshaw_graph(items_json,id,debug=False)">
+<div py:def="rickshaw_graph(items_json,id,mode='line',colorscheme='munin')">
   <div id="chart_container_$id" class="rickshaw_chart_container">
     <div id="y_axis_$id" class="rickshaw_y_axis"></div>
     <div id="chart_$id" class="rickshaw_chart"></div>
     <div id="legend_$id" class="rickshaw_legend"></div>
     <script type="text/javascript">
       $(function() {
-          var series = ${items_json};
-          <py:if test="debug">console.log(series);</py:if>
-          var palette = new Rickshaw.Color.Palette( { scheme: 'spectrum2001' } );
-          $.each(series, function(i, object) {
-              object['color'] = palette.color();
-          });
-          var graph = new Rickshaw.Graph( {
-              element: document.querySelector("#chart_$id"),
-              renderer: 'line',
-              series: series 
-          });
-          var x_axis = new Rickshaw.Graph.Axis.Time( { graph: graph } );
-          var y_axis = new Rickshaw.Graph.Axis.Y( {
-              graph: graph,
-              orientation: 'left',
-              tickFormat: Rickshaw.Fixtures.Number.formatKMBT,
-              element: document.getElementById('y_axis_$id'),
-          } );
-          var legend = new Rickshaw.Graph.Legend( {
-              element: document.querySelector('#legend_$id'),
-              graph: graph
-          } );
-          graph.render();
+          CKAN.GA_Reports.render_rickshaw('$id', $items_json, '$mode', '$colorscheme');
       });
     </script>
   </div>

--- a/ckanext/ga_report/templates/ga_report/publisher/index.html
+++ b/ckanext/ga_report/templates/ga_report/publisher/index.html
@@ -20,6 +20,7 @@
   <py:def function="optional_head">
     <link rel="stylesheet" type="text/css" href="/scripts/vendor/rickshaw.min.css"/>
     <link rel="stylesheet" type="text/css" href="/css/ga_report.css"/>
+    <script type="text/javascript" src="/scripts/ckanext_ga_reports.js"></script>
     <script type="text/javascript" src="/scripts/vendor/jquery.sparkline.modified.js"></script>
     <script type="text/javascript" src="/scripts/vendor/d3.v2.js"></script>
     <script type="text/javascript" src="/scripts/vendor/d3.layout.min.js"></script>
@@ -42,7 +43,7 @@
           </div>
        </form>
 
-       ${rickshaw_graph('[{name:"test series",data:[{x:1,y:200},{x:3,y:300},{x:5,y:100}]}]','dataset-downloads',debug=True)}
+       ${rickshaw_graph(c.top_publishers_graph,'publishers')}
        <table class="table table-condensed table-bordered table-striped">
 	 <tr>
 	   <th>Publisher</th>

--- a/ckanext/ga_report/templates/ga_report/publisher/read.html
+++ b/ckanext/ga_report/templates/ga_report/publisher/read.html
@@ -10,6 +10,7 @@
   <py:def function="optional_head">
     <link rel="stylesheet" type="text/css" href="/scripts/vendor/rickshaw.min.css"/>
     <link rel="stylesheet" type="text/css" href="/css/ga_report.css"/>
+    <script type="text/javascript" src="/scripts/ckanext_ga_reports.js"></script>
     <script type="text/javascript" src="/scripts/vendor/jquery.sparkline.modified.js"></script>
     <script type="text/javascript" src="/scripts/vendor/d3.v2.js"></script>
     <script type="text/javascript" src="/scripts/vendor/d3.layout.min.js"></script>

--- a/ckanext/ga_report/templates/ga_report/site/index.html
+++ b/ckanext/ga_report/templates/ga_report/site/index.html
@@ -10,6 +10,7 @@
   <py:def function="optional_head">
     <link rel="stylesheet" type="text/css" href="/scripts/vendor/rickshaw.min.css"/>
     <link rel="stylesheet" type="text/css" href="/css/ga_report.css"/>
+    <script type="text/javascript" src="/scripts/ckanext_ga_reports.js"></script>
     <script type="text/javascript" src="/scripts/vendor/jquery.sparkline.modified.js"></script>
     <script type="text/javascript" src="/scripts/vendor/d3.v2.js"></script>
     <script type="text/javascript" src="/scripts/vendor/d3.layout.min.js"></script>
@@ -94,20 +95,20 @@
                </table>
         </div>
          <div class="tab-pane" id="browsers_versions">
-             ${rickshaw_graph(c.browser_versions_graph,'browser-versions')}
+             ${rickshaw_graph(c.browser_versions_graph,'browser-versions',mode='stack')}
              <p>Note: Where a browser has a large number of versions, these have been grouped together.</p>
              ${stat_table(c.browser_versions)}
          </div>
          <div class="tab-pane" id="browsers_names">
-             ${rickshaw_graph(c.browsers_graph,'browsers')}
+             ${rickshaw_graph(c.browsers_graph,'browsers',mode='stack')}
              ${stat_table(c.browsers)}
          </div>
          <div class="tab-pane" id="os">
-             ${rickshaw_graph(c.os_graph,'os')}
+             ${rickshaw_graph(c.os_graph,'os',mode='stack')}
              ${stat_table(c.os)}
          </div>
          <div class="tab-pane" id="os_versions">
-             ${rickshaw_graph(c.os_versions_graph,'os_versions')}
+             ${rickshaw_graph(c.os_versions_graph,'os_versions',mode='stack')}
              ${stat_table(c.os_versions)}
          </div>
         <div class="tab-pane" id="social_referrals_totals">
@@ -115,16 +116,16 @@
             ${social_table(c.social_referrer_totals)}
         </div>
         <div class="tab-pane" id="social_networks">
-             ${rickshaw_graph(c.social_networks_graph, 'social_networks')}
+             ${rickshaw_graph(c.social_networks_graph, 'social_networks',mode='stack')}
             <p>Percentage of visits that were referred from these social networks</p>
              ${stat_table(c.social_networks, 'Visits')}
         </div>
         <div class="tab-pane" id="languages">
-             ${rickshaw_graph(c.languages_graph,'languages')}
+             ${rickshaw_graph(c.languages_graph,'languages',mode='stack')}
              ${stat_table(c.languages)}
         </div>
         <div class="tab-pane" id="country">
-             ${rickshaw_graph(c.country_graph,'country')}
+             ${rickshaw_graph(c.country_graph,'country',mode='stack')}
              ${stat_table(c.country)}
         </div>
        </div>