mirror of
https://github.com/koloideal/DutyLog.git
synced 2026-06-10 10:25:29 +03:00
update
This commit is contained in:
@@ -5,6 +5,7 @@ from aiogram_dialog import Dialog, Window, DialogManager
|
||||
from aiogram_dialog.widgets.text import Format, Const
|
||||
from aiogram_dialog.widgets.kbd import Row, SwitchTo, Button, ScrollingGroup, Select, Group
|
||||
from aiogram_dialog.widgets.input import MessageInput
|
||||
from magic_filter import F
|
||||
from dishka import FromDishka
|
||||
from dishka.integrations.aiogram_dialog import inject
|
||||
|
||||
@@ -213,12 +214,12 @@ async def get_resident_info_data(
|
||||
resident_id = dialog_manager.dialog_data.get("selected_resident_id")
|
||||
|
||||
if not resident_id:
|
||||
return {"info_content": "Ошибка: резидент не выбран"}
|
||||
return {"info_content": "Ошибка: резидент не выбран", "is_busy": False, "from_search": False}
|
||||
|
||||
resident = await residents_repository.get_resident_by_id(resident_id)
|
||||
|
||||
if not resident:
|
||||
return {"info_content": "Ошибка: резидент не найден"}
|
||||
return {"info_content": "Ошибка: резидент не найден", "is_busy": False, "from_search": False}
|
||||
|
||||
room = await rooms_repository.get_room_by_id(resident.room)
|
||||
room_number = room.number if room else "???"
|
||||
@@ -251,9 +252,12 @@ async def get_resident_info_data(
|
||||
🔴 <b>Неотработанные часы:</b> <code>{resident.active_hours}</code> ч
|
||||
"""
|
||||
|
||||
from_search = dialog_manager.dialog_data.get("from_search", False)
|
||||
|
||||
return {
|
||||
"info_content": info_content,
|
||||
"is_busy": resident.is_busy,
|
||||
"from_search": from_search,
|
||||
}
|
||||
|
||||
|
||||
@@ -264,6 +268,7 @@ async def on_resident_selected(
|
||||
item_id: str,
|
||||
):
|
||||
dialog_manager.dialog_data["selected_resident_id"] = int(item_id)
|
||||
dialog_manager.dialog_data["from_search"] = False
|
||||
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||
|
||||
|
||||
@@ -389,7 +394,7 @@ async def on_search_residents(
|
||||
button: Button,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
await callback.answer("⚠️ Функционал в разработке", show_alert=True)
|
||||
await dialog_manager.switch_to(AdminMenuSG.residents_search_input)
|
||||
|
||||
|
||||
async def on_rooms_click(
|
||||
@@ -578,6 +583,94 @@ async def on_hours_cancel(
|
||||
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||
|
||||
|
||||
async def on_search_input(
|
||||
message: Message,
|
||||
widget: MessageInput,
|
||||
dialog_manager: DialogManager,
|
||||
):
|
||||
if not message.text or len(message.text.strip()) < 1:
|
||||
await message.answer("⚠️ Пожалуйста, введите поисковый запрос")
|
||||
return
|
||||
|
||||
query = message.text.strip()
|
||||
if query.startswith("@"):
|
||||
query = query[1:]
|
||||
|
||||
dialog_manager.dialog_data["search_query"] = query
|
||||
dialog_manager.dialog_data["is_search_active"] = True
|
||||
await dialog_manager.switch_to(AdminMenuSG.residents_search_results)
|
||||
|
||||
|
||||
@inject
|
||||
async def get_search_results_data(
|
||||
dialog_manager: DialogManager,
|
||||
residents_repository: FromDishka[ResidentsRepository],
|
||||
rooms_repository: FromDishka[RoomsRepository],
|
||||
users_repository: FromDishka[UsersRepository],
|
||||
**kwargs,
|
||||
):
|
||||
query = dialog_manager.dialog_data.get("search_query", "")
|
||||
|
||||
residents, search_type = await residents_repository.search_residents(
|
||||
query, users_repository
|
||||
)
|
||||
|
||||
if not residents:
|
||||
return {
|
||||
"content": f"""
|
||||
<blockquote>🔍 <b>Результаты поиска</b></blockquote>
|
||||
|
||||
<b>Запрос:</b> <code>{query}</code>
|
||||
|
||||
❌ Ничего не найдено
|
||||
""",
|
||||
"residents": [],
|
||||
"has_results": False,
|
||||
}
|
||||
|
||||
residents_with_rooms = []
|
||||
for resident in residents:
|
||||
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>Результаты поиска по {search_type}</b></blockquote>
|
||||
|
||||
<b>Найдено резидентов:</b> <code>{len(residents)}</code>
|
||||
|
||||
Выберите резидента для просмотра информации:
|
||||
"""
|
||||
|
||||
return {
|
||||
"content": content,
|
||||
"residents": residents_data,
|
||||
"has_results": True,
|
||||
}
|
||||
|
||||
|
||||
async def on_search_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_search"] = True
|
||||
await dialog_manager.switch_to(AdminMenuSG.resident_info)
|
||||
|
||||
|
||||
admin_menu_dialog = Dialog(
|
||||
Window(
|
||||
Format("{content}"),
|
||||
@@ -670,10 +763,17 @@ admin_menu_dialog = Dialog(
|
||||
on_click=lambda c, b, m: m.switch_to(AdminMenuSG.resident_logout_confirm),
|
||||
when="is_busy",
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад к результатам"),
|
||||
id="back_to_search_results",
|
||||
state=AdminMenuSG.residents_search_results,
|
||||
when="from_search",
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ Назад к списку"),
|
||||
id="back_to_residents_list",
|
||||
state=AdminMenuSG.residents,
|
||||
when=~F["from_search"],
|
||||
),
|
||||
state=AdminMenuSG.resident_info,
|
||||
getter=get_resident_info_data,
|
||||
@@ -897,4 +997,42 @@ admin_menu_dialog = Dialog(
|
||||
),
|
||||
state=AdminMenuSG.broadcast_confirm,
|
||||
),
|
||||
Window(
|
||||
Const("<blockquote>🔍 <b>Поиск резидентов</b></blockquote>\n\n<i>Введите номер комнаты, имя резидента или username пользователя. Можно указать только часть имени или username для поиска.</i>"),
|
||||
MessageInput(on_search_input),
|
||||
SwitchTo(
|
||||
Const("◀️ Отмена"),
|
||||
id="cancel_search",
|
||||
state=AdminMenuSG.residents,
|
||||
),
|
||||
state=AdminMenuSG.residents_search_input,
|
||||
),
|
||||
Window(
|
||||
Format("{content}"),
|
||||
ScrollingGroup(
|
||||
Select(
|
||||
Format("{item[0]}"),
|
||||
id="search_residents_select",
|
||||
item_id_getter=lambda x: x[1],
|
||||
items="residents",
|
||||
on_click=on_search_resident_selected,
|
||||
),
|
||||
id="search_residents_scroll",
|
||||
width=1,
|
||||
height=7,
|
||||
when="has_results",
|
||||
),
|
||||
SwitchTo(
|
||||
Const("🔍 Новый поиск"),
|
||||
id="new_search",
|
||||
state=AdminMenuSG.residents_search_input,
|
||||
),
|
||||
SwitchTo(
|
||||
Const("◀️ К списку резидентов"),
|
||||
id="back_to_residents_from_search",
|
||||
state=AdminMenuSG.residents,
|
||||
),
|
||||
state=AdminMenuSG.residents_search_results,
|
||||
getter=get_search_results_data,
|
||||
),
|
||||
)
|
||||
|
||||
@@ -16,6 +16,9 @@ from dutylog.infrastructure.database.repositories.rooms_repository import (
|
||||
from dutylog.infrastructure.database.repositories.residents_repository import (
|
||||
ResidentsRepository,
|
||||
)
|
||||
from dutylog.infrastructure.database.repositories.users_repository import (
|
||||
UsersRepository,
|
||||
)
|
||||
|
||||
|
||||
@inject
|
||||
@@ -114,10 +117,18 @@ async def on_resident_selected(
|
||||
dialog_manager: DialogManager,
|
||||
item_id: str,
|
||||
residents_repository: FromDishka[ResidentsRepository],
|
||||
users_repository: FromDishka[UsersRepository],
|
||||
):
|
||||
user_id = callback.from_user.id
|
||||
resident_id = int(item_id)
|
||||
|
||||
await users_repository.get_or_create_user(
|
||||
user_id=user_id,
|
||||
username=callback.from_user.username,
|
||||
first_name=callback.from_user.first_name,
|
||||
last_name=callback.from_user.last_name,
|
||||
)
|
||||
|
||||
await residents_repository.bind_user_to_resident(resident_id, user_id)
|
||||
|
||||
await callback.answer("✅ Регистрация успешна!")
|
||||
@@ -127,7 +138,7 @@ async def on_resident_selected(
|
||||
registration_dialog = Dialog(
|
||||
Window(
|
||||
Const(
|
||||
"<blockquote>🏢 <b>Выбор этажа</b></blockquote>\n\n<blockquote>⚠️ <b>Внимание!</b> Перерегистрацию может выполнить только администратор. Выбирайте внимательно!</blockquote>\n\nВыберите этаж, на котором вы живете:",
|
||||
"<blockquote>🏢 <b>Выбор этажа</b></blockquote>\n\n<blockquote>⚠️ <b>Внимание!</b>\nПеререгистрацию может выполнить только администратор.\nВыбирайте внимательно!</blockquote>\n\nВыберите этаж, на котором вы живете:",
|
||||
when="has_available",
|
||||
),
|
||||
Const(
|
||||
|
||||
@@ -10,6 +10,8 @@ class MainMenuSG(StatesGroup):
|
||||
class AdminMenuSG(StatesGroup):
|
||||
main = State()
|
||||
residents = State()
|
||||
residents_search_input = State()
|
||||
residents_search_results = State()
|
||||
resident_info = State()
|
||||
resident_logout_confirm = State()
|
||||
add_hours_select = State()
|
||||
|
||||
@@ -36,6 +36,30 @@ class ResidentsDAO:
|
||||
result = await self.session.execute(select(Resident))
|
||||
return list(result.scalars().all())
|
||||
|
||||
async def search_by_room_number(self, room_number: int) -> list[Resident]:
|
||||
from dutylog.infrastructure.database.models.room import Room
|
||||
result = await self.session.execute(
|
||||
select(Resident)
|
||||
.join(Room, Resident.room == Room.id)
|
||||
.where(Room.number == room_number)
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
async def search_by_name(self, query: str) -> list[Resident]:
|
||||
result = await self.session.execute(
|
||||
select(Resident).where(Resident.real_name.ilike(f"%{query}%"))
|
||||
)
|
||||
return list(result.scalars().all())
|
||||
|
||||
async def get_busy_residents_with_users(self) -> list[tuple[Resident, int]]:
|
||||
result = await self.session.execute(
|
||||
select(Resident)
|
||||
.where(Resident.is_busy == True)
|
||||
.where(Resident.user_entity.is_not(None))
|
||||
)
|
||||
residents = result.scalars().all()
|
||||
return [(r, r.user_entity) for r in residents if r.user_entity]
|
||||
|
||||
async def create(self, resident: Resident) -> Resident:
|
||||
self.session.add(resident)
|
||||
await self.session.commit()
|
||||
|
||||
@@ -74,5 +74,32 @@ class ResidentsRepository:
|
||||
async def get_all_residents(self) -> list[Resident]:
|
||||
return await self.residents_dao.get_all()
|
||||
|
||||
async def search_residents(
|
||||
self, query: str, users_repository
|
||||
) -> tuple[list[Resident], str]:
|
||||
if query.isdigit():
|
||||
room_number = int(query)
|
||||
residents = await self.residents_dao.search_by_room_number(room_number)
|
||||
if residents:
|
||||
return residents, f"комнате {room_number}"
|
||||
|
||||
residents = await self.residents_dao.search_by_name(query)
|
||||
if residents:
|
||||
return residents, f"имени '{query}'"
|
||||
|
||||
busy_residents_data = await self.residents_dao.get_busy_residents_with_users()
|
||||
matched_residents = []
|
||||
|
||||
for resident, user_id in busy_residents_data:
|
||||
user = await users_repository.get_user_by_id(user_id)
|
||||
if user and user.username:
|
||||
if query.lower() in user.username.lower():
|
||||
matched_residents.append(resident)
|
||||
|
||||
if matched_residents:
|
||||
return matched_residents, f"username '@{query}'"
|
||||
|
||||
return [], query
|
||||
|
||||
async def delete_resident(self, resident_id: int) -> None:
|
||||
await self.residents_dao.delete(resident_id)
|
||||
|
||||
Reference in New Issue
Block a user