reports converted to FPDF2 framework. Fully functional
This commit is contained in:
@@ -7,7 +7,11 @@ from users.models import User
|
||||
|
||||
from bot.utils.pdf_report import generate_pdf_report
|
||||
from bot.utils.database import get_hotels_for_user, get_hotel_by_name
|
||||
from django.utils.timezone import make_aware
|
||||
from datetime import datetime
|
||||
from django.utils.timezone import make_aware, is_aware, is_naive
|
||||
import os
|
||||
import traceback
|
||||
|
||||
|
||||
async def statistics(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
"""Вывод списка отелей для статистики."""
|
||||
@@ -54,6 +58,29 @@ async def stats_select_period(update: Update, context: ContextTypes.DEFAULT_TYPE
|
||||
]
|
||||
reply_markup = InlineKeyboardMarkup(keyboard)
|
||||
await query.edit_message_text("Выберите период времени:", reply_markup=reply_markup)
|
||||
|
||||
|
||||
def ensure_datetime(value):
|
||||
# print(f"statistics.py [DEBUG] ensure_datetime: Received value: {value} ({type(value)})")
|
||||
"""
|
||||
Ensure that the given value is a timezone-aware datetime object.
|
||||
|
||||
If the given value is a string, it is assumed to be in the format
|
||||
'%Y-%m-%d %H:%M:%S'. If the given value is a naive datetime object,
|
||||
it is converted to a timezone-aware datetime object using
|
||||
django.utils.timezone.make_aware.
|
||||
|
||||
:param value: The value to be converted
|
||||
:type value: str or datetime
|
||||
:return: A timezone-aware datetime object
|
||||
:rtype: datetime
|
||||
"""
|
||||
if isinstance(value, str):
|
||||
value = datetime.strptime(value, '%Y-%m-%d %H:%M:%S')
|
||||
if isinstance(value, datetime) and is_naive(value):
|
||||
value = make_aware(value)
|
||||
# print(f"statistics.py [DEBUG] ensure_datetime: Returning value: {value} ({type(value)})")
|
||||
return value
|
||||
|
||||
|
||||
async def generate_statistics(update: Update, context: ContextTypes.DEFAULT_TYPE):
|
||||
@@ -61,17 +88,79 @@ async def generate_statistics(update: Update, context: ContextTypes.DEFAULT_TYPE
|
||||
query = update.callback_query
|
||||
await query.answer()
|
||||
|
||||
hotel_id = context.user_data.get("selected_hotel")
|
||||
if not hotel_id:
|
||||
await query.edit_message_text("Ошибка: ID отеля не найден.")
|
||||
return
|
||||
try:
|
||||
hotel_id = context.user_data.get("selected_hotel")
|
||||
|
||||
period = query.data.split("_")[2]
|
||||
print(f'Period raw: {query.data}')
|
||||
print(f'Selected period: {period}')
|
||||
if not hotel_id:
|
||||
raise ValueError(f"ID отеля не найден в user_data: {context.user_data}")
|
||||
|
||||
now = datetime.utcnow().replace(tzinfo=timezone.utc) # Используем timezone.utc
|
||||
period = query.data.split("_")[2]
|
||||
|
||||
|
||||
now = datetime.utcnow().replace(tzinfo=timezone.utc)
|
||||
|
||||
print(type(now))
|
||||
|
||||
print(type(period))
|
||||
|
||||
start_date, end_date = get_period_dates(period, now)
|
||||
|
||||
|
||||
try:
|
||||
# Получаем бронирования
|
||||
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')
|
||||
)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"statistics.py Ошибка при выборке бронирований: {e}") from e
|
||||
|
||||
if not reservations:
|
||||
await query.edit_message_text("statistics.py Нет данных для статистики за выбранный период.")
|
||||
return
|
||||
|
||||
try:
|
||||
# Получаем данные об отеле
|
||||
hotel = await sync_to_async(Hotel.objects.get)(id=hotel_id)
|
||||
|
||||
except Hotel.DoesNotExist:
|
||||
raise RuntimeError(f"statistics.py Отель с ID {hotel_id} не найден")
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"statistics.py Ошибка при выборке отеля: {e}") from e
|
||||
|
||||
try:
|
||||
# Генерация отчета
|
||||
file_path = await generate_pdf_report(hotel.name, reservations, start_date, end_date)
|
||||
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"statistics.py [ERROR] Ошибка при генерации PDF-отчета: {e}") from e
|
||||
|
||||
try:
|
||||
# Отправка файла через Telegram
|
||||
with open(file_path, "rb") as file:
|
||||
await query.message.reply_document(document=file, filename=f"{hotel.name}_report.pdf")
|
||||
|
||||
except Exception as e:
|
||||
raise RuntimeError(f"Ошибка при отправке PDF-файла: {e}") from e
|
||||
|
||||
# Удаляем временный файл
|
||||
if os.path.exists(file_path):
|
||||
os.remove(file_path)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
# Логируем стек вызовов для детального анализа
|
||||
error_trace = traceback.format_exc()
|
||||
|
||||
await query.edit_message_text(f"Произошла ошибка: {str(e)}")
|
||||
|
||||
|
||||
def get_period_dates(period, now):
|
||||
if period == "day":
|
||||
start_date = (now - timedelta(days=1)).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
end_date = now.replace(hour=23, minute=59, second=59, microsecond=999999)
|
||||
@@ -81,55 +170,13 @@ async def generate_statistics(update: Update, context: ContextTypes.DEFAULT_TYPE
|
||||
elif period == "month":
|
||||
start_date = (now - timedelta(days=30)).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
end_date = now.replace(hour=23, minute=59, second=59, microsecond=999999)
|
||||
else: # "all"
|
||||
start_date = None
|
||||
end_date = None
|
||||
|
||||
print(f'Raw start_date: {start_date}, Raw end_date: {end_date}')
|
||||
|
||||
# Убедитесь, что даты имеют временную зону UTC
|
||||
if start_date:
|
||||
start_date = make_aware(start_date) if start_date.tzinfo is None else start_date
|
||||
if end_date:
|
||||
end_date = make_aware(end_date) if end_date.tzinfo is None else end_date
|
||||
|
||||
print(f'Filtered start_date: {start_date}, Filtered end_date: {end_date}')
|
||||
|
||||
# Фильтрация по "дата заезда"
|
||||
if start_date and end_date:
|
||||
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')
|
||||
)
|
||||
else: # Без фильтра по дате
|
||||
reservations = await sync_to_async(list)(
|
||||
Reservation.objects.filter(
|
||||
hotel_id=hotel_id
|
||||
).select_related('hotel')
|
||||
)
|
||||
|
||||
print(f'Filtered reservations count: {len(reservations)}')
|
||||
|
||||
if not reservations:
|
||||
await query.edit_message_text("Нет данных для статистики за выбранный период.")
|
||||
return
|
||||
|
||||
hotel = await sync_to_async(Hotel.objects.get)(id=hotel_id)
|
||||
print(f'Hotel: {hotel.name}')
|
||||
|
||||
for reservation in reservations:
|
||||
print(f"Reservation ID: {reservation.reservation_id}, Hotel: {reservation.hotel.name}, "
|
||||
f"Room number: {reservation.room_number}, Check-in: {reservation.check_in}, Check-out: {reservation.check_out}")
|
||||
|
||||
# Генерация PDF отчета (пример)
|
||||
file_path = generate_pdf_report(hotel.name, reservations, start_date, end_date)
|
||||
print(f'Generated file path: {file_path}')
|
||||
|
||||
with open(file_path, "rb") as file:
|
||||
await query.message.reply_document(document=file, filename=f"{hotel.name}_report.pdf")
|
||||
elif period == "all":
|
||||
start_date = (now - timedelta(days=1500)).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
end_date = now.replace(hour=23, minute=59, second=59, microsecond=999999)
|
||||
else:
|
||||
start_date = (now - timedelta(days=1500)).replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
end_date = now.replace(hour=23, minute=59, second=59, microsecond=999999)
|
||||
return start_date, end_date
|
||||
|
||||
async def stats_back(update: Update, context):
|
||||
"""Возврат к выбору отеля."""
|
||||
|
||||
Reference in New Issue
Block a user