Commit d8635361 authored by Vincent Pelletier's avatar Vincent Pelletier Committed by Your Name

WorkflowTool: Accelerate action generation.

Include worklist parameter generation in the scope of existing cache.
Otherwise, it will be generated in pure loss if it is followed by a cache
hit. Most of WorkflowTool change is just indentation change.

Also, do some minor optimisations/simplifications in patchess.DCWorkflow.
Some comments:
- Guard_checkWithoutRoles return value is evaluated as a boolean, so no
  need to cast to int before returning based on boolean evaluation...
- DCWorkflowDefinition.worklists is always true, even when empty.
- Listing portal types per workflow requires checking all workflows, so
  build the whole mapping and cache it instead of caching for each workflow
  type individually (many more cache hits, fewer redundant computations)
- getVarMatch is expensive just for a fallback and a wrap, bypass it to
  reduce redundant work.
parent 138dfa05
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
# Optimized rendering of global actions (cache) # Optimized rendering of global actions (cache)
from collections import defaultdict
from Products.ERP5Type.Globals import DTMLFile from Products.ERP5Type.Globals import DTMLFile
from Products.ERP5Type import Permissions, _dtmldir from Products.ERP5Type import Permissions, _dtmldir
from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition, StateChangeInfo, createExprContext from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition, StateChangeInfo, createExprContext
...@@ -74,7 +75,6 @@ def Guard_checkWithoutRoles(self, sm, wf_def, ob, **kw): ...@@ -74,7 +75,6 @@ def Guard_checkWithoutRoles(self, sm, wf_def, ob, **kw):
because we only want this specific behaviour for worklists (Guards are because we only want this specific behaviour for worklists (Guards are
also used in transitions). also used in transitions).
""" """
u_roles = None
if wf_def.manager_bypass: if wf_def.manager_bypass:
# Possibly bypass. # Possibly bypass.
u_roles = sm.getUser().getRolesInContext(ob) u_roles = sm.getUser().getRolesInContext(ob)
...@@ -103,11 +103,11 @@ def Guard_checkWithoutRoles(self, sm, wf_def, ob, **kw): ...@@ -103,11 +103,11 @@ def Guard_checkWithoutRoles(self, sm, wf_def, ob, **kw):
return 0 return 0
expr = self.expr expr = self.expr
if expr is not None: if expr is not None:
econtext = createExprContext( return expr(createExprContext(StateChangeInfo(
StateChangeInfo(ob, wf_def, kwargs=kw)) ob,
res = expr(econtext) wf_def,
if not res: kwargs=kw,
return 0 )))
return 1 return 1
DCWorkflowDefinition.security = ClassSecurityInfo() DCWorkflowDefinition.security = ClassSecurityInfo()
...@@ -262,81 +262,82 @@ def DCWorkflowDefinition_getWorklistVariableMatchDict(self, info, ...@@ -262,81 +262,82 @@ def DCWorkflowDefinition_getWorklistVariableMatchDict(self, info,
(worklist id as key) and which value is a dict composed of (worklist id as key) and which value is a dict composed of
variable matches. variable matches.
""" """
if not self.worklists: worklist_items = self.worklists.items()
if not worklist_items:
return None return None
portal = self.getPortalObject() portal = self.getPortalObject()
def getPortalTypeListForWorkflow(workflow_id): def getPortalTypeListByWorkflowIdDict():
workflow_tool = portal.portal_workflow workflow_tool = portal.portal_workflow
result = [] result = defaultdict(list)
append = result.append
for type_info in workflow_tool._listTypeInfo(): for type_info in workflow_tool._listTypeInfo():
portal_type = type_info.id portal_type = type_info.id
if workflow_id in workflow_tool.getChainFor(portal_type): for workflow_id in workflow_tool.getChainFor(portal_type):
append(portal_type) result[workflow_id].append(portal_type)
return result return result
portal_type_list = CachingMethod(
_getPortalTypeListForWorkflow = CachingMethod(getPortalTypeListForWorkflow, getPortalTypeListByWorkflowIdDict,
id='_getPortalTypeListForWorkflow', cache_factory = 'erp5_ui_long') id='_getPortalTypeListByWorkflowIdDict',
portal_type_list = _getPortalTypeListForWorkflow(self.id) cache_factory='erp5_ui_long',
)()[self.id]
if not portal_type_list: if not portal_type_list:
return None return None
portal_type_set = set(portal_type_list)
variable_match_dict = {} variable_match_dict = {}
security_manager = getSecurityManager() security_manager = getSecurityManager()
workflow_id = self.id workflow_id = self.id
workflow_title = self.title workflow_title = self.title
for worklist_id, worklist_definition in self.worklists.items(): for worklist_id, worklist_definition in worklist_items:
action_box_name = worklist_definition.actbox_name action_box_name = worklist_definition.actbox_name
guard = worklist_definition.guard
if action_box_name: if action_box_name:
variable_match = {} variable_match = {}
for key in worklist_definition.getVarMatchKeys(): for key, var in (worklist_definition.var_matches or {}).iteritems():
var = worklist_definition.getVarMatch(key)
if isinstance(var, Expression): if isinstance(var, Expression):
evaluated_value = var(createExprContext(StateChangeInfo(portal, evaluated_value = var(createExprContext(StateChangeInfo(portal,
self, kwargs=info.__dict__.copy()))) self, kwargs=info.__dict__.copy())))
if isinstance(evaluated_value, (str, int, long)): if isinstance(evaluated_value, (str, int, long)):
evaluated_value = [str(evaluated_value)] evaluated_value = [str(evaluated_value)]
else: else:
if not isinstance(var, tuple):
var = (var, )
evaluated_value = [x % info for x in var] evaluated_value = [x % info for x in var]
variable_match[key] = evaluated_value variable_match[key] = evaluated_value
if 'portal_type' in variable_match and len(variable_match['portal_type']): portal_type_match = variable_match.get('portal_type')
portal_type_intersection = set(variable_match['portal_type'])\ if portal_type_match:
.intersection(portal_type_list)
# in case the current workflow is not associated with portal_types # in case the current workflow is not associated with portal_types
# defined on the worklist, don't display the worklist for this # defined on the worklist, don't display the worklist for this
# portal_type. # portal_type.
variable_match['portal_type'] = list(portal_type_intersection) variable_match['portal_type'] = list(
variable_match.setdefault('portal_type', portal_type_list) portal_type_set.intersection(portal_type_match)
)
if len(variable_match.get('portal_type', [])) == 0: if not variable_match.setdefault('portal_type', portal_type_list):
continue continue
is_permitted_worklist = 0 guard = worklist_definition.guard
if guard is None: if (
is_permitted_worklist = 1 guard is None or
elif (not check_guard) or \ not check_guard or
Guard_checkWithoutRoles(guard, security_manager, self, portal): Guard_checkWithoutRoles(guard, security_manager, self, portal)
is_permitted_worklist = 1 ):
variable_match[SECURITY_PARAMETER_ID] = guard.roles
if is_permitted_worklist:
format_data = TemplateDict() format_data = TemplateDict()
format_data._push(info) format_data._push(info)
variable_match.setdefault(SECURITY_PARAMETER_ID, ()) variable_match.setdefault(SECURITY_PARAMETER_ID, getattr(guard, 'roles', ()))
format_data._push({k: ('&%s:list=' % k).join(v) format_data._push({
for k, v in variable_match.iteritems()}) k: ('&%s:list=' % k).join(v)
variable_match[WORKLIST_METADATA_KEY] = {'format_data': format_data, for k, v in variable_match.iteritems()
})
variable_match[WORKLIST_METADATA_KEY] = {
'format_data': format_data,
'worklist_title': action_box_name, 'worklist_title': action_box_name,
'worklist_id': worklist_id, 'worklist_id': worklist_id,
'workflow_title': workflow_title, 'workflow_title': workflow_title,
'workflow_id': workflow_id, 'workflow_id': workflow_id,
'action_box_url': worklist_definition.actbox_url, 'action_box_url': worklist_definition.actbox_url,
'action_box_category': worklist_definition.actbox_category} 'action_box_category': worklist_definition.actbox_category,
}
variable_match_dict[worklist_id] = variable_match variable_match_dict[worklist_id] = variable_match
if len(variable_match_dict) == 0: if variable_match_dict:
return None
return variable_match_dict return variable_match_dict
return None
DCWorkflowDefinition.security.declarePrivate('getWorklistVariableMatchDict') DCWorkflowDefinition.security.declarePrivate('getWorklistVariableMatchDict')
DCWorkflowDefinition.getWorklistVariableMatchDict = DCWorkflowDefinition_getWorklistVariableMatchDict DCWorkflowDefinition.getWorklistVariableMatchDict = DCWorkflowDefinition_getWorklistVariableMatchDict
......
...@@ -457,35 +457,26 @@ def WorkflowTool_listActions(self, info=None, object=None, src__=False): ...@@ -457,35 +457,26 @@ def WorkflowTool_listActions(self, info=None, object=None, src__=False):
""" """
if object is not None or info is None: if object is not None or info is None:
info = self._getOAI(object) info = self._getOAI(object)
chain = self.getChainFor(info.object)
did = {}
actions = [] actions = []
worklist_dict = {} for wf_id in self.getChainFor(info.object):
for wf_id in chain:
did[wf_id] = None
wf = self.getWorkflowById(wf_id) wf = self.getWorkflowById(wf_id)
if wf is not None: if wf is not None:
a = wf.listObjectActions(info) a = wf.listObjectActions(info)
if a is not None: if a is not None:
actions.extend(a) actions.extend(a)
a = wf.getWorklistVariableMatchDict(info)
if a is not None:
worklist_dict[wf_id] = a
wf_ids = self.getWorkflowIds() portal = self.getPortalObject()
for wf_id in wf_ids: portal_url = portal.portal_url()
if not did.has_key(wf_id): def _getWorklistActionList():
worklist_dict = {}
for wf_id in self.getWorkflowIds():
wf = self.getWorkflowById(wf_id) wf = self.getWorkflowById(wf_id)
if wf is not None: if wf is not None:
a = wf.getWorklistVariableMatchDict(info) a = wf.getWorklistVariableMatchDict(info)
if a is not None: if a is not None:
worklist_dict[wf_id] = a worklist_dict[wf_id] = a
if not worklist_dict:
if worklist_dict: return ()
portal = self.getPortalObject()
portal_url = portal.portal_url()
def _getWorklistActionList():
is_anonymous = portal.portal_membership.isAnonymousUser() is_anonymous = portal.portal_membership.isAnonymousUser()
portal_catalog = portal.portal_catalog portal_catalog = portal.portal_catalog
sql_catalog = portal_catalog.getSQLCatalog() sql_catalog = portal_catalog.getSQLCatalog()
...@@ -605,14 +596,18 @@ def WorkflowTool_listActions(self, info=None, object=None, src__=False): ...@@ -605,14 +596,18 @@ def WorkflowTool_listActions(self, info=None, object=None, src__=False):
key=lambda x: '/'.join((x['workflow_id'], x['worklist_id'])), key=lambda x: '/'.join((x['workflow_id'], x['worklist_id'])),
) )
return action_list return action_list
user = _getAuthenticatedUser(self).getIdOrUserName()
if src__: if src__:
actions = _getWorklistActionList() actions = _getWorklistActionList()
else: else:
_getWorklistActionList = CachingMethod(_getWorklistActionList, actions.extend(CachingMethod(
id=('_getWorklistActionList', user, portal_url), _getWorklistActionList,
cache_factory = 'erp5_ui_short') id=(
actions.extend(_getWorklistActionList()) '_getWorklistActionList',
_getAuthenticatedUser(self).getIdOrUserName(),
portal_url,
),
cache_factory = 'erp5_ui_short',
)())
return actions return actions
WorkflowTool.listActions = WorkflowTool_listActions WorkflowTool.listActions = WorkflowTool_listActions
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment