From a0e9467b0d583ebdc6a81741902dec8c1189aeef Mon Sep 17 00:00:00 2001 From: kolo Date: Fri, 2 Jan 2026 21:31:44 +0300 Subject: [PATCH] commit --- .../versions/a879badde4a5_add_name_to_user.py | 25 +++++++++++++ .../application/bot/admin_dialogs/users.py | 4 ++- .../application/bot/creator_dialogs/users.py | 11 +++--- src/trudex/application/bot/handlers.py | 31 ++++++++++------ .../bot/user_dialogs/registration.py | 35 +++++++++++++++++-- .../application/bot/user_dialogs/states.py | 1 + src/trudex/domain/schemas.py | 1 + .../infrastructure/database/dao/test.py | 4 ++- .../infrastructure/database/dao/user.py | 9 +++++ .../infrastructure/database/dto/user.py | 1 + src/trudex/infrastructure/database/models.py | 1 + 11 files changed, 103 insertions(+), 20 deletions(-) create mode 100644 alembic/versions/a879badde4a5_add_name_to_user.py diff --git a/alembic/versions/a879badde4a5_add_name_to_user.py b/alembic/versions/a879badde4a5_add_name_to_user.py new file mode 100644 index 0000000..dd8dd66 --- /dev/null +++ b/alembic/versions/a879badde4a5_add_name_to_user.py @@ -0,0 +1,25 @@ +"""add_name_to_user + +Revision ID: a879badde4a5 +Revises: 520eccd2e55f +Create Date: 2026-01-02 21:21:22.159248 + +""" +from collections.abc import Sequence + +from alembic import op +import sqlalchemy as sa + + +revision: str = 'a879badde4a5' +down_revision: str | None = '520eccd2e55f' +branch_labels: str | Sequence[str] | None = None +depends_on: str | Sequence[str] | None = None + + +def upgrade() -> None: + op.add_column('users', sa.Column('name', sa.String(length=128), nullable=True)) + + +def downgrade() -> None: + op.drop_column('users', 'name') diff --git a/src/trudex/application/bot/admin_dialogs/users.py b/src/trudex/application/bot/admin_dialogs/users.py index a9c2f20..34a08b6 100644 --- a/src/trudex/application/bot/admin_dialogs/users.py +++ b/src/trudex/application/bot/admin_dialogs/users.py @@ -18,7 +18,7 @@ async def get_users_data(user_dao: FromDishka[UserDAO], **_kwargs): return { "users": [ - (f"{u.first_name} (@{u.username or 'нет'})", u.id) + (f"{u.name or u.first_name} (@{u.username or 'нет'})", u.id) for u in users ], "count": len(users), @@ -37,6 +37,7 @@ async def get_user_detail_data(dialog_manager: DialogManager, user_dao: FromDish username_str = f"@{user.username}" if user.username else "—" last_name_str = user.last_name or "—" + name_str = user.name or "—" group_str = str(user.group) if user.group else "—" admin_status = "✅ Да" if user.is_admin else "❌ Нет" @@ -45,6 +46,7 @@ async def get_user_detail_data(dialog_manager: DialogManager, user_dao: FromDish f"ID: {user.id}\n" f"Имя: {user.first_name}\n" f"Фамилия: {last_name_str}\n" + f"Имя и фамилия: {name_str}\n" f"Username: {username_str}\n" f"Группа: {group_str}\n" f"Администратор: {admin_status}" diff --git a/src/trudex/application/bot/creator_dialogs/users.py b/src/trudex/application/bot/creator_dialogs/users.py index a42b207..9b92fb8 100644 --- a/src/trudex/application/bot/creator_dialogs/users.py +++ b/src/trudex/application/bot/creator_dialogs/users.py @@ -18,7 +18,7 @@ async def get_users_data(user_dao: FromDishka[UserDAO], **_kwargs): return { "users": [ - (f"{u.first_name} (@{u.username or 'нет'})", u.id) + (f"{u.name or u.first_name} (@{u.username or 'нет'})", u.id) for u in users ], "count": len(users), @@ -36,15 +36,15 @@ async def get_user_detail_data(dialog_manager: DialogManager, user_dao: FromDish return {"user_info": "Пользователь не найден", "is_admin": True, "show_make_admin": False} username_str = f"@{user.username}" if user.username else "—" - last_name_str = user.last_name or "—" + name_str = user.name or "—" group_str = str(user.group) if user.group else "—" admin_status = "✅ Да" if user.is_admin else "❌ Нет" user_info = ( f"👤 Информация о пользователе\n\n" f"ID: {user.id}\n" - f"Имя: {user.first_name}\n" - f"Фамилия: {last_name_str}\n" + f"Ник: {user.first_name}\n" + f"Имя и фамилия: {name_str}\n" f"Username: {username_str}\n" f"Группа: {group_str}\n" f"Администратор: {admin_status}" @@ -68,8 +68,9 @@ async def get_confirm_data(dialog_manager: DialogManager, user_dao: FromDishka[U return {"user_info": "Пользователь не найден"} username_str = f"@{user.username}" if user.username else "—" + name_str = user.name or f"{user.first_name} {user.last_name or ''}".strip() return { - "user_info": f"{user.first_name}\n{username_str}\nID: {user.id}" + "user_info": f"{name_str}\n{username_str}\nID: {user.id}" } diff --git a/src/trudex/application/bot/handlers.py b/src/trudex/application/bot/handlers.py index c19724e..663f378 100644 --- a/src/trudex/application/bot/handlers.py +++ b/src/trudex/application/bot/handlers.py @@ -32,7 +32,7 @@ async def start_handler( groups = await group_dao.get_all() if len(groups) > 0: - # Есть группы - создаем пользователя без группы и показываем выбор + # Есть группы - создаем пользователя без группы и имени, показываем регистрацию await user_dao.create( user_id=message.from_user.id, first_name=message.from_user.first_name, @@ -40,7 +40,7 @@ async def start_handler( last_name=message.from_user.last_name, ) await dialog_manager.start( - UserRegistrationSG.select_group, + UserRegistrationSG.input_name, mode=StartMode.RESET_STACK, data={"user_id": message.from_user.id} ) @@ -55,18 +55,27 @@ async def start_handler( await dialog_manager.start(UserMenuSG.main, mode=StartMode.RESET_STACK) else: # Существующий пользователь - # Проверяем, выбрал ли он группу + # Проверяем, заполнил ли он имя и группу groups = await group_dao.get_all() - if len(groups) > 0 and existing_user.group is None: - # Есть группы, но пользователь не выбрал группу - показываем выбор - await dialog_manager.start( - UserRegistrationSG.select_group, - mode=StartMode.RESET_STACK, - data={"user_id": message.from_user.id} - ) + if len(groups) > 0 and (existing_user.name is None or existing_user.group is None): + # Есть группы, но пользователь не завершил регистрацию + if existing_user.name is None: + # Начинаем с ввода имени + await dialog_manager.start( + UserRegistrationSG.input_name, + mode=StartMode.RESET_STACK, + data={"user_id": message.from_user.id} + ) + else: + # Имя есть, но нет группы + await dialog_manager.start( + UserRegistrationSG.select_group, + mode=StartMode.RESET_STACK, + data={"user_id": message.from_user.id} + ) else: - # Группа выбрана или групп нет - обновляем данные и открываем меню + # Регистрация завершена или групп нет - обновляем данные и открываем меню await user_dao.upsert( user_id=message.from_user.id, first_name=message.from_user.first_name, diff --git a/src/trudex/application/bot/user_dialogs/registration.py b/src/trudex/application/bot/user_dialogs/registration.py index d1d77f0..632e983 100644 --- a/src/trudex/application/bot/user_dialogs/registration.py +++ b/src/trudex/application/bot/user_dialogs/registration.py @@ -1,5 +1,6 @@ -from aiogram.types import CallbackQuery +from aiogram.types import CallbackQuery, Message from aiogram_dialog import Dialog, DialogManager, StartMode, Window +from aiogram_dialog.widgets.input import MessageInput from aiogram_dialog.widgets.kbd import ScrollingGroup, Select from aiogram_dialog.widgets.text import Const, Format from dishka import FromDishka @@ -11,6 +12,28 @@ from trudex.infrastructure.database.dao.group import GroupDAO from trudex.infrastructure.database.dao.user import UserDAO +@inject +async def on_name_input(message: Message, _widget: MessageInput, manager: DialogManager, user_dao: FromDishka[UserDAO]): + if not message.text: + await message.answer("❌ Имя и фамилия не могут быть пустыми") + return + + name = message.text.strip() + if not name: + await message.answer("❌ Имя и фамилия не могут быть пустыми") + return + + if len(name) > 128: + await message.answer("❌ Имя и фамилия слишком длинные (максимум 128 символов)") + return + + user_id = manager.start_data.get("user_id") + await user_dao.update(user_id=user_id, name=name) + + manager.dialog_data["name"] = name + await manager.switch_to(UserRegistrationSG.select_group) + + @inject async def get_groups_for_registration(dialog_manager: DialogManager, group_dao: FromDishka[GroupDAO], **_kwargs): groups = await group_dao.get_all() @@ -34,7 +57,15 @@ registration_dialog = Dialog( Window( Const( "👋 Добро пожаловать!\n\n" - "🎓 Выберите вашу группу:\n\n" + "✏️ Введите ваше имя и фамилию:\n\n" + "⚠️ Внимание: Изменить данные можно будет только через 24 часа!" + ), + MessageInput(on_name_input), + state=UserRegistrationSG.input_name, + ), + Window( + Const( + "🎓 Выберите вашу группу:\n\n" "⚠️ Внимание: Изменить группу можно будет только через 24 часа!" ), ScrollingGroup( diff --git a/src/trudex/application/bot/user_dialogs/states.py b/src/trudex/application/bot/user_dialogs/states.py index e89b889..efb7e10 100644 --- a/src/trudex/application/bot/user_dialogs/states.py +++ b/src/trudex/application/bot/user_dialogs/states.py @@ -6,4 +6,5 @@ class UserMenuSG(StatesGroup): class UserRegistrationSG(StatesGroup): + input_name = State() select_group = State() diff --git a/src/trudex/domain/schemas.py b/src/trudex/domain/schemas.py index f8c4634..40c5949 100644 --- a/src/trudex/domain/schemas.py +++ b/src/trudex/domain/schemas.py @@ -8,6 +8,7 @@ class User: first_name: str username: str | None = None last_name: str | None = None + name: str | None = None group: int | None = None is_admin: bool = False created_at: datetime | None = None diff --git a/src/trudex/infrastructure/database/dao/test.py b/src/trudex/infrastructure/database/dao/test.py index 6a67452..88266b5 100644 --- a/src/trudex/infrastructure/database/dao/test.py +++ b/src/trudex/infrastructure/database/dao/test.py @@ -18,7 +18,9 @@ class TestDAO: return TestDTO(model).to_domain() if model else None async def get_all(self) -> list[DomainTest]: - result = await self.session.execute(select(Test)) + result = await self.session.execute( + select(Test).order_by(Test.id) + ) models = list(result.scalars().all()) return [TestDTO(model).to_domain() for model in models] diff --git a/src/trudex/infrastructure/database/dao/user.py b/src/trudex/infrastructure/database/dao/user.py index 8464bf9..a515a8d 100644 --- a/src/trudex/infrastructure/database/dao/user.py +++ b/src/trudex/infrastructure/database/dao/user.py @@ -28,6 +28,7 @@ class UserDAO: first_name: str, username: str | None = None, last_name: str | None = None, + name: str | None = None, group: int | None = None, is_admin: bool = False, ) -> DomainUser: @@ -36,6 +37,7 @@ class UserDAO: username=username, first_name=first_name, last_name=last_name, + name=name, group=group, is_admin=is_admin, ) @@ -50,6 +52,7 @@ class UserDAO: username: str | None = None, first_name: str | None = None, last_name: str | None = None, + name: str | None = None, group: int | None = None, is_admin: bool | None = None, ) -> DomainUser | None: @@ -66,6 +69,8 @@ class UserDAO: user.first_name = first_name if last_name is not None: user.last_name = last_name + if name is not None: + user.name = name if group is not None: user.group = group if is_admin is not None: @@ -93,6 +98,7 @@ class UserDAO: first_name: str, username: str | None = None, last_name: str | None = None, + name: str | None = None, group: int | None = None, is_admin: bool = False, ) -> DomainUser: @@ -108,6 +114,8 @@ class UserDAO: user.first_name = first_name if last_name is not None: user.last_name = last_name + if name is not None: + user.name = name if group is not None: user.group = group if is_admin is not None: @@ -121,6 +129,7 @@ class UserDAO: username=username, first_name=first_name, last_name=last_name, + name=name, group=group, is_admin=is_admin, ) diff --git a/src/trudex/infrastructure/database/dto/user.py b/src/trudex/infrastructure/database/dto/user.py index 5e69349..4354ed6 100644 --- a/src/trudex/infrastructure/database/dto/user.py +++ b/src/trudex/infrastructure/database/dto/user.py @@ -12,6 +12,7 @@ class UserDTO: username=self.model.username, first_name=self.model.first_name, last_name=self.model.last_name, + name=self.model.name, group=self.model.group, is_admin=self.model.is_admin, created_at=self.model.created_at, diff --git a/src/trudex/infrastructure/database/models.py b/src/trudex/infrastructure/database/models.py index 2d03310..123f9e6 100644 --- a/src/trudex/infrastructure/database/models.py +++ b/src/trudex/infrastructure/database/models.py @@ -19,6 +19,7 @@ class User(Base): username: Mapped[str | None] = mapped_column(String(32)) first_name: Mapped[str] = mapped_column(String(64)) last_name: Mapped[str | None] = mapped_column(String(64)) + name: Mapped[str | None] = mapped_column(String(128)) group: Mapped[int | None] = mapped_column(CheckConstraint("group >= 1000 AND group <= 9999")) is_admin: Mapped[bool] = mapped_column(default=False) created_at: Mapped[datetime] = mapped_column(server_default=func.now())