misc: make form/card adjustments after status removal async (#77139) #492

Merged
fpeters merged 1 commits from wip/77139-status-removal-async into main 2023-07-16 09:21:17 +02:00
2 changed files with 108 additions and 64 deletions

View File

@ -14,6 +14,7 @@ from wcs.carddef import CardDef
from wcs.categories import WorkflowCategory
from wcs.formdef import FormDef
from wcs.mail_templates import MailTemplate
from wcs.qommon.afterjobs import AfterJob
from wcs.qommon.errors import ConnectionError
from wcs.qommon.form import UploadedFile
from wcs.qommon.http_request import HTTPRequest
@ -404,11 +405,13 @@ def test_workflows_delete_status(pub):
resp = resp.follow()
@pytest.mark.parametrize(
'subtest', [('forms', (FormDef,)), ('cards', (CardDef,)), ('cards/forms', (FormDef, CardDef))]
)
def test_workflows_delete_status_reassign(pub, subtest):
(name, formdef_classes) = subtest
@pytest.mark.parametrize('name', ['forms', 'cards', 'cards/forms'])
def test_workflows_delete_status_reassign(pub, name):
formdef_classes = {
'forms': (FormDef,),
'cards': (CardDef,),
'cards/forms': (FormDef, CardDef),
}.get(name)
create_superuser(pub)
FormDef.wipe()
CardDef.wipe()
@ -421,37 +424,36 @@ def test_workflows_delete_status_reassign(pub, subtest):
formdefs = []
for i, formdef_class in enumerate(formdef_classes):
formdef = formdef_class()
formdef.name = 'Form titles %s' % i
formdef.name = f'{formdef_class.xml_root_node} title {i}'
formdef.workflow = workflow
formdef.fields = []
formdef.store()
formdef.data_class().wipe()
formdefs.append(formdef)
formdata1 = formdefs[0].data_class()()
formdata1.data = {}
formdata1.status = 'wf-%s' % wf_bar.id
formdata1.store()
formdata2 = formdefs[-1].data_class()()
formdata2.data = {}
formdata2.status = 'wf-%s' % wf_baz.id
formdata2.store()
if name == 'cards':
# add a second one, to check for plural form in message.
formdata2b = formdefs[-1].data_class()()
formdata2b.data = {}
formdata2b.status = 'wf-%s' % wf_baz.id
formdata2b.store()
app = login(get_app(pub))
for action in ('nothing', 'delete', 'reassign'):
# restore workflow & formdata
workflow.store()
for action in ('nothing', 'remove', 'reassign'):
AfterJob.wipe()
formdefs[0].data_class().wipe()
formdefs[-1].data_class().wipe()
formdata1 = formdefs[0].data_class()()
formdata1.status = 'wf-%s' % wf_bar.id
formdata1.store()
formdata2 = formdefs[-1].data_class()()
formdata2.status = 'wf-%s' % wf_baz.id
formdata2.store()
if name == 'cards':
# add a second one, to check for plural form in message.
formdata2b = formdefs[-1].data_class()()
formdata2b.status = 'wf-%s' % wf_baz.id
formdata2b.store()
workflow.store()
resp = app.get('/backoffice/workflows/1/status/%s/' % wf_baz.id)
resp = resp.click('Delete')
resp = resp.form.submit('submit')
@ -462,18 +464,27 @@ def test_workflows_delete_status_reassign(pub, subtest):
resp.form['action'].value = 'Do nothing'
resp = resp.form.submit('submit')
resp = resp.follow()
assert len(Workflow.get(workflow.id).possible_status) == 2
assert formdef.data_class().get(formdata2.id).status == 'wf-%s' % wf_baz.id
elif action == 'remove':
assert resp.request.path == f'/backoffice/workflows/{workflow.id}/status/{wf_baz.id}/'
assert AfterJob.count() == 0
continue
if action == 'remove':
resp.form['action'].value = 'Remove these %s' % name
resp = resp.form.submit('submit')
resp = resp.follow()
assert formdef.data_class().has_key(formdata1.id)
assert not formdef.data_class().has_key(formdata2.id)
assert formdefs[0].data_class().has_key(formdata1.id)
assert not formdefs[-1].data_class().has_key(formdata2.id)
elif action == 'reassign':
resp.form['action'].value = 'Change these %s status to "bar"' % name
resp = resp.form.submit('submit')
resp = resp.follow()
assert formdef.data_class().get(formdata2.id).status == 'wf-%s' % wf_bar.id
assert formdefs[-1].data_class().get(formdata2.id).status == 'wf-%s' % wf_bar.id
assert AfterJob.count() == 2 # status change + rebuild_security
resp = resp.click('Back')
assert resp.request.path == f'/backoffice/workflows/{workflow.id}/'
def test_workflows_order_status(pub):

View File

@ -35,6 +35,7 @@ from wcs.categories import WorkflowCategory
from wcs.formdata import Evolution
from wcs.formdef import FormDef, UpdateStatisticsDataAfterJob
from wcs.qommon import _, errors, force_str, misc, template
from wcs.qommon.afterjobs import AfterJob
from wcs.qommon.form import (
CheckboxWidget,
ColourWidget,
@ -859,43 +860,23 @@ class WorkflowStatusPage(Directory):
r += htmltext('</div>')
return r.getvalue()
else:
self.submit_reassign(form)
del self.workflow.possible_status[self.workflow.possible_status.index(self.status)]
self.workflow.store(comment=_('Removal of status %s') % self.status.name)
return redirect('../..')
def submit_reassign(self, form):
nb_forms = 0
action = form.get_widget('action').parse()
if action.startswith('reassign-'):
new_status = 'wf-%s' % str(action)[9:]
for formdef in itertools.chain(FormDef.select(), CardDef.select()):
if formdef.workflow_id != self.workflow.id:
continue
for item in formdef.data_class().get_with_indexed_value('status', 'wf-%s' % self.status.id):
nb_forms += 1
if action == 'remove':
item.remove_self()
else:
item.status = new_status
evo = Evolution()
evo.time = time.localtime()
evo.status = new_status
evo.comment = str(_('Administrator reassigned status'))
if not item.evolution:
item.evolution = []
item.evolution.append(evo)
item.store()
# delete all (old) status references in evolutions
for item in formdef.data_class().select():
if item.evolution:
modified = False
for evo in item.evolution:
if evo.status == self.status:
evo.status = None
modified = True
if modified:
item.store()
action = form.get_widget('action').parse()
new_status = None
if action.startswith('reassign-'):
new_status = 'wf-%s' % str(action)[9:]
job = StatusChangeJob(
workflow_id=self.workflow.id,
action=action,
current_status=f'wf-{self.status.id}',
new_status=new_status,
)
job.store()
get_response().add_after_job(job)
return redirect(job.get_processing_url())
def display(self):
form = Form(enctype='multipart/form-data')
@ -2100,3 +2081,55 @@ class WorkflowsDirectory(Directory):
workflow.store()
get_session().message = ('info', _('This workflow has been successfully imported.'))
return redirect('%s/' % workflow.id)
class StatusChangeJob(AfterJob):
def __init__(self, workflow_id, action, current_status, new_status):
super().__init__(
label=_('Updating data after workflow change'),
workflow_id=workflow_id,
action=action,
current_status=current_status,
new_status=new_status,
)
def execute(self):
workflow_id = self.kwargs['workflow_id']
current_status = self.kwargs['current_status']
new_status = self.kwargs['new_status']
action = self.kwargs['action']
for formdef in itertools.chain(FormDef.select(), CardDef.select()):
if formdef.workflow_id != workflow_id:
continue
for item in formdef.data_class().get_with_indexed_value('status', current_status):
if action == 'remove':
item.remove_self()
else:
item.status = new_status
evo = Evolution()
evo.time = time.localtime()
evo.status = new_status
evo.comment = str(_('Administrator reassigned status'))
if not item.evolution:
item.evolution = []
item.evolution.append(evo)
item.store()
# delete all (old) status references in evolutions
for item in formdef.data_class().select():
if item.evolution:
modified = False
for evo in item.evolution:
if evo.status == self.status:
evo.status = None
modified = True
if modified:
item._store_all_evolution = True
item.store()
def done_action_url(self):
workflow = Workflow.get(self.kwargs['workflow_id'])
return workflow.get_admin_url()
def done_action_label(self):
return _('Back')