Shelter PMS fully functional

This commit is contained in:
2024-12-09 16:36:11 +09:00
parent 60eaef5527
commit e76a80fb2f
47 changed files with 665 additions and 909 deletions

View File

@@ -1,15 +1,62 @@
from abc import ABC, abstractmethod
class BasePMSPlugin(ABC):
def __init__(self, hotel, pms_config):
self.hotel = hotel
"""
Базовый класс для всех PMS плагинов.
Плагин должен уметь:
- Возвращать данные fetch_data()
- Предоставлять дефолтные parser_settings
- Проходить базовую валидацию (validate_plugin)
"""
def __init__(self, pms_config):
"""
pms_config: объект PMSConfiguration
"""
self.pms_config = pms_config
@abstractmethod
def fetch_and_parse(self):
def _fetch_data(self):
"""
Этот метод должен быть реализован в каждом плагине.
Он должен получать данные из API и возвращать их в нужном формате.
Абстрактный метод для получения данных.
"""
pass
def fetch_data(self):
"""
Обертка для выполнения _fetch_data с возможной дополнительной обработкой.
"""
return self._fetch_data()
@abstractmethod
def get_default_parser_settings(self):
"""
Возвращает словарь/JSON с дефолтными настройками разбора.
Например:
{
"fields_mapping": {
"reservation_id": "id",
"check_in": "from",
"check_out": "until"
},
"conditions": {
"checkInStatus": "Заселен"
}
}
"""
print("get_default_parser_settings. pms_config:", self.pms_config)
return {}
def validate_plugin(self):
"""
Проверка на соответствие требованиям.
Можно проверить наличие методов или полей.
"""
# Например, проверить наличие fetch_data и get_default_parser_settings
required_methods = ["fetch_data", "get_default_parser_settings"]
for m in required_methods:
if not hasattr(self, m):
raise ValueError(f"Плагин {type(self).__name__} не реализует метод {m}.")
# Можно добавить дополнительные проверки
return True

View File

@@ -3,11 +3,51 @@ from .base_plugin import BasePMSPlugin
class BnovoPMS(BasePMSPlugin):
"""
Плагин для интеграции с Bnovo.
"""
json_schema = {
"type": "object",
"properties": {
"id": {"type": "integer"},
"number": {"type": "integer"},
"roomTypeName": {"type": "string"},
"checkInStatus": {"type": "string"},
"guests": {"type": "array"},
},
"required": ["id", "number", "roomTypeName", "checkInStatus", "guests"]
}
def get_default_parser_settings(self):
"""
Возвращает настройки парсера по умолчанию.
"""
return {
"field_mapping": {
"room_name": "roomNumber",
"check_in": "from",
"check_out": "until",
},
"date_format": "%Y-%m-%dT%H:%M:%S"
}
def fetch_data(self):
response = requests.get(self.pms_config.url, headers={"Authorization": f"Bearer {self.pms_config.token}"})
response.raise_for_status()
data = response.json()
# Проверка структуры
expected_fields = self.pms_config.parser_settings.get("fields_mapping", {})
for field in expected_fields.values():
if field not in data[0]: # Проверяем первую запись
raise ValueError(f"Поле {field} отсутствует в ответе API.")
return data
def fetch_and_parse(self):
response = requests.get(
self.pms_config.url,
headers={"Authorization": f"Bearer {self.pms_config.token}"}
)
self.validate_response(response) # Проверка соответствия структуры
if response.status_code != 200:
raise ValueError(f"Ошибка запроса к PMS Bnovo: {response.text}")
@@ -28,3 +68,4 @@ class BnovoPMS(BasePMSPlugin):
}
reservations.append(reservation)
return reservations

View File

