Add organisation public/private dataset count page
Add organisation public/private dataset count page

file:a/README.md -> file:b/README.md
# ckanext-dga-stats # ckanext-dga-stats
   
Fork of CKAN's built-in Statistics plugin modified for data.gov.au Fork of CKAN's built-in Statistics plugin modified for data.gov.au
   
* Remove private datasets from all statistics (except top users) * Remove private datasets from all statistics (except top users)
* Add summary page * Add summary page
* Add activity summary page * Add activity summary page
  * Add organisation public/private dataset count page
   
import ckan.plugins as p import ckan.plugins as p
from ckan.lib.base import BaseController, config from ckan.lib.base import BaseController, config
import stats as stats_lib import stats as stats_lib
import ckan.lib.helpers as h import ckan.lib.helpers as h
   
class StatsController(BaseController): class StatsController(BaseController):
   
def index(self): def index(self):
c = p.toolkit.c c = p.toolkit.c
stats = stats_lib.Stats() stats = stats_lib.Stats()
rev_stats = stats_lib.RevisionStats() rev_stats = stats_lib.RevisionStats()
c.top_rated_packages = stats.top_rated_packages() c.top_rated_packages = stats.top_rated_packages()
c.most_edited_packages = stats.most_edited_packages() c.most_edited_packages = stats.most_edited_packages()
c.largest_groups = stats.largest_groups() c.largest_groups = stats.largest_groups()
c.top_tags = stats.top_tags() c.top_tags = stats.top_tags()
c.top_package_owners = stats.top_package_owners() c.top_package_owners = stats.top_package_owners()
c.summary_stats = stats.summary_stats() c.summary_stats = stats.summary_stats()
c.activity_counts = stats.activity_counts() c.activity_counts = stats.activity_counts()
  c.by_org = stats.by_org()
c.new_packages_by_week = rev_stats.get_by_week('new_packages') c.new_packages_by_week = rev_stats.get_by_week('new_packages')
c.deleted_packages_by_week = rev_stats.get_by_week('deleted_packages') c.deleted_packages_by_week = rev_stats.get_by_week('deleted_packages')
c.num_packages_by_week = rev_stats.get_num_packages_by_week() c.num_packages_by_week = rev_stats.get_num_packages_by_week()
c.package_revisions_by_week = rev_stats.get_by_week('package_revisions') c.package_revisions_by_week = rev_stats.get_by_week('package_revisions')
   
# Used in the legacy CKAN templates. # Used in the legacy CKAN templates.
c.packages_by_week = [] c.packages_by_week = []
   
# Used in new CKAN templates gives more control to the templates for formatting. # Used in new CKAN templates gives more control to the templates for formatting.
c.raw_packages_by_week = [] c.raw_packages_by_week = []
for week_date, num_packages, cumulative_num_packages in c.num_packages_by_week: for week_date, num_packages, cumulative_num_packages in c.num_packages_by_week:
c.packages_by_week.append('[new Date(%s), %s]' % (week_date.replace('-', ','), cumulative_num_packages)) c.packages_by_week.append('[new Date(%s), %s]' % (week_date.replace('-', ','), cumulative_num_packages))
c.raw_packages_by_week.append({'date': h.date_str_to_datetime(week_date), 'total_packages': cumulative_num_packages}) c.raw_packages_by_week.append({'date': h.date_str_to_datetime(week_date), 'total_packages': cumulative_num_packages})
   
c.all_package_revisions = [] c.all_package_revisions = []
c.raw_all_package_revisions = [] c.raw_all_package_revisions = []
for week_date, revs, num_revisions, cumulative_num_revisions in c.package_revisions_by_week: for week_date, revs, num_revisions, cumulative_num_revisions in c.package_revisions_by_week:
c.all_package_revisions.append('[new Date(%s), %s]' % (week_date.replace('-', ','), num_revisions)) c.all_package_revisions.append('[new Date(%s), %s]' % (week_date.replace('-', ','), num_revisions))
c.raw_all_package_revisions.append({'date': h.date_str_to_datetime(week_date), 'total_revisions': num_revisions}) c.raw_all_package_revisions.append({'date': h.date_str_to_datetime(week_date), 'total_revisions': num_revisions})
   
c.new_datasets = [] c.new_datasets = []
c.raw_new_datasets = [] c.raw_new_datasets = []
for week_date, pkgs, num_packages, cumulative_num_packages in c.new_packages_by_week: for week_date, pkgs, num_packages, cumulative_num_packages in c.new_packages_by_week:
c.new_datasets.append('[new Date(%s), %s]' % (week_date.replace('-', ','), num_packages)) c.new_datasets.append('[new Date(%s), %s]' % (week_date.replace('-', ','), num_packages))
c.raw_new_datasets.append({'date': h.date_str_to_datetime(week_date), 'new_packages': num_packages}) c.raw_new_datasets.append({'date': h.date_str_to_datetime(week_date), 'new_packages': num_packages})
   
return p.toolkit.render('ckanext/stats/index.html') return p.toolkit.render('ckanext/stats/index.html')
   
def leaderboard(self, id=None): def leaderboard(self, id=None):
c = p.toolkit.c c = p.toolkit.c
c.solr_core_url = config.get('ckanext.stats.solr_core_url', c.solr_core_url = config.get('ckanext.stats.solr_core_url',
'http://solr.okfn.org/solr/ckan') 'http://solr.okfn.org/solr/ckan')
return p.toolkit.render('ckanext/stats/leaderboard.html') return p.toolkit.render('ckanext/stats/leaderboard.html')
   
   
 Binary files a/ckanext/dga_stats/controller.pyc and b/ckanext/dga_stats/controller.pyc differ
import datetime import datetime
   
from pylons import config from pylons import config
from sqlalchemy import Table, select, func, and_ from sqlalchemy import Table, select, func, and_
   
import ckan.plugins as p import ckan.plugins as p
import ckan.model as model import ckan.model as model
   
cache_enabled = p.toolkit.asbool(config.get('ckanext.stats.cache_enabled', 'True')) cache_enabled = p.toolkit.asbool(config.get('ckanext.stats.cache_enabled', 'True'))
   
if cache_enabled: if cache_enabled:
from pylons import cache from pylons import cache
our_cache = cache.get_cache('stats', type='dbm') our_cache = cache.get_cache('stats', type='dbm')
   
DATE_FORMAT = '%Y-%m-%d' DATE_FORMAT = '%Y-%m-%d'
   
def table(name): def table(name):
return Table(name, model.meta.metadata, autoload=True) return Table(name, model.meta.metadata, autoload=True)
   
