From 4cd78fbcbadfa8924d56687dce2a25977eada764 Mon Sep 17 00:00:00 2001 From: Leandro Roberto da Silva Date: Mon, 17 Sep 2018 11:06:37 -0300 Subject: [PATCH] =?UTF-8?q?d=C3=A1=20func=20ao=20campo=20observa=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20OrdemDia=20e=20ExpedienteMateria=20(#2215)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Foram ajustados o formulário de edição do campo observação e adicionado um migrate para limpar o campo observação nos casos em que o conteúdo é exatamente igual a ementa, desconsiderado a caixa das letras. Uma pequena alteração no Crud tb para tratar mais de um campo por coluna nas listagens --- sapl/crud/base.py | 11 +- sapl/sessao/forms.py | 22 ++-- .../migrations/0023_auto_20180914_1315.py | 37 ++++++ sapl/sessao/models.py | 8 +- sapl/sessao/serializers.py | 16 --- sapl/sessao/views.py | 124 ++++++++++-------- sapl/templates/base/relatorios_list.html | 2 +- .../sessao/expedientemateria_form.html | 25 +++- sapl/templates/sessao/layouts.yaml | 2 + 9 files changed, 154 insertions(+), 93 deletions(-) create mode 100644 sapl/sessao/migrations/0023_auto_20180914_1315.py diff --git a/sapl/crud/base.py b/sapl/crud/base.py index 739fc4f37..77462a5f8 100644 --- a/sapl/crud/base.py +++ b/sapl/crud/base.py @@ -17,8 +17,8 @@ from django.http.response import Http404 from django.shortcuts import redirect from django.utils.decorators import classonlymethod from django.utils.encoding import force_text -from django.utils.translation import ugettext_lazy as _ from django.utils.translation import string_concat +from django.utils.translation import ugettext_lazy as _ from django.views.generic import (CreateView, DeleteView, DetailView, ListView, UpdateView) from django.views.generic.base import ContextMixin @@ -30,6 +30,7 @@ from sapl.rules.map_rules import (RP_ADD, RP_CHANGE, RP_DELETE, RP_DETAIL, from sapl.settings import BASE_DIR from sapl.utils import normalize + logger = logging.getLogger(BASE_DIR.name) ACTION_LIST, ACTION_CREATE, ACTION_DETAIL, ACTION_UPDATE, ACTION_DELETE = \ @@ -411,10 +412,13 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView): m = self.model fn = fn.split('__') for f in fn: + if not f: + continue f = m._meta.get_field(f) if hasattr(f, 'related_model') and f.related_model: m = f.related_model - s.append(force_text(f.verbose_name)) + if f: + s.append(force_text(f.verbose_name)) s = ' / '.join(s) r.append(s) return r @@ -440,6 +444,9 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView): if isinstance(name, tuple): s = '' for j, n in enumerate(name): + if not n: + s += '
' + continue m = obj n = n.split('__') for f in n[:-1]: diff --git a/sapl/sessao/forms.py b/sapl/sessao/forms.py index 0166e848e..f7308f62e 100644 --- a/sapl/sessao/forms.py +++ b/sapl/sessao/forms.py @@ -1,6 +1,5 @@ from datetime import datetime -import django_filters from crispy_forms.helper import FormHelper from crispy_forms.layout import HTML, Button, Fieldset, Layout from django import forms @@ -9,6 +8,7 @@ from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.db import transaction from django.forms import ModelForm from django.utils.translation import ugettext_lazy as _ +import django_filters from sapl.base.models import Autor, TipoAutor from sapl.crispy_layout_mixin import form_actions, to_row @@ -98,7 +98,6 @@ class SessaoPlenariaForm(ModelForm): else: # create raise error - # Condições da verificação abertura_entre_leg = leg.data_inicio <= abertura <= leg.data_fim abertura_entre_sl = sl.data_inicio <= abertura <= sl.data_fim @@ -112,7 +111,8 @@ class SessaoPlenariaForm(ModelForm): if encerramento < abertura: raise ValidationError("A data de encerramento não pode ser " "anterior a data de abertura.") - # Verifica se a data de abertura está entre a data de início e fim da legislatura + # Verifica se a data de abertura está entre a data de início e fim + # da legislatura if abertura_entre_leg and encerramento_entre_leg: if abertura_entre_sl and encerramento_entre_sl: pass @@ -164,7 +164,6 @@ class SessaoPlenariaForm(ModelForm): "datas de início e fim tanto Legislatura " "quanto da Sessão Legislativa.") - # Verificações com a data de encerramento vazia else: if abertura_entre_leg: @@ -454,7 +453,6 @@ class SessaoPlenariaFilterSet(django_filters.FilterSet): # pré-popula o campo do formulário com o ano corrente self.form.fields['data_inicio__year'].initial = timezone.now().year - row1 = to_row( [('data_inicio__year', 3), ('data_inicio__month', 3), @@ -569,13 +567,14 @@ class OradorExpedienteForm(ModelForm): def __init__(self, *args, **kwargs): super(OradorExpedienteForm, self).__init__(*args, **kwargs) - legislatura_vigente = SessaoPlenaria.objects.get(pk=kwargs['initial']['id_sessao']).legislatura + legislatura_vigente = SessaoPlenaria.objects.get( + pk=kwargs['initial']['id_sessao']).legislatura if legislatura_vigente: self.fields['parlamentar'].queryset = \ Parlamentar.objects.filter(ativo=True, mandato__legislatura=legislatura_vigente - ).order_by('nome_parlamentar') + ).order_by('nome_parlamentar') def clean(self): super(OradorExpedienteForm, self).clean() @@ -585,11 +584,11 @@ class OradorExpedienteForm(ModelForm): return self.cleaned_data sessao_id = self.initial['id_sessao'] - numero = self.initial.get('numero') # Retorna None se inexistente + numero = self.initial.get('numero') # Retorna None se inexistente ordem = OradorExpediente.objects.filter( - sessao_plenaria_id=sessao_id, - numero_ordem=cleaned_data['numero_ordem'] - ).exists() + sessao_plenaria_id=sessao_id, + numero_ordem=cleaned_data['numero_ordem'] + ).exists() if ordem and (cleaned_data['numero_ordem'] != numero): raise ValidationError(_( @@ -597,7 +596,6 @@ class OradorExpedienteForm(ModelForm): return self.cleaned_data - class Meta: model = OradorExpediente exclude = ['sessao_plenaria'] diff --git a/sapl/sessao/migrations/0023_auto_20180914_1315.py b/sapl/sessao/migrations/0023_auto_20180914_1315.py new file mode 100644 index 000000000..cd2d076ee --- /dev/null +++ b/sapl/sessao/migrations/0023_auto_20180914_1315.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.13 on 2018-09-14 16:15 +from __future__ import unicode_literals + +from django.db import migrations, models + + +def limpa_observacao_igual_ementa(apps, schema_editor): + + ExpedienteMateria = apps.get_model('sessao', 'ExpedienteMateria') + OrdemDia = apps.get_model('sessao', 'OrdemDia') + + q = models.Q(observacao__iexact=models.F('materia__ementa')) + + ExpedienteMateria.objects.filter(q).update(observacao='') + OrdemDia.objects.filter(q).update(observacao='') + + +class Migration(migrations.Migration): + + dependencies = [ + ('sessao', '0022_auto_20180618_1625'), + ] + + operations = [ + migrations.AlterField( + model_name='expedientemateria', + name='observacao', + field=models.TextField(blank=True, verbose_name='Observação'), + ), + migrations.AlterField( + model_name='ordemdia', + name='observacao', + field=models.TextField(blank=True, verbose_name='Observação'), + ), + migrations.RunPython(limpa_observacao_igual_ementa), + ] diff --git a/sapl/sessao/models.py b/sapl/sessao/models.py index 6af5f490f..11047ed9e 100644 --- a/sapl/sessao/models.py +++ b/sapl/sessao/models.py @@ -1,10 +1,10 @@ from operator import xor -import reversion from django.core.exceptions import ValidationError from django.db import models from django.utils.translation import ugettext_lazy as _ from model_utils import Choices +import reversion from sapl.base.models import Autor from sapl.materia.models import MateriaLegislativa @@ -237,7 +237,7 @@ class AbstractOrdemDia(models.Model): verbose_name=_('Matéria')) data_ordem = models.DateField(verbose_name=_('Data da Sessão')) observacao = models.TextField( - blank=True, verbose_name=_('Ementa')) + blank=True, verbose_name=_('Observação')) numero_ordem = models.PositiveIntegerField(verbose_name=_('Nº Ordem')) resultado = models.TextField(blank=True, verbose_name=_('Resultado')) tipo_votacao = models.PositiveIntegerField( @@ -254,6 +254,10 @@ class AbstractOrdemDia(models.Model): class Meta: abstract = True + @property + def ementa(self): + return self.materia.ementa + def __str__(self): return 'Ordem do Dia/Expediente: %s - %s em %s' % ( self.numero_ordem, self.materia, self.sessao_plenaria) diff --git a/sapl/sessao/serializers.py b/sapl/sessao/serializers.py index b8e64358e..b7d93a4c5 100644 --- a/sapl/sessao/serializers.py +++ b/sapl/sessao/serializers.py @@ -1,19 +1,3 @@ from rest_framework import serializers from .models import SessaoPlenaria - - -class SessaoPlenariaSerializer(serializers.Serializer): - - class Meta: - model = SessaoPlenaria - fields = ('tipo', - 'sessao_legislativa', - 'legislatura', - 'data_inicio', - 'hora_inicio', - 'hora_fim', - 'url_video', - 'iniciada', - 'finalizada' - ) diff --git a/sapl/sessao/views.py b/sapl/sessao/views.py index 5a2b0785a..781aebe5c 100644 --- a/sapl/sessao/views.py +++ b/sapl/sessao/views.py @@ -1,5 +1,5 @@ -from re import sub from operator import itemgetter +from re import sub from django.contrib import messages from django.contrib.auth.decorators import permission_required @@ -49,6 +49,7 @@ from .models import (Bancada, Bloco, CargoBancada, CargoMesa, SessaoPlenaria, SessaoPlenariaPresenca, TipoExpediente, TipoResultadoVotacao, TipoSessaoPlenaria, VotoParlamentar) + TipoSessaoCrud = CrudAux.build(TipoSessaoPlenaria, 'tipo_sessao_plenaria') TipoExpedienteCrud = CrudAux.build(TipoExpediente, 'tipo_expediente') CargoBancadaCrud = CrudAux.build(CargoBancada, '') @@ -325,9 +326,9 @@ def customize_link_materia(context, pk, has_permission, is_expediente): 'mid': obj.materia_id}) resultado = ('%s
%s
' % - (url, - resultado_descricao, - resultado_observacao)) + (url, + resultado_descricao, + resultado_observacao)) else: if obj.tipo_votacao == 2: @@ -338,7 +339,7 @@ def customize_link_materia(context, pk, has_permission, is_expediente): 'pk': obj.sessao_plenaria_id, 'oid': obj.pk, 'mid': obj.materia_id}) + \ - '?&materia=expediente' + '?&materia=expediente' else: url = reverse( 'sapl.sessao:votacao_nominal_transparencia', @@ -346,12 +347,12 @@ def customize_link_materia(context, pk, has_permission, is_expediente): 'pk': obj.sessao_plenaria_id, 'oid': obj.pk, 'mid': obj.materia_id}) + \ - '?&materia=ordem' + '?&materia=ordem' resultado = ('%s
%s
' % - (url, - resultado_descricao, - resultado_observacao)) + (url, + resultado_descricao, + resultado_observacao)) elif obj.tipo_votacao == 1: if is_expediente: @@ -361,7 +362,7 @@ def customize_link_materia(context, pk, has_permission, is_expediente): 'pk': obj.sessao_plenaria_id, 'oid': obj.pk, 'mid': obj.materia_id}) + \ - '?&materia=expediente' + '?&materia=expediente' else: url = reverse( 'sapl.sessao:votacao_simbolica_transparencia', @@ -369,7 +370,7 @@ def customize_link_materia(context, pk, has_permission, is_expediente): 'pk': obj.sessao_plenaria_id, 'oid': obj.pk, 'mid': obj.materia_id}) + \ - '?&materia=ordem' + '?&materia=ordem' resultado = ('%s
%s
' % (url, @@ -377,8 +378,8 @@ def customize_link_materia(context, pk, has_permission, is_expediente): resultado_observacao)) else: resultado = ('%s
%s' % - (resultado_descricao, - resultado_observacao)) + (resultado_descricao, + resultado_observacao)) context['rows'][i][3] = (resultado, None) return context @@ -389,7 +390,8 @@ def get_presencas_generic(model, sessao, legislatura): presentes = [p.parlamentar for p in presencas] - presentes = sorted(presentes, key=lambda x: remover_acentos(x.nome_parlamentar)) + presentes = sorted( + presentes, key=lambda x: remover_acentos(x.nome_parlamentar)) mandato = Mandato.objects.filter( legislatura=legislatura).order_by('parlamentar__nome_parlamentar') @@ -408,7 +410,8 @@ class MateriaOrdemDiaCrud(MasterDetailCrud): public = [RP_LIST, RP_DETAIL] class BaseMixin(MasterDetailCrud.BaseMixin): - list_field_names = ['numero_ordem', 'materia', 'materia__ementa', + list_field_names = ['numero_ordem', 'materia', + ('materia__ementa', '', 'observacao'), 'resultado'] class CreateView(MasterDetailCrud.CreateView): @@ -439,17 +442,18 @@ class MateriaOrdemDiaCrud(MasterDetailCrud): return initial class DetailView(MasterDetailCrud.DetailView): - layout_key = 'OrdemDiaDetail' class ListView(MasterDetailCrud.ListView): paginate_by = None ordering = ['numero_ordem', 'materia', 'resultado'] + def get_context_data(self, **kwargs): context = super().get_context_data(**kwargs) has_permition = self.request.user.has_module_perms(AppConfig.label) return customize_link_materia(context, self.kwargs['pk'], has_permition, False) + def recuperar_materia(request): tipo = TipoMateriaLegislativa.objects.get(pk=request.GET['tipo_materia']) numero = request.GET['numero_materia'] @@ -463,7 +467,7 @@ def recuperar_materia(request): 'id': materia.id, 'indexacao': materia.indexacao}) except ObjectDoesNotExist: - response = JsonResponse({'ementa': '', 'id': 0, 'indexacao':''}) + response = JsonResponse({'ementa': '', 'id': 0, 'indexacao': ''}) return response @@ -476,7 +480,8 @@ class ExpedienteMateriaCrud(MasterDetailCrud): class BaseMixin(MasterDetailCrud.BaseMixin): list_field_names = ['numero_ordem', 'materia', - 'materia__ementa', 'resultado'] + ('materia__ementa', '', 'observacao'), + 'resultado'] class ListView(MasterDetailCrud.ListView): paginate_by = None @@ -544,7 +549,6 @@ class OradorExpedienteCrud(OradorCrud): return reverse('sapl.sessao:oradorexpediente_list', kwargs={'pk': self.kwargs['pk']}) - class UpdateView(MasterDetailCrud.UpdateView): form_class = OradorExpedienteForm @@ -669,10 +673,10 @@ class SessaoCrud(Crud): return { 'legislatura': legislatura, 'sessao_legislativa': legislatura.sessaolegislativa_set.filter( - legislatura_id=legislatura.id, - data_inicio__year=timezone.now().year - ).first() - } + legislatura_id=legislatura.id, + data_inicio__year=timezone.now().year + ).first() + } else: msg = _('Cadastre alguma legislatura antes de adicionar ' + 'uma sessão plenária!') @@ -778,7 +782,8 @@ class PainelView(PermissionRequiredForAppCrudMixin, TemplateView): cronometro_discurso = AppsAppConfig.attr('cronometro_discurso') cronometro_aparte = AppsAppConfig.attr('cronometro_aparte') cronometro_ordem = AppsAppConfig.attr('cronometro_ordem') - cronometro_consideracoes = AppsAppConfig.attr('cronometro_consideracoes') + cronometro_consideracoes = AppsAppConfig.attr( + 'cronometro_consideracoes') if (not cronometro_discurso or not cronometro_aparte or not cronometro_ordem or not cronometro_consideracoes): @@ -999,15 +1004,18 @@ class MesaView(FormMixin, DetailView): cargos_vagos = list(set(cargos) - set(cargos_ocupados)) # FIX-ME: tem formas melhores de fazer isso, poupando linhas. - parlamentares = Legislatura.objects.get(id=sessao.legislatura_id).mandato_set.all() + parlamentares = Legislatura.objects.get( + id=sessao.legislatura_id).mandato_set.all() parlamentares_ocupados = [m.parlamentar for m in mesa] parlamentares_vagos = list( set( [p.parlamentar for p in parlamentares]) - set( parlamentares_ocupados)) org_parlamentares_vagos = parlamentares_vagos - org_parlamentares_vagos.sort(key=lambda x: remover_acentos(x.nome_parlamentar)) - org_parlamentares_vagos = [p for p in org_parlamentares_vagos if p.ativo] + org_parlamentares_vagos.sort( + key=lambda x: remover_acentos(x.nome_parlamentar)) + org_parlamentares_vagos = [ + p for p in org_parlamentares_vagos if p.ativo] # Se todos os cargos estiverem ocupados, a listagem de parlamentares # deve ser renderizada vazia if not cargos_vagos: @@ -1159,15 +1167,15 @@ class ResumoOrdenacaoView(PermissionRequiredMixin, FormView): ordenacao = ResumoOrdenacao.objects.first() if ordenacao: initial.update({'primeiro': ordenacao.primeiro, - 'segundo': ordenacao.segundo, - 'terceiro': ordenacao.terceiro, - 'quarto': ordenacao.quarto, - 'quinto': ordenacao.quinto, - 'sexto': ordenacao.sexto, - 'setimo': ordenacao.setimo, - 'oitavo': ordenacao.oitavo, - 'nono': ordenacao.nono, - 'decimo': ordenacao.decimo}) + 'segundo': ordenacao.segundo, + 'terceiro': ordenacao.terceiro, + 'quarto': ordenacao.quarto, + 'quinto': ordenacao.quinto, + 'sexto': ordenacao.sexto, + 'setimo': ordenacao.setimo, + 'oitavo': ordenacao.oitavo, + 'nono': ordenacao.nono, + 'decimo': ordenacao.decimo}) return initial def form_valid(self, form): @@ -1328,7 +1336,7 @@ class ResumoView(DetailView): ora = {'numero_ordem': numero_ordem, 'url_discurso': url_discurso, 'parlamentar': parlamentar, - 'observacao' : observacao + 'observacao': observacao } oradores.append(ora) @@ -1478,8 +1486,8 @@ class ExpedienteView(FormMixin, DetailView): for tipo, conteudo in zip(list_tipo, list_conteudo): ExpedienteSessao.objects.filter( - sessao_plenaria_id=self.object.id, - tipo_id=tipo).delete() + sessao_plenaria_id=self.object.id, + tipo_id=tipo).delete() expediente = ExpedienteSessao() expediente.sessao_plenaria_id = self.object.id @@ -1825,7 +1833,8 @@ class VotacaoNominalAbstract(SessaoPermissionMixin): elif self.expediente: expediente_id = kwargs['oid'] try: - materia_votacao = ExpedienteMateria.objects.get(id=expediente_id) + materia_votacao = ExpedienteMateria.objects.get( + id=expediente_id) except ObjectDoesNotExist: raise Http404() @@ -1923,7 +1932,8 @@ class VotacaoNominalAbstract(SessaoPermissionMixin): return self.form_invalid(form) def form_invalid(self, form): - errors_tuple = [(form[e].label, form.errors[e]) for e in form.errors if e in form.fields] + errors_tuple = [(form[e].label, form.errors[e]) + for e in form.errors if e in form.fields] error_message = '''