@@ -2,6 +2,38 @@ from .base_plugin import BasePMSPlugin
class EcviPMS(BasePMSPlugin):
def fetch_and_parse(self):
# Реализация логики для ECVI
pass
"""
Плагин для PMS Shelter.
"""
def fetch_data(self):
print("Fetching data from Ecvi PMS...")
# Реализация метода получения данных из PMS Shelter
response = requests.get(self.pms_config.url, headers={"Authorization": f"Bearer {self.pms_config.token}"})
print("Response status:", response.status_code)
response.raise_for_status()
data = response.json()
print("Number of rooms:", len(data))
return data
json_schema = {
"type": "object",
"properties": {
"id": {"type": "integer"},
"number": {"type": "integer"},
"roomTypeName": {"type": "string"},
"checkInStatus": {"type": "string"},
"guests": {"type": "array"},
},
"required": ["id", "number", "roomTypeName", "checkInStatus", "guests"]
}
def get_default_parser_settings(self):
"""
Возвращает настройки парсера по умолчанию.
"""
return {
"field_mapping": {
"room_name": "roomNumber",
"check_in": "from",
"check_out": "until",
},
"date_format": "%Y-%m-%dT%H:%M:%S"
}

View File

@@ -1,31 +1,100 @@
# pms_integration/plugins/shelter_pms.py
import requests
import json
from datetime import datetime, timedelta
from asgiref.sync import sync_to_async
from .base_plugin import BasePMSPlugin
from hotels.models import Reservation
from hotels.models import Hotel
class ShelterPMSPlugin(BasePMSPlugin):
"""Плагин для интеграции с Shelter PMS."""
def fetch_data(self):
"""Получение данных от Shelter PMS."""
url = self.api_config.url
headers = {"Authorization": f"Bearer {self.api_config.token}"}
response = requests.get(url, headers=headers, timeout=10)
response.raise_for_status()
return response.json()
class Shelter(BasePMSPlugin):
def __init__(self, config):
super().__init__(config)
self.token = config.token
def parse_data(self, raw_data):
"""Обработка данных от Shelter PMS."""
reservations = raw_data.get("reservations", [])
return [
{
"id": res["id"],
"room_number": res["room_number"],
"room_type": res["room_type"],
"check_in": res["check_in"],
"check_out": res["check_out"],
"status": res["status"],
"price": res.get("price"),
def get_default_parser_settings(self):
"""
Возвращает настройки по умолчанию для обработки данных.
"""
return {
"date_format": "%Y-%m-%dT%H:%M:%S",
"timezone": "UTC"
}
def _fetch_data(self):
"""
Выполняет запрос к API PMS для получения данных.
"""
url = 'https://pms.frontdesk24.ru/sheltercloudapi/Reservations/ByFilter'
headers = {
'accept': 'text/plain',
'Authorization': f'Bearer {self.token}',
'Content-Type': 'application/json',
}
from_index = 0
count_per_request = 50
total_count = None
all_items = []
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')
while total_count is None or from_index < total_count:
data = {
"from": start_date,
"until": end_date,
"pagination": {
"from": from_index,
"count": count_per_request
}
}
for res in reservations
]
response = requests.post(url, headers=headers, data=json.dumps(data))
if response.status_code == 200:
response_data = response.json()
items = response_data.get("items", [])
all_items.extend(items)
if total_count is None:
total_count = response_data.get("count", 0)
from_index += len(items)
else:
raise ValueError(f'Shelter API Error: {response.status_code}')
return all_items
async def _save_to_db(self, data, hotel_id):
"""
Сохраняет данные о бронированиях в таблицу Reservation.
:param data: Список данных о бронированиях.
:param hotel_id: ID отеля, к которому относятся бронирования.
"""
hotel = await sync_to_async(Hotel.objects.get)(id=hotel_id)
for item in data:
print(f"Данные для сохранения: {item}")
try:
reservation, created = await sync_to_async(Reservation.objects.update_or_create)(
reservation_id=item["id"],
hotel=hotel,
defaults={
"room_number": item.get("roomNumber", ""), # Номер комнаты
"room_type": item.get("roomTypeName", ""), # Тип комнаты
"check_in": datetime.strptime(item["from"], '%Y-%m-%dT%H:%M:%S'), # Дата заезда
"check_out": datetime.strptime(item["until"], '%Y-%m-%dT%H:%M:%S'), # Дата выезда
"status": item.get("checkInStatus", ""), # Статус бронирования
"price": item.get("reservationPrice", 0), # Цена
"discount": item.get("discount", 0), # Скидка
}
)
if created:
print(f"Создана запись: {reservation}")
else:
print(f"Обновлена запись: {reservation}")
except Exception as e:
print(f"Ошибка при сохранении бронирования ID {item['id']}: {e}")