Coverage for ramose / paging.py: 100%
37 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-07-01 13:49 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-07-01 13:49 +0000
1# SPDX-FileCopyrightText: 2026 Arcangelo Massari <arcangelo.massari@unibo.it>
2#
3# SPDX-License-Identifier: ISC
5from __future__ import annotations
7from math import ceil
8from typing import NamedTuple
9from urllib.parse import urlencode
11_PAGINATION_KEYS = frozenset({"page", "page_size"})
14class PaginationInfo(NamedTuple):
15 page: int
16 page_size: int
17 total_items: int
18 self_url: str
19 next_url: str
20 prev_url: str
21 first_url: str
22 last_url: str
25def build_pagination_info(
26 base_path: str,
27 query_params: dict[str, list[str]],
28 page: int,
29 page_size: int,
30 total_items: int,
31) -> PaginationInfo:
32 total_pages = ceil(total_items / page_size) if page_size > 0 else 0
33 self_url = _page_url(base_path, query_params, page, page_size, total_items)
34 next_url = _page_url(base_path, query_params, page + 1, page_size, total_items) if page < total_pages else ""
35 prev_url = _page_url(base_path, query_params, page - 1, page_size, total_items) if page > 1 else ""
36 first_url = _page_url(base_path, query_params, 1, page_size, total_items)
37 last_url = _page_url(base_path, query_params, max(total_pages, 1), page_size, total_items)
38 return PaginationInfo(page, page_size, total_items, self_url, next_url, prev_url, first_url, last_url)
41def build_link_header(pagination_info: PaginationInfo) -> str:
42 links = []
43 if pagination_info.next_url:
44 links.append(f'<{pagination_info.next_url}>; rel="next"')
45 if pagination_info.prev_url:
46 links.append(f'<{pagination_info.prev_url}>; rel="prev"')
47 links.append(f'<{pagination_info.first_url}>; rel="first"')
48 links.append(f'<{pagination_info.last_url}>; rel="last"')
49 return ", ".join(links)
52def _page_url(base_path: str, query_params: dict[str, list[str]], page: int, page_size: int, total_items: int) -> str:
53 params = {k: v for k, v in query_params.items() if k not in _PAGINATION_KEYS}
54 params["page"] = [str(page)]
55 params["page_size"] = [str(page_size)]
56 params["total_items"] = [str(total_items)]
57 return f"{base_path}?{urlencode(params, doseq=True, safe=':,')}"