Bot is functional after full refactor
This commit is contained in:
@@ -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)
|
||||||
|
|||||||
@@ -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)
|
||||||
@@ -23,4 +34,4 @@ async def get_reservations(hotel_id, start_date=None, end_date=None):
|
|||||||
query = query.filter(check_in__gte=start_date)
|
query = query.filter(check_in__gte=start_date)
|
||||||
if end_date:
|
if end_date:
|
||||||
query = query.filter(check_out__lte=end_date)
|
query = query.filter(check_out__lte=end_date)
|
||||||
return await sync_to_async(list)(query.prefetch_related('guests'))
|
return await sync_to_async(list)(query.prefetch_related('guests'))
|
||||||
@@ -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 статус ответа'),
|
||||||
|
),
|
||||||
|
]
|
||||||
20
hotels/migrations/0020_alter_userhotel_user.py
Normal file
20
hotels/migrations/0020_alter_userhotel_user.py
Normal 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='Пользователь'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -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):
|
||||||
@@ -139,4 +138,4 @@ class Guest(models.Model):
|
|||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = "Гость"
|
verbose_name = "Гость"
|
||||||
verbose_name_plural = "Гости"
|
verbose_name_plural = "Гости"
|
||||||
Binary file not shown.
Reference in New Issue
Block a user