def datetime2date(datetime_): def datetime2date(datetime_):
return datetime.date(datetime_.year, datetime_.month, datetime_.day) return datetime.date(datetime_.year, datetime_.month, datetime_.day)
   
   
class Stats(object): class Stats(object):
@classmethod @classmethod
def top_rated_packages(cls, limit=10): def top_rated_packages(cls, limit=10):
# NB Not using sqlalchemy as sqla 0.4 doesn't work using both group_by # NB Not using sqlalchemy as sqla 0.4 doesn't work using both group_by
# and apply_avg # and apply_avg
package = table('package') package = table('package')
rating = table('rating') rating = table('rating')
sql = select([package.c.id, func.avg(rating.c.rating), func.count(rating.c.rating)], from_obj=[package.join(rating)]).\ sql = select([package.c.id, func.avg(rating.c.rating), func.count(rating.c.rating)], from_obj=[package.join(rating)]).\
where(package.c.private == 'f').\ where(package.c.private == 'f').\
group_by(package.c.id).\ group_by(package.c.id).\
order_by(func.avg(rating.c.rating).desc(), func.count(rating.c.rating).desc()).\ order_by(func.avg(rating.c.rating).desc(), func.count(rating.c.rating).desc()).\
limit(limit) limit(limit)
res_ids = model.Session.execute(sql).fetchall() res_ids = model.Session.execute(sql).fetchall()
res_pkgs = [(model.Session.query(model.Package).get(unicode(pkg_id)), avg, num) for pkg_id, avg, num in res_ids] res_pkgs = [(model.Session.query(model.Package).get(unicode(pkg_id)), avg, num) for pkg_id, avg, num in res_ids]
return res_pkgs return res_pkgs
   
@classmethod @classmethod
def most_edited_packages(cls, limit=10): def most_edited_packages(cls, limit=10):
package_revision = table('package_revision') package_revision = table('package_revision')
package = table('package') package = table('package')
s = select([package_revision.c.id, func.count(package_revision.c.revision_id)], from_obj=[package_revision.join(package)]).\ s = select([package_revision.c.id, func.count(package_revision.c.revision_id)], from_obj=[package_revision.join(package)]).\
where(package.c.private == 'f').\ where(package.c.private == 'f').\
group_by(package_revision.c.id).\ group_by(package_revision.c.id).\
order_by(func.count(package_revision.c.revision_id).desc()).\ order_by(func.count(package_revision.c.revision_id).desc()).\
limit(limit) limit(limit)
res_ids = model.Session.execute(s).fetchall() res_ids = model.Session.execute(s).fetchall()
res_pkgs = [(model.Session.query(model.Package).get(unicode(pkg_id)), val) for pkg_id, val in res_ids] res_pkgs = [(model.Session.query(model.Package).get(unicode(pkg_id)), val) for pkg_id, val in res_ids]
return res_pkgs return res_pkgs
   
@classmethod @classmethod
def largest_groups(cls, limit=10): def largest_groups(cls, limit=10):
member = table('member') member = table('member')
package = table('package') s = select([member.c.group_id, func.count(member.c.table_id)]).\
s = select([member.c.group_id, func.count(member.c.table_id)]).\  
group_by(member.c.group_id).\ group_by(member.c.group_id).\
where(and_(member.c.group_id!=None, member.c.table_name=='package', package.c.private == 'f')).\ where(and_(member.c.group_id!=None, member.c.table_name=='package')).\
order_by(func.count(member.c.table_id).desc()).\ order_by(func.count(member.c.table_id).desc()).\
limit(limit) limit(limit)
   
  res_ids = model.Session.execute(s).fetchall()
  res_groups = [(model.Session.query(model.Group).get(unicode(group_id)), val) for group_id, val in res_ids]
  return res_groups
   
  @classmethod
  def by_org(cls, limit=10):
  group = table('group')
  package = table('package')
  s = select([group.c.id, package.c.private, func.count(package.c.private)]).\
  group_by(group.c.id, package.c.private).\
  order_by(group.c.id).\
  limit(limit)
   
res_ids = model.Session.execute(s).fetchall() res_ids = model.Session.execute(s).fetchall()
res_groups = [(model.Session.query(model.Group).get(unicode(group_id)), val) for group_id, val in res_ids] res_groups = [(model.Session.query(model.Group).get(unicode(group_id)), private, val) for group_id, private, val in res_ids]
return res_groups return res_groups
   
@classmethod @classmethod
def top_tags(cls, limit=10, returned_tag_info='object'): # by package def top_tags(cls, limit=10, returned_tag_info='object'): # by package
assert returned_tag_info in ('name', 'id', 'object') assert returned_tag_info in ('name', 'id', 'object')
tag = table('tag') tag = table('tag')
package_tag = table('package_tag') package_tag = table('package_tag')
package = table('package') package = table('package')
#TODO filter out tags with state=deleted #TODO filter out tags with state=deleted
if returned_tag_info == 'name': if returned_tag_info == 'name':
from_obj = [package_tag.join(tag)] from_obj = [package_tag.join(tag)]
tag_column = tag.c.name tag_column = tag.c.name
else: else:
from_obj = None from_obj = None
tag_column = package_tag.c.tag_id tag_column = package_tag.c.tag_id
s = select([tag_column, func.count(package_tag.c.package_id)], s = select([tag_column, func.count(package_tag.c.package_id)],
from_obj=from_obj) from_obj=from_obj)
s = s.group_by(tag_column).\ s = s.group_by(tag_column).\
where(package.c.private == 'f').\ where(package.c.private == 'f').\
order_by(func.count(package_tag.c.package_id).desc()).\ order_by(func.count(package_tag.c.package_id).desc()).\
limit(limit) limit(limit)
res_col = model.Session.execute(s).fetchall() res_col = model.Session.execute(s).fetchall()
if returned_tag_info in ('id', 'name'): if returned_tag_info in ('id', 'name'):
return res_col return res_col
elif returned_tag_info == 'object': elif returned_tag_info == 'object':
res_tags = [(model.Session.query(model.Tag).get(unicode(tag_id)), val) for tag_id, val in res_col] res_tags = [(model.Session.query(model.Tag).get(unicode(tag_id)), val) for tag_id, val in res_col]
return res_tags return res_tags
   
