improve paginator on list.
authorymh <ymh.work@gmail.com>
Tue, 03 Dec 2013 20:55:04 +0100
changeset 161 216b3f9582aa
parent 160 470ea7806537
child 162 5fe64e9de88b
improve paginator on list. Should closes bug #14.
src/p4l/config.py.tmpl
src/p4l/search/views.py
src/p4l/settings.py
src/p4l/templates/p4l/home.html
src/p4l/utils.py
src/p4l/views.py
--- a/src/p4l/config.py.tmpl	Tue Dec 03 13:54:30 2013 +0100
+++ b/src/p4l/config.py.tmpl	Tue Dec 03 20:55:04 2013 +0100
@@ -161,6 +161,9 @@
 
 # pagination of the list of record
 NB_RECORDS_BY_PAGE = 20
+#some control on the pagination appareance
+PAGINATOR_VISIBLE_RANGE = 5 #number of pages
+PAGINATOR_START_RANGE = 2 #number of pages kept at the beggining and at the end of the range
 
 # url of the sesame repository containing all the rdf referentials
 SPARQL_QUERY_ENDPOINT = "http://localhost:8080/openrdf-sesame/repositories/plan4learning"
@@ -172,3 +175,4 @@
     'env' : {'PYTHONPATH': '/Users/ymh/dev/venvs/p4l/lib/python2.7/site-packages'}
 }
 
+
--- a/src/p4l/search/views.py	Tue Dec 03 13:54:30 2013 +0100
+++ b/src/p4l/search/views.py	Tue Dec 03 20:55:04 2013 +0100
@@ -33,11 +33,14 @@
 
 
 from django.conf import settings
+from django.core.paginator import InvalidPage
+from django.http.response import Http404
 from django.template.context import RequestContext
 from haystack.query import SearchQuerySet
 from haystack.views import SearchView, search_view_factory
 
 from p4l.search.forms import RecordSearchForm
+from p4l.utils import P4lPaginator
 
 
 class RecordSearchView(SearchView):
@@ -52,3 +55,32 @@
     @classmethod
     def as_view(cls):
         return search_view_factory(view_class=cls)
+    
+    def build_page(self):
+        """
+        Paginates the results appropriately.
+
+        In case someone does not want to use Django's built-in pagination, it
+        should be a simple matter to override this method to do what they would
+        like.
+        """
+        try:
+            page_no = int(self.request.GET.get('page', 1))
+        except (TypeError, ValueError):
+            raise Http404("Not a valid number for page.")
+
+        if page_no < 1:
+            raise Http404("Pages should be 1 or greater.")
+
+        start_offset = (page_no - 1) * self.results_per_page
+        self.results[start_offset:start_offset + self.results_per_page]
+
+        paginator = P4lPaginator(self.results, self.results_per_page)
+
+        try:
+            page = paginator.page(page_no)
+        except InvalidPage:
+            raise Http404("No such page!")
+
+        return (paginator, page)
+
--- a/src/p4l/settings.py	Tue Dec 03 13:54:30 2013 +0100
+++ b/src/p4l/settings.py	Tue Dec 03 20:55:04 2013 +0100
@@ -212,6 +212,10 @@
 }
 
 NB_RECORDS_BY_PAGE = 20
