mirror of
https://github.com/koloideal/DutyLog.git
synced 2026-06-10 10:25:29 +03:00
update
This commit is contained in:
@@ -12,42 +12,45 @@ from dutylog.application.bot.user_handlers import router as user_router
|
|||||||
from dutylog.application.bot.user_dialogs import main_menu_dialog
|
from dutylog.application.bot.user_dialogs import main_menu_dialog
|
||||||
from dutylog.application.bot.user_dialogs.admin_dialogs import admin_menu_dialog
|
from dutylog.application.bot.user_dialogs.admin_dialogs import admin_menu_dialog
|
||||||
from dutylog.application.bot.user_dialogs.registration_dialog import registration_dialog
|
from dutylog.application.bot.user_dialogs.registration_dialog import registration_dialog
|
||||||
from dutylog.infrastructure.ioc import ConfigProvider, DatabaseProvider, DAOProvider, RepositoryProvider
|
from dutylog.infrastructure.ioc import (
|
||||||
|
ConfigProvider,
|
||||||
|
DatabaseProvider,
|
||||||
|
DAOProvider,
|
||||||
|
RepositoryProvider,
|
||||||
|
)
|
||||||
from dutylog.infrastructure.utils.config import load_config
|
from dutylog.infrastructure.utils.config import load_config
|
||||||
|
|
||||||
|
|
||||||
async def main():
|
async def main():
|
||||||
logging.basicConfig(level=logging.INFO)
|
logging.basicConfig(level=logging.INFO)
|
||||||
|
|
||||||
config = load_config()
|
config = load_config()
|
||||||
|
|
||||||
bot = Bot(
|
bot = Bot(
|
||||||
token=config.bot.token,
|
token=config.bot.token, default=DefaultBotProperties(parse_mode=ParseMode.HTML)
|
||||||
default=DefaultBotProperties(parse_mode=ParseMode.HTML)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
await bot.delete_webhook(drop_pending_updates=True)
|
await bot.delete_webhook(drop_pending_updates=True)
|
||||||
|
|
||||||
dp = Dispatcher()
|
dp = Dispatcher()
|
||||||
|
|
||||||
container = make_async_container(
|
container = make_async_container(
|
||||||
ConfigProvider(),
|
ConfigProvider(),
|
||||||
DatabaseProvider(),
|
DatabaseProvider(),
|
||||||
DAOProvider(),
|
DAOProvider(),
|
||||||
RepositoryProvider(),
|
RepositoryProvider(),
|
||||||
)
|
)
|
||||||
|
|
||||||
dp.include_router(user_router)
|
dp.include_router(user_router)
|
||||||
dp.include_router(main_menu_dialog)
|
dp.include_router(main_menu_dialog)
|
||||||
dp.include_router(admin_menu_dialog)
|
dp.include_router(admin_menu_dialog)
|
||||||
dp.include_router(registration_dialog)
|
dp.include_router(registration_dialog)
|
||||||
|
|
||||||
setup_dialogs(dp)
|
setup_dialogs(dp)
|
||||||
setup_dishka(container, dp, auto_inject=True)
|
setup_dishka(container, dp, auto_inject=True)
|
||||||
|
|
||||||
await dp.start_polling(bot)
|
await dp.start_polling(bot)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
asyncio.run(main())
|
asyncio.run(main())
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
from dutylog.application.bot.user_dialogs.admin_dialogs.admin_menu_dialog import admin_menu_dialog
|
from dutylog.application.bot.user_dialogs.admin_dialogs.admin_menu_dialog import (
|
||||||
|
admin_menu_dialog,
|
||||||
|
)
|
||||||
|
|
||||||
__all__ = ["admin_menu_dialog"]
|
__all__ = ["admin_menu_dialog"]
|
||||||
|
|||||||
@@ -3,15 +3,24 @@ from aiogram import Bot
|
|||||||
from aiogram.exceptions import TelegramForbiddenError, TelegramBadRequest
|
from aiogram.exceptions import TelegramForbiddenError, TelegramBadRequest
|
||||||
from aiogram_dialog import Dialog, Window, DialogManager
|
from aiogram_dialog import Dialog, Window, DialogManager
|
||||||
from aiogram_dialog.widgets.text import Format, Const
|
from aiogram_dialog.widgets.text import Format, Const
|
||||||
from aiogram_dialog.widgets.kbd import Row, SwitchTo, Button, ScrollingGroup, Select
|
from aiogram_dialog.widgets.kbd import Row, SwitchTo, Button, ScrollingGroup, Select, Group
|
||||||
from aiogram_dialog.widgets.input import MessageInput
|
from aiogram_dialog.widgets.input import MessageInput
|
||||||
from dishka import FromDishka
|
from dishka import FromDishka
|
||||||
from dishka.integrations.aiogram_dialog import inject
|
from dishka.integrations.aiogram_dialog import inject
|
||||||
|
|
||||||
from dutylog.application.bot.user_dialogs.states import AdminMenuSG
|
from dutylog.application.bot.user_dialogs.states import AdminMenuSG
|
||||||
from dutylog.infrastructure.database.repositories.users_repository import UsersRepository
|
from dutylog.infrastructure.database.repositories.users_repository import (
|
||||||
from dutylog.infrastructure.database.repositories.residents_repository import ResidentsRepository
|
UsersRepository,
|
||||||
from dutylog.infrastructure.database.repositories.rooms_repository import RoomsRepository
|
)
|
||||||
|
from dutylog.infrastructure.database.repositories.residents_repository import (
|
||||||
|
ResidentsRepository,
|
||||||
|
)
|
||||||
|
from dutylog.infrastructure.database.repositories.rooms_repository import (
|
||||||
|
RoomsRepository,
|
||||||
|
)
|
||||||
|
from dutylog.infrastructure.database.repositories.hours_transactions_repository import (
|
||||||
|
HoursTransactionsRepository,
|
||||||
|
)
|
||||||
from dutylog.infrastructure.utils.config import Config
|
from dutylog.infrastructure.utils.config import Config
|
||||||
|
|
||||||
|
|
||||||
@@ -28,14 +37,14 @@ async def get_admin_menu_data(
|
|||||||
first_name=event_from_user.first_name,
|
first_name=event_from_user.first_name,
|
||||||
last_name=event_from_user.last_name,
|
last_name=event_from_user.last_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
is_creator = event_from_user.id == config.bot.creator_id
|
is_creator = event_from_user.id == config.bot.creator_id
|
||||||
|
|
||||||
if is_creator:
|
if is_creator:
|
||||||
greeting = "👑 <b>Создатель</b>"
|
greeting = "👑 <b>Создатель</b>"
|
||||||
else:
|
else:
|
||||||
greeting = "👨💼 <b>Администратор</b>"
|
greeting = "👨💼 <b>Администратор</b>"
|
||||||
|
|
||||||
content = f"""
|
content = f"""
|
||||||
{greeting}
|
{greeting}
|
||||||
|
|
||||||
@@ -43,7 +52,7 @@ async def get_admin_menu_data(
|
|||||||
|
|
||||||
Выберите действие:
|
Выберите действие:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return {"content": content}
|
return {"content": content}
|
||||||
|
|
||||||
|
|
||||||
@@ -55,14 +64,14 @@ async def get_statistics_data(
|
|||||||
):
|
):
|
||||||
all_users = await users_repository.get_all_users()
|
all_users = await users_repository.get_all_users()
|
||||||
all_residents = await residents_repository.get_all_residents()
|
all_residents = await residents_repository.get_all_residents()
|
||||||
|
|
||||||
total_users = len(all_users)
|
total_users = len(all_users)
|
||||||
total_residents = len(all_residents)
|
total_residents = len(all_residents)
|
||||||
busy_residents = len([r for r in all_residents if r.is_busy])
|
busy_residents = len([r for r in all_residents if r.is_busy])
|
||||||
total_active_hours = sum(r.active_hours for r in all_residents)
|
total_active_hours = sum(r.active_hours for r in all_residents)
|
||||||
total_inactive_hours = sum(r.inactive_hours for r in all_residents)
|
total_inactive_hours = sum(r.inactive_hours for r in all_residents)
|
||||||
admins_count = len([u for u in all_users if u.is_admin])
|
admins_count = len([u for u in all_users if u.is_admin])
|
||||||
|
|
||||||
stats_text = f"""
|
stats_text = f"""
|
||||||
<blockquote>📊 <b>Статистика системы</b></blockquote>
|
<blockquote>📊 <b>Статистика системы</b></blockquote>
|
||||||
|
|
||||||
@@ -79,7 +88,7 @@ async def get_statistics_data(
|
|||||||
🔴 <b>Всего неактивных часов:</b> <code>{total_inactive_hours}</code> ч
|
🔴 <b>Всего неактивных часов:</b> <code>{total_inactive_hours}</code> ч
|
||||||
📊 <b>Общий итог:</b> <code>{total_active_hours + total_inactive_hours}</code> ч
|
📊 <b>Общий итог:</b> <code>{total_active_hours + total_inactive_hours}</code> ч
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return {"stats_content": stats_text}
|
return {"stats_content": stats_text}
|
||||||
|
|
||||||
|
|
||||||
@@ -90,7 +99,7 @@ async def on_broadcast_message(
|
|||||||
):
|
):
|
||||||
dialog_manager.dialog_data["broadcast_message_id"] = message.message_id
|
dialog_manager.dialog_data["broadcast_message_id"] = message.message_id
|
||||||
dialog_manager.dialog_data["broadcast_chat_id"] = message.chat.id
|
dialog_manager.dialog_data["broadcast_chat_id"] = message.chat.id
|
||||||
|
|
||||||
await message.copy_to(message.chat.id)
|
await message.copy_to(message.chat.id)
|
||||||
await dialog_manager.switch_to(AdminMenuSG.broadcast_confirm)
|
await dialog_manager.switch_to(AdminMenuSG.broadcast_confirm)
|
||||||
|
|
||||||
@@ -103,21 +112,21 @@ async def on_broadcast_confirm(
|
|||||||
users_repository: FromDishka[UsersRepository],
|
users_repository: FromDishka[UsersRepository],
|
||||||
):
|
):
|
||||||
assert callback.message is not None
|
assert callback.message is not None
|
||||||
|
|
||||||
bot: Bot = dialog_manager.middleware_data["bot"]
|
bot: Bot = dialog_manager.middleware_data["bot"]
|
||||||
message_id = dialog_manager.dialog_data["broadcast_message_id"]
|
message_id = dialog_manager.dialog_data["broadcast_message_id"]
|
||||||
chat_id = dialog_manager.dialog_data["broadcast_chat_id"]
|
chat_id = dialog_manager.dialog_data["broadcast_chat_id"]
|
||||||
admin_id = callback.from_user.id
|
admin_id = callback.from_user.id
|
||||||
|
|
||||||
all_users = await users_repository.get_all_users()
|
all_users = await users_repository.get_all_users()
|
||||||
|
|
||||||
success_count = 0
|
success_count = 0
|
||||||
failed_count = 0
|
failed_count = 0
|
||||||
|
|
||||||
for user in all_users:
|
for user in all_users:
|
||||||
if user.id == admin_id:
|
if user.id == admin_id:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
await bot.copy_message(
|
await bot.copy_message(
|
||||||
chat_id=user.id,
|
chat_id=user.id,
|
||||||
@@ -129,9 +138,7 @@ async def on_broadcast_confirm(
|
|||||||
failed_count += 1
|
failed_count += 1
|
||||||
except TelegramBadRequest:
|
except TelegramBadRequest:
|
||||||
failed_count += 1
|
failed_count += 1
|
||||||
except Exception:
|
|
||||||
failed_count += 1
|
|
||||||
|
|
||||||
result_text = f"""
|
result_text = f"""
|
||||||
<blockquote>📢 <b>Результаты рассылки</b></blockquote>
|
<blockquote>📢 <b>Результаты рассылки</b></blockquote>
|
||||||
|
|
||||||
@@ -139,7 +146,7 @@ async def on_broadcast_confirm(
|
|||||||
❌ <b>Не удалось отправить:</b> <code>{failed_count}</code>
|
❌ <b>Не удалось отправить:</b> <code>{failed_count}</code>
|
||||||
📊 <b>Всего пользователей:</b> <code>{len(all_users) - 1}</code>
|
📊 <b>Всего пользователей:</b> <code>{len(all_users) - 1}</code>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
await callback.message.answer(result_text)
|
await callback.message.answer(result_text)
|
||||||
await callback.message.delete()
|
await callback.message.delete()
|
||||||
await dialog_manager.start(AdminMenuSG.main)
|
await dialog_manager.start(AdminMenuSG.main)
|
||||||
@@ -160,25 +167,24 @@ async def get_residents_list_data(
|
|||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
all_residents = await residents_repository.get_all_residents()
|
all_residents = await residents_repository.get_all_residents()
|
||||||
|
|
||||||
residents_with_rooms = []
|
residents_with_rooms = []
|
||||||
for resident in all_residents:
|
for resident in all_residents:
|
||||||
room = await rooms_repository.get_room_by_id(resident.room)
|
room = await rooms_repository.get_room_by_id(resident.room)
|
||||||
room_number = room.number if room else 999999
|
room_number = room.number if room else 999999
|
||||||
residents_with_rooms.append((resident, room_number))
|
residents_with_rooms.append((resident, room_number))
|
||||||
|
|
||||||
residents_with_rooms.sort(key=lambda x: x[1])
|
residents_with_rooms.sort(key=lambda x: x[1])
|
||||||
|
|
||||||
residents_data = []
|
residents_data = []
|
||||||
for resident, room_number in residents_with_rooms:
|
for resident, room_number in residents_with_rooms:
|
||||||
status = "🟢" if resident.is_busy else "⚪️"
|
status = "🟢" if resident.is_busy else "⚪️"
|
||||||
name = resident.real_name if resident.real_name else "Без имени"
|
name = resident.real_name if resident.real_name else "Без имени"
|
||||||
|
|
||||||
residents_data.append((
|
residents_data.append(
|
||||||
f"{name} | Комната {room_number} | {status}",
|
(f"{name} | Комната {room_number} | {status}", resident.id)
|
||||||
resident.id
|
)
|
||||||
))
|
|
||||||
|
|
||||||
content = f"""
|
content = f"""
|
||||||
<blockquote>🏠 <b>Резиденты</b></blockquote>
|
<blockquote>🏠 <b>Резиденты</b></blockquote>
|
||||||
|
|
||||||
@@ -186,7 +192,7 @@ async def get_residents_list_data(
|
|||||||
|
|
||||||
Выберите резидента для просмотра информации:
|
Выберите резидента для просмотра информации:
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"content": content,
|
"content": content,
|
||||||
"residents": residents_data,
|
"residents": residents_data,
|
||||||
@@ -202,28 +208,31 @@ async def get_resident_info_data(
|
|||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
|
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
|
||||||
|
|
||||||
if not resident_id:
|
if not resident_id:
|
||||||
return {"info_content": "Ошибка: резидент не выбран"}
|
return {"info_content": "Ошибка: резидент не выбран"}
|
||||||
|
|
||||||
resident = await residents_repository.get_resident_by_id(resident_id)
|
resident = await residents_repository.get_resident_by_id(resident_id)
|
||||||
|
|
||||||
if not resident:
|
if not resident:
|
||||||
return {"info_content": "Ошибка: резидент не найден"}
|
return {"info_content": "Ошибка: резидент не найден"}
|
||||||
|
|
||||||
room = await rooms_repository.get_room_by_id(resident.room)
|
room = await rooms_repository.get_room_by_id(resident.room)
|
||||||
room_number = room.number if room else "???"
|
room_number = room.number if room else "???"
|
||||||
|
|
||||||
name = resident.real_name if resident.real_name else "Без имени"
|
name = resident.real_name if resident.real_name else "Без имени"
|
||||||
status = "🟢 Занят" if resident.is_busy else "⚪️ Свободен"
|
status = "🟢 Занят" if resident.is_busy else "⚪️ Свободен"
|
||||||
|
|
||||||
user_info = "Не привязан"
|
user_info = "Не привязан"
|
||||||
if resident.user_entity:
|
if resident.user_entity:
|
||||||
user = await users_repository.get_user_by_id(resident.user_entity)
|
user = await users_repository.get_user_by_id(resident.user_entity)
|
||||||
if user:
|
if user:
|
||||||
username = f"@{user.username}" if user.username else "без username"
|
if user.username:
|
||||||
|
username = f"@{user.username}"
|
||||||
|
else:
|
||||||
|
username = f"ID: {user.id}"
|
||||||
user_info = f"{user.first_name} ({username})"
|
user_info = f"{user.first_name} ({username})"
|
||||||
|
|
||||||
info_content = f"""
|
info_content = f"""
|
||||||
<blockquote>👤 <b>Информация о резиденте</b></blockquote>
|
<blockquote>👤 <b>Информация о резиденте</b></blockquote>
|
||||||
|
|
||||||
@@ -239,7 +248,7 @@ async def get_resident_info_data(
|
|||||||
🔴 <b>Неактивные часы:</b> <code>{resident.inactive_hours}</code> ч
|
🔴 <b>Неактивные часы:</b> <code>{resident.inactive_hours}</code> ч
|
||||||
📊 <b>Всего часов:</b> <code>{resident.active_hours + resident.inactive_hours}</code> ч
|
📊 <b>Всего часов:</b> <code>{resident.active_hours + resident.inactive_hours}</code> ч
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"info_content": info_content,
|
"info_content": info_content,
|
||||||
"is_busy": resident.is_busy,
|
"is_busy": resident.is_busy,
|
||||||
@@ -288,10 +297,10 @@ async def on_logout_resident_confirm(
|
|||||||
residents_repository: FromDishka[ResidentsRepository],
|
residents_repository: FromDishka[ResidentsRepository],
|
||||||
):
|
):
|
||||||
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
|
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
|
||||||
|
|
||||||
if resident_id:
|
if resident_id:
|
||||||
await residents_repository.unbind_user_from_resident(resident_id)
|
await residents_repository.unbind_user_from_resident(resident_id)
|
||||||
|
|
||||||
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||||
|
|
||||||
|
|
||||||
@@ -303,6 +312,142 @@ async def on_logout_resident_cancel(
|
|||||||
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||||
|
|
||||||
|
|
||||||
|
async def on_add_hours_click(
|
||||||
|
callback: CallbackQuery,
|
||||||
|
button: Button,
|
||||||
|
dialog_manager: DialogManager,
|
||||||
|
):
|
||||||
|
await dialog_manager.switch_to(AdminMenuSG.add_hours_select)
|
||||||
|
|
||||||
|
|
||||||
|
async def on_remove_hours_click(
|
||||||
|
callback: CallbackQuery,
|
||||||
|
button: Button,
|
||||||
|
dialog_manager: DialogManager,
|
||||||
|
):
|
||||||
|
await dialog_manager.switch_to(AdminMenuSG.remove_hours_select)
|
||||||
|
|
||||||
|
|
||||||
|
async def get_hours_select_data(**kwargs):
|
||||||
|
hours_options = [
|
||||||
|
(5, "5"), (10, "10"), (15, "15"), (20, "20"),
|
||||||
|
(25, "25"), (30, "30"), (35, "35"), (40, "40"),
|
||||||
|
(45, "45"), (50, "50"), (55, "55"), (60, "60"),
|
||||||
|
(65, "65"), (70, "70"), (75, "75"), (80, "80"),
|
||||||
|
]
|
||||||
|
return {"hours_options": hours_options}
|
||||||
|
|
||||||
|
|
||||||
|
async def on_hours_selected(
|
||||||
|
callback: CallbackQuery,
|
||||||
|
widget: Select,
|
||||||
|
dialog_manager: DialogManager,
|
||||||
|
item_id: str,
|
||||||
|
):
|
||||||
|
dialog_manager.dialog_data["selected_hours"] = int(item_id)
|
||||||
|
|
||||||
|
if dialog_manager.current_context().state == AdminMenuSG.add_hours_select:
|
||||||
|
await dialog_manager.switch_to(AdminMenuSG.add_hours_confirm)
|
||||||
|
else:
|
||||||
|
await dialog_manager.switch_to(AdminMenuSG.remove_hours_confirm)
|
||||||
|
|
||||||
|
|
||||||
|
async def on_custom_hours_click(
|
||||||
|
callback: CallbackQuery,
|
||||||
|
button: Button,
|
||||||
|
dialog_manager: DialogManager,
|
||||||
|
):
|
||||||
|
if dialog_manager.current_context().state == AdminMenuSG.add_hours_select:
|
||||||
|
await dialog_manager.switch_to(AdminMenuSG.add_hours_custom)
|
||||||
|
else:
|
||||||
|
await dialog_manager.switch_to(AdminMenuSG.remove_hours_custom)
|
||||||
|
|
||||||
|
|
||||||
|
async def on_custom_hours_input(
|
||||||
|
message: Message,
|
||||||
|
widget: MessageInput,
|
||||||
|
dialog_manager: DialogManager,
|
||||||
|
):
|
||||||
|
if not message.text:
|
||||||
|
await message.answer("⚠️ Пожалуйста, введите число")
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
hours = int(message.text)
|
||||||
|
if hours <= 0:
|
||||||
|
await message.answer("⚠️ Количество часов должно быть положительным числом")
|
||||||
|
return
|
||||||
|
|
||||||
|
dialog_manager.dialog_data["selected_hours"] = hours
|
||||||
|
|
||||||
|
if dialog_manager.current_context().state == AdminMenuSG.add_hours_custom:
|
||||||
|
await dialog_manager.switch_to(AdminMenuSG.add_hours_confirm)
|
||||||
|
else:
|
||||||
|
await dialog_manager.switch_to(AdminMenuSG.remove_hours_confirm)
|
||||||
|
except ValueError:
|
||||||
|
await message.answer("⚠️ Пожалуйста, введите корректное число")
|
||||||
|
|
||||||
|
|
||||||
|
async def get_hours_confirm_data(
|
||||||
|
dialog_manager: DialogManager,
|
||||||
|
**kwargs,
|
||||||
|
):
|
||||||
|
hours = dialog_manager.dialog_data.get("selected_hours", 0)
|
||||||
|
return {"hours": hours}
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
|
async def on_add_hours_confirm(
|
||||||
|
callback: CallbackQuery,
|
||||||
|
button: Button,
|
||||||
|
dialog_manager: DialogManager,
|
||||||
|
transactions_repository: FromDishka[HoursTransactionsRepository],
|
||||||
|
):
|
||||||
|
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
|
||||||
|
hours = dialog_manager.dialog_data.get("selected_hours")
|
||||||
|
admin_id = callback.from_user.id
|
||||||
|
|
||||||
|
if resident_id and hours:
|
||||||
|
await transactions_repository.add_hours(
|
||||||
|
resident_id=resident_id,
|
||||||
|
amount=hours,
|
||||||
|
admin_id=admin_id,
|
||||||
|
is_active=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||||
|
|
||||||
|
|
||||||
|
@inject
|
||||||
|
async def on_remove_hours_confirm(
|
||||||
|
callback: CallbackQuery,
|
||||||
|
button: Button,
|
||||||
|
dialog_manager: DialogManager,
|
||||||
|
transactions_repository: FromDishka[HoursTransactionsRepository],
|
||||||
|
):
|
||||||
|
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
|
||||||
|
hours = dialog_manager.dialog_data.get("selected_hours")
|
||||||
|
admin_id = callback.from_user.id
|
||||||
|
|
||||||
|
if resident_id and hours:
|
||||||
|
await transactions_repository.remove_hours(
|
||||||
|
resident_id=resident_id,
|
||||||
|
amount=hours,
|
||||||
|
admin_id=admin_id,
|
||||||
|
is_active=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||||
|
|
||||||
|
|
||||||
|
async def on_hours_cancel(
|
||||||
|
callback: CallbackQuery,
|
||||||
|
button: Button,
|
||||||
|
dialog_manager: DialogManager,
|
||||||
|
):
|
||||||
|
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||||
|
|
||||||
|
|
||||||
admin_menu_dialog = Dialog(
|
admin_menu_dialog = Dialog(
|
||||||
Window(
|
Window(
|
||||||
Format("{content}"),
|
Format("{content}"),
|
||||||
@@ -311,11 +456,6 @@ admin_menu_dialog = Dialog(
|
|||||||
id="residents_btn",
|
id="residents_btn",
|
||||||
state=AdminMenuSG.residents,
|
state=AdminMenuSG.residents,
|
||||||
),
|
),
|
||||||
SwitchTo(
|
|
||||||
Const("👥 Пользователи"),
|
|
||||||
id="users_btn",
|
|
||||||
state=AdminMenuSG.users,
|
|
||||||
),
|
|
||||||
SwitchTo(
|
SwitchTo(
|
||||||
Const("📊 Статистика"),
|
Const("📊 Статистика"),
|
||||||
id="stats_btn",
|
id="stats_btn",
|
||||||
@@ -370,6 +510,18 @@ admin_menu_dialog = Dialog(
|
|||||||
),
|
),
|
||||||
Window(
|
Window(
|
||||||
Format("{info_content}"),
|
Format("{info_content}"),
|
||||||
|
Row(
|
||||||
|
Button(
|
||||||
|
Const("➕ Добавить часы"),
|
||||||
|
id="add_hours_btn",
|
||||||
|
on_click=on_add_hours_click,
|
||||||
|
),
|
||||||
|
Button(
|
||||||
|
Const("➖ Отнять часы"),
|
||||||
|
id="remove_hours_btn",
|
||||||
|
on_click=on_remove_hours_click,
|
||||||
|
),
|
||||||
|
),
|
||||||
Button(
|
Button(
|
||||||
Const("🚪 Разлогинить"),
|
Const("🚪 Разлогинить"),
|
||||||
id="logout_resident_btn",
|
id="logout_resident_btn",
|
||||||
@@ -385,7 +537,9 @@ admin_menu_dialog = Dialog(
|
|||||||
getter=get_resident_info_data,
|
getter=get_resident_info_data,
|
||||||
),
|
),
|
||||||
Window(
|
Window(
|
||||||
Const("<blockquote>⚠️ <b>Подтверждение</b></blockquote>\n\nВы уверены, что хотите разлогинить этого резидента?"),
|
Const(
|
||||||
|
"<blockquote>⚠️ <b>Подтверждение</b></blockquote>\n\nВы уверены, что хотите разлогинить этого резидента?"
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
Button(
|
Button(
|
||||||
Const("✅ Да"),
|
Const("✅ Да"),
|
||||||
@@ -401,9 +555,108 @@ admin_menu_dialog = Dialog(
|
|||||||
state=AdminMenuSG.resident_logout_confirm,
|
state=AdminMenuSG.resident_logout_confirm,
|
||||||
),
|
),
|
||||||
Window(
|
Window(
|
||||||
Const("<blockquote>👥 <b>Пользователи</b></blockquote>\n\n<i>Функционал в разработке</i>"),
|
Const("<blockquote>➕ <b>Добавить часы</b></blockquote>\n\nВыберите количество часов:"),
|
||||||
SwitchTo(Const("◀️ Назад"), id="back_from_users", state=AdminMenuSG.main),
|
Group(
|
||||||
state=AdminMenuSG.users,
|
Select(
|
||||||
|
Format("{item[1]} ч"),
|
||||||
|
id="hours_select_add",
|
||||||
|
item_id_getter=lambda x: x[0],
|
||||||
|
items="hours_options",
|
||||||
|
on_click=on_hours_selected,
|
||||||
|
),
|
||||||
|
width=4,
|
||||||
|
),
|
||||||
|
Button(
|
||||||
|
Const("✏️ Ввести свое количество"),
|
||||||
|
id="custom_hours_add_btn",
|
||||||
|
on_click=on_custom_hours_click,
|
||||||
|
),
|
||||||
|
SwitchTo(
|
||||||
|
Const("◀️ Отмена"),
|
||||||
|
id="cancel_add_hours",
|
||||||
|
state=AdminMenuSG.resident_info,
|
||||||
|
),
|
||||||
|
state=AdminMenuSG.add_hours_select,
|
||||||
|
getter=get_hours_select_data,
|
||||||
|
),
|
||||||
|
Window(
|
||||||
|
Const("<blockquote>➖ <b>Отнять часы</b></blockquote>\n\nВыберите количество часов:"),
|
||||||
|
Group(
|
||||||
|
Select(
|
||||||
|
Format("{item[1]} ч"),
|
||||||
|
id="hours_select_remove",
|
||||||
|
item_id_getter=lambda x: x[0],
|
||||||
|
items="hours_options",
|
||||||
|
on_click=on_hours_selected,
|
||||||
|
),
|
||||||
|
width=4,
|
||||||
|
),
|
||||||
|
Button(
|
||||||
|
Const("✏️ Ввести свое количество"),
|
||||||
|
id="custom_hours_remove_btn",
|
||||||
|
on_click=on_custom_hours_click,
|
||||||
|
),
|
||||||
|
SwitchTo(
|
||||||
|
Const("◀️ Отмена"),
|
||||||
|
id="cancel_remove_hours",
|
||||||
|
state=AdminMenuSG.resident_info,
|
||||||
|
),
|
||||||
|
state=AdminMenuSG.remove_hours_select,
|
||||||
|
getter=get_hours_select_data,
|
||||||
|
),
|
||||||
|
Window(
|
||||||
|
Const("<blockquote>✏️ <b>Добавить часы</b></blockquote>\n\nВведите количество часов:"),
|
||||||
|
MessageInput(on_custom_hours_input),
|
||||||
|
SwitchTo(
|
||||||
|
Const("◀️ Отмена"),
|
||||||
|
id="cancel_custom_add",
|
||||||
|
state=AdminMenuSG.add_hours_select,
|
||||||
|
),
|
||||||
|
state=AdminMenuSG.add_hours_custom,
|
||||||
|
),
|
||||||
|
Window(
|
||||||
|
Const("<blockquote>✏️ <b>Отнять часы</b></blockquote>\n\nВведите количество часов:"),
|
||||||
|
MessageInput(on_custom_hours_input),
|
||||||
|
SwitchTo(
|
||||||
|
Const("◀️ Отмена"),
|
||||||
|
id="cancel_custom_remove",
|
||||||
|
state=AdminMenuSG.remove_hours_select,
|
||||||
|
),
|
||||||
|
state=AdminMenuSG.remove_hours_custom,
|
||||||
|
),
|
||||||
|
Window(
|
||||||
|
Format("<blockquote>➕ <b>Подтверждение</b></blockquote>\n\nВы уверены, что хотите добавить <code>{hours}</code> часов?"),
|
||||||
|
Row(
|
||||||
|
Button(
|
||||||
|
Const("✅ Да"),
|
||||||
|
id="confirm_add_hours",
|
||||||
|
on_click=on_add_hours_confirm,
|
||||||
|
),
|
||||||
|
Button(
|
||||||
|
Const("❌ Нет"),
|
||||||
|
id="cancel_add_hours_confirm",
|
||||||
|
on_click=on_hours_cancel,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
state=AdminMenuSG.add_hours_confirm,
|
||||||
|
getter=get_hours_confirm_data,
|
||||||
|
),
|
||||||
|
Window(
|
||||||
|
Format("<blockquote>➖ <b>Подтверждение</b></blockquote>\n\nВы уверены, что хотите отнять <code>{hours}</code> часов?"),
|
||||||
|
Row(
|
||||||
|
Button(
|
||||||
|
Const("✅ Да"),
|
||||||
|
id="confirm_remove_hours",
|
||||||
|
on_click=on_remove_hours_confirm,
|
||||||
|
),
|
||||||
|
Button(
|
||||||
|
Const("❌ Нет"),
|
||||||
|
id="cancel_remove_hours_confirm",
|
||||||
|
on_click=on_hours_cancel,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
state=AdminMenuSG.remove_hours_confirm,
|
||||||
|
getter=get_hours_confirm_data,
|
||||||
),
|
),
|
||||||
Window(
|
Window(
|
||||||
Format("{stats_content}"),
|
Format("{stats_content}"),
|
||||||
@@ -412,16 +665,26 @@ admin_menu_dialog = Dialog(
|
|||||||
getter=get_statistics_data,
|
getter=get_statistics_data,
|
||||||
),
|
),
|
||||||
Window(
|
Window(
|
||||||
Const("<blockquote>📢 <b>Рассылка</b></blockquote>\n\nОтправьте сообщение, которое хотите разослать всем пользователям:"),
|
Const(
|
||||||
|
"<blockquote>📢 <b>Рассылка</b></blockquote>\n\nОтправьте сообщение, которое хотите разослать всем пользователям:"
|
||||||
|
),
|
||||||
MessageInput(on_broadcast_message),
|
MessageInput(on_broadcast_message),
|
||||||
SwitchTo(Const("◀️ Отмена"), id="cancel_broadcast", state=AdminMenuSG.main),
|
SwitchTo(Const("◀️ Отмена"), id="cancel_broadcast", state=AdminMenuSG.main),
|
||||||
state=AdminMenuSG.broadcast,
|
state=AdminMenuSG.broadcast,
|
||||||
),
|
),
|
||||||
Window(
|
Window(
|
||||||
Const("<blockquote>📢 <b>Подтверждение рассылки</b></blockquote>\n\n⚠️ Вы уверены, что хотите отправить это сообщение всем пользователям?"),
|
Const(
|
||||||
|
"<blockquote>📢 <b>Подтверждение рассылки</b></blockquote>\n\n⚠️ Вы уверены, что хотите отправить это сообщение всем пользователям?"
|
||||||
|
),
|
||||||
Row(
|
Row(
|
||||||
Button(Const("✅ Да"), id="confirm_broadcast", on_click=on_broadcast_confirm),
|
Button(
|
||||||
Button(Const("❌ Нет"), id="cancel_broadcast_confirm", on_click=on_broadcast_cancel),
|
Const("✅ Да"), id="confirm_broadcast", on_click=on_broadcast_confirm
|
||||||
|
),
|
||||||
|
Button(
|
||||||
|
Const("❌ Нет"),
|
||||||
|
id="cancel_broadcast_confirm",
|
||||||
|
on_click=on_broadcast_cancel,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
state=AdminMenuSG.broadcast_confirm,
|
state=AdminMenuSG.broadcast_confirm,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -6,10 +6,18 @@ from dishka import FromDishka
|
|||||||
from dishka.integrations.aiogram_dialog import inject
|
from dishka.integrations.aiogram_dialog import inject
|
||||||
|
|
||||||
from dutylog.application.bot.user_dialogs.states import MainMenuSG
|
from dutylog.application.bot.user_dialogs.states import MainMenuSG
|
||||||
from dutylog.infrastructure.database.repositories.users_repository import UsersRepository
|
from dutylog.infrastructure.database.repositories.users_repository import (
|
||||||
from dutylog.infrastructure.database.repositories.residents_repository import ResidentsRepository
|
UsersRepository,
|
||||||
from dutylog.infrastructure.database.repositories.rooms_repository import RoomsRepository
|
)
|
||||||
from dutylog.infrastructure.database.repositories.hours_transactions_repository import HoursTransactionsRepository
|
from dutylog.infrastructure.database.repositories.residents_repository import (
|
||||||
|
ResidentsRepository,
|
||||||
|
)
|
||||||
|
from dutylog.infrastructure.database.repositories.rooms_repository import (
|
||||||
|
RoomsRepository,
|
||||||
|
)
|
||||||
|
from dutylog.infrastructure.database.repositories.hours_transactions_repository import (
|
||||||
|
HoursTransactionsRepository,
|
||||||
|
)
|
||||||
from dutylog.infrastructure.utils.config import Config
|
from dutylog.infrastructure.utils.config import Config
|
||||||
|
|
||||||
|
|
||||||
@@ -28,27 +36,35 @@ async def get_main_menu_data(
|
|||||||
first_name=event_from_user.first_name,
|
first_name=event_from_user.first_name,
|
||||||
last_name=event_from_user.last_name,
|
last_name=event_from_user.last_name,
|
||||||
)
|
)
|
||||||
|
|
||||||
is_creator = event_from_user.id == config.bot.creator_id
|
is_creator = event_from_user.id == config.bot.creator_id
|
||||||
is_admin = user.is_admin
|
is_admin = user.is_admin
|
||||||
|
|
||||||
if is_creator:
|
if is_creator:
|
||||||
greeting = "👑 <b>Создатель</b>"
|
greeting = "👑 <b>Создатель</b>"
|
||||||
elif is_admin:
|
elif is_admin:
|
||||||
greeting = "👨💼 <b>Администратор</b>"
|
greeting = "👨💼 <b>Администратор</b>"
|
||||||
else:
|
else:
|
||||||
resident = await residents_repository.get_resident_by_user_id(event_from_user.id)
|
resident = await residents_repository.get_resident_by_user_id(
|
||||||
|
event_from_user.id
|
||||||
|
)
|
||||||
if resident:
|
if resident:
|
||||||
room = await rooms_repository.get_room_by_id(resident.room)
|
room = await rooms_repository.get_room_by_id(resident.room)
|
||||||
room_number = room.number if room else "???"
|
room_number = room.number if room else "???"
|
||||||
real_name = resident.real_name if resident.real_name else event_from_user.first_name
|
real_name = (
|
||||||
greeting = f"👋 <b>Привет, {real_name}!</b>\n🚪 Комната <code>{room_number}</code>"
|
resident.real_name if resident.real_name else event_from_user.first_name
|
||||||
|
)
|
||||||
|
greeting = (
|
||||||
|
f"👋 <b>Привет, {real_name}!</b>\n🚪 Комната <code>{room_number}</code>"
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
greeting = f"👋 <b>Привет, {event_from_user.first_name}!</b>"
|
greeting = f"👋 <b>Привет, {event_from_user.first_name}!</b>"
|
||||||
|
|
||||||
if not is_admin and not is_creator:
|
if not is_admin and not is_creator:
|
||||||
resident = await residents_repository.get_resident_by_user_id(event_from_user.id)
|
resident = await residents_repository.get_resident_by_user_id(
|
||||||
|
event_from_user.id
|
||||||
|
)
|
||||||
|
|
||||||
if not resident:
|
if not resident:
|
||||||
content = f"""
|
content = f"""
|
||||||
{greeting}
|
{greeting}
|
||||||
@@ -79,7 +95,7 @@ async def get_main_menu_data(
|
|||||||
Добро пожаловать в систему учета дежурств!
|
Добро пожаловать в систему учета дежурств!
|
||||||
"""
|
"""
|
||||||
has_resident = False
|
has_resident = False
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"content": content,
|
"content": content,
|
||||||
"is_regular_user": not is_admin and not is_creator,
|
"is_regular_user": not is_admin and not is_creator,
|
||||||
@@ -95,7 +111,7 @@ async def get_history_data(
|
|||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
resident = await residents_repository.get_resident_by_user_id(event_from_user.id)
|
resident = await residents_repository.get_resident_by_user_id(event_from_user.id)
|
||||||
|
|
||||||
if not resident:
|
if not resident:
|
||||||
history_text = """
|
history_text = """
|
||||||
<blockquote>📜 <b>История операций</b></blockquote>
|
<blockquote>📜 <b>История операций</b></blockquote>
|
||||||
@@ -105,7 +121,7 @@ async def get_history_data(
|
|||||||
else:
|
else:
|
||||||
transactions = await transactions_repository.get_resident_history(resident.id)
|
transactions = await transactions_repository.get_resident_history(resident.id)
|
||||||
last_10 = transactions[:10]
|
last_10 = transactions[:10]
|
||||||
|
|
||||||
if not last_10:
|
if not last_10:
|
||||||
history_text = """
|
history_text = """
|
||||||
<blockquote>📜 <b>История операций</b></blockquote>
|
<blockquote>📜 <b>История операций</b></blockquote>
|
||||||
@@ -120,14 +136,14 @@ async def get_history_data(
|
|||||||
history_lines.append(
|
history_lines.append(
|
||||||
f"{emoji} <code>{tx.amount}</code> ч • <i>{date_str}</i>"
|
f"{emoji} <code>{tx.amount}</code> ч • <i>{date_str}</i>"
|
||||||
)
|
)
|
||||||
|
|
||||||
history_text = f"""
|
history_text = f"""
|
||||||
<blockquote>📜 <b>История операций</b></blockquote>
|
<blockquote>📜 <b>История операций</b></blockquote>
|
||||||
|
|
||||||
{"".join(f"{line}\n" for line in history_lines)}
|
{"".join(f"{line}\n" for line in history_lines)}
|
||||||
<i>Показаны последние 10 операций</i>
|
<i>Показаны последние 10 операций</i>
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return {"history_content": history_text}
|
return {"history_content": history_text}
|
||||||
|
|
||||||
|
|
||||||
@@ -177,4 +193,3 @@ main_menu_dialog = Dialog(
|
|||||||
state=MainMenuSG.faq,
|
state=MainMenuSG.faq,
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,15 @@ from dishka import FromDishka
|
|||||||
from dishka.integrations.aiogram_dialog import inject
|
from dishka.integrations.aiogram_dialog import inject
|
||||||
|
|
||||||
from dutylog.application.bot.user_dialogs.states import RegistrationSG, MainMenuSG
|
from dutylog.application.bot.user_dialogs.states import RegistrationSG, MainMenuSG
|
||||||
from dutylog.infrastructure.database.repositories.floors_repository import FloorsRepository
|
from dutylog.infrastructure.database.repositories.floors_repository import (
|
||||||
from dutylog.infrastructure.database.repositories.rooms_repository import RoomsRepository
|
FloorsRepository,
|
||||||
from dutylog.infrastructure.database.repositories.residents_repository import ResidentsRepository
|
)
|
||||||
|
from dutylog.infrastructure.database.repositories.rooms_repository import (
|
||||||
|
RoomsRepository,
|
||||||
|
)
|
||||||
|
from dutylog.infrastructure.database.repositories.residents_repository import (
|
||||||
|
ResidentsRepository,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@inject
|
@inject
|
||||||
@@ -21,21 +27,21 @@ async def get_floors_data(
|
|||||||
):
|
):
|
||||||
all_floors = await floors_repository.get_all_floors()
|
all_floors = await floors_repository.get_all_floors()
|
||||||
available_residents = await residents_repository.get_available_residents()
|
available_residents = await residents_repository.get_available_residents()
|
||||||
|
|
||||||
if not available_residents:
|
if not available_residents:
|
||||||
return {
|
return {
|
||||||
"has_available": False,
|
"has_available": False,
|
||||||
"floors": [],
|
"floors": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
available_room_ids = {r.room for r in available_residents}
|
available_room_ids = {r.room for r in available_residents}
|
||||||
all_rooms = await rooms_repository.get_all_rooms()
|
all_rooms = await rooms_repository.get_all_rooms()
|
||||||
available_rooms = [r for r in all_rooms if r.id in available_room_ids]
|
available_rooms = [r for r in all_rooms if r.id in available_room_ids]
|
||||||
available_floor_ids = {r.on_floor for r in available_rooms}
|
available_floor_ids = {r.on_floor for r in available_rooms}
|
||||||
|
|
||||||
available_floors = [f for f in all_floors if f.id in available_floor_ids]
|
available_floors = [f for f in all_floors if f.id in available_floor_ids]
|
||||||
available_floors.sort(key=lambda f: f.number)
|
available_floors.sort(key=lambda f: f.number)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"has_available": True,
|
"has_available": True,
|
||||||
"floors": [(f.id, f"Этаж {f.number}") for f in available_floors],
|
"floors": [(f.id, f"Этаж {f.number}") for f in available_floors],
|
||||||
@@ -50,14 +56,14 @@ async def get_rooms_data(
|
|||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
floor_id = dialog_manager.dialog_data["floor_id"]
|
floor_id = dialog_manager.dialog_data["floor_id"]
|
||||||
|
|
||||||
rooms = await rooms_repository.get_rooms_by_floor(floor_id)
|
rooms = await rooms_repository.get_rooms_by_floor(floor_id)
|
||||||
available_residents = await residents_repository.get_available_residents()
|
available_residents = await residents_repository.get_available_residents()
|
||||||
available_room_ids = {r.room for r in available_residents}
|
available_room_ids = {r.room for r in available_residents}
|
||||||
|
|
||||||
available_rooms = [r for r in rooms if r.id in available_room_ids]
|
available_rooms = [r for r in rooms if r.id in available_room_ids]
|
||||||
available_rooms.sort(key=lambda r: r.number)
|
available_rooms.sort(key=lambda r: r.number)
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"rooms": [(r.id, str(r.number)) for r in available_rooms],
|
"rooms": [(r.id, str(r.number)) for r in available_rooms],
|
||||||
}
|
}
|
||||||
@@ -70,12 +76,14 @@ async def get_residents_data(
|
|||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
):
|
||||||
room_id = dialog_manager.dialog_data["room_id"]
|
room_id = dialog_manager.dialog_data["room_id"]
|
||||||
|
|
||||||
residents = await residents_repository.get_residents_by_room(room_id)
|
residents = await residents_repository.get_residents_by_room(room_id)
|
||||||
available_residents = [r for r in residents if not r.is_busy]
|
available_residents = [r for r in residents if not r.is_busy]
|
||||||
|
|
||||||
return {
|
return {
|
||||||
"residents": [(r.id, r.real_name or f"Резидент #{r.id}") for r in available_residents],
|
"residents": [
|
||||||
|
(r.id, r.real_name or f"Резидент #{r.id}") for r in available_residents
|
||||||
|
],
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -109,17 +117,23 @@ async def on_resident_selected(
|
|||||||
):
|
):
|
||||||
user_id = callback.from_user.id
|
user_id = callback.from_user.id
|
||||||
resident_id = int(item_id)
|
resident_id = int(item_id)
|
||||||
|
|
||||||
await residents_repository.bind_user_to_resident(resident_id, user_id)
|
await residents_repository.bind_user_to_resident(resident_id, user_id)
|
||||||
|
|
||||||
await callback.answer("✅ Регистрация успешна!")
|
await callback.answer("✅ Регистрация успешна!")
|
||||||
await dialog_manager.start(MainMenuSG.main)
|
await dialog_manager.start(MainMenuSG.main)
|
||||||
|
|
||||||
|
|
||||||
registration_dialog = Dialog(
|
registration_dialog = Dialog(
|
||||||
Window(
|
Window(
|
||||||
Const("<blockquote>🏢 <b>Выбор этажа</b></blockquote>\n\n<blockquote>⚠️ <b>Внимание!</b> Перерегистрацию может выполнить только администратор. Выбирайте внимательно!</blockquote>\n\nВыберите этаж, на котором вы живете:", when="has_available"),
|
Const(
|
||||||
Const("<blockquote>⚠️ <b>Нет доступных резидентов</b></blockquote>\n\nВсе резиденты уже заняты.\nОбратитесь к администратору.", when=~F["has_available"]),
|
"<blockquote>🏢 <b>Выбор этажа</b></blockquote>\n\n<blockquote>⚠️ <b>Внимание!</b> Перерегистрацию может выполнить только администратор. Выбирайте внимательно!</blockquote>\n\nВыберите этаж, на котором вы живете:",
|
||||||
|
when="has_available",
|
||||||
|
),
|
||||||
|
Const(
|
||||||
|
"<blockquote>⚠️ <b>Нет доступных резидентов</b></blockquote>\n\nВсе резиденты уже заняты.\nОбратитесь к администратору.",
|
||||||
|
when=~F["has_available"],
|
||||||
|
),
|
||||||
Group(
|
Group(
|
||||||
Select(
|
Select(
|
||||||
Format("{item[1]}"),
|
Format("{item[1]}"),
|
||||||
@@ -135,7 +149,9 @@ registration_dialog = Dialog(
|
|||||||
getter=get_floors_data,
|
getter=get_floors_data,
|
||||||
),
|
),
|
||||||
Window(
|
Window(
|
||||||
Const("<blockquote>🚪 <b>Выбор комнаты</b></blockquote>\n\nВыберите вашу комнату:"),
|
Const(
|
||||||
|
"<blockquote>🚪 <b>Выбор комнаты</b></blockquote>\n\nВыберите вашу комнату:"
|
||||||
|
),
|
||||||
Group(
|
Group(
|
||||||
Select(
|
Select(
|
||||||
Format("{item[1]}"),
|
Format("{item[1]}"),
|
||||||
@@ -146,12 +162,16 @@ registration_dialog = Dialog(
|
|||||||
),
|
),
|
||||||
width=3,
|
width=3,
|
||||||
),
|
),
|
||||||
SwitchTo(Const("◀️ Назад"), id="back_to_floors", state=RegistrationSG.select_floor),
|
SwitchTo(
|
||||||
|
Const("◀️ Назад"), id="back_to_floors", state=RegistrationSG.select_floor
|
||||||
|
),
|
||||||
state=RegistrationSG.select_room,
|
state=RegistrationSG.select_room,
|
||||||
getter=get_rooms_data,
|
getter=get_rooms_data,
|
||||||
),
|
),
|
||||||
Window(
|
Window(
|
||||||
Const("<blockquote>👤 <b>Выбор резидента</b></blockquote>\n\nВыберите себя из списка:"),
|
Const(
|
||||||
|
"<blockquote>👤 <b>Выбор резидента</b></blockquote>\n\nВыберите себя из списка:"
|
||||||
|
),
|
||||||
Group(
|
Group(
|
||||||
Select(
|
Select(
|
||||||
Format("{item[1]}"),
|
Format("{item[1]}"),
|
||||||
@@ -162,7 +182,9 @@ registration_dialog = Dialog(
|
|||||||
),
|
),
|
||||||
width=1,
|
width=1,
|
||||||
),
|
),
|
||||||
SwitchTo(Const("◀️ Назад"), id="back_to_rooms", state=RegistrationSG.select_room),
|
SwitchTo(
|
||||||
|
Const("◀️ Назад"), id="back_to_rooms", state=RegistrationSG.select_room
|
||||||
|
),
|
||||||
state=RegistrationSG.select_resident,
|
state=RegistrationSG.select_resident,
|
||||||
getter=get_residents_data,
|
getter=get_residents_data,
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -12,7 +12,12 @@ class AdminMenuSG(StatesGroup):
|
|||||||
residents = State()
|
residents = State()
|
||||||
resident_info = State()
|
resident_info = State()
|
||||||
resident_logout_confirm = State()
|
resident_logout_confirm = State()
|
||||||
users = State()
|
add_hours_select = State()
|
||||||
|
remove_hours_select = State()
|
||||||
|
add_hours_custom = State()
|
||||||
|
remove_hours_custom = State()
|
||||||
|
add_hours_confirm = State()
|
||||||
|
remove_hours_confirm = State()
|
||||||
statistics = State()
|
statistics = State()
|
||||||
broadcast = State()
|
broadcast = State()
|
||||||
broadcast_confirm = State()
|
broadcast_confirm = State()
|
||||||
|
|||||||
@@ -4,9 +4,17 @@ from aiogram.types import Message
|
|||||||
from aiogram_dialog import DialogManager, StartMode
|
from aiogram_dialog import DialogManager, StartMode
|
||||||
from dishka import FromDishka
|
from dishka import FromDishka
|
||||||
|
|
||||||
from dutylog.application.bot.user_dialogs.states import MainMenuSG, AdminMenuSG, RegistrationSG
|
from dutylog.application.bot.user_dialogs.states import (
|
||||||
from dutylog.infrastructure.database.repositories.users_repository import UsersRepository
|
MainMenuSG,
|
||||||
from dutylog.infrastructure.database.repositories.residents_repository import ResidentsRepository
|
AdminMenuSG,
|
||||||
|
RegistrationSG,
|
||||||
|
)
|
||||||
|
from dutylog.infrastructure.database.repositories.users_repository import (
|
||||||
|
UsersRepository,
|
||||||
|
)
|
||||||
|
from dutylog.infrastructure.database.repositories.residents_repository import (
|
||||||
|
ResidentsRepository,
|
||||||
|
)
|
||||||
from dutylog.infrastructure.utils.config import Config
|
from dutylog.infrastructure.utils.config import Config
|
||||||
|
|
||||||
router = Router()
|
router = Router()
|
||||||
@@ -22,18 +30,20 @@ async def start_handler(
|
|||||||
):
|
):
|
||||||
assert message.from_user is not None
|
assert message.from_user is not None
|
||||||
user = await users_repository.get_user_by_id(message.from_user.id)
|
user = await users_repository.get_user_by_id(message.from_user.id)
|
||||||
|
|
||||||
is_creator = message.from_user.id == config.bot.creator_id
|
is_creator = message.from_user.id == config.bot.creator_id
|
||||||
is_admin = user.is_admin if user else False
|
is_admin = user.is_admin if user else False
|
||||||
|
|
||||||
if is_admin or is_creator:
|
if is_admin or is_creator:
|
||||||
await dialog_manager.start(AdminMenuSG.main, mode=StartMode.RESET_STACK)
|
await dialog_manager.start(AdminMenuSG.main, mode=StartMode.RESET_STACK)
|
||||||
return
|
return
|
||||||
|
|
||||||
if user:
|
if user:
|
||||||
resident = await residents_repository.get_resident_by_user_id(message.from_user.id)
|
resident = await residents_repository.get_resident_by_user_id(
|
||||||
|
message.from_user.id
|
||||||
|
)
|
||||||
if resident:
|
if resident:
|
||||||
await dialog_manager.start(MainMenuSG.main, mode=StartMode.RESET_STACK)
|
await dialog_manager.start(MainMenuSG.main, mode=StartMode.RESET_STACK)
|
||||||
return
|
return
|
||||||
|
|
||||||
await dialog_manager.start(RegistrationSG.select_floor, mode=StartMode.RESET_STACK)
|
await dialog_manager.start(RegistrationSG.select_floor, mode=StartMode.RESET_STACK)
|
||||||
|
|||||||
@@ -9,15 +9,11 @@ class FloorsDAO:
|
|||||||
self.session = session
|
self.session = session
|
||||||
|
|
||||||
async def get_by_id(self, floor_id: int) -> Floor | None:
|
async def get_by_id(self, floor_id: int) -> Floor | None:
|
||||||
result = await self.session.execute(
|
result = await self.session.execute(select(Floor).where(Floor.id == floor_id))
|
||||||
select(Floor).where(Floor.id == floor_id)
|
|
||||||
)
|
|
||||||
return result.scalar_one_or_none()
|
return result.scalar_one_or_none()
|
||||||
|
|
||||||
async def get_by_number(self, number: int) -> Floor | None:
|
async def get_by_number(self, number: int) -> Floor | None:
|
||||||
result = await self.session.execute(
|
result = await self.session.execute(select(Floor).where(Floor.number == number))
|
||||||
select(Floor).where(Floor.number == number)
|
|
||||||
)
|
|
||||||
return result.scalar_one_or_none()
|
return result.scalar_one_or_none()
|
||||||
|
|
||||||
async def get_all(self) -> list[Floor]:
|
async def get_all(self) -> list[Floor]:
|
||||||
@@ -38,7 +34,5 @@ class FloorsDAO:
|
|||||||
return await self.get_by_id(floor_id)
|
return await self.get_by_id(floor_id)
|
||||||
|
|
||||||
async def delete(self, floor_id: int) -> None:
|
async def delete(self, floor_id: int) -> None:
|
||||||
await self.session.execute(
|
await self.session.execute(delete(Floor).where(Floor.id == floor_id))
|
||||||
delete(Floor).where(Floor.id == floor_id)
|
|
||||||
)
|
|
||||||
await self.session.commit()
|
await self.session.commit()
|
||||||
|
|||||||
@@ -50,7 +50,5 @@ class ResidentsDAO:
|
|||||||
return await self.get_by_id(resident_id)
|
return await self.get_by_id(resident_id)
|
||||||
|
|
||||||
async def delete(self, resident_id: int) -> None:
|
async def delete(self, resident_id: int) -> None:
|
||||||
await self.session.execute(
|
await self.session.execute(delete(Resident).where(Resident.id == resident_id))
|
||||||
delete(Resident).where(Resident.id == resident_id)
|
|
||||||
)
|
|
||||||
await self.session.commit()
|
await self.session.commit()
|
||||||
|
|||||||
@@ -9,15 +9,11 @@ class RoomsDAO:
|
|||||||
self.session = session
|
self.session = session
|
||||||
|
|
||||||
async def get_by_id(self, room_id: int) -> Room | None:
|
async def get_by_id(self, room_id: int) -> Room | None:
|
||||||
result = await self.session.execute(
|
result = await self.session.execute(select(Room).where(Room.id == room_id))
|
||||||
select(Room).where(Room.id == room_id)
|
|
||||||
)
|
|
||||||
return result.scalar_one_or_none()
|
return result.scalar_one_or_none()
|
||||||
|
|
||||||
async def get_by_number(self, number: int) -> Room | None:
|
async def get_by_number(self, number: int) -> Room | None:
|
||||||
result = await self.session.execute(
|
result = await self.session.execute(select(Room).where(Room.number == number))
|
||||||
select(Room).where(Room.number == number)
|
|
||||||
)
|
|
||||||
return result.scalar_one_or_none()
|
return result.scalar_one_or_none()
|
||||||
|
|
||||||
async def get_by_floor(self, floor_id: int) -> list[Room]:
|
async def get_by_floor(self, floor_id: int) -> list[Room]:
|
||||||
@@ -44,7 +40,5 @@ class RoomsDAO:
|
|||||||
return await self.get_by_id(room_id)
|
return await self.get_by_id(room_id)
|
||||||
|
|
||||||
async def delete(self, room_id: int) -> None:
|
async def delete(self, room_id: int) -> None:
|
||||||
await self.session.execute(
|
await self.session.execute(delete(Room).where(Room.id == room_id))
|
||||||
delete(Room).where(Room.id == room_id)
|
|
||||||
)
|
|
||||||
await self.session.commit()
|
await self.session.commit()
|
||||||
|
|||||||
@@ -9,9 +9,7 @@ class UsersDAO:
|
|||||||
self.session = session
|
self.session = session
|
||||||
|
|
||||||
async def get_by_id(self, user_id: int) -> User | None:
|
async def get_by_id(self, user_id: int) -> User | None:
|
||||||
result = await self.session.execute(
|
result = await self.session.execute(select(User).where(User.id == user_id))
|
||||||
select(User).where(User.id == user_id)
|
|
||||||
)
|
|
||||||
return result.scalar_one_or_none()
|
return result.scalar_one_or_none()
|
||||||
|
|
||||||
async def get_all(self) -> list[User]:
|
async def get_all(self) -> list[User]:
|
||||||
@@ -32,7 +30,5 @@ class UsersDAO:
|
|||||||
return await self.get_by_id(user_id)
|
return await self.get_by_id(user_id)
|
||||||
|
|
||||||
async def delete(self, user_id: int) -> None:
|
async def delete(self, user_id: int) -> None:
|
||||||
await self.session.execute(
|
await self.session.execute(delete(User).where(User.id == user_id))
|
||||||
delete(User).where(User.id == user_id)
|
|
||||||
)
|
|
||||||
await self.session.commit()
|
await self.session.commit()
|
||||||
|
|||||||
@@ -17,8 +17,14 @@ class HoursTransaction(Base):
|
|||||||
__tablename__ = "hours_transactions"
|
__tablename__ = "hours_transactions"
|
||||||
|
|
||||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
resident_id: Mapped[int] = mapped_column(Integer, ForeignKey("residents.id", ondelete="CASCADE"), nullable=False)
|
resident_id: Mapped[int] = mapped_column(
|
||||||
|
Integer, ForeignKey("residents.id", ondelete="CASCADE"), nullable=False
|
||||||
|
)
|
||||||
transaction_type: Mapped[str] = mapped_column(String(50), nullable=False)
|
transaction_type: Mapped[str] = mapped_column(String(50), nullable=False)
|
||||||
amount: Mapped[int] = mapped_column(Integer, nullable=False)
|
amount: Mapped[int] = mapped_column(Integer, nullable=False)
|
||||||
admin_id: Mapped[int] = mapped_column(BigInteger, ForeignKey("users.id", ondelete="SET NULL"), nullable=True)
|
admin_id: Mapped[int] = mapped_column(
|
||||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=msk_now)
|
BigInteger, ForeignKey("users.id", ondelete="SET NULL"), nullable=True
|
||||||
|
)
|
||||||
|
created_at: Mapped[datetime] = mapped_column(
|
||||||
|
DateTime(timezone=True), default=msk_now
|
||||||
|
)
|
||||||
|
|||||||
@@ -12,10 +12,23 @@ class Resident(Base):
|
|||||||
|
|
||||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
real_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
real_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||||
room: Mapped[int] = mapped_column(Integer, ForeignKey("rooms.id", ondelete="CASCADE"), nullable=False)
|
room: Mapped[int] = mapped_column(
|
||||||
user_entity: Mapped[int | None] = mapped_column(BigInteger, ForeignKey("users.id", ondelete="SET NULL"), nullable=True, unique=True)
|
Integer, ForeignKey("rooms.id", ondelete="CASCADE"), nullable=False
|
||||||
is_busy: Mapped[bool] = mapped_column(Boolean, default=False, server_default="false")
|
)
|
||||||
|
user_entity: Mapped[int | None] = mapped_column(
|
||||||
|
BigInteger,
|
||||||
|
ForeignKey("users.id", ondelete="SET NULL"),
|
||||||
|
nullable=True,
|
||||||
|
unique=True,
|
||||||
|
)
|
||||||
|
is_busy: Mapped[bool] = mapped_column(
|
||||||
|
Boolean, default=False, server_default="false"
|
||||||
|
)
|
||||||
active_hours: Mapped[int] = mapped_column(Integer, default=0, server_default="0")
|
active_hours: Mapped[int] = mapped_column(Integer, default=0, server_default="0")
|
||||||
inactive_hours: Mapped[int] = mapped_column(Integer, default=0, server_default="0")
|
inactive_hours: Mapped[int] = mapped_column(Integer, default=0, server_default="0")
|
||||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=msk_now)
|
created_at: Mapped[datetime] = mapped_column(
|
||||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=msk_now, onupdate=msk_now)
|
DateTime(timezone=True), default=msk_now
|
||||||
|
)
|
||||||
|
updated_at: Mapped[datetime] = mapped_column(
|
||||||
|
DateTime(timezone=True), default=msk_now, onupdate=msk_now
|
||||||
|
)
|
||||||
|
|||||||
@@ -9,4 +9,6 @@ class Room(Base):
|
|||||||
|
|
||||||
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
id: Mapped[int] = mapped_column(Integer, primary_key=True, autoincrement=True)
|
||||||
number: Mapped[int] = mapped_column(Integer, nullable=False, unique=True)
|
number: Mapped[int] = mapped_column(Integer, nullable=False, unique=True)
|
||||||
on_floor: Mapped[int] = mapped_column(Integer, ForeignKey("floors.id", ondelete="CASCADE"), nullable=False)
|
on_floor: Mapped[int] = mapped_column(
|
||||||
|
Integer, ForeignKey("floors.id", ondelete="CASCADE"), nullable=False
|
||||||
|
)
|
||||||
|
|||||||
@@ -14,6 +14,12 @@ class User(Base):
|
|||||||
username: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
username: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||||
first_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
first_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||||
last_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
last_name: Mapped[str | None] = mapped_column(String(255), nullable=True)
|
||||||
is_admin: Mapped[bool] = mapped_column(Boolean, default=False, server_default="false")
|
is_admin: Mapped[bool] = mapped_column(
|
||||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=msk_now)
|
Boolean, default=False, server_default="false"
|
||||||
updated_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=msk_now, onupdate=msk_now)
|
)
|
||||||
|
created_at: Mapped[datetime] = mapped_column(
|
||||||
|
DateTime(timezone=True), default=msk_now
|
||||||
|
)
|
||||||
|
updated_at: Mapped[datetime] = mapped_column(
|
||||||
|
DateTime(timezone=True), default=msk_now, onupdate=msk_now
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
from dutylog.infrastructure.database.dao.hours_transactions_dao import HoursTransactionsDAO
|
from dutylog.infrastructure.database.dao.hours_transactions_dao import (
|
||||||
|
HoursTransactionsDAO,
|
||||||
|
)
|
||||||
from dutylog.infrastructure.database.dao.residents_dao import ResidentsDAO
|
from dutylog.infrastructure.database.dao.residents_dao import ResidentsDAO
|
||||||
from dutylog.infrastructure.database.models.hours_transaction import HoursTransaction, TransactionType
|
from dutylog.infrastructure.database.models.hours_transaction import (
|
||||||
|
HoursTransaction,
|
||||||
|
TransactionType,
|
||||||
|
)
|
||||||
from dutylog.infrastructure.database.models.resident import Resident
|
from dutylog.infrastructure.database.models.resident import Resident
|
||||||
|
|
||||||
|
|
||||||
@@ -32,10 +37,14 @@ class HoursTransactionsRepository:
|
|||||||
if resident:
|
if resident:
|
||||||
if is_active:
|
if is_active:
|
||||||
new_hours = resident.active_hours + amount
|
new_hours = resident.active_hours + amount
|
||||||
resident = await self.residents_dao.update(resident_id, active_hours=new_hours)
|
resident = await self.residents_dao.update(
|
||||||
|
resident_id, active_hours=new_hours
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
new_hours = resident.inactive_hours + amount
|
new_hours = resident.inactive_hours + amount
|
||||||
resident = await self.residents_dao.update(resident_id, inactive_hours=new_hours)
|
resident = await self.residents_dao.update(
|
||||||
|
resident_id, inactive_hours=new_hours
|
||||||
|
)
|
||||||
|
|
||||||
return transaction, resident
|
return transaction, resident
|
||||||
|
|
||||||
@@ -58,10 +67,14 @@ class HoursTransactionsRepository:
|
|||||||
if resident:
|
if resident:
|
||||||
if is_active:
|
if is_active:
|
||||||
new_hours = max(0, resident.active_hours - amount)
|
new_hours = max(0, resident.active_hours - amount)
|
||||||
resident = await self.residents_dao.update(resident_id, active_hours=new_hours)
|
resident = await self.residents_dao.update(
|
||||||
|
resident_id, active_hours=new_hours
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
new_hours = max(0, resident.inactive_hours - amount)
|
new_hours = max(0, resident.inactive_hours - amount)
|
||||||
resident = await self.residents_dao.update(resident_id, inactive_hours=new_hours)
|
resident = await self.residents_dao.update(
|
||||||
|
resident_id, inactive_hours=new_hours
|
||||||
|
)
|
||||||
|
|
||||||
return transaction, resident
|
return transaction, resident
|
||||||
|
|
||||||
@@ -71,5 +84,7 @@ class HoursTransactionsRepository:
|
|||||||
async def get_all_transactions(self) -> list[HoursTransaction]:
|
async def get_all_transactions(self) -> list[HoursTransaction]:
|
||||||
return await self.transactions_dao.get_all()
|
return await self.transactions_dao.get_all()
|
||||||
|
|
||||||
async def get_transaction_by_id(self, transaction_id: int) -> HoursTransaction | None:
|
async def get_transaction_by_id(
|
||||||
|
self, transaction_id: int
|
||||||
|
) -> HoursTransaction | None:
|
||||||
return await self.transactions_dao.get_by_id(transaction_id)
|
return await self.transactions_dao.get_by_id(transaction_id)
|
||||||
|
|||||||
@@ -17,7 +17,9 @@ class ResidentsRepository:
|
|||||||
)
|
)
|
||||||
return await self.residents_dao.create(resident)
|
return await self.residents_dao.create(resident)
|
||||||
|
|
||||||
async def bind_user_to_resident(self, resident_id: int, user_id: int) -> Resident | None:
|
async def bind_user_to_resident(
|
||||||
|
self, resident_id: int, user_id: int
|
||||||
|
) -> Resident | None:
|
||||||
return await self.residents_dao.update(
|
return await self.residents_dao.update(
|
||||||
resident_id,
|
resident_id,
|
||||||
user_entity=user_id,
|
user_entity=user_id,
|
||||||
@@ -52,7 +54,9 @@ class ResidentsRepository:
|
|||||||
resident = await self.residents_dao.get_by_id(resident_id)
|
resident = await self.residents_dao.get_by_id(resident_id)
|
||||||
if resident:
|
if resident:
|
||||||
new_hours = resident.inactive_hours + hours
|
new_hours = resident.inactive_hours + hours
|
||||||
return await self.residents_dao.update(resident_id, inactive_hours=new_hours)
|
return await self.residents_dao.update(
|
||||||
|
resident_id, inactive_hours=new_hours
|
||||||
|
)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
async def get_resident_by_id(self, resident_id: int) -> Resident | None:
|
async def get_resident_by_id(self, resident_id: int) -> Resident | None:
|
||||||
|
|||||||
@@ -5,15 +5,27 @@ from sqlalchemy.ext.asyncio import AsyncEngine, AsyncSession, async_sessionmaker
|
|||||||
|
|
||||||
from dutylog.infrastructure.database.config import create_engine, create_session_maker
|
from dutylog.infrastructure.database.config import create_engine, create_session_maker
|
||||||
from dutylog.infrastructure.database.dao.users_dao import UsersDAO
|
from dutylog.infrastructure.database.dao.users_dao import UsersDAO
|
||||||
from dutylog.infrastructure.database.dao.hours_transactions_dao import HoursTransactionsDAO
|
from dutylog.infrastructure.database.dao.hours_transactions_dao import (
|
||||||
|
HoursTransactionsDAO,
|
||||||
|
)
|
||||||
from dutylog.infrastructure.database.dao.rooms_dao import RoomsDAO
|
from dutylog.infrastructure.database.dao.rooms_dao import RoomsDAO
|
||||||
from dutylog.infrastructure.database.dao.residents_dao import ResidentsDAO
|
from dutylog.infrastructure.database.dao.residents_dao import ResidentsDAO
|
||||||
from dutylog.infrastructure.database.dao.floors_dao import FloorsDAO
|
from dutylog.infrastructure.database.dao.floors_dao import FloorsDAO
|
||||||
from dutylog.infrastructure.database.repositories.users_repository import UsersRepository
|
from dutylog.infrastructure.database.repositories.users_repository import (
|
||||||
from dutylog.infrastructure.database.repositories.hours_transactions_repository import HoursTransactionsRepository
|
UsersRepository,
|
||||||
from dutylog.infrastructure.database.repositories.rooms_repository import RoomsRepository
|
)
|
||||||
from dutylog.infrastructure.database.repositories.residents_repository import ResidentsRepository
|
from dutylog.infrastructure.database.repositories.hours_transactions_repository import (
|
||||||
from dutylog.infrastructure.database.repositories.floors_repository import FloorsRepository
|
HoursTransactionsRepository,
|
||||||
|
)
|
||||||
|
from dutylog.infrastructure.database.repositories.rooms_repository import (
|
||||||
|
RoomsRepository,
|
||||||
|
)
|
||||||
|
from dutylog.infrastructure.database.repositories.residents_repository import (
|
||||||
|
ResidentsRepository,
|
||||||
|
)
|
||||||
|
from dutylog.infrastructure.database.repositories.floors_repository import (
|
||||||
|
FloorsRepository,
|
||||||
|
)
|
||||||
from dutylog.infrastructure.utils.config import Config, load_config
|
from dutylog.infrastructure.utils.config import Config, load_config
|
||||||
|
|
||||||
|
|
||||||
@@ -29,7 +41,9 @@ class DatabaseProvider(Provider):
|
|||||||
return create_engine(config.database.url)
|
return create_engine(config.database.url)
|
||||||
|
|
||||||
@provide(scope=Scope.APP)
|
@provide(scope=Scope.APP)
|
||||||
def get_session_maker(self, engine: AsyncEngine) -> async_sessionmaker[AsyncSession]:
|
def get_session_maker(
|
||||||
|
self, engine: AsyncEngine
|
||||||
|
) -> async_sessionmaker[AsyncSession]:
|
||||||
return create_session_maker(engine)
|
return create_session_maker(engine)
|
||||||
|
|
||||||
@provide(scope=Scope.REQUEST)
|
@provide(scope=Scope.REQUEST)
|
||||||
@@ -80,14 +94,11 @@ class RepositoryProvider(Provider):
|
|||||||
return RoomsRepository(rooms_dao)
|
return RoomsRepository(rooms_dao)
|
||||||
|
|
||||||
@provide(scope=Scope.REQUEST)
|
@provide(scope=Scope.REQUEST)
|
||||||
def get_residents_repository(self, residents_dao: ResidentsDAO) -> ResidentsRepository:
|
def get_residents_repository(
|
||||||
|
self, residents_dao: ResidentsDAO
|
||||||
|
) -> ResidentsRepository:
|
||||||
return ResidentsRepository(residents_dao)
|
return ResidentsRepository(residents_dao)
|
||||||
|
|
||||||
@provide(scope=Scope.REQUEST)
|
@provide(scope=Scope.REQUEST)
|
||||||
def get_floors_repository(self, floors_dao: FloorsDAO) -> FloorsRepository:
|
def get_floors_repository(self, floors_dao: FloorsDAO) -> FloorsRepository:
|
||||||
return FloorsRepository(floors_dao)
|
return FloorsRepository(floors_dao)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,11 +17,11 @@ class DatabaseConfig:
|
|||||||
user: str
|
user: str
|
||||||
password: str
|
password: str
|
||||||
database: str
|
database: str
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def url(self) -> str:
|
def url(self) -> str:
|
||||||
return f"postgresql+asyncpg://{self.user}:{self.password}@{self.host}:{self.port}/{self.database}"
|
return f"postgresql+asyncpg://{self.user}:{self.password}@{self.host}:{self.port}/{self.database}"
|
||||||
|
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Config:
|
class Config:
|
||||||
@@ -31,30 +31,30 @@ class Config:
|
|||||||
|
|
||||||
def load_config() -> Config:
|
def load_config() -> Config:
|
||||||
config_path = Path("config.toml")
|
config_path = Path("config.toml")
|
||||||
|
|
||||||
if not config_path.exists():
|
if not config_path.exists():
|
||||||
raise FileNotFoundError(f"Config file not found: {config_path}")
|
raise FileNotFoundError(f"Config file not found: {config_path}")
|
||||||
|
|
||||||
with open(config_path, "rb") as f:
|
with open(config_path, "rb") as f:
|
||||||
data = tomllib.load(f)
|
data = tomllib.load(f)
|
||||||
|
|
||||||
if "bot" not in data:
|
if "bot" not in data:
|
||||||
raise KeyError("Missing required section: bot")
|
raise KeyError("Missing required section: bot")
|
||||||
if "database" not in data:
|
if "database" not in data:
|
||||||
raise KeyError("Missing required section: database")
|
raise KeyError("Missing required section: database")
|
||||||
|
|
||||||
bot_data = data["bot"]
|
bot_data = data["bot"]
|
||||||
if "token" not in bot_data:
|
if "token" not in bot_data:
|
||||||
raise KeyError("Missing required field: bot.token")
|
raise KeyError("Missing required field: bot.token")
|
||||||
if "creator_id" not in bot_data:
|
if "creator_id" not in bot_data:
|
||||||
raise KeyError("Missing required field: bot.creator_id")
|
raise KeyError("Missing required field: bot.creator_id")
|
||||||
|
|
||||||
database_data = data["database"]
|
database_data = data["database"]
|
||||||
required_db_fields = ["host", "port", "user", "password", "database"]
|
required_db_fields = ["host", "port", "user", "password", "database"]
|
||||||
for field in required_db_fields:
|
for field in required_db_fields:
|
||||||
if field not in database_data:
|
if field not in database_data:
|
||||||
raise KeyError(f"Missing required field: database.{field}")
|
raise KeyError(f"Missing required field: database.{field}")
|
||||||
|
|
||||||
return Config(
|
return Config(
|
||||||
bot=BotConfig(
|
bot=BotConfig(
|
||||||
token=bot_data["token"],
|
token=bot_data["token"],
|
||||||
|
|||||||
Reference in New Issue
Block a user