@classmethod @classmethod
def top_package_owners(cls, limit=10): def top_package_owners(cls, limit=10):
package_role = table('package_role') package_role = table('package_role')
user_object_role = table('user_object_role') user_object_role = table('user_object_role')
package = table('package') package = table('package')
# join(package, package_role.c.package_id == package.c.id).\ # join(package, package_role.c.package_id == package.c.id).\
# where(package.c.private == 'f').\ # where(package.c.private == 'f').\
s = select([user_object_role.c.user_id, func.count(user_object_role.c.role)], from_obj=[user_object_role.join(package_role)]).\ s = select([user_object_role.c.user_id, func.count(user_object_role.c.role)], from_obj=[user_object_role.join(package_role)]).\
where(user_object_role.c.role==model.authz.Role.ADMIN).\ where(user_object_role.c.role==model.authz.Role.ADMIN).\
where(user_object_role.c.user_id!=None).\ where(user_object_role.c.user_id!=None).\
group_by(user_object_role.c.user_id).\ group_by(user_object_role.c.user_id).\
order_by(func.count(user_object_role.c.role).desc()).\ order_by(func.count(user_object_role.c.role).desc()).\
limit(limit) limit(limit)
res_ids = model.Session.execute(s).fetchall() res_ids = model.Session.execute(s).fetchall()
res_users = [(model.Session.query(model.User).get(unicode(user_id)), val) for user_id, val in res_ids] res_users = [(model.Session.query(model.User).get(unicode(user_id)), val) for user_id, val in res_ids]
return res_users return res_users
   
@classmethod @classmethod
def summary_stats(cls): def summary_stats(cls):
connection = model.Session.connection() connection = model.Session.connection()
# select name,role from user_object_role inner join \"user\" on user_object_role.user_id = \"user\".id where name not in ('logged_in','visitor') group by name,role" # select name,role from user_object_role inner join \"user\" on user_object_role.user_id = \"user\".id where name not in ('logged_in','visitor') group by name,role"
   
res = connection.execute("SELECT 'Total Organisations', count(*) from \"group\" where type = 'organization' and state = 'active' union \ res = connection.execute("SELECT 'Total Organisations', count(*) from \"group\" where type = 'organization' and state = 'active' union \
select 'Total Datasets', count(*) from package where state='active' or state='draft' or state='draft-complete' union \ select 'Total Datasets', count(*) from package where state='active' or state='draft' or state='draft-complete' union \
select 'Total Data Files/Resources', count(*) from resource where state='active'").fetchall(); select 'Total Data Files/Resources', count(*) from resource where state='active'").fetchall();
return res return res
   
   
@classmethod @classmethod
def activity_counts(cls): def activity_counts(cls):
connection = model.Session.connection() connection = model.Session.connection()
res = connection.execute("select to_char(timestamp, 'YYYY-MM') as month,activity_type, count(*) from activity group by month, activity_type order by month;").fetchall(); res = connection.execute("select to_char(timestamp, 'YYYY-MM') as month,activity_type, count(*) from activity group by month, activity_type order by month;").fetchall();
return res return res
   
   
class RevisionStats(object): class RevisionStats(object):
@classmethod @classmethod
def package_addition_rate(cls, weeks_ago=0): def package_addition_rate(cls, weeks_ago=0):
week_commenced = cls.get_date_weeks_ago(weeks_ago) week_commenced = cls.get_date_weeks_ago(weeks_ago)
return cls.get_objects_in_a_week(week_commenced, return cls.get_objects_in_a_week(week_commenced,
type_='package_addition_rate') type_='package_addition_rate')
   
@classmethod @classmethod
def package_revision_rate(cls, weeks_ago=0): def package_revision_rate(cls, weeks_ago=0):
week_commenced = cls.get_date_weeks_ago(weeks_ago) week_commenced = cls.get_date_weeks_ago(weeks_ago)
return cls.get_objects_in_a_week(week_commenced, return cls.get_objects_in_a_week(week_commenced,
type_='package_revision_rate') type_='package_revision_rate')
   
@classmethod @classmethod
def get_date_weeks_ago(cls, weeks_ago): def get_date_weeks_ago(cls, weeks_ago):
''' '''
@param weeks_ago: specify how many weeks ago to give count for @param weeks_ago: specify how many weeks ago to give count for
(0 = this week so far) (0 = this week so far)
''' '''
date_ = datetime.date.today() date_ = datetime.date.today()
return date_ - datetime.timedelta(days= return date_ - datetime.timedelta(days=
datetime.date.weekday(date_) + 7 * weeks_ago) datetime.date.weekday(date_) + 7 * weeks_ago)
   
@classmethod @classmethod
def get_week_dates(cls, weeks_ago): def get_week_dates(cls, weeks_ago):
''' '''
@param weeks_ago: specify how many weeks ago to give count for @param weeks_ago: specify how many weeks ago to give count for
(0 = this week so far) (0 = this week so far)
''' '''
package_revision = table('package_revision') package_revision = table('package_revision')
revision = table('revision') revision = table('revision')
today = datetime.date.today() today = datetime.date.today()
date_from = datetime.datetime(today.year, today.month, today.day) -\ date_from = datetime.datetime(today.year, today.month, today.day) -\
datetime.timedelta(days=datetime.date.weekday(today) + \ datetime.timedelta(days=datetime.date.weekday(today) + \
7 * weeks_ago) 7 * weeks_ago)
date_to = date_from + datetime.timedelta(days=7) date_to = date_from + datetime.timedelta(days=7)
return (date_from, date_to) return (date_from, date_to)
   
@classmethod @classmethod
def get_date_week_started(cls, date_): def get_date_week_started(cls, date_):
assert isinstance(date_, datetime.date) assert isinstance(date_, datetime.date)
if isinstance(date_, datetime.datetime): if isinstance(date_, datetime.datetime):
date_ = datetime2date(date_) date_ = datetime2date(date_)
return date_ - datetime.timedelta(days=datetime.date.weekday(date_)) return date_ - datetime.timedelta(days=datetime.date.weekday(date_))
   
@classmethod @classmethod
def get_package_revisions(cls): def get_package_revisions(cls):
''' '''
@return: Returns list of revisions and date of them, in @return: Returns list of revisions and date of them, in
format: [(id, date), ...] format: [(id, date), ...]
''' '''
package_revision = table('package_revision') package_revision = table('package_revision')
revision = table('revision') revision = table('revision')
s = select([package_revision.c.id, revision.c.timestamp], from_obj=[package_revision.join(revision)]).order_by(revision.c.timestamp) s = select([package_revision.c.id, revision.c.timestamp], from_obj=[package_revision.join(revision)]).order_by(revision.c.timestamp)
res = model.Session.execute(s).fetchall() # [(id, datetime), ...] res = model.Session.execute(s).fetchall() # [(id, datetime), ...]
return res return res
   
