From c7815a4b582843098d7e169948cb6bcde031bf7e Mon Sep 17 00:00:00 2001 From: Kazuhiko Shiozaki <kazuhiko@nexedi.com> Date: Thu, 27 Dec 2007 09:35:27 +0000 Subject: [PATCH] use reversely linked list for storing workflow history to reduce data size. git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@18529 20353a03-c40f-0410-a6d1-a30d3c3de9de --- product/ERP5Type/patches/WorkflowTool.py | 113 ++++++++++++++++++++++- 1 file changed, 108 insertions(+), 5 deletions(-) diff --git a/product/ERP5Type/patches/WorkflowTool.py b/product/ERP5Type/patches/WorkflowTool.py index a70fe9cb69..d43a93e9a2 100644 --- a/product/ERP5Type/patches/WorkflowTool.py +++ b/product/ERP5Type/patches/WorkflowTool.py @@ -29,6 +29,9 @@ from Products.ZSQLCatalog.SQLCatalog import Query, ComplexQuery, NegatedQuery from Products.CMFCore.utils import _getAuthenticatedUser from Products.ERP5Type.Cache import CachingMethod from sets import ImmutableSet +from Acquisition import aq_base +from Persistence import Persistent +from Globals import PersistentMapping def DCWorkflowDefinition_notifyWorkflowMethod(self, ob, transition_list, args=None, kw=None): ''' @@ -141,7 +144,7 @@ def groupWorklistListByCondition(worklist_dict, acceptable_key_dict, conditions. Strip any variable which is not a catalog column. Returns metadata in a separate dict. - + Example: Input: worklist_dict: @@ -366,7 +369,7 @@ def sumCatalogResultByWorklist(grouped_worklist_dict, catalog_result): Build a dictionnary summing up which value combination interests which worklist, then iterate catalog result lines and give results to corresponding worklists. - + It is better to avoid reading multiple times the catalog result from flexibility point of view: if it must ever be changed into a cursor, this code will keep working nicely without needing to rewind the cursor. @@ -396,8 +399,8 @@ def sumCatalogResultByWorklist(grouped_worklist_dict, catalog_result): criterion_dict[criterion_id] = [expected_class(x) for x in criterion_value_list] # Get all the possible combinations of values for all criterions for this - # worklist. Worklist filtering on portal_type='Foo' and - # validation_state in ['draft', 'validated'] is "interested" by both + # worklist. Worklist filtering on portal_type='Foo' and + # validation_state in ['draft', 'validated'] is "interested" by both # ('Foo', 'draft') and ('Foo', 'validated'). This generates both tuples # when given initial filter. criterion_value_key_list = ensemblistMultiply([criterion_dict[x] for x in \ @@ -541,7 +544,7 @@ def WorkflowTool_listActions(self, info=None, object=None): id=('_getWorklistActionList', user, portal_url), cache_factory = 'erp5_ui_short') actions.extend(_getWorklistActionList()) - return actions + return actions WorkflowTool.listActions = WorkflowTool_listActions @@ -604,3 +607,103 @@ def WorkflowTool_refreshWorklistCache(self): Base_zInsertIntoWorklistTable(**value_column_dict) WorkflowTool.refreshWorklistCache = WorkflowTool_refreshWorklistCache + +class WorkflowHistoryList(Persistent): + _bucket_size = 16 + + def __init__(self, iterable=None, prev=None): + self._prev = prev + self._slots = [] + if iterable is not None: + for x in iterable: + self.append(x) + + def __add__(self, iterable): + return self.__class__(tuple(self) + tuple(iterable)) + + def __contains__(self, item): + return item in tuple(self) + + def __eq__(self, other): + return tuple(self) == tuple(other) + + def __getitem__(self, index): + if index == -1: + return self._slots[-1] + elif isinstance(index, (int, long)): + # XXX this implementation is not so good, but rarely used. + return tuple(self)[index] + elif isinstance(index, slice): + return self.__class__((self[x] for x in + xrange(*index.indices(len(self))))) + else: + raise TypeError, 'tuple indices must be integers' + + def __getslice__(self, start, end): + return self.__getitem__(slice(start, end)) + + def __getstate__(self): + return (self._prev, self._slots) + + def __iter__(self): + bucket = self + stack = [] + while bucket is not None: + stack.append(bucket) + bucket = bucket._prev + for i in reversed(stack): + for j in i._slots: + yield j + + def __len__(self): + length = len(self._slots) + bucket = self._prev + while bucket is not None: + length += len(bucket._slots) + bucket = bucket._prev + return length + + def __mul__(self, x): + return self.__class__(tuple(self) * x) + + def __repr__(self): + #return '%s' % repr(tuple(self.__iter__())) + return '<%s object at 0x%x %r>' % (self.__class__.__name__, id(self), tuple(self)) + + def __rmul__(self, x): + return self.__class__(x * tuple(self)) + + def __setstate__(self, state): + self._prev, self._slots = state + + def append(self, value): + if len(self._slots) < self._bucket_size: + self._slots.append(value) + self._p_changed = 1 + else: + self._prev = self.__class__(self._slots, prev=self._prev) + self._slots = [value] + +def WorkflowTool_setStatusOf(self, wf_id, ob, status): + """ Append an entry to the workflow history. + + o Invoked by workflow definitions. + """ + wfh = None + has_history = 0 + if getattr(aq_base(ob), 'workflow_history', None) is not None: + history = ob.workflow_history + if history is not None: + has_history = 1 + wfh = history.get(wf_id, None) + if wfh is not None and not isinstance(wfh, WorkflowHistoryList): + wfh = WorkflowHistoryList(list(wfh)) + ob.workflow_history[wf_id] = wfh + if wfh is None: + wfh = WorkflowHistoryList() + if not has_history: + ob.workflow_history = PersistentMapping() + ob.workflow_history[wf_id] = wfh + wfh.append(status) + +WorkflowTool.setStatusOf = WorkflowTool_setStatusOf -- 2.30.9