166 lines
7.5 KiB
Python
166 lines
7.5 KiB
Python
"""
|
||
Сигналы для автоматической отправки уведомлений.
|
||
"""
|
||
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)
|
||
|