merge
merge

file:a/README.rst -> file:b/README.rst
--- a/README.rst
+++ b/README.rst
@@ -31,14 +31,12 @@
 2. Ensure you development.ini (or similar) contains the info about your Google Analytics account and configuration::
 
       googleanalytics.id = UA-1010101-1
-      googleanalytics.account = Account name (i.e. data.gov.uk, see top level item at https://www.google.com/analytics)
+      googleanalytics.account = Account name (e.g. data.gov.uk, see top level item at https://www.google.com/analytics)
       ga-report.period = monthly
       ga-report.bounce_url = /data
 
    The ga-report.bounce_url specifies the path to use when calculating bounces. For DGU this is /data
    but you may want to set this to /.
-
-   Note that your credentials will be readable by system administrators on your server. Rather than use sensitive account details, it is suggested you give access to the GA account to a new Google account that you create just for this purpose.
 
 3. Set up this extension's database tables using a paster command. (Ensure your CKAN pyenv is still activated, run the command from ``src/ckanext-ga-report``, alter the ``--config`` option to point to your site config file)::
 
@@ -47,6 +45,12 @@
 4. Enable the extension in your CKAN config file by adding it to ``ckan.plugins``::
 
     ckan.plugins = ga-report
+
+Problem shooting
+----------------
+
+* ``(ProgrammingError) relation "ga_url" does not exist``
+  This means that the ``paster initdb`` step has not been run successfully. Refer to the installation instructions for this extension.
 
 
 Authorization
@@ -83,7 +87,7 @@
 Tutorial
 --------
 
-Download some GA data and store it in CKAN's db. (Ensure your CKAN pyenv is still activated, run the command from ``src/ckanext-ga-report``, alter the ``--config`` option to point to your site config file) and specifying the name of your auth file (token.dat by default) from the previous step::
+Download some GA data and store it in CKAN's database. (Ensure your CKAN pyenv is still activated, run the command from ``src/ckanext-ga-report``, alter the ``--config`` option to point to your site config file) and specifying the name of your auth file (token.dat by default) from the previous step::
 
     $ paster loadanalytics token.dat latest --config=../ckan/development.ini
 

--- a/ckanext/ga_report/command.py
+++ b/ckanext/ga_report/command.py
@@ -73,6 +73,14 @@
     max_args = 2
     min_args = 1
 
+    def __init__(self, name):
+        super(LoadAnalytics, self).__init__(name)
+        self.parser.add_option('-d', '--delete-first',
+                               action='store_true',
+                               default=False,
+                               dest='delete_first',
+                               help='Delete data for the period first')
+
     def command(self):
         self._load_config()
 
@@ -83,10 +91,11 @@
             svc = init_service(self.args[0], None)
         except TypeError:
             print ('Have you correctly run the getauthtoken task and '
-                   'specified the correct file here')
+                   'specified the correct token file?')
             return
 
-        downloader = DownloadAnalytics(svc, profile_id=get_profile_id(svc))
+        downloader = DownloadAnalytics(svc, profile_id=get_profile_id(svc),
+                                       delete_first=self.options.delete_first)
 
         time_period = self.args[1] if self.args and len(self.args) > 1 \
             else 'latest'

--- a/ckanext/ga_report/controller.py
+++ b/ckanext/ga_report/controller.py
@@ -135,29 +135,7 @@
             c.social_referrer_totals.append((shorten_name(entry[0]), fill_out_url(entry[0]),'',
                                             entry[1]))
 
-
-        browser_version_re = re.compile("(.*)\((.*)\)")
         for k, v in keys.iteritems():
-
-            def clean_field(key):
-                if k != 'Browser versions':
-                    return key
-                m = browser_version_re.match(key)
-                browser = m.groups()[0].strip()
-                ver = m.groups()[1]
-                parts = ver.split('.')
-                if len(parts) > 1:
-                    if parts[1][0] == '0':
-                        ver = parts[0]
-                    else:
-                        ver = "%s.%s" % (parts[0],parts[1])
-                if browser in ['Safari','Android Browser']:  # Special case complex version nums
-                    ver = parts[0]
-                    if len(ver) > 2:
-                        ver = "%s%sX" % (ver[0], ver[1])
-
-                return "%s (%s)" % (browser, ver,)
-
             q = model.Session.query(GA_Stat).\
                 filter(GA_Stat.stat_name==k)
             if c.month:
@@ -167,7 +145,7 @@
 
             d = collections.defaultdict(int)
             for e in q.all():
-                d[clean_field(e.key)] += int(e.value)
+                d[e.key] += int(e.value)
             entries = []
             for key, val in d.iteritems():
                 entries.append((key,val,))
@@ -184,52 +162,60 @@
         return render('ga_report/site/index.html')
 
 
-class GaPublisherReport(BaseController):
+class GaDatasetReport(BaseController):
     """
-    Displays the pageview and visit count for specific publishers based on
-    the datasets associated with the publisher.
+    Displays the pageview and visit count for datasets
+    with options to filter by publisher and time period.
     """
-    def csv(self, month):
-
-        c.month = month if not month =='all' else ''
+    def publisher_csv(self, month):
+        '''
+        Returns a CSV of each publisher with the total number of dataset
+        views & visits.
+        '''
+        c.month = month if not month == 'all' else ''
         response.headers['Content-Type'] = "text/csv; charset=utf-8"
         response.headers['Content-Disposition'] = str('attachment; filename=publishers_%s.csv' % (month,))
 
         writer = csv.writer(response)
-        writer.writerow(["Publisher", "Views", "Visits", "Period Name"])
-
-        for publisher,view,visit in _get_publishers(None):
+        writer.writerow(["Publisher Title", "Publisher Name", "Views", "Visits", "Period Name"])
+
+        for publisher,view,visit in _get_top_publishers(None):
             writer.writerow([publisher.title.encode('utf-8'),
+                             publisher.name.encode('utf-8'),
                              view,
                              visit,
                              month])
 
-
-
-    def publisher_csv(self, id, month):
-
-        c.month = month if not month =='all' else ''
-        c.publisher = model.Group.get(id)
-        if not c.publisher:
-            abort(404, 'A publisher with that name could not be found')
+    def dataset_csv(self, id='all', month='all'):
+        '''
+        Returns a CSV with the number of views & visits for each dataset.
+
+        :param id: A Publisher ID or None if you want for all
+        :param month: The time period, or 'all'
+        '''
+        c.month = month if not month == 'all' else ''
+        if id != 'all':
+            c.publisher = model.Group.get(id)
+            if not c.publisher:
+                abort(404, 'A publisher with that name could not be found')
 
         packages = self._get_packages(c.publisher)
         response.headers['Content-Type'] = "text/csv; charset=utf-8"
         response.headers['Content-Disposition'] = \
-            str('attachment; filename=%s_%s.csv' % (c.publisher.name, month,))
+            str('attachment; filename=datasets_%s_%s.csv' % (c.publisher_name, month,))
 
         writer = csv.writer(response)
-        writer.writerow(["Publisher", "Views", "Visits", "Period Name"])
+        writer.writerow(["Dataset Title", "Dataset Name", "Views", "Visits", "Period Name"])
 
         for package,view,visit in packages:
             writer.writerow([package.title.encode('utf-8'),
+                             package.name.encode('utf-8'),
                              view,
                              visit,
                              month])
 
-
-
-    def index(self):
+    def publishers(self):
+        '''A list of publishers and the number of views/visits for each'''
 
         # Get the month details by fetching distinct values and determining the
         # month names from the values.
@@ -241,37 +227,46 @@
         if c.month:
             c.month_desc = ''.join([m[1] for m in c.months if m[0]==c.month])
 
-        c.top_publishers = _get_publishers()
+        c.top_publishers = _get_top_publishers()
 
         return render('ga_report/publisher/index.html')
 
-
-    def _get_packages(self, publisher, count=-1):
+    def _get_packages(self, publisher=None, count=-1):
+        '''Returns the datasets in order of visits'''
         if count == -1:
             count = sys.maxint
 
-        top_packages = []
-        q =  model.Session.query(GA_Url).\
-            filter(GA_Url.department_id==publisher.name).\
-            filter(GA_Url.url.like('/dataset/%'))
+        q = model.Session.query(GA_Url)\
+            .filter(GA_Url.url.like('/dataset/%'))
+        if publisher:
+            q = q.filter(GA_Url.department_id==publisher.name)
         if c.month:
             q = q.filter(GA_Url.period_name==c.month)
-        q = q.order_by('ga_url.pageviews::int desc')
-
-        if c.month:
-            for entry in q[:count]:
-                p = model.Package.get(entry.url[len('/dataset/'):])
-                top_packages.append((p,entry.pageviews,entry.visitors))
+        q = q.order_by('ga_url.visitors::int desc')
+
+        if c.month:
+            top_packages = []
+            for entry in q.limit(count):
+                package_name = entry.url[len('/dataset/'):]
+                p = model.Package.get(package_name)
+                if p:
+                    top_packages.append((p, entry.pageviews, entry.visitors))
+                else:
+                    log.warning('Could not find package "%s"', package_name)
         else:
             ds = {}
-            for entry in q.all():
+            for entry in q:
                 if len(ds) >= count:
                     break
-                p = model.Package.get(entry.url[len('/dataset/'):])
-                if not p in ds:
-                    ds[p] = {'views':0, 'visits': 0}
-                ds[p]['views'] = ds[p]['views'] + int(entry.pageviews)
-                ds[p]['visits'] = ds[p]['visits'] + int(entry.visitors)
+                package_name = entry.url[len('/dataset/'):]
+                p = model.Package.get(package_name)
+                if p:
+                    if not p in ds:
+                        ds[p] = {'views': 0, 'visits': 0}
+                    ds[p]['views'] = ds[p]['views'] + int(entry.pageviews)
+                    ds[p]['visits'] = ds[p]['visits'] + int(entry.visitors)
+                else:
+                    log.warning('Could not find package "%s"', package_name)
 
             results = []
             for k, v in ds.iteritems():
@@ -280,13 +275,26 @@
             top_packages = sorted(results, key=operator.itemgetter(1), reverse=True)
         return top_packages
 
-
-    def read(self, id):
+    def read(self):
+        '''
+        Lists the most popular datasets across all publishers
+        '''
+        return self.read_publisher(None)
+
+    def read_publisher(self, id):
+        '''
+        Lists the most popular datasets for a publisher (or across all publishers)
+        '''
         count = 20
 
-        c.publisher = model.Group.get(id)
-        if not c.publisher:
-            abort(404, 'A publisher with that name could not be found')
+        c.publishers = _get_publishers()
+
+        id = request.params.get('publisher', id)
+        if id and id != 'all':
+            c.publisher = model.Group.get(id)
+            if not c.publisher:
+                abort(404, 'A publisher with that name could not be found')
+            c.publisher_name = c.publisher.name
         c.top_packages = [] # package, dataset_views in c.top_packages
 
         # Get the month details by fetching distinct values and determining the
@@ -302,7 +310,7 @@
 
         c.publisher_page_views = 0
         q = model.Session.query(GA_Url).\
-            filter(GA_Url.url=='/publisher/%s' % c.publisher.name)
+            filter(GA_Url.url=='/publisher/%s' % c.publisher_name)
         if c.month:
             entry = q.filter(GA_Url.period_name==c.month).first()
             c.publisher_page_views = entry.pageviews if entry else 0
@@ -314,7 +322,11 @@
 
         return render('ga_report/publisher/read.html')
 
-def _get_publishers(limit=20):
+def _get_top_publishers(limit=20):
+    '''
+    Returns a list of the top 20 publishers by dataset visits.
+    (The number to show can be varied with 'limit')
+    '''
     connection = model.Session.connection()
     q = """
         select department_id, sum(pageviews::int) views, sum(visitors::int) visits
@@ -325,7 +337,7 @@
                 and period_name=%s
         """
     q = q + """
-            group by department_id order by views desc
+            group by department_id order by visits desc
         """
     if limit:
         q = q + " limit %s;" % (limit)
@@ -343,6 +355,18 @@
             top_publishers.append((g, row[1], row[2]))
     return top_publishers
 
+def _get_publishers():
+    '''
+    Returns a list of all publishers. Each item is a tuple:
+      (names, title)
+    '''
+    publishers = []
+    for pub in model.Session.query(model.Group).\
+               filter(model.Group.type=='publisher').\
+               filter(model.Group.state=='active').\
+               order_by(model.Group.name):
+        publishers.append((pub.name, pub.title))
+    return publishers
 
 def _percent(num, total):
     p = 100 * float(num)/float(total)

--- a/ckanext/ga_report/download_analytics.py
+++ b/ckanext/ga_report/download_analytics.py
@@ -11,15 +11,17 @@
 log = logging.getLogger('ckanext.ga-report')
 
 FORMAT_MONTH = '%Y-%m'
+MIN_VIEWS = 50
+MIN_VISITS = 20
 
 class DownloadAnalytics(object):
     '''Downloads and stores analytics info'''
 
-    def __init__(self, service=None, profile_id=None):
+    def __init__(self, service=None, profile_id=None, delete_first=False):
         self.period = config['ga-report.period']
         self.service = service
         self.profile_id = profile_id
-
+        self.delete_first = delete_first
 
     def specific_month(self, date):
         import calendar
@@ -90,6 +92,10 @@
 
     def download_and_store(self, periods):
         for period_name, period_complete_day, start_date, end_date in periods:
+            if self.delete_first:
+                log.info('Deleting existing Analytics for period "%s"',
+                         period_name)
+                ga_model.delete(period_name)
             log.info('Downloading Analytics for period "%s" (%s - %s)',
                      self.get_full_period_name(period_name, period_complete_day),
                      start_date.strftime('%Y %m %d'),
@@ -250,11 +256,13 @@
         data = {}
         for result in result_data:
             data[result[0]] = data.get(result[0], 0) + int(result[2])
+        self._filter_out_long_tail(data, MIN_VIEWS)
         ga_model.update_sitewide_stats(period_name, "Languages", data)
 
         data = {}
         for result in result_data:
             data[result[1]] = data.get(result[1], 0) + int(result[2])
+        self._filter_out_long_tail(data, MIN_VIEWS)
         ga_model.update_sitewide_stats(period_name, "Country", data)
 
 
@@ -269,13 +277,11 @@
                                  max_results=10000,
                                  end_date=end_date).execute()
         result_data = results.get('rows')
-        twitter_links = []
         data = {}
         for result in result_data:
             if not result[0] == '(not set)':
                 data[result[0]] = data.get(result[0], 0) + int(result[2])
-                if result[0] == 'Twitter':
-                    twitter_links.append(result[1])
+        self._filter_out_long_tail(data, 3)
         ga_model.update_sitewide_stats(period_name, "Social sources", data)
 
 
@@ -293,12 +299,14 @@
         data = {}
         for result in result_data:
             data[result[0]] = data.get(result[0], 0) + int(result[2])
+        self._filter_out_long_tail(data, MIN_VIEWS)
         ga_model.update_sitewide_stats(period_name, "Operating Systems", data)
 
         data = {}
         for result in result_data:
-            key = "%s (%s)" % (result[0],result[1])
-            data[key] = result[2]
+            if int(result[2]) >= MIN_VIEWS:
+                key = "%s %s" % (result[0],result[1])
+                data[key] = result[2]
         ga_model.update_sitewide_stats(period_name, "Operating Systems versions", data)
 
 
@@ -313,17 +321,42 @@
                                  max_results=10000,
                                  end_date=end_date).execute()
         result_data = results.get('rows')
