Skip to content

Instantly share code, notes, and snippets.

@marcelcaraciolo
Created February 18, 2014 02:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save marcelcaraciolo/9063709 to your computer and use it in GitHub Desktop.
Save marcelcaraciolo/9063709 to your computer and use it in GitHub Desktop.
#-*- coding: utf-8 -*-
import celery
import hashlib
import datetime
from django.db import models
from django.conf import settings
from django.utils.html import escape
from django.utils.translation import ugettext_lazy as _
from maintenance.models import Equipament
from django.core.validators import validate_email
from core.utils import get_send_mail
from django.db.models.signals import pre_save, post_save
from tasks import sendScheduledAlertTask
from core.mail import render_mail_template
from django.template.defaultfilters import striptags
send_email = get_send_mail()
import logging
class Email(models.Model):
address = models.EmailField(verbose_name=_(u'E-mail'), unique=True)
name = models.CharField(verbose_name=_(u'Nome'), max_length=100, blank=True)
allowed_to_receive = models.BooleanField(verbose_name=_(u'Recebe alertas ?'), blank=False, default=True)
created_at = models.DateTimeField(verbose_name=_(u'Criado em'), auto_now_add=True)
def __unicode__(self):
return self.address
class Meta:
verbose_name = u'E-mail'
verbose_name_plural = u'E-mails'
ordering = ['address']
class EmailGroup(models.Model):
STATUS_CHOICES = ((0, 'Salvo'),
(1, 'Importando'),
(2, u'Importação concluída'))
name = models.CharField(_(u'Nome'), max_length=100, unique=True)
emails = models.ManyToManyField(Email,
verbose_name='e-mails',
related_name='emailgroups',
null=True, blank=True)
status = models.IntegerField(choices=STATUS_CHOICES, default=0)
class Meta:
verbose_name = 'grupo de e-mails'
verbose_name_plural = 'grupos de emails'
def __unicode__(self):
return self.name
def emails_count(self):
return self.emails.count()
def status_text(self):
return dict(EmailGroup.STATUS_CHOICES)[self.status]
status_text.short_description = 'status'
class Alert(models.Model):
NOT_SENT = 0
SENDING = 1
SENT = 2
SENDING_INCOMPLETE = 3
SCHEDULED_TO_SEND = 4
STATUS_CHOICES = (
(NOT_SENT, u'Não enviada'),
(SENDING, u'Enviando'),
(SENT, u'Enviada'),
(SENDING_INCOMPLETE, u'Envio incompleto'),
(SCHEDULED_TO_SEND, u'Agendada para envio'),
)
from_email = models.CharField(_(u'Email de origem'), max_length=255, blank=True,
null=True,
help_text=u'Se deixado em branco o alerta será enviado por %s' %\
escape(settings.DEFAULT_FROM_EMAIL))
name = models.CharField(_(u'Nome'), max_length=100)
subject = models.CharField(_(u'Assunto'), blank=True, max_length=120, null=True)
html_content = models.TextField(_(u'Conteúdo html'), blank=True, null=True)
status = models.IntegerField(choices=STATUS_CHOICES, default=0)
created_at = models.DateTimeField(_(u'Data de criação'), auto_now_add=True)
plaintext_content = models.TextField(u'conteúdo em texto plano',
blank=True, null=True)
pointer = models.IntegerField('ponteiro', default=0)
send_at = models.DateTimeField(help_text="Schedule the sending of this alert in the future")
emailgroup = models.ForeignKey(EmailGroup,
verbose_name='grupo de e-mails',
related_name='alerts',
null=True, blank=True)
task_id = models.CharField(max_length=256, blank=True, null=True)
# Below is a field to help celery to identify whether the sending was rescheduled, preventing
# problems like the situation: Scheduled at 3:00. Cancelled. Scheduled at 5:00.
# So, at 3:00, celery will run. However, verifying the ID in the database isn't the same as the
# passed in the apply_async() args, it will not send the campaign, waiting until 5:00 when it'll send.
scheduling_id = models.CharField(u'Identificador do agendamento de envio',
max_length=10, blank=True, null=True)
class Meta:
verbose_name = 'Alerta'
verbose_name_plural = 'Alertas'
ordering = ['-created_at']
def send(self):
logging.info('preparando e-mails')
self.status = Alert.SENDING
self.save()
logging.info('enviando e-mails')
emails = self.emailgroup.emails.filter(allowed_to_receive=True)
emails_count = emails.count()
i = 0
try:
if self.pointer < emails_count:
emails = emails[self.pointer:]
for i, email in zip(xrange(emails.count()), emails):
self._send_message(email, i)
self.increment_pointer()
except IOError:
pass
if self.pointer >= emails_count:
self.status = Alert.SENT
else:
self.status = Alert.SENDING_INCOMPLETE
self.save()
def get_from_email(self):
return unicode(self.from_email or settings.DEFAULT_FROM_EMAIL)
def increment_pointer(self):
self.pointer += 1
self.save()
def _send_message(self, email, index):
logging.info('#%d preparando envio para %s' % (index, email))
try:
validate_email(unicode(email.address))
send_email(self.subject,
self.plaintext_content % email.name.title(),
self.get_from_email(),
[unicode(email.address)], html_message=unicode(self.html_content) % email.name.title())
logging.info('#%d alerta enviado para %s' % (index, email))
except Exception, e:
logging.error('Erro ao enviar alerta para o email %s: %s' % (email, e.message))
class MaintenanceAlert(Alert):
equipament = models.ForeignKey(Equipament, verbose_name=_(u'Equipamento'),
related_name='equipamentos', null=True, blank=True)
class Meta:
verbose_name = 'Alerta de Equipamentos'
verbose_name_plural = 'Alertas de Equipamentos'
ordering = ['-created_at']
def __unicode__(self):
return self.name
def update_html_alert(instance):
template = 'alerts/alert_email.html'
delta = instance.equipament.maintenance_date.replace(tzinfo=None) - datetime.datetime.now().replace(tzinfo=None)
context = {'name': instance.equipament.name, 'maintenance_date': instance.equipament.maintenance_date.strftime('%m/%Y'),
'days_left': delta.days if delta.days >= 0 else 0, 'model': instance.equipament.model, 'serial_number': instance.equipament.serial_number,
'room': instance.equipament.room}
message_html = render_mail_template(template, context)
message_txt = striptags(message_html)
return message_html, message_txt
def pre_alert_handler(*args, **kwargs):
instance = kwargs.get('instance')
old_object = False
try:
obj = MaintenanceAlert.objects.get(pk=instance.pk)
if obj.send_at != instance.send_at:
old_object = True
except MaintenanceAlert.DoesNotExist:
old_object = False
if old_object:
# look to see if we have a task_id for this task already.
if obj.task_id:
# If we do, lets get rid of this scheduled task
celery.task.control.revoke(obj.task_id)
id_gen = hashlib.sha1()
id_gen.update(instance.send_at.strftime('%Y-%m-%d %H:%M:%S'))
scheduling_id = id_gen.hexdigest()[:10]
instance.scheduling_id = scheduling_id
result = sendScheduledAlertTask.apply_async(eta=instance.send_at + datetime.timedelta(hours=3),
kwargs={'pk': instance.pk, 'scheduling_id': instance.scheduling_id})
instance.task_id = result.task_id
message_html, message_txt = update_html_alert(instance)
instance.html_content = message_html
instance.plaintext_content = message_txt
instance.status = MaintenanceAlert.SCHEDULED_TO_SEND
def post_alert_handler(sender, instance, created, **kwargs):
if created:
print instance.pk, instance.scheduling_id, instance.id
id_gen = hashlib.sha1()
id_gen.update(instance.send_at.strftime('%Y-%m-%d %H:%M:%S'))
scheduling_id = id_gen.hexdigest()[:10]
instance.scheduling_id = scheduling_id
result = sendScheduledAlertTask.apply_async(eta=instance.send_at + datetime.timedelta(hours=3),
kwargs={'pk': instance.pk, 'scheduling_id': instance.scheduling_id})
instance.task_id = result.task_id
instance.status = MaintenanceAlert.SCHEDULED_TO_SEND
message_html, message_txt = update_html_alert(instance)
instance.html_content = message_html
instance.plaintext_content = message_txt
instance.save()
pre_save.connect(pre_alert_handler, sender=MaintenanceAlert)
post_save.connect(post_alert_handler, sender=MaintenanceAlert)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment