Browse Source

Move acompanhamento de matéria para a app de matéria.

Descarta o model na app sessão em favor do já existente na app matéria.

Adiciona geração de "hash".

Adiciona campo confirmado para Acompanhamento Matéria

Adiciona telas de confirmação e exclusão de acompanhamento matéria

Cria corpo do email

Pequena correção no template HTML

Refatora corpo html do email

Move acompanhamento de matéria para a app de matéria.

Descarta o model na app sessão em favor do já existente na app matéria.

Adiciona suporte ao envio de email para confirmação e alteração de
tramitação

Adiciona testes e refatora funções de envio de email

QA fixes
pull/237/head
Edward Ribeiro 9 years ago
parent
commit
f3d4913725
  1. 2
      comissoes/models.py
  2. 32
      materia/forms.py
  3. 28
      materia/migrations/0016_auto_20160223_0813.py
  4. 19
      materia/migrations/0017_acompanhamentomateria_confirmado.py
  5. 7
      materia/models.py
  6. 19
      materia/urls.py
  7. 314
      materia/views.py
  8. 2
      norma/models.py
  9. 2
      parlamentares/models.py
  10. 2
      protocoloadm/models.py
  11. 9
      sapl/utils.py
  12. 27
      sessao/forms.py
  13. 21
      sessao/migrations/0014_auto_20160223_0813.py
  14. 10
      sessao/models.py
  15. 18
      sessao/urls.py
  16. 58
      sessao/views.py
  17. 27
      templates/email/acompanhar.html
  18. 16
      templates/email/acompanhar.txt
  19. 1
      templates/email/test_tramitacao.html
  20. 38
      templates/email/tramitacao.html
  21. 27
      templates/email/tramitacao.txt
  22. 0
      templates/materia/acompanhamento_materia.html
  23. 74
      templates/materia/test_email_templates.py
  24. 2
      templates/sessao/pauta/expediente.html
  25. 2
      templates/sessao/pauta/ordem.html

2
comissoes/models.py

@ -1,7 +1,7 @@
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from model_utils import Choices
from model_utils import Choices
from parlamentares.models import Parlamentar from parlamentares.models import Parlamentar
from sapl.utils import YES_NO_CHOICES from sapl.utils import YES_NO_CHOICES

32
materia/forms.py

@ -11,10 +11,11 @@ from norma.models import LegislacaoCitada, TipoNormaJuridica
from parlamentares.models import Parlamentar, Partido from parlamentares.models import Parlamentar, Partido
from sapl.layout import form_actions from sapl.layout import form_actions
from .models import (Anexada, Autor, DespachoInicial, DocumentoAcessorio, from .models import (AcompanhamentoMateria, Anexada, Autor, DespachoInicial,
MateriaLegislativa, Numeracao, Origem, Proposicao, DocumentoAcessorio, MateriaLegislativa, Numeracao, Origem,
Relatoria, StatusTramitacao, TipoAutor, TipoDocumento, Proposicao, Relatoria, StatusTramitacao, TipoAutor,
TipoMateriaLegislativa, Tramitacao, UnidadeTramitacao) TipoDocumento, TipoMateriaLegislativa, Tramitacao,
UnidadeTramitacao)
def get_range_anos(): def get_range_anos():
@ -109,6 +110,29 @@ class ProposicaoForm(ModelForm):
*args, **kwargs) *args, **kwargs)
class AcompanhamentoMateriaForm(ModelForm):
class Meta:
model = AcompanhamentoMateria
fields = ['email']
def __init__(self, *args, **kwargs):
row1 = sapl.layout.to_row([('email', 10)])
row1.append(
Column(form_actions(save_label='Cadastrar'), css_class='col-md-2')
)
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(
'Acompanhamento de Matéria por e-mail', row1
)
)
super(AcompanhamentoMateriaForm, self).__init__(*args, **kwargs)
class DocumentoAcessorioForm(ModelForm): class DocumentoAcessorioForm(ModelForm):
tipo = forms.ModelChoiceField( tipo = forms.ModelChoiceField(

28
materia/migrations/0016_auto_20160223_0813.py

@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
import datetime
from django.utils.timezone import utc
class Migration(migrations.Migration):
dependencies = [
('materia', '0015_auto_20160216_1015'),
]
operations = [
migrations.AddField(
model_name='acompanhamentomateria',
name='data_cadastro',
field=models.DateField(auto_now_add=True, default=datetime.datetime(2016, 2, 23, 11, 13, 25, 362112, tzinfo=utc)),
preserve_default=False,
),
migrations.AddField(
model_name='acompanhamentomateria',
name='usuario',
field=models.CharField(max_length=50, default=''),
preserve_default=False,
),
]

19
materia/migrations/0017_acompanhamentomateria_confirmado.py

@ -0,0 +1,19 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('materia', '0016_auto_20160223_0813'),
]
operations = [
migrations.AddField(
model_name='acompanhamentomateria',
name='confirmado',
field=models.BooleanField(default=False),
),
]

7
materia/models.py

@ -1,8 +1,8 @@
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from model_utils import Choices
from comissoes.models import Comissao from comissoes.models import Comissao
from model_utils import Choices
from parlamentares.models import Parlamentar, Partido from parlamentares.models import Parlamentar, Partido
from sapl.utils import YES_NO_CHOICES, xstr from sapl.utils import YES_NO_CHOICES, xstr
@ -133,11 +133,14 @@ class MateriaLegislativa(models.Model):
'tipo': self.tipo, 'numero': self.numero, 'ano': self.ano} 'tipo': self.tipo, 'numero': self.numero, 'ano': self.ano}
class AcompanhamentoMateria(models.Model): # AcompMateria class AcompanhamentoMateria(models.Model):
usuario = models.CharField(max_length=50)
materia = models.ForeignKey(MateriaLegislativa) materia = models.ForeignKey(MateriaLegislativa)
email = models.CharField( email = models.CharField(
max_length=100, verbose_name=_('Endereço de E-mail')) 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)
confirmado = models.BooleanField(default=False)
class Meta: class Meta:
verbose_name = _('Acompanhamento de Matéria') verbose_name = _('Acompanhamento de Matéria')

19
materia/urls.py

@ -1,9 +1,12 @@
from django.conf.urls import include, url from django.conf.urls import include, url
from materia.views import (AutoriaEditView, AutoriaView, from materia.views import (AcompanhamentoConfirmarView,
DespachoInicialEditView, DespachoInicialView, AcompanhamentoExcluirView,
DocumentoAcessorioEditView, DocumentoAcessorioView, AcompanhamentoMateriaView, AutoriaEditView,
FormularioCadastroView, FormularioSimplificadoView, AutoriaView, DespachoInicialEditView,
DespachoInicialView, DocumentoAcessorioEditView,
DocumentoAcessorioView, FormularioCadastroView,
FormularioSimplificadoView,
LegislacaoCitadaEditView, LegislacaoCitadaView, LegislacaoCitadaEditView, LegislacaoCitadaView,
MateriaAnexadaEditView, MateriaAnexadaView, MateriaAnexadaEditView, MateriaAnexadaView,
MateriaLegislativaPesquisaView, MateriaTaView, MateriaLegislativaPesquisaView, MateriaTaView,
@ -96,4 +99,12 @@ urlpatterns = [
MateriaLegislativaPesquisaView.as_view(), name='pesquisar_materia'), MateriaLegislativaPesquisaView.as_view(), name='pesquisar_materia'),
url(r'^materia/pesquisar-materia-list$', url(r'^materia/pesquisar-materia-list$',
PesquisaMateriaListView.as_view(), name='pesquisar_materia_list'), PesquisaMateriaListView.as_view(), name='pesquisar_materia_list'),
url(r'^materia/(?P<pk>\d+)/acompanhar-materia/$',
AcompanhamentoMateriaView.as_view(), name='acompanhar_materia'),
url(r'^materia/(?P<pk>\d+)/acompanhar-confirmar$',
AcompanhamentoConfirmarView.as_view(),
name='acompanhar_confirmar'),
url(r'^materia/(?P<pk>\d+)/acompanhar-excluir$',
AcompanhamentoExcluirView.as_view(),
name='acompanhar_excluir'),
] ]

314
materia/views.py

@ -1,33 +1,39 @@
from datetime import datetime from datetime import datetime
from random import choice
from re import sub from re import sub
from string import ascii_letters, digits
from django.contrib import messages from django.contrib import messages
from django.core.exceptions import ObjectDoesNotExist from django.core.exceptions import ObjectDoesNotExist
from django.core.mail import send_mail from django.core.mail import send_mail
from django.core.urlresolvers import reverse from django.core.urlresolvers import reverse
from django.http.response import HttpResponseRedirect
from django.shortcuts import redirect from django.shortcuts import redirect
from django.template import Context, loader
from django.utils.html import strip_tags from django.utils.html import strip_tags
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.views.generic import ListView from django.views.generic import ListView, TemplateView
from django.views.generic.edit import FormMixin from django.views.generic.edit import FormMixin
from vanilla.views import GenericView from vanilla.views import GenericView
from base.models import CasaLegislativa
from comissoes.models import Comissao, Composicao from comissoes.models import Comissao, Composicao
from compilacao.views import IntegracaoTaView from compilacao.views import IntegracaoTaView
from crud import Crud, make_pagination from crud import Crud, make_pagination
from norma.models import LegislacaoCitada, NormaJuridica, TipoNormaJuridica from norma.models import LegislacaoCitada, NormaJuridica, TipoNormaJuridica
from parlamentares.models import Partido from parlamentares.models import Partido
from sessao.models import AcompanharMateria from sapl.utils import get_base_url
from .forms import (AutoriaForm, DespachoInicialForm, DocumentoAcessorioForm, from .forms import (AcompanhamentoMateriaForm, AutoriaForm,
DespachoInicialForm, DocumentoAcessorioForm,
FormularioCadastroForm, FormularioSimplificadoForm, FormularioCadastroForm, FormularioSimplificadoForm,
LegislacaoCitadaForm, MateriaAnexadaForm, LegislacaoCitadaForm, MateriaAnexadaForm,
MateriaLegislativaPesquisaForm, NumeracaoForm, MateriaLegislativaPesquisaForm, NumeracaoForm,
ProposicaoForm, RelatoriaForm, TramitacaoForm) ProposicaoForm, RelatoriaForm, TramitacaoForm)
from .models import (Anexada, Autor, Autoria, DespachoInicial, from .models import (AcompanhamentoMateria, Anexada, Autor, Autoria,
DocumentoAcessorio, MateriaLegislativa, Numeracao, Orgao, DespachoInicial, DocumentoAcessorio, MateriaLegislativa,
Origem, Proposicao, RegimeTramitacao, Relatoria, Numeracao, Orgao, Origem, Proposicao, RegimeTramitacao,
StatusTramitacao, TipoAutor, TipoDocumento, Relatoria, StatusTramitacao, TipoAutor, TipoDocumento,
TipoFimRelatoria, TipoMateriaLegislativa, TipoProposicao, TipoFimRelatoria, TipoMateriaLegislativa, TipoProposicao,
Tramitacao, UnidadeTramitacao) Tramitacao, UnidadeTramitacao)
@ -757,6 +763,41 @@ class DocumentoAcessorioView(FormMixin, GenericView):
return reverse('documento_acessorio', kwargs={'pk': pk}) return reverse('documento_acessorio', kwargs={'pk': pk})
class AcompanhamentoConfirmarView(TemplateView):
def get_redirect_url(self):
return reverse("sessaoplenaria:list_pauta_sessao")
def get(self, request, *args, **kwargs):
materia_id = kwargs['pk']
hash_txt = request.GET.get('hash_txt', '')
acompanhar = AcompanhamentoMateria.objects.get(materia_id=materia_id,
hash=hash_txt)
acompanhar.confirmado = True
acompanhar.save()
return HttpResponseRedirect(self.get_redirect_url())
class AcompanhamentoExcluirView(TemplateView):
def get_redirect_url(self):
return reverse("sessaoplenaria:list_pauta_sessao")
def get(self, request, *args, **kwargs):
materia_id = kwargs['pk']
hash_txt = request.GET.get('hash_txt', '')
try:
AcompanhamentoMateria.objects.get(materia_id=materia_id,
hash=hash_txt).delete()
except ObjectDoesNotExist:
pass
return HttpResponseRedirect(self.get_redirect_url())
class DocumentoAcessorioEditView(FormMixin, GenericView): class DocumentoAcessorioEditView(FormMixin, GenericView):
template_name = "materia/documento_acessorio_edit.html" template_name = "materia/documento_acessorio_edit.html"
@ -966,6 +1007,195 @@ class RelatoriaView(FormMixin, GenericView):
'parlamentares': parlamentares}) 'parlamentares': parlamentares})
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
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():
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,
"texto_acao":
materia.tramitacao_set.last().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 = '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:
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): class TramitacaoView(FormMixin, GenericView):
template_name = "materia/tramitacao.html" template_name = "materia/tramitacao.html"
@ -1005,16 +1235,8 @@ class TramitacaoView(FormMixin, GenericView):
'object': materia, 'object': materia,
'tramitacoes': tramitacoes_list}) 'tramitacoes': tramitacoes_list})
corpo_email = ('A tramitação da matéria %s foi alterada.' % materia do_envia_email_tramitacao(request, materia)
)
destinatarios = AcompanharMateria.objects.values_list(
'email', flat=True).filter(
materia_cadastrada=materia)
send_mail('Mudança de Tramitação',
corpo_email,
'sapl-test@interlegis.leg.br',
destinatarios,
fail_silently=True)
return self.form_valid(form) return self.form_valid(form)
else: else:
return self.render_to_response({'form': form, return self.render_to_response({'form': form,
@ -1489,3 +1711,61 @@ class MateriaTaView(IntegracaoTaView):
class ProposicaoTaView(IntegracaoTaView): class ProposicaoTaView(IntegracaoTaView):
model = Proposicao model = Proposicao
model_type_foreignkey = TipoProposicao model_type_foreignkey = TipoProposicao
class AcompanhamentoMateriaView(materia_legislativa_crud.CrudDetailView):
template_name = "materia/acompanhamento_materia.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']
materia = MateriaLegislativa.objects.get(id=pk)
return self.render_to_response(
{'form': AcompanhamentoMateriaForm(),
'materia': materia})
def post(self, request, *args, **kwargs):
form = AcompanhamentoMateriaForm(request.POST)
pk = self.kwargs['pk']
materia = MateriaLegislativa.objects.get(id=pk)
if form.is_valid():
email = form.cleaned_data['email']
usuario = request.user
hash_txt = self.get_random_chars()
try:
AcompanhamentoMateria.objects.get(
email=email,
materia=materia,
hash=hash_txt)
except ObjectDoesNotExist:
acompanhar = form.save(commit=False)
acompanhar.hash = hash_txt
acompanhar.materia = materia
acompanhar.usuario = usuario.username
acompanhar.confirmado = False
acompanhar.save()
do_envia_email_confirmacao(request, materia, email)
else:
return self.render_to_response(
{'form': form,
'materia': materia,
'error': 'Essa matéria já está\
sendo acompanhada por este e-mail.'})
return self.form_valid(form)
else:
return self.render_to_response(
{'form': form,
'materia': materia})
def get_success_url(self):
return reverse('sessaoplenaria:list_pauta_sessao')

2
norma/models.py

@ -1,9 +1,9 @@
from django.db import models from django.db import models
from django.template import defaultfilters from django.template import defaultfilters
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from model_utils import Choices
from materia.models import MateriaLegislativa from materia.models import MateriaLegislativa
from model_utils import Choices
from sapl.utils import YES_NO_CHOICES from sapl.utils import YES_NO_CHOICES

2
parlamentares/models.py

@ -2,8 +2,8 @@ import datetime
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from model_utils import Choices
from model_utils import Choices
from sapl.utils import YES_NO_CHOICES from sapl.utils import YES_NO_CHOICES

2
protocoloadm/models.py

@ -2,9 +2,9 @@ from uuid import uuid4
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from model_utils import Choices
from materia.models import Autor, TipoMateriaLegislativa, UnidadeTramitacao from materia.models import Autor, TipoMateriaLegislativa, UnidadeTramitacao
from model_utils import Choices
from sapl.utils import YES_NO_CHOICES from sapl.utils import YES_NO_CHOICES

9
sapl/utils.py

@ -21,6 +21,15 @@ def xstr(s):
return '' if s is None else str(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): def create_barcode(value):
''' '''
creates a base64 encoded barcode PNG image creates a base64 encoded barcode PNG image

27
sessao/forms.py

@ -1,12 +1,12 @@
from crispy_forms.helper import FormHelper from crispy_forms.helper import FormHelper
from crispy_forms.layout import Column, Fieldset, Layout from crispy_forms.layout import Fieldset, Layout
from django import forms from django import forms
from django.forms import ModelForm from django.forms import ModelForm
import sapl import sapl
from sapl.layout import form_actions from sapl.layout import form_actions
from .models import AcompanharMateria, SessaoPlenaria from .models import SessaoPlenaria
class PresencaForm(forms.Form): class PresencaForm(forms.Form):
@ -66,29 +66,6 @@ class VotacaoEditForm(forms.Form):
pass pass
class AcompanharMateriaForm(ModelForm):
class Meta:
model = AcompanharMateria
fields = ['email']
def __init__(self, *args, **kwargs):
row1 = sapl.layout.to_row([('email', 10)])
row1.append(
Column(form_actions(save_label='Cadastrar'), css_class='col-md-2')
)
self.helper = FormHelper()
self.helper.layout = Layout(
Fieldset(
'Acompanhamento de Matéria por e-mail', row1
)
)
super(AcompanharMateriaForm, self).__init__(*args, **kwargs)
class SessaoForm(ModelForm): class SessaoForm(ModelForm):
hora_inicio = forms.CharField(label='Horário Inicio', hora_inicio = forms.CharField(label='Horário Inicio',

21
sessao/migrations/0014_auto_20160223_0813.py

@ -0,0 +1,21 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import models, migrations
class Migration(migrations.Migration):
dependencies = [
('sessao', '0013_auto_20160216_1015'),
]
operations = [
migrations.RemoveField(
model_name='acompanharmateria',
name='materia_cadastrada',
),
migrations.DeleteModel(
name='AcompanharMateria',
),
]

10
sessao/models.py

@ -1,8 +1,8 @@
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from model_utils import Choices
from materia.models import MateriaLegislativa from materia.models import MateriaLegislativa
from model_utils import Choices
from parlamentares.models import (CargoMesa, Legislatura, Parlamentar, from parlamentares.models import (CargoMesa, Legislatura, Parlamentar,
SessaoLegislativa) SessaoLegislativa)
from sapl.utils import YES_NO_CHOICES from sapl.utils import YES_NO_CHOICES
@ -281,11 +281,3 @@ class SessaoPlenariaPresenca(models.Model):
verbose_name = _('Presença em Sessão Plenária') verbose_name = _('Presença em Sessão Plenária')
verbose_name_plural = _('Presenças em Sessões Plenárias') verbose_name_plural = _('Presenças em Sessões Plenárias')
ordering = ['parlamentar__nome_parlamentar'] ordering = ['parlamentar__nome_parlamentar']
class AcompanharMateria(models.Model):
usuario = models.CharField(max_length=50)
email = models.CharField(
max_length=50, verbose_name=_('Endereço de email'))
data_cadastro = models.DateField(auto_now_add=True)
materia_cadastrada = models.ForeignKey(MateriaLegislativa)

18
sessao/urls.py

@ -1,14 +1,14 @@
from django.conf.urls import include, url from django.conf.urls import include, url
from sapl import settings from sapl import settings
from sessao.views import (AcompanharMateriaView, EditExpedienteOrdemDiaView, from sessao.views import (EditExpedienteOrdemDiaView, EditMateriaOrdemDiaView,
EditMateriaOrdemDiaView, ExpedienteOrdemDiaView, ExpedienteOrdemDiaView, ExpedienteView,
ExpedienteView, ExplicacaoDelete, ExplicacaoEdit, ExplicacaoDelete, ExplicacaoEdit, ExplicacaoView,
ExplicacaoView, ListExpedienteOrdemDiaView, ListExpedienteOrdemDiaView, ListMateriaOrdemDiaView,
ListMateriaOrdemDiaView, MateriaOrdemDiaView, MateriaOrdemDiaView, MesaView,
MesaView, OradorExpedienteDelete, OradorExpedienteDelete, OradorExpedienteEdit,
OradorExpedienteEdit, OradorExpedienteView, OradorExpedienteView, PainelView,
PainelView, PautaExpedienteDetail, PautaOrdemDetail, PautaExpedienteDetail, PautaOrdemDetail,
PautaSessaoDetailView, PautaSessaoListView, PautaSessaoDetailView, PautaSessaoListView,
PresencaOrdemDiaView, PresencaView, ResumoView, PresencaOrdemDiaView, PresencaView, ResumoView,
SessaoCadastroView, SessaoListView, SessaoCadastroView, SessaoListView,
@ -95,8 +95,6 @@ urlpatterns_sessao = sessao_crud.urlpatterns + [
PautaExpedienteDetail.as_view(), name='pauta_expediente_detail'), PautaExpedienteDetail.as_view(), name='pauta_expediente_detail'),
url(r'^pauta-sessao/(?P<pk>\d+)/ordem/$', url(r'^pauta-sessao/(?P<pk>\d+)/ordem/$',
PautaOrdemDetail.as_view(), name='pauta_ordem_detail'), PautaOrdemDetail.as_view(), name='pauta_ordem_detail'),
url(r'^pauta-sessao/(?P<pk>\d+)/acompanhar-materia/$',
AcompanharMateriaView.as_view(), name='acompanhar_materia'),
] ]
sessao_urls = urlpatterns_sessao, sessao_crud.namespace, sessao_crud.namespace sessao_urls = urlpatterns_sessao, sessao_crud.namespace, sessao_crud.namespace

58
sessao/views.py

@ -16,13 +16,13 @@ from norma.models import NormaJuridica
from parlamentares.models import Parlamentar from parlamentares.models import Parlamentar
from sessao.serializers import SessaoPlenariaSerializer from sessao.serializers import SessaoPlenariaSerializer
from .forms import (AcompanharMateriaForm, ExpedienteForm, ListMateriaForm, from .forms import (ExpedienteForm, ListMateriaForm, MateriaOrdemDiaForm,
MateriaOrdemDiaForm, MesaForm, OradorDeleteForm, MesaForm, OradorDeleteForm, OradorForm, PresencaForm,
OradorForm, PresencaForm, SessaoForm, VotacaoEditForm, SessaoForm, VotacaoEditForm, VotacaoForm,
VotacaoForm, VotacaoNominalForm) VotacaoNominalForm)
from .models import (AcompanharMateria, CargoMesa, ExpedienteMateria, from .models import (CargoMesa, ExpedienteMateria, ExpedienteSessao,
ExpedienteSessao, IntegranteMesa, MateriaLegislativa, IntegranteMesa, MateriaLegislativa, Orador,
Orador, OradorExpediente, OrdemDia, PresencaOrdemDia, OradorExpediente, OrdemDia, PresencaOrdemDia,
RegistroVotacao, SessaoPlenaria, SessaoPlenariaPresenca, RegistroVotacao, SessaoPlenaria, SessaoPlenariaPresenca,
TipoExpediente, TipoResultadoVotacao, TipoSessaoPlenaria, TipoExpediente, TipoResultadoVotacao, TipoSessaoPlenaria,
VotoParlamentar) VotoParlamentar)
@ -2363,47 +2363,3 @@ class PautaOrdemDetail(sessao_crud.CrudDetailView):
'norma': norma, 'norma': norma,
'doc_ace': doc_ace, 'doc_ace': doc_ace,
'tramitacao': tramitacao}) 'tramitacao': tramitacao})
class AcompanharMateriaView(sessao_crud.CrudDetailView):
template_name = "sessao/pauta/acompanhar_materia.html"
def get(self, request, *args, **kwargs):
pk = self.kwargs['pk']
materia = MateriaLegislativa.objects.get(id=pk)
return self.render_to_response(
{'form': AcompanharMateriaForm(),
'materia': materia})
def post(self, request, *args, **kwargs):
form = AcompanharMateriaForm(request.POST)
pk = self.kwargs['pk']
materia = MateriaLegislativa.objects.get(id=pk)
if form.is_valid():
email = form.cleaned_data['email']
usuario = request.user
try:
AcompanharMateria.objects.get(
email=email,
materia_cadastrada=materia)
except ObjectDoesNotExist:
acompanhar = form.save(commit=False)
acompanhar.materia_cadastrada = materia
acompanhar.usuario = usuario.username
acompanhar.save()
else:
return self.render_to_response(
{'form': form,
'materia': materia,
'error': 'Essa matéria já está\
sendo acompanhada por este e-mail.'})
return self.form_valid(form)
else:
return self.render_to_response(
{'form': form,
'materia': materia})
def get_success_url(self):
return reverse('sessaoplenaria:list_pauta_sessao')

27
templates/email/acompanhar.html

@ -0,0 +1,27 @@
{% load i18n %}
{% load static %}
<html><head></head><body bgcolor='#ffffff'>
<p align='center'>
<img src="{{base_url}}{% if logotipo %}{{ MEDIA_URL }}{{ logotipo }}{% else %}{% static 'img/logo.png' %}{% endif %}"
alt="Logo" class="img-responsive visible-lg-inline-block vcenter" >
</p>
<h2 align='center'><b>{{casa_legislativa}}</b>
<br/>
Sistema de Apoio ao Processo Legislativo
</h2>
<p>Registramos seu pedido para acompanhamento por e-mail da matéria legislativa identificada a seguir:</p>
<a href="{{base_url}}{{materia_url}}">{{materia}}<b> - {{descricao_materia}}</b></a><br/>
{{ementa}}<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
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.

1
templates/email/test_tramitacao.html

@ -0,0 +1 @@
<html><body>Hello {{name}}</body></html>

38
templates/email/tramitacao.html

@ -0,0 +1,38 @@
{% load i18n %}
{% load static %}
<html>
<head></head>
<body bgcolor='#ffffff'>
<p align='center'>
<img src="{{base_url}}{% if logotipo %}{{ MEDIA_URL }}{{ logotipo }}{% else %}{% static 'img/logo.png' %}{% endif %}"
alt="Logo" class="img-responsive visible-lg-inline-block vcenter" >
</p>
<h2 align='center'><b>{{casa_legislativa}}</b>
<br/>
Sistema de Apoio ao Processo Legislativo
</h2>
<p>A seguinte mat&eacute;ria de seu interesse sofreu
tramita&ccedil;&atilde;o registrada em {{data_registro}}
</p>
<h4>
<a href="{{base_url}}{{materia_url}}"><b>{{materia}} - {{descricao_materia}}</b></a>
<br/><br/>
<b>Autoria:</b></br>
{% for autor in autoria %}
{{ autor }}</br>
{% endfor %}
</h4>
<p></p>
<p>
<b>Data da a&ccedil;&atilde;o</b>: {{data}}<br/>
<b>Status</b>: {{status}}<br/>
<b>Texto da a&ccedil;&atilde;o</b>: {{texto_acao}}</p>
<hr>
<p>
<a href="{{base_url}}{{excluir_url}}?hash_txt={{hash_txt}}">
Clique aqui para excluir seu e-mail da lista de envio</a>
<p>
<p>Esta &eacute; uma mensagem autom&aacute;tica.
Por favor, n&atilde;o a responda.</p>
</body>
</html>

27
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.

0
templates/sessao/pauta/acompanhar_materia.html → templates/materia/acompanhamento_materia.html

74
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 = "<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
@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': '<html></html>',
} 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': '<html></html>',
} 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