+        # e.g. [u'Firefox', u'19.0', u'20']
+
         data = {}
         for result in result_data:
             data[result[0]] = data.get(result[0], 0) + int(result[2])
+        self._filter_out_long_tail(data, MIN_VIEWS)
         ga_model.update_sitewide_stats(period_name, "Browsers", data)
 
         data = {}
         for result in result_data:
-            key = "%s (%s)" % (result[0], result[1])
-            data[key] = result[2]
+            key = "%s %s" % (result[0], self._filter_browser_version(result[0], result[1]))
+            data[key] = data.get(key, 0) + int(result[2])
+        self._filter_out_long_tail(data, MIN_VIEWS)
         ga_model.update_sitewide_stats(period_name, "Browser versions", data)
 
+    @classmethod
+    def _filter_browser_version(cls, browser, version_str):
+        '''
+        Simplifies a browser version string if it is detailed.
+        i.e. groups together Firefox 3.5.1 and 3.5.2 to be just 3.
+        This is helpful when viewing stats and good to protect privacy.
+        '''
+        ver = version_str
+        parts = ver.split('.')
+        if len(parts) > 1:
+            if parts[1][0] == '0':
+                ver = parts[0]
+            else:
+                ver = "%s" % (parts[0])
+        # Special case complex version nums
+        if browser in ['Safari', 'Android Browser']:
+            ver = parts[0]
+            if len(ver) > 2:
+                num_hidden_digits = len(ver) - 2
+                ver = ver[0] + ver[1] + 'X' * num_hidden_digits
+        return ver
 
     def _mobile_stats(self, start_date, end_date, period_name):
         """ Info about mobile devices """
