Browse Source

Fix #2252 Acompanhamento de Documento Administrativo (#2257)

* Fix #2252

* Muda mensagens de ERROR para WARNING in quando da erro no envio do email na tramitação

* Muda mensagens de ERROR para WARNING in quando da erro no envio do email na tramitação

* Fixing travis errors

* Fix tests

* Update acompanhar_documento.txt

* Update email_utils.py

* Update receivers.py

* Update test_email_templates.py

* adicionados testes de template dos email de acompanhamento

* tirar duplicidade
pull/2299/head
Talitha Pumar 6 years ago
committed by Edward
parent
commit
b6b3aec188
  1. 126
      sapl/base/email_utils.py
  2. 42
      sapl/base/receivers.py
  3. 0
      sapl/base/signals.py
  4. 2
      sapl/materia/apps.py
  5. 29
      sapl/materia/receivers.py
  6. 2
      sapl/materia/tests/test_email_templates.py
  7. 15
      sapl/materia/views.py
  8. 3
      sapl/protocoloadm/apps.py
  9. 27
      sapl/protocoloadm/forms.py
  10. 32
      sapl/protocoloadm/migrations/0008_acompanhamentodocumento.py
  11. 27
      sapl/protocoloadm/models.py
  12. 65
      sapl/protocoloadm/tests/test_docadm_email_templates.py
  13. 14
      sapl/protocoloadm/urls.py
  14. 184
      sapl/protocoloadm/views.py
  15. 1
      sapl/rules/map_rules.py
  16. 4
      sapl/rules/tests/test_rules.py
  17. 25
      sapl/templates/email/acompanhar_documento.html
  18. 16
      sapl/templates/email/acompanhar_documento.txt
  19. 2
      sapl/templates/email/tramitacao.html
  20. 4
      sapl/templates/email/tramitacao.txt
  21. 21
      sapl/templates/protocoloadm/acompanhamento_documento.html
  22. 3
      sapl/templates/protocoloadm/documentoadministrativo_filter.html

126
sapl/materia/email_utils.py → sapl/base/email_utils.py

@ -8,7 +8,8 @@ from django.utils import timezone
from sapl.base.models import CasaLegislativa from sapl.base.models import CasaLegislativa
from sapl.settings import EMAIL_SEND_USER 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={}): def load_email_templates(templates, context={}):
@ -61,56 +62,73 @@ def enviar_emails(sender, recipients, messages):
fail_silently=False) 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: if not casa_legislativa:
raise ValueError("Casa Legislativa é obrigatória") raise ValueError("Casa Legislativa é obrigatória")
if not materia: if not doc_mat:
raise ValueError("Matéria é obrigatória") if tipo == "materia":
msg = "Matéria é obrigatória"
else:
msg = "Documento é obrigatório"
raise ValueError(msg)
# FIXME i18n # FIXME i18n
casa_nome = (casa_legislativa.nome + ' de ' + casa_nome = ("{} de {} - {}".format(casa_legislativa.nome,
casa_legislativa.municipio + '-' + casa_legislativa.municipio,
casa_legislativa.uf) casa_legislativa.uf))
materia_url = reverse('sapl.materia:materialegislativa_detail', if tipo == "materia":
kwargs={'pk': materia.id}) doc_mat_url = reverse('sapl.materia:materialegislativa_detail',
kwargs={'pk': doc_mat.id})
confirmacao_url = reverse('sapl.materia:acompanhar_confirmar', confirmacao_url = reverse('sapl.materia:acompanhar_confirmar',
kwargs={'pk': materia.id}) 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 = ""
autores = []
for autoria in materia.autoria_set.all():
autores.append(autoria.autor.nome)
templates = load_email_templates(['email/acompanhar.txt', templates = load_email_templates(['email/acompanhar.txt',
'email/acompanhar.html'], 'email/acompanhar.html'],
{"casa_legislativa": casa_nome, {"casa_legislativa": casa_nome,
"logotipo": casa_legislativa.logotipo, "logotipo": casa_legislativa.logotipo,
"descricao_materia": materia.ementa, "descricao_materia": ementa,
"autoria": autores, "autoria": autores,
"hash_txt": hash_txt, "hash_txt": hash_txt,
"base_url": base_url, "base_url": base_url,
"materia": str(materia), "materia": str(doc_mat),
"materia_url": materia_url, "materia_url": doc_mat_url,
"confirmacao_url": confirmacao_url, }) "confirmacao_url": confirmacao_url, })
return templates 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 # Envia email de confirmacao para atualizações de tramitação
# #
sender = EMAIL_SEND_USER sender = EMAIL_SEND_USER
# FIXME i18n # 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 = [] messages = []
recipients = [] recipients = []
email_texts = criar_email_confirmacao(base_url, email_texts = criar_email_confirmacao(base_url,
casa, casa,
materia, doc_mat,
tipo,
destinatario.hash,) destinatario.hash,)
recipients.append(destinatario.email) recipients.append(destinatario.email)
messages.append({ messages.append({
@ -123,30 +141,41 @@ def do_envia_email_confirmacao(base_url, casa, materia, destinatario):
enviar_emails(sender, recipients, messages) 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=''): unidade_destino, hash_txt=''):
if not casa_legislativa: if not casa_legislativa:
raise ValueError("Casa Legislativa é obrigatória") raise ValueError("Casa Legislativa é obrigatória")
if not materia: if not doc_mat:
raise ValueError("Matéria é obrigatória") if tipo == "materia":
msg = "Matéria é obrigatória"
else:
msg = "Documento é obrigatório"
raise ValueError(msg)
# FIXME i18n # FIXME i18n
casa_nome = (casa_legislativa.nome + ' de ' + casa_nome = ("{} de {} - {}".format(casa_legislativa.nome,
casa_legislativa.municipio + '-' + casa_legislativa.municipio,
casa_legislativa.uf) casa_legislativa.uf))
if tipo == "materia":
url_materia = reverse('sapl.materia:tramitacao_list', doc_mat_url = reverse('sapl.materia:tramitacao_list',
kwargs={'pk': materia.id}) kwargs={'pk': doc_mat.id})
url_excluir = reverse('sapl.materia:acompanhar_excluir', url_excluir = reverse('sapl.materia:acompanhar_excluir',
kwargs={'pk': materia.id}) kwargs={'pk': doc_mat.id})
autores = [] ementa = doc_mat.ementa
for autoria in materia.autoria_set.all(): autores = [autoria.autor.nome for autoria in doc_mat.autoria_set.all()]
autores.append(autoria.autor.nome) tramitacao = doc_mat.tramitacao_set.last()
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', templates = load_email_templates(['email/tramitacao.txt',
'email/tramitacao.html'], 'email/tramitacao.html'],
@ -154,34 +183,42 @@ def criar_email_tramitacao(base_url, casa_legislativa, materia, status,
"data_registro": dt.strftime( "data_registro": dt.strftime(
timezone.now(), timezone.now(),
"%d/%m/%Y"), "%d/%m/%Y"),
"cod_materia": materia.id, "cod_materia": doc_mat.id,
"logotipo": casa_legislativa.logotipo, "logotipo": casa_legislativa.logotipo,
"descricao_materia": materia.ementa, "descricao_materia": ementa,
"autoria": autores, "autoria": autores,
"data": tramitacao.data_tramitacao, "data": tramitacao.data_tramitacao,
"status": status, "status": status,
"localizacao": unidade_destino, "localizacao": unidade_destino,
"texto_acao": tramitacao.texto, "texto_acao": tramitacao.texto,
"hash_txt": hash_txt, "hash_txt": hash_txt,
"materia": str(materia), "materia": str(doc_mat),
"base_url": base_url, "base_url": base_url,
"materia_url": url_materia, "materia_url": doc_mat_url,
"excluir_url": url_excluir}) "excluir_url": url_excluir})
return templates 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 # Envia email de tramitacao para usuarios cadastrados
# #
destinatarios = AcompanhamentoMateria.objects.filter(materia=materia, if tipo == "materia":
destinatarios = AcompanhamentoMateria.objects.filter(materia=doc_mat,
confirmado=True) confirmado=True)
else:
destinatarios = AcompanhamentoDocumento.objects.filter(documento=doc_mat,
confirmado=True)
casa = CasaLegislativa.objects.first() casa = CasaLegislativa.objects.first()
sender = EMAIL_SEND_USER sender = EMAIL_SEND_USER
# FIXME i18n # FIXME i18nn
subject = "[SAPL] " + str(materia) + \ if tipo == "materia":
" - Acompanhamento de Materia Legislativa" msg = " - Acompanhamento de Matéria Legislativa"
else:
msg = " - Acompanhamento de Documento"
subject = "[SAPL] {} {}".format(str(doc_mat), msg)
connection = get_connection() connection = get_connection()
connection.open() connection.open()
@ -190,10 +227,11 @@ def do_envia_email_tramitacao(base_url, materia, status, unidade_destino):
try: try:
email_texts = criar_email_tramitacao(base_url, email_texts = criar_email_tramitacao(base_url,
casa, casa,
materia, tipo,
doc_mat,
status, status,
unidade_destino, unidade_destino,
destinatario.hash,) destinatario.hash)
email = EmailMultiAlternatives( email = EmailMultiAlternatives(
subject, subject,

42
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()

0
sapl/materia/signals.py → sapl/base/signals.py

2
sapl/materia/apps.py

@ -8,4 +8,4 @@ class AppConfig(apps.AppConfig):
verbose_name = _('Matéria') verbose_name = _('Matéria')
def ready(self): def ready(self):
from . import receivers from sapl.base import receivers

29
sapl/materia/receivers.py

@ -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()

2
sapl/materia/tests/test_email_templates.py

@ -1,6 +1,6 @@
from django.core import mail 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(): def test_email_template_loading():

15
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, get_mime_type_from_file_extension, montar_row_autor,
show_results_filter_set) 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, from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm,
AdicionarVariasAutoriasFilterSet, DespachoInicialForm, AdicionarVariasAutoriasFilterSet, DespachoInicialForm,
DocumentoAcessorioForm, EtiquetaPesquisaForm, DocumentoAcessorioForm, EtiquetaPesquisaForm,
@ -65,7 +65,7 @@ from .models import (AcompanhamentoMateria, Anexada, AssuntoMateria, Autoria,
RegimeTramitacao, Relatoria, StatusTramitacao, RegimeTramitacao, Relatoria, StatusTramitacao,
TipoDocumento, TipoFimRelatoria, TipoMateriaLegislativa, TipoDocumento, TipoFimRelatoria, TipoMateriaLegislativa,
TipoProposicao, Tramitacao, UnidadeTramitacao) TipoProposicao, Tramitacao, UnidadeTramitacao)
from .signals import tramitacao_signal from sapl.base.signals import tramitacao_signal
AssuntoMateriaCrud = CrudAux.build(AssuntoMateria, 'assunto_materia') AssuntoMateriaCrud = CrudAux.build(AssuntoMateria, 'assunto_materia')
@ -1114,7 +1114,7 @@ class TramitacaoCrud(MasterDetailCrud):
msg = _('Tramitação criada, mas e-mail de acompanhamento ' msg = _('Tramitação criada, mas e-mail de acompanhamento '
'de matéria não enviado. Há problemas na configuração ' 'de matéria não enviado. Há problemas na configuração '
'do e-mail.') '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 HttpResponseRedirect(self.get_success_url())
return super().form_valid(form) return super().form_valid(form)
@ -1141,7 +1141,7 @@ class TramitacaoCrud(MasterDetailCrud):
msg = _('Tramitação atualizada, mas e-mail de acompanhamento ' msg = _('Tramitação atualizada, mas e-mail de acompanhamento '
'de matéria não enviado. Há problemas na configuração ' 'de matéria não enviado. Há problemas na configuração '
'do e-mail.') '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 HttpResponseRedirect(self.get_success_url())
return super().form_valid(form) return super().form_valid(form)
@ -1683,6 +1683,7 @@ class AcompanhamentoMateriaView(CreateView):
do_envia_email_confirmacao(base_url, do_envia_email_confirmacao(base_url,
casa, casa,
"materia",
materia, materia,
destinatario) destinatario)
@ -1876,9 +1877,9 @@ class PrimeiraTramitacaoEmLoteView(PermissionRequiredMixin, FilterView):
flag_error = True flag_error = True
if flag_error: if flag_error:
msg = _('Tramitação criada, mas e-mail de acompanhamento ' msg = _('Tramitação criada, mas e-mail de acompanhamento '
'de matéria não enviado. Há problemas na configuração ' 'de matéria não enviado. A não configuração do servidor de e-mail '
'do e-mail.') 'impede o envio de aviso de tramitação')
messages.add_message(self.request, messages.ERROR, msg) messages.add_message(self.request, messages.WARNING, msg)
status = StatusTramitacao.objects.get(id=request.POST['status']) status = StatusTramitacao.objects.get(id=request.POST['status'])

3
sapl/protocoloadm/apps.py

@ -6,3 +6,6 @@ class AppConfig(apps.AppConfig):
name = 'sapl.protocoloadm' name = 'sapl.protocoloadm'
label = 'protocoloadm' label = 'protocoloadm'
verbose_name = _('Protocolo Administrativo') verbose_name = _('Protocolo Administrativo')
def ready(self):
from sapl.base import receivers

27
sapl/protocoloadm/forms.py

@ -2,7 +2,7 @@
import django_filters import django_filters
from crispy_forms.bootstrap import InlineRadios from crispy_forms.bootstrap import InlineRadios
from crispy_forms.helper import FormHelper 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 import forms
from django.core.exceptions import (MultipleObjectsReturned, from django.core.exceptions import (MultipleObjectsReturned,
ObjectDoesNotExist, ValidationError) ObjectDoesNotExist, ValidationError)
@ -19,7 +19,8 @@ from sapl.materia.models import (MateriaLegislativa, TipoMateriaLegislativa,
from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, AnoNumeroOrderingFilter, from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, AnoNumeroOrderingFilter,
RangeWidgetOverride, autor_label, autor_modal) RangeWidgetOverride, autor_label, autor_modal)
from .models import (DocumentoAcessorioAdministrativo, DocumentoAdministrativo, from .models import (AcompanhamentoDocumento, DocumentoAcessorioAdministrativo,
DocumentoAdministrativo,
Protocolo, TipoDocumentoAdministrativo, Protocolo, TipoDocumentoAdministrativo,
TramitacaoAdministrativo) TramitacaoAdministrativo)
@ -39,6 +40,28 @@ EM_TRAMITACAO = [('', '---------'),
(0, 'Sim'), (0, 'Sim'),
(1, 'Não')] (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): class ProtocoloFilterSet(django_filters.FilterSet):

32
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',
},
),
]

