diff --git a/src/dutylog/application/bot/admin_dialogs/admin_menu_dialog.py b/src/dutylog/application/bot/admin_dialogs/admin_menu_dialog.py index 4c99eca..8845072 100644 --- a/src/dutylog/application/bot/admin_dialogs/admin_menu_dialog.py +++ b/src/dutylog/application/bot/admin_dialogs/admin_menu_dialog.py @@ -68,6 +68,9 @@ from dutylog.application.bot.admin_dialogs.broadcast import ( from dutylog.application.bot.admin_dialogs.faq import ( admin_faq_window, ) +from dutylog.application.bot.admin_dialogs.top_residents import ( + top_residents_window, +) admin_menu_dialog = Dialog( @@ -121,4 +124,5 @@ admin_menu_dialog = Dialog( filter_select_window, filter_hours_input_window, filtered_results_window, + top_residents_window, ) diff --git a/src/dutylog/application/bot/admin_dialogs/main_menu.py b/src/dutylog/application/bot/admin_dialogs/main_menu.py index 044f90a..ea085d9 100644 --- a/src/dutylog/application/bot/admin_dialogs/main_menu.py +++ b/src/dutylog/application/bot/admin_dialogs/main_menu.py @@ -109,8 +109,6 @@ async def get_statistics_data( admins_count = len([u for u in all_users if u.is_admin]) total_rooms = len(all_rooms) - total_rooms_active_hours = sum(r.active_hours for r in all_rooms) - total_rooms_inactive_hours = sum(r.inactive_hours for r in all_rooms) stats_text = f"""
📊 Статистика системы
@@ -131,10 +129,6 @@ async def get_statistics_data( 🚪 КОМНАТЫ 🏠 Всего комнат: {total_rooms} - -🟢 Отработанных часов: {total_rooms_inactive_hours} ч -🔴 Неотработанных часов: {total_rooms_active_hours} ч -📊 Итого: {total_rooms_active_hours + total_rooms_inactive_hours} ч """ return {"stats_content": stats_text} @@ -193,11 +187,16 @@ main_menu_window = Window( state=AdminMenuSG.statistics, ), SwitchTo( - Const("❓ FAQ"), - id="faq_btn", - state=AdminMenuSG.faq, + Const("🏆 Топ"), + id="top_btn", + state=AdminMenuSG.top_residents, ), ), + SwitchTo( + Const("❓ FAQ"), + id="faq_btn", + state=AdminMenuSG.faq, + ), SwitchTo( Const("📢 Рассылка"), id="broadcast_btn", diff --git a/src/dutylog/application/bot/admin_dialogs/top_residents.py b/src/dutylog/application/bot/admin_dialogs/top_residents.py new file mode 100644 index 0000000..b70d6eb --- /dev/null +++ b/src/dutylog/application/bot/admin_dialogs/top_residents.py @@ -0,0 +1,57 @@ +from aiogram_dialog import Window +from aiogram_dialog.widgets.text import Format, Const +from aiogram_dialog.widgets.kbd import SwitchTo +from dishka import FromDishka +from dishka.integrations.aiogram_dialog import inject + +from dutylog.application.bot.user_dialogs.states import AdminMenuSG +from dutylog.infrastructure.database.repositories.residents_repository import ( + ResidentsRepository, +) + + +@inject +async def get_top_residents_data( + residents_repository: FromDishka[ResidentsRepository], + **kwargs, +) -> dict[str, str]: + all_residents = await residents_repository.get_all_residents() + + residents_with_hours = [ + r for r in all_residents + if (r.inactive_hours + r.active_hours) > 0 + ] + + sorted_residents = sorted( + residents_with_hours, + key=lambda r: r.inactive_hours + r.active_hours, + reverse=True + ) + + top_residents = sorted_residents[:5] + + if not top_residents: + content = "🏆 Топ общежития\n\n⚠️ Нет данных для отображения топа." + else: + content = "🏆 Топ общежития\n\n" + medals = ["🥇", "🥈", "🥉", "4.", "5."] + + for idx, resident in enumerate(top_residents): + total_hours = resident.inactive_hours + resident.active_hours + name = resident.real_name or "Без имени" + + content += f"
{medals[idx]} {name}{total_hours} ч
\n" + + return {"content": content} + + +top_residents_window = Window( + Format("{content}"), + SwitchTo( + Const("◀️ Назад"), + id="back_to_main", + state=AdminMenuSG.main, + ), + state=AdminMenuSG.top_residents, + getter=get_top_residents_data, +) diff --git a/src/dutylog/application/bot/creator_dialogs/admins_management.py b/src/dutylog/application/bot/creator_dialogs/admins_management.py index e71b576..6e405ea 100644 --- a/src/dutylog/application/bot/creator_dialogs/admins_management.py +++ b/src/dutylog/application/bot/creator_dialogs/admins_management.py @@ -304,6 +304,11 @@ admins_list_window = Window( id="history_btn", state=CreatorMenuSG.transactions_history, ), + SwitchTo( + Const("🏆 Топ"), + id="top_btn", + state=CreatorMenuSG.top_residents, + ), Button( Const("◀️ Назад"), id="back_to_main_from_admins", diff --git a/src/dutylog/application/bot/creator_dialogs/creator_menu_dialog.py b/src/dutylog/application/bot/creator_dialogs/creator_menu_dialog.py index b4b747c..8b985b8 100644 --- a/src/dutylog/application/bot/creator_dialogs/creator_menu_dialog.py +++ b/src/dutylog/application/bot/creator_dialogs/creator_menu_dialog.py @@ -10,6 +10,9 @@ from dutylog.application.bot.creator_dialogs.admins_management import ( from dutylog.application.bot.creator_dialogs.transactions_history import ( transactions_history_window, ) +from dutylog.application.bot.creator_dialogs.top_residents import ( + top_residents_window, +) creator_menu_dialog = Dialog( @@ -19,4 +22,5 @@ creator_menu_dialog = Dialog( add_admin_select_user_window, add_admin_confirm_window, transactions_history_window, + top_residents_window, ) diff --git a/src/dutylog/application/bot/creator_dialogs/top_residents.py b/src/dutylog/application/bot/creator_dialogs/top_residents.py new file mode 100644 index 0000000..aa07f87 --- /dev/null +++ b/src/dutylog/application/bot/creator_dialogs/top_residents.py @@ -0,0 +1,53 @@ +from aiogram_dialog import Window +from aiogram_dialog.widgets.text import Format, Const +from aiogram_dialog.widgets.kbd import Back +from dishka import FromDishka +from dishka.integrations.aiogram_dialog import inject + +from dutylog.application.bot.user_dialogs.states import CreatorMenuSG +from dutylog.infrastructure.database.repositories.residents_repository import ( + ResidentsRepository, +) + + +@inject +async def get_top_residents_data( + residents_repository: FromDishka[ResidentsRepository], + **kwargs, +) -> dict[str, str]: + all_residents = await residents_repository.get_all_residents() + + residents_with_hours = [ + r for r in all_residents + if (r.inactive_hours + r.active_hours) > 0 + ] + + sorted_residents = sorted( + residents_with_hours, + key=lambda r: r.inactive_hours + r.active_hours, + reverse=True + ) + + top_residents = sorted_residents[:5] + + if not top_residents: + content = "🏆 Топ общежития\n\n⚠️ Нет данных для отображения топа." + else: + content = "🏆 Топ общежития\n\n" + medals = ["🥇", "🥈", "🥉", "4.", "5."] + + for idx, resident in enumerate(top_residents): + total_hours = resident.inactive_hours + resident.active_hours + name = resident.real_name or "Без имени" + + content += f"
{medals[idx]} {name}{total_hours} ч
\n" + + return {"content": content} + + +top_residents_window = Window( + Format("{content}"), + Back(Const("◀️ Назад")), + state=CreatorMenuSG.top_residents, + getter=get_top_residents_data, +) diff --git a/src/dutylog/application/bot/user_dialogs/states.py b/src/dutylog/application/bot/user_dialogs/states.py index f53c80e..ff13137 100644 --- a/src/dutylog/application/bot/user_dialogs/states.py +++ b/src/dutylog/application/bot/user_dialogs/states.py @@ -59,6 +59,7 @@ class AdminMenuSG(StatesGroup): faq = State() broadcast = State() broadcast_confirm = State() + top_residents = State() class CreatorMenuSG(StatesGroup): @@ -68,6 +69,7 @@ class CreatorMenuSG(StatesGroup): add_admin_select_user = State() add_admin_confirm = State() transactions_history = State() + top_residents = State() class RegistrationSG(StatesGroup):