Files
quickbot/bot/handlers/forms/entity_form.py
Alexander Kalinovsky ca374cdea0 upd get_callable_str async
2025-02-13 02:00:20 +01:00

273 lines
10 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 = 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"<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 = 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' <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())