uchill/backend/apps/notifications/signals.py

166 lines
7.5 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.

"""
Сигналы для автоматической отправки уведомлений.
"""
from django.db.models.signals import post_save
from django.dispatch import receiver
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')
# Создаем системное сообщение в чате
message_content = f"🔔 {instance.title}\n{instance.message}"
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)