@classmethod @classmethod
def get_new_packages(cls): def get_new_packages(cls):
''' '''
@return: Returns list of new pkgs and date when they were created, in @return: Returns list of new pkgs and date when they were created, in
format: [(id, date_ordinal), ...] format: [(id, date_ordinal), ...]
''' '''
def new_packages(): def new_packages():
# Can't filter by time in select because 'min' function has to # Can't filter by time in select because 'min' function has to
# be 'for all time' else you get first revision in the time period. # be 'for all time' else you get first revision in the time period.
package_revision = table('package_revision') package_revision = table('package_revision')
revision = table('revision') revision = table('revision')
s = select([package_revision.c.id, func.min(revision.c.timestamp)], from_obj=[package_revision.join(revision)]).\ s = select([package_revision.c.id, func.min(revision.c.timestamp)], from_obj=[package_revision.join(revision)]).\
where(package.c.private == 'f').\ where(package.c.private == 'f').\
group_by(package_revision.c.id).order_by(func.min(revision.c.timestamp)) group_by(package_revision.c.id).order_by(func.min(revision.c.timestamp))
res = model.Session.execute(s).fetchall() # [(id, datetime), ...] res = model.Session.execute(s).fetchall() # [(id, datetime), ...]
res_pickleable = [] res_pickleable = []
for pkg_id, created_datetime in res: for pkg_id, created_datetime in res:
res_pickleable.append((pkg_id, created_datetime.toordinal())) res_pickleable.append((pkg_id, created_datetime.toordinal()))
return res_pickleable return res_pickleable
if cache_enabled: if cache_enabled:
week_commences = cls.get_date_week_started(datetime.date.today()) week_commences = cls.get_date_week_started(datetime.date.today())
key = 'all_new_packages_%s' + week_commences.strftime(DATE_FORMAT) key = 'all_new_packages_%s' + week_commences.strftime(DATE_FORMAT)
new_packages = our_cache.get_value(key=key, new_packages = our_cache.get_value(key=key,
createfunc=new_packages) createfunc=new_packages)
else: else:
new_packages = new_packages() new_packages = new_packages()
return new_packages return new_packages
   
@classmethod @classmethod
def get_deleted_packages(cls): def get_deleted_packages(cls):
''' '''
@return: Returns list of deleted pkgs and date when they were deleted, in @return: Returns list of deleted pkgs and date when they were deleted, in
format: [(id, date_ordinal), ...] format: [(id, date_ordinal), ...]
''' '''
def deleted_packages(): def deleted_packages():
# Can't filter by time in select because 'min' function has to # Can't filter by time in select because 'min' function has to
# be 'for all time' else you get first revision in the time period. # be 'for all time' else you get first revision in the time period.
package_revision = table('package_revision') package_revision = table('package_revision')
revision = table('revision') revision = table('revision')
s = select([package_revision.c.id, func.min(revision.c.timestamp)], from_obj=[package_revision.join(revision)]).\ s = select([package_revision.c.id, func.min(revision.c.timestamp)], from_obj=[package_revision.join(revision)]).\
where(package_revision.c.state==model.State.DELETED).\ where(package_revision.c.state==model.State.DELETED).\
group_by(package_revision.c.id).\ group_by(package_revision.c.id).\
order_by(func.min(revision.c.timestamp)) order_by(func.min(revision.c.timestamp))
res = model.Session.execute(s).fetchall() # [(id, datetime), ...] res = model.Session.execute(s).fetchall() # [(id, datetime), ...]
res_pickleable = [] res_pickleable = []
for pkg_id, deleted_datetime in res: for pkg_id, deleted_datetime in res:
res_pickleable.append((pkg_id, deleted_datetime.toordinal())) res_pickleable.append((pkg_id, deleted_datetime.toordinal()))
return res_pickleable return res_pickleable
if cache_enabled: if cache_enabled:
week_commences = cls.get_date_week_started(datetime.date.today()) week_commences = cls.get_date_week_started(datetime.date.today())
key = 'all_deleted_packages_%s' + week_commences.strftime(DATE_FORMAT) key = 'all_deleted_packages_%s' + week_commences.strftime(DATE_FORMAT)
deleted_packages = our_cache.get_value(key=key, deleted_packages = our_cache.get_value(key=key,
createfunc=deleted_packages) createfunc=deleted_packages)
else: else:
deleted_packages = deleted_packages() deleted_packages = deleted_packages()
return deleted_packages return deleted_packages
   
@classmethod @classmethod
def get_num_packages_by_week(cls): def get_num_packages_by_week(cls):
def num_packages(): def num_packages():
new_packages_by_week = cls.get_by_week('new_packages') new_packages_by_week = cls.get_by_week('new_packages')
deleted_packages_by_week = cls.get_by_week('deleted_packages') deleted_packages_by_week = cls.get_by_week('deleted_packages')
first_date = (min(datetime.datetime.strptime(new_packages_by_week[0][0], DATE_FORMAT), first_date = (min(datetime.datetime.strptime(new_packages_by_week[0][0], DATE_FORMAT),
datetime.datetime.strptime(deleted_packages_by_week[0][0], DATE_FORMAT))).date() datetime.datetime.strptime(deleted_packages_by_week[0][0], DATE_FORMAT))).date()
cls._cumulative_num_pkgs = 0 cls._cumulative_num_pkgs = 0
new_pkgs = [] new_pkgs = []
deleted_pkgs = [] deleted_pkgs = []
def build_weekly_stats(week_commences, new_pkg_ids, deleted_pkg_ids): def build_weekly_stats(week_commences, new_pkg_ids, deleted_pkg_ids):
num_pkgs = len(new_pkg_ids) - len(deleted_pkg_ids) num_pkgs = len(new_pkg_ids) - len(deleted_pkg_ids)
new_pkgs.extend([model.Session.query(model.Package).get(id).name for id in new_pkg_ids]) new_pkgs.extend([model.Session.query(model.Package).get(id).name for id in new_pkg_ids])
deleted_pkgs.extend([model.Session.query(model.Package).get(id).name for id in deleted_pkg_ids]) deleted_pkgs.extend([model.Session.query(model.Package).get(id).name for id in deleted_pkg_ids])
cls._cumulative_num_pkgs += num_pkgs cls._cumulative_num_pkgs += num_pkgs
return (week_commences.strftime(DATE_FORMAT), return (week_commences.strftime(DATE_FORMAT),
num_pkgs, cls._cumulative_num_pkgs) num_pkgs, cls._cumulative_num_pkgs)
week_ends = first_date week_ends = first_date
today = datetime.date.today() today = datetime.date.today()
new_package_week_index = 0 new_package_week_index = 0
deleted_package_week_index = 0 deleted_package_week_index = 0
weekly_numbers = [] # [(week_commences, num_packages, cumulative_num_pkgs])] weekly_numbers = [] # [(week_commences, num_packages, cumulative_num_pkgs])]
while week_ends <= today: while week_ends <= today:
week_commences = week_ends week_commences = week_ends
week_ends = week_commences + datetime.timedelta(days=7) week_ends = week_commences + datetime.timedelta(days=7)
if datetime.datetime.strptime(new_packages_by_week[new_package_week_index][0], DATE_FORMAT).date() == week_commences: if datetime.datetime.strptime(new_packages_by_week[new_package_week_index][0], DATE_FORMAT).date() == week_commences:
new_pkg_ids = new_packages_by_week[new_package_week_index][1] new_pkg_ids = new_packages_by_week[new_package_week_index][1]
new_package_week_index += 1 new_package_week_index += 1
else: else:
new_pkg_ids = [] new_pkg_ids = []
if datetime.datetime.strptime(deleted_packages_by_week[deleted_package_week_index][0], DATE_FORMAT).date() == week_commences: if datetime.datetime.strptime(deleted_packages_by_week[deleted_package_week_index][0], DATE_FORMAT).date() == week_commences:
deleted_pkg_ids = deleted_packages_by_week[deleted_package_week_index][1] deleted_pkg_ids = deleted_packages_by_week[deleted_package_week_index][1]
deleted_package_week_index += 1 deleted_package_week_index += 1
else: else:
deleted_pkg_ids = [] deleted_pkg_ids = []
weekly_numbers.append(build_weekly_stats(week_commences, new_pkg_ids, deleted_pkg_ids)) weekly_numbers.append(build_weekly_stats(week_commences, new_pkg_ids, deleted_pkg_ids))
# just check we got to the end of each count # just check we got to the end of each count
assert new_package_week_index == len(new_packages_by_week) assert new_package_week_index == len(new_packages_by_week)
assert deleted_package_week_index == len(deleted_packages_by_week) assert deleted_package_week_index == len(deleted_packages_by_week)
return weekly_numbers return weekly_numbers
if cache_enabled: if cache_enabled:
week_commences = cls.get_date_week_started(datetime.date.today()) week_commences = cls.get_date_week_started(datetime.date.today())
key = 'number_packages_%s' + week_commences.strftime(DATE_FORMAT) key = 'number_packages_%s' + week_commences.strftime(DATE_FORMAT)
num_packages = our_cache.get_value(key=key, num_packages = our_cache.get_value(key=key,
createfunc=num_packages) createfunc=num_packages)
else: else:
num_packages = num_packages() num_packages = num_packages()
return num_packages return num_packages
   
