From 642b51f34a6d3621845fda22a719bb1aa5d37d1c Mon Sep 17 00:00:00 2001 From: Benjamin Dauvergne Date: Thu, 26 Nov 2020 15:35:19 +0100 Subject: [PATCH] misc: remap statuses in a transaction (#38579) --- tests/admin_pages/test_form.py | 7 ++++++ wcs/admin/forms.py | 39 +++++++++++------------------- wcs/formdef.py | 43 ++++++++++++++++++++++++++++++++++ wcs/sql.py | 19 +++++++++++++++ 4 files changed, 83 insertions(+), 25 deletions(-) diff --git a/tests/admin_pages/test_form.py b/tests/admin_pages/test_form.py index 0e9d402c..4992b7d3 100644 --- a/tests/admin_pages/test_form.py +++ b/tests/admin_pages/test_form.py @@ -513,6 +513,10 @@ def test_form_workflow_remapping(pub): formdata2.status = 'draft' formdata2.store() + formdata3 = data_class() + formdata3.status = 'wf-1' + formdata3.store() + Workflow.wipe() workflow = Workflow(name='Workflow One') workflow.store() @@ -534,9 +538,11 @@ def test_form_workflow_remapping(pub): assert len(resp.forms[0]['mapping-%s' % status.id].options) == 1 assert data_class.get(formdata1.id).status == 'wf-new' assert data_class.get(formdata2.id).status == 'draft' + assert data_class.get(formdata3.id).status == 'wf-1' resp = resp.forms[0].submit() assert data_class.get(formdata1.id).status == 'wf-finished' assert data_class.get(formdata2.id).status == 'draft' + assert data_class.get(formdata3.id).status == 'wf-1-old-_default' # change to another workflow, with no mapping change workflow2 = workflow @@ -557,6 +563,7 @@ def test_form_workflow_remapping(pub): resp = resp.forms[0].submit() assert data_class.get(formdata1.id).status == 'wf-finished' assert data_class.get(formdata2.id).status == 'draft' + assert data_class.get(formdata3.id).status == 'wf-1-old-_default' def test_form_submitter_roles(pub): diff --git a/wcs/admin/forms.py b/wcs/admin/forms.py index 8ef84d0f..8b6050ba 100644 --- a/wcs/admin/forms.py +++ b/wcs/admin/forms.py @@ -864,8 +864,7 @@ class FormDefPage(Directory): if workflow_id is None: workflow_id = self.formdef_default_workflow return redirect('workflow-status-remapping?new=%s' % workflow_id) - self.formdef.workflow_id = workflow_id - self.formdef.store(comment=_('Workflow change')) + self.formdef.change_workflow(workflow_id) return redirect('.') def workflow_status_remapping(self): @@ -898,32 +897,22 @@ class FormDefPage(Directory): r += form.render() return r.getvalue() else: - get_logger().info('admin - form "%s", workflow is now "%s" (was "%s")' % ( - self.formdef.name, new_workflow.name, self.formdef.workflow.name)) - self.workflow_status_remapping_submit(form) - if new_workflow.id == self.formdef_default_workflow: - self.formdef.workflow_id = None - else: - self.formdef.workflow_id = new_workflow.id - self.formdef.store(comment=_('Workflow change')) - # instruct formdef to update its security rules - self.formdef.data_class().rebuild_security() - return redirect('.') + return self.workflow_status_remapping_submit(form, new_workflow) + + def workflow_status_remapping_submit(self, form, new_workflow): + get_logger().info('admin - form "%s", workflow is now "%s" (was "%s")' % ( + self.formdef.name, new_workflow.name, self.formdef.workflow.name)) - def workflow_status_remapping_submit(self, form): status_mapping = {} for status in self.formdef.workflow.possible_status: - status_mapping['wf-%s' % status.id] = 'wf-%s' % \ - form.get_widget('mapping-%s' % status.id).parse() - if any([x[0] != x[1] for x in status_mapping.items()]): - # if there are status changes, update all formdatas - status_mapping.update({'draft': 'draft'}) - for item in self.formdef.data_class().select(): - item.status = status_mapping.get(item.status) - if item.evolution: - for evo in item.evolution: - evo.status = status_mapping.get(evo.status) - item.store() + status_mapping[status.id] = form.get_widget('mapping-%s' % status.id).parse() + + if new_workflow.id == self.formdef_default_workflow: + new_workflow_id = None + else: + new_workflow_id = new_workflow.id + self.formdef.change_workflow(new_workflow_id, status_mapping) + return redirect('.') def get_preview(self): form = Form(action='#', use_tokens=False) diff --git a/wcs/formdef.py b/wcs/formdef.py index bfeef5a4..31c4d88b 100644 --- a/wcs/formdef.py +++ b/wcs/formdef.py @@ -1517,6 +1517,49 @@ class FormDef(StorableObject): # chunk contains the fields. return pickle.dumps(object, protocol=2) + pickle.dumps(object.fields, protocol=2) + def change_workflow(self, new_workflow_id, status_mapping=None): + old_workflow = self.get_workflow() + + formdata_count = self.data_class().count() + if formdata_count: + assert status_mapping, 'status mapping is required if there are formdatas' + assert all(status.id in status_mapping for status in old_workflow.possible_status), ( + 'a status was not mapped' + ) + + mapping = {} + for old_status, new_status in status_mapping.items(): + mapping['wf-%s' % old_status] = 'wf-%s' % new_status + mapping['draft'] = 'draft' + + if any([x[0] != x[1] for x in mapping.items()]): + # if there are status changes, update all formdatas + if get_publisher().is_using_postgresql(): + from . import sql + sql.formdef_remap_statuses(self, mapping) + else: + def map_status(status): + if status is None: + return None + elif status in mapping: + return mapping[status] + elif '-old-' in status: + return status + else: + return '%s-old-%s' % (status, old_workflow.id) + for formdata in self.data_class().select(): + formdata.status = map_status(formdata.status) + if formdata.evolution: + for evo in formdata.evolution: + evo.status = map_status(evo.status) + formdata.store() + + self.workflow_id = new_workflow_id + self.store(comment=_('Workflow change')) + if formdata_count: + # instruct formdef to update its security rules + self.data_class().rebuild_security() + from .qommon.admin.emails import EmailsDirectory diff --git a/wcs/sql.py b/wcs/sql.py index 09d7d188..bbe736b6 100644 --- a/wcs/sql.py +++ b/wcs/sql.py @@ -2874,3 +2874,22 @@ def reindex(): conn.commit() cur.close() + + +@guard_postgres +def formdef_remap_statuses(formdef, mapping): + conn, cur = get_connection_and_cursor() + table_name = get_formdef_table_name(formdef) + evolutions_table_name = table_name + '_evolutions' + old_workflow_id = formdef.workflow_id or '_default' + case_expression = '(CASE ' + case_expression += 'WHEN status IS NULL THEN NULL ' + for old_id, new_id in mapping.items(): + case_expression += "WHEN status = '%s' THEN '%s' " % (old_id, new_id) + case_expression += " WHEN status LIKE '%-old-%' THEN status " + case_expression += " ELSE (status || '-old-%s') END)" % old_workflow_id + + cur.execute('BEGIN') + cur.execute('UPDATE %s SET status = %s' % (table_name, case_expression)) + cur.execute('UPDATE %s SET status = %s' % (evolutions_table_name, case_expression)) + cur.execute('COMMIT') -- 2.29.2