backoffice: use a single page for all processing afterjob pages (#49772)

This commit is contained in:
Frédéric Péters 2020-12-29 16:18:06 +01:00
parent c006ee855c
commit bbcda9aa66
12 changed files with 110 additions and 207 deletions

View File

@ -113,7 +113,7 @@ def test_settings_export_import(pub):
resp = resp.form.submit('cancel')
resp = app.get('/backoffice/settings/export')
resp = resp.form.submit('submit')
assert resp.location.startswith('http://example.net/backoffice/settings/export?job=')
assert resp.location.startswith('http://example.net/backoffice/processing?job=')
job_id = urlparse.parse_qs(urlparse.urlparse(resp.location).query)['job'][0]
resp = resp.follow()
assert 'completed' in resp.text
@ -161,7 +161,7 @@ def test_settings_export_import(pub):
resp = app.get('/backoffice/settings/export')
resp = resp.form.submit('submit')
assert resp.location.startswith('http://example.net/backoffice/settings/export?job=')
assert resp.location.startswith('http://example.net/backoffice/processing?job=')
job_id = urlparse.parse_qs(urlparse.urlparse(resp.location).query)['job'][0]
resp = resp.follow()
resp = resp.click('Download Export')
@ -241,7 +241,7 @@ def test_settings_export_import(pub):
resp.form['datasources'] = False
resp.form['wscalls'] = False
resp = resp.form.submit('submit')
assert resp.location.startswith('http://example.net/backoffice/settings/export?job=')
assert resp.location.startswith('http://example.net/backoffice/processing?job=')
job_id = urlparse.parse_qs(urlparse.urlparse(resp.location).query)['job'][0]
resp = resp.follow()
resp = resp.click('Download Export')
@ -275,7 +275,7 @@ def test_settings_export_import(pub):
pub.write_cfg()
resp = app.get('/backoffice/settings/export')
resp = resp.form.submit('submit')
assert resp.location.startswith('http://example.net/backoffice/settings/export?job=')
assert resp.location.startswith('http://example.net/backoffice/processing?job=')
job_id = urlparse.parse_qs(urlparse.urlparse(resp.location).query)['job'][0]
resp = resp.follow()
resp = resp.click('Download Export')

View File

@ -356,7 +356,6 @@ def test_backoffice_cards_import_data_from_csv(pub):
'text/csv')
resp = resp.forms[0].submit().follow()
assert 'Importing data into cards' in resp
assert 'Column File will be ignored: type File Upload not supported.' in resp
assert carddef.data_class().count() == 149
card1, card2 = carddef.data_class().select(order_by='id')[:2]
assert card1.data['1'] == '48.81;2.37'

View File

@ -187,7 +187,7 @@ def test_backoffice_export_long_listings(pub, threshold):
app = login(get_app(pub))
resp = app.get('/backoffice/management/form-title/')
resp = resp.click('Export as CSV File')
assert resp.location.startswith('http://example.net/backoffice/management/form-title/export?job=')
assert resp.location.startswith('http://example.net/backoffice/processing?job=')
resp = resp.follow()
assert 'completed' in resp.text
resp = resp.click('Download Export')
@ -201,7 +201,7 @@ def test_backoffice_export_long_listings(pub, threshold):
resp = app.get('/backoffice/management/form-title/')
resp = resp.click('Export a Spreadsheet')
assert resp.location.startswith('http://example.net/backoffice/management/form-title/export?job=')
assert resp.location.startswith('http://example.net/backoffice/processing?job=')
job_id = urlparse.parse_qs(urlparse.urlparse(resp.location).query)['job'][0]
resp = resp.follow()
assert 'completed' in resp.text

View File

