From 07bad1fdc47713f25b78c564c4216aeeb3414aed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20P=C3=A9ters?= Date: Thu, 1 Sep 2016 15:44:11 +0200 Subject: [PATCH] workflows: allow to run "create document" non interactively (#12988) --- tests/test_form_pages.py | 53 ++++++++++++++++++ wcs/qommon/form.py | 4 ++ wcs/wf/export_to_model.py | 136 +++++++++++++++++++++++++++++----------------- 3 files changed, 143 insertions(+), 50 deletions(-) diff --git a/tests/test_form_pages.py b/tests/test_form_pages.py index fde256e..27093e1 100644 --- a/tests/test_form_pages.py +++ b/tests/test_form_pages.py @@ -2340,6 +2340,59 @@ def test_formdata_generated_document_odt_to_pdf_download_push_to_portfolio(pub, assert resp.content_type == 'application/pdf' assert resp.body.startswith('%PDF-') +def test_formdata_generated_document_non_interactive(pub): + create_user(pub) + wf = Workflow(name='status') + st1 = wf.add_status('Status1', 'st1') + export_to = ExportToModel() + export_to.method = 'non-interactive' + template_filename = os.path.join(os.path.dirname(__file__), 'template.odt') + template = open(template_filename).read() + upload = QuixoteUpload('/foo/template.odt', content_type='application/octet-stream') + upload.fp = StringIO.StringIO() + upload.fp.write(template) + upload.fp.seek(0) + export_to.model_file = UploadedFile(pub.app_dir, None, upload) + export_to.id = '_export_to' + export_to.attach_to_history = True + st1.items.append(export_to) + export_to.parent = st1 + + jump = JumpWorkflowStatusItem() + jump.status = 'st2' + st1.items.append(jump) + jump.parent = st1 + + st2 = wf.add_status('Status2', 'st2') + + wf.store() + + formdef = create_formdef() + formdef.workflow_id = wf.id + formdef.fields = [fields.TextField(id='0', label='comment', type='text', varname='comment')] + formdef.store() + formdef.data_class().wipe() + + resp = login(get_app(pub), username='foo', password='foo').get('/test/') + resp.form['f0'] = 'Hello\n\nWorld.' + resp = resp.forms[0].submit('submit') + assert 'Check values then click submit.' in resp.body + resp = resp.forms[0].submit('submit') + assert resp.status_int == 302 + form_location = resp.location + resp = resp.follow() + assert 'The form has been recorded' in resp.body + + resp = resp.click('template.odt') + assert resp.location.endswith('/template.odt') + resp = resp.follow() + assert resp.content_type == 'application/octet-stream' + with open(os.path.join(os.path.dirname(__file__), 'template-out.odt')) as f: + assert_equal_zip(StringIO.StringIO(resp.body), f) + + assert formdef.data_class().count() == 1 + assert formdef.data_class().select()[0].status == 'wf-st2' + def test_formdata_form_file_download(pub): create_user(pub) wf = Workflow(name='status') diff --git a/wcs/qommon/form.py b/wcs/qommon/form.py index 28e10a2..c1c2c2f 100644 --- a/wcs/qommon/form.py +++ b/wcs/qommon/form.py @@ -1317,6 +1317,8 @@ class WidgetList(quixote.form.widget.WidgetList): def render(self): get_response().add_javascript(['jquery.js', 'widget_list.js']) r = TemplateIO(html=True) + if self.attrs: + r += htmltag('div', **self.attrs) r += self.render_title(self.get_title()) r += self.render_error(self.get_error()) add_element_widget = self.get_widget('add_element') @@ -1326,6 +1328,8 @@ class WidgetList(quixote.form.widget.WidgetList): r += widget.render() r += add_element_widget.render() r += self.render_hint(self.get_hint()) + if self.attrs: + r += htmltext('') return r.getvalue() class WidgetDict(quixote.form.widget.WidgetDict): diff --git a/wcs/wf/export_to_model.py b/wcs/wf/export_to_model.py index e096da3..eff0a3b 100644 --- a/wcs/wf/export_to_model.py +++ b/wcs/wf/export_to_model.py @@ -15,6 +15,7 @@ # along with this program; if not, see . import base64 +import collections from StringIO import StringIO from xml.etree import ElementTree as ET import zipfile @@ -29,7 +30,8 @@ from qommon import get_logger from qommon import ezt, ods from qommon.form import (SingleSelectWidget, WidgetList, CheckboxWidget, StringWidget, UploadWidget, WysiwygTextWidget, Upload, - UploadedFile, UploadValidationError, VarnameWidget) + UploadedFile, UploadValidationError, VarnameWidget, + RadiobuttonsWidget) from qommon.errors import PublishError import qommon @@ -180,22 +182,20 @@ class ExportToModel(WorkflowStatusItem): varname = None convert_to_pdf = False push_to_portfolio = False + method = 'interactive' def render_as_line(self): - if self.label: - if self.model_file: - model = _('with model named %(file_name)s of %(size)s bytes') % { - 'file_name': self.model_file.base_filename, - 'size': self.model_file.size} - else: - model = _('no model set') - return _('Create document, labeled %(label)s, %(model)s') % { - 'label': self.label, - 'model': model} + if self.model_file: + model = _('with model named %(file_name)s of %(size)s bytes') % { + 'file_name': self.model_file.base_filename, + 'size': self.model_file.size} else: - return _('Create document (not completed)') + model = _('no model set') + return _('Create document, %(model)s') % {'model': model} def fill_form(self, form, formdata, user): + if not self.method == 'interactive': + return label = self.label if not label: label = _('Create Document') @@ -204,26 +204,16 @@ class ExportToModel(WorkflowStatusItem): widget.backoffice_info_text = self.backoffice_info_text def submit_form(self, form, formdata, user, evo): + if not self.method == 'interactive': + return if not self.model_file: return if form.get_submit() == 'button%s' % self.id: if not evo.comment: evo.comment = _('Form exported in a model') + self.perform_real(formdata, evo) in_backoffice = get_request() and get_request().is_in_backoffice() - outstream = self.apply_template_to_formdata(formdata) - filename = self.model_file.base_filename - content_type = self.model_file.content_type - if self.convert_to_pdf: - filename = filename.rsplit('.', 1)[0] + '.pdf' - content_type = 'application/pdf' - if self.push_to_portfolio: - push_document(formdata.get_user(), filename, outstream) if self.attach_to_history: - evo.add_part(AttachmentEvolutionPart( - filename, - outstream, - content_type=content_type, - varname=self.varname)) return formdata.get_url(backoffice=in_backoffice) base_url = formdata.get_url(backoffice=in_backoffice) return base_url + self.get_directory_name() @@ -257,23 +247,9 @@ class ExportToModel(WorkflowStatusItem): def add_parameters_widgets(self, form, parameters, prefix='', formdef=None): - if 'by' in parameters: - options = [(None, '---', None)] + self.get_list_of_roles() - form.add(WidgetList, '%sby' % prefix, title=_('By'), - element_type=SingleSelectWidget, - value=self.by, - add_element_label=_('Add Role'), - element_kwargs={ - 'render_br': False, - 'options': options}) - if 'attach_to_history' in parameters: - form.add(CheckboxWidget, '%sattach_to_history' % prefix, - title=_('Attach generated file to the form history'), - value=self.attach_to_history) - if 'label' in parameters: - form.add(StringWidget, '%slabel' % prefix, - title=_('Button Label'), - value=self.label) + methods = collections.OrderedDict( + [('interactive', _('Interactive (button)')), + ('non-interactive', _('Non interactive'))]) if 'model_file' in parameters: ids = (self.parent.parent.id, self.parent.id, self.id) if formdef: @@ -304,13 +280,10 @@ class ExportToModel(WorkflowStatusItem): form.add(UploadWidget, widget_name, directory='models', filename=filename, title=_('Model'), hint=hint, validation=self.model_file_validation, value=value) - if 'backoffice_info_text' in parameters: - form.add(WysiwygTextWidget, '%sbackoffice_info_text' % prefix, - title=_('Information Text for Backoffice'), - value=self.backoffice_info_text) - if 'varname' in parameters: - form.add(VarnameWidget, '%svarname' % prefix, - title=_('Variable Name'), value=self.varname) + if 'attach_to_history' in parameters: + form.add(CheckboxWidget, '%sattach_to_history' % prefix, + title=_('Attach generated file to the form history'), + value=self.attach_to_history) if 'convert_to_pdf' in parameters: form.add(CheckboxWidget, '%sconvert_to_pdf' % prefix, title=_('Convert generated file to PDF'), @@ -319,6 +292,48 @@ class ExportToModel(WorkflowStatusItem): form.add(CheckboxWidget, '%spush_to_portfolio' % prefix, title=_('Push generated file to portfolio'), value=self.push_to_portfolio) + if 'varname' in parameters: + form.add(VarnameWidget, '%svarname' % prefix, + title=_('Variable Name'), value=self.varname) + + if 'method' in parameters: + form.add(RadiobuttonsWidget, '%smethod' % prefix, + title=_('Method'), + options=methods.items(), + value=self.method, + attrs={'data-dynamic-display-parent': 'true'}) + + if 'by' in parameters: + options = [(None, '---', None)] + self.get_list_of_roles() + form.add(WidgetList, '%sby' % prefix, title=_('By'), + element_type=SingleSelectWidget, + value=self.by, + add_element_label=_('Add Role'), + attrs={ + 'data-dynamic-display-child-of': '%smethod' % prefix, + 'data-dynamic-display-value': methods.get('interactive'), + }, + element_kwargs={ + 'render_br': False, + 'options': options}) + + if 'label' in parameters: + form.add(StringWidget, '%slabel' % prefix, + title=_('Button Label'), + value=self.label, + attrs={ + 'data-dynamic-display-child-of': '%smethod' % prefix, + 'data-dynamic-display-value': methods.get('interactive'), + }) + + if 'backoffice_info_text' in parameters: + form.add(WysiwygTextWidget, '%sbackoffice_info_text' % prefix, + title=_('Information Text for Backoffice'), + value=self.backoffice_info_text, + attrs={ + 'data-dynamic-display-child-of': '%smethod' % prefix, + 'data-dynamic-display-value': methods.get('interactive'), + }) def get_directory_name(self): return qommon.misc.simplify(self.label or 'export_to_model', space='_') @@ -399,7 +414,7 @@ class ExportToModel(WorkflowStatusItem): return outstream def get_parameters(self): - parameters = ('by', 'label', 'model_file', 'attach_to_history', + parameters = ('method', 'by', 'label', 'model_file', 'attach_to_history', 'backoffice_info_text', 'varname') if transform_to_pdf is not None: parameters += ('convert_to_pdf',) @@ -435,4 +450,25 @@ class ExportToModel(WorkflowStatusItem): upload.fp.seek(0) self.model_file = UploadedFile('models', filename, upload) + def perform(self, formdata): + if self.method == 'interactive': + return + self.perform_real(formdata, formdata.evolution[-1]) + + def perform_real(self, formdata, evo): + outstream = self.apply_template_to_formdata(formdata) + filename = self.model_file.base_filename + content_type = self.model_file.content_type + if self.convert_to_pdf: + filename = filename.rsplit('.', 1)[0] + '.pdf' + content_type = 'application/pdf' + if self.push_to_portfolio: + push_document(formdata.get_user(), filename, outstream) + if self.attach_to_history: + evo.add_part(AttachmentEvolutionPart( + filename, + outstream, + content_type=content_type, + varname=self.varname)) + register_item_class(ExportToModel) -- 2.9.3