Corp/backend/calendar_app/notification_signals.py

225 lines
8.3 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
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}'