mirror of
https://github.com/koloideal/Quizzi.git
synced 2026-06-10 10:25:28 +03:00
commit
This commit is contained in:
@@ -8,22 +8,35 @@ from aiogram_dialog import setup_dialogs
|
||||
from dishka import make_async_container
|
||||
from dishka.integrations.aiogram import setup_dishka
|
||||
|
||||
from trudex.application.bot.admin_dialogs.broadcast import broadcast_dialog as admin_broadcast_dialog
|
||||
from trudex.application.bot.admin_dialogs.groups import groups_dialog as admin_groups_dialog
|
||||
from trudex.application.bot.admin_dialogs.broadcast import \
|
||||
broadcast_dialog as admin_broadcast_dialog
|
||||
from trudex.application.bot.admin_dialogs.groups import \
|
||||
groups_dialog as admin_groups_dialog
|
||||
from trudex.application.bot.admin_dialogs.main_menu import admin_menu_dialog
|
||||
from trudex.application.bot.admin_dialogs.tests import tests_dialog as admin_tests_dialog
|
||||
from trudex.application.bot.admin_dialogs.users import users_dialog as admin_users_dialog
|
||||
from trudex.application.bot.creator_dialogs.broadcast import broadcast_dialog as creator_broadcast_dialog
|
||||
from trudex.application.bot.creator_dialogs.create_test import create_test_dialog
|
||||
from trudex.application.bot.creator_dialogs.groups import groups_dialog as creator_groups_dialog
|
||||
from trudex.application.bot.creator_dialogs.main_menu import creator_menu_dialog
|
||||
from trudex.application.bot.creator_dialogs.tests import tests_dialog as creator_tests_dialog
|
||||
from trudex.application.bot.creator_dialogs.users import users_dialog as creator_users_dialog
|
||||
from trudex.application.bot.admin_dialogs.tests import \
|
||||
tests_dialog as admin_tests_dialog
|
||||
from trudex.application.bot.admin_dialogs.users import \
|
||||
users_dialog as admin_users_dialog
|
||||
from trudex.application.bot.creator_dialogs.broadcast import \
|
||||
broadcast_dialog as creator_broadcast_dialog
|
||||
from trudex.application.bot.creator_dialogs.create_test import \
|
||||
create_test_dialog
|
||||
from trudex.application.bot.creator_dialogs.groups import \
|
||||
groups_dialog as creator_groups_dialog
|
||||
from trudex.application.bot.creator_dialogs.main_menu import \
|
||||
creator_menu_dialog
|
||||
from trudex.application.bot.creator_dialogs.tests import \
|
||||
tests_dialog as creator_tests_dialog
|
||||
from trudex.application.bot.creator_dialogs.users import \
|
||||
users_dialog as creator_users_dialog
|
||||
from trudex.application.bot.handlers import router
|
||||
from trudex.application.bot.middlewares.reject_not_admin import RejectNotAdminMiddleware
|
||||
from trudex.application.bot.middlewares.reject_not_creator import RejectNotCreatorMiddleware
|
||||
from trudex.application.bot.middlewares.reject_not_admin import \
|
||||
RejectNotAdminMiddleware
|
||||
from trudex.application.bot.middlewares.reject_not_creator import \
|
||||
RejectNotCreatorMiddleware
|
||||
from trudex.application.bot.user_dialogs.main_menu import user_menu_dialog
|
||||
from trudex.application.bot.user_dialogs.registration import registration_dialog
|
||||
from trudex.application.bot.user_dialogs.registration import \
|
||||
registration_dialog
|
||||
from trudex.infrastructure.database.repo.user import UserRepository
|
||||
from trudex.infrastructure.di import DatabaseProvider
|
||||
from trudex.infrastructure.utils.bot_commands import setup_bot_commands
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
from aiogram.types import CallbackQuery, Message
|
||||
from aiogram_dialog import Dialog, DialogManager, Window, StartMode
|
||||
from aiogram_dialog import Dialog, DialogManager, StartMode, Window
|
||||
from aiogram_dialog.widgets.input import MessageInput
|
||||
from aiogram_dialog.widgets.kbd import Button, Row
|
||||
from aiogram_dialog.widgets.text import Const
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
from trudex.application.bot.admin_dialogs.states import AdminBroadcastSG, AdminMenuSG
|
||||
from trudex.application.bot.admin_dialogs.states import (AdminBroadcastSG,
|
||||
AdminMenuSG)
|
||||
from trudex.infrastructure.database.dao.user import UserDAO
|
||||
from trudex.infrastructure.utils.broadcast import broadcast_message
|
||||
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
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 Button, Column, Row, ScrollingGroup, Select
|
||||
from aiogram_dialog.widgets.kbd import (Button, Column, Row, ScrollingGroup,
|
||||
Select)
|
||||
from aiogram_dialog.widgets.text import Const, Format
|
||||
from dishka.integrations.aiogram import CONTAINER_NAME
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
from trudex.application.bot.admin_dialogs.states import AdminGroupsSG, AdminMenuSG
|
||||
from trudex.application.bot.admin_dialogs.states import (AdminGroupsSG,
|
||||
AdminMenuSG)
|
||||
from trudex.infrastructure.database.dao.group import GroupDAO
|
||||
|
||||
|
||||
@@ -13,10 +16,8 @@ async def on_group_click(_callback: CallbackQuery, _widget, _manager: DialogMana
|
||||
await _callback.answer("ℹ️ Для удаления используйте кнопку 'Удалить группу'")
|
||||
|
||||
|
||||
async def get_groups_data(dialog_manager: DialogManager, **_kwargs):
|
||||
container = dialog_manager.middleware_data[CONTAINER_NAME]
|
||||
group_dao = await container.get(GroupDAO)
|
||||
|
||||
@inject
|
||||
async def get_groups_data(dialog_manager: DialogManager, group_dao: FromDishka[GroupDAO], **_kwargs):
|
||||
groups = await group_dao.get_all()
|
||||
|
||||
success_message = dialog_manager.dialog_data.pop("success_message", None)
|
||||
@@ -45,7 +46,8 @@ async def on_back_to_menu(_callback: CallbackQuery, _button: Button, manager: Di
|
||||
await manager.start(AdminMenuSG.main, mode=StartMode.RESET_STACK)
|
||||
|
||||
|
||||
async def on_group_number_input(message: Message, _widget: MessageInput, manager: DialogManager):
|
||||
@inject
|
||||
async def on_group_number_input(message: Message, _widget: MessageInput, manager: DialogManager, group_dao: FromDishka[GroupDAO]):
|
||||
if not message.text:
|
||||
await message.answer("❌ Номер группы не может быть пустым")
|
||||
return
|
||||
@@ -62,9 +64,6 @@ async def on_group_number_input(message: Message, _widget: MessageInput, manager
|
||||
await message.answer("❌ Номер группы должен быть четырехзначным (1000-9999)")
|
||||
return
|
||||
|
||||
container = manager.middleware_data[CONTAINER_NAME]
|
||||
group_dao = await container.get(GroupDAO)
|
||||
|
||||
existing = await group_dao.get_by_number(number)
|
||||
if existing:
|
||||
await message.answer(f"❌ Группа с номером {number} уже существует")
|
||||
@@ -84,10 +83,8 @@ async def on_cancel_add(_callback: CallbackQuery, _button: Button, manager: Dial
|
||||
await manager.switch_to(AdminGroupsSG.groups_list)
|
||||
|
||||
|
||||
async def get_delete_groups_data(dialog_manager: DialogManager, **_kwargs):
|
||||
container = dialog_manager.middleware_data[CONTAINER_NAME]
|
||||
group_dao = await container.get(GroupDAO)
|
||||
|
||||
@inject
|
||||
async def get_delete_groups_data(dialog_manager: DialogManager, group_dao: FromDishka[GroupDAO], **_kwargs):
|
||||
groups = await group_dao.get_all()
|
||||
|
||||
return {
|
||||
@@ -96,10 +93,8 @@ async def get_delete_groups_data(dialog_manager: DialogManager, **_kwargs):
|
||||
}
|
||||
|
||||
|
||||
async def on_select_group_to_delete(_callback: CallbackQuery, _widget, manager: DialogManager, item_id: str):
|
||||
container = manager.middleware_data[CONTAINER_NAME]
|
||||
group_dao = await container.get(GroupDAO)
|
||||
|
||||
@inject
|
||||
async def on_select_group_to_delete(_callback: CallbackQuery, _widget, manager: DialogManager, item_id: str, group_dao: FromDishka[GroupDAO]):
|
||||
group = await group_dao.get_by_id(int(item_id))
|
||||
if not group:
|
||||
await _callback.answer("❌ Группа не найдена", show_alert=True)
|
||||
@@ -118,10 +113,8 @@ async def get_delete_confirm_data(dialog_manager: DialogManager, **_kwargs):
|
||||
}
|
||||
|
||||
|
||||
async def on_confirm_delete(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
||||
container = manager.middleware_data[CONTAINER_NAME]
|
||||
group_dao = await container.get(GroupDAO)
|
||||
|
||||
@inject
|
||||
async def on_confirm_delete(_callback: CallbackQuery, _button: Button, manager: DialogManager, group_dao: FromDishka[GroupDAO]):
|
||||
group_id = manager.dialog_data.get("delete_group_id")
|
||||
|
||||
await group_dao.delete(group_id)
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
from aiogram.types import CallbackQuery
|
||||
from aiogram_dialog import Dialog, DialogManager, Window, StartMode
|
||||
from aiogram_dialog import Dialog, DialogManager, StartMode, Window
|
||||
from aiogram_dialog.widgets.kbd import Button, Column
|
||||
from aiogram_dialog.widgets.text import Const
|
||||
|
||||
from trudex.application.bot.admin_dialogs.states import AdminMenuSG, AdminUsersSG, AdminTestsSG, AdminBroadcastSG, AdminGroupsSG
|
||||
from trudex.application.bot.admin_dialogs.states import (AdminBroadcastSG,
|
||||
AdminGroupsSG,
|
||||
AdminMenuSG,
|
||||
AdminTestsSG,
|
||||
AdminUsersSG)
|
||||
|
||||
|
||||
async def on_tests_clicked(_callback: CallbackQuery, _button: Button, manager: DialogManager) -> None:
|
||||
|
||||
@@ -13,6 +13,7 @@ class AdminUsersSG(StatesGroup):
|
||||
|
||||
class AdminTestsSG(StatesGroup):
|
||||
tests_list = State()
|
||||
test_detail = State()
|
||||
|
||||
|
||||
class AdminBroadcastSG(StatesGroup):
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
from aiogram.types import CallbackQuery
|
||||
from aiogram_dialog import Dialog, DialogManager, Window, StartMode
|
||||
from aiogram_dialog.widgets.kbd import Button, Column, ScrollingGroup, Select
|
||||
from aiogram_dialog import Dialog, DialogManager, StartMode, Window
|
||||
from aiogram_dialog.widgets.kbd import (Button, Column, Row, ScrollingGroup,
|
||||
Select)
|
||||
from aiogram_dialog.widgets.text import Const, Format
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
from trudex.application.bot.admin_dialogs.states import AdminTestsSG, AdminMenuSG
|
||||
from trudex.application.bot.admin_dialogs.states import (AdminMenuSG,
|
||||
AdminTestsSG)
|
||||
from trudex.infrastructure.database.dao.test import TestDAO
|
||||
from trudex.infrastructure.database.repo.test import TestRepository
|
||||
|
||||
|
||||
@inject
|
||||
@@ -24,7 +27,74 @@ async def get_tests_data(test_dao: FromDishka[TestDAO], **_kwargs):
|
||||
|
||||
async def on_test_selected(_callback: CallbackQuery, _widget: Select, manager: DialogManager, item_id: str):
|
||||
manager.dialog_data["selected_test_id"] = int(item_id)
|
||||
await _callback.answer("Тест выбран")
|
||||
await manager.switch_to(AdminTestsSG.test_detail)
|
||||
|
||||
|
||||
@inject
|
||||
async def get_test_detail(test_dao: FromDishka[TestDAO], test_repo: FromDishka[TestRepository], dialog_manager: DialogManager, **_kwargs):
|
||||
test_id = dialog_manager.dialog_data.get("selected_test_id")
|
||||
|
||||
if not test_id:
|
||||
return {
|
||||
"test_info": "Тест не найден",
|
||||
"is_active": False,
|
||||
"button_text": "◀️ Назад",
|
||||
}
|
||||
|
||||
test = await test_dao.get_by_id(test_id)
|
||||
questions_count = await test_repo.count_questions_in_test(test_id)
|
||||
|
||||
if not test:
|
||||
return {
|
||||
"test_info": "Тест не найден",
|
||||
"is_active": False,
|
||||
"button_text": "◀️ Назад",
|
||||
}
|
||||
|
||||
status = "🟢 Активен" if test.is_active else "🔴 Деактивирован"
|
||||
password_str = f"🔒 {test.password}" if test.password else "🔓 Без пароля"
|
||||
expires_str = test.expires_at.strftime("%d.%m.%Y %H:%M") if test.expires_at else "♾️ Без срока"
|
||||
group_str = f"🎓 Группа {test.for_group}" if test.for_group else "👥 Для всех"
|
||||
|
||||
test_info = (
|
||||
f"<b>📝 Информация о тесте</b>\n\n"
|
||||
f"<b>Название:</b> {test.title}\n"
|
||||
f"<b>Описание:</b> {test.description or '—'}\n\n"
|
||||
f"<b>Статус:</b> {status}\n"
|
||||
f"<b>Вопросов:</b> {questions_count}\n"
|
||||
f"{password_str}\n"
|
||||
f"{expires_str}\n"
|
||||
f"{group_str}\n\n"
|
||||
f"<b>Создан:</b> {test.created_at.strftime('%d.%m.%Y %H:%M') if test.created_at else '—'}"
|
||||
)
|
||||
|
||||
button_text = "🔴 Деактивировать" if test.is_active else "🟢 Активировать"
|
||||
|
||||
return {
|
||||
"test_info": test_info,
|
||||
"is_active": test.is_active,
|
||||
"button_text": button_text,
|
||||
}
|
||||
|
||||
|
||||
@inject
|
||||
async def on_toggle_active(_callback: CallbackQuery, _button: Button, manager: DialogManager, test_dao: FromDishka[TestDAO]):
|
||||
test_id = manager.dialog_data.get("selected_test_id")
|
||||
if not test_id:
|
||||
await _callback.answer("❌ Тест не найден")
|
||||
return
|
||||
|
||||
test = await test_dao.get_by_id(test_id)
|
||||
|
||||
if test:
|
||||
await test_dao.update(test_id, is_active=not test.is_active)
|
||||
action = "деактивирован" if test.is_active else "активирован"
|
||||
await _callback.answer(f"✅ Тест {action}")
|
||||
await manager.switch_to(AdminTestsSG.test_detail)
|
||||
|
||||
|
||||
async def on_back_to_list(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
||||
await manager.switch_to(AdminTestsSG.tests_list)
|
||||
|
||||
|
||||
async def on_add_test_clicked(_callback: CallbackQuery, _button: Button, _manager: DialogManager):
|
||||
@@ -57,4 +127,17 @@ tests_dialog = Dialog(
|
||||
state=AdminTestsSG.tests_list,
|
||||
getter=get_tests_data,
|
||||
),
|
||||
Window(
|
||||
Format("{test_info}"),
|
||||
Row(
|
||||
Button(
|
||||
Format("{button_text}"),
|
||||
id="toggle_active",
|
||||
on_click=on_toggle_active
|
||||
),
|
||||
Button(Const("◀️ Назад"), id="back", on_click=on_back_to_list),
|
||||
),
|
||||
state=AdminTestsSG.test_detail,
|
||||
getter=get_test_detail,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
from aiogram.types import CallbackQuery, Message
|
||||
from aiogram_dialog import Dialog, DialogManager, Window, StartMode
|
||||
from aiogram_dialog import Dialog, DialogManager, StartMode, Window
|
||||
from aiogram_dialog.widgets.input import MessageInput
|
||||
from aiogram_dialog.widgets.kbd import Button, Column, ScrollingGroup, Select, SwitchTo
|
||||
from aiogram_dialog.widgets.kbd import (Button, Column, ScrollingGroup, Select,
|
||||
SwitchTo)
|
||||
from aiogram_dialog.widgets.text import Const, Format
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram import CONTAINER_NAME
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
from trudex.application.bot.admin_dialogs.states import AdminUsersSG, AdminMenuSG
|
||||
from trudex.application.bot.admin_dialogs.states import (AdminMenuSG,
|
||||
AdminUsersSG)
|
||||
from trudex.infrastructure.database.dao.user import UserDAO
|
||||
|
||||
|
||||
@@ -61,10 +62,8 @@ async def on_input_mode(_callback: CallbackQuery, _button: Button, manager: Dial
|
||||
await manager.switch_to(AdminUsersSG.users_input)
|
||||
|
||||
|
||||
async def on_user_input(message: Message, _widget: MessageInput, manager: DialogManager):
|
||||
container = manager.middleware_data[CONTAINER_NAME]
|
||||
user_dao = await container.get(UserDAO)
|
||||
|
||||
@inject
|
||||
async def on_user_input(message: Message, _widget: MessageInput, manager: DialogManager, user_dao: FromDishka[UserDAO]):
|
||||
text = (message.text or "").strip()
|
||||
|
||||
user = None
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from aiogram.types import CallbackQuery, Message
|
||||
from aiogram_dialog import Dialog, DialogManager, Window
|
||||
from aiogram_dialog.widgets.input import MessageInput
|
||||
from aiogram_dialog.widgets.kbd import Button, Row, Cancel
|
||||
from aiogram_dialog.widgets.kbd import Button, Cancel, Row
|
||||
from aiogram_dialog.widgets.text import Const
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
@@ -1,14 +1,16 @@
|
||||
from datetime import date, datetime
|
||||
|
||||
from aiogram.types import CallbackQuery, ContentType, Message
|
||||
from aiogram_dialog import Dialog, DialogManager, Window, StartMode
|
||||
from aiogram_dialog import Dialog, DialogManager, StartMode, Window
|
||||
from aiogram_dialog.widgets.input import MessageInput
|
||||
from aiogram_dialog.widgets.kbd import Button, Calendar, Cancel, Column, Row, ScrollingGroup, Select
|
||||
from aiogram_dialog.widgets.kbd import (Button, Calendar, Cancel, Column, Row,
|
||||
ScrollingGroup, Select)
|
||||
from aiogram_dialog.widgets.text import Const, Format
|
||||
from dishka.integrations.aiogram import CONTAINER_NAME
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
from trudex.application.bot.creator_dialogs.states import CreateTestSG, CreatorTestsSG
|
||||
from trudex.application.bot.creator_dialogs.states import (CreateTestSG,
|
||||
CreatorTestsSG)
|
||||
from trudex.infrastructure.database.dao.group import GroupDAO
|
||||
from trudex.infrastructure.database.dao.option import OptionDAO
|
||||
from trudex.infrastructure.database.dao.question import QuestionDAO
|
||||
@@ -52,7 +54,8 @@ async def on_description_input(message: Message, _widget: MessageInput, manager:
|
||||
await manager.switch_to(CreateTestSG.input_password)
|
||||
|
||||
|
||||
async def on_password_input(message: Message, _widget: MessageInput, manager: DialogManager):
|
||||
@inject
|
||||
async def on_password_input(message: Message, _widget: MessageInput, manager: DialogManager, group_dao: FromDishka[GroupDAO]):
|
||||
if not message.text:
|
||||
await message.answer("❌ Пароль не может быть пустым")
|
||||
return
|
||||
@@ -68,8 +71,6 @@ async def on_password_input(message: Message, _widget: MessageInput, manager: Di
|
||||
|
||||
manager.dialog_data["password"] = password
|
||||
|
||||
container = manager.middleware_data[CONTAINER_NAME]
|
||||
group_dao = await container.get(GroupDAO)
|
||||
groups = await group_dao.get_all()
|
||||
|
||||
if len(groups) == 0:
|
||||
@@ -79,10 +80,9 @@ async def on_password_input(message: Message, _widget: MessageInput, manager: Di
|
||||
await manager.switch_to(CreateTestSG.input_expires_at)
|
||||
|
||||
|
||||
async def on_skip_password(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
||||
@inject
|
||||
async def on_skip_password(_callback: CallbackQuery, _button: Button, manager: DialogManager, group_dao: FromDishka[GroupDAO]):
|
||||
manager.dialog_data["password"] = None
|
||||
container = manager.middleware_data[CONTAINER_NAME]
|
||||
group_dao = await container.get(GroupDAO)
|
||||
groups = await group_dao.get_all()
|
||||
|
||||
if len(groups) == 0:
|
||||
@@ -102,9 +102,8 @@ async def on_skip_expires(_callback: CallbackQuery, _button: Button, manager: Di
|
||||
await manager.switch_to(CreateTestSG.input_for_group)
|
||||
|
||||
|
||||
async def get_groups_for_test(dialog_manager: DialogManager, **_kwargs):
|
||||
container = dialog_manager.middleware_data[CONTAINER_NAME]
|
||||
group_dao = await container.get(GroupDAO)
|
||||
@inject
|
||||
async def get_groups_for_test(dialog_manager: DialogManager, group_dao: FromDishka[GroupDAO], **_kwargs):
|
||||
groups = await group_dao.get_all()
|
||||
|
||||
return {
|
||||
@@ -145,10 +144,8 @@ async def get_test_info(dialog_manager: DialogManager, **_kwargs):
|
||||
}
|
||||
|
||||
|
||||
async def on_confirm_test(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
||||
container = manager.middleware_data[CONTAINER_NAME]
|
||||
test_dao = await container.get(TestDAO)
|
||||
|
||||
@inject
|
||||
async def on_confirm_test(_callback: CallbackQuery, _button: Button, manager: DialogManager, test_dao: FromDishka[TestDAO]):
|
||||
title = manager.dialog_data.get("title")
|
||||
description = manager.dialog_data.get("description")
|
||||
password = manager.dialog_data.get("password")
|
||||
@@ -351,12 +348,15 @@ async def get_question_preview(dialog_manager: DialogManager, **_kwargs):
|
||||
return {"preview": preview}
|
||||
|
||||
|
||||
async def on_save_question(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
||||
container = manager.middleware_data[CONTAINER_NAME]
|
||||
question_dao = await container.get(QuestionDAO)
|
||||
option_dao = await container.get(OptionDAO)
|
||||
test_repo = await container.get(TestRepository)
|
||||
|
||||
@inject
|
||||
async def on_save_question(
|
||||
_callback: CallbackQuery,
|
||||
_button: Button,
|
||||
manager: DialogManager,
|
||||
question_dao: FromDishka[QuestionDAO],
|
||||
option_dao: FromDishka[OptionDAO],
|
||||
test_repo: FromDishka[TestRepository],
|
||||
):
|
||||
test_id = manager.dialog_data.get("test_id")
|
||||
current_question = manager.dialog_data.get("current_question", {})
|
||||
current_options = manager.dialog_data.get("current_options", [])
|
||||
|
||||
@@ -1,11 +1,14 @@
|
||||
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 Button, Column, Row, ScrollingGroup, Select
|
||||
from aiogram_dialog.widgets.kbd import (Button, Column, Row, ScrollingGroup,
|
||||
Select)
|
||||
from aiogram_dialog.widgets.text import Const, Format
|
||||
from dishka.integrations.aiogram import CONTAINER_NAME
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
from trudex.application.bot.creator_dialogs.states import CreatorGroupsSG
|
||||
from trudex.application.bot.creator_dialogs.states import (CreatorGroupsSG,
|
||||
CreatorMenuSG)
|
||||
from trudex.infrastructure.database.dao.group import GroupDAO
|
||||
|
||||
|
||||
@@ -13,10 +16,8 @@ async def on_group_click(_callback: CallbackQuery, _widget, _manager: DialogMana
|
||||
await _callback.answer("ℹ️ Для удаления используйте кнопку 'Удалить группу'")
|
||||
|
||||
|
||||
async def get_groups_data(dialog_manager: DialogManager, **_kwargs):
|
||||
container = dialog_manager.middleware_data[CONTAINER_NAME]
|
||||
group_dao = await container.get(GroupDAO)
|
||||
|
||||
@inject
|
||||
async def get_groups_data(group_dao: FromDishka[GroupDAO], dialog_manager: DialogManager, **_kwargs):
|
||||
groups = await group_dao.get_all()
|
||||
|
||||
success_message = dialog_manager.dialog_data.pop("success_message", None)
|
||||
@@ -46,7 +47,8 @@ async def on_back_to_menu(_callback: CallbackQuery, _button: Button, manager: Di
|
||||
await manager.start(CreatorMenuSG.main, mode=StartMode.RESET_STACK)
|
||||
|
||||
|
||||
async def on_group_number_input(message: Message, _widget: MessageInput, manager: DialogManager):
|
||||
@inject
|
||||
async def on_group_number_input(message: Message, _widget: MessageInput, manager: DialogManager, group_dao: FromDishka[GroupDAO]):
|
||||
if not message.text:
|
||||
await message.answer("❌ Номер группы не может быть пустым")
|
||||
return
|
||||
@@ -63,9 +65,6 @@ async def on_group_number_input(message: Message, _widget: MessageInput, manager
|
||||
await message.answer("❌ Номер группы должен быть четырехзначным (1000-9999)")
|
||||
return
|
||||
|
||||
container = manager.middleware_data[CONTAINER_NAME]
|
||||
group_dao = await container.get(GroupDAO)
|
||||
|
||||
existing = await group_dao.get_by_number(number)
|
||||
if existing:
|
||||
await message.answer(f"❌ Группа с номером {number} уже существует")
|
||||
@@ -85,10 +84,8 @@ async def on_cancel_add(_callback: CallbackQuery, _button: Button, manager: Dial
|
||||
await manager.switch_to(CreatorGroupsSG.groups_list)
|
||||
|
||||
|
||||
async def get_delete_groups_data(dialog_manager: DialogManager, **_kwargs):
|
||||
container = dialog_manager.middleware_data[CONTAINER_NAME]
|
||||
group_dao = await container.get(GroupDAO)
|
||||
|
||||
@inject
|
||||
async def get_delete_groups_data(group_dao: FromDishka[GroupDAO], **_kwargs):
|
||||
groups = await group_dao.get_all()
|
||||
|
||||
return {
|
||||
@@ -97,10 +94,8 @@ async def get_delete_groups_data(dialog_manager: DialogManager, **_kwargs):
|
||||
}
|
||||
|
||||
|
||||
async def on_select_group_to_delete(_callback: CallbackQuery, _widget, manager: DialogManager, item_id: str):
|
||||
container = manager.middleware_data[CONTAINER_NAME]
|
||||
group_dao = await container.get(GroupDAO)
|
||||
|
||||
@inject
|
||||
async def on_select_group_to_delete(_callback: CallbackQuery, _widget, manager: DialogManager, item_id: str, group_dao: FromDishka[GroupDAO]):
|
||||
group = await group_dao.get_by_id(int(item_id))
|
||||
if not group:
|
||||
await _callback.answer("❌ Группа не найдена", show_alert=True)
|
||||
@@ -119,10 +114,8 @@ async def get_delete_confirm_data(dialog_manager: DialogManager, **_kwargs):
|
||||
}
|
||||
|
||||
|
||||
async def on_confirm_delete(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
||||
container = manager.middleware_data[CONTAINER_NAME]
|
||||
group_dao = await container.get(GroupDAO)
|
||||
|
||||
@inject
|
||||
async def on_confirm_delete(_callback: CallbackQuery, _button: Button, manager: DialogManager, group_dao: FromDishka[GroupDAO]):
|
||||
group_id = manager.dialog_data.get("delete_group_id")
|
||||
|
||||
await group_dao.delete(group_id)
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
from aiogram.types import CallbackQuery
|
||||
from aiogram_dialog import Dialog, DialogManager, Window, StartMode
|
||||
from aiogram_dialog import Dialog, DialogManager, StartMode, Window
|
||||
from aiogram_dialog.widgets.kbd import Button, Column
|
||||
from aiogram_dialog.widgets.text import Const
|
||||
|
||||
from trudex.application.bot.creator_dialogs.states import CreatorMenuSG, CreatorUsersSG, CreatorTestsSG, CreatorBroadcastSG, CreatorGroupsSG
|
||||
from trudex.application.bot.creator_dialogs.states import (CreatorBroadcastSG,
|
||||
CreatorGroupsSG,
|
||||
CreatorMenuSG,
|
||||
CreatorTestsSG,
|
||||
CreatorUsersSG)
|
||||
|
||||
|
||||
async def on_tests_clicked(_callback: CallbackQuery, _button: Button, manager: DialogManager) -> None:
|
||||
|
||||
@@ -14,6 +14,7 @@ class CreatorUsersSG(StatesGroup):
|
||||
|
||||
class CreatorTestsSG(StatesGroup):
|
||||
tests_list = State()
|
||||
test_detail = State()
|
||||
|
||||
|
||||
class CreatorBroadcastSG(StatesGroup):
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
from aiogram.types import CallbackQuery
|
||||
from aiogram_dialog import Dialog, DialogManager, Window, StartMode
|
||||
from aiogram_dialog.widgets.kbd import Button, Column, ScrollingGroup, Select
|
||||
from aiogram_dialog import Dialog, DialogManager, StartMode, Window
|
||||
from aiogram_dialog.widgets.kbd import (Button, Column, Row, ScrollingGroup,
|
||||
Select)
|
||||
from aiogram_dialog.widgets.text import Const, Format
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
from trudex.application.bot.creator_dialogs.states import CreatorTestsSG, CreatorMenuSG, CreateTestSG
|
||||
from trudex.application.bot.creator_dialogs.states import (CreateTestSG,
|
||||
CreatorMenuSG,
|
||||
CreatorTestsSG)
|
||||
from trudex.infrastructure.database.dao.test import TestDAO
|
||||
from trudex.infrastructure.database.repo.test import TestRepository
|
||||
|
||||
|
||||
@inject
|
||||
@@ -24,7 +28,74 @@ async def get_tests_data(test_dao: FromDishka[TestDAO], **_kwargs):
|
||||
|
||||
async def on_test_selected(_callback: CallbackQuery, _widget: Select, manager: DialogManager, item_id: str):
|
||||
manager.dialog_data["selected_test_id"] = int(item_id)
|
||||
await _callback.answer("Тест выбран")
|
||||
await manager.switch_to(CreatorTestsSG.test_detail)
|
||||
|
||||
|
||||
@inject
|
||||
async def get_test_detail(test_dao: FromDishka[TestDAO], test_repo: FromDishka[TestRepository], dialog_manager: DialogManager, **_kwargs):
|
||||
test_id = dialog_manager.dialog_data.get("selected_test_id")
|
||||
|
||||
if not test_id:
|
||||
return {
|
||||
"test_info": "Тест не найден",
|
||||
"is_active": False,
|
||||
"button_text": "◀️ Назад",
|
||||
}
|
||||
|
||||
test = await test_dao.get_by_id(test_id)
|
||||
questions_count = await test_repo.count_questions_in_test(test_id)
|
||||
|
||||
if not test:
|
||||
return {
|
||||
"test_info": "Тест не найден",
|
||||
"is_active": False,
|
||||
"button_text": "◀️ Назад",
|
||||
}
|
||||
|
||||
status = "🟢 Активен" if test.is_active else "🔴 Деактивирован"
|
||||
password_str = f"🔒 {test.password}" if test.password else "🔓 Без пароля"
|
||||
expires_str = test.expires_at.strftime("%d.%m.%Y %H:%M") if test.expires_at else "♾️ Без срока"
|
||||
group_str = f"🎓 Группа {test.for_group}" if test.for_group else "👥 Для всех"
|
||||
|
||||
test_info = (
|
||||
f"<b>📝 Информация о тесте</b>\n\n"
|
||||
f"<b>Название:</b> {test.title}\n"
|
||||
f"<b>Описание:</b> {test.description or '—'}\n\n"
|
||||
f"<b>Статус:</b> {status}\n"
|
||||
f"<b>Вопросов:</b> {questions_count}\n"
|
||||
f"{password_str}\n"
|
||||
f"{expires_str}\n"
|
||||
f"{group_str}\n\n"
|
||||
f"<b>Создан:</b> {test.created_at.strftime('%d.%m.%Y %H:%M') if test.created_at else '—'}"
|
||||
)
|
||||
|
||||
button_text = "🔴 Деактивировать" if test.is_active else "🟢 Активировать"
|
||||
|
||||
return {
|
||||
"test_info": test_info,
|
||||
"is_active": test.is_active,
|
||||
"button_text": button_text,
|
||||
}
|
||||
|
||||
|
||||
@inject
|
||||
async def on_toggle_active(_callback: CallbackQuery, _button: Button, manager: DialogManager, test_dao: FromDishka[TestDAO]):
|
||||
test_id = manager.dialog_data.get("selected_test_id")
|
||||
if not test_id:
|
||||
await _callback.answer("❌ Тест не найден")
|
||||
return
|
||||
|
||||
test = await test_dao.get_by_id(test_id)
|
||||
|
||||
if test:
|
||||
await test_dao.update(test_id, is_active=not test.is_active)
|
||||
action = "деактивирован" if test.is_active else "активирован"
|
||||
await _callback.answer(f"✅ Тест {action}")
|
||||
await manager.switch_to(CreatorTestsSG.test_detail)
|
||||
|
||||
|
||||
async def on_back_to_list(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
||||
await manager.switch_to(CreatorTestsSG.tests_list)
|
||||
|
||||
|
||||
async def on_add_test_clicked(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
||||
@@ -57,4 +128,17 @@ tests_dialog = Dialog(
|
||||
state=CreatorTestsSG.tests_list,
|
||||
getter=get_tests_data,
|
||||
),
|
||||
Window(
|
||||
Format("{test_info}"),
|
||||
Row(
|
||||
Button(
|
||||
Format("{button_text}"),
|
||||
id="toggle_active",
|
||||
on_click=on_toggle_active
|
||||
),
|
||||
Button(Const("◀️ Назад"), id="back", on_click=on_back_to_list),
|
||||
),
|
||||
state=CreatorTestsSG.test_detail,
|
||||
getter=get_test_detail,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
from aiogram.types import CallbackQuery, Message
|
||||
from aiogram_dialog import Dialog, DialogManager, Window, StartMode
|
||||
from aiogram_dialog import Dialog, DialogManager, StartMode, Window
|
||||
from aiogram_dialog.widgets.input import MessageInput
|
||||
from aiogram_dialog.widgets.kbd import Button, Column, Row, ScrollingGroup, Select, SwitchTo
|
||||
from aiogram_dialog.widgets.kbd import (Button, Column, Row, ScrollingGroup,
|
||||
Select, SwitchTo)
|
||||
from aiogram_dialog.widgets.text import Const, Format
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram import CONTAINER_NAME
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
from trudex.application.bot.creator_dialogs.states import CreatorUsersSG, CreatorMenuSG
|
||||
from trudex.application.bot.creator_dialogs.states import (CreatorMenuSG,
|
||||
CreatorUsersSG)
|
||||
from trudex.infrastructure.database.dao.user import UserDAO
|
||||
|
||||
|
||||
@@ -81,10 +82,8 @@ async def on_input_mode(_callback: CallbackQuery, _button: Button, manager: Dial
|
||||
await manager.switch_to(CreatorUsersSG.users_input)
|
||||
|
||||
|
||||
async def on_user_input(message: Message, _widget: MessageInput, manager: DialogManager):
|
||||
container = manager.middleware_data[CONTAINER_NAME]
|
||||
user_dao = await container.get(UserDAO)
|
||||
|
||||
@inject
|
||||
async def on_user_input(message: Message, _widget: MessageInput, manager: DialogManager, user_dao: FromDishka[UserDAO]):
|
||||
text = (message.text or "").strip()
|
||||
|
||||
user = None
|
||||
@@ -107,10 +106,8 @@ async def on_make_admin_clicked(_callback: CallbackQuery, _button: Button, manag
|
||||
await manager.switch_to(CreatorUsersSG.make_admin_confirm)
|
||||
|
||||
|
||||
async def on_confirm_yes(_callback: CallbackQuery, _button: Button, manager: DialogManager):
|
||||
container = manager.middleware_data[CONTAINER_NAME]
|
||||
user_dao = await container.get(UserDAO)
|
||||
|
||||
@inject
|
||||
async def on_confirm_yes(_callback: CallbackQuery, _button: Button, manager: DialogManager, user_dao: FromDishka[UserDAO]):
|
||||
user_id = manager.dialog_data.get("selected_user_id")
|
||||
if not user_id:
|
||||
await _callback.answer("Ошибка: пользователь не выбран")
|
||||
|
||||
@@ -7,11 +7,11 @@ from dishka.integrations.aiogram import FromDishka
|
||||
|
||||
from trudex.application.bot.admin_dialogs.states import AdminMenuSG
|
||||
from trudex.application.bot.creator_dialogs.states import CreatorMenuSG
|
||||
from trudex.application.bot.user_dialogs.states import UserMenuSG, UserRegistrationSG
|
||||
from trudex.application.bot.user_dialogs.states import (UserMenuSG,
|
||||
UserRegistrationSG)
|
||||
from trudex.infrastructure.database.dao.group import GroupDAO
|
||||
from trudex.infrastructure.database.dao.user import UserDAO
|
||||
|
||||
|
||||
router = Router()
|
||||
|
||||
|
||||
|
||||
@@ -2,17 +2,17 @@ from aiogram.types import CallbackQuery
|
||||
from aiogram_dialog import Dialog, DialogManager, StartMode, Window
|
||||
from aiogram_dialog.widgets.kbd import ScrollingGroup, Select
|
||||
from aiogram_dialog.widgets.text import Const, Format
|
||||
from dishka.integrations.aiogram import CONTAINER_NAME
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
from trudex.application.bot.user_dialogs.states import UserMenuSG, UserRegistrationSG
|
||||
from trudex.application.bot.user_dialogs.states import (UserMenuSG,
|
||||
UserRegistrationSG)
|
||||
from trudex.infrastructure.database.dao.group import GroupDAO
|
||||
from trudex.infrastructure.database.dao.user import UserDAO
|
||||
|
||||
|
||||
async def get_groups_for_registration(dialog_manager: DialogManager, **_kwargs):
|
||||
container = dialog_manager.middleware_data[CONTAINER_NAME]
|
||||
group_dao = await container.get(GroupDAO)
|
||||
|
||||
@inject
|
||||
async def get_groups_for_registration(dialog_manager: DialogManager, group_dao: FromDishka[GroupDAO], **_kwargs):
|
||||
groups = await group_dao.get_all()
|
||||
|
||||
return {
|
||||
@@ -20,10 +20,8 @@ async def get_groups_for_registration(dialog_manager: DialogManager, **_kwargs):
|
||||
}
|
||||
|
||||
|
||||
async def on_group_selected(_callback: CallbackQuery, _widget, manager: DialogManager, item_id: str):
|
||||
container = manager.middleware_data[CONTAINER_NAME]
|
||||
user_dao = await container.get(UserDAO)
|
||||
|
||||
@inject
|
||||
async def on_group_selected(_callback: CallbackQuery, _widget, manager: DialogManager, item_id: str, user_dao: FromDishka[UserDAO]):
|
||||
user_id = manager.start_data.get("user_id")
|
||||
|
||||
await user_dao.update(user_id=user_id, group=int(item_id))
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
from .option import OptionDAO as OptionDAO
|
||||
from .question import QuestionDAO as QuestionDAO
|
||||
from .test import TestDAO as TestDAO
|
||||
from .user import UserDAO as UserDAO
|
||||
from .question import QuestionDAO as QuestionDAO
|
||||
from .option import OptionDAO as OptionDAO
|
||||
@@ -1,5 +1,6 @@
|
||||
from trudex.domain.schemas import TestAttempt as DomainTestAttempt
|
||||
from trudex.infrastructure.database.models import TestAttempt as TestAttemptModel
|
||||
from trudex.infrastructure.database.models import \
|
||||
TestAttempt as TestAttemptModel
|
||||
|
||||
|
||||
class TestAttemptDTO:
|
||||
|
||||
@@ -2,7 +2,8 @@ from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import final
|
||||
|
||||
from sqlalchemy import BigInteger, CheckConstraint, ForeignKey, Integer, String, Text, func
|
||||
from sqlalchemy import (BigInteger, CheckConstraint, ForeignKey, Integer,
|
||||
String, Text, func)
|
||||
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, relationship
|
||||
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from trudex.infrastructure.database.repo.test import TestRepository
|
||||
from trudex.infrastructure.database.repo.test_attempt import TestAttemptRepository
|
||||
from trudex.infrastructure.database.repo.test_attempt import \
|
||||
TestAttemptRepository
|
||||
from trudex.infrastructure.database.repo.user import UserRepository
|
||||
|
||||
__all__ = ["TestRepository", "TestAttemptRepository", "UserRepository"]
|
||||
|
||||
@@ -11,11 +11,9 @@ from trudex.infrastructure.database.dao.test import TestDAO
|
||||
from trudex.infrastructure.database.dto.option import OptionDTO
|
||||
from trudex.infrastructure.database.dto.question import QuestionDTO
|
||||
from trudex.infrastructure.database.dto.test import TestDTO
|
||||
from trudex.infrastructure.database.models import (
|
||||
Option as OptionModel,
|
||||
Question as QuestionModel,
|
||||
Test as TestModel,
|
||||
)
|
||||
from trudex.infrastructure.database.models import Option as OptionModel
|
||||
from trudex.infrastructure.database.models import Question as QuestionModel
|
||||
from trudex.infrastructure.database.models import Test as TestModel
|
||||
|
||||
|
||||
@final
|
||||
|
||||
@@ -10,10 +10,9 @@ from trudex.infrastructure.database.dao.test_attempt import TestAttemptDAO
|
||||
from trudex.infrastructure.database.dao.user_answer import UserAnswerDAO
|
||||
from trudex.infrastructure.database.dto.test_attempt import TestAttemptDTO
|
||||
from trudex.infrastructure.database.dto.user_answer import UserAnswerDTO
|
||||
from trudex.infrastructure.database.models import (
|
||||
TestAttempt as TestAttemptModel,
|
||||
UserAnswer as UserAnswerModel,
|
||||
)
|
||||
from trudex.infrastructure.database.models import \
|
||||
TestAttempt as TestAttemptModel
|
||||
from trudex.infrastructure.database.models import UserAnswer as UserAnswerModel
|
||||
|
||||
|
||||
@final
|
||||
@@ -177,7 +176,8 @@ class TestAttemptRepository:
|
||||
}
|
||||
|
||||
async def get_most_difficult_questions(self, test_id: int, limit: int = 10) -> list[tuple[int, float]]:
|
||||
from trudex.infrastructure.database.models import Question as QuestionModel
|
||||
from trudex.infrastructure.database.models import \
|
||||
Question as QuestionModel
|
||||
|
||||
result = await self.session.execute(
|
||||
select(
|
||||
|
||||
@@ -12,7 +12,8 @@ from trudex.infrastructure.database.dao.test_attempt import TestAttemptDAO
|
||||
from trudex.infrastructure.database.dao.user import UserDAO
|
||||
from trudex.infrastructure.database.dao.user_answer import UserAnswerDAO
|
||||
from trudex.infrastructure.database.repo.test import TestRepository
|
||||
from trudex.infrastructure.database.repo.test_attempt import TestAttemptRepository
|
||||
from trudex.infrastructure.database.repo.test_attempt import \
|
||||
TestAttemptRepository
|
||||
from trudex.infrastructure.database.repo.user import UserRepository
|
||||
from trudex.infrastructure.utils.config import Config
|
||||
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from aiogram import Bot
|
||||
from aiogram.types import BotCommand, BotCommandScopeAllPrivateChats, BotCommandScopeChat
|
||||
from aiogram.types import (BotCommand, BotCommandScopeAllPrivateChats,
|
||||
BotCommandScopeChat)
|
||||
|
||||
from trudex.infrastructure.database.repo.user import UserRepository
|
||||
from trudex.infrastructure.utils.config import Config
|
||||
|
||||
Reference in New Issue
Block a user