Add progress for generating "results as XLSX" file to download
This commit is contained in:
parent
32b98ae818
commit
7d8c57170f
|
@ -295,6 +295,13 @@
|
||||||
${parent.modify_this_page_vars()}
|
${parent.modify_this_page_vars()}
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
|
|
||||||
|
## TODO: stop checking for buefy here once we only have the one session.pop()
|
||||||
|
% if use_buefy and request.session.pop('{}.results_xlsx.generated'.format(route_prefix), False):
|
||||||
|
ThisPage.mounted = function() {
|
||||||
|
location.href = '${url('{}.results_xlsx_download'.format(route_prefix))}';
|
||||||
|
}
|
||||||
|
% endif
|
||||||
|
|
||||||
## delete single object
|
## delete single object
|
||||||
% if master.deletable and master.has_perm('delete') and master.delete_confirm == 'simple':
|
% if master.deletable and master.has_perm('delete') and master.delete_confirm == 'simple':
|
||||||
ThisPage.methods.deleteObject = function(url) {
|
ThisPage.methods.deleteObject = function(url) {
|
||||||
|
@ -453,4 +460,14 @@
|
||||||
${h.csrf_token(request)}
|
${h.csrf_token(request)}
|
||||||
${h.end_form()}
|
${h.end_form()}
|
||||||
% endif
|
% endif
|
||||||
|
|
||||||
|
## TODO: can stop checking for buefy above once this legacy chunk is gone
|
||||||
|
% if request.session.pop('{}.results_xlsx.generated'.format(route_prefix), False):
|
||||||
|
<script type="text/javascript">
|
||||||
|
$(function() {
|
||||||
|
location.href = '${url('{}.results_xlsx_download'.format(route_prefix))}';
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
% endif
|
||||||
|
|
||||||
% endif
|
% endif
|
||||||
|
|
|
@ -2704,32 +2704,100 @@ class MasterView(View):
|
||||||
Download current list results as XLSX.
|
Download current list results as XLSX.
|
||||||
"""
|
"""
|
||||||
results = self.get_effective_data()
|
results = self.get_effective_data()
|
||||||
fields = self.get_xlsx_fields()
|
|
||||||
path = temp_path(suffix='.xlsx')
|
|
||||||
writer = ExcelWriter(path, fields, sheet_title=self.get_model_title_plural())
|
|
||||||
writer.write_header()
|
|
||||||
|
|
||||||
rows = []
|
# start thread to actually do work / generate progress data
|
||||||
for obj in results:
|
route_prefix = self.get_route_prefix()
|
||||||
data = self.get_xlsx_row(obj, fields)
|
key = '{}.results_xlsx'.format(route_prefix)
|
||||||
row = [data[field] for field in fields]
|
progress = self.make_progress(key)
|
||||||
rows.append(row)
|
thread = Thread(target=self.results_xlsx_thread,
|
||||||
|
args=(results, self.request.user.uuid, progress))
|
||||||
|
thread.start()
|
||||||
|
|
||||||
writer.write_rows(rows)
|
# send user to progress page
|
||||||
writer.auto_freeze()
|
return self.render_progress(progress, {
|
||||||
writer.auto_filter()
|
'cancel_url': self.get_index_url(),
|
||||||
writer.auto_resize()
|
'cancel_msg': "XLSX download was canceled.",
|
||||||
writer.save()
|
})
|
||||||
|
|
||||||
response = self.request.response
|
def results_xlsx_session(self):
|
||||||
with open(path, 'rb') as f:
|
return RattailSession()
|
||||||
response.body = f.read()
|
|
||||||
os.remove(path)
|
|
||||||
|
|
||||||
response.content_length = len(response.body)
|
def results_xlsx_thread(self, results, user_uuid, progress):
|
||||||
response.content_type = str('application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
|
"""
|
||||||
response.content_disposition = str('attachment; filename={}.xlsx').format(self.get_grid_key())
|
Thread target, responsible for actually generating the Excel file which
|
||||||
return response
|
is to be presented for download.
|
||||||
|
"""
|
||||||
|
route_prefix = self.get_route_prefix()
|
||||||
|
session = self.results_xlsx_session()
|
||||||
|
try:
|
||||||
|
|
||||||
|
# create folder(s) for output; make sure file doesn't exist
|
||||||
|
path = os.path.join(self.rattail_config.datadir(), 'downloads',
|
||||||
|
'results-xlsx', route_prefix,
|
||||||
|
user_uuid[:2], user_uuid[2:])
|
||||||
|
if not os.path.exists(path):
|
||||||
|
os.makedirs(path)
|
||||||
|
path = os.path.join(path, '{}.xlsx'.format(route_prefix))
|
||||||
|
if os.path.exists(path):
|
||||||
|
os.remove(path)
|
||||||
|
|
||||||
|
results = results.with_session(session).all()
|
||||||
|
fields = self.get_xlsx_fields()
|
||||||
|
writer = ExcelWriter(path, fields, sheet_title=self.get_model_title_plural())
|
||||||
|
writer.write_header()
|
||||||
|
|
||||||
|
rows = []
|
||||||
|
def write(obj, i):
|
||||||
|
data = self.get_xlsx_row(obj, fields)
|
||||||
|
row = [data[field] for field in fields]
|
||||||
|
rows.append(row)
|
||||||
|
|
||||||
|
self.progress_loop(write, results, progress,
|
||||||
|
message="Collecting data for Excel")
|
||||||
|
|
||||||
|
def finalize(x, i):
|
||||||
|
writer.write_rows(rows)
|
||||||
|
writer.auto_freeze()
|
||||||
|
writer.auto_filter()
|
||||||
|
writer.auto_resize()
|
||||||
|
writer.save()
|
||||||
|
|
||||||
|
self.progress_loop(finalize, [1], progress,
|
||||||
|
message="Writing Excel file to disk")
|
||||||
|
|
||||||
|
except Exception as error:
|
||||||
|
msg = "generating XLSX file for download failed!"
|
||||||
|
log.warning(msg, exc_info=True)
|
||||||
|
session.rollback()
|
||||||
|
session.close()
|
||||||
|
if progress:
|
||||||
|
progress.session.load()
|
||||||
|
progress.session['error'] = True
|
||||||
|
progress.session['error_msg'] = "{}: {}".format(
|
||||||
|
msg, simple_error(error))
|
||||||
|
progress.session.save()
|
||||||
|
return
|
||||||
|
|
||||||
|
session.commit()
|
||||||
|
session.close()
|
||||||
|
|
||||||
|
if progress:
|
||||||
|
progress.session.load()
|
||||||
|
progress.session['complete'] = True
|
||||||
|
progress.session['success_url'] = self.get_index_url()
|
||||||
|
progress.session['extra_session_bits'] = {
|
||||||
|
'{}.results_xlsx.generated'.format(route_prefix): True,
|
||||||
|
}
|
||||||
|
progress.session.save()
|
||||||
|
|
||||||
|
def results_xlsx_download(self):
|
||||||
|
route_prefix = self.get_route_prefix()
|
||||||
|
user_uuid = self.request.user.uuid
|
||||||
|
path = os.path.join(self.rattail_config.datadir(), 'downloads',
|
||||||
|
'results-xlsx', route_prefix,
|
||||||
|
user_uuid[:2], user_uuid[2:],
|
||||||
|
'{}.xlsx'.format(route_prefix))
|
||||||
|
return self.file_response(path)
|
||||||
|
|
||||||
def get_xlsx_fields(self):
|
def get_xlsx_fields(self):
|
||||||
"""
|
"""
|
||||||
|
@ -3679,6 +3747,9 @@ class MasterView(View):
|
||||||
config.add_route('{}.results_xlsx'.format(route_prefix), '{}/xlsx'.format(url_prefix))
|
config.add_route('{}.results_xlsx'.format(route_prefix), '{}/xlsx'.format(url_prefix))
|
||||||
config.add_view(cls, attr='results_xlsx', route_name='{}.results_xlsx'.format(route_prefix),
|
config.add_view(cls, attr='results_xlsx', route_name='{}.results_xlsx'.format(route_prefix),
|
||||||
permission='{}.results_xlsx'.format(permission_prefix))
|
permission='{}.results_xlsx'.format(permission_prefix))
|
||||||
|
config.add_route('{}.results_xlsx_download'.format(route_prefix), '{}/xlsx/download'.format(url_prefix))
|
||||||
|
config.add_view(cls, attr='results_xlsx_download', route_name='{}.results_xlsx_download'.format(route_prefix),
|
||||||
|
permission='{}.results_xlsx'.format(permission_prefix))
|
||||||
|
|
||||||
# quickie (search)
|
# quickie (search)
|
||||||
if cls.supports_quickie_search:
|
if cls.supports_quickie_search:
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
################################################################################
|
################################################################################
|
||||||
#
|
#
|
||||||
# Rattail -- Retail Software Framework
|
# Rattail -- Retail Software Framework
|
||||||
# Copyright © 2010-2017 Lance Edgar
|
# Copyright © 2010-2020 Lance Edgar
|
||||||
#
|
#
|
||||||
# This file is part of Rattail.
|
# This file is part of Rattail.
|
||||||
#
|
#
|
||||||
|
@ -26,18 +26,31 @@ Progress Views
|
||||||
|
|
||||||
from __future__ import unicode_literals, absolute_import
|
from __future__ import unicode_literals, absolute_import
|
||||||
|
|
||||||
|
import six
|
||||||
|
|
||||||
from tailbone.progress import get_progress_session
|
from tailbone.progress import get_progress_session
|
||||||
|
|
||||||
|
|
||||||
def progress(request):
|
def progress(request):
|
||||||
key = request.matchdict['key']
|
key = request.matchdict['key']
|
||||||
session = get_progress_session(request, key, type=request.GET.get('sessiontype'))
|
session = get_progress_session(request, key,
|
||||||
|
type=request.GET.get('sessiontype'))
|
||||||
|
|
||||||
if session.get('complete'):
|
if session.get('complete'):
|
||||||
|
|
||||||
msg = session.get('success_msg')
|
msg = session.get('success_msg')
|
||||||
if msg:
|
if msg:
|
||||||
request.session.flash(msg)
|
request.session.flash(msg)
|
||||||
|
|
||||||
|
bits = session.get('extra_session_bits')
|
||||||
|
if bits:
|
||||||
|
for key, value in six.iteritems(bits):
|
||||||
|
request.session[key] = value
|
||||||
|
|
||||||
elif session.get('error'):
|
elif session.get('error'):
|
||||||
request.session.flash(session.get('error_msg', "An unspecified error occurred."), 'error')
|
msg = session.get('error_msg', "An unspecified error occurred.")
|
||||||
|
request.session.flash(msg, 'error')
|
||||||
|
|
||||||
return session
|
return session
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue