diff --git a/sapl/api/core/__init__.py b/sapl/api/core/__init__.py index 49ddeeb75..78b2c2f22 100644 --- a/sapl/api/core/__init__.py +++ b/sapl/api/core/__init__.py @@ -22,11 +22,12 @@ from rest_framework.viewsets import ModelViewSet from sapl.api.core.filters import SaplFilterSetMixin from sapl.api.permissions import SaplModelPermissions -from sapl.api.serializers import ChoiceSerializer, ParlamentarSerializer,\ +from sapl.api.serializers import ChoiceSerializer, ParlamentarSerializer, \ ParlamentarEditSerializer, ParlamentarResumeSerializer class BusinessRulesNotImplementedMixin: + def create(self, request, *args, **kwargs): raise Exception(_("POST Create não implementado")) @@ -126,6 +127,7 @@ class SaplApiViewSetConstrutor(): # Define uma classe padrão para filtro caso não tenha sido # criada a classe sapl.api.forms.{model}FilterSet class SaplFilterSet(_filterset_class): + class Meta(_meta_filterset): if not hasattr(_meta_filterset, 'model'): model = _model @@ -159,7 +161,6 @@ class SaplApiViewSetConstrutor(): return cls - """ 1. Constroi uma rest_framework.viewsets.ModelViewSet para todos os models de todas as apps do sapl @@ -232,6 +233,7 @@ class SaplApiViewSetConstrutor(): class wrapper_queryset_response_for_drf_action(object): + def __init__(self, model): self.model = model @@ -268,6 +270,7 @@ class wrapper_queryset_response_for_drf_action(object): # decorator para recuperar e transformar o default class customize(object): + def __init__(self, model): self.model = model diff --git a/sapl/api/core/filters.py b/sapl/api/core/filters.py index bf5f68007..59cf860a1 100644 --- a/sapl/api/core/filters.py +++ b/sapl/api/core/filters.py @@ -3,11 +3,11 @@ from collections import OrderedDict from django.db.models.fields.files import FileField from django.template.defaultfilters import capfirst -import django_filters from django_filters.constants import ALL_FIELDS from django_filters.filters import CharFilter from django_filters.filterset import FilterSet from django_filters.utils import resolve_field, get_all_model_fields +import django_filters class SaplFilterSetMixin(FilterSet): diff --git a/sapl/api/deprecated.py b/sapl/api/deprecated.py index 6ef840771..f8186ce08 100644 --- a/sapl/api/deprecated.py +++ b/sapl/api/deprecated.py @@ -1,5 +1,4 @@ -import logging import logging from django.contrib.contenttypes.models import ContentType @@ -10,19 +9,17 @@ from django.forms.widgets import MultiWidget, TextInput from django.http import Http404 from django.utils import timezone from django.utils.translation import ugettext_lazy as _ -from django.utils.translation import ugettext_lazy as _ from django_filters.filters import CharFilter, ModelChoiceFilter, DateFilter from django_filters.rest_framework.backends import DjangoFilterBackend from django_filters.rest_framework.filterset import FilterSet from rest_framework import serializers -from rest_framework import serializers from rest_framework.generics import ListAPIView from rest_framework.mixins import ListModelMixin, RetrieveModelMixin from rest_framework.permissions import (IsAuthenticated, IsAuthenticatedOrReadOnly, AllowAny) from rest_framework.viewsets import GenericViewSet -from sapl.api.serializers import ModelChoiceSerializer, AutorSerializer,\ +from sapl.api.serializers import ModelChoiceSerializer, AutorSerializer, \ ChoiceSerializer from sapl.base.models import TipoAutor, Autor, CasaLegislativa from sapl.materia.models import MateriaLegislativa @@ -514,7 +511,7 @@ class AutorListView(ListAPIView): model = Autor filter_class = AutorChoiceFilterSet - filter_backends = (DjangoFilterBackend, ) + filter_backends = (DjangoFilterBackend,) serializer_class = AutorChoiceSerializer @property @@ -660,7 +657,7 @@ class MateriaLegislativaViewSet(ListModelMixin, serializer_class = MateriaLegislativaOldSerializer queryset = MateriaLegislativa.objects.all() filter_backends = (DjangoFilterBackend,) - filter_fields = ('numero', 'ano', 'tipo', ) + filter_fields = ('numero', 'ano', 'tipo',) class SessaoPlenariaViewSet(ListModelMixin, diff --git a/sapl/api/forms.py b/sapl/api/forms.py index e1b293fbc..be2034bc6 100644 --- a/sapl/api/forms.py +++ b/sapl/api/forms.py @@ -2,7 +2,17 @@ from sapl.api.core.filters import SaplFilterSetMixin from sapl.sessao.models import SessaoPlenaria +# esta classe não é necessária +# a api construiría uma igual +# mas está demonstrar que caso queira customizar um filter_set +# que a api consiga recuperá-lo, para os endpoints básicos +# deve seguir os critérios de nomenclatura e herança + +# class [Model]FilterSet(SaplFilterSetMixin): +# class Meta(SaplFilterSetMixin.Meta): + class SessaoPlenariaFilterSet(SaplFilterSetMixin): + class Meta(SaplFilterSetMixin.Meta): model = SessaoPlenaria diff --git a/sapl/api/serializers.py b/sapl/api/serializers.py index 2dcb518ba..fe3133055 100644 --- a/sapl/api/serializers.py +++ b/sapl/api/serializers.py @@ -1,16 +1,18 @@ import logging + from django.conf import settings from django.core.exceptions import MultipleObjectsReturned, ObjectDoesNotExist -from django.db.models import F, Q +from django.db.models import Q +from image_cropping.utils import get_backend from rest_framework import serializers from rest_framework.relations import StringRelatedField -from sapl.parlamentares.models import Parlamentar, Mandato, Filiacao, Legislatura + from sapl.base.models import Autor, CasaLegislativa -from sapl.utils import filiacao_data -from image_cropping.utils import get_backend +from sapl.parlamentares.models import Parlamentar, Mandato, Legislatura class IntRelatedField(StringRelatedField): + def to_representation(self, value): return int(value) @@ -86,33 +88,33 @@ class ParlamentarResumeSerializer(serializers.ModelSerializer): fotografia_cropped = serializers.SerializerMethodField('crop_fotografia') logger = logging.getLogger(__name__) - def crop_fotografia(self,obj): + def crop_fotografia(self, obj): thumbnail_url = "" try: import os if not obj.fotografia or not os.path.exists(obj.fotografia.path): return thumbnail_url - thumbnail_url = get_backend().get_thumbnail_url( - obj.fotografia, - { - 'size': (128, 128), - 'box': obj.cropping, - 'crop': True, - 'detail': True, - } + thumbnail_url = get_backend().get_thumbnail_url( + obj.fotografia, + { + 'size': (128, 128), + 'box': obj.cropping, + 'crop': True, + 'detail': True, + } ) except Exception as e: self.logger.error(e) self.logger.error('erro processando arquivo: %s' % obj.fotografia.path) - + return thumbnail_url - def check_titular(self,obj): + def check_titular(self, obj): is_titular = None if not Legislatura.objects.exists(): self.logger.error("Não há legislaturas cadastradas.") return "" - + try: legislatura = Legislatura.objects.get(id=self.context.get('legislatura')) except ObjectDoesNotExist: @@ -125,17 +127,17 @@ class ParlamentarResumeSerializer(serializers.ModelSerializer): if mandato: is_titular = 'Sim' if mandato.titular else 'Não' else: - is_titular = '-' + is_titular = '-' return is_titular - def check_partido(self,obj): + def check_partido(self, obj): # Coloca a filiação atual ao invés da última # As condições para mostrar a filiação são: # A data de filiacao deve ser menor que a data de fim # da legislatura e data de desfiliação deve nula, ou maior, # ou igual a data de fim da legislatura - - username = self.context['request'].user.username + + username = self.context['request'].user.username if not Legislatura.objects.exists(): self.logger.error("Não há legislaturas cadastradas.") return "" @@ -143,7 +145,7 @@ class ParlamentarResumeSerializer(serializers.ModelSerializer): legislatura = Legislatura.objects.get(id=self.context.get('legislatura')) except ObjectDoesNotExist: legislatura = Legislatura.objects.first() - + try: self.logger.debug("user=" + username + ". Tentando obter filiação do parlamentar com (data<={} e data_desfiliacao>={}) " "ou (data<={} e data_desfiliacao=Null))." @@ -174,7 +176,7 @@ class ParlamentarResumeSerializer(serializers.ModelSerializer): self.logger.debug("user=" + username + ". Filiação encontrada com sucesso.") filiacao = filiacao.partido.sigla - + return filiacao class Meta: diff --git a/sapl/api/urls.py b/sapl/api/urls.py index a35390b1e..0feb1768d 100644 --- a/sapl/api/urls.py +++ b/sapl/api/urls.py @@ -1,32 +1,28 @@ from django.conf.urls import include, url -from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView,\ +from drf_spectacular.views import SpectacularAPIView, SpectacularSwaggerView, \ SpectacularRedocView from rest_framework.routers import DefaultRouter -from sapl.api.deprecated import MateriaLegislativaViewSet, SessaoPlenariaViewSet,\ - AutoresProvaveisListView, AutoresPossiveisListView, AutorListView,\ +from sapl.api.deprecated import MateriaLegislativaViewSet, SessaoPlenariaViewSet, \ + AutoresProvaveisListView, AutoresPossiveisListView, AutorListView, \ ModelChoiceView from sapl.api.views import AppVersionView, recria_token from sapl.api.views_customize import SaplApiViewSetConstrutor from .apps import AppConfig - app_name = AppConfig.name - router = DefaultRouter() router.register(r'materia$', MateriaLegislativaViewSet) router.register(r'sessao-plenaria', SessaoPlenariaViewSet) - for app, built_sets in SaplApiViewSetConstrutor._built_sets.items(): for view_prefix, viewset in built_sets.items(): router.register(app.label + '/' + view_prefix._meta.model_name, viewset) - urlpatterns_router = router.urls urlpatterns_api_doc = [ @@ -38,28 +34,6 @@ urlpatterns_api_doc = [ # YOUR PATTERNS url('^schema/', SpectacularAPIView.as_view(), name='schema_api'), ] -"""if 'drf_yasg' in settings.INSTALLED_APPS: - from drf_yasg import openapi - from drf_yasg.views import get_schema_view - schema_view = get_schema_view( - openapi.Info( - title="Sapl API - docs", - default_version='v1', - description="Sapl API - Docs - Configuração Básica", - ), - url=settings.SITE_URL, - public=True, - permission_classes=(permissions.AllowAny,), - ) - - urlpatterns_api_doc = [ - url(r'^docs/swagger(?P\.json|\.yaml)$', - schema_view.without_ui(cache_timeout=0), name='schema-json'), - url(r'^docs/swagger/$', - schema_view.with_ui('swagger', cache_timeout=0), name='schema-swagger-ui'), - url(r'^docs/redoc/$', - schema_view.with_ui('redoc', cache_timeout=0), name='schema-redoc'), - ]""" # TODO: refatorar para customização da api automática deprecated_urlpatterns_api = [ @@ -67,13 +41,9 @@ deprecated_urlpatterns_api = [ 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'), - - ] urlpatterns = [ diff --git a/sapl/api/views_customize.py b/sapl/api/views_customize.py index 8336fdffe..b2b330990 100644 --- a/sapl/api/views_customize.py +++ b/sapl/api/views_customize.py @@ -8,24 +8,23 @@ from django.utils.translation import ugettext_lazy as _ from rest_framework.decorators import action from rest_framework.response import Response -from sapl.api.core import customize, SaplApiViewSetConstrutor,\ - wrapper_queryset_response_for_drf_action,\ +from sapl.api.core import customize, SaplApiViewSetConstrutor, \ + wrapper_queryset_response_for_drf_action, \ BusinessRulesNotImplementedMixin from sapl.api.permissions import SaplModelPermissions from sapl.api.serializers import ChoiceSerializer, \ ParlamentarEditSerializer, ParlamentarResumeSerializer from sapl.base.models import Autor, AppConfig, DOC_ADM_OSTENSIVO -from sapl.materia.models import Proposicao, TipoMateriaLegislativa,\ +from sapl.materia.models import Proposicao, TipoMateriaLegislativa, \ MateriaLegislativa, Tramitacao from sapl.norma.models import NormaJuridica from sapl.parlamentares.models import Mandato, Legislatura from sapl.parlamentares.models import Parlamentar -from sapl.protocoloadm.models import DocumentoAdministrativo,\ +from sapl.protocoloadm.models import DocumentoAdministrativo, \ DocumentoAcessorioAdministrativo, TramitacaoAdministrativo, Anexado from sapl.sessao.models import SessaoPlenaria, ExpedienteSessao from sapl.utils import models_with_gr_for_model, choice_anos_com_sessaoplenaria - SaplApiViewSetConstrutor = SaplApiViewSetConstrutor.build_class() @@ -33,7 +32,7 @@ SaplApiViewSetConstrutor = SaplApiViewSetConstrutor.build_class() class _AutorViewSet: # Customização para AutorViewSet com implementação de actions específicas """ - Neste exemplo de customização do que foi criado em + Nesta customização do que foi criado em SaplApiViewSetConstrutor além do ofertado por rest_framework.viewsets.ModelViewSet, dentre outras customizações possíveis, foi adicionado as rotas referentes aos relacionamentos genéricos @@ -100,7 +99,9 @@ class _AutorViewSet: @customize(Parlamentar) class _ParlamentarViewSet: + class ParlamentarPermission(SaplModelPermissions): + def has_permission(self, request, view): if request.method == 'GET': return True @@ -108,7 +109,7 @@ class _ParlamentarViewSet: perm = super().has_permission(request, view) return perm - permission_classes = (ParlamentarPermission, ) + permission_classes = (ParlamentarPermission,) def get_serializer(self, *args, **kwargs): if self.request.user.has_perm('parlamentares.add_parlamentar'): @@ -219,7 +220,9 @@ class _ProposicaoViewSet: * Pode recuperar qualquer das proposições incorporadas """ + class ProposicaoPermission(SaplModelPermissions): + def has_permission(self, request, view): if request.method == 'GET': return True @@ -234,7 +237,7 @@ class _ProposicaoViewSet: # não é list ou detail, então passa pelas regras de permissão e, # depois disso ainda passa pelo filtro de get_queryset - permission_classes = (ProposicaoPermission, ) + permission_classes = (ProposicaoPermission,) def get_queryset(self): qs = super().get_queryset() @@ -259,6 +262,7 @@ class _ProposicaoViewSet: @customize(MateriaLegislativa) class _MateriaLegislativaViewSet: + class Meta: ordering = ['-ano', 'tipo', 'numero'] @@ -305,6 +309,7 @@ class _TipoMateriaLegislativaViewSet: class _DocumentoAdministrativoViewSet: class DocumentoAdministrativoPermission(SaplModelPermissions): + def has_permission(self, request, view): if request.method == 'GET': comportamento = AppConfig.attr('documentos_administrativos') @@ -320,7 +325,7 @@ class _DocumentoAdministrativoViewSet: """ return super().has_permission(request, view) - permission_classes = (DocumentoAdministrativoPermission, ) + permission_classes = (DocumentoAdministrativoPermission,) def get_queryset(self): """ @@ -340,7 +345,7 @@ class _DocumentoAdministrativoViewSet: class _DocumentoAcessorioAdministrativoViewSet: permission_classes = ( - _DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission, ) + _DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission,) def get_queryset(self): qs = super().get_queryset() @@ -355,7 +360,7 @@ class _TramitacaoAdministrativoViewSet(BusinessRulesNotImplementedMixin): # TODO: Implementar regras de manutenção das tramitações de docs adms permission_classes = ( - _DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission, ) + _DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission,) def get_queryset(self): qs = super().get_queryset() @@ -369,7 +374,7 @@ class _TramitacaoAdministrativoViewSet(BusinessRulesNotImplementedMixin): class _AnexadoViewSet(BusinessRulesNotImplementedMixin): permission_classes = ( - _DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission, ) + _DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission,) def get_queryset(self): qs = super().get_queryset()