diff --git a/sapl/base/forms.py b/sapl/base/forms.py
index 5af6ee179..595814d34 100644
--- a/sapl/base/forms.py
+++ b/sapl/base/forms.py
@@ -10,6 +10,7 @@ from django.utils.translation import ugettext_lazy as _
from sapl.crispy_layout_mixin import form_actions, to_row
from sapl.materia.models import MateriaLegislativa
+from sapl.sessao.models import SessaoPlenaria
from sapl.settings import MAX_IMAGE_UPLOAD_SIZE
from sapl.utils import (RANGE_ANOS, ImageThumbnailFileInput, autor_label,
autor_modal)
@@ -37,6 +38,66 @@ class RangeWidgetOverride(forms.MultiWidget):
return ''.join(rendered_widgets)
+class RelatorioAtasFilterSet(django_filters.FilterSet):
+
+ filter_overrides = {models.DateField: {
+ 'filter_class': django_filters.DateFromToRangeFilter,
+ 'extra': lambda f: {
+ 'label': '%s (%s)' % (f.verbose_name, _('Inicial - Final')),
+ 'widget': RangeWidgetOverride}
+ }}
+
+ class Meta:
+ model = SessaoPlenaria
+ fields = ['data_inicio']
+
+ def __init__(self, *args, **kwargs):
+ super(RelatorioAtasFilterSet, self).__init__(
+ *args, **kwargs)
+
+ self.filters['data_inicio'].label = 'Período (Inicial - Final)'
+ self.form.fields['data_inicio'].required = True
+
+ row1 = to_row([('data_inicio', 12)])
+
+ self.form.helper = FormHelper()
+ self.form.helper.form_method = 'GET'
+ self.form.helper.layout = Layout(
+ Fieldset(_('Atas das Sessões Plenárias'),
+ row1, form_actions(save_label='Pesquisar'))
+ )
+
+
+class RelatorioPresencaSessaoFilterSet(django_filters.FilterSet):
+
+ filter_overrides = {models.DateField: {
+ 'filter_class': django_filters.DateFromToRangeFilter,
+ 'extra': lambda f: {
+ 'label': '%s (%s)' % (f.verbose_name, _('Inicial - Final')),
+ 'widget': RangeWidgetOverride}
+ }}
+
+ class Meta:
+ model = SessaoPlenaria
+ fields = ['data_inicio']
+
+ def __init__(self, *args, **kwargs):
+ super(RelatorioPresencaSessaoFilterSet, self).__init__(
+ *args, **kwargs)
+
+ self.filters['data_inicio'].label = 'Período (Inicial - Final)'
+ self.form.fields['data_inicio'].required = True
+
+ row1 = to_row([('data_inicio', 12)])
+
+ self.form.helper = FormHelper()
+ self.form.helper.form_method = 'GET'
+ self.form.helper.layout = Layout(
+ Fieldset(_('Presença dos parlamentares nas sessões plenárias'),
+ row1, form_actions(save_label='Pesquisar'))
+ )
+
+
class RelatorioHistoricoTramitacaoFilterSet(django_filters.FilterSet):
filter_overrides = {models.DateField: {
diff --git a/sapl/base/urls.py b/sapl/base/urls.py
index b3dcac6ea..60804abea 100644
--- a/sapl/base/urls.py
+++ b/sapl/base/urls.py
@@ -4,11 +4,12 @@ from django.views.generic.base import TemplateView
from .apps import AppConfig
from .forms import LoginForm
-from .views import (CasaLegislativaCrud, HelpView,
+from .views import (CasaLegislativaCrud, HelpView, RelatorioAtasView,
RelatorioHistoricoTramitacaoView,
RelatorioMateriasPorAnoAutorTipoView,
RelatorioMateriasPorAutorView,
- RelatorioMateriasTramitacaoView)
+ RelatorioMateriasTramitacaoView,
+ RelatorioPresencaSessaoView)
app_name = AppConfig.name
@@ -39,5 +40,11 @@ urlpatterns = [
url(r'^relatorio/historico-tramitacoes$',
RelatorioHistoricoTramitacaoView.as_view(),
name='historico_tramitacoes'),
+ url(r'^relatorio/presenca$',
+ RelatorioPresencaSessaoView.as_view(),
+ name='presenca_sessao'),
+ url(r'^relatorio/atas$',
+ RelatorioAtasView.as_view(),
+ name='atas'),
]
diff --git a/sapl/base/views.py b/sapl/base/views.py
index 66267d3e6..d813d4aff 100644
--- a/sapl/base/views.py
+++ b/sapl/base/views.py
@@ -1,5 +1,8 @@
+from itertools import chain
+
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.urlresolvers import reverse
+from django.db.models import Count, Q
from django.http import HttpResponseRedirect
from django.utils.translation import ugettext_lazy as _
from django.views.generic.base import TemplateView
@@ -8,12 +11,17 @@ from django_filters.views import FilterView
from sapl.crud.base import (Crud, CrudBaseMixin, CrudCreateView,
CrudDetailView, CrudUpdateView)
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa
+from sapl.parlamentares.models import Parlamentar
+from sapl.sessao.models import (OrdemDia, PresencaOrdemDia, SessaoPlenaria,
+ SessaoPlenariaPresenca)
from sapl.utils import permissao_tb_aux
-from .forms import (CasaLegislativaForm, RelatorioHistoricoTramitacaoFilterSet,
+from .forms import (CasaLegislativaForm, RelatorioAtasFilterSet,
+ RelatorioHistoricoTramitacaoFilterSet,
RelatorioMateriasPorAnoAutorTipoFilterSet,
RelatorioMateriasPorAutorFilterSet,
- RelatorioMateriasTramitacaoilterSet)
+ RelatorioMateriasTramitacaoilterSet,
+ RelatorioPresencaSessaoFilterSet)
from .models import CasaLegislativa
@@ -21,6 +29,91 @@ def get_casalegislativa():
return CasaLegislativa.objects.first()
+class RelatorioAtasView(FilterView):
+ model = SessaoPlenaria
+ filterset_class = RelatorioAtasFilterSet
+ template_name = 'base/RelatorioAtas_filter.html'
+
+ def get_context_data(self, **kwargs):
+ context = super(RelatorioAtasView,
+ self).get_context_data(**kwargs)
+ context['title'] = _('Atas das Sessões Plenárias')
+
+ # Verifica se os campos foram preenchidos
+ if not self.filterset.form.is_valid():
+ return context
+
+ context['object_list'] = context['object_list'].exclude(upload_ata='')
+ qr = self.request.GET.copy()
+ context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
+ return context
+
+
+class RelatorioPresencaSessaoView(FilterView):
+ model = SessaoPlenaria
+ filterset_class = RelatorioPresencaSessaoFilterSet
+ template_name = 'base/RelatorioPresencaSessao_filter.html'
+
+ def calcular_porcentagem_presenca(self,
+ parlamentares,
+ total_sessao,
+ total_ordemdia):
+ for p in parlamentares:
+ p.sessao_porc = round(p.sessao_count * 100 / total_sessao, 1)
+ p.ordemdia_porc = round(p.ordemdia_count * 100 / total_ordemdia, 1)
+ return parlamentares
+
+ def get_context_data(self, **kwargs):
+ context = super(RelatorioPresencaSessaoView,
+ self).get_context_data(**kwargs)
+ context['title'] = _('Presença dos parlamentares nas sessões')
+
+ # Verifica se os campos foram preenchidos
+ if not self.filterset.form.is_valid():
+ return context
+
+ # =====================================================================
+ if 'salvar' in self.request.GET:
+ where = context['object_list'].query.where
+ _range = where.children[0].rhs
+
+ sufixo = 'sessao_plenaria__data_inicio__range'
+ param0 = {'%s' % sufixo: _range}
+ param1 = {'presencaordemdia__%s' % sufixo: _range}
+ param2 = {'sessaoplenariapresenca__%s' % sufixo: _range}
+
+ pls = Parlamentar.objects.filter(
+ Q(**param1) & Q(**param2)).annotate(
+ sessao_count=Count(
+ 'sessaoplenariapresenca__sessao_plenaria__data_inicio',
+ distinct=True),
+ ordemdia_count=Count(
+ 'presencaordemdia__sessao_plenaria',
+ distinct=True),
+ sessao_porc=Count(0),
+ ordemdia_porc=Count(0))
+
+ total_ordemdia = OrdemDia.objects.order_by(
+ 'sessao_plenaria').filter(**param0).distinct(
+ 'sessao_plenaria').count()
+
+ self.calcular_porcentagem_presenca(
+ pls,
+ context['object_list'].count(),
+ total_ordemdia)
+
+ context['total_ordemdia'] = total_ordemdia
+ context['total_sessao'] = context['object_list'].count()
+ context['parlamentares'] = pls
+ context['periodo'] = (
+ self.request.GET['data_inicio_0'] +
+ ' - ' + self.request.GET['data_inicio_1'])
+ # =====================================================================
+ qr = self.request.GET.copy()
+ context['filter_url'] = ('&' + qr.urlencode()) if len(qr) > 0 else ''
+ return context
+
+
class RelatorioHistoricoTramitacaoView(FilterView):
model = MateriaLegislativa
filterset_class = RelatorioHistoricoTramitacaoFilterSet
diff --git a/sapl/materia/admin.py b/sapl/materia/admin.py
index 5c02ebd40..eef5d7213 100644
--- a/sapl/materia/admin.py
+++ b/sapl/materia/admin.py
@@ -1,3 +1,22 @@
+from django.contrib import admin
+from sapl.materia.models import Proposicao
+from sapl.settings import DEBUG
from sapl.utils import register_all_models_in_admin
register_all_models_in_admin(__name__)
+
+if not DEBUG:
+
+ admin.site.unregister(Proposicao)
+
+ class ProposicaoAdmin(admin.ModelAdmin):
+ def has_add_permission(self, request, obj=None):
+ return False
+
+ def has_change_permission(self, request, obj=None):
+ return False
+
+ def has_delete_permission(self, request, obj=None):
+ return False
+
+ admin.site.register(Proposicao, ProposicaoAdmin)
diff --git a/sapl/materia/views.py b/sapl/materia/views.py
index 239384030..4f7966118 100644
--- a/sapl/materia/views.py
+++ b/sapl/materia/views.py
@@ -4,6 +4,7 @@ from string import ascii_letters, digits
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Button
+from django.db.models import Q
from django.conf import settings
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
@@ -31,7 +32,7 @@ from sapl.crud.masterdetail import MasterDetailCrud
from sapl.norma.models import LegislacaoCitada
from sapl.utils import (autor_label, autor_modal, gerar_hash_arquivo,
get_base_url, permissao_tb_aux, permissoes_autor,
- permissoes_materia)
+ permissoes_materia, permissoes_protocoloadm)
from .forms import (AcompanhamentoMateriaForm, AnexadaForm, AutorForm,
AutoriaForm, ConfirmarProposicaoForm, DespachoInicialForm,
@@ -243,11 +244,12 @@ class UnidadeTramitacaoCrud(Crud):
permission_required = permissoes_materia()
-class ProposicaoDevolvida(ListView):
+class ProposicaoDevolvida(PermissionRequiredMixin, ListView):
template_name = 'materia/prop_devolvidas_list.html'
model = Proposicao
ordering = ['data_envio']
paginate_by = 10
+ permission_required = permissoes_protocoloadm()
def get_queryset(self):
return Proposicao.objects.filter(
@@ -265,11 +267,12 @@ class ProposicaoDevolvida(ListView):
return context
-class ProposicaoPendente(ListView):
+class ProposicaoPendente(PermissionRequiredMixin, ListView):
template_name = 'materia/prop_pendentes_list.html'
model = Proposicao
ordering = ['data_envio', 'autor', 'tipo', 'descricao']
paginate_by = 10
+ permission_required = permissoes_protocoloadm()
def get_queryset(self):
return Proposicao.objects.filter(
@@ -287,11 +290,12 @@ class ProposicaoPendente(ListView):
return context
-class ProposicaoRecebida(ListView):
+class ProposicaoRecebida(PermissionRequiredMixin, ListView):
template_name = 'materia/prop_recebidas_list.html'
model = Proposicao
ordering = ['data_envio']
paginate_by = 10
+ permission_required = permissoes_protocoloadm()
def get_queryset(self):
return Proposicao.objects.filter(
@@ -309,9 +313,10 @@ class ProposicaoRecebida(ListView):
return context
-class ReceberProposicao(CreateView):
+class ReceberProposicao(PermissionRequiredMixin, CreateView):
template_name = "materia/receber_proposicao.html"
form_class = ReceberProposicaoForm
+ permission_required = permissoes_protocoloadm()
def get_context_data(self, **kwargs):
context = super(ReceberProposicao, self).get_context_data(**kwargs)
@@ -341,9 +346,10 @@ class ReceberProposicao(CreateView):
return reverse('sapl.materia:receber-proposicao')
-class ConfirmarProposicao(CreateView):
+class ConfirmarProposicao(PermissionRequiredMixin, CreateView):
template_name = "materia/confirmar_proposicao.html"
form_class = ConfirmarProposicaoForm
+ permission_required = permissoes_protocoloadm()
def get_context_data(self, **kwargs):
context = super(ConfirmarProposicao, self).get_context_data(**kwargs)
@@ -433,38 +439,36 @@ class ProposicaoCrud(Crud):
def has_permission(self):
perms = self.get_permission_required()
- if self.request.user.has_perms(perms):
- if (Proposicao.objects.filter(
- id=self.kwargs['pk'],
- autor__user_id=self.request.user.id).exists()):
- proposicao = Proposicao.objects.get(
- id=self.kwargs['pk'],
- autor__user_id=self.request.user.id)
- if not proposicao.data_recebimento:
- return True
- else:
- msg = _('Essa proposição já foi recebida. ' +
- 'Não pode mais ser editada')
- messages.add_message(self.request, messages.ERROR, msg)
- return False
- else:
+ if not self.request.user.has_perms(perms):
return False
+ if (Proposicao.objects.filter(
+ id=self.kwargs['pk'],
+ autor__user_id=self.request.user.id).exists()):
+ proposicao = Proposicao.objects.get(
+ id=self.kwargs['pk'],
+ autor__user_id=self.request.user.id)
+ if (not proposicao.data_recebimento or
+ proposicao.data_devolucao):
+ return True
+ else:
+ msg = _('Essa proposição já foi recebida. ' +
+ 'Não pode mais ser editada')
+ messages.add_message(self.request, messages.ERROR, msg)
+ return False
+
class DetailView(PermissionRequiredMixin, CrudDetailView):
permission_required = permissoes_autor()
def has_permission(self):
perms = self.get_permission_required()
- if self.request.user.has_perms(perms):
- if (Proposicao.objects.filter(
- id=self.kwargs['pk'],
- autor__user_id=self.request.user.id).exists()):
- return True
- else:
- return False
- else:
+ if not self.request.user.has_perms(perms):
return False
+ return (Proposicao.objects.filter(
+ id=self.kwargs['pk'],
+ autor__user_id=self.request.user.id).exists())
+
class ListView(PermissionRequiredMixin, CrudListView):
ordering = ['-data_envio', 'descricao']
permission_required = permissoes_autor()
@@ -480,35 +484,69 @@ class ProposicaoCrud(Crud):
obj.data_recebimento = 'Não recebida'
else:
obj.data_recebimento = obj.data_recebimento.strftime(
- "%d/%m/%Y %H:%M")
+ "%d/%m/%Y %H:%M")
return [self._as_row(obj) for obj in object_list]
def get_queryset(self):
+ # Só tem acesso as Proposicoes criadas por ele que ainda nao foram
+ # recebidas ou foram devolvidas
lista = Proposicao.objects.filter(
autor__user_id=self.request.user.id)
+ lista = lista.filter(
+ Q(data_recebimento__isnull=True) |
+ Q(data_devolucao__isnull=False))
+
return lista
class DeleteView(PermissionRequiredMixin, CrudDeleteView):
permission_required = {'materia.delete_proposicao'}
+ def has_permission(self):
+ perms = self.get_permission_required()
+ if not self.request.user.has_perms(perms):
+ return False
+
+ return (Proposicao.objects.filter(
+ id=self.kwargs['pk'],
+ autor__user_id=self.request.user.id).exists())
+
def delete(self, request, *args, **kwargs):
proposicao = Proposicao.objects.get(id=self.kwargs['pk'])
- if not proposicao.data_envio:
+ if not proposicao.data_envio or proposicao.data_devolucao:
proposicao.delete()
return HttpResponseRedirect(
reverse('sapl.materia:proposicao_list'))
- else:
+
+ elif not proposicao.data_recebimento:
proposicao.data_envio = None
proposicao.save()
return HttpResponseRedirect(
reverse('sapl.materia:proposicao_detail',
kwargs={'pk': proposicao.pk}))
+ else:
+ msg = _('Essa proposição já foi recebida. ' +
+ 'Não pode mais ser excluída/recuperada')
+ messages.add_message(self.request, messages.ERROR, msg)
+ return HttpResponseRedirect(
+ reverse('sapl.materia:proposicao_detail',
+ kwargs={'pk': proposicao.pk}))
+
class ReciboProposicaoView(TemplateView):
template_name = "materia/recibo_proposicao.html"
+ permission_required = permissoes_autor()
+
+ def has_permission(self):
+ perms = self.get_permission_required()
+ if not self.request.user.has_perms(perms):
+ return False
+
+ return (Proposicao.objects.filter(
+ id=self.kwargs['pk'],
+ autor__user_id=self.request.user.id).exists())
def get_context_data(self, **kwargs):
context = super(ReciboProposicaoView, self).get_context_data(
@@ -516,8 +554,8 @@ class ReciboProposicaoView(TemplateView):
proposicao = Proposicao.objects.get(pk=self.kwargs['pk'])
context.update({'proposicao': proposicao,
'hash': gerar_hash_arquivo(
- proposicao.texto_original.path,
- self.kwargs['pk'])})
+ proposicao.texto_original.path,
+ self.kwargs['pk'])})
return context
diff --git a/sapl/templates/base/RelatorioAtas_filter.html b/sapl/templates/base/RelatorioAtas_filter.html
new file mode 100644
index 000000000..e83971d42
--- /dev/null
+++ b/sapl/templates/base/RelatorioAtas_filter.html
@@ -0,0 +1,38 @@
+{% extends "crud/list.html" %}
+{% load i18n %}
+{% load crispy_forms_tags staticfiles %}
+
+{% block base_content %}
+ {% if not filter_url %}
+ {% crispy filter.form %}
+ {% endif %}
+
+ {% if filter_url %}
+
+
+ {% if object_list|length > 0 %}
+
+
+
+ Sessão |
+ Ata |
+
+
+
+ {% for sessao in object_list %}
+
+ {{sessao}} |
+
+
+ |
+
+ {% endfor %}
+
+
+ {% else %}
+ Nenhum sessão com ata foi encontrada!
+ {% endif %}
+ {% endif %}
+{% endblock base_content %}
diff --git a/sapl/templates/base/RelatorioPresencaSessao_filter.html b/sapl/templates/base/RelatorioPresencaSessao_filter.html
new file mode 100644
index 000000000..8aeae7aa6
--- /dev/null
+++ b/sapl/templates/base/RelatorioPresencaSessao_filter.html
@@ -0,0 +1,50 @@
+{% extends "crud/list.html" %}
+{% load i18n %}
+{% load crispy_forms_tags %}
+
+{% block base_content %}
+ {% if not filter_url %}
+ {% crispy filter.form %}
+ {% endif %}
+
+ {% if filter_url %}
+
+
+
+
+ PERÍODO: {{periodo}}
+ TOTAIS NO PERÍODO - SESSÕES: {{total_sessao}} - ORDENS DO DIA: {{total_ordemdia}}
+
+
+
+ Nome Parlamentar / Partido |
+ Sessão |
+ Ordem do Dia |
+
+
+ (Qtd) |
+ ( % ) |
+ (Qtd) |
+ ( % ) |
+
+
+
+ {% for p in parlamentares %}
+
+ {{p}} / {{p.filiacao_set.first.partido.sigla|default:"Sem Partido"}} |
+ {{p.sessao_count}} |
+ {{p.sessao_porc}} |
+ {{p.ordemdia_count}} |
+ {{p.ordemdia_porc}} |
+
+ {% endfor %}
+
+
+ {% endif %}
+{% endblock base_content %}
diff --git a/sapl/templates/base/relatorios_list.html b/sapl/templates/base/relatorios_list.html
index 49edf946c..c7e47bd69 100644
--- a/sapl/templates/base/relatorios_list.html
+++ b/sapl/templates/base/relatorios_list.html
@@ -25,11 +25,11 @@
Totalização anual de matérias agrupadas por autor e tipo. |
- Presença nas sessões |
+ Presença nas sessões |
Presença dos parlamentares nas sessões plenárias. |
- Atas |
+ Atas |
Atas de Sessão Plenária. |
diff --git a/sapl/templates/sessao/pauta_sessao_list.html b/sapl/templates/sessao/pauta_sessao_list.html
index 66566d20b..ce3e5c77b 100644
--- a/sapl/templates/sessao/pauta_sessao_list.html
+++ b/sapl/templates/sessao/pauta_sessao_list.html
@@ -9,7 +9,6 @@
{% endif %}
-
- {% include "paginacao.html" %}
{% endblock %}