Add context to workflow service for issue452

This commit is contained in:
Cédric Krier
2008-10-27 16:39:37 +01:00
parent 27fb4ca538
commit aaa5cfc8c6
7 changed files with 87 additions and 61 deletions
+5 -3
View File
@@ -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
View File
@@ -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()
+2 -2
View File
@@ -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):
+13 -11
View File
@@ -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
+20 -11
View File
@@ -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
+14 -11
View File
@@ -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
+26 -19
View File
@@ -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)