Projet

Général

Profil

0001-workflows-use-check_condition-method-to-evaluate-jum.patch

Frédéric Péters, 21 décembre 2021 16:30

Télécharger (14 ko)

Voir les différences:

Subject: [PATCH] workflows: use check_condition method to evaluate jump
 condition (#59914)

 tests/form_pages/test_all.py | 48 ++++++++++++++++++++++++++
 tests/workflow/test_all.py   | 65 ++++++++++++++++++++++++++----------
 wcs/formdata.py              |  4 +++
 wcs/wf/jump.py               | 49 ++++++++++-----------------
 4 files changed, 117 insertions(+), 49 deletions(-)
tests/form_pages/test_all.py
4800 4800
    assert formdata.evolution[1].comment == 'new-evolution-2'
4801 4801
    assert formdata.evolution[2].comment is None
4802 4802

  
4803
    # mark user as owner so it can check the UI
4804
    formdata.user_id = user.id
4805
    formdata.store()
4803 4806
    resp = app.get(formdata.get_url())
4804 4807
    assert resp.text.count('Status1') == 3  # once in summary and two in journal
4805 4808
    assert resp.text.count('CSS-STATUS1') == 2
......
5181 5184
        formdata.evolution[-1].add_part(JournalWsCallErrorPart('bla', 'bla', {}))
5182 5185
        formdata.store()
5183 5186

  
5187
    # mark user as owner so it can check the UI
5188
    formdata.user_id = user.id
5189
    formdata.store()
5184 5190
    resp = app.get(formdata.get_url())
5185 5191
    assert resp.text.count('<li class="msg') == 1
5186 5192

  
......
8483 8489

  
8484 8490
    formdata.refresh_from_storage()
8485 8491
    assert not formdata.workflow_data
8492

  
8493

  
8494
def test_jumps_with_by_and_no_trigger(pub):
8495
    FormDef.wipe()
8496
    Workflow.wipe()
8497
    pub.role_class.wipe()
8498

  
8499
    role = pub.role_class(name='xxx')
8500
    role.store()
8501

  
8502
    workflow = Workflow(name='test')
8503
    st1 = workflow.add_status('Status1', 'st1')
8504

  
8505
    jump = JumpWorkflowStatusItem()
8506
    jump.status = 'st2'
8507
    st1.items.append(jump)
8508
    jump.by = [role.id]
8509
    jump.parent = st1
8510

  
8511
    jump = JumpWorkflowStatusItem()
8512
    jump.status = 'st3'
8513
    st1.items.append(jump)
8514
    jump.by = []
8515
    jump.parent = st1
8516

  
8517
    workflow.add_status('Status2', 'st2')
8518
    workflow.add_status('Status3', 'st3')
8519
    workflow.store()
8520

  
8521
    formdef = create_formdef()
8522
    formdef.fields = []
8523
    formdef.workflow = workflow
8524
    formdef.store()
8525
    formdef.data_class().wipe()
8526

  
8527
    resp = get_app(pub).get('/test/')
8528
    resp = resp.form.submit('submit')
8529
    resp = resp.form.submit('submit')
8530

  
8531
    # it jumps to st2, as jump.by is only related to triggers
8532
    assert formdef.data_class().count() == 1
8533
    assert formdef.data_class().select()[0].status == 'wf-st2'
tests/workflow/test_all.py
252 252
    formdef.store()
253 253
    formdata = formdef.data_class()()
254 254
    item = JumpWorkflowStatusItem()
255
    assert item.must_jump(formdata) is True
255
    assert item.check_condition(formdata) is True
256 256

  
257 257

  
258 258
def test_jump_datetime_condition(pub):
......
267 267
        'type': 'python',
268 268
        'value': 'datetime.datetime.now() > datetime.datetime(%s, %s, %s)' % yesterday.timetuple()[:3],
269 269
    }
270
    assert item.must_jump(formdata) is True
270
    assert item.check_condition(formdata) is True
271 271

  
272 272
    tomorrow = datetime.datetime.now() + datetime.timedelta(days=1)
273 273
    item.condition = {
274 274
        'type': 'python',
275 275
        'value': 'datetime.datetime.now() > datetime.datetime(%s, %s, %s)' % tomorrow.timetuple()[:3],
276 276
    }
277
    assert item.must_jump(formdata) is False
277
    assert item.check_condition(formdata) is False
278 278

  
279 279

  
280 280
def test_jump_date_conditions(pub):
......
297 297
        'type': 'python',
298 298
        'value': 'utils.make_date(form_var_date) == utils.make_date("2015-01-04")',
299 299
    }
300
    assert item.must_jump(formdata) is True
300
    assert item.check_condition(formdata) is True
301 301

  
302 302
    item = JumpWorkflowStatusItem()
303 303
    item.condition = {'type': 'python', 'value': 'utils.time_delta(form_var_date, "2015-01-04").days == 0'}
304
    assert item.must_jump(formdata) is True
304
    assert item.check_condition(formdata) is True
305 305

  
306 306
    item = JumpWorkflowStatusItem()
