Style fixes and wrong function call fix in soc.views.helper.lists module.
[Melange.git] / app / soc / views / helper / lists.py
blobf9edb30c8f4f99fd5795214c991c4045072f31d8
1 #!/usr/bin/python2.5
3 # Copyright 2008 the Melange authors.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Helpers used to render lists.
18 """
20 __authors__ = [
21 '"Chen Lunpeng" <forever.clp@gmail.com>',
22 '"Pawel Solyga" <pawel.solyga@gmail.com>',
26 from soc.logic import dicts
27 from soc.logic.models.user import logic as user_logic
29 import soc.views.helper.forms
32 DEF_DEFAULT_PAGINATION = 50
33 DEF_MAX_PAGINATION = 100
34 DEF_MAX_DEV_PAGINATION = 1000
36 DEF_PAGINATION_CHOICES = [
37 ('10', '10 items per page'),
38 ('25', '25 items per page'),
39 ('50', '50 items per page'),
40 ('100', '100 items per page'),
43 DEF_DEVELOPER_CHOICES = [
44 ('500', '500 items per page'),
45 ('1000', '1000 items per page'),
49 def getPreferredListPagination(user=None):
50 """Returns User's preferred list pagination limit.
52 Args:
53 user: User entity containing the list pagination preference;
54 default is None, to use the current logged-in User
55 """
56 # TODO: eventually this limit should be a User profile preference
57 # (stored in the site-wide User Model) preference
58 return DEF_DEFAULT_PAGINATION
61 OFFSET_KEY = 'offset_%d'
62 LIMIT_KEY = 'limit_%d'
65 def makeOffsetKey(limit_idx):
66 return OFFSET_KEY % limit_idx
69 def makeLimitKey(limit_idx):
70 return LIMIT_KEY % limit_idx
73 def getListParameters(request, list_index):
74 """Retrieves, converts and validates values for one list
76 Args:
77 list_index, int: which list to get the values for.
78 (there may be multiple lists on one page, which are multiplexed
79 by an integer.)
81 Returns:
82 a dictionary of str -> str. field name -> field value.
83 """
85 offset = request.GET.get(makeOffsetKey(list_index))
86 limit = request.GET.get(makeLimitKey(list_index))
88 if offset is None:
89 offset = ''
91 if limit is None:
92 limit = ''
94 try:
95 offset = int(offset)
96 except ValueError:
97 offset = 0
99 try:
100 limit = int(limit)
101 except ValueError:
102 limit = getPreferredListPagination()
104 offset = max(0, offset)
105 limit = max(1, limit)
107 if user_logic.isDeveloper():
108 limit = min(DEF_MAX_DEV_PAGINATION, limit)
109 else:
110 limit = min(DEF_MAX_PAGINATION, limit)
112 return dict(limit=limit, offset=offset)
115 def generateLinkFromGetArgs(request, offset_and_limits):
116 """Constructs the get args for the url.
119 args = ["%s=%s" % (k, v) for k, v in offset_and_limits.iteritems()]
120 link_suffix = '?' + '&'.join(args)
122 return request.path + link_suffix
125 def generateLinkForRequest(request, base_params, updated_params):
126 """Create a link to the same page as request but with different params
128 Params:
129 request: the request for the page
130 base_params: the base parameters
131 updated_params: the parameters to update
133 params = base_params.copy()
134 params.update(updated_params)
135 return generateLinkFromGetArgs(request, params)
138 def getListContent(request, params, filter=None, order=None,
139 idx=0, need_content=False):
140 """Returns a dict with fields used for rendering lists.
142 TODO(dbentley): we need better terminology. List, in this context, can have
143 one of two meanings.
144 Meaning 1: the underlying list, which may be very large.
145 Meaning 2: the returned list, which is at most 'limit' items.
147 Args:
148 request: the Django HTTP request object
149 params: a dict with params for the View this list belongs to
150 filter: a filter for this list
151 order: the order which should be used for the list (in getForFields format)
152 idx: the index of this list
153 need_content: iff True will return None if there is no data
155 Returns:
156 A dictionary with the following values set:
159 'data': list data to be displayed
160 'main': url to list main template
161 'pagination': url to list pagination template
162 'row': url to list row template
163 'heading': url to list heading template
164 'limit': max amount of items per page,
165 'newest': url to first page of the list
166 'prev': url to previous page
167 'next': url to next page
168 'first': offset of the first item in the list
169 'last': offset of the last item in the list
172 # TODO(dbentley): this appears to be unnecessary indirection,
173 # as we only use this logic for getForFields, which is never overridden
174 logic = params['logic']
176 limit_key, offset_key = makeLimitKey(idx), makeOffsetKey(idx)
178 list_params = getListParameters(request, idx)
179 limit, offset = list_params['limit'], list_params['offset']
180 pagination_form = makePaginationForm(request, list_params['limit'],
181 limit_key)
183 # Fetch one more to see if there should be a 'next' link
184 data = logic.getForFields(filter=filter, limit=limit+1, offset=offset,
185 order=order)
187 if need_content and not data:
188 return None
190 more = len(data) > limit
192 if more:
193 del data[limit:]
195 newest = next = prev = export_link = ''
197 base_params = dict(i for i in request.GET.iteritems() if
198 i[0].startswith('offset_') or i[0].startswith('limit_'))
200 if params.get('list_key_order'):
201 export_link = generateLinkForRequest(request, base_params, {'export': idx})
203 if more:
204 # TODO(dbentley): here we need to implement a new field "last_key"
205 next = generateLinkForRequest(request, base_params,
206 {offset_key: offset + limit,
207 limit_key: limit})
209 if offset > 0:
210 # TODO(dbentley): here we need to implement previous in the good way.
211 prev = generateLinkForRequest(request, base_params,
212 {offset_key: max(0, offset-limit),
213 limit_key: limit})
215 if offset > limit:
216 # Having a link to the first doesn't make sense on the first page (we're on
217 # it). It also doesn't make sense on the second page (because the first
218 # page is the previous page).
220 # NOTE(dbentley): I personally disagree that it's simpler to do that way,
221 # because sometimes you want to go to the first page without having to
222 # consider what page you're on now.
223 newest = generateLinkForRequest(request, base_params, {offset_key: 0,
224 limit_key: limit})
226 content = {
227 'idx': idx,
228 'data': data,
229 'export': export_link,
230 'first': offset+1,
231 'last': len(data) > 1 and offset+len(data) or None,
232 'logic': logic,
233 'limit': limit,
234 'newest': newest,
235 'next': next,
236 'pagination_form': pagination_form,
237 'prev': prev,
240 updates = dicts.rename(params, params['list_params'])
241 content.update(updates)
243 return content
246 def makePaginationForm(
247 request, limit, arg_name, choices=DEF_PAGINATION_CHOICES,
248 field_name_fmt=soc.views.helper.forms.DEF_SELECT_QUERY_ARG_FIELD_NAME_FMT):
249 """Returns a customized pagination limit selection form.
251 Args:
252 request: the standard Django HTTP request object
253 limit: the initial value of the selection control
254 arg_name: see soc.views.helper.forms.makeSelectQueryArgForm(); default is 'limit'
255 choices: see soc.views.helper.forms.makeSelectQueryArgForm(); default is
256 DEF_PAGINATION_CHOICES
257 field_name_fmt: see soc.views.helper.forms.makeSelectQueryArgForm()
259 choices = makeNewPaginationChoices(limit=limit, choices=choices)
261 return soc.views.helper.forms.makeSelectQueryArgForm(
262 request, arg_name, limit, choices)
265 def makeNewPaginationChoices(limit=DEF_DEFAULT_PAGINATION,
266 choices=DEF_PAGINATION_CHOICES):
267 """Updates the pagination limit selection form.
269 Args:
270 limit: the initial value of the selection control;
271 default is DEF_DEFAULT_PAGINATION
272 choices: see soc.views.helper.forms.makeSelectQueryArgForm();
273 default is DEF_PAGINATION_CHOICES
275 Returns:
276 a new pagination choices list if limit is not in
277 DEF_PAGINATION_CHOICES, or DEF_PAGINATION_CHOICES otherwise
280 new_choices = []
281 new_choice = (str(limit), '%s items per page' % limit)
283 new_choices.append(new_choice)
284 new_choices.extend(choices)
286 if user_logic.isDeveloper():
287 new_choices.extend(DEF_DEVELOPER_CHOICES)
289 new_choices = set(new_choices)
291 return sorted(new_choices, key=lambda (x, y): int(x))