feat: Added ability to change travel

This commit is contained in:
ITQ
2024-03-23 23:53:54 +03:00
parent 40ea9689ab
commit 02508ea9fd
9 changed files with 206 additions and 24 deletions
-16
View File
@@ -1,7 +1,6 @@
__all__ = ("router",) __all__ = ("router",)
from aiogram import F, Router from aiogram import F, Router
from aiogram.exceptions import TelegramBadRequest
from aiogram.filters import StateFilter from aiogram.filters import StateFilter
from aiogram.fsm.context import FSMContext from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, Message from aiogram.types import CallbackQuery, Message
@@ -35,11 +34,6 @@ async def profile_callback(callback: CallbackQuery) -> None:
) )
await callback.answer() await callback.answer()
try:
await callback.message.delete()
except TelegramBadRequest:
pass
@router.callback_query( @router.callback_query(
F.data == "menu_create_travel", F.data == "menu_create_travel",
@@ -63,11 +57,6 @@ async def create_travel_callback(
await callback.answer() await callback.answer()
try:
await callback.message.delete()
except TelegramBadRequest:
pass
@router.callback_query( @router.callback_query(
F.data == "menu_travels", F.data == "menu_travels",
@@ -101,9 +90,4 @@ async def travels_callback(
), ),
) )
try:
await callback.message.delete()
except TelegramBadRequest:
pass
await callback.answer() await callback.answer()
+2 -2
View File
@@ -102,7 +102,7 @@ async def profile_change_entered(message: Message, state: FSMContext) -> None:
if column == "username": if column == "username":
try: try:
validated_value = User().validate_username( validated_title = User().validate_username(
key="username", key="username",
value=value, value=value,
) )
@@ -111,7 +111,7 @@ async def profile_change_entered(message: Message, state: FSMContext) -> None:
return return
await state.update_data(value=validated_value, successfully=True) await state.update_data(value=validated_title, successfully=True)
elif column == "age": elif column == "age":
try: try:
validated_age = User().validate_age(key="age", value=value) validated_age = User().validate_age(key="age", value=value)
+171 -2
View File
@@ -3,20 +3,54 @@ __all__ = ("router",)
from aiogram import F, Router from aiogram import F, Router
from aiogram.exceptions import TelegramBadRequest from aiogram.exceptions import TelegramBadRequest
from aiogram.filters import StateFilter from aiogram.filters import StateFilter
from aiogram.fsm.context import FSMContext
from aiogram.types import CallbackQuery, Message from aiogram.types import CallbackQuery, Message
from app import messages from app import messages, session
from app.config import Config from app.config import Config
from app.filters.user import RegisteredCallback from app.filters.user import Registered, RegisteredCallback
from app.keyboards.builders import travels_keyboard from app.keyboards.builders import travels_keyboard
from app.keyboards.travel import get from app.keyboards.travel import get
from app.models.travel import Travel from app.models.travel import Travel
from app.models.user import User from app.models.user import User
from app.states.travel import TravelAlteringState
from app.utils.states import delete_message_from_state, handle_validation_error
router = Router(name="menu_callback") router = Router(name="menu_callback")
@router.callback_query(
F.data == "travels",
RegisteredCallback(),
StateFilter(None),
)
async def travels_index_callback(callback: CallbackQuery) -> None:
page = 0
if callback.from_user is None or not isinstance(callback.message, Message):
return
user = User().get_user_by_telegram_id(callback.from_user.id)
travels = user.get_user_travels()
if not travels or travels == []:
await callback.message.edit_text(messages.NO_TRAVELS)
else:
pages = (len(travels) + Config.PAGE_SIZE - 1) // Config.PAGE_SIZE
await callback.message.edit_text(
messages.TRAVELS,
reply_markup=travels_keyboard(
travels,
page,
pages,
user.telegram_id,
),
)
@router.callback_query( @router.callback_query(
F.data.startswith("travels_page"), F.data.startswith("travels_page"),
RegisteredCallback(), RegisteredCallback(),
@@ -71,3 +105,138 @@ async def travel_detail_callback(callback: CallbackQuery) -> None:
travel.get_travel_text(), travel.get_travel_text(),
reply_markup=get(travel_id), reply_markup=get(travel_id),
) )
@router.callback_query(
F.data.startswith("travel_change"),
RegisteredCallback(),
StateFilter(None),
)
async def travel_change_callback(
callback: CallbackQuery,
state: FSMContext,
) -> None:
if (
callback.data is None
or callback.message is None
or not isinstance(callback.message, Message)
):
return
travel_id, column = callback.data.replace("travel_change_", "").split("_")
if column == "title":
message = await callback.message.answer(
f"{messages.INPUT_TRAVEL_TITLE}\n{messages.CANCEL_CHANGE}",
)
elif column == "description":
message = await callback.message.answer(
f"{messages.EDIT_TRAVEL_DESCRIPTION}\n{messages.CANCEL_CHANGE}",
)
await state.update_data(
column=column,
travel_message_id=callback.message.message_id,
input_message_id=message.message_id,
travel_id=travel_id,
)
await state.set_state(TravelAlteringState.value)
await callback.answer()
@router.message(TravelAlteringState.value, F.text, Registered())
async def travel_change_entered(message: Message, state: FSMContext) -> None:
if (
message.text is None
or message.from_user is None
or message.bot is None
):
return
data = await state.get_data()
column = data["column"]
travel_id = data["travel_id"]
value = message.text.strip()
if value == "/cancel":
await message.answer(
messages.CHANGE_CANCELED,
)
await state.update_data(successfully=True)
await message.delete()
await delete_message_from_state(
state,
message.chat.id,
message.bot,
)
await state.clear()
return
if column == "title":
try:
validated_title = Travel().validate_title(
key="title",
value=value,
)
except AssertionError as e:
await handle_validation_error(message, state, e)
return
await state.update_data(value=validated_title, successfully=True)
elif column == "description":
if value == "/skip":
await state.update_data(value=None, successfully=True)
await delete_message_from_state(
state,
message.chat.id,
message.bot,
)
else:
try:
validated_description = Travel().validate_description(
key="description", value=value,
)
except AssertionError as e:
await handle_validation_error(message, state, e)
return
await state.update_data(
value=validated_description, successfully=True,
)
await message.delete()
await delete_message_from_state(state, message.chat.id, message.bot)
state_data = await state.get_data()
travel = Travel().get_travel_queryset_by_id(travel_id)
data = {state_data["column"]: state_data["value"]}
travel.update(data)
session.commit()
travel = travel.first()
session.refresh(travel)
try:
await message.bot.edit_message_text(
travel.get_travel_text(),
message.chat.id,
state_data["travel_message_id"],
reply_markup=get(travel_id),
)
except TelegramBadRequest:
pass
await message.answer(
messages.TRAVEL_UPDATED,
)
await state.clear()
+1 -1
View File
@@ -15,7 +15,7 @@ router = Router(name="travels_command")
@router.message(Command("travels"), Registered(), StateFilter(None)) @router.message(Command("travels"), Registered(), StateFilter(None))
async def command_help_handler(message: Message) -> None: async def command_travels_handler(message: Message) -> None:
page = 0 page = 0
if message.from_user is None: if message.from_user is None:
+11 -1
View File
@@ -17,6 +17,16 @@ def get(travel_id: int):
callback_data=f"travel_change_{travel_id}_description", callback_data=f"travel_change_{travel_id}_description",
), ),
) )
builder.row(
types.InlineKeyboardButton(
text="🗺️ Locations",
callback_data=f"travel_locations_{travel_id}",
),
types.InlineKeyboardButton(
text="👤 Users",
callback_data=f"travel_users_{travel_id}",
),
)
builder.row( builder.row(
types.InlineKeyboardButton( types.InlineKeyboardButton(
text=" Add location", text=" Add location",
@@ -30,7 +40,7 @@ def get(travel_id: int):
builder.row( builder.row(
types.InlineKeyboardButton( types.InlineKeyboardButton(
text="⬅️", text="⬅️",
callback_data="menu_travels", callback_data="travels",
), ),
) )
+2
View File
@@ -7,6 +7,8 @@ NO_TRAVELS = "No travels yet. You can create one with /create_travel command."
CREATE_TRAVEL = ( CREATE_TRAVEL = (
"🧳 Let's create new travel!\n<i>Enter /cancel to cancel creating.</i>" "🧳 Let's create new travel!\n<i>Enter /cancel to cancel creating.</i>"
) )
TRAVEL_UPDATED = "✅ Travel updated"
EDIT_TRAVEL_DESCRIPTION = "Enter travel description (enter /skip if you want to set it to None):\n<i>Maximum length: 100 characters</i>"
INPUT_TRAVEL_TITLE = ( INPUT_TRAVEL_TITLE = (
"Enter travel title:\n<i>Maximum length: 30 characters</i>" "Enter travel title:\n<i>Maximum length: 30 characters</i>"
) )
+7 -1
View File
@@ -74,13 +74,19 @@ class Travel(Base):
def get_travel_text(self): def get_travel_text(self):
return messages.TRAVEL_DETAIL.format( return messages.TRAVEL_DETAIL.format(
title=self.title, title=self.title,
description=self.description, description=(
self.description if self.description else messages.NOT_SET
),
) )
@classmethod @classmethod
def get_travel_by_id(cls, travel_id): def get_travel_by_id(cls, travel_id):
return session.query(Travel).filter(Travel.id == travel_id).first() return session.query(Travel).filter(Travel.id == travel_id).first()
@classmethod
def get_travel_queryset_by_id(cls, travel_id):
return session.query(Travel).filter(Travel.id == travel_id)
class Location(Base): class Location(Base):
__tablename__ = "locations" __tablename__ = "locations"
+2 -1
View File
@@ -98,7 +98,8 @@ class User(Base):
return normalized_value return normalized_value
def get_user_travels(self) -> list: def get_user_travels(self) -> list:
return self.owned_travels + self.travels all_travels = self.owned_travels + self.travels
return sorted(all_travels, key=lambda travel: travel.id)
def get_human_readable_datejoined(self) -> str: def get_human_readable_datejoined(self) -> str:
return self.date_joined.strftime("%Y-%m-%d %H:%M:%S") return self.date_joined.strftime("%Y-%m-%d %H:%M:%S")
+10
View File
@@ -7,3 +7,13 @@ class TravelCreationState(StatesGroup):
error_message_id = State() error_message_id = State()
title = State() title = State()
description = State() description = State()
class TravelAlteringState(StatesGroup):
travel_message_id = State()
input_message_id = State()
error_message_id = State()
successfully = State()
travel_id = State()
column = State()
value = State()