data_sync

This commit is contained in:
2024-12-17 11:49:26 +09:00
parent 43431dd752
commit 66750015e2
8 changed files with 167 additions and 8 deletions

View File

@@ -4,7 +4,7 @@ from django.http import JsonResponse
from django.shortcuts import redirect, get_object_or_404
from django.contrib import messages
from django.db import transaction
from antifroud.models import UserActivityLog, ExternalDBSettings, RoomDiscrepancy, ImportedHotel, SyncLog
from antifroud.models import UserActivityLog, ExternalDBSettings, RoomDiscrepancy, ImportedHotel, SyncLog, ViolationLog
from hotels.models import Hotel
import pymysql
import logging
@@ -112,7 +112,7 @@ class ExternalDBSettingsAdmin(admin.ModelAdmin):
class UserActivityLogAdmin(admin.ModelAdmin):
list_display = ("id", "timestamp", "date_time", "page_id", "url_parameters", "created", "page_title", "type", "hits")
search_fields = ("page_title", "url_parameters")
list_filter = ("type", "created")
list_filter = ("page_title", "created")
readonly_fields = ("created", "timestamp")
@@ -184,4 +184,15 @@ class SyncLogAdmin(admin.ModelAdmin):
class Meta:
model = SyncLog
fields = ['hotel', 'received_records', 'processed_records']
fields = ['hotel', 'received_records', 'processed_records']
@admin.register(ViolationLog)
class ViolationLogAdmin(admin.ModelAdmin):
list_display = ['id', 'hotel', 'room_number', 'created_at', 'violation_type', 'violation_details', 'detected_at']
search_fields = ['id', 'hotel', 'room_number', 'created_at', 'violation_type', 'violation_details', 'detected_at']
list_filter = ['id', 'hotel', 'room_number', 'created_at', 'violation_type', 'violation_details', 'detected_at']
class Meta:
model = ViolationLog
fields = ['hotel', 'room_number', 'created_at', 'violation_type', 'violation_details', 'detected_at']

79
antifroud/check_fraud.py Normal file
View File

@@ -0,0 +1,79 @@
import logging
from datetime import datetime, timedelta
from urllib.parse import parse_qs
from django.db.models import Q
from hotels.models import Reservation, Hotel
from .models import UserActivityLog, ViolationLog
# Настройка логирования
logging.basicConfig(level=logging.INFO) # Устанавливаем уровень логирования
logger = logging.getLogger(__name__) # Создаем логгер для текущего модуля
def check_reservations_and_generate_report():
now = datetime.now()
start_time = (now - timedelta(hours=12))
end_time = now
logger.info(f"Starting reservation check from {start_time} to {end_time}")
# Получаем логи активности за период
user_logs = UserActivityLog.objects.filter(created__range=(start_time, end_time))
logger.info(f"Found {len(user_logs)} logs for analysis.")
violations = [] # Список для записи нарушений
for i, log in enumerate(user_logs, start=1):
logger.debug(f"Processing log {i}: {log}")
if not log.url_parameters:
logger.warning(f"Log {i} skipped due to missing URL parameters.")
continue # Пропускаем записи без параметров URL
# Парсим параметры URL
params = parse_qs(log.url_parameters)
external_id = params.get("utm_content", [None])[0] # ID отеля
room_number = params.get("utm_term", [None])[0] # Номер комнаты
logger.debug(f"Log {i} parsed parameters: external_id={external_id}, room_number={room_number}")
if not external_id or not room_number:
logger.warning(f"Log {i} skipped due to missing external_id or room_number.")
continue # Пропускаем, если данные не извлечены
try:
# Находим отель по external_id
hotel = Hotel.objects.get(external_id=external_id)
logger.debug(f"Log {i}: Found hotel {hotel.name} with external_id {external_id}.")
except Hotel.DoesNotExist:
logger.error(f"Log {i} skipped: No hotel found with external_id {external_id}.")
continue
# Ищем бронирование в Reservation
matching_reservations = Reservation.objects.filter(
hotel=hotel,
room_number=room_number,
check_in__lte=log.created,
check_out__gte=log.created
)
logger.debug(f"Log {i}: Found {len(matching_reservations)} matching reservations.")
if not matching_reservations.exists():
# Если бронирование не найдено — записываем нарушение
violation_details = (
f"Log {i}: No reservation found for room {room_number} in hotel {external_id} at {log.created}."
)
violations.append(ViolationLog(
hotel=hotel,
room_number=room_number,
violation_type="missed",
violation_details=violation_details
))
logger.warning(f"Log {i}: Violation recorded - {violation_details}")
# Сохраняем все нарушения в базу
if violations:
ViolationLog.objects.bulk_create(violations)
logger.info(f"Created {len(violations)} records in violation log.")
else:
logger.info("No violations found during this check.")
logger.info("Reservation check completed.")

