Files
quickbot/bot/handlers/forms/entity_form.py
Alexander Kalinovsky e24f9e0f79 add InlineButton to forms
2025-02-05 19:00:29 +01:00

264 lines
9.9 KiB
Python

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)
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 = 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 '【 】 '}{
get_callable_str(
field_descriptor.caption,
field_descriptor,
entity_item,
field_value,
)
if field_descriptor.caption
else field_name
}"
else:
btn_text = (
f"✏️ {
get_callable_str(
field_descriptor.caption,
field_descriptor,
entity_item,
field_value,
)
}"
if field_descriptor.caption
else f"✏️ {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 = 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 = (
get_callable_str(
entity_descriptor.full_name, entity_descriptor, entity_item
)
if entity_descriptor.full_name
else entity_descriptor.name
)
entity_item_repr = (
get_callable_str(
entity_descriptor.item_repr, entity_descriptor, entity_item
)
if entity_descriptor.item_repr
else str(entity_item.id)
)
item_text = f"<b><u><i>{entity_caption or entity_descriptor.name}:</i></u></b> <b>{entity_item_repr}</b>"
for field_descriptor in entity_descriptor.fields_descriptors.values():
if field_descriptor.is_visible:
field_caption = get_callable_str(
field_descriptor.caption, field_descriptor, entity_item
)
value = get_value_repr(
value=getattr(entity_item, field_descriptor.name),
field_descriptor=field_descriptor,
locale=user.lang,
)
item_text += f"\n{field_caption or field_descriptor.name}:{f' <b>{value}</b>' 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())