""" Бизнес-логика отправки уведомлений для django-scheduler. Функции: - get_event_recipients - определение получателей уведомлений - notify_event_created - уведомление о новом событии - notify_event_changed - уведомление об изменении события - notify_event_cancelled - уведомление об отмене события """ import logging from django.contrib.auth import get_user_model from django.contrib.contenttypes.models import ContentType logger = logging.getLogger(__name__) Employee = get_user_model() def get_event_recipients(event) -> list: """ Возвращает список получателей уведомлений для события django-scheduler. Args: event: Объект schedule.Event Returns: list: Список пользователей (Employee) с доступом к календарю события """ from schedule.models import CalendarRelation if not event.calendar: return [] # Получаем всех пользователей с доступом к календарю user_ct = ContentType.objects.get_for_model(Employee) relations = CalendarRelation.objects.filter( calendar=event.calendar, content_type=user_ct, object_id__isnull=False ) # Собираем активных пользователей user_ids = relations.values_list("object_id", flat=True) recipients = list(Employee.objects.filter(id__in=user_ids, is_active=True)) return recipients def notify_event_created(event, creator=None): """ Отправляет уведомления о новом событии всем участникам календаря. Args: event: Объект schedule.Event creator: Пользователь, создавший событие (исключается из получателей) """ from scheduling.services import calendar_notification_enabled if not calendar_notification_enabled(event.calendar, "notify_event_created"): return try: from notifications.signals import notify from .config import ( ActionURLs, MessageTemplates, NotificationVerbs, format_date, ) recipients = get_event_recipients(event) # Исключаем создателя события if creator: recipients = [r for r in recipients if r.id != creator.id] if not recipients: return # Формируем сообщение event_date = format_date(event.start) calendar_name = event.calendar.name if event.calendar else None description = MessageTemplates.event_created( event.title, event_date, calendar_name ) # Отправляем уведомления for recipient in recipients: notify.send( sender=creator, recipient=recipient, verb=NotificationVerbs.EVENT_CREATED, action_object=event, description=description, action_url=ActionURLs.CALENDAR, data={ "title": MessageTemplates.event_created_title(), "event_id": event.id, "event_title": event.title, "event_start": (event.start.isoformat() if event.start else None), "calendar_id": (event.calendar.id if event.calendar else None), "calendar_name": calendar_name, "creator_id": creator.id if creator else None, }, ) logger.info( "Schedule event creation notifications sent", extra={ "event": "schedule.event.created_notifications_sent", "schedule_event_id": event.id, "recipient_count": len(recipients), }, ) except Exception as e: logger.exception( "Schedule event creation notifications failed", extra={ "event": "schedule.event.created_notifications_failed", "schedule_event_id": getattr(event, "id", None), "error_type": type(e).__name__, }, ) def notify_event_changed(event, changed_fields: list[str] = None, modifier=None): """ Отправляет уведомления об изменении события. Args: event: Объект schedule.Event changed_fields: Список изменённых полей modifier: Пользователь, изменивший событие """ from scheduling.services import calendar_notification_enabled if not calendar_notification_enabled(event.calendar, "notify_event_changed"): return try: from notifications.signals import notify from .config import ( ActionURLs, MessageTemplates, NotificationVerbs, format_changes, ) recipients = get_event_recipients(event) # Исключаем модификатора if modifier: recipients = [r for r in recipients if r.id != modifier.id] if not recipients: return # Формируем сообщение об изменениях changes_text = format_changes(changed_fields) if changed_fields else None description = MessageTemplates.event_changed(event.title, changes_text) # Отправляем уведомления for recipient in recipients: notify.send( sender=modifier, recipient=recipient, verb=NotificationVerbs.EVENT_CHANGED, action_object=event, description=description, action_url=ActionURLs.CALENDAR, data={ "title": MessageTemplates.event_changed_title(), "event_id": event.id, "event_title": event.title, "changed_fields": changed_fields, "event_start": event.start.isoformat() if event.start else None, "calendar_id": event.calendar.id if event.calendar else None, "modifier_id": modifier.id if modifier else None, }, ) logger.info( "Schedule event update notifications sent", extra={ "event": "schedule.event.updated_notifications_sent", "schedule_event_id": event.id, "recipient_count": len(recipients), "changed_field_count": len(changed_fields or []), }, ) except Exception as e: logger.exception( "Schedule event update notifications failed", extra={ "event": "schedule.event.updated_notifications_failed", "schedule_event_id": getattr(event, "id", None), "error_type": type(e).__name__, }, ) def notify_event_cancelled(event, canceller=None): """ Отправляет уведомления об отмене (удалении) события. Args: event: Объект schedule.Event canceller: Пользователь, отменивший событие """ from scheduling.services import calendar_notification_enabled if not calendar_notification_enabled(event.calendar, "notify_event_cancelled"): return try: from notifications.signals import notify from .config import ( ActionURLs, MessageTemplates, NotificationVerbs, format_date, ) recipients = get_event_recipients(event) # Исключаем отменившего if canceller: recipients = [r for r in recipients if r.id != canceller.id] if not recipients: return # Формируем сообщение event_date = format_date(event.start) description = MessageTemplates.event_cancelled(event.title, event_date) # Отправляем уведомления for recipient in recipients: notify.send( sender=canceller, recipient=recipient, verb=NotificationVerbs.EVENT_CANCELLED, description=description, action_url=ActionURLs.CALENDAR, data={ "title": MessageTemplates.event_cancelled_title(), "event_id": event.id, "event_title": event.title, "event_start": event.start.isoformat() if event.start else None, "calendar_id": event.calendar.id if event.calendar else None, "canceller_id": canceller.id if canceller else None, }, ) logger.info( "Schedule event cancellation notifications sent", extra={ "event": "schedule.event.cancelled_notifications_sent", "schedule_event_id": event.id, "recipient_count": len(recipients), }, ) except Exception as e: logger.exception( "Schedule event cancellation notifications failed", extra={ "event": "schedule.event.cancelled_notifications_failed", "schedule_event_id": getattr(event, "id", None), "error_type": type(e).__name__, }, )