307 307
    item.condition = {'type': 'python', 'value': 'utils.time_delta(utils.today(), "2015-01-04").days > 0'}
308
    assert item.must_jump(formdata) is True
308
    assert item.check_condition(formdata) is True
309 309

  
310 310
    item = JumpWorkflowStatusItem()
311 311
    item.condition = {
312 312
        'type': 'python',
313 313
        'value': 'utils.time_delta(datetime.datetime.now(), "2015-01-04").days > 0',
314 314
    }
315
    assert item.must_jump(formdata) is True
315
    assert item.check_condition(formdata) is True
316 316

  
317 317
    item = JumpWorkflowStatusItem()
318 318
    item.condition = {
319 319
        'type': 'python',
320 320
        'value': 'utils.time_delta(utils.time.localtime(), "2015-01-04").days > 0',
321 321
    }
322
    assert item.must_jump(formdata) is True
322
    assert item.check_condition(formdata) is True
323 323

  
324 324

  
325 325
def test_jump_count_condition(pub):
......
332 332
    formdata = formdef.data_class()()
333 333
    item = JumpWorkflowStatusItem()
334 334
    item.condition = {'type': 'python', 'value': 'form_objects.count < 2'}
335
    assert item.must_jump(formdata) is True
335
    assert item.check_condition(formdata) is True
336 336

  
337 337
    for _ in range(10):
338 338
        formdata = formdef.data_class()()
339 339
        formdata.store()
340 340

  
341 341
    item.condition = {'type': 'python', 'value': 'form_objects.count < 2'}
342
    assert item.must_jump(formdata) is False
342
    assert item.check_condition(formdata) is False
343 343

  
344 344

  
345 345
def test_jump_bad_python_condition(two_pubs):
......
357 357
    item = JumpWorkflowStatusItem()
358 358

  
359 359
    item.condition = {'type': 'python', 'value': 'form_var_foobar == 0'}
360
    assert item.must_jump(formdata) is False
360
    assert item.check_condition(formdata) is False
361 361
    assert two_pubs.loggederror_class.count() == 1
362 362
    logged_error = two_pubs.loggederror_class.select()[0]
363 363
    assert logged_error.summary == 'Failed to evaluate condition'
......
368 368

  
369 369
    two_pubs.loggederror_class.wipe()
370 370
    item.condition = {'type': 'python', 'value': '~ invalid ~'}
371
    assert item.must_jump(formdata) is False
371
    assert item.check_condition(formdata) is False
372 372
    assert two_pubs.loggederror_class.count() == 1
373 373
    logged_error = two_pubs.loggederror_class.select()[0]
374 374
    assert logged_error.summary == 'Failed to evaluate condition'
......
392 392
    item = JumpWorkflowStatusItem()
393 393

  
394 394
    item.condition = {'type': 'django', 'value': '1 < 2'}
395
    assert item.must_jump(formdata) is True
395
    assert item.check_condition(formdata) is True
396 396

  
397 397
    item.condition = {'type': 'django', 'value': 'form_var_foo == "hello"'}
398
    assert item.must_jump(formdata) is True
398
    assert item.check_condition(formdata) is True
399 399

  
400 400
    item.condition = {'type': 'django', 'value': 'form_var_foo|first|upper == "H"'}
401
    assert item.must_jump(formdata) is True
401
    assert item.check_condition(formdata) is True
402 402

  
403 403
    item.condition = {'type': 'django', 'value': 'form_var_foo|first|upper == "X"'}
404
    assert item.must_jump(formdata) is False
404
    assert item.check_condition(formdata) is False
405 405

  
406 406
    if two_pubs.is_using_postgresql():
407 407
        assert two_pubs.loggederror_class.count() == 0
408 408

  
409 409
    item.condition = {'type': 'django', 'value': '~ invalid ~'}
410
    assert item.must_jump(formdata) is False
410
    assert item.check_condition(formdata) is False
411 411
    if two_pubs.is_using_postgresql():
412 412
        assert two_pubs.loggederror_class.count() == 1
413 413
        logged_error = two_pubs.loggederror_class.select()[0]
......
2390 2390
        formdata.just_created()
2391 2391
        formdata.store()
2392 2392
        formdata_id = formdata.id
2393
        with mock.patch('wcs.wf.jump.JumpWorkflowStatusItem.must_jump') as must_jump:
2393
        with mock.patch('wcs.wf.jump.JumpWorkflowStatusItem.check_condition') as must_jump:
2394 2394
            must_jump.return_value = False
2395 2395
            _apply_timeouts(two_pubs)
2396 2396
            assert must_jump.call_count == 0  # not enough time has passed
......
6506 6506
    assert two_pubs.loggederror_class.count() == 1
6507 6507
    logged_error = two_pubs.loggederror_class.select()[0]
6508 6508
    assert logged_error.summary == 'Mismatch in target object: expected "Other data", got "Data"'
6509

  
6510

  
6511
def test_conditional_jump_vs_tracing(pub):
6512
    workflow = Workflow(name='wf')