@ -1251,8 +1251,6 @@ class FormDefPage(Directory):
if get_publisher().is_using_postgresql():
raise TraversalError()
if get_request().form.get('job'):
return self.archive_processing()
if get_request().form.get('download'):
return self.archive_download()
@ -1319,7 +1317,10 @@ class FormDefPage(Directory):
job = get_response().add_after_job(
str(N_('Archiving forms')),
archiver.archive)
return redirect('archive?job=%s' % job.id)
job.done_action_url = self.formdef.get_admin_url() + 'archive?job=%s' % job.id
job.done_action_label = _('Download Archive')
job.store()
return redirect(job.get_processing_url())
else:
archiver.archive()
@ -1329,32 +1330,6 @@ class FormDefPage(Directory):
'attachment; filename=%s-archive.wcs' % self.formdef.url_name)
return archiver.fd.getvalue()
def archive_processing(self):
try:
job = AfterJob.get(get_request().form.get('job'))
except KeyError:
return redirect('.')
self.html_top(title=_('Archiving'))
r = TemplateIO(html=True)
r += get_session().display_message()
get_response().add_javascript(['jquery.js', 'afterjob.js'])
r += htmltext('<dl class="job-status">')
r += htmltext('<dt>')
r += _(job.label)
r += htmltext('</dt>')
r += htmltext('<dd>')
r += htmltext('<span class="afterjob" id="%s">') % job.id
r += _(job.status)
r += htmltext('</span>')
r += htmltext('</dd>')
r += htmltext('</dl>')
r += htmltext('<div class="done">')
r += htmltext('<a href="archive?download=%s">%s</a>') % (job.id, _('Download Archive'))
r += htmltext('</div>')
return r.getvalue()
def archive_download(self):
try:
job = AfterJob.get(get_request().form.get('download'))
@ -1370,9 +1345,6 @@ class FormDefPage(Directory):
return job.file_content
def anonymise(self):
if get_request().form.get('job'):
return self.anonymise_processing()
endpoints = []
for status in self.formdef.workflow.get_endpoint_status():
endpoints.append((str(status.id), status.name, str(status.id)))
@ -1432,38 +1404,15 @@ class FormDefPage(Directory):
job = get_response().add_after_job(
str(N_('Anonymising forms')),
anonymiser.anonymise)
return redirect('anonymise?job=%s' % job.id)
job.done_action_url = self.formdef.get_admin_url()
job.done_action_label = _('Back')
job.store()
return redirect(job.get_processing_url())
else:
anonymiser.anonymise()
return redirect('.')
def anonymise_processing(self):
try:
job = AfterJob.get(get_request().form.get('job'))
except KeyError:
return redirect('.')
html_top('forms', title=_('Anonymising'))
r = TemplateIO(html=True)
r += get_session().display_message()
get_response().add_javascript(['jquery.js', 'afterjob.js'])
r += htmltext('<dl class="job-status">')
r += htmltext('<dt>')
r += _(job.label)
r += htmltext('</dt>')
r += htmltext('<dd>')
r += htmltext('<span class="afterjob" id="%s">') % job.id
r += _(job.status)
r += htmltext('</span>')
r += htmltext('</dd>')
r += htmltext('</dl>')
r += htmltext('<div class="done">')
r += htmltext('<a href="./">%s</a>') % _('Back')
r += htmltext('</div>')
return r.getvalue()
def enable(self):
self.formdef.disabled = False
self.formdef.store(comment=_('Enable'))

View File

@ -858,8 +858,8 @@ class SettingsDirectory(QommonSettingsDirectory):
get_publisher().write_cfg()
def export(self):
if get_request().form.get('job') or get_request().form.get('download'):
return self.export_pending()
if get_request().form.get('download'):
return self.export_download()
form = Form(enctype="multipart/form-data")
form.add(CheckboxWidget, 'formdefs', title = _('Forms'), value = True)
@ -961,43 +961,23 @@ class SettingsDirectory(QommonSettingsDirectory):
job = get_response().add_after_job(
N_('Exporting site settings'),
exporter.export)
job.done_action_url = get_request().get_url() + '?download=%s' % job.id
job.done_action_label = _('Download Export')
job.done_button_attributes = {'download': 'export.wcs'}
job.store()
return redirect('export?job=%s' % job.id)
return redirect(job.get_processing_url())
def export_pending(self):
job_id = get_request().form.get('job') or get_request().form.get('download')
def export_download(self):
job_id = get_request().form.get('download')
try:
job = AfterJob.get(job_id)
except KeyError:
return redirect('.')
if get_request().form.get('download'):
response = get_response()
response.set_content_type('application/x-wcs')
response.set_header('content-disposition', 'attachment; filename=export.wcs')
return job.file_content
html_top('settings', title=_('Exporting'))
r = TemplateIO(html=True)
get_response().add_javascript(['jquery.js', 'afterjob.js'])
r += htmltext('<h2>%s</h2>') % _('Export')
r += htmltext('<div class="section"><dl class="job-status">')
r += htmltext('<dt>')
r += _(job.label)
r += htmltext('</dt>')
r += htmltext('<dd>')
r += htmltext('<span class="afterjob" id="%s">') % job.id
r += _(job.status)
r += htmltext('</span>')
r += htmltext('</dd>')
r += htmltext('</dl>')
r += htmltext('<div class="done">')
r += htmltext('<a download="export.wcs" href="export?download=%s">%s</a>') % (
job.id, _('Download Export'))
r += htmltext('</div>')
r += htmltext('</div>')
return r.getvalue()
response = get_response()
response.set_content_type('application/x-wcs')
response.set_header('content-disposition', 'attachment; filename=export.wcs')
return job.file_content
def p_import(self):
form = Form(enctype='multipart/form-data')

View File

