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

@@ -0,0 +1,284 @@
from aiogram import Router, F
from aiogram.types import Message, CallbackQuery
from aiogram.fsm.context import FSMContext
from sqlmodel.ext.asyncio.session import AsyncSession
from typing import TYPE_CHECKING
from decimal import Decimal
import json
from ..context import ContextData, CallbackCommand, CommandContext
from ...command_context_filter import CallbackCommandFilter
from ....model import EntityPermission
from ....model.user import UserBase
from ....model.settings import Settings
from ....model.descriptors import EntityFieldDescriptor
from ....model.language import LanguageBase
from ....auth import authorize_command
from ....utils.main import (
get_user_permissions,
check_entity_permission,
clear_state,
get_entity_descriptor,
get_field_descriptor,
)
from ....utils.serialization import deserialize
from ..common.routing import route_callback
from .common import show_editor
if TYPE_CHECKING:
from ....main import QBotApp
router = Router()
@router.message(CallbackCommandFilter(CallbackCommand.FIELD_EDITOR_CALLBACK))
@router.callback_query(
ContextData.filter(F.command == CallbackCommand.FIELD_EDITOR_CALLBACK)
)
async def field_editor_callback(message: Message | CallbackQuery, **kwargs):
app: "QBotApp" = kwargs["app"]
state: FSMContext = kwargs["state"]
state_data = await state.get_data()
kwargs["state_data"] = state_data
if isinstance(message, Message):
callback_data: ContextData = kwargs.get("callback_data", None)
context_data = state_data.get("context_data")
if context_data:
callback_data = ContextData.unpack(context_data)
value = message.text
field_descriptor = get_field_descriptor(app, callback_data)
type_base = field_descriptor.type_base
if type_base is str and field_descriptor.localizable:
locale_index = int(state_data.get("locale_index"))
value = state_data.get("value")
if value:
value = json.loads(value)
else:
value = {}
value[list(LanguageBase.all_members.keys())[locale_index]] = message.text
value = json.dumps(value, ensure_ascii=False)
if locale_index < len(LanguageBase.all_members.values()) - 1:
current_value = state_data.get("current_value")
state_data.update({"value": value})
entity_descriptor = get_entity_descriptor(app, callback_data)
kwargs.update({"callback_data": callback_data})
return await show_editor(
message=message,
locale_index=locale_index + 1,
field_descriptor=field_descriptor,
entity_descriptor=entity_descriptor,
current_value=current_value,
value=value,
**kwargs,
)
# else:
# value = state_data.get("value")
# if value:
# value = json.loads(value)
# else:
# value = {}
# value[list(LanguageBase.all_members.keys())[locale_index]] = (
# message.text
# )
# value = json.dumps(value, ensure_ascii=False)
elif type_base in [int, float, Decimal]:
try:
_ = type_base(value) # @IgnoreException
except Exception:
return await message.answer(
text=(await Settings.get(Settings.APP_STRINGS_INVALID_INPUT))
)
else:
callback_data: ContextData = kwargs["callback_data"]
if callback_data.data:
if callback_data.data == "skip":
value = None
else:
value = callback_data.data
else:
value = state_data.get("value")
field_descriptor = get_field_descriptor(app, callback_data)
kwargs.update(
{
"callback_data": callback_data,
}
)
await process_field_edit_callback(
message=message, value=value, field_descriptor=field_descriptor, **kwargs
)
async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs):
user: UserBase = kwargs["user"]
db_session: AsyncSession = kwargs["db_session"]
callback_data: ContextData = kwargs.get("callback_data", None)
state_data: dict = kwargs["state_data"]
value = kwargs["value"]
field_descriptor: EntityFieldDescriptor = kwargs["field_descriptor"]
if callback_data.context == CommandContext.SETTING_EDIT:
if callback_data.data != "cancel":
if await authorize_command(user=user, callback_data=callback_data):
value = await deserialize(
session=db_session, type_=field_descriptor.type_, value=value
)
await Settings.set_param(field_descriptor, value)
else:
return await message.answer(
text=(await Settings.get(Settings.APP_STRINGS_FORBIDDEN))
)
return await route_callback(message=message, back=True, **kwargs)
elif callback_data.context in [
CommandContext.ENTITY_CREATE,
CommandContext.ENTITY_EDIT,
CommandContext.ENTITY_FIELD_EDIT,
]:
app: "QBotApp" = kwargs["app"]
entity_descriptor = get_entity_descriptor(app, callback_data)
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
current_index = (
field_sequence.index(callback_data.field_name)
if callback_data.context
in [CommandContext.ENTITY_CREATE, CommandContext.ENTITY_EDIT]
else 0
)
entity_data = state_data.get("entity_data", {})
if (
callback_data.context
in [CommandContext.ENTITY_CREATE, CommandContext.ENTITY_EDIT]
and current_index < len(field_sequence) - 1
):
entity_data[field_descriptor.field_name] = value
state_data.update({"entity_data": entity_data})
next_field_name = field_sequence[current_index + 1]
next_field_descriptor = entity_descriptor.fields_descriptors[
next_field_name
]
kwargs.update({"field_descriptor": next_field_descriptor})
callback_data.field_name = next_field_name
state_entity_val = entity_data.get(next_field_descriptor.field_name)
current_value = (
await deserialize(
session=db_session,
type_=next_field_descriptor.type_,
value=state_entity_val,
)
if state_entity_val
else None
)
await show_editor(
message=message,
entity_descriptor=entity_descriptor,
current_value=current_value,
**kwargs,
)
else:
entity_type = entity_descriptor.type_
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
# if user has no CREATE_ALL permission
# for role in user.roles:
# if role in entity_descriptor.ownership_fields and not EntityPermission.CREATE_ALL in user_permissions:
# entity_data[entity_descriptor.ownership_fields[role]] = user.id
deser_entity_data = {
key: await deserialize(
session=db_session,
type_=entity_descriptor.fields_descriptors[key].type_,
value=value,
)
for key, value in entity_data.items()
}
if callback_data.context == CommandContext.ENTITY_CREATE:
user_permissions = get_user_permissions(user, entity_descriptor)
if (
EntityPermission.CREATE not in user_permissions
and EntityPermission.CREATE_ALL not in user_permissions
):
return await message.answer(
text=(await Settings.get(Settings.APP_STRINGS_FORBIDDEN))
)
new_entity = await entity_type.create(
session=db_session,
obj_in=entity_type(**deser_entity_data),
commit=True,
)
form_name = (
callback_data.form_params.split("&")[0]
if callback_data.form_params
else "default"
)
form_list = entity_descriptor.lists.get(
form_name or "default", entity_descriptor.default_list
)
state_data["navigation_context"] = ContextData(
command=CallbackCommand.ENTITY_ITEM,
entity_name=entity_descriptor.name,
form_params=form_list.item_form,
entity_id=str(new_entity.id),
).pack()
state_data.update(state_data)
elif callback_data.context in [
CommandContext.ENTITY_EDIT,
CommandContext.ENTITY_FIELD_EDIT,
]:
entity_id = int(callback_data.entity_id)
entity = await entity_type.get(session=db_session, id=entity_id)
if not entity:
return await message.answer(
text=(await Settings.get(Settings.APP_STRINGS_NOT_FOUND))
)
if not check_entity_permission(
entity=entity, user=user, permission=EntityPermission.UPDATE
):
return await message.answer(
text=(await Settings.get(Settings.APP_STRINGS_FORBIDDEN))
)
for key, value in deser_entity_data.items():
setattr(entity, key, value)
await db_session.commit()
clear_state(state_data=state_data)
await route_callback(message=message, back=True, **kwargs)