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

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)