2
templates/sessao/pauta/expediente.html

@ -39,7 +39,7 @@
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Tramitação{% if expediente.materia.em_tramitacao %}<a href="{% url 'sessaoplenaria:acompanhar_materia' expediente.materia.id %}"> >>> Acompanhar matéria <<<</a>{% endif %}</legend> <legend>Tramitação{% if expediente.materia.em_tramitacao %}<a href="{% url 'acompanhar_materia' expediente.materia.id %}"> >>> Acompanhar matéria <<<</a>{% endif %}</legend>
{% for t in tramitacao %} {% for t in tramitacao %}
<b>Data: </b> {{t.data_tramitacao}}<br /> <b>Data: </b> {{t.data_tramitacao}}<br />

2
templates/sessao/pauta/ordem.html

@ -48,7 +48,7 @@
</fieldset> </fieldset>
<fieldset> <fieldset>
<legend>Tramitação{% if ordem.materia.em_tramitacao %}<a href="{% url 'sessaoplenaria:acompanhar_materia' ordem.materia.id %}"> >>> Acompanhar matéria <<<</a>{% endif %}</legend> <legend>Tramitação{% if ordem.materia.em_tramitacao %}<a href="{% url 'acompanhar_materia' ordem.materia.id %}"> >>> Acompanhar matéria <<<</a>{% endif %}</legend>
{% for t in tramitacao %} {% for t in tramitacao %}
<b>Data: </b> {{t.data_tramitacao}}<br /> <b>Data: </b> {{t.data_tramitacao}}<br />

Loading…
Cancel
Save