@classmethod @classmethod
def get_by_week(cls, object_type): def get_by_week(cls, object_type):
cls._object_type = object_type cls._object_type = object_type
def objects_by_week(): def objects_by_week():
if cls._object_type == 'new_packages': if cls._object_type == 'new_packages':
objects = cls.get_new_packages() objects = cls.get_new_packages()
def get_date(object_date): def get_date(object_date):
return datetime.date.fromordinal(object_date) return datetime.date.fromordinal(object_date)
elif cls._object_type == 'deleted_packages': elif cls._object_type == 'deleted_packages':
objects = cls.get_deleted_packages() objects = cls.get_deleted_packages()
def get_date(object_date): def get_date(object_date):
return datetime.date.fromordinal(object_date) return datetime.date.fromordinal(object_date)
elif cls._object_type == 'package_revisions': elif cls._object_type == 'package_revisions':
objects = cls.get_package_revisions() objects = cls.get_package_revisions()
def get_date(object_date): def get_date(object_date):
return datetime2date(object_date) return datetime2date(object_date)
else: else:
raise NotImplementedError() raise NotImplementedError()
first_date = get_date(objects[0][1]) if objects else datetime.date.today() first_date = get_date(objects[0][1]) if objects else datetime.date.today()
week_commences = cls.get_date_week_started(first_date) week_commences = cls.get_date_week_started(first_date)
week_ends = week_commences + datetime.timedelta(days=7) week_ends = week_commences + datetime.timedelta(days=7)
week_index = 0 week_index = 0
weekly_pkg_ids = [] # [(week_commences, [pkg_id1, pkg_id2, ...])] weekly_pkg_ids = [] # [(week_commences, [pkg_id1, pkg_id2, ...])]
pkg_id_stack = [] pkg_id_stack = []
cls._cumulative_num_pkgs = 0 cls._cumulative_num_pkgs = 0
def build_weekly_stats(week_commences, pkg_ids): def build_weekly_stats(week_commences, pkg_ids):
num_pkgs = len(pkg_ids) num_pkgs = len(pkg_ids)
cls._cumulative_num_pkgs += num_pkgs cls._cumulative_num_pkgs += num_pkgs
return (week_commences.strftime(DATE_FORMAT), return (week_commences.strftime(DATE_FORMAT),
pkg_ids, num_pkgs, cls._cumulative_num_pkgs) pkg_ids, num_pkgs, cls._cumulative_num_pkgs)
for pkg_id, date_field in objects: for pkg_id, date_field in objects:
date_ = get_date(date_field) date_ = get_date(date_field)
if date_ >= week_ends: if date_ >= week_ends:
weekly_pkg_ids.append(build_weekly_stats(week_commences, pkg_id_stack)) weekly_pkg_ids.append(build_weekly_stats(week_commences, pkg_id_stack))
pkg_id_stack = [] pkg_id_stack = []
week_commences = week_ends week_commences = week_ends
week_ends = week_commences + datetime.timedelta(days=7) week_ends = week_commences + datetime.timedelta(days=7)
pkg_id_stack.append(pkg_id) pkg_id_stack.append(pkg_id)
weekly_pkg_ids.append(build_weekly_stats(week_commences, pkg_id_stack)) weekly_pkg_ids.append(build_weekly_stats(week_commences, pkg_id_stack))
today = datetime.date.today() today = datetime.date.today()
while week_ends <= today: while week_ends <= today:
week_commences = week_ends week_commences = week_ends
week_ends = week_commences + datetime.timedelta(days=7) week_ends = week_commences + datetime.timedelta(days=7)
weekly_pkg_ids.append(build_weekly_stats(week_commences, [])) weekly_pkg_ids.append(build_weekly_stats(week_commences, []))
return weekly_pkg_ids return weekly_pkg_ids
if cache_enabled: if cache_enabled:
week_commences = cls.get_date_week_started(datetime.date.today()) week_commences = cls.get_date_week_started(datetime.date.today())
key = '%s_by_week_%s' % (cls._object_type, week_commences.strftime(DATE_FORMAT)) key = '%s_by_week_%s' % (cls._object_type, week_commences.strftime(DATE_FORMAT))
objects_by_week_ = our_cache.get_value(key=key, objects_by_week_ = our_cache.get_value(key=key,
createfunc=objects_by_week) createfunc=objects_by_week)
else: else:
objects_by_week_ = objects_by_week() objects_by_week_ = objects_by_week()
return objects_by_week_ return objects_by_week_
   
