from typing import TYPE_CHECKING from aiogram import Router, F from aiogram.fsm.context import FSMContext from aiogram.types import CallbackQuery, InlineKeyboardButton from aiogram.utils.keyboard import InlineKeyboardBuilder from logging import getLogger from sqlmodel.ext.asyncio.session import AsyncSession from ....model.descriptors import FieldEditButton, CommandButton, InlineButton from ....model.settings import Settings from ....model.user import UserBase from ....model import EntityPermission from ....utils.main import ( check_entity_permission, get_send_message, clear_state, get_value_repr, get_callable_str, get_entity_descriptor, ) from ..context import ContextData, CallbackCommand, CommandContext from ....utils.navigation import ( pop_navigation_context, save_navigation_context, ) if TYPE_CHECKING: from ....main import QBotApp logger = getLogger(__name__) router = Router() @router.callback_query(ContextData.filter(F.command == CallbackCommand.ENTITY_ITEM)) async def entity_item_callback(query: CallbackQuery, **kwargs): callback_data: ContextData = kwargs["callback_data"] 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_item(query=query, navigation_stack=stack, **kwargs) async def entity_item( query: CallbackQuery, callback_data: ContextData, db_session: AsyncSession, user: UserBase, app: "QBotApp", navigation_stack: list[ContextData], **kwargs, ): entity_descriptor = get_entity_descriptor(app, callback_data) # user_permissions = get_user_permissions(user, entity_descriptor) entity_type = entity_descriptor.type_ keyboard_builder = InlineKeyboardBuilder() entity_item = await entity_type.get(session=db_session, id=callback_data.entity_id) state: FSMContext = kwargs["state"] state_data = kwargs["state_data"] await state.set_data(state_data) if not entity_item: return await query.answer( text=(await Settings.get(Settings.APP_STRINGS_NOT_FOUND)) ) # is_owned = issubclass(entity_type, OwnedBotEntity) if not check_entity_permission( entity=entity_item, user=user, permission=EntityPermission.READ ): return await query.answer( text=(await Settings.get(Settings.APP_STRINGS_FORBIDDEN)) ) can_edit = check_entity_permission( entity=entity_item, user=user, permission=EntityPermission.UPDATE ) form = entity_descriptor.forms.get( callback_data.form_params or "default", entity_descriptor.default_form ) if form.form_buttons: for edit_buttons_row in form.form_buttons: btn_row = [] for button in edit_buttons_row: if button.visibility and not button.visibility(entity_item): continue if isinstance(button, FieldEditButton) and can_edit: field_name = button.field_name btn_caption = button.caption if field_name in entity_descriptor.fields_descriptors: field_descriptor = entity_descriptor.fields_descriptors[ field_name ] field_value = getattr(entity_item, field_descriptor.field_name) if btn_caption: btn_text = await get_callable_str( btn_caption, field_descriptor, entity_item, field_value ) else: if field_descriptor.type_base is bool: btn_text = f"{'【✔︎】 ' if field_value else '【 】 '}{ await get_callable_str( field_descriptor.caption, field_descriptor, entity_item, field_value, ) if field_descriptor.caption else field_name }" else: btn_text = f"{ field_descriptor.icon if field_descriptor.icon else '✏️' } { await get_callable_str( field_descriptor.caption, field_descriptor, entity_item, field_value, ) if field_descriptor.caption else field_name }" btn_row.append( InlineKeyboardButton( text=btn_text, callback_data=ContextData( command=CallbackCommand.FIELD_EDITOR, context=CommandContext.ENTITY_FIELD_EDIT, entity_name=entity_descriptor.name, entity_id=str(entity_item.id), field_name=field_name, ).pack(), ) ) elif isinstance(button, CommandButton): btn_caption = button.caption btn_text = await get_callable_str( btn_caption, entity_descriptor, entity_item ) if isinstance(button.command, ContextData): btn_cdata = button.command elif callable(button.command): btn_cdata = button.command(callback_data, entity_item) elif isinstance(button.command, str): btn_cdata = ContextData( command=CallbackCommand.USER_COMMAND, user_command=button.command, ) btn_row.append( InlineKeyboardButton( text=btn_text, callback_data=btn_cdata.pack(), ) ) elif isinstance(button, InlineButton): if isinstance(button.inline_button, InlineKeyboardButton): btn_row.append(button.inline_button) elif callable(button.inline_button): btn_row.append(button.inline_button(entity_item)) if btn_row: keyboard_builder.row(*btn_row) edit_delete_row = [] if can_edit and form.show_edit_button: edit_delete_row.append( InlineKeyboardButton( text=(await Settings.get(Settings.APP_STRINGS_EDIT_BTN)), callback_data=ContextData( command=CallbackCommand.FIELD_EDITOR, context=CommandContext.ENTITY_EDIT, entity_name=entity_descriptor.name, entity_id=str(entity_item.id), form_params=callback_data.form_params, field_name=form.edit_field_sequence[0], ).pack(), ) ) if ( check_entity_permission( entity=entity_item, user=user, permission=EntityPermission.DELETE ) and form.show_delete_button ): edit_delete_row.append( InlineKeyboardButton( text=(await Settings.get(Settings.APP_STRINGS_DELETE_BTN)), callback_data=ContextData( command=CallbackCommand.ENTITY_DELETE, entity_name=entity_descriptor.name, form_params=callback_data.form_params, entity_id=str(entity_item.id), ).pack(), ) ) if edit_delete_row: keyboard_builder.row(*edit_delete_row) if form.item_repr: item_text = form.item_repr(entity_descriptor, entity_item) else: entity_caption = ( await get_callable_str( entity_descriptor.full_name, entity_descriptor, entity_item ) if entity_descriptor.full_name else entity_descriptor.name ) entity_item_repr = ( await get_callable_str( entity_descriptor.item_repr, entity_descriptor, entity_item ) if entity_descriptor.item_repr else str(entity_item.id) ) item_text = f"{entity_caption or entity_descriptor.name}: {entity_item_repr}" for field_descriptor in entity_descriptor.fields_descriptors.values(): if field_descriptor.is_visible: field_caption = await get_callable_str( field_descriptor.caption, field_descriptor, entity_item ) if field_descriptor.caption_value: value = await get_callable_str( field_descriptor.caption_value, field_descriptor, entity_item, getattr(entity_item, field_descriptor.field_name), ) else: value = await get_value_repr( value=getattr(entity_item, field_descriptor.field_name), field_descriptor=field_descriptor, locale=user.lang, ) item_text += f"\n{field_caption or field_descriptor.name}:{f' {value}' if value else ''}" 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(), ) ) # state: FSMContext = kwargs["state"] # state_data = kwargs["state_data"] # await state.set_data(state_data) send_message = get_send_message(query) await send_message(text=item_text, reply_markup=keyboard_builder.as_markup())