From 1096069ad1e94d2c0fd6c54be591c096dae0e014 Mon Sep 17 00:00:00 2001 From: Doug Blank Date: Tue, 18 Oct 2011 19:07:18 +0000 Subject: [PATCH] Not needed svn: r18346 --- src/webapp/sortheaders.py | 229 -------------------------------------- 1 file changed, 229 deletions(-) delete mode 100644 src/webapp/sortheaders.py diff --git a/src/webapp/sortheaders.py b/src/webapp/sortheaders.py deleted file mode 100644 index 011c24d37..000000000 --- a/src/webapp/sortheaders.py +++ /dev/null @@ -1,229 +0,0 @@ -# Author: insin -# Site: http://www.djangosnippets.org/snippets/308/ - -from django.core.paginator import InvalidPage, EmptyPage -from collections import defaultdict - -from unicodedata import normalize -import locale - -ORDER_VAR = 'o' -ORDER_TYPE_VAR = 'ot' - -def first_letter(text): - """ - Text should be a unicode string. Returns first letter. - """ - if len(text) > 0: - letter = normalize('NFKC', text)[0].upper() - (lang_country, modifier ) = locale.getlocale() - if lang_country == "sv_SE" and letter in [u'W', u'V']: - letter = u'V,W' - return letter - else: - return u'?' - -class SortHeaders: - """ - Handles generation of an argument for the Django ORM's - ``order_by`` method and generation of table headers which reflect - the currently selected sort, based on defined table headers with - matching sort criteria. - - Based in part on the Django Admin application's ``ChangeList`` - functionality. - """ - def __init__(self, request, headers, default_order_field=None, - default_order_type='asc', additional_params=None): - """ - request - The request currently being processed - the current sort - order field and type are determined based on GET - parameters. - - headers - A list of two-tuples of header text and matching ordering - criteria for use with the Django ORM's ``order_by`` - method. A criterion of ``None`` indicates that a header - is not sortable. - - default_order_field - The index of the header definition to be used for default - ordering and when an invalid or non-sortable header is - specified in GET parameters. If not specified, the index - of the first sortable header will be used. - - default_order_type - The default type of ordering used - must be one of - ``'asc`` or ``'desc'``. - - additional_params: - Query parameters which should always appear in sort links, - specified as a dictionary mapping parameter names to - values. For example, this might contain the current page - number if you're sorting a paginated list of items. - """ - if default_order_field is None: - for i, (header, query_lookup) in enumerate(headers): - if query_lookup is not None: - default_order_field = i - break - if default_order_field is None: - raise AttributeError('No default_order_field was specified and none of the header definitions given were sortable.') - if default_order_type not in ('asc', 'desc'): - raise AttributeError('If given, default_order_type must be one of \'asc\' or \'desc\'.') - if additional_params is None: additional_params = {} - - self.header_defs = headers - self.additional_params = additional_params - self.order_field, self.order_type = default_order_field, default_order_type - - # Determine order field and order type for the current request - params = dict(request.GET.items()) - if ORDER_VAR in params: - try: - new_order_field = int(params[ORDER_VAR]) - if headers[new_order_field][1] is not None: - self.order_field = new_order_field - except (IndexError, ValueError): - pass # Use the default - if ORDER_TYPE_VAR in params and params[ORDER_TYPE_VAR] in ('asc', 'desc'): - self.order_type = params[ORDER_TYPE_VAR] - - def headers(self): - """ - Generates dicts containing header and sort link details for - all defined headers. - """ - for i, (header, order_criterion) in enumerate(self.header_defs): - th_classes = [] - new_order_type = 'asc' - if i == self.order_field: - th_classes.append('sorted %sending' % self.order_type) - new_order_type = {'asc': 'desc', 'desc': 'asc'}[self.order_type] - yield { - 'text': header, - 'sortable': order_criterion is not None, - 'url': self.get_query_string({ORDER_VAR: i, ORDER_TYPE_VAR: new_order_type}), - 'class_attr': (th_classes and ' class="%s"' % ' '.join(th_classes) or ''), - } - - def get_query_string(self, params): - """ - Creates a query string from the given dictionary of - parameters, including any additonal parameters which should - always be present. - """ - params.update(self.additional_params) - return '?%s' % '&'.join(['%s=%s' % (param, value) - for param, value in params.items()]) - - def get_order_by(self): - """ - Creates an ordering criterion based on the current order - field and order type, for use with the Django ORM's - ``order_by`` method. - """ - return '%s%s' % ( - self.order_type == 'desc' and '-' or '', - self.header_defs[self.order_field][1], - ) - -class NamePaginator(object): - """Pagination for string-based objects""" - - def __init__(self, object_list, on=None, per_page=25): - self.object_list = object_list - self.count = len(object_list) - self.pages = [] - - # chunk up the objects so we don't need to iterate over the whole list for each letter - chunks = defaultdict(list) - - for obj in self.object_list: - if on: - obj_str = unicode(getattr(obj, on)) - else: - obj_str = unicode(obj) - - letter = first_letter(obj_str[0]) - chunks[letter].append(obj) - - # the process for assigning objects to each page - current_page = NamePage(self) - - for letter in string.ascii_uppercase: - if letter not in chunks: - current_page.add([], letter) - continue - - sub_list = chunks[letter] # the items in object_list starting with this letter - - new_page_count = len(sub_list) + current_page.count - # first, check to see if sub_list will fit or it needs to go onto a new page. - # if assigning this list will cause the page to overflow... - # and an underflow is closer to per_page than an overflow... - # and the page isn't empty (which means len(sub_list) > per_page)... - if (new_page_count > per_page and - abs(per_page - current_page.count) < abs(per_page - new_page_count) and - current_page.count > 0): - # make a new page - self.pages.append(current_page) - current_page = NamePage(self) - - current_page.add(sub_list, letter) - - # if we finished the for loop with a page that isn't empty, add it - if current_page.count > 0: self.pages.append(current_page) - - def page(self, num): - """Returns a Page object for the given 1-based page number.""" - if len(self.pages) == 0: - return None - elif num > 0 and num <= len(self.pages): - return self.pages[num-1] - else: - raise InvalidPage - - @property - def num_pages(self): - """Returns the total number of pages""" - return len(self.pages) - -class NamePage(object): - def __init__(self, paginator): - self.paginator = paginator - self.object_list = [] - self.letters = [] - - @property - def count(self): - return len(self.object_list) - - @property - def start_letter(self): - if len(self.letters) > 0: - self.letters.sort(key=locale.strxfrm) - return first_letter(self.letters) - else: return None - - @property - def end_letter(self): - if len(self.letters) > 0: - self.letters.sort(key=locale.strxfrm) - return self.letters[-1] - else: return None - - @property - def number(self): - return self.paginator.pages.index(self) + 1 - - def add(self, new_list, letter=None): - if len(new_list) > 0: self.object_list = self.object_list + new_list - if letter: self.letters.append(letter) - - def __unicode__(self): - if self.start_letter == self.end_letter: - return self.start_letter - else: - return u'%c-%c' % (self.start_letter, self.end_letter)