Files
Touchh/pms_integration/plugins/realtycalendar_pms.py
2024-12-23 21:23:45 +09:00

157 lines
6.8 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 hashlib
import json
from .base_plugin import BasePMSPlugin
from datetime import datetime, timedelta
from asgiref.sync import sync_to_async
from math import ceil
class RealtyCalendarPlugin(BasePMSPlugin):
"""Плагин для импорта данных из системы RealtyCalendar
"""
def __init__(self, config):
super().__init__(config)
self.public_key = config.public_key
self.private_key = config.private_key
self.api_url = config.url.rstrip("/")
if not self.public_key or not self.private_key:
raise ValueError("Публичный или приватный ключ отсутствует для RealtyCalendar")
def get_default_parser_settings(self):
"""
Возвращает настройки по умолчанию для обработки данных.
"""
return {
"date_format": "%Y-%m-%dT%H:%M:%S",
"timezone": "UTC"
}
def _get_sorted_keys(self, obj):
"""
Возвращает отсортированный по имени список ключей.
"""
sorted_keys = sorted(list(obj.keys()))
print(f"[DEBUG] Отсортированные ключи: {sorted_keys}")
return sorted_keys
def _generate_data_string(self, obj):
"""
Формирует строку параметров для подписи.
"""
sorted_keys = self._get_sorted_keys(obj)
string = "".join(f"{key}={obj[key]}" for key in sorted_keys)
print(f"[DEBUG] Сформированная строка данных: {string}")
return string + self.private_key
def _generate_md5(self, string):
"""
Генерирует MD5-хеш от строки.
"""
md5_hash = hashlib.md5(string.encode("utf-8")).hexdigest()
print(f"[DEBUG] Сформированный MD5-хеш: {md5_hash}")
return md5_hash
def _generate_sign(self, data):
"""
Генерирует подпись для данных запроса.
"""
data_string = self._generate_data_string(data)
print(f"[DEBUG] Строка для подписи: {data_string}")
sign = self._generate_md5(data_string)
print(f"[DEBUG] Подпись: {sign}")
return sign
def _fetch_data(self):
"""
Выполняет запрос к API RealtyCalendar для получения данных о бронированиях.
"""
base_url = f"https://realtycalendar.ru/api/v1/bookings/{self.public_key}/"
headers = {
"Accept": "application/json",
"Content-Type": "application/json",
}
# Определяем даты выборки
now = datetime.now()
data = {
"begin_date": (now - timedelta(days=7)).strftime("%Y-%m-%d"),
"end_date": now.strftime("%Y-%m-%d"),
"room_number": ""
}
print(f"[DEBUG] Даты выборки: {data}")
# Генерация подписи
data["sign"] = self._generate_sign(data)
# Отправляем запрос
print(f"[DEBUG] URL запроса: {base_url}")
print(f"[DEBUG] Заголовки: {headers}")
print(f"[DEBUG] Данные запроса: {data}")
response = requests.post(url=base_url, headers=headers, json=data)
# Логируем результат
print(f"[DEBUG] Статус ответа: {response.status_code}")
print(f"[DEBUG] Ответ: {response.text}")
# Проверяем успешность запроса
if response.status_code == 200:
bookings = response.json().get("bookings", [])
print(f"[DEBUG] Полученные данные бронирований: {bookings}")
return bookings
else:
raise ValueError(f"Ошибка API RealtyCalendar: {response.status_code}, {response.text}")
async def _save_to_db(self, data, hotel_id, batch_size=50):
"""
Сохраняет данные о бронированиях в базу данных партиями.
"""
from hotels.models import Reservation, Hotel
try:
hotel = await sync_to_async(Hotel.objects.get)(id=hotel_id)
self.logger.info(f"Загружен отель: {hotel.name}")
# Разделение данных на батчи
total_records = len(data)
batches = [data[i:i + batch_size] for i in range(0, total_records, batch_size)]
self.logger.info(f"Обработка {total_records} записей в {len(batches)} партиях...")
for batch_index, batch in enumerate(batches):
self.logger.info(f"Обработка партии {batch_index + 1}/{len(batches)}")
for item in batch:
try:
if item.get("is_delete", False):
self.logger.info(f"Пропущена запись с ID {item.get('id')} (удалена).")
continue
client_data = item.get("client", {})
if not item.get("id") or not item.get("begin_date") or not item.get("end_date"):
self.logger.warning(f"Пропущена запись с неполными данными: {item}")
continue
reservation_defaults = {
"room_number": item.get("apartment_id", ""),
"check_in": datetime.strptime(item["begin_date"], "%Y-%m-%d"),
"check_out": datetime.strptime(item["end_date"], "%Y-%m-%d"),
"status": item.get("status", ""),
"price": item.get("amount", 0),
"client_name": client_data.get("fio", ""),
"client_email": client_data.get("email", ""),
"client_phone": client_data.get("phone", ""),
}
await sync_to_async(Reservation.objects.update_or_create)(
reservation_id=item["id"],
hotel=hotel,
defaults=reservation_defaults
)
self.logger.info(f"Сохранена запись для бронирования ID {item['id']}.")
except Exception as e:
self.logger.error(f"Ошибка при обработке бронирования ID {item.get('id', 'неизвестно')}: {e}")
except Exception as e:
self.logger.error(f"Ошибка при обработке данных: {e}")