0001-workflows-add-possibility-to-dispatch-function-accor.patch
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 |
- |