# Проект управления отелями

## Описание

Проект для управления отелями, пользователями, интеграциями с PMS, а также для контроля статистики и уведомлений. Включает веб-админку, REST API для управления данными и Telegram-бота для взаимодействия с пользователями.

## Стек технологий

- Django (для бэкенда)
- Jazzmin (для кастомизации админки)
- MySQL (для хранения данных)
- python-telegram-bot (для Telegram-бота)
- Docker (для контейнеризации базы данных и проекта)

## Установка

### 1. Клонирование репозитория

```bash
git clone <repo url>
cd yourrepository
````

### 2. Установка зависимостей

```bash

python -m venv .venv
source .venv/bin/activate  # Для Linux/MacOS
.venv\Scripts\activate  # Для Windows

pip install -r requirements.txt
```
### 3. Настройка базы данных MySQL
#### 3.1. Запуск контейнера с MySQL

Используем Docker для поднятия MySQL контейнера:

``` bash
docker-compose up -d mysql
```
#### 3.2. Создание базы данных и пользователя

После поднятия контейнера с MySQL, создайте базу данных и пользователя:

### 3.2. Настройка базы данных

После поднятия контейнера с MySQL, создайте таблицы и загрузите дамп:

```
docker exec -it mysql_container bash
mysql -u root -p 
```

Введите пароль от MySQL, а затем загрузите ваш дамп:

```bash
mysql -u root -p your_database_name < /path/to/your_dump.sql
```
### 3.3. Применение миграций

После настройки базы данных выполните миграции для вашего Django-проекта:

```bash 
python manage.py migrate
```

### 4. Настройка админки

Для настройки админки с использованием Dazzling и Jazzmin, добавьте соответствующие настройки в settings.py:

```python
INSTALLED_APPS = [
    'dazzle',
    'jazzmin',
    ...
]

JAZZMIN_SETTINGS = {
    "site_title": "My Admin",
    "site_header": "My Administration",
    "site_brand": "My Brand",
    "footer": {
        "copyright": False,
        "version": False,
    },
}
```

### 5. Запуск проекта

Запустите сервер Django:

```bash 
python manage.py runserver
```

Теперь проект будет доступен по адресу http://127.0.0.1:8000.

##### Структура проекта

    hotel_manager/ — основная директория проекта.
    hotel_manager/settings.py — настройки Django.
    hotel_manager/models.py — модели данных для отелей, пользователей и статистики.
    hotel_manager/views.py — представления для работы с данными.
    hotel_manager/urls.py — маршруты проекта.
    bot/ — директория для бота, использующего python-telegram-bot.

##### Модели

    Отель (Hotel)
        Название отеля
        ID отеля
        PMS (Bnovo, Travel Line, Realty)
        Статус интеграции с PMS

    Пользователь (User)
        Имя пользователя
        Роль (Admin или Hotel User)
        Связь с отелем

    Настройки уведомлений (Notification Settings)
        Включено/выключено уведомление
        Часовой пояс
        Время отправки уведомлений

    Статистика (Statistics)
        Количество несанкционированных заселений за период
        Статус ошибок
        Даты и номера нарушений

##### API

    Администратор:
        Добавить/удалить отель
        Добавить/удалить пользователя
        Проверка статуса интеграции с PMS
        Управление уведомлениями

    Пользователь отеля:
        Получение статистики по заселениям
        Управление уведомлениями

##### Интеграция с Telegram-ботом

    Бот для администраторов позволяет управлять отелями, пользователями, уведомлениями и проверять статус интеграций.
    
    Бот для пользователей отелей позволяет получать статистику по заселениям и управлять уведомлениями.

##### Пример команды для администратора

    Добавить отель
    Список отелей
    Удалить отель
    Проверить статус PMS

##### Пример команды для пользователя отеля

    Показать статистику за вчера
    Управление уведомлениями

#### Проверка интеграции с PMS

Для каждого отеля можно проверять статус интеграции с PMS (Bnovo, Travel Line, Realty) и получать ответ о доступности PMS.


#### Разработка плагинов для интеграции с PMS

Для разработки плагина используются следующие инструменты:

- Django
- Python
- Pydantic

*код примера для плагина*

```python
from datetime import datetime, timedelta
import requests
from asgiref.sync import sync_to_async
from hotels.models import Reservation
from .base_plugin import BasePMSPlugin
import logging

class ExamplePMSPlugin(BasePMSPlugin):
    """
    Плагин для интеграции с PMS Example.
    """

    def __init__(self, hotel):
        super().__init__(hotel.pms)
        self.hotel = hotel
        self.api_url = self.hotel.pms.url
        self.token = self.hotel.pms.token
        self.logger = self._configure_logger()

    def _configure_logger(self):
        logger = logging.getLogger(self.__class__.__name__)
        handler_console = logging.StreamHandler()
        handler_file = logging.FileHandler(f'{self.__class__.__name__.lower()}.log')
        formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')
        handler_console.setFormatter(formatter)
        handler_file.setFormatter(formatter)
        logger.addHandler(handler_console)
        logger.addHandler(handler_file)
        logger.setLevel(logging.DEBUG)
        return logger

    def get_default_parser_settings(self):
        """
        Возвращает настройки для обработки данных.
        """
        return {
            "field_mapping": {
                "check_in": "arrival_date",
                "check_out": "departure_date",
                "room_number": "room",
                "status": "status",
            },
            "date_format": "%Y-%m-%d %H:%M:%S"
        }

    async def _fetch_data(self):
        """
        Получает данные из API Example PMS.
        """
        headers = {
            "Authorization": f"Bearer {self.token}",
            "Content-Type": "application/json",
        }
        now = datetime.now()
        payload = {
            "from_date": (now - timedelta(days=7)).strftime("%Y-%m-%d"),
            "to_date": now.strftime("%Y-%m-%d"),
        }

        try:
            response = await sync_to_async(requests.post)(
                self.api_url, json=payload, headers=headers
            )
            response.raise_for_status()
            data = response.json()
            return await self._process_data(data)
        except requests.exceptions.RequestException as e:
            self.logger.error(f"Ошибка API: {e}")
            return {"processed_items": 0, "errors": [str(e)]}

    async def _process_data(self, data):
        """
        Обрабатывает и сохраняет данные в базу.
        """
        processed_items = 0
        errors = []

        for item in data.get("bookings", []):
            try:
                reservation, created = await sync_to_async(Reservation.objects.update_or_create)(
                    reservation_id=item['id'],
                    defaults={
                        'room_number': item['room'],
                        'check_in': datetime.strptime(item['arrival_date'], "%Y-%m-%d"),
                        'check_out': datetime.strptime(item['departure_date'], "%Y-%m-%d"),
                        'status': item['status'],
                        'hotel': self.hotel,
                    }
                )
                processed_items += 1
            except Exception as e:
                self.logger.error(f"Ошибка обработки записи {item['id']}: {e}")
                errors.append(str(e))

        return {"processed_items": processed_items, "errors": errors}

```
