Changing the content disposition, just in case
[ckanext-ga-report.git] / ckanext / ga_report / controller.py
blob:a/ckanext/ga_report/controller.py -> blob:b/ckanext/ga_report/controller.py
  import re
  import csv
  import sys
import logging import logging
import operator import operator
from ckan.lib.base import BaseController, c, render, request, response import collections
  from ckan.lib.base import (BaseController, c, g, render, request, response, abort)
   
import sqlalchemy import sqlalchemy
from sqlalchemy import func, cast, Integer from sqlalchemy import func, cast, Integer
import ckan.model as model import ckan.model as model
from ga_model import GA_Url, GA_Stat from ga_model import GA_Url, GA_Stat, GA_ReferralStat
   
log = logging.getLogger('ckanext.ga-report') log = logging.getLogger('ckanext.ga-report')
   
   
def _get_month_name(strdate): def _get_month_name(strdate):
import calendar import calendar
from time import strptime from time import strptime
d = strptime(strdate, '%Y-%m') d = strptime(strdate, '%Y-%m')
return '%s %s' % (calendar.month_name[d.tm_mon], d.tm_year) return '%s %s' % (calendar.month_name[d.tm_mon], d.tm_year)
   
   
def _month_details(cls): def _month_details(cls):
months = [] months = []
vals = model.Session.query(cls.period_name).distinct().all() vals = model.Session.query(cls.period_name).distinct().all()
for m in vals: for m in vals:
months.append( (m[0], _get_month_name(m[0]))) months.append( (m[0], _get_month_name(m[0])))
return sorted(months, key=operator.itemgetter(0), reverse=True) return sorted(months, key=operator.itemgetter(0), reverse=True)
   
   
class GaReport(BaseController): class GaReport(BaseController):
   
def csv(self, month): def csv(self, month):
import csv import csv
   
entries = model.Session.query(GA_Stat).\ q = model.Session.query(GA_Stat)
filter(GA_Stat.period_name==month).\ if month != 'all':
order_by('GA_Stat.stat_name, GA_Stat.key').all() q = q.filter(GA_Stat.period_name==month)
  entries = q.order_by('GA_Stat.period_name, GA_Stat.stat_name, GA_Stat.key').all()
#response.headers['Content-disposition'] = 'attachment; filename=dgu_analytics_%s.csv' % (month,)  
response.headers['Content-Type'] = "text/csv; charset=utf-8" response.headers['Content-Type'] = "text/csv; charset=utf-8"
  response.headers['Content-Disposition'] = str('attachment; filename=stats_%s.csv' % (month,))
   
writer = csv.writer(response) writer = csv.writer(response)
writer.writerow(["Period", "Statistic", "Key", "Value"]) writer.writerow(["Period", "Statistic", "Key", "Value"])
   
for entry in entries: for entry in entries:
writer.writerow([entry.period_name.encode('utf-8'), writer.writerow([entry.period_name.encode('utf-8'),
entry.stat_name.encode('utf-8'), entry.stat_name.encode('utf-8'),
entry.key.encode('utf-8'), entry.key.encode('utf-8'),
entry.value.encode('utf-8')]) entry.value.encode('utf-8')])
   
def index(self): def index(self):
   
# Get the month details by fetching distinct values and determining the # Get the month details by fetching distinct values and determining the
# month names from the values. # month names from the values.
c.months = _month_details(GA_Stat) c.months = _month_details(GA_Stat)
   
# Work out which month to show, based on query params of the first item # Work out which month to show, based on query params of the first item
c.month = request.params.get('month', c.months[0][0] if c.months else '') c.month_desc = 'all months'
c.month_desc = ''.join([m[1] for m in c.months if m[0]==c.month]) c.month = request.params.get('month', '')
  if c.month:
