From 3de6022da89b4593b1216e0cda4946f9c6972a82 Mon Sep 17 00:00:00 2001 From: Edward Ribeiro Date: Wed, 24 Feb 2016 12:59:49 -0300 Subject: [PATCH] =?UTF-8?q?Adiciona=20suporte=20ao=20envio=20de=20email=20?= =?UTF-8?q?para=20confirma=C3=A7=C3=A3o=20e=20altera=C3=A7=C3=A3o=20de=20t?= =?UTF-8?q?ramita=C3=A7=C3=A3o?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adiciona testes e refatora funções de envio de email --- materia/models.py | 2 +- materia/urls.py | 4 - materia/views.py | 265 +++++++++++++----- sapl/utils.py | 9 + templates/email/acompanhar.html | 27 ++ templates/email/acompanhar.txt | 16 ++ templates/email/test_tramitacao.html | 1 + templates/email/tramitacao.html | 38 +++ templates/email/tramitacao.txt | 27 ++ templates/materia/acompanhamento_materia.html | 1 - templates/materia/test_email_templates.py | 74 +++++ 11 files changed, 382 insertions(+), 82 deletions(-) create mode 100644 templates/email/acompanhar.html create mode 100644 templates/email/acompanhar.txt create mode 100644 templates/email/test_tramitacao.html create mode 100644 templates/email/tramitacao.html create mode 100644 templates/email/tramitacao.txt create mode 100644 templates/materia/test_email_templates.py diff --git a/materia/models.py b/materia/models.py index 2c8308a25..6df7eb50d 100644 --- a/materia/models.py +++ b/materia/models.py @@ -138,7 +138,7 @@ class AcompanhamentoMateria(models.Model): email = models.CharField( max_length=100, verbose_name=_('Endereço de E-mail')) data_cadastro = models.DateField(auto_now_add=True) - hash = models.CharField(max_length=8) + hash = models.CharField(max_length=8) # TODO LOL isso não é um HASH!!!! confirmado = models.BooleanField(default=False) class Meta: diff --git a/materia/urls.py b/materia/urls.py index b688aa122..3475189c0 100644 --- a/materia/urls.py +++ b/materia/urls.py @@ -1,12 +1,8 @@ from django.conf.urls import include, url -<<<<<<< HEAD from materia.views import (AcompanhamentoConfirmarView, AcompanhamentoExcluirView, AcompanhamentoMateriaView, AutoriaEditView, -======= -from materia.views import (AcompanhamentoMateriaView, AutoriaEditView, ->>>>>>> Move acompanhamento de matéria para a app de matéria. AutoriaView, DespachoInicialEditView, DespachoInicialView, DocumentoAcessorioEditView, DocumentoAcessorioView, FormularioCadastroView, diff --git a/materia/views.py b/materia/views.py index 9ae2e7a2a..bc726f3ae 100644 --- a/materia/views.py +++ b/materia/views.py @@ -10,6 +10,7 @@ from django.core.mail import send_mail from django.core.urlresolvers import reverse from django.http.response import HttpResponseRedirect from django.shortcuts import redirect +from django.template import Context, loader from django.utils.html import strip_tags from django.utils.translation import ugettext_lazy as _ from django.views.generic import ListView, TemplateView @@ -23,6 +24,8 @@ from crud import build_crud, make_pagination from norma.models import LegislacaoCitada, NormaJuridica, TipoNormaJuridica from parlamentares.models import Partido +from sapl.utils import get_base_url + from .forms import (AcompanhamentoMateriaForm, AutoriaForm, DespachoInicialForm, DocumentoAcessorioForm, FormularioCadastroForm, FormularioSimplificadoForm, @@ -769,11 +772,10 @@ class AcompanhamentoConfirmarView(TemplateView): def get(self, request, *args, **kwargs): materia_id = kwargs['pk'] - hash_txt = request.GET.get('hash', '') + hash_txt = request.GET.get('hash_txt', '') acompanhar = AcompanhamentoMateria.objects.get(materia_id=materia_id, hash=hash_txt) - acompanhar.confirmado = True acompanhar.save() @@ -787,10 +789,13 @@ class AcompanhamentoExcluirView(TemplateView): def get(self, request, *args, **kwargs): materia_id = kwargs['pk'] - hash_txt = request.GET.get('hash', '') + hash_txt = request.GET.get('hash_txt', '') - AcompanhamentoMateria.objects.get(materia_id=materia_id, + try: + AcompanhamentoMateria.objects.get(materia_id=materia_id, hash=hash_txt).delete() + except ObjectDoesNotExist: + pass return HttpResponseRedirect(self.get_redirect_url()) @@ -1004,84 +1009,190 @@ class RelatoriaView(FormMixin, GenericView): 'parlamentares': parlamentares}) -def criar_html_email(materia, hash_txt): - - html_tpl = Template(''' - - - -

