from __future__ import annotations from django.core.exceptions import ValidationError from django.db.models import ( CharField, DateTimeField, EmailField, ForeignKey, Index, JSONField, TextChoices, TextField, SET_NULL, CASCADE, ) from django.utils import timezone from django.utils.translation import gettext_lazy as _ from engine.core.abstract import NiceModel from engine.authv.models import User class ThreadStatus(TextChoices): OPEN = "open", _("Open") CLOSED = "closed", _("Closed") class SenderType(TextChoices): USER = "user", _("User") STAFF = "staff", _("Staff") SYSTEM = "system", _("System") class ChatThread(NiceModel): user = ForeignKey(User, null=True, blank=True, on_delete=SET_NULL, related_name="chat_threads") email = EmailField(blank=True, default="", help_text=_("For anonymous threads")) assigned_to = ForeignKey(User, null=True, blank=True, on_delete=SET_NULL, related_name="assigned_chat_threads") status = CharField(max_length=16, choices=ThreadStatus.choices, default=ThreadStatus.OPEN) last_message_at = DateTimeField(default=timezone.now) attributes = JSONField(default=dict, blank=True) class Meta: indexes = [ Index(fields=["status", "modified"], name="chatthread_status_mod_idx"), Index(fields=["assigned_to", "status"], name="chatthread_assigned_status_idx"), Index(fields=["user"], name="chatthread_user_idx"), Index(fields=["email"], name="chatthread_email_idx"), ] ordering = ("-modified",) verbose_name = _("Chat thread") verbose_name_plural = _("Chat threads") def clean(self) -> None: super().clean() if not self.user and not self.email: raise ValidationError({"email": _("Provide user or email for anonymous thread.")}) if self.assigned_to and not self.assigned_to.is_staff: raise ValidationError({"assigned_to": _("Assignee must be a staff user.")}) class ChatMessage(NiceModel): thread = ForeignKey(ChatThread, on_delete=CASCADE, related_name="messages") sender_type = CharField(max_length=16, choices=SenderType.choices) sender_user = ForeignKey(User, null=True, blank=True, on_delete=SET_NULL, related_name="chat_messages") text = TextField() sent_at = DateTimeField(default=timezone.now) attributes = JSONField(default=dict, blank=True) class Meta: indexes = [ Index(fields=["thread", "sent_at"], name="chatmessage_thread_sent_idx"), ] ordering = ("sent_at", "uuid") verbose_name = _("Chat message") verbose_name_plural = _("Chat messages")