[Checkins] SVN: z3c.table/trunk/ Fixed broken implementation of advanced batching
Christophe Combelles
ccomb at free.fr
Fri Oct 10 17:18:47 EDT 2008
Log message for revision 92028:
Fixed broken implementation of advanced batching
Changed:
U z3c.table/trunk/CHANGES.txt
U z3c.table/trunk/src/z3c/table/README.txt
U z3c.table/trunk/src/z3c/table/batch.py
U z3c.table/trunk/src/z3c/table/tests.py
-=-
Modified: z3c.table/trunk/CHANGES.txt
===================================================================
--- z3c.table/trunk/CHANGES.txt 2008-10-10 20:42:22 UTC (rev 92027)
+++ z3c.table/trunk/CHANGES.txt 2008-10-10 21:18:46 UTC (rev 92028)
@@ -5,6 +5,9 @@
Version 0.5.1dev (unreleased)
-----------------------------
+- Fixed advanced batching implementation, and extracted it in a separate
+ function
+
- Implemented GetAttrFormatterColumn. This column can be used for simple
value formatting columns.
Modified: z3c.table/trunk/src/z3c/table/README.txt
===================================================================
--- z3c.table/trunk/src/z3c/table/README.txt 2008-10-10 20:42:22 UTC (rev 92027)
+++ z3c.table/trunk/src/z3c/table/README.txt 2008-10-10 21:18:46 UTC (rev 92028)
@@ -1,12 +1,14 @@
=========
-Z3C Table
+z3c Table
=========
+.. sectnum::
+.. contents::
+
The goal of this package is to offer a modular table rendering library. We use
the content provider pattern and the column are implemented as adapters which
will give us a powerful base concept.
-
Some important concepts we use
------------------------------
@@ -23,7 +25,7 @@
This package does not provide any kind of template or skin support. Most the
time if you need to render a table, you will use your own skin concept. This means
you can render the table or batch within your own templates. This will ensure
-that we have as less dependencies as possible in this package and the package
+that we have as few dependencies as possible in this package and the package
can get reused with any skin concept.
Note
@@ -32,7 +34,7 @@
As you probably know, batching is only possible after sorting columns. This is
a nightmare if it comes to performance. The reason is, all data need to get
sorted before the batch can start at the given position. And sorting can most
-the time only be done by touching each object. This means you have to be careful
+of the time only be done by touching each object. This means you have to be careful
if you are using a large set of data, even if you use batching.
Sample data setup
@@ -86,7 +88,7 @@
Column Adapter
--------------
-Now we can register a column for our table:
+We can create a column for our table:
>>> import zope.component
>>> from z3c.table import interfaces
@@ -100,7 +102,7 @@
... def renderCell(self, item):
... return u'Title: %s' % item.title
-Now we can register our column adapter.
+Now we can register the column.
>>> zope.component.provideAdapter(TitleColumn,
... (None, None, interfaces.ITable), provides=interfaces.IColumn,
@@ -166,7 +168,7 @@
Colspan
-------
-Now let's show how we can define a colspan condition of 2 for an column:
+Now let's show how we can define a colspan condition of 2 for a column:
>>> class ColspanColumn(column.NameColumn):
...
@@ -244,7 +246,7 @@
-------------
The existing implementation allows us to define a table in a class without
-to use the modular adapter pattern for columns.
+using the modular adapter pattern for columns.
First we need to define a column which can render a value for our items:
@@ -1216,7 +1218,7 @@
<a href="...html?table-batchStart=11&table-batchSize=5" class="current">3</a>
<a href="...html?table-batchStart=15&table-batchSize=5" class="last">4</a>
-Now let's add more items that we can test the skipped links in large batches:
+Now let's add more items so that we can test the skipped links in large batches:
>>> for i in range(1000):
... idx = i+20
@@ -1232,7 +1234,7 @@
>>> requestBatchingTable = BatchingTable(container, batchingRequest)
>>> requestBatchingTable.startBatchingAt = 5
-We also need to give the table a location and a name like we normaly setup
+We also need to give the table a location and a name like we normally setup
in traversing:
>>> requestBatchingTable.__parent__ = container
@@ -1320,6 +1322,47 @@
xxx
<a href="...html?table-batchStart=1015&table-batchSize=5" class="last">204</a>
+
+Now test the extremities, need to define a new batchingRequest:
+Beginning by the left end point:
+
+ >>> leftBatchingRequest = TestRequest(form={'table-batchStart': '10',
+ ... 'table-batchSize': '5',
+ ... 'table-sortOn': 'table-number-1'})
+ >>> leftRequestBatchingTable = BatchingTable(container, leftBatchingRequest)
+ >>> leftRequestBatchingTable.__parent__ = container
+ >>> leftRequestBatchingTable.__name__ = u'leftRequestBatchingTable.html'
+ >>> leftRequestBatchingTable.update()
+ >>> print leftRequestBatchingTable.renderBatch()
+ <a href="http://...html?table-batchStart=0&table-batchSize=5" class="first">1</a>
+ <a href="http://...html?table-batchStart=5&table-batchSize=5">2</a>
+ <a href="http://...html?table-batchStart=10&table-batchSize=5" class="current">3</a>
+ <a href="http://...html?table-batchStart=15&table-batchSize=5">4</a>
+ <a href="http://...html?table-batchStart=20&table-batchSize=5">5</a>
+ <a href="http://...html?table-batchStart=25&table-batchSize=5">6</a>
+ xxx
+ <a href="http://...html?table-batchStart=1015&table-batchSize=5" class="last">204</a>
+
+Go on with the right extremity:
+
+ >>> rightBatchingRequest = TestRequest(form={'table-batchStart': '1005',
+ ... 'table-batchSize': '5',
+ ... 'table-sortOn': 'table-number-1'})
+ >>> rightRequestBatchingTable = BatchingTable(container, rightBatchingRequest)
+ >>> rightRequestBatchingTable.__parent__ = container
+ >>> rightRequestBatchingTable.__name__ = u'rightRequestBatchingTable.html'
+ >>> rightRequestBatchingTable.update()
+ >>> print rightRequestBatchingTable.renderBatch()
+ <a href="http://...html?table-batchStart=0&table-batchSize=5" class="first">1</a>
+ xxx
+ <a href="http://...html?table-batchStart=990&table-batchSize=5">199</a>
+ <a href="http://...html?table-batchStart=995&table-batchSize=5">200</a>
+ <a href="http://...html?table-batchStart=1000&table-batchSize=5">201</a>
+ <a href="http://...html?table-batchStart=1005&table-batchSize=5" class="current">202</a>
+ <a href="http://...html?table-batchStart=1010&table-batchSize=5">203</a>
+ <a href="http://...html?table-batchStart=1015&table-batchSize=5" class="last">204</a>
+
+
None previous and next batch size. Probably it doesn't make sense but let's
show what happens if we set the previous and next batch size to 0 (zero):
Modified: z3c.table/trunk/src/z3c/table/batch.py
===================================================================
--- z3c.table/trunk/src/z3c/table/batch.py 2008-10-10 20:42:22 UTC (rev 92027)
+++ z3c.table/trunk/src/z3c/table/batch.py 2008-10-10 21:18:46 UTC (rev 92028)
@@ -25,6 +25,119 @@
_ = zope.i18nmessageid.MessageFactory('z3c')
+def advanced_subset(batches, currentBatchIdx, prevBatchSize, nextBatchSize):
+ """build an advanced subset from a large batch list
+
+ This is used to display batch links for a table.
+
+ arguments:
+ batches: all the batches
+ currentBatchIdx: index of the current batch
+ prevBatchSize: number of displayed batches before the current batch
+ nextBatchSize: number of displayed batches after the current batch
+ return:
+ an advanced reduced list of batches, with None as spaces
+
+ Example:
+ We build a large batch, and define a convenient display function::
+
+ >>> from z3c.table.batch import advanced_subset as subset
+ >>> batches = range(100) # it works with real batches as well
+
+ We try to get subsets at different levels::
+
+ >>> for i in range(0,6):
+ ... subset(batches, i, 2, 2)
+ [0, 1, 2, None, 99]
+ [0, 1, 2, 3, None, 99]
+ [0, 1, 2, 3, 4, None, 99]
+ [0, 1, 2, 3, 4, 5, None, 99]
+ [0, None, 2, 3, 4, 5, 6, None, 99]
+ [0, None, 3, 4, 5, 6, 7, None, 99]
+
+ >>> for i in range(93, 99):
+ ... subset(batches, i, 2, 2)
+ [0, None, 91, 92, 93, 94, 95, None, 99]
+ [0, None, 92, 93, 94, 95, 96, None, 99]
+ [0, None, 93, 94, 95, 96, 97, None, 99]
+ [0, None, 94, 95, 96, 97, 98, 99]
+ [0, None, 95, 96, 97, 98, 99]
+ [0, None, 96, 97, 98, 99]
+
+ Try with no previous and no next batch::
+
+ >>> subset(batches, 0, 0, 0)
+ [0, None, 99]
+ >>> subset(batches, 1, 0, 0)
+ [0, 1, None, 99]
+ >>> subset(batches, 2, 0, 0)
+ [0, None, 2, None, 99]
+
+ Try with only 1 previous and 1 next batch:
+
+ >>> subset(batches, 0, 1, 1)
+ [0, 1, None, 99]
+ >>> subset(batches, 1, 1, 1)
+ [0, 1, 2, None, 99]
+ >>> subset(batches, 2, 1, 1)
+ [0, 1, 2, 3, None, 99]
+
+ Try with incoherent values values::
+ >>> subset(batches, 0, -4, -10)
+ Traceback (most recent call last):
+ ...
+ AssertionError
+ >>> subset(batches, 2000, 3, 3)
+ Traceback (most recent call last):
+ ...
+ AssertionError
+ """
+ batchItems = []
+ # setup some batches and indexes
+ firstIdx = 0
+ lastIdx = len(batches) - 1
+ assert(currentBatchIdx >= 0 and currentBatchIdx <= lastIdx)
+ assert(prevBatchSize >= 0 and nextBatchSize >= 0)
+ prevIdx = currentBatchIdx - prevBatchSize
+ nextIdx = currentBatchIdx + 1
+ firstBatch = batches[0]
+ lastBatch = batches[len(batches)-1]
+
+ # add first batch
+ if firstIdx < currentBatchIdx:
+ batchItems.append(firstBatch)
+
+ # there must probably be space
+ if firstIdx + 1 < prevIdx:
+ # we skip batches between first batch and first previous batch
+ batchItems.append(None)
+
+ # add previous batches
+ for i in range(prevIdx, prevIdx+prevBatchSize):
+ if firstIdx < i:
+ # append previous batches
+ batchItems.append(batches[i])
+
+ # add current batch
+ batchItems.append(batches[currentBatchIdx])
+
+ # add next batches
+ for i in range(nextIdx, nextIdx+nextBatchSize):
+ if i < lastIdx:
+ # append previous batch
+ batchItems.append(batches[i])
+
+ # there must probably be space
+ if nextIdx + nextBatchSize < lastIdx:
+ # we skip batches between last batch and last next batch
+ batchItems.append(None)
+
+ # add last batch
+ if currentBatchIdx < lastIdx:
+ batchItems.append(lastBatch)
+ return batchItems
+
+
class BatchProvider(object):
"""Batch provider.
@@ -33,14 +146,12 @@
get only used if the table rows is a batch.
This batch provider offers a batch presentation for a given table. The
- batch provides different configuration options which can be ovreriden in
+ batch provides different configuration options which can be overriden in
custom implementations:
- The batch acts like this. If we have more batches then then
- (previousBatchSize + nextBatchSize + 3) then the advanced batch is used.
-
- If the total amount of items is smaller then the previousBatchSize, current
- item and nextBatchSize. We will render all batch links.
+ The batch acts like this. If we have more batches than
+ (prevBatchSize + nextBatchSize + 3) then the advanced batch subset is used.
+ Otherwise, we will render all batch links.
Note, the additional factor 3 is the placeholder for the first, current and
last item.
@@ -52,17 +163,23 @@
previous batches and the link for the last batch.
Sample for 1000 items with 100 batches with batchSize of 10 and a
- previousBatchSize of 3 and a nextBatchSize of 3:
-
+ prevBatchSize of 3 and a nextBatchSize of 3:
+
+ For the first item:
+ [*1*][2][3][4] ... [100]
+
+ In the middle:
[1] ... [6][7][8][*9*][10][11][12] ... [100]
+ At the end:
+ [1] ... [97][98][99][*100*]
"""
zope.interface.implements(interfaces.IBatchProvider)
batchItems = []
- previousBatchSize = 3
+ prevBatchSize = 3
nextBatchSize = 3
batchSpacer = u'...'
@@ -83,51 +200,20 @@
cssClass = cssClass and css or u''
return '<a href="%s?%s"%s>%s</a>' % (tableURL, query, cssClass, idx)
+
def update(self):
- self.batchItems = []
- total = self.previousBatchSize + self.nextBatchSize + 3
- if total < self.batch.total:
-
- # setup some batches and indexes
- currentBatchIdx = self.batch.index
- prevIdx = currentBatchIdx - self.previousBatchSize
- nextIdx = currentBatchIdx +1
- firstBatch = self.batches[0]
- lastBatch = self.batches[len(self.batches)-1]
-
- # add first batch
- self.batchItems.append(firstBatch)
-
- # there must probably be space
- if firstBatch.index +1 != prevIdx:
- # we skip batches between first batch and first previous batch
- self.batchItems.append(None)
-
- # add previous batches
- for i in range(self.previousBatchSize):
- # append previous batch
- self.batchItems.append(self.batches[prevIdx])
- prevIdx += 1
-
- # add current batch
- self.batchItems.append(self.batch)
-
- # add next batches
- for i in range(self.nextBatchSize):
- # append previous batch
- self.batchItems.append(self.batches[nextIdx])
- nextIdx += 1
-
- # there must probably be space
- if lastBatch.index -1 != nextIdx:
- # we skip batches between last batch and last next batch
- self.batchItems.append(None)
-
- # add last batch
- self.batchItems.append(lastBatch)
-
+ # 3 is is the placeholder for the first, current and last item.
+ total = self.prevBatchSize + self.nextBatchSize + 3
+ if self.batch.total <= total:
+ # give all batches
+ self.batchItems = self.batch.batches
else:
- self.batchItems = self.batch.batches
+ # switch to an advanced batch subset
+ self.batchItems = advanced_subset(self.batches,
+ self.batch.index,
+ self.prevBatchSize,
+ self.nextBatchSize,
+ )
def render(self):
self.update()
Modified: z3c.table/trunk/src/z3c/table/tests.py
===================================================================
--- z3c.table/trunk/src/z3c/table/tests.py 2008-10-10 20:42:22 UTC (rev 92027)
+++ z3c.table/trunk/src/z3c/table/tests.py 2008-10-10 21:18:46 UTC (rev 92028)
@@ -156,6 +156,10 @@
setUp=testing.setUp, tearDown=testing.tearDown,
optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
),
+ doctest.DocTestSuite(batch,
+ setUp=testing.setUp, tearDown=testing.tearDown,
+ optionflags=doctest.NORMALIZE_WHITESPACE|doctest.ELLIPSIS,
+ ),
unittest.makeSuite(TestTable),
unittest.makeSuite(TestSequenceTable),
unittest.makeSuite(TestColumn),
More information about the Checkins
mailing list