from typing import get_args, get_origin, TYPE_CHECKING from aiogram import Router, F from aiogram.filters import Command from aiogram.fsm.context import FSMContext from aiogram.fsm.state import StatesGroup, State from aiogram.types import Message, CallbackQuery, InlineKeyboardButton from aiogram.utils.i18n import I18n from aiogram.utils.keyboard import InlineKeyboardBuilder from logging import getLogger from sqlmodel.ext.asyncio.session import AsyncSession from ....model.bot_entity import BotEntity from ....model.bot_enum import BotEnum from ....model.owned_bot_entity import OwnedBotEntity from ....model.settings import Settings from ....model.user import UserBase from ....model.view_setting import ViewSetting from ....model.descriptors import EntityFieldDescriptor, EntityDescriptor from ....model import EntityPermission from ....utils import serialize, deserialize, get_user_permissions from ..context import ContextData, CallbackCommand, CommandContext from ..common import (add_pagination_controls, get_local_text, get_entity_descriptor, get_callable_str, get_send_message, add_filter_controls) if TYPE_CHECKING: from ....main import QBotApp logger = getLogger(__name__) router = Router() @router.callback_query(ContextData.filter(F.command == CallbackCommand.ENTITY_LIST)) async def entity_list_callback(query: CallbackQuery, **kwargs): callback_data: ContextData = kwargs["callback_data"] if callback_data.data == "skip": return state: FSMContext = kwargs["state"] state_data = await state.get_data() kwargs["state_data"] = state_data clear_state(state_data = state_data) stack = save_navigation_context(callback_data = callback_data, state_data = state_data) await entity_list(message = query, navigation_stack = stack, **kwargs) async def entity_list(message: CallbackQuery | Message, callback_data: ContextData, db_session: AsyncSession, user: UserBase, app: "QBotApp", navigation_stack: list[ContextData], **kwargs): page = int(callback_data.data or "1") entity_descriptor = get_entity_descriptor(app, callback_data) user_permissions = get_user_permissions(user, entity_descriptor) entity_type = entity_descriptor.type_ keyboard_builder = InlineKeyboardBuilder() if EntityPermission.CREATE in user_permissions or EntityPermission.CREATE_ALL in user_permissions: keyboard_builder.row( InlineKeyboardButton( text = (await Settings.get(Settings.APP_STRINGS_ADD_BTN)), callback_data = ContextData( command = CallbackCommand.FIELD_EDITOR, context = CommandContext.ENTITY_CREATE, entity_name = entity_descriptor.name, field_name = entity_descriptor.field_sequence[0], save_state = True).pack())) page_size = await Settings.get(Settings.PAGE_SIZE) entity_filter = await ViewSetting.get_filter(session = db_session, user_id = user.id, entity_name = entity_descriptor.class_name) def calc_total_pages(items_count: int, page_size: int) -> int: return max(items_count // page_size + (1 if items_count % page_size else 0), 1) if issubclass(entity_type, OwnedBotEntity): if EntityPermission.READ_ALL in user_permissions or EntityPermission.LIST_ALL in user_permissions: items_count = await entity_type.get_count(session = db_session, filter = entity_filter) total_pages = calc_total_pages(items_count, page_size) page = min(page, total_pages) items = await entity_type.get_multi( session = db_session, order_by = entity_type.name, filter = entity_filter, skip = page_size * (page - 1), limit = page_size) elif EntityPermission.READ in user_permissions or EntityPermission.LIST in user_permissions: items_count = await entity_type.get_count_by_user(session = db_session, user_id = user.id, filter = entity_filter) total_pages = calc_total_pages(items_count, page_size) page = min(page, total_pages) items = await entity_type.get_multi_by_user( session = db_session, user_id = user.id, order_by = entity_type.name, filter = entity_filter, skip = page_size * (page - 1), limit = page_size) else: items = list[OwnedBotEntity]() items_count = 0 total_pages = 1 page = 1 elif issubclass(entity_type, BotEntity): if (EntityPermission.READ in user_permissions or EntityPermission.LIST in user_permissions or EntityPermission.READ_ALL in user_permissions or EntityPermission.LIST_ALL in user_permissions): items_count = await entity_type.get_count(session = db_session, filter = entity_filter) total_pages = calc_total_pages(items_count, page_size) page = min(page, total_pages) items = await entity_type.get_multi( session = db_session, order_by = entity_type.name, filter = entity_filter, skip = page_size * (page - 1), limit = page_size) else: items = list[BotEntity]() total_pages = 1 page = 1 items_count = 0 else: raise ValueError(f"Unsupported entity type: {entity_type}") for item in items: if entity_descriptor.item_caption: caption = entity_descriptor.item_caption(entity_descriptor, item) elif entity_descriptor.fields_descriptors["name"].localizable: caption = get_local_text(item.name, user.lang) else: caption = item.name keyboard_builder.row( InlineKeyboardButton( text = caption, callback_data = ContextData( command = CallbackCommand.ENTITY_ITEM, entity_name = entity_descriptor.name, entity_id = str(item.id)).pack())) add_pagination_controls(keyboard_builder = keyboard_builder, callback_data = callback_data, total_pages = total_pages, command = CallbackCommand.ENTITY_LIST, page = page) add_filter_controls(keyboard_builder = keyboard_builder, entity_descriptor = entity_descriptor, filter = entity_filter) context = pop_navigation_context(navigation_stack) if context: keyboard_builder.row( InlineKeyboardButton( text = (await Settings.get(Settings.APP_STRINGS_BACK_BTN)), callback_data = context.pack())) if entity_descriptor.caption: entity_text = get_callable_str(entity_descriptor.caption_plural, entity_descriptor) else: entity_text = entity_descriptor.name if entity_descriptor.description: entity_desciption = get_callable_str(entity_descriptor.description, entity_descriptor) else: entity_desciption = None state: FSMContext = kwargs["state"] state_data = kwargs["state_data"] await state.set_data(state_data) send_message = get_send_message(message) await send_message(text = f"{entity_text}{f"\n{entity_desciption}" if entity_desciption else ""}", reply_markup = keyboard_builder.as_markup()) from ..navigation import pop_navigation_context, save_navigation_context, clear_state