antifroud_check
This commit is contained in:
@@ -5,7 +5,8 @@ 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, ViolationLog
|
||||
from hotels.models import Hotel
|
||||
|
||||
from hotels.models import Hotel, Room
|
||||
import pymysql
|
||||
import logging
|
||||
from django.urls import reverse
|
||||
@@ -114,14 +115,32 @@ class UserActivityLogAdmin(admin.ModelAdmin):
|
||||
search_fields = ("page_title", "url_parameters")
|
||||
list_filter = ("page_title", "created")
|
||||
readonly_fields = ("created", "timestamp")
|
||||
def get_hotel_name(self):
|
||||
"""
|
||||
Возвращает название отеля на основе связанного page_id.
|
||||
"""
|
||||
if self.page_id:
|
||||
try:
|
||||
room = Room.objects.get(id=self.page_id)
|
||||
return room.hotel.name
|
||||
except Room.DoesNotExist:
|
||||
return "Отель не найден"
|
||||
return "Нет данных"
|
||||
|
||||
def get_room_number(self):
|
||||
"""
|
||||
Возвращает номер комнаты на основе связанного page_id.
|
||||
"""
|
||||
if self.page_id:
|
||||
try:
|
||||
room = Room.objects.get(id=self.page_id)
|
||||
return room.number
|
||||
except Room.DoesNotExist:
|
||||
return "Комната не найдена"
|
||||
return "Нет данных"
|
||||
|
||||
@admin.register(RoomDiscrepancy)
|
||||
class RoomDiscrepancyAdmin(admin.ModelAdmin):
|
||||
list_display = ("hotel", "room_number", "booking_id", "check_in_date_expected", "check_in_date_actual", "discrepancy_type", "created_at")
|
||||
search_fields = ("hotel__name", "room_number", "booking_id")
|
||||
list_filter = ("discrepancy_type", "created_at")
|
||||
readonly_fields = ("created_at",)
|
||||
get_hotel_name.short_description = "Отель"
|
||||
get_room_number.short_description = "Комната"
|
||||
|
||||
|
||||
from .views import import_selected_hotels
|
||||
|
||||
@@ -1,79 +1,160 @@
|
||||
import logging
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import timedelta
|
||||
from urllib.parse import parse_qs
|
||||
from django.utils import timezone
|
||||
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__) # Создаем логгер для текущего модуля
|
||||
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}")
|
||||
class ReservationChecker:
|
||||
"""
|
||||
Класс для проверки несоответствий между бронированиями и логами заселения.
|
||||
"""
|
||||
|
||||
# Получаем логи активности за период
|
||||
user_logs = UserActivityLog.objects.filter(created__range=(start_time, end_time))
|
||||
logger.info(f"Found {len(user_logs)} logs for analysis.")
|
||||
def __init__(self):
|
||||
"""
|
||||
Инициализация времени проверки и списка нарушений.
|
||||
"""
|
||||
self.start_time = timezone.now() - timedelta(days=30)
|
||||
self.end_time = timezone.now()
|
||||
self.violations = []
|
||||
|
||||
violations = [] # Список для записи нарушений
|
||||
def log_info(self, message):
|
||||
"""Логирование информационных сообщений."""
|
||||
logger.info(message)
|
||||
|
||||
for i, log in enumerate(user_logs, start=1):
|
||||
logger.debug(f"Processing log {i}: {log}")
|
||||
def log_warning(self, message):
|
||||
"""Логирование предупреждений."""
|
||||
logger.warning(message)
|
||||
|
||||
if not log.url_parameters:
|
||||
logger.warning(f"Log {i} skipped due to missing URL parameters.")
|
||||
continue # Пропускаем записи без параметров URL
|
||||
def log_error(self, message):
|
||||
"""Логирование ошибок."""
|
||||
logger.error(message)
|
||||
|
||||
# Парсим параметры URL
|
||||
params = parse_qs(log.url_parameters)
|
||||
external_id = params.get("utm_content", [None])[0] # ID отеля
|
||||
room_number = params.get("utm_term", [None])[0] # Номер комнаты
|
||||
def fetch_user_logs(self):
|
||||
"""
|
||||
Извлекает записи из UserActivityLog за последние 12 часов.
|
||||
"""
|
||||
print(f"Fetching user logs from {self.start_time} to {self.end_time}")
|
||||
user_logs = UserActivityLog.objects.filter(created__range=(self.start_time, self.end_time))
|
||||
print(f"Found {user_logs.count()} logs for analysis.")
|
||||
return user_logs
|
||||
|
||||
logger.debug(f"Log {i} parsed parameters: external_id={external_id}, room_number={room_number}")
|
||||
def fetch_hotels(self, hotel_ids):
|
||||
"""
|
||||
Извлекает отели по hotel_id из логов.
|
||||
"""
|
||||
hotels = {hotel.hotel_id: hotel for hotel in Hotel.objects.filter(hotel_id__in=hotel_ids)}
|
||||
self.log_info(f"Найдено {len(hotels)} отелей для сверки.")
|
||||
return hotels
|
||||
|
||||
if not external_id or not room_number:
|
||||
logger.warning(f"Log {i} skipped due to missing external_id or room_number.")
|
||||
continue # Пропускаем, если данные не извлечены
|
||||
def find_violations(self, user_logs, hotels):
|
||||
"""
|
||||
Сопоставляет логи активности с бронированиями и фиксирует нарушения.
|
||||
"""
|
||||
for log in user_logs:
|
||||
if not log.url_parameters:
|
||||
self.log_warning(f"Пропущена запись ID {log.id}: отсутствуют URL-параметры.")
|
||||
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
|
||||
# Парсинг параметров URL
|
||||
params = parse_qs(log.url_parameters)
|
||||
hotel_id = params.get("utm_content", [None])[0]
|
||||
room_number = params.get("utm_term", [None])[0]
|
||||
|
||||
# Ищем бронирование в 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.")
|
||||
print(f"Processing log ID {log.id} with hotel ID {hotel_id} and room number {room_number}")
|
||||
|
||||
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(
|
||||
if not hotel_id or not room_number:
|
||||
self.log_warning(f"Пропущена запись ID {log.id}: некорректные параметры URL.")
|
||||
continue
|
||||
|
||||
if hotel_id not in hotels:
|
||||
self.log_warning(f"Пропущена запись ID {log.id}: отель с ID {hotel_id} не найден.")
|
||||
continue
|
||||
|
||||
hotel = hotels[hotel_id]
|
||||
log_time = timezone.localtime(log.created)
|
||||
|
||||
# Проверка существования бронирования
|
||||
matching_reservations = Reservation.objects.filter(
|
||||
hotel=hotel,
|
||||
room_number=room_number,
|
||||
violation_type="missed",
|
||||
violation_details=violation_details
|
||||
))
|
||||
logger.warning(f"Log {i}: Violation recorded - {violation_details}")
|
||||
check_in__lte=log_time,
|
||||
check_out__gte=log_time
|
||||
)
|
||||
|
||||
# Сохраняем все нарушения в базу
|
||||
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.")
|
||||
print(f"Found {matching_reservations.count()} matching reservations")
|
||||
|
||||
logger.info("Reservation check completed.")
|
||||
if not matching_reservations.exists():
|
||||
violation_details = (
|
||||
f"Не найдено бронирование для номера {room_number} в отеле '{hotel.name}' на {log_time}."
|
||||
)
|
||||
# Добавляем нарушение, если его ещё нет в базе
|
||||
if not ViolationLog.objects.filter(
|
||||
hotel=hotel,
|
||||
room_number=room_number,
|
||||
violation_type="missed",
|
||||
violation_details=violation_details
|
||||
).exists():
|
||||
self.violations.append(ViolationLog(
|
||||
hotel=hotel,
|
||||
room_number=room_number,
|
||||
violation_type="missed",
|
||||
violation_details=violation_details
|
||||
))
|
||||
self.log_warning(f"Зафиксировано нарушение: {violation_details}")
|
||||
|
||||
def save_violations(self):
|
||||
"""
|
||||
Сохраняет найденные нарушения в базу данных.
|
||||
"""
|
||||
if self.violations:
|
||||
ViolationLog.objects.bulk_create(self.violations)
|
||||
self.log_info(f"Создано {len(self.violations)} записей в ViolationLog.")
|
||||
else:
|
||||
self.log_info("Нарушений не обнаружено.")
|
||||
|
||||
def run_check(self):
|
||||
"""
|
||||
Основной метод для запуска проверки.
|
||||
"""
|
||||
self.log_info(f"Запуск проверки бронирований с {self.start_time} по {self.end_time}.")
|
||||
|
||||
try:
|
||||
# Получаем логи пользователей
|
||||
user_logs = self.fetch_user_logs()
|
||||
|
||||
# Извлекаем hotel_id из логов
|
||||
hotel_ids = set()
|
||||
for log in user_logs:
|
||||
if log.url_parameters:
|
||||
params = parse_qs(log.url_parameters)
|
||||
hotel_id = params.get("utm_content", [None])[0]
|
||||
if hotel_id:
|
||||
hotel_ids.add(hotel_id)
|
||||
|
||||
# Предзагружаем отели
|
||||
hotels = self.fetch_hotels(hotel_ids)
|
||||
|
||||
# Сравниваем логи с бронированиями
|
||||
self.find_violations(user_logs, hotels)
|
||||
|
||||
# Сохраняем результаты
|
||||
self.save_violations()
|
||||
|
||||
except Exception as e:
|
||||
self.log_error(f"Произошла ошибка при выполнении проверки: {e}")
|
||||
|
||||
self.log_info("Проверка бронирований завершена.")
|
||||
|
||||
# Функция для запуска проверки из планировщика
|
||||
def run_reservation_check():
|
||||
"""
|
||||
Функция для запуска проверки бронирований.
|
||||
"""
|
||||
checker = ReservationChecker()
|
||||
checker.run_check()
|
||||
File diff suppressed because it is too large
Load Diff
18
antifroud/migrations/0013_alter_useractivitylog_timestamp.py
Normal file
18
antifroud/migrations/0013_alter_useractivitylog_timestamp.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# 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='Метка времени'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,68 @@
|
||||
# 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 пользователя'),
|
||||
),
|
||||
]
|
||||
18
antifroud/migrations/0015_alter_useractivitylog_page_id.py
Normal file
18
antifroud/migrations/0015_alter_useractivitylog_page_id.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# 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 страницы'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,39 @@
|
||||
# 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 пользователя'),
|
||||
),
|
||||
]
|
||||
@@ -4,30 +4,40 @@ from hotels.models import Reservation
|
||||
|
||||
|
||||
class UserActivityLog(models.Model):
|
||||
external_id = models.CharField(max_length=255, null=True, blank=True)
|
||||
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="Дата и время")
|
||||
external_id = models.CharField(max_length=255, unique=True, verbose_name="Внешний ID", db_index=True)
|
||||
user_id = models.BigIntegerField(verbose_name="ID пользователя", blank=True, null=True, db_index=True)
|
||||
ip = models.GenericIPAddressField(verbose_name="IP-адрес", blank=True, null=True, db_index=True)
|
||||
created = models.DateTimeField(verbose_name="Дата создания", blank=True, null=True, db_index=True)
|
||||
timestamp = models.BigIntegerField(verbose_name="Метка времени", blank=True, null=True)
|
||||
date_time = models.DateTimeField(verbose_name="Дата и время", blank=True, null=True)
|
||||
referred = models.TextField(blank=True, null=True, verbose_name="Реферальная ссылка")
|
||||
agent = models.TextField(verbose_name="Агент пользователя")
|
||||
agent = models.TextField(verbose_name="Агент пользователя", blank=True, null=True)
|
||||
platform = models.CharField(max_length=255, blank=True, null=True, verbose_name="Платформа")
|
||||
version = models.CharField(max_length=255, blank=True, null=True, verbose_name="Версия")
|
||||
model = models.CharField(max_length=255, blank=True, null=True, verbose_name="Модель устройства")
|
||||
device = models.CharField(max_length=255, blank=True, null=True, verbose_name="Тип устройства")
|
||||
UAString = models.TextField(verbose_name="User-Agent строка")
|
||||
UAString = models.TextField(verbose_name="User-Agent строка", blank=True, null=True)
|
||||
location = models.CharField(max_length=255, blank=True, null=True, verbose_name="Местоположение")
|
||||
page_id = models.BigIntegerField(blank=True, null=True, verbose_name="ID страницы")
|
||||
page_id = models.BigIntegerField(blank=True, null=True, verbose_name="ID страницы", db_index=True)
|
||||
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="Ответ пользователя")
|
||||
type = models.CharField(max_length=50, verbose_name="Тип", blank=True, null=True)
|
||||
last_counter = models.IntegerField(verbose_name="Последний счетчик", blank=True, null=True)
|
||||
hits = models.IntegerField(verbose_name="Количество обращений", blank=True, null=True)
|
||||
honeypot = models.BooleanField(verbose_name="Метка honeypot", blank=True, null=True)
|
||||
reply = models.BooleanField(verbose_name="Ответ пользователя", blank=True, null=True)
|
||||
page_url = models.URLField(blank=True, null=True, verbose_name="URL страницы")
|
||||
|
||||
class Meta:
|
||||
indexes = [
|
||||
models.Index(fields=["external_id"], name="idx_external_id"),
|
||||
models.Index(fields=["user_id"], name="idx_user_id"),
|
||||
models.Index(fields=["ip"], name="idx_ip"),
|
||||
models.Index(fields=["created"], name="idx_created"),
|
||||
models.Index(fields=["page_id"], name="idx_page_id"),
|
||||
]
|
||||
verbose_name = "Лог активности пользователя"
|
||||
verbose_name_plural = "Логи активности пользователей"
|
||||
def __str__(self):
|
||||
return f"UserActivityLog {self.id}: {self.page_title}"
|
||||
|
||||
|
||||
Reference in New Issue
Block a user