View File

@@ -223,7 +223,7 @@ class DataSyncManager:
return None
# Генерация external_id в формате 'hotel_name_hotel_id'
external_id = f"{hotel_name}_{hotel_id}"
external_id = f"{hotel_name}"
# Проверяем, существует ли запись с таким external_id в ImportedHotel
existing_hotel = ImportedHotel.objects.filter(external_id=external_id).first()

View File

@@ -0,0 +1,31 @@
# 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': 'Журналы нарушений',
},
),
]

View File

@@ -166,4 +166,24 @@ class SyncLog(models.Model):
class Meta:
verbose_name = "Журнал синхронизации"
verbose_name_plural = "Журналы синхронизации"
verbose_name_plural = "Журналы синхронизации"
class ViolationLog(models.Model):
hotel = models.ForeignKey(Hotel, on_delete=models.CASCADE, verbose_name="Отель")
room_number = models.CharField(max_length=50, verbose_name="Номер комнаты", null=True, blank=True)
violation_type = models.CharField(
max_length=50,
choices=[("missed", "Неявка"), ("early", "Раннее заселение"), ("late", "Позднее заселение")],
verbose_name="Тип нарушения"
)
violation_details = models.TextField(verbose_name="Детали нарушения", blank=True, null=True)
detected_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата обнаружения")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Дата создания")
def __str__(self):
return f"{self.hotel.name} - {self.room_number or 'N/A'}: {self.violation_type}"
class Meta:
verbose_name = "Журнал нарушений"
verbose_name_plural = "Журналы нарушений"

View File

@@ -20,7 +20,6 @@ class APIConfiguration(models.Model):
import pytz
class Hotel(models.Model):
id = models.BigAutoField(primary_key=True, auto_created=True, verbose_name="ID")
name = models.CharField(max_length=255, verbose_name="Название отеля")
hotel_id = models.CharField(max_length=255, unique=True, null=True, blank=True, verbose_name="ID отеля")
created_at = models.DateTimeField(auto_now_add=True, verbose_name="Создан")
@@ -49,7 +48,25 @@ class Hotel(models.Model):
class Meta:
verbose_name = "Отель"
verbose_name_plural = "Отели"
class Room(models.Model):
"""
Модель номера отеля.
"""
hotel = models.ForeignKey(Hotel, on_delete=models.CASCADE, related_name="rooms", verbose_name="Отель")
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="Дата обновления")
def __str__(self):
return f"{self.hotel.name} - {self.number}"
class Meta:
verbose_name = "Номер"
verbose_name_plural = "Номера"
unique_together = ("hotel", "number") # Уникальность пары (отель, номер)
class UserHotel(models.Model):
user = models.ForeignKey(

View File

@@ -79,7 +79,7 @@ class EcviPMSPlugin(BasePMSPlugin):
filtered_data = [
{
'reservation_id': item.get('task_id'),
'room_number': item.get('room_number'),
'room_number': item.get('room_name'),
'room_type': item.get('room_type'),
'checkin': datetime.strptime(item.get('checkin'), '%Y-%m-%d %H:%M:%S'),
'checkout': datetime.strptime(item.get('checkout'), '%Y-%m-%d %H:%M:%S'),

View File

@@ -65,4 +65,5 @@ urllib3==2.2.3
user-agents==2.2.0
yarl==1.18.3
mysqlclient
chardet
chardet
decouple