add ruff format, ruff check, time_picker, project structure and imports reorganized

This commit is contained in:
Alexander Kalinovsky
2025-01-21 23:50:19 +01:00
parent ced47ac993
commit 9dd0708a5b
58 changed files with 3690 additions and 2583 deletions

View File

@@ -1,26 +1,29 @@
from typing import get_args, get_origin, TYPE_CHECKING
from typing import TYPE_CHECKING
from aiogram import Router, F
from aiogram.filters import Command
from aiogram.fsm.context import FSMContext
from aiogram.fsm.state import StatesGroup, State
from aiogram.types import Message, CallbackQuery, InlineKeyboardButton
from aiogram.utils.i18n import I18n
from aiogram.utils.keyboard import InlineKeyboardBuilder
from logging import getLogger
from sqlmodel.ext.asyncio.session import AsyncSession
from ....model.bot_entity import BotEntity
from ....model.bot_enum import BotEnum
from ....model.owned_bot_entity import OwnedBotEntity
from ....model.settings import Settings
from ....model.user import UserBase
from ....model.view_setting import ViewSetting
from ....model.descriptors import EntityFieldDescriptor, EntityDescriptor
from ....model.descriptors import EntityDescriptor, Filter
from ....model import EntityPermission
from ....utils import serialize, deserialize, get_user_permissions
from ....utils.main import (
get_user_permissions,
get_send_message,
clear_state,
get_entity_descriptor,
get_callable_str,
)
from ....utils.serialization import deserialize
from ..context import ContextData, CallbackCommand, CommandContext
from ..common import (add_pagination_controls, get_local_text, get_entity_descriptor,
get_callable_str, get_send_message, add_filter_controls)
from ..common.pagination import add_pagination_controls
from ..common.filtering import add_filter_controls
from ..navigation import pop_navigation_context, save_navigation_context
if TYPE_CHECKING:
from ....main import QBotApp
@@ -32,135 +35,226 @@ router = Router()
@router.callback_query(ContextData.filter(F.command == CallbackCommand.ENTITY_LIST))
async def entity_list_callback(query: CallbackQuery, **kwargs):
callback_data: ContextData = kwargs["callback_data"]
if callback_data.data == "skip":
return
state: FSMContext = kwargs["state"]
state_data = await state.get_data()
kwargs["state_data"] = state_data
clear_state(state_data = state_data)
stack = save_navigation_context(callback_data = callback_data, state_data = state_data)
await entity_list(message = query, navigation_stack = stack, **kwargs)
clear_state(state_data=state_data)
stack = save_navigation_context(callback_data=callback_data, state_data=state_data)
await entity_list(message=query, navigation_stack=stack, **kwargs)
def calc_total_pages(items_count: int, page_size: int) -> int:
return max(items_count // page_size + (1 if items_count % page_size else 0), 1)
return max(items_count // page_size + (1 if items_count % page_size else 0), 1)
async def entity_list(message: CallbackQuery | Message,
callback_data: ContextData,
db_session: AsyncSession,
user: UserBase,
app: "QBotApp",
navigation_stack: list[ContextData],
**kwargs):
async def _prepare_static_filter(
db_session: AsyncSession,
entity_descriptor: EntityDescriptor,
static_filters: list[Filter],
params: list[str],
) -> list[Filter]:
return (
[
Filter(
field_name=f.field_name,
operator=f.operator,
value_type="const",
value=(
f.value
if f.value_type == "const"
else await deserialize(
session=db_session,
type_=entity_descriptor.fields_descriptors[
f.field_name
].type_base,
value=params[f.param_index],
)
),
)
for f in static_filters
]
if static_filters
else None
)
async def entity_list(
message: CallbackQuery | Message,
callback_data: ContextData,
db_session: AsyncSession,
user: UserBase,
app: "QBotApp",
navigation_stack: list[ContextData],
**kwargs,
):
page = int(callback_data.data or "1")
entity_descriptor = get_entity_descriptor(app, callback_data)
user_permissions = get_user_permissions(user, entity_descriptor)
entity_type = entity_descriptor.type_
form_params = (
callback_data.form_params.split("&") if callback_data.form_params else []
)
form_name = form_params.pop(0) if form_params else "default"
form_list = entity_descriptor.lists.get(
form_name or "default", entity_descriptor.default_list
)
form_item = entity_descriptor.forms.get(
form_list.item_form or "default", entity_descriptor.default_form
)
keyboard_builder = InlineKeyboardBuilder()
if EntityPermission.CREATE in user_permissions or EntityPermission.CREATE_ALL in user_permissions:
if (
EntityPermission.CREATE in user_permissions
or EntityPermission.CREATE_ALL in user_permissions
) and form_list.show_add_new_button:
keyboard_builder.row(
InlineKeyboardButton(
text = (await Settings.get(Settings.APP_STRINGS_ADD_BTN)),
callback_data = ContextData(
command = CallbackCommand.FIELD_EDITOR,
context = CommandContext.ENTITY_CREATE,
entity_name = entity_descriptor.name,
field_name = entity_descriptor.field_sequence[0],
save_state = True).pack()))
text=(await Settings.get(Settings.APP_STRINGS_ADD_BTN)),
callback_data=ContextData(
command=CallbackCommand.FIELD_EDITOR,
context=CommandContext.ENTITY_CREATE,
entity_name=entity_descriptor.name,
field_name=form_item.edit_field_sequence[0],
form_params=form_list.item_form,
).pack(),
)
)
page_size = await Settings.get(Settings.PAGE_SIZE)
entity_filter = await ViewSetting.get_filter(session = db_session, user_id = user.id, entity_name = entity_descriptor.class_name)
if issubclass(entity_type, OwnedBotEntity):
if EntityPermission.READ_ALL in user_permissions or EntityPermission.LIST_ALL in user_permissions:
items_count = await entity_type.get_count(session = db_session, filter = entity_filter)
total_pages = calc_total_pages(items_count, page_size)
page = min(page, total_pages)
items = await entity_type.get_multi(
session = db_session, order_by = entity_type.name, filter = entity_filter,
skip = page_size * (page - 1), limit = page_size)
elif EntityPermission.READ in user_permissions or EntityPermission.LIST in user_permissions:
items_count = await entity_type.get_count_by_user(session = db_session, user_id = user.id, filter = entity_filter)
total_pages = calc_total_pages(items_count, page_size)
page = min(page, total_pages)
items = await entity_type.get_multi_by_user(
session = db_session, user_id = user.id, order_by = entity_type.name, filter = entity_filter,
skip = page_size * (page - 1), limit = page_size)
else:
items = list[OwnedBotEntity]()
items_count = 0
total_pages = 1
page = 1
elif issubclass(entity_type, BotEntity):
if (EntityPermission.READ in user_permissions or EntityPermission.LIST in user_permissions or
EntityPermission.READ_ALL in user_permissions or EntityPermission.LIST_ALL in user_permissions):
items_count = await entity_type.get_count(session = db_session, filter = entity_filter)
total_pages = calc_total_pages(items_count, page_size)
page = min(page, total_pages)
items = await entity_type.get_multi(
session = db_session, order_by = entity_type.name, filter = entity_filter,
skip = page_size * (page - 1), limit = page_size)
else:
items = list[BotEntity]()
total_pages = 1
page = 1
items_count = 0
if form_list.filtering:
entity_filter = await ViewSetting.get_filter(
session=db_session,
user_id=user.id,
entity_name=entity_descriptor.class_name,
)
else:
raise ValueError(f"Unsupported entity type: {entity_type}")
entity_filter = None
list_all = (
EntityPermission.LIST_ALL in user_permissions
or EntityPermission.READ_ALL in user_permissions
)
if (
list_all
or EntityPermission.LIST in user_permissions
or EntityPermission.READ in user_permissions
):
if form_list.pagination:
page_size = await Settings.get(Settings.PAGE_SIZE)
items_count = await entity_type.get_count(
session=db_session,
static_filter=await _prepare_static_filter(
db_session=db_session,
entity_descriptor=entity_descriptor,
static_filters=form_list.static_filters,
params=form_params,
),
filter=entity_filter,
filter_fields=form_list.filtering_fields,
user=user if not list_all else None,
)
total_pages = calc_total_pages(items_count, page_size)
page = min(page, total_pages)
skip = page_size * (page - 1)
limit = page_size
else:
skip = 0
limit = None
items = await entity_type.get_multi(
session=db_session,
order_by=form_list.order_by,
static_filter=await _prepare_static_filter(
db_session=db_session,
entity_descriptor=entity_descriptor,
static_filters=form_list.static_filters,
params=form_params,
),
filter=entity_filter,
filter_fields=form_list.filtering_fields,
user=user if not list_all else None,
skip=skip,
limit=limit,
)
else:
items = list[BotEntity]()
items_count = 0
total_pages = 1
page = 1
for item in items:
if entity_descriptor.item_caption:
caption = entity_descriptor.item_caption(entity_descriptor, item)
elif entity_descriptor.fields_descriptors["name"].localizable:
caption = get_local_text(item.name, user.lang)
if form_list.item_repr:
caption = form_list.item_repr(entity_descriptor, item)
elif entity_descriptor.item_repr:
caption = entity_descriptor.item_repr(entity_descriptor, item)
elif entity_descriptor.full_name:
caption = f"{
get_callable_str(
callable_str=entity_descriptor.full_name,
descriptor=entity_descriptor,
entity=item,
)
}: {item.id}"
else:
caption = item.name
caption = f"{entity_descriptor.name}: {item.id}"
keyboard_builder.row(
InlineKeyboardButton(
text = caption,
callback_data = ContextData(
command = CallbackCommand.ENTITY_ITEM,
entity_name = entity_descriptor.name,
entity_id = str(item.id)).pack()))
add_pagination_controls(keyboard_builder = keyboard_builder,
callback_data = callback_data,
total_pages = total_pages,
command = CallbackCommand.ENTITY_LIST,
page = page)
add_filter_controls(keyboard_builder = keyboard_builder,
entity_descriptor = entity_descriptor,
filter = entity_filter)
text=caption,
callback_data=ContextData(
command=CallbackCommand.ENTITY_ITEM,
entity_name=entity_descriptor.name,
form_params=form_list.item_form,
entity_id=str(item.id),
).pack(),
)
)
if form_list.pagination:
add_pagination_controls(
keyboard_builder=keyboard_builder,
callback_data=callback_data,
total_pages=total_pages,
command=CallbackCommand.ENTITY_LIST,
page=page,
)
if form_list.filtering and form_list.filtering_fields:
add_filter_controls(
keyboard_builder=keyboard_builder,
entity_descriptor=entity_descriptor,
filter=entity_filter,
)
context = pop_navigation_context(navigation_stack)
if context:
keyboard_builder.row(
InlineKeyboardButton(
text = (await Settings.get(Settings.APP_STRINGS_BACK_BTN)),
callback_data = context.pack()))
if entity_descriptor.caption:
entity_text = get_callable_str(entity_descriptor.caption_plural, entity_descriptor)
text=(await Settings.get(Settings.APP_STRINGS_BACK_BTN)),
callback_data=context.pack(),
)
)
if form_list.caption:
entity_text = get_callable_str(form_list.caption, entity_descriptor)
else:
entity_text = entity_descriptor.name
if entity_descriptor.description:
entity_desciption = get_callable_str(entity_descriptor.description, entity_descriptor)
else:
entity_desciption = None
if entity_descriptor.full_name_plural:
entity_text = get_callable_str(
entity_descriptor.full_name_plural, entity_descriptor
)
else:
entity_text = entity_descriptor.name
if entity_descriptor.description:
entity_text = f"{entity_text} {get_callable_str(entity_descriptor.description, entity_descriptor)}"
state: FSMContext = kwargs["state"]
state_data = kwargs["state_data"]
@@ -168,8 +262,4 @@ async def entity_list(message: CallbackQuery | Message,
send_message = get_send_message(message)
await send_message(text = f"{entity_text}{f"\n{entity_desciption}" if entity_desciption else ""}",
reply_markup = keyboard_builder.as_markup())
from ..navigation import pop_navigation_context, save_navigation_context, clear_state
await send_message(text=entity_text, reply_markup=keyboard_builder.as_markup())