[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