entries = model.Session.query(GA_Stat).\ c.month_desc = ''.join([m[1] for m in c.months if m[0]==c.month])
filter(GA_Stat.stat_name=='Totals').\  
filter(GA_Stat.period_name==c.month).\ q = model.Session.query(GA_Stat).\
order_by('ga_stat.key').all() filter(GA_Stat.stat_name=='Totals')
c.global_totals = [(s.key, s.value) for s in entries ] if c.month:
  q = q.filter(GA_Stat.period_name==c.month)
  entries = q.order_by('ga_stat.key').all()
   
  def clean_key(key, val):
  if key in ['Average time on site', 'Pages per visit', 'New visits', 'Bounces']:
  val = "%.2f" % round(float(val), 2)
  if key == 'Average time on site':
  mins, secs = divmod(float(val), 60)
  hours, mins = divmod(mins, 60)
  val = '%02d:%02d:%02d (%s seconds) ' % (hours, mins, secs, val)
  if key in ['New visits','Bounces']:
  val = "%s%%" % val
  if key in ['Total page views', 'Total visits']:
  val = int(val)
   
  return key, val
   
  c.global_totals = []
  if c.month:
  for e in entries:
  key, val = clean_key(e.key, e.value)
  c.global_totals.append((key, val))
  else:
  d = collections.defaultdict(list)
  for e in entries:
  d[e.key].append(float(e.value))
  for k, v in d.iteritems():
  if k in ['Total page views', 'Total visits']:
  v = sum(v)
  else:
  v = float(sum(v))/len(v)
  key, val = clean_key(k,v)
   
  c.global_totals.append((key, val))
  c.global_totals = sorted(c.global_totals, key=operator.itemgetter(0))
   
keys = { keys = {
'Browser versions': 'browsers', 'Browser versions': 'browser_versions',
'Operating Systems versions': 'os', 'Browsers': 'browsers',
  'Operating Systems versions': 'os_versions',
  'Operating Systems': 'os',
'Social sources': 'social_networks', 'Social sources': 'social_networks',
'Languages': 'languages', 'Languages': 'languages',
'Country': 'country' 'Country': 'country'
} }
   
  def shorten_name(name, length=60):
  return (name[:length] + '..') if len(name) > 60 else name
   
  def fill_out_url(url):
  import urlparse
  return urlparse.urljoin(g.site_url, url)
   
  c.social_referrer_totals, c.social_referrers = [], []
  q = model.Session.query(GA_ReferralStat)
  q = q.filter(GA_ReferralStat.period_name==c.month) if c.month else q
  q = q.order_by('ga_referrer.count::int desc')
  for entry in q.all():
  c.social_referrers.append((shorten_name(entry.url), fill_out_url(entry.url),
  entry.source,entry.count))
   
  q = model.Session.query(GA_ReferralStat.url,
  func.sum(GA_ReferralStat.count).label('count'))
  q = q.filter(GA_ReferralStat.period_name==c.month) if c.month else q
  q = q.order_by('count desc').group_by(GA_ReferralStat.url)
  for entry in q.all():
  c.social_referrer_totals.append((shorten_name(entry[0]), fill_out_url(entry[0]),'',
  entry[1]))
   
for k, v in keys.iteritems(): for k, v in keys.iteritems():
entries = model.Session.query(GA_Stat).\ q = model.Session.query(GA_Stat).\
filter(GA_Stat.stat_name==k).\ filter(GA_Stat.stat_name==k)
filter(GA_Stat.period_name==c.month).\ if c.month:
order_by('ga_stat.value::int desc').all() entries = []
setattr(c, v, [(s.key, s.value) for s in entries ]) q = q.filter(GA_Stat.period_name==c.month).\
  order_by('ga_stat.value::int desc')
   
  d = collections.defaultdict(int)
  for e in q.all():
  d[e.key] += int(e.value)
  entries = []
  for key, val in d.iteritems():
  entries.append((key,val,))
  entries = sorted(entries, key=operator.itemgetter(1), reverse=True)
   
  # Get the total for each set of values and then set the value as
  # a percentage of the total
  if k == 'Social sources':
  total = sum([x for n,x in c.global_totals if n == 'Total visits'])
  else:
  total = sum([num for _,num in entries])
  setattr(c, v, [(k,_percent(v,total)) for k,v in entries ])
   
