Files
Touchh/pms_integration/plugins/shelter_pms.py
2024-12-28 18:57:24 +09:00

238 lines
9.7 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
import requests
import json
import os
from datetime import datetime, timedelta
from asgiref.sync import sync_to_async
from hotels.models import Hotel, Reservation
from .base_plugin import BasePMSPlugin
from touchh.utils.log import CustomLogger
class ShelterPMSPlugin(BasePMSPlugin):
"""
Плагин для интеграции с PMS Shelter.
"""
def __init__(self, hotel):
super().__init__(hotel.pms) # Передаем PMS-конфигурацию в базовый класс
self.hotel = hotel # Сохраняем объект отеля
# Проверка PMS-конфигурации
if not self.hotel.pms:
raise ValueError(f"Отель {self.hotel.name} не имеет связанной PMS конфигурации.")
# Инициализация параметров API
self.api_url = self.hotel.pms.url
self.token = self.hotel.pms.token
# Настройка логгера
self.logger = CustomLogger(name="ShelterPMSPlugin", log_level="WARNING").get_logger()
handler_console = logging.StreamHandler()
handler_file = logging.FileHandler('var/log/shelter_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.WARNING)
def get_default_parser_settings(self):
"""
Возвращает настройки парсера по умолчанию.
"""
return {
"field_mapping": {
"check_in": "from",
"check_out": "until",
"room_number": "roomNumber",
"room_type_name": "roomTypeName",
"status": "checkInStatus",
},
"date_format": "%Y-%m-%dT%H:%M:%S"
}
async def _fetch_data(self):
"""
Получает данные из PMS API и сохраняет их в базу.
"""
now = datetime.now()
start_date = (now - timedelta(days=60)).strftime('%Y-%m-%dT%H:%M:%SZ')
end_date = (now + timedelta(days=60)).strftime('%Y-%m-%dT%H:%M:%SZ')
headers = {
"Content-Type": "application/json",
"Accept": "text/plain",
"Authorization": f"Bearer {self.token}"
}
from_index = 0
count_per_request = 50
all_items = []
try:
while True:
data = {
"from": start_date,
"until": end_date,
"pagination": {
"from": from_index,
"count": count_per_request
}
}
response = await sync_to_async(requests.post)(self.api_url, headers=headers, data=json.dumps(data))
response.raise_for_status()
response_data = response.json()
items = response_data.get("items", [])
all_items.extend(items)
total_count = response_data.get("count", 0)
from_index += len(items)
if from_index >= total_count:
break
self.logger.info(f"Получено записей: {len(all_items)}")
# Сохранение данных во временный файл
temp_dir = os.path.join("temp", "shelter")
os.makedirs(temp_dir, exist_ok=True)
temp_file = os.path.join(temp_dir, f"shelter_data_{datetime.now().strftime('%Y%m%d%H%M%S')}.json")
with open(temp_file, 'w') as file:
json.dump(all_items, file)
self.logger.info(f"Данные сохранены во временный файл: {temp_file}")
return await self._process_data(all_items)
except requests.exceptions.RequestException as e:
self.logger.error(f"Ошибка API: {e}")
return {
"processed_intervals": 0,
"processed_items": 0,
"errors": [str(e)]
}
async def _process_data(self, data):
"""
Обрабатывает данные и сохраняет их в базу.
"""
processed_items = 0
errors = []
date_formats = ["%Y-%m-%d %H:%M:%S", "%Y-%m-%dT%H:%M:%S"] # Поддержка нескольких форматов даты
for item in data:
try:
# Парсинг даты с поддержкой нескольких форматов
checkin = self._parse_date(item['from'], date_formats)
checkout = self._parse_date(item['until'], date_formats)
reservation, created = await sync_to_async(Reservation.objects.update_or_create)(
reservation_id=item['id'],
defaults={
'room_number': item.get('roomNumber'),
'room_type': item.get('roomTypeName'),
'check_in': checkin,
'check_out': checkout,
'status': item.get('checkInStatus'),
'hotel': self.hotel,
}
)
if created:
self.logger.debug(f"Создана новая резервация: {reservation.reservation_id}")
else:
self.logger.debug(f"Обновлена существующая резервация: {reservation.reservation_id}")
processed_items += 1
except Exception as e:
self.logger.error(f"Ошибка обработки записи: {e}")
errors.append(str(e))
return {
"processed_intervals": 1,
"processed_items": processed_items,
"errors": errors
}
@staticmethod
def _parse_date(date_str, formats):
"""
Парсит дату, пытаясь использовать несколько форматов.
"""
for fmt in formats:
try:
return datetime.strptime(date_str, fmt)
except ValueError:
continue
raise ValueError(f"Дата '{date_str}' не соответствует ожидаемым форматам: {formats}")
def validate_plugin(self):
"""
Проверка корректности реализации плагина.
"""
required_methods = ["fetch_data", "get_default_parser_settings", "_fetch_data"]
for method in required_methods:
if not hasattr(self, method):
raise ValueError(f"Плагин {type(self).__name__} не реализует метод {method}.")
self.logger.debug(f"Плагин {self.__class__.__name__} прошел валидацию.")
return True
async def _save_to_db(self, item):
"""
Сохраняет данные в БД (например, информацию о номере).
"""
try:
# Проверяем, что item — это словарь
if not isinstance(item, dict):
self.logger.error(f"Ожидался словарь, но получен: {type(item)}. Данные: {item}")
return
# Получаем отель по настройкам PMS
hotel = await sync_to_async(Hotel.objects.get)(pms=self.pms_config)
self.logger.debug(f"Отель найден: {hotel.name}")
# Сохраняем данные бронирования
room_number = item.get("room_number")
check_in = item.get("checkin")
check_out = item.get("checkout")
room_type = item.get("room_type")
# Логируем полученные данные
self.logger.debug(f"Полученные данные для сохранения: room_number={room_number}, check_in={check_in}, check_out={check_out}, room_type={room_type}")
# Проверяем, существует ли запись с таким номером и датой заезда
existing_reservation = await sync_to_async(
Reservation.objects.filter(room_number=room_number, check_in=check_in).first
)()
if existing_reservation:
self.logger.debug(f"Резервация с таким номером и датой заезда уже существует. Обновляем...")
await sync_to_async(Reservation.objects.update_or_create)(
room_number=room_number,
check_in=check_in,
defaults={
"check_out": check_out,
"hotel": hotel,
"room_type": room_type,
},
)
self.logger.debug(f"Обновлена существующая резервация.")
else:
self.logger.debug(f"Резервация не найдена, создаем новую...")
reservation = await sync_to_async(Reservation.objects.create)(
room_number=room_number,
check_in=check_in,
check_out=check_out,
hotel=hotel,
room_type=room_type,
)
self.logger.debug(f"Создана новая резервация. ID: {reservation.reservation_id}")
except Exception as e:
self.logger.error(f"Ошибка сохранения данных: {e}")