This commit is contained in:
2026-03-01 01:25:22 +03:00
parent f899eb9199
commit d081b3b63c
4 changed files with 249 additions and 5 deletions
@@ -15,6 +15,11 @@ from dutylog.application.bot.user_dialogs.admin_dialogs.residents_management imp
search_input_window,
search_results_window,
)
from dutylog.application.bot.user_dialogs.admin_dialogs.residents_filter import (
filter_select_window,
filter_hours_input_window,
filtered_results_window,
)
from dutylog.application.bot.user_dialogs.admin_dialogs.hours_management import (
add_hours_select_window,
remove_hours_select_window,
@@ -49,4 +54,7 @@ admin_menu_dialog = Dialog(
broadcast_confirm_window,
search_input_window,
search_results_window,
filter_select_window,
filter_hours_input_window,
filtered_results_window,
)
@@ -0,0 +1,223 @@
from aiogram.types import Message, CallbackQuery
from aiogram_dialog import Window, DialogManager
from aiogram_dialog.widgets.text import Format, Const
from aiogram_dialog.widgets.kbd import SwitchTo, Button, ScrollingGroup, Select, Group
from aiogram_dialog.widgets.input import MessageInput
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,
)
from dutylog.infrastructure.database.repositories.rooms_repository import (
RoomsRepository,
)
async def on_filter_residents(
callback: CallbackQuery,
button: Button,
dialog_manager: DialogManager,
):
await dialog_manager.switch_to(AdminMenuSG.residents_filter_select)
async def get_filter_options_data(**kwargs):
filter_options = [
("busy", "🟢 Только занятые"),
("free", "⚪️ Только свободные"),
("hours_more", "🔴 Неотработанных часов > X"),
("hours_less", "🔴 Неотработанных часов < X"),
("worked_more", "🟢 Отработанных часов > X"),
("worked_less", "🟢 Отработанных часов < X"),
]
return {"filter_options": filter_options}
async def on_filter_selected(
callback: CallbackQuery,
widget: Select,
dialog_manager: DialogManager,
item_id: str,
):
dialog_manager.dialog_data["filter_type"] = item_id
if item_id in ["hours_more", "hours_less", "worked_more", "worked_less"]:
await dialog_manager.switch_to(AdminMenuSG.residents_filter_hours_input)
else:
dialog_manager.dialog_data["filter_hours"] = None
await dialog_manager.switch_to(AdminMenuSG.residents_filtered_results)
async def on_filter_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["filter_hours"] = hours
await dialog_manager.switch_to(AdminMenuSG.residents_filtered_results)
except ValueError:
await message.answer("⚠️ Пожалуйста, введите корректное число")
@inject
async def get_filtered_results_data(
dialog_manager: DialogManager,
residents_repository: FromDishka[ResidentsRepository],
rooms_repository: FromDishka[RoomsRepository],
**kwargs,
):
filter_type = dialog_manager.dialog_data.get("filter_type", "")
filter_hours = dialog_manager.dialog_data.get("filter_hours")
all_residents = await residents_repository.get_all_residents()
if filter_type == "busy":
filtered = [r for r in all_residents if r.is_busy]
filter_description = "занятым резидентам"
elif filter_type == "free":
filtered = [r for r in all_residents if not r.is_busy]
filter_description = "свободным резидентам"
elif filter_type == "hours_more":
filtered = [r for r in all_residents if r.active_hours > filter_hours]
filter_description = f"неотработанных часов > {filter_hours}"
elif filter_type == "hours_less":
filtered = [r for r in all_residents if r.active_hours < filter_hours]
filter_description = f"неотработанных часов < {filter_hours}"
elif filter_type == "worked_more":
filtered = [r for r in all_residents if r.inactive_hours > filter_hours]
filter_description = f"отработанных часов > {filter_hours}"
elif filter_type == "worked_less":
filtered = [r for r in all_residents if r.inactive_hours < filter_hours]
filter_description = f"отработанных часов < {filter_hours}"
else:
filtered = all_residents
filter_description = "всем резидентам"
if not filtered:
return {
"content": f"""
<blockquote>🔽 <b>Результаты фильтрации</b></blockquote>
<b>Фильтр:</b> {filter_description}
❌ Ничего не найдено
""",
"residents": [],
"has_results": False,
}
residents_with_rooms = []
for resident in filtered:
room = await rooms_repository.get_room_by_id(resident.room)
room_number = room.number if room else 999999
residents_with_rooms.append((resident, room_number))
residents_with_rooms.sort(key=lambda x: x[1])
residents_data = []
for resident, room_number in residents_with_rooms:
status = "🟢" if resident.is_busy else "⚪️"
name = resident.real_name if resident.real_name else "Без имени"
residents_data.append(
(f"{name} | Комната {room_number} | {status}", resident.id)
)
content = f"""
<blockquote>🔽 <b>Результаты фильтрации по {filter_description}</b></blockquote>
<b>Найдено резидентов:</b> <code>{len(filtered)}</code>
Выберите резидента для просмотра информации:
"""
return {
"content": content,
"residents": residents_data,
"has_results": True,
}
async def on_filtered_resident_selected(
callback: CallbackQuery,
widget: Select,
dialog_manager: DialogManager,
item_id: str,
):
dialog_manager.dialog_data["selected_resident_id"] = int(item_id)
dialog_manager.dialog_data["from_filter"] = True
await dialog_manager.switch_to(AdminMenuSG.resident_info)
filter_select_window = Window(
Const("<blockquote>🔽 <b>Фильтрация резидентов</b></blockquote>\n\n<blockquote>Выберите критерий фильтрации для отображения нужных резидентов.</blockquote>"),
Group(
Select(
Format("{item[1]}"),
id="filter_select",
item_id_getter=lambda x: x[0],
items="filter_options",
on_click=on_filter_selected,
),
width=1,
),
SwitchTo(
Const("◀️ Отмена"),
id="cancel_filter",
state=AdminMenuSG.residents,
),
state=AdminMenuSG.residents_filter_select,
getter=get_filter_options_data,
)
filter_hours_input_window = Window(
Const("<blockquote>🔽 <b>Фильтрация по часам</b></blockquote>\n\n<blockquote>Введите количество часов для фильтрации.</blockquote>"),
MessageInput(on_filter_hours_input),
SwitchTo(
Const("◀️ Назад"),
id="back_to_filter_select",
state=AdminMenuSG.residents_filter_select,
),
state=AdminMenuSG.residents_filter_hours_input,
)
filtered_results_window = Window(
Format("{content}"),
ScrollingGroup(
Select(
Format("{item[0]}"),
id="filtered_residents_select",
item_id_getter=lambda x: x[1],
items="residents",
on_click=on_filtered_resident_selected,
),
id="filtered_residents_scroll",
width=1,
height=7,
when="has_results",
),
SwitchTo(
Const("🔽 Новый фильтр"),
id="new_filter",
state=AdminMenuSG.residents_filter_select,
),
SwitchTo(
Const("◀️ К списку резидентов"),
id="back_to_residents_from_filter",
state=AdminMenuSG.residents,
),
state=AdminMenuSG.residents_filtered_results,
getter=get_filtered_results_data,
)
@@ -72,12 +72,12 @@ async def get_resident_info_data(
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
if not resident_id:
return {"info_content": "Ошибка: резидент не выбран", "is_busy": False, "from_search": False}
return {"info_content": "Ошибка: резидент не выбран", "is_busy": False, "from_search": False, "from_filter": False}
resident = await residents_repository.get_resident_by_id(resident_id)
if not resident:
return {"info_content": "Ошибка: резидент не найден", "is_busy": False, "from_search": False}
return {"info_content": "Ошибка: резидент не найден", "is_busy": False, "from_search": False, "from_filter": False}
room = await rooms_repository.get_room_by_id(resident.room)
room_number = room.number if room else "???"
@@ -111,11 +111,13 @@ async def get_resident_info_data(
"""
from_search = dialog_manager.dialog_data.get("from_search", False)
from_filter = dialog_manager.dialog_data.get("from_filter", False)
return {
"info_content": info_content,
"is_busy": resident.is_busy,
"from_search": from_search,
"from_filter": from_filter,
}
@@ -127,6 +129,7 @@ async def on_resident_selected(
):
dialog_manager.dialog_data["selected_resident_id"] = int(item_id)
dialog_manager.dialog_data["from_search"] = False
dialog_manager.dialog_data["from_filter"] = False
await dialog_manager.switch_to(AdminMenuSG.resident_info)
@@ -143,7 +146,7 @@ async def on_filter_residents(
button: Button,
dialog_manager: DialogManager,
):
await callback.answer("⚠️ Функционал в разработке", show_alert=True)
await dialog_manager.switch_to(AdminMenuSG.residents_filter_select)
async def on_search_residents(
@@ -363,6 +366,7 @@ async def on_search_resident_selected(
):
dialog_manager.dialog_data["selected_resident_id"] = int(item_id)
dialog_manager.dialog_data["from_search"] = True
dialog_manager.dialog_data["from_filter"] = False
await dialog_manager.switch_to(AdminMenuSG.resident_info)
@@ -427,16 +431,22 @@ resident_info_window = Window(
when="is_busy",
),
SwitchTo(
Const("◀️ Назад к результатам"),
Const("◀️ Назад к результатам поиска"),
id="back_to_search_results",
state=AdminMenuSG.residents_search_results,
when="from_search",
),
SwitchTo(
Const("◀️ Назад к результатам фильтра"),
id="back_to_filter_results",
state=AdminMenuSG.residents_filtered_results,
when="from_filter",
),
SwitchTo(
Const("◀️ Назад к списку"),
id="back_to_residents_list",
state=AdminMenuSG.residents,
when=~F["from_search"],
when=~F["from_search"] & ~F["from_filter"],
),
state=AdminMenuSG.resident_info,
getter=get_resident_info_data,
@@ -12,6 +12,9 @@ class AdminMenuSG(StatesGroup):
residents = State()
residents_search_input = State()
residents_search_results = State()
residents_filter_select = State()
residents_filter_hours_input = State()
residents_filtered_results = State()
resident_info = State()
resident_logout_confirm = State()
add_hours_select = State()