225 lines
8.3 KiB
Python
225 lines
8.3 KiB
Python
"""
|
||
Signals для автоматической генерации уведомлений в модуле Calendar.
|
||
|
||
Обрабатывает события:
|
||
- Новое событие календаря
|
||
- Изменение события
|
||
- Отмена события
|
||
|
||
Напоминания (за час, за день) будут реализованы через Celery tasks.
|
||
"""
|
||
|
||
from django.db.models.signals import post_save, pre_delete, pre_save
|
||
from django.dispatch import receiver
|
||
from django.contrib.auth import get_user_model
|
||
|
||
from .models import CalendarEvent
|
||
from notifications.services import NotificationService
|
||
|
||
Employee = get_user_model()
|
||
|
||
|
||
@receiver(post_save, sender=CalendarEvent)
|
||
def create_event_notifications(sender, instance, created, **kwargs):
|
||
"""
|
||
Создает уведомления при создании или изменении события.
|
||
|
||
Уведомления отправляются:
|
||
- При создании - всем сотрудникам компании или отдела
|
||
- При изменении - участникам события
|
||
"""
|
||
event = instance
|
||
|
||
if created:
|
||
# Новое событие - уведомляем всех участников
|
||
notify_event_created(event)
|
||
else:
|
||
# Проверяем изменение ключевых полей
|
||
if hasattr(event, '_changed_fields'):
|
||
notify_event_changed(event, event._changed_fields)
|
||
|
||
|
||
@receiver(pre_save, sender=CalendarEvent)
|
||
def track_event_changes(sender, instance, **kwargs):
|
||
"""
|
||
Отслеживает изменения важных полей события для уведомлений.
|
||
"""
|
||
if instance.pk:
|
||
try:
|
||
old_event = CalendarEvent.objects.get(pk=instance.pk)
|
||
changed_fields = []
|
||
|
||
# Отслеживаем важные поля
|
||
important_fields = [
|
||
'title', 'start_date', 'end_date',
|
||
'start_time', 'end_time', 'location'
|
||
]
|
||
|
||
for field in important_fields:
|
||
old_value = getattr(old_event, field)
|
||
new_value = getattr(instance, field)
|
||
if old_value != new_value:
|
||
changed_fields.append(field)
|
||
|
||
instance._changed_fields = changed_fields
|
||
except CalendarEvent.DoesNotExist:
|
||
instance._changed_fields = []
|
||
|
||
|
||
@receiver(pre_delete, sender=CalendarEvent)
|
||
def notify_event_cancelled(sender, instance, **kwargs):
|
||
"""
|
||
Создает уведомления при удалении (отмене) события.
|
||
"""
|
||
event = instance
|
||
|
||
# Определяем получателей
|
||
recipients = get_event_recipients(event)
|
||
|
||
# Создаем уведомления об отмене
|
||
for recipient in recipients:
|
||
NotificationService.create_notification(
|
||
recipient=recipient,
|
||
notification_type_code='event_cancelled',
|
||
title='Событие отменено',
|
||
message=(
|
||
f'Событие "{event.title}" '
|
||
f'({event.start_date.strftime("%d.%m.%Y")}) отменено'
|
||
),
|
||
action_url=get_calendar_url(event),
|
||
metadata={
|
||
'event_title': event.title,
|
||
'event_date': event.start_date.isoformat(),
|
||
'department_id': (
|
||
event.department.id if event.department else None
|
||
),
|
||
'is_company_event': event.is_company,
|
||
}
|
||
)
|
||
|
||
|
||
# ===== Вспомогательные функции =====
|
||
|
||
def notify_event_created(event):
|
||
"""
|
||
Отправляет уведомления о новом событии всем участникам.
|
||
"""
|
||
recipients = get_event_recipients(event)
|
||
|
||
# Исключаем создателя события
|
||
if event.created_by:
|
||
recipients = [r for r in recipients if r.id != event.created_by.id]
|
||
|
||
event_scope = (
|
||
f'отдела {event.department.name}'
|
||
if event.department
|
||
else 'компании'
|
||
)
|
||
|
||
for recipient in recipients:
|
||
NotificationService.create_notification(
|
||
recipient=recipient,
|
||
notification_type_code='event_created',
|
||
title='Новое событие в календаре',
|
||
message=(
|
||
f'Добавлено событие {event_scope}: "{event.title}" '
|
||
f'({event.start_date.strftime("%d.%m.%Y")})'
|
||
),
|
||
content_object=event,
|
||
action_url=get_calendar_url(event),
|
||
metadata={
|
||
'event_id': event.id,
|
||
'event_title': event.title,
|
||
'event_date': event.start_date.isoformat(),
|
||
'department_id': (
|
||
event.department.id if event.department else None
|
||
),
|
||
'is_company_event': event.is_company,
|
||
'created_by_id': (
|
||
event.created_by.id if event.created_by else None
|
||
),
|
||
}
|
||
)
|
||
|
||
|
||
def notify_event_changed(event, changed_fields):
|
||
"""
|
||
Отправляет уведомления об изменении события.
|
||
"""
|
||
if not changed_fields:
|
||
return
|
||
|
||
recipients = get_event_recipients(event)
|
||
|
||
# Формируем сообщение об изменениях
|
||
field_names = {
|
||
'title': 'название',
|
||
'start_date': 'дата начала',
|
||
'end_date': 'дата окончания',
|
||
'start_time': 'время начала',
|
||
'end_time': 'время окончания',
|
||
'location': 'место проведения',
|
||
}
|
||
|
||
changes_text = ', '.join([
|
||
field_names.get(f, f) for f in changed_fields
|
||
])
|
||
|
||
for recipient in recipients:
|
||
NotificationService.create_notification(
|
||
recipient=recipient,
|
||
notification_type_code='event_changed',
|
||
title='Событие изменено',
|
||
message=(
|
||
f'Событие "{event.title}" изменено. '
|
||
f'Изменения: {changes_text}'
|
||
),
|
||
content_object=event,
|
||
action_url=get_calendar_url(event),
|
||
metadata={
|
||
'event_id': event.id,
|
||
'event_title': event.title,
|
||
'changed_fields': changed_fields,
|
||
'event_date': event.start_date.isoformat(),
|
||
'department_id': (
|
||
event.department.id if event.department else None
|
||
),
|
||
}
|
||
)
|
||
|
||
|
||
def get_event_recipients(event):
|
||
"""
|
||
Возвращает список получателей уведомлений для события.
|
||
|
||
- Для события компании - все активные сотрудники
|
||
- Для события отдела - сотрудники этого отдела
|
||
- Для личного события - только владелец
|
||
"""
|
||
if event.is_personal:
|
||
# Личное событие - только владелец
|
||
return [event.employee] if event.employee else []
|
||
elif event.is_company:
|
||
# Событие компании - все активные сотрудники
|
||
return list(Employee.objects.filter(is_active=True))
|
||
elif event.department:
|
||
# Событие отдела - сотрудники через EmployeeDepartment
|
||
from employees.models import EmployeeDepartment
|
||
employee_ids = EmployeeDepartment.objects.filter(
|
||
department=event.department,
|
||
is_active=True
|
||
).values_list('employee_id', flat=True)
|
||
return list(Employee.objects.filter(id__in=employee_ids, is_active=True))
|
||
|
||
return []
|
||
|
||
|
||
def get_calendar_url(event):
|
||
"""
|
||
Возвращает URL календаря в зависимости от типа события.
|
||
Для всех типов событий используем параметр event_id для прямого открытия.
|
||
"""
|
||
# Возвращаем главную страницу календаря с параметром event_id
|
||
# Это позволит открыть модал события автоматически
|
||
return f'/?event_id={event.id}'
|