diff --git a/sapl/api/forms.py b/sapl/api/forms.py index 41e40eacf..d1445f7df 100644 --- a/sapl/api/forms.py +++ b/sapl/api/forms.py @@ -1,11 +1,15 @@ -from django.db.models import Q +from django.db.models import Q, F from django.forms.fields import CharField, MultiValueField from django.forms.widgets import MultiWidget, TextInput -from django_filters.filters import MethodFilter, ModelChoiceFilter +from django.utils import timezone +from django.utils.translation import ugettext_lazy as _ +from django_filters.filters import MethodFilter, ModelChoiceFilter, DateFilter +from rest_framework import serializers 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 @@ -38,11 +42,13 @@ class SaplGenericRelationSearchFilterSet(FilterSet): item.related_query_name(), field[0]) ) - if len(field) == 3 and field[2](qtext) is not None: - q_fs = q_fs | Q(**{'%s__%s%s' % ( - item.related_query_name(), - field[0], - field[1]): qtext if len(field) == 2 else field[2](qtext)}) + # if len(field) == 3 and field[2](qtext) is not + # None: + q_fs = q_fs | Q(**{'%s__%s%s' % ( + item.related_query_name(), + field[0], + field[1]): qtext if len(field) == 2 + else field[2](qtext)}) q = q & q_fs @@ -115,3 +121,93 @@ class AutorSearchForFieldFilterSet(AutorChoiceFilterSet): 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 + + return getattr(self, filter_for_model)(qs, data_relativa) + + def filter_parlamentar(self, queryset, data_relativa): + # não leva em conta afastamentos + if not data_relativa: + data_relativa = timezone.now() + + legislatura_relativa = Legislatura.objects.filter( + data_inicio__lte=data_relativa, + data_fim__gte=data_relativa).first() + + params = { + 'parlamentar_set__mandato__data_inicio_mandato__lte': + data_relativa, + 'parlamentar_set__mandato__data_fim_mandato__gte': data_relativa + } + + if legislatura_relativa.atual(): + params['parlamentar_set__ativo'] = True + + qs = queryset.filter(**params).distinct() + + return qs + + def filter_frente(self, queryset, data_relativa): + # implementar regras específicas para frente + return queryset + + def filter_comissao(self, queryset, data_relativa): + # implementar regras específicas para comissao + return queryset + + def filter_orgao(self, queryset, data_relativa): + # implementar regras específicas para orgao + return queryset + + def filter_bancada(self, queryset, data_relativa): + # implementar regras específicas para bancada + return queryset + + def filter_bloco(self, queryset, data_relativa): + # implementar regras específicas para bloco + return queryset diff --git a/sapl/api/urls.py b/sapl/api/urls.py index bf9c16c51..314b2b339 100644 --- a/sapl/api/urls.py +++ b/sapl/api/urls.py @@ -3,7 +3,8 @@ from django.conf.urls import include, url from rest_framework.routers import DefaultRouter from sapl.api.views import (AutorListView, MateriaLegislativaViewSet, - ModelChoiceView, SessaoPlenariaViewSet) + ModelChoiceView, SessaoPlenariaViewSet, + AutoresPossiveisListView, AutoresProvaveisListView) from .apps import AppConfig @@ -17,6 +18,11 @@ 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*)$', diff --git a/sapl/api/views.py b/sapl/api/views.py index 7d89b28b0..7c8ddcf8d 100644 --- a/sapl/api/views.py +++ b/sapl/api/views.py @@ -9,7 +9,8 @@ from rest_framework.permissions import (AllowAny, IsAuthenticated, IsAuthenticatedOrReadOnly) from rest_framework.viewsets import GenericViewSet, ModelViewSet -from sapl.api.forms import AutorChoiceFilterSet, AutorSearchForFieldFilterSet +from sapl.api.forms import AutorChoiceFilterSet, AutorSearchForFieldFilterSet,\ + AutoresPossiveisFilterSet from sapl.api.serializers import (AutorChoiceSerializer, AutorSerializer, ChoiceSerializer, MateriaLegislativaSerializer, @@ -57,14 +58,6 @@ class AutorListView(ListAPIView): de Autores feita pelo django-filter -> processo usado nas pesquisas, o mais usado. - = 2 -> para (value, text) usados geralmente - em combobox, radiobox, checkbox, etc com pesquisa básica - de Autores mas feito para Possíveis Autores armazenados - segundo o ContentType associado ao Tipo de Autor via - relacionamento genérico. - Busca feita sem django-filter processada no get_queryset - -> processo no cadastro de autores para seleção e busca - dos possíveis autores = 3 -> Devolve instancias da classe Autor filtradas pelo django-filter @@ -78,14 +71,14 @@ class AutorListView(ListAPIView): o django-filter é desativado e a busca é feita no model do ContentType associado ao tipo. - - q_0 / q_1 - q_0 faz o código ignorar "q"... + - 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. + possibilidade de filtrar mais de um campo. http://localhost:8000 @@ -114,7 +107,7 @@ class AutorListView(ListAPIView): não importa o campo que vc passe de qualquer dos Models - ligados... é possível ver que models são esses, + 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 @@ -127,15 +120,13 @@ class AutorListView(ListAPIView): sapl.sessao.models.Bancada, sapl.sessao.models.Bloco] - qualquer atributo destes models podem ser passados + qualquer atributo destes models podem ser passados para busca """ TR_AUTOR_CHOICE_SERIALIZER = 1 - TR_CHOICE_SERIALIZER = 2 TR_AUTOR_SERIALIZER = 3 - # FIXME aplicar permissão correta de usuário permission_classes = (IsAuthenticatedOrReadOnly,) queryset = Autor.objects.all() model = Autor @@ -152,7 +143,6 @@ class AutorListView(ListAPIView): assert tr in ( AutorListView.TR_AUTOR_CHOICE_SERIALIZER, - AutorListView.TR_CHOICE_SERIALIZER, AutorListView.TR_AUTOR_SERIALIZER), sapl_logger.info( _("Tipo do Resultado a ser fornecido não existe!")) except: @@ -161,16 +151,8 @@ class AutorListView(ListAPIView): return tr def get(self, request, *args, **kwargs): - """ - desativa o django-filter se a busca for por possiveis autores - parametro tr = TR_CHOICE_SERIALIZER - """ - if self.tr == AutorListView.TR_CHOICE_SERIALIZER: - self.filter_class = None - self.filter_backends = [] - self.serializer_class = ChoiceSerializer - - elif self.tr == AutorListView.TR_AUTOR_SERIALIZER: + + if self.tr == AutorListView.TR_AUTOR_SERIALIZER: self.serializer_class = AutorSerializer self.permission_classes = (IsAuthenticated,) @@ -179,12 +161,20 @@ class AutorListView(ListAPIView): 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): queryset = ListAPIView.get_queryset(self) - if self.filter_backends: - return queryset - params = {'content_type__isnull': False} tipo = '' @@ -252,6 +242,18 @@ class AutorListView(ListAPIView): 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): diff --git a/sapl/base/models.py b/sapl/base/models.py index a5e94b321..0a4c8d8f2 100644 --- a/sapl/base/models.py +++ b/sapl/base/models.py @@ -1,11 +1,12 @@ -import reversion from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.db import models from django.utils.translation import ugettext_lazy as _ +import reversion from sapl.utils import UF, YES_NO_CHOICES, get_settings_auth_user_model + TIPO_DOCUMENTO_ADMINISTRATIVO = (('O', _('Ostensivo')), ('R', _('Restritivo'))) @@ -221,6 +222,7 @@ class Autor(models.Model): verbose_name = _('Autor') verbose_name_plural = _('Autores') unique_together = (('content_type', 'object_id'), ) + ordering = ('nome',) def __str__(self): diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py index a8cf6cb66..a125c6f84 100644 --- a/sapl/materia/forms.py +++ b/sapl/materia/forms.py @@ -14,16 +14,15 @@ from django.core.urlresolvers import reverse from django.db import models, transaction from django.db.models import Max from django.forms import ModelForm, ModelChoiceField, widgets +from django.forms.fields import BooleanField from django.forms.forms import Form -from django.forms.widgets import Select +from django.forms.widgets import Select, HiddenInput from django.utils import six from django.utils.encoding import force_text from django.utils.html import format_html from django.utils.safestring import mark_safe from django.utils.translation import ugettext_lazy as _ - from django_filters.filterset import STRICTNESS - import django_filters from sapl.base.models import Autor, TipoAutor @@ -693,10 +692,13 @@ class AutoriaForm(ModelForm): tipo_autor = ModelChoiceField(label=_('Tipo Autor'), required=False, - queryset= - TipoAutor.objects.all().order_by('descricao'), + queryset=TipoAutor.objects.all().order_by( + 'descricao'), empty_label='Selecione',) + data_relativa = forms.DateField( + widget=forms.HiddenInput()) + def __init__(self, *args, **kwargs): super(AutoriaForm, self).__init__(*args, **kwargs) @@ -707,11 +709,14 @@ class AutoriaForm(ModelForm): self.helper = FormHelper() self.helper.layout = Layout( Fieldset(_('Autoria'), - row1, form_actions(save_label='Salvar'))) + row1, 'data_relativa', form_actions(save_label='Salvar'))) + + if not kwargs['instance']: + self.fields['autor'].choices = [] class Meta: model = Autoria - fields = ['tipo_autor', 'autor', 'primeiro_autor'] + fields = ['tipo_autor', 'autor', 'primeiro_autor', 'data_relativa'] def clean(self): super(AutoriaForm, self).clean() diff --git a/sapl/materia/views.py b/sapl/materia/views.py index bc2478d49..5b5d0e9f8 100644 --- a/sapl/materia/views.py +++ b/sapl/materia/views.py @@ -2,23 +2,6 @@ from datetime import datetime, date from random import choice from string import ascii_letters, digits -from .email_utils import do_envia_email_confirmacao -from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm, - AdicionarVariasAutoriasFilterSet, DespachoInicialForm, - DocumentoAcessorioForm, MateriaAssuntoForm, - MateriaLegislativaFilterSet, MateriaSimplificadaForm, - PrimeiraTramitacaoEmLoteFilterSet, ReceberProposicaoForm, - RelatoriaForm, TramitacaoEmLoteFilterSet, - filtra_tramitacao_destino, - filtra_tramitacao_destino_and_status, - filtra_tramitacao_status) -from .models import (AcompanhamentoMateria, Anexada, AssuntoMateria, Autoria, - DespachoInicial, DocumentoAcessorio, MateriaAssunto, - MateriaLegislativa, Numeracao, Orgao, Origem, Proposicao, - RegimeTramitacao, Relatoria, StatusTramitacao, - TipoDocumento, TipoFimRelatoria, TipoMateriaLegislativa, - TipoProposicao, Tramitacao, UnidadeTramitacao) -from .signals import tramitacao_signal from crispy_forms.helper import FormHelper from crispy_forms.layout import HTML from django import forms @@ -39,7 +22,7 @@ from django.views.generic import CreateView, ListView, TemplateView, UpdateView from django.views.generic.base import RedirectView from django.views.generic.edit import FormView from django_filters.views import FilterView -import sapl + from sapl.base.models import Autor, CasaLegislativa from sapl.comissoes.models import Comissao from sapl.comissoes.models import Comissao, Participacao @@ -62,7 +45,25 @@ from sapl.protocoloadm.models import Protocolo from sapl.utils import (TURNO_TRAMITACAO_CHOICES, YES_NO_CHOICES, autor_label, autor_modal, gerar_hash_arquivo, get_base_url, montar_row_autor) +import sapl +from .email_utils import do_envia_email_confirmacao +from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm, + AdicionarVariasAutoriasFilterSet, DespachoInicialForm, + DocumentoAcessorioForm, MateriaAssuntoForm, + MateriaLegislativaFilterSet, MateriaSimplificadaForm, + PrimeiraTramitacaoEmLoteFilterSet, ReceberProposicaoForm, + RelatoriaForm, TramitacaoEmLoteFilterSet, + filtra_tramitacao_destino, + filtra_tramitacao_destino_and_status, + filtra_tramitacao_status) +from .models import (AcompanhamentoMateria, Anexada, AssuntoMateria, Autoria, + DespachoInicial, DocumentoAcessorio, MateriaAssunto, + MateriaLegislativa, Numeracao, Orgao, Origem, Proposicao, + RegimeTramitacao, Relatoria, StatusTramitacao, + TipoDocumento, TipoFimRelatoria, TipoMateriaLegislativa, + TipoProposicao, Tramitacao, UnidadeTramitacao) +from .signals import tramitacao_signal AssuntoMateriaCrud = Crud.build(AssuntoMateria, 'assunto_materia') @@ -93,8 +94,8 @@ def proposicao_texto(request, pk): if proposicao.texto_original: if (not proposicao.data_recebimento and - proposicao.autor.user_id != request.user.id): - raise Http404 + proposicao.autor.user_id != request.user.id): + raise Http404 arquivo = proposicao.texto_original @@ -1093,24 +1094,27 @@ class AutoriaCrud(MasterDetailCrud): def layout_key(self): return 'AutoriaCreate' - def get_context_data(self, **kwargs): - context = super(CreateView, self).get_context_data(**kwargs) - autores_ativos = self.autores_ativos() - - autores = [] - - context['form'].fields['autor'].choices = autores - return context + def get_initial(self): + initial = super().get_initial() + materia = MateriaLegislativa.objects.get(id=self.kwargs['pk']) + initial['data_relativa'] = materia.data_apresentacao + initial['autor'] = [] + return initial def autores_ativos(self): - lista_parlamentares = Parlamentar.objects.filter(ativo=True).values_list('id', flat=True) + lista_parlamentares = Parlamentar.objects.filter( + ativo=True).values_list('id', flat=True) model_parlamentar = ContentType.objects.get_for_model(Parlamentar) - autor_parlamentar = Autor.objects.filter(content_type=model_parlamentar, object_id__in=lista_parlamentares) + autor_parlamentar = Autor.objects.filter( + content_type=model_parlamentar, object_id__in=lista_parlamentares) - lista_comissoes = Comissao.objects.filter(Q(data_extincao__isnull=True)|Q(data_extincao__gt=date.today())).values_list('id', flat=True) + lista_comissoes = Comissao.objects.filter(Q(data_extincao__isnull=True) | Q( + data_extincao__gt=date.today())).values_list('id', flat=True) model_comissao = ContentType.objects.get_for_model(Comissao) - autor_comissoes = Autor.objects.filter(content_type=model_comissao, object_id__in=lista_comissoes) - autores_outros = Autor.objects.exclude(content_type__in=[model_parlamentar, model_comissao]) + autor_comissoes = Autor.objects.filter( + content_type=model_comissao, object_id__in=lista_comissoes) + autores_outros = Autor.objects.exclude( + content_type__in=[model_parlamentar, model_comissao]) q = autor_parlamentar | autor_comissoes | autores_outros return q @@ -1516,9 +1520,9 @@ class AcompanhamentoMateriaView(CreateView): base_url = get_base_url(request) destinatario = AcompanhamentoMateria.objects.get( - materia=materia, - email=email, - confirmado=False) + materia=materia, + email=email, + confirmado=False) casa = CasaLegislativa.objects.first() do_envia_email_confirmacao(base_url, @@ -1699,10 +1703,10 @@ class TramitacaoEmLoteView(PrimeiraTramitacaoEmLoteView): context['primeira_tramitacao'] = False if ('tramitacao__status' in qr and - 'tramitacao__unidade_tramitacao_destino' in qr and - qr['tramitacao__status'] and - qr['tramitacao__unidade_tramitacao_destino'] - ): + 'tramitacao__unidade_tramitacao_destino' in qr and + qr['tramitacao__status'] and + qr['tramitacao__unidade_tramitacao_destino'] + ): lista = filtra_tramitacao_destino_and_status( qr['tramitacao__status'], qr['tramitacao__unidade_tramitacao_destino']) diff --git a/sapl/settings.py b/sapl/settings.py index d6590c5b4..0905edb1b 100644 --- a/sapl/settings.py +++ b/sapl/settings.py @@ -22,6 +22,7 @@ from unipath import Path from .temp_suppress_crispy_form_warnings import \ SUPRESS_CRISPY_FORM_WARNINGS_LOGGING + BASE_DIR = Path(__file__).ancestor(1) PROJECT_DIR = Path(__file__).ancestor(2) @@ -101,7 +102,7 @@ if SOLR_URL: HAYSTACK_CONNECTIONS = { 'default': { 'ENGINE': SEARCH_BACKEND, - SEARCH_URL[0] : SEARCH_URL[1] + SEARCH_URL[0]: SEARCH_URL[1] }, } @@ -200,7 +201,7 @@ MAX_IMAGE_UPLOAD_SIZE = 2 * 1024 * 1024 # 2MB # https://docs.djangoproject.com/en/1.8/topics/i18n/ LANGUAGE_CODE = 'pt-br' LANGUAGES = ( - ('pt-br','Português'), + ('pt-br', 'Português'), ) TIME_ZONE = 'America/Sao_Paulo' diff --git a/sapl/static/js/app.js b/sapl/static/js/app.js index 1afa1eec5..697564024 100644 --- a/sapl/static/js/app.js +++ b/sapl/static/js/app.js @@ -58,7 +58,7 @@ function autorModal() { autoOpen: false, modal: true, width: 500, - height: 300, + height: 340, show: { effect: "blind", duration: 500}, @@ -90,11 +90,11 @@ function autorModal() { $("#pesquisar").click(function() { var name_in_query = $("#q").val() - var q_0 = "q_0=nome__icontains" - var q_1 = "q_1=" + name_in_query - query = q_0 + "&" + q_1 + //var q_0 = "q_0=nome__icontains" + //var q_1 = name_in_query + //query = q_1 - $.get("/api/autor?" + query, function(data, status) { + $.get("/api/autor?q=" + name_in_query, function(data, status) { $("#div-resultado").children().remove(); if (data.pagination.total_entries == 0) { $("#selecionar").attr("hidden", "hidden"); @@ -111,11 +111,12 @@ function autorModal() { select.append($(""); }