add command params
This commit is contained in:
@@ -8,6 +8,5 @@ from .model.descriptors import (
|
|||||||
EntityForm as EntityForm,
|
EntityForm as EntityForm,
|
||||||
EntityList as EntityList,
|
EntityList as EntityList,
|
||||||
EntityPermission as EntityPermission,
|
EntityPermission as EntityPermission,
|
||||||
Command as Command,
|
|
||||||
CommandCallbackContext as CommandCallbackContext,
|
CommandCallbackContext as CommandCallbackContext,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ def add_pagination_controls(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data="1" if page != 1 else "skip",
|
data="1" if page != 1 else "skip",
|
||||||
).pack(),
|
).pack(),
|
||||||
)
|
)
|
||||||
@@ -39,6 +40,7 @@ def add_pagination_controls(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=str(max(page - 10, 1)) if page > 1 else "skip",
|
data=str(max(page - 10, 1)) if page > 1 else "skip",
|
||||||
).pack(),
|
).pack(),
|
||||||
)
|
)
|
||||||
@@ -54,6 +56,7 @@ def add_pagination_controls(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=str(max(page - 1, 1)) if page > 1 else "skip",
|
data=str(max(page - 1, 1)) if page > 1 else "skip",
|
||||||
).pack(),
|
).pack(),
|
||||||
)
|
)
|
||||||
@@ -68,6 +71,7 @@ def add_pagination_controls(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=(
|
data=(
|
||||||
str(min(page + 1, total_pages))
|
str(min(page + 1, total_pages))
|
||||||
if page < total_pages
|
if page < total_pages
|
||||||
@@ -88,6 +92,7 @@ def add_pagination_controls(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=(
|
data=(
|
||||||
str(min(page + 10, total_pages))
|
str(min(page + 10, total_pages))
|
||||||
if page < total_pages
|
if page < total_pages
|
||||||
@@ -106,6 +111,7 @@ def add_pagination_controls(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=str(total_pages) if page != total_pages else "skip",
|
data=str(total_pages) if page != total_pages else "skip",
|
||||||
).pack(),
|
).pack(),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
from aiogram.types import Message, CallbackQuery
|
from aiogram.types import Message, CallbackQuery
|
||||||
from ..context import CallbackCommand
|
from ..context import CallbackCommand
|
||||||
|
|
||||||
from ..navigation import (
|
from ....utils.navigation import (
|
||||||
get_navigation_context,
|
get_navigation_context,
|
||||||
save_navigation_context,
|
save_navigation_context,
|
||||||
pop_navigation_context,
|
pop_navigation_context,
|
||||||
@@ -41,7 +41,12 @@ async def route_callback(message: Message | CallbackQuery, back: bool = True, **
|
|||||||
await form_item.entity_item(message, **kwargs)
|
await form_item.entity_item(message, **kwargs)
|
||||||
elif context.command == CallbackCommand.FIELD_EDITOR:
|
elif context.command == CallbackCommand.FIELD_EDITOR:
|
||||||
await editor.field_editor(message, **kwargs)
|
await editor.field_editor(message, **kwargs)
|
||||||
|
elif context.command == CallbackCommand.USER_COMMAND:
|
||||||
|
import qbot.bot.handlers.user_handlers.main as user_handler
|
||||||
|
|
||||||
|
await user_handler.cammand_handler(
|
||||||
|
message, from_callback_query=True, **kwargs
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unknown command {context.command}")
|
raise ValueError(f"Unknown command {context.command}")
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ class CommandContext(StrEnum):
|
|||||||
ENTITY_CREATE = "ec"
|
ENTITY_CREATE = "ec"
|
||||||
ENTITY_EDIT = "ee"
|
ENTITY_EDIT = "ee"
|
||||||
ENTITY_FIELD_EDIT = "ef"
|
ENTITY_FIELD_EDIT = "ef"
|
||||||
|
COMMAND_FORM = "cf"
|
||||||
|
|
||||||
|
|
||||||
class ContextData(BaseCallbackData, prefix="cd"):
|
class ContextData(BaseCallbackData, prefix="cd"):
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
|
|||||||
from babel.support import LazyProxy
|
from babel.support import LazyProxy
|
||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
|
|
||||||
from ....model.descriptors import EntityFieldDescriptor
|
from ....model.descriptors import FieldDescriptor
|
||||||
from ..context import ContextData, CallbackCommand
|
from ..context import ContextData, CallbackCommand
|
||||||
from ....utils.main import get_send_message
|
from ....utils.main import get_send_message
|
||||||
from .wrapper import wrap_editor
|
from .wrapper import wrap_editor
|
||||||
@@ -18,7 +18,7 @@ router = Router()
|
|||||||
async def bool_editor(
|
async def bool_editor(
|
||||||
message: Message | CallbackQuery,
|
message: Message | CallbackQuery,
|
||||||
edit_prompt: str,
|
edit_prompt: str,
|
||||||
field_descriptor: EntityFieldDescriptor,
|
field_descriptor: FieldDescriptor,
|
||||||
callback_data: ContextData,
|
callback_data: ContextData,
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
@@ -44,6 +44,7 @@ async def bool_editor(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=str(True),
|
data=str(True),
|
||||||
).pack(),
|
).pack(),
|
||||||
),
|
),
|
||||||
@@ -56,6 +57,7 @@ async def bool_editor(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=str(False),
|
data=str(False),
|
||||||
).pack(),
|
).pack(),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ from datetime import datetime, time
|
|||||||
|
|
||||||
from ....model.bot_entity import BotEntity
|
from ....model.bot_entity import BotEntity
|
||||||
from ....model.bot_enum import BotEnum
|
from ....model.bot_enum import BotEnum
|
||||||
from ....model.descriptors import EntityFieldDescriptor
|
from ....model.descriptors import FieldDescriptor
|
||||||
from ....model.settings import Settings
|
from ....model.settings import Settings
|
||||||
from ....model.user import UserBase
|
from ....model.user import UserBase
|
||||||
from ....utils.main import get_callable_str, get_value_repr
|
from ....utils.main import get_callable_str, get_value_repr
|
||||||
@@ -16,7 +16,7 @@ from .string import string_editor
|
|||||||
|
|
||||||
|
|
||||||
async def show_editor(message: Message | CallbackQuery, **kwargs):
|
async def show_editor(message: Message | CallbackQuery, **kwargs):
|
||||||
field_descriptor: EntityFieldDescriptor = kwargs["field_descriptor"]
|
field_descriptor: FieldDescriptor = kwargs["field_descriptor"]
|
||||||
current_value = kwargs["current_value"]
|
current_value = kwargs["current_value"]
|
||||||
user: UserBase = kwargs["user"]
|
user: UserBase = kwargs["user"]
|
||||||
callback_data: ContextData = kwargs.get("callback_data", None)
|
callback_data: ContextData = kwargs.get("callback_data", None)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
|
|||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
from ....model.descriptors import EntityFieldDescriptor
|
from ....model.descriptors import FieldDescriptor
|
||||||
from ....model.settings import Settings
|
from ....model.settings import Settings
|
||||||
from ..context import ContextData, CallbackCommand
|
from ..context import ContextData, CallbackCommand
|
||||||
from ....utils.main import get_send_message, get_field_descriptor
|
from ....utils.main import get_send_message, get_field_descriptor
|
||||||
@@ -45,7 +45,7 @@ async def time_picker_callback(
|
|||||||
|
|
||||||
async def time_picker(
|
async def time_picker(
|
||||||
message: Message | CallbackQuery,
|
message: Message | CallbackQuery,
|
||||||
field_descriptor: EntityFieldDescriptor,
|
field_descriptor: FieldDescriptor,
|
||||||
callback_data: ContextData,
|
callback_data: ContextData,
|
||||||
current_value: datetime | time,
|
current_value: datetime | time,
|
||||||
state: FSMContext,
|
state: FSMContext,
|
||||||
@@ -70,6 +70,7 @@ async def time_picker(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=current_value.replace(
|
data=current_value.replace(
|
||||||
hour=i if current_value.hour < 12 else i + 12
|
hour=i if current_value.hour < 12 else i + 12
|
||||||
).strftime(
|
).strftime(
|
||||||
@@ -92,6 +93,7 @@ async def time_picker(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=current_value.replace(minute=i * 5).strftime(
|
data=current_value.replace(minute=i * 5).strftime(
|
||||||
"%Y-%m-%d %H-%M"
|
"%Y-%m-%d %H-%M"
|
||||||
if isinstance(current_value, datetime)
|
if isinstance(current_value, datetime)
|
||||||
@@ -112,6 +114,7 @@ async def time_picker(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=current_value.replace(
|
data=current_value.replace(
|
||||||
hour=current_value.hour + 12
|
hour=current_value.hour + 12
|
||||||
if current_value.hour < 12
|
if current_value.hour < 12
|
||||||
@@ -130,6 +133,7 @@ async def time_picker(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=current_value.strftime(
|
data=current_value.strftime(
|
||||||
"%Y-%m-%d %H-%M" if isinstance(current_value, datetime) else "%H-%M"
|
"%Y-%m-%d %H-%M" if isinstance(current_value, datetime) else "%H-%M"
|
||||||
),
|
),
|
||||||
@@ -157,7 +161,7 @@ async def time_picker(
|
|||||||
|
|
||||||
async def date_picker(
|
async def date_picker(
|
||||||
message: Message | CallbackQuery,
|
message: Message | CallbackQuery,
|
||||||
field_descriptor: EntityFieldDescriptor,
|
field_descriptor: FieldDescriptor,
|
||||||
callback_data: ContextData,
|
callback_data: ContextData,
|
||||||
current_value: datetime,
|
current_value: datetime,
|
||||||
state: FSMContext,
|
state: FSMContext,
|
||||||
@@ -185,6 +189,7 @@ async def date_picker(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=previous_month.strftime("%Y-%m-%d %H-%M"),
|
data=previous_month.strftime("%Y-%m-%d %H-%M"),
|
||||||
).pack(),
|
).pack(),
|
||||||
),
|
),
|
||||||
@@ -197,6 +202,7 @@ async def date_picker(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=start_date.strftime("%Y-%m-%d %H-%M"),
|
data=start_date.strftime("%Y-%m-%d %H-%M"),
|
||||||
).pack(),
|
).pack(),
|
||||||
),
|
),
|
||||||
@@ -209,6 +215,7 @@ async def date_picker(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=next_month.strftime("%Y-%m-%d %H-%M"),
|
data=next_month.strftime("%Y-%m-%d %H-%M"),
|
||||||
).pack(),
|
).pack(),
|
||||||
),
|
),
|
||||||
@@ -237,6 +244,7 @@ async def date_picker(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=current_day.strftime("%Y-%m-%d %H-%M"),
|
data=current_day.strftime("%Y-%m-%d %H-%M"),
|
||||||
).pack(),
|
).pack(),
|
||||||
)
|
)
|
||||||
@@ -288,6 +296,7 @@ async def date_picker_year(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=start_date.replace(year=start_date.year - 20).strftime(
|
data=start_date.replace(year=start_date.year - 20).strftime(
|
||||||
"%Y-%m-%d %H-%M"
|
"%Y-%m-%d %H-%M"
|
||||||
),
|
),
|
||||||
@@ -309,6 +318,7 @@ async def date_picker_year(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=current_date.strftime("%Y-%m-%d %H-%M"),
|
data=current_date.strftime("%Y-%m-%d %H-%M"),
|
||||||
).pack(),
|
).pack(),
|
||||||
)
|
)
|
||||||
@@ -326,6 +336,7 @@ async def date_picker_year(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=start_date.replace(year=start_date.year + 20).strftime(
|
data=start_date.replace(year=start_date.year + 20).strftime(
|
||||||
"%Y-%m-%d %H-%M"
|
"%Y-%m-%d %H-%M"
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ from ....model.bot_enum import BotEnum
|
|||||||
from ....model.settings import Settings
|
from ....model.settings import Settings
|
||||||
from ....model.user import UserBase
|
from ....model.user import UserBase
|
||||||
from ....model.view_setting import ViewSetting
|
from ....model.view_setting import ViewSetting
|
||||||
from ....model.descriptors import EntityFieldDescriptor, Filter
|
from ....model.descriptors import FieldDescriptor, Filter
|
||||||
from ....model import EntityPermission
|
from ....model import EntityPermission
|
||||||
from ....utils.main import (
|
from ....utils.main import (
|
||||||
get_user_permissions,
|
get_user_permissions,
|
||||||
@@ -37,7 +37,7 @@ router = Router()
|
|||||||
|
|
||||||
async def entity_picker(
|
async def entity_picker(
|
||||||
message: Message | CallbackQuery,
|
message: Message | CallbackQuery,
|
||||||
field_descriptor: EntityFieldDescriptor,
|
field_descriptor: FieldDescriptor,
|
||||||
edit_prompt: str,
|
edit_prompt: str,
|
||||||
current_value: BotEntity | BotEnum | list[BotEntity] | list[BotEnum],
|
current_value: BotEntity | BotEnum | list[BotEntity] | list[BotEnum],
|
||||||
**kwargs,
|
**kwargs,
|
||||||
@@ -67,7 +67,7 @@ def calc_total_pages(items_count: int, page_size: int) -> int:
|
|||||||
|
|
||||||
async def render_entity_picker(
|
async def render_entity_picker(
|
||||||
*,
|
*,
|
||||||
field_descriptor: EntityFieldDescriptor,
|
field_descriptor: FieldDescriptor,
|
||||||
message: Message | CallbackQuery,
|
message: Message | CallbackQuery,
|
||||||
callback_data: ContextData,
|
callback_data: ContextData,
|
||||||
user: UserBase,
|
user: UserBase,
|
||||||
@@ -239,6 +239,7 @@ async def render_entity_picker(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data=f"{page}&{item['value']}" if is_list else item["value"],
|
data=f"{page}&{item['value']}" if is_list else item["value"],
|
||||||
).pack(),
|
).pack(),
|
||||||
)
|
)
|
||||||
@@ -275,6 +276,7 @@ async def render_entity_picker(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
).pack(),
|
).pack(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from ....utils.main import (
|
|||||||
from ....utils.serialization import deserialize, serialize
|
from ....utils.serialization import deserialize, serialize
|
||||||
from ..context import ContextData, CallbackCommand, CommandContext
|
from ..context import ContextData, CallbackCommand, CommandContext
|
||||||
from ....auth import authorize_command
|
from ....auth import authorize_command
|
||||||
from ..navigation import (
|
from ....utils.navigation import (
|
||||||
get_navigation_context,
|
get_navigation_context,
|
||||||
save_navigation_context,
|
save_navigation_context,
|
||||||
)
|
)
|
||||||
@@ -37,14 +37,22 @@ router = Router()
|
|||||||
|
|
||||||
|
|
||||||
@router.callback_query(ContextData.filter(F.command == CallbackCommand.FIELD_EDITOR))
|
@router.callback_query(ContextData.filter(F.command == CallbackCommand.FIELD_EDITOR))
|
||||||
|
async def field_editor_callback(query: CallbackQuery, **kwargs):
|
||||||
|
state: FSMContext = kwargs["state"]
|
||||||
|
state_data = await state.get_data()
|
||||||
|
kwargs["state_data"] = state_data
|
||||||
|
|
||||||
|
await field_editor(message=query, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
async def field_editor(message: Message | CallbackQuery, **kwargs):
|
async def field_editor(message: Message | CallbackQuery, **kwargs):
|
||||||
callback_data: ContextData = kwargs.get("callback_data", None)
|
callback_data: ContextData = kwargs.get("callback_data", None)
|
||||||
db_session: AsyncSession = kwargs["db_session"]
|
db_session: AsyncSession = kwargs["db_session"]
|
||||||
user: UserBase = kwargs["user"]
|
user: UserBase = kwargs["user"]
|
||||||
app: "QBotApp" = kwargs["app"]
|
app: "QBotApp" = kwargs["app"]
|
||||||
state: FSMContext = kwargs["state"]
|
# state: FSMContext = kwargs["state"]
|
||||||
|
|
||||||
state_data = await state.get_data()
|
state_data: dict = kwargs["state_data"]
|
||||||
entity_data = state_data.get("entity_data")
|
entity_data = state_data.get("entity_data")
|
||||||
|
|
||||||
for key in ["current_value", "value", "locale_index"]:
|
for key in ["current_value", "value", "locale_index"]:
|
||||||
|
|||||||
@@ -8,10 +8,11 @@ import json
|
|||||||
|
|
||||||
from ..context import ContextData, CallbackCommand, CommandContext
|
from ..context import ContextData, CallbackCommand, CommandContext
|
||||||
from ...command_context_filter import CallbackCommandFilter
|
from ...command_context_filter import CallbackCommandFilter
|
||||||
|
from ..user_handlers.main import cammand_handler
|
||||||
from ....model import EntityPermission
|
from ....model import EntityPermission
|
||||||
from ....model.user import UserBase
|
from ....model.user import UserBase
|
||||||
from ....model.settings import Settings
|
from ....model.settings import Settings
|
||||||
from ....model.descriptors import EntityFieldDescriptor
|
from ....model.descriptors import FieldDescriptor
|
||||||
from ....model.language import LanguageBase
|
from ....model.language import LanguageBase
|
||||||
from ....auth import authorize_command
|
from ....auth import authorize_command
|
||||||
from ....utils.main import (
|
from ....utils.main import (
|
||||||
@@ -60,7 +61,9 @@ async def field_editor_callback(message: Message | CallbackQuery, **kwargs):
|
|||||||
else:
|
else:
|
||||||
value = {}
|
value = {}
|
||||||
|
|
||||||
value[list(LanguageBase.all_members.keys())[locale_index]] = message.text
|
value[list(LanguageBase.all_members.values())[locale_index].value] = (
|
||||||
|
message.text
|
||||||
|
)
|
||||||
value = json.dumps(value, ensure_ascii=False)
|
value = json.dumps(value, ensure_ascii=False)
|
||||||
|
|
||||||
if locale_index < len(LanguageBase.all_members.values()) - 1:
|
if locale_index < len(LanguageBase.all_members.values()) - 1:
|
||||||
@@ -125,7 +128,7 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
|
|||||||
callback_data: ContextData = kwargs.get("callback_data", None)
|
callback_data: ContextData = kwargs.get("callback_data", None)
|
||||||
state_data: dict = kwargs["state_data"]
|
state_data: dict = kwargs["state_data"]
|
||||||
value = kwargs["value"]
|
value = kwargs["value"]
|
||||||
field_descriptor: EntityFieldDescriptor = kwargs["field_descriptor"]
|
field_descriptor: FieldDescriptor = kwargs["field_descriptor"]
|
||||||
|
|
||||||
if callback_data.context == CommandContext.SETTING_EDIT:
|
if callback_data.context == CommandContext.SETTING_EDIT:
|
||||||
if callback_data.data != "cancel":
|
if callback_data.data != "cancel":
|
||||||
@@ -145,29 +148,37 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
|
|||||||
CommandContext.ENTITY_CREATE,
|
CommandContext.ENTITY_CREATE,
|
||||||
CommandContext.ENTITY_EDIT,
|
CommandContext.ENTITY_EDIT,
|
||||||
CommandContext.ENTITY_FIELD_EDIT,
|
CommandContext.ENTITY_FIELD_EDIT,
|
||||||
|
CommandContext.COMMAND_FORM,
|
||||||
]:
|
]:
|
||||||
app: "QBotApp" = kwargs["app"]
|
app: "QBotApp" = kwargs["app"]
|
||||||
|
|
||||||
entity_descriptor = get_entity_descriptor(app, callback_data)
|
entity_descriptor = get_entity_descriptor(app, callback_data)
|
||||||
|
|
||||||
form_name = (
|
if callback_data.context == CommandContext.COMMAND_FORM:
|
||||||
callback_data.form_params.split("&")[0]
|
field_sequence = list(field_descriptor.command.param_form.keys())
|
||||||
if callback_data.form_params
|
current_index = field_sequence.index(callback_data.field_name)
|
||||||
else "default"
|
field_descriptors = field_descriptor.command.param_form
|
||||||
)
|
else:
|
||||||
form = entity_descriptor.forms.get(form_name, entity_descriptor.default_form)
|
form_name = (
|
||||||
|
callback_data.form_params.split("&")[0]
|
||||||
|
if callback_data.form_params
|
||||||
|
else "default"
|
||||||
|
)
|
||||||
|
form = entity_descriptor.forms.get(
|
||||||
|
form_name, entity_descriptor.default_form
|
||||||
|
)
|
||||||
|
|
||||||
field_sequence = form.edit_field_sequence
|
field_sequence = form.edit_field_sequence
|
||||||
current_index = (
|
current_index = (
|
||||||
field_sequence.index(callback_data.field_name)
|
field_sequence.index(callback_data.field_name)
|
||||||
if callback_data.context
|
if callback_data.context
|
||||||
in [CommandContext.ENTITY_CREATE, CommandContext.ENTITY_EDIT]
|
in [CommandContext.ENTITY_CREATE, CommandContext.ENTITY_EDIT]
|
||||||
else 0
|
else 0
|
||||||
)
|
)
|
||||||
|
field_descriptors = entity_descriptor.fields_descriptors
|
||||||
|
|
||||||
entity_data = state_data.get("entity_data", {})
|
entity_data = state_data.get("entity_data", {})
|
||||||
|
|
||||||
if callback_data.context == CommandContext.ENTITY_CREATE:
|
if callback_data.context == CommandContext.ENTITY_CREATE and not entity_data:
|
||||||
stack = state_data.get("navigation_stack", [])
|
stack = state_data.get("navigation_stack", [])
|
||||||
prev_callback_data = ContextData.unpack(stack[-1]) if stack else None
|
prev_callback_data = ContextData.unpack(stack[-1]) if stack else None
|
||||||
if (
|
if (
|
||||||
@@ -199,16 +210,18 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
callback_data.context
|
callback_data.context
|
||||||
in [CommandContext.ENTITY_CREATE, CommandContext.ENTITY_EDIT]
|
in [
|
||||||
|
CommandContext.ENTITY_CREATE,
|
||||||
|
CommandContext.ENTITY_EDIT,
|
||||||
|
CommandContext.COMMAND_FORM,
|
||||||
|
]
|
||||||
and current_index < len(field_sequence) - 1
|
and current_index < len(field_sequence) - 1
|
||||||
):
|
):
|
||||||
entity_data[field_descriptor.field_name] = value
|
entity_data[field_descriptor.field_name] = value
|
||||||
state_data.update({"entity_data": entity_data})
|
state_data.update({"entity_data": entity_data})
|
||||||
|
|
||||||
next_field_name = field_sequence[current_index + 1]
|
next_field_name = field_sequence[current_index + 1]
|
||||||
next_field_descriptor = entity_descriptor.fields_descriptors[
|
next_field_descriptor = field_descriptors[next_field_name]
|
||||||
next_field_name
|
|
||||||
]
|
|
||||||
kwargs.update({"field_descriptor": next_field_descriptor})
|
kwargs.update({"field_descriptor": next_field_descriptor})
|
||||||
callback_data.field_name = next_field_name
|
callback_data.field_name = next_field_name
|
||||||
|
|
||||||
@@ -232,8 +245,6 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
|
|||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
entity_type = entity_descriptor.type_
|
|
||||||
|
|
||||||
entity_data[field_descriptor.field_name] = value
|
entity_data[field_descriptor.field_name] = value
|
||||||
|
|
||||||
# What if user has several roles and each role has its own ownership field? Should we allow creation even
|
# What if user has several roles and each role has its own ownership field? Should we allow creation even
|
||||||
@@ -246,13 +257,14 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
|
|||||||
deser_entity_data = {
|
deser_entity_data = {
|
||||||
key: await deserialize(
|
key: await deserialize(
|
||||||
session=db_session,
|
session=db_session,
|
||||||
type_=entity_descriptor.fields_descriptors[key].type_,
|
type_=field_descriptors[key].type_,
|
||||||
value=value,
|
value=value,
|
||||||
)
|
)
|
||||||
for key, value in entity_data.items()
|
for key, value in entity_data.items()
|
||||||
}
|
}
|
||||||
|
|
||||||
if callback_data.context == CommandContext.ENTITY_CREATE:
|
if callback_data.context == CommandContext.ENTITY_CREATE:
|
||||||
|
entity_type = entity_descriptor.type_
|
||||||
user_permissions = get_user_permissions(user, entity_descriptor)
|
user_permissions = get_user_permissions(user, entity_descriptor)
|
||||||
if (
|
if (
|
||||||
EntityPermission.CREATE not in user_permissions
|
EntityPermission.CREATE not in user_permissions
|
||||||
@@ -290,6 +302,7 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
|
|||||||
CommandContext.ENTITY_EDIT,
|
CommandContext.ENTITY_EDIT,
|
||||||
CommandContext.ENTITY_FIELD_EDIT,
|
CommandContext.ENTITY_FIELD_EDIT,
|
||||||
]:
|
]:
|
||||||
|
entity_type = entity_descriptor.type_
|
||||||
entity_id = int(callback_data.entity_id)
|
entity_id = int(callback_data.entity_id)
|
||||||
entity = await entity_type.get(session=db_session, id=entity_id)
|
entity = await entity_type.get(session=db_session, id=entity_id)
|
||||||
if not entity:
|
if not entity:
|
||||||
@@ -309,6 +322,23 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
|
|||||||
|
|
||||||
await db_session.commit()
|
await db_session.commit()
|
||||||
|
|
||||||
|
elif callback_data.context == CommandContext.COMMAND_FORM:
|
||||||
|
clear_state(state_data=state_data)
|
||||||
|
state_data["entity_data"] = entity_data
|
||||||
|
|
||||||
|
kwargs.update(
|
||||||
|
{
|
||||||
|
"callback_data": ContextData(
|
||||||
|
command=CallbackCommand.USER_COMMAND,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
|
data=callback_data.data,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return await cammand_handler(message=message, **kwargs)
|
||||||
|
|
||||||
clear_state(state_data=state_data)
|
clear_state(state_data=state_data)
|
||||||
|
|
||||||
|
# TODO: Try back=False and check if it works to navigate to newly created entity
|
||||||
await route_callback(message=message, back=True, **kwargs)
|
await route_callback(message=message, back=True, **kwargs)
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
|
|||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from ....model.descriptors import EntityFieldDescriptor
|
from ....model.descriptors import FieldDescriptor
|
||||||
from ....model.language import LanguageBase
|
from ....model.language import LanguageBase
|
||||||
from ....model.settings import Settings
|
from ....model.settings import Settings
|
||||||
from ....utils.main import get_send_message, get_local_text
|
from ....utils.main import get_send_message, get_local_text
|
||||||
@@ -20,7 +20,7 @@ router = Router()
|
|||||||
|
|
||||||
async def string_editor(
|
async def string_editor(
|
||||||
message: Message | CallbackQuery,
|
message: Message | CallbackQuery,
|
||||||
field_descriptor: EntityFieldDescriptor,
|
field_descriptor: FieldDescriptor,
|
||||||
callback_data: ContextData,
|
callback_data: ContextData,
|
||||||
current_value: Any,
|
current_value: Any,
|
||||||
edit_prompt: str,
|
edit_prompt: str,
|
||||||
@@ -41,6 +41,7 @@ async def string_editor(
|
|||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
)
|
)
|
||||||
|
|
||||||
if field_descriptor.type_base is str and field_descriptor.localizable:
|
if field_descriptor.type_base is str and field_descriptor.localizable:
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ from aiogram.types import InlineKeyboardButton
|
|||||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||||
|
|
||||||
from ....model.settings import Settings
|
from ....model.settings import Settings
|
||||||
from ....model.descriptors import EntityFieldDescriptor
|
from ....model.descriptors import FieldDescriptor
|
||||||
from ..context import ContextData, CallbackCommand, CommandContext
|
from ..context import ContextData, CallbackCommand, CommandContext
|
||||||
from ..navigation import get_navigation_context, pop_navigation_context
|
from ....utils.navigation import get_navigation_context, pop_navigation_context
|
||||||
|
|
||||||
|
|
||||||
async def wrap_editor(
|
async def wrap_editor(
|
||||||
keyboard_builder: InlineKeyboardBuilder,
|
keyboard_builder: InlineKeyboardBuilder,
|
||||||
field_descriptor: EntityFieldDescriptor,
|
field_descriptor: FieldDescriptor,
|
||||||
callback_data: ContextData,
|
callback_data: ContextData,
|
||||||
state_data: dict,
|
state_data: dict,
|
||||||
):
|
):
|
||||||
@@ -17,28 +17,37 @@ async def wrap_editor(
|
|||||||
CommandContext.ENTITY_CREATE,
|
CommandContext.ENTITY_CREATE,
|
||||||
CommandContext.ENTITY_EDIT,
|
CommandContext.ENTITY_EDIT,
|
||||||
CommandContext.ENTITY_FIELD_EDIT,
|
CommandContext.ENTITY_FIELD_EDIT,
|
||||||
|
CommandContext.COMMAND_FORM,
|
||||||
]:
|
]:
|
||||||
form_name = (
|
|
||||||
callback_data.form_params.split("&")[0]
|
|
||||||
if callback_data.form_params
|
|
||||||
else "default"
|
|
||||||
)
|
|
||||||
form = field_descriptor.entity_descriptor.forms.get(
|
|
||||||
form_name, field_descriptor.entity_descriptor.default_form
|
|
||||||
)
|
|
||||||
|
|
||||||
btns = []
|
btns = []
|
||||||
field_index = (
|
show_back = True
|
||||||
form.edit_field_sequence.index(field_descriptor.name)
|
show_cancel = True
|
||||||
if callback_data.context
|
if callback_data.context == CommandContext.COMMAND_FORM:
|
||||||
in [CommandContext.ENTITY_CREATE, CommandContext.ENTITY_EDIT]
|
field_sequence = list(field_descriptor.command.param_form.keys())
|
||||||
else 0
|
field_index = field_sequence.index(callback_data.field_name)
|
||||||
)
|
show_back = field_descriptor.command.show_back_in_param_form
|
||||||
|
show_cancel = field_descriptor.command.show_cancel_in_param_form
|
||||||
|
else:
|
||||||
|
form_name = (
|
||||||
|
callback_data.form_params.split("&")[0]
|
||||||
|
if callback_data.form_params
|
||||||
|
else "default"
|
||||||
|
)
|
||||||
|
form = field_descriptor.entity_descriptor.forms.get(
|
||||||
|
form_name, field_descriptor.entity_descriptor.default_form
|
||||||
|
)
|
||||||
|
field_sequence = form.edit_field_sequence
|
||||||
|
field_index = (
|
||||||
|
field_sequence.index(field_descriptor.name)
|
||||||
|
if callback_data.context
|
||||||
|
in [CommandContext.ENTITY_CREATE, CommandContext.ENTITY_EDIT]
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
|
||||||
stack, context = get_navigation_context(state_data=state_data)
|
stack, context = get_navigation_context(state_data=state_data)
|
||||||
context = pop_navigation_context(stack)
|
context = pop_navigation_context(stack)
|
||||||
|
|
||||||
if field_index > 0:
|
if field_index > 0 and show_back:
|
||||||
btns.append(
|
btns.append(
|
||||||
InlineKeyboardButton(
|
InlineKeyboardButton(
|
||||||
text=(await Settings.get(Settings.APP_STRINGS_BACK_BTN)),
|
text=(await Settings.get(Settings.APP_STRINGS_BACK_BTN)),
|
||||||
@@ -48,7 +57,8 @@ async def wrap_editor(
|
|||||||
entity_name=callback_data.entity_name,
|
entity_name=callback_data.entity_name,
|
||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
field_name=form.edit_field_sequence[field_index - 1],
|
user_command=callback_data.user_command,
|
||||||
|
field_name=field_sequence[field_index - 1],
|
||||||
).pack(),
|
).pack(),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -62,8 +72,9 @@ async def wrap_editor(
|
|||||||
context=callback_data.context,
|
context=callback_data.context,
|
||||||
entity_name=callback_data.entity_name,
|
entity_name=callback_data.entity_name,
|
||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
form_params=callback_data.form_params,
|
|
||||||
field_name=callback_data.field_name,
|
field_name=callback_data.field_name,
|
||||||
|
form_params=callback_data.form_params,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
data="skip",
|
data="skip",
|
||||||
).pack(),
|
).pack(),
|
||||||
)
|
)
|
||||||
@@ -71,12 +82,13 @@ async def wrap_editor(
|
|||||||
|
|
||||||
keyboard_builder.row(*btns)
|
keyboard_builder.row(*btns)
|
||||||
|
|
||||||
keyboard_builder.row(
|
if show_cancel:
|
||||||
InlineKeyboardButton(
|
keyboard_builder.row(
|
||||||
text=(await Settings.get(Settings.APP_STRINGS_CANCEL_BTN)),
|
InlineKeyboardButton(
|
||||||
callback_data=context.pack(),
|
text=(await Settings.get(Settings.APP_STRINGS_CANCEL_BTN)),
|
||||||
|
callback_data=context.pack(),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
|
|
||||||
elif callback_data.context == CommandContext.SETTING_EDIT:
|
elif callback_data.context == CommandContext.SETTING_EDIT:
|
||||||
keyboard_builder.row(
|
keyboard_builder.row(
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ from ....utils.main import (
|
|||||||
get_entity_descriptor,
|
get_entity_descriptor,
|
||||||
)
|
)
|
||||||
from ..context import ContextData, CallbackCommand, CommandContext
|
from ..context import ContextData, CallbackCommand, CommandContext
|
||||||
from ..navigation import (
|
from ....utils.navigation import (
|
||||||
pop_navigation_context,
|
pop_navigation_context,
|
||||||
save_navigation_context,
|
save_navigation_context,
|
||||||
)
|
)
|
||||||
@@ -84,94 +84,89 @@ async def entity_item(
|
|||||||
callback_data.form_params or "default", entity_descriptor.default_form
|
callback_data.form_params or "default", entity_descriptor.default_form
|
||||||
)
|
)
|
||||||
|
|
||||||
for edit_buttons_row in form.form_buttons:
|
if form.form_buttons:
|
||||||
btn_row = []
|
for edit_buttons_row in form.form_buttons:
|
||||||
for button in edit_buttons_row:
|
btn_row = []
|
||||||
|
for button in edit_buttons_row:
|
||||||
|
if button.visibility and not button.visibility(entity_item):
|
||||||
|
continue
|
||||||
|
|
||||||
if button.visibility and not button.visibility(entity_item):
|
if isinstance(button, FieldEditButton) and can_edit:
|
||||||
continue
|
field_name = button.field_name
|
||||||
|
btn_caption = button.caption
|
||||||
if isinstance(button, FieldEditButton) and can_edit:
|
if field_name in entity_descriptor.fields_descriptors:
|
||||||
|
field_descriptor = entity_descriptor.fields_descriptors[
|
||||||
field_name = button.field_name
|
field_name
|
||||||
btn_caption = button.caption
|
]
|
||||||
if field_name in entity_descriptor.fields_descriptors:
|
field_value = getattr(entity_item, field_descriptor.field_name)
|
||||||
field_descriptor = entity_descriptor.fields_descriptors[
|
if btn_caption:
|
||||||
field_name
|
btn_text = get_callable_str(
|
||||||
]
|
btn_caption, field_descriptor, entity_item, field_value
|
||||||
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:
|
else:
|
||||||
btn_text = (
|
if field_descriptor.type_base is bool:
|
||||||
f"✏️ {
|
btn_text = f"{'【✔︎】 ' if field_value else '【 】 '}{
|
||||||
get_callable_str(
|
get_callable_str(
|
||||||
field_descriptor.caption,
|
field_descriptor.caption,
|
||||||
field_descriptor,
|
field_descriptor,
|
||||||
entity_item,
|
entity_item,
|
||||||
field_value,
|
field_value,
|
||||||
)
|
)
|
||||||
|
if field_descriptor.caption
|
||||||
|
else field_name
|
||||||
}"
|
}"
|
||||||
if field_descriptor.caption
|
else:
|
||||||
else f"✏️ {field_name}"
|
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(),
|
||||||
)
|
)
|
||||||
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):
|
elif isinstance(button, CommandButton):
|
||||||
|
btn_caption = button.caption
|
||||||
|
|
||||||
btn_caption = button.caption
|
|
||||||
if btn_caption:
|
|
||||||
btn_text = get_callable_str(
|
btn_text = get_callable_str(
|
||||||
btn_caption, entity_descriptor, entity_item
|
btn_caption, entity_descriptor, entity_item
|
||||||
)
|
)
|
||||||
else:
|
|
||||||
btn_text = button.command
|
|
||||||
|
|
||||||
if isinstance(button.context_data, ContextData):
|
if isinstance(button.command, ContextData):
|
||||||
btn_cdata = button.context_data
|
btn_cdata = button.command
|
||||||
elif callable(button.context_data):
|
elif callable(button.command):
|
||||||
btn_cdata = button.context_data(callback_data, entity_item)
|
btn_cdata = button.command(callback_data, entity_item)
|
||||||
else:
|
elif isinstance(button.command, str):
|
||||||
btn_cdata = ContextData(
|
btn_cdata = ContextData(
|
||||||
command=CallbackCommand.USER_COMMAND,
|
command=CallbackCommand.USER_COMMAND,
|
||||||
user_command=button.command,
|
user_command=button.command,
|
||||||
data=str(entity_item.id),
|
)
|
||||||
|
|
||||||
|
btn_row.append(
|
||||||
|
InlineKeyboardButton(
|
||||||
|
text=btn_text,
|
||||||
|
callback_data=btn_cdata.pack(),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
btn_row.append(
|
if btn_row:
|
||||||
InlineKeyboardButton(
|
keyboard_builder.row(*btn_row)
|
||||||
text=btn_text,
|
|
||||||
callback_data=btn_cdata.pack(),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
if btn_row:
|
|
||||||
keyboard_builder.row(*btn_row)
|
|
||||||
|
|
||||||
edit_delete_row = []
|
edit_delete_row = []
|
||||||
if can_edit and form.show_edit_button:
|
if can_edit and form.show_edit_button:
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
from aiogram import Router, F
|
from aiogram import Router, F
|
||||||
|
from aiogram.fsm.context import FSMContext
|
||||||
from aiogram.types import CallbackQuery, InlineKeyboardButton
|
from aiogram.types import CallbackQuery, InlineKeyboardButton
|
||||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||||
@@ -10,7 +11,7 @@ from ....model.settings import Settings
|
|||||||
from ....model import EntityPermission
|
from ....model import EntityPermission
|
||||||
from ....utils.main import (
|
from ....utils.main import (
|
||||||
check_entity_permission,
|
check_entity_permission,
|
||||||
get_value_repr,
|
get_entity_item_repr,
|
||||||
get_entity_descriptor,
|
get_entity_descriptor,
|
||||||
)
|
)
|
||||||
from ..common.routing import route_callback
|
from ..common.routing import route_callback
|
||||||
@@ -28,6 +29,9 @@ async def entity_delete_callback(query: CallbackQuery, **kwargs):
|
|||||||
user: UserBase = kwargs["user"]
|
user: UserBase = kwargs["user"]
|
||||||
db_session: AsyncSession = kwargs["db_session"]
|
db_session: AsyncSession = kwargs["db_session"]
|
||||||
app: "QBotApp" = kwargs["app"]
|
app: "QBotApp" = kwargs["app"]
|
||||||
|
state: FSMContext = kwargs["state"]
|
||||||
|
state_data = await state.get_data()
|
||||||
|
kwargs["state_data"] = state_data
|
||||||
|
|
||||||
entity_descriptor = get_entity_descriptor(app=app, callback_data=callback_data)
|
entity_descriptor = get_entity_descriptor(app=app, callback_data=callback_data)
|
||||||
|
|
||||||
@@ -49,12 +53,7 @@ async def entity_delete_callback(query: CallbackQuery, **kwargs):
|
|||||||
|
|
||||||
await route_callback(message=query, **kwargs)
|
await route_callback(message=query, **kwargs)
|
||||||
|
|
||||||
elif callback_data.data == "no":
|
|
||||||
await route_callback(message=query, back=False, **kwargs)
|
|
||||||
|
|
||||||
elif not callback_data.data:
|
elif not callback_data.data:
|
||||||
field_descriptor = entity_descriptor.fields_descriptors["name"]
|
|
||||||
|
|
||||||
entity = await entity_descriptor.type_.get(
|
entity = await entity_descriptor.type_.get(
|
||||||
session=db_session, id=int(callback_data.entity_id)
|
session=db_session, id=int(callback_data.entity_id)
|
||||||
)
|
)
|
||||||
@@ -62,13 +61,7 @@ async def entity_delete_callback(query: CallbackQuery, **kwargs):
|
|||||||
return await query.message.edit_text(
|
return await query.message.edit_text(
|
||||||
text=(
|
text=(
|
||||||
await Settings.get(Settings.APP_STRINGS_CONFIRM_DELETE_P_NAME)
|
await Settings.get(Settings.APP_STRINGS_CONFIRM_DELETE_P_NAME)
|
||||||
).format(
|
).format(name=get_entity_item_repr(entity=entity)),
|
||||||
name=get_value_repr(
|
|
||||||
value=getattr(entity, field_descriptor.name),
|
|
||||||
field_descriptor=field_descriptor,
|
|
||||||
locale=user.lang,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
reply_markup=InlineKeyboardBuilder()
|
reply_markup=InlineKeyboardBuilder()
|
||||||
.row(
|
.row(
|
||||||
InlineKeyboardButton(
|
InlineKeyboardButton(
|
||||||
@@ -88,7 +81,6 @@ async def entity_delete_callback(query: CallbackQuery, **kwargs):
|
|||||||
entity_name=callback_data.entity_name,
|
entity_name=callback_data.entity_name,
|
||||||
entity_id=callback_data.entity_id,
|
entity_id=callback_data.entity_id,
|
||||||
form_params=callback_data.form_params,
|
form_params=callback_data.form_params,
|
||||||
data="no",
|
|
||||||
).pack(),
|
).pack(),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ from ....utils.serialization import deserialize
|
|||||||
from ..context import ContextData, CallbackCommand, CommandContext
|
from ..context import ContextData, CallbackCommand, CommandContext
|
||||||
from ..common.pagination import add_pagination_controls
|
from ..common.pagination import add_pagination_controls
|
||||||
from ..common.filtering import add_filter_controls
|
from ..common.filtering import add_filter_controls
|
||||||
from ..navigation import pop_navigation_context, save_navigation_context
|
from ....utils.navigation import pop_navigation_context, save_navigation_context
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ....main import QBotApp
|
from ....main import QBotApp
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from ....model.settings import Settings
|
|||||||
from ..context import ContextData, CallbackCommand
|
from ..context import ContextData, CallbackCommand
|
||||||
from ....utils.main import get_send_message
|
from ....utils.main import get_send_message
|
||||||
from ....model.descriptors import EntityCaptionCallable
|
from ....model.descriptors import EntityCaptionCallable
|
||||||
from ..navigation import save_navigation_context, pop_navigation_context
|
from ....utils.navigation import save_navigation_context, pop_navigation_context
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from ....main import QBotApp
|
from ....main import QBotApp
|
||||||
@@ -45,9 +45,7 @@ async def entities_menu(
|
|||||||
entity_metadata = app.entity_metadata
|
entity_metadata = app.entity_metadata
|
||||||
|
|
||||||
for entity in entity_metadata.entity_descriptors.values():
|
for entity in entity_metadata.entity_descriptors.values():
|
||||||
|
|
||||||
if entity.show_in_entities_menu:
|
if entity.show_in_entities_menu:
|
||||||
|
|
||||||
if entity.full_name_plural.__class__ == EntityCaptionCallable:
|
if entity.full_name_plural.__class__ == EntityCaptionCallable:
|
||||||
caption = entity.full_name_plural(entity) or entity.name
|
caption = entity.full_name_plural(entity) or entity.name
|
||||||
elif entity.full_name_plural.__class__ == LazyProxy:
|
elif entity.full_name_plural.__class__ == LazyProxy:
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ from aiogram.utils.i18n import I18n
|
|||||||
from logging import getLogger
|
from logging import getLogger
|
||||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||||
|
|
||||||
from ..navigation import pop_navigation_context, save_navigation_context
|
from ....utils.navigation import pop_navigation_context, save_navigation_context
|
||||||
from ....model.language import LanguageBase
|
from ....model.language import LanguageBase
|
||||||
from ....model.settings import Settings
|
from ....model.settings import Settings
|
||||||
from ....model.user import UserBase
|
from ....model.user import UserBase
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from logging import getLogger
|
|||||||
from ....model.settings import Settings
|
from ....model.settings import Settings
|
||||||
from ..context import ContextData, CallbackCommand
|
from ..context import ContextData, CallbackCommand
|
||||||
from ....utils.main import get_send_message
|
from ....utils.main import get_send_message
|
||||||
from ..navigation import save_navigation_context, pop_navigation_context
|
from ....utils.navigation import save_navigation_context, pop_navigation_context
|
||||||
|
|
||||||
import qbot.bot.handlers.menu.entities as entities
|
import qbot.bot.handlers.menu.entities as entities
|
||||||
import qbot.bot.handlers.menu.settings as settings
|
import qbot.bot.handlers.menu.settings as settings
|
||||||
@@ -17,7 +17,7 @@ import qbot.bot.handlers.forms.entity_list as entity_list
|
|||||||
import qbot.bot.handlers.forms.entity_form as entity_form
|
import qbot.bot.handlers.forms.entity_form as entity_form
|
||||||
import qbot.bot.handlers.forms.entity_form_callbacks as entity_form_callbacks
|
import qbot.bot.handlers.forms.entity_form_callbacks as entity_form_callbacks
|
||||||
import qbot.bot.handlers.common.filtering_callbacks as filtering_callbacks
|
import qbot.bot.handlers.common.filtering_callbacks as filtering_callbacks
|
||||||
import qbot.bot.handlers.user_handlers as user_handlers
|
import qbot.bot.handlers.user_handlers.main as user_handlers_main
|
||||||
|
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
@@ -26,7 +26,7 @@ router = Router()
|
|||||||
|
|
||||||
@router.callback_query(ContextData.filter(F.command == CallbackCommand.MENU_ENTRY_MAIN))
|
@router.callback_query(ContextData.filter(F.command == CallbackCommand.MENU_ENTRY_MAIN))
|
||||||
async def menu_entry_main(message: CallbackQuery, **kwargs):
|
async def menu_entry_main(message: CallbackQuery, **kwargs):
|
||||||
stack = await save_navigation_context(
|
stack = save_navigation_context(
|
||||||
callback_data=kwargs["callback_data"], state=kwargs["state"]
|
callback_data=kwargs["callback_data"], state=kwargs["state"]
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -84,5 +84,5 @@ router.include_routers(
|
|||||||
entity_form.router,
|
entity_form.router,
|
||||||
entity_form_callbacks.router,
|
entity_form_callbacks.router,
|
||||||
filtering_callbacks.router,
|
filtering_callbacks.router,
|
||||||
user_handlers.router,
|
user_handlers_main.router,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ from ....utils.main import (
|
|||||||
get_value_repr,
|
get_value_repr,
|
||||||
get_callable_str,
|
get_callable_str,
|
||||||
)
|
)
|
||||||
from ..navigation import save_navigation_context, pop_navigation_context
|
from ....utils.navigation import save_navigation_context, pop_navigation_context
|
||||||
from ....auth import authorize_command
|
from ....auth import authorize_command
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ from ....model.user import UserBase
|
|||||||
from ....utils.main import get_send_message
|
from ....utils.main import get_send_message
|
||||||
from ..context import ContextData, CallbackCommand
|
from ..context import ContextData, CallbackCommand
|
||||||
from ....auth import authorize_command
|
from ....auth import authorize_command
|
||||||
from ..navigation import save_navigation_context, pop_navigation_context
|
from ....utils.navigation import save_navigation_context, pop_navigation_context
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
router = Router()
|
router = Router()
|
||||||
|
|||||||
@@ -1,102 +0,0 @@
|
|||||||
from typing import TYPE_CHECKING
|
|
||||||
from aiogram import Router, F
|
|
||||||
from aiogram.types import Message, CallbackQuery, InlineKeyboardButton
|
|
||||||
from aiogram.fsm.context import FSMContext
|
|
||||||
from ..context import ContextData, CallbackCommand
|
|
||||||
from ....model.settings import Settings
|
|
||||||
from ....utils.main import get_send_message, clear_state
|
|
||||||
from ....model.descriptors import CommandCallbackContext
|
|
||||||
from ..navigation import (
|
|
||||||
save_navigation_context,
|
|
||||||
get_navigation_context,
|
|
||||||
pop_navigation_context,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
|
||||||
from ....main import QBotApp
|
|
||||||
|
|
||||||
|
|
||||||
router = Router()
|
|
||||||
|
|
||||||
|
|
||||||
@router.message(F.text.startswith("/"))
|
|
||||||
async def command_text(message: Message, **kwargs):
|
|
||||||
str_command = message.text.lstrip("/")
|
|
||||||
callback_data = ContextData(
|
|
||||||
command=CallbackCommand.USER_COMMAND, user_command=str_command
|
|
||||||
)
|
|
||||||
|
|
||||||
await command_handler(message=message, callback_data=callback_data, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
@router.callback_query(ContextData.filter(F.command == CallbackCommand.USER_COMMAND))
|
|
||||||
async def command_callback(message: CallbackQuery, **kwargs):
|
|
||||||
await command_handler(message=message, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
async def command_handler(message: Message | CallbackQuery, **kwargs):
|
|
||||||
callback_data: ContextData = kwargs.pop("callback_data")
|
|
||||||
str_command = callback_data.user_command
|
|
||||||
app: "QBotApp" = kwargs.pop("app")
|
|
||||||
command = app.bot_commands.get(str_command)
|
|
||||||
|
|
||||||
if not command:
|
|
||||||
return
|
|
||||||
|
|
||||||
state: FSMContext = kwargs.pop("state")
|
|
||||||
state_data = await state.get_data()
|
|
||||||
|
|
||||||
if command.register_navigation:
|
|
||||||
clear_state(state_data=state_data)
|
|
||||||
|
|
||||||
if command.clear_navigation:
|
|
||||||
state_data.pop("navigation_stack", None)
|
|
||||||
state_data.pop("navigation_context", None)
|
|
||||||
|
|
||||||
if command.register_navigation:
|
|
||||||
stack = save_navigation_context(
|
|
||||||
callback_data=callback_data, state_data=state_data
|
|
||||||
)
|
|
||||||
|
|
||||||
callback_context = CommandCallbackContext[app.user_class](
|
|
||||||
message=message,
|
|
||||||
callback_data=callback_data,
|
|
||||||
db_session=kwargs.pop("db_session"),
|
|
||||||
user=kwargs.pop("user"),
|
|
||||||
app=app,
|
|
||||||
state_data=state_data,
|
|
||||||
state=state,
|
|
||||||
i18n=kwargs.pop("i18n"),
|
|
||||||
kwargs=kwargs,
|
|
||||||
)
|
|
||||||
|
|
||||||
await command.handler(callback_context)
|
|
||||||
|
|
||||||
await state.set_data(state_data)
|
|
||||||
|
|
||||||
if command.register_navigation:
|
|
||||||
stack, navigation_context = get_navigation_context(state_data=state_data)
|
|
||||||
back_callback_data = pop_navigation_context(stack=stack)
|
|
||||||
if back_callback_data:
|
|
||||||
callback_context.keyboard_builder.row(
|
|
||||||
InlineKeyboardButton(
|
|
||||||
text=(await Settings.get(Settings.APP_STRINGS_BACK_BTN)),
|
|
||||||
callback_data=back_callback_data.pack(),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
send_message = get_send_message(message)
|
|
||||||
|
|
||||||
if isinstance(message, CallbackCommand):
|
|
||||||
message = message.message
|
|
||||||
|
|
||||||
if callback_context.message_text:
|
|
||||||
await send_message(
|
|
||||||
text=callback_context.message_text,
|
|
||||||
reply_markup=callback_context.keyboard_builder.as_markup(),
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
await message.edit_reply_markup(
|
|
||||||
reply_markup=callback_context.keyboard_builder.as_markup()
|
|
||||||
)
|
|
||||||
150
bot/handlers/user_handlers/main.py
Normal file
150
bot/handlers/user_handlers/main.py
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
from aiogram import Router, F
|
||||||
|
from aiogram.fsm.context import FSMContext
|
||||||
|
from aiogram.types import Message, CallbackQuery, InlineKeyboardButton
|
||||||
|
from inspect import iscoroutinefunction
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
from qbot.utils.main import clear_state
|
||||||
|
from qbot.utils.navigation import (
|
||||||
|
save_navigation_context,
|
||||||
|
get_navigation_context,
|
||||||
|
pop_navigation_context,
|
||||||
|
)
|
||||||
|
from qbot.bot.handlers.editors.main import field_editor
|
||||||
|
from qbot.bot.handlers.common.routing import route_callback
|
||||||
|
from qbot.utils.serialization import deserialize
|
||||||
|
from qbot.utils.main import get_send_message
|
||||||
|
from qbot.model.descriptors import CommandCallbackContext
|
||||||
|
from qbot.model.settings import Settings
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from qbot.main import QBotApp
|
||||||
|
|
||||||
|
from ..context import ContextData, CallbackCommand, CommandContext
|
||||||
|
|
||||||
|
|
||||||
|
router = Router()
|
||||||
|
|
||||||
|
|
||||||
|
@router.message(F.text.startswith("/"))
|
||||||
|
async def command_text(message: Message, **kwargs):
|
||||||
|
str_command = message.text.lstrip("/")
|
||||||
|
callback_data = ContextData(
|
||||||
|
command=CallbackCommand.USER_COMMAND, user_command=str_command
|
||||||
|
)
|
||||||
|
|
||||||
|
state: FSMContext = kwargs["state"]
|
||||||
|
state_data = await state.get_data()
|
||||||
|
kwargs["state_data"] = state_data
|
||||||
|
|
||||||
|
await cammand_handler(message=message, callback_data=callback_data, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
@router.callback_query(ContextData.filter(F.command == CallbackCommand.USER_COMMAND))
|
||||||
|
async def command_callback(message: CallbackQuery, **kwargs):
|
||||||
|
state: FSMContext = kwargs["state"]
|
||||||
|
state_data = await state.get_data()
|
||||||
|
kwargs["state_data"] = state_data
|
||||||
|
|
||||||
|
await cammand_handler(message=message, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
async def cammand_handler(message: Message | CallbackQuery, **kwargs):
|
||||||
|
callback_data: ContextData = kwargs["callback_data"]
|
||||||
|
state: FSMContext = kwargs["state"]
|
||||||
|
state_data: dict = kwargs["state_data"]
|
||||||
|
app: "QBotApp" = kwargs["app"]
|
||||||
|
cmd = app.bot_commands.get(callback_data.user_command.split("&")[0])
|
||||||
|
|
||||||
|
if cmd is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
entity_data_dict: dict = state_data.get("entity_data")
|
||||||
|
form_data = (
|
||||||
|
{
|
||||||
|
key: await deserialize(
|
||||||
|
session=kwargs["db_session"],
|
||||||
|
type_=cmd.param_form[key].type_,
|
||||||
|
value=value,
|
||||||
|
)
|
||||||
|
for key, value in entity_data_dict.items()
|
||||||
|
}
|
||||||
|
if entity_data_dict and cmd.param_form
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
|
||||||
|
callback_context = CommandCallbackContext(
|
||||||
|
message=message,
|
||||||
|
callback_data=callback_data,
|
||||||
|
form_data=form_data,
|
||||||
|
db_session=kwargs["db_session"],
|
||||||
|
user=kwargs["user"],
|
||||||
|
app=app,
|
||||||
|
state_data=state_data,
|
||||||
|
state=state,
|
||||||
|
i18n=kwargs["i18n"],
|
||||||
|
register_navigation=cmd.register_navigation,
|
||||||
|
kwargs=kwargs,
|
||||||
|
)
|
||||||
|
|
||||||
|
if cmd.pre_check and (not cmd.param_form or (cmd.param_form and form_data is None)):
|
||||||
|
if iscoroutinefunction(cmd.pre_check):
|
||||||
|
if not await cmd.pre_check(callback_context):
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
if not cmd.pre_check(callback_context):
|
||||||
|
return
|
||||||
|
|
||||||
|
if form_data is None and cmd.param_form:
|
||||||
|
field_descriptor = list(cmd.param_form.values())[0]
|
||||||
|
kwargs["callback_data"] = ContextData(
|
||||||
|
command=CallbackCommand.FIELD_EDITOR,
|
||||||
|
context=CommandContext.COMMAND_FORM,
|
||||||
|
field_name=field_descriptor.name,
|
||||||
|
user_command=callback_data.user_command,
|
||||||
|
)
|
||||||
|
|
||||||
|
return await field_editor(message=message, **kwargs)
|
||||||
|
|
||||||
|
if cmd.clear_navigation:
|
||||||
|
state_data.pop("navigation_stack", None)
|
||||||
|
state_data.pop("navigation_context", None)
|
||||||
|
|
||||||
|
if cmd.register_navigation:
|
||||||
|
clear_state(state_data=state_data)
|
||||||
|
save_navigation_context(callback_data=callback_data, state_data=state_data)
|
||||||
|
|
||||||
|
await cmd.handler(callback_context)
|
||||||
|
|
||||||
|
if callback_context.register_navigation:
|
||||||
|
await state.set_data(state_data)
|
||||||
|
|
||||||
|
# if command.register_navigation:
|
||||||
|
stack, navigation_context = get_navigation_context(state_data=state_data)
|
||||||
|
back_callback_data = pop_navigation_context(stack=stack)
|
||||||
|
if back_callback_data:
|
||||||
|
callback_context.keyboard_builder.row(
|
||||||
|
InlineKeyboardButton(
|
||||||
|
text=(await Settings.get(Settings.APP_STRINGS_BACK_BTN)),
|
||||||
|
callback_data=back_callback_data.pack(),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
send_message = get_send_message(message)
|
||||||
|
|
||||||
|
if isinstance(message, CallbackQuery):
|
||||||
|
message = message.message
|
||||||
|
|
||||||
|
if callback_context.message_text:
|
||||||
|
await send_message(
|
||||||
|
text=callback_context.message_text,
|
||||||
|
reply_markup=callback_context.keyboard_builder.as_markup(),
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
await message.edit_reply_markup(
|
||||||
|
reply_markup=callback_context.keyboard_builder.as_markup()
|
||||||
|
)
|
||||||
|
|
||||||
|
else:
|
||||||
|
clear_state(state_data=state_data)
|
||||||
|
await route_callback(message, back=True, **kwargs)
|
||||||
23
helpers/__init__.py
Normal file
23
helpers/__init__.py
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
from aiogram.types import InlineKeyboardButton
|
||||||
|
|
||||||
|
from ..utils.navigation import pop_navigation_context
|
||||||
|
from ..model.descriptors import CommandCallbackContext
|
||||||
|
from ..model.settings import Settings
|
||||||
|
|
||||||
|
|
||||||
|
async def get_back_button(
|
||||||
|
context: CommandCallbackContext, text: str = None
|
||||||
|
) -> InlineKeyboardButton | None:
|
||||||
|
stack = context.state_data.get("navigation_stack")
|
||||||
|
if not stack:
|
||||||
|
return None
|
||||||
|
|
||||||
|
back_callback_data = pop_navigation_context(stack)
|
||||||
|
|
||||||
|
if not text:
|
||||||
|
text = await Settings.get(Settings.APP_STRINGS_BACK_BTN)
|
||||||
|
|
||||||
|
return InlineKeyboardButton(
|
||||||
|
text=text,
|
||||||
|
callback_data=back_callback_data.pack(),
|
||||||
|
)
|
||||||
@@ -11,13 +11,13 @@ from typing import (
|
|||||||
TYPE_CHECKING,
|
TYPE_CHECKING,
|
||||||
)
|
)
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
from sqlmodel import SQLModel, BIGINT, Field, select, func, column
|
from sqlmodel import SQLModel, BigInteger, Field, select, func, column
|
||||||
|
|
||||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||||
from sqlmodel.sql.expression import SelectOfScalar
|
from sqlmodel.sql.expression import SelectOfScalar
|
||||||
from sqlmodel.main import SQLModelMetaclass, RelationshipInfo
|
from sqlmodel.main import SQLModelMetaclass, RelationshipInfo
|
||||||
|
|
||||||
from .descriptors import EntityDescriptor, EntityField, EntityFieldDescriptor, Filter
|
from .descriptors import EntityDescriptor, EntityField, FieldDescriptor, Filter
|
||||||
from .entity_metadata import EntityMetadata
|
from .entity_metadata import EntityMetadata
|
||||||
from . import session_dep
|
from . import session_dep
|
||||||
|
|
||||||
@@ -26,7 +26,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
|
|
||||||
class BotEntityMetaclass(SQLModelMetaclass):
|
class BotEntityMetaclass(SQLModelMetaclass):
|
||||||
__future_references__ = {}
|
_future_references = {}
|
||||||
|
|
||||||
def __new__(mcs, name, bases, namespace, **kwargs):
|
def __new__(mcs, name, bases, namespace, **kwargs):
|
||||||
bot_fields_descriptors = {}
|
bot_fields_descriptors = {}
|
||||||
@@ -35,7 +35,7 @@ class BotEntityMetaclass(SQLModelMetaclass):
|
|||||||
bot_entity_descriptor = bases[0].__dict__.get("bot_entity_descriptor")
|
bot_entity_descriptor = bases[0].__dict__.get("bot_entity_descriptor")
|
||||||
bot_fields_descriptors = (
|
bot_fields_descriptors = (
|
||||||
{
|
{
|
||||||
key: EntityFieldDescriptor(**value.__dict__.copy())
|
key: FieldDescriptor(**value.__dict__.copy())
|
||||||
for key, value in bot_entity_descriptor.fields_descriptors.items()
|
for key, value in bot_entity_descriptor.fields_descriptors.items()
|
||||||
}
|
}
|
||||||
if bot_entity_descriptor
|
if bot_entity_descriptor
|
||||||
@@ -71,7 +71,7 @@ class BotEntityMetaclass(SQLModelMetaclass):
|
|||||||
|
|
||||||
type_origin = get_origin(type_)
|
type_origin = get_origin(type_)
|
||||||
|
|
||||||
field_descriptor = EntityFieldDescriptor(
|
field_descriptor = FieldDescriptor(
|
||||||
name=descriptor_name,
|
name=descriptor_name,
|
||||||
field_name=annotation,
|
field_name=annotation,
|
||||||
type_=type_,
|
type_=type_,
|
||||||
@@ -80,18 +80,19 @@ class BotEntityMetaclass(SQLModelMetaclass):
|
|||||||
)
|
)
|
||||||
|
|
||||||
is_list = False
|
is_list = False
|
||||||
|
is_optional = False
|
||||||
if type_origin is list:
|
if type_origin is list:
|
||||||
field_descriptor.is_list = is_list = True
|
field_descriptor.is_list = is_list = True
|
||||||
field_descriptor.type_base = type_ = get_args(type_)[0]
|
field_descriptor.type_base = type_ = get_args(type_)[0]
|
||||||
|
|
||||||
if type_origin == Union and isinstance(get_args(type_)[0], ForwardRef):
|
if type_origin == Union and isinstance(get_args(type_)[0], ForwardRef):
|
||||||
field_descriptor.is_optional = True
|
field_descriptor.is_optional = is_optional = True
|
||||||
field_descriptor.type_base = type_ = get_args(type_)[
|
field_descriptor.type_base = type_ = get_args(type_)[
|
||||||
0
|
0
|
||||||
].__forward_arg__
|
].__forward_arg__
|
||||||
|
|
||||||
if type_origin == UnionType and get_args(type_)[1] == NoneType:
|
if type_origin == UnionType and get_args(type_)[1] == NoneType:
|
||||||
field_descriptor.is_optional = True
|
field_descriptor.is_optional = is_optional = True
|
||||||
field_descriptor.type_base = type_ = get_args(type_)[0]
|
field_descriptor.type_base = type_ = get_args(type_)[0]
|
||||||
|
|
||||||
if isinstance(type_, str):
|
if isinstance(type_, str):
|
||||||
@@ -100,18 +101,16 @@ class BotEntityMetaclass(SQLModelMetaclass):
|
|||||||
entity_descriptor
|
entity_descriptor
|
||||||
) in EntityMetadata().entity_descriptors.values():
|
) in EntityMetadata().entity_descriptors.values():
|
||||||
if type_ == entity_descriptor.class_name:
|
if type_ == entity_descriptor.class_name:
|
||||||
|
field_descriptor.type_base = entity_descriptor.type_
|
||||||
field_descriptor.type_ = (
|
field_descriptor.type_ = (
|
||||||
list[entity_descriptor.type_]
|
list[entity_descriptor.type_]
|
||||||
if is_list
|
if is_list
|
||||||
else (
|
else (
|
||||||
Optional[entity_descriptor.type_]
|
Optional[entity_descriptor.type_]
|
||||||
if type_origin == Optional
|
if type_origin == Union and is_optional
|
||||||
else (
|
else (
|
||||||
entity_descriptor.type_ | None
|
entity_descriptor.type_ | None
|
||||||
if (
|
if (type_origin == UnionType and is_optional)
|
||||||
type_origin == UnionType
|
|
||||||
and get_args(type_)[1] == NoneType
|
|
||||||
)
|
|
||||||
else entity_descriptor.type_
|
else entity_descriptor.type_
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -119,10 +118,10 @@ class BotEntityMetaclass(SQLModelMetaclass):
|
|||||||
type_not_found = False
|
type_not_found = False
|
||||||
break
|
break
|
||||||
if type_not_found:
|
if type_not_found:
|
||||||
if type_ in mcs.__future_references__:
|
if type_ in mcs._future_references:
|
||||||
mcs.__future_references__[type_].append(field_descriptor)
|
mcs._future_references[type_].append(field_descriptor)
|
||||||
else:
|
else:
|
||||||
mcs.__future_references__[type_] = [field_descriptor]
|
mcs._future_references[type_] = [field_descriptor]
|
||||||
|
|
||||||
bot_fields_descriptors[descriptor_name] = field_descriptor
|
bot_fields_descriptors[descriptor_name] = field_descriptor
|
||||||
|
|
||||||
@@ -191,14 +190,14 @@ class BotEntityMetaclass(SQLModelMetaclass):
|
|||||||
|
|
||||||
type_ = super().__new__(mcs, name, bases, namespace, **kwargs)
|
type_ = super().__new__(mcs, name, bases, namespace, **kwargs)
|
||||||
|
|
||||||
if name in mcs.__future_references__:
|
if name in mcs._future_references:
|
||||||
for field_descriptor in mcs.__future_references__[name]:
|
for field_descriptor in mcs._future_references[name]:
|
||||||
type_origin = get_origin(field_descriptor.type_)
|
type_origin = get_origin(field_descriptor.type_)
|
||||||
field_descriptor.type_base = type_
|
field_descriptor.type_base = type_
|
||||||
|
|
||||||
field_descriptor.type_ = (
|
field_descriptor.type_ = (
|
||||||
list[type_]
|
list[type_]
|
||||||
if get_origin(field_descriptor.type_) is list
|
if type_origin is list
|
||||||
else (
|
else (
|
||||||
Optional[type_]
|
Optional[type_]
|
||||||
if type_origin == Union
|
if type_origin == Union
|
||||||
@@ -220,7 +219,9 @@ class BotEntity[CreateSchemaType: BaseModel, UpdateSchemaType: BaseModel](
|
|||||||
bot_entity_descriptor: ClassVar[EntityDescriptor]
|
bot_entity_descriptor: ClassVar[EntityDescriptor]
|
||||||
entity_metadata: ClassVar[EntityMetadata]
|
entity_metadata: ClassVar[EntityMetadata]
|
||||||
|
|
||||||
id: int = Field(primary_key=True, sa_type=BIGINT)
|
id: int = EntityField(
|
||||||
|
sm_descriptor=Field(primary_key=True, sa_type=BigInteger), is_visible=False
|
||||||
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@session_dep
|
@session_dep
|
||||||
@@ -228,7 +229,7 @@ class BotEntity[CreateSchemaType: BaseModel, UpdateSchemaType: BaseModel](
|
|||||||
return await session.get(cls, id, populate_existing=True)
|
return await session.get(cls, id, populate_existing=True)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def _static_fiter_condition(
|
def _static_filter_condition(
|
||||||
cls, select_statement: SelectOfScalar[Self], static_filter: list[Filter]
|
cls, select_statement: SelectOfScalar[Self], static_filter: list[Filter]
|
||||||
):
|
):
|
||||||
for sfilt in static_filter:
|
for sfilt in static_filter:
|
||||||
@@ -292,7 +293,7 @@ class BotEntity[CreateSchemaType: BaseModel, UpdateSchemaType: BaseModel](
|
|||||||
select_statement = select(func.count()).select_from(cls)
|
select_statement = select(func.count()).select_from(cls)
|
||||||
if static_filter:
|
if static_filter:
|
||||||
if isinstance(static_filter, list):
|
if isinstance(static_filter, list):
|
||||||
select_statement = cls._static_fiter_condition(
|
select_statement = cls._static_filter_condition(
|
||||||
select_statement, static_filter
|
select_statement, static_filter
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
@@ -327,7 +328,7 @@ class BotEntity[CreateSchemaType: BaseModel, UpdateSchemaType: BaseModel](
|
|||||||
select_statement = select_statement.limit(limit)
|
select_statement = select_statement.limit(limit)
|
||||||
if static_filter:
|
if static_filter:
|
||||||
if isinstance(static_filter, list):
|
if isinstance(static_filter, list):
|
||||||
select_statement = cls._static_fiter_condition(
|
select_statement = cls._static_filter_condition(
|
||||||
select_statement, static_filter
|
select_statement, static_filter
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -143,7 +143,13 @@ class BotEnum(EnumMember, metaclass=BotEnumMetaclass):
|
|||||||
|
|
||||||
|
|
||||||
class EnumType(TypeDecorator):
|
class EnumType(TypeDecorator):
|
||||||
impl = String(256)
|
impl = String(64)
|
||||||
|
cache_ok = True
|
||||||
|
|
||||||
|
# class comparator_factory(TypeDecorator.Comparator):
|
||||||
|
# def __eq__(self, other):
|
||||||
|
# expr = type_coerce(self.expr, String)
|
||||||
|
# return expr != other.value
|
||||||
|
|
||||||
def __init__(self, enum_type: BotEnum):
|
def __init__(self, enum_type: BotEnum):
|
||||||
self._enum_type = enum_type
|
self._enum_type = enum_type
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ from aiogram.types import Message, CallbackQuery
|
|||||||
from aiogram.fsm.context import FSMContext
|
from aiogram.fsm.context import FSMContext
|
||||||
from aiogram.utils.i18n import I18n
|
from aiogram.utils.i18n import I18n
|
||||||
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
from aiogram.utils.keyboard import InlineKeyboardBuilder
|
||||||
from typing import Any, Callable, TYPE_CHECKING, Literal
|
from typing import Any, Callable, TYPE_CHECKING, Literal, Union
|
||||||
from babel.support import LazyProxy
|
from babel.support import LazyProxy
|
||||||
from dataclasses import dataclass, field
|
from dataclasses import dataclass, field
|
||||||
from sqlmodel.ext.asyncio.session import AsyncSession
|
from sqlmodel.ext.asyncio.session import AsyncSession
|
||||||
@@ -18,22 +18,21 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
EntityCaptionCallable = Callable[["EntityDescriptor"], str]
|
EntityCaptionCallable = Callable[["EntityDescriptor"], str]
|
||||||
EntityItemCaptionCallable = Callable[["EntityDescriptor", Any], str]
|
EntityItemCaptionCallable = Callable[["EntityDescriptor", Any], str]
|
||||||
EntityFieldCaptionCallable = Callable[["EntityFieldDescriptor", Any, Any], str]
|
EntityFieldCaptionCallable = Callable[["FieldDescriptor", Any, Any], str]
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class FieldEditButton:
|
class FieldEditButton:
|
||||||
field_name: str
|
field_name: str
|
||||||
visibility: Callable[[Any], bool] | None = None
|
|
||||||
caption: str | LazyProxy | EntityFieldCaptionCallable | None = None
|
caption: str | LazyProxy | EntityFieldCaptionCallable | None = None
|
||||||
|
visibility: Callable[[Any], bool] | None = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class CommandButton:
|
class CommandButton:
|
||||||
command: str
|
command: ContextData | Callable[[ContextData, Any], ContextData] | str
|
||||||
caption: str | LazyProxy | EntityItemCaptionCallable | None = None
|
caption: str | LazyProxy | EntityItemCaptionCallable
|
||||||
visibility: Callable[[Any], bool] | None = None
|
visibility: Callable[[Any], bool] | None = None
|
||||||
context_data: ContextData | Callable[[ContextData, Any], ContextData] | None = None
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
@@ -81,7 +80,7 @@ class EntityForm:
|
|||||||
|
|
||||||
|
|
||||||
@dataclass(kw_only=True)
|
@dataclass(kw_only=True)
|
||||||
class _BaseEntityFieldDescriptor:
|
class _BaseFieldDescriptor:
|
||||||
icon: str = None
|
icon: str = None
|
||||||
caption: str | LazyProxy | EntityFieldCaptionCallable | None = None
|
caption: str | LazyProxy | EntityFieldCaptionCallable | None = None
|
||||||
description: str | LazyProxy | EntityFieldCaptionCallable | None = None
|
description: str | LazyProxy | EntityFieldCaptionCallable | None = None
|
||||||
@@ -99,18 +98,24 @@ class _BaseEntityFieldDescriptor:
|
|||||||
|
|
||||||
|
|
||||||
@dataclass(kw_only=True)
|
@dataclass(kw_only=True)
|
||||||
class EntityField(_BaseEntityFieldDescriptor):
|
class EntityField(_BaseFieldDescriptor):
|
||||||
name: str | None = None
|
name: str | None = None
|
||||||
sm_descriptor: Any = None
|
sm_descriptor: Any = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass(kw_only=True)
|
@dataclass(kw_only=True)
|
||||||
class Setting(_BaseEntityFieldDescriptor):
|
class Setting(_BaseFieldDescriptor):
|
||||||
name: str | None = None
|
name: str | None = None
|
||||||
|
|
||||||
|
|
||||||
@dataclass(kw_only=True)
|
@dataclass(kw_only=True)
|
||||||
class EntityFieldDescriptor(_BaseEntityFieldDescriptor):
|
class FormField(_BaseFieldDescriptor):
|
||||||
|
name: str | None = None
|
||||||
|
type_: type
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(kw_only=True)
|
||||||
|
class FieldDescriptor(_BaseFieldDescriptor):
|
||||||
name: str
|
name: str
|
||||||
field_name: str
|
field_name: str
|
||||||
type_: type
|
type_: type
|
||||||
@@ -118,6 +123,7 @@ class EntityFieldDescriptor(_BaseEntityFieldDescriptor):
|
|||||||
is_list: bool = False
|
is_list: bool = False
|
||||||
is_optional: bool = False
|
is_optional: bool = False
|
||||||
entity_descriptor: "EntityDescriptor" = None
|
entity_descriptor: "EntityDescriptor" = None
|
||||||
|
command: "BotCommand" = None
|
||||||
|
|
||||||
def __hash__(self):
|
def __hash__(self):
|
||||||
return self.name.__hash__()
|
return self.name.__hash__()
|
||||||
@@ -162,7 +168,7 @@ class EntityDescriptor(_BaseEntityDescriptor):
|
|||||||
name: str
|
name: str
|
||||||
class_name: str
|
class_name: str
|
||||||
type_: type["BotEntity"]
|
type_: type["BotEntity"]
|
||||||
fields_descriptors: dict[str, EntityFieldDescriptor]
|
fields_descriptors: dict[str, FieldDescriptor]
|
||||||
|
|
||||||
|
|
||||||
@dataclass(kw_only=True)
|
@dataclass(kw_only=True)
|
||||||
@@ -179,24 +185,21 @@ class CommandCallbackContext[UT: UserBase]:
|
|||||||
app: "QBotApp"
|
app: "QBotApp"
|
||||||
state_data: dict[str, Any]
|
state_data: dict[str, Any]
|
||||||
state: FSMContext
|
state: FSMContext
|
||||||
|
form_data: dict[str, Any]
|
||||||
i18n: I18n
|
i18n: I18n
|
||||||
kwargs: dict[str, Any] = field(default_factory=dict)
|
kwargs: dict[str, Any] = field(default_factory=dict)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(kw_only=True)
|
@dataclass(kw_only=True)
|
||||||
class _BotCommand:
|
class BotCommand:
|
||||||
name: str
|
name: str
|
||||||
caption: str | dict[str, str] | None = None
|
caption: str | dict[str, str] | None = None
|
||||||
|
pre_check: Callable[[Union[Message, CallbackQuery], Any], bool] | None = None
|
||||||
show_in_bot_commands: bool = False
|
show_in_bot_commands: bool = False
|
||||||
register_navigation: bool = True
|
register_navigation: bool = True
|
||||||
clear_navigation: bool = False
|
clear_navigation: bool = False
|
||||||
clear_state: bool = True
|
clear_state: bool = True
|
||||||
|
param_form: dict[str, FieldDescriptor] | None = None
|
||||||
|
show_cancel_in_param_form: bool = True
|
||||||
@dataclass(kw_only=True)
|
show_back_in_param_form: bool = True
|
||||||
class BotCommand(_BotCommand):
|
|
||||||
handler: Callable[[CommandCallbackContext], None]
|
handler: Callable[[CommandCallbackContext], None]
|
||||||
|
|
||||||
|
|
||||||
@dataclass(kw_only=True)
|
|
||||||
class Command(_BotCommand): ...
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ from typing import Any, get_args, get_origin
|
|||||||
|
|
||||||
from ..db import async_session
|
from ..db import async_session
|
||||||
from .role import RoleBase
|
from .role import RoleBase
|
||||||
from .descriptors import EntityFieldDescriptor, Setting
|
from .descriptors import FieldDescriptor, Setting
|
||||||
from ..utils.serialization import deserialize, serialize
|
from ..utils.serialization import deserialize, serialize
|
||||||
|
|
||||||
import ujson as json
|
import ujson as json
|
||||||
@@ -39,7 +39,7 @@ class SettingsMetaclass(type):
|
|||||||
if isinstance(attr_value, Setting):
|
if isinstance(attr_value, Setting):
|
||||||
descriptor_kwargs = attr_value.__dict__.copy()
|
descriptor_kwargs = attr_value.__dict__.copy()
|
||||||
name = descriptor_kwargs.pop("name") or annotation
|
name = descriptor_kwargs.pop("name") or annotation
|
||||||
attributes[annotation] = EntityFieldDescriptor(
|
attributes[annotation] = FieldDescriptor(
|
||||||
name=name,
|
name=name,
|
||||||
field_name=annotation,
|
field_name=annotation,
|
||||||
type_=type_,
|
type_=type_,
|
||||||
@@ -48,7 +48,7 @@ class SettingsMetaclass(type):
|
|||||||
)
|
)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
attributes[annotation] = EntityFieldDescriptor(
|
attributes[annotation] = FieldDescriptor(
|
||||||
name=annotation,
|
name=annotation,
|
||||||
field_name=annotation,
|
field_name=annotation,
|
||||||
type_=type_,
|
type_=type_,
|
||||||
@@ -83,7 +83,7 @@ class SettingsMetaclass(type):
|
|||||||
|
|
||||||
class Settings(metaclass=SettingsMetaclass):
|
class Settings(metaclass=SettingsMetaclass):
|
||||||
_cache: dict[str, Any] = dict[str, Any]()
|
_cache: dict[str, Any] = dict[str, Any]()
|
||||||
_settings_descriptors: dict[str, EntityFieldDescriptor] = {}
|
_settings_descriptors: dict[str, FieldDescriptor] = {}
|
||||||
|
|
||||||
PAGE_SIZE: int = Setting(
|
PAGE_SIZE: int = Setting(
|
||||||
default=10,
|
default=10,
|
||||||
@@ -213,7 +213,7 @@ class Settings(metaclass=SettingsMetaclass):
|
|||||||
return ret_val
|
return ret_val
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def load_param(cls, param: EntityFieldDescriptor) -> Any:
|
async def load_param(cls, param: FieldDescriptor) -> Any:
|
||||||
async with async_session() as session:
|
async with async_session() as session:
|
||||||
db_setting = (
|
db_setting = (
|
||||||
await session.exec(
|
await session.exec(
|
||||||
@@ -244,7 +244,7 @@ class Settings(metaclass=SettingsMetaclass):
|
|||||||
db_settings = (await session.exec(select(DbSettings))).all()
|
db_settings = (await session.exec(select(DbSettings))).all()
|
||||||
for db_setting in db_settings:
|
for db_setting in db_settings:
|
||||||
if db_setting.name in cls.__dict__:
|
if db_setting.name in cls.__dict__:
|
||||||
setting = cls.__dict__[db_setting.name] # type: EntityFieldDescriptor
|
setting = cls.__dict__[db_setting.name] # type: FieldDescriptor
|
||||||
cls._cache[db_setting.name] = await deserialize(
|
cls._cache[db_setting.name] = await deserialize(
|
||||||
session=session,
|
session=session,
|
||||||
type_=setting.type_,
|
type_=setting.type_,
|
||||||
@@ -255,7 +255,7 @@ class Settings(metaclass=SettingsMetaclass):
|
|||||||
cls._loaded = True
|
cls._loaded = True
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def set_param(cls, param: str | EntityFieldDescriptor, value) -> None:
|
async def set_param(cls, param: str | FieldDescriptor, value) -> None:
|
||||||
if isinstance(param, str):
|
if isinstance(param, str):
|
||||||
param = cls._settings_descriptors[param]
|
param = cls._settings_descriptors[param]
|
||||||
ser_value = serialize(value, param)
|
ser_value = serialize(value, param)
|
||||||
@@ -273,11 +273,11 @@ class Settings(metaclass=SettingsMetaclass):
|
|||||||
cls._cache[param.field_name] = value
|
cls._cache[param.field_name] = value
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def list_params(cls) -> dict[str, EntityFieldDescriptor]:
|
def list_params(cls) -> dict[str, FieldDescriptor]:
|
||||||
return cls._settings_descriptors
|
return cls._settings_descriptors
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
async def get_params(cls) -> dict[EntityFieldDescriptor, Any]:
|
async def get_params(cls) -> dict[FieldDescriptor, Any]:
|
||||||
params = cls.list_params()
|
params = cls.list_params()
|
||||||
return {
|
return {
|
||||||
param: await cls.get(param, all_locales=True) for _, param in params.items()
|
param: await cls.get(param, all_locales=True) for _, param in params.items()
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from sqlmodel import SQLModel, Field, BIGINT
|
from sqlmodel import SQLModel, Field, BigInteger
|
||||||
from sqlalchemy.ext.asyncio.session import AsyncSession
|
from sqlalchemy.ext.asyncio.session import AsyncSession
|
||||||
|
|
||||||
from . import session_dep
|
from . import session_dep
|
||||||
@@ -7,7 +7,7 @@ from . import session_dep
|
|||||||
class ViewSetting(SQLModel, table=True):
|
class ViewSetting(SQLModel, table=True):
|
||||||
__tablename__ = "view_setting"
|
__tablename__ = "view_setting"
|
||||||
user_id: int = Field(
|
user_id: int = Field(
|
||||||
sa_type=BIGINT, primary_key=True, foreign_key="user.id", ondelete="CASCADE"
|
sa_type=BigInteger, primary_key=True, foreign_key="user.id", ondelete="CASCADE"
|
||||||
)
|
)
|
||||||
entity_name: str = Field(primary_key=True)
|
entity_name: str = Field(primary_key=True)
|
||||||
filter: str | None = None
|
filter: str | None = None
|
||||||
|
|||||||
75
router.py
75
router.py
@@ -1,26 +1,75 @@
|
|||||||
|
from aiogram.types import CallbackQuery, Message
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Callable, overload
|
from types import UnionType
|
||||||
from .model.descriptors import BotCommand, Command, CommandCallbackContext
|
from typing import Callable, Union, get_args, get_origin, Any
|
||||||
|
|
||||||
|
from .model.descriptors import (
|
||||||
|
BotCommand,
|
||||||
|
CommandCallbackContext,
|
||||||
|
FieldDescriptor,
|
||||||
|
FormField,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class Router:
|
class Router:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._commands = dict[str, BotCommand]()
|
self._commands = dict[str, BotCommand]()
|
||||||
|
|
||||||
@overload
|
|
||||||
def command(self, command: Command): ...
|
|
||||||
|
|
||||||
@overload
|
|
||||||
def command(self, command: str, caption: str | dict[str, str] | None = None): ...
|
|
||||||
|
|
||||||
def command(
|
def command(
|
||||||
self, command: str | Command, caption: str | dict[str, str] | None = None
|
self,
|
||||||
|
name: str,
|
||||||
|
caption: str | dict[str, str] | None = None,
|
||||||
|
pre_check: Callable[[Union[Message, CallbackQuery], Any], bool] | None = None,
|
||||||
|
# handle_message: bool = False,
|
||||||
|
show_in_bot_commands: bool = False,
|
||||||
|
register_navigation: bool = True,
|
||||||
|
clear_navigation: bool = False,
|
||||||
|
clear_state: bool = True,
|
||||||
|
show_cancel_in_param_form: bool = True,
|
||||||
|
show_back_in_param_form: bool = True,
|
||||||
|
form_fields: list[FormField] = list[FormField](),
|
||||||
):
|
):
|
||||||
def decorator(func: Callable[[CommandCallbackContext], None]):
|
def decorator(func: Callable[[CommandCallbackContext], None]):
|
||||||
if isinstance(command, str):
|
form_fields_dict = dict[str, FieldDescriptor]()
|
||||||
cmd = BotCommand(name=command, handler=func, caption=caption)
|
for field in form_fields:
|
||||||
else:
|
is_list = False
|
||||||
cmd = BotCommand(handler=func, **command.__dict__)
|
is_optional = False
|
||||||
|
type_origin = get_origin(field.type_)
|
||||||
|
if type_origin is list:
|
||||||
|
is_list = True
|
||||||
|
type_base = get_args(field.type_)[0]
|
||||||
|
elif type_origin in [Union, UnionType] and type(None) in get_args(
|
||||||
|
field.type_
|
||||||
|
):
|
||||||
|
is_optional = True
|
||||||
|
type_base = get_args(field.type_)[0]
|
||||||
|
else:
|
||||||
|
type_base = field.type_
|
||||||
|
|
||||||
|
form_fields_dict[field.name] = FieldDescriptor(
|
||||||
|
field_name=field.name,
|
||||||
|
type_base=type_base,
|
||||||
|
is_list=is_list,
|
||||||
|
is_optional=is_optional,
|
||||||
|
**field.__dict__,
|
||||||
|
)
|
||||||
|
|
||||||
|
cmd = BotCommand(
|
||||||
|
name=name,
|
||||||
|
caption=caption,
|
||||||
|
pre_check=pre_check,
|
||||||
|
show_in_bot_commands=show_in_bot_commands,
|
||||||
|
register_navigation=register_navigation,
|
||||||
|
clear_navigation=clear_navigation,
|
||||||
|
clear_state=clear_state,
|
||||||
|
param_form=form_fields_dict,
|
||||||
|
show_cancel_in_param_form=show_cancel_in_param_form,
|
||||||
|
show_back_in_param_form=show_back_in_param_form,
|
||||||
|
handler=func,
|
||||||
|
)
|
||||||
|
for field in form_fields_dict.values():
|
||||||
|
field.command = cmd
|
||||||
|
|
||||||
self._commands[cmd.name] = cmd
|
self._commands[cmd.name] = cmd
|
||||||
|
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
from babel.support import LazyProxy
|
from babel.support import LazyProxy
|
||||||
from inspect import signature
|
from inspect import signature
|
||||||
from aiogram.types import Message, CallbackQuery
|
from aiogram.types import Message, CallbackQuery
|
||||||
|
from aiogram.utils.i18n import I18n
|
||||||
from typing import Any, TYPE_CHECKING
|
from typing import Any, TYPE_CHECKING
|
||||||
import ujson as json
|
import ujson as json
|
||||||
|
|
||||||
@@ -9,7 +10,7 @@ from ..model.bot_enum import BotEnum
|
|||||||
from ..model.settings import Settings
|
from ..model.settings import Settings
|
||||||
|
|
||||||
from ..model.descriptors import (
|
from ..model.descriptors import (
|
||||||
EntityFieldDescriptor,
|
FieldDescriptor,
|
||||||
EntityDescriptor,
|
EntityDescriptor,
|
||||||
EntityItemCaptionCallable,
|
EntityItemCaptionCallable,
|
||||||
EntityFieldCaptionCallable,
|
EntityFieldCaptionCallable,
|
||||||
@@ -36,7 +37,13 @@ def get_user_permissions(
|
|||||||
return permissions
|
return permissions
|
||||||
|
|
||||||
|
|
||||||
def get_local_text(text: str, locale: str) -> str:
|
def get_local_text(text: str, locale: str = None) -> str:
|
||||||
|
if not locale:
|
||||||
|
i18n = I18n.get_current(no_error=True)
|
||||||
|
if i18n:
|
||||||
|
locale = i18n.current_locale
|
||||||
|
else:
|
||||||
|
locale = "en"
|
||||||
try:
|
try:
|
||||||
obj = json.loads(text) # @IgnoreException
|
obj = json.loads(text) # @IgnoreException
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -116,7 +123,7 @@ def get_entity_item_repr(
|
|||||||
|
|
||||||
|
|
||||||
def get_value_repr(
|
def get_value_repr(
|
||||||
value: Any, field_descriptor: EntityFieldDescriptor, locale: str | None = None
|
value: Any, field_descriptor: FieldDescriptor, locale: str | None = None
|
||||||
) -> str:
|
) -> str:
|
||||||
if value is None:
|
if value is None:
|
||||||
return ""
|
return ""
|
||||||
@@ -157,7 +164,7 @@ def get_callable_str(
|
|||||||
| EntityItemCaptionCallable
|
| EntityItemCaptionCallable
|
||||||
| EntityFieldCaptionCallable
|
| EntityFieldCaptionCallable
|
||||||
),
|
),
|
||||||
descriptor: EntityFieldDescriptor | EntityDescriptor,
|
descriptor: FieldDescriptor | EntityDescriptor,
|
||||||
entity: Any = None,
|
entity: Any = None,
|
||||||
value: Any = None,
|
value: Any = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
@@ -185,9 +192,17 @@ def get_entity_descriptor(
|
|||||||
|
|
||||||
def get_field_descriptor(
|
def get_field_descriptor(
|
||||||
app: "QBotApp", callback_data: ContextData
|
app: "QBotApp", callback_data: ContextData
|
||||||
) -> EntityFieldDescriptor | None:
|
) -> FieldDescriptor | None:
|
||||||
if callback_data.context == CommandContext.SETTING_EDIT:
|
if callback_data.context == CommandContext.SETTING_EDIT:
|
||||||
return Settings.list_params()[callback_data.field_name]
|
return Settings.list_params()[callback_data.field_name]
|
||||||
|
elif callback_data.context == CommandContext.COMMAND_FORM:
|
||||||
|
command = app.bot_commands[callback_data.user_command.split("&")[0]]
|
||||||
|
if (
|
||||||
|
command
|
||||||
|
and command.param_form
|
||||||
|
and callback_data.field_name in command.param_form
|
||||||
|
):
|
||||||
|
return command.param_form[callback_data.field_name]
|
||||||
elif callback_data.context in [
|
elif callback_data.context in [
|
||||||
CommandContext.ENTITY_CREATE,
|
CommandContext.ENTITY_CREATE,
|
||||||
CommandContext.ENTITY_EDIT,
|
CommandContext.ENTITY_EDIT,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
from .context import ContextData, CallbackCommand
|
from ..bot.handlers.context import ContextData, CallbackCommand
|
||||||
|
|
||||||
|
|
||||||
def save_navigation_context(
|
def save_navigation_context(
|
||||||
@@ -8,7 +8,7 @@ import ujson as json
|
|||||||
|
|
||||||
from ..model.bot_entity import BotEntity
|
from ..model.bot_entity import BotEntity
|
||||||
from ..model.bot_enum import BotEnum
|
from ..model.bot_enum import BotEnum
|
||||||
from ..model.descriptors import EntityFieldDescriptor
|
from ..model.descriptors import FieldDescriptor
|
||||||
|
|
||||||
|
|
||||||
async def deserialize[T](session: AsyncSession, type_: type[T], value: str = None) -> T:
|
async def deserialize[T](session: AsyncSession, type_: type[T], value: str = None) -> T:
|
||||||
@@ -73,7 +73,7 @@ async def deserialize[T](session: AsyncSession, type_: type[T], value: str = Non
|
|||||||
return type_(value)
|
return type_(value)
|
||||||
|
|
||||||
|
|
||||||
def serialize(value: Any, field_descriptor: EntityFieldDescriptor) -> str:
|
def serialize(value: Any, field_descriptor: FieldDescriptor) -> str:
|
||||||
if value is None:
|
if value is None:
|
||||||
return ""
|
return ""
|
||||||
type_ = field_descriptor.type_base
|
type_ = field_descriptor.type_base
|
||||||
|
|||||||
Reference in New Issue
Block a user