From 13636e119872296248d885f1939ce38baef08946 Mon Sep 17 00:00:00 2001 From: Leandro Roberto Date: Mon, 6 Aug 2018 14:10:20 -0300 Subject: [PATCH 1/7] fragmenta app api para pastas por app do projeto --- sapl/api/base/__init__.py | 0 sapl/api/base/forms.py | 147 ++++++++++++++++++++++ sapl/api/base/serializers.py | 174 ++++++++++++++++++++++++++ sapl/api/base/urls.py | 16 +++ sapl/api/base/views.py | 223 ++++++++++++++++++++++++++++++++++ sapl/api/forms.py | 145 +--------------------- sapl/api/serializers.py | 18 --- sapl/api/urls.py | 17 +-- sapl/api/views.py | 228 +---------------------------------- 9 files changed, 571 insertions(+), 397 deletions(-) create mode 100644 sapl/api/base/__init__.py create mode 100644 sapl/api/base/forms.py create mode 100644 sapl/api/base/serializers.py create mode 100644 sapl/api/base/urls.py create mode 100644 sapl/api/base/views.py diff --git a/sapl/api/base/__init__.py b/sapl/api/base/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sapl/api/base/forms.py b/sapl/api/base/forms.py new file mode 100644 index 000000000..128b13257 --- /dev/null +++ b/sapl/api/base/forms.py @@ -0,0 +1,147 @@ +from django.db.models import Q +from django.utils import timezone +from django.utils.translation import ugettext_lazy as _ +from django_filters.filters import DateFilter, MethodFilter, ModelChoiceFilter +from rest_framework import serializers +from rest_framework.filters import FilterSet + +from sapl.api.forms import SaplGenericRelationSearchFilterSet,\ + SearchForFieldFilter +from sapl.base.models import Autor, TipoAutor +from sapl.parlamentares.models import Legislatura + + +class AutorChoiceFilterSet(SaplGenericRelationSearchFilterSet): + q = MethodFilter() + tipo = ModelChoiceFilter(queryset=TipoAutor.objects.all()) + + class Meta: + model = Autor + fields = ['q', + 'tipo', + 'nome', ] + + def filter_q(self, queryset, value): + return SaplGenericRelationSearchFilterSet.filter_q( + self, queryset, value).distinct('nome').order_by('nome') + + +class AutorSearchForFieldFilterSet(AutorChoiceFilterSet): + q = SearchForFieldFilter() + + class Meta(AutorChoiceFilterSet.Meta): + pass + + def filter_q(self, queryset, value): + + value[0] = value[0].split(',') + value[1] = value[1].split(',') + + params = {} + for key, v in list(zip(value[0], value[1])): + if v in ['True', 'False']: + v = '1' if v == 'True' else '0' + params[key] = v + return queryset.filter(**params).distinct('nome').order_by('nome') + + +class AutoresPossiveisFilterSet(FilterSet): + data_relativa = DateFilter(method='filter_data_relativa') + tipo = MethodFilter(required=True) + + class Meta: + model = Autor + fields = ['data_relativa', 'tipo', ] + + def filter_data_relativa(self, queryset, name, value): + return queryset + + def filter_tipo(self, queryset, value): + try: + tipo = TipoAutor.objects.get(pk=value) + except: + raise serializers.ValidationError(_('Tipo de Autor inexistente.')) + + qs = queryset.filter(tipo=tipo) + + return qs + + @property + def qs(self): + qs = super().qs + + data_relativa = self.form.cleaned_data['data_relativa'] \ + if 'data_relativa' in self.form.cleaned_data else None + + tipo = self.form.cleaned_data['tipo'] \ + if 'tipo' in self.form.cleaned_data else None + + if not tipo: + return qs + else: + tipo = TipoAutor.objects.get(pk=tipo) + if not tipo.content_type: + return qs + + filter_for_model = 'filter_%s' % tipo.content_type.model + + if not hasattr(self, filter_for_model): + return qs + + if not data_relativa: + data_relativa = timezone.now() + + return getattr(self, filter_for_model)(qs, data_relativa).distinct() + + def filter_parlamentar(self, queryset, data_relativa): + # não leva em conta afastamentos + legislatura_relativa = Legislatura.objects.filter( + data_inicio__lte=data_relativa, + data_fim__gte=data_relativa).first() + + q = Q( + parlamentar_set__mandato__data_inicio_mandato__lte=data_relativa, + parlamentar_set__mandato__data_fim_mandato__isnull=True) | Q( + parlamentar_set__mandato__data_inicio_mandato__lte=data_relativa, + parlamentar_set__mandato__data_fim_mandato__gte=data_relativa) + + if legislatura_relativa.atual(): + q = q & Q(parlamentar_set__ativo=True) + + return queryset.filter(q) + + def filter_comissao(self, queryset, data_relativa): + return queryset.filter( + Q(comissao_set__data_extincao__isnull=True, + comissao_set__data_fim_comissao__isnull=True) | + Q(comissao_set__data_extincao__gte=data_relativa, + comissao_set__data_fim_comissao__isnull=True) | + Q(comissao_set__data_extincao__gte=data_relativa, + comissao_set__data_fim_comissao__isnull=True) | + Q(comissao_set__data_extincao__isnull=True, + comissao_set__data_fim_comissao__gte=data_relativa) | + Q(comissao_set__data_extincao__gte=data_relativa, + comissao_set__data_fim_comissao__gte=data_relativa), + comissao_set__data_criacao__lte=data_relativa) + + def filter_frente(self, queryset, data_relativa): + return queryset.filter( + Q(frente_set__data_extincao__isnull=True) | + Q(frente_set__data_extincao__gte=data_relativa), + frente_set__data_criacao__lte=data_relativa) + + def filter_bancada(self, queryset, data_relativa): + return queryset.filter( + Q(bancada_set__data_extincao__isnull=True) | + Q(bancada_set__data_extincao__gte=data_relativa), + bancada_set__data_criacao__lte=data_relativa) + + def filter_bloco(self, queryset, data_relativa): + return queryset.filter( + Q(bloco_set__data_extincao__isnull=True) | + Q(bloco_set__data_extincao__gte=data_relativa), + bloco_set__data_criacao__lte=data_relativa) + + def filter_orgao(self, queryset, data_relativa): + # na implementação, não havia regras a implementar para orgao + return queryset diff --git a/sapl/api/base/serializers.py b/sapl/api/base/serializers.py new file mode 100644 index 000000000..9219bf0e4 --- /dev/null +++ b/sapl/api/base/serializers.py @@ -0,0 +1,174 @@ +from rest_framework import serializers + +from sapl.base.models import Autor, CasaLegislativa +from sapl.materia.models import MateriaLegislativa +from sapl.sessao.models import OrdemDia, SessaoPlenaria + + +class ChoiceSerializer(serializers.Serializer): + value = serializers.SerializerMethodField() + text = serializers.SerializerMethodField() + + def get_text(self, obj): + return obj[1] + + def get_value(self, obj): + return obj[0] + + +class ModelChoiceSerializer(ChoiceSerializer): + + def get_text(self, obj): + return str(obj) + + def get_value(self, obj): + return obj.id + + +class ModelChoiceObjectRelatedField(serializers.RelatedField): + + def to_representation(self, value): + return ModelChoiceSerializer(value).data + + +class AutorChoiceSerializer(ModelChoiceSerializer): + + def get_text(self, obj): + return obj.nome + + class Meta: + model = Autor + fields = ['id', 'nome'] + + +class AutorSerializer(serializers.ModelSerializer): + autor_related = ModelChoiceObjectRelatedField(read_only=True) + + class Meta: + model = Autor + fields = '__all__' + + +class MateriaLegislativaSerializer(serializers.ModelSerializer): + + class Meta: + model = MateriaLegislativa + fields = '__all__' + + +class SessaoPlenariaSerializer(serializers.ModelSerializer): + + codReuniao = serializers.SerializerMethodField('get_pk_sessao') + codReuniaoPrincipal = serializers.SerializerMethodField('get_pk_sessao') + txtTituloReuniao = serializers.SerializerMethodField('get_name') + txtSiglaOrgao = serializers.SerializerMethodField('get_sigla_orgao') + txtApelido = serializers.SerializerMethodField('get_name') + txtNomeOrgao = serializers.SerializerMethodField('get_nome_orgao') + codEstadoReuniao = serializers.SerializerMethodField( + 'get_estadoSessaoPlenaria') + txtTipoReuniao = serializers.SerializerMethodField('get_tipo_sessao') + txtObjeto = serializers.SerializerMethodField('get_assunto_sessao') + txtLocal = serializers.SerializerMethodField('get_endereco_orgao') + bolReuniaoConjunta = serializers.SerializerMethodField( + 'get_reuniao_conjunta') + bolHabilitarEventoInterativo = serializers.SerializerMethodField( + 'get_iterativo') + idYoutube = serializers.SerializerMethodField('get_url') + codEstadoTransmissaoYoutube = serializers.SerializerMethodField( + 'get_estadoTransmissaoYoutube') + datReuniaoString = serializers.SerializerMethodField('get_date') + + # Constantes SessaoPlenaria (de 1-9) (apenas 3 serão usados) + SESSAO_FINALIZADA = 4 + SESSAO_EM_ANDAMENTO = 3 + SESSAO_CONVOCADA = 2 + + # Constantes EstadoTranmissaoYoutube (de 0 a 2) + TRANSMISSAO_ENCERRADA = 2 + TRANSMISSAO_EM_ANDAMENTO = 1 + SEM_TRANSMISSAO = 0 + + class Meta: + model = SessaoPlenaria + fields = ( + 'codReuniao', + 'codReuniaoPrincipal', + 'txtTituloReuniao', + 'txtSiglaOrgao', + 'txtApelido', + 'txtNomeOrgao', + 'codEstadoReuniao', + 'txtTipoReuniao', + 'txtObjeto', + 'txtLocal', + 'bolReuniaoConjunta', + 'bolHabilitarEventoInterativo', + 'idYoutube', + 'codEstadoTransmissaoYoutube', + 'datReuniaoString' + ) + + def __init__(self, *args, **kwargs): + super(SessaoPlenariaSerializer, self).__init__(args, kwargs) + + def get_pk_sessao(self, obj): + return obj.pk + + def get_name(self, obj): + return obj.__str__() + + def get_estadoSessaoPlenaria(self, obj): + if obj.finalizada: + return self.SESSAO_FINALIZADA + elif obj.iniciada: + return self.SESSAO_EM_ANDAMENTO + else: + return self.SESSAO_CONVOCADA + + def get_tipo_sessao(self, obj): + return obj.tipo.__str__() + + def get_url(self, obj): + return obj.url_video if obj.url_video else None + + def get_iterativo(self, obj): + return obj.interativa if obj.interativa else False + + def get_date(self, obj): + return "{} {}{}".format( + obj.data_inicio.strftime("%d/%m/%Y"), + obj.hora_inicio, + ":00" + ) + + def get_estadoTransmissaoYoutube(self, obj): + if obj.url_video: + if obj.finalizada: + return self.TRANSMISSAO_ENCERRADA + else: + return self.TRANSMISSAO_EM_ANDAMENTO + else: + return self.SEM_TRANSMISSAO + + def get_assunto_sessao(self, obj): + pauta_sessao = '' + ordem_dia = OrdemDia.objects.filter(sessao_plenaria=obj.pk) + pauta_sessao = ', '.join([i.materia.__str__() for i in ordem_dia]) + + return str(pauta_sessao) + + def get_endereco_orgao(self, obj): + return self.casa().endereco + + def get_reuniao_conjunta(self, obj): + return False + + def get_sigla_orgao(self, obj): + return self.casa().sigla + + def get_nome_orgao(self, obj): + return self.casa().nome + + def casa(self): + casa = CasaLegislativa.objects.first() + return casa diff --git a/sapl/api/base/urls.py b/sapl/api/base/urls.py new file mode 100644 index 000000000..d02622941 --- /dev/null +++ b/sapl/api/base/urls.py @@ -0,0 +1,16 @@ +from django.conf.urls import include, url +from sapl.api.apps import AppConfig +from sapl.api.base.views import AutorListView, AutoresPossiveisListView,\ + AutoresProvaveisListView + + +app_name = AppConfig.name + + +urlpatterns = [ + url(r'^autor/$', AutorListView.as_view(), name='autor_list'), + url(r'^autor/provaveis', + AutoresProvaveisListView.as_view(), name='autores_provaveis_list'), + url(r'^autor/possiveis', + AutoresPossiveisListView.as_view(), name='autores_possiveis_list'), +] diff --git a/sapl/api/base/views.py b/sapl/api/base/views.py new file mode 100644 index 000000000..37e750dd8 --- /dev/null +++ b/sapl/api/base/views.py @@ -0,0 +1,223 @@ +from django.db.models import Q +from django.http import Http404 +from django.utils.translation import ugettext_lazy as _ +from rest_framework.filters import DjangoFilterBackend +from rest_framework.generics import ListAPIView +from rest_framework.permissions import (IsAuthenticated, + IsAuthenticatedOrReadOnly) + +from sapl.api.base.forms import AutoresPossiveisFilterSet,\ + AutorSearchForFieldFilterSet, AutorChoiceFilterSet +from sapl.api.base.serializers import AutorChoiceSerializer, AutorSerializer +from sapl.api.serializers import (ChoiceSerializer) +from sapl.base.models import Autor, TipoAutor +from sapl.utils import SaplGenericRelation, sapl_logger + + +class AutorListView(ListAPIView): + """ + Listagem de Autores com filtro para autores já cadastrados + e/ou possíveis autores. + + - tr - tipo do resultado + Prepera Lista de Autores para 3 cenários distintos + + - default = 1 + + = 1 -> para (value, text) usados geralmente + em combobox, radiobox, checkbox, etc com pesquisa básica + de Autores feita pelo django-filter + -> processo usado nas pesquisas, o mais usado. + + + = 3 -> Devolve instancias da classe Autor filtradas pelo + django-filter + + - tipo - chave primária do Tipo de Autor a ser filtrado + + - q - busca textual no nome do Autor ou em fields_search + declarados no field SaplGenericRelation das GenericFks + A busca textual acontece via django-filter com a + variável `tr` igual 1 ou 3. Em caso contrário, + o django-filter é desativado e a busca é feita + no model do ContentType associado ao tipo. + + - q_0 / q_1 - q_0 é opcional e quando usado, faz o código ignorar "q"... + + q_0 -> campos lookup a serem filtrados em qualquer Model + que implemente SaplGenericRelation + q_1 -> o valor que será pesquisado no lookup de q_0 + + q_0 e q_1 podem ser separados por ","... isso dará a + possibilidade de filtrar mais de um campo. + + + http://localhost:8000 + /api/autor?tr=1&q_0=parlamentar_set__ativo&q_1=False + /api/autor?tr=1&q_0=parlamentar_set__ativo&q_1=True + /api/autor?tr=3&q_0=parlamentar_set__ativo&q_1=False + /api/autor?tr=3&q_0=parlamentar_set__ativo&q_1=True + + http://localhost:8000 + /api/autor?tr=1 + &q_0=parlamentar_set__nome_parlamentar__icontains, + parlamentar_set__ativo + &q_1=Carvalho,False + /api/autor?tr=1 + &q_0=parlamentar_set__nome_parlamentar__icontains, + parlamentar_set__ativo + &q_1=Carvalho,True + /api/autor?tr=3 + &q_0=parlamentar_set__nome_parlamentar__icontains, + parlamentar_set__ativo + &q_1=Carvalho,False + /api/autor?tr=3 + &q_0=parlamentar_set__nome_parlamentar__icontains, + parlamentar_set__ativo + &q_1=Carvalho,True + + + não importa o campo que vc passe de qualquer dos Models + ligados... é possível ver que models são esses, + na ocasião do commit deste texto, executando: + In [6]: from sapl.utils import models_with_gr_for_model + + In [7]: models_with_gr_for_model(Autor) + Out[7]: + [sapl.parlamentares.models.Parlamentar, + sapl.parlamentares.models.Frente, + sapl.comissoes.models.Comissao, + sapl.materia.models.Orgao, + sapl.sessao.models.Bancada, + sapl.sessao.models.Bloco] + + qualquer atributo destes models podem ser passados + para busca + """ + + TR_AUTOR_CHOICE_SERIALIZER = 1 + TR_AUTOR_SERIALIZER = 3 + + permission_classes = (IsAuthenticatedOrReadOnly,) + queryset = Autor.objects.all() + model = Autor + + filter_class = AutorChoiceFilterSet + filter_backends = (DjangoFilterBackend, ) + serializer_class = AutorChoiceSerializer + + @property + def tr(self): + try: + tr = int(self.request.GET.get + ('tr', AutorListView.TR_AUTOR_CHOICE_SERIALIZER)) + + assert tr in ( + AutorListView.TR_AUTOR_CHOICE_SERIALIZER, + AutorListView.TR_AUTOR_SERIALIZER), sapl_logger.info( + _("Tipo do Resultado a ser fornecido não existe!")) + except: + return AutorListView.TR_AUTOR_CHOICE_SERIALIZER + else: + return tr + + def get(self, request, *args, **kwargs): + + if self.tr == AutorListView.TR_AUTOR_SERIALIZER: + self.serializer_class = AutorSerializer + self.permission_classes = (IsAuthenticated,) + + if self.filter_class and 'q_0' in request.GET: + self.filter_class = AutorSearchForFieldFilterSet + + return ListAPIView.get(self, request, *args, **kwargs) + + +class AutoresProvaveisListView(ListAPIView): + + permission_classes = (IsAuthenticatedOrReadOnly,) + queryset = Autor.objects.all() + model = Autor + + filter_class = None + filter_backends = [] + serializer_class = ChoiceSerializer + + def get_queryset(self): + params = {'content_type__isnull': False} + + tipo = '' + try: + tipo = int(self.request.GET.get('tipo', '')) + if tipo: + params['id'] = tipo + except: + pass + + tipos = TipoAutor.objects.filter(**params) + + if not tipos.exists() and tipo: + raise Http404() + + r = [] + for tipo in tipos: + q = self.request.GET.get('q', '').strip() + + model_class = tipo.content_type.model_class() + + fields = list(filter( + lambda field: isinstance(field, SaplGenericRelation) and + field.related_model == Autor, + model_class._meta.get_fields(include_hidden=True))) + + """ + fields - é um array de SaplGenericRelation que deve possuir o + atributo fields_search. Verifique na documentação da classe + a estrutura de fields_search. + """ + + assert len(fields) >= 1, (_( + 'Não foi encontrado em %(model)s um atributo do tipo ' + 'SaplGenericRelation que use o model %(model_autor)s') % { + 'model': model_class._meta.verbose_name, + 'model_autor': Autor._meta.verbose_name}) + + qs = model_class.objects.all() + + q_filter = Q() + if q: + for item in fields: + if item.related_model != Autor: + continue + q_fs = Q() + for field in item.fields_search: + q_fs = q_fs | Q(**{'%s%s' % ( + field[0], + field[1]): q}) + q_filter = q_filter & q_fs + + qs = qs.filter(q_filter).distinct( + fields[0].fields_search[0][0]).order_by( + fields[0].fields_search[0][0]) + else: + qs = qs.order_by(fields[0].fields_search[0][0]) + + qs = qs.values_list( + 'id', fields[0].fields_search[0][0]) + r += list(qs) + + if tipos.count() > 1: + r.sort(key=lambda x: x[1].upper()) + return r + + +class AutoresPossiveisListView(ListAPIView): + + permission_classes = (IsAuthenticatedOrReadOnly,) + queryset = Autor.objects.all() + model = Autor + + pagination_class = None + + filter_class = AutoresPossiveisFilterSet + serializer_class = AutorChoiceSerializer diff --git a/sapl/api/forms.py b/sapl/api/forms.py index 94bc80de4..542601f90 100644 --- a/sapl/api/forms.py +++ b/sapl/api/forms.py @@ -1,15 +1,10 @@ from django.db.models import Q from django.forms.fields import CharField, MultiValueField from django.forms.widgets import MultiWidget, TextInput -from django.utils import timezone -from django.utils.translation import ugettext_lazy as _ -from django_filters.filters import DateFilter, MethodFilter, ModelChoiceFilter -from rest_framework import serializers +from django_filters.filters import MethodFilter from rest_framework.compat import django_filters from rest_framework.filters import FilterSet -from sapl.base.models import Autor, TipoAutor -from sapl.parlamentares.models import Legislatura from sapl.utils import generic_relations_for_model @@ -87,141 +82,3 @@ class SearchForFieldField(MultiValueField): class SearchForFieldFilter(django_filters.filters.MethodFilter): field_class = SearchForFieldField - - -class AutorChoiceFilterSet(SaplGenericRelationSearchFilterSet): - q = MethodFilter() - tipo = ModelChoiceFilter(queryset=TipoAutor.objects.all()) - - class Meta: - model = Autor - fields = ['q', - 'tipo', - 'nome', ] - - def filter_q(self, queryset, value): - return SaplGenericRelationSearchFilterSet.filter_q( - self, queryset, value).distinct('nome').order_by('nome') - - -class AutorSearchForFieldFilterSet(AutorChoiceFilterSet): - q = SearchForFieldFilter() - - class Meta(AutorChoiceFilterSet.Meta): - pass - - def filter_q(self, queryset, value): - - value[0] = value[0].split(',') - value[1] = value[1].split(',') - - params = {} - for key, v in list(zip(value[0], value[1])): - if v in ['True', 'False']: - v = '1' if v == 'True' else '0' - params[key] = v - return queryset.filter(**params).distinct('nome').order_by('nome') - - -class AutoresPossiveisFilterSet(FilterSet): - data_relativa = DateFilter(method='filter_data_relativa') - tipo = MethodFilter() - - class Meta: - model = Autor - fields = ['data_relativa', 'tipo', ] - - def filter_data_relativa(self, queryset, name, value): - return queryset - - def filter_tipo(self, queryset, value): - try: - tipo = TipoAutor.objects.get(pk=value) - except: - raise serializers.ValidationError(_('Tipo de Autor inexistente.')) - - qs = queryset.filter(tipo=tipo) - - return qs - - @property - def qs(self): - qs = super().qs - - data_relativa = self.form.cleaned_data['data_relativa'] \ - if 'data_relativa' in self.form.cleaned_data else None - - tipo = self.form.cleaned_data['tipo'] \ - if 'tipo' in self.form.cleaned_data else None - - if not tipo and not data_relativa: - return qs - - if tipo: - # não precisa de try except, já foi validado em filter_tipo - tipo = TipoAutor.objects.get(pk=tipo) - if not tipo.content_type: - return qs - - filter_for_model = 'filter_%s' % tipo.content_type.model - - if not hasattr(self, filter_for_model): - return qs - - if not data_relativa: - data_relativa = timezone.now() - - return getattr(self, filter_for_model)(qs, data_relativa).distinct() - - def filter_parlamentar(self, queryset, data_relativa): - # não leva em conta afastamentos - legislatura_relativa = Legislatura.objects.filter( - data_inicio__lte=data_relativa, - data_fim__gte=data_relativa).first() - - q = Q( - parlamentar_set__mandato__data_inicio_mandato__lte=data_relativa, - parlamentar_set__mandato__data_fim_mandato__isnull=True) | Q( - parlamentar_set__mandato__data_inicio_mandato__lte=data_relativa, - parlamentar_set__mandato__data_fim_mandato__gte=data_relativa) - - if legislatura_relativa.atual(): - q = q & Q(parlamentar_set__ativo=True) - - return queryset.filter(q) - - def filter_comissao(self, queryset, data_relativa): - return queryset.filter( - Q(comissao_set__data_extincao__isnull=True, - comissao_set__data_fim_comissao__isnull=True) | - Q(comissao_set__data_extincao__gte=data_relativa, - comissao_set__data_fim_comissao__isnull=True) | - Q(comissao_set__data_extincao__gte=data_relativa, - comissao_set__data_fim_comissao__isnull=True) | - Q(comissao_set__data_extincao__isnull=True, - comissao_set__data_fim_comissao__gte=data_relativa) | - Q(comissao_set__data_extincao__gte=data_relativa, - comissao_set__data_fim_comissao__gte=data_relativa), - comissao_set__data_criacao__lte=data_relativa) - - def filter_frente(self, queryset, data_relativa): - return queryset.filter( - Q(frente_set__data_extincao__isnull=True) | - Q(frente_set__data_extincao__gte=data_relativa), - frente_set__data_criacao__lte=data_relativa) - - def filter_bancada(self, queryset, data_relativa): - return queryset.filter( - Q(bancada_set__data_extincao__isnull=True) | - Q(bancada_set__data_extincao__gte=data_relativa), - bancada_set__data_criacao__lte=data_relativa) - - def filter_bloco(self, queryset, data_relativa): - return queryset.filter( - Q(bloco_set__data_extincao__isnull=True) | - Q(bloco_set__data_extincao__gte=data_relativa), - bloco_set__data_criacao__lte=data_relativa) - - def filter_orgao(self, queryset, data_relativa): - # na implementação, não havia regras a implementar para orgao - return queryset diff --git a/sapl/api/serializers.py b/sapl/api/serializers.py index 9219bf0e4..b2f1e2ccb 100644 --- a/sapl/api/serializers.py +++ b/sapl/api/serializers.py @@ -31,24 +31,6 @@ class ModelChoiceObjectRelatedField(serializers.RelatedField): return ModelChoiceSerializer(value).data -class AutorChoiceSerializer(ModelChoiceSerializer): - - def get_text(self, obj): - return obj.nome - - class Meta: - model = Autor - fields = ['id', 'nome'] - - -class AutorSerializer(serializers.ModelSerializer): - autor_related = ModelChoiceObjectRelatedField(read_only=True) - - class Meta: - model = Autor - fields = '__all__' - - class MateriaLegislativaSerializer(serializers.ModelSerializer): class Meta: diff --git a/sapl/api/urls.py b/sapl/api/urls.py index 1f19e9d1f..800d222b3 100644 --- a/sapl/api/urls.py +++ b/sapl/api/urls.py @@ -2,12 +2,13 @@ from django.conf import settings from django.conf.urls import include, url from rest_framework.routers import DefaultRouter -from sapl.api.views import (AutoresPossiveisListView, AutoresProvaveisListView, - AutorListView, MateriaLegislativaViewSet, - ModelChoiceView, SessaoPlenariaViewSet) +import sapl.api.base.urls +from sapl.api.views import MateriaLegislativaViewSet, SessaoPlenariaViewSet,\ + ModelChoiceView from .apps import AppConfig + app_name = AppConfig.name @@ -17,17 +18,8 @@ router.register(r'sessao-plenaria', SessaoPlenariaViewSet) urlpatterns_router = router.urls urlpatterns_api = [ - - url(r'^autor/provaveis', - AutoresProvaveisListView.as_view(), name='autores_provaveis_list'), - url(r'^autor/possiveis', - AutoresPossiveisListView.as_view(), name='autores_possiveis_list'), - - url(r'^autor', AutorListView.as_view(), name='autor_list'), - url(r'^model/(?P\d+)/(?P\d*)$', ModelChoiceView.as_view(), name='model_list'), - ] if settings.DEBUG: @@ -35,6 +27,7 @@ if settings.DEBUG: url(r'^docs', include('rest_framework_docs.urls')), ] urlpatterns = [ + url(r'^api/', include(sapl.api.base.urls)), url(r'^api/', include(urlpatterns_api)), url(r'^api/', include(urlpatterns_router)) ] diff --git a/sapl/api/views.py b/sapl/api/views.py index 9853e6af0..a8dee010f 100644 --- a/sapl/api/views.py +++ b/sapl/api/views.py @@ -1,25 +1,16 @@ from django.contrib.contenttypes.models import ContentType -from django.db.models import Q -from django.http import Http404 -from django.utils.translation import ugettext_lazy as _ from rest_framework.filters import DjangoFilterBackend from rest_framework.generics import ListAPIView from rest_framework.mixins import ListModelMixin, RetrieveModelMixin -from rest_framework.permissions import (AllowAny, IsAuthenticated, - IsAuthenticatedOrReadOnly) +from rest_framework.permissions import (AllowAny, IsAuthenticated) from rest_framework.viewsets import GenericViewSet -from sapl.api.forms import (AutorChoiceFilterSet, AutoresPossiveisFilterSet, - AutorSearchForFieldFilterSet) -from sapl.api.serializers import (AutorChoiceSerializer, AutorSerializer, - ChoiceSerializer, - MateriaLegislativaSerializer, - ModelChoiceSerializer, - SessaoPlenariaSerializer) -from sapl.base.models import Autor, TipoAutor +from sapl.api.serializers import ( + MateriaLegislativaSerializer, + ModelChoiceSerializer, + SessaoPlenariaSerializer) from sapl.materia.models import MateriaLegislativa from sapl.sessao.models import SessaoPlenaria -from sapl.utils import SaplGenericRelation, sapl_logger class ModelChoiceView(ListAPIView): @@ -43,215 +34,6 @@ class ModelChoiceView(ListAPIView): return self.model.objects.all() -class AutorListView(ListAPIView): - """ - Listagem de Autores com filtro para autores já cadastrados - e/ou possíveis autores. - - - tr - tipo do resultado - Prepera Lista de Autores para 3 cenários distintos - - - default = 1 - - = 1 -> para (value, text) usados geralmente - em combobox, radiobox, checkbox, etc com pesquisa básica - de Autores feita pelo django-filter - -> processo usado nas pesquisas, o mais usado. - - - = 3 -> Devolve instancias da classe Autor filtradas pelo - django-filter - - - tipo - chave primária do Tipo de Autor a ser filtrado - - - q - busca textual no nome do Autor ou em fields_search - declarados no field SaplGenericRelation das GenericFks - A busca textual acontece via django-filter com a - variável `tr` igual 1 ou 3. Em caso contrário, - o django-filter é desativado e a busca é feita - no model do ContentType associado ao tipo. - - - q_0 / q_1 - q_0 é opcional e quando usado, faz o código ignorar "q"... - - q_0 -> campos lookup a serem filtrados em qualquer Model - que implemente SaplGenericRelation - q_1 -> o valor que será pesquisado no lookup de q_0 - - q_0 e q_1 podem ser separados por ","... isso dará a - possibilidade de filtrar mais de um campo. - - - http://localhost:8000 - /api/autor?tr=1&q_0=parlamentar_set__ativo&q_1=False - /api/autor?tr=1&q_0=parlamentar_set__ativo&q_1=True - /api/autor?tr=3&q_0=parlamentar_set__ativo&q_1=False - /api/autor?tr=3&q_0=parlamentar_set__ativo&q_1=True - - http://localhost:8000 - /api/autor?tr=1 - &q_0=parlamentar_set__nome_parlamentar__icontains, - parlamentar_set__ativo - &q_1=Carvalho,False - /api/autor?tr=1 - &q_0=parlamentar_set__nome_parlamentar__icontains, - parlamentar_set__ativo - &q_1=Carvalho,True - /api/autor?tr=3 - &q_0=parlamentar_set__nome_parlamentar__icontains, - parlamentar_set__ativo - &q_1=Carvalho,False - /api/autor?tr=3 - &q_0=parlamentar_set__nome_parlamentar__icontains, - parlamentar_set__ativo - &q_1=Carvalho,True - - - não importa o campo que vc passe de qualquer dos Models - ligados... é possível ver que models são esses, - na ocasião do commit deste texto, executando: - In [6]: from sapl.utils import models_with_gr_for_model - - In [7]: models_with_gr_for_model(Autor) - Out[7]: - [sapl.parlamentares.models.Parlamentar, - sapl.parlamentares.models.Frente, - sapl.comissoes.models.Comissao, - sapl.materia.models.Orgao, - sapl.sessao.models.Bancada, - sapl.sessao.models.Bloco] - - qualquer atributo destes models podem ser passados - para busca - """ - - TR_AUTOR_CHOICE_SERIALIZER = 1 - TR_AUTOR_SERIALIZER = 3 - - permission_classes = (IsAuthenticatedOrReadOnly,) - queryset = Autor.objects.all() - model = Autor - - filter_class = AutorChoiceFilterSet - filter_backends = (DjangoFilterBackend, ) - serializer_class = AutorChoiceSerializer - - @property - def tr(self): - try: - tr = int(self.request.GET.get - ('tr', AutorListView.TR_AUTOR_CHOICE_SERIALIZER)) - - assert tr in ( - AutorListView.TR_AUTOR_CHOICE_SERIALIZER, - AutorListView.TR_AUTOR_SERIALIZER), sapl_logger.info( - _("Tipo do Resultado a ser fornecido não existe!")) - except: - return AutorListView.TR_AUTOR_CHOICE_SERIALIZER - else: - return tr - - def get(self, request, *args, **kwargs): - - if self.tr == AutorListView.TR_AUTOR_SERIALIZER: - self.serializer_class = AutorSerializer - self.permission_classes = (IsAuthenticated,) - - if self.filter_class and 'q_0' in request.GET: - self.filter_class = AutorSearchForFieldFilterSet - - return ListAPIView.get(self, request, *args, **kwargs) - - -class AutoresProvaveisListView(ListAPIView): - - permission_classes = (IsAuthenticatedOrReadOnly,) - queryset = Autor.objects.all() - model = Autor - - filter_class = None - filter_backends = [] - serializer_class = ChoiceSerializer - - def get_queryset(self): - params = {'content_type__isnull': False} - - tipo = '' - try: - tipo = int(self.request.GET.get('tipo', '')) - if tipo: - params['id'] = tipo - except: - pass - - tipos = TipoAutor.objects.filter(**params) - - if not tipos.exists() and tipo: - raise Http404() - - r = [] - for tipo in tipos: - q = self.request.GET.get('q', '').strip() - - model_class = tipo.content_type.model_class() - - fields = list(filter( - lambda field: isinstance(field, SaplGenericRelation) and - field.related_model == Autor, - model_class._meta.get_fields(include_hidden=True))) - - """ - fields - é um array de SaplGenericRelation que deve possuir o - atributo fields_search. Verifique na documentação da classe - a estrutura de fields_search. - """ - - assert len(fields) >= 1, (_( - 'Não foi encontrado em %(model)s um atributo do tipo ' - 'SaplGenericRelation que use o model %(model_autor)s') % { - 'model': model_class._meta.verbose_name, - 'model_autor': Autor._meta.verbose_name}) - - qs = model_class.objects.all() - - q_filter = Q() - if q: - for item in fields: - if item.related_model != Autor: - continue - q_fs = Q() - for field in item.fields_search: - q_fs = q_fs | Q(**{'%s%s' % ( - field[0], - field[1]): q}) - q_filter = q_filter & q_fs - - qs = qs.filter(q_filter).distinct( - fields[0].fields_search[0][0]).order_by( - fields[0].fields_search[0][0]) - else: - qs = qs.order_by(fields[0].fields_search[0][0]) - - qs = qs.values_list( - 'id', fields[0].fields_search[0][0]) - r += list(qs) - - if tipos.count() > 1: - r.sort(key=lambda x: x[1].upper()) - return r - - -class AutoresPossiveisListView(ListAPIView): - - permission_classes = (IsAuthenticatedOrReadOnly,) - queryset = Autor.objects.all() - model = Autor - - pagination_class = None - - filter_class = AutoresPossiveisFilterSet - serializer_class = AutorChoiceSerializer - - class MateriaLegislativaViewSet(ListModelMixin, RetrieveModelMixin, GenericViewSet): From 6576c13cfe9efe74f159acb43e166fb845158144 Mon Sep 17 00:00:00 2001 From: Leandro Roberto Date: Mon, 6 Aug 2018 14:28:46 -0300 Subject: [PATCH 2/7] =?UTF-8?q?ajusta=20namespace=20para=20resolu=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20urls?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- sapl/api/base/urls.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sapl/api/base/urls.py b/sapl/api/base/urls.py index d02622941..53ef3a9fe 100644 --- a/sapl/api/base/urls.py +++ b/sapl/api/base/urls.py @@ -3,8 +3,8 @@ from sapl.api.apps import AppConfig from sapl.api.base.views import AutorListView, AutoresPossiveisListView,\ AutoresProvaveisListView - -app_name = AppConfig.name +# Não adicione app_name +# app_name = AppConfig.name urlpatterns = [ From 83dac70b19a203f6ada2d70685810955ee27252f Mon Sep 17 00:00:00 2001 From: Leandro Roberto Date: Mon, 6 Aug 2018 14:57:33 -0300 Subject: [PATCH 3/7] add estrutura para api materia e api sessao --- sapl/api/base/serializers.py | 157 +------------------------------- sapl/api/base/urls.py | 3 +- sapl/api/materia/__init__.py | 0 sapl/api/materia/forms.py | 0 sapl/api/materia/serializers.py | 19 ++++ sapl/api/materia/urls.py | 8 ++ sapl/api/materia/views.py | 9 ++ sapl/api/serializers.py | 129 -------------------------- sapl/api/sessao/__init__.py | 0 sapl/api/sessao/forms.py | 0 sapl/api/sessao/serializers.py | 122 +++++++++++++++++++++++++ sapl/api/sessao/urls.py | 8 ++ sapl/api/sessao/views.py | 18 ++++ sapl/api/views.py | 34 +------ 14 files changed, 190 insertions(+), 317 deletions(-) create mode 100644 sapl/api/materia/__init__.py create mode 100644 sapl/api/materia/forms.py create mode 100644 sapl/api/materia/serializers.py create mode 100644 sapl/api/materia/urls.py create mode 100644 sapl/api/materia/views.py create mode 100644 sapl/api/sessao/__init__.py create mode 100644 sapl/api/sessao/forms.py create mode 100644 sapl/api/sessao/serializers.py create mode 100644 sapl/api/sessao/urls.py create mode 100644 sapl/api/sessao/views.py diff --git a/sapl/api/base/serializers.py b/sapl/api/base/serializers.py index 9219bf0e4..c669e8807 100644 --- a/sapl/api/base/serializers.py +++ b/sapl/api/base/serializers.py @@ -1,34 +1,8 @@ from rest_framework import serializers -from sapl.base.models import Autor, CasaLegislativa -from sapl.materia.models import MateriaLegislativa -from sapl.sessao.models import OrdemDia, SessaoPlenaria - - -class ChoiceSerializer(serializers.Serializer): - value = serializers.SerializerMethodField() - text = serializers.SerializerMethodField() - - def get_text(self, obj): - return obj[1] - - def get_value(self, obj): - return obj[0] - - -class ModelChoiceSerializer(ChoiceSerializer): - - def get_text(self, obj): - return str(obj) - - def get_value(self, obj): - return obj.id - - -class ModelChoiceObjectRelatedField(serializers.RelatedField): - - def to_representation(self, value): - return ModelChoiceSerializer(value).data +from sapl.api.serializers import ModelChoiceSerializer,\ + ModelChoiceObjectRelatedField +from sapl.base.models import Autor class AutorChoiceSerializer(ModelChoiceSerializer): @@ -47,128 +21,3 @@ class AutorSerializer(serializers.ModelSerializer): class Meta: model = Autor fields = '__all__' - - -class MateriaLegislativaSerializer(serializers.ModelSerializer): - - class Meta: - model = MateriaLegislativa - fields = '__all__' - - -class SessaoPlenariaSerializer(serializers.ModelSerializer): - - codReuniao = serializers.SerializerMethodField('get_pk_sessao') - codReuniaoPrincipal = serializers.SerializerMethodField('get_pk_sessao') - txtTituloReuniao = serializers.SerializerMethodField('get_name') - txtSiglaOrgao = serializers.SerializerMethodField('get_sigla_orgao') - txtApelido = serializers.SerializerMethodField('get_name') - txtNomeOrgao = serializers.SerializerMethodField('get_nome_orgao') - codEstadoReuniao = serializers.SerializerMethodField( - 'get_estadoSessaoPlenaria') - txtTipoReuniao = serializers.SerializerMethodField('get_tipo_sessao') - txtObjeto = serializers.SerializerMethodField('get_assunto_sessao') - txtLocal = serializers.SerializerMethodField('get_endereco_orgao') - bolReuniaoConjunta = serializers.SerializerMethodField( - 'get_reuniao_conjunta') - bolHabilitarEventoInterativo = serializers.SerializerMethodField( - 'get_iterativo') - idYoutube = serializers.SerializerMethodField('get_url') - codEstadoTransmissaoYoutube = serializers.SerializerMethodField( - 'get_estadoTransmissaoYoutube') - datReuniaoString = serializers.SerializerMethodField('get_date') - - # Constantes SessaoPlenaria (de 1-9) (apenas 3 serão usados) - SESSAO_FINALIZADA = 4 - SESSAO_EM_ANDAMENTO = 3 - SESSAO_CONVOCADA = 2 - - # Constantes EstadoTranmissaoYoutube (de 0 a 2) - TRANSMISSAO_ENCERRADA = 2 - TRANSMISSAO_EM_ANDAMENTO = 1 - SEM_TRANSMISSAO = 0 - - class Meta: - model = SessaoPlenaria - fields = ( - 'codReuniao', - 'codReuniaoPrincipal', - 'txtTituloReuniao', - 'txtSiglaOrgao', - 'txtApelido', - 'txtNomeOrgao', - 'codEstadoReuniao', - 'txtTipoReuniao', - 'txtObjeto', - 'txtLocal', - 'bolReuniaoConjunta', - 'bolHabilitarEventoInterativo', - 'idYoutube', - 'codEstadoTransmissaoYoutube', - 'datReuniaoString' - ) - - def __init__(self, *args, **kwargs): - super(SessaoPlenariaSerializer, self).__init__(args, kwargs) - - def get_pk_sessao(self, obj): - return obj.pk - - def get_name(self, obj): - return obj.__str__() - - def get_estadoSessaoPlenaria(self, obj): - if obj.finalizada: - return self.SESSAO_FINALIZADA - elif obj.iniciada: - return self.SESSAO_EM_ANDAMENTO - else: - return self.SESSAO_CONVOCADA - - def get_tipo_sessao(self, obj): - return obj.tipo.__str__() - - def get_url(self, obj): - return obj.url_video if obj.url_video else None - - def get_iterativo(self, obj): - return obj.interativa if obj.interativa else False - - def get_date(self, obj): - return "{} {}{}".format( - obj.data_inicio.strftime("%d/%m/%Y"), - obj.hora_inicio, - ":00" - ) - - def get_estadoTransmissaoYoutube(self, obj): - if obj.url_video: - if obj.finalizada: - return self.TRANSMISSAO_ENCERRADA - else: - return self.TRANSMISSAO_EM_ANDAMENTO - else: - return self.SEM_TRANSMISSAO - - def get_assunto_sessao(self, obj): - pauta_sessao = '' - ordem_dia = OrdemDia.objects.filter(sessao_plenaria=obj.pk) - pauta_sessao = ', '.join([i.materia.__str__() for i in ordem_dia]) - - return str(pauta_sessao) - - def get_endereco_orgao(self, obj): - return self.casa().endereco - - def get_reuniao_conjunta(self, obj): - return False - - def get_sigla_orgao(self, obj): - return self.casa().sigla - - def get_nome_orgao(self, obj): - return self.casa().nome - - def casa(self): - casa = CasaLegislativa.objects.first() - return casa diff --git a/sapl/api/base/urls.py b/sapl/api/base/urls.py index 53ef3a9fe..b376b86cc 100644 --- a/sapl/api/base/urls.py +++ b/sapl/api/base/urls.py @@ -1,5 +1,4 @@ -from django.conf.urls import include, url -from sapl.api.apps import AppConfig +from django.conf.urls import url from sapl.api.base.views import AutorListView, AutoresPossiveisListView,\ AutoresProvaveisListView diff --git a/sapl/api/materia/__init__.py b/sapl/api/materia/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sapl/api/materia/forms.py b/sapl/api/materia/forms.py new file mode 100644 index 000000000..e69de29bb diff --git a/sapl/api/materia/serializers.py b/sapl/api/materia/serializers.py new file mode 100644 index 000000000..796d64a11 --- /dev/null +++ b/sapl/api/materia/serializers.py @@ -0,0 +1,19 @@ + +from rest_framework.filters import DjangoFilterBackend +from rest_framework.mixins import ListModelMixin, RetrieveModelMixin +from rest_framework.permissions import IsAuthenticated +from rest_framework.viewsets import GenericViewSet + +from sapl.api.materia.views import MateriaLegislativaSerializer +from sapl.materia.models import MateriaLegislativa + + +class MateriaLegislativaViewSet(ListModelMixin, + RetrieveModelMixin, + GenericViewSet): + + permission_classes = (IsAuthenticated,) + serializer_class = MateriaLegislativaSerializer + queryset = MateriaLegislativa.objects.all() + filter_backends = (DjangoFilterBackend,) + filter_fields = ('numero', 'ano', 'tipo', ) diff --git a/sapl/api/materia/urls.py b/sapl/api/materia/urls.py new file mode 100644 index 000000000..ce14115da --- /dev/null +++ b/sapl/api/materia/urls.py @@ -0,0 +1,8 @@ + +# Não adicione app_name +# app_name = AppConfig.name + + +urlpatterns = [ + +] diff --git a/sapl/api/materia/views.py b/sapl/api/materia/views.py new file mode 100644 index 000000000..632e25c26 --- /dev/null +++ b/sapl/api/materia/views.py @@ -0,0 +1,9 @@ +from rest_framework import serializers +from sapl.materia.models import MateriaLegislativa + + +class MateriaLegislativaSerializer(serializers.ModelSerializer): + + class Meta: + model = MateriaLegislativa + fields = '__all__' diff --git a/sapl/api/serializers.py b/sapl/api/serializers.py index b2f1e2ccb..35758ffb7 100644 --- a/sapl/api/serializers.py +++ b/sapl/api/serializers.py @@ -1,9 +1,5 @@ from rest_framework import serializers -from sapl.base.models import Autor, CasaLegislativa -from sapl.materia.models import MateriaLegislativa -from sapl.sessao.models import OrdemDia, SessaoPlenaria - class ChoiceSerializer(serializers.Serializer): value = serializers.SerializerMethodField() @@ -29,128 +25,3 @@ class ModelChoiceObjectRelatedField(serializers.RelatedField): def to_representation(self, value): return ModelChoiceSerializer(value).data - - -class MateriaLegislativaSerializer(serializers.ModelSerializer): - - class Meta: - model = MateriaLegislativa - fields = '__all__' - - -class SessaoPlenariaSerializer(serializers.ModelSerializer): - - codReuniao = serializers.SerializerMethodField('get_pk_sessao') - codReuniaoPrincipal = serializers.SerializerMethodField('get_pk_sessao') - txtTituloReuniao = serializers.SerializerMethodField('get_name') - txtSiglaOrgao = serializers.SerializerMethodField('get_sigla_orgao') - txtApelido = serializers.SerializerMethodField('get_name') - txtNomeOrgao = serializers.SerializerMethodField('get_nome_orgao') - codEstadoReuniao = serializers.SerializerMethodField( - 'get_estadoSessaoPlenaria') - txtTipoReuniao = serializers.SerializerMethodField('get_tipo_sessao') - txtObjeto = serializers.SerializerMethodField('get_assunto_sessao') - txtLocal = serializers.SerializerMethodField('get_endereco_orgao') - bolReuniaoConjunta = serializers.SerializerMethodField( - 'get_reuniao_conjunta') - bolHabilitarEventoInterativo = serializers.SerializerMethodField( - 'get_iterativo') - idYoutube = serializers.SerializerMethodField('get_url') - codEstadoTransmissaoYoutube = serializers.SerializerMethodField( - 'get_estadoTransmissaoYoutube') - datReuniaoString = serializers.SerializerMethodField('get_date') - - # Constantes SessaoPlenaria (de 1-9) (apenas 3 serão usados) - SESSAO_FINALIZADA = 4 - SESSAO_EM_ANDAMENTO = 3 - SESSAO_CONVOCADA = 2 - - # Constantes EstadoTranmissaoYoutube (de 0 a 2) - TRANSMISSAO_ENCERRADA = 2 - TRANSMISSAO_EM_ANDAMENTO = 1 - SEM_TRANSMISSAO = 0 - - class Meta: - model = SessaoPlenaria - fields = ( - 'codReuniao', - 'codReuniaoPrincipal', - 'txtTituloReuniao', - 'txtSiglaOrgao', - 'txtApelido', - 'txtNomeOrgao', - 'codEstadoReuniao', - 'txtTipoReuniao', - 'txtObjeto', - 'txtLocal', - 'bolReuniaoConjunta', - 'bolHabilitarEventoInterativo', - 'idYoutube', - 'codEstadoTransmissaoYoutube', - 'datReuniaoString' - ) - - def __init__(self, *args, **kwargs): - super(SessaoPlenariaSerializer, self).__init__(args, kwargs) - - def get_pk_sessao(self, obj): - return obj.pk - - def get_name(self, obj): - return obj.__str__() - - def get_estadoSessaoPlenaria(self, obj): - if obj.finalizada: - return self.SESSAO_FINALIZADA - elif obj.iniciada: - return self.SESSAO_EM_ANDAMENTO - else: - return self.SESSAO_CONVOCADA - - def get_tipo_sessao(self, obj): - return obj.tipo.__str__() - - def get_url(self, obj): - return obj.url_video if obj.url_video else None - - def get_iterativo(self, obj): - return obj.interativa if obj.interativa else False - - def get_date(self, obj): - return "{} {}{}".format( - obj.data_inicio.strftime("%d/%m/%Y"), - obj.hora_inicio, - ":00" - ) - - def get_estadoTransmissaoYoutube(self, obj): - if obj.url_video: - if obj.finalizada: - return self.TRANSMISSAO_ENCERRADA - else: - return self.TRANSMISSAO_EM_ANDAMENTO - else: - return self.SEM_TRANSMISSAO - - def get_assunto_sessao(self, obj): - pauta_sessao = '' - ordem_dia = OrdemDia.objects.filter(sessao_plenaria=obj.pk) - pauta_sessao = ', '.join([i.materia.__str__() for i in ordem_dia]) - - return str(pauta_sessao) - - def get_endereco_orgao(self, obj): - return self.casa().endereco - - def get_reuniao_conjunta(self, obj): - return False - - def get_sigla_orgao(self, obj): - return self.casa().sigla - - def get_nome_orgao(self, obj): - return self.casa().nome - - def casa(self): - casa = CasaLegislativa.objects.first() - return casa diff --git a/sapl/api/sessao/__init__.py b/sapl/api/sessao/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sapl/api/sessao/forms.py b/sapl/api/sessao/forms.py new file mode 100644 index 000000000..e69de29bb diff --git a/sapl/api/sessao/serializers.py b/sapl/api/sessao/serializers.py new file mode 100644 index 000000000..1904e3e64 --- /dev/null +++ b/sapl/api/sessao/serializers.py @@ -0,0 +1,122 @@ +from rest_framework import serializers + +from sapl.base.models import CasaLegislativa +from sapl.sessao.models import SessaoPlenaria, OrdemDia + + +class SessaoPlenariaSerializer(serializers.ModelSerializer): + + codReuniao = serializers.SerializerMethodField('get_pk_sessao') + codReuniaoPrincipal = serializers.SerializerMethodField('get_pk_sessao') + txtTituloReuniao = serializers.SerializerMethodField('get_name') + txtSiglaOrgao = serializers.SerializerMethodField('get_sigla_orgao') + txtApelido = serializers.SerializerMethodField('get_name') + txtNomeOrgao = serializers.SerializerMethodField('get_nome_orgao') + codEstadoReuniao = serializers.SerializerMethodField( + 'get_estadoSessaoPlenaria') + txtTipoReuniao = serializers.SerializerMethodField('get_tipo_sessao') + txtObjeto = serializers.SerializerMethodField('get_assunto_sessao') + txtLocal = serializers.SerializerMethodField('get_endereco_orgao') + bolReuniaoConjunta = serializers.SerializerMethodField( + 'get_reuniao_conjunta') + bolHabilitarEventoInterativo = serializers.SerializerMethodField( + 'get_iterativo') + idYoutube = serializers.SerializerMethodField('get_url') + codEstadoTransmissaoYoutube = serializers.SerializerMethodField( + 'get_estadoTransmissaoYoutube') + datReuniaoString = serializers.SerializerMethodField('get_date') + + # Constantes SessaoPlenaria (de 1-9) (apenas 3 serão usados) + SESSAO_FINALIZADA = 4 + SESSAO_EM_ANDAMENTO = 3 + SESSAO_CONVOCADA = 2 + + # Constantes EstadoTranmissaoYoutube (de 0 a 2) + TRANSMISSAO_ENCERRADA = 2 + TRANSMISSAO_EM_ANDAMENTO = 1 + SEM_TRANSMISSAO = 0 + + class Meta: + model = SessaoPlenaria + fields = ( + 'codReuniao', + 'codReuniaoPrincipal', + 'txtTituloReuniao', + 'txtSiglaOrgao', + 'txtApelido', + 'txtNomeOrgao', + 'codEstadoReuniao', + 'txtTipoReuniao', + 'txtObjeto', + 'txtLocal', + 'bolReuniaoConjunta', + 'bolHabilitarEventoInterativo', + 'idYoutube', + 'codEstadoTransmissaoYoutube', + 'datReuniaoString' + ) + + def __init__(self, *args, **kwargs): + super(SessaoPlenariaSerializer, self).__init__(args, kwargs) + + def get_pk_sessao(self, obj): + return obj.pk + + def get_name(self, obj): + return obj.__str__() + + def get_estadoSessaoPlenaria(self, obj): + if obj.finalizada: + return self.SESSAO_FINALIZADA + elif obj.iniciada: + return self.SESSAO_EM_ANDAMENTO + else: + return self.SESSAO_CONVOCADA + + def get_tipo_sessao(self, obj): + return obj.tipo.__str__() + + def get_url(self, obj): + return obj.url_video if obj.url_video else None + + def get_iterativo(self, obj): + return obj.interativa if obj.interativa else False + + def get_date(self, obj): + return "{} {}{}".format( + obj.data_inicio.strftime("%d/%m/%Y"), + obj.hora_inicio, + ":00" + ) + + def get_estadoTransmissaoYoutube(self, obj): + if obj.url_video: + if obj.finalizada: + return self.TRANSMISSAO_ENCERRADA + else: + return self.TRANSMISSAO_EM_ANDAMENTO + else: + return self.SEM_TRANSMISSAO + + def get_assunto_sessao(self, obj): + pauta_sessao = '' + ordem_dia = OrdemDia.objects.filter(sessao_plenaria=obj.pk) + pauta_sessao = ', '.join([i.materia.__str__() for i in ordem_dia]) + + return str(pauta_sessao) + + def get_endereco_orgao(self, obj): + return self.casa().endereco + + def get_reuniao_conjunta(self, obj): + return False + + def get_sigla_orgao(self, obj): + return self.casa().sigla + + def get_nome_orgao(self, obj): + return self.casa().nome + + def casa(self): + casa = CasaLegislativa.objects.first() + return casa diff --git a/sapl/api/sessao/urls.py b/sapl/api/sessao/urls.py new file mode 100644 index 000000000..b2796fd7a --- /dev/null +++ b/sapl/api/sessao/urls.py @@ -0,0 +1,8 @@ +from django.conf.urls import include, url + +# Não adicione app_name +# app_name = AppConfig.name + + +urlpatterns = [ +] diff --git a/sapl/api/sessao/views.py b/sapl/api/sessao/views.py new file mode 100644 index 000000000..ace1243d7 --- /dev/null +++ b/sapl/api/sessao/views.py @@ -0,0 +1,18 @@ +from rest_framework.filters import DjangoFilterBackend +from rest_framework.mixins import ListModelMixin, RetrieveModelMixin +from rest_framework.permissions import AllowAny +from rest_framework.viewsets import GenericViewSet + +from sapl.api.sessao.serializers import SessaoPlenariaSerializer +from sapl.sessao.models import SessaoPlenaria + + +class SessaoPlenariaViewSet(ListModelMixin, + RetrieveModelMixin, + GenericViewSet): + + permission_classes = (AllowAny,) + serializer_class = SessaoPlenariaSerializer + queryset = SessaoPlenaria.objects.all() + filter_backends = (DjangoFilterBackend,) + filter_fields = ('data_inicio', 'data_fim', 'interativa') diff --git a/sapl/api/views.py b/sapl/api/views.py index a8dee010f..d99ffcacb 100644 --- a/sapl/api/views.py +++ b/sapl/api/views.py @@ -1,16 +1,8 @@ from django.contrib.contenttypes.models import ContentType -from rest_framework.filters import DjangoFilterBackend from rest_framework.generics import ListAPIView -from rest_framework.mixins import ListModelMixin, RetrieveModelMixin -from rest_framework.permissions import (AllowAny, IsAuthenticated) -from rest_framework.viewsets import GenericViewSet +from rest_framework.permissions import (IsAuthenticated) -from sapl.api.serializers import ( - MateriaLegislativaSerializer, - ModelChoiceSerializer, - SessaoPlenariaSerializer) -from sapl.materia.models import MateriaLegislativa -from sapl.sessao.models import SessaoPlenaria +from sapl.api.serializers import ModelChoiceSerializer class ModelChoiceView(ListAPIView): @@ -32,25 +24,3 @@ class ModelChoiceView(ListAPIView): def get_queryset(self): return self.model.objects.all() - - -class MateriaLegislativaViewSet(ListModelMixin, - RetrieveModelMixin, - GenericViewSet): - - permission_classes = (IsAuthenticated,) - serializer_class = MateriaLegislativaSerializer - queryset = MateriaLegislativa.objects.all() - filter_backends = (DjangoFilterBackend,) - filter_fields = ('numero', 'ano', 'tipo', ) - - -class SessaoPlenariaViewSet(ListModelMixin, - RetrieveModelMixin, - GenericViewSet): - - permission_classes = (AllowAny,) - serializer_class = SessaoPlenariaSerializer - queryset = SessaoPlenaria.objects.all() - filter_backends = (DjangoFilterBackend,) - filter_fields = ('data_inicio', 'data_fim', 'interativa') From c0b498a60ae69ca66a83bb1f5c51457e85e98bd6 Mon Sep 17 00:00:00 2001 From: Leandro Roberto Date: Mon, 6 Aug 2018 15:11:09 -0300 Subject: [PATCH 4/7] corrige sapl.api.urls --- sapl/api/urls.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/sapl/api/urls.py b/sapl/api/urls.py index 800d222b3..771c26439 100644 --- a/sapl/api/urls.py +++ b/sapl/api/urls.py @@ -3,8 +3,11 @@ from django.conf.urls import include, url from rest_framework.routers import DefaultRouter import sapl.api.base.urls -from sapl.api.views import MateriaLegislativaViewSet, SessaoPlenariaViewSet,\ - ModelChoiceView +from sapl.api.materia.serializers import MateriaLegislativaViewSet +import sapl.api.materia.urls +import sapl.api.sessao.urls +from sapl.api.sessao.views import SessaoPlenariaViewSet +from sapl.api.views import ModelChoiceView from .apps import AppConfig @@ -27,6 +30,8 @@ if settings.DEBUG: url(r'^docs', include('rest_framework_docs.urls')), ] urlpatterns = [ + url(r'^api/', include(sapl.api.materia.urls)), + url(r'^api/', include(sapl.api.sessao.urls)), url(r'^api/', include(sapl.api.base.urls)), url(r'^api/', include(urlpatterns_api)), url(r'^api/', include(urlpatterns_router)) From 6222349f3c7240ded27bfc42a61adb10cb9cbc94 Mon Sep 17 00:00:00 2001 From: Leandro Roberto Date: Thu, 9 Aug 2018 09:18:35 -0300 Subject: [PATCH 5/7] ajusta urls e routers da app api --- sapl/api/materia/urls.py | 5 +++++ sapl/api/sessao/serializers.py | 20 ++++++++++++++++++-- sapl/api/sessao/urls.py | 7 +++++++ sapl/api/sessao/views.py | 19 ++++++++++++------- sapl/api/urls.py | 7 +++---- sapl/sessao/serializers.py | 16 ---------------- sapl/templates/base/relatorios_list.html | 2 +- 7 files changed, 46 insertions(+), 30 deletions(-) diff --git a/sapl/api/materia/urls.py b/sapl/api/materia/urls.py index ce14115da..feea5bf60 100644 --- a/sapl/api/materia/urls.py +++ b/sapl/api/materia/urls.py @@ -1,8 +1,13 @@ +from rest_framework.routers import DefaultRouter +from sapl.api.materia.serializers import MateriaLegislativaViewSet # Não adicione app_name # app_name = AppConfig.name +router = DefaultRouter() +router.register(r'materia', MateriaLegislativaViewSet) + urlpatterns = [ ] diff --git a/sapl/api/sessao/serializers.py b/sapl/api/sessao/serializers.py index 1904e3e64..fc183d19a 100644 --- a/sapl/api/sessao/serializers.py +++ b/sapl/api/sessao/serializers.py @@ -4,7 +4,7 @@ from sapl.base.models import CasaLegislativa from sapl.sessao.models import SessaoPlenaria, OrdemDia -class SessaoPlenariaSerializer(serializers.ModelSerializer): +class SessaoPlenariaOldSerializer(serializers.ModelSerializer): codReuniao = serializers.SerializerMethodField('get_pk_sessao') codReuniaoPrincipal = serializers.SerializerMethodField('get_pk_sessao') @@ -57,7 +57,7 @@ class SessaoPlenariaSerializer(serializers.ModelSerializer): ) def __init__(self, *args, **kwargs): - super(SessaoPlenariaSerializer, self).__init__(args, kwargs) + super(SessaoPlenariaOldSerializer, self).__init__(args, kwargs) def get_pk_sessao(self, obj): return obj.pk @@ -120,3 +120,19 @@ class SessaoPlenariaSerializer(serializers.ModelSerializer): def casa(self): casa = CasaLegislativa.objects.first() return casa + + +class SessaoPlenariaSerializer(serializers.ModelSerializer): + + class Meta: + model = SessaoPlenaria + fields = ('tipo', + 'sessao_legislativa', + 'legislatura', + 'data_inicio', + 'hora_inicio', + 'hora_fim', + 'url_video', + 'iniciada', + 'finalizada' + ) diff --git a/sapl/api/sessao/urls.py b/sapl/api/sessao/urls.py index b2796fd7a..a8fd6e56f 100644 --- a/sapl/api/sessao/urls.py +++ b/sapl/api/sessao/urls.py @@ -1,8 +1,15 @@ from django.conf.urls import include, url +from rest_framework.routers import DefaultRouter +from sapl.api.sessao.views import SessaoPlenariaViewSet,\ + SessaoPlenariaOldViewSet # Não adicione app_name # app_name = AppConfig.name +router = DefaultRouter() +router.register(r'sessao-plenaria-old', SessaoPlenariaOldViewSet, + base_name='sessao-plenaria-old') +router.register(r'sessao-plenaria', SessaoPlenariaViewSet) urlpatterns = [ ] diff --git a/sapl/api/sessao/views.py b/sapl/api/sessao/views.py index ace1243d7..9dcdd2423 100644 --- a/sapl/api/sessao/views.py +++ b/sapl/api/sessao/views.py @@ -1,18 +1,23 @@ from rest_framework.filters import DjangoFilterBackend -from rest_framework.mixins import ListModelMixin, RetrieveModelMixin from rest_framework.permissions import AllowAny -from rest_framework.viewsets import GenericViewSet +from rest_framework.viewsets import ReadOnlyModelViewSet -from sapl.api.sessao.serializers import SessaoPlenariaSerializer +from sapl.api.sessao.serializers import SessaoPlenariaOldSerializer,\ + SessaoPlenariaSerializer from sapl.sessao.models import SessaoPlenaria -class SessaoPlenariaViewSet(ListModelMixin, - RetrieveModelMixin, - GenericViewSet): +class SessaoPlenariaOldViewSet(ReadOnlyModelViewSet): permission_classes = (AllowAny,) - serializer_class = SessaoPlenariaSerializer + serializer_class = SessaoPlenariaOldSerializer queryset = SessaoPlenaria.objects.all() filter_backends = (DjangoFilterBackend,) filter_fields = ('data_inicio', 'data_fim', 'interativa') + + +class SessaoPlenariaViewSet(ReadOnlyModelViewSet): + + permission_classes = (AllowAny,) + serializer_class = SessaoPlenariaSerializer + queryset = SessaoPlenaria.objects.all() diff --git a/sapl/api/urls.py b/sapl/api/urls.py index 771c26439..7a747a995 100644 --- a/sapl/api/urls.py +++ b/sapl/api/urls.py @@ -3,10 +3,8 @@ from django.conf.urls import include, url from rest_framework.routers import DefaultRouter import sapl.api.base.urls -from sapl.api.materia.serializers import MateriaLegislativaViewSet import sapl.api.materia.urls import sapl.api.sessao.urls -from sapl.api.sessao.views import SessaoPlenariaViewSet from sapl.api.views import ModelChoiceView from .apps import AppConfig @@ -16,8 +14,9 @@ app_name = AppConfig.name router = DefaultRouter() -router.register(r'materia', MateriaLegislativaViewSet) -router.register(r'sessao-plenaria', SessaoPlenariaViewSet) +router.registry += sapl.api.materia.urls.router.registry + \ + sapl.api.sessao.urls.router.registry + urlpatterns_router = router.urls urlpatterns_api = [ 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/templates/base/relatorios_list.html b/sapl/templates/base/relatorios_list.html index 1bd0f5eda..c482e0449 100644 --- a/sapl/templates/base/relatorios_list.html +++ b/sapl/templates/base/relatorios_list.html @@ -42,5 +42,5 @@ - {% endblock base_content %} From 43326a96b423984082085319a8cbf293bfad5401 Mon Sep 17 00:00:00 2001 From: Leandro Roberto Date: Tue, 11 Sep 2018 08:24:13 -0300 Subject: [PATCH 6/7] add last global time refresh --- sapl/api/urls.py | 4 +++- sapl/api/views.py | 14 +++++++++++++- sapl/rules/apps.py | 10 ++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/sapl/api/urls.py b/sapl/api/urls.py index 7a747a995..e2ba3ccfa 100644 --- a/sapl/api/urls.py +++ b/sapl/api/urls.py @@ -5,7 +5,7 @@ from rest_framework.routers import DefaultRouter import sapl.api.base.urls import sapl.api.materia.urls import sapl.api.sessao.urls -from sapl.api.views import ModelChoiceView +from sapl.api.views import ModelChoiceView, TimeRefreshDatabaseView from .apps import AppConfig @@ -22,6 +22,8 @@ urlpatterns_router = router.urls urlpatterns_api = [ url(r'^model/(?P\d+)/(?P\d*)$', ModelChoiceView.as_view(), name='model_list'), + url(r'time_refresh$', + TimeRefreshDatabaseView.as_view(), name="time_refresh") ] if settings.DEBUG: diff --git a/sapl/api/views.py b/sapl/api/views.py index d99ffcacb..4b1a151f0 100644 --- a/sapl/api/views.py +++ b/sapl/api/views.py @@ -1,8 +1,12 @@ +from django.apps import apps from django.contrib.contenttypes.models import ContentType from rest_framework.generics import ListAPIView -from rest_framework.permissions import (IsAuthenticated) +from rest_framework.permissions import (IsAuthenticated, AllowAny) +from rest_framework.response import Response +from rest_framework.views import APIView from sapl.api.serializers import ModelChoiceSerializer +from sapl.rules.apps import AppConfig class ModelChoiceView(ListAPIView): @@ -24,3 +28,11 @@ class ModelChoiceView(ListAPIView): def get_queryset(self): return self.model.objects.all() + + +class TimeRefreshDatabaseView(APIView): + + permission_classes = (AllowAny,) + + def get(self, request, *args, **kwargs): + return Response({'last_global_refresh_time': apps.get_app_config('rules').time_refresh}) diff --git a/sapl/rules/apps.py b/sapl/rules/apps.py index dbdfce8ce..994c2b887 100644 --- a/sapl/rules/apps.py +++ b/sapl/rules/apps.py @@ -6,7 +6,10 @@ from django.contrib.auth import get_user_model from django.contrib.auth.management import _get_all_permissions from django.core import exceptions from django.db import models, router +from django.db.models.signals import post_save, post_delete from django.db.utils import DEFAULT_DB_ALIAS +from django.dispatch.dispatcher import receiver +from django.utils import timezone from django.utils.translation import string_concat from django.utils.translation import ugettext_lazy as _ import reversion @@ -21,6 +24,7 @@ class AppConfig(django.apps.AppConfig): name = 'sapl.rules' label = 'rules' verbose_name = _('Regras de Acesso') + time_refresh = timezone.now() def create_proxy_permissions( @@ -254,3 +258,9 @@ models.signals.post_migrate.connect( models.signals.pre_delete.connect( receiver=revision_pre_delete_signal, dispatch_uid="pre_delete_signal") + + +@receiver([post_save, post_delete]) +def refresh_time_update_base(sender, instance, **kwargs): + rule_app = apps.get_app_config('rules') + rule_app.time_refresh = timezone.now() From bdcddb546f880ff5c773fb9d55429fd206a28ee6 Mon Sep 17 00:00:00 2001 From: Leandro Roberto Date: Fri, 14 Sep 2018 16:49:55 -0300 Subject: [PATCH 7/7] =?UTF-8?q?d=C3=A1=20func=20ao=20campo=20observa=C3=A7?= =?UTF-8?q?=C3=A3o=20de=20OrdemDia=20e=20ExpedienteMateria?= 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/views.py | 124 ++++++++++-------- .../sessao/expedientemateria_form.html | 25 +++- sapl/templates/sessao/layouts.yaml | 2 + 7 files changed, 153 insertions(+), 76 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/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 = '''
    ''' for e in errors_tuple: error_message += '''
  • %s: %s
  • ''' % (e[0], e[1][0]) @@ -2000,8 +2010,10 @@ class VotacaoNominalEditAbstract(SessaoPermissionMixin): elif self.expediente: expediente_id = kwargs['oid'] - expediente = ExpedienteMateria.objects.filter(id=expediente_id).last() - votacao = RegistroVotacao.objects.filter(expediente_id=expediente_id).last() + expediente = ExpedienteMateria.objects.filter( + id=expediente_id).last() + votacao = RegistroVotacao.objects.filter( + expediente_id=expediente_id).last() if not expediente or not votacao: raise Http404() @@ -2120,9 +2132,11 @@ class VotacaoNominalTransparenciaDetailView(TemplateView): materia_votacao = self.request.GET.get('materia', None) if materia_votacao == 'ordem': - votacao = RegistroVotacao.objects.filter(ordem=self.kwargs['oid']).last() + votacao = RegistroVotacao.objects.filter( + ordem=self.kwargs['oid']).last() elif materia_votacao == 'expediente': - votacao = RegistroVotacao.objects.filter(expediente=self.kwargs['oid']).last() + votacao = RegistroVotacao.objects.filter( + expediente=self.kwargs['oid']).last() else: raise Http404() @@ -2203,18 +2217,20 @@ class VotacaoSimbolicaTransparenciaDetailView(TemplateView): materia_votacao = self.request.GET.get('materia', None) if materia_votacao == 'ordem': - votacao = RegistroVotacao.objects.filter(ordem=self.kwargs['oid']).last() + votacao = RegistroVotacao.objects.filter( + ordem=self.kwargs['oid']).last() elif materia_votacao == 'expediente': - votacao = RegistroVotacao.objects.filter(expediente=self.kwargs['oid']).last() + votacao = RegistroVotacao.objects.filter( + expediente=self.kwargs['oid']).last() else: raise Http404() context['votacao'] = votacao registro_votacao = {'numero_votos_sim': votacao.numero_votos_sim, - 'numero_votos_nao': votacao.numero_votos_nao, - 'numero_abstencoes': votacao.numero_abstencoes} - context.update({'registro_votacao':registro_votacao}) + 'numero_votos_nao': votacao.numero_votos_nao, + 'numero_abstencoes': votacao.numero_abstencoes} + context.update({'registro_votacao': registro_votacao}) votacao_existente = {'observacao': sub( ' ', ' ', strip_tags(votacao.observacao)), @@ -2230,6 +2246,7 @@ class VotacaoSimbolicaTransparenciaDetailView(TemplateView): for tipo in TipoResultadoVotacao.objects.all(): yield tipo + class VotacaoExpedienteView(SessaoPermissionMixin): """ @@ -2413,7 +2430,8 @@ class VotacaoExpedienteEditView(SessaoPermissionMixin): expediente_id = kwargs['oid'] if int(request.POST['anular_votacao']) == 1: - RegistroVotacao.objects.filter(expediente_id=expediente_id).delete() + RegistroVotacao.objects.filter( + expediente_id=expediente_id).delete() expediente = ExpedienteMateria.objects.get( sessao_plenaria_id=self.object.id, @@ -2452,10 +2470,10 @@ class PautaSessaoView(TemplateView): sessao = SessaoPlenaria.objects.order_by("-data_inicio").first() if not sessao: - return self.render_to_response({}) + return self.render_to_response({}) return HttpResponseRedirect( - reverse('sapl.sessao:pauta_sessao_detail', kwargs={'pk': sessao.pk})) + reverse('sapl.sessao:pauta_sessao_detail', kwargs={'pk': sessao.pk})) class PautaSessaoDetailView(DetailView): diff --git a/sapl/templates/sessao/expedientemateria_form.html b/sapl/templates/sessao/expedientemateria_form.html index dc1fc1e96..a392bc40a 100644 --- a/sapl/templates/sessao/expedientemateria_form.html +++ b/sapl/templates/sessao/expedientemateria_form.html @@ -6,7 +6,7 @@ {% block extra_js %} {% endblock %} diff --git a/sapl/templates/sessao/layouts.yaml b/sapl/templates/sessao/layouts.yaml index 0d756684c..b0127565f 100644 --- a/sapl/templates/sessao/layouts.yaml +++ b/sapl/templates/sessao/layouts.yaml @@ -56,12 +56,14 @@ OrdemDia: ExpedienteMateriaDetail: {% trans 'Matérias do Expediente' %}: - materia + - ementa - tipo_votacao - observacao OrdemDiaDetail: {% trans 'Matérias da Ordem do Dia' %}: - materia + - ementa - tipo_votacao - observacao