Commit bba23043 authored by Vincent Pelletier's avatar Vincent Pelletier

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 43dd2515
......@@ -15,6 +15,7 @@
# Optimized rendering of global actions (cache)
from collections import defaultdict
from Products.ERP5Type.Globals import DTMLFile
from Products.ERP5Type import Permissions, _dtmldir
from Products.DCWorkflow.DCWorkflow import DCWorkflowDefinition, StateChangeInfo, createExprContext
......@@ -74,7 +75,6 @@ def Guard_checkWithoutRoles(self, sm, wf_def, ob, **kw):
because we only want this specific behaviour for worklists (Guards are
also used in transitions).
"""
u_roles = None
if wf_def.manager_bypass:
# Possibly bypass.
u_roles = sm.getUser().getRolesInContext(ob)
......@@ -103,11 +103,11 @@ def Guard_checkWithoutRoles(self, sm, wf_def, ob, **kw):
return 0
expr = self.expr
if expr is not None:
econtext = createExprContext(
StateChangeInfo(ob, wf_def, kwargs=kw))
res = expr(econtext)
if not res:
return 0
return expr(createExprContext(StateChangeInfo(
ob,
wf_def,
kwargs=kw,
)))
return 1
DCWorkflowDefinition.security = ClassSecurityInfo()
......@@ -262,81 +262,82 @@ def DCWorkflowDefinition_getWorklistVariableMatchDict(self, info,
(worklist id as key) and which value is a dict composed of
variable matches.
"""
if not self.worklists:
worklist_items = self.worklists.items()
if not worklist_items:
return None
portal = self.getPortalObject()
def getPortalTypeListForWorkflow(workflow_id):
workflow_tool = portal.portal_workflow
result = []
append = result.append
for type_info in workflow_tool._listTypeInfo():
portal_type = type_info.id
if workflow_id in workflow_tool.getChainFor(portal_type):
append(portal_type)
return result
_getPortalTypeListForWorkflow = CachingMethod(getPortalTypeListForWorkflow,
id='_getPortalTypeListForWorkflow', cache_factory = 'erp5_ui_long')
portal_type_list = _getPortalTypeListForWorkflow(self.id)
def getPortalTypeListByWorkflowIdDict():
workflow_tool = portal.portal_workflow
result = defaultdict(list)
for type_info in workflow_tool._listTypeInfo():
portal_type = type_info.id
for workflow_id in workflow_tool.getChainFor(portal_type):
result[workflow_id].append(portal_type)
return result
portal_type_list = CachingMethod(
getPortalTypeListByWorkflowIdDict,
id='_getPortalTypeListByWorkflowIdDict',
cache_factory='erp5_ui_long',
)()[self.id]
if not portal_type_list:
return None
portal_type_set = set(portal_type_list)
variable_match_dict = {}
security_manager = getSecurityManager()
workflow_id = self.id
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
guard = worklist_definition.guard
if action_box_name:
variable_match = {}
for key in worklist_definition.getVarMatchKeys():
var = worklist_definition.getVarMatch(key)
for key, var in (worklist_definition.var_matches or {}).iteritems():
if isinstance(var, Expression):
evaluated_value = var(createExprContext(StateChangeInfo(portal,
self, kwargs=info.__dict__.copy())))
if isinstance(evaluated_value, (str, int, long)):
evaluated_value = [str(evaluated_value)]
else:
if not isinstance(var, tuple):
var = (var, )
evaluated_value = [x % info for x in var]
variable_match[key] = evaluated_value
if 'portal_type' in variable_match and len(variable_match['portal_type']):
portal_type_intersection = set(variable_match['portal_type'])\
.intersection(portal_type_list)
portal_type_match = variable_match.get('portal_type')
if portal_type_match:
# in case the current workflow is not associated with portal_types
# defined on the worklist, don't display the worklist for this
# portal_type.
variable_match['portal_type'] = list(portal_type_intersection)
variable_match.setdefault('portal_type', portal_type_list)
if len(variable_match.get('portal_type', [])) == 0:
variable_match['portal_type'] = list(
portal_type_set.intersection(portal_type_match)
)
if not variable_match.setdefault('portal_type', portal_type_list):
continue
is_permitted_worklist = 0
if guard is None:
is_permitted_worklist = 1
elif (not check_guard) or \
Guard_checkWithoutRoles(guard, security_manager, self, portal):
is_permitted_worklist = 1
variable_match[SECURITY_PARAMETER_ID] = guard.roles
if is_permitted_worklist:
guard = worklist_definition.guard
if (
guard is None or
not check_guard or
Guard_checkWithoutRoles(guard, security_manager, self, portal)
):
format_data = TemplateDict()
format_data._push(info)
variable_match.setdefault(SECURITY_PARAMETER_ID, ())
format_data._push({k: ('&%s:list=' % k).join(v)
for k, v in variable_match.iteritems()})
variable_match[WORKLIST_METADATA_KEY] = {'format_data': format_data,
'worklist_title': action_box_name,
'worklist_id': worklist_id,
'workflow_title': workflow_title,
'workflow_id': workflow_id,
'action_box_url': worklist_definition.actbox_url,
'action_box_category': worklist_definition.actbox_category}
variable_match.setdefault(SECURITY_PARAMETER_ID, getattr(guard, 'roles', ()))
format_data._push({
k: ('&%s:list=' % k).join(v)
for k, v in variable_match.iteritems()
})
variable_match[WORKLIST_METADATA_KEY] = {
'format_data': format_data,
'worklist_title': action_box_name,
'worklist_id': worklist_id,
'workflow_title': workflow_title,
'workflow_id': workflow_id,
'action_box_url': worklist_definition.actbox_url,
'action_box_category': worklist_definition.actbox_category,
}
variable_match_dict[worklist_id] = variable_match
if len(variable_match_dict) == 0:
return None
return variable_match_dict
if variable_match_dict:
return variable_match_dict
return None
DCWorkflowDefinition.security.declarePrivate('getWorklistVariableMatchDict')
DCWorkflowDefinition.getWorklistVariableMatchDict = DCWorkflowDefinition_getWorklistVariableMatchDict
......
......@@ -457,162 +457,157 @@ def WorkflowTool_listActions(self, info=None, object=None, src__=False):
"""
if object is not None or info is None:
info = self._getOAI(object)
chain = self.getChainFor(info.object)
did = {}
actions = []
worklist_dict = {}
for wf_id in chain:
did[wf_id] = None
for wf_id in self.getChainFor(info.object):
wf = self.getWorkflowById(wf_id)
if wf is not None:
a = wf.listObjectActions(info)
if a is not None:
actions.extend(a)
a = wf.getWorklistVariableMatchDict(info)
if a is not None:
worklist_dict[wf_id] = a
wf_ids = self.getWorkflowIds()
for wf_id in wf_ids:
if not did.has_key(wf_id):
portal = self.getPortalObject()
portal_url = portal.portal_url()
def _getWorklistActionList():
worklist_dict = {}
for wf_id in self.getWorkflowIds():
wf = self.getWorkflowById(wf_id)
if wf is not None:
a = wf.getWorklistVariableMatchDict(info)
if a is not None:
worklist_dict[wf_id] = a
if worklist_dict:
portal = self.getPortalObject()
portal_url = portal.portal_url()
def _getWorklistActionList():
is_anonymous = portal.portal_membership.isAnonymousUser()
portal_catalog = portal.portal_catalog
sql_catalog = portal_catalog.getSQLCatalog()
catalog_security_uid_groups_columns_dict = \
sql_catalog.getSQLCatalogSecurityUidGroupsColumnsDict()
getSecurityUidDictAndRoleColumnDict = \
portal_catalog.getSecurityUidDictAndRoleColumnDict
search_result_ = getattr(self, "Base_getCountFromWorklistTable", None)
use_cache = search_result_ is not None
if use_cache:
ignored_security_column_id_set = self._getWorklistIgnoredSecurityColumnSet()
ignored_security_uid_parameter_set = {x
for x, y in catalog_security_uid_groups_columns_dict.iteritems()
if y in ignored_security_column_id_set
}
_getSecurityUidDictAndRoleColumnDict = getSecurityUidDictAndRoleColumnDict
def getSecurityUidDictAndRoleColumnDict(**kw):
security_uid_dict, role_column_dict, local_role_column_dict = \
_getSecurityUidDictAndRoleColumnDict(**kw)
for ignored_security_column_id in ignored_security_column_id_set:
role_column_dict.pop(ignored_security_column_id, None)
local_role_column_dict.pop(ignored_security_column_id, None)
for ignored_security_uid_parameter in \
ignored_security_uid_parameter_set:
security_uid_dict.pop(ignored_security_uid_parameter)
return security_uid_dict, role_column_dict, local_role_column_dict
count_column_expression = 'sum(`%s`)' % (COUNT_COLUMN_TITLE, )
# Prevent catalog from trying to join
getQuery = SimpleQuery
# BBB
def search_result(select_dict, group_by, query, limit, src__):
select_item_list = []
for alias, expression in select_dict.iteritems():
if expression is None:
expression = alias
select_item_list.append('%s AS %s' % (expression, alias))
return search_result_(
select_expression=','.join(select_item_list),
group_by_expression=','.join(group_by),
query=query,
limit=limit,
src__=src__,
)
else:
search_result = portal_catalog.unrestrictedSearchResults
count_column_expression = 'count(*)'
# Let catalog join as needed
getQuery = lambda comparison_operator=None, **kw: AutoQuery(
operator=comparison_operator,
**kw
if not worklist_dict:
return ()
is_anonymous = portal.portal_membership.isAnonymousUser()
portal_catalog = portal.portal_catalog
sql_catalog = portal_catalog.getSQLCatalog()
catalog_security_uid_groups_columns_dict = \
sql_catalog.getSQLCatalogSecurityUidGroupsColumnsDict()
getSecurityUidDictAndRoleColumnDict = \
portal_catalog.getSecurityUidDictAndRoleColumnDict
search_result_ = getattr(self, "Base_getCountFromWorklistTable", None)
use_cache = search_result_ is not None
if use_cache:
ignored_security_column_id_set = self._getWorklistIgnoredSecurityColumnSet()
ignored_security_uid_parameter_set = {x
for x, y in catalog_security_uid_groups_columns_dict.iteritems()
if y in ignored_security_column_id_set
}
_getSecurityUidDictAndRoleColumnDict = getSecurityUidDictAndRoleColumnDict
def getSecurityUidDictAndRoleColumnDict(**kw):
security_uid_dict, role_column_dict, local_role_column_dict = \
_getSecurityUidDictAndRoleColumnDict(**kw)
for ignored_security_column_id in ignored_security_column_id_set:
role_column_dict.pop(ignored_security_column_id, None)
local_role_column_dict.pop(ignored_security_column_id, None)
for ignored_security_uid_parameter in \
ignored_security_uid_parameter_set:
security_uid_dict.pop(ignored_security_uid_parameter)
return security_uid_dict, role_column_dict, local_role_column_dict
count_column_expression = 'sum(`%s`)' % (COUNT_COLUMN_TITLE, )
# Prevent catalog from trying to join
getQuery = SimpleQuery
# BBB
def search_result(select_dict, group_by, query, limit, src__):
select_item_list = []
for alias, expression in select_dict.iteritems():
if expression is None:
expression = alias
select_item_list.append('%s AS %s' % (expression, alias))
return search_result_(
select_expression=','.join(select_item_list),
group_by_expression=','.join(group_by),
query=query,
limit=limit,
src__=src__,
)
worklist_result_dict = {}
# Get a list of dict of WorklistVariableMatchDict grouped by compatible
# conditions
(worklist_list_grouped_by_condition, worklist_metadata) = \
groupWorklistListByCondition(
worklist_dict=worklist_dict,
sql_catalog=sql_catalog,
getSecurityUidDictAndRoleColumnDict=\
getSecurityUidDictAndRoleColumnDict,
catalog_security_uid_groups_columns_dict=\
catalog_security_uid_groups_columns_dict,
else:
search_result = portal_catalog.unrestrictedSearchResults
count_column_expression = 'count(*)'
# Let catalog join as needed
getQuery = lambda comparison_operator=None, **kw: AutoQuery(
operator=comparison_operator,
**kw
)
worklist_result_dict = {}
# Get a list of dict of WorklistVariableMatchDict grouped by compatible
# conditions
(worklist_list_grouped_by_condition, worklist_metadata) = \
groupWorklistListByCondition(
worklist_dict=worklist_dict,
sql_catalog=sql_catalog,
getSecurityUidDictAndRoleColumnDict=\
getSecurityUidDictAndRoleColumnDict,
catalog_security_uid_groups_columns_dict=\
catalog_security_uid_groups_columns_dict,
)
if src__:
action_list = []
for grouped_worklist_dict in worklist_list_grouped_by_condition:
# Generate the query for this worklist_list
(total_criterion_id_list, query) = \
getWorklistListQuery(
getQuery=getQuery,
grouped_worklist_dict=grouped_worklist_dict,
)
if src__:
action_list = []
for grouped_worklist_dict in worklist_list_grouped_by_condition:
# Generate the query for this worklist_list
(total_criterion_id_list, query) = \
getWorklistListQuery(
getQuery=getQuery,
grouped_worklist_dict=grouped_worklist_dict,
)
group_by = total_criterion_id_list
assert COUNT_COLUMN_TITLE not in total_criterion_id_list
select_dict = dict.fromkeys(total_criterion_id_list)
select_dict[COUNT_COLUMN_TITLE] = count_column_expression
catalog_brain_result = []
group_by = total_criterion_id_list
assert COUNT_COLUMN_TITLE not in total_criterion_id_list
select_dict = dict.fromkeys(total_criterion_id_list)
select_dict[COUNT_COLUMN_TITLE] = count_column_expression
catalog_brain_result = []
try:
catalog_brain_result = search_result(
select_dict=select_dict,
group_by=group_by,
query=query,
limit=None,
src__=src__)
except Unauthorized:
if not is_anonymous:
raise
LOG('WorkflowTool.listActions', WARNING,
'Exception while computing worklists: %s'
% grouped_worklist_dict.keys(),
error=sys.exc_info())
continue
except ProgrammingError, error_value:
# 1146 = table does not exist
if not use_cache or error_value[0] != 1146:
raise
try:
catalog_brain_result = search_result(
select_dict=select_dict,
group_by=group_by,
query=query,
limit=None,
src__=src__)
except Unauthorized:
if not is_anonymous:
raise
LOG('WorkflowTool.listActions', WARNING,
'Exception while computing worklists: %s'
% grouped_worklist_dict.keys(),
error=sys.exc_info())
continue
self.Base_zCreateWorklistTable()
except ProgrammingError, error_value:
# 1146 = table does not exist
if not use_cache or error_value[0] != 1146:
# 1050 = table exists (alarm run just a bit too late)
if error_value[0] != 1050:
raise
try:
self.Base_zCreateWorklistTable()
except ProgrammingError, error_value:
# 1050 = table exists (alarm run just a bit too late)
if error_value[0] != 1050:
raise
if src__:
action_list.append(catalog_brain_result)
else:
grouped_worklist_result = sumCatalogResultByWorklist(
grouped_worklist_dict=grouped_worklist_dict,
catalog_result=catalog_brain_result)
for key, value in grouped_worklist_result.iteritems():
worklist_result_dict[key] = value + worklist_result_dict.get(key, 0)
if not src__:
action_list = sorted(
generateActionList(
worklist_metadata=worklist_metadata,
worklist_result=worklist_result_dict,
portal_url=portal_url),
key=lambda x: '/'.join((x['workflow_id'], x['worklist_id'])),
)
return action_list
user = _getAuthenticatedUser(self).getIdOrUserName()
if src__:
actions = _getWorklistActionList()
else:
_getWorklistActionList = CachingMethod(_getWorklistActionList,
id=('_getWorklistActionList', user, portal_url),
cache_factory = 'erp5_ui_short')
actions.extend(_getWorklistActionList())
if src__:
action_list.append(catalog_brain_result)
else:
grouped_worklist_result = sumCatalogResultByWorklist(
grouped_worklist_dict=grouped_worklist_dict,
catalog_result=catalog_brain_result)
for key, value in grouped_worklist_result.iteritems():
worklist_result_dict[key] = value + worklist_result_dict.get(key, 0)
if not src__:
action_list = sorted(
generateActionList(
worklist_metadata=worklist_metadata,
worklist_result=worklist_result_dict,
portal_url=portal_url),
key=lambda x: '/'.join((x['workflow_id'], x['worklist_id'])),
)
return action_list
if src__:
actions = _getWorklistActionList()
else:
actions.extend(CachingMethod(
_getWorklistActionList,
id=(
'_getWorklistActionList',
_getAuthenticatedUser(self).getIdOrUserName(),
portal_url,
),
cache_factory = 'erp5_ui_short',
)())
return actions
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