186 lines
7.0 KiB
Python
186 lines
7.0 KiB
Python
"""
|
||
Сервис для работы с пагинацией
|
||
"""
|
||
from typing import Any, List, Optional, Tuple
|
||
|
||
from aiogram.types import InlineKeyboardMarkup
|
||
|
||
from config.constants import DEFAULT_PAGE_SIZE, MIN_PAGE_NUMBER
|
||
from services.infrastructure.logger import get_logger
|
||
|
||
logger = get_logger(__name__)
|
||
|
||
|
||
class PaginationService:
|
||
"""Сервис для работы с пагинацией"""
|
||
|
||
def __init__(self):
|
||
pass
|
||
|
||
async def calculate_pagination_from_db(
|
||
self,
|
||
total_count: int,
|
||
page: int,
|
||
per_page: int = DEFAULT_PAGE_SIZE
|
||
) -> Tuple[int, int, int, int]:
|
||
"""
|
||
Расчет пагинации на основе общего количества записей в БД
|
||
|
||
Args:
|
||
total_count: Общее количество записей в БД
|
||
page: Номер страницы (начиная с 0)
|
||
per_page: Количество элементов на странице
|
||
|
||
Returns:
|
||
Кортеж (общее_количество, текущая_страница, общее_количество_страниц, offset)
|
||
"""
|
||
try:
|
||
total_pages = (total_count + per_page - 1) // per_page # Округление вверх
|
||
|
||
# Проверяем корректность номера страницы
|
||
if page < MIN_PAGE_NUMBER:
|
||
page = MIN_PAGE_NUMBER
|
||
elif page >= total_pages and total_pages > 0:
|
||
page = total_pages - 1
|
||
|
||
# Вычисляем offset для БД
|
||
offset = page * per_page
|
||
|
||
return total_count, page, total_pages, offset
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при расчете пагинации из БД: {e}")
|
||
return MIN_PAGE_NUMBER, MIN_PAGE_NUMBER, MIN_PAGE_NUMBER, MIN_PAGE_NUMBER
|
||
|
||
def calculate_pagination(
|
||
self,
|
||
items: List[Any],
|
||
page: int,
|
||
per_page: int = DEFAULT_PAGE_SIZE
|
||
) -> Tuple[List[Any], int, int, int]:
|
||
"""
|
||
Расчет пагинации для списка элементов
|
||
|
||
Args:
|
||
items: Список элементов
|
||
page: Номер страницы (начиная с 0)
|
||
per_page: Количество элементов на странице
|
||
|
||
Returns:
|
||
Кортеж (элементы_страницы, общее_количество, текущая_страница, общее_количество_страниц)
|
||
"""
|
||
try:
|
||
total_items = len(items)
|
||
total_pages = (total_items + per_page - 1) // per_page # Округление вверх
|
||
|
||
# Проверяем корректность номера страницы
|
||
if page < MIN_PAGE_NUMBER:
|
||
page = MIN_PAGE_NUMBER
|
||
elif page >= total_pages and total_pages > 0:
|
||
page = total_pages - 1
|
||
|
||
# Вычисляем диапазон элементов для текущей страницы
|
||
start_idx = page * per_page
|
||
end_idx = min(start_idx + per_page, total_items)
|
||
page_items = items[start_idx:end_idx]
|
||
|
||
return page_items, total_items, page, total_pages
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при расчете пагинации: {e}")
|
||
return [], MIN_PAGE_NUMBER, MIN_PAGE_NUMBER, MIN_PAGE_NUMBER
|
||
|
||
def format_pagination_info(
|
||
self,
|
||
current_page: int,
|
||
total_pages: int,
|
||
start_idx: int,
|
||
end_idx: int,
|
||
total_items: int
|
||
) -> str:
|
||
"""
|
||
Форматирование информации о пагинации
|
||
|
||
Args:
|
||
current_page: Текущая страница
|
||
total_pages: Общее количество страниц
|
||
start_idx: Начальный индекс
|
||
end_idx: Конечный индекс
|
||
total_items: Общее количество элементов
|
||
|
||
Returns:
|
||
Отформатированная строка с информацией о пагинации
|
||
"""
|
||
try:
|
||
info = f"📊 Показано {start_idx + 1}-{end_idx} из {total_items}\n"
|
||
info += f"📄 Страница {current_page + 1} из {total_pages}\n\n"
|
||
|
||
return info
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при форматировании информации о пагинации: {e}")
|
||
return ""
|
||
|
||
def get_pagination_buttons(
|
||
self,
|
||
current_page: int,
|
||
total_pages: int,
|
||
callback_prefix: str,
|
||
additional_buttons: Optional[List[Tuple[str, str]]] = None
|
||
) -> List[Tuple[str, str]]:
|
||
"""
|
||
Получение кнопок пагинации
|
||
|
||
Args:
|
||
current_page: Текущая страница
|
||
total_pages: Общее количество страниц
|
||
callback_prefix: Префикс для callback_data
|
||
additional_buttons: Дополнительные кнопки
|
||
|
||
Returns:
|
||
Список кортежей (текст_кнопки, callback_data)
|
||
"""
|
||
try:
|
||
buttons = []
|
||
|
||
# Кнопка "Предыдущая"
|
||
if current_page > MIN_PAGE_NUMBER:
|
||
buttons.append(("⬅️", f"{callback_prefix}_page_{current_page - 1}"))
|
||
|
||
# Кнопка "Следующая"
|
||
if current_page < total_pages - 1:
|
||
buttons.append(("➡️", f"{callback_prefix}_page_{current_page + 1}"))
|
||
|
||
# Дополнительные кнопки
|
||
if additional_buttons:
|
||
buttons.extend(additional_buttons)
|
||
|
||
return buttons
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при создании кнопок пагинации: {e}")
|
||
return []
|
||
|
||
def validate_page_number(self, page: int, total_pages: int) -> int:
|
||
"""
|
||
Валидация номера страницы
|
||
|
||
Args:
|
||
page: Номер страницы
|
||
total_pages: Общее количество страниц
|
||
|
||
Returns:
|
||
Валидный номер страницы
|
||
"""
|
||
try:
|
||
if page < 0:
|
||
return 0
|
||
elif page >= total_pages and total_pages > 0:
|
||
return total_pages - 1
|
||
else:
|
||
return page
|
||
|
||
except Exception as e:
|
||
logger.error(f"Ошибка при валидации номера страницы: {e}")
|
||
return MIN_PAGE_NUMBER
|