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.
 
 
 
 
 
 

370 lines
14 KiB

from smtplib import SMTP
from datetime import datetime
from datetime import timedelta
from django.db import models
from django.utils.encoding import smart_str
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext_lazy as _
from django.contrib.contenttypes import generic
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import Group
from django.utils.encoding import force_unicode
from newsletter.managers import ContactManager
from newsletter.settings import BASE_PATH
from newsletter.settings import MAILER_HARD_LIMIT
from newsletter.settings import DEFAULT_HEADER_REPLY
from newsletter.settings import DEFAULT_HEADER_SENDER
from newsletter.utils.vcard import vcard_contact_export
class SMTPServer(models.Model):
"""Configuration of a SMTP server"""
name = models.CharField(_('name'), max_length=255)
host = models.CharField(_('server host'), max_length=255)
user = models.CharField(_('server user'), max_length=128, blank=True,
help_text=_('Leave it empty if the host is public.'))
password = models.CharField(_('server password'), max_length=128, blank=True,
help_text=_('Leave it empty if the host is public.'))
port = models.IntegerField(_('server port'), default=25)
tls = models.BooleanField(_('server use TLS'))
headers = models.TextField(_('custom headers'), blank=True,
help_text=_('key1: value1 key2: value2, splitted by return line.\n'\
'Useful for passing some tracking headers if your provider allows it.'))
mails_hour = models.IntegerField(_('mails per hour'), default=0)
def connect(self):
"""Connect the SMTP Server"""
smtp = SMTP(smart_str(self.host), int(self.port))
smtp.ehlo_or_helo_if_needed()
if self.tls:
smtp.starttls()
smtp.ehlo_or_helo_if_needed()
if self.user or self.password:
smtp.login(smart_str(self.user), smart_str(self.password))
return smtp
def delay(self):
"""compute the delay (in seconds) between mails to ensure mails
per hour limit is not reached
:rtype: float
"""
if not self.mails_hour:
return 0.0
else:
return 3600.0 / self.mails_hour
def credits(self):
"""Return how many mails the server can send"""
if not self.mails_hour:
return MAILER_HARD_LIMIT
last_hour = datetime.now() - timedelta(hours=1)
sent_last_hour = ContactMailingStatus.objects.filter(
models.Q(status=ContactMailingStatus.SENT) |
models.Q(status=ContactMailingStatus.SENT_TEST),
newsletter__server=self,
creation_date__gte=last_hour).count()
return self.mails_hour - sent_last_hour
@property
def custom_headers(self):
if self.headers:
headers = {}
for header in self.headers.splitlines():
if header:
key, value = header.split(':')
headers[key.strip()] = value.strip()
return headers
return {}
def __unicode__(self):
return '%s (%s)' % (self.name, self.host)
class Meta:
verbose_name = _('SMTP server')
verbose_name_plural = _('SMTP servers')
class Contact(models.Model):
"""Contact for emailing"""
user = models.OneToOneField('accounts.User')
email = models.EmailField(_('email'), unique=True)
first_name = models.CharField(_('first name'), max_length=50, blank=True)
last_name = models.CharField(_('last name'), max_length=50, blank=True)
subscriber = models.BooleanField(_('subscriber'), default=True)
valid = models.BooleanField(_('valid email'), default=True)
tester = models.BooleanField(_('contact tester'), default=False)
content_type = models.ForeignKey(ContentType, blank=True, null=True)
object_id = models.PositiveIntegerField(blank=True, null=True)
content_object = generic.GenericForeignKey('content_type', 'object_id')
creation_date = models.DateTimeField(_('creation date'), auto_now_add=True)
modification_date = models.DateTimeField(_('modification date'), auto_now=True)
objects = ContactManager()
def subscriptions(self):
"""Return the user subscriptions"""
return MailingList.objects.filter(subscribers=self)
def unsubscriptions(self):
"""Return the user unsubscriptions"""
return MailingList.objects.filter(unsubscribers=self)
def vcard_format(self):
return vcard_contact_export(self)
def mail_format(self):
if self.first_name and self.last_name:
return '%s %s <%s>' % (self.last_name, self.first_name, self.email)
return self.email
mail_format.short_description = _('mail format')
def get_absolute_url(self):
if self.content_type and self.object_id:
return self.content_object.get_absolute_url()
return reverse('admin:newsletter_contact_change', args=(self.pk,))
def __unicode__(self):
if self.first_name and self.last_name:
contact_name = '%s %s' % (self.last_name, self.first_name)
else:
contact_name = self.email
if self.tags:
return '%s | %s' % (contact_name, self.tags)
return contact_name
class Meta:
ordering = ('creation_date',)
verbose_name = _('contact')
verbose_name_plural = _('contacts')
class ContactSettings(models.Model):
WEEK = 1 # every week
WEEK_2 = 2 # every 2 weeks
MONTH = 3 # every 4 month
PERIODIC_CHOICES = ((WEEK, _('draft')),
(WEEK_2, _('waiting sending')),
(MONTH, _('sending')),
)
periodic = models.PositiveIntegerField(choices=PERIODIC_CHOICES, default=WEEK_2)
exponent_practicum = models.BooleanField(default=False)
organiser_practicum = models.BooleanField(default=False)
theme = models.ManyToManyField('theme.Theme', blank=True, null=True)
area = models.ManyToManyField('country.Area', blank=True, null=True)
country = models.ManyToManyField('country.Country', blank=True, null=True)
city = models.ManyToManyField('city.City', blank=True, null=True)
class MailingList(models.Model):
"""Mailing list"""
name = models.CharField(_('name'), max_length=255)
description = models.TextField(_('description'), blank=True)
subscribers = models.ManyToManyField(Contact, verbose_name=_('subscribers'),
related_name='mailinglist_subscriber')
unsubscribers = models.ManyToManyField(Contact, verbose_name=_('unsubscribers'),
related_name='mailinglist_unsubscriber',
null=True, blank=True)
announce = models.BooleanField(default=False)
announce_public_day = models.IntegerField(default=2)
creation_date = models.DateTimeField(_('creation date'), auto_now_add=True)
modification_date = models.DateTimeField(_('modification date'), auto_now=True)
def subscribers_count(self):
return self.subscribers.all().count()
subscribers_count.short_description = _('subscribers')
def unsubscribers_count(self):
return self.unsubscribers.all().count()
unsubscribers_count.short_description = _('unsubscribers')
def expedition_set(self):
unsubscribers_id = self.unsubscribers.values_list('id', flat=True)
return self.subscribers.valid_subscribers().exclude(
id__in=unsubscribers_id)
def __unicode__(self):
return self.name
class Meta:
ordering = ('-creation_date',)
verbose_name = _('mailing list')
verbose_name_plural = _('mailing lists')
class Newsletter(models.Model):
"""Newsletter to be sended to contacts"""
DRAFT = 0
WAITING = 1
SENDING = 2
SENT = 4
CANCELED = 5
STATUS_CHOICES = ((DRAFT, _('draft')),
(WAITING, _('waiting sending')),
(SENDING, _('sending')),
(SENT, _('sent')),
(CANCELED, _('canceled')),
)
title = models.CharField(_('title'), max_length=255,
help_text=_('You can use the "{{ UNIQUE_KEY }}" variable ' \
'for unique identifier within the newsletter\'s title.'))
content = models.TextField(_('content'), help_text=_('Or paste an URL.'),
default=_('<body>\n<!-- Edit your newsletter here -->\n</body>'))
mailing_list = models.ForeignKey(MailingList, verbose_name=_('mailing list'))
test_contacts = models.ManyToManyField(Contact, verbose_name=_('test contacts'),
blank=True, null=True)
server = models.ForeignKey(SMTPServer, verbose_name=_('smtp server'),
default=1)
header_sender = models.CharField(_('sender'), max_length=255,
default=DEFAULT_HEADER_SENDER)
header_reply = models.CharField(_('reply to'), max_length=255,
default=DEFAULT_HEADER_REPLY)
status = models.IntegerField(_('status'), choices=STATUS_CHOICES, default=DRAFT)
sending_date = models.DateTimeField(_('sending date'), default=datetime.now)
slug = models.SlugField(help_text=_('Used for displaying the newsletter on the site.'),
unique=True)
creation_date = models.DateTimeField(_('creation date'), auto_now_add=True)
modification_date = models.DateTimeField(_('modification date'), auto_now=True)
def mails_sent(self):
return self.contactmailingstatus_set.filter(status=ContactMailingStatus.SENT).count()
@models.permalink
def get_absolute_url(self):
return ('newsletter_newsletter_preview', (self.slug,))
@models.permalink
def get_historic_url(self):
return ('newsletter_newsletter_historic', (self.slug,))
@models.permalink
def get_statistics_url(self):
return ('newsletter_newsletter_statistics', (self.slug,))
def __unicode__(self):
return self.title
class Meta:
ordering = ('-creation_date',)
verbose_name = _('newsletter')
verbose_name_plural = _('newsletters')
permissions = (('can_change_status', 'Can change status'),)
class Link(models.Model):
"""Link sended in a newsletter"""
title = models.CharField(_('title'), max_length=255)
url = models.CharField(_('url'), max_length=255)
creation_date = models.DateTimeField(_('creation date'), auto_now_add=True)
def get_absolute_url(self):
return self.url
def __unicode__(self):
return self.title
class Meta:
ordering = ('-creation_date',)
verbose_name = _('link')
verbose_name_plural = _('links')
class Attachment(models.Model):
"""Attachment file in a newsletter"""
def get_newsletter_storage_path(self, filename):
filename = force_unicode(filename)
return '/'.join([BASE_PATH, self.newsletter.slug, filename])
newsletter = models.ForeignKey(Newsletter, verbose_name=_('newsletter'))
title = models.CharField(_('title'), max_length=255)
file_attachment = models.FileField(_('file to attach'), max_length=255,
upload_to=get_newsletter_storage_path)
class Meta:
verbose_name = _('attachment')
verbose_name_plural = _('attachments')
def __unicode__(self):
return self.title
def get_absolute_url(self):
return self.file_attachment.url
class ContactMailingStatus(models.Model):
"""Status of the reception"""
SENT_TEST = -1
SENT = 0
ERROR = 1
INVALID = 2
OPENED = 4
OPENED_ON_SITE = 5
LINK_OPENED = 6
UNSUBSCRIPTION = 7
STATUS_CHOICES = ((SENT_TEST, _('sent in test')),
(SENT, _('sent')),
(ERROR, _('error')),
(INVALID, _('invalid email')),
(OPENED, _('opened')),
(OPENED_ON_SITE, _('opened on site')),
(LINK_OPENED, _('link opened')),
(UNSUBSCRIPTION, _('unsubscription')),
)
newsletter = models.ForeignKey(Newsletter, verbose_name=_('newsletter'))
contact = models.ForeignKey(Contact, verbose_name=_('contact'))
status = models.IntegerField(_('status'), choices=STATUS_CHOICES)
link = models.ForeignKey(Link, verbose_name=_('link'),
blank=True, null=True)
creation_date = models.DateTimeField(_('creation date'), auto_now_add=True)
def __unicode__(self):
return '%s : %s : %s' % (self.newsletter.__unicode__(),
self.contact.__unicode__(),
self.get_status_display())
class Meta:
ordering = ('-creation_date',)
verbose_name = _('contact mailing status')
verbose_name_plural = _('contact mailing statuses')
class WorkGroup(models.Model):
"""Work Group for privatization of the ressources"""
name = models.CharField(_('name'), max_length=255)
group = models.ForeignKey(Group, verbose_name=_('permissions group'))
contacts = models.ManyToManyField(Contact, verbose_name=_('contacts'),
blank=True, null=True)
mailinglists = models.ManyToManyField(MailingList, verbose_name=_('mailing lists'),
blank=True, null=True)
newsletters = models.ManyToManyField(Newsletter, verbose_name=_('newsletters'),
blank=True, null=True)
def __unicode__(self):
return self.name
class Meta:
verbose_name = _('workgroup')
verbose_name_plural = _('workgroups')