- -

-

$casa_legislativa
- Sistema de Apoio ao Processo Legislativo -

-

- A seguinte matéria de seu interesse - sofreu tramitação registrada - $data_registro -

-

- $descricao_materia -

- Autoria: $autoria -

-

- Data da ação: $data
- Status: $status
- Texto da ação: $texto_acao

-
-

- - Clique aqui para excluir seu e-mail da lista - de envio -

-

Esta é uma mensagem automática. - Por favor, não a responda.

- - ''') +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") + + casa_nome = (casa_legislativa.nome + ' de ' + \ + casa_legislativa.municipio + '-' + \ + casa_legislativa.uf) + + base_url = get_base_url(request) + materia_url = reverse('acompanhar_materia', kwargs={'pk': materia.id}) + confirmacao_url = reverse('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 - casa = CasaLegislativa.objects.first() - casa_nome = (casa.nome + ' de ' + casa.municipio + '-' + casa.uf) - url = reverse('acompanhar_excluir', kwargs={'pk': materia.id}) +def criar_email_tramitacao(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") + + casa_nome = (casa_legislativa.nome + ' de ' + \ + casa_legislativa.municipio + '-' + \ + casa_legislativa.uf) + + base_url = get_base_url(request) + url_materia = reverse('acompanhar_materia', kwargs={'pk': materia.id}) + url_excluir = reverse('acompanhar_excluir', kwargs={'pk': materia.id}) + + autores = [] for autoria in materia.autoria_set.all(): - autoria_html += autoria.autor.nome + "
" - - html_body = html_tpl.substitute(image=static('img/logo.png'), - casa_legislativa=casa_nome, - data_registro=datetime.now().strftime( - "%d/%m/%Y"), - cod_materia=materia.id, - descricao_materia=materia.ementa, - autoria=autoria_html, - data=materia.tramitacao_set.last( + autores.append(autoria.autor.nome) + + 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":materia.tramitacao_set.last( ).data_tramitacao, - status=materia.tramitacao_set.last( + "status":materia.tramitacao_set.last( ).status, - texto_acao=materia.tramitacao_set.last( + "texto_acao":materia.tramitacao_set.last( ).texto, - hash_txt=hash_txt, - url=url,) - return html_body - - -def enviar_emails(materia): + "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() - destinatarios = AcompanhamentoMateria.objects.filter( - materia=materia, - confirmado=True) + sender = 'sapl-test@interlegis.leg.br' + 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): + # + # Envia email de tramitacao para usuarios cadastrados + # + destinatarios = AcompanhamentoMateria.objects.filter(materia=materia, + confirmado=True) + casa = CasaLegislativa.objects.first() + sender = 'sapl-test@interlegis.leg.br' + subject = "[SAPL] "+ str(materia) +" - Acompanhamento de Materia Legislativa" + messages = [] + recipients = [] for destinatario in destinatarios: - corpo_email_html = criar_html_email(materia, destinatario.hash_txt) - send_mail('Mudança de Tramitação', - corpo_email_html, - 'sapl-test@interlegis.leg.br', - destinatario, - fail_silently=True) + email_texts = criar_email_tramitacao(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 class TramitacaoView(FormMixin, GenericView): @@ -1123,9 +1234,8 @@ class TramitacaoView(FormMixin, GenericView): 'object': materia, 'tramitacoes': tramitacoes_list}) - # Manda por parametro para 'enviar_emails' ? - img_url = request.get_host() + static('img/logo.png') - self.enviar_emails(materia) + do_envia_email_tramitacao(request, materia) + return self.form_valid(form) else: return self.render_to_response({'form': form, @@ -1136,7 +1246,6 @@ class TramitacaoView(FormMixin, GenericView): pk = self.kwargs['pk'] return reverse('tramitacao_materia', kwargs={'pk': pk}) - class TramitacaoEditView(FormMixin, GenericView): template_name = "materia/tramitacao_edit.html" @@ -1615,6 +1724,7 @@ class AcompanhamentoMateriaView(FormMixin, def get(self, request, *args, **kwargs): pk = self.kwargs['pk'] materia = MateriaLegislativa.objects.get(id=pk) + return self.render_to_response( {'form': AcompanhamentoMateriaForm(), 'materia': materia}) @@ -1644,6 +1754,9 @@ class AcompanhamentoMateriaView(FormMixin, acompanhar.usuario = usuario.username acompanhar.confirmado = False acompanhar.save() + + do_envia_email_confirmacao(request, materia, email) + else: return self.render_to_response( {'form': form, diff --git a/sapl/utils.py b/sapl/utils.py index d86c59616..adac82dd8 100644 --- a/sapl/utils.py +++ b/sapl/utils.py @@ -21,6 +21,15 @@ def xstr(s): return '' if s is None else str(s) +def get_base_url(request): + # TODO substituir por Site.objects.get_current().domain + # from django.contrib.sites.models import Site + + current_domain = request.get_host() + protocol = 'https' if request.is_secure() else 'http' + return "{0}://{1}".format(protocol, current_domain) + + def create_barcode(value): ''' creates a base64 encoded barcode PNG image diff --git a/templates/email/acompanhar.html b/templates/email/acompanhar.html new file mode 100644 index 000000000..c95092bf2 --- /dev/null +++ b/templates/email/acompanhar.html @@ -0,0 +1,27 @@ +{% load i18n %} +{% load static %} + +

