diff --git a/src/quickbot/bot/handlers/editors/boolean.py b/src/quickbot/bot/handlers/editors/boolean.py
index 29584f0..9481dcb 100644
--- a/src/quickbot/bot/handlers/editors/boolean.py
+++ b/src/quickbot/bot/handlers/editors/boolean.py
@@ -5,7 +5,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
from babel.support import LazyProxy
from logging import getLogger
-from ....model.descriptors import FieldDescriptor
+from ....model.descriptors import BotContext, FieldDescriptor
from ....model.user import UserBase
from ..context import ContextData, CallbackCommand
from ....utils.main import get_send_message
@@ -67,12 +67,20 @@ async def bool_editor(
state_data = kwargs["state_data"]
+ context = BotContext(
+ db_session=kwargs["db_session"],
+ app=kwargs["app"],
+ app_state=kwargs["app_state"],
+ message=message,
+ )
+
await wrap_editor(
keyboard_builder=keyboard_builder,
field_descriptor=field_descriptor,
callback_data=callback_data,
state_data=state_data,
user=user,
+ context=context,
)
state: FSMContext = kwargs["state"]
diff --git a/src/quickbot/bot/handlers/editors/date.py b/src/quickbot/bot/handlers/editors/date.py
index 040094f..716dcb5 100644
--- a/src/quickbot/bot/handlers/editors/date.py
+++ b/src/quickbot/bot/handlers/editors/date.py
@@ -6,7 +6,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
from logging import getLogger
from typing import TYPE_CHECKING
-from ....model.descriptors import FieldDescriptor
+from ....model.descriptors import BotContext, FieldDescriptor
from ....model.settings import Settings
from ....model.user import UserBase
from ..context import ContextData, CallbackCommand
@@ -158,6 +158,12 @@ async def time_picker(
)
state_data = kwargs["state_data"]
+ context = BotContext(
+ db_session=kwargs["db_session"],
+ app=kwargs["app"],
+ app_state=kwargs["app_state"],
+ message=message,
+ )
await wrap_editor(
keyboard_builder=keyboard_builder,
@@ -165,6 +171,7 @@ async def time_picker(
callback_data=callback_data,
state_data=state_data,
user=user,
+ context=context,
)
await state.set_data(state_data)
@@ -272,12 +279,20 @@ async def date_picker(
state_data = kwargs["state_data"]
+ context = BotContext(
+ db_session=kwargs["db_session"],
+ app=kwargs["app"],
+ app_state=kwargs["app_state"],
+ message=message,
+ )
+
await wrap_editor(
keyboard_builder=keyboard_builder,
field_descriptor=field_descriptor,
callback_data=callback_data,
state_data=state_data,
user=user,
+ context=context,
)
await state.set_data(state_data)
@@ -295,11 +310,11 @@ async def date_picker(
async def date_picker_year(
query: CallbackQuery,
callback_data: ContextData,
- app: "QBotApp",
state: FSMContext,
user: UserBase,
**kwargs,
):
+ app: "QBotApp" = kwargs["app"]
start_date = datetime.strptime(callback_data.data, "%Y-%m-%d %H-%M")
state_data = await state.get_data()
@@ -366,12 +381,20 @@ async def date_picker_year(
field_descriptor = get_field_descriptor(app, callback_data)
+ context = BotContext(
+ db_session=kwargs["db_session"],
+ app=app,
+ app_state=kwargs["app_state"],
+ message=query,
+ )
+
await wrap_editor(
keyboard_builder=keyboard_builder,
field_descriptor=field_descriptor,
callback_data=callback_data,
state_data=state_data,
user=user,
+ context=context,
)
await query.message.edit_reply_markup(reply_markup=keyboard_builder.as_markup())
@@ -380,9 +403,8 @@ async def date_picker_year(
@router.callback_query(
ContextData.filter(F.command == CallbackCommand.DATE_PICKER_MONTH)
)
-async def date_picker_month(
- query: CallbackQuery, callback_data: ContextData, app: "QBotApp", **kwargs
-):
+async def date_picker_month(query: CallbackQuery, callback_data: ContextData, **kwargs):
+ app: "QBotApp" = kwargs["app"]
field_descriptor = get_field_descriptor(app, callback_data)
state: FSMContext = kwargs["state"]
state_data = await state.get_data()
diff --git a/src/quickbot/bot/handlers/editors/entity.py b/src/quickbot/bot/handlers/editors/entity.py
index 6fdcc5f..15a1199 100644
--- a/src/quickbot/bot/handlers/editors/entity.py
+++ b/src/quickbot/bot/handlers/editors/entity.py
@@ -293,12 +293,20 @@ async def render_entity_picker(
state_data = kwargs["state_data"]
+ context = BotContext(
+ db_session=db_session,
+ app=kwargs["app"],
+ app_state=kwargs["app_state"],
+ message=message,
+ )
+
await wrap_editor(
keyboard_builder=keyboard_builder,
field_descriptor=field_descriptor,
callback_data=callback_data,
state_data=state_data,
user=user,
+ context=context,
)
await state.set_data(state_data)
diff --git a/src/quickbot/bot/handlers/editors/main.py b/src/quickbot/bot/handlers/editors/main.py
index f584a67..fbce44b 100644
--- a/src/quickbot/bot/handlers/editors/main.py
+++ b/src/quickbot/bot/handlers/editors/main.py
@@ -5,12 +5,13 @@ from aiogram.types import Message, CallbackQuery
from logging import getLogger
from sqlmodel.ext.asyncio.session import AsyncSession
-from quickbot.model.descriptors import EntityForm
+from quickbot.model.descriptors import BotContext, EntityForm
from ....model import EntityPermission
from ....model.settings import Settings
from ....model.user import UserBase
from ....utils.main import (
+ build_field_sequence,
check_entity_permission,
get_field_descriptor,
)
@@ -134,6 +135,22 @@ async def field_editor(message: Message | CallbackQuery, **kwargs):
form: EntityForm = entity_descriptor.forms.get(
form_name, entity_descriptor.default_form
)
+ if form.edit_field_sequence:
+ field_sequence = form.edit_field_sequence
+ else:
+ context = BotContext(
+ db_session=db_session,
+ app=app,
+ app_state=kwargs["app_state"],
+ message=message,
+ )
+ field_sequence = await build_field_sequence(
+ entity_descriptor=entity_descriptor,
+ user=user,
+ callback_data=callback_data,
+ state_data=state_data,
+ context=context,
+ )
entity_data = {
key: serialize(
getattr(
@@ -143,7 +160,7 @@ async def field_editor(message: Message | CallbackQuery, **kwargs):
entity_descriptor.fields_descriptors[key],
)
for key in (
- form.edit_field_sequence
+ field_sequence
if callback_data.context == CommandContext.ENTITY_EDIT
else [callback_data.field_name]
)
diff --git a/src/quickbot/bot/handlers/editors/main_callbacks.py b/src/quickbot/bot/handlers/editors/main_callbacks.py
index b100a73..9660e87 100644
--- a/src/quickbot/bot/handlers/editors/main_callbacks.py
+++ b/src/quickbot/bot/handlers/editors/main_callbacks.py
@@ -3,7 +3,7 @@ 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 typing import TYPE_CHECKING, Any
from decimal import Decimal
import json
@@ -39,6 +39,26 @@ if TYPE_CHECKING:
router = Router()
+async def _validate_value(
+ field_descriptor: FieldDescriptor,
+ value: Any,
+ message: Message | CallbackQuery,
+ **kwargs: Any,
+) -> bool | str:
+ if field_descriptor.validator:
+ context = BotContext(
+ db_session=kwargs["db_session"],
+ app=kwargs["app"],
+ app_state=kwargs["app_state"],
+ message=message,
+ )
+ if iscoroutinefunction(field_descriptor.validator):
+ return await field_descriptor.validator(value, context)
+ else:
+ return field_descriptor.validator(value, context)
+ return True
+
+
@router.message(CallbackCommandFilter(CallbackCommand.FIELD_EDITOR_CALLBACK))
@router.callback_query(
ContextData.filter(F.command == CallbackCommand.FIELD_EDITOR_CALLBACK)
@@ -59,6 +79,39 @@ async def field_editor_callback(message: Message | CallbackQuery, **kwargs):
field_descriptor = get_field_descriptor(app, callback_data)
type_base = field_descriptor.type_base
+ if type_base in [int, float, Decimal]:
+ try:
+ val = type_base(value) # @IgnoreException
+ except Exception:
+ return await message.answer(
+ text=(await Settings.get(Settings.APP_STRINGS_INVALID_INPUT))
+ )
+ elif type_base is str and field_descriptor.localizable:
+ locale_index = int(state_data.get("locale_index"))
+ val = {
+ list(LanguageBase.all_members.values())[
+ locale_index
+ ].value: message.text
+ }
+ else:
+ val = value
+
+ validation_result = await _validate_value(
+ field_descriptor=field_descriptor,
+ value=val,
+ message=message,
+ **kwargs,
+ )
+
+ if isinstance(validation_result, str):
+ return await message.answer(
+ text=f"{await Settings.get(Settings.APP_STRINGS_INVALID_INPUT)}\n{validation_result}"
+ )
+ elif not validation_result:
+ return await message.answer(
+ text=(await Settings.get(Settings.APP_STRINGS_INVALID_INPUT))
+ )
+
if type_base is str and field_descriptor.localizable:
locale_index = int(state_data.get("locale_index"))
@@ -90,13 +143,6 @@ async def field_editor_callback(message: Message | CallbackQuery, **kwargs):
**kwargs,
)
- 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:
@@ -150,6 +196,9 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
app: "QBotApp" = kwargs["app"]
entity_descriptor = get_entity_descriptor(app, callback_data)
+ entity_data = state_data.get("entity_data", {})
+ entity_data[field_descriptor.field_name] = value
+
if callback_data.context == CommandContext.COMMAND_FORM:
field_sequence = list(field_descriptor.command.param_form.keys())
current_index = field_sequence.index(callback_data.field_name)
@@ -167,11 +216,18 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
if form.edit_field_sequence:
field_sequence = form.edit_field_sequence
else:
- field_sequence = build_field_sequence(
+ context = BotContext(
+ db_session=kwargs["db_session"],
+ app=kwargs["app"],
+ app_state=kwargs["app_state"],
+ message=message,
+ )
+ field_sequence = await build_field_sequence(
entity_descriptor=entity_descriptor,
user=user,
callback_data=callback_data,
state_data=state_data,
+ context=context,
)
current_index = (
@@ -182,8 +238,6 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
)
field_descriptors = entity_descriptor.fields_descriptors
- entity_data = state_data.get("entity_data", {})
-
if callback_data.context == CommandContext.ENTITY_CREATE:
stack = state_data.get("navigation_stack", [])
prev_callback_data = ContextData.unpack(stack[-1]) if stack else None
@@ -224,7 +278,7 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
]
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})
next_field_name = field_sequence[current_index + 1]
@@ -252,7 +306,7 @@ async def process_field_edit_callback(message: Message | CallbackQuery, **kwargs
)
else:
- entity_data[field_descriptor.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
# if user has no CREATE_ALL permission
diff --git a/src/quickbot/bot/handlers/editors/string.py b/src/quickbot/bot/handlers/editors/string.py
index ad8cbca..81c0dd6 100644
--- a/src/quickbot/bot/handlers/editors/string.py
+++ b/src/quickbot/bot/handlers/editors/string.py
@@ -5,7 +5,7 @@ from aiogram.utils.keyboard import InlineKeyboardBuilder
from logging import getLogger
from typing import Any
-from ....model.descriptors import FieldDescriptor
+from ....model.descriptors import BotContext, FieldDescriptor
from ....model.language import LanguageBase
from ....model.settings import Settings
from ....model.user import UserBase
@@ -87,12 +87,20 @@ async def string_editor(
state_data = kwargs["state_data"]
+ context = BotContext(
+ db_session=kwargs["db_session"],
+ app=kwargs["app"],
+ app_state=kwargs["app_state"],
+ message=message,
+ )
+
await wrap_editor(
keyboard_builder=keyboard_builder,
field_descriptor=field_descriptor,
callback_data=callback_data,
state_data=state_data,
user=user,
+ context=context,
)
await state.set_data(state_data)
diff --git a/src/quickbot/bot/handlers/editors/wrapper.py b/src/quickbot/bot/handlers/editors/wrapper.py
index 4e5da80..29b8c7a 100644
--- a/src/quickbot/bot/handlers/editors/wrapper.py
+++ b/src/quickbot/bot/handlers/editors/wrapper.py
@@ -2,7 +2,7 @@ from aiogram.types import InlineKeyboardButton
from aiogram.utils.keyboard import InlineKeyboardBuilder
from ....model.settings import Settings
-from ....model.descriptors import EntityForm, FieldDescriptor
+from ....model.descriptors import BotContext, EntityForm, FieldDescriptor
from ....model.user import UserBase
from ..context import ContextData, CallbackCommand, CommandContext
from ....utils.navigation import get_navigation_context, pop_navigation_context
@@ -15,6 +15,7 @@ async def wrap_editor(
callback_data: ContextData,
state_data: dict,
user: UserBase,
+ context: BotContext,
):
if callback_data.context in [
CommandContext.ENTITY_CREATE,
@@ -42,11 +43,12 @@ async def wrap_editor(
if form.edit_field_sequence:
field_sequence = form.edit_field_sequence
else:
- field_sequence = build_field_sequence(
+ field_sequence = await build_field_sequence(
entity_descriptor=field_descriptor.entity_descriptor,
user=user,
callback_data=callback_data,
state_data=state_data,
+ context=context,
)
field_index = (
field_sequence.index(field_descriptor.name)
diff --git a/src/quickbot/bot/handlers/forms/entity_form.py b/src/quickbot/bot/handlers/forms/entity_form.py
index e063827..042dce4 100644
--- a/src/quickbot/bot/handlers/forms/entity_form.py
+++ b/src/quickbot/bot/handlers/forms/entity_form.py
@@ -208,11 +208,12 @@ async def entity_item(
if form.edit_field_sequence:
field_sequence = form.edit_field_sequence
else:
- field_sequence = build_field_sequence(
+ field_sequence = await build_field_sequence(
entity_descriptor=entity_descriptor,
user=user,
callback_data=callback_data,
state_data=state_data,
+ context=context,
)
edit_delete_row.append(
InlineKeyboardButton(
@@ -314,30 +315,32 @@ async def entity_item(
if skip:
continue
- field_caption = (
- await get_callable_str(
- callable_str=field_descriptor.caption,
- context=context,
- descriptor=field_descriptor,
- )
- if field_descriptor.caption
- else field_descriptor.field_name
- )
if field_descriptor.caption_value:
- value = await get_callable_str(
- callable_str=field_descriptor.caption_value,
- context=context,
- descriptor=field_descriptor,
- entity=entity_item,
- )
+ item_text += f"\n{
+ await get_callable_str(
+ callable_str=field_descriptor.caption_value,
+ context=context,
+ descriptor=field_descriptor,
+ entity=entity_item,
+ )
+ }"
else:
+ field_caption = (
+ await get_callable_str(
+ callable_str=field_descriptor.caption,
+ context=context,
+ descriptor=field_descriptor,
+ )
+ if field_descriptor.caption
+ else field_descriptor.field_name
+ )
value = await get_value_repr(
value=getattr(entity_item, field_descriptor.field_name),
field_descriptor=field_descriptor,
context=context,
locale=user.lang,
)
- item_text += f"\n{field_caption or field_descriptor.name}:{f' {value}' if value else ''}"
+ item_text += f"\n{field_caption or field_descriptor.name}:{f' {value}' if value else ''}"
context = pop_navigation_context(navigation_stack)
if context:
diff --git a/src/quickbot/bot/handlers/forms/entity_list.py b/src/quickbot/bot/handlers/forms/entity_list.py
index ed816df..6fbc555 100644
--- a/src/quickbot/bot/handlers/forms/entity_list.py
+++ b/src/quickbot/bot/handlers/forms/entity_list.py
@@ -118,6 +118,12 @@ async def entity_list(
)
keyboard_builder = InlineKeyboardBuilder()
+ context = BotContext(
+ db_session=db_session,
+ app=app,
+ app_state=kwargs["app_state"],
+ message=message,
+ )
if (
EntityPermission.CREATE in user_permissions
@@ -126,11 +132,12 @@ async def entity_list(
if form_item.edit_field_sequence:
field_sequence = form_item.edit_field_sequence
else:
- field_sequence = build_field_sequence(
+ field_sequence = await build_field_sequence(
entity_descriptor=entity_descriptor,
user=user,
callback_data=callback_data,
state_data=kwargs["state_data"],
+ context=context,
)
keyboard_builder.row(
InlineKeyboardButton(
@@ -206,13 +213,6 @@ async def entity_list(
total_pages = 1
page = 1
- context = BotContext(
- db_session=db_session,
- app=app,
- app_state=kwargs["app_state"],
- message=message,
- )
-
for item in items:
caption = None
diff --git a/src/quickbot/bot/handlers/user_handlers/main.py b/src/quickbot/bot/handlers/user_handlers/main.py
index b80352f..1b9dcb5 100644
--- a/src/quickbot/bot/handlers/user_handlers/main.py
+++ b/src/quickbot/bot/handlers/user_handlers/main.py
@@ -98,6 +98,7 @@ async def cammand_handler(message: Message | CallbackQuery, cmd: BotCommand, **k
db_session=kwargs["db_session"],
user=kwargs["user"],
app=app,
+ app_state=kwargs["app_state"],
state_data=state_data,
state=state,
i18n=kwargs["i18n"],
diff --git a/src/quickbot/main.py b/src/quickbot/main.py
index 291a343..e2c20c9 100644
--- a/src/quickbot/main.py
+++ b/src/quickbot/main.py
@@ -161,6 +161,7 @@ class QBotApp(Generic[UserType, ConfigType], FastAPI):
)
else:
for locale, description in command.caption.items():
+ locale = "default" if locale == "en" else locale
if locale not in commands_captions:
commands_captions[locale] = []
commands_captions[locale].append((command_name, description))
diff --git a/src/quickbot/model/descriptors.py b/src/quickbot/model/descriptors.py
index cc4f835..8aec2af 100644
--- a/src/quickbot/model/descriptors.py
+++ b/src/quickbot/model/descriptors.py
@@ -31,7 +31,7 @@ class FieldEditButton:
@dataclass
class CommandButton:
- command: ContextData | Callable[[ContextData, Any], ContextData] | str
+ command: ContextData | Callable[["BotEntity", "BotContext"], ContextData] | str
caption: str | LazyProxy | Callable[["BotEntity", "BotContext"], str] | None = None
visibility: Callable[["BotEntity", "BotContext"], bool] | None = None
@@ -113,6 +113,12 @@ class _BaseFieldDescriptor:
is_visible: (
bool | Callable[["FieldDescriptor", "BotEntity", "BotContext"], bool] | None
) = None
+ is_visible_in_edit_form: (
+ bool
+ | Callable[["FieldDescriptor", Union["BotEntity", Any], "BotContext"], bool]
+ | None
+ ) = None
+ validator: Callable[[Any, "BotContext"], Union[bool, str]] | None = None
localizable: bool = False
bool_false_value: str | LazyProxy = "no"
bool_true_value: str | LazyProxy = "yes"
@@ -219,6 +225,7 @@ class CommandCallbackContext[UT: UserBase]:
db_session: AsyncSession
user: UT
app: "QBotApp"
+ app_state: State
state_data: dict[str, Any]
state: FSMContext
form_data: dict[str, Any]
diff --git a/src/quickbot/model/settings.py b/src/quickbot/model/settings.py
index ed22011..cd8d26d 100644
--- a/src/quickbot/model/settings.py
+++ b/src/quickbot/model/settings.py
@@ -2,6 +2,7 @@ from types import NoneType, UnionType
from aiogram.utils.i18n.context import get_i18n
from datetime import datetime
from sqlmodel import SQLModel, Field, select
+from sqlmodel.ext.asyncio.session import AsyncSession
from typing import Any, get_args, get_origin
from ..db import async_session
@@ -193,11 +194,23 @@ class Settings(metaclass=SettingsMetaclass):
)
@classmethod
- async def get[T](cls, param: T, all_locales=False, locale: str = None) -> T:
+ async def get[T](
+ cls,
+ param: T,
+ session: AsyncSession = None,
+ all_locales=False,
+ locale: str = None,
+ ) -> T:
name = param.field_name
if name not in cls._cache.keys():
- cls._cache[name] = await cls.load_param(param)
+ if session is None:
+ async with async_session() as session:
+ cls._cache[name] = await cls.load_param(
+ session=session, param=param
+ )
+ else:
+ cls._cache[name] = await cls.load_param(session=session, param=param)
ret_val = cls._cache[name]
@@ -213,18 +226,17 @@ class Settings(metaclass=SettingsMetaclass):
return ret_val
@classmethod
- async def load_param(cls, param: FieldDescriptor) -> Any:
- async with async_session() as session:
- db_setting = (
- await session.exec(
- select(DbSettings).where(DbSettings.name == param.field_name)
- )
- ).first()
+ async def load_param(cls, session: AsyncSession, param: FieldDescriptor) -> Any:
+ db_setting = (
+ await session.exec(
+ select(DbSettings).where(DbSettings.name == param.field_name)
+ )
+ ).first()
- if db_setting:
- return await deserialize(
- session=session, type_=param.type_, value=db_setting.value
- )
+ if db_setting:
+ return await deserialize(
+ session=session, type_=param.type_, value=db_setting.value
+ )
return (
param.default_factory()
@@ -240,20 +252,20 @@ class Settings(metaclass=SettingsMetaclass):
)
)
- @classmethod
- async def load_params(cls):
- async with async_session() as session:
- db_settings = (await session.exec(select(DbSettings))).all()
- for db_setting in db_settings:
- if db_setting.name in cls.__dict__:
- setting = cls.__dict__[db_setting.name] # type: FieldDescriptor
- cls._cache[db_setting.name] = await deserialize(
- session=session,
- type_=setting.type_,
- value=db_setting.value,
- )
+ # @classmethod
+ # async def load_params(cls):
+ # async with async_session() as session:
+ # db_settings = (await session.exec(select(DbSettings))).all()
+ # for db_setting in db_settings:
+ # if db_setting.name in cls.__dict__:
+ # setting = cls.__dict__[db_setting.name] # type: FieldDescriptor
+ # cls._cache[db_setting.name] = await deserialize(
+ # session=session,
+ # type_=setting.type_,
+ # value=db_setting.value,
+ # )
- cls._loaded = True
+ # cls._loaded = True
@classmethod
async def set_param(cls, param: str | FieldDescriptor, value) -> None:
diff --git a/src/quickbot/utils/main.py b/src/quickbot/utils/main.py
index 3fd4393..455a907 100644
--- a/src/quickbot/utils/main.py
+++ b/src/quickbot/utils/main.py
@@ -15,6 +15,7 @@ from ..model.descriptors import (
FieldDescriptor,
EntityDescriptor,
EntityPermission,
+ _BaseFieldDescriptor,
)
from ..bot.handlers.context import CallbackCommand, ContextData, CommandContext
@@ -198,10 +199,13 @@ async def get_callable_str(
if len(args) == 3:
return await callable_str(descriptor, entity, context)
else:
- if issubclass(args[0].annotation, BotEntity):
- return await callable_str(entity, context)
- else:
+ param = args[next(iter(args))]
+ if not isinstance(param.annotation, str) and issubclass(
+ param.annotation, _BaseFieldDescriptor
+ ):
return await callable_str(descriptor, context)
+ else:
+ return await callable_str(entity, context)
else:
if len(args) == 3:
return callable_str(descriptor, entity, context)
@@ -245,11 +249,12 @@ def get_field_descriptor(
return None
-def build_field_sequence(
+async def build_field_sequence(
entity_descriptor: EntityDescriptor,
user: "UserBase",
callback_data: ContextData,
state_data: dict,
+ context: BotContext,
):
prev_form_list = None
@@ -269,47 +274,57 @@ def build_field_sequence(
prev_form_name, entity_descriptor.default_list
)
+ entity_data = state_data.get("entity_data", {})
+
field_sequence = list[str]()
# exclude ownership fields from edit if user has no CREATE_ALL/UPDATE_ALL permission
user_permissions = get_user_permissions(user, entity_descriptor)
for fd in entity_descriptor.fields_descriptors.values():
- skip = False
- if (
- fd.is_optional
- or fd.field_name == "id"
- or (
- fd.field_name[-3:] == "_id"
- and fd.field_name[:-3] in entity_descriptor.fields_descriptors
- )
- or fd.default is not None
- or fd.default_factory is not None
- ):
- skip = True
- for own_field in entity_descriptor.ownership_fields.items():
+ if isinstance(fd.is_visible_in_edit_form, bool):
+ skip = not fd.is_visible_in_edit_form
+ elif callable(fd.is_visible_in_edit_form):
+ if iscoroutinefunction(fd.is_visible_in_edit_form):
+ skip = not await fd.is_visible_in_edit_form(fd, entity_data, context)
+ else:
+ skip = not fd.is_visible_in_edit_form(fd, entity_data, context)
+ else:
+ skip = False
if (
- own_field[1].rstrip("_id") == fd.field_name.rstrip("_id")
- and own_field[0] in user.roles
- and (
- (
- EntityPermission.CREATE_ALL not in user_permissions
- and callback_data.context == CommandContext.ENTITY_CREATE
- )
- or (
- EntityPermission.UPDATE_ALL not in user_permissions
- and callback_data.context == CommandContext.ENTITY_EDIT
- )
+ fd.is_optional
+ or fd.field_name == "id"
+ or (
+ fd.field_name[-3:] == "_id"
+ and fd.field_name[:-3] in entity_descriptor.fields_descriptors
)
+ or fd.default is not None
+ or fd.default_factory is not None
):
skip = True
- break
+ for own_field in entity_descriptor.ownership_fields.items():
+ if (
+ own_field[1].rstrip("_id") == fd.field_name.rstrip("_id")
+ and own_field[0] in user.roles
+ and (
+ (
+ EntityPermission.CREATE_ALL not in user_permissions
+ and callback_data.context == CommandContext.ENTITY_CREATE
+ )
+ or (
+ EntityPermission.UPDATE_ALL not in user_permissions
+ and callback_data.context == CommandContext.ENTITY_EDIT
+ )
+ )
+ ):
+ skip = True
+ break
- if (
- prev_form_list
- and prev_form_list.static_filters
- and fd.field_name.rstrip("_id")
- in [f.field_name.rstrip("_id") for f in prev_form_list.static_filters]
- ):
- skip = True
+ if (
+ prev_form_list
+ and prev_form_list.static_filters
+ and fd.field_name.rstrip("_id")
+ in [f.field_name.rstrip("_id") for f in prev_form_list.static_filters]
+ ):
+ skip = True
if not skip:
field_sequence.append(fd.field_name)