plugins devel

This commit is contained in:
2024-12-23 21:23:45 +09:00
parent d850f50e25
commit 77bf0b8381
5 changed files with 225 additions and 203 deletions

View File

@@ -16,6 +16,7 @@ from touchh.utils.log import CustomLogger
logger = CustomLogger(__name__).get_logger()
async def statistics(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Вывод списка отелей для статистики."""
@@ -37,7 +38,7 @@ async def statistics(update: Update, context: ContextTypes.DEFAULT_TYPE):
# Формируем кнопки для выбора отеля
keyboard = [
[InlineKeyboardButton(hotel.hotel.name, callback_data=f"stats_hotel_{hotel.hotel.id}")]
[InlineKeyboardButton(f'🏨 {hotel.hotel.name}', callback_data=f"stats_hotel_{hotel.hotel.id}")]
for hotel in user_hotels
]
keyboard.append([InlineKeyboardButton("🏠 Главная", callback_data="main_menu")])
@@ -91,53 +92,8 @@ def ensure_datetime(value):
logging.warning(f"Получено значение неизвестного типа для преобразования в datetime: {value}")
return None
# async def generate_statistics(update: Update, context: ContextTypes.DEFAULT_TYPE):
# """Генерация и отправка статистики."""
# query = update.callback_query
# await query.answer()
# try:
# hotel_id = context.user_data.get("selected_hotel")
# if not hotel_id:
# raise ValueError(f"ID отеля не найден в user_data: {context.user_data}")
# period = query.data.split("_")[2]
# now = ensure_datetime(datetime.utcnow())
# # Получаем диапазон дат
# start_date, end_date = get_period_dates(period, now)
# reservations = await sync_to_async(list)(
# Reservation.objects.filter(
# hotel_id=hotel_id,
# check_in__gte=start_date,
# check_in__lte=end_date
# ).select_related('hotel')
# )
# if not reservations:
# await query.edit_message_text("Нет данных для статистики за выбранный период.")
# return
# hotel = await sync_to_async(Hotel.objects.get)(id=hotel_id)
# file_path = await generate_pdf_report(hotel.name, reservations, start_date, end_date)
# with open(file_path, "rb") as file:
# await query.message.reply_document(document=file, filename=f"{hotel.name}_report.pdf")
# if os.path.exists(file_path):
# os.remove(file_path)
# except Exception as e:
# logging.error(f"Ошибка в generate_statistics: {str(e)}", exc_info=True)
# logging.error(f'start_date_type: {type(start_date)}, \n end_date_type: {type(end_date)}\n')
# await query.edit_message_text(f"Произошла ошибка: {str(e)}")
async def generate_statistics(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Генерация и отправка статистики."""
logger = CustomLogger(__name__).get_logger()
query = update.callback_query
await query.answer()
@@ -166,27 +122,17 @@ async def generate_statistics(update: Update, context: ContextTypes.DEFAULT_TYPE
hotel = await sync_to_async(Hotel.objects.get)(id=hotel_id)
print(f"[DEBUG] start_date: {start_date}, type: {type(start_date)}")
print(f"[DEBUG] end_date: {end_date}, type: {type(end_date)}")
file_path = await generate_pdf_report(hotel.name, reservations, start_date, end_date)
# Генерация PDF-отчета
file_path = await generate_pdf_report(
hotel.name,
reservations,
start_date=start_date.strftime('%Y-%m-%d %H:%M:%S'),
end_date=end_date.strftime('%Y-%m-%d %H:%M:%S')
)
# Отправка PDF-файла
with open(file_path, "rb") as file:
await query.message.reply_document(document=file, filename=f"{hotel.name}_report.pdf")
# Удаление временного файла
if os.path.exists(file_path):
os.remove(file_path)
except Exception as e:
logger.error(f"Ошибка в generate_statistics: {str(e)}", exc_info=True)
logging.error(f"Ошибка в generate_statistics: {str(e)}", exc_info=True)
logging.error(f'start_date_type: {type(start_date)}, \n end_date_type: {type(end_date)}\n')
await query.edit_message_text(f"Произошла ошибка: {str(e)}")
@@ -235,3 +181,6 @@ async def stats_back(update: Update, context):
keyboard.append([InlineKeyboardButton("🏠 Главная", callback_data="main_menu")])
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("Выберите отель:", reply_markup=reply_markup)

53
bot/utils/date_utils.py Normal file
View File

@@ -0,0 +1,53 @@
# bot/utils/date_utils.py
from datetime import datetime, timedelta
import pytz
def ensure_datetime(value):
"""
Приводит значение к объекту datetime с учетом временной зоны.
:param value: Значение даты (строка или datetime).
:return: Объект datetime.
:raises: TypeError, если передан некорректный тип.
"""
if isinstance(value, str):
try:
# Если строка соответствует формату ISO 8601
return datetime.fromisoformat(value)
except ValueError:
raise ValueError(f"Некорректный формат даты: {value}")
elif isinstance(value, datetime):
return value
else:
raise TypeError(f"Ожидался тип str или datetime, получено: {type(value)}")
def get_period_dates(period, now=None):
"""
Возвращает диапазон дат (start_date, end_date) для заданного периода.
:param period: Период (строка: 'today', 'yesterday', 'last_week', 'last_month').
:param now: Текущая дата/время (опционально).
:return: Кортеж (start_date, end_date).
:raises: ValueError, если период не поддерживается.
"""
if now is None:
now = datetime.now(pytz.UTC)
if period == "today":
start_date = now.replace(hour=0, minute=0, second=0, microsecond=0)
end_date = now.replace(hour=23, minute=59, second=59, microsecond=999999)
elif period == "yesterday":
start_date = (now - timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0)
end_date = (now - timedelta(days=1)).replace(hour=23, minute=59, second=59, microsecond=999999)
elif period == "last_week":
start_date = (now - timedelta(days=now.weekday() + 7)).replace(hour=0, minute=0, second=0, microsecond=0)
end_date = (start_date + timedelta(days=6)).replace(hour=23, minute=59, second=59, microsecond=999999)
elif period == "last_month":
first_day_of_current_month = now.replace(day=1, hour=0, minute=0, second=0, microsecond=0)
last_day_of_previous_month = first_day_of_current_month - timedelta(days=1)
start_date = last_day_of_previous_month.replace(day=1)
end_date = last_day_of_previous_month.replace(hour=23, minute=59, second=59, microsecond=999999)
else:
raise ValueError(f"Неподдерживаемый период: {period}")
return start_date, end_date