@ -188,34 +188,27 @@ class CardPage(FormPage):
if not self.can_user_add_cards():
raise errors.AccessForbiddenError()
context = {
'unsupported_fields': [],
'required_fields': []
}
try:
job = AfterJob.get(get_request().form.get('job'))
get_response().add_javascript(['jquery.js', 'afterjob.js'])
context['job'] = job
except KeyError:
form = Form(enctype='multipart/form-data', use_tokens=False)
form.add(FileWidget, 'file', title=_('File'), required=True)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
if form.is_submitted() and not form.has_errors():
try:
return self.import_csv_submit(form.get_widget('file').parse().fp)
except ValueError as e:
form.set_error('file', e)
context['form'] = form
form = Form(enctype='multipart/form-data', use_tokens=False)
form.add(FileWidget, 'file', title=_('File'), required=True)
form.add_submit('submit', _('Submit'))
form.add_submit('cancel', _('Cancel'))
if form.get_widget('cancel').parse():
return redirect('.')
if form.is_submitted() and not form.has_errors():
try:
return self.import_csv_submit(form.get_widget('file').parse().fp)
except ValueError as e:
form.set_error('file', e)
get_response().breadcrumb.append(('import_csv', _('Import CSV')))
html_top('data_management', _('Import CSV'))
context['form'] = form
for field in self.get_import_csv_fields():
if field.convert_value_from_str is None:
context['unsupported_fields'].append(field)
if not hasattr(field, 'required'):
continue
if field.required and field.convert_value_from_str is None:
@ -293,7 +286,7 @@ class CardPage(FormPage):
job = ImportFromCsvAfterJob(carddef=self.formdef, data_lines=data_lines)
if afterjob:
get_response().add_after_job(job)
return redirect('import-csv?job=%s' % job.id)
return redirect(job.get_processing_url())
else:
job.execute()
@ -378,3 +371,13 @@ class ImportFromCsvAfterJob(AfterJob):
data_instance.just_created()
data_instance.store()
data_instance.perform_workflow()
def done_action_url(self):
carddef = self.kwargs['carddef_class'].get(self.kwargs['carddef_id'])
return carddef.get_url()
def done_action_label(self):
return _('Back to Listing')
def done_button_attributes(self):
return {'data-redirect-auto': 'true'}

View File

@ -1963,33 +1963,7 @@ class FormPage(Directory):
action_id=action['action'].id,
item_ids=item_ids))
job.store()
return redirect('./?job=%s' % job.id)
def job_multi(self):
try:
job = AfterJob.get(get_request().form.get('job'))
except KeyError:
return redirect('.')
html_top('management', title=_('Executing Task'))
r = TemplateIO(html=True)
r += get_session().display_message()
get_response().add_javascript(['jquery.js', 'afterjob.js'])
r += htmltext('<dl class="job-status">')
r += htmltext('<dt>')
r += job.label
r += htmltext('</dt>')
r += htmltext('<dd>')
r += htmltext('<span class="afterjob" id="%s">') % job.id
r += _(job.status)
r += htmltext('</span>')
r += htmltext('</dd>')
r += htmltext('</dl>')
r += htmltext('<div class="done">')
r += htmltext('<a data-redirect-auto="true" href="./?%s">%s</a>') % (job.query_string, _('Back to Listing'))
r += htmltext('</div>')
return r.getvalue()
return redirect(job.get_processing_url())
def csv(self):
self.check_access()
@ -2014,7 +1988,7 @@ class FormPage(Directory):
if count > self.WCS_SYNC_EXPORT_LIMIT:
job = get_response().add_after_job(job)
job.store()
return redirect('export?job=%s' % job.id)
return redirect(job.get_processing_url())
else:
job.execute()
@ -2025,36 +1999,6 @@ class FormPage(Directory):
def export(self):
self.check_access()
if get_request().form.get('download'):
return self.export_download()
try:
job = AfterJob.get(get_request().form.get('job'))
except KeyError:
return redirect('.')
html_top('management', title=_('Exporting'))
r = TemplateIO(html=True)
r += get_session().display_message()
get_response().add_javascript(['jquery.js', 'afterjob.js'])
r += htmltext('<dl class="job-status">')
r += htmltext('<dt>')
r += _(job.label)
r += htmltext('</dt>')
r += htmltext('<dd>')
r += htmltext('<span class="afterjob" id="%s">') % job.id
r += _(job.status)
r += htmltext('</span>')
r += htmltext('</dd>')
r += htmltext('</dl>')
r += htmltext('<div class="done">')
r += htmltext('<a download="%s" href="export?download=%s">%s</a>') % (
job.file_name, job.id, _('Download Export'))
r += htmltext('</div>')
return r.getvalue()
def export_download(self):
try:
job = AfterJob.get(get_request().form.get('download'))
except KeyError:
@ -2092,7 +2036,7 @@ class FormPage(Directory):
if count > self.WCS_SYNC_EXPORT_LIMIT and not get_request().is_api_url():
job = get_response().add_after_job(job)
job.store()
return redirect('export?job=%s' % job.id)
return redirect(job.get_processing_url())
else:
job.execute()
@ -3449,6 +3393,16 @@ class MassActionAfterJob(AfterJob):
self.completion_status = '{}/{}'.format(i + 1, len(formdatas))
self.store()
def done_action_url(self):
formdef = self.kwargs['formdef_class'].get(self.kwargs['formdef_id'])
return formdef.get_url(backoffice=True)
def done_action_label(self):
return _('Back to Listing')
def done_button_attributes(self):
return {'data-redirect-auto': 'true'}
class CsvExportAfterJob(AfterJob):
label = N_('Exporting forms in CSV')
@ -3506,6 +3460,16 @@ class CsvExportAfterJob(AfterJob):
self.content_type = 'text/csv'
self.store()
def done_action_url(self):
formdef = self.kwargs['formdef_class'].get(self.kwargs['formdef_id'])
return formdef.get_url(backoffice=True) + 'export?download=%s' % self.id
def done_action_label(self):
return _('Download Export')
def done_button_attributes(self):
return {'download': self.file_name}
class OdsExportAfterJob(CsvExportAfterJob):
def __init__(self, formdef, **kwargs):

