Bot is functional after full refactor

This commit is contained in:
2024-12-07 23:44:12 +09:00
parent 626f378303
commit 9b539ca586
6 changed files with 114 additions and 27 deletions

View File

@@ -6,7 +6,8 @@ from hotels.models import Reservation, Hotel
from users.models import User from users.models import User
from bot.utils.pdf_report import generate_pdf_report from bot.utils.pdf_report import generate_pdf_report
from bot.utils.database import get_hotels_for_user from bot.utils.database import get_hotels_for_user, get_hotel_by_name
async def statistics(update: Update, context: ContextTypes.DEFAULT_TYPE): async def statistics(update: Update, context: ContextTypes.DEFAULT_TYPE):
"""Вывод списка отелей для статистики.""" """Вывод списка отелей для статистики."""
@@ -21,19 +22,21 @@ async def statistics(update: Update, context: ContextTypes.DEFAULT_TYPE):
return return
# Получаем отели, связанные с пользователем # Получаем отели, связанные с пользователем
hotels = await get_hotels_for_user(user) user_hotels = await get_hotels_for_user(user)
if not hotels: if not user_hotels:
await query.edit_message_text("У вас нет доступных отелей для статистики.") await query.edit_message_text("У вас нет доступных отелей для статистики.")
return return
# Формируем кнопки для выбора отеля # Формируем кнопки для выбора отеля
keyboard = [[InlineKeyboardButton(hotel.name, callback_data=f"stats_hotel_{hotel.id}")] for hotel in hotels] keyboard = [
[InlineKeyboardButton(hotel.hotel.name, callback_data=f"stats_hotel_{hotel.hotel.id}")]
for hotel in user_hotels
]
keyboard.append([InlineKeyboardButton("🏠 Главная", callback_data="main_menu")])
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("Выберите отель:", reply_markup=reply_markup) await query.edit_message_text("Выберите отель:", reply_markup=reply_markup)
async def stats_select_period(update: Update, context: ContextTypes.DEFAULT_TYPE):
async def stats_select_period(update: Update, context):
"""Выбор периода времени для статистики.""" """Выбор периода времени для статистики."""
query = update.callback_query query = update.callback_query
await query.answer() await query.answer()
@@ -46,18 +49,21 @@ async def stats_select_period(update: Update, context):
[InlineKeyboardButton("Месяц", callback_data="stats_period_month")], [InlineKeyboardButton("Месяц", callback_data="stats_period_month")],
[InlineKeyboardButton("Все время", callback_data="stats_period_all")], [InlineKeyboardButton("Все время", callback_data="stats_period_all")],
[InlineKeyboardButton("🏠 Главная", callback_data="main_menu")], [InlineKeyboardButton("🏠 Главная", callback_data="main_menu")],
[InlineKeyboardButton("🔙 Назад", callback_data="back")], [InlineKeyboardButton("🔙 Назад", callback_data="statistics")],
] ]
reply_markup = InlineKeyboardMarkup(keyboard) reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("Выберите период времени:", reply_markup=reply_markup) await query.edit_message_text("Выберите период времени:", reply_markup=reply_markup)
async def generate_statistics(update: Update, context: ContextTypes.DEFAULT_TYPE):
async def generate_statistics(update: Update, context):
"""Генерация и отправка статистики.""" """Генерация и отправка статистики."""
query = update.callback_query query = update.callback_query
await query.answer() await query.answer()
hotel_id = context.user_data["selected_hotel"] hotel_id = context.user_data.get("selected_hotel")
if not hotel_id:
await query.edit_message_text("Ошибка: ID отеля не найден.")
return
period = query.data.split("_")[2] period = query.data.split("_")[2]
now = datetime.now() now = datetime.now()
@@ -74,8 +80,40 @@ async def generate_statistics(update: Update, context):
reservations = await sync_to_async(list)( reservations = await sync_to_async(list)(
Reservation.objects.filter(hotel_id=hotel_id).prefetch_related('guests') Reservation.objects.filter(hotel_id=hotel_id).prefetch_related('guests')
) )
if not reservations:
await query.edit_message_text("Нет данных для статистики за выбранный период.")
return
hotel = await sync_to_async(Hotel.objects.get)(id=hotel_id) hotel = await sync_to_async(Hotel.objects.get)(id=hotel_id)
file_path = generate_pdf_report(hotel.name, reservations, start_date, end_date) file_path = generate_pdf_report(hotel.name, reservations, start_date, end_date)
with open(file_path, "rb") as file: with open(file_path, "rb") as file:
await query.message.reply_document(document=file, filename=file_path) await query.message.reply_document(document=file, filename=f"{hotel.name}_report.pdf")
async def stats_back(update: Update, context):
"""Возврат к выбору отеля."""
query = update.callback_query
await query.answer()
# Получаем отели, связанные с пользователем
user_id = query.from_user.id
user = await sync_to_async(User.objects.filter(chat_id=user_id).first)()
if not user:
await query.edit_message_text("Ошибка: Пользователь не найден.")
return
hotels = await get_hotels_for_user(user)
if not hotels:
await query.edit_message_text("У вас нет доступных отелей.")
return
# Формируем кнопки для выбора отеля
keyboard = [
[InlineKeyboardButton(hotel.name, callback_data=f"stats_hotel_{hotel.id}")]
for hotel in hotels
]
keyboard.append([InlineKeyboardButton("🏠 Главная", callback_data="main_menu")])
reply_markup = InlineKeyboardMarkup(keyboard)
await query.edit_message_text("Выберите отель:", reply_markup=reply_markup)