+ Logo +

+

{{casa_legislativa}} +
+ Sistema de Apoio ao Processo Legislativo +

+

Registramos seu pedido para acompanhamento por e-mail da matéria legislativa identificada a seguir:

+{{materia}} - {{descricao_materia}}
+{{ementa}}
+ +

+

Para garantia de sua privacidade, solicitamos que ative o recebimento das futuras mensagens clicando no link:

+ +

+ {{base_url}}{{confirmacao_url}}?hash_txt={{hash_txt}} +

+
+
+

Caso não tenha realizado o cadastramento em nosso sistema, favor desconsiderar a presente mensagem
+Esta é uma mensagem automática. Por favor, não responda.

+ + diff --git a/templates/email/acompanhar.txt b/templates/email/acompanhar.txt new file mode 100644 index 000000000..f1745a917 --- /dev/null +++ b/templates/email/acompanhar.txt @@ -0,0 +1,16 @@ +{{casa_legislativa}} + +Sistema de Apoio ao Processo Legislativo + +>Registramos seu pedido para acompanhamento por e-mail da matéria legislativa identificada a seguir: + +{{base_url}}{{materia_url}} - {{materia}} - {{descricao_materia}} + +{{ementa}} + +Para garantia de sua privacidade, solicitamos que ative o recebimento das futuras mensagens acessando no link: + +{{base_url}}{{url_confirmar}}?hash_txt={{hash_txt}} + +Caso não tenha realizado o cadastramento em nosso sistema, favor desconsiderar a presente mensagem +Esta é uma mensagem automática. Por favor, não responda. diff --git a/templates/email/test_tramitacao.html b/templates/email/test_tramitacao.html new file mode 100644 index 000000000..dfb057fe8 --- /dev/null +++ b/templates/email/test_tramitacao.html @@ -0,0 +1 @@ +Hello {{name}} diff --git a/templates/email/tramitacao.html b/templates/email/tramitacao.html new file mode 100644 index 000000000..6ca454903 --- /dev/null +++ b/templates/email/tramitacao.html @@ -0,0 +1,38 @@ +{% load i18n %} +{% load static %} + + + +

+ Logo +

+

{{casa_legislativa}} +
+ Sistema de Apoio ao Processo Legislativo +

+

A seguinte matéria de seu interesse sofreu + tramitação registrada em {{data_registro}} +

+

+ {{materia}} - {{descricao_materia}} +

+Autoria:
+{% for autor in autoria %} + {{ autor }}
+{% endfor %} +

+

+

