diff --git a/sapl/materia/apps.py b/sapl/materia/apps.py index d8eda8c44..ecc8d09de 100644 --- a/sapl/materia/apps.py +++ b/sapl/materia/apps.py @@ -7,5 +7,5 @@ class AppConfig(apps.AppConfig): label = 'materia' verbose_name = _('Matéria') -# def ready(self): -# from . import signals + def ready(self): + from . import signals diff --git a/sapl/materia/email_utils.py b/sapl/materia/email_utils.py new file mode 100644 index 000000000..78c0483b5 --- /dev/null +++ b/sapl/materia/email_utils.py @@ -0,0 +1,211 @@ +from datetime import datetime + +from django.core.mail import EmailMultiAlternatives, get_connection, send_mail +from django.core.urlresolvers import reverse +from django.template import Context, loader + +from sapl.base.models import CasaLegislativa +from sapl.settings import EMAIL_SEND_USER + +from .models import AcompanhamentoMateria + + +def load_email_templates(templates, context={}): + + emails = [] + for t in templates: + tpl = loader.get_template(t) + email = tpl.render(Context(context)) + if t.endswith(".html"): + email = email.replace('\n', '').replace('\r', '') + emails.append(email) + return emails + + +def enviar_emails(sender, recipients, messages): + ''' + Recipients is a string list of email addresses + + Messages is an array of dicts of the form: + {'recipient': 'address', # useless???? + 'subject': 'subject text', + 'txt_message': 'text message', + 'html_message': 'html message' + } + ''' + + if len(messages) == 1: + # sends an email simultaneously to all recipients + send_mail(messages[0]['subject'], + messages[0]['txt_message'], + sender, + recipients, + html_message=messages[0]['html_message'], + fail_silently=False) + + elif len(recipients) > len(messages): + raise ValueError("Message list should have size 1 \ + or equal recipient list size. \ + recipients: %s, messages: %s" % (recipients, messages) + ) + + else: + # sends an email simultaneously to all reciepients + for (d, m) in zip(recipients, messages): + send_mail(m['subject'], + m['txt_message'], + sender, + [d], + html_message=m['html_message'], + fail_silently=False) + + +def criar_email_confirmacao(base_url, casa_legislativa, materia, hash_txt=''): + + if not casa_legislativa: + raise ValueError("Casa Legislativa é obrigatória") + + if not materia: + raise ValueError("Matéria é obrigatória") + + # FIXME i18n + casa_nome = (casa_legislativa.nome + ' de ' + + casa_legislativa.municipio + '-' + + casa_legislativa.uf) + + materia_url = reverse('sapl.materia:materialegislativa_detail', + kwargs={'pk': materia.id}) + confirmacao_url = reverse('sapl.materia:acompanhar_confirmar', + kwargs={'pk': materia.id}) + + autores = [] + for autoria in materia.autoria_set.all(): + autores.append(autoria.autor.nome) + + templates = load_email_templates(['email/acompanhar.txt', + 'email/acompanhar.html'], + {"casa_legislativa": casa_nome, + "logotipo": casa_legislativa.logotipo, + "descricao_materia": materia.ementa, + "autoria": autores, + "hash_txt": hash_txt, + "base_url": base_url, + "materia": str(materia), + "materia_url": materia_url, + "confirmacao_url": confirmacao_url, }) + return templates + + +def do_envia_email_confirmacao(base_url, casa, materia, destinatario): + # + # Envia email de confirmacao para atualizações de tramitação + # + + sender = EMAIL_SEND_USER + # FIXME i18n + subject = "[SAPL] " + str(materia) + " - Ative o Acompanhamento da Materia" + messages = [] + recipients = [] + + email_texts = criar_email_confirmacao(base_url, + casa, + materia, + destinatario.hash,) + recipients.append(destinatario.email) + messages.append({ + 'recipient': destinatario.email, + 'subject': subject, + 'txt_message': email_texts[0], + 'html_message': email_texts[1] + }) + + enviar_emails(sender, recipients, messages) + + +def criar_email_tramitacao(base_url, casa_legislativa, materia, status, + unidade_destino, hash_txt=''): + + if not casa_legislativa: + raise ValueError("Casa Legislativa é obrigatória") + + if not materia: + raise ValueError("Matéria é obrigatória") + + # FIXME i18n + casa_nome = (casa_legislativa.nome + ' de ' + + casa_legislativa.municipio + '-' + + casa_legislativa.uf) + + url_materia = reverse('sapl.materia:tramitacao_list', + kwargs={'pk': materia.id}) + url_excluir = reverse('sapl.materia:acompanhar_excluir', + kwargs={'pk': materia.id}) + + autores = [] + for autoria in materia.autoria_set.all(): + autores.append(autoria.autor.nome) + + tramitacao = materia.tramitacao_set.last() + + templates = load_email_templates(['email/tramitacao.txt', + 'email/tramitacao.html'], + {"casa_legislativa": casa_nome, + "data_registro": datetime.now().strftime( + "%d/%m/%Y"), + "cod_materia": materia.id, + "logotipo": casa_legislativa.logotipo, + "descricao_materia": materia.ementa, + "autoria": autores, + "data": tramitacao.data_tramitacao, + "status": status, + "localizacao": unidade_destino, + "texto_acao": tramitacao.texto, + "hash_txt": hash_txt, + "materia": str(materia), + "base_url": base_url, + "materia_url": url_materia, + "excluir_url": url_excluir}) + return templates + + +def do_envia_email_tramitacao(base_url, materia, status, unidade_destino): + # + # Envia email de tramitacao para usuarios cadastrados + # + destinatarios = AcompanhamentoMateria.objects.filter(materia=materia, + confirmado=True) + casa = CasaLegislativa.objects.first() + + sender = EMAIL_SEND_USER + # FIXME i18n + subject = "[SAPL] " + str(materia) + \ + " - Acompanhamento de Materia Legislativa" + + connection = get_connection() + connection.open() + + for destinatario in destinatarios: + try: + email_texts = criar_email_tramitacao(base_url, + casa, + materia, + status, + unidade_destino, + destinatario.hash,) + + email = EmailMultiAlternatives( + subject, + email_texts[0], + sender, + [destinatario.email], + connection=connection) + email.attach_alternative(email_texts[1], "text/html") + email.send() + + # Garantia de que, mesmo com o lançamento de qualquer exceção, + # a conexão será fechada + except Exception: + connection.close() + raise Exception('Erro ao enviar e-mail de acompanhamento de matéria.') + + connection.close() diff --git a/sapl/materia/receivers.py b/sapl/materia/receivers.py new file mode 100644 index 000000000..31f353fe0 --- /dev/null +++ b/sapl/materia/receivers.py @@ -0,0 +1,19 @@ +from django.dispatch import receiver + +from sapl.materia.signals import tramitacao_signal +from sapl.utils import get_base_url + +from .email_utils import do_envia_email_tramitacao + + +@receiver(tramitacao_signal) +def handle_tramitacao_signal(sender, **kwargs): + tramitacao = kwargs.get("post") + request = kwargs.get("request") + materia = tramitacao.materia + + do_envia_email_tramitacao( + get_base_url(request), + materia, + tramitacao.status, + tramitacao.unidade_tramitacao_destino) diff --git a/sapl/materia/signals.py b/sapl/materia/signals.py index 7314d7504..9dd5e4fb9 100644 --- a/sapl/materia/signals.py +++ b/sapl/materia/signals.py @@ -1,10 +1,15 @@ from django.db.models.signals import post_delete, post_save +import django.dispatch + from sapl.utils import delete_texto, save_texto from .models import DocumentoAcessorio, MateriaLegislativa -post_save.connect(save_texto, sender=MateriaLegislativa) -post_save.connect(save_texto, sender=DocumentoAcessorio) -post_delete.connect(delete_texto, sender=MateriaLegislativa) -post_delete.connect(delete_texto, sender=DocumentoAcessorio) + +tramitacao_signal = django.dispatch.Signal(providing_args=['post', 'request']) + +# post_save.connect(save_texto, sender=MateriaLegislativa) +# post_save.connect(save_texto, sender=DocumentoAcessorio) +# post_delete.connect(delete_texto, sender=MateriaLegislativa) +# post_delete.connect(delete_texto, sender=DocumentoAcessorio) diff --git a/sapl/materia/tests/test_email_templates.py b/sapl/materia/tests/test_email_templates.py index 03982b150..aac13cbb7 100644 --- a/sapl/materia/tests/test_email_templates.py +++ b/sapl/materia/tests/test_email_templates.py @@ -1,6 +1,6 @@ from django.core import mail -from sapl.materia.views import enviar_emails, load_email_templates +from sapl.materia.email_utils import enviar_emails, load_email_templates def test_email_template_loading(): diff --git a/sapl/materia/urls.py b/sapl/materia/urls.py index 7eac42e5c..600fadfc6 100644 --- a/sapl/materia/urls.py +++ b/sapl/materia/urls.py @@ -25,6 +25,8 @@ from sapl.materia.views import (AcompanhamentoConfirmarView, from .apps import AppConfig +from . import receivers + app_name = AppConfig.name urlpatterns_materia = [ diff --git a/sapl/materia/views.py b/sapl/materia/views.py index 5ceb73377..8f6d2394d 100644 --- a/sapl/materia/views.py +++ b/sapl/materia/views.py @@ -7,13 +7,11 @@ from crispy_forms.layout import HTML from django.contrib import messages from django.contrib.auth.decorators import permission_required from django.contrib.auth.mixins import PermissionRequiredMixin -from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist -from django.core.mail import send_mail +from django.core.exceptions import ObjectDoesNotExist, MultipleObjectsReturned from django.core.urlresolvers import reverse from django.http import HttpResponse, JsonResponse from django.http.response import Http404, HttpResponseRedirect from django.shortcuts import get_object_or_404, redirect -from django.template import Context, loader from django.utils import formats from django.utils.translation import ugettext_lazy as _ from django.views.generic import CreateView, ListView, TemplateView, UpdateView @@ -37,12 +35,13 @@ from sapl.materia.forms import (AnexadaForm, ConfirmarProposicaoForm, TramitacaoUpdateForm) from sapl.norma.models import LegislacaoCitada from sapl.protocoloadm.models import Protocolo -from sapl.settings import EMAIL_SEND_USER from sapl.utils import (TURNO_TRAMITACAO_CHOICES, YES_NO_CHOICES, autor_label, autor_modal, gerar_hash_arquivo, get_base_url, montar_row_autor) import sapl +from .email_utils import do_envia_email_confirmacao + from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm, AdicionarVariasAutoriasFilterSet, DespachoInicialForm, DocumentoAcessorioForm, MateriaAssuntoForm, @@ -59,6 +58,8 @@ from .models import (AcompanhamentoMateria, Anexada, AssuntoMateria, Autoria, TipoDocumento, TipoFimRelatoria, TipoMateriaLegislativa, TipoProposicao, Tramitacao, UnidadeTramitacao) +from .signals import tramitacao_signal + AssuntoMateriaCrud = Crud.build(AssuntoMateria, 'assunto_materia') @@ -906,47 +907,27 @@ class TramitacaoCrud(MasterDetailCrud): self.initial['data_tramitacao'] = datetime.now() return self.initial - def post(self, request, *args, **kwargs): - materia = MateriaLegislativa.objects.get(id=kwargs['pk']) - - if 'status' in request.POST and request.POST['status']: - status = StatusTramitacao.objects.filter( - id=request.POST['status']).first() - unidade_destino = UnidadeTramitacao.objects.get( - id=request.POST['unidade_tramitacao_destino'] - ) - texto = request.POST['texto'] - data_tramitacao = request.POST['data_tramitacao'] - do_envia_email_tramitacao( - request, materia, status, - unidade_destino, texto, data_tramitacao) - return super(CreateView, self).post(request, *args, **kwargs) + def form_valid(self, form): + self.object = form.save() + tramitacao_signal.send(sender=Tramitacao, + post=self.object, + request=self.request) + return super().form_valid(form) class UpdateView(MasterDetailCrud.UpdateView): form_class = TramitacaoUpdateForm - def post(self, request, *args, **kwargs): - materia = MateriaLegislativa.objects.get( - tramitacao__id=kwargs['pk']) - - if 'status' in request.POST and request.POST['status']: - status = StatusTramitacao.objects.filter( - id=request.POST['status']).first() - unidade_destino = UnidadeTramitacao.objects.get( - id=request.POST['unidade_tramitacao_destino'] - ) - texto = request.POST['texto'] - data_tramitacao = request.POST['data_tramitacao'] - do_envia_email_tramitacao( - request, materia, status, - unidade_destino, texto, data_tramitacao) - - return super(UpdateView, self).post(request, *args, **kwargs) - @property def layout_key(self): return 'TramitacaoUpdate' + def form_valid(self, form): + self.object = form.save() + tramitacao_signal.send(sender=Tramitacao, + post=self.object, + request=self.request) + return super().form_valid(form) + class ListView(MasterDetailCrud.ListView): def get_queryset(self): @@ -1301,6 +1282,8 @@ class AcompanhamentoConfirmarView(TemplateView): class AcompanhamentoExcluirView(TemplateView): def get_success_url(self): + msg = _('Você parou de acompanhar esta matéria.') + messages.add_message(self.request, messages.INFO, msg) return reverse('sapl.materia:materialegislativa_detail', kwargs={'pk': self.kwargs['pk']}) @@ -1416,7 +1399,20 @@ class AcompanhamentoMateriaView(CreateView): acompanhar.usuario = usuario.username acompanhar.confirmado = False acompanhar.save() - do_envia_email_confirmacao(request, materia, email) + + base_url = get_base_url(request) + + destinatario = AcompanhamentoMateria.objects.get( + materia=materia, + email=email, + confirmado=False) + casa = CasaLegislativa.objects.first() + + do_envia_email_confirmacao(base_url, + casa, + materia, + destinatario) + msg = _('Foi enviado um e-mail de confirmação. Confira sua caixa \ de mensagens e clique no link que nós enviamos para \ confirmar o acompanhamento desta matéria.') @@ -1444,207 +1440,6 @@ class AcompanhamentoMateriaView(CreateView): kwargs={'pk': self.kwargs['pk']}) -def load_email_templates(templates, context={}): - - emails = [] - for t in templates: - tpl = loader.get_template(t) - email = tpl.render(Context(context)) - if t.endswith(".html"): - email = email.replace('\n', '').replace('\r', '') - emails.append(email) - return emails - - -def criar_email_confirmacao(request, casa_legislativa, materia, hash_txt=''): - - if not casa_legislativa: - raise ValueError("Casa Legislativa é obrigatória") - - if not materia: - raise ValueError("Matéria é obrigatória") - - # FIXME i18n - casa_nome = (casa_legislativa.nome + ' de ' + - casa_legislativa.municipio + '-' + - casa_legislativa.uf) - - base_url = get_base_url(request) - materia_url = reverse('sapl.materia:materialegislativa_detail', - kwargs={'pk': materia.id}) - confirmacao_url = reverse('sapl.materia:acompanhar_confirmar', - kwargs={'pk': materia.id}) - - autores = [] - for autoria in materia.autoria_set.all(): - autores.append(autoria.autor.nome) - - templates = load_email_templates(['email/acompanhar.txt', - 'email/acompanhar.html'], - {"casa_legislativa": casa_nome, - "logotipo": casa_legislativa.logotipo, - "descricao_materia": materia.ementa, - "autoria": autores, - "hash_txt": hash_txt, - "base_url": base_url, - "materia": str(materia), - "materia_url": materia_url, - "confirmacao_url": confirmacao_url, }) - return templates - - -def criar_email_tramitacao( - request, casa_legislativa, materia, status, - unidade_destino, texto, data_tramitacao, hash_txt=''): - - if not casa_legislativa: - raise ValueError("Casa Legislativa é obrigatória") - - if not materia: - raise ValueError("Matéria é obrigatória") - - # FIXME i18n - casa_nome = (casa_legislativa.nome + ' de ' + - casa_legislativa.municipio + '-' + - casa_legislativa.uf) - - base_url = get_base_url(request) - url_materia = reverse('sapl.materia:tramitacao_list', - kwargs={'pk': materia.id}) - url_excluir = reverse('sapl.materia:acompanhar_excluir', - kwargs={'pk': materia.id}) - - autores = [] - for autoria in materia.autoria_set.all(): - autores.append(autoria.autor.nome) - - tramitacao = materia.tramitacao_set.last() - - templates = load_email_templates(['email/tramitacao.txt', - 'email/tramitacao.html'], - {"casa_legislativa": casa_nome, - "data_registro": datetime.now().strftime( - "%d/%m/%Y"), - "cod_materia": materia.id, - "logotipo": casa_legislativa.logotipo, - "descricao_materia": materia.ementa, - "autoria": autores, - "data": data_tramitacao, - "status": status, - "localizacao": unidade_destino, - "texto_acao": texto, - "hash_txt": hash_txt, - "materia": str(materia), - "base_url": base_url, - "materia_url": url_materia, - "excluir_url": url_excluir}) - return templates - - -def enviar_emails(sender, recipients, messages): - ''' - Recipients is a string list of email addresses - - Messages is an array of dicts of the form: - {'recipient': 'address', # useless???? - 'subject': 'subject text', - 'txt_message': 'text message', - 'html_message': 'html message' - } - ''' - - if len(messages) == 1: - # sends an email simultaneously to all recipients - send_mail(messages[0]['subject'], - messages[0]['txt_message'], - sender, - recipients, - html_message=messages[0]['html_message'], - fail_silently=False) - - elif len(recipients) > len(messages): - raise ValueError("Message list should have size 1 \ - or equal recipient list size. \ - recipients: %s, messages: %s" % (recipients, messages) - ) - - else: - # sends an email simultaneously to all reciepients - for (d, m) in zip(recipients, messages): - send_mail(m['subject'], - m['txt_message'], - sender, - [d], - html_message=m['html_message'], - fail_silently=False) - return None - - -def do_envia_email_confirmacao(request, materia, email): - # - # Envia email de confirmacao para atualizações de tramitação - # - destinatario = AcompanhamentoMateria.objects.get(materia=materia, - email=email, - confirmado=False) - casa = CasaLegislativa.objects.first() - - sender = EMAIL_SEND_USER - # FIXME i18n - subject = "[SAPL] " + str(materia) + " - Ative o Acompanhamento da Materia" - messages = [] - recipients = [] - - email_texts = criar_email_confirmacao(request, - casa, - materia, - destinatario.hash,) - recipients.append(destinatario.email) - messages.append({ - 'recipient': destinatario.email, - 'subject': subject, - 'txt_message': email_texts[0], - 'html_message': email_texts[1] - }) - - enviar_emails(sender, recipients, messages) - return None - - -def do_envia_email_tramitacao( - request, materia, status, unidade_destino, texto, data_tramitacao): - # - # Envia email de tramitacao para usuarios cadastrados - # - destinatarios = AcompanhamentoMateria.objects.filter(materia=materia, - confirmado=True) - casa = CasaLegislativa.objects.first() - - sender = EMAIL_SEND_USER - # FIXME i18n - subject = "[SAPL] " + str(materia) + \ - " - Acompanhamento de Materia Legislativa" - messages = [] - recipients = [] - for destinatario in destinatarios: - email_texts = criar_email_tramitacao(request, - casa, - materia, - status, - unidade_destino, - texto, - data_tramitacao, - destinatario.hash,) - recipients.append(destinatario.email) - messages.append({ - 'recipient': destinatario.email, - 'subject': subject, - 'txt_message': email_texts[0], - 'html_message': email_texts[1], - }) - enviar_emails(sender, recipients, messages) - - class DocumentoAcessorioEmLoteView(PermissionRequiredMixin, FilterView): filterset_class = AcessorioEmLoteFilterSet template_name = 'materia/em_lote/acessorio.html' @@ -1787,10 +1582,10 @@ class TramitacaoEmLoteView(PrimeiraTramitacaoEmLoteView): qr = self.request.GET.copy() if ('tramitacao__status' in qr and - 'tramitacao__unidade_tramitacao_destino' in qr and - qr['tramitacao__status'] and - qr['tramitacao__unidade_tramitacao_destino'] - ): + 'tramitacao__unidade_tramitacao_destino' in qr and + qr['tramitacao__status'] and + qr['tramitacao__unidade_tramitacao_destino'] + ): lista = filtra_tramitacao_destino_and_status( qr['tramitacao__status'], qr['tramitacao__unidade_tramitacao_destino'])