Add context to workflow service for issue452
This commit is contained in:
+5
-3
@@ -1422,7 +1422,8 @@ class ORM(object):
|
||||
|
||||
wf_service = LocalService("workflow")
|
||||
for obj_id in ids:
|
||||
wf_service.trg_delete(user, self._name, obj_id, cursor)
|
||||
wf_service.trg_delete(user, self._name, obj_id, cursor,
|
||||
context=context)
|
||||
|
||||
if not self.check_xml_record(cursor, user, ids, None, context=context):
|
||||
self.raise_user_error(cursor, 'delete_xml_record',
|
||||
@@ -1699,7 +1700,8 @@ class ORM(object):
|
||||
|
||||
wf_service = LocalService("workflow")
|
||||
for obj_id in ids:
|
||||
wf_service.trg_write(user, self._name, obj_id, cursor)
|
||||
wf_service.trg_write(user, self._name, obj_id, cursor,
|
||||
context=context)
|
||||
return True
|
||||
|
||||
def __clean_defaults(self, defaults):
|
||||
@@ -1839,7 +1841,7 @@ class ORM(object):
|
||||
self._update_tree(cursor, user, id_new, k, field.left, field.right)
|
||||
|
||||
wf_service = LocalService("workflow")
|
||||
wf_service.trg_create(user, self._name, id_new, cursor)
|
||||
wf_service.trg_create(user, self._name, id_new, cursor, context=context)
|
||||
return id_new
|
||||
|
||||
def fields_get(self, cursor, user, fields_names=None, context=None):
|
||||
|
||||
+7
-4
@@ -90,10 +90,12 @@ class OSVService(Service):
|
||||
cursor.close()
|
||||
return res
|
||||
|
||||
def exec_workflow_cr(self, cursor, user, object_name, method, *args):
|
||||
def exec_workflow_cr(self, cursor, user, object_name, method, object_id,
|
||||
*args):
|
||||
wf_service = LocalService("workflow")
|
||||
try:
|
||||
wf_service.trg_validate(user, object_name, args[0], method, cursor)
|
||||
wf_service.trg_validate(user, object_name, object_id, method,
|
||||
cursor, *args)
|
||||
except Exception, exception:
|
||||
if CONFIG['verbose']:
|
||||
tb_s = reduce(lambda x, y: x+y,
|
||||
@@ -108,13 +110,14 @@ class OSVService(Service):
|
||||
raise
|
||||
return True
|
||||
|
||||
def exec_workflow(self, dbname, user, object_name, method, *args):
|
||||
def exec_workflow(self, dbname, user, object_name, method, object_id,
|
||||
*args):
|
||||
cursor = pooler.get_db(dbname).cursor()
|
||||
# TODO add retry when exception for concurency update
|
||||
try:
|
||||
try:
|
||||
res = self.exec_workflow_cr(cursor, user, object_name, method,
|
||||
*args)
|
||||
object_id, *args)
|
||||
cursor.commit()
|
||||
except Exception:
|
||||
cursor.rollback()
|
||||
|
||||
@@ -13,12 +13,12 @@ class Object(Service):
|
||||
Service.export_method(self, self.obj_list)
|
||||
|
||||
def exec_workflow(self, database, user, session, object_name, method,
|
||||
object_id):
|
||||
object_id, *args):
|
||||
security.check(database, user, session)
|
||||
Cache.clean(database)
|
||||
service = LocalService("object_proxy")
|
||||
res = service.exec_workflow(database, user, object_name, method,
|
||||
object_id)
|
||||
object_id, *args)
|
||||
return res
|
||||
|
||||
def execute(self, database, user, session, object_name, method, *args):
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
#This file is part of Tryton. The COPYRIGHT file at the top level of this repository contains the full copyright notices and license terms.
|
||||
import workitem
|
||||
|
||||
def create(cursor, ident, wkf_id):
|
||||
def create(cursor, ident, wkf_id, context=None):
|
||||
(user, res_type, res_id) = ident
|
||||
cursor.execute("SELECT NEXTVAL('wkf_instance_id_seq')")
|
||||
(id_new,) = cursor.fetchone()
|
||||
@@ -12,30 +12,32 @@ def create(cursor, ident, wkf_id):
|
||||
cursor.execute('SELECT * FROM wkf_activity ' \
|
||||
'WHERE flow_start = True and workflow = %s', (wkf_id,))
|
||||
res = cursor.dictfetchall()
|
||||
workitem.create(cursor, res, id_new, ident)
|
||||
workitem.create(cursor, res, id_new, ident, context=context)
|
||||
update(cursor, id_new, ident)
|
||||
return id_new
|
||||
|
||||
def delete(cursor, ident):
|
||||
def delete(cursor, ident, context=None):
|
||||
(user, res_type, res_id) = ident
|
||||
cursor.execute('DELETE FROM wkf_instance ' \
|
||||
'WHERE res_id = %s AND res_type = %s', (res_id, res_type))
|
||||
|
||||
def validate(cursor, inst_id, ident, signal, force_running=False):
|
||||
def validate(cursor, inst_id, ident, signal, force_running=False,
|
||||
context=None):
|
||||
cursor.execute("SELECT * FROM wkf_workitem WHERE instance = %s",
|
||||
(inst_id,))
|
||||
for witem in cursor.dictfetchall():
|
||||
workitem.process(cursor, witem, ident, signal, force_running)
|
||||
return _update_end(cursor, inst_id, ident)
|
||||
workitem.process(cursor, witem, ident, signal, force_running,
|
||||
context=context)
|
||||
return _update_end(cursor, inst_id, ident, context=context)
|
||||
|
||||
def update(cursor, inst_id, ident):
|
||||
def update(cursor, inst_id, ident, context=None):
|
||||
cursor.execute("SELECT * FROM wkf_workitem WHERE instance = %s",
|
||||
(inst_id,))
|
||||
for witem in cursor.dictfetchall():
|
||||
workitem.process(cursor, witem, ident)
|
||||
return _update_end(cursor, inst_id, ident)
|
||||
workitem.process(cursor, witem, ident, context=context)
|
||||
return _update_end(cursor, inst_id, ident, context=context)
|
||||
|
||||
def _update_end(cursor, inst_id, ident):
|
||||
def _update_end(cursor, inst_id, ident, context=None):
|
||||
cursor.execute('SELECT state, flow_stop FROM wkf_workitem w ' \
|
||||
'LEFT JOIN wkf_activity a ' \
|
||||
'ON (a.id = w.activity) WHERE w.instance = %s',
|
||||
@@ -65,5 +67,5 @@ def _update_end(cursor, inst_id, ident):
|
||||
for i in cursor.fetchall():
|
||||
for act_name in act_names:
|
||||
validate(cursor, i[0], (ident[0], i[1], i[2]),
|
||||
'subflow.' + act_name[0])
|
||||
'subflow.' + act_name[0], context=context)
|
||||
return res
|
||||
|
||||
@@ -4,46 +4,53 @@ from trytond import pooler
|
||||
|
||||
class EnvCall(object):
|
||||
|
||||
def __init__(self, obj, cursor, user, method, obj_id):
|
||||
def __init__(self, obj, cursor, user, method, obj_id, context=None):
|
||||
self.obj = obj
|
||||
self.cursor = cursor
|
||||
self.user = user
|
||||
self.method = method
|
||||
self.obj_id = obj_id
|
||||
self.context = context
|
||||
|
||||
def __call__(self, *args, **kargs):
|
||||
kargs = kargs.copy()
|
||||
kargs['context'] = self.context
|
||||
return getattr(self.obj, self.method)(self.cursor, self.user,
|
||||
self.obj_id, *args, **kargs)
|
||||
|
||||
|
||||
class Env(dict):
|
||||
|
||||
def __init__(self, cursor, user, model, obj_id):
|
||||
def __init__(self, cursor, user, model, obj_id, context=None):
|
||||
super(Env, self).__init__()
|
||||
self.cursor = cursor
|
||||
self.user = user
|
||||
self.model = model
|
||||
self.obj_id = obj_id
|
||||
self.context = context
|
||||
self.obj = pooler.get_pool(cursor.dbname).get(model)
|
||||
self.browse = self.obj.browse(cursor, user, obj_id)
|
||||
self.columns = self.obj._columns.keys() + \
|
||||
self.obj._inherit_fields.keys()
|
||||
|
||||
def __getitem__(self, key):
|
||||
if key in self.columns:
|
||||
if key == 'context':
|
||||
return self.context
|
||||
elif key in self.columns:
|
||||
res = self.browse[key]
|
||||
return res
|
||||
elif key in dir(self.obj):
|
||||
return EnvCall(self.obj, self.cursor, self.user, key, self.obj_id)
|
||||
return EnvCall(self.obj, self.cursor, self.user, key, self.obj_id,
|
||||
context=self.context)
|
||||
else:
|
||||
return super(Env, self).__getitem__(key)
|
||||
|
||||
def eval_expr(cursor, ident, action):
|
||||
def eval_expr(cursor, ident, action, context=None):
|
||||
res = False
|
||||
user = ident[0]
|
||||
model = ident[1]
|
||||
obj_id = ident[2]
|
||||
env = Env(cursor, user, model, obj_id)
|
||||
env = Env(cursor, user, model, obj_id, context=context)
|
||||
for line in action.split('\n'):
|
||||
if line == 'True':
|
||||
res = True
|
||||
@@ -53,10 +60,10 @@ def eval_expr(cursor, ident, action):
|
||||
res = eval(line, env)
|
||||
return res
|
||||
|
||||
def execute(cursor, ident, activity):
|
||||
return eval_expr(cursor, ident, activity['action'])
|
||||
def execute(cursor, ident, activity, context=None):
|
||||
return eval_expr(cursor, ident, activity['action'], context=context)
|
||||
|
||||
def check(cursor, ident, transition, signal):
|
||||
def check(cursor, ident, transition, signal, context=None):
|
||||
res = True
|
||||
if transition['signal']:
|
||||
res = (signal == transition['signal'])
|
||||
@@ -64,7 +71,9 @@ def check(cursor, ident, transition, signal):
|
||||
if transition['group']:
|
||||
user = ident[0]
|
||||
user_obj = pooler.get_pool(cursor.dbname).get('res.user')
|
||||
user_groups = user_obj.read(cursor, user, user, ['groups'])['groups']
|
||||
user_groups = user_obj.read(cursor, user, user, ['groups'],
|
||||
context=context)['groups']
|
||||
res = res and transition['group'] in user_groups
|
||||
res = res and eval_expr(cursor, ident, transition['condition'])
|
||||
res = res and eval_expr(cursor, ident, transition['condition'],
|
||||
context=context)
|
||||
return res
|
||||
|
||||
@@ -22,16 +22,16 @@ class WorkflowService(Service):
|
||||
"Clear workflow cache"
|
||||
self.wkf_on_create_cache[cursor.dbname] = {}
|
||||
|
||||
def trg_write(self, user, res_type, res_id, cursor):
|
||||
def trg_write(self, user, res_type, res_id, cursor, context=None):
|
||||
"Trigger write"
|
||||
ident = (user, res_type, res_id)
|
||||
cursor.execute('SELECT id FROM wkf_instance ' \
|
||||
'WHERE res_id = %s AND res_type = %s AND state = %s',
|
||||
(res_id, res_type, 'active'))
|
||||
for (instance_id,) in cursor.fetchall():
|
||||
instance.update(cursor, instance_id, ident)
|
||||
instance.update(cursor, instance_id, ident, context=context)
|
||||
|
||||
def trg_trigger(self, user, res_type, res_id, cursor):
|
||||
def trg_trigger(self, user, res_type, res_id, cursor, context=None):
|
||||
"Trigger trigger"
|
||||
cursor.execute('SELECT instance FROM wkf_trigger ' \
|
||||
'WHERE res_id = %s AND model = %s', (res_id, res_type))
|
||||
@@ -40,14 +40,14 @@ class WorkflowService(Service):
|
||||
cursor.execute('SELECT uid, res_type, res_id FROM wkf_instance ' \
|
||||
'WHERE id = %s', (instance_id,))
|
||||
ident = cursor.fetchone()
|
||||
instance.update(cursor, instance_id, ident)
|
||||
instance.update(cursor, instance_id, ident, context=context)
|
||||
|
||||
def trg_delete(self, user, res_type, res_id, cursor):
|
||||
def trg_delete(self, user, res_type, res_id, cursor, context=None):
|
||||
"Trigger delete"
|
||||
ident = (user, res_type, res_id)
|
||||
instance.delete(cursor, ident)
|
||||
instance.delete(cursor, ident, context=context)
|
||||
|
||||
def trg_create(self, user, res_type, res_id, cursor):
|
||||
def trg_create(self, user, res_type, res_id, cursor, context=None):
|
||||
"Trigger create"
|
||||
ident = (user, res_type, res_id)
|
||||
self.wkf_on_create_cache.setdefault(cursor.dbname, {})
|
||||
@@ -68,9 +68,10 @@ class WorkflowService(Service):
|
||||
raise Exception("Error", "Another active workflow already "\
|
||||
"exist for this record: %s@%s."% \
|
||||
(res_id, res_type))
|
||||
instance.create(cursor, ident, wkf_id)
|
||||
instance.create(cursor, ident, wkf_id, context=context)
|
||||
|
||||
def trg_validate(self, user, res_type, res_id, signal, cursor):
|
||||
def trg_validate(self, user, res_type, res_id, signal, cursor,
|
||||
context=None):
|
||||
"Trigger validate"
|
||||
ident = (user, res_type, res_id)
|
||||
# ids of all active workflow instances
|
||||
@@ -79,9 +80,11 @@ class WorkflowService(Service):
|
||||
'WHERE res_id = %s AND res_type = %s AND state = %s',
|
||||
(res_id, res_type, 'active'))
|
||||
for (instance_id,) in cursor.fetchall():
|
||||
instance.validate(cursor, instance_id, ident, signal)
|
||||
instance.validate(cursor, instance_id, ident, signal,
|
||||
context=context)
|
||||
|
||||
def trg_redirect(self, user, res_type, res_id, new_rid, cursor):
|
||||
def trg_redirect(self, user, res_type, res_id, new_rid, cursor,
|
||||
context=None):
|
||||
"""
|
||||
Trigger redirect
|
||||
make all workitems which are waiting for a (subflow) workflow instance
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import wkf_expr
|
||||
import instance
|
||||
|
||||
def create(cursor, act_datas, inst_id, ident):
|
||||
def create(cursor, act_datas, inst_id, ident, context=None):
|
||||
for act in act_datas:
|
||||
cursor.execute("SELECT NEXTVAL('wkf_workitem_id_seq')")
|
||||
(id_new,) = cursor.fetchone()
|
||||
@@ -11,16 +11,17 @@ def create(cursor, act_datas, inst_id, ident):
|
||||
(id_new, act['id'], inst_id))
|
||||
cursor.execute('SELECT * FROM wkf_workitem WHERE id=%s', (id_new,))
|
||||
res = cursor.dictfetchone()
|
||||
process(cursor, res, ident)
|
||||
process(cursor, res, ident, context=context)
|
||||
|
||||
def process(cursor, workitem, ident, signal=None, force_running=False):
|
||||
def process(cursor, workitem, ident, signal=None, force_running=False,
|
||||
context=None):
|
||||
cursor.execute('SELECT * FROM wkf_activity WHERE id = %s',
|
||||
(workitem['activity'],))
|
||||
activity = cursor.dictfetchone()
|
||||
triggers = False
|
||||
if workitem['state'] == 'active':
|
||||
triggers = True
|
||||
if not _execute(cursor, workitem, activity, ident):
|
||||
if not _execute(cursor, workitem, activity, ident, context=context):
|
||||
return False
|
||||
|
||||
if workitem['state'] == 'running':
|
||||
@@ -28,7 +29,7 @@ def process(cursor, workitem, ident, signal=None, force_running=False):
|
||||
|
||||
if workitem['state'] == 'complete' or force_running:
|
||||
res = _split_test(cursor, workitem, activity['split_mode'], ident,
|
||||
signal)
|
||||
signal, context=context)
|
||||
triggers = triggers and not res
|
||||
|
||||
if triggers:
|
||||
@@ -38,7 +39,7 @@ def process(cursor, workitem, ident, signal=None, force_running=False):
|
||||
for trans in alltrans:
|
||||
if trans['trigger_model']:
|
||||
ids = wkf_expr.eval_expr(cursor, ident,
|
||||
trans['trigger_expr_id'])
|
||||
trans['trigger_expr_id'], context=context)
|
||||
for res_id in ids:
|
||||
cursor.execute('SELECT NEXTVAL(\'wkf_trigger_id_seq\')')
|
||||
(new_id,) = cursor.fetchone()
|
||||
@@ -54,7 +55,7 @@ def _state_set(cursor, workitem, state):
|
||||
'SET state = %s WHERE id = %s', (state, workitem['id']))
|
||||
workitem['state'] = state
|
||||
|
||||
def _execute(cursor, workitem, activity, ident):
|
||||
def _execute(cursor, workitem, activity, ident, context=None):
|
||||
"send a signal to parent workflow (signal: subflow.signal_name)"
|
||||
|
||||
if (workitem['state'] == 'active') and activity['signal_send']:
|
||||
@@ -67,7 +68,8 @@ def _execute(cursor, workitem, activity, ident):
|
||||
"WHERE wi.subflow = %s)", (workitem['instance'],))
|
||||
for i in cursor.fetchall():
|
||||
instance.validate(cursor, i[0], (ident[0], i[1], i[2]),
|
||||
activity['signal_send'], force_running=True)
|
||||
activity['signal_send'], force_running=True,
|
||||
context=context)
|
||||
|
||||
if activity['kind'] == 'dummy':
|
||||
if workitem['state'] == 'active':
|
||||
@@ -75,7 +77,7 @@ def _execute(cursor, workitem, activity, ident):
|
||||
elif activity['kind'] == 'function':
|
||||
if workitem['state'] == 'active':
|
||||
_state_set(cursor, workitem, 'running')
|
||||
wkf_expr.execute(cursor, ident, activity)
|
||||
wkf_expr.execute(cursor, ident, activity, context=context)
|
||||
_state_set(cursor, workitem, 'complete')
|
||||
elif activity['kind'] == 'stopall':
|
||||
if workitem['state'] == 'active':
|
||||
@@ -84,13 +86,14 @@ def _execute(cursor, workitem, activity, ident):
|
||||
'WHERE instance = %s AND id <> %s',
|
||||
(workitem['instance'], workitem['id']))
|
||||
if activity['action']:
|
||||
wkf_expr.execute(cursor, ident, activity)
|
||||
wkf_expr.execute(cursor, ident, activity, context=context)
|
||||
_state_set(cursor, workitem, 'complete')
|
||||
elif activity['kind'] == 'subflow':
|
||||
if workitem['state'] == 'active':
|
||||
_state_set(cursor, workitem, 'running')
|
||||
if activity.get('action', False):
|
||||
id_new = wkf_expr.execute(cursor, ident, activity)
|
||||
id_new = wkf_expr.execute(cursor, ident, activity,
|
||||
context=context)
|
||||
if not (id_new):
|
||||
cursor.execute('DELETE FROM wkf_workitem ' \
|
||||
'WHERE id = %s', (workitem['id'],))
|
||||
@@ -103,7 +106,8 @@ def _execute(cursor, workitem, activity, ident):
|
||||
(id_new, activity['subflow']))
|
||||
(id_new,) = cursor.fetchone()
|
||||
else:
|
||||
id_new = instance.create(cursor, ident, activity['subflow'])
|
||||
id_new = instance.create(cursor, ident, activity['subflow'],
|
||||
context=context)
|
||||
cursor.execute('UPDATE wkf_workitem ' \
|
||||
'SET subflow = %s WHERE id = %s',
|
||||
(id_new, workitem['id']))
|
||||
@@ -116,7 +120,7 @@ def _execute(cursor, workitem, activity, ident):
|
||||
_state_set(cursor, workitem, 'complete')
|
||||
return True
|
||||
|
||||
def _split_test(cursor, workitem, split_mode, ident, signal=None):
|
||||
def _split_test(cursor, workitem, split_mode, ident, signal=None, context=None):
|
||||
test = False
|
||||
transitions = []
|
||||
cursor.execute('SELECT * FROM wkf_transition ' \
|
||||
@@ -124,7 +128,8 @@ def _split_test(cursor, workitem, split_mode, ident, signal=None):
|
||||
alltrans = cursor.dictfetchall()
|
||||
if split_mode == 'XOR' or split_mode == 'OR':
|
||||
for transition in alltrans:
|
||||
if wkf_expr.check(cursor, ident, transition, signal):
|
||||
if wkf_expr.check(cursor, ident, transition, signal,
|
||||
context=context):
|
||||
test = True
|
||||
transitions.append((transition['id'], workitem['instance']))
|
||||
if split_mode == 'XOR':
|
||||
@@ -132,7 +137,8 @@ def _split_test(cursor, workitem, split_mode, ident, signal=None):
|
||||
else:
|
||||
test = True
|
||||
for transition in alltrans:
|
||||
if not wkf_expr.check(cursor, ident, transition, signal):
|
||||
if not wkf_expr.check(cursor, ident, transition, signal,
|
||||
context=context):
|
||||
test = False
|
||||
break
|
||||
cursor.execute('SELECT COUNT(*) ' \
|
||||
@@ -147,18 +153,19 @@ def _split_test(cursor, workitem, split_mode, ident, signal=None):
|
||||
cursor.execute('DELETE FROM wkf_workitem WHERE id = %s',
|
||||
(workitem['id'],))
|
||||
for transition in transitions:
|
||||
_join_test(cursor, transition[0], transition[1], ident)
|
||||
_join_test(cursor, transition[0], transition[1], ident,
|
||||
context=context)
|
||||
return True
|
||||
return False
|
||||
|
||||
def _join_test(cursor, trans_id, inst_id, ident):
|
||||
def _join_test(cursor, trans_id, inst_id, ident, context=None):
|
||||
cursor.execute('SELECT a.* FROM wkf_activity a ' \
|
||||
'LEFT JOIN wkf_transition t ' \
|
||||
'ON (a.id = t.act_to) ' \
|
||||
'WHERE t.id = %s', (trans_id,))
|
||||
activity = cursor.dictfetchone()
|
||||
if activity['join_mode'] == 'XOR':
|
||||
create(cursor, [activity], inst_id, ident)
|
||||
create(cursor, [activity], inst_id, ident, context=context)
|
||||
cursor.execute('DELETE FROM wkf_witm_trans ' \
|
||||
'WHERE inst_id = %s AND trans_id = %s', (inst_id, trans_id))
|
||||
else:
|
||||
@@ -179,4 +186,4 @@ def _join_test(cursor, trans_id, inst_id, ident):
|
||||
cursor.execute('DELETE FROM wkf_witm_trans ' \
|
||||
'WHERE trans_id = %s AND inst_id = %s',
|
||||
(trans_id, inst_id))
|
||||
create(cursor, [activity], inst_id, ident)
|
||||
create(cursor, [activity], inst_id, ident, context=context)
|
||||
|
||||
Reference in New Issue
Block a user