mirror of
https://github.com/koloideal/DutyLog.git
synced 2026-06-10 10:25:29 +03:00
update
This commit is contained in:
@@ -2,11 +2,13 @@ from aiogram_dialog import Dialog
|
|||||||
|
|
||||||
from dutylog.application.bot.user_dialogs.user_menu.main_menu import main_menu_window
|
from dutylog.application.bot.user_dialogs.user_menu.main_menu import main_menu_window
|
||||||
from dutylog.application.bot.user_dialogs.user_menu.history import history_window
|
from dutylog.application.bot.user_dialogs.user_menu.history import history_window
|
||||||
|
from dutylog.application.bot.user_dialogs.user_menu.top_residents import top_residents_window
|
||||||
from dutylog.application.bot.user_dialogs.user_menu.faq import faq_window
|
from dutylog.application.bot.user_dialogs.user_menu.faq import faq_window
|
||||||
|
|
||||||
|
|
||||||
main_menu_dialog = Dialog(
|
main_menu_dialog = Dialog(
|
||||||
main_menu_window,
|
main_menu_window,
|
||||||
history_window,
|
history_window,
|
||||||
|
top_residents_window,
|
||||||
faq_window,
|
faq_window,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ from aiogram.fsm.state import State, StatesGroup
|
|||||||
class MainMenuSG(StatesGroup):
|
class MainMenuSG(StatesGroup):
|
||||||
main = State()
|
main = State()
|
||||||
history = State()
|
history = State()
|
||||||
|
top_residents = State()
|
||||||
faq = State()
|
faq = State()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
from aiogram.types import User
|
from aiogram.types import User
|
||||||
from aiogram_dialog import Window
|
from aiogram_dialog import Window
|
||||||
from aiogram_dialog.widgets.text import Format
|
from aiogram_dialog.widgets.text import Format, Const
|
||||||
from aiogram_dialog.widgets.kbd import Back
|
from aiogram_dialog.widgets.kbd import Back
|
||||||
from aiogram_dialog.widgets.text import Const
|
|
||||||
from dishka import FromDishka
|
from dishka import FromDishka
|
||||||
from dishka.integrations.aiogram_dialog import inject
|
from dishka.integrations.aiogram_dialog import inject
|
||||||
|
|
||||||
@@ -13,6 +12,7 @@ from dutylog.infrastructure.database.repositories.residents_repository import (
|
|||||||
from dutylog.infrastructure.database.repositories.hours_transactions_repository import (
|
from dutylog.infrastructure.database.repositories.hours_transactions_repository import (
|
||||||
HoursTransactionsRepository,
|
HoursTransactionsRepository,
|
||||||
)
|
)
|
||||||
|
from dutylog.infrastructure.utils.datetime import msk_now
|
||||||
|
|
||||||
|
|
||||||
@inject
|
@inject
|
||||||
@@ -21,19 +21,19 @@ async def get_history_data(
|
|||||||
residents_repository: FromDishka[ResidentsRepository],
|
residents_repository: FromDishka[ResidentsRepository],
|
||||||
transactions_repository: FromDishka[HoursTransactionsRepository],
|
transactions_repository: FromDishka[HoursTransactionsRepository],
|
||||||
**kwargs,
|
**kwargs,
|
||||||
):
|
) -> dict[str, str]:
|
||||||
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>
|
||||||
|
|
||||||
<i>Профиль не найден</i>
|
⚠️ <i>Профиль не найден</i>
|
||||||
"""
|
"""
|
||||||
else:
|
else:
|
||||||
transactions = await transactions_repository.get_resident_history(resident.id)
|
transactions = await transactions_repository.get_resident_history(resident.id)
|
||||||
transactions_sorted = sorted(transactions, key=lambda x: x.created_at)
|
transactions_sorted = sorted(transactions, key=lambda x: x.created_at)
|
||||||
last_10 = transactions_sorted[:10]
|
last_10 = transactions_sorted[-10:]
|
||||||
|
|
||||||
if not last_10:
|
if not last_10:
|
||||||
history_text = """
|
history_text = """
|
||||||
@@ -42,20 +42,20 @@ async def get_history_data(
|
|||||||
<i>История операций пуста</i>
|
<i>История операций пуста</i>
|
||||||
"""
|
"""
|
||||||
else:
|
else:
|
||||||
history_lines = []
|
history_text = """
|
||||||
for tx in last_10:
|
|
||||||
emoji = "+" if tx.transaction_type == "increase" else "-"
|
|
||||||
date_str = tx.created_at.strftime("%d.%m.%Y %H:%M")
|
|
||||||
history_lines.append(
|
|
||||||
f"{emoji} <code>{tx.amount}</code> ч • <i>{date_str}</i>"
|
|
||||||
)
|
|
||||||
|
|
||||||
history_text = f"""
|
|
||||||
<blockquote>📜 <b>История операций</b></blockquote>
|
<blockquote>📜 <b>История операций</b></blockquote>
|
||||||
|
|
||||||
{"".join(f"{line}\n" for line in history_lines)}
|
|
||||||
<i>Показаны последние 10 операций</i>
|
|
||||||
"""
|
"""
|
||||||
|
for tx in last_10:
|
||||||
|
operation = "Начислено" if tx.transaction_type == "increase" else "Списано"
|
||||||
|
emoji = "+" if tx.transaction_type == "increase" else "−"
|
||||||
|
|
||||||
|
msk_time = tx.created_at.astimezone(msk_now().tzinfo).replace(tzinfo=None)
|
||||||
|
date_str = msk_time.strftime("%d.%m.%Y %H:%M")
|
||||||
|
|
||||||
|
remark_text = f"\n💬 <i>{tx.remark}</i>" if tx.remark else ""
|
||||||
|
|
||||||
|
history_text += f"<blockquote><b>{operation}</b> {emoji}<code>{tx.amount}</code> ч\n📅 {date_str}{remark_text}</blockquote>\n"
|
||||||
|
|
||||||
return {"history_content": history_text}
|
return {"history_content": history_text}
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,12 @@ main_menu_window = Window(
|
|||||||
state=MainMenuSG.history,
|
state=MainMenuSG.history,
|
||||||
when="has_resident",
|
when="has_resident",
|
||||||
),
|
),
|
||||||
|
SwitchTo(
|
||||||
|
Const("🏆 Топ общежития"),
|
||||||
|
id="top_btn",
|
||||||
|
state=MainMenuSG.top_residents,
|
||||||
|
when="is_regular_user",
|
||||||
|
),
|
||||||
SwitchTo(
|
SwitchTo(
|
||||||
Const("❓ FAQ"),
|
Const("❓ FAQ"),
|
||||||
id="faq_btn",
|
id="faq_btn",
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
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 MainMenuSG
|
||||||
|
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 = """
|
||||||
|
<blockquote>🏆 <b>Топ общежития</b></blockquote>
|
||||||
|
|
||||||
|
⚠️ Нет данных для отображения топа.
|
||||||
|
"""
|
||||||
|
else:
|
||||||
|
content = """
|
||||||
|
<blockquote>🏆 <b>Топ общежития</b></blockquote>
|
||||||
|
|
||||||
|
"""
|
||||||
|
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"<blockquote>{medals[idx]} <b>{name}</b> — <code>{total_hours}</code> ч</blockquote>\n"
|
||||||
|
|
||||||
|
return {"content": content}
|
||||||
|
|
||||||
|
|
||||||
|
top_residents_window = Window(
|
||||||
|
Format("{content}"),
|
||||||
|
SwitchTo(
|
||||||
|
Const("◀️ Назад"),
|
||||||
|
id="back_to_main",
|
||||||
|
state=MainMenuSG.main,
|
||||||
|
),
|
||||||
|
state=MainMenuSG.top_residents,
|
||||||
|
getter=get_top_residents_data,
|
||||||
|
)
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
from aiogram import Router
|
from aiogram import Router
|
||||||
from aiogram.filters import CommandStart
|
from aiogram.filters import CommandStart
|
||||||
from aiogram.types import Message
|
from aiogram.types import Message, CallbackQuery, ErrorEvent
|
||||||
from aiogram_dialog import DialogManager, StartMode
|
from aiogram_dialog import DialogManager, StartMode
|
||||||
|
from aiogram_dialog.api.exceptions import UnknownIntent
|
||||||
from dishka import FromDishka
|
from dishka import FromDishka
|
||||||
|
|
||||||
from dutylog.application.bot.user_dialogs.states import (
|
from dutylog.application.bot.user_dialogs.states import (
|
||||||
@@ -27,7 +28,7 @@ async def start_handler(
|
|||||||
users_repository: FromDishka[UsersRepository],
|
users_repository: FromDishka[UsersRepository],
|
||||||
residents_repository: FromDishka[ResidentsRepository],
|
residents_repository: FromDishka[ResidentsRepository],
|
||||||
config: FromDishka[Config],
|
config: FromDishka[Config],
|
||||||
):
|
) -> None:
|
||||||
assert message.from_user is not None
|
assert message.from_user is not None
|
||||||
|
|
||||||
user = await users_repository.get_or_create_user(
|
user = await users_repository.get_or_create_user(
|
||||||
@@ -53,3 +54,39 @@ async def start_handler(
|
|||||||
return
|
return
|
||||||
|
|
||||||
await dialog_manager.start(RegistrationSG.select_floor, mode=StartMode.RESET_STACK)
|
await dialog_manager.start(RegistrationSG.select_floor, mode=StartMode.RESET_STACK)
|
||||||
|
|
||||||
|
|
||||||
|
@router.error()
|
||||||
|
async def unknown_intent_handler(
|
||||||
|
event: ErrorEvent,
|
||||||
|
dialog_manager: DialogManager,
|
||||||
|
users_repository: FromDishka[UsersRepository],
|
||||||
|
residents_repository: FromDishka[ResidentsRepository],
|
||||||
|
config: FromDishka[Config],
|
||||||
|
) -> None:
|
||||||
|
if not isinstance(event.exception, UnknownIntent):
|
||||||
|
raise event.exception
|
||||||
|
|
||||||
|
user_id = None
|
||||||
|
if event.update.message and event.update.message.from_user:
|
||||||
|
user_id = event.update.message.from_user.id
|
||||||
|
elif event.update.callback_query and event.update.callback_query.from_user:
|
||||||
|
user_id = event.update.callback_query.from_user.id
|
||||||
|
|
||||||
|
if not user_id:
|
||||||
|
return
|
||||||
|
|
||||||
|
user = await users_repository.get_user_by_id(user_id)
|
||||||
|
|
||||||
|
is_creator = user_id == config.bot.creator_id
|
||||||
|
is_admin = user.is_admin if user else False
|
||||||
|
|
||||||
|
if is_admin or is_creator:
|
||||||
|
await dialog_manager.start(AdminMenuSG.main, mode=StartMode.RESET_STACK)
|
||||||
|
return
|
||||||
|
|
||||||
|
resident = await residents_repository.get_resident_by_user_id(user_id)
|
||||||
|
if resident:
|
||||||
|
await dialog_manager.start(MainMenuSG.main, mode=StartMode.RESET_STACK)
|
||||||
|
else:
|
||||||
|
await dialog_manager.start(RegistrationSG.select_floor, mode=StartMode.RESET_STACK)
|
||||||
|
|||||||
Reference in New Issue
Block a user