|
|
|
|
|
|
|
|
|
|
|
from django.db import models |
|
|
from apps.core.models import TimestampMixin, SoftDeleteMixin, VersionedFieldMixin |
|
|
from apps.users.models import User |
|
|
|
|
|
class Conversation(TimestampMixin, SoftDeleteMixin): |
|
|
"""Conversation entre utilisateurs""" |
|
|
last_message_at = models.DateTimeField(null=True, blank=True, db_index=True) |
|
|
|
|
|
class Meta: |
|
|
db_table = 'conversations' |
|
|
verbose_name = 'Conversation' |
|
|
verbose_name_plural = 'Conversations' |
|
|
ordering = ['-last_message_at'] |
|
|
|
|
|
def __str__(self): |
|
|
participants = self.participants.filter(is_active=True) |
|
|
users = [p.user.email for p in participants[:3]] |
|
|
return f"Conversation: {', '.join(users)}" |
|
|
|
|
|
class ConversationParticipant(TimestampMixin, SoftDeleteMixin): |
|
|
"""Participants d'une conversation""" |
|
|
conversation = models.ForeignKey(Conversation, on_delete=models.CASCADE, related_name='participants') |
|
|
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='conversation_participations') |
|
|
joined_at = models.DateTimeField(auto_now_add=True) |
|
|
left_at = models.DateTimeField(null=True, blank=True) |
|
|
last_read_at = models.DateTimeField(null=True, blank=True) |
|
|
|
|
|
class Meta: |
|
|
db_table = 'conversation_participants' |
|
|
unique_together = ['conversation', 'user', 'is_active'] |
|
|
indexes = [ |
|
|
models.Index(fields=['user', 'is_active']), |
|
|
] |
|
|
|
|
|
def get_unread_count(self): |
|
|
"""Nombre de messages non lus""" |
|
|
if not self.last_read_at: |
|
|
return self.conversation.messages.filter(is_active=True).count() |
|
|
return self.conversation.messages.filter( |
|
|
is_active=True, |
|
|
created_at__gt=self.last_read_at |
|
|
).exclude(sender=self.user).count() |
|
|
|
|
|
class Message(TimestampMixin, SoftDeleteMixin): |
|
|
"""Message dans une conversation""" |
|
|
conversation = models.ForeignKey(Conversation, on_delete=models.CASCADE, related_name='messages') |
|
|
sender = models.ForeignKey(User, on_delete=models.CASCADE, related_name='sent_messages') |
|
|
is_encrypted = models.BooleanField(default=False) |
|
|
encrypted_keys = models.JSONField(default=dict, blank=True) |
|
|
is_visible_to_recipient = models.BooleanField(default=False, help_text="Visible seulement pendant un rendez-vous") |
|
|
|
|
|
class Meta: |
|
|
db_table = 'messages' |
|
|
verbose_name = 'Message' |
|
|
verbose_name_plural = 'Messages' |
|
|
indexes = [ |
|
|
models.Index(fields=['conversation', '-created_at']), |
|
|
models.Index(fields=['sender', '-created_at']), |
|
|
] |
|
|
ordering = ['created_at'] |
|
|
|
|
|
def __str__(self): |
|
|
content = self.contents.filter(is_current=True).first() |
|
|
return f"Message from {self.sender.email}: {content.content[:50] if content else ''}" |
|
|
|
|
|
class MessageContent(TimestampMixin, VersionedFieldMixin): |
|
|
"""Contenu du message (éditable)""" |
|
|
message = models.ForeignKey(Message, on_delete=models.CASCADE, related_name='contents') |
|
|
content = models.TextField() |
|
|
|
|
|
class Meta: |
|
|
db_table = 'message_contents' |
|
|
|
|
|
class MessageReadStatus(TimestampMixin): |
|
|
"""Statut de lecture par utilisateur""" |
|
|
message = models.ForeignKey(Message, on_delete=models.CASCADE, related_name='read_statuses') |
|
|
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='message_reads') |
|
|
read_at = models.DateTimeField(auto_now_add=True) |
|
|
|
|
|
class Meta: |
|
|
db_table = 'message_read_statuses' |
|
|
unique_together = ['message', 'user'] |
|
|
|
|
|
class MessageAttachment(TimestampMixin, SoftDeleteMixin): |
|
|
"""Pièces jointes (optionnel)""" |
|
|
message = models.ForeignKey(Message, on_delete=models.CASCADE, related_name='attachments') |
|
|
file_url = models.URLField(max_length=500) |
|
|
file_name = models.CharField(max_length=255) |
|
|
file_type = models.CharField(max_length=50) |
|
|
file_size = models.IntegerField() |
|
|
|
|
|
class Meta: |
|
|
db_table = 'message_attachments' |
|
|
|
|
|
|