6513
    st1 = workflow.add_status('Status1', 'st1')
6514
    workflow.add_status('Status2', 'st2')
6515
    comment = st1.append_item('register-comment')
6516
    comment.comment = 'hello world'
6517
    jump1 = st1.append_item('jump')
6518
    jump1.parent = st1
6519
    jump1.condition = {'type': 'django', 'value': 'False'}
6520
    jump1.status = 'wf-st2'
6521
    jump2 = st1.append_item('jump')
6522
    jump2.parent = st1
6523
    jump2.status = 'wf-st2'
6524
    workflow.store()
6525

  
6526
    formdef = FormDef()
6527
    formdef.name = 'baz'
6528
    formdef.workflow = workflow
6529
    formdef.store()
6530

  
6531
    formdata = formdef.data_class()()
6532
    formdata.just_created()
6533
    formdata.store()
6534
    perform_items(st1.items, formdata)
6535
    formdata.refresh_from_storage()
6536
    assert formdata.evolution[0].parts[-1].actions[0][1:] == ('register-comment', str(comment.id))
6537
    assert formdata.evolution[0].parts[-1].actions[1][1:] == ('jump', str(jump2.id))
wcs/formdata.py
1103 1103
        for item in wf_status.items or []:
1104 1104
            if not hasattr(item, 'by') or not item.by:
1105 1105
                continue
1106
            if item.key == 'jump':
1107
                # automatic jump has a 'by' attribute but it's only for triggers,
1108
                # it's not a real interactive action.
1109
                continue
1106 1110
            with get_publisher().substitutions.temporary_feed(self):
1107 1111
                if not item.check_condition(self, **(condition_kwargs or {})):
1108 1112
                    continue
wcs/wf/jump.py
27 27
from quixote.html import htmltext
28 28

  
29 29
from wcs.api import get_user_from_api_query_string, is_url_signed
30
from wcs.conditions import Condition
31 30
from wcs.workflows import Workflow, WorkflowGlobalAction, WorkflowStatusJumpItem, register_item_class
32 31

  
33 32
from ..qommon import _, errors, force_str
......
85 84
                    pass
86 85
                elif not item.check_auth(self.formdata, user):
87 86
                    raise errors.AccessForbiddenError()
88
                if item.must_jump(self.formdata, trigger=item.trigger):
87
                if item.check_condition(self.formdata, trigger=component):
89 88
                    workflow_data = None
90 89
                    if hasattr(get_request(), '_json'):
91 90
                        workflow_data = get_request().json
......
249 248
            return None
250 249

  
251 250
    def perform(self, formdata):
252
        if not self.status:
253
            return
254

  
255
        if self.must_jump(formdata):
256
            wf_status = self.get_target_status(formdata)
257
            if wf_status:
258
                self.handle_markers_stack(formdata)
259
                formdata.status = 'wf-%s' % wf_status[0].id
260

  
261
    def check_condition(self, formdata, *args, **kwargs):
262
        # ship condition check here so it is not evaluated twice.
263
        return True
264

  
265
    def must_jump(self, formdata, trigger=None):
266
        must_jump = True
251
        wf_status = self.get_target_status(formdata)
252
        if wf_status:
253
            self.handle_markers_stack(formdata)
254
            formdata.status = 'wf-%s' % wf_status[0].id
267 255

  
268
        if self.condition:
269
            context = {'formdata': formdata, 'status_item': self}
270
            try:
271
                must_jump = Condition(self.condition, context).evaluate()
272
            except RuntimeError:
273
                must_jump = False
274

  
275
        if self.trigger:
276
            triggered = trigger is not None and trigger == self.trigger
277
            must_jump = must_jump and triggered
256
    def check_condition(self, formdata, *args, trigger=None, **kwargs):
257
        result = super().check_condition(formdata, *args, **kwargs)
258
        if not result:
259
            return False
278 260

  
279 261
        if self.timeout:
280 262
            timeout_str = self.compute(self.timeout)
......
294 276
                        notify=False,
295 277
                        record=True,
296 278
                    )
297
                    must_jump = False
279
                    return False
298 280
            last = formdata.last_update_time
299 281
            if last and timeout_seconds:
300 282
                diff = time.time() - time.mktime(last)
301
                must_jump = (diff > timeout_seconds) and must_jump
283
                if diff < timeout_seconds:
284
                    return False
302 285

  
303
        return must_jump
286
        if self.trigger:
287
            if trigger is None or trigger != self.trigger:
288
                return False
289

  
290
        return True
304 291

  
305 292

  
306 293
register_item_class(JumpWorkflowStatusItem)
......
376 363
                        get_publisher().substitutions.feed(get_publisher())
377 364
                        get_publisher().substitutions.feed(formdef)
378 365
                        get_publisher().substitutions.feed(formdata)
379
                        if jump_action.must_jump(formdata):
366
                        if jump_action.check_condition(formdata):
380 367
                            jump_and_perform(formdata, jump_action, event=('timeout-jump', jump_action.id))
381 368
                            break
382 369

  
383
-