@classmethod @classmethod
def get_objects_in_a_week(cls, date_week_commences, def get_objects_in_a_week(cls, date_week_commences,
type_='new-package-rate'): type_='new-package-rate'):
''' '''
@param type: Specifies what to return about the specified week: @param type: Specifies what to return about the specified week:
"package_addition_rate" number of new packages "package_addition_rate" number of new packages
"package_revision_rate" number of package revisions "package_revision_rate" number of package revisions
"new_packages" a list of the packages created "new_packages" a list of the packages created
in a tuple with the date. in a tuple with the date.
"deleted_packages" a list of the packages deleted "deleted_packages" a list of the packages deleted
in a tuple with the date. in a tuple with the date.
@param dates: date range of interest - a tuple: @param dates: date range of interest - a tuple:
(start_date, end_date) (start_date, end_date)
''' '''
assert isinstance(date_week_commences, datetime.date) assert isinstance(date_week_commences, datetime.date)
if type_ in ('package_addition_rate', 'new_packages'): if type_ in ('package_addition_rate', 'new_packages'):
object_type = 'new_packages' object_type = 'new_packages'
elif type_ == 'deleted_packages': elif type_ == 'deleted_packages':
object_type = 'deleted_packages' object_type = 'deleted_packages'
elif type_ == 'package_revision_rate': elif type_ == 'package_revision_rate':
object_type = 'package_revisions' object_type = 'package_revisions'
else: else:
raise NotImplementedError() raise NotImplementedError()
objects_by_week = cls.get_by_week(object_type) objects_by_week = cls.get_by_week(object_type)
date_wc_str = date_week_commences.strftime(DATE_FORMAT) date_wc_str = date_week_commences.strftime(DATE_FORMAT)
object_ids = None object_ids = None
for objects_in_a_week in objects_by_week: for objects_in_a_week in objects_by_week:
if objects_in_a_week[0] == date_wc_str: if objects_in_a_week[0] == date_wc_str:
object_ids = objects_in_a_week[1] object_ids = objects_in_a_week[1]
break break
if object_ids is None: if object_ids is None:
raise TypeError('Week specified is outside range') raise TypeError('Week specified is outside range')
assert isinstance(object_ids, list) assert isinstance(object_ids, list)
if type_ in ('package_revision_rate', 'package_addition_rate'): if type_ in ('package_revision_rate', 'package_addition_rate'):
return len(object_ids) return len(object_ids)
elif type_ in ('new_packages', 'deleted_packages'): elif type_ in ('new_packages', 'deleted_packages'):
return [ model.Session.query(model.Package).get(pkg_id) \ return [ model.Session.query(model.Package).get(pkg_id) \
for pkg_id in object_ids ] for pkg_id in object_ids ]
   
   
 Binary files a/ckanext/dga_stats/stats.pyc and b/ckanext/dga_stats/stats.pyc differ
{% extends "page.html" %} {% extends "page.html" %}
   
{% block breadcrumb_content %} {% block breadcrumb_content %}
<li class="active">{{ 'Statistics' }}</li> <li class="active">{{ 'Statistics' }}</li>
{% endblock %} {% endblock %}
   
{% block primary_content %} {% block primary_content %}
<article class="module"> <article class="module">
<section id="summary" class="module-content tab-content"> <section id="summary" class="module-content tab-content">
<h2>{{ _('Summary') }}</h2> <h2>{{ _('Summary') }}</h2>
{% if c.summary_stats %} {% if c.summary_stats %}
<table class="table table-chunky table-bordered table-striped"> <table class="table table-chunky table-bordered table-striped">
<thead> <thead>
<tr> <tr>
<th>{{ _('Measure') }}</th> <th>{{ _('Measure') }}</th>
<th class="metric">{{ _('Value') }}</th> <th class="metric">{{ _('Value') }}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for measure,value in c.summary_stats %} {% for measure,value in c.summary_stats %}
<tr> <tr>
<td>{{measure}}</td> <td>{{measure}}</td>
<td class="metric">{{ value }}</td> <td class="metric">{{ value }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% else %} {% else %}
<p class="empty">{{ _('No groups') }}</p> <p class="empty">{{ _('No groups') }}</p>
{% endif %} {% endif %}
</section> </section>
<section id="activity-counts" class="module-content tab-content"> <section id="activity-counts" class="module-content tab-content">
<h2>{{ _('Site Activity Log') }}</h2> <h2>{{ _('Site Activity Log') }}</h2>
{% if c.activity_counts %} {% if c.activity_counts %}
<table class="table table-chunky table-bordered table-striped"> <table class="table table-chunky table-bordered table-striped">
<thead> <thead>
<tr> <tr>
<th>{{ _('Month') }}</th> <th>{{ _('Month') }}</th>
<th>{{ _('Activity Type') }}</th> <th>{{ _('Activity Type') }}</th>
<th class="metric">{{ _('Count') }}</th> <th class="metric">{{ _('Count') }}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for month, type, count in c.activity_counts %} {% for month, type, count in c.activity_counts %}
<tr> <tr>
<td>{{ month }}</td> <td>{{ month }}</td>
<td>{{ type }}</td> <td>{{ type }}</td>
<td class="metric">{{ count }}</td> <td class="metric">{{ count }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% else %} {% else %}
<p class="empty">{{ _('No groups') }}</p> <p class="empty">{{ _('No groups') }}</p>
{% endif %} {% endif %}
</section> </section>
   
<section id="stats-total-datasets" class="module-content tab-content active"> <section id="stats-total-datasets" class="module-content tab-content active">
<h2>{{ _('Total number of Datasets') }}</h2> <h2>{{ _('Total number of Datasets') }}</h2>
   
