diff --git a/app/main/views/platform_admin.py b/app/main/views/platform_admin.py index b23277674..37abf57a0 100644 --- a/app/main/views/platform_admin.py +++ b/app/main/views/platform_admin.py @@ -238,10 +238,10 @@ def live_services_csv(): } -@main.route("/platform-admin/reports/performance-platform.csv") +@main.route("/platform-admin/reports/performance-platform.xlsx") @login_required @user_is_platform_admin -def performance_platform_csv(): +def performance_platform_xlsx(): results = service_api_client.get_live_services_data()["data"] live_services_columns = ["service_id", "agency", "service_name", "_timestamp", "service", "count"] live_services_data = [] @@ -258,9 +258,9 @@ def performance_platform_csv(): 1 ]) - return Spreadsheet.from_rows(live_services_data).as_csv_data, 200, { - 'Content-Type': 'text/csv; charset=utf-8', - 'Content-Disposition': 'inline; filename="{} performance platform report.csv"'.format( + return Spreadsheet.from_rows(live_services_data).as_excel_file, 200, { + 'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'Content-Disposition': 'attachment; filename="{} performance platform report.xlsx"'.format( format_date_numeric(datetime.now().strftime("%Y-%m-%dT%H:%M:%S.%fZ")), ) } diff --git a/app/navigation.py b/app/navigation.py index ee7a5ac42..972323802 100644 --- a/app/navigation.py +++ b/app/navigation.py @@ -87,7 +87,7 @@ class HeaderNavigation(Navigation): 'live_services', 'live_services_csv', 'organisations', - 'performance_platform_csv', + 'performance_platform_xlsx', 'platform_admin', 'platform_admin_letter_validation_preview', 'platform_admin_list_complaints', @@ -496,7 +496,7 @@ class MainNavigation(Navigation): 'organisation_preview_letter_branding', 'organisation_settings', 'organisations', - 'performance_platform_csv', + 'performance_platform_xlsx', 'platform_admin', 'platform_admin_letter_validation_preview', 'platform_admin_list_complaints', @@ -713,7 +713,7 @@ class CaseworkNavigation(Navigation): 'organisation_preview_letter_branding', 'organisation_settings', 'organisations', - 'performance_platform_csv', + 'performance_platform_xlsx', 'platform_admin_letter_validation_preview', 'platform_admin_list_complaints', 'platform_admin_reports', @@ -970,7 +970,7 @@ class OrgNavigation(Navigation): 'old_terms', 'old_using_notify', 'organisations', - 'performance_platform_csv', + 'performance_platform_xlsx', 'platform_admin', 'platform_admin_letter_validation_preview', 'platform_admin_list_complaints', diff --git a/app/templates/views/platform-admin/reports.html b/app/templates/views/platform-admin/reports.html index 2135c388c..bed419859 100644 --- a/app/templates/views/platform-admin/reports.html +++ b/app/templates/views/platform-admin/reports.html @@ -15,6 +15,6 @@

- Download performance platform csv report + Download performance platform report (.xlsx)

{% endblock %} diff --git a/app/utils.py b/app/utils.py index ce47074cf..83774dc06 100644 --- a/app/utils.py +++ b/app/utils.py @@ -4,7 +4,7 @@ import re import unicodedata from datetime import datetime, time, timedelta, timezone from functools import wraps -from io import StringIO +from io import BytesIO, StringIO from itertools import chain from os import path from urllib.parse import urlparse @@ -12,6 +12,7 @@ from urllib.parse import urlparse import ago import dateutil import pyexcel +import pyexcel_xlsx import yaml from flask import abort, current_app, redirect, request, session, url_for from flask_login import current_user @@ -278,6 +279,20 @@ class Spreadsheet(): pyexcel.free_resources() return instance + @property + def as_rows(self): + return list(csv.reader( + self.as_csv_data.strip().splitlines(), + quoting=csv.QUOTE_MINIMAL, + skipinitialspace=True, + )) + + @property + def as_excel_file(self): + io = BytesIO() + pyexcel_xlsx.save_data(io, {'Sheet 1': self.as_rows}) + return io.getvalue() + def get_help_argument(): return request.args.get('help') if request.args.get('help') in ('1', '2', '3') else None diff --git a/tests/app/main/views/test_platform_admin.py b/tests/app/main/views/test_platform_admin.py index 6cb8c965b..d3aabed6b 100644 --- a/tests/app/main/views/test_platform_admin.py +++ b/tests/app/main/views/test_platform_admin.py @@ -4,6 +4,7 @@ import uuid from functools import partial from unittest.mock import ANY, call +import pyexcel import pytest import requests_mock from bs4 import BeautifulSoup @@ -946,8 +947,8 @@ def test_reports_page( ).attrs['href'] == '/platform-admin/reports/live-services.csv' assert page.find( - 'a', text="Download performance platform csv report" - ).attrs['href'] == '/platform-admin/reports/performance-platform.csv' + 'a', text="Download performance platform report (.xlsx)" + ).attrs['href'] == '/platform-admin/reports/performance-platform.xlsx' def test_get_live_services_report(client, platform_admin_user, mocker): @@ -1000,11 +1001,13 @@ def test_get_performance_platform_report(client, platform_admin_user, mocker): 'letter_volume_intent': 0, 'sms_totals': 0, 'email_totals': 0, 'letter_totals': 0}, ]} ) - response = client.get(url_for('main.performance_platform_csv')) + response = client.get(url_for('main.performance_platform_xlsx')) assert response.status_code == 200 - report = response.get_data(as_text=True) - assert report.strip() == ( - 'service_id,agency,service_name,_timestamp,service,count' - + '\r\n1,Forest,jessie the oak tree,2014-03-29T00:00:00Z,govuk-notify,1' - + '\r\n2,Forest,james the pine tree,,govuk-notify,1' - ) + assert pyexcel.get_array( + file_type='xlsx', + file_stream=response.get_data(), + ) == [ + ['service_id', 'agency', 'service_name', '_timestamp', 'service', 'count'], + ['1', 'Forest', 'jessie the oak tree', '2014-03-29T00:00:00Z', 'govuk-notify', '1'], + ['2', 'Forest', 'james the pine tree', '', 'govuk-notify', '1'], + ]