import uuid from django.db import models from django.contrib.auth import get_user_model from django.utils import timezone from django.conf import settings from sync.models import BaseModel User = get_user_model() class MailingList(BaseModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) name = models.CharField(max_length=100) description = models.TextField(blank=True, null=True) def delete_dependencies(self): pass def __str__(self): return self.name def subscriber_count(self): return self.subscribers.filter(is_active=True).count() class Subscriber(BaseModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) email = models.EmailField() first_name = models.CharField(max_length=100, blank=True, null=True) last_name = models.CharField(max_length=100, blank=True, null=True) mailing_lists = models.ManyToManyField(MailingList, related_name='subscribers', blank=True) is_active = models.BooleanField(default=True) unsubscribe_token = models.UUIDField(default=uuid.uuid4, unique=True) # Optional reference to actual user models user = models.ForeignKey(User, on_delete=models.CASCADE, blank=True, null=True) # prospect = models.ForeignKey('biz.Prospect', on_delete=models.CASCADE, blank=True, null=True) class Meta: unique_together = ['email'] def delete_dependencies(self): pass def __str__(self): if self.first_name and self.last_name: return f"{self.first_name} {self.last_name} ({self.email})" return self.email def full_name(self): if self.first_name and self.last_name: return f"{self.first_name} {self.last_name}" elif self.first_name: return self.first_name elif self.last_name: return self.last_name return "" class EmailTemplate(BaseModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) name = models.CharField(max_length=100) subject = models.CharField(max_length=200) html_content = models.TextField(help_text="HTML content with image support") text_content = models.TextField(blank=True, null=True, help_text="Plain text fallback") def delete_dependencies(self): pass def __str__(self): return self.name class Campaign(BaseModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) name = models.CharField(max_length=100) subject = models.CharField(max_length=200) template = models.ForeignKey(EmailTemplate, on_delete=models.PROTECT, blank=True, null=True) html_content = models.TextField(blank=True, null=True) text_content = models.TextField(blank=True, null=True) mailing_lists = models.ManyToManyField(MailingList, related_name='campaigns') sent_at = models.DateTimeField(blank=True, null=True) is_sent = models.BooleanField(default=False) # Email tracking total_sent = models.IntegerField(default=0) total_delivered = models.IntegerField(default=0) total_opened = models.IntegerField(default=0) total_clicked = models.IntegerField(default=0) total_bounced = models.IntegerField(default=0) def delete_dependencies(self): pass def __str__(self): return self.name def get_content(self): if self.template: return { 'html': self.template.html_content, 'text': self.template.text_content } return { 'html': self.html_content, 'text': self.text_content } def get_subject(self): return self.subject or (self.template.subject if self.template else "") class EmailLog(BaseModel): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=True) campaign = models.ForeignKey(Campaign, on_delete=models.CASCADE, related_name='email_logs') subscriber = models.ForeignKey(Subscriber, on_delete=models.CASCADE, related_name='email_logs') tracking_id = models.UUIDField(default=uuid.uuid4, unique=True) # Email status sent_at = models.DateTimeField(blank=True, null=True) delivered_at = models.DateTimeField(blank=True, null=True) opened_at = models.DateTimeField(blank=True, null=True) clicked_at = models.DateTimeField(blank=True, null=True) bounced_at = models.DateTimeField(blank=True, null=True) # Error tracking error_message = models.TextField(blank=True, null=True) class Meta: unique_together = ['campaign', 'subscriber'] def delete_dependencies(self): pass def __str__(self): return f"{self.campaign.name} - {self.subscriber.email}" @property def is_sent(self): return self.sent_at is not None @property def is_delivered(self): return self.delivered_at is not None @property def is_opened(self): return self.opened_at is not None @property def is_clicked(self): return self.clicked_at is not None @property def is_bounced(self): return self.bounced_at is not None