Implement user-specific question numbering and update database schema. Added triggers for automatic question numbering and adjustments upon deletion. Enhanced CRUD operations to manage user_question_number effectively.
This commit is contained in:
185
services/business/pagination_service.py
Normal file
185
services/business/pagination_service.py
Normal file
@@ -0,0 +1,185 @@
|
||||
"""
|
||||
Сервис для работы с пагинацией
|
||||
"""
|
||||
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
|
||||
Reference in New Issue
Block a user