From 472611d374c8e8c7210149c7ac40057810f81926 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Thu, 12 Nov 2020 09:16:38 +0100
Subject: [PATCH 2/2] logs: change paginator to avoid count (#48074)

---
 .../includes/resource-logs-table.html         | 18 ++++-
 passerelle/utils/paginator.py                 | 66 +++++++++++++++++++
 passerelle/views.py                           |  2 +
 3 files changed, 83 insertions(+), 3 deletions(-)
 create mode 100644 passerelle/utils/paginator.py

diff --git a/passerelle/templates/passerelle/includes/resource-logs-table.html b/passerelle/templates/passerelle/includes/resource-logs-table.html
index 06cb3883..a61efc83 100644
--- a/passerelle/templates/passerelle/includes/resource-logs-table.html
+++ b/passerelle/templates/passerelle/includes/resource-logs-table.html
@@ -1,5 +1,4 @@
-{% load i18n passerelle %}
-{% load tz %}
+{% load i18n passerelle tz gadjo %}
 
 {% if logrecords %}
 <table class="main" data-log-base-url="{% url 'view-logs-connector' connector=object.get_connector_slug slug=object.slug %}" >
@@ -23,7 +22,20 @@
 </table>
 
 {% with page_obj=logrecords %}
-  {% include "gadjo/pagination.html" with anchor="#logs" without_key="log_id" %}
+{% if page_obj.has_previous or page_obj.has_next %}
+    <p class="paginator">
+    {% if page_obj.has_previous %}
+        <a href="{% querystring "page"=page_obj.previous_page_number %}{{ anchor }}">&laquo;</a>
+    {% else %}
+        <span>&laquo;</span>
+    {% endif %}
+    {% if page_obj.has_next %}
+        <a href="{% querystring "page"=page_obj.next_page_number %}{{ anchor }}">&raquo;</a>
+    {% else %}
+        <span>&raquo;</span>
+    {% endif %}
+    </p>
+{% endif %}
 {% endwith %}
 
 {% else %}
diff --git a/passerelle/utils/paginator.py b/passerelle/utils/paginator.py
new file mode 100644
index 00000000..1da78107
--- /dev/null
+++ b/passerelle/utils/paginator.py
@@ -0,0 +1,66 @@
+from django.core.paginator import Paginator, Page, PageNotAnInteger, EmptyPage
+from django.utils.translation import ugettext_lazy as _
+
+
+class InfinitePaginator(Paginator):
+    def __init__(self, object_list, per_page, orphans=None,
+                 allow_empty_first_page=True):
+        super().__init__(
+            object_list, per_page,
+            orphans=0, allow_empty_first_page=allow_empty_first_page)
+
+    def validate_number(self, number):
+        try:
+            number = int(number)
+        except (TypeError, ValueError):
+            raise PageNotAnInteger(_('That page number is not an integer'))
+        if number < 1:
+            raise EmptyPage(_('That page number is less than 1'))
+        return number
+
+    def page(self, number):
+        number = self.validate_number(number)
+        offset = (number - 1) * self.per_page
+
+        window_items = list(self.object_list[offset:offset + self.per_page + 1])
+        page_items = window_items[:self.per_page]
+
+        if not page_items:
+            if number == 1 and self.allow_empty_first_page:
+                pass
+            else:
+                raise EmptyPage("That page contains no results")
+
+        has_next = len(window_items) > len(page_items)
+        return InfinitePage(page_items, number, self, has_next)
+
+    def _get_count(self):
+        raise NotImplementedError
+    count = property(_get_count)
+
+    def _get_num_pages(self):
+        raise NotImplementedError
+    num_pages = property(_get_num_pages)
+
+    def _get_page_range(self):
+        raise NotImplementedError
+    page_range = property(_get_page_range)
+
+
+class InfinitePage(Page):
+
+    def __init__(self, object_list, number, paginator, has_next):
+        super(InfinitePage, self).__init__(object_list, number, paginator)
+        self._has_next = has_next
+
+    def __repr__(self):
+        return "<Page %s>" % self.number
+
+    def has_next(self):
+        return self._has_next
+
+    def end_index(self):
+        return (self.number - 1) * self.paginator.per_page + len(self.object_list)
+
+
+__all__ = ["InfinitePaginator", "InfinitePage"]
diff --git a/passerelle/views.py b/passerelle/views.py
index 052bb74e..f46d5d4b 100644
--- a/passerelle/views.py
+++ b/passerelle/views.py
@@ -49,6 +49,7 @@ from passerelle.base.models import BaseResource, ResourceLog
 from passerelle.compat import json_loads
 from passerelle.utils.jsonresponse import APIError
 from passerelle.utils.json import unflatten
+from passerelle.utils.paginator import InfinitePaginator
 
 from .utils import to_json, is_authorized
 from .forms import ResourceLogSearchForm
@@ -213,6 +214,7 @@ class GenericDeleteConnectorView(GenericConnectorMixin, DeleteView):
 class GenericViewLogsConnectorView(GenericConnectorMixin, ListView):
     template_name = 'passerelle/manage/service_logs.html'
     paginate_by = 25
+    paginator_class = InfinitePaginator
 
     def get_context_data(self, **kwargs):
         context = super(GenericViewLogsConnectorView, self).get_context_data(**kwargs)
-- 
2.28.0

