Skip to content

Commit

Permalink
HDX-10191 org stats download as xlsx
Browse files Browse the repository at this point in the history
  • Loading branch information
danmihaila committed Oct 31, 2024
1 parent 77095eb commit b64aefd
Show file tree
Hide file tree
Showing 8 changed files with 328 additions and 133 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@
import logging
import os
import six

import openpyxl
import ckanext.hdx_search.cli.click_feature_search_command as lunr
import ckanext.hdx_theme.helpers.helpers as h
import ckanext.hdx_users.helpers.mailer as hdx_mailer
from sqlalchemy import func
import ckanext.hdx_org_group.helpers.static_lists as static_lists

from flask import make_response
from tempfile import NamedTemporaryFile
import ckan.lib.dictization as dictization
import ckan.lib.dictization.model_dictize as model_dictize
import ckan.lib.dictization.model_save as model_save
Expand All @@ -30,6 +31,7 @@
from ckan.common import _, c, config
import ckan.plugins.toolkit as toolkit
import ckan.lib.base as base
import ckanext.hdx_theme.util.jql as jql

BUCKET = str(uploader.get_storage_path()) + '/storage/uploads/group/'
abort = base.abort
Expand Down Expand Up @@ -799,3 +801,43 @@ def org_add_last_updated_field(displayed_orgs):
def hdx_organization_type_get_value(org_type_key):
return next((org_type[0] for org_type in static_lists.ORGANIZATION_TYPE_LIST if org_type[1] == org_type_key),
org_type_key)

def hdx_generate_organization_stats(org_dict):
# Define variable to load the dataframe
wb = openpyxl.Workbook()

# Create SheetOne with Data
sheetOne = wb.create_sheet("Downloads and Page Views")

result = jql.pageviews_downloads_per_organization_last_5_years(org_dict.get('id'))
data = [('Date', 'Page View - Unique', 'Page Views - Total', 'Resource Download - Unique', 'Resource Download - Total')]
for key, value in result.items():
data.append((key, value.get('pageviews_unique'), value.get('pageviews_total'), value.get('downloads_unique'), value.get('downloads_total')))
for item in data:
sheetOne.append(item)

# Create SheetTwo with Data
sheetTwo = wb.create_sheet("README")

data = [('Overview', 'This spreadsheet contains the number of downloads of files and page views of datasets of the organization, tracked monthly over the past 4 years.'),
('Data Source', 'The data has been sourced from the analytics platform Mixpanel.'),
('Contents', 'The spreadsheet includes the following information: 1. Page Views: Total page views by month. 2. Downloads: Total number of downloads by month.'),
('Caveats', 'To ensure accurate data representation, we have excluded as much bot traffic as possible.'),
('Update Frequency', 'The spreadsheet is refreshed automatically on the first day of each month.'),
('Contact', 'For additional inquiries, please contact us at [email protected]')]

for item in data:
sheetTwo.append(item)

# Remove default Sheet
wb.remove(wb['Sheet'])

# Iterate the loop to read the cell values
with NamedTemporaryFile() as tmp:
wb.save(tmp)
tmp.seek(0)
output = make_response(tmp.read())
output.headers['Content-Type'] = 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
output.headers['Content-Disposition'] = f'attachment; filename="{org_dict.get("name")}_stats.xlsx"'

return output
2 changes: 1 addition & 1 deletion ckanext-hdx_org_group/ckanext/hdx_org_group/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def group_types(self):

# IGroupForm
def setup_template_variables(self, context, data_dict):
org.new_org_template_variables(context, data_dict)
org.new_org_template_variables(data_dict)

# IValidators
def get_validators(self):
Expand Down
74 changes: 51 additions & 23 deletions ckanext-hdx_org_group/ckanext/hdx_org_group/views/organization.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,19 @@

from flask import Blueprint
from six.moves.urllib.parse import urlencode

from ckan.types import Context
import ckan.lib.plugins as lib_plugins
import ckan.model as model
import ckan.plugins.toolkit as tk
import ckan.lib.plugins as lib_plugins

import ckanext.hdx_org_group.helpers.org_meta_dao as org_meta_dao
import ckanext.hdx_org_group.helpers.organization_helper as helper
import ckanext.hdx_org_group.helpers.static_lists as static_lists
import ckanext.hdx_theme.helpers.helpers as hdx_helpers

from ckan.views.group import _get_group_template, CreateGroupView, EditGroupView
from ckan.views.group import CreateGroupView, EditGroupView, _get_group_template
from ckanext.hdx_org_group.controller_logic.organization_read_logic import OrgReadLogic
from ckanext.hdx_org_group.controller_logic.organization_stats_logic import OrganizationStatsLogic
from ckanext.hdx_org_group.controller_logic.organization_stats_logic import (
OrganizationStatsLogic,
)
from ckanext.hdx_org_group.views.light_organization import _index
from ckanext.hdx_theme.util.light_redirect import check_redirect_needed
from ckanext.hdx_theme.util.mail import NoRecipientException
Expand Down Expand Up @@ -46,7 +46,7 @@ def index():

