diff --git a/accounting/templates/accounting/cash.html b/accounting/templates/accounting/cash.html
index 9139867..eb6c620 100644
--- a/accounting/templates/accounting/cash.html
+++ b/accounting/templates/accounting/cash.html
@@ -34,6 +34,8 @@ First written: 2020/7/1
{{ request.resolver_match.app_name }}
{% if records %}
+ {% include "mia_core/pagination.html" %}
+
{# The table for large screens #}
diff --git a/accounting/views.py b/accounting/views.py
index 043a7fa..ecf65b8 100644
--- a/accounting/views.py
+++ b/accounting/views.py
@@ -201,5 +201,5 @@ ORDER BY
self.kwargs["subject_code"] + "%",
self.kwargs["subject_code"] + "%"])
self.pagination = Pagination(
- records, self.page_no, self.page_size, True)
+ self.request, records, self.page_no, self.page_size, True)
return self.pagination.records
diff --git a/mia_core/templates/mia_core/pagination.html b/mia_core/templates/mia_core/pagination.html
new file mode 100644
index 0000000..c564980
--- /dev/null
+++ b/mia_core/templates/mia_core/pagination.html
@@ -0,0 +1,38 @@
+{% comment %}
+The Mia Website
+base.html: The side-wide layout template
+
+ Copyright (c) 2020 imacat.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+Author: imacat@mail.imacat.idv.tw (imacat)
+First written: 2020/7/1
+{% endcomment %}
+
+{# The pagination, if any #}
+{% if pagination.is_paged %}
+
+{% endif %}
\ No newline at end of file
diff --git a/mia_core/utils.py b/mia_core/utils.py
index 2152658..5a3d322 100644
--- a/mia_core/utils.py
+++ b/mia_core/utils.py
@@ -21,6 +21,8 @@
import urllib.parse
+from django.utils.translation import pgettext
+
class UrlBuilder:
"""The URL builder.
@@ -91,6 +93,17 @@ class UrlBuilder:
"""
return self.del_param(name).add_param(name, value)
+ def clone(self):
+ """Returns a copy of this URL builder.
+
+ Returns:
+ UrlBuilder: A copy of this URL builder.
+ """
+ another = UrlBuilder(self.base_path)
+ another.params = [
+ self.Param(x.name, x.value) for x in self.params]
+ return another
+
def __str__(self):
if len(self.params) == 0:
return self.base_path
@@ -134,42 +147,170 @@ class Pagination:
"""The pagination.
Args:
+ request (HttpRequest): The request
records (list[Model]): All the records
page_no (int): The specified page number
page_size (int): The specified number of records per page
- is_reverse (bool): Whether we should display the last
- page first
+ is_reversed (bool): Whether we should display the last
+ page first
Raises:
PageNoOutOfRangeError: if the specified page number is out
of range or is redundant.
Attributes:
- page_no (int): The current page number
- page_size (int): The page size
- records (list[Model]): The records in the current page
+ is_reversed (bool): Whether we should display the last
+ page first
+ page_size (int): The page size.
+ total_pages (int): The total number of pages available.
+ is_paged (bool): Whether there are more than one page.
+ page_no (int): The current page number.
+ records (list[Model]): The records in the current page.
+ links (list[Link]): The navigation links in the pagination
+ bar.
"""
- page_no = None
+ is_reversed = False
page_size = None
+ total_pages = None
+ is_paged = None
+ page_no = None
records = None
+ links = None
DEFAULT_PAGE_SIZE = 10
- def __init__(self, records, page_no, page_size, is_reverse=False):
+ def __init__(self, request, records, page_no,
+ page_size, is_reversed=False):
+ self.is_reversed = is_reversed
self.page_size = page_size \
if page_size is not None \
else self.DEFAULT_PAGE_SIZE
- total_pages = int((len(records) - 1) / self.page_size) + 1
- default_page = 1 if not is_reverse else total_pages
+ self.total_pages = int(
+ (len(records) - 1) / self.page_size) + 1
+ self.is_paged = self.total_pages > 1
+ if not self.is_paged:
+ self.page_no = 1
+ self.records = records
+ self.links = []
+ return
+ default_page = 1 if not is_reversed else self.total_pages
if page_no == default_page:
raise PageNoOutOfRangeException()
self.page_no = page_no \
if page_no is not None \
else default_page
- if self.page_no > total_pages:
+ if self.page_no > self.total_pages:
raise PageNoOutOfRangeException()
start_no = self.page_size * (self.page_no - 1)
self.records = records[start_no:start_no + self.page_size]
+ self.create_pagination_bar(request)
+
+ def create_pagination_bar(self, request):
+ """Creates the pagination bar.
+
+ Args:
+ request (HttpRequest): The request
+ """
+ base_url = UrlBuilder(
+ request.get_full_path()).del_param("page")
+ self.links = []
+ # The previous page
+ link = self.Link()
+ link.title = pgettext("Pagination|", "Previous")
+ if self.page_no > 1:
+ if self.page_no - 1 == 1:
+ if not self.is_reversed:
+ link.url = str(base_url)
+ else:
+ link.url = str(base_url.clone().add_param(
+ "page", "1"))
+ else:
+ link.url = str(base_url.clone().add_param(
+ "page", str(self.page_no - 1)))
+ link.is_small_screen = True
+ self.links.append(link)
+ # The first page
+ link = self.Link()
+ link.title = "1"
+ if not self.is_reversed:
+ link.url = str(base_url)
+ else:
+ link.url = str(base_url.clone().add_param("page", "1"))
+ if self.page_no == 1:
+ link.is_active = True
+ self.links.append(link)
+ # The previous ellipsis
+ if self.page_no > 4:
+ link = self.Link()
+ if self.page_no > 5:
+ link.title = pgettext("Pagination|", "...")
+ else:
+ link.title = "2"
+ link.url = str(base_url.clone().add_param(
+ "page", "2"))
+ self.links.append(link)
+ # The nearby pages
+ for no in range(self.page_no - 2, self.page_no + 3):
+ if no <= 1 or no >= self.total_pages:
+ continue
+ link = self.Link()
+ link.title = str(no)
+ link.url = str(base_url.clone().add_param(
+ "page", str(no)))
+ if no == self.page_no:
+ link.is_active = True
+ self.links.append(link)
+ # The next ellipsis
+ if self.page_no + 3 < self.total_pages:
+ link = self.Link()
+ if self.page_no + 4 < self.total_pages:
+ link.title = pgettext("Pagination|", "...")
+ else:
+ link.title = str(self.total_pages - 1)
+ link.url = str(base_url.clone().add_param(
+ "page", str(self.total_pages - 1)))
+ self.links.append(link)
+ # The last page
+ link = self.Link()
+ link.title = str(self.total_pages)
+ if self.is_reversed:
+ link.url = str(base_url)
+ else:
+ link.url = str(base_url.clone().add_param(
+ "page", str(self.total_pages)))
+ if self.page_no == self.total_pages:
+ link.is_active = True
+ self.links.append(link)
+ # The next page
+ link = self.Link()
+ link.title = pgettext("Pagination|", "Next")
+ if self.page_no < self.total_pages:
+ if self.page_no + 1 == self.total_pages:
+ if self.is_reversed:
+ link.url = str(base_url)
+ else:
+ link.url = str(base_url.clone().add_param(
+ "page", str(self.total_pages)))
+ else:
+ link.url = str(base_url.clone().add_param(
+ "page", str(self.page_no + 1)))
+ link.is_small_screen = True
+ self.links.append(link)
+
+ class Link:
+ """A navigation link in the pagination bar.
+
+ Attributes:
+ url (str): The link URL, or for a non-link slot.
+ title (str): The title of the link.
+ is_active (bool): Whether this link is currently active.
+ is_small_screen (bool): Whether this link is for small
+ screens
+ """
+ url = None
+ title = None
+ is_active = False
+ is_small_screen = False
class PageNoOutOfRangeException(Exception):