import os | import os |
import logging | import logging |
import datetime | import datetime |
import httplib | import httplib |
import collections | import collections |
import requests | import requests |
import json | import json |
from pylons import config | from pylons import config |
from ga_model import _normalize_url | from ga_model import _normalize_url |
import ga_model | import ga_model |
#from ga_client import GA | #from ga_client import GA |
log = logging.getLogger('ckanext.ga-report') | log = logging.getLogger('ckanext.ga-report') |
FORMAT_MONTH = '%Y-%m' | FORMAT_MONTH = '%Y-%m' |
MIN_VIEWS = 50 | MIN_VIEWS = 50 |
MIN_VISITS = 20 | MIN_VISITS = 20 |
MIN_DOWNLOADS = 10 | MIN_DOWNLOADS = 10 |
class DownloadAnalytics(object): | class DownloadAnalytics(object): |
'''Downloads and stores analytics info''' | '''Downloads and stores analytics info''' |
def __init__(self, service=None, token=None, profile_id=None, delete_first=False, | def __init__(self, service=None, token=None, profile_id=None, delete_first=False, |
skip_url_stats=False): | skip_url_stats=False): |
self.period = config['ga-report.period'] | self.period = config['ga-report.period'] |
self.service = service | self.service = service |
self.profile_id = profile_id | self.profile_id = profile_id |
self.delete_first = delete_first | self.delete_first = delete_first |
self.skip_url_stats = skip_url_stats | self.skip_url_stats = skip_url_stats |
self.token = token | self.token = token |
def specific_month(self, date): | def specific_month(self, date): |
import calendar | import calendar |
first_of_this_month = datetime.datetime(date.year, date.month, 1) | first_of_this_month = datetime.datetime(date.year, date.month, 1) |
_, last_day_of_month = calendar.monthrange(int(date.year), int(date.month)) | _, last_day_of_month = calendar.monthrange(int(date.year), int(date.month)) |
last_of_this_month = datetime.datetime(date.year, date.month, last_day_of_month) | last_of_this_month = datetime.datetime(date.year, date.month, last_day_of_month) |
# if this is the latest month, note that it is only up until today | # if this is the latest month, note that it is only up until today |
now = datetime.datetime.now() | now = datetime.datetime.now() |
if now.year == date.year and now.month == date.month: | if now.year == date.year and now.month == date.month: |
last_day_of_month = now.day | last_day_of_month = now.day |
last_of_this_month = now | last_of_this_month = now |
periods = ((date.strftime(FORMAT_MONTH), | periods = ((date.strftime(FORMAT_MONTH), |
last_day_of_month, | last_day_of_month, |
first_of_this_month, last_of_this_month),) | first_of_this_month, last_of_this_month),) |
self.download_and_store(periods) | self.download_and_store(periods) |
def latest(self): | def latest(self): |
if self.period == 'monthly': | if self.period == 'monthly': |
# from first of this month to today | # from first of this month to today |
now = datetime.datetime.now() | now = datetime.datetime.now() |
first_of_this_month = datetime.datetime(now.year, now.month, 1) | first_of_this_month = datetime.datetime(now.year, now.month, 1) |
periods = ((now.strftime(FORMAT_MONTH), | periods = ((now.strftime(FORMAT_MONTH), |
now.day, | now.day, |
first_of_this_month, now),) | first_of_this_month, now),) |
else: | else: |
raise NotImplementedError | raise NotImplementedError |
self.download_and_store(periods) | self.download_and_store(periods) |
def for_date(self, for_date): | def for_date(self, for_date): |
assert isinstance(since_date, datetime.datetime) | assert isinstance(since_date, datetime.datetime) |
periods = [] # (period_name, period_complete_day, start_date, end_date) | periods = [] # (period_name, period_complete_day, start_date, end_date) |
if self.period == 'monthly': | if self.period == 'monthly': |
first_of_the_months_until_now = [] | first_of_the_months_until_now = [] |
year = for_date.year | year = for_date.year |
month = for_date.month | month = for_date.month |
now = datetime.datetime.now() | now = datetime.datetime.now() |
first_of_this_month = datetime.datetime(now.year, now.month, 1) | first_of_this_month = datetime.datetime(now.year, now.month, 1) |
while True: | while True: |
first_of_the_month = datetime.datetime(year, month, 1) | first_of_the_month = datetime.datetime(year, month, 1) |
if first_of_the_month == first_of_this_month: | if first_of_the_month == first_of_this_month: |
periods.append((now.strftime(FORMAT_MONTH), | periods.append((now.strftime(FORMAT_MONTH), |
now.day, | now.day, |
first_of_this_month, now)) | first_of_this_month, now)) |
break | break |
elif first_of_the_month < first_of_this_month: | elif first_of_the_month < first_of_this_month: |
in_the_next_month = first_of_the_month + datetime.timedelta(40) | in_the_next_month = first_of_the_month + datetime.timedelta(40) |
last_of_the_month = datetime.datetime(in_the_next_month.year, | last_of_the_month = datetime.datetime(in_the_next_month.year, |
in_the_next_month.month, 1)\ | in_the_next_month.month, 1)\ |
- datetime.timedelta(1) | - datetime.timedelta(1) |
periods.append((now.strftime(FORMAT_MONTH), 0, | periods.append((now.strftime(FORMAT_MONTH), 0, |
first_of_the_month, last_of_the_month)) | first_of_the_month, last_of_the_month)) |
else: | else: |
# first_of_the_month has got to the future somehow | # first_of_the_month has got to the future somehow |
break | break |
month += 1 | month += 1 |
if month > 12: | if month > 12: |
year += 1 | year += 1 |
month = 1 | month = 1 |
else: | else: |
raise NotImplementedError | raise NotImplementedError |
self.download_and_store(periods) | self.download_and_store(periods) |
@staticmethod | @staticmethod |
def get_full_period_name(period_name, period_complete_day): | def get_full_period_name(period_name, period_complete_day): |
if period_complete_day: | if period_complete_day: |
return period_name + ' (up to %ith)' % period_complete_day | return period_name + ' (up to %ith)' % period_complete_day |
else: | else: |
return period_name | return period_name |
def download_and_store(self, periods): | def download_and_store(self, periods): |
for period_name, period_complete_day, start_date, end_date in periods: | for period_name, period_complete_day, start_date, end_date in periods: |
log.info('Period "%s" (%s - %s)', | log.info('Period "%s" (%s - %s)', |
self.get_full_period_name(period_name, period_complete_day), | self.get_full_period_name(period_name, period_complete_day), |
start_date.strftime('%Y-%m-%d'), | start_date.strftime('%Y-%m-%d'), |
end_date.strftime('%Y-%m-%d')) | end_date.strftime('%Y-%m-%d')) |
if self.delete_first: | if self.delete_first: |
log.info('Deleting existing Analytics for this period "%s"', | log.info('Deleting existing Analytics for this period "%s"', |
period_name) | period_name) |
ga_model.delete(period_name) | ga_model.delete(period_name) |
if not self.skip_url_stats: | if not self.skip_url_stats: |
# Clean out old url data before storing the new | # Clean out old url data before storing the new |
ga_model.pre_update_url_stats(period_name) | ga_model.pre_update_url_stats(period_name) |
accountName = config.get('googleanalytics.account') | accountName = config.get('googleanalytics.account') |
log.info('Downloading analytics for dataset views') | log.info('Downloading analytics for dataset views') |
data = self.download(start_date, end_date, '~/%s/dataset/[a-z0-9-_]+' % accountName) | data = self.download(start_date, end_date, '~/%s/dataset/[a-z0-9-_]+' % accountName) |
log.info('Storing dataset views (%i rows)', len(data.get('url'))) | log.info('Storing dataset views (%i rows)', len(data.get('url'))) |
self.store(period_name, period_complete_day, data, ) | self.store(period_name, period_complete_day, data, ) |
log.info('Downloading analytics for publisher views') | log.info('Downloading analytics for publisher views') |
data = self.download(start_date, end_date, '~/%s/publisher/[a-z0-9-_]+' % accountName) | data = self.download(start_date, end_date, '~/%s/publisher/[a-z0-9-_]+' % accountName) |
log.info('Storing publisher views (%i rows)', len(data.get('url'))) | log.info('Storing publisher views (%i rows)', len(data.get('url'))) |
self.store(period_name, period_complete_day, data,) | self.store(period_name, period_complete_day, data,) |
# Make sure the All records are correct. | # Make sure the All records are correct. |
ga_model.post_update_url_stats() | ga_model.post_update_url_stats() |
log.info('Associating datasets with their publisher') | log.info('Associating datasets with their publisher') |
ga_model.update_publisher_stats(period_name) # about 30 seconds. | ga_model.update_publisher_stats(period_name) # about 30 seconds. |
log.info('Downloading and storing analytics for site-wide stats') | log.info('Downloading and storing analytics for site-wide stats') |
self.sitewide_stats( period_name, period_complete_day ) | self.sitewide_stats( period_name, period_complete_day ) |
log.info('Downloading and storing analytics for social networks') | log.info('Downloading and storing analytics for social networks') |
self.update_social_info(period_name, start_date, end_date) | self.update_social_info(period_name, start_date, end_date) |
def update_social_info(self, period_name, start_date, end_date): | def update_social_info(self, period_name, start_date, end_date): |
start_date = start_date.strftime('%Y-%m-%d') | start_date = start_date.strftime('%Y-%m-%d') |
end_date = end_date.strftime('%Y-%m-%d') | end_date = end_date.strftime('%Y-%m-%d') |
query = 'ga:hasSocialSourceReferral=~Yes$' | query = 'ga:hasSocialSourceReferral=~Yes$' |
metrics = 'ga:entrances' | metrics = 'ga:entrances' |
sort = '-ga:entrances' | sort = '-ga:entrances' |
try: | try: |
# Because of issues of invalid responses, we are going to make these requests | # Because of issues of invalid responses, we are going to make these requests |
# ourselves. | # ourselves. |
headers = {'authorization': 'Bearer ' + self.token} | headers = {'authorization': 'Bearer ' + self.token} |
args = dict(ids='ga:' + self.profile_id, | args = dict(ids='ga:' + self.profile_id, |
filters=query, | filters=query, |
metrics=metrics, | metrics=metrics, |
sort=sort, | sort=sort, |
dimensions="ga:landingPagePath,ga:socialNetwork", | dimensions="ga:landingPagePath,ga:socialNetwork", |
max_results=10000) | max_results=10000) |
args['start-date'] = start_date | args['start-date'] = start_date |
args['end-date'] = end_date | args['end-date'] = end_date |
results = self._get_json(args) | results = self._get_json(args) |
except Exception, e: | except Exception, e: |
log.exception(e) | log.exception(e) |
results = dict(url=[]) | results = dict(url=[]) |
data = collections.defaultdict(list) | data = collections.defaultdict(list) |
rows = results.get('rows',[]) | rows = results.get('rows',[]) |
for row in rows: | for row in rows: |
url = _normalize_url('http:/' + row[0]) | url = _normalize_url('http:/' + row[0]) |
data[url].append( (row[1], int(row[2]),) ) | data[url].append( (row[1], int(row[2]),) ) |
ga_model.update_social(period_name, data) | ga_model.update_social(period_name, data) |
def download(self, start_date, end_date, path=None): | def download(self, start_date, end_date, path=None): |
'''Get data from GA for a given time period''' | '''Get data from GA for a given time period''' |
start_date = start_date.strftime('%Y-%m-%d') | start_date = start_date.strftime('%Y-%m-%d') |
end_date = end_date.strftime('%Y-%m-%d') | end_date = end_date.strftime('%Y-%m-%d') |
query = 'ga:pagePath=%s$' % path | query = 'ga:pagePath=%s$' % path |
metrics = 'ga:pageviews, ga:visits' | metrics = 'ga:pageviews, ga:visits' |
sort = '-ga:pageviews' | sort = '-ga:pageviews' |
# Supported query params at | # Supported query params at |
# https://developers.google.com/analytics/devguides/reporting/core/v3/reference | # https://developers.google.com/analytics/devguides/reporting/core/v3/reference |
try: | try: |
# Because of issues of invalid responses, we are going to make these requests | |
# ourselves. | |
headers = {'authorization': 'Bearer ' + self.token} | |
args = {} | args = {} |
args["sort"] = "-ga:pageviews" | args["sort"] = "-ga:pageviews" |
args["max-results"] = 100000 | args["max-results"] = 100000 |
args["dimensions"] = "ga:pagePath" | args["dimensions"] = "ga:pagePath" |
args["start-date"] = start_date | args["start-date"] = start_date |
args["end-date"] = end_date | args["end-date"] = end_date |
args["metrics"] = metrics | args["metrics"] = metrics |
args["ids"] = "ga:" + self.profile_id | args["ids"] = "ga:" + self.profile_id |
args["filters"] = query | args["filters"] = query |
args["alt"] = "json" | args["alt"] = "json" |
r = requests.get("https://www.googleapis.com/analytics/v3/data/ga", params=args, headers=headers) | results = self._get_json(args) |
if r.status_code != 200: | |
raise Exception("Request with params: %s failed" % args) | except Exception, e: |
log.exception(e) | |
results = json.loads(r.content) | return dict(url=[]) |
print len(results.keys()) | |
except Exception, e: | |
log.exception(e) | |
#return dict(url=[]) | |
raise e | |
packages = [] | packages = [] |
log.info("There are %d results" % results['totalResults']) | log.info("There are %d results" % results['totalResults']) |
for entry in results.get('rows'): | for entry in results.get('rows'): |
(loc,pageviews,visits) = entry | (loc,pageviews,visits) = entry |
url = _normalize_url('http:/' + loc) # strips off domain e.g. www.data.gov.uk or data.gov.uk | url = _normalize_url('http:/' + loc) # strips off domain e.g. www.data.gov.uk or data.gov.uk |
if not url.startswith('/dataset/') and not url.startswith('/publisher/'): | if not url.startswith('/dataset/') and not url.startswith('/publisher/'): |
# filter out strays like: | # filter out strays like: |
# /data/user/login?came_from=http://data.gov.uk/dataset/os-code-point-open | # /data/user/login?came_from=http://data.gov.uk/dataset/os-code-point-open |
# /403.html?page=/about&from=http://data.gov.uk/publisher/planning-inspectorate | # /403.html?page=/about&from=http://data.gov.uk/publisher/planning-inspectorate |
continue | continue |
packages.append( (url, pageviews, visits,) ) # Temporary hack | packages.append( (url, pageviews, visits,) ) # Temporary hack |
return dict(url=packages) | return dict(url=packages) |
def store(self, period_name, period_complete_day, data): | def store(self, period_name, period_complete_day, data): |
if 'url' in data: | if 'url' in data: |
ga_model.update_url_stats(period_name, period_complete_day, data['url']) | ga_model.update_url_stats(period_name, period_complete_day, data['url']) |
def sitewide_stats(self, period_name, period_complete_day): | def sitewide_stats(self, period_name, period_complete_day): |
import calendar | import calendar |
year, month = period_name.split('-') | year, month = period_name.split('-') |
_, last_day_of_month = calendar.monthrange(int(year), int(month)) | _, last_day_of_month = calendar.monthrange(int(year), int(month)) |
start_date = '%s-01' % period_name | start_date = '%s-01' % period_name |
end_date = '%s-%s' % (period_name, last_day_of_month) | end_date = '%s-%s' % (period_name, last_day_of_month) |
funcs = ['_totals_stats', '_social_stats', '_os_stats', | funcs = ['_totals_stats', '_social_stats', '_os_stats', |
'_locale_stats', '_browser_stats', '_mobile_stats', '_download_stats'] | '_locale_stats', '_browser_stats', '_mobile_stats', '_download_stats'] |
for f in funcs: | for f in funcs: |
log.info('Downloading analytics for %s' % f.split('_')[1]) | log.info('Downloading analytics for %s' % f.split('_')[1]) |
getattr(self, f)(start_date, end_date, period_name, period_complete_day) | getattr(self, f)(start_date, end_date, period_name, period_complete_day) |
def _get_results(result_data, f): | def _get_results(result_data, f): |
data = {} | data = {} |
for result in result_data: | for result in result_data: |
key = f(result) | key = f(result) |
data[key] = data.get(key,0) + result[1] | data[key] = data.get(key,0) + result[1] |
return data | return data |
def _get_json(self, params, prev_fail=False): | def _get_json(self, params, prev_fail=False): |
ga_token_filepath = os.path.expanduser(config.get('googleanalytics.token.filepath', '')) | ga_token_filepath = os.path.expanduser(config.get('googleanalytics.token.filepath', '')) |
if not ga_token_filepath: | if not ga_token_filepath: |
print 'ERROR: In the CKAN config you need to specify the filepath of the ' \ | print 'ERROR: In the CKAN config you need to specify the filepath of the ' \ |
'Google Analytics token file under key: googleanalytics.token.filepath' | 'Google Analytics token file under key: googleanalytics.token.filepath' |
return | return |
try: | log.info("Trying to refresh our OAuth token") |
log.info("Trying to refresh our OAuth token") | try: |
from ga_auth import init_service | |
self.token, svc = init_service(ga_token_filepath, None) | self.token, svc = init_service(ga_token_filepath, None) |
log.info("OAuth token refreshed") | log.info("OAuth token refreshed") |
except Exception, auth_exception: | except Exception, auth_exception: |
log.error("Oauth refresh failed") | log.error("Oauth refresh failed") |
log.exception(auth_exception) | log.exception(auth_exception) |
return | return |
try: | try: |
headers = {'authorization': 'Bearer ' + self.token} | headers = {'authorization': 'Bearer ' + self.token} |
r = requests.get("https://www.googleapis.com/analytics/v3/data/ga", params=params, headers=headers) | r = requests.get("https://www.googleapis.com/analytics/v3/data/ga", params=params, headers=headers) |
if r.status_code != 200: | if r.status_code != 200: |
log.info("STATUS: %s" % (r.status_code,)) | log.info("STATUS: %s" % (r.status_code,)) |
log.info("CONTENT: %s" % (r.content,)) | log.info("CONTENT: %s" % (r.content,)) |
raise Exception("Request with params: %s failed" % params) | raise Exception("Request with params: %s failed" % params) |
return json.loads(r.content) | return json.loads(r.content) |
except Exception, e: | except Exception, e: |
log.exception(e) | log.exception(e) |
return dict(url=[]) | return dict(url=[]) |
def _totals_stats(self, start_date, end_date, period_name, period_complete_day): | def _totals_stats(self, start_date, end_date, period_name, period_complete_day): |
""" Fetches distinct totals, total pageviews etc """ | """ Fetches distinct totals, total pageviews etc """ |
try: | try: |
args = {} | args = {} |
args["max-results"] = 100000 | args["max-results"] = 100000 |
args["start-date"] = start_date | args["start-date"] = start_date |
args["end-date"] = end_date | args["end-date"] = end_date |
args["ids"] = "ga:" + self.profile_id | args["ids"] = "ga:" + self.profile_id |
args["metrics"] = "ga:pageviews" | args["metrics"] = "ga:pageviews" |
args["sort"] = "-ga:pageviews" | args["sort"] = "-ga:pageviews" |
args["alt"] = "json" | args["alt"] = "json" |
results = self._get_json(args) | results = self._get_json(args) |
except Exception, e: | except Exception, e: |
log.exception(e) | log.exception(e) |
results = dict(url=[]) | results = dict(url=[]) |
result_data = results.get('rows') | result_data = results.get('rows') |
ga_model.update_sitewide_stats(period_name, "Totals", {'Total page views': result_data[0][0]}, | ga_model.update_sitewide_stats(period_name, "Totals", {'Total page views': result_data[0][0]}, |
period_complete_day) | period_complete_day) |
try: | try: |
# Because of issues of invalid responses, we are going to make these requests | # Because of issues of invalid responses, we are going to make these requests |
# ourselves. | # ourselves. |
headers = {'authorization': 'Bearer ' + self.token} | headers = {'authorization': 'Bearer ' + self.token} |
args = {} | args = {} |
args["max-results"] = 100000 | args["max-results"] = 100000 |
args["start-date"] = start_date | args["start-date"] = start_date |
args["end-date"] = end_date | args["end-date"] = end_date |
args["ids"] = "ga:" + self.profile_id | args["ids"] = "ga:" + self.profile_id |
args["metrics"] = "ga:pageviewsPerVisit,ga:avgTimeOnSite,ga:percentNewVisits,ga:visits" | args["metrics"] = "ga:pageviewsPerVisit,ga:avgTimeOnSite,ga:percentNewVisits,ga:visits" |
args["alt"] = "json" | args["alt"] = "json" |
results = self._get_json(args) | results = self._get_json(args) |
except Exception, e: | except Exception, e: |
log.exception(e) | log.exception(e) |
results = dict(url=[]) | results = dict(url=[]) |
result_data = results.get('rows') | result_data = results.get('rows') |
data = { | data = { |
'Pages per visit': result_data[0][0], | 'Pages per visit': result_data[0][0], |
'Average time on site': result_data[0][1], | 'Average time on site': result_data[0][1], |
'New visits': result_data[0][2], | 'New visits': result_data[0][2], |
'Total visits': result_data[0][3], | 'Total visits': result_data[0][3], |
} | } |
ga_model.update_sitewide_stats(period_name, "Totals", data, period_complete_day) | ga_model.update_sitewide_stats(period_name, "Totals", data, period_complete_day) |
# Bounces from / or another configurable page. | # Bounces from / or another configurable page. |
path = '/%s%s' % (config.get('googleanalytics.account'), | path = '/%s%s' % (config.get('googleanalytics.account'), |
config.get('ga-report.bounce_url', '/')) | config.get('ga-report.bounce_url', '/')) |
try: | try: |
# Because of issues of invalid responses, we are going to make these requests | # Because of issues of invalid responses, we are going to make these requests |
# ourselves. | # ourselves. |
headers = {'authorization': 'Bearer ' + self.token} | headers = {'authorization': 'Bearer ' + self.token} |
args = {} | args = {} |
args["max-results"] = 100000 | args["max-results"] = 100000 |
args["start-date"] = start_date | args["start-date"] = start_date |
args["end-date"] = end_date | args["end-date"] = end_date |
args["ids"] = "ga:" + self.profile_id | args["ids"] = "ga:" + self.profile_id |
args["filters"] = 'ga:pagePath==%s' % (path,) | args["filters"] = 'ga:pagePath==%s' % (path,) |
args["dimensions"] = 'ga:pagePath' | args["dimensions"] = 'ga:pagePath' |
args["metrics"] = "ga:visitBounceRate" | args["metrics"] = "ga:visitBounceRate" |
args["alt"] = "json" | args["alt"] = "json" |
results = self._get_json(args) | results = self._get_json(args) |
except Exception, e: | except Exception, e: |
log.exception(e) | log.exception(e) |
results = dict(url=[]) | results = dict(url=[]) |
result_data = results.get('rows') | result_data = results.get('rows') |
if not result_data or len(result_data) != 1: | if not result_data or len(result_data) != 1: |
log.error('Could not pinpoint the bounces for path: %s. Got results: %r', | log.error('Could not pinpoint the bounces for path: %s. Got results: %r', |
path, result_data) | path, result_data) |
return | return |
results = result_data[0] | results = result_data[0] |
bounces = float(results[1]) | bounces = float(results[1]) |
# visitBounceRate is already a % | # visitBounceRate is already a % |
log.info('Google reports visitBounceRate as %s', bounces) | log.info('Google reports visitBounceRate as %s', bounces) |
ga_model.update_sitewide_stats(period_name, "Totals", {'Bounce rate (home page)': float(bounces)}, | ga_model.update_sitewide_stats(period_name, "Totals", {'Bounce rate (home page)': float(bounces)}, |
period_complete_day) | period_complete_day) |
def _locale_stats(self, start_date, end_date, period_name, period_complete_day): | def _locale_stats(self, start_date, end_date, period_name, period_complete_day): |
""" Fetches stats about language and country """ | """ Fetches stats about language and country """ |
try: | try: |
# Because of issues of invalid responses, we are going to make these requests | # Because of issues of invalid responses, we are going to make these requests |
# ourselves. | # ourselves. |
headers = {'authorization': 'Bearer ' + self.token} | headers = {'authorization': 'Bearer ' + self.token} |
args = {} | args = {} |
args["max-results"] = 100000 | args["max-results"] = 100000 |
args["start-date"] = start_date | args["start-date"] = start_date |
args["end-date"] = end_date | args["end-date"] = end_date |
args["ids"] = "ga:" + self.profile_id | args["ids"] = "ga:" + self.profile_id |
args["dimensions"] = "ga:language,ga:country" | args["dimensions"] = "ga:language,ga:country" |
args["metrics"] = "ga:pageviews" | args["metrics"] = "ga:pageviews" |
args["sort"] = "-ga:pageviews" | args["sort"] = "-ga:pageviews" |
args["alt"] = "json" | args["alt"] = "json" |
results = self._get_json(args) | results = self._get_json(args) |
except Exception, e: | except Exception, e: |
log.exception(e) | log.exception(e) |
results = dict(url=[]) | results = dict(url=[]) |
result_data = results.get('rows') | result_data = results.get('rows') |
data = {} | data = {} |
for result in result_data: | for result in result_data: |
data[result[0]] = data.get(result[0], 0) + int(result[2]) | data[result[0]] = data.get(result[0], 0) + int(result[2]) |
self._filter_out_long_tail(data, MIN_VIEWS) | self._filter_out_long_tail(data, MIN_VIEWS) |
ga_model.update_sitewide_stats(period_name, "Languages", data, period_complete_day) | ga_model.update_sitewide_stats(period_name, "Languages", data, period_complete_day) |
data = {} | data = {} |
for result in result_data: | for result in result_data: |
data[result[1]] = data.get(result[1], 0) + int(result[2]) | data[result[1]] = data.get(result[1], 0) + int(result[2]) |
self._filter_out_long_tail(data, MIN_VIEWS) | self._filter_out_long_tail(data, MIN_VIEWS) |
ga_model.update_sitewide_stats(period_name, "Country", data, period_complete_day) | ga_model.update_sitewide_stats(period_name, "Country", data, period_complete_day) |
def _download_stats(self, start_date, end_date, period_name, period_complete_day): | def _download_stats(self, start_date, end_date, period_name, period_complete_day): |
""" Fetches stats about data downloads """ | """ Fetches stats about data downloads """ |
import ckan.model as model | import ckan.model as model |
data = {} | data = {} |
try: | try: |
# Because of issues of invalid responses, we are going to make these requests | # Because of issues of invalid responses, we are going to make these requests |
# ourselves. | # ourselves. |
headers = {'authorization': 'Bearer ' + self.token} | headers = {'authorization': 'Bearer ' + self.token} |
args = {} | args = {} |
args["max-results"] = 100000 | args["max-results"] = 100000 |
args["start-date"] = start_date | args["start-date"] = start_date |
args["end-date"] = end_date | args["end-date"] = end_date |
args["ids"] = "ga:" + self.profile_id | args["ids"] = "ga:" + self.profile_id |
args["filters"] = 'ga:eventAction==download' | args["filters"] = 'ga:eventAction==download' |
args["dimensions"] = "ga:eventLabel" | args["dimensions"] = "ga:eventLabel" |
args["metrics"] = "ga:totalEvents" | args["metrics"] = "ga:totalEvents" |
args["alt"] = "json" | args["alt"] = "json" |
results = self._get_json(args) | results = self._get_json(args) |
except Exception, e: | except Exception, e: |
log.exception(e) | log.exception(e) |
results = dict(url=[]) | results = dict(url=[]) |
result_data = results.get('rows') | result_data = results.get('rows') |
if not result_data: | if not result_data: |
# We may not have data for this time period, so we need to bail | # We may not have data for this time period, so we need to bail |
# early. | # early. |
log.info("There is no download data for this time period") | log.info("There is no download data for this time period") |
return | return |
def process_result_data(result_data, cached=False): | def process_result_data(result_data, cached=False): |
progress_total = len(result_data) | progress_total = len(result_data) |
progress_count = 0 | progress_count = 0 |
resources_not_matched = [] | resources_not_matched = [] |
for result in result_data: | for result in result_data: |
progress_count += 1 | progress_count += 1 |
if progress_count % 100 == 0: | if progress_count % 100 == 0: |
log.debug('.. %d/%d done so far', progress_count, progress_total) | log.debug('.. %d/%d done so far', progress_count, progress_total) |
url = result[0].strip() | url = result[0].strip() |
# Get package id associated with the resource that has this URL. | # Get package id associated with the resource that has this URL. |
q = model.Session.query(model.Resource) | q = model.Session.query(model.Resource) |
if cached: | if cached: |
r = q.filter(model.Resource.cache_url.like("%s%%" % url)).first() | r = q.filter(model.Resource.cache_url.like("%s%%" % url)).first() |
else: | else: |
r = q.filter(model.Resource.url.like("%s%%" % url)).first() | r = q.filter(model.Resource.url.like("%s%%" % url)).first() |
package_name = r.resource_group.package.name if r else "" | package_name = r.resource_group.package.name if r else "" |
if package_name: | if package_name: |
data[package_name] = data.get(package_name, 0) + int(result[1]) | data[package_name] = data.get(package_name, 0) + int(result[1]) |
else: | else: |
resources_not_matched.append(url) | resources_not_matched.append(url) |
continue | continue |
if resources_not_matched: | if resources_not_matched: |
log.debug('Could not match %i or %i resource URLs to datasets. e.g. %r', | log.debug('Could not match %i or %i resource URLs to datasets. e.g. %r', |
len(resources_not_matched), progress_total, resources_not_matched[:3]) | len(resources_not_matched), progress_total, resources_not_matched[:3]) |
log.info('Associating downloads of resource URLs with their respective datasets') | log.info('Associating downloads of resource URLs with their respective datasets') |
process_result_data(results.get('rows')) | process_result_data(results.get('rows')) |
try: | try: |
# Because of issues of invalid responses, we are going to make these requests | # Because of issues of invalid responses, we are going to make these requests |
# ourselves. | # ourselves. |
headers = {'authorization': 'Bearer ' + self.token} | headers = {'authorization': 'Bearer ' + self.token} |
args = dict( ids='ga:' + self.profile_id, | args = dict( ids='ga:' + self.profile_id, |
filters='ga:eventAction==download-cache', | filters='ga:eventAction==download-cache', |
metrics='ga:totalEvents', | metrics='ga:totalEvents', |
sort='-ga:totalEvents', | sort='-ga:totalEvents', |
dimensions="ga:eventLabel", | dimensions="ga:eventLabel", |
max_results=10000) | max_results=10000) |
args['start-date'] = start_date | args['start-date'] = start_date |
args['end-date'] = end_date | args['end-date'] = end_date |
results = self._get_json(args) | results = self._get_json(args) |
except Exception, e: | except Exception, e: |
log.exception(e) | log.exception(e) |
results = dict(url=[]) | results = dict(url=[]) |
log.info('Associating downloads of cache resource URLs with their respective datasets') | log.info('Associating downloads of cache resource URLs with their respective datasets') |
process_result_data(results.get('rows'), cached=False) | process_result_data(results.get('rows'), cached=False) |
self._filter_out_long_tail(data, MIN_DOWNLOADS) | self._filter_out_long_tail(data, MIN_DOWNLOADS) |
ga_model.update_sitewide_stats(period_name, "Downloads", data, period_complete_day) | ga_model.update_sitewide_stats(period_name, "Downloads", data, period_complete_day) |
def _social_stats(self, start_date, end_date, period_name, period_complete_day): | def _social_stats(self, start_date, end_date, period_name, period_complete_day): |
""" Finds out which social sites people are referred from """ | """ Finds out which social sites people are referred from """ |
try: | try: |
# Because of issues of invalid responses, we are going to make these requests | # Because of issues of invalid responses, we are going to make these requests |
# ourselves. | # ourselves. |
headers = {'authorization': 'Bearer ' + self.token} | headers = {'authorization': 'Bearer ' + self.token} |
args = dict( ids='ga:' + self.profile_id, | args = dict( ids='ga:' + self.profile_id, |
metrics='ga:pageviews', | metrics='ga:pageviews', |
sort='-ga:pageviews', | sort='-ga:pageviews', |
dimensions="ga:socialNetwork,ga:referralPath", | dimensions="ga:socialNetwork,ga:referralPath", |
max_results=10000) | max_results=10000) |
args['start-date'] = start_date | args['start-date'] = start_date |
args['end-date'] = end_date | args['end-date'] = end_date |
results = self._get_json(args) | results = self._get_json(args) |
except Exception, e: | except Exception, e: |
log.exception(e) | log.exception(e) |
results = dict(url=[]) | results = dict(url=[]) |
result_data = results.get('rows') | result_data = results.get('rows') |
data = {} | data = {} |
for result in result_data: | for result in result_data: |
if not result[0] == '(not set)': | if not result[0] == '(not set)': |
data[result[0]] = data.get(result[0], 0) + int(result[2]) | data[result[0]] = data.get(result[0], 0) + int(result[2]) |
self._filter_out_long_tail(data, 3) | self._filter_out_long_tail(data, 3) |
ga_model.update_sitewide_stats(period_name, "Social sources", data, period_complete_day) | ga_model.update_sitewide_stats(period_name, "Social sources", data, period_complete_day) |
def _os_stats(self, start_date, end_date, period_name, period_complete_day): | def _os_stats(self, start_date, end_date, period_name, period_complete_day): |
""" Operating system stats """ | """ Operating system stats """ |
try: | try: |
# Because of issues of invalid responses, we are going to make these requests | # Because of issues of invalid responses, we are going to make these requests |
# ourselves. | # ourselves. |
headers = {'authorization': 'Bearer ' + self.token} | headers = {'authorization': 'Bearer ' + self.token} |
args = dict( ids='ga:' + self.profile_id, | args = dict( ids='ga:' + self.profile_id, |
metrics='ga:pageviews', | metrics='ga:pageviews', |
sort='-ga:pageviews', | sort='-ga:pageviews', |
dimensions="ga:operatingSystem,ga:operatingSystemVersion", | dimensions="ga:operatingSystem,ga:operatingSystemVersion", |
max_results=10000) | max_results=10000) |
args['start-date'] = start_date | args['start-date'] = start_date |
args['end-date'] = end_date | args['end-date'] = end_date |
results = self._get_json(args) | results = self._get_json(args) |
except Exception, e: | except Exception, e: |
log.exception(e) | log.exception(e) |
results = dict(url=[]) | results = dict(url=[]) |
result_data = results.get('rows') | result_data = results.get('rows') |
data = {} | data = {} |
for result in result_data: | for result in result_data: |
data[result[0]] = data.get(result[0], 0) + int(result[2]) | data[result[0]] = data.get(result[0], 0) + int(result[2]) |
self._filter_out_long_tail(data, MIN_VIEWS) | self._filter_out_long_tail(data, MIN_VIEWS) |
ga_model.update_sitewide_stats(period_name, "Operating Systems", data, period_complete_day) | ga_model.update_sitewide_stats(period_name, "Operating Systems", data, period_complete_day) |
data = {} | data = {} |
for result in result_data: | for result in result_data: |
if int(result[2]) >= MIN_VIEWS: | if int(result[2]) >= MIN_VIEWS: |
key = "%s %s" % (result[0],result[1]) | key = "%s %s" % (result[0],result[1]) |
data[key] = result[2] | data[key] = result[2] |
ga_model.update_sitewide_stats(period_name, "Operating Systems versions", data, period_complete_day) | ga_model.update_sitewide_stats(period_name, "Operating Systems versions", data, period_complete_day) |
def _browser_stats(self, start_date, end_date, period_name, period_complete_day): | def _browser_stats(self, start_date, end_date, period_name, period_complete_day): |
""" Information about browsers and browser versions """ | """ Information about browsers and browser versions """ |
try: | try: |
# Because of issues of invalid responses, we are going to make these requests | # Because of issues of invalid responses, we are going to make these requests |
# ourselves. | # ourselves. |
headers = {'authorization': 'Bearer ' + self.token} | headers = {'authorization': 'Bearer ' + self.token} |
args = dict( ids='ga:' + self.profile_id, | args = dict( ids='ga:' + self.profile_id, |
metrics='ga:pageviews', | metrics='ga:pageviews', |
sort='-ga:pageviews', | sort='-ga:pageviews', |
dimensions="ga:browser,ga:browserVersion", | dimensions="ga:browser,ga:browserVersion", |
max_results=10000) | max_results=10000) |
args['start-date'] = start_date | args['start-date'] = start_date |
args['end-date'] = end_date | args['end-date'] = end_date |
results = self._get_json(args) | results = self._get_json(args) |
except Exception, e: | except Exception, e: |
log.exception(e) | log.exception(e) |
results = dict(url=[]) | results = dict(url=[]) |
result_data = results.get('rows') | result_data = results.get('rows') |
# e.g. [u'Firefox', u'19.0', u'20'] | # e.g. [u'Firefox', u'19.0', u'20'] |
data = {} | data = {} |
for result in result_data: | for result in result_data: |
data[result[0]] = data.get(result[0], 0) + int(result[2]) | data[result[0]] = data.get(result[0], 0) + int(result[2]) |
self._filter_out_long_tail(data, MIN_VIEWS) | self._filter_out_long_tail(data, MIN_VIEWS) |
ga_model.update_sitewide_stats(period_name, "Browsers", data, period_complete_day) | ga_model.update_sitewide_stats(period_name, "Browsers", data, period_complete_day) |
data = {} | data = {} |
for result in result_data: | for result in result_data: |
key = "%s %s" % (result[0], self._filter_browser_version(result[0], result[1])) | key = "%s %s" % (result[0], self._filter_browser_version(result[0], result[1])) |
data[key] = data.get(key, 0) + int(result[2]) | data[key] = data.get(key, 0) + int(result[2]) |
self._filter_out_long_tail(data, MIN_VIEWS) | self._filter_out_long_tail(data, MIN_VIEWS) |
ga_model.update_sitewide_stats(period_name, "Browser versions", data, period_complete_day) | ga_model.update_sitewide_stats(period_name, "Browser versions", data, period_complete_day) |
@classmethod | @classmethod |
def _filter_browser_version(cls, browser, version_str): | def _filter_browser_version(cls, browser, version_str): |
''' | ''' |
Simplifies a browser version string if it is detailed. | 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. | 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. | This is helpful when viewing stats and good to protect privacy. |
''' | ''' |
ver = version_str | ver = version_str |
parts = ver.split('.') | parts = ver.split('.') |
if len(parts) > 1: | if len(parts) > 1: |
if parts[1][0] == '0': | if parts[1][0] == '0': |
ver = parts[0] | ver = parts[0] |
else: | else: |
ver = "%s" % (parts[0]) | ver = "%s" % (parts[0]) |
# Special case complex version nums | # Special case complex version nums |
if browser in ['Safari', 'Android Browser']: | if browser in ['Safari', 'Android Browser']: |
ver = parts[0] | ver = parts[0] |
if len(ver) > 2: | if len(ver) > 2: |
num_hidden_digits = len(ver) - 2 | num_hidden_digits = len(ver) - 2 |
ver = ver[0] + ver[1] + 'X' * num_hidden_digits | ver = ver[0] + ver[1] + 'X' * num_hidden_digits |
return ver | return ver |
def _mobile_stats(self, start_date, end_date, period_name, period_complete_day): | def _mobile_stats(self, start_date, end_date, period_name, period_complete_day): |
""" Info about mobile devices """ | """ Info about mobile devices """ |
try: | try: |
# Because of issues of invalid responses, we are going to make these requests | # Because of issues of invalid responses, we are going to make these requests |
# ourselves. | # ourselves. |
headers = {'authorization': 'Bearer ' + self.token} | headers = {'authorization': 'Bearer ' + self.token} |
args = dict( ids='ga:' + self.profile_id, | args = dict( ids='ga:' + self.profile_id, |
metrics='ga:pageviews', | metrics='ga:pageviews', |
sort='-ga:pageviews', | sort='-ga:pageviews', |
dimensions="ga:mobileDeviceBranding, ga:mobileDeviceInfo", | dimensions="ga:mobileDeviceBranding, ga:mobileDeviceInfo", |
max_results=10000) | max_results=10000) |
args['start-date'] = start_date | args['start-date'] = start_date |
args['end-date'] = end_date | args['end-date'] = end_date |
results = self._get_json(args) | results = self._get_json(args) |
except Exception, e: | except Exception, e: |
log.exception(e) | log.exception(e) |
results = dict(url=[]) | results = dict(url=[]) |
result_data = results.get('rows') | result_data = results.get('rows') |
data = {} | data = {} |
for result in result_data: | for result in result_data: |
data[result[0]] = data.get(result[0], 0) + int(result[2]) | data[result[0]] = data.get(result[0], 0) + int(result[2]) |
self._filter_out_long_tail(data, MIN_VIEWS) | self._filter_out_long_tail(data, MIN_VIEWS) |
ga_model.update_sitewide_stats(period_name, "Mobile brands", data, period_complete_day) | ga_model.update_sitewide_stats(period_name, "Mobile brands", data, period_complete_day) |
data = {} | data = {} |
for result in result_data: | for result in result_data: |
data[result[1]] = data.get(result[1], 0) + int(result[2]) | data[result[1]] = data.get(result[1], 0) + int(result[2]) |
self._filter_out_long_tail(data, MIN_VIEWS) | self._filter_out_long_tail(data, MIN_VIEWS) |
ga_model.update_sitewide_stats(period_name, "Mobile devices", data, period_complete_day) | ga_model.update_sitewide_stats(period_name, "Mobile devices", data, period_complete_day) |
@classmethod | @classmethod |
def _filter_out_long_tail(cls, data, threshold=10): | def _filter_out_long_tail(cls, data, threshold=10): |
''' | ''' |
Given data which is a frequency distribution, filter out | Given data which is a frequency distribution, filter out |
results which are below a threshold count. This is good to protect | results which are below a threshold count. This is good to protect |
privacy. | privacy. |
''' | ''' |
for key, value in data.items(): | for key, value in data.items(): |
if value < threshold: | if value < threshold: |
del data[key] | del data[key] |
.table-condensed td.sparkline-cell { | .table-condensed td.sparkline-cell { |
padding: 1px 0 0 0; | padding: 1px 0 0 0; |
width: 108px; | width: 108px; |
text-align: center; | text-align: center; |
/* Hack to hide the momentary flash of text | /* Hack to hide the momentary flash of text |
* before sparklines are fully rendered */ | * before sparklines are fully rendered */ |
font-size: 1px; | font-size: 1px; |
color: transparent; | color: transparent; |
overflow: hidden; | overflow: hidden; |
} | } |
.rickshaw_chart_container { | .rickshaw_chart_container { |
position: relative; | position: relative; |
height: 350px; | height: 350px; |
margin: 0 auto 20px auto; | margin: 0 auto 20px auto; |
} | } |
.rickshaw_chart { | .rickshaw_chart { |
position: absolute; | position: absolute; |
left: 40px; | left: 40px; |
width: 500px; | width: 500px; |
top: 0; | top: 0; |
bottom: 0; | bottom: 0; |
} | } |
.rickshaw_legend { | .rickshaw_legend { |
background: transparent; | background: transparent; |
width: 100%; | width: 100%; |
padding-top: 4px; | padding-top: 4px; |
} | } |
.rickshaw_y_axis { | .rickshaw_y_axis { |
position: absolute; | position: absolute; |
top: 0; | top: 0; |
bottom: 0; | bottom: 0; |
width: 40px; | width: 40px; |
} | } |
.rickshaw_legend .label { | .rickshaw_legend .label { |
background: transparent !important; | background: transparent !important; |
color: #000000 !important; | color: #000000 !important; |
font-weight: normal !important; | font-weight: normal !important; |
} | } |
.rickshaw_legend .instructions { | .rickshaw_legend .instructions { |
color: #000; | color: #000; |
margin-bottom: 6px; | margin-bottom: 6px; |
} | } |
.rickshaw_legend .line .action { | .rickshaw_legend .line .action { |
display: none; | display: none; |
} | } |
.rickshaw_legend .line .swatch { | .rickshaw_legend .line .swatch { |
display: block; | display: block; |
float: left; | float: left; |
} | } |
.rickshaw_legend .line .label { | .rickshaw_legend .line .label { |
display: block; | display: block; |
white-space: normal; | white-space: normal; |
float: left; | float: left; |
width: 200px; | width: 200px; |
} | } |
.rickshaw_legend .line .label:hover { | .rickshaw_legend .line .label:hover { |
text-decoration: underline; | text-decoration: underline; |
} | } |
.ga-reports-table .td-numeric { | .ga-reports-table .td-numeric { |
text-align: center; | text-align: center; |
} | } |
.ga-reports-heading { | .ga-reports-heading { |
padding-right: 10px; | padding-right: 10px; |
margin-top: 4px; | margin-top: 4px; |
float: left; | float: left; |
} | } |
.tab-content { | |
padding-top: 12px; | |
} | |
<html | <html |
xmlns="http://www.w3.org/1999/xhtml" | xmlns="http://www.w3.org/1999/xhtml" |
xmlns:i18n="http://genshi.edgewall.org/i18n" | xmlns:i18n="http://genshi.edgewall.org/i18n" |
xmlns:py="http://genshi.edgewall.org/" | xmlns:py="http://genshi.edgewall.org/" |
xmlns:xi="http://www.w3.org/2001/XInclude" | xmlns:xi="http://www.w3.org/2001/XInclude" |
py:strip="" | py:strip="" |
> | > |
<select name="month" py:def="month_selector(current_month, months, day)"> | <select name="month" py:def="month_selector(current_month, months, day)"> |
<option value='' py:attrs="{'selected': 'selected' if not current_month else None}">All months</option> | <option value='' py:attrs="{'selected': 'selected' if not current_month else None}">All months</option> |
<py:for each="(iso_code,string_name) in months"> | <py:for each="(iso_code,string_name) in months"> |
<option value='${iso_code}' py:attrs="{'selected': 'selected' if current_month == iso_code else None}">${h.month_option_title(iso_code,months,day)}</option> | <option value='${iso_code}' py:attrs="{'selected': 'selected' if current_month == iso_code else None}">${h.month_option_title(iso_code,months,day)}</option> |
</py:for> | </py:for> |
</select> | </select> |
<table py:def="social_table(items, with_source=False)" class="ga-reports-table table table-condensed table-bordered table-striped"> | <table py:def="social_table(items, with_source=False)" class="ga-reports-table table table-condensed table-bordered table-striped"> |
<tr> | <tr> |
<th>Name</th> | <th>Name</th> |
<th py:if="with_source">Source</th> | <th py:if="with_source">Source</th> |
<th>Visits</th> | <th>Visits</th> |
</tr> | </tr> |
<py:for each="name, url, source, count in items"> | <py:for each="name, url, source, count in items"> |
<tr> | <tr> |
<td><a href="${url}">${name}</a></td> | <td><a href="${url}">${name}</a></td> |
<td py:if="with_source">${source}</td> | <td py:if="with_source">${source}</td> |
<td class="td-numeric">${count}</td> | <td class="td-numeric">${count}</td> |
</tr> | </tr> |
</py:for> | </py:for> |
</table> | </table> |
<div py:def="rickshaw_graph(items_json,id,mode='line',colorscheme='munin')"> | <div py:def="rickshaw_graph(items_json,id,mode='line',colorscheme='munin')"> |
<div id="chart_container_$id" class="rickshaw_chart_container"> | <div id="chart_container_$id" class="rickshaw_chart_container"> |
<div id="y_axis_$id" class="rickshaw_y_axis"></div> | <div id="y_axis_$id" class="rickshaw_y_axis"></div> |
<div id="chart_$id" class="rickshaw_chart"></div> | <div id="chart_$id" class="rickshaw_chart"></div> |
<script type="text/javascript"> | <script type="text/javascript"> |
$(function() { | $(function() { |
var items = $items_json; | var items = $items_json; |
CKAN.GA_Reports.render_rickshaw('$id', items, '$mode', '$colorscheme'); | CKAN.GA_Reports.render_rickshaw('$id', items, '$mode', '$colorscheme'); |
}); | }); |
</script> | </script> |
</div> | </div> |
</div> | </div> |
<table py:def="stat_table(items, title='Views')" class="ga-reports-table table table-condensed table-bordered table-striped"> | <table py:def="stat_table(items, title='Views')" class="ga-reports-table table table-condensed table-bordered table-striped"> |
<tr> | <tr> |
<th>Name</th> | <th>Name</th> |
<th class="td-numeric">% ${title}</th> | <th class="td-numeric">% ${title}</th> |
</tr> | </tr> |
<py:for each="name, value in items"> | <py:for each="name, value in items"> |
<tr> | <tr> |
<td>${name}</td> | <td>${name}</td> |
<td class="td-numeric">${value}</td> | <td class="td-numeric">${value}</td> |
</tr> | </tr> |
</py:for> | </py:for> |
</table> | </table> |
<py:def function="ga_footer()"> | |
<div py:def="usage_nav(active_name)"> | <div class="panel panel-default"> |
<span class="subheading"> | <div class="panel-heading"><strong>Notes</strong></div> |
<img src="/images/arrow-down-right.png" /> | <div class="panel-body"> |
<a class="${if_(active_name=='Site-wide','active')}" href="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}${if_(c.month,'?month='+c.month)}">Site-wide</a> | |
<span class="divider">|</span> | |
<a class="${if_(active_name=='Publishers','active')}" href="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='publishers')}${if_(c.month,'?month='+c.month)}">Publishers</a> | |
<span class="divider">|</span> | |
<a class="${if_(active_name=='Datasets','active')}" href="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='read')}${if_(c.month,'?month='+c.month)}">Datasets</a> | |
</span> | |
</div> | |
<div py:def="ga_sidebar(download_link)"> | |
<div class="boxed"> | |
<div class="widget-container widget_text"> | |
<h4>Download</h4> | |
<p><center> | |
<a class="btn button btn-primary" href="${download_link}">Download as CSV</a></center> | |
</p> | |
</div> | |
<div class="widget-container widget_text"> | |
<h4>Graph Legend</h4> | |
<div id="graph-legend-container"> | |
<div style="display: none;" id="legend_none">(No graph is loaded)</div> | |
</div> | |
</div> | |
<div class="widget-container widget_text"> | |
<h4>Notes</h4> | |
<ul> | <ul> |
<li>"Views" is the number of times a page was loaded in users' browsers.</li> | <li>"Views" is the number of times a page was loaded in users' browsers.</li> |
<li>"Downloads" is the number of times a user has clicked to download either an original or cached resource for a particular dataset. Download information is only available from 2nd December 2012; 'No data' is shown for records before that date.</li> | <li>"Downloads" is the number of times a user has clicked to download either an original or cached resource for a particular dataset. Download information is only available from 2nd December 2012; 'No data' is shown for records before that date.</li> |
<li>These usage statistics are confined to users with javascript enabled, which excludes web crawlers and API calls.</li> | <li>These usage statistics are confined to users with javascript enabled, which excludes web crawlers and API calls.</li> |
<li>The results are not shown when the number of views/visits is tiny. Where these relate to site pages, results are available in full in the CSV download. Where these relate to users' web browser information, results are not disclosed, for privacy reasons.</li> | <li>The results are not shown when the number of views/visits is tiny. Where these relate to site pages, results are available in full in the CSV download. Where these relate to users' web browser information, results are not disclosed, for privacy reasons.</li> |
</ul> | </ul> |
</div> | </div> |
</div> | </div> |
</div> | </py:def> |
</html> | </html> |
<html xmlns:py="http://genshi.edgewall.org/" | <html xmlns:py="http://genshi.edgewall.org/" |
xmlns:i18n="http://genshi.edgewall.org/i18n" | xmlns:i18n="http://genshi.edgewall.org/i18n" |
xmlns:xi="http://www.w3.org/2001/XInclude" | xmlns:xi="http://www.w3.org/2001/XInclude" |
py:strip=""> | py:strip=""> |
<xi:include href="../ga_util.html" /> | <xi:include href="../ga_util.html" /> |
<py:def function="page_title">Usage by Publisher</py:def> | <py:def function="page_title">Usage by Publisher</py:def> |
<py:match path="primarysidebar"> | |
${ga_sidebar(download_link=h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='publisher_csv',month=c.month or 'all'))} | |
</py:match> | |
<py:def function="optional_head"> | <py:def function="optional_head"> |
<link rel="stylesheet" type="text/css" href="/scripts/vendor/rickshaw.min.css"/> | <link rel="stylesheet" type="text/css" href="/scripts/vendor/rickshaw.min.css"/> |
<link rel="stylesheet" type="text/css" href="/css/ga_report.css?1"/> | <link rel="stylesheet" type="text/css" href="/css/ga_report.css?1"/> |
<script type="text/javascript" src="/scripts/modernizr-2.6.2.custom.js"></script> | <script type="text/javascript" src="/scripts/modernizr-2.6.2.custom.js"></script> |
<script type="text/javascript" src="/scripts/ckanext_ga_reports.js?1"></script> | <script type="text/javascript" src="/scripts/ckanext_ga_reports.js?1"></script> |
<script type="text/javascript" src="/scripts/vendor/jquery.sparkline.modified.js"></script> | <script type="text/javascript" src="/scripts/vendor/jquery.sparkline.modified.js"></script> |
<script type="text/javascript" src="/scripts/rickshaw_ie7_shim.js"></script> | <script type="text/javascript" src="/scripts/rickshaw_ie7_shim.js"></script> |
<script type="text/javascript" src="/scripts/vendor/d3.v2.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> | <script type="text/javascript" src="/scripts/vendor/d3.layout.min.js"></script> |
<script type="text/javascript" src="/scripts/vendor/rickshaw.min.js"></script> | <script type="text/javascript" src="/scripts/vendor/rickshaw.min.js"></script> |
</py:def> | </py:def> |
<py:def function="page_heading">Site Usage ${usage_nav('Publishers')}</py:def> | |
<py:match path="breadcrumbs"> | |
<li><a href="/data/site-usage">Site Analytics</a></li> | |
<li><a href="/data/site-usage/publisher">Publishers</a></li> | |
</py:match> | |
<div py:match="content"> | <div py:match="content"> |
<py:with vars="download_link=h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='publisher_csv',month=c.month or 'all')"> | |
<a class="btn button btn-primary btn-sm pull-right" href="${download_link}"><i class="icon-download"></i> Download as CSV</a> | |
</py:with> | |
<h1>Site Usage</h1> | |
<div class="row" style="background: #fff;"> | |
<div class="boxed"> | <div class="col-md-4"> |
<div class="whitebox"> | |
${rickshaw_graph(c.top_publishers_graph,'publishers')} | <strong>Graph Legend</strong> |
<div id="graph-legend-container"> | |
<div style="display: none;" id="legend_none">(No graph is loaded)</div> | |
</div> | |
</div> | |
</div> | |
<div class="col-md-8"> | |
<div class="whitebox"> | |
<strong>Publishers</strong> | |
${rickshaw_graph(c.top_publishers_graph,'publishers')} | |
</div> | |
</div> | |
</div> | |
<hr/> | <hr/> |
<form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='publishers')}" method="get"> | <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='publishers')}" method="get"> |
<div class="controls"> | <div class="controls"> |
<h4 class="ga-reports-heading">Statistics for</h4> | <h2 class="ga-reports-heading">Statistics for</h2> |
${month_selector(c.month, c.months, c.day)} | ${month_selector(c.month, c.months, c.day)} |
</div> | </div> |
</form> | </form> |
<table class="ga-reports-table table table-condensed table-bordered table-striped"> | <table class="ga-reports-table table table-condensed table-bordered table-striped"> |
<tr> | <tr> |
<th>Publisher</th> | <th>Publisher</th> |
<th class="td-numeric">Dataset Views</th> | <th class="td-numeric">Dataset Views</th> |
</tr> | </tr> |
<py:for each="publisher, views, visits in c.top_publishers"> | <py:for each="publisher, views, visits in c.top_publishers"> |
<tr> | <tr> |
<td> | <td> |
${h.link_to(publisher.title, h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport', action='read_publisher', id=publisher.name) + (("?month=" + c.month) if c.month else ''))} | ${h.link_to(publisher.title, h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport', action='read_publisher', id=publisher.name) + (("?month=" + c.month) if c.month else ''))} |
</td> | </td> |
<td class="td-numeric">${views}</td> | <td class="td-numeric">${views}</td> |
</tr> | </tr> |
</py:for> | </py:for> |
</table> | </table> |
</div><!--/boxed--> | |
</div><!--/content--> | </div><!--/content--> |
<py:def function="optional_footer"> | <py:def function="optional_footer"> |
<script type="text/javascript"> | <script type="text/javascript"> |
$(function() { | $(function() { |
CKAN.GA_Reports.bind_month_selector(); | CKAN.GA_Reports.bind_month_selector(); |
}); | }); |
</script> | </script> |
</py:def> | </py:def> |
<xi:include href="../../layout.html" /> | <xi:include href="../../layout.html" /> |
</html> | </html> |
<html xmlns:py="http://genshi.edgewall.org/" | <html xmlns:py="http://genshi.edgewall.org/" |
xmlns:i18n="http://genshi.edgewall.org/i18n" | xmlns:i18n="http://genshi.edgewall.org/i18n" |
xmlns:xi="http://www.w3.org/2001/XInclude" | xmlns:xi="http://www.w3.org/2001/XInclude" |
py:strip=""> | py:strip=""> |
<xi:include href="../ga_util.html" /> | <xi:include href="../ga_util.html" /> |
<py:def function="page_title">Usage by Dataset</py:def> | <py:def function="page_title">Usage by Dataset</py:def> |
<py:def function="optional_head"> | <py:def function="optional_head"> |
<link rel="stylesheet" type="text/css" href="/scripts/vendor/rickshaw.min.css"/> | <link rel="stylesheet" type="text/css" href="/scripts/vendor/rickshaw.min.css"/> |
<link rel="stylesheet" type="text/css" href="/css/ga_report.css?1"/> | <link rel="stylesheet" type="text/css" href="/css/ga_report.css?1"/> |
<script type="text/javascript" src="/scripts/modernizr-2.6.2.custom.js"></script> | <script type="text/javascript" src="/scripts/modernizr-2.6.2.custom.js"></script> |
<script type="text/javascript" src="/scripts/ckanext_ga_reports.js?1"></script> | <script type="text/javascript" src="/scripts/ckanext_ga_reports.js?1"></script> |
<script type="text/javascript" src="/scripts/vendor/jquery.sparkline.modified.js"></script> | <script type="text/javascript" src="/scripts/vendor/jquery.sparkline.modified.js"></script> |
<script type="text/javascript" src="/scripts/rickshaw_ie7_shim.js"></script> | <script type="text/javascript" src="/scripts/rickshaw_ie7_shim.js"></script> |
<script type="text/javascript" src="/scripts/vendor/d3.v2.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> | <script type="text/javascript" src="/scripts/vendor/d3.layout.min.js"></script> |
<script type="text/javascript" src="/scripts/vendor/rickshaw.min.js"></script> | <script type="text/javascript" src="/scripts/vendor/rickshaw.min.js"></script> |
</py:def> | </py:def> |
<py:match path="primarysidebar"> | |
${ga_sidebar(download_link=h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='dataset_csv',id=c.publisher_name or 'all',month=c.month or 'all'))} | <py:match path="breadcrumbs"> |
<li><a href="/data/site-usage">Site Analytics</a></li> | |
<py:if test="c.publisher"> | |
<li><a href="/data/site-usage/publisher">Publishers</a></li> | |
<li py:if="c.publisher"><a href="/data/site-usage/publisher/${c.publisher.name}">${c.publisher.title}</a></li> | |
</py:if> | |
<py:if test="not c.publisher"> | |
<li><a href="${request.url}">Usage By Dataset</a></li> | |
</py:if> | |
</py:match> | </py:match> |
<py:def function="page_heading">Site Usage ${usage_nav('Datasets')}</py:def> | |
<div py:match="content"> | <div py:match="content"> |
<div class="boxed"> | <py:with vars="download_link=h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='dataset_csv',id=c.publisher_name or 'all',month=c.month or 'all')"> |
<h3 py:if="c.publisher"><a href="${h.url_for(controller='ckanext.dgu.controllers.publisher:PublisherController',action='read',id=c.publisher.name)}">${c.publisher.title}</a></h3> | <a class="btn button btn-primary btn-sm pull-right" href="${download_link}"><i class="icon-download"></i> Download as CSV</a> |
</py:with> | |
<h1>Site Usage</h1> | |
<py:if test="c.graph_data"> | <div class="row" style="background: #fff;"> |
${rickshaw_graph(c.graph_data,'dataset-downloads',debug=True)} | <div class="col-md-4"> |
</py:if> | <div class="whitebox"> |
<form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='read')}" method="get"> | <strong>Graph Legend</strong> |
<div class="controls"> | <div id="graph-legend-container"> |
<div style="display: none;" id="legend_none">(No graph is loaded)</div> | |
</div> | |
</div> | |
</div> | |
<div class="col-md-8"> | |
<div class="whitebox"> | |
<strong py:if="c.publisher">Datasets From <a href="${h.url_for(controller='ckanext.dgu.controllers.publisher:PublisherController',action='read',id=c.publisher.name)}">${c.publisher.title}</a></strong> | |
<strong py:if="not c.publisher">All Datasets</strong> | |
<py:if test="c.graph_data"> | |
${rickshaw_graph(c.graph_data,'dataset-downloads',debug=True)} | |
</py:if> | |
</div> | |
</div> | |
</div> | |
<hr/> | |
<py:if test="c.month"> | |
<h4>Statistics for ${h.month_option_title(c.month,c.months,c.day)}:</h4> | |
</py:if> | |
<py:if test="not c.month"> | |
<h2>Statistics for all months</h2> | |
</py:if> | |
<form style="margin-bottom:10px;" class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaDatasetReport',action='read')}" method="get"> | |
<div class="controls"> | |
${month_selector(c.month, c.months, c.day)} | ${month_selector(c.month, c.months, c.day)} |
<select name="publisher"> | <select name="publisher"> |
<option value='' py:attrs="{'selected': 'selected' if not c.publisher else None}">All publishers</option> | <option value='' py:attrs="{'selected': 'selected' if not c.publisher else None}">All publishers</option> |
<py:for each="val,desc in c.publishers"> | <py:for each="val,desc in c.publishers"> |
<option value='${val}' py:attrs="{'selected': 'selected' if c.publisher_name == val else None}">${desc}</option> | <option value='${val}' py:attrs="{'selected': 'selected' if c.publisher_name == val else None}">${desc}</option> |
</py:for> | </py:for> |
</select> | </select> |
<input class="btn button btn-primary" type='submit' value="Update"/> | <input class="btn button btn-primary btn-xs" type='submit' value="Update"/> |
</div> | </div> |
</form> | </form> |
<py:if test="c.month"> | <div class="alert alert-info" py:if="not c.top_packages">No page views in this period.</div> |
<h4>Statistics for ${h.month_option_title(c.month,c.months,c.day)}:</h4> | <py:if test="c.top_packages"> |
</py:if> | <table class="ga-reports-table table table-condensed table-bordered"> |
<py:if test="not c.month"> | <tr> |
<h4>Statistics for all months:</h4> | <th>Dataset</th> |
</py:if> | <th>Views</th> |
<div class="alert alert-info" py:if="not c.top_packages">No page views in this period.</div> | <th>Downloads</th> |
<py:if test="c.top_packages"> | </tr> |
<table class="ga-reports-table table table-condensed table-bordered table-striped"> | <py:for each="package, views, visits,downloads in c.top_packages"> |
<tr> | <tr> |
<th>Dataset</th> | <td> |
<th>Views</th> | ${h.link_to(package.title or package.name, h.url_for(controller='package', action='read', id=package.name))} |
<th>Downloads</th> | </td> |
</tr> | <td class="td-numeric">${views}</td> |
<py:for each="package, views, visits,downloads in c.top_packages"> | <td class="td-numeric">${downloads}</td> |
<tr> | </tr> |
<td> | </py:for> |
${h.link_to(package.title or package.name, h.url_for(controller='package', action='read', id=package.name))} | </table> |
</td> | </py:if> |
<td class="td-numeric">${views}</td> | |
<td class="td-numeric">${downloads}</td> | |
</tr> | |
</py:for> | |
</table> | |
</py:if> | |
</div> | ${ga_footer()} |
</div> | </div> |
<xi:include href="../../layout.html" /> | <xi:include href="../../layout.html" /> |
</html> | </html> |
<html xmlns:py="http://genshi.edgewall.org/" | <html xmlns:py="http://genshi.edgewall.org/" |
xmlns:i18n="http://genshi.edgewall.org/i18n" | xmlns:i18n="http://genshi.edgewall.org/i18n" |
xmlns:xi="http://www.w3.org/2001/XInclude" | xmlns:xi="http://www.w3.org/2001/XInclude" |
py:strip=""> | py:strip=""> |
<xi:include href="../ga_util.html" /> | <xi:include href="../ga_util.html" /> |
<py:def function="page_title">Downloads</py:def> | <py:def function="page_title">Downloads</py:def> |
<py:match path="primarysidebar"> | <py:def path="sidebar"> |
${ga_sidebar(download_link=h.url_for(controller='ckanext.ga_report.controller:GaReport',action='csv_downloads',month=c.month or 'all'))} | ${ga_sidebar(download_link=h.url_for(controller='ckanext.ga_report.controller:GaReport',action='csv_downloads',month=c.month or 'all'))} |
</py:match> | </py:def> |
<py:def function="page_heading">Downloads ${usage_nav('Downloads')}</py:def> | <py:def function="page_heading">Downloads ${usage_nav('Downloads')}</py:def> |
<div py:match="content"> | <div py:match="content"> |
<div class="boxed"> | <div class="boxed"> |
<form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='downloads')}" method="get"> | <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='downloads')}" method="get"> |
<div class="controls"> | <div class="controls"> |
${month_selector(c.month, c.months, c.day)} | ${month_selector(c.month, c.months, c.day)} |
<input class="btn button btn-primary" type='submit' value="Update"/> | <input class="btn button btn-primary" type='submit' value="Update"/> |
</div> | </div> |
</form> | </form> |
<py:if test="c.downloads"> | <py:if test="c.downloads"> |
${downloads_table(c.downloads)} | ${downloads_table(c.downloads)} |
</py:if> | </py:if> |
<py:if test="not c.downloads"> | <py:if test="not c.downloads"> |
<h4>No data</h4> | <h4>No data</h4> |
<p>There is no download data available for this month</p> | <p>There is no download data available for this month</p> |
</py:if> | </py:if> |
</div> | </div> |
</div> | </div> |
<xi:include href="../../layout.html" /> | <xi:include href="../../layout.html" /> |
<py:def function="optional_footer"> | <py:def function="optional_footer"> |
<script type='text/javascript'> | <script type='text/javascript'> |
$('.dropdown-toggle').dropdown(); | $('.dropdown-toggle').dropdown(); |
$('.nav-tabs li a').click(function (e) { | $('.nav-tabs li a').click(function (e) { |
e.preventDefault(); | e.preventDefault(); |
$(this).tab('show'); | $(this).tab('show'); |
}) | }) |
alert(window.location.hash); | alert(window.location.hash); |
</script> | </script> |
</py:def> | </py:def> |
</html> | </html> |
<html xmlns:py="http://genshi.edgewall.org/" | <html xmlns:py="http://genshi.edgewall.org/" |
xmlns:i18n="http://genshi.edgewall.org/i18n" | xmlns:i18n="http://genshi.edgewall.org/i18n" |
xmlns:xi="http://www.w3.org/2001/XInclude" | xmlns:xi="http://www.w3.org/2001/XInclude" |
py:strip=""> | py:strip=""> |
<xi:include href="../ga_util.html" /> | <xi:include href="../ga_util.html" /> |
<py:def function="page_title">Site usage</py:def> | <py:def function="page_title">Site usage</py:def> |
<py:def function="optional_head"> | <py:def function="optional_head"> |
<link rel="stylesheet" type="text/css" href="/scripts/vendor/rickshaw.min.css"/> | <link rel="stylesheet" type="text/css" href="/scripts/vendor/rickshaw.min.css"/> |
<link rel="stylesheet" type="text/css" href="/css/ga_report.css?1"/> | <link rel="stylesheet" type="text/css" href="/css/ga_report.css?1"/> |
<script type="text/javascript" src="/scripts/modernizr-2.6.2.custom.js"></script> | <script type="text/javascript" src="/scripts/modernizr-2.6.2.custom.js"></script> |
<script type="text/javascript" src="/scripts/ckanext_ga_reports.js?1"></script> | <script type="text/javascript" src="/scripts/ckanext_ga_reports.js?1"></script> |
<script type="text/javascript" src="/scripts/vendor/jquery.sparkline.modified.js"></script> | <script type="text/javascript" src="/scripts/vendor/jquery.sparkline.modified.js"></script> |
<script type="text/javascript" src="/scripts/rickshaw_ie7_shim.js"></script> | <script type="text/javascript" src="/scripts/rickshaw_ie7_shim.js"></script> |
<script type="text/javascript" src="/scripts/vendor/d3.v2.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> | <script type="text/javascript" src="/scripts/vendor/d3.layout.min.js"></script> |
<script type="text/javascript" src="/scripts/vendor/rickshaw.min.js"></script> | <script type="text/javascript" src="/scripts/vendor/rickshaw.min.js"></script> |
</py:def> | </py:def> |
<py:match path="primarysidebar"> | <py:match path="breadcrumbs"> |
${ga_sidebar(download_link=h.url_for(controller='ckanext.ga_report.controller:GaReport',action='csv',month=c.month or 'all'))} | <li><a href="/data/site-usage">Site Analytics</a></li> |
<li><a href="/data/site-usage">Site-wide</a></li> | |
</py:match> | </py:match> |
<py:def function="page_heading">Site Usage ${usage_nav('Site-wide')}</py:def> | |
<div py:match="content"> | <div py:match="content"> |
<div class="row"> | |
<div class="boxed"> | <div class="col-sm-7 col-md-8 col-lg-9"> |
<div class="tabbable"> | <py:with vars="download_link=h.url_for(controller='ckanext.ga_report.controller:GaReport',action='csv',month=c.month or 'all')"> |
<ul class="nav nav-tabs"> | <a class="btn button btn-primary btn-sm pull-right" href="${download_link}"><i class="icon-download"></i> Download as CSV</a> |
<li class="active"><a href="#totals" data-toggle="hashtab">Totals</a></li> | </py:with> |
<li class="dropdown"> | <h1>Site Usage</h1> |
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Browsers | </div> |
<b class="caret"></b></a> | <div class="col-sm-5 col-md-4 col-lg-3"> |
<ul class="dropdown-menu"> | <div class="panel panel-default"> |
<li><a href="#browsers_names" data-toggle="hashtab">Browsers</a></li> | <div class="panel-heading"><strong>Jump To...</strong></div> |
<li><a href="#browsers_versions" data-toggle="hashtab">Versions</a></li> | <div class="panel-body"> |
<ul> | |
<li><a href="/data/site-usage/publisher">Publisher Usage Statistics</a></li> | |
<li><a href="/data/site-usage/dataset">Dataset Usage Statistics</a></li> | |
</ul> | </ul> |
</li> | </div> |
<li class="dropdown"> | </div> |
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Operating Systems | </div> |
<b class="caret"></b></a> | </div> |
<ul class="dropdown-menu"> | |
<li><a href="#os" data-toggle="hashtab">Operating Systems</a></li> | <div class="row" style="background: #fff;"> |
<li><a href="#os_versions" data-toggle="hashtab">Versions</a></li> | <div class="col-md-4"> |
<div class="whitebox"> | |
<strong>Graph Legend</strong> | |
<div id="graph-legend-container"> | |
<div style="display: none;" id="legend_none">(No graph is loaded)</div> | |
</div> | |
</div> | |
</div> | |
<div class="col-md-8"> | |
<div class="whitebox"> | |
<div class="tabbable"> | |
<ul class="nav nav-tabs"> | |
<li class="active"><a href="#totals" data-toggle="hashtab">Totals</a></li> | |
<li class="dropdown"> | |
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Browsers | |
<b class="caret"></b></a> | |
<ul class="dropdown-menu"> | |
<li><a href="#browsers_names" data-toggle="hashtab">Browsers</a></li> | |
<li><a href="#browsers_versions" data-toggle="hashtab">Versions</a></li> | |
</ul> | |
</li> | |
<li class="dropdown"> | |
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Operating Systems | |
<b class="caret"></b></a> | |
<ul class="dropdown-menu"> | |
<li><a href="#os" data-toggle="hashtab">Operating Systems</a></li> | |
<li><a href="#os_versions" data-toggle="hashtab">Versions</a></li> | |
</ul> | |
</li> | |
<li class="dropdown"> | |
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Social | |
<b class="caret"></b></a> | |
<ul class="dropdown-menu"> | |
<li><a href="#social_networks" data-toggle="hashtab">All networks</a></li> | |
<li><a href="#social_referrals_totals" data-toggle="hashtab">Referral links</a></li> | |
</ul> | |
</li> | |
<li><a href="#languages" data-toggle="hashtab">Languages</a></li> | |
<li><a href="#country" data-toggle="hashtab">Country</a></li> | |
</ul> | </ul> |
</li> | <div class="tab-content"> |
<li class="dropdown"> | <div class="tab-pane active" id="totals"> |
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Social | <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> |
<b class="caret"></b></a> | <h4 class="ga-reports-heading">Show stats table for:</h4> |
<ul class="dropdown-menu"> | ${month_selector(c.month, c.months, c.day)} |
<li><a href="#social_networks" data-toggle="hashtab">All networks</a></li> | </form> |
<li><a href="#social_referrals_totals" data-toggle="hashtab">Referral links</a></li> | <table class="ga-reports-table table table-condensed table-bordered table-striped"> |
</ul> | <tr> |
</li> | <th>Name</th> |
<li><a href="#languages" data-toggle="hashtab">Languages</a></li> | <th class="td-numeric">Value</th> |
<li><a href="#country" data-toggle="hashtab">Country</a></li> | <th>History</th> |
</ul> | </tr> |
<div class="tab-content"> | <py:for each="name, value, graph in c.global_totals"> |
<div class="tab-pane active" id="totals"> | <tr> |
<form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> | <td>${name}</td> |
<h4 class="ga-reports-heading">Show stats table for:</h4> | <td class="td-numeric">${value}</td> |
${month_selector(c.month, c.months, c.day)} | <td class="sparkline-cell"> |
</form> | <span class="sparkline" sparkTooltips="${','.join([x for x,y in graph])}"> |
<table class="ga-reports-table table table-condensed table-bordered table-striped"> | ${','.join([y for x,y in graph])} |
<tr> | </span> |
<th>Name</th> | </td> |
<th class="td-numeric">Value</th> | </tr> |
<th>History</th> | </py:for> |
</tr> | </table> |
<py:for each="name, value, graph in c.global_totals"> | </div> |
<tr> | <div class="tab-pane" id="browsers_versions"> |
<td>${name}</td> | ${rickshaw_graph(c.browser_versions_graph,'browser-versions',mode='stack')} |
<td class="td-numeric">${value}</td> | <hr/> |
<td class="sparkline-cell"> | <p>Note: Where a browser has a large number of versions, these have been grouped together.</p> |
<span class="sparkline" sparkTooltips="${','.join([x for x,y in graph])}"> | <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> |
${','.join([y for x,y in graph])} | <h4 class="ga-reports-heading">Show stats table for:</h4> |
</span> | ${month_selector(c.month, c.months, c.day)} |
</td> | </form> |
</tr> | <hr/> |
</py:for> | ${stat_table(c.browser_versions)} |
</table> | </div> |
</div> | <div class="tab-pane" id="browsers_names"> |
<div class="tab-pane" id="browsers_versions"> | ${rickshaw_graph(c.browsers_graph,'browsers',mode='stack')} |
${rickshaw_graph(c.browser_versions_graph,'browser-versions',mode='stack')} | <hr/> |
<hr/> | <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> |
<p>Note: Where a browser has a large number of versions, these have been grouped together.</p> | <h4 class="ga-reports-heading">Show stats table for:</h4> |
<form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> | ${month_selector(c.month, c.months, c.day)} |
<h4 class="ga-reports-heading">Show stats table for:</h4> | </form> |
${month_selector(c.month, c.months, c.day)} | <hr/> |
</form> | ${stat_table(c.browsers)} |
${stat_table(c.browser_versions)} | </div> |
</div> | <div class="tab-pane" id="os"> |
<div class="tab-pane" id="browsers_names"> | ${rickshaw_graph(c.os_graph,'os',mode='stack')} |
${rickshaw_graph(c.browsers_graph,'browsers',mode='stack')} | <hr/> |
<hr/> | <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> |
<form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> | <h4 class="ga-reports-heading">Show stats table for:</h4> |
<h4 class="ga-reports-heading">Show stats table for:</h4> | ${month_selector(c.month, c.months, c.day)} |
${month_selector(c.month, c.months, c.day)} | </form> |
</form> | <hr/> |
${stat_table(c.browsers)} | ${stat_table(c.os)} |
</div> | </div> |
<div class="tab-pane" id="os"> | <div class="tab-pane" id="os_versions"> |
${rickshaw_graph(c.os_graph,'os',mode='stack')} | ${rickshaw_graph(c.os_versions_graph,'os_versions',mode='stack')} |
<hr/> | <hr/> |
<form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> | <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> |
<h4 class="ga-reports-heading">Show stats table for:</h4> | <h4 class="ga-reports-heading">Show stats table for:</h4> |
${month_selector(c.month, c.months, c.day)} | ${month_selector(c.month, c.months, c.day)} |
</form> | </form> |
${stat_table(c.os)} | <hr/> |
</div> | ${stat_table(c.os_versions)} |
<div class="tab-pane" id="os_versions"> | </div> |
${rickshaw_graph(c.os_versions_graph,'os_versions',mode='stack')} | <div class="tab-pane" id="social_referrals_totals"> |
<hr/> | <p>Number of visits that were referred from social networks</p> |
<form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> | <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> |
<h4 class="ga-reports-heading">Show stats table for:</h4> | <h4 class="ga-reports-heading">Show stats table for:</h4> |
${month_selector(c.month, c.months, c.day)} | ${month_selector(c.month, c.months, c.day)} |
</form> | </form> |
${stat_table(c.os_versions)} | ${social_table(c.social_referrer_totals)} |
</div> | </div> |
<div class="tab-pane" id="social_referrals_totals"> | <div class="tab-pane" id="social_networks"> |
<p>Number of visits that were referred from social networks</p> | ${rickshaw_graph(c.social_networks_graph, 'social_networks',mode='stack')} |
<form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> | <hr/> |
<h4 class="ga-reports-heading">Show stats table for:</h4> | <p>Percentage of visits that were referred from these social networks</p> |
${month_selector(c.month, c.months, c.day)} | <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> |
</form> | <h4 class="ga-reports-heading">Show stats table for:</h4> |
${social_table(c.social_referrer_totals)} | ${month_selector(c.month, c.months, c.day)} |
</div> | </form> |
<div class="tab-pane" id="social_networks"> | <hr/> |
${rickshaw_graph(c.social_networks_graph, 'social_networks',mode='stack')} | ${stat_table(c.social_networks, 'Visits')} |
<hr/> | </div> |
<p>Percentage of visits that were referred from these social networks</p> | <div class="tab-pane" id="languages"> |
<form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> | ${rickshaw_graph(c.languages_graph,'languages',mode='stack')} |
<h4 class="ga-reports-heading">Show stats table for:</h4> | <hr/> |
${month_selector(c.month, c.months, c.day)} | <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> |
</form> | <h4 class="ga-reports-heading">Show stats table for:</h4> |
${stat_table(c.social_networks, 'Visits')} | ${month_selector(c.month, c.months, c.day)} |
</div> | </form> |
<div class="tab-pane" id="languages"> | <hr/> |
${rickshaw_graph(c.languages_graph,'languages',mode='stack')} | ${stat_table(c.languages)} |
<hr/> | </div> |
<form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> | <div class="tab-pane" id="country"> |
<h4 class="ga-reports-heading">Show stats table for:</h4> | ${rickshaw_graph(c.country_graph,'country',mode='stack')} |
${month_selector(c.month, c.months, c.day)} | <hr/> |
</form> | <form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> |
${stat_table(c.languages)} | <h4 class="ga-reports-heading">Show stats table for:</h4> |
</div> | ${month_selector(c.month, c.months, c.day)} |
<div class="tab-pane" id="country"> | </form> |
${rickshaw_graph(c.country_graph,'country',mode='stack')} | <hr/> |
<hr/> | ${stat_table(c.country)} |
<form class="form-inline" action="${h.url_for(controller='ckanext.ga_report.controller:GaReport',action='index')}" method="get"> | </div> |
<h4 class="ga-reports-heading">Show stats table for:</h4> | </div> |
${month_selector(c.month, c.months, c.day)} | </div> |
</form> | |
${stat_table(c.country)} | |
</div> | |
</div> | </div> |
</div> | </div> |
</div> | </div> |
</div> | </div> |
<py:def function="optional_footer"> | <py:def function="optional_footer"> |
<script type="text/javascript"> | <script type="text/javascript"> |
$(function() { | $(function() { |
CKAN.GA_Reports.bind_sparklines(); | CKAN.GA_Reports.bind_sparklines(); |
CKAN.GA_Reports.bind_sidebar(); | CKAN.GA_Reports.bind_sidebar(); |
CKAN.GA_Reports.bind_month_selector(); | CKAN.GA_Reports.bind_month_selector(); |
}); | }); |
</script> | </script> |
</py:def> | </py:def> |
<xi:include href="../../layout.html" /> | <xi:include href="../../layout.html" /> |
</html> | </html> |