Add support for downloading batch rows as XLSX file

This commit is contained in:
Lance Edgar 2019-02-05 18:18:02 -06:00
parent 10c30cd21a
commit 6cfc72c875
3 changed files with 75 additions and 0 deletions

View file

@ -67,6 +67,9 @@
% if master.has_rows and master.rows_downloadable_csv and request.has_perm('{}.row_results_csv'.format(permission_prefix)): % if master.has_rows and master.rows_downloadable_csv and request.has_perm('{}.row_results_csv'.format(permission_prefix)):
<li>${h.link_to("Download row results as CSV", url('{}.row_results_csv'.format(route_prefix), uuid=instance.uuid))}</li> <li>${h.link_to("Download row results as CSV", url('{}.row_results_csv'.format(route_prefix), uuid=instance.uuid))}</li>
% endif % endif
% if master.has_rows and master.rows_downloadable_xlsx and request.has_perm('{}.row_results_xlsx'.format(permission_prefix)):
<li>${h.link_to("Download row results as XLSX", url('{}.row_results_xlsx'.format(route_prefix), uuid=instance.uuid))}</li>
% endif
</%def> </%def>
<%def name="object_helpers()"></%def> <%def name="object_helpers()"></%def>

View file

@ -74,6 +74,7 @@ class BatchMasterView(MasterView):
has_rows = True has_rows = True
rows_deletable = True rows_deletable = True
rows_downloadable_csv = True rows_downloadable_csv = True
rows_downloadable_xlsx = True
refreshable = True refreshable = True
refresh_after_create = False refresh_after_create = False
cloneable = False cloneable = False
@ -1333,6 +1334,9 @@ class BatchMasterView(MasterView):
def get_row_results_csv_filename(self, batch): def get_row_results_csv_filename(self, batch):
return '{}.{}.csv'.format(self.get_route_prefix(), batch.id_str) return '{}.{}.csv'.format(self.get_route_prefix(), batch.id_str)
def get_row_results_xlsx_filename(self, batch):
return '{}.{}.xlsx'.format(self.get_route_prefix(), batch.id_str)
def clone(self): def clone(self):
""" """
Clone current batch as new batch Clone current batch as new batch

View file

@ -45,6 +45,7 @@ from rattail.threads import Thread
from rattail.csvutil import UnicodeDictWriter from rattail.csvutil import UnicodeDictWriter
from rattail.files import temp_path from rattail.files import temp_path
from rattail.excel import ExcelWriter from rattail.excel import ExcelWriter
from rattail.gpc import GPC
import colander import colander
import deform import deform
@ -141,6 +142,7 @@ class MasterView(View):
rows_bulk_deletable = False rows_bulk_deletable = False
rows_default_pagesize = 20 rows_default_pagesize = 20
rows_downloadable_csv = False rows_downloadable_csv = False
rows_downloadable_xlsx = False
mobile_rows_creatable = False mobile_rows_creatable = False
mobile_rows_creatable_via_browse = False mobile_rows_creatable_via_browse = False
@ -2354,6 +2356,64 @@ class MasterView(View):
row[field] = getattr(obj, field, None) row[field] = getattr(obj, field, None)
return row return row
def row_results_xlsx(self):
"""
Download current *row* results as XLSX.
"""
obj = self.get_instance()
results = self.get_effective_row_data(sort=True)
fields = self.get_row_xlsx_fields()
path = temp_path(suffix='.xlsx')
writer = ExcelWriter(path, fields, sheet_title=self.get_row_model_title_plural())
writer.write_header()
rows = []
for row_obj in results:
data = self.get_row_xlsx_row(row_obj, fields)
row = [data[field] for field in fields]
rows.append(row)
writer.write_rows(rows)
writer.auto_freeze()
writer.auto_filter()
writer.auto_resize()
writer.save()
response = self.request.response
with open(path, 'rb') as f:
response.body = f.read()
os.remove(path)
response.content_length = len(response.body)
response.content_type = str('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
filename = self.get_row_results_xlsx_filename(obj)
response.content_disposition = str('attachment; filename={}'.format(filename))
return response
def get_row_xlsx_fields(self):
"""
Return the list of row fields to be written to XLSX download.
"""
# TODO: should this be shared at all? in a better way?
return self.get_row_csv_fields()
def get_row_xlsx_row(self, row, fields):
"""
Return a dict for use when writing the row's data to XLSX download.
"""
xlrow = {}
for field in fields:
value = getattr(row, field, None)
if isinstance(value, GPC):
value = six.text_type(value)
xlrow[field] = value
return xlrow
def get_row_results_xlsx_filename(self, obj):
return '{}.xlsx'.format(self.get_row_grid_key())
def row_results_csv(self): def row_results_csv(self):
""" """
Download current row results data for an object, as CSV Download current row results data for an object, as CSV
@ -3265,6 +3325,14 @@ class MasterView(View):
config.add_view(cls, attr='row_results_csv', route_name='{}.row_results_csv'.format(route_prefix), config.add_view(cls, attr='row_results_csv', route_name='{}.row_results_csv'.format(route_prefix),
permission='{}.row_results_csv'.format(permission_prefix)) permission='{}.row_results_csv'.format(permission_prefix))
# download row results as Excel
if cls.has_rows and cls.rows_downloadable_xlsx:
config.add_tailbone_permission(permission_prefix, '{}.row_results_xlsx'.format(permission_prefix),
"Download {} results as XLSX".format(row_model_title))
config.add_route('{}.row_results_xlsx'.format(route_prefix), '{}/{{uuid}}/rows-xlsx'.format(url_prefix))
config.add_view(cls, attr='row_results_xlsx', route_name='{}.row_results_xlsx'.format(route_prefix),
permission='{}.row_results_xlsx'.format(permission_prefix))
# create row # create row
if cls.has_rows: if cls.has_rows:
if cls.rows_creatable or cls.mobile_rows_creatable: if cls.rows_creatable or cls.mobile_rows_creatable: