""" Сигналы для автоматической отправки уведомлений. """ from django.db.models.signals import post_save from django.dispatch import receiver from django.utils.html import strip_tags from .services import NotificationService, create_notification_preferences # Сигналы пользователей @receiver(post_save, sender='users.User') def create_user_notification_preferences(sender, instance, created, **kwargs): """Создание настроек уведомлений для нового пользователя.""" if created: create_notification_preferences(instance) # Сигналы занятий @receiver(post_save, sender='schedule.Lesson') def handle_lesson_notifications(sender, instance, created, **kwargs): """ Обработка уведомлений для занятий. Примечание: Уведомление о создании занятия отправляется явно в perform_create, чтобы избежать дублирования. Здесь обрабатываем только обновления. """ if not created: # Занятие обновлено - проверяем статус # Уведомление об отмене отправляется явно в perform_destroy, # но оставляем здесь на случай, если статус меняется другим способом if instance.status == 'cancelled' and instance.cancelled_at: # Проверяем, не было ли уже отправлено уведомление # (чтобы избежать дублирования с perform_destroy) pass # Уведомление об отмене отправляется явно в perform_destroy # Сигналы уведомлений - дублирование в чат @receiver(post_save, sender='notifications.Notification') def duplicate_notification_to_chat(sender, instance, created, **kwargs): """ Дублирование системных уведомлений в чат между ментором и учеником/родителем. Когда создается уведомление для ученика или родителя от ментора, оно также создается как системное сообщение в соответствующем чате. """ if not created: return # Дублируем только in_app уведомления if instance.channel != 'in_app': return # Дублируем только определенные типы уведомлений notification_types_to_duplicate = [ 'lesson_created', 'lesson_updated', 'lesson_cancelled', 'lesson_rescheduled', 'lesson_reminder', 'lesson_started', 'lesson_completed', 'homework_assigned', 'homework_submitted', 'homework_reviewed', 'homework_returned', 'homework_deadline_reminder', 'homework_overdue', 'material_added', 'subscription_expiring', 'subscription_expired', 'payment_received', 'system', ] if instance.notification_type not in notification_types_to_duplicate: return try: from apps.chat.models import Chat, Message, ChatParticipant from apps.users.models import User, Client, Parent recipient = instance.recipient # Определяем ментора для создания чата mentor = None # Если получатель - ученик, находим его ментора if recipient.role == 'client': try: client = Client.objects.get(user=recipient) mentors = client.mentors.all() if mentors.exists(): mentor = mentors.first() except Client.DoesNotExist: pass # Если получатель - родитель, находим ментора через детей elif recipient.role == 'parent': try: parent = Parent.objects.get(user=recipient) children = parent.children.all() if children.exists(): # Берем первого ментора первого ребенка child = children.first() mentors = child.mentors.all() if mentors.exists(): mentor = mentors.first() except Parent.DoesNotExist: pass # Если получатель - ментор, находим ученика/родителя из контекста уведомления elif recipient.role == 'mentor': # Для уведомлений ментору нужно найти связанного ученика/родителя # Это зависит от типа уведомления и content_object if instance.content_object: content_obj = instance.content_object # Если это занятие, берем клиента из занятия if hasattr(content_obj, 'client'): client = content_obj.client if client and client.user: recipient = client.user # Если это ДЗ, берем студента из ДЗ elif hasattr(content_obj, 'student'): recipient = content_obj.student # Если это submission, берем студента elif hasattr(content_obj, 'homework') and hasattr(content_obj, 'student'): recipient = content_obj.student else: return # Не можем определить получателя mentor = instance.recipient if not mentor: return # Находим или создаем личный чат между ментором и получателем chat = Chat.objects.filter( chat_type='direct', participants__user=mentor ).filter( participants__user=recipient ).first() if not chat: # Создаем чат если его нет chat = Chat.objects.create( chat_type='direct', created_by=mentor ) ChatParticipant.objects.create(chat=chat, user=mentor, role='admin') ChatParticipant.objects.create(chat=chat, user=recipient, role='member') # Создаем системное сообщение в чате (без HTML-тегов, чтобы в чате не отображались теги) title_plain = strip_tags(instance.title or '') message_plain = strip_tags(instance.message or '') message_content = f"🔔 {title_plain}\n{message_plain}" Message.objects.create( chat=chat, sender=None, # Системное сообщение message_type='system', content=message_content ) except Exception as e: # Логируем ошибку, но не прерываем создание уведомления import logging logger = logging.getLogger(__name__) logger.error(f'Error duplicating notification to chat: {e}', exc_info=True)