3 changed files with 287 additions and 0 deletions
-
184apps/account/tests/notifications/test_communication_notifications.py
-
3apps/chat/apps.py
-
100apps/chat/signals.py
@ -0,0 +1,184 @@ |
|||||
|
import datetime |
||||
|
from django.utils import timezone |
||||
|
from django.test import TestCase |
||||
|
from unittest.mock import patch |
||||
|
from django.core.files.uploadedfile import SimpleUploadedFile |
||||
|
|
||||
|
from dj_language.models import Language |
||||
|
from apps.account.models import StudentUser, User, Notification |
||||
|
from apps.course.models.course import Course, CourseCategory |
||||
|
from apps.course.models.participant import Participant |
||||
|
from apps.chat.models import RoomMessage, ChatMessage |
||||
|
|
||||
|
class CommunicationNotificationsFlowTests(TestCase): |
||||
|
def setUp(self): |
||||
|
# Create languages |
||||
|
self.lang_fa, _ = Language.objects.update_or_create( |
||||
|
code='fa', |
||||
|
defaults={'name': 'Persian', 'status': True, 'countries': []} |
||||
|
) |
||||
|
self.lang_en, _ = Language.objects.update_or_create( |
||||
|
code='en', |
||||
|
defaults={'name': 'English', 'status': True, 'countries': []} |
||||
|
) |
||||
|
|
||||
|
# Create category |
||||
|
self.category = CourseCategory.objects.create( |
||||
|
name=[{"title": "Category", "language_code": "en"}], |
||||
|
slug=[{"title": "category", "language_code": "en"}] |
||||
|
) |
||||
|
|
||||
|
# Create student user |
||||
|
self.student = StudentUser.objects.create( |
||||
|
email='student_comm@example.com', |
||||
|
username='student_comm', |
||||
|
fullname='Comm Student', |
||||
|
language=self.lang_fa, |
||||
|
fcm='token_comm_student' |
||||
|
) |
||||
|
|
||||
|
# Create teacher user |
||||
|
self.teacher = User.objects.create( |
||||
|
email='teacher_comm@example.com', |
||||
|
username='teacher_comm', |
||||
|
fullname='Teacher Comm', |
||||
|
user_type='professor', |
||||
|
is_active=True |
||||
|
) |
||||
|
|
||||
|
# Create support user (admin) |
||||
|
self.support = User.objects.create( |
||||
|
email='support_comm@example.com', |
||||
|
username='support_comm', |
||||
|
fullname='Support Comm', |
||||
|
user_type='admin', |
||||
|
is_active=True |
||||
|
) |
||||
|
|
||||
|
# Create Course |
||||
|
thumbnail = SimpleUploadedFile('thumb.jpg', b'filecontent', content_type='image/jpeg') |
||||
|
self.course = Course.objects.create( |
||||
|
title=[ |
||||
|
{"title": "Course 1", "language_code": "en"}, |
||||
|
{"title": "دوره ۱", "language_code": "fa"} |
||||
|
], |
||||
|
slug=[{"title": "course-1", "language_code": "en"}], |
||||
|
category=self.category, |
||||
|
thumbnail=thumbnail, |
||||
|
video_type=Course.VedioTypeChoices.YOUTUBE_LINK, |
||||
|
video_link='https://example.com/video', |
||||
|
is_online=False, |
||||
|
level=Course.LevelChoices.BEGINNER, |
||||
|
duration=10, |
||||
|
lessons_count=0, |
||||
|
description='Description', |
||||
|
short_description='Short description', |
||||
|
status=[{"title": "ongoing", "language_code": "en"}], |
||||
|
is_free=True, |
||||
|
) |
||||
|
|
||||
|
# Enroll student in the course |
||||
|
self.participant = Participant.objects.create( |
||||
|
student=self.student, |
||||
|
course=self.course, |
||||
|
is_active=True |
||||
|
) |
||||
|
|
||||
|
@patch('apps.account.notification_service.send_notification') |
||||
|
def test_teacher_replied_private_chat(self, mock_send): |
||||
|
# Create a private chat room between student and teacher |
||||
|
room = RoomMessage.objects.create( |
||||
|
name="Private chat with teacher", |
||||
|
initiator=self.student, |
||||
|
recipient=self.teacher, |
||||
|
room_type=RoomMessage.RoomTypeChoices.PRIVATE |
||||
|
) |
||||
|
|
||||
|
# Teacher sends a message |
||||
|
ChatMessage.objects.create( |
||||
|
room=room, |
||||
|
sender=self.teacher, |
||||
|
content="Hello from teacher", |
||||
|
content_type=ChatMessage.ChatTypeChoices.TEXT |
||||
|
) |
||||
|
|
||||
|
# Verify notification sent to the student |
||||
|
notif = Notification.objects.filter(user=self.student, title="پیام جدید از طرف استاد").first() |
||||
|
self.assertIsNotNone(notif) |
||||
|
self.assertIn("Hello from teacher", notif.message) |
||||
|
self.assertTrue(mock_send.called) |
||||
|
|
||||
|
@patch('apps.account.notification_service.send_notification') |
||||
|
def test_support_replied_private_chat(self, mock_send): |
||||
|
# Create a private chat room between student and support |
||||
|
room = RoomMessage.objects.create( |
||||
|
name="Support chat", |
||||
|
initiator=self.student, |
||||
|
recipient=self.support, |
||||
|
room_type=RoomMessage.RoomTypeChoices.PRIVATE |
||||
|
) |
||||
|
|
||||
|
# Support agent sends a message |
||||
|
ChatMessage.objects.create( |
||||
|
room=room, |
||||
|
sender=self.support, |
||||
|
content="How can I help you?", |
||||
|
content_type=ChatMessage.ChatTypeChoices.TEXT |
||||
|
) |
||||
|
|
||||
|
# Verify notification sent to the student |
||||
|
notif = Notification.objects.filter(user=self.student, title="پاسخ پشتیبانی").first() |
||||
|
self.assertIsNotNone(notif) |
||||
|
self.assertIn("How can I help you?", notif.message) |
||||
|
self.assertTrue(mock_send.called) |
||||
|
|
||||
|
@patch('apps.account.notification_service.send_notification') |
||||
|
def test_teacher_posted_in_course_chat(self, mock_send): |
||||
|
# Create a course chat room |
||||
|
room = RoomMessage.objects.create( |
||||
|
name="Course group chat", |
||||
|
course=self.course, |
||||
|
initiator=self.teacher, |
||||
|
room_type=RoomMessage.RoomTypeChoices.GROUP |
||||
|
) |
||||
|
|
||||
|
# Teacher sends a message to course chat |
||||
|
ChatMessage.objects.create( |
||||
|
room=room, |
||||
|
sender=self.teacher, |
||||
|
content="Hello class, remember the homework", |
||||
|
content_type=ChatMessage.ChatTypeChoices.TEXT |
||||
|
) |
||||
|
|
||||
|
# Verify notification sent to the student enrolled |
||||
|
notif = Notification.objects.filter(user=self.student, title="پیام استاد در گفتگوی دوره").first() |
||||
|
self.assertIsNotNone(notif) |
||||
|
self.assertIn("homework", notif.message) |
||||
|
self.assertIn("دوره ۱", notif.message) |
||||
|
self.assertTrue(mock_send.called) |
||||
|
|
||||
|
@patch('apps.account.notification_service.send_notification') |
||||
|
def test_student_message_no_notification(self, mock_send): |
||||
|
# Create a private chat room |
||||
|
room = RoomMessage.objects.create( |
||||
|
name="Private chat", |
||||
|
initiator=self.student, |
||||
|
recipient=self.teacher, |
||||
|
room_type=RoomMessage.RoomTypeChoices.PRIVATE |
||||
|
) |
||||
|
|
||||
|
# Clear notifications |
||||
|
Notification.objects.filter(user=self.student).delete() |
||||
|
mock_send.reset_mock() |
||||
|
|
||||
|
# Student sends a message |
||||
|
ChatMessage.objects.create( |
||||
|
room=room, |
||||
|
sender=self.student, |
||||
|
content="Hello professor", |
||||
|
content_type=ChatMessage.ChatTypeChoices.TEXT |
||||
|
) |
||||
|
|
||||
|
# Student should not receive a notification for their own message |
||||
|
self.assertFalse(Notification.objects.filter(user=self.student).exists()) |
||||
|
self.assertFalse(mock_send.called) |
||||
@ -0,0 +1,100 @@ |
|||||
|
import logging |
||||
|
from django.db.models.signals import post_save |
||||
|
from django.dispatch import receiver |
||||
|
from apps.chat.models import ChatMessage, RoomMessage |
||||
|
from apps.account.models import User |
||||
|
from apps.account.notification_service import create_and_send_notification |
||||
|
from apps.course.models.course import get_localized_field |
||||
|
from apps.course.models.participant import Participant |
||||
|
|
||||
|
logger = logging.getLogger(__name__) |
||||
|
|
||||
|
def get_message_preview(instance, lang='fa'): |
||||
|
if instance.content_type == 'text': |
||||
|
preview = instance.content or '' |
||||
|
if len(preview) > 50: |
||||
|
return preview[:50] + '...' |
||||
|
return preview |
||||
|
elif instance.content_type == 'image': |
||||
|
return 'تصویر' if lang == 'fa' else 'Image' |
||||
|
elif instance.content_type == 'audio': |
||||
|
return 'صدا' if lang == 'fa' else 'Audio' |
||||
|
else: |
||||
|
return 'فایل ضمیمه' if lang == 'fa' else 'Attachment' |
||||
|
|
||||
|
@receiver(post_save, sender=ChatMessage) |
||||
|
def notify_on_new_chat_message(sender, instance, created, **kwargs): |
||||
|
if not created: |
||||
|
return |
||||
|
|
||||
|
sender_user = instance.sender |
||||
|
room = instance.room |
||||
|
|
||||
|
# 1. Check if the message is in a PRIVATE room |
||||
|
if room.room_type == RoomMessage.RoomTypeChoices.PRIVATE: |
||||
|
# Determine the recipient user (the other person in the private room) |
||||
|
recipient_user = None |
||||
|
if room.initiator == sender_user: |
||||
|
recipient_user = room.recipient |
||||
|
else: |
||||
|
recipient_user = room.initiator |
||||
|
|
||||
|
if not recipient_user: |
||||
|
return |
||||
|
|
||||
|
# Case A: Teacher Replied in a Private Chat |
||||
|
if sender_user.user_type == User.UserType.PROFESSOR: |
||||
|
if recipient_user.user_type in [User.UserType.STUDENT, User.UserType.CLIENT]: |
||||
|
preview_en = get_message_preview(instance, 'en') |
||||
|
preview_fa = get_message_preview(instance, 'fa') |
||||
|
|
||||
|
create_and_send_notification( |
||||
|
user=recipient_user, |
||||
|
title_en="New Message from Teacher", |
||||
|
body_en=f"Teacher {sender_user.fullname or sender_user.username} replied: {preview_en}", |
||||
|
title_fa="پیام جدید از طرف استاد", |
||||
|
body_fa=f"استاد {sender_user.fullname or sender_user.username} پاسخ داد: {preview_fa}", |
||||
|
service='imam-javad', |
||||
|
data={'type': 'teacher_reply_private', 'message_id': instance.id, 'room_id': room.id} |
||||
|
) |
||||
|
|
||||
|
# Case B: Support Replied |
||||
|
elif sender_user.user_type in [User.UserType.ADMIN, User.UserType.SUPER_ADMIN, User.UserType.CONSULTANT]: |
||||
|
if recipient_user.user_type in [User.UserType.STUDENT, User.UserType.CLIENT]: |
||||
|
preview_en = get_message_preview(instance, 'en') |
||||
|
preview_fa = get_message_preview(instance, 'fa') |
||||
|
|
||||
|
create_and_send_notification( |
||||
|
user=recipient_user, |
||||
|
title_en="New Message from Support", |
||||
|
body_en=f"Support agent {sender_user.fullname or sender_user.username} replied to your message: {preview_en}", |
||||
|
title_fa="پاسخ پشتیبانی", |
||||
|
body_fa=f"پشتیبان {sender_user.fullname or sender_user.username} به پیام شما پاسخ داد: {preview_fa}", |
||||
|
service='imam-javad', |
||||
|
data={'type': 'support_reply', 'message_id': instance.id, 'room_id': room.id} |
||||
|
) |
||||
|
|
||||
|
# 2. Check if the message is in a GROUP/COURSE room |
||||
|
elif room.room_type == RoomMessage.RoomTypeChoices.GROUP and room.course: |
||||
|
# Case A: Teacher Replied in a Course Chat |
||||
|
if sender_user.user_type == User.UserType.PROFESSOR: |
||||
|
course = room.course |
||||
|
course_title_en = get_localized_field('en', course.title) |
||||
|
course_title_fa = get_localized_field('fa', course.title) |
||||
|
|
||||
|
preview_en = get_message_preview(instance, 'en') |
||||
|
preview_fa = get_message_preview(instance, 'fa') |
||||
|
|
||||
|
# Notify all active enrolled students of the course |
||||
|
participants = Participant.objects.filter(course=course, is_active=True).select_related('student') |
||||
|
for p in participants: |
||||
|
if p.student != sender_user: # Exclude sender |
||||
|
create_and_send_notification( |
||||
|
user=p.student, |
||||
|
title_en="Teacher Post in Course Chat", |
||||
|
body_en=f"Teacher {sender_user.fullname or sender_user.username} posted in '{course_title_en}': {preview_en}", |
||||
|
title_fa="پیام استاد در گفتگوی دوره", |
||||
|
body_fa=f"استاد {sender_user.fullname or sender_user.username} در گفتگوی دوره «{course_title_fa}» پیام جدیدی فرستاد: {preview_fa}", |
||||
|
service='imam-javad', |
||||
|
data={'type': 'teacher_reply_course', 'message_id': instance.id, 'room_id': room.id} |
||||
|
) |
||||
Write
Preview
Loading…
Cancel
Save
Reference in new issue