settings application

.env db params+global settings in admin model
ECVI plugin module
This commit is contained in:
2024-12-14 20:50:11 +09:00
parent 09eb249d68
commit 93994ed929
328 changed files with 190143 additions and 538 deletions

View File

@@ -1,212 +1,137 @@
import logging
import requests
import json
from datetime import datetime, timedelta, timezone
from datetime import datetime, timedelta
from asgiref.sync import sync_to_async
from hotels.models import Reservation, Hotel
from .base_plugin import BasePMSPlugin
from pms_integration.models import PMSConfiguration
from hotels.models import Hotel, Reservation
from .base_plugin import BasePMSPlugin
class Shelter(BasePMSPlugin):
class EcviPMSPlugin(BasePMSPlugin):
"""
Плагин для интеграции с Shelter PMS.
Плагин для интеграции с PMS Ecvi (интерфейс для получения данных об отеле).
"""
def __init__(self, pms_config):
# Инициализация родительского конструктора
super().__init__(pms_config)
# Инициализация логгера
self.logger = logging.getLogger(self.__class__.__name__) # Логгер с именем класса
handler = logging.StreamHandler() # Потоковый обработчик для вывода в консоль
formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
self.logger.addHandler(handler)
self.logger.setLevel(logging.DEBUG) # Уровень логирования
# Инициализация параметров API
self.api_url = pms_config.url
self.token = pms_config.token
self.pagination_count = 50
def get_default_parser_settings(self):
"""
Возвращает настройки по умолчанию для разбора данных PMS Shelter.
"""
return {
"fields_mapping": {
"reservation_id": "id",
"hotel_id": "hotelId",
"hotel_name": "hotelName",
"check_in": "from",
"check_out": "until",
"reservation_date": "date",
"room_type_id": "roomTypeId",
"room_id": "roomId",
"room_number": "roomNumber",
"room_type_name": "roomTypeName",
"check_in_status": "checkInStatus",
"is_annul": "isAnnul",
"reservation_price": "reservationPrice",
"discount": "discount",
self.username = pms_config.username
self.password = pms_config.password
self.pagination_count = 50 # Максимальное количество записей на страницу (если используется пагинация)
},
"date_format": "%Y-%m-%dT%H:%M:%S",
"timezone": "UTC",
}
async def _get_last_saved_date(self):
def get_default_parser_settings(self):
"""
Получает дату последнего сохраненного бронирования для отеля.
Возвращает настройки парсера по умолчанию.
"""
try:
last_reservation = await sync_to_async(
Reservation.objects.filter(hotel__pms=self.pms_config).order_by('-check_in').first
)()
return last_reservation.check_in if last_reservation else None
except Exception as e:
print(f"[ERROR] Ошибка получения последнего сохраненного бронирования: {e}")
return None
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.
Данные обрабатываются по временным промежуткам и сразу записываются в БД.
Возвращает отчёт о проделанной работе.
Получает данные из PMS API, фильтрует и сохраняет в базу данных.
"""
now = datetime.utcnow().replace(tzinfo=timezone.utc)
start_date = await self._get_last_saved_date() or (now - timedelta(days=90))
end_date = now + timedelta(days=30)
now = datetime.now()
current_date = now.strftime('%Y-%m-%d')
yesterday_date = (now - timedelta(days=1)).strftime('%Y-%m-%d')
headers = {
'accept': 'text/plain',
'Authorization': f'Bearer {self.token}',
'Content-Type': 'application/json',
"Content-Type": "application/json",
}
data = {
"token": self.token,
}
print(f"[DEBUG] Fetching data from {start_date} to {end_date}")
# Результаты выполнения
report = {
"processed_intervals": 0,
"processed_items": 0,
"errors": [],
}
# Разделение на временные интервалы
interval_days = 5 # Например, каждые 5 дней
current_start_date = start_date
while current_start_date < end_date:
current_end_date = min(current_start_date + timedelta(days=interval_days), end_date)
# Формирование payload
payload = {
"from": current_start_date.strftime('%Y-%m-%dT%H:%M:%SZ'),
"until": current_end_date.strftime('%Y-%m-%dT%H:%M:%SZ'),
}
print(f"[DEBUG] Sending payload: {json.dumps(payload)}")
try:
response = await sync_to_async(requests.post)(self.api_url, headers=headers, data=json.dumps(payload))
response.raise_for_status()
except requests.exceptions.RequestException as e:
error_message = f"[ERROR] Request error between {current_start_date} and {current_end_date}: {e}"
print(error_message)
report["errors"].append(error_message)
current_start_date = current_end_date
continue
try:
data = response.json()
print(f"[DEBUG] Received response: {data}")
except json.JSONDecodeError as e:
error_message = f"[ERROR] Ошибка декодирования JSON между {current_start_date} и {current_end_date}: {e}"
print(error_message)
report["errors"].append(error_message)
current_start_date = current_end_date
continue
total_count = data.get("count", 0)
items = data.get("items", [])
print(f"[DEBUG] Retrieved {len(items)} items (Total: {total_count}).")
# Если данных нет, пропускаем текущий интервал
if not items:
print(f"[WARNING] No items found between {current_start_date} and {current_end_date}.")
else:
for idx, item in enumerate(items, start=1):
print(f"[DEBUG] Processing item #{idx}/{len(items)}: {item}")
try:
await self._save_to_db(item)
report["processed_items"] += 1
except Exception as e:
error_message = f"[ERROR] Error processing item {item.get('id', 'Unknown')}: {e}"
print(error_message)
report["errors"].append(error_message)
# Отмечаем обработанный интервал
report["processed_intervals"] += 1
# Обновляем начало интервала
current_start_date = current_end_date
print(f"[DEBUG] Data fetching completed from {start_date} to {end_date}.")
return report
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 = [
{
'checkin': datetime.strptime(item.get('checkin'), '%Y-%m-%d %H:%M:%S'),
'checkout': datetime.strptime(item.get('checkout'), '%Y-%m-%d %H:%M:%S'),
'room_name': item.get('room_name'),
'room_type': item.get('room_type'),
'status': item.get('occupancy')
} for item in data if item.get('occupancy') in ['проживание', 'под выезд', 'под заезд']
]
# Сохранение данных в базу данных
for item in filtered_data:
await self._save_to_db(item)
self.logger.debug(f"Данные успешно сохранены.")
return filtered_data
async def _save_to_db(self, item):
"""
Сохраняет данные о бронировании в БД.
Проверяет, существует ли уже бронирование с таким ID.
Сохраняет данные в БД (например, информацию о номере).
"""
try:
print(f"[DEBUG] Starting to save reservation {item['id']} to the database.")
# Получаем отель по настройкам PMS
hotel = await sync_to_async(Hotel.objects.get)(pms=self.pms_config)
print(f"[DEBUG] Hotel found: {hotel.name}")
self.logger.debug(f"Отель найден: {hotel.name}")
date_format = '%Y-%m-%dT%H:%M:%S'
print(f"[DEBUG] Parsing check-in and check-out dates for reservation {item['id']}")
# Сохраняем данные бронирования
room_number = item.get("room_name")
check_in = item.get("checkin")
check_out = item.get("checkout")
room_type = item.get("room_type")
try:
check_in = datetime.strptime(item["from"], date_format).replace(tzinfo=timezone.utc)
check_out = datetime.strptime(item["until"], date_format).replace(tzinfo=timezone.utc)
print(f"[DEBUG] Parsed check-in: {check_in}, check-out: {check_out}")
except Exception as e:
print(f"[ERROR] Ошибка парсинга дат для бронирования {item['id']}: {e}")
raise
room_number = item.get("roomNumber", "") or "Unknown"
print(f"[DEBUG] Room number determined: {room_number}")
print(f"[DEBUG] Checking if reservation with ID {item['id']} already exists.")
# Проверяем, существует ли запись с таким номером и датой заезда
existing_reservation = await sync_to_async(
Reservation.objects.filter(reservation_id=item["id"]).first
Reservation.objects.filter(room_number=room_number, check_in=check_in).first
)()
if existing_reservation:
print(f"[DEBUG] Reservation with ID {item['id']} already exists. Updating it...")
print(f"[DEBUG] Updating existing reservation {item['id']}.")
self.logger.debug(f"Резервация с таким номером и датой заезда уже существует. Обновляем...")
await sync_to_async(Reservation.objects.update_or_create)(
reservation_id=item["id"],
hotel=hotel,
room_number=room_number,
check_in=check_in,
defaults={
"room_number": room_number,
"room_type": item.get("roomTypeName", ""),
"check_in": check_in,
"check_out": check_out,
"status": item.get("checkInStatus", ""),
"price": item.get("reservationPrice", 0),
"discount": item.get("discount", 0),
"hotel": hotel,
"room_type": room_type,
},
)
print(f"[DEBUG] Updated existing reservation {item['id']}.")
self.logger.debug(f"Обновлена существующая резервация.")
else:
print(f"[DEBUG] No existing reservation found for ID {item['id']}. Creating a new one...")
print(f"[DEBUG] Creating a new reservation {item['id']}.")
self.logger.debug(f"Резервация не найдена, создаем новую...")
await sync_to_async(Reservation.objects.create)(
reservation_id=item["id"],
hotel=hotel,
room_number=room_number,
room_type=item.get("roomTypeName", ""),
check_in=check_in,
check_out=check_out,
status=item.get("checkInStatus", ""),
price=item.get("reservationPrice", 0),
discount=item.get("discount", 0),
hotel=hotel,
room_type=room_type,
)
print(f"[DEBUG] Created a new reservation {item['id']}.")
self.logger.debug(f"Создана новая резервация.")
print(f"[DEBUG] {'Created' if not existing_reservation else 'Updated'} reservation {item['id']} / {hotel.name}.")
except ValueError as ve:
print(f"[ERROR] Ошибка обработки даты для бронирования {item['id']}: {ve}")
except Exception as e:
print(f"[ERROR] Ошибка сохранения бронирования {item['id']}: {e}")
self.logger.error(f"Ошибка сохранения данных: {e}")