diff --git a/src/trudex/application/bot/user_dialogs/main_menu.py b/src/trudex/application/bot/user_dialogs/main_menu.py index b6fe9a7..4254bed 100644 --- a/src/trudex/application/bot/user_dialogs/main_menu.py +++ b/src/trudex/application/bot/user_dialogs/main_menu.py @@ -11,6 +11,7 @@ from dishka.integrations.aiogram_dialog import inject from trudex.application.bot.user_dialogs.states import UserMenuSG from trudex.infrastructure.database.dao.group import GroupDAO from trudex.infrastructure.database.dao.user import UserDAO +from trudex.infrastructure.database.repo.test import TestRepository from trudex.infrastructure.database.repo.test_attempt import TestAttemptRepository @@ -101,8 +102,8 @@ async def on_edit_group_clicked( await manager.switch_to(UserMenuSG.edit_group) -async def on_tests_clicked(_callback: CallbackQuery, _button: Button, _manager: DialogManager): - await _callback.answer("🚧 В разработке") +async def on_tests_clicked(_callback: CallbackQuery, _button: Button, manager: DialogManager): + await manager.switch_to(UserMenuSG.available_tests) async def on_results_clicked(_callback: CallbackQuery, _button: Button, _manager: DialogManager): @@ -149,6 +150,31 @@ async def on_group_selected( await manager.switch_to(UserMenuSG.main) +@inject +async def get_available_tests( + dialog_manager: DialogManager, + user_dao: FromDishka[UserDAO], + test_repo: FromDishka[TestRepository], + **_kwargs +): + user_id = dialog_manager.event.from_user.id + user = await user_dao.get_by_id(user_id) + + if not user: + return {"tests": [], "count": 0} + + tests = await test_repo.get_available_tests_for_user(user_id, user.group) + + return { + "tests": [(f"📝 {t.title}", t.id) for t in tests], + "count": len(tests), + } + + +async def on_test_selected(_callback: CallbackQuery, _widget: Select, manager: DialogManager, item_id: str): + await _callback.answer("🚧 В разработке") + + user_menu_dialog = Dialog( Window( Format("{user_info}"), @@ -163,6 +189,24 @@ user_menu_dialog = Dialog( state=UserMenuSG.main, getter=get_user_data, ), + Window( + Format("📝 Доступные тесты\n\nВсего: {count}"), + ScrollingGroup( + Select( + Format("{item[0]}"), + id="test_select", + item_id_getter=lambda x: x[1], + items="tests", + on_click=on_test_selected, + ), + id="tests_scroll", + width=1, + height=7, + ), + Button(Const("◀️ Назад"), id="back", on_click=on_back_to_main), + state=UserMenuSG.available_tests, + getter=get_available_tests, + ), Window( Const("✏️ Изменение имени\n\nВведите новое имя:"), MessageInput(on_name_input), diff --git a/src/trudex/application/bot/user_dialogs/states.py b/src/trudex/application/bot/user_dialogs/states.py index dfe9a07..d34d148 100644 --- a/src/trudex/application/bot/user_dialogs/states.py +++ b/src/trudex/application/bot/user_dialogs/states.py @@ -3,6 +3,7 @@ from aiogram.fsm.state import State, StatesGroup class UserMenuSG(StatesGroup): main = State() + available_tests = State() edit_name = State() edit_group = State() diff --git a/src/trudex/infrastructure/database/repo/test.py b/src/trudex/infrastructure/database/repo/test.py index fba422b..4597f64 100644 --- a/src/trudex/infrastructure/database/repo/test.py +++ b/src/trudex/infrastructure/database/repo/test.py @@ -152,3 +152,35 @@ class TestRepository: ) return new_test + + async def get_available_tests_for_user(self, user_id: int, user_group: int | None) -> list[Test]: + from trudex.infrastructure.database.models import TestAttempt + + subquery = ( + select( + TestAttempt.test_id, + func.count(TestAttempt.id).label("attempts_count") + ) + .where(TestAttempt.user_id == user_id) + .where(TestAttempt.finished_at.isnot(None)) + .group_by(TestAttempt.test_id) + .subquery() + ) + + query = ( + select(TestModel) + .outerjoin(subquery, TestModel.id == subquery.c.test_id) + .where(TestModel.is_active == True) + .where( + (TestModel.for_group == user_group) | (TestModel.for_group.is_(None)) + ) + .where( + (TestModel.attempts.is_(None)) | + (subquery.c.attempts_count.is_(None)) | + (subquery.c.attempts_count < TestModel.attempts) + ) + ) + + result = await self.session.execute(query) + models = list(result.scalars().all()) + return [TestDTO(model).to_domain() for model in models]