""" Django signals для автоматической генерации уведомлений в модуле Documents. Обрабатывает события: - post_save для Document - создание нового документа - m2m_changed для Document.recipients - добавление конкретных получателей - m2m_changed для Document.departments - добавление отделов - post_save для DocumentAcknowledgement - проверка полного ознакомления - post_save для DocumentComment - новый комментарий или ответ """ import logging from django.contrib.auth import get_user_model from django.db.models.signals import post_save, m2m_changed from django.dispatch import receiver from ..models import Document, DocumentAcknowledgement, DocumentComment from .handlers import ( notify_all_employees, notify_specific_users, notify_department_employees, notify_all_acknowledged, notify_document_comment, notify_comment_reply, ) logger = logging.getLogger(__name__) Employee = get_user_model() @receiver(post_save, sender=Document) def create_document_notification(sender, instance, created, **kwargs): """ Создает уведомления при создании нового документа. Уведомления отправляются: - Всем активным сотрудникам (если sent_to_all=True) - Сотрудникам выбранных отделов (через m2m_changed signal) - Выбранным получателям (через m2m_changed signal) """ logger.info( f"[signals] post_save Document id={instance.pk} " f"created={created} sent_to_all={instance.sent_to_all}" ) if not created: return document = instance # Если документ отправляется всем - создаем уведомления напрямую # channels.py автоматически отправит через Celery if document.sent_to_all: logger.info( f"[signals] Creating notifications for doc={document.pk} (sent_to_all=True)" ) notify_all_employees(document) else: logger.info( f"[signals] Skipping notifications (sent_to_all=False), " f"waiting for m2m_changed" ) @receiver(m2m_changed, sender=Document.recipients.through) def notify_specific_recipients(sender, instance, action, pk_set, **kwargs): """ Уведомляет конкретных получателей при добавлении в список. Использует быструю обработку для большого количества получателей. """ logger.info( f"[signals] m2m_changed Document id={instance.pk} " f"action={action} pk_set={pk_set} sent_to_all={instance.sent_to_all}" ) if action != 'post_add': logger.info( f"[signals] Skipping (action={action} != 'post_add')" ) return document = instance # Не отправляем индивидуальные уведомления если документ для всех if document.sent_to_all: logger.info( "[signals] Skipping (sent_to_all=True)" ) return # Отправляем уведомления через handlers notify_specific_users(document, pk_set) @receiver(m2m_changed, sender=Document.departments.through) def notify_department_employees_signal(sender, instance, action, pk_set, **kwargs): """ Уведомляет всех сотрудников выбранных отделов при добавлении отделов. Уведомления получат ВСЕ текущие и будущие сотрудники этих отделов. """ logger.info( f"[signals] m2m_changed Document.departments " f"id={instance.pk} action={action} pk_set={pk_set}" ) if action != 'post_add': logger.info( f"[signals] Skipping (action={action} != 'post_add')" ) return document = instance # Не отправляем если документ для всех if document.sent_to_all: logger.info( "[signals] Skipping (sent_to_all=True)" ) return # Отправляем уведомления через handlers notify_department_employees(document, pk_set) @receiver(post_save, sender=DocumentAcknowledgement) def check_all_acknowledged(sender, instance, created, **kwargs): """ Проверяет, все ли получатели ознакомились с документом. Если да - уведомляет загрузившего. """ if not created: return acknowledgement = instance document = acknowledgement.document # Определяем общее количество получателей if document.sent_to_all: total_recipients = Employee.objects.filter(is_active=True).count() else: # Считаем получателей из recipients + departments recipients_count = document.recipients.count() # Добавляем сотрудников из отделов from employees.models import Department department_employees = set() for department in document.departments.all(): department_employees.update(department.active_employees) # Исключаем дубликаты с recipients total_recipients = recipients_count + len(department_employees) # Проверяем количество ознакомившихся acknowledged_count = document.acknowledgements.count() # Если все ознакомились - уведомляем загрузившего if (acknowledged_count >= total_recipients and total_recipients > 0 and document.uploaded_by): notify_all_acknowledged(document, total_recipients, acknowledged_count) @receiver(post_save, sender=DocumentComment) def notify_on_new_comment(sender, instance, created, **kwargs): """ Отправка уведомлений при создании нового комментария. - Если это ответ на комментарий (parent != None): уведомить автора родительского комментария - Если это обычный комментарий: уведомить автора документа (если он не автор комментария) """ if not created: return comment = instance document = comment.document author = comment.author logger.info( f"[signals] New comment on document={document.pk} " f"by user={author.pk} parent={comment.parent_id}" ) # Если это ответ на комментарий if comment.parent: notify_comment_reply(comment.parent, comment) else: # Обычный комментарий - уведомляем автора документа notify_document_comment(document, comment)