{% set xaxis = {'mode': 'time', 'timeformat': '%y-%b'} %} {% set xaxis = {'mode': 'time', 'timeformat': '%y-%b'} %}
{% set yaxis = {'min': 0} %} {% set yaxis = {'min': 0} %}
<table class="table table-chunky table-bordered table-striped" data-module="plot" data-module-xaxis="{{ h.dump_json(xaxis) }}" data-module-yaxis="{{ h.dump_json(yaxis) }}"> <table class="table table-chunky table-bordered table-striped" data-module="plot" data-module-xaxis="{{ h.dump_json(xaxis) }}" data-module-yaxis="{{ h.dump_json(yaxis) }}">
<thead> <thead>
<tr> <tr>
<th>{{ _("Date") }}</th> <th>{{ _("Date") }}</th>
<th>{{ _("Total datasets") }}</th> <th>{{ _("Total datasets") }}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for row in c.raw_packages_by_week %} {% for row in c.raw_packages_by_week %}
<tr> <tr>
<th data-type="date" data-value="{{ row.date.strftime("%s") }}"><time datetime="{{ row.date.isoformat() }}">{{ h.render_datetime(row.date) }}</time></th> <th data-type="date" data-value="{{ row.date.strftime("%s") }}"><time datetime="{{ row.date.isoformat() }}">{{ h.render_datetime(row.date) }}</time></th>
<td>{{ row.total_packages }}</td> <td>{{ row.total_packages }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</section> </section>
   
<section id="stats-dataset-revisions" class="module-content tab-content"> <section id="stats-dataset-revisions" class="module-content tab-content">
<h2>{{ _('Dataset Revisions per Week') }}</h2> <h2>{{ _('Dataset Revisions per Week') }}</h2>
   
{% set xaxis = {'mode': 'time', 'timeformat': '%y-%b'} %} {% set xaxis = {'mode': 'time', 'timeformat': '%y-%b'} %}
{% set lines = {'fill': 1} %} {% set lines = {'fill': 1} %}
<table class="table table-chunky table-bordered table-striped" data-module="plot" data-module-xaxis="{{ h.dump_json(xaxis) }}" data-module-lines="{{ h.dump_json(lines) }}"> <table class="table table-chunky table-bordered table-striped" data-module="plot" data-module-xaxis="{{ h.dump_json(xaxis) }}" data-module-lines="{{ h.dump_json(lines) }}">
<thead> <thead>
<tr> <tr>
<th>{{ _("Date") }}</th> <th>{{ _("Date") }}</th>
<th>{{ _("All dataset revisions") }}</th> <th>{{ _("All dataset revisions") }}</th>
<th>{{ _("New datasets") }}</th> <th>{{ _("New datasets") }}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for row in c.raw_all_package_revisions %} {% for row in c.raw_all_package_revisions %}
<tr> <tr>
<th data-type="date" data-value="{{ row.date.strftime("%s") }}"><time datetime="{{ row.date.isoformat() }}">{{ h.render_datetime(row.date) }}</time></th> <th data-type="date" data-value="{{ row.date.strftime("%s") }}"><time datetime="{{ row.date.isoformat() }}">{{ h.render_datetime(row.date) }}</time></th>
<td>{{ row.total_revisions }}</td> <td>{{ row.total_revisions }}</td>
<td>{{ c.raw_new_datasets[loop.index0].new_packages }}</td> <td>{{ c.raw_new_datasets[loop.index0].new_packages }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</section> </section>
   
<section id="stats-top-rated" class="module-content tab-content"> <section id="stats-top-rated" class="module-content tab-content">
<h2>{{ _('Top Rated Datasets') }}</h2> <h2>{{ _('Top Rated Datasets') }}</h2>
{% if c.top_rated_packages %} {% if c.top_rated_packages %}
<table class="table table-chunky table-bordered table-striped"> <table class="table table-chunky table-bordered table-striped">
<thead> <thead>
<tr> <tr>
<th>Dataset</th> <th>Dataset</th>
<th class="metric">{{ _('Average rating') }}</th> <th class="metric">{{ _('Average rating') }}</th>
<th class="metric">{{ _('Number of ratings') }}</th> <th class="metric">{{ _('Number of ratings') }}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for package, rating, num_ratings in c.top_rated_packages %} {% for package, rating, num_ratings in c.top_rated_packages %}
<tr> <tr>
<th>{{ h.link_to(package.title or package.name, h.url_for(controller='package', action='read', id=package.name)) }}</th> <th>{{ h.link_to(package.title or package.name, h.url_for(controller='package', action='read', id=package.name)) }}</th>
<td class="metric">{{ rating }}</td> <td class="metric">{{ rating }}</td>
<td class="metric">{{ num_ratings }}</td> <td class="metric">{{ num_ratings }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% else %} {% else %}
<p class="empty">{{ _('No ratings') }}</p> <p class="empty">{{ _('No ratings') }}</p>
{% endif %} {% endif %}
</section> </section>
   
<section id="stats-most-edited" class="module-content tab-content"> <section id="stats-most-edited" class="module-content tab-content">
<h2>{{ _('Most Edited Datasets') }}</h2> <h2>{{ _('Most Edited Datasets') }}</h2>
{% if c.most_edited_packages %} {% if c.most_edited_packages %}
<table class="table table-chunky table-bordered table-striped"> <table class="table table-chunky table-bordered table-striped">
<thead> <thead>
<tr> <tr>
<th>{{ _('Dataset') }}</th> <th>{{ _('Dataset') }}</th>
<th class="metric">{{ _('Number of edits') }}</th> <th class="metric">{{ _('Number of edits') }}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for package, edits in c.most_edited_packages %} {% for package, edits in c.most_edited_packages %}
<tr py:for="package, edits in c.most_edited_packages"> <tr py:for="package, edits in c.most_edited_packages">
<td>{{ h.link_to(package.title or package.name, h.url_for(controller='package', action='read', id=package.name)) }}</td> <td>{{ h.link_to(package.title or package.name, h.url_for(controller='package', action='read', id=package.name)) }}</td>
<td class="metric">{{ edits }}</td> <td class="metric">{{ edits }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% else %} {% else %}
<p class="empty">{{ _('No edited datasets') }}</p> <p class="empty">{{ _('No edited datasets') }}</p>
{% endif %} {% endif %}
</section> </section>
   