@check_redirect_needed
def read(id):
context = {
context: Context = {
'model': model,
'session': model.Session,
'for_view': True,
Expand Down Expand Up @@ -82,19 +82,19 @@ def read(id):
}
template_file = _get_group_template('read_template', 'organization')
return render(template_file, template_data)
except NotFound as e:
except NotFound:
abort(404, _('Page not found'))
except NotAuthorized as e:
except NotAuthorized:
abort(403, _('Not authorized to see this page'))


def _generate_template_data_for_custom_org(org_read_logic):
'''
"""
:param org_read_logic:
:type org_read_logic: OrgReadLogic
:returns: the template data dict
:rtype: dict
'''
"""
org_meta = org_read_logic.org_meta
org_dict = org_meta.org_dict
org_id = org_dict['id']
Expand Down Expand Up @@ -147,7 +147,7 @@ def _generate_template_data_for_custom_org(org_read_logic):

},

# This is hear for compatibility with the custom_org_header.html template, which is still
# This is here for compatibility with the custom_org_header.html template, which is still
# used from pylon controllers
'org_meta': {
'id': org_dict['name'],
Expand Down Expand Up @@ -177,7 +177,7 @@ def _generate_template_data_for_custom_org(org_read_logic):


def request_new():
context = {'model': model, 'session': model.Session, 'user': g.user}
context: Context = {'model': model, 'session': model.Session, 'user': g.user}
try:
check_access('hdx_send_new_org_request', context)
except NotAuthorized:
Expand Down Expand Up @@ -231,7 +231,7 @@ def _process_new_org_request():

if hdx_org_type_code:
hdx_org_type = next(
(type[0] for type in static_lists.ORGANIZATION_TYPE_LIST if type[1] == hdx_org_type_code), '-1')
(_type[0] for _type in static_lists.ORGANIZATION_TYPE_LIST if _type[1] == hdx_org_type_code), '-1')

data = {
'name': request.form.get('name', ''),
Expand Down Expand Up @@ -271,7 +271,7 @@ def _transform_dict_for_mailing(data_dict):
return data_dict_for_mailing


def new_org_template_variables(context, data_dict):
def new_org_template_variables(data_dict):
data_dict['hdx_org_type_list'] = [{'value': '-1', 'text': _('-- Please select --')}] + \
[{'value': t[1], 'text': _(t[0])} for t in static_lists.ORGANIZATION_TYPE_LIST]

Expand All @@ -296,15 +296,15 @@ def stats(id):


def restore(id):
context = {
context: Context = {
'model': model, 'session': model.Session,
'user': g.user,
'for_edit': True,
}

try:
check_access('organization_patch', context, {'id': id})
except NotAuthorized as e:
except NotAuthorized:
return abort(403, _('Unauthorized to restore this organization'))

try:
Expand All @@ -329,7 +329,7 @@ def activity(id):


def activity_offset(id, offset=0):
'''
"""
Modified core functionality to use the new OrgMetaDao class
for fetching information needed on all org-related pages.
Expand All @@ -340,7 +340,7 @@ def activity_offset(id, offset=0):
:param offset:
:type offset: int
:return:
'''
"""
org_meta = org_meta_dao.OrgMetaDao(id, g.user, g.userobj)
org_meta.fetch_all()
org_dict = org_meta.org_dict
Expand All @@ -350,26 +350,53 @@ def activity_offset(id, offset=0):

# Add the group's activity stream (already rendered to HTML) to the
# template context for the group/read.html template to retrieve later.
context = {'model': model, 'session': model.Session,
context: Context = {'model': model, 'session': model.Session,
'user': g.user, 'for_view': True}
group_activity_stream = get_action('organization_activity_list')(
context, {'id': org_dict['id'], 'offset': offset})



extra_vars = {
'org_dict': org_dict,
'org_meta': org_meta,
'group_activity_stream': group_activity_stream,

}
template = None
if org_meta.is_custom:
template = 'organization/custom_activity_stream.html'
else:
template = lib_plugins.lookup_group_plugin('organization').activity_template()
return render(template, extra_vars)

def organization_stats(id):
"""
Handles downloading .xlsx organization stats
:returns: xlsx
"""

context: Context = {
'model': model,
'session': model.Session,
'user': g.user or g.author,
'auth_user_obj': g.userobj
}

try:
check_access('organization_update', context, {'id': id})
except NotAuthorized:
return abort(403, _('Unauthorized to restore this organization'))

# check if organization exists
try:
org_dict = get_action('organization_show')(context, {'id': id})
output = helper.hdx_generate_organization_stats(org_dict)
return output

except NotFound:
return abort(404, _('Organization not found'))
except NotAuthorized:
return abort(404, _('Organization not found'))


hdx_org.add_url_rule(u'/', view_func=index, strict_slashes=False)
hdx_org.add_url_rule(
Expand All @@ -387,3 +414,4 @@ def activity_offset(id, offset=0):
hdx_org.add_url_rule(u'/restore/<id>', view_func=restore, methods=[u'POST'])
hdx_org.add_url_rule(u'/activity/<id>', view_func=activity)
hdx_org.add_url_rule(u'/activity/<id>/<int:offset>', view_func=activity_offset, defaults={'offset': 0})
hdx_org.add_url_rule(u'/<id>/download_stats', view_func=organization_stats)
Loading

0 comments on commit b64aefd

Please sign in to comment.