Projet

Général

Profil

0001-workflows-add-possibility-to-dispatch-function-accor.patch

Frédéric Péters, 08 janvier 2016 17:46

Télécharger (14,1 ko)

Voir les différences:

Subject: [PATCH 1/2] workflows: add possibility to dispatch function according
 to a variable (#9091)

 tests/test_admin_pages.py     |  33 +++++++++
 tests/test_workflow_import.py |  50 +++++++++++++
 tests/test_workflows.py       |  41 +++++++++++
 wcs/wf/dispatch.py            | 159 +++++++++++++++++++++++++++++++++++++++---
 wcs/workflows.py              |   4 +-
 5 files changed, 278 insertions(+), 9 deletions(-)
tests/test_admin_pages.py
1329 1329
        resp = resp.follow() # redirect to items/
1330 1330
        resp = resp.follow() # redirect to ./
1331 1331

  
1332
def test_workflows_edit_dispatch_action(pub):
1333
    create_superuser(pub)
1334
    role = create_role()
1335
    Workflow.wipe()
1336
    workflow = Workflow(name='foo')
1337
    workflow.add_status(name='baz')
1338
    workflow.store()
1339

  
1340
    app = login(get_app(pub))
1341
    resp = app.get('/backoffice/workflows/1/')
1342
    resp = resp.click('baz')
1343

  
1344
    resp.forms[0]['type'] = 'Assign a Function'
1345
    resp = resp.forms[0].submit()
1346
    resp = resp.follow()
1347

  
1348
    resp = resp.click('Assign a Function')
1349
    resp.form['rules$element0$value'].value = 'FOOBAR'
1350
    resp.form['rules$element0$role_id'].value = str(role.id)
1351
    resp = resp.form.submit('submit')
1352
    resp = resp.follow()
1353
    resp = resp.follow()
1354

  
1355
    resp = resp.click('Assign a Function')
1356
    assert resp.form['rules$element0$value'].value == 'FOOBAR'
1357
    resp.form['rules$element1$value'].value = 'BARFOO'
1358
    resp.form['rules$element1$role_id'].value = str(role.id)
1359
    resp = resp.form.submit('submit')
1360

  
1361
    workflow = Workflow.get(workflow.id)
1362
    assert workflow.possible_status[0].items[0].rules == [
1363
            {'value': 'FOOBAR', 'role_id': '1'}, {'value': 'BARFOO', 'role_id': '1'}]
1364

  
1332 1365
def test_workflows_variables(pub):
1333 1366
    create_superuser(pub)
1334 1367
    create_role()
tests/test_workflow_import.py
8 8

  
9 9
from wcs.workflows import Workflow, CommentableWorkflowStatusItem
10 10
from wcs.wf.wscall import WebserviceCallStatusItem
11
from wcs.wf.dispatch import DispatchWorkflowStatusItem
11 12
from wcs.wf.register_comment import RegisterCommenterWorkflowStatusItem
12 13
from wcs.roles import Role
13 14
from wcs.fields import StringField
......
356 357
    assert wf2.global_actions[0].triggers[0].roles == [role.id]
357 358

  
358 359
    wf2 = assert_import_export_works(wf, True)
360

  
361
def test_complex_dispatch_action():
362
    wf = Workflow(name='status')
363
    st1 = wf.add_status('Status1', 'st1')
364

  
365
    Role.wipe()
366

  
367
    role1 = Role()
368
    role1.name = 'Test Role 1'
369
    role1.store()
370

  
371
    role2 = Role()
372
    role2.name = 'Test Role 2'
373
    role2.store()
374

  
375
    dispatch = DispatchWorkflowStatusItem()
376
    dispatch.id = '_dispatch'
377
    dispatch.role_key = '_receiver'
378
    dispatch.dispatch_type = 'automatic'
379
    dispatch.variable = 'plop'
380
    dispatch.rules = [{'value': 'a', 'role_id': role1.id},
381
                      {'value': 'b', 'role_id': role2.id}]
382
    st1.items.append(dispatch)
383
    dispatch.parent = st1
384

  
385
    wf2 = assert_import_export_works(wf)
386
    assert wf2.possible_status[0].items[0].variable == dispatch.variable
387
    assert wf2.possible_status[0].items[0].rules == dispatch.rules
388
    assert wf2.possible_status[0].items[0].dispatch_type == 'automatic'
389

  
390
    Role.wipe()
391

  
392
    role3 = Role()
393
    role3.name = 'Test Role 1'
394
    role3.store()
395

  
396
    role4 = Role()
397
    role4.name = 'Test Role 2'
398
    role4.store()
399

  
400
    role1.remove_self()
401
    role2.remove_self()
402

  
403
    xml_export_orig = export_to_indented_xml(wf, include_id=True)
404
    wf2 = Workflow.import_from_xml_tree(xml_export_orig)
405
    assert wf2.possible_status[0].items[0].variable == dispatch.variable
406
    assert wf2.possible_status[0].items[0].rules == [
407
            {'value': 'a', 'role_id': role3.id}, {'value': 'b', 'role_id': role4.id}]
408
    assert wf2.possible_status[0].items[0].dispatch_type == 'automatic'
tests/test_workflows.py
170 170
    item.perform(formdata)
171 171
    assert formdata.workflow_roles == {'_receiver': '1'}
172 172

  
173
def test_dispatch_auto(pub):
174
    formdef = FormDef()
175
    formdef.name = 'baz'
176
    formdef.fields = [
177
        StringField(id='1', label='Test', type='string', varname='foo'),
178
    ]
179
    formdef.store()
180

  
181
    item = DispatchWorkflowStatusItem()
182
    item.role_key = '_receiver'
183
    item.dispatch_type = 'automatic'
184

  
185
    formdata = formdef.data_class()()
186
    pub.substitutions.feed(formdata)
187
    item.perform(formdata)
188
    assert not formdata.workflow_roles
189

  
190
    item.variable = 'form_var_foo'
191
    item.rules = [
192
        {'role_id': '1', 'value': 'foo'},
193
        {'role_id': '2', 'value': 'bar'},
194
    ]
195

  
196
    item.perform(formdata)
197
    assert not formdata.workflow_roles
198

  
199
    # no match
200
    formdata.data = {'1': 'XXX'}
201
    item.perform(formdata)
202
    assert not formdata.workflow_roles
203

  
204
    # match
205
    formdata.data = {'1': 'foo'}
206
    item.perform(formdata)
207
    assert formdata.workflow_roles == {'_receiver': '1'}
208

  
209
    # other match
210
    formdata.data = {'1': 'bar'}
211
    item.perform(formdata)
212
    assert formdata.workflow_roles == {'_receiver': '2'}
213

  
173 214
def test_roles(pub):
174 215
    user = pub.user_class()
175 216
    user.store()
wcs/wf/dispatch.py
14 14
# You should have received a copy of the GNU General Public License
15 15
# along with this program; if not, see <http://www.gnu.org/licenses/>.
16 16

  
17
import collections
18
import xml.etree.ElementTree as ET
19

  
17 20
from qommon.form import *
21
from qommon import get_logger
18 22
from wcs.roles import get_user_roles
19
from wcs.workflows import WorkflowStatusItem, register_item_class
23
from wcs.workflows import XmlSerialisable, WorkflowStatusItem, register_item_class
24

  
25

  
26
class AutomaticDispatchRowWidget(CompositeWidget):
27
    def __init__(self, name, value=None, **kwargs):
28
        CompositeWidget.__init__(self, name, value, **kwargs)
29
        if not value:
30
            value = {}
31
        self.add(StringWidget, name='value', title=_('Value'),
32
                value=value.get('value'), **kwargs)
33
        self.add(SingleSelectWidget, name='role_id', title=_('Role'),
34
                value=value.get('role_id'),
35
                options=[(None, '----', None)] + get_user_roles())
36

  
37
    def _parse(self, request):
38
        if self.get('value') or self.get('role_id'):
39
            self.value = {
40
                'value': self.get('value'),
41
                'role_id': self.get('role_id')
42
            }
43
        else:
44
            self.value = None
45

  
46

  
47
class AutomaticDispatchTableWidget(WidgetListAsTable):
48
    readonly = False
49
    def __init__(self, name, **kwargs):
50
        super(AutomaticDispatchTableWidget, self).__init__(name,
51
                element_type=AutomaticDispatchRowWidget, **kwargs)
52

  
53

  
54
class RuleNode(XmlSerialisable):
55
    node_name = 'rule'
56

  
57
    def __init__(self, rule={}):
58
        self.role_id = rule.get('role_id')
59
        self.value = rule.get('value')
60

  
61
    def as_dict(self):
62
        return {
63
            'role_id': self.role_id,
64
            'value': self.value
65
        }
66

  
67
    def get_parameters(self):
68
        return ('role_id', 'value')
69

  
70
    def role_id_export_to_xml(self, item, charset, include_id=False):
71
        self._role_export_to_xml('role_id', item, charset,
72
                include_id=include_id)
73

  
74
    def role_id_init_with_xml(self, elem, charset, include_id=False):
75
        self._role_init_with_xml('role_id', elem, charset,
76
                include_id=include_id)
77

  
20 78

  
21 79
class DispatchWorkflowStatusItem(WorkflowStatusItem):
22 80
    description = N_('Assign a Function')
......
24 82

  
25 83
    role_id = None
26 84
    role_key = None
85
    dispatch_type = 'manual'
86
    variable = None
87
    rules = None
27 88

  
28 89
    def get_parameters(self):
29
        return ('role_key', 'role_id')
90
        return ('role_key', 'role_id', 'dispatch_type', 'variable', 'rules')
30 91

  
31 92
    def role_id_export_to_xml(self, item, charset, include_id=False):
32 93
        self._role_export_to_xml('role_id', item, charset,
......
36 97
        self._role_init_with_xml('role_id', elem, charset,
37 98
                include_id=include_id)
38 99

  
100
    def rules_export_to_xml(self, item, charset, include_id=False):
101
        if not self.rules:
102
            return
103

  
104
        rules_node = ET.SubElement(item, 'rules')
105
        for rule in self.rules:
106
            rules_node.append(RuleNode(rule).export_to_xml(charset=charset,
107
                include_id=include_id))
108

  
109
        return rules_node
110

  
111
    def rules_init_with_xml(self, elem, charset, include_id=False):
112
        rules = []
113
        if elem is None:
114
            return
115
        for rule_xml_node in elem.findall('rule'):
116
            rule_node = RuleNode()
117
            rule_node.init_with_xml(rule_xml_node, charset,
118
                    include_id=include_id)
119
            rules.append(rule_node.as_dict())
120
        if rules:
121
            self.rules = rules
122

  
39 123
    def render_as_line(self):
40 124
        if self.role_key:
41 125
            function_label = self.parent.parent.roles.get(self.role_key, '?')
......
49 133
            form.add(SingleSelectWidget, '%srole_key' % prefix,
50 134
                     title=_('Function to Set'), value=self.role_key,
51 135
                     options=[(None, '----')] + self.parent.parent.roles.items())
136
        dispatch_types = collections.OrderedDict(
137
                [('manual', _('Manual')), ('automatic', _('Automatic'))])
138
        if 'dispatch_type' in parameters:
139
            form.add(RadiobuttonsWidget, '%sdispatch_type' % prefix,
140
                    title=_('Type'),
141
                    options=dispatch_types.items(),
142
                    value=self.dispatch_type,
143
                    required=True,
144
                    attrs={'data-dynamic-display-parent': 'true'})
52 145
        if 'role_id' in parameters:
53 146
            form.add(SingleSelectWidget, '%srole_id' % prefix,
54 147
                     title=_('Role'), value=str(self.role_id),
55
                     options=[(None, '----', None)] + get_user_roles())
148
                     options=[(None, '----', None)] + get_user_roles(),
149
                     attrs={
150
                         'data-dynamic-display-child-of': '%sdispatch_type' % prefix,
151
                         'data-dynamic-display-value': dispatch_types.get('manual'),
152
                         }
153
                     )
154
        if 'variable' in parameters:
155
            form.add(StringWidget, '%svariable' % prefix,
156
                    title=_('Variable'),
157
                    value=self.variable,
158
                    attrs={
159
                        'data-dynamic-display-child-of': '%sdispatch_type' % prefix,
160
                        'data-dynamic-display-value': dispatch_types.get('automatic'),
161
                        }
162
                    )
163
        if 'rules' in parameters:
164
            form.add(AutomaticDispatchTableWidget, '%srules' % prefix,
165
                    title=_('Rules'),
166
                    value=self.rules,
167
                    attrs={
168
                        'data-dynamic-display-child-of': '%sdispatch_type' % prefix,
169
                        'data-dynamic-display-value': dispatch_types.get('automatic'),
170
                        }
171
                    )
56 172

  
57 173
    def perform(self, formdata):
58
        if not (self.role_id and self.role_key):
59
            return
60 174
        if not formdata.workflow_roles:
61 175
            formdata.workflow_roles = {}
62
        formdata.workflow_roles[self.role_key] = str(self.role_id)
63
        formdata.store()
64 176

  
65
register_item_class(DispatchWorkflowStatusItem)
177
        new_role_id = None
66 178

  
179
        if self.dispatch_type == 'manual' or not self.dispatch_type:
180
            if not (self.role_id and self.role_key):
181
                return
182
            new_role_id = self.role_id
183
        elif self.dispatch_type == 'automatic':
184
            if not (self.role_key and self.variable and self.rules):
185
                return
186
            variables = get_publisher().substitutions.get_context_variables()
187
            # convert the given value to a few different types, to allow more
188
            # diversity in matching.
189
            variable_values = [variables.get(self.variable)]
190
            if not variable_values[0]:
191
                variable_values.append(None)
192
            try:
193
                variable_values.append(int(variable_values[0]))
194
            except (ValueError, TypeError):
195
                pass
196

  
197
            for rule in self.rules:
198
                if rule.get('value') in variable_values:
199
                    new_role_id = rule.get('role_id')
200
                    break
201

  
202
        if new_role_id:
203
            if not Role.has_key(new_role_id):
204
                get_logger().error('error in dispatch, missing role %s' % new_role_id)
205
            else:
206
                formdata.workflow_roles[self.role_key] = str(new_role_id)
207
                formdata.store()
208

  
209
register_item_class(DispatchWorkflowStatusItem)
wcs/workflows.py
675 675

  
676 676
class XmlSerialisable(object):
677 677
    node_name = None
678
    key = None
678 679

  
679 680
    def export_to_xml(self, charset, include_id=False):
680 681
        node = ET.Element(self.node_name)
681
        node.attrib['type'] = self.key
682
        if self.key:
683
            node.attrib['type'] = self.key
682 684
        for attribute in self.get_parameters():
683 685
            if hasattr(self, '%s_export_to_xml' % attribute):
684 686
                getattr(self, '%s_export_to_xml' % attribute)(node, charset,
685
-