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.
158 lines
5.1 KiB
158 lines
5.1 KiB
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
|
|
|