Back to index

plone3  3.1.7
PloneBatch.py
Go to the documentation of this file.
00001 from __future__ import nested_scopes
00002 
00003 from ZTUtils.Batch import Batch as ZTUBatch
00004 from ZTUtils import make_query
00005 from ExtensionClass import Base
00006 
00007 # These have to be duplicated from ZTUtils.Batch to use the correct Batch
00008 # class, otherwise orphans will come out wrong in the 'show x next messages'.
00009 class LazyPrevBatch(Base):
00010     def __of__(self, parent):
00011         return Batch(parent._sequence, parent._size,
00012                      parent.first - parent._size + parent.overlap, 0,
00013                      parent.orphan, parent.overlap)
00014 
00015 class LazyNextBatch(Base):
00016     def __of__(self, parent):
00017         try: parent._sequence[parent.end]
00018         except IndexError: return None
00019         return Batch(parent._sequence, parent._size,
00020                      parent.end - parent.overlap, 0,
00021                      parent.orphan, parent.overlap)
00022 
00023 class LazySequenceLength(Base):
00024     def __of__(self, parent):
00025         parent.sequence_length = l = len(parent._sequence)
00026         return l
00027 
00028 class Batch(ZTUBatch):
00029     """Create a sequence batch"""
00030     __allow_access_to_unprotected_subobjects__ = 1
00031 
00032     previous = LazyPrevBatch()
00033     next = LazyNextBatch()
00034     sequence_length = LazySequenceLength()
00035 
00036     size = first= start = end = orphan = overlap = navlist = None
00037     numpages = pagenumber = pagerange = pagerangeend = pagerangestart = pagenumber = quantumleap = None
00038 
00039     def __init__( self, sequence, size, start=0, end=0, orphan=0, overlap=0, pagerange=7, quantumleap=0, b_start_str='b_start'):
00040         """ Encapsulate sequence in batches of size
00041         sequence    - the data to batch.
00042         size        - the number of items in each batch. This will be computed if left out.
00043         start       - the first element of sequence to include in batch (0-index)
00044         end         - the last element of sequence to include in batch (0-index, optional)
00045         orphan      - the next page will be combined with the current page if it does not contain more than orphan elements
00046         overlap     - the number of overlapping elements in each batch
00047         pagerange   - the number of pages to display in the navigation
00048         quantumleap - 0 or 1 to indicate if bigger increments should be used in the navigation list for big results.
00049         b_start_str - the request variable used for start, default 'b_start'
00050         """
00051         start = start + 1
00052 
00053         start,end,sz = opt(start,end,size,orphan,sequence)
00054 
00055         self._sequence = sequence
00056         self.size = sz
00057         self._size = size
00058         self.start = start
00059         self.end = end
00060         self.orphan = orphan
00061         self.overlap = overlap
00062         self.first = max(start - 1, 0)
00063         self.length = self.end - self.first
00064 
00065         self.b_start_str = b_start_str
00066 
00067         self.last = self.sequence_length - size
00068 
00069         # Set up next and previous
00070         if self.first == 0:
00071             self.previous = None
00072 
00073         # Set up the total number of pages
00074         self.numpages = calculate_pagenumber(self.sequence_length - self.orphan, self.size, self.overlap)
00075 
00076         # Set up the current page number
00077         self.pagenumber = calculate_pagenumber(self.start, self.size, self.overlap)
00078 
00079         # Set up pagerange for the navigation quick links
00080         self.pagerange, self.pagerangestart, self.pagerangeend = calculate_pagerange(self.pagenumber,self.numpages,pagerange)
00081 
00082         # Set up the lists for the navigation: 4 5 [6] 7 8
00083         #  navlist is the complete list, including pagenumber
00084         #  prevlist is the 4 5 in the example above
00085         #  nextlist is 7 8 in the example above
00086         self.navlist = self.prevlist = self.nextlist = []
00087         if self.pagerange and self.numpages >= 1:
00088             self.navlist  = range(self.pagerangestart, self.pagerangeend)
00089             self.prevlist = range(self.pagerangestart, self.pagenumber)
00090             self.nextlist = range(self.pagenumber + 1, self.pagerangeend)
00091 
00092         # QuantumLeap - faster navigation for big result sets
00093         self.quantumleap = quantumleap
00094         self.leapback = self.leapforward = []
00095         if self.quantumleap:
00096             self.leapback = calculate_leapback(self.pagenumber, self.numpages, self.pagerange)
00097             self.leapforward = calculate_leapforward(self.pagenumber, self.numpages, self.pagerange)
00098 
00099     def pageurl(self, formvariables, pagenumber=-1):
00100         """ Makes the url for a given page """
00101         if pagenumber == -1:
00102             pagenumber = self.pagenumber
00103         b_start = pagenumber * (self.size - self.overlap) - self.size
00104         return make_query(formvariables, {self.b_start_str:b_start})
00105 
00106     def navurls(self, formvariables, navlist=[]):
00107         """ Returns the page number and url for the navigation quick links """
00108         if not navlist: navlist = self.navlist
00109         return map(lambda x, formvariables = formvariables: (x, self.pageurl(formvariables, x)), navlist)
00110 
00111     def prevurls(self, formvariables):
00112         """ Helper method to get prev navigation list from templates """
00113         return self.navurls(formvariables, self.prevlist)
00114 
00115     def nexturls(self, formvariables):
00116         """ Helper method to get next navigation list from templates """
00117         return self.navurls(formvariables, self.nextlist)
00118 
00119 # Calculate start, end, batchsize
00120 # This is copied from ZTUtils.Batch.py because orphans were not correct there.
00121 # 04/16/04 modified by Danny Bloemendaal (_ender_). Removed try/except structs because
00122 # in some situations they cause some unexpected problems. Also fixed some problems with the orphan stuff. Seems to work now.
00123 def opt(start,end,size,orphan,sequence):
00124     length = len(sequence)
00125     if size < 1:
00126         if start > 0 and end > 0 and end >= start:
00127             size = end + 1 - start
00128         else: size = 25
00129     if start > 0: 
00130         if start>length: 
00131             start = length
00132         if end > 0:
00133             if end < start: end = start
00134         else:
00135             end = start + size - 1
00136             if (end+orphan)>=length:
00137                 end = length
00138     elif end > 0:
00139         if (end)>length:
00140             end = length
00141         start = end + 1 - size
00142         if start - 1 < orphan: start = 1
00143     else:
00144         start = 1
00145         end = start + size - 1
00146         if (end+orphan)>=length:
00147             end = length
00148     return start,end,size
00149 
00150 def calculate_pagenumber(elementnumber, batchsize, overlap=0):
00151     """ Calculate the pagenumber for the navigation """
00152     # To find first element in a page,
00153     # elementnumber = pagenumber * (size - overlap) - size (- orphan?)
00154     try:
00155         pagenumber,remainder = divmod(elementnumber, batchsize - overlap)
00156     except ZeroDivisionError:
00157         pagenumber, remainder = divmod(elementnumber, 1)
00158     if remainder > overlap:
00159         pagenumber = pagenumber + 1
00160     pagenumber = max(pagenumber, 1)
00161     return pagenumber
00162 
00163 def calculate_pagerange(pagenumber, numpages, pagerange):
00164     """ Calculate the pagerange for the navigation quicklinks """
00165     # Pagerange is the number of pages linked to in the navigation, odd number
00166     pagerange = max(0 , pagerange + pagerange % 2 - 1)
00167     # Making sure the list will not start with negative values
00168     pagerangestart = max ( 1, pagenumber - (pagerange - 1 ) / 2 )
00169     # Making sure the list does not expand beyond the last page
00170     pagerangeend = min ( pagenumber + (pagerange - 1 ) / 2, numpages) + 1
00171     return pagerange, pagerangestart, pagerangeend
00172 
00173 def calculate_quantum_leap_gap(numpages, pagerange):
00174     """ Find the QuantumLeap gap. Current width of list is 6 clicks (30/5) """
00175     return int(max(1,round(float(numpages - pagerange)/30))*5)
00176 
00177 def calculate_leapback(pagenumber, numpages, pagerange):
00178     """ Check the distance between start and 0 and add links as necessary """
00179     leapback = []
00180     quantum_leap_gap = calculate_quantum_leap_gap(numpages, pagerange)
00181     num_back_leaps = max(0,min(3, int(round(float(pagenumber - pagerange)/quantum_leap_gap) - 0.3)))
00182     if num_back_leaps:
00183         pagerange, pagerangestart, pagerangeend = calculate_pagerange( pagenumber, numpages, pagerange)
00184         leapback = range(pagerangestart - num_back_leaps * quantum_leap_gap, pagerangestart, quantum_leap_gap)
00185     return leapback
00186 
00187 def calculate_leapforward(pagenumber, numpages, pagerange):
00188     """ Check the distance between end and length and add links as necessary """
00189     leapforward = []
00190     quantum_leap_gap = calculate_quantum_leap_gap(numpages, pagerange)
00191     num_forward_leaps = max(0,min(3, int(round(float(numpages - pagenumber - pagerange)/quantum_leap_gap) - 0.3)))
00192     if num_forward_leaps:
00193         pagerange, pagerangestart, pagerangeend = calculate_pagerange( pagenumber, numpages, pagerange)
00194         leapforward = range(pagerangeend-1 + quantum_leap_gap, pagerangeend-1 + (num_forward_leaps+1) * quantum_leap_gap, quantum_leap_gap)
00195     return leapforward