Files
quickbot/bot/handlers/editors/date.py

363 lines
13 KiB
Python

from datetime import datetime, time, timedelta
from aiogram import Router, F
from aiogram.fsm.context import FSMContext
from aiogram.types import Message, CallbackQuery, InlineKeyboardButton
from aiogram.utils.keyboard import InlineKeyboardBuilder
from logging import getLogger
from typing import TYPE_CHECKING
from ....model.descriptors import EntityFieldDescriptor
from ....model.settings import Settings
from ..context import ContextData, CallbackCommand
from ....utils.main import get_send_message, get_field_descriptor
from .wrapper import wrap_editor
if TYPE_CHECKING:
from ....main import QBotApp
logger = getLogger(__name__)
router = Router()
@router.callback_query(ContextData.filter(F.command == CallbackCommand.TIME_PICKER))
async def time_picker_callback(
query: CallbackQuery, callback_data: ContextData, app: "QBotApp", **kwargs
):
if not callback_data.data:
return
field_descriptor = get_field_descriptor(app, callback_data)
state: FSMContext = kwargs["state"]
state_data = await state.get_data()
kwargs["state_data"] = state_data
await time_picker(
query.message,
field_descriptor=field_descriptor,
callback_data=callback_data,
current_value=datetime.strptime(callback_data.data, "%Y-%m-%d %H-%M")
if len(callback_data.data) > 10
else time.fromisoformat(callback_data.data.replace("-", ":")),
**kwargs,
)
async def time_picker(
message: Message | CallbackQuery,
field_descriptor: EntityFieldDescriptor,
callback_data: ContextData,
current_value: datetime | time,
state: FSMContext,
edit_prompt: str | None = None,
**kwargs,
):
keyboard_builder = InlineKeyboardBuilder()
for i in range(12):
keyboard_builder.row(
InlineKeyboardButton(
text=(
"▶︎ {v:02d} ◀︎" if i == (current_value.hour % 12) else "{v:02d}"
).format(v=i if current_value.hour < 12 else i + 12),
callback_data=ContextData(
command=CallbackCommand.TIME_PICKER,
context=callback_data.context,
entity_name=callback_data.entity_name,
entity_id=callback_data.entity_id,
field_name=callback_data.field_name,
form_params=callback_data.form_params,
data=current_value.replace(
hour=i if current_value.hour < 12 else i + 12
).strftime(
"%Y-%m-%d %H-%M"
if isinstance(current_value, datetime)
else "%H-%M"
)
if i != current_value.hour % 12
else None,
).pack(),
),
InlineKeyboardButton(
text=(
"▶︎ {v:02d} ◀︎" if i == current_value.minute // 5 else "{v:02d}"
).format(v=i * 5),
callback_data=ContextData(
command=CallbackCommand.TIME_PICKER,
context=callback_data.context,
entity_name=callback_data.entity_name,
entity_id=callback_data.entity_id,
field_name=callback_data.field_name,
form_params=callback_data.form_params,
data=current_value.replace(minute=i * 5).strftime(
"%Y-%m-%d %H-%M"
if isinstance(current_value, datetime)
else "%H-%M"
)
if i != current_value.minute // 5
else None,
).pack(),
),
)
keyboard_builder.row(
InlineKeyboardButton(
text="AM/PM",
callback_data=ContextData(
command=CallbackCommand.TIME_PICKER,
context=callback_data.context,
entity_name=callback_data.entity_name,
entity_id=callback_data.entity_id,
field_name=callback_data.field_name,
form_params=callback_data.form_params,
data=current_value.replace(
hour=current_value.hour + 12
if current_value.hour < 12
else current_value.hour - 12
).strftime(
"%Y-%m-%d %H-%M" if isinstance(current_value, datetime) else "%H-%M"
),
).pack(),
),
InlineKeyboardButton(
text=await Settings.get(Settings.APP_STRINGS_DONE_BTN),
callback_data=ContextData(
command=CallbackCommand.FIELD_EDITOR_CALLBACK,
context=callback_data.context,
entity_name=callback_data.entity_name,
entity_id=callback_data.entity_id,
field_name=callback_data.field_name,
form_params=callback_data.form_params,
data=current_value.strftime(
"%Y-%m-%d %H-%M" if isinstance(current_value, datetime) else "%H-%M"
),
).pack(),
),
)
state_data = kwargs["state_data"]
await wrap_editor(
keyboard_builder=keyboard_builder,
field_descriptor=field_descriptor,
callback_data=callback_data,
state_data=state_data,
)
await state.set_data(state_data)
if edit_prompt:
send_message = get_send_message(message)
await send_message(text=edit_prompt, reply_markup=keyboard_builder.as_markup())
else:
await message.edit_reply_markup(reply_markup=keyboard_builder.as_markup())
async def date_picker(
message: Message | CallbackQuery,
field_descriptor: EntityFieldDescriptor,
callback_data: ContextData,
current_value: datetime,
state: FSMContext,
edit_prompt: str | None = None,
**kwargs,
):
if not current_value:
start_date = datetime.now()
else:
start_date = current_value
start_date = current_value.replace(day=1)
previous_month = start_date - timedelta(days=1)
next_month = start_date.replace(day=28) + timedelta(days=4)
keyboard_builder = InlineKeyboardBuilder()
keyboard_builder.row(
InlineKeyboardButton(
text="◀️",
callback_data=ContextData(
command=CallbackCommand.DATE_PICKER_MONTH,
context=callback_data.context,
entity_name=callback_data.entity_name,
entity_id=callback_data.entity_id,
field_name=callback_data.field_name,
form_params=callback_data.form_params,
data=previous_month.strftime("%Y-%m-%d %H-%M"),
).pack(),
),
InlineKeyboardButton(
text=start_date.strftime("%b %Y"),
callback_data=ContextData(
command=CallbackCommand.DATE_PICKER_YEAR,
context=callback_data.context,
entity_name=callback_data.entity_name,
entity_id=callback_data.entity_id,
field_name=callback_data.field_name,
form_params=callback_data.form_params,
data=start_date.strftime("%Y-%m-%d %H-%M"),
).pack(),
),
InlineKeyboardButton(
text="▶️",
callback_data=ContextData(
command=CallbackCommand.DATE_PICKER_MONTH,
context=callback_data.context,
entity_name=callback_data.entity_name,
entity_id=callback_data.entity_id,
field_name=callback_data.field_name,
form_params=callback_data.form_params,
data=next_month.strftime("%Y-%m-%d %H-%M"),
).pack(),
),
)
first_day = start_date - timedelta(days=start_date.weekday())
weeks = (
(
(start_date.replace(day=28) + timedelta(days=4)).replace(day=1) - first_day
).days
- 1
) // 7 + 1
for week in range(weeks):
buttons = []
for day in range(7):
current_day = first_day + timedelta(days=week * 7 + day)
buttons.append(
InlineKeyboardButton(
text=current_day.strftime("%d"),
callback_data=ContextData(
command=CallbackCommand.FIELD_EDITOR_CALLBACK
if field_descriptor.dt_type == "date"
else CallbackCommand.TIME_PICKER,
context=callback_data.context,
entity_name=callback_data.entity_name,
entity_id=callback_data.entity_id,
field_name=callback_data.field_name,
form_params=callback_data.form_params,
data=current_day.strftime("%Y-%m-%d %H-%M"),
).pack(),
)
)
keyboard_builder.row(*buttons)
state_data = kwargs["state_data"]
await wrap_editor(
keyboard_builder=keyboard_builder,
field_descriptor=field_descriptor,
callback_data=callback_data,
state_data=state_data,
)
await state.set_data(state_data)
if edit_prompt:
send_message = get_send_message(message)
await send_message(text=edit_prompt, reply_markup=keyboard_builder.as_markup())
else:
await message.edit_reply_markup(reply_markup=keyboard_builder.as_markup())
@router.callback_query(
ContextData.filter(F.command == CallbackCommand.DATE_PICKER_YEAR)
)
async def date_picker_year(
query: CallbackQuery,
callback_data: ContextData,
app: "QBotApp",
state: FSMContext,
**kwargs,
):
start_date = datetime.strptime(callback_data.data, "%Y-%m-%d %H-%M")
state_data = await state.get_data()
kwargs["state_data"] = state_data
keyboard_builder = InlineKeyboardBuilder()
keyboard_builder.row(
InlineKeyboardButton(
text="🔼",
callback_data=ContextData(
command=CallbackCommand.DATE_PICKER_YEAR,
context=callback_data.context,
entity_name=callback_data.entity_name,
entity_id=callback_data.entity_id,
field_name=callback_data.field_name,
form_params=callback_data.form_params,
data=start_date.replace(year=start_date.year - 20).strftime(
"%Y-%m-%d %H-%M"
),
).pack(),
)
)
for r in range(4):
buttons = []
for c in range(5):
current_date = start_date.replace(year=start_date.year + r * 5 + c - 10)
buttons.append(
InlineKeyboardButton(
text=current_date.strftime("%Y"),
callback_data=ContextData(
command=CallbackCommand.DATE_PICKER_MONTH,
context=callback_data.context,
entity_name=callback_data.entity_name,
entity_id=callback_data.entity_id,
field_name=callback_data.field_name,
form_params=callback_data.form_params,
data=current_date.strftime("%Y-%m-%d %H-%M"),
).pack(),
)
)
keyboard_builder.row(*buttons)
keyboard_builder.row(
InlineKeyboardButton(
text="🔽",
callback_data=ContextData(
command=CallbackCommand.DATE_PICKER_YEAR,
context=callback_data.context,
entity_name=callback_data.entity_name,
entity_id=callback_data.entity_id,
field_name=callback_data.field_name,
form_params=callback_data.form_params,
data=start_date.replace(year=start_date.year + 20).strftime(
"%Y-%m-%d %H-%M"
),
).pack(),
)
)
field_descriptor = get_field_descriptor(app, callback_data)
await wrap_editor(
keyboard_builder=keyboard_builder,
field_descriptor=field_descriptor,
callback_data=callback_data,
state_data=state_data,
)
await query.message.edit_reply_markup(reply_markup=keyboard_builder.as_markup())
@router.callback_query(
ContextData.filter(F.command == CallbackCommand.DATE_PICKER_MONTH)
)
async def date_picker_month(
query: CallbackQuery, callback_data: ContextData, app: "QBotApp", **kwargs
):
field_descriptor = get_field_descriptor(app, callback_data)
state: FSMContext = kwargs["state"]
state_data = await state.get_data()
kwargs["state_data"] = state_data
await date_picker(
query.message,
field_descriptor=field_descriptor,
callback_data=callback_data,
current_value=datetime.strptime(callback_data.data, "%Y-%m-%d %H-%M"),
**kwargs,
)