+#some control on the pagination appareance
+PAGINATOR_VISIBLE_RANGE = 5 #number of visible pages
+PAGINATOR_START_RANGE = 2 #number of pages kept at the beggining and at the end of the range
+
 
 HAYSTACK_CONNECTIONS = {
     'default': {
@@ -285,6 +289,7 @@
 SCRIPT_WAIT = .250
 SCRIPT_MAX_WAIT = 40 # * SCRIPT_WAIT = 10 sec
 
+
 from config import *  # @UnusedWildImport
 
 if not "SRC_BASE_URL" in locals():
--- a/src/p4l/templates/p4l/home.html	Tue Dec 03 13:54:30 2013 +0100
+++ b/src/p4l/templates/p4l/home.html	Tue Dec 03 20:55:04 2013 +0100
@@ -25,19 +25,23 @@
 	  </form>
 	</div>
 </div>
- <div class="pagination pull-right">
- {% if page.has_previous or page.has_next %}
-    <span class="page-links">
-        {% if page.has_previous %}
-            <a href="?page={{ page.previous_page_number }}&q={{query}}">{% trans 'Previous' %}</a>
+<div class="pagination pull-right">
+  {% if page.has_previous or page.has_next %}
+    <ul class="pagination">
+        <li class="{% if page.number == 1 %}disabled{% endif %}"><a {% if page.number != 1 %}href="?page=1&q={{query}}"{% endif %}>&lt;&lt;</a></li>
+        <li class="{% if not page.has_previous %}disabled{% endif %}"><a {% if page.has_previous %}href="?page={{ page.previous_page_number }}&q={{query}}"{% endif %}>&lt;</a></li>
+        {% for p in page.visible_page_range %}
+        {% if p == 0 %}
+        <li class="disabled"><a>&hellip;</a></li>
+        {% elif p == page.number %}
+        <li class="active"><a>{{ page.number }}</a></li>
+        {% else %}
+        <li><a href="?page={{ p }}&q={{query}}">{{ p }}</a></li>
         {% endif %}
-        <span class="page-current">
-            {% trans 'Page' %} {{ page.number }} {% trans 'on' %} {{ paginator.num_pages }}
-        </span>
-        {% if page.has_next %}
-            <a href="?page={{ page.next_page_number }}&q={{query}}">{% trans 'Next' %}</a>
-        {% endif %}
-    </span>
+        {% endfor %}
+        <li class="{% if not page.has_next %}disabled{% endif %}"><a {% if page.has_next %}href="?page={{ page.next_page_number }}&q={{query}}"{% endif %}>&gt;</a></li>
+        <li class="{% if not page.has_next or page.number == paginator.num_pages %}disabled{% endif %}"><a {% if page.has_next and page.number != paginator.num_pages %}href="?page={{ paginator.num_pages }}&q={{query}}"{% endif %} >&gt;&gt;</a></li>
+    </ul>
 {% else %}
     <span>&nbsp;</span>
 {% endif %}    
--- a/src/p4l/utils.py	Tue Dec 03 13:54:30 2013 +0100
+++ b/src/p4l/utils.py	Tue Dec 03 20:55:04 2013 +0100
@@ -32,13 +32,14 @@
 #
 
 import codecs
+import hashlib
 import logging
 import math
-import hashlib
 import sys
 import unicodedata
 
 from django.conf import settings
+from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage, Page
 from django.core.validators import URLValidator
 from django.utils.http import urlquote_plus
 import requests
@@ -246,4 +247,55 @@
     return hashlib.md5(value).hexdigest()
 
 
+class P4lPaginator(Paginator):
+
+    def validate_number(self, number):
+        "Validates the given 1-based page number."
+        try:
+            number = int(number)
+        except (TypeError, ValueError):
+            raise PageNotAnInteger('That page number is not an integer')
+        if number < 1:
+            return 1
+        if number > self.num_pages:
+            if number == 1 and self.allow_empty_first_page:
+                return 1
+            elif self.num_pages > 0: 
+                return self.num_pages
+            else:
+                raise EmptyPage('That page contains no results')
+        return number
+
+    
+    def page(self, number):        
+        page = super(P4lPaginator, self).page(number)
+        return P4lPage(page.object_list, page.number, self)
+
+
+class P4lPage(Page):
+    
+    visible_range = getattr(settings, 'PAGINATOR_VISIBLE_RANGE', 5)
+    start_range = getattr(settings, 'PAGINATOR_START_RANGE', visible_range/2)
+        
+    def __get_start_range(self):
+        return max(1,self.number-self.visible_range/2)
+    
+    def __get_end_range(self):
+        return min(self.paginator.num_pages, self.number+self.visible_range/2) + 1
+    
+    def visible_page_range(self):        
+        start = self.__get_start_range()
+        end = self.__get_end_range()
+                
+        ranges = set(range(1,self.start_range+1)) | set(range(start , end)) | set(range(self.paginator.num_pages - self.start_range +1, self.paginator.num_pages+1))
+        prev = None
+        res = []
+        for i in sorted(ranges):
+            if 1 <= i <= self.paginator.num_pages:
+                if prev and i-prev > 1:
+                    res.append(0)
+                res.append(i)
+                prev = i
+        return res
+
     
\ No newline at end of file
--- a/src/p4l/views.py	Tue Dec 03 13:54:30 2013 +0100
+++ b/src/p4l/views.py	Tue Dec 03 20:55:04 2013 +0100
@@ -149,7 +149,7 @@
         context['languages_list'] = json.dumps(settings.LANGUAGES_LIST)
         
         context['is_create_view'] = json.dumps(self.is_create_view)
-                
+
         return context