+ Data da ação: {{data}}
+ Status: {{status}}
+ Texto da ação: {{texto_acao}}

+
+

+ + Clique aqui para excluir seu e-mail da lista de envio +

+

Esta é uma mensagem automática. + Por favor, não a responda.

+ + diff --git a/templates/email/tramitacao.txt b/templates/email/tramitacao.txt new file mode 100644 index 000000000..0e84653e8 --- /dev/null +++ b/templates/email/tramitacao.txt @@ -0,0 +1,27 @@ +{{casa_legislativa}} + +Sistema de Apoio ao Processo Legislativo +----------------------------------------- + +A seguinte matéria de seu interesse sofreu tramitação registrada em {{data_registro}} + +Matéria: {{materia}} - {{descricao_materia}} + +{{url_materia}} + +Autoria: +{% for autor in autoria %} + {{ autor }} +{% endfor %} + +Data da ação: {{data}} + +Status: {{status}} + +Texto da ação: {{texto_acao}} + +Acesse o link abaixo para excluir seu e-mail da lista de envio + +{{url_excluir_acompanhamento}}?hash_txt={{hash_txt}} + +Esta é uma mensagem automática. Por favor, não a responda. diff --git a/templates/materia/acompanhamento_materia.html b/templates/materia/acompanhamento_materia.html index a3cfc30c7..d22e90e61 100644 --- a/templates/materia/acompanhamento_materia.html +++ b/templates/materia/acompanhamento_materia.html @@ -18,5 +18,4 @@ {% if error %}
{{ error }}
{% endif %} {% crispy form %} - {% endblock %} diff --git a/templates/materia/test_email_templates.py b/templates/materia/test_email_templates.py new file mode 100644 index 000000000..8e5b0d57b --- /dev/null +++ b/templates/materia/test_email_templates.py @@ -0,0 +1,74 @@ +import pytest +from base.models import CasaLegislativa +from django.core import mail +from model_mommy import mommy +from materia.views import load_email_templates, enviar_email, criar_email_tramitacao +from materia.models import MateriaLegislativa, TipoMateriaLegislativa + + +def test_email_template_loading(): + expected = "Hello Django" + emails = load_email_templates(['email/test_tramitacao.html'], + context={"name": "Django"}) + + # strip \n and \r to compare with expected + actual = emails[0].replace('\n', '').replace('\r', '') + + assert actual == expected + + +@pytest.mark.django_db(transaction=False) +def test_email_body_creation_with_empty_materia(): + casa = CasaLegislativa.objects.create() + + with pytest.raises(ValueError): + criar_email_tramitacao(casa, materia=None) + + +def test_html_email_body_with_materia(): + templates = load_email_templates(['email/tramitacao.txt', + 'email/tramitacao.html'], + {"image":'img/logo.png', + "casa_legislativa":"Assembléia Parlamentar", + "data_registro":"25/02/2016", + "cod_materia":"1", + "descricao_materia":"Ementa de teste", + "autoria": ["Autor1", "Autor2"], + "data":"25/02/2016", + "status":"Arquivado", + "texto_acao":"Deliberado", + "hash_txt":"abc01f", + "materia_id": "794", + "base_url": "http://localhost:8000", + "materia_url": "/materia/764/acompanhar-materia", + "excluir_url": "/materia/764/acompanhar-excluir",}) + + assert len(templates) == 2 + + +def test_enviar_email_distintos(): + NUM_MESSAGES = 10 + messages = [{'recipient': 'user-' + str(i) + '@test.com', + 'subject': 'subject: ' + str(i), + 'txt_message': 'txt: ' + str(i), + 'html_message': '', + } for i in range(NUM_MESSAGES)] + + recipients = [m['recipient'] for m in messages] + + enviar_email('test@sapl.com', recipients, messages) + assert len(mail.outbox) == NUM_MESSAGES + + +def test_enviar_same_email(): + NUM_MESSAGES = 10 + messages = [{'recipient': 'user-' + str(i) + '@test.com', + 'subject': 'subject: ' + str(i), + 'txt_message': 'txt: ' + str(i), + 'html_message': '', + } for i in range(NUM_MESSAGES)] + + recipients = [m['recipient'] for m in messages] + + enviar_email('test@sapl.com', recipients, [messages[0]]) + assert len(mail.outbox) == 1