2 ## The following code comes from Plone project http://plone.org
4 """ Useful utilities function from the Plone project like Batch.
6 $Id: PloneMisc.py 1502 2009-05-31 17:33:32Z pin $
7 $URL: http://svn.cri.ensmp.fr/svn/Plinn/branches/CMF-2.1/PloneMisc.py $
11 from __future__
import nested_scopes
13 from ZTUtils
.Batch
import Batch
as ZTUBatch
14 from ZTUtils
import make_query
15 from ExtensionClass
import Base
17 # These have to be duplicated from ZTUtils.Batch to use the correct Batch
18 # class, otherwise orphans will come out wrong in the 'show x next messages'.
19 class LazyPrevBatch(Base
):
20 def __of__(self
, parent
):
21 return Batch(parent
._sequence
, parent
._size
,
22 parent
.first
- parent
._size
+ parent
.overlap
, 0,
23 parent
.orphan
, parent
.overlap
)
25 class LazyNextBatch(Base
):
26 def __of__(self
, parent
):
27 try: parent
._sequence
[parent
.end
]
28 except IndexError: return None
29 return Batch(parent
._sequence
, parent
._size
,
30 parent
.end
- parent
.overlap
, 0,
31 parent
.orphan
, parent
.overlap
)
33 class LazySequenceLength(Base
):
34 def __of__(self
, parent
):
35 parent
.sequence_length
= l
= len(parent
._sequence
)
38 class Batch(ZTUBatch
):
39 """Create a sequence batch"""
40 __allow_access_to_unprotected_subobjects__
= 1
42 previous
= LazyPrevBatch()
43 next
= LazyNextBatch()
44 sequence_length
= LazySequenceLength()
46 size
= first
= start
= end
= orphan
= overlap
= navlist
= None
47 numpages
= pagenumber
= pagerange
= pagerangeend
= pagerangestart
= pagenumber
= quantumleap
= None
58 , b_start_str
='b_start'
59 , before_getitem
=lambda item
: item
61 """ Encapsulate sequence in batches of size
62 sequence - the data to batch.
63 size - the number of items in each batch. This will be computed if left out.
64 start - the first element of sequence to include in batch (0-index)
65 end - the last element of sequence to include in batch (0-index, optional)
66 orphan - the next page will be combined with the current page if it does not contain more than orphan elements
67 overlap - the number of overlapping elements in each batch
68 pagerange - the number of pages to display in the navigation
69 quantumleap - 0 or 1 to indicate if bigger increments should be used in the navigation list for big results.
70 b_start_str - the request variable used for start, default 'b_start'
71 before_getitem - function that compute the item before getting it
75 start
,end
,sz
= opt(start
,end
,size
,orphan
,sequence
)
77 self
._sequence
= sequence
83 self
.overlap
= overlap
84 self
.first
= max(start
- 1, 0)
85 self
.length
= self
.end
- self
.first
87 self
.b_start_str
= b_start_str
89 self
.last
= self
.sequence_length
- size
91 # Set up next and previous
95 # Set up the total number of pages
96 self
.numpages
= calculate_pagenumber(self
.sequence_length
- self
.orphan
, self
.size
, self
.overlap
)
98 # Set up the current page number
99 self
.pagenumber
= calculate_pagenumber(self
.start
, self
.size
, self
.overlap
)
101 # Set up pagerange for the navigation quick links
102 self
.pagerange
, self
.pagerangestart
, self
.pagerangeend
= calculate_pagerange(self
.pagenumber
,self
.numpages
,pagerange
)
104 # Set up the lists for the navigation: 4 5 [6] 7 8
105 # navlist is the complete list, including pagenumber
106 # prevlist is the 4 5 in the example above
107 # nextlist is 7 8 in the example above
108 self
.navlist
= self
.prevlist
= self
.nextlist
= []
109 if self
.pagerange
and self
.numpages
>= 1:
110 self
.navlist
= range(self
.pagerangestart
, self
.pagerangeend
)
111 self
.prevlist
= range(self
.pagerangestart
, self
.pagenumber
)
112 self
.nextlist
= range(self
.pagenumber
+ 1, self
.pagerangeend
)
114 # QuantumLeap - faster navigation for big result sets
115 self
.quantumleap
= quantumleap
116 self
.leapback
= self
.leapforward
= []
118 self
.leapback
= calculate_leapback(self
.pagenumber
, self
.numpages
, self
.pagerange
)
119 self
.leapforward
= calculate_leapforward(self
.pagenumber
, self
.numpages
, self
.pagerange
)
121 # comptute item before getting it
122 self
.before_getitem
= before_getitem
124 def __getitem__(self
, index
):
126 if index
+ self
.end
< self
.first
: raise IndexError, index
127 item
= self
._sequence
[index
+ self
.end
]
128 return self
.before_getitem(item
)
130 if index
>= self
.length
: raise IndexError, index
131 item
= self
._sequence
[index
+self
.first
]
132 return self
.before_getitem(item
)
134 def pageurl(self
, formvariables
, pagenumber
=-1):
135 """ Makes the url for a given page """
137 pagenumber
= self
.pagenumber
138 b_start
= pagenumber
* (self
.size
- self
.overlap
) - self
.size
139 return make_query(formvariables
, {self
.b_start_str
:b_start
})
141 def navurls(self
, formvariables
, navlist
=[]):
142 """ Returns the page number and url for the navigation quick links """
143 if not navlist
: navlist
= self
.navlist
144 return map(lambda x
, formvariables
= formvariables
: (x
, self
.pageurl(formvariables
, x
)), navlist
)
146 def prevurls(self
, formvariables
):
147 """ Helper method to get prev navigation list from templates """
148 return self
.navurls(formvariables
, self
.prevlist
)
150 def nexturls(self
, formvariables
):
151 """ Helper method to get next navigation list from templates """
152 return self
.navurls(formvariables
, self
.nextlist
)
154 # Calculate start, end, batchsize
155 # This is copied from ZTUtils.Batch.py because orphans were not correct there.
156 # 04/16/04 modified by Danny Bloemendaal (_ender_). Removed try/except structs because
157 # in some situations they cause some unexpected problems. Also fixed some problems with the orphan stuff. Seems to work now.
158 def opt(start
,end
,size
,orphan
,sequence
):
159 length
= len(sequence
)
161 if start
> 0 and end
> 0 and end
>= start
:
162 size
= end
+ 1 - start
168 if end
< start
: end
= start
170 end
= start
+ size
- 1
171 if (end
+orphan
)>=length
:
176 start
= end
+ 1 - size
177 if start
- 1 < orphan
: start
= 1
180 end
= start
+ size
- 1
181 if (end
+orphan
)>=length
:
183 return start
,end
,size
185 def calculate_pagenumber(elementnumber
, batchsize
, overlap
=0):
186 """ Calculate the pagenumber for the navigation """
187 # To find first element in a page,
188 # elementnumber = pagenumber * (size - overlap) - size (- orphan?)
190 pagenumber
,remainder
= divmod(elementnumber
, batchsize
- overlap
)
191 except ZeroDivisionError:
192 pagenumber
, remainder
= divmod(elementnumber
, 1)
193 if remainder
> overlap
:
194 pagenumber
= pagenumber
+ 1
195 pagenumber
= max(pagenumber
, 1)
198 def calculate_pagerange(pagenumber
, numpages
, pagerange
):
199 """ Calculate the pagerange for the navigation quicklinks """
200 # Pagerange is the number of pages linked to in the navigation, odd number
201 pagerange
= max(0 , pagerange
+ pagerange
% 2 - 1)
202 # Making sure the list will not start with negative values
203 pagerangestart
= max ( 1, pagenumber
- (pagerange
- 1 ) / 2 )
204 # Making sure the list does not expand beyond the last page
205 pagerangeend
= min ( pagenumber
+ (pagerange
- 1 ) / 2, numpages
) + 1
206 return pagerange
, pagerangestart
, pagerangeend
208 def calculate_quantum_leap_gap(numpages
, pagerange
):
209 """ Find the QuantumLeap gap. Current width of list is 6 clicks (30/5) """
210 return int(max(1,round(float(numpages
- pagerange
)/30))*5)
212 def calculate_leapback(pagenumber
, numpages
, pagerange
):
213 """ Check the distance between start and 0 and add links as necessary """
215 quantum_leap_gap
= calculate_quantum_leap_gap(numpages
, pagerange
)
216 num_back_leaps
= max(0,min(3, int(round(float(pagenumber
- pagerange
)/quantum_leap_gap
) - 0.3)))
218 pagerange
, pagerangestart
, pagerangeend
= calculate_pagerange( pagenumber
, numpages
, pagerange
)
219 leapback
= range(pagerangestart
- num_back_leaps
* quantum_leap_gap
, pagerangestart
, quantum_leap_gap
)
222 def calculate_leapforward(pagenumber
, numpages
, pagerange
):
223 """ Check the distance between end and length and add links as necessary """
225 quantum_leap_gap
= calculate_quantum_leap_gap(numpages
, pagerange
)
226 num_forward_leaps
= max(0,min(3, int(round(float(numpages
- pagenumber
- pagerange
)/quantum_leap_gap
) - 0.3)))
227 if num_forward_leaps
:
228 pagerange
, pagerangestart
, pagerangeend
= calculate_pagerange( pagenumber
, numpages
, pagerange
)
229 leapforward
= range(pagerangeend
-1 + quantum_leap_gap
, pagerangeend
-1 + (num_forward_leaps
+1) * quantum_leap_gap
, quantum_leap_gap
)
234 __allow_access_to_unprotected_subobjects__
= 1
236 def __init__(self
, upper
=100000, pos
=0):
241 if self
.pos
<= self
.upper
:
244 raise KeyError, 'Reached upper bounds'