From d5622f41a2b5429d04fb916be26589ac3557cca7 Mon Sep 17 00:00:00 2001 From: trevor Date: Tue, 24 Dec 2024 15:43:43 +0900 Subject: [PATCH] RealtyCalendar plugin --- antifroud/check_fraud.py | 5 +- antifroud/data_sync.py | 81 +++---- antifroud/migrations/0001_initial.py | 86 -------- ...ve_externaldbsettings_database_and_more.py | 42 ---- ...03_externaldbsettings_database_and_more.py | 43 ---- ...ter_externaldbsettings_options_and_more.py | 27 --- antifroud/migrations/0005_importedhotel.py | 24 -- .../0006_alter_importedhotel_options.py | 17 -- .../0007_useractivitylog_external_id.py | 18 -- .../0008_alter_useractivitylog_id.py | 18 -- .../0009_importedhotel_display_name.py | 18 -- antifroud/migrations/0010_synclog.py | 30 --- .../0011_alter_importedhotel_external_id.py | 18 -- antifroud/migrations/0012_violationlog.py | 31 --- .../0013_alter_useractivitylog_timestamp.py | 18 -- ...alter_useractivitylog_uastring_and_more.py | 68 ------ .../0015_alter_useractivitylog_page_id.py | 18 -- ..._alter_useractivitylog_created_and_more.py | 39 ---- .../migrations/0017_violationlog_hits.py | 19 -- ...ervation_alter_synclog_created_and_more.py | 44 ---- .../migrations/0019_alter_synclog_hotel.py | 20 -- .../0020_alter_useractivitylog_hits.py | 18 -- app_settings/app_settings.py | 64 ++---- app_settings/migrations/0001_initial.py | 86 -------- bot/management/commands/run_bot.py | 90 ++++++-- bot/operations/hotels.py | 8 +- bot/utils/scheduler.py | 10 - hotels/migrations/0001_initial.py | 116 ---------- hotels/migrations/0002_initial.py | 42 ---- hotels/migrations/0003_initial.py | 30 --- .../0004_alter_reservation_room_number.py | 18 -- ...mport_status_hotel_imported_at_and_more.py | 29 --- ...api_remove_hotel_import_status_and_more.py | 33 --- hotels/migrations/0007_hotel_hotel_id.py | 18 -- ...l_city_hotel_email_hotel_phone_and_more.py | 38 ---- hotels/migrations/0009_hotel_description.py | 18 -- .../migrations/0010_alter_hotel_timezone.py | 18 -- ...m_alter_fraudlog_check_in_date_and_more.py | 95 -------- hotels/migrations/0012_alter_room_number.py | 18 -- hotels/migrations/0013_alter_room_number.py | 18 -- hotels/migrations/0014_alter_room_number.py | 18 -- pms_integration/manager.py | 4 +- pms_integration/migrations/0001_initial.py | 48 ---- ...0002_alter_pmsconfiguration_plugin_name.py | 18 -- ...0003_alter_pmsconfiguration_plugin_name.py | 18 -- ...0004_alter_pmsconfiguration_plugin_name.py | 18 -- ...5_pmsconfiguration_private_key_and_more.py | 23 -- pms_integration/plugins/ecvi_pms.py | 5 +- pms_integration/plugins/realtycalendar_pms.py | 205 ++++++++++++------ scheduler/admin.py | 26 ++- scheduler/apps.py | 10 +- .../management/commands/start_scheduler.py | 39 ++++ scheduler/migrations/0001_initial.py | 30 --- scheduler/reload_tasks.py | 17 ++ scheduler/tasks.py | 6 +- touchh/settings.py | 10 +- users/migrations/0001_initial.py | 120 ---------- ...vitylog_delete_useractivitylog_and_more.py | 22 -- 58 files changed, 374 insertions(+), 1714 deletions(-) delete mode 100644 antifroud/migrations/0001_initial.py delete mode 100644 antifroud/migrations/0002_remove_externaldbsettings_database_and_more.py delete mode 100644 antifroud/migrations/0003_externaldbsettings_database_and_more.py delete mode 100644 antifroud/migrations/0004_alter_externaldbsettings_options_and_more.py delete mode 100644 antifroud/migrations/0005_importedhotel.py delete mode 100644 antifroud/migrations/0006_alter_importedhotel_options.py delete mode 100644 antifroud/migrations/0007_useractivitylog_external_id.py delete mode 100644 antifroud/migrations/0008_alter_useractivitylog_id.py delete mode 100644 antifroud/migrations/0009_importedhotel_display_name.py delete mode 100644 antifroud/migrations/0010_synclog.py delete mode 100644 antifroud/migrations/0011_alter_importedhotel_external_id.py delete mode 100644 antifroud/migrations/0012_violationlog.py delete mode 100644 antifroud/migrations/0013_alter_useractivitylog_timestamp.py delete mode 100644 antifroud/migrations/0014_alter_useractivitylog_uastring_and_more.py delete mode 100644 antifroud/migrations/0015_alter_useractivitylog_page_id.py delete mode 100644 antifroud/migrations/0016_alter_useractivitylog_created_and_more.py delete mode 100644 antifroud/migrations/0017_violationlog_hits.py delete mode 100644 antifroud/migrations/0018_remove_synclog_reservation_alter_synclog_created_and_more.py delete mode 100644 antifroud/migrations/0019_alter_synclog_hotel.py delete mode 100644 antifroud/migrations/0020_alter_useractivitylog_hits.py delete mode 100644 app_settings/migrations/0001_initial.py delete mode 100644 bot/utils/scheduler.py delete mode 100644 hotels/migrations/0001_initial.py delete mode 100644 hotels/migrations/0002_initial.py delete mode 100644 hotels/migrations/0003_initial.py delete mode 100644 hotels/migrations/0004_alter_reservation_room_number.py delete mode 100644 hotels/migrations/0005_hotel_import_status_hotel_imported_at_and_more.py delete mode 100644 hotels/migrations/0006_remove_hotel_api_remove_hotel_import_status_and_more.py delete mode 100644 hotels/migrations/0007_hotel_hotel_id.py delete mode 100644 hotels/migrations/0008_hotel_address_hotel_city_hotel_email_hotel_phone_and_more.py delete mode 100644 hotels/migrations/0009_hotel_description.py delete mode 100644 hotels/migrations/0010_alter_hotel_timezone.py delete mode 100644 hotels/migrations/0011_room_alter_fraudlog_check_in_date_and_more.py delete mode 100644 hotels/migrations/0012_alter_room_number.py delete mode 100644 hotels/migrations/0013_alter_room_number.py delete mode 100644 hotels/migrations/0014_alter_room_number.py delete mode 100644 pms_integration/migrations/0001_initial.py delete mode 100644 pms_integration/migrations/0002_alter_pmsconfiguration_plugin_name.py delete mode 100644 pms_integration/migrations/0003_alter_pmsconfiguration_plugin_name.py delete mode 100644 pms_integration/migrations/0004_alter_pmsconfiguration_plugin_name.py delete mode 100644 pms_integration/migrations/0005_pmsconfiguration_private_key_and_more.py create mode 100644 scheduler/management/commands/start_scheduler.py delete mode 100644 scheduler/migrations/0001_initial.py create mode 100644 scheduler/reload_tasks.py delete mode 100644 users/migrations/0001_initial.py delete mode 100644 users/migrations/0002_delete_localuseractivitylog_delete_useractivitylog_and_more.py diff --git a/antifroud/check_fraud.py b/antifroud/check_fraud.py index 97df113e..190358da 100644 --- a/antifroud/check_fraud.py +++ b/antifroud/check_fraud.py @@ -1,4 +1,3 @@ -import logging from datetime import timedelta from urllib.parse import parse_qs from django.utils import timezone @@ -7,8 +6,8 @@ from hotels.models import Reservation, Hotel from .models import UserActivityLog, ViolationLog from touchh.utils.log import CustomLogger # Настройка логирования -logging.basicConfig(level=logging.INFO) -logger = logging.getLogger(__name__) +logger = CustomLogger(__name__).get_logger() + class ReservationChecker: """ diff --git a/antifroud/data_sync.py b/antifroud/data_sync.py index df58c3c9..54baeae8 100644 --- a/antifroud/data_sync.py +++ b/antifroud/data_sync.py @@ -202,63 +202,56 @@ class DataSyncManager: self.logger.error(f"Error fetching data: {e}") return [] def update_sync_log(self, hotel, recieved_records, processed_records): + """ + Обновляет или создает запись в таблице SyncLog. + """ try: - log, created = SyncLog.objects.get_or_create(hotel=hotel) + log, created = SyncLog.objects.update_or_create( + hotel=hotel, + defaults={ + "recieved_records": recieved_records, + "processed_records": processed_records, + "created": timezone.now(), # Убедитесь, что дата обновляется + } + ) if created: - log.recieved_records = recieved_records - log.processed_records = processed_records + self.logger.info(f"Sync log created for hotel '{hotel.name}'.") else: - log.recieved_records += recieved_records - log.processed_records += processed_records - log.save() - self.logger.info(f"Sync log updated for hotel '{hotel.name}'.") + self.logger.info(f"Sync log updated for hotel '{hotel.name}'.") except Exception as e: self.logger.error(f"Error updating sync log for hotel '{hotel.name}': {e}") + + self.logger.info(f"Attempting to update sync log for hotel: {hotel.name}") + self.update_sync_log(hotel, recieved_records, processed_records) def process_and_save_data(self, rows): - """ - Обрабатывает и сохраняет данные из внешнего источника. - - :param rows: Список строк данных, полученных из базы данных. - """ - seen_entries = set() + hotel_processed_counts = {} # Словарь для подсчёта записей по каждому отелю for row in rows: - # Получение и декодирование URL-параметров - url_parameters = row.get("url_parameters") - if not url_parameters: - self.logger.warning(f"Skipping record with missing URL parameters: {row}") - continue - - parsed_params = self.data_processor.parse_url_parameters(url_parameters) - hotel_id = parsed_params.get("utm_content") # Извлекаем hotel_id из параметров - room_number = parsed_params.get("utm_term") # Извлекаем room_number из параметров - - if not hotel_id or not room_number: - self.logger.warning(f"Skipping record with missing data: hotel_id={hotel_id}, room_number={room_number}") - continue - - # Проверка на дубликаты - if (hotel_id, room_number) in seen_entries: - self.logger.warning(f"Duplicate record skipped: hotel_id={hotel_id}, room_number={room_number}") - continue - - seen_entries.add((hotel_id, room_number)) - try: - # Получение или создание отеля + url_parameters = row.get("url_parameters") + if not url_parameters: + self.logger.warning(f"Skipping record with missing URL parameters: {row}") + continue + + parsed_params = self.data_processor.parse_url_parameters(url_parameters) + hotel_id = parsed_params.get("utm_content") + room_number = parsed_params.get("utm_term") + + if not hotel_id or not room_number: + self.logger.warning(f"Skipping record with missing data: hotel_id={hotel_id}, room_number={room_number}") + continue + hotel = self.hotel_manager.get_or_create_hotel(hotel_id, row.get("page_title")) if not hotel: self.logger.warning(f"Skipping record: Failed to create or retrieve hotel with ID {hotel_id}") continue - # Получение или создание комнаты room = self.hotel_manager.get_or_create_room(hotel, room_number) if not room: self.logger.warning(f"Skipping record: Failed to create or retrieve room {room_number} in hotel {hotel.name}") continue - # Создание или обновление записи активности пользователя UserActivityLog.objects.update_or_create( external_id=row.get("id"), defaults={ @@ -270,14 +263,23 @@ class DataSyncManager: "url_parameters": parsed_params, "page_title": self.data_processor.decode_html_entities(row.get("page_title")) or "Untitled", "page_url": row.get("page_url") or "", + "page_id": row.get("page_id") or 0, "hits": row.get("hits") or 0, } ) self.logger.info(f"Record ID {row.get('id')} processed successfully.") + + if hotel.id not in hotel_processed_counts: + hotel_processed_counts[hotel.id] = {"recieved_records": 0, "processed_records": 0} + hotel_processed_counts[hotel.id]["processed_records"] += 1 + except Exception as e: self.logger.error(f"Error processing record ID {row.get('id')}: {e}") - self.logger.info(f"Data processing completed. Processed {len(seen_entries)} unique records.") + for hotel_id, counts in hotel_processed_counts.items(): + hotel = Hotel.objects.get(id=hotel_id) + self.update_sync_log(hotel, recieved_records=len(rows), processed_records=counts["processed_records"]) + def sync(self): @@ -292,7 +294,8 @@ class DataSyncManager: def scheduled_sync(): - logger = CustomLogger(name="DatabaseSyncScheduler", log_level="ERROR").get_logger() + import os + logger = CustomLogger(name="DatabaseSyncScheduler", log_level=os.getenv("SCHEDULED_SYNC_LOG_LEVEL", default="ERROR")).get_logger() logger.info("Starting scheduled sync.") active_db_settings = ExternalDBSettings.objects.filter(is_active=True) diff --git a/antifroud/migrations/0001_initial.py b/antifroud/migrations/0001_initial.py deleted file mode 100644 index 2ee9be3e..00000000 --- a/antifroud/migrations/0001_initial.py +++ /dev/null @@ -1,86 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-12 12:28 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('hotels', '0004_alter_reservation_room_number'), - ] - - operations = [ - migrations.CreateModel( - name='ExternalDBSettings', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(help_text='Имя подключения для идентификации.', max_length=255, unique=True)), - ('host', models.CharField(help_text='Адрес сервера базы данных.', max_length=255)), - ('port', models.PositiveIntegerField(default=3306, help_text='Порт сервера базы данных.')), - ('database', models.CharField(help_text='Имя базы данных.', max_length=255)), - ('user', models.CharField(help_text='Имя пользователя базы данных.', max_length=255)), - ('password', models.CharField(help_text='Пароль для подключения.', max_length=255)), - ('table_name', models.CharField(blank=True, help_text='Имя таблицы для загрузки данных.', max_length=255, null=True)), - ('selected_fields', models.TextField(blank=True, help_text='Список полей для загрузки (через запятую).', null=True)), - ('is_active', models.BooleanField(default=True, help_text='Флаг активности подключения.')), - ('created_at', models.DateTimeField(auto_now_add=True)), - ('updated_at', models.DateTimeField(auto_now=True)), - ], - options={ - 'verbose_name': 'Настройки подключения к БД', - 'verbose_name_plural': 'Настройки подключений к БД', - }, - ), - migrations.CreateModel( - name='UserActivityLog', - fields=[ - ('id', models.BigIntegerField(primary_key=True, serialize=False)), - ('user_id', models.BigIntegerField(verbose_name='ID пользователя')), - ('ip', models.GenericIPAddressField(verbose_name='IP-адрес')), - ('created', models.DateTimeField(verbose_name='Дата создания')), - ('timestamp', models.BigIntegerField(verbose_name='Метка времени')), - ('date_time', models.DateTimeField(verbose_name='Дата и время')), - ('referred', models.TextField(blank=True, null=True, verbose_name='Реферальная ссылка')), - ('agent', models.TextField(verbose_name='Агент пользователя')), - ('platform', models.CharField(blank=True, max_length=255, null=True, verbose_name='Платформа')), - ('version', models.CharField(blank=True, max_length=255, null=True, verbose_name='Версия')), - ('model', models.CharField(blank=True, max_length=255, null=True, verbose_name='Модель устройства')), - ('device', models.CharField(blank=True, max_length=255, null=True, verbose_name='Тип устройства')), - ('UAString', models.TextField(verbose_name='User-Agent строка')), - ('location', models.CharField(blank=True, max_length=255, null=True, verbose_name='Местоположение')), - ('page_id', models.BigIntegerField(blank=True, null=True, verbose_name='ID страницы')), - ('url_parameters', models.TextField(blank=True, null=True, verbose_name='Параметры URL')), - ('page_title', models.TextField(blank=True, null=True, verbose_name='Заголовок страницы')), - ('type', models.CharField(max_length=50, verbose_name='Тип')), - ('last_counter', models.IntegerField(verbose_name='Последний счетчик')), - ('hits', models.IntegerField(verbose_name='Количество обращений')), - ('honeypot', models.BooleanField(verbose_name='Метка honeypot')), - ('reply', models.BooleanField(verbose_name='Ответ пользователя')), - ('page_url', models.URLField(blank=True, null=True, verbose_name='URL страницы')), - ], - options={ - 'verbose_name': 'Регистрация посетителей', - 'verbose_name_plural': 'Регистрации посетителей', - }, - ), - migrations.CreateModel( - name='RoomDiscrepancy', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('room_number', models.CharField(max_length=50, verbose_name='Номер комнаты')), - ('booking_id', models.CharField(max_length=255, verbose_name='ID бронирования')), - ('check_in_date_expected', models.DateField(verbose_name='Ожидаемая дата заселения')), - ('check_in_date_actual', models.DateField(verbose_name='Фактическая дата заселения')), - ('discrepancy_type', models.CharField(choices=[('early', 'Раннее заселение'), ('late', 'Позднее заселение'), ('missed', 'Неявка')], max_length=50, verbose_name='Тип несоответствия')), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')), - ('hotel', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hotels.hotel', verbose_name='Отель')), - ], - options={ - 'verbose_name': 'Несовпадение в заселении', - 'verbose_name_plural': 'Несовпадения в заселении', - }, - ), - ] diff --git a/antifroud/migrations/0002_remove_externaldbsettings_database_and_more.py b/antifroud/migrations/0002_remove_externaldbsettings_database_and_more.py deleted file mode 100644 index 9f0c28cd..00000000 --- a/antifroud/migrations/0002_remove_externaldbsettings_database_and_more.py +++ /dev/null @@ -1,42 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-12 13:07 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0001_initial'), - ] - - operations = [ - migrations.RemoveField( - model_name='externaldbsettings', - name='database', - ), - migrations.AlterField( - model_name='externaldbsettings', - name='host', - field=models.CharField(default='', help_text='Адрес сервера базы данных.', max_length=255), - ), - migrations.AlterField( - model_name='externaldbsettings', - name='is_active', - field=models.BooleanField(default=False, help_text='Флаг активности подключения.'), - ), - migrations.AlterField( - model_name='externaldbsettings', - name='name', - field=models.CharField(default='Новая настройка', help_text='Имя подключения для идентификации.', max_length=255, unique=True), - ), - migrations.AlterField( - model_name='externaldbsettings', - name='password', - field=models.CharField(default='', help_text='Пароль для подключения.', max_length=255), - ), - migrations.AlterField( - model_name='externaldbsettings', - name='user', - field=models.CharField(default='', help_text='Имя пользователя базы данных.', max_length=255), - ), - ] diff --git a/antifroud/migrations/0003_externaldbsettings_database_and_more.py b/antifroud/migrations/0003_externaldbsettings_database_and_more.py deleted file mode 100644 index 76732d77..00000000 --- a/antifroud/migrations/0003_externaldbsettings_database_and_more.py +++ /dev/null @@ -1,43 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-12 13:10 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0002_remove_externaldbsettings_database_and_more'), - ] - - operations = [ - migrations.AddField( - model_name='externaldbsettings', - name='database', - field=models.CharField(default='', help_text='Имя базы данных.', max_length=255), - ), - migrations.AlterField( - model_name='externaldbsettings', - name='host', - field=models.CharField(help_text='Адрес сервера базы данных.', max_length=255), - ), - migrations.AlterField( - model_name='externaldbsettings', - name='is_active', - field=models.BooleanField(default=True, help_text='Флаг активности подключения.'), - ), - migrations.AlterField( - model_name='externaldbsettings', - name='name', - field=models.CharField(help_text='Имя подключения для идентификации.', max_length=255, unique=True), - ), - migrations.AlterField( - model_name='externaldbsettings', - name='password', - field=models.CharField(help_text='Пароль для подключения.', max_length=255), - ), - migrations.AlterField( - model_name='externaldbsettings', - name='user', - field=models.CharField(help_text='Имя пользователя базы данных.', max_length=255), - ), - ] diff --git a/antifroud/migrations/0004_alter_externaldbsettings_options_and_more.py b/antifroud/migrations/0004_alter_externaldbsettings_options_and_more.py deleted file mode 100644 index c9e24c37..00000000 --- a/antifroud/migrations/0004_alter_externaldbsettings_options_and_more.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-12 14:42 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0003_externaldbsettings_database_and_more'), - ] - - operations = [ - migrations.AlterModelOptions( - name='externaldbsettings', - options={'verbose_name': 'Настройка подключения к БД', 'verbose_name_plural': 'Настройки подключений к БД'}, - ), - migrations.AlterField( - model_name='externaldbsettings', - name='database', - field=models.CharField(default='u1510415_wp832', help_text='Имя базы данных.', max_length=255), - ), - migrations.AlterField( - model_name='externaldbsettings', - name='table_name', - field=models.CharField(blank=True, default='wpts_user_activity_log', help_text='Имя таблицы для загрузки данных.', max_length=255, null=True), - ), - ] diff --git a/antifroud/migrations/0005_importedhotel.py b/antifroud/migrations/0005_importedhotel.py deleted file mode 100644 index 23659006..00000000 --- a/antifroud/migrations/0005_importedhotel.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-12 23:57 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0004_alter_externaldbsettings_options_and_more'), - ] - - operations = [ - migrations.CreateModel( - name='ImportedHotel', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('external_id', models.CharField(max_length=255, unique=True, verbose_name='Внешний ID отеля')), - ('name', models.CharField(max_length=255, verbose_name='Имя отеля')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')), - ('updated', models.DateTimeField(auto_now=True, verbose_name='Дата обновления')), - ('imported', models.BooleanField(default=False, verbose_name='Импортирован в основную базу')), - ], - ), - ] diff --git a/antifroud/migrations/0006_alter_importedhotel_options.py b/antifroud/migrations/0006_alter_importedhotel_options.py deleted file mode 100644 index c166c76c..00000000 --- a/antifroud/migrations/0006_alter_importedhotel_options.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-12 23:58 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0005_importedhotel'), - ] - - operations = [ - migrations.AlterModelOptions( - name='importedhotel', - options={'verbose_name': 'Импортированный отель', 'verbose_name_plural': 'Импортированные отели'}, - ), - ] diff --git a/antifroud/migrations/0007_useractivitylog_external_id.py b/antifroud/migrations/0007_useractivitylog_external_id.py deleted file mode 100644 index 4111155e..00000000 --- a/antifroud/migrations/0007_useractivitylog_external_id.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-13 00:03 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0006_alter_importedhotel_options'), - ] - - operations = [ - migrations.AddField( - model_name='useractivitylog', - name='external_id', - field=models.CharField(blank=True, max_length=255, null=True), - ), - ] diff --git a/antifroud/migrations/0008_alter_useractivitylog_id.py b/antifroud/migrations/0008_alter_useractivitylog_id.py deleted file mode 100644 index e1277403..00000000 --- a/antifroud/migrations/0008_alter_useractivitylog_id.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-13 00:09 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0007_useractivitylog_external_id'), - ] - - operations = [ - migrations.AlterField( - model_name='useractivitylog', - name='id', - field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), - ), - ] diff --git a/antifroud/migrations/0009_importedhotel_display_name.py b/antifroud/migrations/0009_importedhotel_display_name.py deleted file mode 100644 index d1c30b9a..00000000 --- a/antifroud/migrations/0009_importedhotel_display_name.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-13 00:32 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0008_alter_useractivitylog_id'), - ] - - operations = [ - migrations.AddField( - model_name='importedhotel', - name='display_name', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Отображаемое имя'), - ), - ] diff --git a/antifroud/migrations/0010_synclog.py b/antifroud/migrations/0010_synclog.py deleted file mode 100644 index de524ad2..00000000 --- a/antifroud/migrations/0010_synclog.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-14 04:29 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0009_importedhotel_display_name'), - ('hotels', '0010_alter_hotel_timezone'), - ] - - operations = [ - migrations.CreateModel( - name='SyncLog', - fields=[ - ('id', models.BigIntegerField(primary_key=True, serialize=False, unique=True, verbose_name='ID')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')), - ('recieved_records', models.IntegerField(verbose_name='Полученные записи')), - ('processed_records', models.IntegerField(verbose_name='Обработанные записи')), - ('hotel', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hotels.hotel', verbose_name='Отель')), - ('reservation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hotels.reservation', verbose_name='Бронирование')), - ], - options={ - 'verbose_name': 'Журнал синхронизации', - 'verbose_name_plural': 'Журналы синхронизации', - }, - ), - ] diff --git a/antifroud/migrations/0011_alter_importedhotel_external_id.py b/antifroud/migrations/0011_alter_importedhotel_external_id.py deleted file mode 100644 index 5e82a772..00000000 --- a/antifroud/migrations/0011_alter_importedhotel_external_id.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-14 06:13 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0010_synclog'), - ] - - operations = [ - migrations.AlterField( - model_name='importedhotel', - name='external_id', - field=models.CharField(max_length=255, verbose_name='Внешний ID отеля'), - ), - ] diff --git a/antifroud/migrations/0012_violationlog.py b/antifroud/migrations/0012_violationlog.py deleted file mode 100644 index 6c681a31..00000000 --- a/antifroud/migrations/0012_violationlog.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-16 23:37 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0011_alter_importedhotel_external_id'), - ('hotels', '0010_alter_hotel_timezone'), - ] - - operations = [ - migrations.CreateModel( - name='ViolationLog', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('room_number', models.CharField(blank=True, max_length=50, null=True, verbose_name='Номер комнаты')), - ('violation_type', models.CharField(choices=[('missed', 'Неявка'), ('early', 'Раннее заселение'), ('late', 'Позднее заселение')], max_length=50, verbose_name='Тип нарушения')), - ('violation_details', models.TextField(blank=True, null=True, verbose_name='Детали нарушения')), - ('detected_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата обнаружения')), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')), - ('hotel', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hotels.hotel', verbose_name='Отель')), - ], - options={ - 'verbose_name': 'Журнал нарушений', - 'verbose_name_plural': 'Журналы нарушений', - }, - ), - ] diff --git a/antifroud/migrations/0013_alter_useractivitylog_timestamp.py b/antifroud/migrations/0013_alter_useractivitylog_timestamp.py deleted file mode 100644 index 190e1b65..00000000 --- a/antifroud/migrations/0013_alter_useractivitylog_timestamp.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-17 03:23 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0012_violationlog'), - ] - - operations = [ - migrations.AlterField( - model_name='useractivitylog', - name='timestamp', - field=models.BigIntegerField(blank=True, null=True, verbose_name='Метка времени'), - ), - ] diff --git a/antifroud/migrations/0014_alter_useractivitylog_uastring_and_more.py b/antifroud/migrations/0014_alter_useractivitylog_uastring_and_more.py deleted file mode 100644 index b3d89e5d..00000000 --- a/antifroud/migrations/0014_alter_useractivitylog_uastring_and_more.py +++ /dev/null @@ -1,68 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-17 03:26 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0013_alter_useractivitylog_timestamp'), - ] - - operations = [ - migrations.AlterField( - model_name='useractivitylog', - name='UAString', - field=models.TextField(blank=True, null=True, verbose_name='User-Agent строка'), - ), - migrations.AlterField( - model_name='useractivitylog', - name='agent', - field=models.TextField(blank=True, null=True, verbose_name='Агент пользователя'), - ), - migrations.AlterField( - model_name='useractivitylog', - name='created', - field=models.DateTimeField(blank=True, null=True, verbose_name='Дата создания'), - ), - migrations.AlterField( - model_name='useractivitylog', - name='date_time', - field=models.DateTimeField(blank=True, null=True, verbose_name='Дата и время'), - ), - migrations.AlterField( - model_name='useractivitylog', - name='hits', - field=models.IntegerField(blank=True, null=True, verbose_name='Количество обращений'), - ), - migrations.AlterField( - model_name='useractivitylog', - name='honeypot', - field=models.BooleanField(blank=True, null=True, verbose_name='Метка honeypot'), - ), - migrations.AlterField( - model_name='useractivitylog', - name='ip', - field=models.GenericIPAddressField(blank=True, null=True, verbose_name='IP-адрес'), - ), - migrations.AlterField( - model_name='useractivitylog', - name='last_counter', - field=models.IntegerField(blank=True, null=True, verbose_name='Последний счетчик'), - ), - migrations.AlterField( - model_name='useractivitylog', - name='reply', - field=models.BooleanField(blank=True, null=True, verbose_name='Ответ пользователя'), - ), - migrations.AlterField( - model_name='useractivitylog', - name='type', - field=models.CharField(blank=True, max_length=50, null=True, verbose_name='Тип'), - ), - migrations.AlterField( - model_name='useractivitylog', - name='user_id', - field=models.BigIntegerField(blank=True, null=True, verbose_name='ID пользователя'), - ), - ] diff --git a/antifroud/migrations/0015_alter_useractivitylog_page_id.py b/antifroud/migrations/0015_alter_useractivitylog_page_id.py deleted file mode 100644 index d857cc43..00000000 --- a/antifroud/migrations/0015_alter_useractivitylog_page_id.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-17 04:49 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0014_alter_useractivitylog_uastring_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='useractivitylog', - name='page_id', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='ID страницы'), - ), - ] diff --git a/antifroud/migrations/0016_alter_useractivitylog_created_and_more.py b/antifroud/migrations/0016_alter_useractivitylog_created_and_more.py deleted file mode 100644 index 3bc93d7d..00000000 --- a/antifroud/migrations/0016_alter_useractivitylog_created_and_more.py +++ /dev/null @@ -1,39 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-17 05:27 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0015_alter_useractivitylog_page_id'), - ] - - operations = [ - migrations.AlterField( - model_name='useractivitylog', - name='created', - field=models.DateTimeField(blank=True, db_index=True, null=True, verbose_name='Дата создания'), - ), - migrations.AlterField( - model_name='useractivitylog', - name='external_id', - field=models.CharField(db_index=True, default=1, max_length=255, unique=True, verbose_name='Внешний ID'), - preserve_default=False, - ), - migrations.AlterField( - model_name='useractivitylog', - name='ip', - field=models.GenericIPAddressField(blank=True, db_index=True, null=True, verbose_name='IP-адрес'), - ), - migrations.AlterField( - model_name='useractivitylog', - name='page_id', - field=models.BigIntegerField(blank=True, db_index=True, null=True, verbose_name='ID страницы'), - ), - migrations.AlterField( - model_name='useractivitylog', - name='user_id', - field=models.BigIntegerField(blank=True, db_index=True, null=True, verbose_name='ID пользователя'), - ), - ] diff --git a/antifroud/migrations/0017_violationlog_hits.py b/antifroud/migrations/0017_violationlog_hits.py deleted file mode 100644 index 42a002ff..00000000 --- a/antifroud/migrations/0017_violationlog_hits.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-18 01:13 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0016_alter_useractivitylog_created_and_more'), - ] - - operations = [ - migrations.AddField( - model_name='violationlog', - name='hits', - field=models.IntegerField(default=1, verbose_name='Срабатывания'), - preserve_default=False, - ), - ] diff --git a/antifroud/migrations/0018_remove_synclog_reservation_alter_synclog_created_and_more.py b/antifroud/migrations/0018_remove_synclog_reservation_alter_synclog_created_and_more.py deleted file mode 100644 index 43660759..00000000 --- a/antifroud/migrations/0018_remove_synclog_reservation_alter_synclog_created_and_more.py +++ /dev/null @@ -1,44 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-18 04:46 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0017_violationlog_hits'), - ('hotels', '0014_alter_room_number'), - ] - - operations = [ - migrations.RemoveField( - model_name='synclog', - name='reservation', - ), - migrations.AlterField( - model_name='synclog', - name='created', - field=models.DateTimeField(auto_now=True, verbose_name='Дата обновления'), - ), - migrations.AlterField( - model_name='synclog', - name='hotel', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hotels.hotel', unique=True, verbose_name='Отель'), - ), - migrations.AlterField( - model_name='synclog', - name='id', - field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), - ), - migrations.AlterField( - model_name='synclog', - name='processed_records', - field=models.IntegerField(default=0, verbose_name='Обработанные записи'), - ), - migrations.AlterField( - model_name='synclog', - name='recieved_records', - field=models.IntegerField(default=0, verbose_name='Полученные записи'), - ), - ] diff --git a/antifroud/migrations/0019_alter_synclog_hotel.py b/antifroud/migrations/0019_alter_synclog_hotel.py deleted file mode 100644 index 2d699b44..00000000 --- a/antifroud/migrations/0019_alter_synclog_hotel.py +++ /dev/null @@ -1,20 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-18 04:55 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0018_remove_synclog_reservation_alter_synclog_created_and_more'), - ('hotels', '0014_alter_room_number'), - ] - - operations = [ - migrations.AlterField( - model_name='synclog', - name='hotel', - field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='hotels.hotel', verbose_name='Отель'), - ), - ] diff --git a/antifroud/migrations/0020_alter_useractivitylog_hits.py b/antifroud/migrations/0020_alter_useractivitylog_hits.py deleted file mode 100644 index 67ad400f..00000000 --- a/antifroud/migrations/0020_alter_useractivitylog_hits.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-18 10:45 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('antifroud', '0019_alter_synclog_hotel'), - ] - - operations = [ - migrations.AlterField( - model_name='useractivitylog', - name='hits', - field=models.IntegerField(blank=True, default='0', null=True, verbose_name='Количество обращений'), - ), - ] diff --git a/app_settings/app_settings.py b/app_settings/app_settings.py index 130acbc8..4a96070e 100644 --- a/app_settings/app_settings.py +++ b/app_settings/app_settings.py @@ -1,50 +1,24 @@ -# settings.py -from decouple import config -from django.conf import settings from django.apps import apps -def load_database_settings(): - # Загружаем настройки из базы данных +def load_database_settings(databases): + """ + Загружает дополнительные базы данных из таблицы LocalDatabase и добавляет их в конфигурацию. + :param databases: Существующий словарь DATABASES + """ LocalDatabase = apps.get_model('app_settings', 'LocalDatabase') - local_db_settings = LocalDatabase.objects.all() - - for db in local_db_settings: - # Пример добавления дополнительной базы данных - settings.DATABASES[db.name] = { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': db.db_name, - 'USER': db.username, - 'PASSWORD': db.password, - 'HOST': db.host, - 'PORT': db.port, - } -# Вызов этой функции при старте проекта, например, в файле wsgi.py -load_database_settings() - -# Чтение локальных баз данных -local_databases = LocalDatabase.objects.filter(is_active=True) - -# Основная база данных -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': config('DB_NAME'), - 'USER': config('DB_USER'), - 'PASSWORD': config('DB_PASSWORD'), - 'HOST': config('DB_HOST'), - 'PORT': config('DB_PORT'), - }, -} - -# Добавление локальных баз данных -for db in local_databases: - DATABASES[db.name] = { - 'ENGINE': 'django.db.backends.postgresql', - 'NAME': db.name, - 'USER': db.user, - 'PASSWORD': db.password, - 'HOST': db.host, - 'PORT': db.port, - } + try: + local_db_settings = LocalDatabase.objects.filter(is_active=True) + for db in local_db_settings: + databases[db.name] = { + 'ENGINE': db.engine, # Можно хранить тип движка в базе + 'NAME': db.database, + 'USER': db.user, + 'PASSWORD': db.password, + 'HOST': db.host, + 'PORT': db.port, + 'ATOMIC_REQUESTS': True, # Убедитесь, что добавляете ATOMIC_REQUESTS + } + except Exception as e: + print(f"Ошибка загрузки локальных баз данных: {e}") diff --git a/app_settings/migrations/0001_initial.py b/app_settings/migrations/0001_initial.py deleted file mode 100644 index 4cc957fb..00000000 --- a/app_settings/migrations/0001_initial.py +++ /dev/null @@ -1,86 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-23 00:57 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='EmailSettings', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('smtp_server', models.CharField(help_text='SMTP сервер для отправки почты', max_length=255)), - ('smtp_port', models.IntegerField(default=587, help_text='SMTP порт для почты')), - ('smtp_user', models.CharField(help_text='Имя пользователя для SMTP', max_length=255)), - ('smtp_password', models.CharField(help_text='Пароль для SMTP', max_length=255)), - ('from_email', models.EmailField(help_text='Email для отправки сообщений', max_length=254)), - ], - options={ - 'verbose_name': 'E-mail', - 'verbose_name_plural': 'E-mails', - }, - ), - migrations.CreateModel( - name='GlobalHotelSettings', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('check_in_time', models.TimeField(help_text='Время заезда')), - ('check_out_time', models.TimeField(help_text='Время выезда')), - ('currency', models.CharField(help_text='Валюта', max_length=3)), - ('global_timezone', models.CharField(choices=[('Africa/Abidjan', 'Africa/Abidjan'), ('Africa/Accra', 'Africa/Accra'), ('Africa/Addis_Ababa', 'Africa/Addis_Ababa'), ('Africa/Algiers', 'Africa/Algiers'), ('Africa/Asmara', 'Africa/Asmara'), ('Africa/Asmera', 'Africa/Asmera'), ('Africa/Bamako', 'Africa/Bamako'), ('Africa/Bangui', 'Africa/Bangui'), ('Africa/Banjul', 'Africa/Banjul'), ('Africa/Bissau', 'Africa/Bissau'), ('Africa/Blantyre', 'Africa/Blantyre'), ('Africa/Brazzaville', 'Africa/Brazzaville'), ('Africa/Bujumbura', 'Africa/Bujumbura'), ('Africa/Cairo', 'Africa/Cairo'), ('Africa/Casablanca', 'Africa/Casablanca'), ('Africa/Ceuta', 'Africa/Ceuta'), ('Africa/Conakry', 'Africa/Conakry'), ('Africa/Dakar', 'Africa/Dakar'), ('Africa/Dar_es_Salaam', 'Africa/Dar_es_Salaam'), ('Africa/Djibouti', 'Africa/Djibouti'), ('Africa/Douala', 'Africa/Douala'), ('Africa/El_Aaiun', 'Africa/El_Aaiun'), ('Africa/Freetown', 'Africa/Freetown'), ('Africa/Gaborone', 'Africa/Gaborone'), ('Africa/Harare', 'Africa/Harare'), ('Africa/Johannesburg', 'Africa/Johannesburg'), ('Africa/Juba', 'Africa/Juba'), ('Africa/Kampala', 'Africa/Kampala'), ('Africa/Khartoum', 'Africa/Khartoum'), ('Africa/Kigali', 'Africa/Kigali'), ('Africa/Kinshasa', 'Africa/Kinshasa'), ('Africa/Lagos', 'Africa/Lagos'), ('Africa/Libreville', 'Africa/Libreville'), ('Africa/Lome', 'Africa/Lome'), ('Africa/Luanda', 'Africa/Luanda'), ('Africa/Lubumbashi', 'Africa/Lubumbashi'), ('Africa/Lusaka', 'Africa/Lusaka'), ('Africa/Malabo', 'Africa/Malabo'), ('Africa/Maputo', 'Africa/Maputo'), ('Africa/Maseru', 'Africa/Maseru'), ('Africa/Mbabane', 'Africa/Mbabane'), ('Africa/Mogadishu', 'Africa/Mogadishu'), ('Africa/Monrovia', 'Africa/Monrovia'), ('Africa/Nairobi', 'Africa/Nairobi'), ('Africa/Ndjamena', 'Africa/Ndjamena'), ('Africa/Niamey', 'Africa/Niamey'), ('Africa/Nouakchott', 'Africa/Nouakchott'), ('Africa/Ouagadougou', 'Africa/Ouagadougou'), ('Africa/Porto-Novo', 'Africa/Porto-Novo'), ('Africa/Sao_Tome', 'Africa/Sao_Tome'), ('Africa/Timbuktu', 'Africa/Timbuktu'), ('Africa/Tripoli', 'Africa/Tripoli'), ('Africa/Tunis', 'Africa/Tunis'), ('Africa/Windhoek', 'Africa/Windhoek'), ('America/Adak', 'America/Adak'), ('America/Anchorage', 'America/Anchorage'), ('America/Anguilla', 'America/Anguilla'), ('America/Antigua', 'America/Antigua'), ('America/Araguaina', 'America/Araguaina'), ('America/Argentina/Buenos_Aires', 'America/Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'America/Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'America/Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'America/Argentina/Cordoba'), ('America/Argentina/Jujuy', 'America/Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'America/Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'America/Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'America/Argentina/Salta'), ('America/Argentina/San_Juan', 'America/Argentina/San_Juan'), ('America/Argentina/San_Luis', 'America/Argentina/San_Luis'), ('America/Argentina/Tucuman', 'America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia'), ('America/Aruba', 'America/Aruba'), ('America/Asuncion', 'America/Asuncion'), ('America/Atikokan', 'America/Atikokan'), ('America/Atka', 'America/Atka'), ('America/Bahia', 'America/Bahia'), ('America/Bahia_Banderas', 'America/Bahia_Banderas'), ('America/Barbados', 'America/Barbados'), ('America/Belem', 'America/Belem'), ('America/Belize', 'America/Belize'), ('America/Blanc-Sablon', 'America/Blanc-Sablon'), ('America/Boa_Vista', 'America/Boa_Vista'), ('America/Bogota', 'America/Bogota'), ('America/Boise', 'America/Boise'), ('America/Buenos_Aires', 'America/Buenos_Aires'), ('America/Cambridge_Bay', 'America/Cambridge_Bay'), ('America/Campo_Grande', 'America/Campo_Grande'), ('America/Cancun', 'America/Cancun'), ('America/Caracas', 'America/Caracas'), ('America/Catamarca', 'America/Catamarca'), ('America/Cayenne', 'America/Cayenne'), ('America/Cayman', 'America/Cayman'), ('America/Chicago', 'America/Chicago'), ('America/Chihuahua', 'America/Chihuahua'), ('America/Ciudad_Juarez', 'America/Ciudad_Juarez'), ('America/Coral_Harbour', 'America/Coral_Harbour'), ('America/Cordoba', 'America/Cordoba'), ('America/Costa_Rica', 'America/Costa_Rica'), ('America/Creston', 'America/Creston'), ('America/Cuiaba', 'America/Cuiaba'), ('America/Curacao', 'America/Curacao'), ('America/Danmarkshavn', 'America/Danmarkshavn'), ('America/Dawson', 'America/Dawson'), ('America/Dawson_Creek', 'America/Dawson_Creek'), ('America/Denver', 'America/Denver'), ('America/Detroit', 'America/Detroit'), ('America/Dominica', 'America/Dominica'), ('America/Edmonton', 'America/Edmonton'), ('America/Eirunepe', 'America/Eirunepe'), ('America/El_Salvador', 'America/El_Salvador'), ('America/Ensenada', 'America/Ensenada'), ('America/Fort_Nelson', 'America/Fort_Nelson'), ('America/Fort_Wayne', 'America/Fort_Wayne'), ('America/Fortaleza', 'America/Fortaleza'), ('America/Glace_Bay', 'America/Glace_Bay'), ('America/Godthab', 'America/Godthab'), ('America/Goose_Bay', 'America/Goose_Bay'), ('America/Grand_Turk', 'America/Grand_Turk'), ('America/Grenada', 'America/Grenada'), ('America/Guadeloupe', 'America/Guadeloupe'), ('America/Guatemala', 'America/Guatemala'), ('America/Guayaquil', 'America/Guayaquil'), ('America/Guyana', 'America/Guyana'), ('America/Halifax', 'America/Halifax'), ('America/Havana', 'America/Havana'), ('America/Hermosillo', 'America/Hermosillo'), ('America/Indiana/Indianapolis', 'America/Indiana/Indianapolis'), ('America/Indiana/Knox', 'America/Indiana/Knox'), ('America/Indiana/Marengo', 'America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'America/Indiana/Tell_City'), ('America/Indiana/Vevay', 'America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'America/Indiana/Winamac'), ('America/Indianapolis', 'America/Indianapolis'), ('America/Inuvik', 'America/Inuvik'), ('America/Iqaluit', 'America/Iqaluit'), ('America/Jamaica', 'America/Jamaica'), ('America/Jujuy', 'America/Jujuy'), ('America/Juneau', 'America/Juneau'), ('America/Kentucky/Louisville', 'America/Kentucky/Louisville'), ('America/Kentucky/Monticello', 'America/Kentucky/Monticello'), ('America/Knox_IN', 'America/Knox_IN'), ('America/Kralendijk', 'America/Kralendijk'), ('America/La_Paz', 'America/La_Paz'), ('America/Lima', 'America/Lima'), ('America/Los_Angeles', 'America/Los_Angeles'), ('America/Louisville', 'America/Louisville'), ('America/Lower_Princes', 'America/Lower_Princes'), ('America/Maceio', 'America/Maceio'), ('America/Managua', 'America/Managua'), ('America/Manaus', 'America/Manaus'), ('America/Marigot', 'America/Marigot'), ('America/Martinique', 'America/Martinique'), ('America/Matamoros', 'America/Matamoros'), ('America/Mazatlan', 'America/Mazatlan'), ('America/Mendoza', 'America/Mendoza'), ('America/Menominee', 'America/Menominee'), ('America/Merida', 'America/Merida'), ('America/Metlakatla', 'America/Metlakatla'), ('America/Mexico_City', 'America/Mexico_City'), ('America/Miquelon', 'America/Miquelon'), ('America/Moncton', 'America/Moncton'), ('America/Monterrey', 'America/Monterrey'), ('America/Montevideo', 'America/Montevideo'), ('America/Montreal', 'America/Montreal'), ('America/Montserrat', 'America/Montserrat'), ('America/Nassau', 'America/Nassau'), ('America/New_York', 'America/New_York'), ('America/Nipigon', 'America/Nipigon'), ('America/Nome', 'America/Nome'), ('America/Noronha', 'America/Noronha'), ('America/North_Dakota/Beulah', 'America/North_Dakota/Beulah'), ('America/North_Dakota/Center', 'America/North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'America/North_Dakota/New_Salem'), ('America/Nuuk', 'America/Nuuk'), ('America/Ojinaga', 'America/Ojinaga'), ('America/Panama', 'America/Panama'), ('America/Pangnirtung', 'America/Pangnirtung'), ('America/Paramaribo', 'America/Paramaribo'), ('America/Phoenix', 'America/Phoenix'), ('America/Port-au-Prince', 'America/Port-au-Prince'), ('America/Port_of_Spain', 'America/Port_of_Spain'), ('America/Porto_Acre', 'America/Porto_Acre'), ('America/Porto_Velho', 'America/Porto_Velho'), ('America/Puerto_Rico', 'America/Puerto_Rico'), ('America/Punta_Arenas', 'America/Punta_Arenas'), ('America/Rainy_River', 'America/Rainy_River'), ('America/Rankin_Inlet', 'America/Rankin_Inlet'), ('America/Recife', 'America/Recife'), ('America/Regina', 'America/Regina'), ('America/Resolute', 'America/Resolute'), ('America/Rio_Branco', 'America/Rio_Branco'), ('America/Rosario', 'America/Rosario'), ('America/Santa_Isabel', 'America/Santa_Isabel'), ('America/Santarem', 'America/Santarem'), ('America/Santiago', 'America/Santiago'), ('America/Santo_Domingo', 'America/Santo_Domingo'), ('America/Sao_Paulo', 'America/Sao_Paulo'), ('America/Scoresbysund', 'America/Scoresbysund'), ('America/Shiprock', 'America/Shiprock'), ('America/Sitka', 'America/Sitka'), ('America/St_Barthelemy', 'America/St_Barthelemy'), ('America/St_Johns', 'America/St_Johns'), ('America/St_Kitts', 'America/St_Kitts'), ('America/St_Lucia', 'America/St_Lucia'), ('America/St_Thomas', 'America/St_Thomas'), ('America/St_Vincent', 'America/St_Vincent'), ('America/Swift_Current', 'America/Swift_Current'), ('America/Tegucigalpa', 'America/Tegucigalpa'), ('America/Thule', 'America/Thule'), ('America/Thunder_Bay', 'America/Thunder_Bay'), ('America/Tijuana', 'America/Tijuana'), ('America/Toronto', 'America/Toronto'), ('America/Tortola', 'America/Tortola'), ('America/Vancouver', 'America/Vancouver'), ('America/Virgin', 'America/Virgin'), ('America/Whitehorse', 'America/Whitehorse'), ('America/Winnipeg', 'America/Winnipeg'), ('America/Yakutat', 'America/Yakutat'), ('America/Yellowknife', 'America/Yellowknife'), ('Antarctica/Casey', 'Antarctica/Casey'), ('Antarctica/Davis', 'Antarctica/Davis'), ('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville'), ('Antarctica/Macquarie', 'Antarctica/Macquarie'), ('Antarctica/Mawson', 'Antarctica/Mawson'), ('Antarctica/McMurdo', 'Antarctica/McMurdo'), ('Antarctica/Palmer', 'Antarctica/Palmer'), ('Antarctica/Rothera', 'Antarctica/Rothera'), ('Antarctica/South_Pole', 'Antarctica/South_Pole'), ('Antarctica/Syowa', 'Antarctica/Syowa'), ('Antarctica/Troll', 'Antarctica/Troll'), ('Antarctica/Vostok', 'Antarctica/Vostok'), ('Arctic/Longyearbyen', 'Arctic/Longyearbyen'), ('Asia/Aden', 'Asia/Aden'), ('Asia/Almaty', 'Asia/Almaty'), ('Asia/Amman', 'Asia/Amman'), ('Asia/Anadyr', 'Asia/Anadyr'), ('Asia/Aqtau', 'Asia/Aqtau'), ('Asia/Aqtobe', 'Asia/Aqtobe'), ('Asia/Ashgabat', 'Asia/Ashgabat'), ('Asia/Ashkhabad', 'Asia/Ashkhabad'), ('Asia/Atyrau', 'Asia/Atyrau'), ('Asia/Baghdad', 'Asia/Baghdad'), ('Asia/Bahrain', 'Asia/Bahrain'), ('Asia/Baku', 'Asia/Baku'), ('Asia/Bangkok', 'Asia/Bangkok'), ('Asia/Barnaul', 'Asia/Barnaul'), ('Asia/Beirut', 'Asia/Beirut'), ('Asia/Bishkek', 'Asia/Bishkek'), ('Asia/Brunei', 'Asia/Brunei'), ('Asia/Calcutta', 'Asia/Calcutta'), ('Asia/Chita', 'Asia/Chita'), ('Asia/Choibalsan', 'Asia/Choibalsan'), ('Asia/Chongqing', 'Asia/Chongqing'), ('Asia/Chungking', 'Asia/Chungking'), ('Asia/Colombo', 'Asia/Colombo'), ('Asia/Dacca', 'Asia/Dacca'), ('Asia/Damascus', 'Asia/Damascus'), ('Asia/Dhaka', 'Asia/Dhaka'), ('Asia/Dili', 'Asia/Dili'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Dushanbe', 'Asia/Dushanbe'), ('Asia/Famagusta', 'Asia/Famagusta'), ('Asia/Gaza', 'Asia/Gaza'), ('Asia/Harbin', 'Asia/Harbin'), ('Asia/Hebron', 'Asia/Hebron'), ('Asia/Ho_Chi_Minh', 'Asia/Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Asia/Hong_Kong'), ('Asia/Hovd', 'Asia/Hovd'), ('Asia/Irkutsk', 'Asia/Irkutsk'), ('Asia/Istanbul', 'Asia/Istanbul'), ('Asia/Jakarta', 'Asia/Jakarta'), ('Asia/Jayapura', 'Asia/Jayapura'), ('Asia/Jerusalem', 'Asia/Jerusalem'), ('Asia/Kabul', 'Asia/Kabul'), ('Asia/Kamchatka', 'Asia/Kamchatka'), ('Asia/Karachi', 'Asia/Karachi'), ('Asia/Kashgar', 'Asia/Kashgar'), ('Asia/Kathmandu', 'Asia/Kathmandu'), ('Asia/Katmandu', 'Asia/Katmandu'), ('Asia/Khandyga', 'Asia/Khandyga'), ('Asia/Kolkata', 'Asia/Kolkata'), ('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Asia/Kuala_Lumpur'), ('Asia/Kuching', 'Asia/Kuching'), ('Asia/Kuwait', 'Asia/Kuwait'), ('Asia/Macao', 'Asia/Macao'), ('Asia/Macau', 'Asia/Macau'), ('Asia/Magadan', 'Asia/Magadan'), ('Asia/Makassar', 'Asia/Makassar'), ('Asia/Manila', 'Asia/Manila'), ('Asia/Muscat', 'Asia/Muscat'), ('Asia/Nicosia', 'Asia/Nicosia'), ('Asia/Novokuznetsk', 'Asia/Novokuznetsk'), ('Asia/Novosibirsk', 'Asia/Novosibirsk'), ('Asia/Omsk', 'Asia/Omsk'), ('Asia/Oral', 'Asia/Oral'), ('Asia/Phnom_Penh', 'Asia/Phnom_Penh'), ('Asia/Pontianak', 'Asia/Pontianak'), ('Asia/Pyongyang', 'Asia/Pyongyang'), ('Asia/Qatar', 'Asia/Qatar'), ('Asia/Qostanay', 'Asia/Qostanay'), ('Asia/Qyzylorda', 'Asia/Qyzylorda'), ('Asia/Rangoon', 'Asia/Rangoon'), ('Asia/Riyadh', 'Asia/Riyadh'), ('Asia/Saigon', 'Asia/Saigon'), ('Asia/Sakhalin', 'Asia/Sakhalin'), ('Asia/Samarkand', 'Asia/Samarkand'), ('Asia/Seoul', 'Asia/Seoul'), ('Asia/Shanghai', 'Asia/Shanghai'), ('Asia/Singapore', 'Asia/Singapore'), ('Asia/Srednekolymsk', 'Asia/Srednekolymsk'), ('Asia/Taipei', 'Asia/Taipei'), ('Asia/Tashkent', 'Asia/Tashkent'), ('Asia/Tbilisi', 'Asia/Tbilisi'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Tel_Aviv', 'Asia/Tel_Aviv'), ('Asia/Thimbu', 'Asia/Thimbu'), ('Asia/Thimphu', 'Asia/Thimphu'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Asia/Tomsk', 'Asia/Tomsk'), ('Asia/Ujung_Pandang', 'Asia/Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar'), ('Asia/Ulan_Bator', 'Asia/Ulan_Bator'), ('Asia/Urumqi', 'Asia/Urumqi'), ('Asia/Ust-Nera', 'Asia/Ust-Nera'), ('Asia/Vientiane', 'Asia/Vientiane'), ('Asia/Vladivostok', 'Asia/Vladivostok'), ('Asia/Yakutsk', 'Asia/Yakutsk'), ('Asia/Yangon', 'Asia/Yangon'), ('Asia/Yekaterinburg', 'Asia/Yekaterinburg'), ('Asia/Yerevan', 'Asia/Yerevan'), ('Atlantic/Azores', 'Atlantic/Azores'), ('Atlantic/Bermuda', 'Atlantic/Bermuda'), ('Atlantic/Canary', 'Atlantic/Canary'), ('Atlantic/Cape_Verde', 'Atlantic/Cape_Verde'), ('Atlantic/Faeroe', 'Atlantic/Faeroe'), ('Atlantic/Faroe', 'Atlantic/Faroe'), ('Atlantic/Jan_Mayen', 'Atlantic/Jan_Mayen'), ('Atlantic/Madeira', 'Atlantic/Madeira'), ('Atlantic/Reykjavik', 'Atlantic/Reykjavik'), ('Atlantic/South_Georgia', 'Atlantic/South_Georgia'), ('Atlantic/St_Helena', 'Atlantic/St_Helena'), ('Atlantic/Stanley', 'Atlantic/Stanley'), ('Australia/ACT', 'Australia/ACT'), ('Australia/Adelaide', 'Australia/Adelaide'), ('Australia/Brisbane', 'Australia/Brisbane'), ('Australia/Broken_Hill', 'Australia/Broken_Hill'), ('Australia/Canberra', 'Australia/Canberra'), ('Australia/Currie', 'Australia/Currie'), ('Australia/Darwin', 'Australia/Darwin'), ('Australia/Eucla', 'Australia/Eucla'), ('Australia/Hobart', 'Australia/Hobart'), ('Australia/LHI', 'Australia/LHI'), ('Australia/Lindeman', 'Australia/Lindeman'), ('Australia/Lord_Howe', 'Australia/Lord_Howe'), ('Australia/Melbourne', 'Australia/Melbourne'), ('Australia/NSW', 'Australia/NSW'), ('Australia/North', 'Australia/North'), ('Australia/Perth', 'Australia/Perth'), ('Australia/Queensland', 'Australia/Queensland'), ('Australia/South', 'Australia/South'), ('Australia/Sydney', 'Australia/Sydney'), ('Australia/Tasmania', 'Australia/Tasmania'), ('Australia/Victoria', 'Australia/Victoria'), ('Australia/West', 'Australia/West'), ('Australia/Yancowinna', 'Australia/Yancowinna'), ('Brazil/Acre', 'Brazil/Acre'), ('Brazil/DeNoronha', 'Brazil/DeNoronha'), ('Brazil/East', 'Brazil/East'), ('Brazil/West', 'Brazil/West'), ('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Canada/Atlantic', 'Canada/Atlantic'), ('Canada/Central', 'Canada/Central'), ('Canada/Eastern', 'Canada/Eastern'), ('Canada/Mountain', 'Canada/Mountain'), ('Canada/Newfoundland', 'Canada/Newfoundland'), ('Canada/Pacific', 'Canada/Pacific'), ('Canada/Saskatchewan', 'Canada/Saskatchewan'), ('Canada/Yukon', 'Canada/Yukon'), ('Chile/Continental', 'Chile/Continental'), ('Chile/EasterIsland', 'Chile/EasterIsland'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('Etc/GMT', 'Etc/GMT'), ('Etc/GMT+0', 'Etc/GMT+0'), ('Etc/GMT+1', 'Etc/GMT+1'), ('Etc/GMT+10', 'Etc/GMT+10'), ('Etc/GMT+11', 'Etc/GMT+11'), ('Etc/GMT+12', 'Etc/GMT+12'), ('Etc/GMT+2', 'Etc/GMT+2'), ('Etc/GMT+3', 'Etc/GMT+3'), ('Etc/GMT+4', 'Etc/GMT+4'), ('Etc/GMT+5', 'Etc/GMT+5'), ('Etc/GMT+6', 'Etc/GMT+6'), ('Etc/GMT+7', 'Etc/GMT+7'), ('Etc/GMT+8', 'Etc/GMT+8'), ('Etc/GMT+9', 'Etc/GMT+9'), ('Etc/GMT-0', 'Etc/GMT-0'), ('Etc/GMT-1', 'Etc/GMT-1'), ('Etc/GMT-10', 'Etc/GMT-10'), ('Etc/GMT-11', 'Etc/GMT-11'), ('Etc/GMT-12', 'Etc/GMT-12'), ('Etc/GMT-13', 'Etc/GMT-13'), ('Etc/GMT-14', 'Etc/GMT-14'), ('Etc/GMT-2', 'Etc/GMT-2'), ('Etc/GMT-3', 'Etc/GMT-3'), ('Etc/GMT-4', 'Etc/GMT-4'), ('Etc/GMT-5', 'Etc/GMT-5'), ('Etc/GMT-6', 'Etc/GMT-6'), ('Etc/GMT-7', 'Etc/GMT-7'), ('Etc/GMT-8', 'Etc/GMT-8'), ('Etc/GMT-9', 'Etc/GMT-9'), ('Etc/GMT0', 'Etc/GMT0'), ('Etc/Greenwich', 'Etc/Greenwich'), ('Etc/UCT', 'Etc/UCT'), ('Etc/UTC', 'Etc/UTC'), ('Etc/Universal', 'Etc/Universal'), ('Etc/Zulu', 'Etc/Zulu'), ('Europe/Amsterdam', 'Europe/Amsterdam'), ('Europe/Andorra', 'Europe/Andorra'), ('Europe/Astrakhan', 'Europe/Astrakhan'), ('Europe/Athens', 'Europe/Athens'), ('Europe/Belfast', 'Europe/Belfast'), ('Europe/Belgrade', 'Europe/Belgrade'), ('Europe/Berlin', 'Europe/Berlin'), ('Europe/Bratislava', 'Europe/Bratislava'), ('Europe/Brussels', 'Europe/Brussels'), ('Europe/Bucharest', 'Europe/Bucharest'), ('Europe/Budapest', 'Europe/Budapest'), ('Europe/Busingen', 'Europe/Busingen'), ('Europe/Chisinau', 'Europe/Chisinau'), ('Europe/Copenhagen', 'Europe/Copenhagen'), ('Europe/Dublin', 'Europe/Dublin'), ('Europe/Gibraltar', 'Europe/Gibraltar'), ('Europe/Guernsey', 'Europe/Guernsey'), ('Europe/Helsinki', 'Europe/Helsinki'), ('Europe/Isle_of_Man', 'Europe/Isle_of_Man'), ('Europe/Istanbul', 'Europe/Istanbul'), ('Europe/Jersey', 'Europe/Jersey'), ('Europe/Kaliningrad', 'Europe/Kaliningrad'), ('Europe/Kiev', 'Europe/Kiev'), ('Europe/Kirov', 'Europe/Kirov'), ('Europe/Kyiv', 'Europe/Kyiv'), ('Europe/Lisbon', 'Europe/Lisbon'), ('Europe/Ljubljana', 'Europe/Ljubljana'), ('Europe/London', 'Europe/London'), ('Europe/Luxembourg', 'Europe/Luxembourg'), ('Europe/Madrid', 'Europe/Madrid'), ('Europe/Malta', 'Europe/Malta'), ('Europe/Mariehamn', 'Europe/Mariehamn'), ('Europe/Minsk', 'Europe/Minsk'), ('Europe/Monaco', 'Europe/Monaco'), ('Europe/Moscow', 'Europe/Moscow'), ('Europe/Nicosia', 'Europe/Nicosia'), ('Europe/Oslo', 'Europe/Oslo'), ('Europe/Paris', 'Europe/Paris'), ('Europe/Podgorica', 'Europe/Podgorica'), ('Europe/Prague', 'Europe/Prague'), ('Europe/Riga', 'Europe/Riga'), ('Europe/Rome', 'Europe/Rome'), ('Europe/Samara', 'Europe/Samara'), ('Europe/San_Marino', 'Europe/San_Marino'), ('Europe/Sarajevo', 'Europe/Sarajevo'), ('Europe/Saratov', 'Europe/Saratov'), ('Europe/Simferopol', 'Europe/Simferopol'), ('Europe/Skopje', 'Europe/Skopje'), ('Europe/Sofia', 'Europe/Sofia'), ('Europe/Stockholm', 'Europe/Stockholm'), ('Europe/Tallinn', 'Europe/Tallinn'), ('Europe/Tirane', 'Europe/Tirane'), ('Europe/Tiraspol', 'Europe/Tiraspol'), ('Europe/Ulyanovsk', 'Europe/Ulyanovsk'), ('Europe/Uzhgorod', 'Europe/Uzhgorod'), ('Europe/Vaduz', 'Europe/Vaduz'), ('Europe/Vatican', 'Europe/Vatican'), ('Europe/Vienna', 'Europe/Vienna'), ('Europe/Vilnius', 'Europe/Vilnius'), ('Europe/Volgograd', 'Europe/Volgograd'), ('Europe/Warsaw', 'Europe/Warsaw'), ('Europe/Zagreb', 'Europe/Zagreb'), ('Europe/Zaporozhye', 'Europe/Zaporozhye'), ('Europe/Zurich', 'Europe/Zurich'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('GMT', 'GMT'), ('GMT+0', 'GMT+0'), ('GMT-0', 'GMT-0'), ('GMT0', 'GMT0'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Indian/Antananarivo', 'Indian/Antananarivo'), ('Indian/Chagos', 'Indian/Chagos'), ('Indian/Christmas', 'Indian/Christmas'), ('Indian/Cocos', 'Indian/Cocos'), ('Indian/Comoro', 'Indian/Comoro'), ('Indian/Kerguelen', 'Indian/Kerguelen'), ('Indian/Mahe', 'Indian/Mahe'), ('Indian/Maldives', 'Indian/Maldives'), ('Indian/Mauritius', 'Indian/Mauritius'), ('Indian/Mayotte', 'Indian/Mayotte'), ('Indian/Reunion', 'Indian/Reunion'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('Mexico/BajaNorte', 'Mexico/BajaNorte'), ('Mexico/BajaSur', 'Mexico/BajaSur'), ('Mexico/General', 'Mexico/General'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Pacific/Apia', 'Pacific/Apia'), ('Pacific/Auckland', 'Pacific/Auckland'), ('Pacific/Bougainville', 'Pacific/Bougainville'), ('Pacific/Chatham', 'Pacific/Chatham'), ('Pacific/Chuuk', 'Pacific/Chuuk'), ('Pacific/Easter', 'Pacific/Easter'), ('Pacific/Efate', 'Pacific/Efate'), ('Pacific/Enderbury', 'Pacific/Enderbury'), ('Pacific/Fakaofo', 'Pacific/Fakaofo'), ('Pacific/Fiji', 'Pacific/Fiji'), ('Pacific/Funafuti', 'Pacific/Funafuti'), ('Pacific/Galapagos', 'Pacific/Galapagos'), ('Pacific/Gambier', 'Pacific/Gambier'), ('Pacific/Guadalcanal', 'Pacific/Guadalcanal'), ('Pacific/Guam', 'Pacific/Guam'), ('Pacific/Honolulu', 'Pacific/Honolulu'), ('Pacific/Johnston', 'Pacific/Johnston'), ('Pacific/Kanton', 'Pacific/Kanton'), ('Pacific/Kiritimati', 'Pacific/Kiritimati'), ('Pacific/Kosrae', 'Pacific/Kosrae'), ('Pacific/Kwajalein', 'Pacific/Kwajalein'), ('Pacific/Majuro', 'Pacific/Majuro'), ('Pacific/Marquesas', 'Pacific/Marquesas'), ('Pacific/Midway', 'Pacific/Midway'), ('Pacific/Nauru', 'Pacific/Nauru'), ('Pacific/Niue', 'Pacific/Niue'), ('Pacific/Norfolk', 'Pacific/Norfolk'), ('Pacific/Noumea', 'Pacific/Noumea'), ('Pacific/Pago_Pago', 'Pacific/Pago_Pago'), ('Pacific/Palau', 'Pacific/Palau'), ('Pacific/Pitcairn', 'Pacific/Pitcairn'), ('Pacific/Pohnpei', 'Pacific/Pohnpei'), ('Pacific/Ponape', 'Pacific/Ponape'), ('Pacific/Port_Moresby', 'Pacific/Port_Moresby'), ('Pacific/Rarotonga', 'Pacific/Rarotonga'), ('Pacific/Saipan', 'Pacific/Saipan'), ('Pacific/Samoa', 'Pacific/Samoa'), ('Pacific/Tahiti', 'Pacific/Tahiti'), ('Pacific/Tarawa', 'Pacific/Tarawa'), ('Pacific/Tongatapu', 'Pacific/Tongatapu'), ('Pacific/Truk', 'Pacific/Truk'), ('Pacific/Wake', 'Pacific/Wake'), ('Pacific/Wallis', 'Pacific/Wallis'), ('Pacific/Yap', 'Pacific/Yap'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('US/Alaska', 'US/Alaska'), ('US/Aleutian', 'US/Aleutian'), ('US/Arizona', 'US/Arizona'), ('US/Central', 'US/Central'), ('US/East-Indiana', 'US/East-Indiana'), ('US/Eastern', 'US/Eastern'), ('US/Hawaii', 'US/Hawaii'), ('US/Indiana-Starke', 'US/Indiana-Starke'), ('US/Michigan', 'US/Michigan'), ('US/Mountain', 'US/Mountain'), ('US/Pacific', 'US/Pacific'), ('US/Samoa', 'US/Samoa'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')], default='UTC', max_length=63)), - ], - options={ - 'verbose_name': 'Настройки отеля', - 'verbose_name_plural': 'Настройки отеля', - }, - ), - migrations.CreateModel( - name='GlobalSystemSettings', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('system_name', models.CharField(help_text='Название системы', max_length=255)), - ('system_version', models.CharField(help_text='Версия системы', max_length=255)), - ('server_timezone', models.CharField(choices=[('Africa/Abidjan', 'Africa/Abidjan'), ('Africa/Accra', 'Africa/Accra'), ('Africa/Addis_Ababa', 'Africa/Addis_Ababa'), ('Africa/Algiers', 'Africa/Algiers'), ('Africa/Asmara', 'Africa/Asmara'), ('Africa/Asmera', 'Africa/Asmera'), ('Africa/Bamako', 'Africa/Bamako'), ('Africa/Bangui', 'Africa/Bangui'), ('Africa/Banjul', 'Africa/Banjul'), ('Africa/Bissau', 'Africa/Bissau'), ('Africa/Blantyre', 'Africa/Blantyre'), ('Africa/Brazzaville', 'Africa/Brazzaville'), ('Africa/Bujumbura', 'Africa/Bujumbura'), ('Africa/Cairo', 'Africa/Cairo'), ('Africa/Casablanca', 'Africa/Casablanca'), ('Africa/Ceuta', 'Africa/Ceuta'), ('Africa/Conakry', 'Africa/Conakry'), ('Africa/Dakar', 'Africa/Dakar'), ('Africa/Dar_es_Salaam', 'Africa/Dar_es_Salaam'), ('Africa/Djibouti', 'Africa/Djibouti'), ('Africa/Douala', 'Africa/Douala'), ('Africa/El_Aaiun', 'Africa/El_Aaiun'), ('Africa/Freetown', 'Africa/Freetown'), ('Africa/Gaborone', 'Africa/Gaborone'), ('Africa/Harare', 'Africa/Harare'), ('Africa/Johannesburg', 'Africa/Johannesburg'), ('Africa/Juba', 'Africa/Juba'), ('Africa/Kampala', 'Africa/Kampala'), ('Africa/Khartoum', 'Africa/Khartoum'), ('Africa/Kigali', 'Africa/Kigali'), ('Africa/Kinshasa', 'Africa/Kinshasa'), ('Africa/Lagos', 'Africa/Lagos'), ('Africa/Libreville', 'Africa/Libreville'), ('Africa/Lome', 'Africa/Lome'), ('Africa/Luanda', 'Africa/Luanda'), ('Africa/Lubumbashi', 'Africa/Lubumbashi'), ('Africa/Lusaka', 'Africa/Lusaka'), ('Africa/Malabo', 'Africa/Malabo'), ('Africa/Maputo', 'Africa/Maputo'), ('Africa/Maseru', 'Africa/Maseru'), ('Africa/Mbabane', 'Africa/Mbabane'), ('Africa/Mogadishu', 'Africa/Mogadishu'), ('Africa/Monrovia', 'Africa/Monrovia'), ('Africa/Nairobi', 'Africa/Nairobi'), ('Africa/Ndjamena', 'Africa/Ndjamena'), ('Africa/Niamey', 'Africa/Niamey'), ('Africa/Nouakchott', 'Africa/Nouakchott'), ('Africa/Ouagadougou', 'Africa/Ouagadougou'), ('Africa/Porto-Novo', 'Africa/Porto-Novo'), ('Africa/Sao_Tome', 'Africa/Sao_Tome'), ('Africa/Timbuktu', 'Africa/Timbuktu'), ('Africa/Tripoli', 'Africa/Tripoli'), ('Africa/Tunis', 'Africa/Tunis'), ('Africa/Windhoek', 'Africa/Windhoek'), ('America/Adak', 'America/Adak'), ('America/Anchorage', 'America/Anchorage'), ('America/Anguilla', 'America/Anguilla'), ('America/Antigua', 'America/Antigua'), ('America/Araguaina', 'America/Araguaina'), ('America/Argentina/Buenos_Aires', 'America/Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'America/Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'America/Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'America/Argentina/Cordoba'), ('America/Argentina/Jujuy', 'America/Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'America/Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'America/Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'America/Argentina/Salta'), ('America/Argentina/San_Juan', 'America/Argentina/San_Juan'), ('America/Argentina/San_Luis', 'America/Argentina/San_Luis'), ('America/Argentina/Tucuman', 'America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia'), ('America/Aruba', 'America/Aruba'), ('America/Asuncion', 'America/Asuncion'), ('America/Atikokan', 'America/Atikokan'), ('America/Atka', 'America/Atka'), ('America/Bahia', 'America/Bahia'), ('America/Bahia_Banderas', 'America/Bahia_Banderas'), ('America/Barbados', 'America/Barbados'), ('America/Belem', 'America/Belem'), ('America/Belize', 'America/Belize'), ('America/Blanc-Sablon', 'America/Blanc-Sablon'), ('America/Boa_Vista', 'America/Boa_Vista'), ('America/Bogota', 'America/Bogota'), ('America/Boise', 'America/Boise'), ('America/Buenos_Aires', 'America/Buenos_Aires'), ('America/Cambridge_Bay', 'America/Cambridge_Bay'), ('America/Campo_Grande', 'America/Campo_Grande'), ('America/Cancun', 'America/Cancun'), ('America/Caracas', 'America/Caracas'), ('America/Catamarca', 'America/Catamarca'), ('America/Cayenne', 'America/Cayenne'), ('America/Cayman', 'America/Cayman'), ('America/Chicago', 'America/Chicago'), ('America/Chihuahua', 'America/Chihuahua'), ('America/Ciudad_Juarez', 'America/Ciudad_Juarez'), ('America/Coral_Harbour', 'America/Coral_Harbour'), ('America/Cordoba', 'America/Cordoba'), ('America/Costa_Rica', 'America/Costa_Rica'), ('America/Creston', 'America/Creston'), ('America/Cuiaba', 'America/Cuiaba'), ('America/Curacao', 'America/Curacao'), ('America/Danmarkshavn', 'America/Danmarkshavn'), ('America/Dawson', 'America/Dawson'), ('America/Dawson_Creek', 'America/Dawson_Creek'), ('America/Denver', 'America/Denver'), ('America/Detroit', 'America/Detroit'), ('America/Dominica', 'America/Dominica'), ('America/Edmonton', 'America/Edmonton'), ('America/Eirunepe', 'America/Eirunepe'), ('America/El_Salvador', 'America/El_Salvador'), ('America/Ensenada', 'America/Ensenada'), ('America/Fort_Nelson', 'America/Fort_Nelson'), ('America/Fort_Wayne', 'America/Fort_Wayne'), ('America/Fortaleza', 'America/Fortaleza'), ('America/Glace_Bay', 'America/Glace_Bay'), ('America/Godthab', 'America/Godthab'), ('America/Goose_Bay', 'America/Goose_Bay'), ('America/Grand_Turk', 'America/Grand_Turk'), ('America/Grenada', 'America/Grenada'), ('America/Guadeloupe', 'America/Guadeloupe'), ('America/Guatemala', 'America/Guatemala'), ('America/Guayaquil', 'America/Guayaquil'), ('America/Guyana', 'America/Guyana'), ('America/Halifax', 'America/Halifax'), ('America/Havana', 'America/Havana'), ('America/Hermosillo', 'America/Hermosillo'), ('America/Indiana/Indianapolis', 'America/Indiana/Indianapolis'), ('America/Indiana/Knox', 'America/Indiana/Knox'), ('America/Indiana/Marengo', 'America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'America/Indiana/Tell_City'), ('America/Indiana/Vevay', 'America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'America/Indiana/Winamac'), ('America/Indianapolis', 'America/Indianapolis'), ('America/Inuvik', 'America/Inuvik'), ('America/Iqaluit', 'America/Iqaluit'), ('America/Jamaica', 'America/Jamaica'), ('America/Jujuy', 'America/Jujuy'), ('America/Juneau', 'America/Juneau'), ('America/Kentucky/Louisville', 'America/Kentucky/Louisville'), ('America/Kentucky/Monticello', 'America/Kentucky/Monticello'), ('America/Knox_IN', 'America/Knox_IN'), ('America/Kralendijk', 'America/Kralendijk'), ('America/La_Paz', 'America/La_Paz'), ('America/Lima', 'America/Lima'), ('America/Los_Angeles', 'America/Los_Angeles'), ('America/Louisville', 'America/Louisville'), ('America/Lower_Princes', 'America/Lower_Princes'), ('America/Maceio', 'America/Maceio'), ('America/Managua', 'America/Managua'), ('America/Manaus', 'America/Manaus'), ('America/Marigot', 'America/Marigot'), ('America/Martinique', 'America/Martinique'), ('America/Matamoros', 'America/Matamoros'), ('America/Mazatlan', 'America/Mazatlan'), ('America/Mendoza', 'America/Mendoza'), ('America/Menominee', 'America/Menominee'), ('America/Merida', 'America/Merida'), ('America/Metlakatla', 'America/Metlakatla'), ('America/Mexico_City', 'America/Mexico_City'), ('America/Miquelon', 'America/Miquelon'), ('America/Moncton', 'America/Moncton'), ('America/Monterrey', 'America/Monterrey'), ('America/Montevideo', 'America/Montevideo'), ('America/Montreal', 'America/Montreal'), ('America/Montserrat', 'America/Montserrat'), ('America/Nassau', 'America/Nassau'), ('America/New_York', 'America/New_York'), ('America/Nipigon', 'America/Nipigon'), ('America/Nome', 'America/Nome'), ('America/Noronha', 'America/Noronha'), ('America/North_Dakota/Beulah', 'America/North_Dakota/Beulah'), ('America/North_Dakota/Center', 'America/North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'America/North_Dakota/New_Salem'), ('America/Nuuk', 'America/Nuuk'), ('America/Ojinaga', 'America/Ojinaga'), ('America/Panama', 'America/Panama'), ('America/Pangnirtung', 'America/Pangnirtung'), ('America/Paramaribo', 'America/Paramaribo'), ('America/Phoenix', 'America/Phoenix'), ('America/Port-au-Prince', 'America/Port-au-Prince'), ('America/Port_of_Spain', 'America/Port_of_Spain'), ('America/Porto_Acre', 'America/Porto_Acre'), ('America/Porto_Velho', 'America/Porto_Velho'), ('America/Puerto_Rico', 'America/Puerto_Rico'), ('America/Punta_Arenas', 'America/Punta_Arenas'), ('America/Rainy_River', 'America/Rainy_River'), ('America/Rankin_Inlet', 'America/Rankin_Inlet'), ('America/Recife', 'America/Recife'), ('America/Regina', 'America/Regina'), ('America/Resolute', 'America/Resolute'), ('America/Rio_Branco', 'America/Rio_Branco'), ('America/Rosario', 'America/Rosario'), ('America/Santa_Isabel', 'America/Santa_Isabel'), ('America/Santarem', 'America/Santarem'), ('America/Santiago', 'America/Santiago'), ('America/Santo_Domingo', 'America/Santo_Domingo'), ('America/Sao_Paulo', 'America/Sao_Paulo'), ('America/Scoresbysund', 'America/Scoresbysund'), ('America/Shiprock', 'America/Shiprock'), ('America/Sitka', 'America/Sitka'), ('America/St_Barthelemy', 'America/St_Barthelemy'), ('America/St_Johns', 'America/St_Johns'), ('America/St_Kitts', 'America/St_Kitts'), ('America/St_Lucia', 'America/St_Lucia'), ('America/St_Thomas', 'America/St_Thomas'), ('America/St_Vincent', 'America/St_Vincent'), ('America/Swift_Current', 'America/Swift_Current'), ('America/Tegucigalpa', 'America/Tegucigalpa'), ('America/Thule', 'America/Thule'), ('America/Thunder_Bay', 'America/Thunder_Bay'), ('America/Tijuana', 'America/Tijuana'), ('America/Toronto', 'America/Toronto'), ('America/Tortola', 'America/Tortola'), ('America/Vancouver', 'America/Vancouver'), ('America/Virgin', 'America/Virgin'), ('America/Whitehorse', 'America/Whitehorse'), ('America/Winnipeg', 'America/Winnipeg'), ('America/Yakutat', 'America/Yakutat'), ('America/Yellowknife', 'America/Yellowknife'), ('Antarctica/Casey', 'Antarctica/Casey'), ('Antarctica/Davis', 'Antarctica/Davis'), ('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville'), ('Antarctica/Macquarie', 'Antarctica/Macquarie'), ('Antarctica/Mawson', 'Antarctica/Mawson'), ('Antarctica/McMurdo', 'Antarctica/McMurdo'), ('Antarctica/Palmer', 'Antarctica/Palmer'), ('Antarctica/Rothera', 'Antarctica/Rothera'), ('Antarctica/South_Pole', 'Antarctica/South_Pole'), ('Antarctica/Syowa', 'Antarctica/Syowa'), ('Antarctica/Troll', 'Antarctica/Troll'), ('Antarctica/Vostok', 'Antarctica/Vostok'), ('Arctic/Longyearbyen', 'Arctic/Longyearbyen'), ('Asia/Aden', 'Asia/Aden'), ('Asia/Almaty', 'Asia/Almaty'), ('Asia/Amman', 'Asia/Amman'), ('Asia/Anadyr', 'Asia/Anadyr'), ('Asia/Aqtau', 'Asia/Aqtau'), ('Asia/Aqtobe', 'Asia/Aqtobe'), ('Asia/Ashgabat', 'Asia/Ashgabat'), ('Asia/Ashkhabad', 'Asia/Ashkhabad'), ('Asia/Atyrau', 'Asia/Atyrau'), ('Asia/Baghdad', 'Asia/Baghdad'), ('Asia/Bahrain', 'Asia/Bahrain'), ('Asia/Baku', 'Asia/Baku'), ('Asia/Bangkok', 'Asia/Bangkok'), ('Asia/Barnaul', 'Asia/Barnaul'), ('Asia/Beirut', 'Asia/Beirut'), ('Asia/Bishkek', 'Asia/Bishkek'), ('Asia/Brunei', 'Asia/Brunei'), ('Asia/Calcutta', 'Asia/Calcutta'), ('Asia/Chita', 'Asia/Chita'), ('Asia/Choibalsan', 'Asia/Choibalsan'), ('Asia/Chongqing', 'Asia/Chongqing'), ('Asia/Chungking', 'Asia/Chungking'), ('Asia/Colombo', 'Asia/Colombo'), ('Asia/Dacca', 'Asia/Dacca'), ('Asia/Damascus', 'Asia/Damascus'), ('Asia/Dhaka', 'Asia/Dhaka'), ('Asia/Dili', 'Asia/Dili'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Dushanbe', 'Asia/Dushanbe'), ('Asia/Famagusta', 'Asia/Famagusta'), ('Asia/Gaza', 'Asia/Gaza'), ('Asia/Harbin', 'Asia/Harbin'), ('Asia/Hebron', 'Asia/Hebron'), ('Asia/Ho_Chi_Minh', 'Asia/Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Asia/Hong_Kong'), ('Asia/Hovd', 'Asia/Hovd'), ('Asia/Irkutsk', 'Asia/Irkutsk'), ('Asia/Istanbul', 'Asia/Istanbul'), ('Asia/Jakarta', 'Asia/Jakarta'), ('Asia/Jayapura', 'Asia/Jayapura'), ('Asia/Jerusalem', 'Asia/Jerusalem'), ('Asia/Kabul', 'Asia/Kabul'), ('Asia/Kamchatka', 'Asia/Kamchatka'), ('Asia/Karachi', 'Asia/Karachi'), ('Asia/Kashgar', 'Asia/Kashgar'), ('Asia/Kathmandu', 'Asia/Kathmandu'), ('Asia/Katmandu', 'Asia/Katmandu'), ('Asia/Khandyga', 'Asia/Khandyga'), ('Asia/Kolkata', 'Asia/Kolkata'), ('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Asia/Kuala_Lumpur'), ('Asia/Kuching', 'Asia/Kuching'), ('Asia/Kuwait', 'Asia/Kuwait'), ('Asia/Macao', 'Asia/Macao'), ('Asia/Macau', 'Asia/Macau'), ('Asia/Magadan', 'Asia/Magadan'), ('Asia/Makassar', 'Asia/Makassar'), ('Asia/Manila', 'Asia/Manila'), ('Asia/Muscat', 'Asia/Muscat'), ('Asia/Nicosia', 'Asia/Nicosia'), ('Asia/Novokuznetsk', 'Asia/Novokuznetsk'), ('Asia/Novosibirsk', 'Asia/Novosibirsk'), ('Asia/Omsk', 'Asia/Omsk'), ('Asia/Oral', 'Asia/Oral'), ('Asia/Phnom_Penh', 'Asia/Phnom_Penh'), ('Asia/Pontianak', 'Asia/Pontianak'), ('Asia/Pyongyang', 'Asia/Pyongyang'), ('Asia/Qatar', 'Asia/Qatar'), ('Asia/Qostanay', 'Asia/Qostanay'), ('Asia/Qyzylorda', 'Asia/Qyzylorda'), ('Asia/Rangoon', 'Asia/Rangoon'), ('Asia/Riyadh', 'Asia/Riyadh'), ('Asia/Saigon', 'Asia/Saigon'), ('Asia/Sakhalin', 'Asia/Sakhalin'), ('Asia/Samarkand', 'Asia/Samarkand'), ('Asia/Seoul', 'Asia/Seoul'), ('Asia/Shanghai', 'Asia/Shanghai'), ('Asia/Singapore', 'Asia/Singapore'), ('Asia/Srednekolymsk', 'Asia/Srednekolymsk'), ('Asia/Taipei', 'Asia/Taipei'), ('Asia/Tashkent', 'Asia/Tashkent'), ('Asia/Tbilisi', 'Asia/Tbilisi'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Tel_Aviv', 'Asia/Tel_Aviv'), ('Asia/Thimbu', 'Asia/Thimbu'), ('Asia/Thimphu', 'Asia/Thimphu'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Asia/Tomsk', 'Asia/Tomsk'), ('Asia/Ujung_Pandang', 'Asia/Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar'), ('Asia/Ulan_Bator', 'Asia/Ulan_Bator'), ('Asia/Urumqi', 'Asia/Urumqi'), ('Asia/Ust-Nera', 'Asia/Ust-Nera'), ('Asia/Vientiane', 'Asia/Vientiane'), ('Asia/Vladivostok', 'Asia/Vladivostok'), ('Asia/Yakutsk', 'Asia/Yakutsk'), ('Asia/Yangon', 'Asia/Yangon'), ('Asia/Yekaterinburg', 'Asia/Yekaterinburg'), ('Asia/Yerevan', 'Asia/Yerevan'), ('Atlantic/Azores', 'Atlantic/Azores'), ('Atlantic/Bermuda', 'Atlantic/Bermuda'), ('Atlantic/Canary', 'Atlantic/Canary'), ('Atlantic/Cape_Verde', 'Atlantic/Cape_Verde'), ('Atlantic/Faeroe', 'Atlantic/Faeroe'), ('Atlantic/Faroe', 'Atlantic/Faroe'), ('Atlantic/Jan_Mayen', 'Atlantic/Jan_Mayen'), ('Atlantic/Madeira', 'Atlantic/Madeira'), ('Atlantic/Reykjavik', 'Atlantic/Reykjavik'), ('Atlantic/South_Georgia', 'Atlantic/South_Georgia'), ('Atlantic/St_Helena', 'Atlantic/St_Helena'), ('Atlantic/Stanley', 'Atlantic/Stanley'), ('Australia/ACT', 'Australia/ACT'), ('Australia/Adelaide', 'Australia/Adelaide'), ('Australia/Brisbane', 'Australia/Brisbane'), ('Australia/Broken_Hill', 'Australia/Broken_Hill'), ('Australia/Canberra', 'Australia/Canberra'), ('Australia/Currie', 'Australia/Currie'), ('Australia/Darwin', 'Australia/Darwin'), ('Australia/Eucla', 'Australia/Eucla'), ('Australia/Hobart', 'Australia/Hobart'), ('Australia/LHI', 'Australia/LHI'), ('Australia/Lindeman', 'Australia/Lindeman'), ('Australia/Lord_Howe', 'Australia/Lord_Howe'), ('Australia/Melbourne', 'Australia/Melbourne'), ('Australia/NSW', 'Australia/NSW'), ('Australia/North', 'Australia/North'), ('Australia/Perth', 'Australia/Perth'), ('Australia/Queensland', 'Australia/Queensland'), ('Australia/South', 'Australia/South'), ('Australia/Sydney', 'Australia/Sydney'), ('Australia/Tasmania', 'Australia/Tasmania'), ('Australia/Victoria', 'Australia/Victoria'), ('Australia/West', 'Australia/West'), ('Australia/Yancowinna', 'Australia/Yancowinna'), ('Brazil/Acre', 'Brazil/Acre'), ('Brazil/DeNoronha', 'Brazil/DeNoronha'), ('Brazil/East', 'Brazil/East'), ('Brazil/West', 'Brazil/West'), ('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Canada/Atlantic', 'Canada/Atlantic'), ('Canada/Central', 'Canada/Central'), ('Canada/Eastern', 'Canada/Eastern'), ('Canada/Mountain', 'Canada/Mountain'), ('Canada/Newfoundland', 'Canada/Newfoundland'), ('Canada/Pacific', 'Canada/Pacific'), ('Canada/Saskatchewan', 'Canada/Saskatchewan'), ('Canada/Yukon', 'Canada/Yukon'), ('Chile/Continental', 'Chile/Continental'), ('Chile/EasterIsland', 'Chile/EasterIsland'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('Etc/GMT', 'Etc/GMT'), ('Etc/GMT+0', 'Etc/GMT+0'), ('Etc/GMT+1', 'Etc/GMT+1'), ('Etc/GMT+10', 'Etc/GMT+10'), ('Etc/GMT+11', 'Etc/GMT+11'), ('Etc/GMT+12', 'Etc/GMT+12'), ('Etc/GMT+2', 'Etc/GMT+2'), ('Etc/GMT+3', 'Etc/GMT+3'), ('Etc/GMT+4', 'Etc/GMT+4'), ('Etc/GMT+5', 'Etc/GMT+5'), ('Etc/GMT+6', 'Etc/GMT+6'), ('Etc/GMT+7', 'Etc/GMT+7'), ('Etc/GMT+8', 'Etc/GMT+8'), ('Etc/GMT+9', 'Etc/GMT+9'), ('Etc/GMT-0', 'Etc/GMT-0'), ('Etc/GMT-1', 'Etc/GMT-1'), ('Etc/GMT-10', 'Etc/GMT-10'), ('Etc/GMT-11', 'Etc/GMT-11'), ('Etc/GMT-12', 'Etc/GMT-12'), ('Etc/GMT-13', 'Etc/GMT-13'), ('Etc/GMT-14', 'Etc/GMT-14'), ('Etc/GMT-2', 'Etc/GMT-2'), ('Etc/GMT-3', 'Etc/GMT-3'), ('Etc/GMT-4', 'Etc/GMT-4'), ('Etc/GMT-5', 'Etc/GMT-5'), ('Etc/GMT-6', 'Etc/GMT-6'), ('Etc/GMT-7', 'Etc/GMT-7'), ('Etc/GMT-8', 'Etc/GMT-8'), ('Etc/GMT-9', 'Etc/GMT-9'), ('Etc/GMT0', 'Etc/GMT0'), ('Etc/Greenwich', 'Etc/Greenwich'), ('Etc/UCT', 'Etc/UCT'), ('Etc/UTC', 'Etc/UTC'), ('Etc/Universal', 'Etc/Universal'), ('Etc/Zulu', 'Etc/Zulu'), ('Europe/Amsterdam', 'Europe/Amsterdam'), ('Europe/Andorra', 'Europe/Andorra'), ('Europe/Astrakhan', 'Europe/Astrakhan'), ('Europe/Athens', 'Europe/Athens'), ('Europe/Belfast', 'Europe/Belfast'), ('Europe/Belgrade', 'Europe/Belgrade'), ('Europe/Berlin', 'Europe/Berlin'), ('Europe/Bratislava', 'Europe/Bratislava'), ('Europe/Brussels', 'Europe/Brussels'), ('Europe/Bucharest', 'Europe/Bucharest'), ('Europe/Budapest', 'Europe/Budapest'), ('Europe/Busingen', 'Europe/Busingen'), ('Europe/Chisinau', 'Europe/Chisinau'), ('Europe/Copenhagen', 'Europe/Copenhagen'), ('Europe/Dublin', 'Europe/Dublin'), ('Europe/Gibraltar', 'Europe/Gibraltar'), ('Europe/Guernsey', 'Europe/Guernsey'), ('Europe/Helsinki', 'Europe/Helsinki'), ('Europe/Isle_of_Man', 'Europe/Isle_of_Man'), ('Europe/Istanbul', 'Europe/Istanbul'), ('Europe/Jersey', 'Europe/Jersey'), ('Europe/Kaliningrad', 'Europe/Kaliningrad'), ('Europe/Kiev', 'Europe/Kiev'), ('Europe/Kirov', 'Europe/Kirov'), ('Europe/Kyiv', 'Europe/Kyiv'), ('Europe/Lisbon', 'Europe/Lisbon'), ('Europe/Ljubljana', 'Europe/Ljubljana'), ('Europe/London', 'Europe/London'), ('Europe/Luxembourg', 'Europe/Luxembourg'), ('Europe/Madrid', 'Europe/Madrid'), ('Europe/Malta', 'Europe/Malta'), ('Europe/Mariehamn', 'Europe/Mariehamn'), ('Europe/Minsk', 'Europe/Minsk'), ('Europe/Monaco', 'Europe/Monaco'), ('Europe/Moscow', 'Europe/Moscow'), ('Europe/Nicosia', 'Europe/Nicosia'), ('Europe/Oslo', 'Europe/Oslo'), ('Europe/Paris', 'Europe/Paris'), ('Europe/Podgorica', 'Europe/Podgorica'), ('Europe/Prague', 'Europe/Prague'), ('Europe/Riga', 'Europe/Riga'), ('Europe/Rome', 'Europe/Rome'), ('Europe/Samara', 'Europe/Samara'), ('Europe/San_Marino', 'Europe/San_Marino'), ('Europe/Sarajevo', 'Europe/Sarajevo'), ('Europe/Saratov', 'Europe/Saratov'), ('Europe/Simferopol', 'Europe/Simferopol'), ('Europe/Skopje', 'Europe/Skopje'), ('Europe/Sofia', 'Europe/Sofia'), ('Europe/Stockholm', 'Europe/Stockholm'), ('Europe/Tallinn', 'Europe/Tallinn'), ('Europe/Tirane', 'Europe/Tirane'), ('Europe/Tiraspol', 'Europe/Tiraspol'), ('Europe/Ulyanovsk', 'Europe/Ulyanovsk'), ('Europe/Uzhgorod', 'Europe/Uzhgorod'), ('Europe/Vaduz', 'Europe/Vaduz'), ('Europe/Vatican', 'Europe/Vatican'), ('Europe/Vienna', 'Europe/Vienna'), ('Europe/Vilnius', 'Europe/Vilnius'), ('Europe/Volgograd', 'Europe/Volgograd'), ('Europe/Warsaw', 'Europe/Warsaw'), ('Europe/Zagreb', 'Europe/Zagreb'), ('Europe/Zaporozhye', 'Europe/Zaporozhye'), ('Europe/Zurich', 'Europe/Zurich'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('GMT', 'GMT'), ('GMT+0', 'GMT+0'), ('GMT-0', 'GMT-0'), ('GMT0', 'GMT0'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Indian/Antananarivo', 'Indian/Antananarivo'), ('Indian/Chagos', 'Indian/Chagos'), ('Indian/Christmas', 'Indian/Christmas'), ('Indian/Cocos', 'Indian/Cocos'), ('Indian/Comoro', 'Indian/Comoro'), ('Indian/Kerguelen', 'Indian/Kerguelen'), ('Indian/Mahe', 'Indian/Mahe'), ('Indian/Maldives', 'Indian/Maldives'), ('Indian/Mauritius', 'Indian/Mauritius'), ('Indian/Mayotte', 'Indian/Mayotte'), ('Indian/Reunion', 'Indian/Reunion'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('Mexico/BajaNorte', 'Mexico/BajaNorte'), ('Mexico/BajaSur', 'Mexico/BajaSur'), ('Mexico/General', 'Mexico/General'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Pacific/Apia', 'Pacific/Apia'), ('Pacific/Auckland', 'Pacific/Auckland'), ('Pacific/Bougainville', 'Pacific/Bougainville'), ('Pacific/Chatham', 'Pacific/Chatham'), ('Pacific/Chuuk', 'Pacific/Chuuk'), ('Pacific/Easter', 'Pacific/Easter'), ('Pacific/Efate', 'Pacific/Efate'), ('Pacific/Enderbury', 'Pacific/Enderbury'), ('Pacific/Fakaofo', 'Pacific/Fakaofo'), ('Pacific/Fiji', 'Pacific/Fiji'), ('Pacific/Funafuti', 'Pacific/Funafuti'), ('Pacific/Galapagos', 'Pacific/Galapagos'), ('Pacific/Gambier', 'Pacific/Gambier'), ('Pacific/Guadalcanal', 'Pacific/Guadalcanal'), ('Pacific/Guam', 'Pacific/Guam'), ('Pacific/Honolulu', 'Pacific/Honolulu'), ('Pacific/Johnston', 'Pacific/Johnston'), ('Pacific/Kanton', 'Pacific/Kanton'), ('Pacific/Kiritimati', 'Pacific/Kiritimati'), ('Pacific/Kosrae', 'Pacific/Kosrae'), ('Pacific/Kwajalein', 'Pacific/Kwajalein'), ('Pacific/Majuro', 'Pacific/Majuro'), ('Pacific/Marquesas', 'Pacific/Marquesas'), ('Pacific/Midway', 'Pacific/Midway'), ('Pacific/Nauru', 'Pacific/Nauru'), ('Pacific/Niue', 'Pacific/Niue'), ('Pacific/Norfolk', 'Pacific/Norfolk'), ('Pacific/Noumea', 'Pacific/Noumea'), ('Pacific/Pago_Pago', 'Pacific/Pago_Pago'), ('Pacific/Palau', 'Pacific/Palau'), ('Pacific/Pitcairn', 'Pacific/Pitcairn'), ('Pacific/Pohnpei', 'Pacific/Pohnpei'), ('Pacific/Ponape', 'Pacific/Ponape'), ('Pacific/Port_Moresby', 'Pacific/Port_Moresby'), ('Pacific/Rarotonga', 'Pacific/Rarotonga'), ('Pacific/Saipan', 'Pacific/Saipan'), ('Pacific/Samoa', 'Pacific/Samoa'), ('Pacific/Tahiti', 'Pacific/Tahiti'), ('Pacific/Tarawa', 'Pacific/Tarawa'), ('Pacific/Tongatapu', 'Pacific/Tongatapu'), ('Pacific/Truk', 'Pacific/Truk'), ('Pacific/Wake', 'Pacific/Wake'), ('Pacific/Wallis', 'Pacific/Wallis'), ('Pacific/Yap', 'Pacific/Yap'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('US/Alaska', 'US/Alaska'), ('US/Aleutian', 'US/Aleutian'), ('US/Arizona', 'US/Arizona'), ('US/Central', 'US/Central'), ('US/East-Indiana', 'US/East-Indiana'), ('US/Eastern', 'US/Eastern'), ('US/Hawaii', 'US/Hawaii'), ('US/Indiana-Starke', 'US/Indiana-Starke'), ('US/Michigan', 'US/Michigan'), ('US/Mountain', 'US/Mountain'), ('US/Pacific', 'US/Pacific'), ('US/Samoa', 'US/Samoa'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')], default='UTC', max_length=63)), - ], - options={ - 'verbose_name': 'Настройки системы', - 'verbose_name_plural': 'Настройки системы', - }, - ), - migrations.CreateModel( - name='LocalDatabase', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Имя базы данных')), - ('host', models.CharField(default='localhost', max_length=255, verbose_name='Хост базы данных')), - ('port', models.IntegerField(default=5432, verbose_name='Порт базы данных')), - ('user', models.CharField(max_length=255, verbose_name='Пользователь базы данных')), - ('database', models.CharField(max_length=255, verbose_name='Название базы данных')), - ('password', models.CharField(max_length=255, verbose_name='Пароль базы данных')), - ('is_active', models.BooleanField(default=True, verbose_name='Активна ли база данных')), - ], - options={ - 'verbose_name': 'База данных', - 'verbose_name_plural': 'Базы данных', - }, - ), - migrations.CreateModel( - name='TelegramSettings', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('bot_token', models.CharField(help_text='Токен вашего бота Telegram', max_length=255)), - ('chat_id', models.CharField(help_text='ID чата для отправки сообщений', max_length=255)), - ('username', models.CharField(blank=True, help_text='Имя пользователя для бота', max_length=255, null=True)), - ], - options={ - 'verbose_name': 'Telegram', - 'verbose_name_plural': 'Telegram', - }, - ), - ] diff --git a/bot/management/commands/run_bot.py b/bot/management/commands/run_bot.py index 3edae6c0..76988698 100644 --- a/bot/management/commands/run_bot.py +++ b/bot/management/commands/run_bot.py @@ -1,16 +1,79 @@ +# import os +# import django +# import asyncio +# from apscheduler.schedulers.asyncio import AsyncIOScheduler +# from django.core.management.base import BaseCommand +# from telegram.ext import Application +# from bot.utils.bot_setup import setup_bot +# from scheduler.tasks import load_tasks_to_scheduler +# from app_settings.models import TelegramSettings +# from touchh.utils.log import CustomLogger + +# class Command(BaseCommand): +# help = "Запуск Telegram бота и планировщика" + +# def handle(self, *args, **options): +# # Установка Django окружения +# os.environ.setdefault("DJANGO_SETTINGS_MODULE", "touchh.settings") +# django.setup() + +# # Создаем новый цикл событий +# loop = asyncio.new_event_loop() +# asyncio.set_event_loop(loop) + +# # Настройка планировщика +# scheduler = AsyncIOScheduler(event_loop=loop) +# scheduler.start() + +# # Загрузка задач в планировщик +# try: +# load_tasks_to_scheduler(scheduler) +# except Exception as e: +# self.stderr.write(f"Ошибка при загрузке задач в планировщик: {e}") +# return + +# # Настройка Telegram бота +# # bot_token = os.getenv("TELEGRAM_BOT_TOKEN") +# bot_token = TelegramSettings.objects.first().bot_token + +# if not bot_token: +# raise ValueError("Токен бота не найден в переменных окружения.") +# application = Application.builder().token(bot_token).build() +# setup_bot(application) +# # Основная асинхронная функция +# async def main(): +# await application.initialize() +# await application.start() +# await application.updater.start_polling() +# self.stdout.write(self.style.SUCCESS("Telegram бот и планировщик успешно запущены.")) +# try: +# while True: +# await asyncio.sleep(3600) +# except asyncio.CancelledError: +# await application.stop() +# scheduler.shutdown() + +# # Запуск асинхронной программы +# try: +# loop.run_until_complete(main()) +# except KeyboardInterrupt: +# self.stdout.write(self.style.ERROR("Завершение работы Telegram бота и планировщика")) +# finally: +# loop.close() + + + import os import django import asyncio -from apscheduler.schedulers.asyncio import AsyncIOScheduler from django.core.management.base import BaseCommand from telegram.ext import Application from bot.utils.bot_setup import setup_bot -from scheduler.tasks import load_tasks_to_scheduler from app_settings.models import TelegramSettings from touchh.utils.log import CustomLogger class Command(BaseCommand): - help = "Запуск Telegram бота и планировщика" + help = "Запуск Telegram бота" def handle(self, *args, **options): # Установка Django окружения @@ -21,42 +84,31 @@ class Command(BaseCommand): loop = asyncio.new_event_loop() asyncio.set_event_loop(loop) - # Настройка планировщика - scheduler = AsyncIOScheduler(event_loop=loop) - scheduler.start() - - # Загрузка задач в планировщик - try: - load_tasks_to_scheduler(scheduler) - except Exception as e: - self.stderr.write(f"Ошибка при загрузке задач в планировщик: {e}") - return - # Настройка Telegram бота - # bot_token = os.getenv("TELEGRAM_BOT_TOKEN") bot_token = TelegramSettings.objects.first().bot_token - + if not bot_token: raise ValueError("Токен бота не найден в переменных окружения.") + application = Application.builder().token(bot_token).build() setup_bot(application) + # Основная асинхронная функция async def main(): await application.initialize() await application.start() await application.updater.start_polling() - self.stdout.write(self.style.SUCCESS("Telegram бот и планировщик успешно запущены.")) + self.stdout.write(self.style.SUCCESS("Telegram бот успешно запущен.")) try: while True: await asyncio.sleep(3600) except asyncio.CancelledError: await application.stop() - scheduler.shutdown() # Запуск асинхронной программы try: loop.run_until_complete(main()) except KeyboardInterrupt: - self.stdout.write(self.style.ERROR("Завершение работы Telegram бота и планировщика")) + self.stdout.write(self.style.ERROR("Завершение работы Telegram бота")) finally: loop.close() diff --git a/bot/operations/hotels.py b/bot/operations/hotels.py index d0ed673c..3ec5371c 100644 --- a/bot/operations/hotels.py +++ b/bot/operations/hotels.py @@ -4,6 +4,9 @@ from hotels.models import Hotel, UserHotel from users.models import User from pms_integration.manager import PMSIntegrationManager from bot.utils.froud_check import detect_fraud +from touchh.utils.log import CustomLogger + +logger = CustomLogger(name="BOT-hotels Manager", log_level="DEBUG").get_logger() async def manage_hotels(update: Update, context): """Отображение списка отелей, связанных с пользователем.""" query = update.callback_query @@ -81,7 +84,8 @@ async def check_pms(update, context): try: # Получение ID отеля из callback_data hotel_id = query.data.split("_")[2] - + logger.debug(f"Hotel ID: {hotel_id}") + logger.debug(f"Hotel ID type : {type(hotel_id)}") # Получение конфигурации отеля и PMS hotel = await sync_to_async(Hotel.objects.select_related('pms').get)(id=hotel_id) pms_config = hotel.pms @@ -99,6 +103,7 @@ async def check_pms(update, context): if hasattr(pms_manager.plugin, 'fetch_data') and callable(pms_manager.plugin.fetch_data): # Плагин поддерживает метод fetch_data report = await pms_manager.plugin._fetch_data() + else: await query.edit_message_text("Подходящий способ интеграции с PMS не найден.") return @@ -110,6 +115,7 @@ async def check_pms(update, context): f"Обработано записей: {report['processed_items']}\n" f"Ошибки: {len(report['errors'])}" ) + logger.info(f'Result_Message: {result_message}\n Result_meaage_type: {type(result_message)}') if report["errors"]: result_message += "\n\nСписок ошибок:\n" + "\n".join(report["errors"]) diff --git a/bot/utils/scheduler.py b/bot/utils/scheduler.py deleted file mode 100644 index 7075b3a8..00000000 --- a/bot/utils/scheduler.py +++ /dev/null @@ -1,10 +0,0 @@ -from apscheduler.schedulers.asyncio import AsyncIOScheduler -from bot.operations.notifications import schedule_notifications - - -def setup_scheduler(): - """Настройка планировщика уведомлений.""" - print("Настройка планировщика...") - scheduler = AsyncIOScheduler() - scheduler.add_job(schedule_notifications, "cron", minute="*") - return scheduler diff --git a/hotels/migrations/0001_initial.py b/hotels/migrations/0001_initial.py deleted file mode 100644 index 7df79502..00000000 --- a/hotels/migrations/0001_initial.py +++ /dev/null @@ -1,116 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-09 09:30 - -import django.core.validators -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='APIConfiguration', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Название API')), - ('url', models.URLField(verbose_name='URL API')), - ('token', models.CharField(blank=True, max_length=255, null=True, verbose_name='Токен')), - ('username', models.CharField(blank=True, max_length=255, null=True, verbose_name='Логин')), - ('password', models.CharField(blank=True, max_length=255, null=True, verbose_name='Пароль')), - ('last_updated', models.DateTimeField(auto_now=True, verbose_name='Дата последнего обновления')), - ], - options={ - 'verbose_name': 'Конфигурация API', - 'verbose_name_plural': 'Конфигурации API', - }, - ), - migrations.CreateModel( - name='FraudLog', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('reservation_id', models.BigIntegerField(unique=True, verbose_name='ID бронирования')), - ('guest_name', models.CharField(blank=True, max_length=255, null=True)), - ('check_in_date', models.DateField()), - ('detected_at', models.DateTimeField(auto_now_add=True)), - ('message', models.TextField()), - ], - options={ - 'verbose_name': 'Журнал мошенничества', - 'verbose_name_plural': 'Журналы мошенничества', - }, - ), - migrations.CreateModel( - name='Guest', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Имя гостя')), - ('birthdate', models.DateField(blank=True, null=True, verbose_name='Дата рождения')), - ('phone', models.CharField(blank=True, max_length=50, null=True, verbose_name='Телефон')), - ('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Email')), - ], - options={ - 'verbose_name': 'Гость', - 'verbose_name_plural': 'Гости', - }, - ), - migrations.CreateModel( - name='Reservation', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('reservation_id', models.BigIntegerField(unique=True, verbose_name='ID бронирования')), - ('room_number', models.CharField(max_length=50, verbose_name='Номер комнаты')), - ('room_type', models.CharField(max_length=255, verbose_name='Тип комнаты')), - ('check_in', models.DateTimeField(verbose_name='Дата заезда')), - ('check_out', models.DateTimeField(verbose_name='Дата выезда')), - ('status', models.CharField(max_length=50, verbose_name='Статус')), - ('price', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='Цена')), - ('discount', models.DecimalField(blank=True, decimal_places=2, max_digits=10, null=True, verbose_name='Скидка')), - ], - options={ - 'verbose_name': 'Бронирование', - 'verbose_name_plural': 'Бронирования', - }, - ), - migrations.CreateModel( - name='UserHotel', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ], - options={ - 'verbose_name': 'Пользователь отеля', - 'verbose_name_plural': 'Пользователи отелей', - }, - ), - migrations.CreateModel( - name='APIRequestLog', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('request_time', models.DateTimeField(auto_now_add=True, verbose_name='Время запроса')), - ('response_status', models.IntegerField(validators=[django.core.validators.MinValueValidator(100), django.core.validators.MaxValueValidator(599)], verbose_name='HTTP статус ответа')), - ('response_data', models.JSONField(blank=True, null=True, verbose_name='Данные ответа')), - ('api', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hotels.apiconfiguration', verbose_name='API')), - ], - options={ - 'verbose_name': 'Журнал запросов API', - 'verbose_name_plural': 'Журналы запросов API', - }, - ), - migrations.CreateModel( - name='Hotel', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Название отеля')), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создан')), - ('api', models.OneToOneField(blank=True, help_text='API, связанный с этим отелем.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='hotels.apiconfiguration', verbose_name='API')), - ], - options={ - 'verbose_name': 'Отель', - 'verbose_name_plural': 'Отели', - }, - ), - ] diff --git a/hotels/migrations/0002_initial.py b/hotels/migrations/0002_initial.py deleted file mode 100644 index 5cf7f3a9..00000000 --- a/hotels/migrations/0002_initial.py +++ /dev/null @@ -1,42 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-09 09:30 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('hotels', '0001_initial'), - ('pms_integration', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='hotel', - name='pms', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='pms_integration.pmsconfiguration', verbose_name='PMS система'), - ), - migrations.AddField( - model_name='fraudlog', - name='hotel', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='frauds', to='hotels.hotel'), - ), - migrations.AddField( - model_name='reservation', - name='hotel', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hotels.hotel', verbose_name='Отель'), - ), - migrations.AddField( - model_name='guest', - name='reservation', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='guests', to='hotels.reservation', verbose_name='Бронирование'), - ), - migrations.AddField( - model_name='userhotel', - name='hotel', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='hotel_users', to='hotels.hotel', verbose_name='Отель'), - ), - ] diff --git a/hotels/migrations/0003_initial.py b/hotels/migrations/0003_initial.py deleted file mode 100644 index 1bf5dc33..00000000 --- a/hotels/migrations/0003_initial.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-09 09:30 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('hotels', '0002_initial'), - ('users', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='userhotel', - name='user', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_hotels', to='users.user', verbose_name='Пользователь'), - ), - migrations.AddIndex( - model_name='apirequestlog', - index=models.Index(fields=['api'], name='hotels_apir_api_id_686bb0_idx'), - ), - migrations.AddIndex( - model_name='apirequestlog', - index=models.Index(fields=['request_time'], name='hotels_apir_request_f65147_idx'), - ), - ] diff --git a/hotels/migrations/0004_alter_reservation_room_number.py b/hotels/migrations/0004_alter_reservation_room_number.py deleted file mode 100644 index 1aed70e5..00000000 --- a/hotels/migrations/0004_alter_reservation_room_number.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-11 10:52 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('hotels', '0003_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='reservation', - name='room_number', - field=models.CharField(blank=True, max_length=255, null=True), - ), - ] diff --git a/hotels/migrations/0005_hotel_import_status_hotel_imported_at_and_more.py b/hotels/migrations/0005_hotel_import_status_hotel_imported_at_and_more.py deleted file mode 100644 index 91dde748..00000000 --- a/hotels/migrations/0005_hotel_import_status_hotel_imported_at_and_more.py +++ /dev/null @@ -1,29 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-13 01:01 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('hotels', '0004_alter_reservation_room_number'), - ] - - operations = [ - migrations.AddField( - model_name='hotel', - name='import_status', - field=models.CharField(choices=[('not_started', 'Не начат'), ('in_progress', 'В процессе'), ('completed', 'Завершен')], default='not_started', max_length=50, verbose_name='Статус импорта'), - ), - migrations.AddField( - model_name='hotel', - name='imported_at', - field=models.DateTimeField(blank=True, null=True, verbose_name='Дата импорта'), - ), - migrations.AddField( - model_name='hotel', - name='imported_from', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='imported_hotels', to='hotels.hotel', verbose_name='Импортированный отель'), - ), - ] diff --git a/hotels/migrations/0006_remove_hotel_api_remove_hotel_import_status_and_more.py b/hotels/migrations/0006_remove_hotel_api_remove_hotel_import_status_and_more.py deleted file mode 100644 index 308b9fde..00000000 --- a/hotels/migrations/0006_remove_hotel_api_remove_hotel_import_status_and_more.py +++ /dev/null @@ -1,33 +0,0 @@ -# hotels/migrations/0006_remove_hotel_import_status_remove_hotel_imported_at_and_more.py - -from django.db import migrations - -def remove_unused_fields(apps, schema_editor): - Hotel = apps.get_model('hotels', 'Hotel') - Hotel._meta.get_field('import_status').remote_field.model._meta.db_table - Hotel._meta.get_field('imported_at').remote_field.model._meta.db_table - -class Migration(migrations.Migration): - - dependencies = [ - ('hotels', '0005_hotel_import_status_hotel_imported_at_and_more'), - ] - - operations = [ - migrations.RemoveField( - model_name='hotel', - name='import_status', - ), - migrations.RemoveField( - model_name='hotel', - name='imported_at', - ), - migrations.RemoveField( - model_name='hotel', - name='imported_from', - ), - migrations.RemoveField( - model_name='hotel', - name='api', - ), - ] diff --git a/hotels/migrations/0007_hotel_hotel_id.py b/hotels/migrations/0007_hotel_hotel_id.py deleted file mode 100644 index 07fd5371..00000000 --- a/hotels/migrations/0007_hotel_hotel_id.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-14 02:43 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('hotels', '0006_remove_hotel_api_remove_hotel_import_status_and_more'), - ] - - operations = [ - migrations.AddField( - model_name='hotel', - name='hotel_id', - field=models.CharField(blank=True, max_length=255, null=True, unique=True, verbose_name='ID отеля'), - ), - ] diff --git a/hotels/migrations/0008_hotel_address_hotel_city_hotel_email_hotel_phone_and_more.py b/hotels/migrations/0008_hotel_address_hotel_city_hotel_email_hotel_phone_and_more.py deleted file mode 100644 index ceef8f0b..00000000 --- a/hotels/migrations/0008_hotel_address_hotel_city_hotel_email_hotel_phone_and_more.py +++ /dev/null @@ -1,38 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-14 02:55 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('hotels', '0007_hotel_hotel_id'), - ] - - operations = [ - migrations.AddField( - model_name='hotel', - name='address', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Адрес'), - ), - migrations.AddField( - model_name='hotel', - name='city', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Город'), - ), - migrations.AddField( - model_name='hotel', - name='email', - field=models.EmailField(blank=True, max_length=254, null=True, verbose_name='Email'), - ), - migrations.AddField( - model_name='hotel', - name='phone', - field=models.CharField(blank=True, max_length=50, null=True, verbose_name='Телефон'), - ), - migrations.AddField( - model_name='hotel', - name='timezone', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Часовой пояс'), - ), - ] diff --git a/hotels/migrations/0009_hotel_description.py b/hotels/migrations/0009_hotel_description.py deleted file mode 100644 index 53556159..00000000 --- a/hotels/migrations/0009_hotel_description.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-14 02:55 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('hotels', '0008_hotel_address_hotel_city_hotel_email_hotel_phone_and_more'), - ] - - operations = [ - migrations.AddField( - model_name='hotel', - name='description', - field=models.TextField(blank=True, null=True, verbose_name='Описание'), - ), - ] diff --git a/hotels/migrations/0010_alter_hotel_timezone.py b/hotels/migrations/0010_alter_hotel_timezone.py deleted file mode 100644 index cb898a00..00000000 --- a/hotels/migrations/0010_alter_hotel_timezone.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-14 03:00 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('hotels', '0009_hotel_description'), - ] - - operations = [ - migrations.AlterField( - model_name='hotel', - name='timezone', - field=models.CharField(choices=[('Africa/Abidjan', 'Africa/Abidjan'), ('Africa/Accra', 'Africa/Accra'), ('Africa/Addis_Ababa', 'Africa/Addis_Ababa'), ('Africa/Algiers', 'Africa/Algiers'), ('Africa/Asmara', 'Africa/Asmara'), ('Africa/Asmera', 'Africa/Asmera'), ('Africa/Bamako', 'Africa/Bamako'), ('Africa/Bangui', 'Africa/Bangui'), ('Africa/Banjul', 'Africa/Banjul'), ('Africa/Bissau', 'Africa/Bissau'), ('Africa/Blantyre', 'Africa/Blantyre'), ('Africa/Brazzaville', 'Africa/Brazzaville'), ('Africa/Bujumbura', 'Africa/Bujumbura'), ('Africa/Cairo', 'Africa/Cairo'), ('Africa/Casablanca', 'Africa/Casablanca'), ('Africa/Ceuta', 'Africa/Ceuta'), ('Africa/Conakry', 'Africa/Conakry'), ('Africa/Dakar', 'Africa/Dakar'), ('Africa/Dar_es_Salaam', 'Africa/Dar_es_Salaam'), ('Africa/Djibouti', 'Africa/Djibouti'), ('Africa/Douala', 'Africa/Douala'), ('Africa/El_Aaiun', 'Africa/El_Aaiun'), ('Africa/Freetown', 'Africa/Freetown'), ('Africa/Gaborone', 'Africa/Gaborone'), ('Africa/Harare', 'Africa/Harare'), ('Africa/Johannesburg', 'Africa/Johannesburg'), ('Africa/Juba', 'Africa/Juba'), ('Africa/Kampala', 'Africa/Kampala'), ('Africa/Khartoum', 'Africa/Khartoum'), ('Africa/Kigali', 'Africa/Kigali'), ('Africa/Kinshasa', 'Africa/Kinshasa'), ('Africa/Lagos', 'Africa/Lagos'), ('Africa/Libreville', 'Africa/Libreville'), ('Africa/Lome', 'Africa/Lome'), ('Africa/Luanda', 'Africa/Luanda'), ('Africa/Lubumbashi', 'Africa/Lubumbashi'), ('Africa/Lusaka', 'Africa/Lusaka'), ('Africa/Malabo', 'Africa/Malabo'), ('Africa/Maputo', 'Africa/Maputo'), ('Africa/Maseru', 'Africa/Maseru'), ('Africa/Mbabane', 'Africa/Mbabane'), ('Africa/Mogadishu', 'Africa/Mogadishu'), ('Africa/Monrovia', 'Africa/Monrovia'), ('Africa/Nairobi', 'Africa/Nairobi'), ('Africa/Ndjamena', 'Africa/Ndjamena'), ('Africa/Niamey', 'Africa/Niamey'), ('Africa/Nouakchott', 'Africa/Nouakchott'), ('Africa/Ouagadougou', 'Africa/Ouagadougou'), ('Africa/Porto-Novo', 'Africa/Porto-Novo'), ('Africa/Sao_Tome', 'Africa/Sao_Tome'), ('Africa/Timbuktu', 'Africa/Timbuktu'), ('Africa/Tripoli', 'Africa/Tripoli'), ('Africa/Tunis', 'Africa/Tunis'), ('Africa/Windhoek', 'Africa/Windhoek'), ('America/Adak', 'America/Adak'), ('America/Anchorage', 'America/Anchorage'), ('America/Anguilla', 'America/Anguilla'), ('America/Antigua', 'America/Antigua'), ('America/Araguaina', 'America/Araguaina'), ('America/Argentina/Buenos_Aires', 'America/Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'America/Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'America/Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'America/Argentina/Cordoba'), ('America/Argentina/Jujuy', 'America/Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'America/Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'America/Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'America/Argentina/Salta'), ('America/Argentina/San_Juan', 'America/Argentina/San_Juan'), ('America/Argentina/San_Luis', 'America/Argentina/San_Luis'), ('America/Argentina/Tucuman', 'America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia'), ('America/Aruba', 'America/Aruba'), ('America/Asuncion', 'America/Asuncion'), ('America/Atikokan', 'America/Atikokan'), ('America/Atka', 'America/Atka'), ('America/Bahia', 'America/Bahia'), ('America/Bahia_Banderas', 'America/Bahia_Banderas'), ('America/Barbados', 'America/Barbados'), ('America/Belem', 'America/Belem'), ('America/Belize', 'America/Belize'), ('America/Blanc-Sablon', 'America/Blanc-Sablon'), ('America/Boa_Vista', 'America/Boa_Vista'), ('America/Bogota', 'America/Bogota'), ('America/Boise', 'America/Boise'), ('America/Buenos_Aires', 'America/Buenos_Aires'), ('America/Cambridge_Bay', 'America/Cambridge_Bay'), ('America/Campo_Grande', 'America/Campo_Grande'), ('America/Cancun', 'America/Cancun'), ('America/Caracas', 'America/Caracas'), ('America/Catamarca', 'America/Catamarca'), ('America/Cayenne', 'America/Cayenne'), ('America/Cayman', 'America/Cayman'), ('America/Chicago', 'America/Chicago'), ('America/Chihuahua', 'America/Chihuahua'), ('America/Ciudad_Juarez', 'America/Ciudad_Juarez'), ('America/Coral_Harbour', 'America/Coral_Harbour'), ('America/Cordoba', 'America/Cordoba'), ('America/Costa_Rica', 'America/Costa_Rica'), ('America/Creston', 'America/Creston'), ('America/Cuiaba', 'America/Cuiaba'), ('America/Curacao', 'America/Curacao'), ('America/Danmarkshavn', 'America/Danmarkshavn'), ('America/Dawson', 'America/Dawson'), ('America/Dawson_Creek', 'America/Dawson_Creek'), ('America/Denver', 'America/Denver'), ('America/Detroit', 'America/Detroit'), ('America/Dominica', 'America/Dominica'), ('America/Edmonton', 'America/Edmonton'), ('America/Eirunepe', 'America/Eirunepe'), ('America/El_Salvador', 'America/El_Salvador'), ('America/Ensenada', 'America/Ensenada'), ('America/Fort_Nelson', 'America/Fort_Nelson'), ('America/Fort_Wayne', 'America/Fort_Wayne'), ('America/Fortaleza', 'America/Fortaleza'), ('America/Glace_Bay', 'America/Glace_Bay'), ('America/Godthab', 'America/Godthab'), ('America/Goose_Bay', 'America/Goose_Bay'), ('America/Grand_Turk', 'America/Grand_Turk'), ('America/Grenada', 'America/Grenada'), ('America/Guadeloupe', 'America/Guadeloupe'), ('America/Guatemala', 'America/Guatemala'), ('America/Guayaquil', 'America/Guayaquil'), ('America/Guyana', 'America/Guyana'), ('America/Halifax', 'America/Halifax'), ('America/Havana', 'America/Havana'), ('America/Hermosillo', 'America/Hermosillo'), ('America/Indiana/Indianapolis', 'America/Indiana/Indianapolis'), ('America/Indiana/Knox', 'America/Indiana/Knox'), ('America/Indiana/Marengo', 'America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'America/Indiana/Tell_City'), ('America/Indiana/Vevay', 'America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'America/Indiana/Winamac'), ('America/Indianapolis', 'America/Indianapolis'), ('America/Inuvik', 'America/Inuvik'), ('America/Iqaluit', 'America/Iqaluit'), ('America/Jamaica', 'America/Jamaica'), ('America/Jujuy', 'America/Jujuy'), ('America/Juneau', 'America/Juneau'), ('America/Kentucky/Louisville', 'America/Kentucky/Louisville'), ('America/Kentucky/Monticello', 'America/Kentucky/Monticello'), ('America/Knox_IN', 'America/Knox_IN'), ('America/Kralendijk', 'America/Kralendijk'), ('America/La_Paz', 'America/La_Paz'), ('America/Lima', 'America/Lima'), ('America/Los_Angeles', 'America/Los_Angeles'), ('America/Louisville', 'America/Louisville'), ('America/Lower_Princes', 'America/Lower_Princes'), ('America/Maceio', 'America/Maceio'), ('America/Managua', 'America/Managua'), ('America/Manaus', 'America/Manaus'), ('America/Marigot', 'America/Marigot'), ('America/Martinique', 'America/Martinique'), ('America/Matamoros', 'America/Matamoros'), ('America/Mazatlan', 'America/Mazatlan'), ('America/Mendoza', 'America/Mendoza'), ('America/Menominee', 'America/Menominee'), ('America/Merida', 'America/Merida'), ('America/Metlakatla', 'America/Metlakatla'), ('America/Mexico_City', 'America/Mexico_City'), ('America/Miquelon', 'America/Miquelon'), ('America/Moncton', 'America/Moncton'), ('America/Monterrey', 'America/Monterrey'), ('America/Montevideo', 'America/Montevideo'), ('America/Montreal', 'America/Montreal'), ('America/Montserrat', 'America/Montserrat'), ('America/Nassau', 'America/Nassau'), ('America/New_York', 'America/New_York'), ('America/Nipigon', 'America/Nipigon'), ('America/Nome', 'America/Nome'), ('America/Noronha', 'America/Noronha'), ('America/North_Dakota/Beulah', 'America/North_Dakota/Beulah'), ('America/North_Dakota/Center', 'America/North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'America/North_Dakota/New_Salem'), ('America/Nuuk', 'America/Nuuk'), ('America/Ojinaga', 'America/Ojinaga'), ('America/Panama', 'America/Panama'), ('America/Pangnirtung', 'America/Pangnirtung'), ('America/Paramaribo', 'America/Paramaribo'), ('America/Phoenix', 'America/Phoenix'), ('America/Port-au-Prince', 'America/Port-au-Prince'), ('America/Port_of_Spain', 'America/Port_of_Spain'), ('America/Porto_Acre', 'America/Porto_Acre'), ('America/Porto_Velho', 'America/Porto_Velho'), ('America/Puerto_Rico', 'America/Puerto_Rico'), ('America/Punta_Arenas', 'America/Punta_Arenas'), ('America/Rainy_River', 'America/Rainy_River'), ('America/Rankin_Inlet', 'America/Rankin_Inlet'), ('America/Recife', 'America/Recife'), ('America/Regina', 'America/Regina'), ('America/Resolute', 'America/Resolute'), ('America/Rio_Branco', 'America/Rio_Branco'), ('America/Rosario', 'America/Rosario'), ('America/Santa_Isabel', 'America/Santa_Isabel'), ('America/Santarem', 'America/Santarem'), ('America/Santiago', 'America/Santiago'), ('America/Santo_Domingo', 'America/Santo_Domingo'), ('America/Sao_Paulo', 'America/Sao_Paulo'), ('America/Scoresbysund', 'America/Scoresbysund'), ('America/Shiprock', 'America/Shiprock'), ('America/Sitka', 'America/Sitka'), ('America/St_Barthelemy', 'America/St_Barthelemy'), ('America/St_Johns', 'America/St_Johns'), ('America/St_Kitts', 'America/St_Kitts'), ('America/St_Lucia', 'America/St_Lucia'), ('America/St_Thomas', 'America/St_Thomas'), ('America/St_Vincent', 'America/St_Vincent'), ('America/Swift_Current', 'America/Swift_Current'), ('America/Tegucigalpa', 'America/Tegucigalpa'), ('America/Thule', 'America/Thule'), ('America/Thunder_Bay', 'America/Thunder_Bay'), ('America/Tijuana', 'America/Tijuana'), ('America/Toronto', 'America/Toronto'), ('America/Tortola', 'America/Tortola'), ('America/Vancouver', 'America/Vancouver'), ('America/Virgin', 'America/Virgin'), ('America/Whitehorse', 'America/Whitehorse'), ('America/Winnipeg', 'America/Winnipeg'), ('America/Yakutat', 'America/Yakutat'), ('America/Yellowknife', 'America/Yellowknife'), ('Antarctica/Casey', 'Antarctica/Casey'), ('Antarctica/Davis', 'Antarctica/Davis'), ('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville'), ('Antarctica/Macquarie', 'Antarctica/Macquarie'), ('Antarctica/Mawson', 'Antarctica/Mawson'), ('Antarctica/McMurdo', 'Antarctica/McMurdo'), ('Antarctica/Palmer', 'Antarctica/Palmer'), ('Antarctica/Rothera', 'Antarctica/Rothera'), ('Antarctica/South_Pole', 'Antarctica/South_Pole'), ('Antarctica/Syowa', 'Antarctica/Syowa'), ('Antarctica/Troll', 'Antarctica/Troll'), ('Antarctica/Vostok', 'Antarctica/Vostok'), ('Arctic/Longyearbyen', 'Arctic/Longyearbyen'), ('Asia/Aden', 'Asia/Aden'), ('Asia/Almaty', 'Asia/Almaty'), ('Asia/Amman', 'Asia/Amman'), ('Asia/Anadyr', 'Asia/Anadyr'), ('Asia/Aqtau', 'Asia/Aqtau'), ('Asia/Aqtobe', 'Asia/Aqtobe'), ('Asia/Ashgabat', 'Asia/Ashgabat'), ('Asia/Ashkhabad', 'Asia/Ashkhabad'), ('Asia/Atyrau', 'Asia/Atyrau'), ('Asia/Baghdad', 'Asia/Baghdad'), ('Asia/Bahrain', 'Asia/Bahrain'), ('Asia/Baku', 'Asia/Baku'), ('Asia/Bangkok', 'Asia/Bangkok'), ('Asia/Barnaul', 'Asia/Barnaul'), ('Asia/Beirut', 'Asia/Beirut'), ('Asia/Bishkek', 'Asia/Bishkek'), ('Asia/Brunei', 'Asia/Brunei'), ('Asia/Calcutta', 'Asia/Calcutta'), ('Asia/Chita', 'Asia/Chita'), ('Asia/Choibalsan', 'Asia/Choibalsan'), ('Asia/Chongqing', 'Asia/Chongqing'), ('Asia/Chungking', 'Asia/Chungking'), ('Asia/Colombo', 'Asia/Colombo'), ('Asia/Dacca', 'Asia/Dacca'), ('Asia/Damascus', 'Asia/Damascus'), ('Asia/Dhaka', 'Asia/Dhaka'), ('Asia/Dili', 'Asia/Dili'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Dushanbe', 'Asia/Dushanbe'), ('Asia/Famagusta', 'Asia/Famagusta'), ('Asia/Gaza', 'Asia/Gaza'), ('Asia/Harbin', 'Asia/Harbin'), ('Asia/Hebron', 'Asia/Hebron'), ('Asia/Ho_Chi_Minh', 'Asia/Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Asia/Hong_Kong'), ('Asia/Hovd', 'Asia/Hovd'), ('Asia/Irkutsk', 'Asia/Irkutsk'), ('Asia/Istanbul', 'Asia/Istanbul'), ('Asia/Jakarta', 'Asia/Jakarta'), ('Asia/Jayapura', 'Asia/Jayapura'), ('Asia/Jerusalem', 'Asia/Jerusalem'), ('Asia/Kabul', 'Asia/Kabul'), ('Asia/Kamchatka', 'Asia/Kamchatka'), ('Asia/Karachi', 'Asia/Karachi'), ('Asia/Kashgar', 'Asia/Kashgar'), ('Asia/Kathmandu', 'Asia/Kathmandu'), ('Asia/Katmandu', 'Asia/Katmandu'), ('Asia/Khandyga', 'Asia/Khandyga'), ('Asia/Kolkata', 'Asia/Kolkata'), ('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Asia/Kuala_Lumpur'), ('Asia/Kuching', 'Asia/Kuching'), ('Asia/Kuwait', 'Asia/Kuwait'), ('Asia/Macao', 'Asia/Macao'), ('Asia/Macau', 'Asia/Macau'), ('Asia/Magadan', 'Asia/Magadan'), ('Asia/Makassar', 'Asia/Makassar'), ('Asia/Manila', 'Asia/Manila'), ('Asia/Muscat', 'Asia/Muscat'), ('Asia/Nicosia', 'Asia/Nicosia'), ('Asia/Novokuznetsk', 'Asia/Novokuznetsk'), ('Asia/Novosibirsk', 'Asia/Novosibirsk'), ('Asia/Omsk', 'Asia/Omsk'), ('Asia/Oral', 'Asia/Oral'), ('Asia/Phnom_Penh', 'Asia/Phnom_Penh'), ('Asia/Pontianak', 'Asia/Pontianak'), ('Asia/Pyongyang', 'Asia/Pyongyang'), ('Asia/Qatar', 'Asia/Qatar'), ('Asia/Qostanay', 'Asia/Qostanay'), ('Asia/Qyzylorda', 'Asia/Qyzylorda'), ('Asia/Rangoon', 'Asia/Rangoon'), ('Asia/Riyadh', 'Asia/Riyadh'), ('Asia/Saigon', 'Asia/Saigon'), ('Asia/Sakhalin', 'Asia/Sakhalin'), ('Asia/Samarkand', 'Asia/Samarkand'), ('Asia/Seoul', 'Asia/Seoul'), ('Asia/Shanghai', 'Asia/Shanghai'), ('Asia/Singapore', 'Asia/Singapore'), ('Asia/Srednekolymsk', 'Asia/Srednekolymsk'), ('Asia/Taipei', 'Asia/Taipei'), ('Asia/Tashkent', 'Asia/Tashkent'), ('Asia/Tbilisi', 'Asia/Tbilisi'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Tel_Aviv', 'Asia/Tel_Aviv'), ('Asia/Thimbu', 'Asia/Thimbu'), ('Asia/Thimphu', 'Asia/Thimphu'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Asia/Tomsk', 'Asia/Tomsk'), ('Asia/Ujung_Pandang', 'Asia/Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar'), ('Asia/Ulan_Bator', 'Asia/Ulan_Bator'), ('Asia/Urumqi', 'Asia/Urumqi'), ('Asia/Ust-Nera', 'Asia/Ust-Nera'), ('Asia/Vientiane', 'Asia/Vientiane'), ('Asia/Vladivostok', 'Asia/Vladivostok'), ('Asia/Yakutsk', 'Asia/Yakutsk'), ('Asia/Yangon', 'Asia/Yangon'), ('Asia/Yekaterinburg', 'Asia/Yekaterinburg'), ('Asia/Yerevan', 'Asia/Yerevan'), ('Atlantic/Azores', 'Atlantic/Azores'), ('Atlantic/Bermuda', 'Atlantic/Bermuda'), ('Atlantic/Canary', 'Atlantic/Canary'), ('Atlantic/Cape_Verde', 'Atlantic/Cape_Verde'), ('Atlantic/Faeroe', 'Atlantic/Faeroe'), ('Atlantic/Faroe', 'Atlantic/Faroe'), ('Atlantic/Jan_Mayen', 'Atlantic/Jan_Mayen'), ('Atlantic/Madeira', 'Atlantic/Madeira'), ('Atlantic/Reykjavik', 'Atlantic/Reykjavik'), ('Atlantic/South_Georgia', 'Atlantic/South_Georgia'), ('Atlantic/St_Helena', 'Atlantic/St_Helena'), ('Atlantic/Stanley', 'Atlantic/Stanley'), ('Australia/ACT', 'Australia/ACT'), ('Australia/Adelaide', 'Australia/Adelaide'), ('Australia/Brisbane', 'Australia/Brisbane'), ('Australia/Broken_Hill', 'Australia/Broken_Hill'), ('Australia/Canberra', 'Australia/Canberra'), ('Australia/Currie', 'Australia/Currie'), ('Australia/Darwin', 'Australia/Darwin'), ('Australia/Eucla', 'Australia/Eucla'), ('Australia/Hobart', 'Australia/Hobart'), ('Australia/LHI', 'Australia/LHI'), ('Australia/Lindeman', 'Australia/Lindeman'), ('Australia/Lord_Howe', 'Australia/Lord_Howe'), ('Australia/Melbourne', 'Australia/Melbourne'), ('Australia/NSW', 'Australia/NSW'), ('Australia/North', 'Australia/North'), ('Australia/Perth', 'Australia/Perth'), ('Australia/Queensland', 'Australia/Queensland'), ('Australia/South', 'Australia/South'), ('Australia/Sydney', 'Australia/Sydney'), ('Australia/Tasmania', 'Australia/Tasmania'), ('Australia/Victoria', 'Australia/Victoria'), ('Australia/West', 'Australia/West'), ('Australia/Yancowinna', 'Australia/Yancowinna'), ('Brazil/Acre', 'Brazil/Acre'), ('Brazil/DeNoronha', 'Brazil/DeNoronha'), ('Brazil/East', 'Brazil/East'), ('Brazil/West', 'Brazil/West'), ('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Canada/Atlantic', 'Canada/Atlantic'), ('Canada/Central', 'Canada/Central'), ('Canada/Eastern', 'Canada/Eastern'), ('Canada/Mountain', 'Canada/Mountain'), ('Canada/Newfoundland', 'Canada/Newfoundland'), ('Canada/Pacific', 'Canada/Pacific'), ('Canada/Saskatchewan', 'Canada/Saskatchewan'), ('Canada/Yukon', 'Canada/Yukon'), ('Chile/Continental', 'Chile/Continental'), ('Chile/EasterIsland', 'Chile/EasterIsland'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('Etc/GMT', 'Etc/GMT'), ('Etc/GMT+0', 'Etc/GMT+0'), ('Etc/GMT+1', 'Etc/GMT+1'), ('Etc/GMT+10', 'Etc/GMT+10'), ('Etc/GMT+11', 'Etc/GMT+11'), ('Etc/GMT+12', 'Etc/GMT+12'), ('Etc/GMT+2', 'Etc/GMT+2'), ('Etc/GMT+3', 'Etc/GMT+3'), ('Etc/GMT+4', 'Etc/GMT+4'), ('Etc/GMT+5', 'Etc/GMT+5'), ('Etc/GMT+6', 'Etc/GMT+6'), ('Etc/GMT+7', 'Etc/GMT+7'), ('Etc/GMT+8', 'Etc/GMT+8'), ('Etc/GMT+9', 'Etc/GMT+9'), ('Etc/GMT-0', 'Etc/GMT-0'), ('Etc/GMT-1', 'Etc/GMT-1'), ('Etc/GMT-10', 'Etc/GMT-10'), ('Etc/GMT-11', 'Etc/GMT-11'), ('Etc/GMT-12', 'Etc/GMT-12'), ('Etc/GMT-13', 'Etc/GMT-13'), ('Etc/GMT-14', 'Etc/GMT-14'), ('Etc/GMT-2', 'Etc/GMT-2'), ('Etc/GMT-3', 'Etc/GMT-3'), ('Etc/GMT-4', 'Etc/GMT-4'), ('Etc/GMT-5', 'Etc/GMT-5'), ('Etc/GMT-6', 'Etc/GMT-6'), ('Etc/GMT-7', 'Etc/GMT-7'), ('Etc/GMT-8', 'Etc/GMT-8'), ('Etc/GMT-9', 'Etc/GMT-9'), ('Etc/GMT0', 'Etc/GMT0'), ('Etc/Greenwich', 'Etc/Greenwich'), ('Etc/UCT', 'Etc/UCT'), ('Etc/UTC', 'Etc/UTC'), ('Etc/Universal', 'Etc/Universal'), ('Etc/Zulu', 'Etc/Zulu'), ('Europe/Amsterdam', 'Europe/Amsterdam'), ('Europe/Andorra', 'Europe/Andorra'), ('Europe/Astrakhan', 'Europe/Astrakhan'), ('Europe/Athens', 'Europe/Athens'), ('Europe/Belfast', 'Europe/Belfast'), ('Europe/Belgrade', 'Europe/Belgrade'), ('Europe/Berlin', 'Europe/Berlin'), ('Europe/Bratislava', 'Europe/Bratislava'), ('Europe/Brussels', 'Europe/Brussels'), ('Europe/Bucharest', 'Europe/Bucharest'), ('Europe/Budapest', 'Europe/Budapest'), ('Europe/Busingen', 'Europe/Busingen'), ('Europe/Chisinau', 'Europe/Chisinau'), ('Europe/Copenhagen', 'Europe/Copenhagen'), ('Europe/Dublin', 'Europe/Dublin'), ('Europe/Gibraltar', 'Europe/Gibraltar'), ('Europe/Guernsey', 'Europe/Guernsey'), ('Europe/Helsinki', 'Europe/Helsinki'), ('Europe/Isle_of_Man', 'Europe/Isle_of_Man'), ('Europe/Istanbul', 'Europe/Istanbul'), ('Europe/Jersey', 'Europe/Jersey'), ('Europe/Kaliningrad', 'Europe/Kaliningrad'), ('Europe/Kiev', 'Europe/Kiev'), ('Europe/Kirov', 'Europe/Kirov'), ('Europe/Kyiv', 'Europe/Kyiv'), ('Europe/Lisbon', 'Europe/Lisbon'), ('Europe/Ljubljana', 'Europe/Ljubljana'), ('Europe/London', 'Europe/London'), ('Europe/Luxembourg', 'Europe/Luxembourg'), ('Europe/Madrid', 'Europe/Madrid'), ('Europe/Malta', 'Europe/Malta'), ('Europe/Mariehamn', 'Europe/Mariehamn'), ('Europe/Minsk', 'Europe/Minsk'), ('Europe/Monaco', 'Europe/Monaco'), ('Europe/Moscow', 'Europe/Moscow'), ('Europe/Nicosia', 'Europe/Nicosia'), ('Europe/Oslo', 'Europe/Oslo'), ('Europe/Paris', 'Europe/Paris'), ('Europe/Podgorica', 'Europe/Podgorica'), ('Europe/Prague', 'Europe/Prague'), ('Europe/Riga', 'Europe/Riga'), ('Europe/Rome', 'Europe/Rome'), ('Europe/Samara', 'Europe/Samara'), ('Europe/San_Marino', 'Europe/San_Marino'), ('Europe/Sarajevo', 'Europe/Sarajevo'), ('Europe/Saratov', 'Europe/Saratov'), ('Europe/Simferopol', 'Europe/Simferopol'), ('Europe/Skopje', 'Europe/Skopje'), ('Europe/Sofia', 'Europe/Sofia'), ('Europe/Stockholm', 'Europe/Stockholm'), ('Europe/Tallinn', 'Europe/Tallinn'), ('Europe/Tirane', 'Europe/Tirane'), ('Europe/Tiraspol', 'Europe/Tiraspol'), ('Europe/Ulyanovsk', 'Europe/Ulyanovsk'), ('Europe/Uzhgorod', 'Europe/Uzhgorod'), ('Europe/Vaduz', 'Europe/Vaduz'), ('Europe/Vatican', 'Europe/Vatican'), ('Europe/Vienna', 'Europe/Vienna'), ('Europe/Vilnius', 'Europe/Vilnius'), ('Europe/Volgograd', 'Europe/Volgograd'), ('Europe/Warsaw', 'Europe/Warsaw'), ('Europe/Zagreb', 'Europe/Zagreb'), ('Europe/Zaporozhye', 'Europe/Zaporozhye'), ('Europe/Zurich', 'Europe/Zurich'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('GMT', 'GMT'), ('GMT+0', 'GMT+0'), ('GMT-0', 'GMT-0'), ('GMT0', 'GMT0'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Indian/Antananarivo', 'Indian/Antananarivo'), ('Indian/Chagos', 'Indian/Chagos'), ('Indian/Christmas', 'Indian/Christmas'), ('Indian/Cocos', 'Indian/Cocos'), ('Indian/Comoro', 'Indian/Comoro'), ('Indian/Kerguelen', 'Indian/Kerguelen'), ('Indian/Mahe', 'Indian/Mahe'), ('Indian/Maldives', 'Indian/Maldives'), ('Indian/Mauritius', 'Indian/Mauritius'), ('Indian/Mayotte', 'Indian/Mayotte'), ('Indian/Reunion', 'Indian/Reunion'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('Mexico/BajaNorte', 'Mexico/BajaNorte'), ('Mexico/BajaSur', 'Mexico/BajaSur'), ('Mexico/General', 'Mexico/General'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Pacific/Apia', 'Pacific/Apia'), ('Pacific/Auckland', 'Pacific/Auckland'), ('Pacific/Bougainville', 'Pacific/Bougainville'), ('Pacific/Chatham', 'Pacific/Chatham'), ('Pacific/Chuuk', 'Pacific/Chuuk'), ('Pacific/Easter', 'Pacific/Easter'), ('Pacific/Efate', 'Pacific/Efate'), ('Pacific/Enderbury', 'Pacific/Enderbury'), ('Pacific/Fakaofo', 'Pacific/Fakaofo'), ('Pacific/Fiji', 'Pacific/Fiji'), ('Pacific/Funafuti', 'Pacific/Funafuti'), ('Pacific/Galapagos', 'Pacific/Galapagos'), ('Pacific/Gambier', 'Pacific/Gambier'), ('Pacific/Guadalcanal', 'Pacific/Guadalcanal'), ('Pacific/Guam', 'Pacific/Guam'), ('Pacific/Honolulu', 'Pacific/Honolulu'), ('Pacific/Johnston', 'Pacific/Johnston'), ('Pacific/Kanton', 'Pacific/Kanton'), ('Pacific/Kiritimati', 'Pacific/Kiritimati'), ('Pacific/Kosrae', 'Pacific/Kosrae'), ('Pacific/Kwajalein', 'Pacific/Kwajalein'), ('Pacific/Majuro', 'Pacific/Majuro'), ('Pacific/Marquesas', 'Pacific/Marquesas'), ('Pacific/Midway', 'Pacific/Midway'), ('Pacific/Nauru', 'Pacific/Nauru'), ('Pacific/Niue', 'Pacific/Niue'), ('Pacific/Norfolk', 'Pacific/Norfolk'), ('Pacific/Noumea', 'Pacific/Noumea'), ('Pacific/Pago_Pago', 'Pacific/Pago_Pago'), ('Pacific/Palau', 'Pacific/Palau'), ('Pacific/Pitcairn', 'Pacific/Pitcairn'), ('Pacific/Pohnpei', 'Pacific/Pohnpei'), ('Pacific/Ponape', 'Pacific/Ponape'), ('Pacific/Port_Moresby', 'Pacific/Port_Moresby'), ('Pacific/Rarotonga', 'Pacific/Rarotonga'), ('Pacific/Saipan', 'Pacific/Saipan'), ('Pacific/Samoa', 'Pacific/Samoa'), ('Pacific/Tahiti', 'Pacific/Tahiti'), ('Pacific/Tarawa', 'Pacific/Tarawa'), ('Pacific/Tongatapu', 'Pacific/Tongatapu'), ('Pacific/Truk', 'Pacific/Truk'), ('Pacific/Wake', 'Pacific/Wake'), ('Pacific/Wallis', 'Pacific/Wallis'), ('Pacific/Yap', 'Pacific/Yap'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('US/Alaska', 'US/Alaska'), ('US/Aleutian', 'US/Aleutian'), ('US/Arizona', 'US/Arizona'), ('US/Central', 'US/Central'), ('US/East-Indiana', 'US/East-Indiana'), ('US/Eastern', 'US/Eastern'), ('US/Hawaii', 'US/Hawaii'), ('US/Indiana-Starke', 'US/Indiana-Starke'), ('US/Michigan', 'US/Michigan'), ('US/Mountain', 'US/Mountain'), ('US/Pacific', 'US/Pacific'), ('US/Samoa', 'US/Samoa'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')], default='UTC', max_length=63), - ), - ] diff --git a/hotels/migrations/0011_room_alter_fraudlog_check_in_date_and_more.py b/hotels/migrations/0011_room_alter_fraudlog_check_in_date_and_more.py deleted file mode 100644 index 50aee261..00000000 --- a/hotels/migrations/0011_room_alter_fraudlog_check_in_date_and_more.py +++ /dev/null @@ -1,95 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-17 02:51 - -import django.core.validators -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('hotels', '0010_alter_hotel_timezone'), - ] - - operations = [ - migrations.CreateModel( - name='Room', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('number', models.CharField(max_length=50, verbose_name='Номер комнаты')), - ('external_id', models.CharField(max_length=255, unique=True, verbose_name='Внешний ID комнаты')), - ('description', models.TextField(blank=True, null=True, verbose_name='Описание')), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')), - ('updated_at', models.DateTimeField(auto_now=True, verbose_name='Дата обновления')), - ], - options={ - 'verbose_name': 'Номер', - 'verbose_name_plural': 'Номера', - }, - ), - migrations.AlterField( - model_name='fraudlog', - name='check_in_date', - field=models.DateField(verbose_name='Дата заезда'), - ), - migrations.AlterField( - model_name='fraudlog', - name='detected_at', - field=models.DateTimeField(auto_now_add=True, verbose_name='Дата обнаружения'), - ), - migrations.AlterField( - model_name='fraudlog', - name='message', - field=models.TextField(verbose_name='Сообщение'), - ), - migrations.AlterField( - model_name='fraudlog', - name='reservation_id', - field=models.BigIntegerField(verbose_name='ID бронирования'), - ), - migrations.AlterField( - model_name='guest', - name='phone', - field=models.CharField(blank=True, max_length=50, null=True, validators=[django.core.validators.RegexValidator(message='Введите корректный номер телефона (до 15 цифр).', regex='^\\+?1?\\d{9,15}$')], verbose_name='Телефон'), - ), - migrations.AlterField( - model_name='hotel', - name='phone', - field=models.CharField(blank=True, max_length=50, null=True, validators=[django.core.validators.RegexValidator(message='Введите корректный номер телефона (до 15 цифр).', regex='^\\+?1?\\d{9,15}$')], verbose_name='Телефон'), - ), - migrations.AlterField( - model_name='hotel', - name='timezone', - field=models.CharField(choices=[('Africa/Abidjan', 'Africa/Abidjan'), ('Africa/Accra', 'Africa/Accra'), ('Africa/Addis_Ababa', 'Africa/Addis_Ababa'), ('Africa/Algiers', 'Africa/Algiers'), ('Africa/Asmara', 'Africa/Asmara'), ('Africa/Asmera', 'Africa/Asmera'), ('Africa/Bamako', 'Africa/Bamako'), ('Africa/Bangui', 'Africa/Bangui'), ('Africa/Banjul', 'Africa/Banjul'), ('Africa/Bissau', 'Africa/Bissau'), ('Africa/Blantyre', 'Africa/Blantyre'), ('Africa/Brazzaville', 'Africa/Brazzaville'), ('Africa/Bujumbura', 'Africa/Bujumbura'), ('Africa/Cairo', 'Africa/Cairo'), ('Africa/Casablanca', 'Africa/Casablanca'), ('Africa/Ceuta', 'Africa/Ceuta'), ('Africa/Conakry', 'Africa/Conakry'), ('Africa/Dakar', 'Africa/Dakar'), ('Africa/Dar_es_Salaam', 'Africa/Dar_es_Salaam'), ('Africa/Djibouti', 'Africa/Djibouti'), ('Africa/Douala', 'Africa/Douala'), ('Africa/El_Aaiun', 'Africa/El_Aaiun'), ('Africa/Freetown', 'Africa/Freetown'), ('Africa/Gaborone', 'Africa/Gaborone'), ('Africa/Harare', 'Africa/Harare'), ('Africa/Johannesburg', 'Africa/Johannesburg'), ('Africa/Juba', 'Africa/Juba'), ('Africa/Kampala', 'Africa/Kampala'), ('Africa/Khartoum', 'Africa/Khartoum'), ('Africa/Kigali', 'Africa/Kigali'), ('Africa/Kinshasa', 'Africa/Kinshasa'), ('Africa/Lagos', 'Africa/Lagos'), ('Africa/Libreville', 'Africa/Libreville'), ('Africa/Lome', 'Africa/Lome'), ('Africa/Luanda', 'Africa/Luanda'), ('Africa/Lubumbashi', 'Africa/Lubumbashi'), ('Africa/Lusaka', 'Africa/Lusaka'), ('Africa/Malabo', 'Africa/Malabo'), ('Africa/Maputo', 'Africa/Maputo'), ('Africa/Maseru', 'Africa/Maseru'), ('Africa/Mbabane', 'Africa/Mbabane'), ('Africa/Mogadishu', 'Africa/Mogadishu'), ('Africa/Monrovia', 'Africa/Monrovia'), ('Africa/Nairobi', 'Africa/Nairobi'), ('Africa/Ndjamena', 'Africa/Ndjamena'), ('Africa/Niamey', 'Africa/Niamey'), ('Africa/Nouakchott', 'Africa/Nouakchott'), ('Africa/Ouagadougou', 'Africa/Ouagadougou'), ('Africa/Porto-Novo', 'Africa/Porto-Novo'), ('Africa/Sao_Tome', 'Africa/Sao_Tome'), ('Africa/Timbuktu', 'Africa/Timbuktu'), ('Africa/Tripoli', 'Africa/Tripoli'), ('Africa/Tunis', 'Africa/Tunis'), ('Africa/Windhoek', 'Africa/Windhoek'), ('America/Adak', 'America/Adak'), ('America/Anchorage', 'America/Anchorage'), ('America/Anguilla', 'America/Anguilla'), ('America/Antigua', 'America/Antigua'), ('America/Araguaina', 'America/Araguaina'), ('America/Argentina/Buenos_Aires', 'America/Argentina/Buenos_Aires'), ('America/Argentina/Catamarca', 'America/Argentina/Catamarca'), ('America/Argentina/ComodRivadavia', 'America/Argentina/ComodRivadavia'), ('America/Argentina/Cordoba', 'America/Argentina/Cordoba'), ('America/Argentina/Jujuy', 'America/Argentina/Jujuy'), ('America/Argentina/La_Rioja', 'America/Argentina/La_Rioja'), ('America/Argentina/Mendoza', 'America/Argentina/Mendoza'), ('America/Argentina/Rio_Gallegos', 'America/Argentina/Rio_Gallegos'), ('America/Argentina/Salta', 'America/Argentina/Salta'), ('America/Argentina/San_Juan', 'America/Argentina/San_Juan'), ('America/Argentina/San_Luis', 'America/Argentina/San_Luis'), ('America/Argentina/Tucuman', 'America/Argentina/Tucuman'), ('America/Argentina/Ushuaia', 'America/Argentina/Ushuaia'), ('America/Aruba', 'America/Aruba'), ('America/Asuncion', 'America/Asuncion'), ('America/Atikokan', 'America/Atikokan'), ('America/Atka', 'America/Atka'), ('America/Bahia', 'America/Bahia'), ('America/Bahia_Banderas', 'America/Bahia_Banderas'), ('America/Barbados', 'America/Barbados'), ('America/Belem', 'America/Belem'), ('America/Belize', 'America/Belize'), ('America/Blanc-Sablon', 'America/Blanc-Sablon'), ('America/Boa_Vista', 'America/Boa_Vista'), ('America/Bogota', 'America/Bogota'), ('America/Boise', 'America/Boise'), ('America/Buenos_Aires', 'America/Buenos_Aires'), ('America/Cambridge_Bay', 'America/Cambridge_Bay'), ('America/Campo_Grande', 'America/Campo_Grande'), ('America/Cancun', 'America/Cancun'), ('America/Caracas', 'America/Caracas'), ('America/Catamarca', 'America/Catamarca'), ('America/Cayenne', 'America/Cayenne'), ('America/Cayman', 'America/Cayman'), ('America/Chicago', 'America/Chicago'), ('America/Chihuahua', 'America/Chihuahua'), ('America/Ciudad_Juarez', 'America/Ciudad_Juarez'), ('America/Coral_Harbour', 'America/Coral_Harbour'), ('America/Cordoba', 'America/Cordoba'), ('America/Costa_Rica', 'America/Costa_Rica'), ('America/Creston', 'America/Creston'), ('America/Cuiaba', 'America/Cuiaba'), ('America/Curacao', 'America/Curacao'), ('America/Danmarkshavn', 'America/Danmarkshavn'), ('America/Dawson', 'America/Dawson'), ('America/Dawson_Creek', 'America/Dawson_Creek'), ('America/Denver', 'America/Denver'), ('America/Detroit', 'America/Detroit'), ('America/Dominica', 'America/Dominica'), ('America/Edmonton', 'America/Edmonton'), ('America/Eirunepe', 'America/Eirunepe'), ('America/El_Salvador', 'America/El_Salvador'), ('America/Ensenada', 'America/Ensenada'), ('America/Fort_Nelson', 'America/Fort_Nelson'), ('America/Fort_Wayne', 'America/Fort_Wayne'), ('America/Fortaleza', 'America/Fortaleza'), ('America/Glace_Bay', 'America/Glace_Bay'), ('America/Godthab', 'America/Godthab'), ('America/Goose_Bay', 'America/Goose_Bay'), ('America/Grand_Turk', 'America/Grand_Turk'), ('America/Grenada', 'America/Grenada'), ('America/Guadeloupe', 'America/Guadeloupe'), ('America/Guatemala', 'America/Guatemala'), ('America/Guayaquil', 'America/Guayaquil'), ('America/Guyana', 'America/Guyana'), ('America/Halifax', 'America/Halifax'), ('America/Havana', 'America/Havana'), ('America/Hermosillo', 'America/Hermosillo'), ('America/Indiana/Indianapolis', 'America/Indiana/Indianapolis'), ('America/Indiana/Knox', 'America/Indiana/Knox'), ('America/Indiana/Marengo', 'America/Indiana/Marengo'), ('America/Indiana/Petersburg', 'America/Indiana/Petersburg'), ('America/Indiana/Tell_City', 'America/Indiana/Tell_City'), ('America/Indiana/Vevay', 'America/Indiana/Vevay'), ('America/Indiana/Vincennes', 'America/Indiana/Vincennes'), ('America/Indiana/Winamac', 'America/Indiana/Winamac'), ('America/Indianapolis', 'America/Indianapolis'), ('America/Inuvik', 'America/Inuvik'), ('America/Iqaluit', 'America/Iqaluit'), ('America/Jamaica', 'America/Jamaica'), ('America/Jujuy', 'America/Jujuy'), ('America/Juneau', 'America/Juneau'), ('America/Kentucky/Louisville', 'America/Kentucky/Louisville'), ('America/Kentucky/Monticello', 'America/Kentucky/Monticello'), ('America/Knox_IN', 'America/Knox_IN'), ('America/Kralendijk', 'America/Kralendijk'), ('America/La_Paz', 'America/La_Paz'), ('America/Lima', 'America/Lima'), ('America/Los_Angeles', 'America/Los_Angeles'), ('America/Louisville', 'America/Louisville'), ('America/Lower_Princes', 'America/Lower_Princes'), ('America/Maceio', 'America/Maceio'), ('America/Managua', 'America/Managua'), ('America/Manaus', 'America/Manaus'), ('America/Marigot', 'America/Marigot'), ('America/Martinique', 'America/Martinique'), ('America/Matamoros', 'America/Matamoros'), ('America/Mazatlan', 'America/Mazatlan'), ('America/Mendoza', 'America/Mendoza'), ('America/Menominee', 'America/Menominee'), ('America/Merida', 'America/Merida'), ('America/Metlakatla', 'America/Metlakatla'), ('America/Mexico_City', 'America/Mexico_City'), ('America/Miquelon', 'America/Miquelon'), ('America/Moncton', 'America/Moncton'), ('America/Monterrey', 'America/Monterrey'), ('America/Montevideo', 'America/Montevideo'), ('America/Montreal', 'America/Montreal'), ('America/Montserrat', 'America/Montserrat'), ('America/Nassau', 'America/Nassau'), ('America/New_York', 'America/New_York'), ('America/Nipigon', 'America/Nipigon'), ('America/Nome', 'America/Nome'), ('America/Noronha', 'America/Noronha'), ('America/North_Dakota/Beulah', 'America/North_Dakota/Beulah'), ('America/North_Dakota/Center', 'America/North_Dakota/Center'), ('America/North_Dakota/New_Salem', 'America/North_Dakota/New_Salem'), ('America/Nuuk', 'America/Nuuk'), ('America/Ojinaga', 'America/Ojinaga'), ('America/Panama', 'America/Panama'), ('America/Pangnirtung', 'America/Pangnirtung'), ('America/Paramaribo', 'America/Paramaribo'), ('America/Phoenix', 'America/Phoenix'), ('America/Port-au-Prince', 'America/Port-au-Prince'), ('America/Port_of_Spain', 'America/Port_of_Spain'), ('America/Porto_Acre', 'America/Porto_Acre'), ('America/Porto_Velho', 'America/Porto_Velho'), ('America/Puerto_Rico', 'America/Puerto_Rico'), ('America/Punta_Arenas', 'America/Punta_Arenas'), ('America/Rainy_River', 'America/Rainy_River'), ('America/Rankin_Inlet', 'America/Rankin_Inlet'), ('America/Recife', 'America/Recife'), ('America/Regina', 'America/Regina'), ('America/Resolute', 'America/Resolute'), ('America/Rio_Branco', 'America/Rio_Branco'), ('America/Rosario', 'America/Rosario'), ('America/Santa_Isabel', 'America/Santa_Isabel'), ('America/Santarem', 'America/Santarem'), ('America/Santiago', 'America/Santiago'), ('America/Santo_Domingo', 'America/Santo_Domingo'), ('America/Sao_Paulo', 'America/Sao_Paulo'), ('America/Scoresbysund', 'America/Scoresbysund'), ('America/Shiprock', 'America/Shiprock'), ('America/Sitka', 'America/Sitka'), ('America/St_Barthelemy', 'America/St_Barthelemy'), ('America/St_Johns', 'America/St_Johns'), ('America/St_Kitts', 'America/St_Kitts'), ('America/St_Lucia', 'America/St_Lucia'), ('America/St_Thomas', 'America/St_Thomas'), ('America/St_Vincent', 'America/St_Vincent'), ('America/Swift_Current', 'America/Swift_Current'), ('America/Tegucigalpa', 'America/Tegucigalpa'), ('America/Thule', 'America/Thule'), ('America/Thunder_Bay', 'America/Thunder_Bay'), ('America/Tijuana', 'America/Tijuana'), ('America/Toronto', 'America/Toronto'), ('America/Tortola', 'America/Tortola'), ('America/Vancouver', 'America/Vancouver'), ('America/Virgin', 'America/Virgin'), ('America/Whitehorse', 'America/Whitehorse'), ('America/Winnipeg', 'America/Winnipeg'), ('America/Yakutat', 'America/Yakutat'), ('America/Yellowknife', 'America/Yellowknife'), ('Antarctica/Casey', 'Antarctica/Casey'), ('Antarctica/Davis', 'Antarctica/Davis'), ('Antarctica/DumontDUrville', 'Antarctica/DumontDUrville'), ('Antarctica/Macquarie', 'Antarctica/Macquarie'), ('Antarctica/Mawson', 'Antarctica/Mawson'), ('Antarctica/McMurdo', 'Antarctica/McMurdo'), ('Antarctica/Palmer', 'Antarctica/Palmer'), ('Antarctica/Rothera', 'Antarctica/Rothera'), ('Antarctica/South_Pole', 'Antarctica/South_Pole'), ('Antarctica/Syowa', 'Antarctica/Syowa'), ('Antarctica/Troll', 'Antarctica/Troll'), ('Antarctica/Vostok', 'Antarctica/Vostok'), ('Arctic/Longyearbyen', 'Arctic/Longyearbyen'), ('Asia/Aden', 'Asia/Aden'), ('Asia/Almaty', 'Asia/Almaty'), ('Asia/Amman', 'Asia/Amman'), ('Asia/Anadyr', 'Asia/Anadyr'), ('Asia/Aqtau', 'Asia/Aqtau'), ('Asia/Aqtobe', 'Asia/Aqtobe'), ('Asia/Ashgabat', 'Asia/Ashgabat'), ('Asia/Ashkhabad', 'Asia/Ashkhabad'), ('Asia/Atyrau', 'Asia/Atyrau'), ('Asia/Baghdad', 'Asia/Baghdad'), ('Asia/Bahrain', 'Asia/Bahrain'), ('Asia/Baku', 'Asia/Baku'), ('Asia/Bangkok', 'Asia/Bangkok'), ('Asia/Barnaul', 'Asia/Barnaul'), ('Asia/Beirut', 'Asia/Beirut'), ('Asia/Bishkek', 'Asia/Bishkek'), ('Asia/Brunei', 'Asia/Brunei'), ('Asia/Calcutta', 'Asia/Calcutta'), ('Asia/Chita', 'Asia/Chita'), ('Asia/Choibalsan', 'Asia/Choibalsan'), ('Asia/Chongqing', 'Asia/Chongqing'), ('Asia/Chungking', 'Asia/Chungking'), ('Asia/Colombo', 'Asia/Colombo'), ('Asia/Dacca', 'Asia/Dacca'), ('Asia/Damascus', 'Asia/Damascus'), ('Asia/Dhaka', 'Asia/Dhaka'), ('Asia/Dili', 'Asia/Dili'), ('Asia/Dubai', 'Asia/Dubai'), ('Asia/Dushanbe', 'Asia/Dushanbe'), ('Asia/Famagusta', 'Asia/Famagusta'), ('Asia/Gaza', 'Asia/Gaza'), ('Asia/Harbin', 'Asia/Harbin'), ('Asia/Hebron', 'Asia/Hebron'), ('Asia/Ho_Chi_Minh', 'Asia/Ho_Chi_Minh'), ('Asia/Hong_Kong', 'Asia/Hong_Kong'), ('Asia/Hovd', 'Asia/Hovd'), ('Asia/Irkutsk', 'Asia/Irkutsk'), ('Asia/Istanbul', 'Asia/Istanbul'), ('Asia/Jakarta', 'Asia/Jakarta'), ('Asia/Jayapura', 'Asia/Jayapura'), ('Asia/Jerusalem', 'Asia/Jerusalem'), ('Asia/Kabul', 'Asia/Kabul'), ('Asia/Kamchatka', 'Asia/Kamchatka'), ('Asia/Karachi', 'Asia/Karachi'), ('Asia/Kashgar', 'Asia/Kashgar'), ('Asia/Kathmandu', 'Asia/Kathmandu'), ('Asia/Katmandu', 'Asia/Katmandu'), ('Asia/Khandyga', 'Asia/Khandyga'), ('Asia/Kolkata', 'Asia/Kolkata'), ('Asia/Krasnoyarsk', 'Asia/Krasnoyarsk'), ('Asia/Kuala_Lumpur', 'Asia/Kuala_Lumpur'), ('Asia/Kuching', 'Asia/Kuching'), ('Asia/Kuwait', 'Asia/Kuwait'), ('Asia/Macao', 'Asia/Macao'), ('Asia/Macau', 'Asia/Macau'), ('Asia/Magadan', 'Asia/Magadan'), ('Asia/Makassar', 'Asia/Makassar'), ('Asia/Manila', 'Asia/Manila'), ('Asia/Muscat', 'Asia/Muscat'), ('Asia/Nicosia', 'Asia/Nicosia'), ('Asia/Novokuznetsk', 'Asia/Novokuznetsk'), ('Asia/Novosibirsk', 'Asia/Novosibirsk'), ('Asia/Omsk', 'Asia/Omsk'), ('Asia/Oral', 'Asia/Oral'), ('Asia/Phnom_Penh', 'Asia/Phnom_Penh'), ('Asia/Pontianak', 'Asia/Pontianak'), ('Asia/Pyongyang', 'Asia/Pyongyang'), ('Asia/Qatar', 'Asia/Qatar'), ('Asia/Qostanay', 'Asia/Qostanay'), ('Asia/Qyzylorda', 'Asia/Qyzylorda'), ('Asia/Rangoon', 'Asia/Rangoon'), ('Asia/Riyadh', 'Asia/Riyadh'), ('Asia/Saigon', 'Asia/Saigon'), ('Asia/Sakhalin', 'Asia/Sakhalin'), ('Asia/Samarkand', 'Asia/Samarkand'), ('Asia/Seoul', 'Asia/Seoul'), ('Asia/Shanghai', 'Asia/Shanghai'), ('Asia/Singapore', 'Asia/Singapore'), ('Asia/Srednekolymsk', 'Asia/Srednekolymsk'), ('Asia/Taipei', 'Asia/Taipei'), ('Asia/Tashkent', 'Asia/Tashkent'), ('Asia/Tbilisi', 'Asia/Tbilisi'), ('Asia/Tehran', 'Asia/Tehran'), ('Asia/Tel_Aviv', 'Asia/Tel_Aviv'), ('Asia/Thimbu', 'Asia/Thimbu'), ('Asia/Thimphu', 'Asia/Thimphu'), ('Asia/Tokyo', 'Asia/Tokyo'), ('Asia/Tomsk', 'Asia/Tomsk'), ('Asia/Ujung_Pandang', 'Asia/Ujung_Pandang'), ('Asia/Ulaanbaatar', 'Asia/Ulaanbaatar'), ('Asia/Ulan_Bator', 'Asia/Ulan_Bator'), ('Asia/Urumqi', 'Asia/Urumqi'), ('Asia/Ust-Nera', 'Asia/Ust-Nera'), ('Asia/Vientiane', 'Asia/Vientiane'), ('Asia/Vladivostok', 'Asia/Vladivostok'), ('Asia/Yakutsk', 'Asia/Yakutsk'), ('Asia/Yangon', 'Asia/Yangon'), ('Asia/Yekaterinburg', 'Asia/Yekaterinburg'), ('Asia/Yerevan', 'Asia/Yerevan'), ('Atlantic/Azores', 'Atlantic/Azores'), ('Atlantic/Bermuda', 'Atlantic/Bermuda'), ('Atlantic/Canary', 'Atlantic/Canary'), ('Atlantic/Cape_Verde', 'Atlantic/Cape_Verde'), ('Atlantic/Faeroe', 'Atlantic/Faeroe'), ('Atlantic/Faroe', 'Atlantic/Faroe'), ('Atlantic/Jan_Mayen', 'Atlantic/Jan_Mayen'), ('Atlantic/Madeira', 'Atlantic/Madeira'), ('Atlantic/Reykjavik', 'Atlantic/Reykjavik'), ('Atlantic/South_Georgia', 'Atlantic/South_Georgia'), ('Atlantic/St_Helena', 'Atlantic/St_Helena'), ('Atlantic/Stanley', 'Atlantic/Stanley'), ('Australia/ACT', 'Australia/ACT'), ('Australia/Adelaide', 'Australia/Adelaide'), ('Australia/Brisbane', 'Australia/Brisbane'), ('Australia/Broken_Hill', 'Australia/Broken_Hill'), ('Australia/Canberra', 'Australia/Canberra'), ('Australia/Currie', 'Australia/Currie'), ('Australia/Darwin', 'Australia/Darwin'), ('Australia/Eucla', 'Australia/Eucla'), ('Australia/Hobart', 'Australia/Hobart'), ('Australia/LHI', 'Australia/LHI'), ('Australia/Lindeman', 'Australia/Lindeman'), ('Australia/Lord_Howe', 'Australia/Lord_Howe'), ('Australia/Melbourne', 'Australia/Melbourne'), ('Australia/NSW', 'Australia/NSW'), ('Australia/North', 'Australia/North'), ('Australia/Perth', 'Australia/Perth'), ('Australia/Queensland', 'Australia/Queensland'), ('Australia/South', 'Australia/South'), ('Australia/Sydney', 'Australia/Sydney'), ('Australia/Tasmania', 'Australia/Tasmania'), ('Australia/Victoria', 'Australia/Victoria'), ('Australia/West', 'Australia/West'), ('Australia/Yancowinna', 'Australia/Yancowinna'), ('Brazil/Acre', 'Brazil/Acre'), ('Brazil/DeNoronha', 'Brazil/DeNoronha'), ('Brazil/East', 'Brazil/East'), ('Brazil/West', 'Brazil/West'), ('CET', 'CET'), ('CST6CDT', 'CST6CDT'), ('Canada/Atlantic', 'Canada/Atlantic'), ('Canada/Central', 'Canada/Central'), ('Canada/Eastern', 'Canada/Eastern'), ('Canada/Mountain', 'Canada/Mountain'), ('Canada/Newfoundland', 'Canada/Newfoundland'), ('Canada/Pacific', 'Canada/Pacific'), ('Canada/Saskatchewan', 'Canada/Saskatchewan'), ('Canada/Yukon', 'Canada/Yukon'), ('Chile/Continental', 'Chile/Continental'), ('Chile/EasterIsland', 'Chile/EasterIsland'), ('Cuba', 'Cuba'), ('EET', 'EET'), ('EST', 'EST'), ('EST5EDT', 'EST5EDT'), ('Egypt', 'Egypt'), ('Eire', 'Eire'), ('Etc/GMT', 'Etc/GMT'), ('Etc/GMT+0', 'Etc/GMT+0'), ('Etc/GMT+1', 'Etc/GMT+1'), ('Etc/GMT+10', 'Etc/GMT+10'), ('Etc/GMT+11', 'Etc/GMT+11'), ('Etc/GMT+12', 'Etc/GMT+12'), ('Etc/GMT+2', 'Etc/GMT+2'), ('Etc/GMT+3', 'Etc/GMT+3'), ('Etc/GMT+4', 'Etc/GMT+4'), ('Etc/GMT+5', 'Etc/GMT+5'), ('Etc/GMT+6', 'Etc/GMT+6'), ('Etc/GMT+7', 'Etc/GMT+7'), ('Etc/GMT+8', 'Etc/GMT+8'), ('Etc/GMT+9', 'Etc/GMT+9'), ('Etc/GMT-0', 'Etc/GMT-0'), ('Etc/GMT-1', 'Etc/GMT-1'), ('Etc/GMT-10', 'Etc/GMT-10'), ('Etc/GMT-11', 'Etc/GMT-11'), ('Etc/GMT-12', 'Etc/GMT-12'), ('Etc/GMT-13', 'Etc/GMT-13'), ('Etc/GMT-14', 'Etc/GMT-14'), ('Etc/GMT-2', 'Etc/GMT-2'), ('Etc/GMT-3', 'Etc/GMT-3'), ('Etc/GMT-4', 'Etc/GMT-4'), ('Etc/GMT-5', 'Etc/GMT-5'), ('Etc/GMT-6', 'Etc/GMT-6'), ('Etc/GMT-7', 'Etc/GMT-7'), ('Etc/GMT-8', 'Etc/GMT-8'), ('Etc/GMT-9', 'Etc/GMT-9'), ('Etc/GMT0', 'Etc/GMT0'), ('Etc/Greenwich', 'Etc/Greenwich'), ('Etc/UCT', 'Etc/UCT'), ('Etc/UTC', 'Etc/UTC'), ('Etc/Universal', 'Etc/Universal'), ('Etc/Zulu', 'Etc/Zulu'), ('Europe/Amsterdam', 'Europe/Amsterdam'), ('Europe/Andorra', 'Europe/Andorra'), ('Europe/Astrakhan', 'Europe/Astrakhan'), ('Europe/Athens', 'Europe/Athens'), ('Europe/Belfast', 'Europe/Belfast'), ('Europe/Belgrade', 'Europe/Belgrade'), ('Europe/Berlin', 'Europe/Berlin'), ('Europe/Bratislava', 'Europe/Bratislava'), ('Europe/Brussels', 'Europe/Brussels'), ('Europe/Bucharest', 'Europe/Bucharest'), ('Europe/Budapest', 'Europe/Budapest'), ('Europe/Busingen', 'Europe/Busingen'), ('Europe/Chisinau', 'Europe/Chisinau'), ('Europe/Copenhagen', 'Europe/Copenhagen'), ('Europe/Dublin', 'Europe/Dublin'), ('Europe/Gibraltar', 'Europe/Gibraltar'), ('Europe/Guernsey', 'Europe/Guernsey'), ('Europe/Helsinki', 'Europe/Helsinki'), ('Europe/Isle_of_Man', 'Europe/Isle_of_Man'), ('Europe/Istanbul', 'Europe/Istanbul'), ('Europe/Jersey', 'Europe/Jersey'), ('Europe/Kaliningrad', 'Europe/Kaliningrad'), ('Europe/Kiev', 'Europe/Kiev'), ('Europe/Kirov', 'Europe/Kirov'), ('Europe/Kyiv', 'Europe/Kyiv'), ('Europe/Lisbon', 'Europe/Lisbon'), ('Europe/Ljubljana', 'Europe/Ljubljana'), ('Europe/London', 'Europe/London'), ('Europe/Luxembourg', 'Europe/Luxembourg'), ('Europe/Madrid', 'Europe/Madrid'), ('Europe/Malta', 'Europe/Malta'), ('Europe/Mariehamn', 'Europe/Mariehamn'), ('Europe/Minsk', 'Europe/Minsk'), ('Europe/Monaco', 'Europe/Monaco'), ('Europe/Moscow', 'Europe/Moscow'), ('Europe/Nicosia', 'Europe/Nicosia'), ('Europe/Oslo', 'Europe/Oslo'), ('Europe/Paris', 'Europe/Paris'), ('Europe/Podgorica', 'Europe/Podgorica'), ('Europe/Prague', 'Europe/Prague'), ('Europe/Riga', 'Europe/Riga'), ('Europe/Rome', 'Europe/Rome'), ('Europe/Samara', 'Europe/Samara'), ('Europe/San_Marino', 'Europe/San_Marino'), ('Europe/Sarajevo', 'Europe/Sarajevo'), ('Europe/Saratov', 'Europe/Saratov'), ('Europe/Simferopol', 'Europe/Simferopol'), ('Europe/Skopje', 'Europe/Skopje'), ('Europe/Sofia', 'Europe/Sofia'), ('Europe/Stockholm', 'Europe/Stockholm'), ('Europe/Tallinn', 'Europe/Tallinn'), ('Europe/Tirane', 'Europe/Tirane'), ('Europe/Tiraspol', 'Europe/Tiraspol'), ('Europe/Ulyanovsk', 'Europe/Ulyanovsk'), ('Europe/Uzhgorod', 'Europe/Uzhgorod'), ('Europe/Vaduz', 'Europe/Vaduz'), ('Europe/Vatican', 'Europe/Vatican'), ('Europe/Vienna', 'Europe/Vienna'), ('Europe/Vilnius', 'Europe/Vilnius'), ('Europe/Volgograd', 'Europe/Volgograd'), ('Europe/Warsaw', 'Europe/Warsaw'), ('Europe/Zagreb', 'Europe/Zagreb'), ('Europe/Zaporozhye', 'Europe/Zaporozhye'), ('Europe/Zurich', 'Europe/Zurich'), ('GB', 'GB'), ('GB-Eire', 'GB-Eire'), ('GMT', 'GMT'), ('GMT+0', 'GMT+0'), ('GMT-0', 'GMT-0'), ('GMT0', 'GMT0'), ('Greenwich', 'Greenwich'), ('HST', 'HST'), ('Hongkong', 'Hongkong'), ('Iceland', 'Iceland'), ('Indian/Antananarivo', 'Indian/Antananarivo'), ('Indian/Chagos', 'Indian/Chagos'), ('Indian/Christmas', 'Indian/Christmas'), ('Indian/Cocos', 'Indian/Cocos'), ('Indian/Comoro', 'Indian/Comoro'), ('Indian/Kerguelen', 'Indian/Kerguelen'), ('Indian/Mahe', 'Indian/Mahe'), ('Indian/Maldives', 'Indian/Maldives'), ('Indian/Mauritius', 'Indian/Mauritius'), ('Indian/Mayotte', 'Indian/Mayotte'), ('Indian/Reunion', 'Indian/Reunion'), ('Iran', 'Iran'), ('Israel', 'Israel'), ('Jamaica', 'Jamaica'), ('Japan', 'Japan'), ('Kwajalein', 'Kwajalein'), ('Libya', 'Libya'), ('MET', 'MET'), ('MST', 'MST'), ('MST7MDT', 'MST7MDT'), ('Mexico/BajaNorte', 'Mexico/BajaNorte'), ('Mexico/BajaSur', 'Mexico/BajaSur'), ('Mexico/General', 'Mexico/General'), ('NZ', 'NZ'), ('NZ-CHAT', 'NZ-CHAT'), ('Navajo', 'Navajo'), ('PRC', 'PRC'), ('PST8PDT', 'PST8PDT'), ('Pacific/Apia', 'Pacific/Apia'), ('Pacific/Auckland', 'Pacific/Auckland'), ('Pacific/Bougainville', 'Pacific/Bougainville'), ('Pacific/Chatham', 'Pacific/Chatham'), ('Pacific/Chuuk', 'Pacific/Chuuk'), ('Pacific/Easter', 'Pacific/Easter'), ('Pacific/Efate', 'Pacific/Efate'), ('Pacific/Enderbury', 'Pacific/Enderbury'), ('Pacific/Fakaofo', 'Pacific/Fakaofo'), ('Pacific/Fiji', 'Pacific/Fiji'), ('Pacific/Funafuti', 'Pacific/Funafuti'), ('Pacific/Galapagos', 'Pacific/Galapagos'), ('Pacific/Gambier', 'Pacific/Gambier'), ('Pacific/Guadalcanal', 'Pacific/Guadalcanal'), ('Pacific/Guam', 'Pacific/Guam'), ('Pacific/Honolulu', 'Pacific/Honolulu'), ('Pacific/Johnston', 'Pacific/Johnston'), ('Pacific/Kanton', 'Pacific/Kanton'), ('Pacific/Kiritimati', 'Pacific/Kiritimati'), ('Pacific/Kosrae', 'Pacific/Kosrae'), ('Pacific/Kwajalein', 'Pacific/Kwajalein'), ('Pacific/Majuro', 'Pacific/Majuro'), ('Pacific/Marquesas', 'Pacific/Marquesas'), ('Pacific/Midway', 'Pacific/Midway'), ('Pacific/Nauru', 'Pacific/Nauru'), ('Pacific/Niue', 'Pacific/Niue'), ('Pacific/Norfolk', 'Pacific/Norfolk'), ('Pacific/Noumea', 'Pacific/Noumea'), ('Pacific/Pago_Pago', 'Pacific/Pago_Pago'), ('Pacific/Palau', 'Pacific/Palau'), ('Pacific/Pitcairn', 'Pacific/Pitcairn'), ('Pacific/Pohnpei', 'Pacific/Pohnpei'), ('Pacific/Ponape', 'Pacific/Ponape'), ('Pacific/Port_Moresby', 'Pacific/Port_Moresby'), ('Pacific/Rarotonga', 'Pacific/Rarotonga'), ('Pacific/Saipan', 'Pacific/Saipan'), ('Pacific/Samoa', 'Pacific/Samoa'), ('Pacific/Tahiti', 'Pacific/Tahiti'), ('Pacific/Tarawa', 'Pacific/Tarawa'), ('Pacific/Tongatapu', 'Pacific/Tongatapu'), ('Pacific/Truk', 'Pacific/Truk'), ('Pacific/Wake', 'Pacific/Wake'), ('Pacific/Wallis', 'Pacific/Wallis'), ('Pacific/Yap', 'Pacific/Yap'), ('Poland', 'Poland'), ('Portugal', 'Portugal'), ('ROC', 'ROC'), ('ROK', 'ROK'), ('Singapore', 'Singapore'), ('Turkey', 'Turkey'), ('UCT', 'UCT'), ('US/Alaska', 'US/Alaska'), ('US/Aleutian', 'US/Aleutian'), ('US/Arizona', 'US/Arizona'), ('US/Central', 'US/Central'), ('US/East-Indiana', 'US/East-Indiana'), ('US/Eastern', 'US/Eastern'), ('US/Hawaii', 'US/Hawaii'), ('US/Indiana-Starke', 'US/Indiana-Starke'), ('US/Michigan', 'US/Michigan'), ('US/Mountain', 'US/Mountain'), ('US/Pacific', 'US/Pacific'), ('US/Samoa', 'US/Samoa'), ('UTC', 'UTC'), ('Universal', 'Universal'), ('W-SU', 'W-SU'), ('WET', 'WET'), ('Zulu', 'Zulu')], default='UTC', max_length=63, verbose_name='Часовой пояс'), - ), - migrations.AlterField( - model_name='reservation', - name='room_number', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Номер комнаты'), - ), - migrations.AddIndex( - model_name='fraudlog', - index=models.Index(fields=['reservation_id'], name='hotels_frau_reserva_5a26b7_idx'), - ), - migrations.AddIndex( - model_name='fraudlog', - index=models.Index(fields=['detected_at'], name='hotels_frau_detecte_07e626_idx'), - ), - migrations.AddIndex( - model_name='reservation', - index=models.Index(fields=['hotel', 'check_in', 'check_out'], name='hotels_rese_hotel_i_6c527e_idx'), - ), - migrations.AddField( - model_name='room', - name='hotel', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rooms', to='hotels.hotel', verbose_name='Отель'), - ), - migrations.AddIndex( - model_name='room', - index=models.Index(fields=['hotel', 'number'], name='hotels_room_hotel_i_a7c4fc_idx'), - ), - migrations.AddConstraint( - model_name='room', - constraint=models.UniqueConstraint(fields=('hotel', 'number'), name='unique_hotel_room'), - ), - ] diff --git a/hotels/migrations/0012_alter_room_number.py b/hotels/migrations/0012_alter_room_number.py deleted file mode 100644 index c974215e..00000000 --- a/hotels/migrations/0012_alter_room_number.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-17 11:21 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('hotels', '0011_room_alter_fraudlog_check_in_date_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='room', - name='number', - field=models.CharField(max_length=50, unique=True, verbose_name='Номер комнаты'), - ), - ] diff --git a/hotels/migrations/0013_alter_room_number.py b/hotels/migrations/0013_alter_room_number.py deleted file mode 100644 index 45e2751b..00000000 --- a/hotels/migrations/0013_alter_room_number.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-17 11:21 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('hotels', '0012_alter_room_number'), - ] - - operations = [ - migrations.AlterField( - model_name='room', - name='number', - field=models.CharField(max_length=50, verbose_name='Номер комнаты'), - ), - ] diff --git a/hotels/migrations/0014_alter_room_number.py b/hotels/migrations/0014_alter_room_number.py deleted file mode 100644 index 307f6cc3..00000000 --- a/hotels/migrations/0014_alter_room_number.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-17 11:24 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('hotels', '0013_alter_room_number'), - ] - - operations = [ - migrations.AlterField( - model_name='room', - name='number', - field=models.CharField(max_length=50, unique=True, verbose_name='Номер комнаты'), - ), - ] diff --git a/pms_integration/manager.py b/pms_integration/manager.py index 6c2823a0..b1251aab 100644 --- a/pms_integration/manager.py +++ b/pms_integration/manager.py @@ -18,7 +18,7 @@ class PluginLoader: print("Загрузка плагинов:") for file in os.listdir(PluginLoader.PLUGIN_PATH): if file.endswith("_pms.py") and not file.startswith("__"): - print(f" Plugin {file}") + # print(f" Plugin {file}") module_name = f"pms_integration.plugins.{file[:-3]}" try: module = importlib.import_module(module_name) @@ -26,7 +26,7 @@ class PluginLoader: cls = getattr(module, attr) if isinstance(cls, type) and issubclass(cls, BasePMSPlugin) and cls is not BasePMSPlugin: plugin_name = file[:-7] # Убираем `_pms` из имени файла - print(f" Загружен плагин {plugin_name}: {cls.__name__}") + # print(f" Загружен плагин {plugin_name}: {cls.__name__}") plugins[plugin_name] = cls except Exception as e: print(f" Ошибка загрузки плагина {module_name}: {e}") diff --git a/pms_integration/migrations/0001_initial.py b/pms_integration/migrations/0001_initial.py deleted file mode 100644 index e21fe089..00000000 --- a/pms_integration/migrations/0001_initial.py +++ /dev/null @@ -1,48 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-09 09:30 - -import django.db.models.deletion -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('hotels', '0001_initial'), - ] - - operations = [ - migrations.CreateModel( - name='PMSConfiguration', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=255, verbose_name='Название PMS')), - ('url', models.URLField(verbose_name='URL API')), - ('token', models.CharField(blank=True, max_length=255, null=True, verbose_name='Токен')), - ('username', models.CharField(blank=True, max_length=255, null=True, verbose_name='Логин')), - ('password', models.CharField(blank=True, max_length=255, null=True, verbose_name='Пароль')), - ('plugin_name', models.CharField(max_length=255, verbose_name='Название плагина')), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Дата создания')), - ], - options={ - 'verbose_name': 'PMS система', - 'verbose_name_plural': 'PMS системы', - }, - ), - migrations.CreateModel( - name='PMSIntegrationLog', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('checked_at', models.DateTimeField(auto_now_add=True, verbose_name='Время проверки')), - ('status', models.CharField(choices=[('success', 'Успех'), ('error', 'Ошибка')], max_length=50, verbose_name='Статус')), - ('message', models.TextField(blank=True, null=True, verbose_name='Сообщение')), - ('hotel', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='hotels.hotel', verbose_name='Отель')), - ], - options={ - 'verbose_name': 'Журнал интеграции PMS', - 'verbose_name_plural': 'Журналы интеграции PMS', - 'indexes': [models.Index(fields=['hotel'], name='pms_integra_hotel_i_ade4da_idx'), models.Index(fields=['checked_at'], name='pms_integra_checked_938acc_idx'), models.Index(fields=['status'], name='pms_integra_status_358b64_idx')], - }, - ), - ] diff --git a/pms_integration/migrations/0002_alter_pmsconfiguration_plugin_name.py b/pms_integration/migrations/0002_alter_pmsconfiguration_plugin_name.py deleted file mode 100644 index 014b8df1..00000000 --- a/pms_integration/migrations/0002_alter_pmsconfiguration_plugin_name.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-10 02:52 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('pms_integration', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='pmsconfiguration', - name='plugin_name', - field=models.CharField(blank=True, choices=[], max_length=255, null=True), - ), - ] diff --git a/pms_integration/migrations/0003_alter_pmsconfiguration_plugin_name.py b/pms_integration/migrations/0003_alter_pmsconfiguration_plugin_name.py deleted file mode 100644 index 9335ab81..00000000 --- a/pms_integration/migrations/0003_alter_pmsconfiguration_plugin_name.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-10 02:58 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('pms_integration', '0002_alter_pmsconfiguration_plugin_name'), - ] - - operations = [ - migrations.AlterField( - model_name='pmsconfiguration', - name='plugin_name', - field=models.CharField(blank=True, choices=[], max_length=255, null=True, verbose_name='Плагин'), - ), - ] diff --git a/pms_integration/migrations/0004_alter_pmsconfiguration_plugin_name.py b/pms_integration/migrations/0004_alter_pmsconfiguration_plugin_name.py deleted file mode 100644 index eb3629b8..00000000 --- a/pms_integration/migrations/0004_alter_pmsconfiguration_plugin_name.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-10 03:00 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('pms_integration', '0003_alter_pmsconfiguration_plugin_name'), - ] - - operations = [ - migrations.AlterField( - model_name='pmsconfiguration', - name='plugin_name', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Плагин'), - ), - ] diff --git a/pms_integration/migrations/0005_pmsconfiguration_private_key_and_more.py b/pms_integration/migrations/0005_pmsconfiguration_private_key_and_more.py deleted file mode 100644 index dd9fe759..00000000 --- a/pms_integration/migrations/0005_pmsconfiguration_private_key_and_more.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-10 03:03 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('pms_integration', '0004_alter_pmsconfiguration_plugin_name'), - ] - - operations = [ - migrations.AddField( - model_name='pmsconfiguration', - name='private_key', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Приватный ключ'), - ), - migrations.AddField( - model_name='pmsconfiguration', - name='public_key', - field=models.CharField(blank=True, max_length=255, null=True, verbose_name='Публичный ключ'), - ), - ] diff --git a/pms_integration/plugins/ecvi_pms.py b/pms_integration/plugins/ecvi_pms.py index 64fa5246..41b14230 100644 --- a/pms_integration/plugins/ecvi_pms.py +++ b/pms_integration/plugins/ecvi_pms.py @@ -74,7 +74,7 @@ class EcviPMSPlugin(BasePMSPlugin): except requests.exceptions.RequestException as e: self.logger.error(f"Ошибка запроса: {e}") return [] - self.logger.debug(f"\n\n\n\n\ndata: {data}\n\n\n\n\n") + # Фильтрация данных filtered_data = [ { @@ -87,7 +87,6 @@ class EcviPMSPlugin(BasePMSPlugin): } for item in data if isinstance(item, dict) and item.get('occupancy') in ['проживание', 'под выезд', 'под заезд'] ] - self.logger.debug(f"filtered_data: {filtered_data}") # Сохранение данных в базу данных @@ -108,7 +107,6 @@ class EcviPMSPlugin(BasePMSPlugin): # Проверяем, существует ли уже резервация с таким внешним ID reservation_id = item.get('reservation_id') - self.logger.debug(f"-----\n\n\nITEM : {item}\n\n\n---") if not reservation_id: self.logger.error("Ошибка: 'reservation_id' отсутствует в данных.") return @@ -123,6 +121,7 @@ class EcviPMSPlugin(BasePMSPlugin): await sync_to_async(Reservation.objects.update_or_create)( reservation_id=reservation_id, defaults={ + '' 'room_number': item.get('room_number'), 'room_type': item.get('room_type'), 'check_in': item.get('checkin'), diff --git a/pms_integration/plugins/realtycalendar_pms.py b/pms_integration/plugins/realtycalendar_pms.py index 80544dad..7f80ae46 100644 --- a/pms_integration/plugins/realtycalendar_pms.py +++ b/pms_integration/plugins/realtycalendar_pms.py @@ -4,17 +4,16 @@ import json from .base_plugin import BasePMSPlugin from datetime import datetime, timedelta from asgiref.sync import sync_to_async -from math import ceil - +from touchh.utils.log import CustomLogger +from hotels.models import Hotel, Reservation class RealtyCalendarPlugin(BasePMSPlugin): - """Плагин для импорта данных из системы RealtyCalendar - """ + """Плагин для импорта данных из системы 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("/") - + self.logger = CustomLogger(name="RealtyCalendarPlugin", log_level="DEBUG").get_logger() if not self.public_key or not self.private_key: raise ValueError("Публичный или приватный ключ отсутствует для RealtyCalendar") @@ -31,8 +30,8 @@ class RealtyCalendarPlugin(BasePMSPlugin): """ Возвращает отсортированный по имени список ключей. """ - sorted_keys = sorted(list(obj.keys())) - print(f"[DEBUG] Отсортированные ключи: {sorted_keys}") + sorted_keys = sorted(obj.keys()) + self.logger.debug(f"Отсортированные ключи: {sorted_keys}") return sorted_keys def _generate_data_string(self, obj): @@ -41,7 +40,7 @@ class RealtyCalendarPlugin(BasePMSPlugin): """ sorted_keys = self._get_sorted_keys(obj) string = "".join(f"{key}={obj[key]}" for key in sorted_keys) - print(f"[DEBUG] Сформированная строка данных: {string}") + self.logger.debug(f"Сформированная строка данных: {string}") return string + self.private_key def _generate_md5(self, string): @@ -49,7 +48,7 @@ class RealtyCalendarPlugin(BasePMSPlugin): Генерирует MD5-хеш от строки. """ md5_hash = hashlib.md5(string.encode("utf-8")).hexdigest() - print(f"[DEBUG] Сформированный MD5-хеш: {md5_hash}") + self.logger.debug(f"Сформированный MD5-хеш: {md5_hash}") return md5_hash def _generate_sign(self, data): @@ -57,16 +56,17 @@ class RealtyCalendarPlugin(BasePMSPlugin): Генерирует подпись для данных запроса. """ data_string = self._generate_data_string(data) - print(f"[DEBUG] Строка для подписи: {data_string}") + self.logger.debug(f"Строка для подписи: {data_string}") sign = self._generate_md5(data_string) - print(f"[DEBUG] Подпись: {sign}") + self.logger.debug(f"Подпись: {sign}") return sign - def _fetch_data(self): + async def _fetch_data(self): """ Выполняет запрос к API RealtyCalendar для получения данных о бронированиях. """ - base_url = f"https://realtycalendar.ru/api/v1/bookings/{self.public_key}/" + self.logger.debug("Начало выполнения функции _fetch_data") + base_url = f"{self.api_url}/api/v1/bookings/{self.public_key}/" headers = { "Accept": "application/json", "Content-Type": "application/json", @@ -77,81 +77,150 @@ class RealtyCalendarPlugin(BasePMSPlugin): data = { "begin_date": (now - timedelta(days=7)).strftime("%Y-%m-%d"), "end_date": now.strftime("%Y-%m-%d"), - "room_number": "" } - print(f"[DEBUG] Даты выборки: {data}") + self.logger.debug(f"Даты выборки: {data}") # Генерация подписи data["sign"] = self._generate_sign(data) # Отправляем запрос - print(f"[DEBUG] URL запроса: {base_url}") - print(f"[DEBUG] Заголовки: {headers}") - print(f"[DEBUG] Данные запроса: {data}") + self.logger.debug(f"URL запроса: {base_url}") + self.logger.debug(f"Заголовки: {headers}") + self.logger.debug(f"Данные запроса: {data}") response = requests.post(url=base_url, headers=headers, json=data) + self.logger.debug(f"Запрос: {response}") + self.logger.debug(f"Статус ответа: {response.status_code}") + # self.logger.debug(f"Ответ: {response.text}") - # Логируем результат - 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}") + try: + response_data = response.json() + self.logger.debug(f"Тип данных ответа: {type(response_data)}") + if not isinstance(response_data, dict) or "bookings" not in response_data: + raise ValueError(f"Неожиданная структура ответа: {response_data}") + bookings = response_data.get("bookings", []) + # self.logger.debug(f"Полученные данные бронирований: {bookings}") + except json.JSONDecodeError as e: + self.logger.error(f"Ошибка декодирования JSON: {e}") + raise ValueError("Ошибка декодирования JSON ответа.") + # Фильтрация данных + filtered_data = [ + { + "id": item.get("id"), + "begin_date": item.get("begin_date"), + "end_date": item.get("end_date"), + "amount": item.get("amount"), + "status": item.get("status"), + "is_delete": item.get("is_delete"), + "apartment_id": item.get("apartment_id"), + "prepayment": item.get("prepayment"), + "deposit": item.get("deposit"), + "source": item.get("source"), + "notes": item.get("notes"), + } + for item in bookings + if isinstance(item, dict) and item.get("status") in ["booked", "request"] + ] + self.logger.debug(f"Отфильтрованные данные: {type(filtered_data)}") - async def _save_to_db(self, data, hotel_id, batch_size=50): + for item in filtered_data: + self.logger.debug(f"Данные бронирования: {item}") + await self._save_to_db(item) + + from django.utils import timezone + + async def _save_to_db(self, data): + from django.utils import timezone """ - Сохраняет данные о бронированиях в базу данных партиями. + Сохраняет данные в БД (например, информацию о номере). """ - from hotels.models import Reservation, Hotel - try: - hotel = await sync_to_async(Hotel.objects.get)(id=hotel_id) - self.logger.info(f"Загружен отель: {hotel.name}") + # Проверяем общее количество записей для обработки + if not isinstance(data, list): + self.logger.error(f"Ожидался список записей, но получен {type(data).__name__}") + return - # Разделение данных на батчи 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)} партиях...") + self.logger.info(f"Общее количество записей для обработки: {total_records}") - for batch_index, batch in enumerate(batches): - self.logger.info(f"Обработка партии {batch_index + 1}/{len(batches)}") + for index, item in enumerate(data, start=1): + try: + self.logger.info(f"Обработка записи {index}/{total_records}") - for item in batch: + # Проверка типа данных + if not isinstance(item, dict): + self.logger.error(f"Пропущена запись {index}/{total_records}: ожидался dict, но получен {type(item).__name__}") + continue + + # Получаем отель по настройкам PMS + hotel = await sync_to_async(Hotel.objects.get)(pms=self.pms_config) + self.logger.debug(f"Отель найден: {hotel.name}") + + # Проверяем, существует ли уже резервация с таким внешним ID + reservation_id = item.get('id') + if not reservation_id: + self.logger.error(f"Пропущена запись {index}/{total_records}: отсутствует 'id' в данных.") + continue + + # Преобразуем даты в "aware" объекты 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']}.") + check_in = timezone.make_aware(datetime.strptime(item.get('begin_date'), "%Y-%m-%d")) + check_out = timezone.make_aware(datetime.strptime(item.get('end_date'), "%Y-%m-%d")) except Exception as e: - self.logger.error(f"Ошибка при обработке бронирования ID {item.get('id', 'неизвестно')}: {e}") + self.logger.error(f"Ошибка преобразования дат для записи {index}/{total_records}: {e}") + continue + + existing_reservation = await sync_to_async(Reservation.objects.filter)(reservation_id=reservation_id) + + # Теперь вызываем .first() после асинхронного вызова + existing_reservation = await sync_to_async(existing_reservation.first)() + + if existing_reservation: + self.logger.debug(f"Резервация {reservation_id} уже существует. Обновляем...") + await sync_to_async(Reservation.objects.update_or_create)( + reservation_id=reservation_id, + defaults={ + 'room_number': item.get('apartment_id'), + 'room_type': 'Описание отсутствует', + 'check_in': check_in, + 'check_out': check_out, + 'status': item.get('status'), + 'hotel': hotel + } + ) + self.logger.debug(f"Резервация обновлена.") + else: + self.logger.debug(f"Резервация не найдена, создаем новую...") + reservation = await sync_to_async(Reservation.objects.create)( + reservation_id=reservation_id, + room_number=item.get('apartment_id'), + room_type='Описание отсутствует', + check_in=check_in, + check_out=check_out, + status=item.get('status'), + hotel=hotel + ) + self.logger.debug(f"Новая резервация создана с ID: {reservation.reservation_id}") + + except Exception as e: + self.logger.error(f"Ошибка при обработке записи {index}/{total_records}: {e}") + except Exception as e: - self.logger.error(f"Ошибка при обработке данных: {e}") \ No newline at end of file + self.logger.error(f"Ошибка обработки данных в _save_to_db: {e}") + + + def validate_plugin(self): + """ + Проверка на соответствие требованиям. + Можно проверить наличие методов или полей. + """ + # Проверяем наличие обязательных методов + required_methods = ["fetch_data", "get_default_parser_settings", "_fetch_data"] + for m in required_methods: + if not hasattr(self, m): + raise ValueError(f"Плагин {type(self).__name__} не реализует метод {m}.") + self.logger.debug(f"Плагин {self.__class__.__name__} прошел валидацию.") + return True diff --git a/scheduler/admin.py b/scheduler/admin.py index a2be9192..968ef8b2 100644 --- a/scheduler/admin.py +++ b/scheduler/admin.py @@ -1,16 +1,14 @@ from django.contrib import admin from django import forms -from django.utils.functional import cached_property from .models import ScheduledTask from django.templatetags.static import static from scheduler.utils import get_project_functions -class CustomAdmin(admin.ModelAdmin): - class Media: - css = {"all": (static("scheduler/admin.css"),)} - js = (static("scheduler/admin.js"),) class ScheduledTaskForm(forms.ModelForm): + """ + Форма для модели ScheduledTask с кастомным полем для выбора дней недели. + """ DAYS_OF_WEEK_CHOICES = [ (0, "Воскресенье"), (1, "Понедельник"), @@ -25,7 +23,7 @@ class ScheduledTaskForm(forms.ModelForm): choices=DAYS_OF_WEEK_CHOICES, widget=forms.CheckboxSelectMultiple, label="Дни недели", - required=False, # Опционально + required=False, ) class Meta: @@ -36,24 +34,32 @@ class ScheduledTaskForm(forms.ModelForm): "minutes", "hours", "months", - "weekdays", # Используем только поле с галочками + "weekdays", "active", ] def clean_weekdays(self): """ - Преобразуем список выбранных дней в строку для хранения в базе. + Преобразует список выбранных дней в строку для сохранения в базе. """ weekdays = self.cleaned_data.get("weekdays", []) return ",".join(map(str, weekdays)) - + + @admin.register(ScheduledTask) class ScheduledTaskAdmin(admin.ModelAdmin): + """ + Кастомный класс для управления ScheduledTask в админке. + """ form = ScheduledTaskForm list_display = ("task_name", "function_path", "minutes", "hours", "months", "weekdays", "active", "formatted_last_run") list_filter = ("active",) search_fields = ("task_name", "function_path") def formatted_last_run(self, obj): + """ + Отформатированный вывод времени последнего запуска задачи. + """ return obj.last_run.strftime("%Y-%m-%d %H:%M:%S") if obj.last_run else "Никогда" - formatted_last_run.short_description = "Последний запуск" \ No newline at end of file + + formatted_last_run.short_description = "Последний запуск" diff --git a/scheduler/apps.py b/scheduler/apps.py index ece24f17..48d74841 100644 --- a/scheduler/apps.py +++ b/scheduler/apps.py @@ -1,7 +1,15 @@ from django.apps import AppConfig +from apscheduler.schedulers.asyncio import AsyncIOScheduler +scheduler_instance = AsyncIOScheduler() class SchedulerConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'scheduler' - verbose_name="Планировщик заданий" \ No newline at end of file + verbose_name = 'Планировщик задач' + def ready(self): + """ + Метод ready вызывается при старте приложения. + Здесь не нужно запускать scheduler_instance.start(), чтобы избежать ошибок. + """ + pass diff --git a/scheduler/management/commands/start_scheduler.py b/scheduler/management/commands/start_scheduler.py new file mode 100644 index 00000000..7d6f147e --- /dev/null +++ b/scheduler/management/commands/start_scheduler.py @@ -0,0 +1,39 @@ +import asyncio +from django.core.management.base import BaseCommand +from scheduler.apps import scheduler_instance +from scheduler.tasks import load_tasks_to_scheduler + + +class Command(BaseCommand): + help = "Запуск планировщика задач" + + def handle(self, *args, **kwargs): + """ + Создаёт новый event loop, запускает планировщик и загружает задачи. + """ + try: + print("Проверка состояния перед запуском:") + print(f"Scheduler instance: {scheduler_instance}") + + # Создаём новый event loop + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + # Устанавливаем event loop в планировщик + scheduler_instance.configure(event_loop=loop) + + # Запускаем планировщик + scheduler_instance.start() + + # Загружаем задачи + load_tasks_to_scheduler(scheduler_instance) + + self.stdout.write(self.style.SUCCESS("Планировщик успешно запущен.")) + + # Удерживаем цикл событий + loop.run_forever() + except KeyboardInterrupt: + self.stdout.write(self.style.WARNING("Остановка планировщика.")) + finally: + # Завершаем работу планировщика + scheduler_instance.shutdown(wait=False) diff --git a/scheduler/migrations/0001_initial.py b/scheduler/migrations/0001_initial.py deleted file mode 100644 index b67db412..00000000 --- a/scheduler/migrations/0001_initial.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-10 08:38 - -import scheduler.models -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='ScheduledTask', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('task_name', models.CharField(max_length=255, verbose_name='Название задачи')), - ('function_path', models.CharField(choices=scheduler.models.get_available_functions, max_length=500, verbose_name='Путь к функции (модуль.функция)')), - ('minutes', models.CharField(default='*', max_length=255, verbose_name='Минуты')), - ('hours', models.CharField(default='*', max_length=255, verbose_name='Часы')), - ('days', models.CharField(default='*', max_length=255, verbose_name='Дни')), - ('months', models.CharField(default='*', max_length=255, verbose_name='Месяцы')), - ('weekdays', models.JSONField(default=list, verbose_name='Дни недели')), - ('active', models.BooleanField(default=True, verbose_name='Активно')), - ('last_run', models.DateTimeField(blank=True, null=True, verbose_name='Последний запуск')), - ], - ), - ] diff --git a/scheduler/reload_tasks.py b/scheduler/reload_tasks.py new file mode 100644 index 00000000..8c6b6362 --- /dev/null +++ b/scheduler/reload_tasks.py @@ -0,0 +1,17 @@ +from django.core.management.base import BaseCommand +from scheduler.tasks import load_tasks_to_scheduler +from scheduler.apps import scheduler_instance + +class Command(BaseCommand): + help = "Перезагрузка задач в планировщике" + + def handle(self, *args, **kwargs): + try: + # Удаляем все существующие задачи + scheduler_instance.remove_all_jobs() + + # Загружаем задачи заново + load_tasks_to_scheduler(scheduler_instance) + self.stdout.write(self.style.SUCCESS("Задачи успешно перезагружены.")) + except Exception as e: + self.stdout.write(self.style.ERROR(f"Ошибка перезагрузки задач: {e}")) diff --git a/scheduler/tasks.py b/scheduler/tasks.py index eb93f250..8bfc65e1 100644 --- a/scheduler/tasks.py +++ b/scheduler/tasks.py @@ -5,7 +5,6 @@ from scheduler.models import ScheduledTask import importlib from apscheduler.triggers.cron import CronTrigger - def format_weekdays(weekdays): """Преобразует список дней недели в строку.""" if isinstance(weekdays, list): @@ -43,7 +42,10 @@ def setup_scheduler(): print("Планировщик запущен.") return scheduler -def load_tasks_to_scheduler(scheduler: BaseScheduler): +def load_tasks_to_scheduler(scheduler): + """ + Загружает активные задачи в планировщик. + """ tasks = ScheduledTask.objects.filter(active=True) for task in tasks: try: diff --git a/touchh/settings.py b/touchh/settings.py index 3a825257..0f46219f 100644 --- a/touchh/settings.py +++ b/touchh/settings.py @@ -16,6 +16,9 @@ import os from dotenv import load_dotenv load_dotenv() +import os +from pprint import pprint +from app_settings.app_settings import load_database_settings # Build paths inside the project like this: BASE_DIR / 'subdir'. @@ -31,10 +34,10 @@ SECRET_KEY = 'django-insecure-l_8uu8#p*^zf)9zry80)6u+!+2g1a4tg!wx7@^!uw(+^axyh&h # SECURITY WARNING: don't run with debug turned on in production! DEBUG = True -ALLOWED_HOSTS = ['0.0.0.0', '192.168.219.140', '127.0.0.1', '192.168.219.114', 'a66a-182-226-158-253.ngrok-free.app', '*.ngrok-free.app'] +ALLOWED_HOSTS = ['0.0.0.0', '192.168.219.140', '127.0.0.1', 'localhost', '192.168.219.114', '588a-182-226-158-253.ngrok-free.app', '*.ngrok-free.app'] CSRF_TRUSTED_ORIGINS = [ - 'http://a66a-182-226-158-253.ngrok-free.app', + 'http://588a-182-226-158-253.ngrok-free.app', 'https://*.ngrok-free.app', # Это подойдет для любых URL, связанных с ngrok ] @@ -103,10 +106,11 @@ DATABASES = { 'PASSWORD': os.getenv('DB_PASSWORD'), # Пароль пользователя 'HOST': os.getenv('DB_HOST', default='0.0.0.0'), # Хост (по умолчанию localhost) 'PORT': os.getenv('DB_PORT', default=3308), # Порт (по умолчанию 3306) + 'ATOMIC_REQUESTS': True, }, } - +# load_database_settings(DATABASES) # Password validation # https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators diff --git a/users/migrations/0001_initial.py b/users/migrations/0001_initial.py deleted file mode 100644 index acfcf34a..00000000 --- a/users/migrations/0001_initial.py +++ /dev/null @@ -1,120 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-09 09:30 - -import django.contrib.auth.models -import django.contrib.auth.validators -import django.db.models.deletion -import django.utils.timezone -import uuid -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('auth', '0012_alter_user_first_name_max_length'), - ] - - operations = [ - migrations.CreateModel( - name='LocalUserActivityLog', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('user_id', models.IntegerField()), - ('activity_type', models.CharField(max_length=255)), - ('timestamp', models.DateTimeField()), - ('additional_data', models.JSONField(blank=True, null=True)), - ], - ), - migrations.CreateModel( - name='UserActivityLog', - fields=[ - ('id', models.BigAutoField(primary_key=True, serialize=False, verbose_name='ID')), - ('user_id', models.BigIntegerField(verbose_name='ID пользователя')), - ('ip', models.CharField(blank=True, max_length=100, null=True, verbose_name='IP адрес')), - ('created', models.DateTimeField(auto_now_add=True, verbose_name='Создан')), - ('timestamp', models.IntegerField(verbose_name='Время')), - ('date_time', models.DateTimeField(verbose_name='Дата')), - ('referred', models.CharField(blank=True, max_length=255, null=True)), - ('agent', models.CharField(blank=True, max_length=255, null=True, verbose_name='Браузер')), - ('platform', models.CharField(blank=True, max_length=255, null=True)), - ('version', models.CharField(blank=True, max_length=50, null=True)), - ('model', models.CharField(blank=True, max_length=255, null=True)), - ('device', models.CharField(blank=True, max_length=50, null=True)), - ('UAString', models.TextField(blank=True, null=True)), - ('location', models.CharField(blank=True, max_length=255, null=True)), - ('page_id', models.BigIntegerField(blank=True, null=True)), - ('url_parameters', models.TextField(blank=True, null=True)), - ('page_title', models.CharField(blank=True, max_length=255, null=True)), - ('type', models.CharField(blank=True, max_length=50, null=True)), - ('last_counter', models.IntegerField(blank=True, null=True)), - ('hits', models.IntegerField(blank=True, null=True)), - ('honeypot', models.BooleanField(blank=True, null=True)), - ('reply', models.BooleanField(blank=True, null=True)), - ('page_url', models.CharField(blank=True, max_length=255, null=True)), - ], - options={ - 'verbose_name': 'Журнал активности', - 'verbose_name_plural': 'Журналы активности', - 'db_table': 'user_activity_log', - }, - ), - migrations.CreateModel( - name='User', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), - ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), - ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), - ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), - ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), - ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), - ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), - ('telegram_id', models.BigIntegerField(blank=True, null=True, unique=True, verbose_name='ID Телеграм')), - ('chat_id', models.BigIntegerField(blank=True, null=True, unique=True, verbose_name='ID чата в телеграм')), - ('role', models.CharField(choices=[('admin', 'Администратор системы'), ('hotel_user', 'Сотрудник отеля')], default='hotel_user', max_length=20, verbose_name='Роль')), - ('confirmed', models.BooleanField(default=False, verbose_name='Подтвержден')), - ('groups', models.ManyToManyField(blank=True, related_name='custom_user_set', to='auth.group')), - ('user_permissions', models.ManyToManyField(blank=True, related_name='custom_user_set', to='auth.permission')), - ], - options={ - 'verbose_name': 'Пользователь', - 'verbose_name_plural': 'Пользователи', - }, - managers=[ - ('objects', django.contrib.auth.models.UserManager()), - ], - ), - migrations.CreateModel( - name='NotificationSettings', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('telegram_enabled', models.BooleanField(default=True, verbose_name='Уведомления в Telegram')), - ('email_enabled', models.BooleanField(default=False, verbose_name='Уведомления по Email')), - ('email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Email для уведомлений')), - ('notification_time', models.TimeField(default='09:00', verbose_name='Время отправки уведомлений')), - ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='users.user', verbose_name='Пользователь')), - ], - options={ - 'verbose_name': 'Способ оповещения', - 'verbose_name_plural': 'Способы оповещений', - }, - ), - migrations.CreateModel( - name='UserConfirmation', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('confirmation_code', models.UUIDField(default=uuid.uuid4, verbose_name='Код подтверждения')), - ('created_at', models.DateTimeField(auto_now_add=True, verbose_name='Создан: ')), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='users.user', verbose_name='Пользователь')), - ], - options={ - 'verbose_name': 'Подтверждение пользователя', - 'verbose_name_plural': 'Подтверждения пользователей', - }, - ), - ] diff --git a/users/migrations/0002_delete_localuseractivitylog_delete_useractivitylog_and_more.py b/users/migrations/0002_delete_localuseractivitylog_delete_useractivitylog_and_more.py deleted file mode 100644 index 4f150e4c..00000000 --- a/users/migrations/0002_delete_localuseractivitylog_delete_useractivitylog_and_more.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 5.1.4 on 2024-12-13 00:15 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('users', '0001_initial'), - ] - - operations = [ - migrations.DeleteModel( - name='LocalUserActivityLog', - ), - migrations.DeleteModel( - name='UserActivityLog', - ), - migrations.DeleteModel( - name='UserConfirmation', - ), - ]