<section id="stats-largest-groups" class="module-content tab-content"> <section id="stats-largest-groups" class="module-content tab-content">
<h2>{{ _('Largest Groups') }}</h2> <h2>{{ _('Largest Groups') }}</h2>
{% if c.largest_groups %} {% if c.largest_groups %}
<table class="table table-chunky table-bordered table-striped"> <table class="table table-chunky table-bordered table-striped">
<thead> <thead>
<tr> <tr>
<th>{{ _('Group') }}</th> <th>{{ _('Group') }}</th>
<th class="metric">{{ _('Number of datasets') }}</th> <th class="metric">{{ _('Number of datasets') }}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for group, num_packages in c.largest_groups %} {% for group, num_packages in c.largest_groups %}
<tr> <tr>
<td>{{ h.link_to(group.title or group.name, h.url_for(controller='group', action='read', id=group.name)) }}</td> <td>{{ h.link_to(group.title or group.name, h.url_for(controller='group', action='read', id=group.name)) }}</td>
<td class="metric">{{ num_packages }}</td> <td class="metric">{{ num_packages }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
{% else %} {% else %}
<p class="empty">{{ _('No groups') }}</p> <p class="empty">{{ _('No groups') }}</p>
{% endif %}  
</section>  
<section id="stats-largest-groups" class="module-content tab-content">  
<h2>{{ _('Largest Groups') }}</h2>  
{% if c.largest_groups %}  
<table class="table table-chunky table-bordered table-striped">  
<thead>  
<tr>  
<th>{{ _('Group') }}</th>  
<th class="metric">{{ _('Number of datasets') }}</th>  
</tr>  
</thead>  
<tbody>  
{% for group, num_packages in c.largest_groups %}  
<tr>  
<td>{{ h.link_to(group.title or group.name, h.url_for(controller='group', action='read', id=group.name)) }}</td>  
<td class="metric">{{ num_packages }}</td>  
</tr>  
{% endfor %}  
</tbody>  
</table>  
{% else %}  
<p class="empty">{{ _('No groups') }}</p>  
{% endif %} {% endif %}
</section> </section>
   
<section id="stats-top-tags" class="module-content tab-content"> <section id="stats-top-tags" class="module-content tab-content">
<h2>{{ _('Top Tags') }}</h2> <h2>{{ _('Top Tags') }}</h2>
<table class="table table-chunky table-bordered table-striped"> <table class="table table-chunky table-bordered table-striped">
<thead> <thead>
<tr> <tr>
<th>{{ _('Tag Name') }}</th> <th>{{ _('Tag Name') }}</th>
<th class="metric">{{ _('Number of Datasets') }}</th> <th class="metric">{{ _('Number of Datasets') }}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for tag, num_packages in c.top_tags %} {% for tag, num_packages in c.top_tags %}
<tr> <tr>
<td>{{ h.link_to(tag.name, h.url_for(controller='package', action='search', tags=tag.name)) }}</td> <td>{{ h.link_to(tag.name, h.url_for(controller='package', action='search', tags=tag.name)) }}</td>
<td class="metric">{{ num_packages }}</td> <td class="metric">{{ num_packages }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</section> </section>
   
<section id="stats-most-owned" class="module-content tab-content"> <section id="stats-most-owned" class="module-content tab-content">
<h2>{{ _('Users Owning Most Datasets') }}</h2> <h2>{{ _('Users Owning Most Datasets') }}</h2>
<table class="table table-chunky table-bordered table-striped"> <table class="table table-chunky table-bordered table-striped">
<thead> <thead>
<tr> <tr>
<th>{{ _('User') }}</th> <th>{{ _('User') }}</th>
<th class="metric">{{ _('Number of Datasets') }}</th> <th class="metric">{{ _('Number of Datasets') }}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for user, num_packages in c.top_package_owners %} {% for user, num_packages in c.top_package_owners %}
<tr> <tr>
<td class="media">{{ h.linked_user(user) }}</td> <td class="media">{{ h.linked_user(user) }}</td>
<td class="metric">{{ num_packages }}</td> <td class="metric">{{ num_packages }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>
</table> </table>
</section> </section>
  <section id="stats-by-org" class="module-content tab-content">
  <h2>{{ _('Datasets by Organization') }}</h2>
  {% if c.by_org %}
  <table class="table table-chunky table-bordered table-striped">
  <thead>
  <tr>
  <th>{{ _('Group') }}</th>
  <th>{{ _('Public/Archived') }}</th>
  <th class="metric">{{ _('Number of datasets') }}</th>
  </tr>
  </thead>
  <tbody>
  {% for group,private, num_packages in c.by_org %}
  <tr>
  <td>{{ h.link_to(group.title or group.name, h.url_for(controller='organization', action='read', id=group.name)) }}</td>
  {% if private == True %}
  <td>Archived</td>
  {% else %}
  <td>Public</td>
  {% endif %}
  <td class="metric">{{ num_packages }}</td>
  </tr>
  {% endfor %}
  </tbody>
  </table>
  {% else %}
  <p class="empty">{{ _('No groups') }}</p>
  {% endif %}
  </section>
   
</article> </article>
{% endblock %} {% endblock %}
   
{% block secondary_content %} {% block secondary_content %}
<section class="module module-narrow"> <section class="module module-narrow">
<h2 class="module-heading"><i class="icon-bar-chart icon-medium"></i> {{ _('Statistics Menu') }}</h2> <h2 class="module-heading"><i class="icon-bar-chart icon-medium"></i> {{ _('Statistics Menu') }}</h2>
<nav data-module="stats-nav"> <nav data-module="stats-nav">
<ul class="unstyled nav nav-simple"> <ul class="unstyled nav nav-simple">
<li class="nav-item"><a href="#summary" data-toggle="tab">{{ _('Summary') }}</a></li> <li class="nav-item"><a href="#summary" data-toggle="tab">{{ _('Summary') }}</a></li>
<li class="nav-item"><a href="#activity-counts" data-toggle="tab">{{ _('Site Activity Log') }}</a></li> <li class="nav-item"><a href="#activity-counts" data-toggle="tab">{{ _('Site Activity Log') }}</a></li>
<li class="nav-item active"><a href="#stats-total-datasets" data-toggle="tab">{{ _('Total Number of Datasets') }}</a></li> <li class="nav-item active"><a href="#stats-total-datasets" data-toggle="tab">{{ _('Total Number of Datasets') }}</a></li>
<li class="nav-item"><a href="#stats-dataset-revisions" data-toggle="tab">{{ _('Dataset Revisions per Week') }}</a></li> <li class="nav-item"><a href="#stats-dataset-revisions" data-toggle="tab">{{ _('Dataset Revisions per Week') }}</a></li>
<li class="nav-item"><a href="#stats-top-rated" data-toggle="tab">{{ _('Top Rated Datasets') }}</a></li> <!-- <li class="nav-item"><a href="#stats-top-rated" data-toggle="tab">{{ _('Top Rated Datasets') }}</a></li> -->
<li class="nav-item"><a href="#stats-most-edited" data-toggle="tab">{{ _('Most Edited Datasets') }}</a></li> <li class="nav-item"><a href="#stats-most-edited" data-toggle="tab">{{ _('Most Edited Datasets') }}</a></li>
<li class="nav-item"><a href="#stats-largest-groups" data-toggle="tab">{{ _('Largest Groups') }}</a></li>  
<li class="nav-item"><a href="#stats-top-tags" data-toggle="tab">{{ _('Top Tags') }}</a></li> <li class="nav-item"><a href="#stats-top-tags" data-toggle="tab">{{ _('Top Tags') }}</a></li>
<li class="nav-item"><a href="#stats-most-owned" data-toggle="tab">{{ _('Users Owning Most Datasets') }}</a></li> <li class="nav-item"><a href="#stats-most-owned" data-toggle="tab">{{ _('Users Owning Most Datasets') }}</a></li>
  <li class="nav-item"><a href="#stats-by-org" data-toggle="tab">{{ _('Datasets by Organization') }}</a></li>
</ul> </ul>
</nav> </nav>
</section> </section>
{% endblock %} {% endblock %}
   
{% block scripts %} {% block scripts %}
{{ super() }} {{ super() }}
{# {#
Hellish hack to get excanvas to work in IE8. We disable html5shiv from Hellish hack to get excanvas to work in IE8. We disable html5shiv from
overriding the createElement() method on this page. overriding the createElement() method on this page.
See: http://stackoverflow.com/questions/10208062/using-flot-with-bootstrap-ie8-incompatibility See: http://stackoverflow.com/questions/10208062/using-flot-with-bootstrap-ie8-incompatibility
#} #}
{% resource "vendor/block_html5_shim" %} {% resource "vendor/block_html5_shim" %}
{% resource "ckanext_dga_stats/stats" %} {% resource "ckanext_dga_stats/stats" %}
{% endblock %} {% endblock %}