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]