View File

@ -25,6 +25,8 @@ from ..qommon.backoffice.menu import html_top
from ..qommon import misc, get_cfg
from ..qommon import errors
from ..qommon import template
from ..qommon.afterjobs import AfterJob
from ..qommon.form import *
from wcs.formdef import FormDef
@ -44,7 +46,7 @@ from . import data_management
class RootDirectory(BackofficeRootDirectory):
_q_exports = ['', 'pending', 'statistics', ('menu.json', 'menu_json')]
_q_exports = ['', 'pending', 'statistics', ('menu.json', 'menu_json'), 'processing']
forms = wcs.admin.forms.FormsDirectory()
roles = wcs.admin.roles.RolesDirectory()
@ -285,3 +287,15 @@ class RootDirectory(BackofficeRootDirectory):
'studio', 'cards', 'data'):
menu_items[-1]['icon'] = k.strip('/')
return menu_items
def processing(self):
html_top('/')
try:
job = AfterJob.get(get_request().form.get('job'))
except KeyError:
return redirect('.')
get_response().add_javascript(['jquery.js', 'afterjob.js'])
return template.QommonTemplateResponse(
templates=['wcs/backoffice/processing.html'],
context={'job': job})

View File

@ -97,3 +97,6 @@ class AfterJob(StorableObject):
obj_dict['job_cmd'] = None
return obj_dict
return self.__dict__
def get_processing_url(self):
return '/backoffice/processing?job=%s' % self.id

View File

@ -1717,6 +1717,7 @@ ul#evolutions li div.msg li::after {
div.section > dl {
padding-bottom: 0;
padding-top: 0;
}
div.data-source-preview ul {

View File

@ -8,33 +8,6 @@
{% endblock %}
{% block content %}
{% if job %}
{% if unsupported_fields %}
{% for field in unsupported_fields %}
<div class="warningnotice">
{% blocktrans with label=field.label description=field.description %}
Column {{ label }} will be ignored: type {{ description }} not supported.
{% endblocktrans %}
</div>
{% endfor %}
{% endif %}
<dl class="job-status">
<dt>{{ job.label }}</dt>
{% if warnings %}
<dt>
{% for warning in warnings %}
<div class="warningnotice">{{ warning }}</div>
{% endfor %}
</dt>
{% endif %}
<dd><span class="afterjob" id="{{ job.id }}">{% trans job.status %}</span></dd>
</dl>
<div class="done">
<a data-redirect-auto="true" href=".">{% trans "Back to Listing" %}</a>
</div>
{% else %}
{% if required_fields %}
<div class="errornotice">
@ -51,5 +24,4 @@
<p><a href="data-sample-csv">{% trans "Download sample file for this card" %}</a></p>
{{ form.render|safe }}
{% endif %}
{% endif %}
{% endblock %}

View File

@ -0,0 +1,18 @@
{% extends "wcs/backoffice/base.html" %}
{% load i18n %}
{% block appbar-title %}{% trans "Executing task..." %}{% endblock %}
{% block content %}
<div class="section">
<dl class="job-status">
<dt>{% trans job.label %}</dt>
<dd><span class="afterjob" id="{{ job.id }}">{% trans job.status %}</span></dd>
</dl>
</div>
<div class="done" style="display: none">
<a {% for attr in job.done_button_attributes.items %}{{ attr.0 }}="{{ attr.1 }}"{% endfor %}
class="button" href="{{ job.done_action_url }}">{{ job.done_action_label }}</a>
</div>
{% endblock %}