Files
Touchh/pms_integration/plugins/ecvi_pms.py
2024-12-25 12:01:19 +09:00

186 lines
8.5 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 logging
from datetime import datetime, timedelta
import requests
from asgiref.sync import sync_to_async
from hotels.models import Hotel, Reservation
from .base_plugin import BasePMSPlugin
class EcviPMSPlugin(BasePMSPlugin):
"""
Плагин для интеграции с PMS Ecvi (интерфейс для получения данных об отеле).
"""
def __init__(self, pms_config):
super().__init__(pms_config)
# Инициализация логгера
self.logger = logging.getLogger(self.__class__.__name__) # Логгер с именем класса
handler_console = logging.StreamHandler() # Потоковый обработчик для вывода в консоль
handler_file = logging.FileHandler('ecvi_pms_plugin.log') # Обработчик для записи в файл
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler_console.setFormatter(formatter)
handler_file.setFormatter(formatter)
# Добавляем оба обработчика к логгеру
self.logger.addHandler(handler_console)
self.logger.addHandler(handler_file)
self.logger.setLevel(logging.DEBUG) # Уровень логирования
# Инициализация параметров API
self.api_url = pms_config.url
self.token = pms_config.token
self.username = pms_config.username
self.password = pms_config.password
self.pagination_count = 50 # Максимальное количество записей на страницу (если используется пагинация)
def get_default_parser_settings(self):
"""
Возвращает настройки парсера по умолчанию.
"""
self.logger.debug(f"get_default_parser_settings. pms_config: {self.pms_config}")
return {
"field_mapping": {
"check_in": "checkin",
"check_out": "checkout",
"room_number": "room_name", # Заменили на room_number
"room_type_name": "room_type",
"status": "occupancy",
},
"date_format": "%Y-%m-%dT%H:%M:%S"
}
async def _fetch_data(self):
"""
Получает данные из PMS API, фильтрует и сохраняет в базу данных.
"""
now = datetime.now()
current_date = now.strftime('%Y-%m-%d')
yesterday_date = (now - timedelta(days=1)).strftime('%Y-%m-%d')
processed_items = 0
errors = []
headers = {
"Content-Type": "application/json",
}
data = {
"token": self.token,
}
try:
# Запрос данных из PMS API
response = await sync_to_async(requests.post)(self.api_url, headers=headers, json=data, auth=(self.username, self.password))
response.raise_for_status() # Если ошибка, выбросит исключение
data = response.json() # Преобразуем ответ в JSON
self.logger.debug(f"Получены данные с API: {data}")
except requests.exceptions.RequestException as e:
self.logger.error(f"Ошибка запроса: {e}")
return []
# Фильтрация данных
filtered_data = []
for item in data:
try:
if not isinstance(item, dict):
raise ValueError(f"Некорректный формат элемента: {item}")
reservation_id = item.get('task_id')
if not reservation_id:
raise ValueError("Отсутствует task_id в записи")
checkin = datetime.strptime(item.get('checkin'), '%Y-%m-%d %H:%M:%S')
checkout = datetime.strptime(item.get('checkout'), '%Y-%m-%d %H:%M:%S')
filtered_data.append({
'reservation_id': reservation_id,
'room_number': item.get('room_name'),
'room_type': item.get('room_type'),
'checkin': checkin,
'checkout': checkout,
'status': item.get('occupancy')
})
processed_items += 1
except Exception as e:
self.logger.error(f"Ошибка обработки элемента: {e}")
errors.append(str(e))
# Сохранение данных в базу данных
try:
for item in filtered_data:
await self._save_to_db(item)
except Exception as e:
self.logger.error(f"Ошибка сохранения данных в БД: {e}")
errors.append(f"Ошибка сохранения данных в БД: {str(e)}")
# Формирование отчета
report = {
"processed_intervals": 1,
"processed_items": processed_items,
"errors": errors
}
self.logger.debug(f"Сформированный отчет: {report}")
return report
async def _save_to_db(self, item):
"""
Сохраняет данные в БД (например, информацию о номере).
"""
try:
# Получаем отель по настройкам PMS
hotel = await sync_to_async(Hotel.objects.get)(pms=self.pms_config)
self.logger.debug(f"Отель найден: {hotel.name}")
# Проверяем, существует ли уже резервация с таким внешним ID
reservation_id = item.get('reservation_id')
if not reservation_id:
self.logger.error("Ошибка: 'reservation_id' отсутствует в данных.")
return
existing_reservation = await sync_to_async(Reservation.objects.filter)(reservation_id=reservation_id)
# Теперь вызываем .first() после асинхронного вызова
existing_reservation = await sync_to_async(existing_reservation.first)()
if existing_reservation:
self.logger.debug(f"Резервация {reservation_id} уже существует. Обновляем...")
await sync_to_async(Reservation.objects.update_or_create)(
reservation_id=reservation_id,
defaults={
''
'room_number': item.get('room_number'),
'room_type': item.get('room_type'),
'check_in': item.get('checkin'),
'check_out': item.get('checkout'),
'status': item.get('status'),
'hotel': hotel
}
)
self.logger.debug(f"Резервация обновлена.")
else:
self.logger.debug(f"Резервация не найдена, создаем новую...")
reservation = await sync_to_async(Reservation.objects.create)(
reservation_id=reservation_id,
room_number=item.get('room_number'),
room_type=item.get('room_type'),
check_in=item.get('checkin'),
check_out=item.get('checkout'),
status=item.get('status'),
hotel=hotel
)
self.logger.debug(f"Новая резервация создана с ID: {reservation.reservation_id}")
except Exception as e:
self.logger.error(f"Ошибка сохранения данных: {e}")
def validate_plugin(self):
"""
Проверка на соответствие требованиям.
Можно проверить наличие методов или полей.
"""
# Проверяем наличие обязательных методов
required_methods = ["fetch_data", "get_default_parser_settings", "_fetch_data"]
for m in required_methods:
if not hasattr(self, m):
raise ValueError(f"Плагин {type(self).__name__} не реализует метод {m}.")
self.logger.debug(f"Плагин {self.__class__.__name__} прошел валидацию.")
return True