return render('ga_report/site/index.html') return render('ga_report/site/index.html')
   
   
class GaPublisherReport(BaseController): class GaDatasetReport(BaseController):
""" """
Displays the pageview and visit count for specific publishers based on Displays the pageview and visit count for datasets
the datasets associated with the publisher. with options to filter by publisher and time period.
""" """
  def publisher_csv(self, month):
def index(self): '''
  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 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 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=datasets_%s_%s.csv' % (c.publisher_name, month,))
   
  writer = csv.writer(response)
  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 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 # Get the month details by fetching distinct values and determining the
# month names from the values. # month names from the values.
c.months = _month_details(GA_Url) c.months = _month_details(GA_Url)
   
# Work out which month to show, based on query params of the first item # Work out which month to show, based on query params of the first item
c.month = request.params.get('month', c.months[0][0] if c.months else '') c.month = request.params.get('month', '')
c.month_desc = ''.join([m[1] for m in c.months if m[0]==c.month]) c.month_desc = 'all months'
  if c.month:
connection = model.Session.connection() c.month_desc = ''.join([m[1] for m in c.months if m[0]==c.month])
q = """  
select department_id, sum(pageviews::int) views, sum(visitors::int) visits c.top_publishers = _get_top_publishers()
from ga_url  
where department_id <> ''  
and not url like '/publisher/%%'  
and period_name=%s  
group by department_id order by views desc limit 20;  
"""  
c.top_publishers = []  
res = connection.execute(q, c.month)  
for row in res:  
c.top_publishers.append((model.Group.get(row[0]), row[1], row[2]))  
   
return render('ga_report/publisher/index.html') return render('ga_report/publisher/index.html')
   
  def _get_packages(self, publisher=None, count=-1):
def read(self, id): '''Returns the datasets in order of visits'''
c.publisher = model.Group.get(id) if count == -1:
  count = sys.maxint
   
  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.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.limit(count):
  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():
  results.append((k,v['views'],v['visits']))
   
  top_packages = sorted(results, key=operator.itemgetter(1), reverse=True)
  return top_packages
   
  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.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 c.top_packages = [] # package, dataset_views in c.top_packages
   
# Get the month details by fetching distinct values and determining the # Get the month details by fetching distinct values and determining the
# month names from the values. # month names from the values.
c.months = _month_details(GA_Url) c.months = _month_details(GA_Url)
   
# Work out which month to show, based on query params of the first item # Work out which month to show, based on query params of the first item
c.month = request.params.get('month', c.months[0][0] if c.months else '') c.month = request.params.get('month', '')
c.month_desc = ''.join([m[1] for m in c.months if m[0]==c.month]) if not c.month:
  c.month_desc = 'all months'
entry = model.Session.query(GA_Url).\ else:
filter(GA_Url.url=='/publisher/%s' % c.publisher.name).\ c.month_desc = ''.join([m[1] for m in c.months if m[0]==c.month])
filter(GA_Url.period_name==c.month).first()  
c.publisher_page_views = entry.pageviews if entry else 0 c.publisher_page_views = 0
  q = model.Session.query(GA_Url).\
entries = model.Session.query(GA_Url).\ filter(GA_Url.url=='/publisher/%s' % c.publisher_name)
filter(GA_Url.department_id==c.publisher.name).\ if c.month:
filter(GA_Url.period_name==c.month).\ entry = q.filter(GA_Url.period_name==c.month).first()
order_by('ga_url.pageviews::int desc')[:20] c.publisher_page_views = entry.pageviews if entry else 0
for entry in entries: else:
if entry.url.startswith('/dataset/'): for e in q.all():
p = model.Package.get(entry.url[len('/dataset/'):]) c.publisher_page_views = c.publisher_page_views + int(e.pageviews)
c.top_packages.append((p,entry.pageviews,entry.visitors))  
  c.top_packages = self._get_packages(c.publisher, 20)
   
return render('ga_report/publisher/read.html') return render('ga_report/publisher/read.html')
   
  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_