diff --git a/sapl/materia/email_utils.py b/sapl/base/email_utils.py similarity index 56% rename from sapl/materia/email_utils.py rename to sapl/base/email_utils.py index 3dc6b220d..737449e57 100644 --- a/sapl/materia/email_utils.py +++ b/sapl/base/email_utils.py @@ -8,7 +8,8 @@ from django.utils import timezone from sapl.base.models import CasaLegislativa from sapl.settings import EMAIL_SEND_USER -from .models import AcompanhamentoMateria +from sapl.materia.models import AcompanhamentoMateria +from sapl.protocoloadm.models import AcompanhamentoDocumento def load_email_templates(templates, context={}): @@ -61,56 +62,73 @@ def enviar_emails(sender, recipients, messages): fail_silently=False) -def criar_email_confirmacao(base_url, casa_legislativa, materia, hash_txt=''): +def criar_email_confirmacao(base_url, casa_legislativa, doc_mat, tipo, hash_txt=''): if not casa_legislativa: raise ValueError("Casa Legislativa é obrigatória") - if not materia: - raise ValueError("Matéria é obrigatória") + if not doc_mat: + if tipo == "materia": + msg = "Matéria é obrigatória" + else: + msg = "Documento é obrigatório" + raise ValueError(msg) # FIXME i18n - casa_nome = (casa_legislativa.nome + ' de ' + - casa_legislativa.municipio + '-' + - casa_legislativa.uf) + casa_nome = ("{} de {} - {}".format(casa_legislativa.nome, + casa_legislativa.municipio, + casa_legislativa.uf)) + + if tipo == "materia": + doc_mat_url = reverse('sapl.materia:materialegislativa_detail', + kwargs={'pk': doc_mat.id}) + confirmacao_url = reverse('sapl.materia:acompanhar_confirmar', + kwargs={'pk': doc_mat.id}) + ementa = doc_mat.ementa + autores = [autoria.autor.nome for autoria in doc_mat.autoria_set.all()] + else: + doc_mat_url = reverse('sapl.protocoloadm:documentoadministrativo_detail', + kwargs={'pk': doc_mat.id}) + confirmacao_url = reverse('sapl.protocoloadm:acompanhar_confirmar', + kwargs={'pk': doc_mat.id}) + ementa = doc_mat.assunto + autores = "" - 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, + "descricao_materia": ementa, "autoria": autores, "hash_txt": hash_txt, "base_url": base_url, - "materia": str(materia), - "materia_url": materia_url, + "materia": str(doc_mat), + "materia_url": doc_mat_url, "confirmacao_url": confirmacao_url, }) return templates -def do_envia_email_confirmacao(base_url, casa, materia, destinatario): +def do_envia_email_confirmacao(base_url, casa, tipo, doc_mat, 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" + if tipo == "materia": + msg = " - Ative o Acompanhamento da Matéria" + else: + msg = " - Ative o Acompanhamento de Documento" + subject = "[SAPL] {} {}".format(str(doc_mat), msg) messages = [] recipients = [] email_texts = criar_email_confirmacao(base_url, casa, - materia, + doc_mat, + tipo, destinatario.hash,) recipients.append(destinatario.email) messages.append({ @@ -123,30 +141,41 @@ def do_envia_email_confirmacao(base_url, casa, materia, destinatario): enviar_emails(sender, recipients, messages) -def criar_email_tramitacao(base_url, casa_legislativa, materia, status, +def criar_email_tramitacao(base_url, casa_legislativa, tipo, doc_mat, status, unidade_destino, hash_txt=''): if not casa_legislativa: raise ValueError("Casa Legislativa é obrigatória") - if not materia: - raise ValueError("Matéria é obrigatória") + if not doc_mat: + if tipo == "materia": + msg = "Matéria é obrigatória" + else: + msg = "Documento é obrigatório" + raise ValueError(msg) # 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}) + casa_nome = ("{} de {} - {}".format(casa_legislativa.nome, + casa_legislativa.municipio, + casa_legislativa.uf)) + if tipo == "materia": + doc_mat_url = reverse('sapl.materia:tramitacao_list', + kwargs={'pk': doc_mat.id}) + url_excluir = reverse('sapl.materia:acompanhar_excluir', + kwargs={'pk': doc_mat.id}) + + ementa = doc_mat.ementa + autores = [autoria.autor.nome for autoria in doc_mat.autoria_set.all()] + tramitacao = doc_mat.tramitacao_set.last() - autores = [] - for autoria in materia.autoria_set.all(): - autores.append(autoria.autor.nome) - - tramitacao = materia.tramitacao_set.last() + else: + doc_mat_url = reverse('sapl.protocoloadm:tramitacaoadministrativo_list', + kwargs={'pk': doc_mat.id}) + url_excluir = reverse('sapl.protocoloadm:acompanhar_excluir', + kwargs={'pk': doc_mat.id}) + autores = "" + ementa = doc_mat.assunto + tramitacao = doc_mat.tramitacaoadministrativo_set.last() templates = load_email_templates(['email/tramitacao.txt', 'email/tramitacao.html'], @@ -154,34 +183,42 @@ def criar_email_tramitacao(base_url, casa_legislativa, materia, status, "data_registro": dt.strftime( timezone.now(), "%d/%m/%Y"), - "cod_materia": materia.id, + "cod_materia": doc_mat.id, "logotipo": casa_legislativa.logotipo, - "descricao_materia": materia.ementa, + "descricao_materia": ementa, "autoria": autores, "data": tramitacao.data_tramitacao, "status": status, "localizacao": unidade_destino, "texto_acao": tramitacao.texto, "hash_txt": hash_txt, - "materia": str(materia), + "materia": str(doc_mat), "base_url": base_url, - "materia_url": url_materia, + "materia_url": doc_mat_url, "excluir_url": url_excluir}) return templates -def do_envia_email_tramitacao(base_url, materia, status, unidade_destino): +def do_envia_email_tramitacao(base_url, tipo, doc_mat, status, unidade_destino): # # Envia email de tramitacao para usuarios cadastrados # - destinatarios = AcompanhamentoMateria.objects.filter(materia=materia, - confirmado=True) + if tipo == "materia": + destinatarios = AcompanhamentoMateria.objects.filter(materia=doc_mat, + confirmado=True) + else: + destinatarios = AcompanhamentoDocumento.objects.filter(documento=doc_mat, + confirmado=True) + casa = CasaLegislativa.objects.first() sender = EMAIL_SEND_USER - # FIXME i18n - subject = "[SAPL] " + str(materia) + \ - " - Acompanhamento de Materia Legislativa" + # FIXME i18nn + if tipo == "materia": + msg = " - Acompanhamento de Matéria Legislativa" + else: + msg = " - Acompanhamento de Documento" + subject = "[SAPL] {} {}".format(str(doc_mat), msg) connection = get_connection() connection.open() @@ -190,10 +227,11 @@ def do_envia_email_tramitacao(base_url, materia, status, unidade_destino): try: email_texts = criar_email_tramitacao(base_url, casa, - materia, + tipo, + doc_mat, status, unidade_destino, - destinatario.hash,) + destinatario.hash) email = EmailMultiAlternatives( subject, diff --git a/sapl/base/receivers.py b/sapl/base/receivers.py new file mode 100644 index 000000000..b2176be14 --- /dev/null +++ b/sapl/base/receivers.py @@ -0,0 +1,42 @@ +from django.db.models.signals import post_delete, post_save +from django.dispatch import receiver + +from sapl.materia.models import Tramitacao +from sapl.protocoloadm.models import TramitacaoAdministrativo +from sapl.base.signals import tramitacao_signal +from sapl.utils import get_base_url + +from sapl.base.email_utils import do_envia_email_tramitacao + + +@receiver(tramitacao_signal) +def handle_tramitacao_signal(sender, **kwargs): + tramitacao = kwargs.get("post") + request = kwargs.get("request") + if 'protocoloadm' in str(sender): + doc_mat = tramitacao.documento + tipo = "documento" + elif 'materia' in str(sender): + tipo = "materia" + doc_mat = tramitacao.materia + + do_envia_email_tramitacao( + get_base_url(request), + tipo, + doc_mat, + tramitacao.status, + tramitacao.unidade_tramitacao_destino) + + +@receiver(post_delete) +def status_tramitacao_materia(sender, instance, **kwargs): + if isinstance(sender, TramitacaoAdministrativo): + if instance.status.indicador == 'F': + materia = instance.materia + materia.em_tramitacao = True + materia.save() + elif isinstance(sender, TramitacaoAdministrativo): + if instance.status.indicador == 'F': + documento = instance.documento + documento.tramitacao = True + documento.save() diff --git a/sapl/materia/signals.py b/sapl/base/signals.py similarity index 100% rename from sapl/materia/signals.py rename to sapl/base/signals.py diff --git a/sapl/materia/apps.py b/sapl/materia/apps.py index bb4f72f73..2cc3559ae 100644 --- a/sapl/materia/apps.py +++ b/sapl/materia/apps.py @@ -8,4 +8,4 @@ class AppConfig(apps.AppConfig): verbose_name = _('Matéria') def ready(self): - from . import receivers + from sapl.base import receivers diff --git a/sapl/materia/receivers.py b/sapl/materia/receivers.py deleted file mode 100644 index 945c6636e..000000000 --- a/sapl/materia/receivers.py +++ /dev/null @@ -1,29 +0,0 @@ -from django.db.models.signals import post_delete, post_save -from django.dispatch import receiver - -from sapl.materia.models import Tramitacao -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) - - -@receiver(post_delete, sender=Tramitacao) -def status_tramitacao_materia(sender, instance, **kwargs): - if instance.status.indicador == 'F': - materia = instance.materia - materia.em_tramitacao = True - materia.save() diff --git a/sapl/materia/tests/test_email_templates.py b/sapl/materia/tests/test_email_templates.py index aac13cbb7..32b2f7ec8 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.email_utils import enviar_emails, load_email_templates +from sapl.base.email_utils import enviar_emails, load_email_templates def test_email_template_loading(): diff --git a/sapl/materia/views.py b/sapl/materia/views.py index 9e738dbe9..767726a97 100644 --- a/sapl/materia/views.py +++ b/sapl/materia/views.py @@ -46,7 +46,7 @@ from sapl.utils import (YES_NO_CHOICES, autor_label, autor_modal, get_mime_type_from_file_extension, montar_row_autor, show_results_filter_set) -from .email_utils import do_envia_email_confirmacao +from sapl.base.email_utils import do_envia_email_confirmacao from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm, AdicionarVariasAutoriasFilterSet, DespachoInicialForm, DocumentoAcessorioForm, EtiquetaPesquisaForm, @@ -65,7 +65,7 @@ from .models import (AcompanhamentoMateria, Anexada, AssuntoMateria, Autoria, RegimeTramitacao, Relatoria, StatusTramitacao, TipoDocumento, TipoFimRelatoria, TipoMateriaLegislativa, TipoProposicao, Tramitacao, UnidadeTramitacao) -from .signals import tramitacao_signal +from sapl.base.signals import tramitacao_signal AssuntoMateriaCrud = CrudAux.build(AssuntoMateria, 'assunto_materia') @@ -1114,7 +1114,7 @@ class TramitacaoCrud(MasterDetailCrud): msg = _('Tramitação criada, mas e-mail de acompanhamento ' 'de matéria não enviado. Há problemas na configuração ' 'do e-mail.') - messages.add_message(self.request, messages.ERROR, msg) + messages.add_message(self.request, messages.WARNING, msg) return HttpResponseRedirect(self.get_success_url()) return super().form_valid(form) @@ -1141,7 +1141,7 @@ class TramitacaoCrud(MasterDetailCrud): msg = _('Tramitação atualizada, mas e-mail de acompanhamento ' 'de matéria não enviado. Há problemas na configuração ' 'do e-mail.') - messages.add_message(self.request, messages.ERROR, msg) + messages.add_message(self.request, messages.WARNING, msg) return HttpResponseRedirect(self.get_success_url()) return super().form_valid(form) @@ -1683,6 +1683,7 @@ class AcompanhamentoMateriaView(CreateView): do_envia_email_confirmacao(base_url, casa, + "materia", materia, destinatario) @@ -1876,9 +1877,9 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView): flag_error = True if flag_error: msg = _('Tramitação criada, mas e-mail de acompanhamento ' - 'de matéria não enviado. Há problemas na configuração ' - 'do e-mail.') - messages.add_message(self.request, messages.ERROR, msg) + 'de matéria não enviado. A não configuração do servidor de e-mail ' + 'impede o envio de aviso de tramitação') + messages.add_message(self.request, messages.WARNING, msg) status = StatusTramitacao.objects.get(id=request.POST['status']) diff --git a/sapl/protocoloadm/apps.py b/sapl/protocoloadm/apps.py index 8697e58d9..98e28ea36 100644 --- a/sapl/protocoloadm/apps.py +++ b/sapl/protocoloadm/apps.py @@ -6,3 +6,6 @@ class AppConfig(apps.AppConfig): name = 'sapl.protocoloadm' label = 'protocoloadm' verbose_name = _('Protocolo Administrativo') + + def ready(self): + from sapl.base import receivers diff --git a/sapl/protocoloadm/forms.py b/sapl/protocoloadm/forms.py index 9985f522f..8c441ad93 100644 --- a/sapl/protocoloadm/forms.py +++ b/sapl/protocoloadm/forms.py @@ -2,7 +2,7 @@ import django_filters from crispy_forms.bootstrap import InlineRadios from crispy_forms.helper import FormHelper -from crispy_forms.layout import HTML, Button, Fieldset, Layout +from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout from django import forms from django.core.exceptions import (MultipleObjectsReturned, ObjectDoesNotExist, ValidationError) @@ -19,7 +19,8 @@ from sapl.materia.models import (MateriaLegislativa, TipoMateriaLegislativa, from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, AnoNumeroOrderingFilter, RangeWidgetOverride, autor_label, autor_modal) -from .models import (DocumentoAcessorioAdministrativo, DocumentoAdministrativo, +from .models import (AcompanhamentoDocumento, DocumentoAcessorioAdministrativo, + DocumentoAdministrativo, Protocolo, TipoDocumentoAdministrativo, TramitacaoAdministrativo) @@ -39,6 +40,28 @@ EM_TRAMITACAO = [('', '---------'), (0, 'Sim'), (1, 'Não')] +class AcompanhamentoDocumentoForm(ModelForm): + + class Meta: + model = AcompanhamentoDocumento + fields = ['email'] + + def __init__(self, *args, **kwargs): + + row1 = to_row([('email', 10)]) + + row1.append( + Column(form_actions(label='Cadastrar'), css_class='col-md-2') + ) + + self.helper = FormHelper() + self.helper.layout = Layout( + Fieldset( + _('Acompanhamento de Documento por e-mail'), row1 + ) + ) + super(AcompanhamentoDocumentoForm, self).__init__(*args, **kwargs) + class ProtocoloFilterSet(django_filters.FilterSet): diff --git a/sapl/protocoloadm/migrations/0008_acompanhamentodocumento.py b/sapl/protocoloadm/migrations/0008_acompanhamentodocumento.py new file mode 100644 index 000000000..dcc25ed45 --- /dev/null +++ b/sapl/protocoloadm/migrations/0008_acompanhamentodocumento.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2018-09-27 15:24 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('protocoloadm', '0007_auto_20180924_1724'), + ] + + operations = [ + migrations.CreateModel( + name='AcompanhamentoDocumento', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('usuario', models.CharField(max_length=50)), + ('email', models.EmailField(max_length=100, verbose_name='E-mail')), + ('data_cadastro', models.DateField(auto_now_add=True)), + ('hash', models.CharField(max_length=8)), + ('confirmado', models.BooleanField(default=False)), + ('documento', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='protocoloadm.DocumentoAdministrativo')), + ], + options={ + 'verbose_name_plural': 'Acompanhamentos de Documento', + 'verbose_name': 'Acompanhamento de Documento', + }, + ), + ] diff --git a/sapl/protocoloadm/models.py b/sapl/protocoloadm/models.py index 52bd9ad22..908aac542 100644 --- a/sapl/protocoloadm/models.py +++ b/sapl/protocoloadm/models.py @@ -298,3 +298,30 @@ class TramitacaoAdministrativo(models.Model): return _('%(documento)s - %(status)s') % { 'documento': self.documento, 'status': self.status } + +@reversion.register() +class AcompanhamentoDocumento(models.Model): + usuario = models.CharField(max_length=50) + documento = models.ForeignKey(DocumentoAdministrativo, on_delete=models.CASCADE) + email = models.EmailField( + max_length=100, verbose_name=_('E-mail')) + data_cadastro = models.DateField(auto_now_add=True) + hash = models.CharField(max_length=8) + confirmado = models.BooleanField(default=False) + + class Meta: + verbose_name = _('Acompanhamento de Documento') + verbose_name_plural = _('Acompanhamentos de Documento') + + def __str__(self): + if self.data_cadastro is None: + return _('%(documento)s - %(email)s') % { + 'documento': self.documento, + 'email': self.email + } + else: + return _('%(documento)s - %(email)s - Registrado em: %(data)s') % { + 'documento': self.documento, + 'email': self.email, + 'data': str(self.data_cadastro.strftime('%d/%m/%Y')) + } diff --git a/sapl/protocoloadm/tests/test_docadm_email_templates.py b/sapl/protocoloadm/tests/test_docadm_email_templates.py new file mode 100644 index 000000000..ec79098fa --- /dev/null +++ b/sapl/protocoloadm/tests/test_docadm_email_templates.py @@ -0,0 +1,65 @@ +from django.core import mail + +from sapl.base.email_utils import enviar_emails, load_email_templates + + +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 + + +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": "Assunto de teste", + "data": "25/02/2016", + "status": "Arquivado", + "texto_acao": "Deliberado", + "hash_txt": "abc01f", + "materia_id": "794", + "base_url": "http://localhost:8000", + "materia_url": + "/docadm/764/acompanhar-documento", + "excluir_url": + "/docadm/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_emails('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_emails('test@sapl.com', recipients, [messages[0]]) + assert len(mail.outbox) == 1 diff --git a/sapl/protocoloadm/urls.py b/sapl/protocoloadm/urls.py index 8ed9216f2..67d5edb65 100644 --- a/sapl/protocoloadm/urls.py +++ b/sapl/protocoloadm/urls.py @@ -1,6 +1,9 @@ from django.conf.urls import include, url -from sapl.protocoloadm.views import (AnularProtocoloAdmView, +from sapl.protocoloadm.views import (AcompanhamentoDocumentoView, + AcompanhamentoConfirmarView, + AcompanhamentoExcluirView, + AnularProtocoloAdmView, ComprovanteProtocoloView, CriarDocumentoProtocolo, DocumentoAcessorioAdministrativoCrud, @@ -56,6 +59,15 @@ urlpatterns_protocolo = [ url(r'^protocoloadm/(?PRegistramos seu pedido para acompanhamento por e-mail do documento administrativo identificado a seguir:
+{{documento}} - {{descricao_documento}}Para garantia de sua privacidade, solicitamos que ative o recebimento das futuras mensagens clicando no link:
+ +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/sapl/templates/email/tramitacao.txt b/sapl/templates/email/tramitacao.txt index f0a06687a..bb68a87a0 100644 --- a/sapl/templates/email/tramitacao.txt +++ b/sapl/templates/email/tramitacao.txt @@ -8,12 +8,12 @@ A seguinte matéria, de seu interesse, sofreu Tramitação registrada em {{data_ Matéria: {{materia}} - {{descricao_materia}} {{url_materia}} - +{% if autoria %} Autoria: {% for autor in autoria %} {{ autor }} {% endfor %} - +{% endif %} Data da ação: {{data}} Status: {{status}} diff --git a/sapl/templates/protocoloadm/acompanhamento_documento.html b/sapl/templates/protocoloadm/acompanhamento_documento.html new file mode 100644 index 000000000..b6526cc52 --- /dev/null +++ b/sapl/templates/protocoloadm/acompanhamento_documento.html @@ -0,0 +1,21 @@ +{% extends "crud/detail.html" %} +{% load i18n %} +{% load crispy_forms_tags %} +{% block actions %} {% endblock %} +{% block detail_content %} + +