View File

@@ -1,21 +1,32 @@
from users.models import User from users.models import User
from hotels.models import Hotel, Reservation from hotels.models import Hotel, Reservation, UserHotel
from asgiref.sync import sync_to_async from asgiref.sync import sync_to_async
from django.core.exceptions import ObjectDoesNotExist
async def get_user_from_chat_id(chat_id): async def get_user_from_chat_id(chat_id):
return await sync_to_async(User.objects.filter(chat_id=chat_id).first)() try:
return await sync_to_async(User.objects.get)(chat_id=chat_id)
except ObjectDoesNotExist:
return None
async def get_hotel_by_id(hotel_id): async def get_hotel_by_id(hotel_id):
return await sync_to_async(Hotel.objects.get)(id=hotel_id) try:
return await sync_to_async(Hotel.objects.get)(id=hotel_id)
except ObjectDoesNotExist:
return None
async def get_hotel_by_name(hotel_name):
try:
return await sync_to_async(Hotel.objects.get)(name=hotel_name)
except ObjectDoesNotExist:
return None
async def get_hotels_for_user(user): async def get_hotels_for_user(user):
"""Получение отелей, связанных с пользователем.""" """Получение отелей, связанных с пользователем."""
# Проверяем, является ли пользователь сотрудником какого-либо отеля return await sync_to_async(list)(
user_hotels = await sync_to_async(list)( UserHotel.objects.filter(user=user).select_related('hotel')
Hotel.objects.filter(user_hotel__user=user).distinct()
) )
print(user_hotels)
return user_hotels
async def get_reservations(hotel_id, start_date=None, end_date=None): async def get_reservations(hotel_id, start_date=None, end_date=None):
query = Reservation.objects.filter(hotel_id=hotel_id) query = Reservation.objects.filter(hotel_id=hotel_id)

View File

@@ -0,0 +1,19 @@
# Generated by Django 5.1.4 on 2024-12-07 11:11
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hotels', '0018_alter_userhotel_hotel_alter_userhotel_user'),
]
operations = [
migrations.AlterField(
model_name='apirequestlog',
name='response_status',
field=models.IntegerField(validators=[django.core.validators.MinValueValidator(100), django.core.validators.MaxValueValidator(599)], verbose_name='HTTP статус ответа'),
),
]

View File

@@ -0,0 +1,20 @@
# Generated by Django 5.1.4 on 2024-12-07 14:09
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('hotels', '0019_alter_apirequestlog_response_status'),
('users', '0005_notificationsettings'),
]
operations = [
migrations.AlterField(
model_name='userhotel',
name='user',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_hotels', to='users.user', verbose_name='Пользователь'),
),
]

View File

@@ -1,5 +1,6 @@
from django.db import models from django.db import models
from users.models import User from users.models import User
from django.core.validators import MinValueValidator, MaxValueValidator
class PMSConfiguration(models.Model): class PMSConfiguration(models.Model):
@@ -74,11 +75,11 @@ class PMSIntegrationLog(models.Model):
class UserHotel(models.Model): class UserHotel(models.Model):
user = models.ForeignKey( user = models.ForeignKey(
User, on_delete=models.CASCADE, related_name="user_hotel", verbose_name="Пользователь" User, on_delete=models.CASCADE, related_name="user_hotels", verbose_name="Пользователь"
) )
hotel = models.ForeignKey( hotel = models.ForeignKey(
Hotel, on_delete=models.CASCADE, related_name="hotel_users", verbose_name="Отель" Hotel, on_delete=models.CASCADE, related_name="hotel_users", verbose_name="Отель"
) )
def __str__(self): def __str__(self):
return f"{self.user.username} - {self.hotel.name}" return f"{self.user.username} - {self.hotel.name}"
@@ -88,12 +89,10 @@ class UserHotel(models.Model):
verbose_name_plural = "Пользователи отелей" verbose_name_plural = "Пользователи отелей"
class APIRequestLog(models.Model): class APIRequestLog(models.Model):
api = models.ForeignKey(APIConfiguration, on_delete=models.CASCADE, verbose_name="API") api = models.ForeignKey(APIConfiguration, on_delete=models.CASCADE, verbose_name="API")
request_time = models.DateTimeField(auto_now_add=True, verbose_name="Время запроса") request_time = models.DateTimeField(auto_now_add=True, verbose_name="Время запроса")
response_status = models.IntegerField(verbose_name="HTTP статус ответа") response_status = models.IntegerField(verbose_name="HTTP статус ответа", validators=[MinValueValidator(100), MaxValueValidator(599)])
response_data = models.JSONField(verbose_name="Данные ответа", blank=True, null=True) response_data = models.JSONField(verbose_name="Данные ответа", blank=True, null=True)
def __str__(self): def __str__(self):

Binary file not shown.