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 %} +
+ {% trans 'Fazer nova pesquisa' %} +
+


+ {% if object_list|length > 0 %} + + + + + + + + + {% for sessao in object_list %} + + + + + {% endfor %} + +
SessãoAta
{{sessao}} + +
+ {% 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 %} + + +
+ {% trans 'Fazer nova pesquisa' %} +
+



+ PERÍODO: {{periodo}}
+ TOTAIS NO PERÍODO - SESSÕES: {{total_sessao}} - ORDENS DO DIA: {{total_ordemdia}} + + + + + + + + + + + + + + + + {% for p in parlamentares %} + + + + + + + + {% endfor %} + +
Nome Parlamentar / PartidoSessãoOrdem do Dia
(Qtd)( % )(Qtd)( % )
{{p}} / {{p.filiacao_set.first.partido.sigla|default:"Sem Partido"}}{{p.sessao_count}}{{p.sessao_porc}}{{p.ordemdia_count}}{{p.ordemdia_porc}}
+ {% 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 @@ - @@ -17,9 +16,6 @@ {% for sessao in page_obj %} -
Data Sessão PDF
- {{sessao.data_inicio}} - {{sessao.hora_inicio}} - {{sessao}} @@ -29,6 +25,4 @@
{% endif %} - - {% include "paginacao.html" %} {% endblock %}