refactoring

This commit is contained in:
Alexander Kalinovsky
2025-01-09 13:11:10 +01:00
parent 7793a0cb77
commit 3898a333fa
29 changed files with 1065 additions and 381 deletions

View File

@@ -1,7 +1,7 @@
from datetime import datetime
from decimal import Decimal
from types import NoneType, UnionType
from typing import get_args, get_origin
from typing import Union, get_args, get_origin, TYPE_CHECKING
from aiogram import Router, F
from aiogram.fsm.context import FSMContext
from aiogram.types import Message, CallbackQuery
@@ -9,7 +9,6 @@ from logging import getLogger
from sqlmodel.ext.asyncio.session import AsyncSession
import ujson as json
from ....main import QBotApp
from ....model import EntityPermission
from ....model.bot_entity import BotEntity
from ....model.owned_bot_entity import OwnedBotEntity
@@ -21,14 +20,16 @@ from ....model.descriptors import EntityFieldDescriptor
from ....utils import deserialize, get_user_permissions, serialize
from ...command_context_filter import CallbackCommandFilter
from ..context import ContextData, CallbackCommand, CommandContext
from ..common import (get_value_repr, authorize_command, get_callable_str,
get_entity_descriptor, get_field_descriptor)
from ..menu.parameters import parameters_menu
from .string import string_editor, router as string_editor_router
from .date import date_picker, router as date_picker_router
from .boolean import bool_editor, router as bool_editor_router
from .entity import entity_picker, router as entity_picker_router
if TYPE_CHECKING:
from ....main import QBotApp
logger = getLogger(__name__)
router = Router()
@@ -42,21 +43,22 @@ router.include_routers(
@router.callback_query(ContextData.filter(F.command == CallbackCommand.FIELD_EDITOR))
async def settings_field_editor(message: Message | CallbackQuery, **kwargs):
async def field_editor(message: Message | CallbackQuery, **kwargs):
callback_data: ContextData = kwargs.get("callback_data", None)
db_session: AsyncSession = kwargs["db_session"]
user: UserBase = kwargs["user"]
app: QBotApp = kwargs["app"]
app: "QBotApp" = kwargs["app"]
state: FSMContext = kwargs["state"]
state_data = await state.get_data()
entity_data = state_data.get("entity_data")
for key in ["current_value", "value", "locale_index"]:
if key in state_data:
state_data.pop(key)
await state.clear()
await state.update_data(state_data)
kwargs["state_data"] = state_data
entity_descriptor = None
@@ -69,41 +71,69 @@ async def settings_field_editor(message: Message | CallbackQuery, **kwargs):
else:
return await message.answer(text = (await Settings.get(Settings.APP_STRINGS_FORBIDDEN)))
stack, context = await get_navigation_context(state = state)
stack, context = get_navigation_context(state_data = state_data)
return await parameters_menu(message = message,
navigation_stack = stack,
**kwargs)
current_value = await Settings.get(field_descriptor)
current_value = await Settings.get(field_descriptor, all_locales = True)
else:
entity_descriptor = get_entity_descriptor(app, callback_data)
field_descriptor = get_field_descriptor(app, callback_data)
entity_descriptor = field_descriptor.entity_descriptor
current_value = None
user_permissions = get_user_permissions(user, entity_descriptor)
if not entity_data and callback_data.context == CommandContext.ENTITY_EDIT:
if (EntityPermission.READ_ALL in get_user_permissions(user, entity_descriptor) or
(EntityPermission.READ in get_user_permissions(user, entity_descriptor) and
not issubclass(entity_descriptor.type_, OwnedBotEntity)) or
(EntityPermission.READ in get_user_permissions(user, entity_descriptor) and
issubclass(entity_descriptor.type_, OwnedBotEntity) and
entity_data.user_id == user.id)):
if field_descriptor.type_base == bool and callback_data.context == CommandContext.ENTITY_FIELD_EDIT:
entity = await entity_descriptor.type_.get(session = db_session, id = int(callback_data.entity_id))
if (EntityPermission.UPDATE_ALL in user_permissions or
(EntityPermission.UPDATE in user_permissions and
not isinstance(entity, OwnedBotEntity)) or
(EntityPermission.UPDATE in user_permissions and
isinstance(entity, OwnedBotEntity) and
entity.user_id == user.id)):
current_value: bool = getattr(entity, field_descriptor.field_name) or False
setattr(entity, field_descriptor.field_name, not current_value)
await db_session.commit()
stack, context = get_navigation_context(state_data = state_data)
return await entity_item(query = message, navigation_stack = stack, **kwargs)
if not entity_data and callback_data.context in [CommandContext.ENTITY_EDIT, CommandContext.ENTITY_FIELD_EDIT]:
entity = await entity_descriptor.type_.get(session = kwargs["db_session"], id = int(callback_data.entity_id))
if (EntityPermission.READ_ALL in user_permissions or
(EntityPermission.READ in user_permissions and
not isinstance(entity, OwnedBotEntity)) or
(EntityPermission.READ in user_permissions and
isinstance(entity, OwnedBotEntity) and
entity.user_id == user.id)):
entity = await entity_descriptor.type_.get(session = kwargs["db_session"], id = int(callback_data.entity_id))
if entity:
entity_data = {key: serialize(getattr(entity, key), entity_descriptor.fields_descriptors[key]) for key in entity_descriptor.field_sequence}
await state.update_data({"entity_data": entity_data})
entity_data = {key: serialize(getattr(entity, key), entity_descriptor.fields_descriptors[key])
for key in (entity_descriptor.field_sequence if callback_data.context == CommandContext.ENTITY_EDIT
else [callback_data.field_name])}
state_data.update({"entity_data": entity_data})
if entity_data:
current_value = await deserialize(session = db_session,
type_= field_descriptor.type_,
value = entity_data.get(callback_data.field_name))
kwargs.update({"field_descriptor": field_descriptor})
save_navigation_context(state_data = state_data, callback_data = callback_data)
await show_editor(message = message,
field_descriptor = field_descriptor,
entity_descriptor = entity_descriptor,
current_value = current_value,
**kwargs)
@@ -116,14 +146,15 @@ async def show_editor(message: Message | CallbackQuery,
user: UserBase = kwargs["user"]
callback_data: ContextData = kwargs.get("callback_data", None)
state: FSMContext = kwargs["state"]
state_data: dict = kwargs["state_data"]
value_type = field_descriptor.type_
value_type = field_descriptor.type_base
if field_descriptor.edit_prompt:
edit_prompt = get_callable_str(field_descriptor.edit_prompt, field_descriptor, None, current_value)
else:
if field_descriptor.caption_str:
caption_str = get_callable_str(field_descriptor.caption_str, field_descriptor, None, current_value)
if field_descriptor.caption:
caption_str = get_callable_str(field_descriptor.caption, field_descriptor, None, current_value)
else:
caption_str = field_descriptor.name
if callback_data.context == CommandContext.ENTITY_EDIT:
@@ -135,15 +166,15 @@ async def show_editor(message: Message | CallbackQuery,
kwargs["edit_prompt"] = edit_prompt
type_origin = get_origin(value_type)
# type_origin = get_origin(value_type)
if type_origin == UnionType:
args = get_args(value_type)
if args[1] == NoneType:
value_type = args[0]
# if type_origin in [UnionType, Union]:
# args = get_args(value_type)
# if args[1] == NoneType:
# value_type = args[0]
if value_type not in [int, float, Decimal, str]:
await state.update_data({"context_data": callback_data.pack()})
state_data.update({"context_data": callback_data.pack()})
if value_type == str:
await string_editor(message = message, **kwargs)
@@ -157,12 +188,12 @@ async def show_editor(message: Message | CallbackQuery,
elif value_type == datetime:
await date_picker(message = message, **kwargs)
elif type_origin == list:
type_args = get_args(value_type)
if type_args and issubclass(type_args[0], BotEntity) or issubclass(type_args[0], BotEnum):
await entity_picker(message = message, **kwargs)
else:
await string_editor(message = message, **kwargs)
# elif type_origin == list:
# type_args = get_args(value_type)
# if type_args and issubclass(type_args[0], BotEntity) or issubclass(type_args[0], BotEnum):
# await entity_picker(message = message, **kwargs)
# else:
# await string_editor(message = message, **kwargs)
elif issubclass(value_type, BotEntity) or issubclass(value_type, BotEnum):
await entity_picker(message = message, **kwargs)
@@ -175,26 +206,22 @@ async def show_editor(message: Message | CallbackQuery,
@router.callback_query(ContextData.filter(F.command == CallbackCommand.FIELD_EDITOR_CALLBACK))
async def field_editor_callback(message: Message | CallbackQuery, **kwargs):
callback_data: ContextData = kwargs.get("callback_data", None)
app: QBotApp = kwargs["app"]
app: "QBotApp" = kwargs["app"]
state: FSMContext = kwargs["state"]
state_data = await state.get_data()
kwargs["state_data"] = state_data
if isinstance(message, Message):
callback_data: ContextData = kwargs.get("callback_data", None)
context_data = state_data.get("context_data")
if context_data:
context_data = ContextData.unpack(context_data)
callback_data = context_data
callback_data = ContextData.unpack(context_data)
value = message.text
field_descriptor = get_field_descriptor(app, callback_data)
base_type = field_descriptor.type_
if get_origin(base_type) == UnionType:
args = get_args(base_type)
if args[1] == NoneType:
base_type = args[0]
type_base = field_descriptor.type_base
if base_type == str and field_descriptor.localizable:
if type_base == str and field_descriptor.localizable:
locale_index = int(state_data.get("locale_index"))
if locale_index < len(LanguageBase.all_members.values()) - 1:
@@ -209,9 +236,9 @@ async def field_editor_callback(message: Message | CallbackQuery, **kwargs):
value = {}
value[list(LanguageBase.all_members.values())[locale_index]] = message.text
value = json.dumps(value)
value = json.dumps(value, ensure_ascii = False)
await state.update_data({"value": value})
state_data.update({"value": value})
entity_descriptor = get_entity_descriptor(app, callback_data)
@@ -231,16 +258,20 @@ async def field_editor_callback(message: Message | CallbackQuery, **kwargs):
else:
value = {}
value[list(LanguageBase.all_members.values())[locale_index]] = message.text
value = json.dumps(value)
value = json.dumps(value, ensure_ascii = False)
elif (base_type in [int, float, Decimal]):
elif (type_base in [int, float, Decimal]):
try:
_ = base_type(value) #@IgnoreException
_ = type_base(value) #@IgnoreException
except:
return await message.answer(text = (await Settings.get(Settings.APP_STRINGS_INVALID_INPUT)))
else:
callback_data: ContextData = kwargs["callback_data"]
if callback_data.data:
value = callback_data.data
if callback_data.data == "skip":
value = None
else:
value = callback_data.data
else:
value = state_data.get("value")
field_descriptor = get_field_descriptor(app, callback_data)
@@ -258,13 +289,14 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
user: UserBase = kwargs["user"]
db_session: AsyncSession = kwargs["db_session"]
callback_data: ContextData = kwargs.get("callback_data", None)
state: FSMContext = kwargs["state"]
# state: FSMContext = kwargs["state"]
state_data: dict = kwargs["state_data"]
value = kwargs["value"]
field_descriptor: EntityFieldDescriptor = kwargs["field_descriptor"]
if callback_data.context == CommandContext.SETTING_EDIT:
await clear_state(state = state)
# clear_state(state_data = state_data)
if callback_data.data != "cancel":
if await authorize_command(user = user, callback_data = callback_data):
@@ -273,28 +305,31 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
else:
return await message.answer(text = (await Settings.get(Settings.APP_STRINGS_FORBIDDEN)))
stack, context = await get_navigation_context(state = state)
# stack, context = get_navigation_context(state_data = state_data)
return await parameters_menu(message = message,
navigation_stack = stack,
**kwargs)
return await route_callback(message = message, back = True, **kwargs)
# return await parameters_menu(message = message,
# navigation_stack = stack,
# **kwargs)
elif callback_data.context in [CommandContext.ENTITY_CREATE, CommandContext.ENTITY_EDIT]:
elif callback_data.context in [CommandContext.ENTITY_CREATE, CommandContext.ENTITY_EDIT, CommandContext.ENTITY_FIELD_EDIT]:
app: QBotApp = kwargs["app"]
app: "QBotApp" = kwargs["app"]
entity_descriptor = get_entity_descriptor(app, callback_data)
field_sequence = entity_descriptor.field_sequence
current_index = field_sequence.index(callback_data.field_name)
current_index = (field_sequence.index(callback_data.field_name)
if callback_data.context in [CommandContext.ENTITY_CREATE, CommandContext.ENTITY_EDIT] else 0)
state_data = await state.get_data()
entity_data = state_data.get("entity_data", {})
if current_index < len(field_sequence) - 1:
if (callback_data.context in [CommandContext.ENTITY_CREATE, CommandContext.ENTITY_EDIT] and
current_index < len(field_sequence) - 1):
entity_data[field_descriptor.field_name] = value
await state.update_data({"entity_data": entity_data})
state_data.update({"entity_data": entity_data})
next_field_name = field_sequence[current_index + 1]
next_field_descriptor = entity_descriptor.fields_descriptors[next_field_name]
@@ -319,7 +354,7 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
if ((callback_data.context == CommandContext.ENTITY_CREATE and
EntityPermission.CREATE not in user_permissions and
EntityPermission.CREATE_ALL not in user_permissions) or
(callback_data.context == CommandContext.ENTITY_EDIT and
(callback_data.context in [CommandContext.ENTITY_EDIT, CommandContext.ENTITY_FIELD_EDIT] and
EntityPermission.UPDATE not in user_permissions and
EntityPermission.UPDATE_ALL not in user_permissions)):
return await message.answer(text = (await Settings.get(Settings.APP_STRINGS_FORBIDDEN)))
@@ -341,13 +376,20 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
obj_in = entity_type(**deser_entity_data),
commit = True)
await save_navigation_context(state = state, callback_data = ContextData(
state_data["navigation_context"] = ContextData(
command = CallbackCommand.ENTITY_ITEM,
entity_name = entity_descriptor.name,
entity_id = str(new_entity.id)
))
entity_id = str(new_entity.id)).pack()
state_data.update(state_data)
elif callback_data.context == CommandContext.ENTITY_EDIT:
# await save_navigation_context(state = state, callback_data = ContextData(
# command = CallbackCommand.ENTITY_ITEM,
# entity_name = entity_descriptor.name,
# entity_id = str(new_entity.id)
# ))
elif callback_data.context in [CommandContext.ENTITY_EDIT, CommandContext.ENTITY_FIELD_EDIT]:
entity_id = int(callback_data.entity_id)
entity = await entity_type.get(session = db_session, id = entity_id)
@@ -363,9 +405,12 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
await db_session.commit()
await clear_state(state = state)
clear_state(state_data = state_data)
await route_callback(message = message, back = False, **kwargs)
await route_callback(message = message, back = True, **kwargs)
from ..navigation import get_navigation_context, route_callback, clear_state, save_navigation_context
from ..common import (get_value_repr, authorize_command, get_callable_str,
get_entity_descriptor, get_field_descriptor)
from ..navigation import get_navigation_context, route_callback, clear_state, save_navigation_context, pop_navigation_context
from ..forms.entity_form import entity_item

View File

@@ -17,22 +17,21 @@ router = Router()
async def bool_editor(message: Message | CallbackQuery,
edit_prompt: str,
entity_descriptor: EntityDescriptor,
field_descriptor: EntityFieldDescriptor,
callback_data: ContextData,
**kwargs):
keyboard_builder = InlineKeyboardBuilder()
if isinstance(field_descriptor.bool_true_value_btn, LazyProxy):
true_caption = field_descriptor.bool_true_value_btn.value
if isinstance(field_descriptor.bool_true_value, LazyProxy):
true_caption = field_descriptor.bool_true_value.value
else:
true_caption = field_descriptor.bool_true_value_btn
true_caption = field_descriptor.bool_true_value
if isinstance(field_descriptor.bool_false_value_btn, LazyProxy):
false_caption = field_descriptor.bool_false_value_btn.value
if isinstance(field_descriptor.bool_false_value, LazyProxy):
false_caption = field_descriptor.bool_false_value.value
else:
false_caption = field_descriptor.bool_false_value_btn
false_caption = field_descriptor.bool_false_value
keyboard_builder.row(
InlineKeyboardButton(text = true_caption,
@@ -55,11 +54,15 @@ async def bool_editor(message: Message | CallbackQuery,
save_state = True).pack())
)
state_data = kwargs["state_data"]
await wrap_editor(keyboard_builder = keyboard_builder,
field_descriptor = field_descriptor,
entity_descriptor = entity_descriptor,
callback_data = callback_data,
state = kwargs["state"])
state_data = state_data)
state: FSMContext = kwargs["state"]
await state.set_data(state_data)
send_message = get_send_message(message)

View File

@@ -7,21 +7,24 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
from ....model.descriptors import EntityFieldDescriptor, EntityDescriptor
from ....model.settings import Settings
from ..context import ContextData, CallbackCommand, CommandContext
from ..navigation import get_navigation_context
from ..navigation import get_navigation_context, pop_navigation_context
async def wrap_editor(keyboard_builder: InlineKeyboardBuilder,
field_descriptor: EntityFieldDescriptor,
entity_descriptor: EntityDescriptor,
callback_data: ContextData,
state: FSMContext):
state_data: dict):
if callback_data.context in [CommandContext.ENTITY_CREATE, CommandContext.ENTITY_EDIT]:
if callback_data.context in [CommandContext.ENTITY_CREATE, CommandContext.ENTITY_EDIT, CommandContext.ENTITY_FIELD_EDIT]:
btns = []
field_index = entity_descriptor.field_sequence.index(field_descriptor.name)
entity_descriptor = field_descriptor.entity_descriptor
field_index = (entity_descriptor.field_sequence.index(field_descriptor.name)
if callback_data.context in [CommandContext.ENTITY_CREATE, CommandContext.ENTITY_EDIT]
else 0)
stack, context = await get_navigation_context(state)
stack, context = get_navigation_context(state_data = state_data)
context = pop_navigation_context(stack)
if field_index > 0:
btns.append(InlineKeyboardButton(
@@ -31,21 +34,18 @@ async def wrap_editor(keyboard_builder: InlineKeyboardBuilder,
context = callback_data.context,
entity_name = callback_data.entity_name,
entity_id = callback_data.entity_id,
field_name = entity_descriptor.field_sequence[field_index - 1],
save_state = True).pack()))
field_name = entity_descriptor.field_sequence[field_index - 1]).pack()))
if get_origin(field_descriptor.type_) == UnionType:
args = get_args(field_descriptor.type_)
if args[1] == NoneType:
btns.append(InlineKeyboardButton(
text = (await Settings.get(Settings.APP_STRINGS_SKIP_BTN)),
callback_data = ContextData(
command = CallbackCommand.FIELD_EDITOR_CALLBACK,
context = callback_data.context,
entity_name = callback_data.entity_name,
entity_id = callback_data.entity_id,
field_name = callback_data.field_name,
save_state = True).pack()))
if field_descriptor.is_optional:
btns.append(InlineKeyboardButton(
text = (await Settings.get(Settings.APP_STRINGS_SKIP_BTN)),
callback_data = ContextData(
command = CallbackCommand.FIELD_EDITOR_CALLBACK,
context = callback_data.context,
entity_name = callback_data.entity_name,
entity_id = callback_data.entity_id,
field_name = callback_data.field_name,
data = "skip").pack()))
keyboard_builder.row(*btns)

