You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
166 lines
5.3 KiB
166 lines
5.3 KiB
from django.conf import settings
|
|
from django.db import models
|
|
from django.utils.translation import gettext_lazy as _
|
|
from apps.profiles.models.institution import Institution
|
|
|
|
|
|
class ChatRoom(models.Model):
|
|
class RoomType(models.TextChoices):
|
|
DIRECT = 'direct', _('Direct Message')
|
|
GROUP = 'group', _('Group Conversation')
|
|
|
|
room_type = models.CharField(
|
|
max_length=20,
|
|
choices=RoomType.choices,
|
|
default=RoomType.DIRECT,
|
|
verbose_name=_('Room Type')
|
|
)
|
|
title = models.CharField(
|
|
max_length=255,
|
|
blank=True,
|
|
null=True,
|
|
verbose_name=_('Conversation Title')
|
|
)
|
|
participant_users = models.ManyToManyField(
|
|
settings.AUTH_USER_MODEL,
|
|
blank=True,
|
|
related_name='chat_rooms',
|
|
verbose_name=_('Participant Users')
|
|
)
|
|
participant_institutions = models.ManyToManyField(
|
|
Institution,
|
|
blank=True,
|
|
related_name='chat_rooms',
|
|
verbose_name=_('Participant Institutions')
|
|
)
|
|
created_by = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='created_chat_rooms',
|
|
verbose_name=_('Created By')
|
|
)
|
|
created_at = models.DateTimeField(
|
|
auto_now_add=True,
|
|
verbose_name=_('Created At')
|
|
)
|
|
updated_at = models.DateTimeField(
|
|
auto_now=True,
|
|
verbose_name=_('Last Activity')
|
|
)
|
|
|
|
class Meta:
|
|
ordering = ('-updated_at',)
|
|
verbose_name = _('Chat Room')
|
|
verbose_name_plural = _('Chat Rooms')
|
|
|
|
def __str__(self):
|
|
return self.title or f"Chat Room #{self.id} ({self.room_type})"
|
|
|
|
@property
|
|
def centrifugo_channel(self) -> str:
|
|
return f"chat:room_{self.id}"
|
|
|
|
def is_participant(self, user) -> bool:
|
|
if not user or not user.is_authenticated:
|
|
return False
|
|
if getattr(user, 'is_super_admin', False) or getattr(user, 'is_regional_admin', False):
|
|
return True
|
|
if self.participant_users.filter(id=user.id).exists():
|
|
return True
|
|
# Check if user belongs to any participant institution
|
|
user_inst_ids = user.institution_memberships.values_list('institution_id', flat=True)
|
|
if self.participant_institutions.filter(id__in=user_inst_ids).exists():
|
|
return True
|
|
return False
|
|
|
|
def get_last_message(self):
|
|
return self.messages.all().select_related('sender', 'sender_institution').order_by('-created_at', '-id').first()
|
|
|
|
def get_unread_count(self, user) -> int:
|
|
if not user or not user.is_authenticated:
|
|
return 0
|
|
return self.messages.exclude(sender=user).exclude(read_by=user).count()
|
|
|
|
def get_display_title(self, current_user=None) -> str:
|
|
if self.title:
|
|
return self.title
|
|
if self.room_type == self.RoomType.DIRECT and current_user:
|
|
other_user = self.participant_users.exclude(id=current_user.id).first()
|
|
if other_user:
|
|
return other_user.fullname or other_user.email
|
|
other_inst = self.participant_institutions.first()
|
|
if other_inst:
|
|
return other_inst.name
|
|
return f"Conversation #{self.id}"
|
|
|
|
|
|
class ChatMessage(models.Model):
|
|
room = models.ForeignKey(
|
|
ChatRoom,
|
|
on_delete=models.CASCADE,
|
|
related_name='messages',
|
|
verbose_name=_('Chat Room')
|
|
)
|
|
sender = models.ForeignKey(
|
|
settings.AUTH_USER_MODEL,
|
|
on_delete=models.CASCADE,
|
|
related_name='sent_chat_messages',
|
|
verbose_name=_('Sender')
|
|
)
|
|
sender_institution = models.ForeignKey(
|
|
Institution,
|
|
on_delete=models.SET_NULL,
|
|
null=True,
|
|
blank=True,
|
|
related_name='institution_chat_messages',
|
|
verbose_name=_('Sender Institution Affiliation')
|
|
)
|
|
content = models.TextField(
|
|
blank=True,
|
|
null=True,
|
|
verbose_name=_('Message Text')
|
|
)
|
|
attachment = models.FileField(
|
|
upload_to='chat/attachments/%Y/%m/',
|
|
blank=True,
|
|
null=True,
|
|
verbose_name=_('Attachment File')
|
|
)
|
|
attachment_name = models.CharField(
|
|
max_length=255,
|
|
blank=True,
|
|
null=True,
|
|
verbose_name=_('Attachment Filename')
|
|
)
|
|
read_by = models.ManyToManyField(
|
|
settings.AUTH_USER_MODEL,
|
|
blank=True,
|
|
related_name='read_chat_messages',
|
|
verbose_name=_('Read Receipts')
|
|
)
|
|
created_at = models.DateTimeField(
|
|
auto_now_add=True,
|
|
verbose_name=_('Timestamp')
|
|
)
|
|
|
|
class Meta:
|
|
ordering = ('created_at',)
|
|
verbose_name = _('Chat Message')
|
|
verbose_name_plural = _('Chat Messages')
|
|
|
|
def __str__(self):
|
|
return f"[{self.room.id}] {self.sender.fullname or self.sender.email}: {self.content[:30] if self.content else 'Attachment'}"
|
|
|
|
def save(self, *args, **kwargs):
|
|
is_new = self.pk is None
|
|
if self.attachment and not self.attachment_name:
|
|
self.attachment_name = self.attachment.name.split('/')[-1]
|
|
super().save(*args, **kwargs)
|
|
|
|
if is_new:
|
|
# Mark as read by sender immediately
|
|
self.read_by.add(self.sender)
|
|
# Update room activity timestamp
|
|
ChatRoom.objects.filter(id=self.room_id).update(updated_at=self.created_at)
|