@@ -341,10 +374,23 @@
         data = {}
         for result in result_data:
             data[result[0]] = data.get(result[0], 0) + int(result[2])
+        self._filter_out_long_tail(data, MIN_VIEWS)
         ga_model.update_sitewide_stats(period_name, "Mobile brands", data)
 
         data = {}
         for result in result_data:
             data[result[1]] = data.get(result[1], 0) + int(result[2])
+        self._filter_out_long_tail(data, MIN_VIEWS)
         ga_model.update_sitewide_stats(period_name, "Mobile devices", data)
 
+    @classmethod
+    def _filter_out_long_tail(cls, data, threshold=10):
+        '''
+        Given data which is a frequency distribution, filter out
+        results which are below a threshold count. This is good to protect
+        privacy.
+        '''
+        for key, value in data.items():
+            if value < threshold:
+                del data[key]
+

--- a/ckanext/ga_report/ga_auth.py
+++ b/ckanext/ga_report/ga_auth.py
@@ -53,7 +53,11 @@
         return None
 
     accountName = config.get('googleanalytics.account')
+    if not accountName:
+        raise Exception('googleanalytics.account needs to be configured')
     webPropertyId = config.get('googleanalytics.id')
+    if not webPropertyId:
+        raise Exception('googleanalytics.id needs to be configured')
     for acc in accounts.get('items'):
         if acc.get('name') == accountName:
             accountId = acc.get('id')

