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.
 
 
 
 
 

296 lines
9.1 KiB

from django.db import models
from django.utils.translation import gettext_lazy as _
from accounts_app.models import UserProfile
from djing.tasks import send_email_notify
class MessageError(Exception):
pass
class MessageStatus(models.Model):
msg = models.ForeignKey(
'Message', on_delete=models.CASCADE,
related_name='msg_statuses'
)
user = models.ForeignKey(
UserProfile, on_delete=models.CASCADE,
related_name='usr_msg_status'
)
MESSAGE_STATES = (
('new', _('New')),
('old', _('Seen')),
('del', _('Deleted'))
)
state = models.CharField(
max_length=3, choices=MESSAGE_STATES,
default='new'
)
def __str__(self):
return "%s for %s (%s)" % (
self.get_state_display(),
self.user, self.msg
)
class Meta:
db_table = 'message_status'
unique_together = (
('msg', 'user', 'state')
)
verbose_name = _('Message status')
verbose_name_plural = _('Messages statuses')
class Message(models.Model):
text = models.TextField(_("Body"))
sent_at = models.DateTimeField(_("sent at"), auto_now_add=True)
author = models.ForeignKey(
UserProfile, on_delete=models.CASCADE,
related_name='messages'
)
conversation = models.ForeignKey(
'Conversation', on_delete=models.CASCADE,
verbose_name=_('Conversation')
)
attachment = models.FileField(
upload_to='messages_attachments/%Y_%m_%d',
blank=True, null=True
)
account_status = models.ManyToManyField(
UserProfile, through=MessageStatus,
through_fields=('msg', 'user')
)
def __str__(self):
return self.text[:9]
def _set_status(self, account, code):
try:
ms = MessageStatus.objects.get(msg=self, user=account)
if ms.status == code:
return False
ms.status = code
ms.save(update_fields=('status',))
return True
except MessageStatus.DoesNotExist:
return False
def set_status_old(self, account):
return self._set_status(account, 'old')
def set_status_del(self, account):
return self._set_status(account, 'del')
def set_status_new(self, account):
return self._set_status(account, 'new')
class Meta:
db_table = 'messages'
ordering = ('-sent_at',)
verbose_name = _("Message")
verbose_name_plural = _("Messages")
class ConversationMembership(models.Model):
account = models.ForeignKey(
UserProfile, on_delete=models.CASCADE,
related_name='memberships'
)
conversation = models.ForeignKey('Conversation', on_delete=models.CASCADE)
PARTICIPANT_STATUS = (
('adm', _('Admin')),
('gst', _('Guest')),
('ban', _('Banned user')),
('inv', _('Inviter'))
)
status = models.CharField(
max_length=3, choices=PARTICIPANT_STATUS, default='gst'
)
who_invite_that_user = models.ForeignKey(
UserProfile, on_delete=models.CASCADE,
null=True, blank=True,
related_name='self_conversations'
)
def __str__(self):
return "%s < %s" % (self.conversation, self.account)
class Meta:
db_table = 'conversation_memberships'
verbose_name = _("Conversation membership")
verbose_name_plural = _("Conversation memberships")
def id_to_userprofile(acc):
if isinstance(acc, UserProfile):
return acc
try:
return UserProfile.objects.get(pk=acc)
except UserProfile.DoesNotExist:
raise MessageError(_('Participant profile does not found'))
class ConversationManager(models.Manager):
def create_conversation(self, author, other_participants, title=None):
other_participants = tuple(
id_to_userprofile(acc) for acc in other_participants
)
if not title:
usernames = tuple(acc.username for acc in other_participants)
if not usernames:
title = _('No name')
else:
title = ', '.join(usernames)
conversation = self.create(title=title, author=author)
for acc in other_participants:
ConversationMembership.objects.create(
account=acc, conversation=conversation,
status='adm', who_invite_that_user=author
)
ConversationMembership.objects.create(
account=author, conversation=conversation, status='inv'
)
return conversation
@staticmethod
def get_new_messages_count(account):
if isinstance(account, UserProfile):
return MessageStatus.objects.filter(
user=account, state='new'
).count()
else:
return 0
def fetch(self, account):
conversations = self.filter(
models.Q(author=account) | models.Q(participants__in=(account,))
).annotate(
msg_count=models.Count('message', distinct=True)
)
return conversations
class Conversation(models.Model):
title = models.CharField(max_length=32)
participants = models.ManyToManyField(
UserProfile, related_name='conversations',
through='ConversationMembership',
through_fields=('conversation', 'account')
)
author = models.ForeignKey(UserProfile, on_delete=models.CASCADE)
date_create = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
objects = ConversationManager()
def get_messages(self):
return Message.objects.filter(conversation=self).order_by('sent_at')
def get_messages_new_count(self, account):
msgs = Message.objects.filter(conversation=self)
return MessageStatus.objects.filter(
user=account, msg__in=msgs, state='new'
).count()
def last_message(self):
messages = Message.objects.filter(conversation=self)
if messages.count() > 0:
return messages[0]
def new_message(self, text, attachment, author, with_status=True):
msg = Message.objects.create(
text=text, conversation=self,
attachment=attachment, author=author
)
if with_status:
for participant in self.participants.filter(is_active=True):
if participant == author:
continue
MessageStatus.objects.create(msg=msg, user=participant)
if participant.flags.notify_msg:
send_email_notify.delay(
msg_text=text,
account_id=participant.pk
)
return msg
@staticmethod
def remove_message(msg):
if isinstance(msg, Message):
m = msg
else:
m = Message.objects.get(pk=int(msg))
if m is None:
return False
else:
m.delete()
return True
def _make_participant_status(self, user, status, cm=None):
if cm is None:
cm = ConversationMembership.objects.get(
account=user, conversation=self
)
else:
if not isinstance(cm, ConversationMembership):
raise TypeError('cm must be instance of '
'msg_app.ConversationMembership')
cm.status = status
cm.save(update_fields=('status',))
return cm
def make_participant_status_admin(self, user):
return self._make_participant_status(user, 'adm')
def make_participant_status_guest(self, user):
return self._make_participant_status(user, 'gst')
def make_participant_status_ban(self, user):
return self._make_participant_status(user, 'ban')
def make_participant_status_inviter(self, user):
return self._make_participant_status(user, 'inv')
def remove_participant(self, user):
try:
cm = ConversationMembership.objects.get(
account=user, conversation=self
)
cm.delete()
except ConversationMembership.DoesNotExist:
pass
def add_participant(self, author, user):
return ConversationMembership.objects.create(
account=user, conversation=self,
status='gst', who_invite_that_user=author
)
def find_messages_by_text(self, text):
return Message.objects.filter(
text__icontains=text, conversation=self
)
def _make_messages_status(self, account, status):
qs = MessageStatus.objects.filter(
msg__conversation=self, user=account
).exclude(state='del')
if status != 'del':
qs = qs.exclude(state=status)
return qs.update(state=status)
def make_messages_status_new(self, account):
return self._make_messages_status(account, 'new')
def make_messages_status_old(self, account):
return self._make_messages_status(account, 'old')
class Meta:
db_table = 'conversations'
verbose_name = _("Conversation")
verbose_name_plural = _("Conversations")
ordering = ('title',)