Files
Touchh/pms_integration/plugins/bnovo_pms.py
2024-12-25 15:50:33 +09:00

240 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import requests
import json
from datetime import datetime, timedelta
from asgiref.sync import sync_to_async
from .base_plugin import BasePMSPlugin
from pms_integration.models import PMSConfiguration
from touchh.utils.log import CustomLogger
from pms_integration.models import PMSConfiguration
from hotels.models import Hotel, Reservation
logger = CustomLogger(name="BnovoPMS Plugin", log_level="DEBUG").get_logger()
class BnovoPMSPlugin(BasePMSPlugin):
"""Плагин для работы с PMS Bnovo."""
def __init__(self, config):
super().__init__(config)
self.api_url = config.url.rstrip("/")
self.username = config.username
self.password = config.password
self.token = None
if not self.api_url:
logger.error("Не указан URL для работы плагина.")
raise ValueError("Не указан URL для работы плагина.")
if not self.username or not self.password:
logger.error("Не указаны логин или пароль для авторизации.")
raise ValueError("Не указаны логин или пароль для авторизации.")
def get_default_parser_settings(self):
"""Возвращает настройки по умолчанию для обработки данных."""
logger.debug("Получение настроек парсера по умолчанию.")
return {
"date_format": "%Y-%m-%dT%H:%M:%S",
"timezone": "UTC"
}
async def _get_stored_token(self, hotel_id):
"""Получение токена из базы данных для конкретного отеля."""
try:
logger.debug(f"Попытка получения токена из базы данных для отеля: {hotel_id}.")
config = await sync_to_async(PMSConfiguration.objects.get)(plugin_name="bnovo", hotel_id=hotel_id)
logger.debug(f"Токен из базы данных: {config.token}")
return config.token
except PMSConfiguration.DoesNotExist:
logger.warning(f"Токен отсутствует в базе данных для отеля: {hotel_id}.")
return None
async def _save_token_to_db(self, sid, hotel_id):
"""Сохраняет токен (SID) в базу данных для конкретного отеля."""
try:
logger.debug(f"Сохранение токена в базу данных для отеля {hotel_id}: {sid}")
await sync_to_async(PMSConfiguration.objects.update_or_create)(
plugin_name="bnovo", hotel_id=hotel_id, defaults={"token": sid}
)
logger.debug("Токен успешно сохранен.")
except Exception as e:
logger.error(f"Ошибка сохранения токена в базу данных для отеля {hotel_id}: {e}")
def _get_auth_headers(self):
"""Создает заголовки авторизации."""
logger.debug("Создание заголовков авторизации.")
headers = {
"Content-Type": "application/json",
"Accept": "application/json",
}
if self.token:
headers["Cookie"] = f"SID={self.token}"
logger.debug(f"Добавлен токен в заголовки: {self.token}")
return headers
async def _fetch_session(self, hotel_id):
"""Получение нового токена (SID) через запрос для конкретного отеля."""
url = f"{self.api_url}/"
payload = {"username": self.username, "password": self.password}
headers = self._get_auth_headers()
logger.debug(f"Авторизация по адресу: {url} с данными: {json.dumps(payload)}")
response = requests.post(url, json=payload, headers=headers, allow_redirects=False)
logger.debug(f"Ответ авторизации: статус {response.status_code}, заголовки {response.headers}")
if response.status_code == 302:
cookies = response.cookies.get_dict()
sid = cookies.get("SID")
if sid:
self.token = sid
logger.debug(f"Получен новый SID: {sid}")
await self._save_token_to_db(sid, hotel_id)
else:
logger.error("Не удалось извлечь SID из ответа.")
raise ValueError("Не удалось извлечь SID из ответа.")
else:
logger.error(f"Ошибка авторизации: {response.status_code}, {response.text}")
raise ValueError(f"Ошибка авторизации: {response.status_code}, {response.text}")
async def _fetch_account_data(self, hotel_id):
"""Получение данных аккаунта через эндпоинт /account/current для конкретного отеля."""
logger.info(f"Начало получения данных аккаунта для отеля: {hotel_id}.")
self.token = await self._get_stored_token(hotel_id)
if not self.token:
logger.info("Токен отсутствует, выполняем авторизацию.")
await self._fetch_session(hotel_id)
url = f"{self.api_url}/account/current"
headers = self._get_auth_headers()
logger.debug(f"Выполнение запроса к {url}")
response = requests.get(url, headers=headers)
if response.status_code != 200:
logger.error(f"Ошибка при запросе данных аккаунта: {response.status_code}, {response.text}")
raise ValueError("Ошибка запроса к /account/current")
try:
account_data = response.json()
logger.debug(f"Полученные данные аккаунта: {json.dumps(account_data, indent=2)}")
except json.JSONDecodeError as e:
logger.error(f"Ошибка декодирования JSON: {e}")
raise ValueError(f"Ошибка декодирования JSON: {e}")
return account_data
async def fetch_and_log_account_data(self, hotel_id):
"""Вызов метода _fetch_account_data и вывод результата в лог."""
logger.info(f"Запуск получения и логирования данных аккаунта для отеля: {hotel_id}.")
try:
account_data = await self._fetch_account_data(hotel_id)
logger.info(f"Успешно полученные данные аккаунта: {json.dumps(account_data, indent=2)}")
return account_data
except Exception as e:
logger.error(f"Ошибка при получении данных аккаунта: {e}")
raise
async def fetch_data_with_account_info(self, hotel_id):
"""Получение данных аккаунта и бронирований для конкретного отеля."""
logger.info(f"Запуск процесса получения данных аккаунта и бронирований для отеля: {hotel_id}.")
try:
account_data = await self.fetch_and_log_account_data(hotel_id)
logger.info("Данные аккаунта успешно получены, продолжение с бронированиями.")
await self.__fetch_data()
except Exception as e:
logger.error(f"Ошибка при выполнении полной операции: {e}")
async def _fetch_data(self):
"""Получение данных о бронированиях с помощью эндпоинта /dashboard."""
# Проверяем наличие токена в базе данных
logger.info("Начало процесса получения данных.")
self.token = await self._get_stored_token()
if not self.token:
logger.info("Токен отсутствует, выполняем авторизацию.")
await self._fetch_session()
accounts = await self._fetch_account_data()
print(f'\n------\nACCOUNTS: {accounts}\n-------\n')
url = f"{self.api_url}/dashboard"
now = datetime.now()
create_from = (now - timedelta(days=90)).strftime("%d.%m.%Y")
create_to = now.strftime("%d.%m.%Y")
params = {
"create_from": create_from,
"create_to": create_to,
"advanced_search": 2,
"c": 100,
"page": 1,
"order_by": "create_date.asc",
}
headers = self._get_auth_headers()
logger.debug(f"Начальный запрос к {url} с параметрами: {json.dumps(params, indent=2)}")
all_bookings = []
while True:
logger.debug(f"Выполнение запроса к {url}")
response = requests.get(url, headers=headers, params=params)
logger.debug(f"Ответ запроса: статус {response.status_code}, тело {response.text}")
if response.status_code != 200:
logger.error(f"Ошибка при запросе: {response.status_code}, {response.text}")
raise ValueError("Ошибка запроса к /dashboard")
try:
data = response.json()
logger.debug(f"Полученные данные: {json.dumps(data, indent=2)}")
except json.JSONDecodeError as e:
logger.error(f"Ошибка декодирования JSON: {e}")
raise ValueError(f"Ошибка декодирования JSON: {e}")
bookings = data.get("bookings", [])
all_bookings.extend(bookings)
logger.info(f"Получено бронирований за запрос: {len(bookings)}. Всего: {len(all_bookings)}.")
# Проверяем наличие следующей страницы
pages_info = data.get("pages", {})
current_page = pages_info.get("current_page", 1)
total_pages = pages_info.get("total_pages", 1)
logger.debug(f"Информация о страницах: текущая {current_page}, всего {total_pages}")
if current_page >= total_pages:
break
params["page"] += 1
# Сопоставляем бронирования с существующими записями
for booking in all_bookings:
booking_id = booking.get("id")
hotel_id = booking.get("hotel_id")
if not booking_id or not hotel_id:
logger.warning("У бронирования отсутствует id или hotel_id. Пропуск.")
continue
if hotel_id != str(self.config.hotel.external_id_pms):
logger.debug(f"Бронирование {booking_id} не относится к отелю {self.config.hotel.external_id_pms}. Пропуск.")
continue
reservation, created = await sync_to_async(Reservation.objects.update_or_create)(
external_id=booking_id,
defaults={
"hotel": self.config.hotel,
"status": booking.get("status_name"),
"create_date": booking.get("create_date"),
"arrival": booking.get("arrival"),
"departure": booking.get("departure"),
"room_type": booking.get("initial_room_type_name"),
"data": booking
}
)
if created:
logger.info(f"Создана новая запись бронирования: {reservation}")
else:
logger.info(f"Обновлено существующее бронирование: {reservation}")
logger.info(f"Все бронирования получены и обработаны. Итоговое количество: {len(all_bookings)}")
return all_bookings