--- a/ckanext/ga_report/ga_model.py
+++ b/ckanext/ga_report/ga_model.py
@@ -295,3 +295,15 @@
         for grandchild in go_down_tree(child):
             yield grandchild
 
+def delete(period_name):
+    '''
+    Deletes table data for the specified period, or specify 'all'
+    for all periods.
+    '''
+    for object_type in (GA_Url, GA_Stat, GA_Publisher, GA_ReferralStat):
+        q = model.Session.query(object_type)
+        if period_name != 'all':
+            q = q.filter_by(period_name=period_name)
+        q.delete()
+    model.Session.commit()
+

--- a/ckanext/ga_report/helpers.py
+++ b/ckanext/ga_report/helpers.py
@@ -28,28 +28,37 @@
     return base.render_snippet('ga_report/ga_popular_datasets.html', **ctx)
 
 def single_popular_dataset(top=20):
+    '''Returns a random dataset from the most popular ones.
+
+    :param top: the number of top datasets to select from
+    '''
     import random
 
-    datasets = {}
-    rand = random.randrange(0, top)
-    entry = model.Session.query(GA_Url).\
-        filter(GA_Url.url.like('/dataset/%')).\
-        order_by('ga_url.pageviews::int desc')[rand]
+    top_datasets = model.Session.query(GA_Url).\
+                   filter(GA_Url.url.like('/dataset/%')).\
+                   order_by('ga_url.pageviews::int desc')
+    num_top_datasets = top_datasets.count()
 