27
sapl/protocoloadm/models.py

@ -298,3 +298,30 @@ class TramitacaoAdministrativo(models.Model):
return _('%(documento)s - %(status)s') % { return _('%(documento)s - %(status)s') % {
'documento': self.documento, 'status': self.status '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'))
}

65
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 = "<html><body>Hello Django</body></html>"
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': '<html></html>',
} 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': '<html></html>',
} 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

14
sapl/protocoloadm/urls.py

@ -1,6 +1,9 @@
from django.conf.urls import include, url from django.conf.urls import include, url
from sapl.protocoloadm.views import (AnularProtocoloAdmView, from sapl.protocoloadm.views import (AcompanhamentoDocumentoView,
AcompanhamentoConfirmarView,
AcompanhamentoExcluirView,
AnularProtocoloAdmView,
ComprovanteProtocoloView, ComprovanteProtocoloView,
CriarDocumentoProtocolo, CriarDocumentoProtocolo,
DocumentoAcessorioAdministrativoCrud, DocumentoAcessorioAdministrativoCrud,
@ -56,6 +59,15 @@ urlpatterns_protocolo = [
url(r'^protocoloadm/(?P<pk>\d+)/protocolo-mostrar$', url(r'^protocoloadm/(?P<pk>\d+)/protocolo-mostrar$',
ProtocoloMostrarView.as_view(), name='protocolo_mostrar'), ProtocoloMostrarView.as_view(), name='protocolo_mostrar'),
url(r'^docadm/(?P<pk>\d+)/acompanhar-documento/$',
AcompanhamentoDocumentoView.as_view(), name='acompanhar_documento'),
url(r'^docadm/(?P<pk>\d+)/acompanhar-confirmar$',
AcompanhamentoConfirmarView.as_view(),
name='acompanhar_confirmar'),
url(r'^docadm/(?P<pk>\d+)/acompanhar-excluir$',
AcompanhamentoExcluirView.as_view(),
name='acompanhar_excluir'),
url(r'^protocoloadm/(?P<pk>\d+)/continuar$', url(r'^protocoloadm/(?P<pk>\d+)/continuar$',

184
sapl/protocoloadm/views.py

@ -1,3 +1,6 @@
from datetime import datetime
from random import choice
from string import ascii_letters, digits
from braces.views import FormValidMessageMixin from braces.views import FormValidMessageMixin
from django.contrib import messages from django.contrib import messages
@ -18,25 +21,30 @@ from django.views.generic.edit import FormView
from django_filters.views import FilterView from django_filters.views import FilterView
import sapl import sapl
from sapl.base.models import Autor from sapl.base.models import Autor, CasaLegislativa
from sapl.comissoes.models import Comissao from sapl.comissoes.models import Comissao
from sapl.crud.base import Crud, CrudAux, MasterDetailCrud, make_pagination from sapl.crud.base import Crud, CrudAux, MasterDetailCrud, make_pagination
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa
from sapl.parlamentares.models import Legislatura, Parlamentar from sapl.parlamentares.models import Legislatura, Parlamentar
from sapl.protocoloadm.models import Protocolo from sapl.protocoloadm.models import Protocolo
from sapl.utils import (create_barcode, get_client_ip, from sapl.utils import (create_barcode, get_base_url, get_client_ip,
get_mime_type_from_file_extension, get_mime_type_from_file_extension,
show_results_filter_set) show_results_filter_set)
from sapl.base.email_utils import do_envia_email_confirmacao
from .forms import (AnularProcoloAdmForm, DocumentoAcessorioAdministrativoForm, from .forms import (AcompanhamentoDocumentoForm, AnularProcoloAdmForm,
DocumentoAcessorioAdministrativoForm,
DocumentoAdministrativoFilterSet, DocumentoAdministrativoFilterSet,
DocumentoAdministrativoForm, ProtocoloDocumentForm, DocumentoAdministrativoForm, ProtocoloDocumentForm,
ProtocoloFilterSet, ProtocoloMateriaForm, ProtocoloFilterSet, ProtocoloMateriaForm,
TramitacaoAdmEditForm, TramitacaoAdmForm, DesvincularDocumentoForm, DesvincularMateriaForm, TramitacaoAdmEditForm, TramitacaoAdmForm,
filtra_tramitacao_adm_destino_and_status, filtra_tramitacao_adm_destino, filtra_tramitacao_adm_status) DesvincularDocumentoForm, DesvincularMateriaForm,
from .models import (DocumentoAcessorioAdministrativo, DocumentoAdministrativo, filtra_tramitacao_adm_destino_and_status,
StatusTramitacaoAdministrativo, filtra_tramitacao_adm_destino, filtra_tramitacao_adm_status)
from .models import (AcompanhamentoDocumento, DocumentoAcessorioAdministrativo,
DocumentoAdministrativo, StatusTramitacaoAdministrativo,
TipoDocumentoAdministrativo, TramitacaoAdministrativo) TipoDocumentoAdministrativo, TramitacaoAdministrativo)
from sapl.base.signals import tramitacao_signal
TipoDocumentoAdministrativoCrud = CrudAux.build( TipoDocumentoAdministrativoCrud = CrudAux.build(
TipoDocumentoAdministrativo, '') TipoDocumentoAdministrativo, '')
@ -89,6 +97,136 @@ def doc_texto_integral(request, pk):
return response return response
raise Http404 raise Http404
class AcompanhamentoConfirmarView(TemplateView):
def get_redirect_url(self, email):
msg = _('Este documento está sendo acompanhado pelo e-mail: %s') % (
email)
messages.add_message(self.request, messages.SUCCESS, msg)
return reverse('sapl.protocoloadm:documentoadministrativo_detail',
kwargs={'pk': self.kwargs['pk']})
def get(self, request, *args, **kwargs):
documento_id = kwargs['pk']
hash_txt = request.GET.get('hash_txt', '')
try:
acompanhar = AcompanhamentoDocumento.objects.get(
documento_id=documento_id,
hash=hash_txt)
except ObjectDoesNotExist:
raise Http404()
# except MultipleObjectsReturned:
# A melhor solução deve ser permitir que a exceção
# (MultipleObjectsReturned) seja lançada e vá para o log,
# pois só poderá ser causada por um erro de desenvolvimente
acompanhar.confirmado = True
acompanhar.save()
return HttpResponseRedirect(self.get_redirect_url(acompanhar.email))
class AcompanhamentoExcluirView(TemplateView):
def get_success_url(self):
msg = _('Você parou de acompanhar este Documento.')
messages.add_message(self.request, messages.INFO, msg)
return reverse('sapl.protocoloadm:documentoadministrativo_detail',
kwargs={'pk': self.kwargs['pk']})
def get(self, request, *args, **kwargs):
materia_id = kwargs['pk']
hash_txt = request.GET.get('hash_txt', '')
try:
AcompanhamentoDocumento.objects.get(documento_id=documento_id,
hash=hash_txt).delete()
except ObjectDoesNotExist:
pass
return HttpResponseRedirect(self.get_success_url())
class AcompanhamentoDocumentoView(CreateView):
template_name = "protocoloadm/acompanhamento_documento.html"
def get_random_chars(self):
s = ascii_letters + digits
return ''.join(choice(s) for i in range(choice([6, 7])))
def get(self, request, *args, **kwargs):
pk = self.kwargs['pk']
documento = DocumentoAdministrativo.objects.get(id=pk)
return self.render_to_response(
{'form': AcompanhamentoDocumentoForm(),
'documento': documento})
def post(self, request, *args, **kwargs):
form = AcompanhamentoDocumentoForm(request.POST)
pk = self.kwargs['pk']
documento = DocumentoAdministrativo.objects.get(id=pk)
if form.is_valid():
email = form.cleaned_data['email']
usuario = request.user
hash_txt = self.get_random_chars()
acompanhar = AcompanhamentoDocumento.objects.get_or_create(
documento=documento,
email=form.data['email'])
# Se o segundo elemento do retorno do get_or_create for True
# quer dizer que o elemento não existia
if acompanhar[1]:
acompanhar = acompanhar[0]
acompanhar.hash = hash_txt
acompanhar.usuario = usuario.username
acompanhar.confirmado = False
acompanhar.save()
base_url = get_base_url(request)
destinatario = AcompanhamentoDocumento.objects.get(
documento=documento,
email=email,
confirmado=False)
casa = CasaLegislativa.objects.first()
do_envia_email_confirmacao(base_url,
casa,
"documento",
documento,
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 deste documento.')
messages.add_message(request, messages.SUCCESS, msg)
# Caso esse Acompanhamento já exista
# avisa ao usuário que esse documento já está sendo acompanhado
else:
msg = _('Este e-mail já está acompanhando esse documento.')
messages.add_message(request, messages.INFO, msg)
return self.render_to_response(
{'form': form,
'documento': documento,
'error': _('Esse documento já está\
sendo acompanhada por este e-mail.')})
return HttpResponseRedirect(self.get_success_url())
else:
return self.render_to_response(
{'form': form,
'documento': documento})
def get_success_url(self):
return reverse('sapl.protocoloadm:documentoadministrativo_detail',
kwargs={'pk': self.kwargs['pk']})
class DocumentoAdministrativoMixin: class DocumentoAdministrativoMixin:
@ -686,8 +824,38 @@ class TramitacaoAdmCrud(MasterDetailCrud):
'unidade_tramitacao_local'].widget.attrs['disabled'] = True 'unidade_tramitacao_local'].widget.attrs['disabled'] = True
return context return context
def form_valid(self, form):
self.object = form.save()
try:
tramitacao_signal.send(sender=TramitacaoAdministrativo,
post=self.object,
request=self.request)
except Exception as e:
# TODO log error
msg = _('Tramitação criada, mas e-mail de acompanhamento '
'de documento 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)
return HttpResponseRedirect(self.get_success_url())
return super().form_valid(form)
class UpdateView(MasterDetailCrud.UpdateView): class UpdateView(MasterDetailCrud.UpdateView):
form_class = TramitacaoAdmEditForm form_class = TramitacaoAdmEditForm
def form_valid(self, form):
self.object = form.save()
try:
tramitacao_signal.send(sender=TramitacaoAdministrativo,
post=self.object,
request=self.request)
except Exception as e:
# TODO log error
msg = _('Tramitação criada, mas e-mail de acompanhamento '
'de documento 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)
return HttpResponseRedirect(self.get_success_url())
return super().form_valid(form)
class ListView(DocumentoAdministrativoMixin, MasterDetailCrud.ListView): class ListView(DocumentoAdministrativoMixin, MasterDetailCrud.ListView):

1
sapl/rules/map_rules.py

@ -302,6 +302,7 @@ rules_group_anonymous = {
'group': SAPL_GROUP_ANONYMOUS, 'group': SAPL_GROUP_ANONYMOUS,
'rules': [ 'rules': [
(materia.AcompanhamentoMateria, [RP_ADD, RP_DELETE]), (materia.AcompanhamentoMateria, [RP_ADD, RP_DELETE]),
(protocoloadm.AcompanhamentoDocumento, [RP_ADD, RP_DELETE]),
] ]
} }

4
sapl/rules/tests/test_rules.py

@ -11,6 +11,7 @@ from sapl.compilacao.models import (PerfilEstruturalTextoArticulado,
TipoDispositivo, TipoDispositivo,
TipoDispositivoRelationship) TipoDispositivoRelationship)
from sapl.materia.models import AcompanhamentoMateria from sapl.materia.models import AcompanhamentoMateria
from sapl.protocoloadm.models import AcompanhamentoDocumento
from sapl.rules import SAPL_GROUPS, map_rules from sapl.rules import SAPL_GROUPS, map_rules
from sapl.test_urls import create_perms_post_migrate from sapl.test_urls import create_perms_post_migrate
from scripts.lista_permissions_in_decorators import \ from scripts.lista_permissions_in_decorators import \
@ -61,6 +62,7 @@ __fp__in__test_permission_of_models_in_rules_patterns = {
PerfilEstruturalTextoArticulado], PerfilEstruturalTextoArticulado],
map_rules.RP_CHANGE: [AcompanhamentoMateria, map_rules.RP_CHANGE: [AcompanhamentoMateria,
AcompanhamentoDocumento,
TipoDispositivo, TipoDispositivo,
TipoDispositivoRelationship, TipoDispositivoRelationship,
PerfilEstruturalTextoArticulado], PerfilEstruturalTextoArticulado],
@ -71,11 +73,13 @@ __fp__in__test_permission_of_models_in_rules_patterns = {
PerfilEstruturalTextoArticulado], PerfilEstruturalTextoArticulado],
map_rules.RP_LIST: [AcompanhamentoMateria, map_rules.RP_LIST: [AcompanhamentoMateria,
AcompanhamentoDocumento,
TipoDispositivo, TipoDispositivo,
TipoDispositivoRelationship, TipoDispositivoRelationship,
PerfilEstruturalTextoArticulado], PerfilEstruturalTextoArticulado],
map_rules.RP_DETAIL: [AcompanhamentoMateria, map_rules.RP_DETAIL: [AcompanhamentoMateria,
AcompanhamentoDocumento,
TipoDispositivo, TipoDispositivo,
TipoDispositivoRelationship, TipoDispositivoRelationship,
PerfilEstruturalTextoArticulado] PerfilEstruturalTextoArticulado]

25
sapl/templates/email/acompanhar_documento.html

@ -0,0 +1,25 @@
{% load i18n %}
{% load static %}
<html><head></head><body bgcolor='#ffffff'>
<h2 align='center'><b>{{casa_legislativa}}</b>
<br/>
Sistema de Apoio ao Processo Legislativo
</h2>
<p>Registramos seu pedido para acompanhamento por e-mail do documento administrativo identificado a seguir:</p>
<a href="{{base_url}}{{documento_url}}">{{documento}}<b> - {{descricao_documento}}</b></a><br/>
{{assunto}}<br/>
</h4>
<p></p>
<p>Para garantia de sua privacidade, solicitamos que ative o recebimento das futuras mensagens clicando no link:</p>
<h4>
<a href="{{base_url}}{{confirmacao_url}}?hash_txt={{hash_txt}}">{{base_url}}{{confirmacao_url}}?hash_txt={{hash_txt}}</a>
</h4>
<br/>
<hr>
<p>Caso não tenha realizado o cadastramento em nosso sistema, favor desconsiderar a presente mensagem<br/>
Esta é uma mensagem automática. Por favor, não responda.</p>
</body>
</html>

16
sapl/templates/email/acompanhar_documento.txt

@ -0,0 +1,16 @@
{{casa_legislativa}}
Sistema de Apoio ao Processo Legislativo
>Registramos seu pedido para acompanhamento por e-mail do documento administrativo identificado a seguir:
{{base_url}}{{documento_url}} - {{documento}} - {{descricao_documento}}
{{assunto}}
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.

2
sapl/templates/email/tramitacao.html

@ -13,10 +13,12 @@
<h4> <h4>
<a href="{{base_url}}{{materia_url}}"><b>{{materia}} - {{descricao_materia}}</b></a> <a href="{{base_url}}{{materia_url}}"><b>{{materia}} - {{descricao_materia}}</b></a>
<br/><br/> <br/><br/>
{% if autoria %}
<b>Autoria:</b></br> <b>Autoria:</b></br>
{% for autor in autoria %} {% for autor in autoria %}
{{ autor }}</br> {{ autor }}</br>
{% endfor %} {% endfor %}
{% endif %}
</h4> </h4>
<p></p> <p></p>
<p> <p>

4
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}} Matéria: {{materia}} - {{descricao_materia}}
{{url_materia}} {{url_materia}}
{% if autoria %}
Autoria: Autoria:
{% for autor in autoria %} {% for autor in autoria %}
{{ autor }} {{ autor }}
{% endfor %} {% endfor %}
{% endif %}
Data da ação: {{data}} Data da ação: {{data}}
Status: {{status}} Status: {{status}}

