from django.db import models from django.contrib.auth import get_user_model from django.utils import timezone from django.core.validators import FileExtensionValidator import uuid User = get_user_model() class EmailFolder(models.Model): """Email folders for organizing emails.""" FOLDER_TYPES = [ ('inbox', 'Inbox'), ('sent', 'Sent'), ('drafts', 'Drafts'), ('trash', 'Trash'), ('spam', 'Spam'), ('archive', 'Archive'), ('custom', 'Custom'), ] user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='folders') name = models.CharField(max_length=100) folder_type = models.CharField(max_length=20, choices=FOLDER_TYPES, default='custom') parent = models.ForeignKey('self', on_delete=models.CASCADE, null=True, blank=True, related_name='children') color = models.CharField(max_length=7, default='#007bff') # Hex color is_system = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'email_folders' unique_together = ['user', 'name'] ordering = ['name'] def __str__(self): return f"{self.user.email} - {self.name}" class Email(models.Model): """Email model for storing email messages.""" PRIORITY_CHOICES = [ ('low', 'Low'), ('normal', 'Normal'), ('high', 'High'), ] STATUS_CHOICES = [ ('draft', 'Draft'), ('sent', 'Sent'), ('received', 'Received'), ('failed', 'Failed'), ] # Basic email information uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False) user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='emails') folder = models.ForeignKey(EmailFolder, on_delete=models.CASCADE, related_name='emails') # Email headers subject = models.CharField(max_length=500) from_email = models.EmailField() to_emails = models.JSONField(default=list) # List of email addresses cc_emails = models.JSONField(default=list) # List of email addresses bcc_emails = models.JSONField(default=list) # List of email addresses reply_to = models.EmailField(null=True, blank=True) # Email content body_text = models.TextField(blank=True) body_html = models.TextField(blank=True) # Email metadata message_id = models.CharField(max_length=500, unique=True, null=True, blank=True) in_reply_to = models.CharField(max_length=500, null=True, blank=True) references = models.TextField(blank=True) priority = models.CharField(max_length=10, choices=PRIORITY_CHOICES, default='normal') status = models.CharField(max_length=20, choices=STATUS_CHOICES, default='draft') # Flags and status is_read = models.BooleanField(default=False) is_starred = models.BooleanField(default=False) is_important = models.BooleanField(default=False) is_encrypted = models.BooleanField(default=False) # Timestamps sent_at = models.DateTimeField(null=True, blank=True) received_at = models.DateTimeField(null=True, blank=True) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) # Size information size = models.BigIntegerField(default=0) # Size in bytes class Meta: db_table = 'emails' ordering = ['-created_at'] indexes = [ models.Index(fields=['user', 'folder', '-created_at']), models.Index(fields=['user', 'is_read']), models.Index(fields=['user', 'is_starred']), models.Index(fields=['message_id']), ] def __str__(self): return f"{self.subject} - {self.from_email}" def mark_as_read(self): """Mark email as read.""" self.is_read = True self.save(update_fields=['is_read', 'updated_at']) def mark_as_unread(self): """Mark email as unread.""" self.is_read = False self.save(update_fields=['is_read', 'updated_at']) def move_to_folder(self, folder): """Move email to another folder.""" self.folder = folder self.save(update_fields=['folder', 'updated_at']) def get_recipients(self): """Get all recipients (to, cc, bcc).""" return self.to_emails + self.cc_emails + self.bcc_emails class EmailAttachment(models.Model): """Email attachments.""" email = models.ForeignKey(Email, on_delete=models.CASCADE, related_name='attachments') filename = models.CharField(max_length=255) content_type = models.CharField(max_length=100) size = models.BigIntegerField() file = models.FileField( upload_to='email_attachments/', validators=[FileExtensionValidator(allowed_extensions=['pdf', 'doc', 'docx', 'txt', 'jpg', 'jpeg', 'png', 'gif', 'zip', 'rar'])] ) is_inline = models.BooleanField(default=False) content_id = models.CharField(max_length=100, blank=True) # For inline attachments created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'email_attachments' ordering = ['filename'] def __str__(self): return f"{self.email.subject} - {self.filename}" class EmailThread(models.Model): """Email threads for conversation view.""" emails = models.ManyToManyField(Email, related_name='threads') subject = models.CharField(max_length=500) participants = models.JSONField(default=list) # List of email addresses last_activity = models.DateTimeField(auto_now=True) created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'email_threads' ordering = ['-last_activity'] def __str__(self): return f"Thread: {self.subject}" class EmailTemplate(models.Model): """Email templates for quick composition.""" user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='email_templates') name = models.CharField(max_length=100) subject = models.CharField(max_length=500) body_html = models.TextField() body_text = models.TextField(blank=True) is_public = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'email_templates' unique_together = ['user', 'name'] ordering = ['name'] def __str__(self): return f"{self.user.email} - {self.name}" class EmailSignature(models.Model): """Email signatures.""" user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='email_signatures') name = models.CharField(max_length=100) content_html = models.TextField() content_text = models.TextField(blank=True) is_default = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'email_signatures' ordering = ['name'] def __str__(self): return f"{self.user.email} - {self.name}" def save(self, *args, **kwargs): if self.is_default: # Ensure only one default signature per user EmailSignature.objects.filter(user=self.user, is_default=True).update(is_default=False) super().save(*args, **kwargs) class EmailRule(models.Model): """Email rules for automatic processing.""" ACTION_CHOICES = [ ('move', 'Move to folder'), ('mark_read', 'Mark as read'), ('mark_important', 'Mark as important'), ('forward', 'Forward to'), ('delete', 'Delete'), ('reply', 'Auto-reply'), ] user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='email_rules') name = models.CharField(max_length=100) is_active = models.BooleanField(default=True) # Conditions condition_field = models.CharField(max_length=50) # from, to, subject, body, etc. condition_operator = models.CharField(max_length=20) # contains, equals, starts_with, etc. condition_value = models.CharField(max_length=500) # Actions action = models.CharField(max_length=20, choices=ACTION_CHOICES) action_value = models.CharField(max_length=500, blank=True) # Folder name, email address, etc. created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) class Meta: db_table = 'email_rules' ordering = ['name'] def __str__(self): return f"{self.user.email} - {self.name}" class EmailSearch(models.Model): """Saved email searches.""" user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='email_searches') name = models.CharField(max_length=100) query = models.JSONField() # Search parameters created_at = models.DateTimeField(auto_now_add=True) class Meta: db_table = 'email_searches' unique_together = ['user', 'name'] ordering = ['name'] def __str__(self): return f"{self.user.email} - {self.name}"