several FontAwesome icons changed.

This commit is contained in:
2024-12-23 15:11:49 +09:00
parent 31e4a50ad8
commit d850f50e25
7 changed files with 244 additions and 120 deletions

View File

@@ -11,6 +11,10 @@ from datetime import datetime
from django.utils.timezone import make_aware, is_aware, is_naive
import os
import traceback
import logging
from touchh.utils.log import CustomLogger
async def statistics(update: Update, context: ContextTypes.DEFAULT_TYPE):
@@ -61,106 +65,133 @@ async def stats_select_period(update: Update, context: ContextTypes.DEFAULT_TYPE
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
Преобразует значение в timezone-aware datetime объект, если это возможно.
:param value: Значение для преобразования
:type value: str, datetime или другое
:return: timezone-aware 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
if isinstance(value, datetime):
# Если это объект datetime, проверяем, наивен ли он
return make_aware(value) if is_naive(value) else value
elif isinstance(value, str):
# Если это строка, пытаемся преобразовать в datetime
try:
return make_aware(datetime.strptime(value, '%Y-%m-%d %H:%M:%S'))
except ValueError:
# Если формат не соответствует, пробуем более общую обработку
try:
return make_aware(datetime.fromisoformat(value))
except ValueError:
# Если не получилось распознать формат
logging.warning(f"Невозможно преобразовать строку в datetime: {value}")
else:
# Если тип неизвестен, просто возвращаем None
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()
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())
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
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("statistics.py Нет данных для статистики за выбранный период.")
await query.edit_message_text("Нет данных для статистики за выбранный период.")
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
hotel = await sync_to_async(Hotel.objects.get)(id=hotel_id)
try:
# Генерация отчета
file_path = await generate_pdf_report(hotel.name, reservations, start_date, end_date)
print(f"[DEBUG] start_date: {start_date}, type: {type(start_date)}")
print(f"[DEBUG] end_date: {end_date}, type: {type(end_date)}")
except Exception as e:
raise RuntimeError(f"statistics.py [ERROR] Ошибка при генерации PDF-отчета: {e}") from e
# Генерация 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')
)
try:
# Отправка файла через Telegram
with open(file_path, "rb") as file:
await query.message.reply_document(document=file, filename=f"{hotel.name}_report.pdf")
# Отправка PDF-файла
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()
logger.error(f"Ошибка в generate_statistics: {str(e)}", exc_info=True)
await query.edit_message_text(f"Произошла ошибка: {str(e)}")
def get_period_dates(period, now):
now = ensure_datetime(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)
@@ -178,6 +209,7 @@ def get_period_dates(period, now):
end_date = now.replace(hour=23, minute=59, second=59, microsecond=999999)
return start_date, end_date
async def stats_back(update: Update, context):
"""Возврат к выбору отеля."""
query = update.callback_query