View File

@@ -4,13 +4,16 @@ from aiogram.fsm.context import FSMContext
from aiogram.types import Message, CallbackQuery, InlineKeyboardButton
from aiogram.utils.keyboard import InlineKeyboardBuilder
from logging import getLogger
from typing import TYPE_CHECKING
from ....main import QBotApp
from ....model.descriptors import EntityFieldDescriptor, EntityDescriptor
from ..context import ContextData, CallbackCommand
from ..common import get_send_message, get_field_descriptor, get_entity_descriptor
from .common import wrap_editor
if TYPE_CHECKING:
from ....main import QBotApp
logger = getLogger(__name__)
router = Router()
@@ -18,7 +21,6 @@ router = Router()
async def date_picker(message: Message | CallbackQuery,
field_descriptor: EntityFieldDescriptor,
entity_descriptor: EntityDescriptor,
callback_data: ContextData,
current_value: datetime,
state: FSMContext,
@@ -82,11 +84,14 @@ async def date_picker(message: Message | CallbackQuery,
keyboard_builder.row(*buttons)
state_data = kwargs["state_data"]
await wrap_editor(keyboard_builder = keyboard_builder,
field_descriptor = field_descriptor,
entity_descriptor = entity_descriptor,
callback_data = callback_data,
state = state)
state_data = state_data)
await state.set_data(state_data)
if edit_prompt:
send_message = get_send_message(message)
@@ -98,7 +103,7 @@ async def date_picker(message: Message | CallbackQuery,
@router.callback_query(ContextData.filter(F.command == CallbackCommand.DATE_PICKER_YEAR))
async def date_picker_year(query: CallbackQuery,
callback_data: ContextData,
app: QBotApp,
app: "QBotApp",
state: FSMContext,
**kwargs):
@@ -142,11 +147,9 @@ async def date_picker_year(query: CallbackQuery,
save_state = True).pack()))
field_descriptor = get_field_descriptor(app, callback_data)
entity_descriptor = get_entity_descriptor(app, callback_data)
await wrap_editor(keyboard_builder = keyboard_builder,
field_descriptor = field_descriptor,
entity_descriptor = entity_descriptor,
callback_data = callback_data,
state = state)
@@ -154,14 +157,12 @@ async def date_picker_year(query: CallbackQuery,
@router.callback_query(ContextData.filter(F.command == CallbackCommand.DATE_PICKER_MONTH))
async def date_picker_month(query: CallbackQuery, callback_data: ContextData, app: QBotApp, **kwargs):
entity_descriptor = get_entity_descriptor(app, callback_data)
async def date_picker_month(query: CallbackQuery, callback_data: ContextData, app: "QBotApp", **kwargs):
field_descriptor = get_field_descriptor(app, callback_data)
await date_picker(query.message,
field_descriptor = field_descriptor,
entity_descriptor = entity_descriptor,
callback_data = callback_data,
current_value = datetime.strptime(callback_data.data, "%Y-%m-%d"),
**kwargs)

View File

@@ -5,20 +5,24 @@ from aiogram.types import Message, CallbackQuery, InlineKeyboardButton
from aiogram.utils.keyboard import InlineKeyboardBuilder
from logging import getLogger
from sqlmodel.ext.asyncio.session import AsyncSession
from typing import get_args, get_origin
from typing import get_args, get_origin, TYPE_CHECKING
from ....main import QBotApp
from ....model.bot_entity import BotEntity
from ....model.owned_bot_entity import OwnedBotEntity
from ....model.bot_enum import BotEnum
from ....model.settings import Settings
from ....model.user import UserBase
from ....model.view_setting import ViewSetting
from ....model.descriptors import EntityFieldDescriptor, EntityDescriptor
from ....utils import serialize, deserialize
from ....model import EntityPermission
from ....utils import serialize, deserialize, get_user_permissions
from ..context import ContextData, CallbackCommand
from ..common import (get_send_message, get_local_text, get_field_descriptor,
get_entity_descriptor, add_pagination_controls)
get_entity_descriptor, add_pagination_controls, add_filter_controls)
from .common import wrap_editor
if TYPE_CHECKING:
from ....main import QBotApp
logger = getLogger(__name__)
router = Router()
@@ -28,24 +32,28 @@ async def entity_picker(message: Message | CallbackQuery,
field_descriptor: EntityFieldDescriptor,
edit_prompt: str,
current_value: BotEntity | BotEnum | list[BotEntity] | list[BotEnum],
state: FSMContext,
**kwargs):
await state.update_data({"current_value": serialize(current_value, field_descriptor),
state_data: dict = kwargs["state_data"]
state_data.update({"current_value": serialize(current_value, field_descriptor),
"value": serialize(current_value, field_descriptor),
"edit_prompt": edit_prompt})
await render_entity_picker(field_descriptor = field_descriptor,
message = message,
state = state,
current_value = current_value,
edit_prompt = edit_prompt,
**kwargs)
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)
async def render_entity_picker(*,
field_descriptor: EntityFieldDescriptor,
entity_descriptor: EntityDescriptor,
message: Message | CallbackQuery,
callback_data: ContextData,
user: UserBase,
@@ -60,18 +68,21 @@ async def render_entity_picker(*,
if callback_data.command in [CallbackCommand.ENTITY_PICKER_PAGE, CallbackCommand.ENTITY_PICKER_TOGGLE_ITEM]:
page = int(callback_data.data.split("&")[0])
is_list = False
# is_list = False
type_origin = get_origin(field_descriptor.type_)
if type_origin == UnionType:
type_ = get_args(field_descriptor.type_)[0]
# type_origin = get_origin(field_descriptor.type_)
# if type_origin == UnionType:
# type_ = get_args(field_descriptor.type_)[0]
elif type_origin == list:
type_ = get_args(field_descriptor.type_)[0]
is_list = True
# elif type_origin == list:
# type_ = get_args(field_descriptor.type_)[0]
# is_list = True
else:
type_ = field_descriptor.type_
# else:
# type_ = field_descriptor.type_
type_ = field_descriptor.type_base
is_list = field_descriptor.is_list
if not issubclass(type_, BotEntity) and not issubclass(type_, BotEnum):
raise ValueError("Unsupported type")
@@ -80,18 +91,43 @@ async def render_entity_picker(*,
if issubclass(type_, BotEnum):
items_count = len(type_.all_members)
total_pages = calc_total_pages(items_count, page_size)
page = min(page, total_pages)
enum_items = list(type_.all_members.values())[page_size * (page - 1):page_size * page]
items = [{"text": f"{"" if not is_list else "【✔︎】 " if item in (current_value or []) else "【 】 "}{item.localized(user.lang)}",
"value": item.value} for item in enum_items]
else:
items_count = await type_.get_count(session = db_session)
entity_items = await type_.get_multi(session = db_session, order_by = type_.name, skip = page_size * (page - 1), limit = page_size)
permissions = get_user_permissions(user, type_.bot_entity_descriptor)
entity_filter = await ViewSetting.get_filter(session = db_session, user_id = user.id, entity_name = type_.bot_entity_descriptor.class_name)
if (EntityPermission.LIST_ALL in permissions or
(EntityPermission.LIST in permissions and
not issubclass(type_, OwnedBotEntity))):
items_count = await type_.get_count(session = db_session, filter = entity_filter)
total_pages = calc_total_pages(items_count, page_size)
page = min(page, total_pages)
entity_items = await type_.get_multi(
session = db_session, order_by = type_.name, filter = entity_filter,
skip = page_size * (page - 1), limit = page_size)
elif (EntityPermission.LIST in permissions and
issubclass(type_, OwnedBotEntity)):
items_count = await 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)
entity_items = await type_.get_multi_by_user(
session = db_session, user_id = user.id, order_by = type_.name, filter = entity_filter,
skip = page_size * (page - 1), limit = page_size)
else:
items_count = 0
total_pages = 1
page = 1
entity_items = list[BotEntity]()
items = [{"text": f"{"" if not is_list else "【✔︎】 " if item in (current_value or []) else "【 】 "}{
type_.bot_entity_descriptor.item_caption_btn(type_.bot_entity_descriptor, item) if type_.bot_entity_descriptor.item_caption_btn
else get_local_text(item.name, user.lang) if field_descriptor.localizable else item.name}",
type_.bot_entity_descriptor.item_caption(type_.bot_entity_descriptor, item) if type_.bot_entity_descriptor.item_caption
else get_local_text(item.name, user.lang) if type_.bot_entity_descriptor.fields_descriptors["name"].localizable else item.name}",
"value": str(item.id)} for item in entity_items]
total_pages = items_count // page_size + (1 if items_count % page_size else 0)
# total_pages = items_count // page_size + (1 if items_count % page_size else 0)
keyboard_builder = InlineKeyboardBuilder()
@@ -112,6 +148,11 @@ async def render_entity_picker(*,
total_pages = total_pages,
command = CallbackCommand.ENTITY_PICKER_PAGE,
page = page)
if issubclass(type_, BotEntity):
add_filter_controls(keyboard_builder = keyboard_builder,
entity_descriptor = type_.bot_entity_descriptor,
filter = entity_filter)
if is_list:
keyboard_builder.row(
@@ -124,11 +165,14 @@ async def render_entity_picker(*,
field_name = callback_data.field_name,
save_state = True).pack()))
await wrap_editor(keyboard_builder = keyboard_builder,
field_descriptor = field_descriptor,
entity_descriptor = entity_descriptor,
callback_data = callback_data,
state = state)
state_data = kwargs["state_data"]
await wrap_editor(keyboard_builder = keyboard_builder,
field_descriptor = field_descriptor,
callback_data = callback_data,
state_data = state_data)
await state.set_data(state_data)
send_message = get_send_message(message)
@@ -140,14 +184,14 @@ async def render_entity_picker(*,
async def entity_picker_callback(query: CallbackQuery,
callback_data: ContextData,
db_session: AsyncSession,
app: QBotApp,
app: "QBotApp",
state: FSMContext,
**kwargs):
state_data = await state.get_data()
kwargs["state_data"] = state_data
field_descriptor = get_field_descriptor(app = app, callback_data = callback_data)
entity_descriptor = get_entity_descriptor(app = app, callback_data = callback_data)
current_value = await deserialize(session = db_session, type_ = field_descriptor.type_, value = state_data["current_value"])
edit_prompt = state_data["edit_prompt"]
@@ -156,7 +200,7 @@ async def entity_picker_callback(query: CallbackQuery,
if callback_data.command == CallbackCommand.ENTITY_PICKER_TOGGLE_ITEM:
page, id_value = callback_data.data.split("&")
page = int(page)
type_ = get_args(field_descriptor.type_)[0]
type_ = field_descriptor.type_base
if issubclass(type_, BotEnum):
item = type_(id_value)
if item in value:
@@ -170,7 +214,7 @@ async def entity_picker_callback(query: CallbackQuery,
else:
value.append(item)
await state.update_data({"value": serialize(value, field_descriptor)})
state_data.update({"value": serialize(value, field_descriptor)})
elif callback_data.command == CallbackCommand.ENTITY_PICKER_PAGE:
if callback_data.data == "skip":
return
@@ -179,7 +223,6 @@ async def entity_picker_callback(query: CallbackQuery,
raise ValueError("Unsupported command")
await render_entity_picker(field_descriptor = field_descriptor,
entity_descriptor = entity_descriptor,
message = query,
callback_data = callback_data,
current_value = value,

View File

@@ -21,7 +21,6 @@ router = Router()
async def string_editor(message: Message | CallbackQuery,
field_descriptor: EntityFieldDescriptor,
entity_descriptor: EntityDescriptor,
callback_data: ContextData,
current_value: Any,
edit_prompt: str,
@@ -31,14 +30,16 @@ async def string_editor(message: Message | CallbackQuery,
keyboard_builder = InlineKeyboardBuilder()
state_data: dict = kwargs["state_data"]
_edit_prompt = edit_prompt
type_ = field_descriptor.type_
type_origin = get_origin(type_)
if type_origin == UnionType:
type_ = get_args(type_)[0]
# type_ = field_descriptor.type_
# type_origin = get_origin(type_)
# if type_origin == UnionType:
# type_ = get_args(type_)[0]
if type_ == str and field_descriptor.localizable:
if field_descriptor.type_base == str and field_descriptor.localizable:
current_locale = list(LanguageBase.all_members.values())[locale_index]
context_data = ContextData(
@@ -51,9 +52,9 @@ async def string_editor(message: Message | CallbackQuery,
_edit_prompt = f"{edit_prompt}\n{(await Settings.get(
Settings.APP_STRINGS_STRING_EDITOR_LOCALE_TEMPLATE_P_NAME)).format(name = current_locale)}"
_current_value = get_local_text(current_value, current_locale)
_current_value = get_local_text(current_value, current_locale) if current_value else None
await state.update_data({
state_data.update({
"context_data": context_data.pack(),
"edit_prompt": edit_prompt,
"locale_index": str(locale_index),
@@ -70,7 +71,7 @@ async def string_editor(message: Message | CallbackQuery,
_current_value = serialize(current_value, field_descriptor)
await state.update_data({
state_data.update({
"context_data": context_data.pack()})
if _current_value:
@@ -79,16 +80,19 @@ async def string_editor(message: Message | CallbackQuery,
keyboard_builder.row(InlineKeyboardButton(text = _current_value_caption,
copy_text = CopyTextButton(text = _current_value)))
state_data = kwargs["state_data"]
await wrap_editor(keyboard_builder = keyboard_builder,
field_descriptor = field_descriptor,
entity_descriptor = entity_descriptor,
callback_data = callback_data,
state = state)
state_data = state_data)
await state.set_data(state_data)
send_message = get_send_message(message)
await send_message(text = _edit_prompt, reply_markup = keyboard_builder.as_markup())
async def context_command_fiter(*args, **kwargs):
print(args, kwargs)
return True
# async def context_command_fiter(*args, **kwargs):
# print(args, kwargs)
# return True