-
-    dataset = None
-    while not dataset:
-        dataset = model.Package.get(entry.url[len('/dataset/'):])
-        if dataset and not dataset.state == 'active':
-            dataset = None
-        else:
-            publisher = model.Group.get(entry.department_id)
-
-    ctx = {
+    if num_top_datasets:
+        dataset = None
+        while not dataset:
+            rand = random.randrange(0, min(top, num_top_datasets))
+            ga_url = top_datasets[rand]
+            dataset = model.Package.get(ga_url.url[len('/dataset/'):])
+            if dataset and not dataset.state == 'active':
+                dataset = None
+    else:
+        dataset = model.Session.query(model.Package)\
+                  .filter_by(state='active').first()
+    publisher = dataset.get_groups('publisher')[0]
+    return {
         'dataset': dataset,
         'publisher': publisher
     }
-    return base.render_snippet('ga_report/ga_popular_single.html', **ctx)
+
+def single_popular_dataset_html(top=20):
+    context = single_popular_dataset(top)
+    return base.render_snippet('ga_report/ga_popular_single.html', **context)
 
 
 def most_popular_datasets(publisher, count=20):
@@ -61,7 +70,7 @@
     results = _datasets_for_publisher(publisher, count)
 
     ctx = {
-        'dataset_count': len(datasets),
+        'dataset_count': len(results),
         'datasets': results,
 
         'publisher': publisher

--- a/ckanext/ga_report/plugin.py
+++ b/ckanext/ga_report/plugin.py
@@ -31,26 +31,7 @@
         }
 
     def after_map(self, map):
-        map.connect(
-            '/data/site-usage/publisher',
-            controller='ckanext.ga_report.controller:GaPublisherReport',
-            action='index'
-        )
-        map.connect(
-            '/data/site-usage/publisher_{month}.csv',
-            controller='ckanext.ga_report.controller:GaPublisherReport',
-            action='csv'
-        )
-        map.connect(
-            '/data/site-usage/publisher/{id}_{month}.csv',
-            controller='ckanext.ga_report.controller:GaPublisherReport',
-            action='publisher_csv'
-        )
-        map.connect(
-            '/data/site-usage/publisher/{id}',
-            controller='ckanext.ga_report.controller:GaPublisherReport',
-            action='read'
-        )
+        # GaReport
         map.connect(
             '/data/site-usage',
             controller='ckanext.ga_report.controller:GaReport',
@@ -61,6 +42,33 @@
             controller='ckanext.ga_report.controller:GaReport',
             action='csv'
         )
+
+        # GaDatasetReport
+        map.connect(
+            '/data/site-usage/publisher',
+            controller='ckanext.ga_report.controller:GaDatasetReport',
+            action='publishers'
+        )
+        map.connect(
+            '/data/site-usage/publishers_{month}.csv',
+            controller='ckanext.ga_report.controller:GaDatasetReport',
+            action='publisher_csv'
+        )
+        map.connect(
+            '/data/site-usage/dataset/datasets_{id}_{month}.csv',
+            controller='ckanext.ga_report.controller:GaDatasetReport',
+            action='dataset_csv'
+        )
+        map.connect(
+            '/data/site-usage/dataset',
+            controller='ckanext.ga_report.controller:GaDatasetReport',
+            action='read'
+        )
+        map.connect(
+            '/data/site-usage/dataset/{id}',
+            controller='ckanext.ga_report.controller:GaDatasetReport',
+            action='read_publisher'
+        )
         return map
 
 

--- a/ckanext/ga_report/templates/ga_report/ga_popular_datasets.html
+++ b/ckanext/ga_report/templates/ga_report/ga_popular_datasets.html
@@ -5,7 +5,7 @@
 
 <div class="popular_datasets">
     <div class="pull-right">
-        <a href="${h.url_for(controller='ckanext.ga_report.controller:GaPublisherReport',action='index')}" class="btn btn-primary">More popular datasets »</a>
+        <a href="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='read')}" class="btn btn-primary">More popular datasets »</a>
     </div>
     <h2>Popular datasets</h2>
     <h4>${publisher.title}</h4>

--- a/ckanext/ga_report/templates/ga_report/ga_popular_single.html
+++ b/ckanext/ga_report/templates/ga_report/ga_popular_single.html
@@ -7,11 +7,19 @@
 <div class="popular_datasets">
     <h2>Featured dataset</h2>
 
-    <h3>${h.link_to(dataset.title, h.url_for(controller='package', action='read', id=dataset.name))}</h3>        <div>${h.truncate(dataset.notes, length=200, whole_word=True)}</div>
-    <p></p>
+    <div class="dataset-summary boxed">
+          <a class="dataset-header" href="${h.url_for(controller='package', action='read', id=dataset.name)}">
+            <h3>${dataset.title}</h3>
+          </a>
+          <h4>
+            <strong>Publisher</strong> :
+            <a href="/publisher/${publisher.name}">${publisher.title}</a>
+          </h4>
+          <div>${h.truncate(dataset.notes, length=200, whole_word=True)}</div>
+    </div>
     <div>
-<a href="${h.url_for(controller='ckanext.ga_report.controller:GaPublisherReport',action='index')}" class="btn btn-primary">More popular datasets</a>
-<a href="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" class="btn btn-primary">All usage data</a>
+      <a href="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='read')}" class="btn">Other popular datasets</a>
+      <a href="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" class="btn">All usage data</a>
     </div>
  </div>
 

--- a/ckanext/ga_report/templates/ga_report/ga_util.html
+++ b/ckanext/ga_report/templates/ga_report/ga_util.html
@@ -36,19 +36,18 @@
  </table>
 
 
-<div py:def="usage_nav(active_name,publisher)" id="minornavigation">
+<div py:def="usage_nav(active_name)" id="minornavigation">
     <div id="minornavigation-bg-left">
     <div id="minornavigation-bg-right">
-        <ul class="nav nav-pills">
+      <ul class="nav nav-pills">
         <li py:attrs="{'class': 'active' if active_name=='Site-wide' else None}"><a py:attrs="{'class': 'active' if active_name=='Site-wide' else None}"  href="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}"><img src="/images/icons/page_white.png" height="16px" width="16px" alt="None" class="inline-icon "/> Site-wide</a></li>
         <li py:attrs="{'class': 'active' if active_name=='Publishers' else None}">
-                <a py:attrs="{'class': 'active' if active_name=='Publishers' else None}"  href="${h.url_for(controller='ckanext.ga_report.controller:GaPublisherReport',action='index')}"><img src="/images/icons/page_white.png" height="16px" width="16px" alt="None" class="inline-icon "/> Publishers</a>
+                <a py:attrs="{'class': 'active' if active_name=='Publishers' else None}"  href="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='publishers')}"><img src="/images/icons/page_white.png" height="16px" width="16px" alt="None" class="inline-icon "/> Publishers</a>
         </li>
-        <li py:if="publisher" class="active">
-                <a class="active"  href="${h.url_for(controller='ckanext.ga_report.controller:GaPublisherReport',action='read', id=publisher.name)}"><img src="/images/icons/page_white.png" height="16px" width="16px" alt="None" class="inline-icon "/>${publisher.title}</a>
+        <li py:attrs="{'class': 'active' if active_name=='Datasets' else None}">
+                <a py:attrs="{'class': 'active' if active_name=='Datasets' else None}"  href="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='read')}"><img src="/images/icons/page_white.png" height="16px" width="16px" alt="None" class="inline-icon "/> Datasets</a>
         </li>
-
-        </ul>
+      </ul>
     </div>
     </div>
 </div>

--- /dev/null
+++ b/ckanext/ga_report/templates/ga_report/notes.html
@@ -1,1 +1,17 @@
+<html xmlns:py="http://genshi.edgewall.org/"
+  xmlns:i18n="http://genshi.edgewall.org/i18n"
+  xmlns:xi="http://www.w3.org/2001/XInclude"