21
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 %}
<h1>Acompanhamento de Documento</h1>
<hr>
<div class="row">
<div class="col-md-4"><b>Tipo:</b> {{documento.tipo.sigla}} - {{documento.tipo.descricao}}</div>
<div class="col-md-4"><b>Número:</b> {{documento.numero}}</div>
<div class="col-md-4"><b>Ano:</b> {{documento.ano}}</div>
</div>
<div class="row">
<div class="col-md-12"><b>Assunto:</b> {{documento.assunto|safe}}</div>
</div>
{% if error %} <h5 align="center"><font color="#FF0000">{{ error }}</font></h5> {% endif %}
{% crispy form %}
{% endblock %}

3
sapl/templates/protocoloadm/documentoadministrativo_filter.html

@ -63,6 +63,9 @@
{% if d.texto_integral %} {% if d.texto_integral %}
<strong><a href="{{ d.texto_integral.url }}">Texto Integral</a></strong></br> <strong><a href="{{ d.texto_integral.url }}">Texto Integral</a></strong></br>
{% endif %} {% endif %}
{% if d.tramitacao %}
<a href="{% url 'sapl.protocoloadm:acompanhar_documento' d.id %}">Acompanhar Documento</a>
{% endif %}
</td> </td>
</tr> </tr>

Loading…
Cancel
Save