diff --git a/drfautoapi/drfautoapi.py b/drfautoapi/drfautoapi.py index 1a5dd65de..cca4eacdb 100644 --- a/drfautoapi/drfautoapi.py +++ b/drfautoapi/drfautoapi.py @@ -125,6 +125,7 @@ class ApiFilterSetMixin(FilterSet): class BusinessRulesNotImplementedMixin: + http_method_names = ['get', 'head', 'options', 'trace'] def create(self, request, *args, **kwargs): raise Exception(_("POST Create não implementado")) @@ -138,15 +139,19 @@ class BusinessRulesNotImplementedMixin: class ApiViewSetConstrutor(): + _built_sets = {} + class ApiViewSet(ModelViewSet): filter_backends = (DjangoFilterBackend,) - _built_sets = {} - @classmethod def get_class_for_model(cls, model): return cls._built_sets[model._meta.app_config][model] + @classmethod + def update(cls, other): + cls._built_sets.update(other._built_sets) + @classmethod def build_class(cls, apps): diff --git a/sapl/api/views.py b/sapl/api/views.py index ab629c435..9999af462 100644 --- a/sapl/api/views.py +++ b/sapl/api/views.py @@ -1,34 +1,23 @@ import logging -from django.apps.registry import apps from django.conf import settings -from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import ObjectDoesNotExist -from django.db.models import Q -from django.utils.translation import ugettext_lazy as _ from rest_framework.authtoken.models import Token -from rest_framework.decorators import action from rest_framework.decorators import api_view, permission_classes from rest_framework.permissions import IsAuthenticated, IsAdminUser from rest_framework.response import Response from rest_framework.views import APIView -from drfautoapi.drfautoapi import customize, ApiViewSetConstrutor, \ - wrapper_queryset_response_for_drf_action, \ - BusinessRulesNotImplementedMixin -from sapl.api.permissions import SaplModelPermissions -from sapl.api.serializers import ParlamentarSerializerVerbose, \ - ParlamentarSerializerPublic, ChoiceSerializer -from sapl.base.models import Autor, AppConfig, DOC_ADM_OSTENSIVO -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, \ - DocumentoAcessorioAdministrativo, TramitacaoAdministrativo, Anexado -from sapl.sessao.models import SessaoPlenaria, ExpedienteSessao -from sapl.utils import models_with_gr_for_model, choice_anos_com_sessaoplenaria +from drfautoapi.drfautoapi import ApiViewSetConstrutor +from sapl.api.views_audiencia import AudienciaApiViewSetConstrutor +from sapl.api.views_base import BaseApiViewSetConstrutor +from sapl.api.views_comissoes import ComissoesApiViewSetConstrutor +from sapl.api.views_compilacao import CompilacaoApiViewSetConstrutor +from sapl.api.views_materia import MateriaApiViewSetConstrutor +from sapl.api.views_norma import NormaApiViewSetConstrutor +from sapl.api.views_painel import PainelApiViewSetConstrutor +from sapl.api.views_parlamentares import ParlamentaresApiViewSetConstrutor +from sapl.api.views_protocoloadm import ProtocoloAdmApiViewSetConstrutor +from sapl.api.views_sessao import SessaoApiViewSetConstrutor logger = logging.getLogger(__name__) @@ -57,13 +46,19 @@ class AppVersionView(APIView): return Response(content) -SaplApiViewSetConstrutor = ApiViewSetConstrutor.build_class( - [ - apps.get_app_config('contenttypes') - ] + [ - apps.get_app_config(n[5:]) for n in settings.SAPL_APPS - ] -) +SaplApiViewSetConstrutor = ApiViewSetConstrutor +SaplApiViewSetConstrutor.update(AudienciaApiViewSetConstrutor) +SaplApiViewSetConstrutor.update(BaseApiViewSetConstrutor) +SaplApiViewSetConstrutor.update(ComissoesApiViewSetConstrutor) +SaplApiViewSetConstrutor.update(CompilacaoApiViewSetConstrutor) +SaplApiViewSetConstrutor.update(MateriaApiViewSetConstrutor) +SaplApiViewSetConstrutor.update(NormaApiViewSetConstrutor) +SaplApiViewSetConstrutor.update(PainelApiViewSetConstrutor) +SaplApiViewSetConstrutor.update(ParlamentaresApiViewSetConstrutor) +SaplApiViewSetConstrutor.update(ProtocoloAdmApiViewSetConstrutor) +SaplApiViewSetConstrutor.update(SessaoApiViewSetConstrutor) + + """ 1. ApiViewSetConstrutor constroi uma rest_framework.viewsets.ModelViewSet para todos os models de todas as app_configs passadas no list @@ -127,392 +122,3 @@ SaplApiViewSetConstrutor = ApiViewSetConstrutor.build_class( } """ - - -@customize(ContentType) -class _ContentTypeSet: - http_method_names = ['get', 'head', 'options', 'trace'] - - -@customize(Parlamentar) -class _ParlamentarViewSet: - - class ParlamentarPermission(SaplModelPermissions): - - def has_permission(self, request, view): - if request.method == 'GET': - return True - else: - perm = super().has_permission(request, view) - return perm - - permission_classes = (ParlamentarPermission,) - - def get_serializer(self, *args, **kwargs): - if not self.request.user.has_perm('parlamentares.add_parlamentar'): - self.serializer_class = ParlamentarSerializerPublic - return super().get_serializer(*args, **kwargs) - - @action(detail=True) - def proposicoes(self, request, *args, **kwargs): - """ - Lista de proposições públicas de parlamentar específico - - :param int id: - Identificador do parlamentar que se quer recuperar as proposições - :return: uma lista de proposições - """ - # /api/parlamentares/parlamentar/{id}/proposicoes/ - # recupera proposições enviadas e incorporadas do parlamentar - # deve coincidir com - # /parlamentar/{pk}/proposicao - - return self.get_proposicoes(**kwargs) - - @wrapper_queryset_response_for_drf_action(model=Proposicao) - def get_proposicoes(self, **kwargs): - - return self.get_queryset().filter( - data_envio__isnull=False, - data_recebimento__isnull=False, - cancelado=False, - autor__object_id=kwargs['pk'], - autor__content_type=ContentType.objects.get_for_model(Parlamentar) - ) - - @action(detail=False, methods=['GET']) - def search_parlamentares(self, request, *args, **kwargs): - nome = request.query_params.get('nome_parlamentar', '') - parlamentares = Parlamentar.objects.filter( - nome_parlamentar__icontains=nome) - serializer_class = ParlamentarSerializerVerbose( - parlamentares, many=True, context={'request': request}) - return Response(serializer_class.data) - - -@customize(Legislatura) -class _LegislaturaViewSet: - - @action(detail=True) - def parlamentares(self, request, *args, **kwargs): - - def get_serializer_context(): - return { - 'request': self.request, 'legislatura': kwargs['pk'] - } - - def get_serializer_class(): - return ParlamentarSerializerVerbose - - self.get_serializer_context = get_serializer_context - self.get_serializer_class = get_serializer_class - - return self.get_parlamentares() - - @wrapper_queryset_response_for_drf_action(model=Parlamentar) - def get_parlamentares(self): - - try: - legislatura = Legislatura.objects.get(pk=self.kwargs['pk']) - except ObjectDoesNotExist: - return Response("") - - filter_params = { - 'legislatura': legislatura, - 'data_inicio_mandato__gte': legislatura.data_inicio, - 'data_fim_mandato__lte': legislatura.data_fim, - } - - mandatos = Mandato.objects.filter( - **filter_params).order_by('-data_inicio_mandato') - - parlamentares = self.get_queryset().filter( - mandato__in=mandatos).distinct() - - return parlamentares - - -@customize(Proposicao) -class _ProposicaoViewSet: - """ - list: - Retorna lista de Proposições - - * Permissões: - - * Usuário Dono: - * Pode listar todas suas Proposições - - * Usuário Conectado ou Anônimo: - * Pode listar todas as Proposições incorporadas - - retrieve: - Retorna uma proposição passada pelo 'id' - - * Permissões: - - * Usuário Dono: - * Pode recuperar qualquer de suas Proposições - - * Usuário Conectado ou Anônimo: - * Pode recuperar qualquer das proposições incorporadas - - """ - - class ProposicaoPermission(SaplModelPermissions): - - def has_permission(self, request, view): - if request.method == 'GET': - return True - # se a solicitação é list ou detail, libera o teste de permissão - # e deixa o get_queryset filtrar de acordo com a regra de - # visibilidade das proposições, ou seja: - # 1. proposição incorporada é proposição pública - # 2. não incorporada só o autor pode ver - else: - perm = super().has_permission(request, view) - return perm - # 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,) - - def get_queryset(self): - qs = super().get_queryset() - - q = Q(data_recebimento__isnull=False, object_id__isnull=False) - if not self.request.user.is_anonymous: - - autor_do_usuario_logado = self.request.user.autor_set.first() - - # se usuário logado é operador de algum autor - if autor_do_usuario_logado: - q = Q(autor=autor_do_usuario_logado) - - # se é operador de protocolo, ve qualquer coisa enviada - if self.request.user.has_perm('protocoloadm.list_protocolo'): - q = Q(data_envio__isnull=False) | Q( - data_devolucao__isnull=False) - - qs = qs.filter(q) - return qs - - -@customize(MateriaLegislativa) -class _MateriaLegislativaViewSet: - - class Meta: - ordering = ['-ano', 'tipo', 'numero'] - - @action(detail=True, methods=['GET']) - def ultima_tramitacao(self, request, *args, **kwargs): - - materia = self.get_object() - if not materia.tramitacao_set.exists(): - return Response({}) - - ultima_tramitacao = materia.tramitacao_set.order_by( - '-data_tramitacao', '-id').first() - - serializer_class = SaplApiViewSetConstrutor.get_class_for_model( - Tramitacao).serializer_class(ultima_tramitacao) - - return Response(serializer_class.data) - - @action(detail=True, methods=['GET']) - def anexadas(self, request, *args, **kwargs): - self.queryset = self.get_object().anexadas.all() - return self.list(request, *args, **kwargs) - - -@customize(TipoMateriaLegislativa) -class _TipoMateriaLegislativaViewSet: - - @action(detail=True, methods=['POST']) - def change_position(self, request, *args, **kwargs): - result = { - 'status': 200, - 'message': 'OK' - } - d = request.data - if 'pos_ini' in d and 'pos_fim' in d: - if d['pos_ini'] != d['pos_fim']: - pk = kwargs['pk'] - TipoMateriaLegislativa.objects.reposicione(pk, d['pos_fim']) - - return Response(result) - - -@customize(DocumentoAdministrativo) -class _DocumentoAdministrativoViewSet: - - class DocumentoAdministrativoPermission(SaplModelPermissions): - - def has_permission(self, request, view): - if request.method == 'GET': - comportamento = AppConfig.attr('documentos_administrativos') - if comportamento == DOC_ADM_OSTENSIVO: - return True - """ - Diante da lógica implementada na manutenção de documentos - administrativos: - - Se o comportamento é doc adm ostensivo, deve passar pelo - teste de permissões sem avaliá-las - - se o comportamento é doc adm restritivo, deve passar pelo - teste de permissões avaliando-as - """ - return super().has_permission(request, view) - - permission_classes = (DocumentoAdministrativoPermission,) - - def get_queryset(self): - """ - mesmo tendo passado pelo teste de permissões, deve ser filtrado, - pelo campo restrito. Sendo este igual a True, disponibilizar apenas - a um usuário conectado. Apenas isso, sem critérios outros de permissão, - conforme implementado em DocumentoAdministrativoCrud - """ - qs = super().get_queryset() - - if self.request.user.is_anonymous: - qs = qs.exclude(restrito=True) - return qs - - -@customize(DocumentoAcessorioAdministrativo) -class _DocumentoAcessorioAdministrativoViewSet: - - permission_classes = ( - _DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission,) - - def get_queryset(self): - qs = super().get_queryset() - - if self.request.user.is_anonymous: - qs = qs.exclude(documento__restrito=True) - return qs - - -@customize(TramitacaoAdministrativo) -class _TramitacaoAdministrativoViewSet(BusinessRulesNotImplementedMixin): - # TODO: Implementar regras de manutenção das tramitações de docs adms - - permission_classes = ( - _DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission,) - - def get_queryset(self): - qs = super().get_queryset() - - if self.request.user.is_anonymous: - qs = qs.exclude(documento__restrito=True) - return qs - - -@customize(Anexado) -class _AnexadoViewSet(BusinessRulesNotImplementedMixin): - - permission_classes = ( - _DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission,) - - def get_queryset(self): - qs = super().get_queryset() - - if self.request.user.is_anonymous: - qs = qs.exclude(documento__restrito=True) - return qs - - -@customize(SessaoPlenaria) -class _SessaoPlenariaViewSet: - - @action(detail=False) - def years(self, request, *args, **kwargs): - years = choice_anos_com_sessaoplenaria() - - serializer = ChoiceSerializer(years, many=True) - return Response(serializer.data) - - @action(detail=True) - def expedientes(self, request, *args, **kwargs): - return self.get_expedientes() - - @wrapper_queryset_response_for_drf_action(model=ExpedienteSessao) - def get_expedientes(self): - return self.get_queryset().filter(sessao_plenaria_id=self.kwargs['pk']) - - -@customize(NormaJuridica) -class _NormaJuridicaViewset: - - @action(detail=False, methods=['GET']) - def destaques(self, request, *args, **kwargs): - self.queryset = self.get_queryset().filter(norma_de_destaque=True) - return self.list(request, *args, **kwargs) - - -@customize(Autor) -class _AutorViewSet: - # Customização para AutorViewSet com implementação de actions específicas - """ - 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 - - * padrão de ModelViewSet - /api/base/autor/ POST - create - /api/base/autor/ GET - list - /api/base/autor/{pk}/ GET - detail - /api/base/autor/{pk}/ PUT - update - /api/base/autor/{pk}/ PATCH - partial_update - /api/base/autor/{pk}/ DELETE - destroy - - * rotas desta classe local criadas pelo método build: - /api/base/autor/parlamentar - devolve apenas autores que são parlamentares - /api/base/autor/comissao - devolve apenas autores que são comissões - /api/base/autor/bloco - devolve apenas autores que são blocos parlamentares - /api/base/autor/bancada - devolve apenas autores que são bancadas parlamentares - /api/base/autor/frente - devolve apenas autores que são Frene parlamentares - /api/base/autor/orgao - devolve apenas autores que são Órgãos - """ - - def list_for_content_type(self, content_type): - qs = self.get_queryset() - qs = qs.filter(content_type=content_type) - - page = self.paginate_queryset(qs) - if page is not None: - serializer = self.serializer_class(page, many=True) - return self.get_paginated_response(serializer.data) - - serializer = self.get_serializer(page, many=True) - return Response(serializer.data) - - @classmethod - def build(cls): - - models_with_gr_for_autor = models_with_gr_for_model(Autor) - - for _model in models_with_gr_for_autor: - - @action(detail=False, name=_model._meta.model_name) - def actionclass(self, request, *args, **kwargs): - model = getattr(self, self.action)._AutorViewSet__model - - content_type = ContentType.objects.get_for_model(model) - return self.list_for_content_type(content_type) - - func = actionclass - func.mapping['get'] = func.kwargs['name'] - func.url_name = func.kwargs['name'] - func.url_path = func.kwargs['name'] - func.__name__ = func.kwargs['name'] - func.__model = _model - - setattr(cls, _model._meta.model_name, func) - return cls diff --git a/sapl/api/views_audiencia.py b/sapl/api/views_audiencia.py new file mode 100644 index 000000000..c636be92f --- /dev/null +++ b/sapl/api/views_audiencia.py @@ -0,0 +1,11 @@ + +from django.apps.registry import apps + +from drfautoapi.drfautoapi import ApiViewSetConstrutor, \ + customize, wrapper_queryset_response_for_drf_action + +AudienciaApiViewSetConstrutor = ApiViewSetConstrutor.build_class( + [ + apps.get_app_config('audiencia') + ] +) diff --git a/sapl/api/views_base.py b/sapl/api/views_base.py new file mode 100644 index 000000000..8d08de92a --- /dev/null +++ b/sapl/api/views_base.py @@ -0,0 +1,89 @@ +from django.apps.registry import apps +from django.contrib.contenttypes.models import ContentType +from rest_framework.decorators import action +from rest_framework.response import Response + +from drfautoapi.drfautoapi import ApiViewSetConstrutor, customize +from sapl.base.models import Autor +from sapl.utils import models_with_gr_for_model + + +BaseApiViewSetConstrutor = ApiViewSetConstrutor.build_class( + [ + apps.get_app_config('contenttypes'), + apps.get_app_config('base') + ] +) + + +@customize(ContentType) +class _ContentTypeSet: + http_method_names = ['get', 'head', 'options', 'trace'] + + +@customize(Autor) +class _AutorViewSet: + """ + Nesta customização do que foi criado em + BaseApiViewSetConstrutor além do ofertado por + rest_framework.viewsets.ModelViewSet, dentre outras customizações + possíveis, foi adicionado as rotas referentes aos relacionamentos genéricos + + * padrão de ModelViewSet + * /api/base/autor/ POST - create + * /api/base/autor/ GET - list + * /api/base/autor/{pk}/ GET - detail + * /api/base/autor/{pk}/ PUT - update + * /api/base/autor/{pk}/ PATCH - partial_update + * /api/base/autor/{pk}/ DELETE - destroy + + * rotas desta classe local criadas pelo método build local: + * /api/base/autor/parlamentar + devolve apenas autores que são parlamentares + * /api/base/autor/comissao + devolve apenas autores que são comissões + * /api/base/autor/bloco + devolve apenas autores que são blocos parlamentares + * /api/base/autor/bancada + devolve apenas autores que são bancadas parlamentares + * /api/base/autor/frente + devolve apenas autores que são Frene parlamentares + * /api/base/autor/orgao + devolve apenas autores que são Órgãos + """ + + def list_for_content_type(self, content_type): + qs = self.get_queryset() + qs = qs.filter(content_type=content_type) + + page = self.paginate_queryset(qs) + if page is not None: + serializer = self.serializer_class(page, many=True) + return self.get_paginated_response(serializer.data) + + serializer = self.get_serializer(page, many=True) + return Response(serializer.data) + + @classmethod + def build(cls): + + models_with_gr_for_autor = models_with_gr_for_model(Autor) + + for _model in models_with_gr_for_autor: + + @action(detail=False, name=_model._meta.model_name) + def actionclass(self, request, *args, **kwargs): + model = getattr(self, self.action)._AutorViewSet__model + + content_type = ContentType.objects.get_for_model(model) + return self.list_for_content_type(content_type) + + func = actionclass + func.mapping['get'] = func.kwargs['name'] + func.url_name = func.kwargs['name'] + func.url_path = func.kwargs['name'] + func.__name__ = func.kwargs['name'] + func.__model = _model + + setattr(cls, _model._meta.model_name, func) + return cls diff --git a/sapl/api/views_comissoes.py b/sapl/api/views_comissoes.py new file mode 100644 index 000000000..76a9b7baa --- /dev/null +++ b/sapl/api/views_comissoes.py @@ -0,0 +1,12 @@ + +from django.apps.registry import apps + +from drfautoapi.drfautoapi import ApiViewSetConstrutor, \ + customize, wrapper_queryset_response_for_drf_action + + +ComissoesApiViewSetConstrutor = ApiViewSetConstrutor.build_class( + [ + apps.get_app_config('comissoes') + ] +) diff --git a/sapl/api/views_compilacao.py b/sapl/api/views_compilacao.py new file mode 100644 index 000000000..96225c357 --- /dev/null +++ b/sapl/api/views_compilacao.py @@ -0,0 +1,12 @@ + +from django.apps.registry import apps + +from drfautoapi.drfautoapi import ApiViewSetConstrutor, \ + customize, wrapper_queryset_response_for_drf_action + + +CompilacaoApiViewSetConstrutor = ApiViewSetConstrutor.build_class( + [ + apps.get_app_config('compilacao') + ] +) diff --git a/sapl/api/views_materia.py b/sapl/api/views_materia.py new file mode 100644 index 000000000..5f54e3b8c --- /dev/null +++ b/sapl/api/views_materia.py @@ -0,0 +1,129 @@ + +from django.apps.registry import apps +from django.db.models import Q +from rest_framework.decorators import action +from rest_framework.response import Response + +from drfautoapi.drfautoapi import ApiViewSetConstrutor, \ + customize, wrapper_queryset_response_for_drf_action +from sapl.api.permissions import SaplModelPermissions +from sapl.materia.models import TipoMateriaLegislativa, Tramitacao,\ + MateriaLegislativa, Proposicao + + +MateriaApiViewSetConstrutor = ApiViewSetConstrutor.build_class( + [ + apps.get_app_config('materia') + ] +) + + +@customize(Proposicao) +class _ProposicaoViewSet: + """ + list: + Retorna lista de Proposições + + * Permissões: + + * Usuário Dono: + * Pode listar todas suas Proposições + + * Usuário Conectado ou Anônimo: + * Pode listar todas as Proposições incorporadas + + retrieve: + Retorna uma proposição passada pelo 'id' + + * Permissões: + + * Usuário Dono: + * Pode recuperar qualquer de suas Proposições + + * Usuário Conectado ou Anônimo: + * Pode recuperar qualquer das proposições incorporadas + + """ + + class ProposicaoPermission(SaplModelPermissions): + + def has_permission(self, request, view): + if request.method == 'GET': + return True + # se a solicitação é list ou detail, libera o teste de permissão + # e deixa o get_queryset filtrar de acordo com a regra de + # visibilidade das proposições, ou seja: + # 1. proposição incorporada é proposição pública + # 2. não incorporada só o autor pode ver + else: + perm = super().has_permission(request, view) + return perm + # 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,) + + def get_queryset(self): + qs = super().get_queryset() + + q = Q(data_recebimento__isnull=False, object_id__isnull=False) + if not self.request.user.is_anonymous: + + autor_do_usuario_logado = self.request.user.autor_set.first() + + # se usuário logado é operador de algum autor + if autor_do_usuario_logado: + q = Q(autor=autor_do_usuario_logado) + + # se é operador de protocolo, ve qualquer coisa enviada + if self.request.user.has_perm('protocoloadm.list_protocolo'): + q = Q(data_envio__isnull=False) | Q( + data_devolucao__isnull=False) + + qs = qs.filter(q) + return qs + + +@customize(MateriaLegislativa) +class _MateriaLegislativaViewSet: + + class Meta: + ordering = ['-ano', 'tipo', 'numero'] + + @action(detail=True, methods=['GET']) + def ultima_tramitacao(self, request, *args, **kwargs): + + materia = self.get_object() + if not materia.tramitacao_set.exists(): + return Response({}) + + ultima_tramitacao = materia.tramitacao_set.order_by( + '-data_tramitacao', '-id').first() + + serializer_class = MateriaApiViewSetConstrutor.get_class_for_model( + Tramitacao).serializer_class(ultima_tramitacao) + + return Response(serializer_class.data) + + @action(detail=True, methods=['GET']) + def anexadas(self, request, *args, **kwargs): + self.queryset = self.get_object().anexadas.all() + return self.list(request, *args, **kwargs) + + +@customize(TipoMateriaLegislativa) +class _TipoMateriaLegislativaViewSet: + + @action(detail=True, methods=['POST']) + def change_position(self, request, *args, **kwargs): + result = { + 'status': 200, + 'message': 'OK' + } + d = request.data + if 'pos_ini' in d and 'pos_fim' in d: + if d['pos_ini'] != d['pos_fim']: + pk = kwargs['pk'] + TipoMateriaLegislativa.objects.reposicione(pk, d['pos_fim']) + + return Response(result) diff --git a/sapl/api/views_norma.py b/sapl/api/views_norma.py new file mode 100644 index 000000000..5810a85aa --- /dev/null +++ b/sapl/api/views_norma.py @@ -0,0 +1,14 @@ + +from django.apps.registry import apps +from rest_framework.decorators import action + +from drfautoapi.drfautoapi import ApiViewSetConstrutor, \ + customize, wrapper_queryset_response_for_drf_action +from sapl.norma.models import NormaJuridica + + +NormaApiViewSetConstrutor = ApiViewSetConstrutor.build_class( + [ + apps.get_app_config('norma') + ] +) diff --git a/sapl/api/views_painel.py b/sapl/api/views_painel.py new file mode 100644 index 000000000..d2c78937b --- /dev/null +++ b/sapl/api/views_painel.py @@ -0,0 +1,11 @@ + +from django.apps.registry import apps + +from drfautoapi.drfautoapi import ApiViewSetConstrutor, \ + customize, wrapper_queryset_response_for_drf_action + +PainelApiViewSetConstrutor = ApiViewSetConstrutor.build_class( + [ + apps.get_app_config('painel') + ] +) diff --git a/sapl/api/views_parlamentares.py b/sapl/api/views_parlamentares.py new file mode 100644 index 000000000..e8db3a245 --- /dev/null +++ b/sapl/api/views_parlamentares.py @@ -0,0 +1,119 @@ + +from django.apps.registry import apps +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ObjectDoesNotExist +from rest_framework.decorators import action +from rest_framework.response import Response + +from drfautoapi.drfautoapi import customize, ApiViewSetConstrutor, \ + wrapper_queryset_response_for_drf_action + +from sapl.api.permissions import SaplModelPermissions +from sapl.api.serializers import ParlamentarSerializerVerbose, \ + ParlamentarSerializerPublic +from sapl.materia.models import Proposicao +from sapl.parlamentares.models import Mandato, Legislatura +from sapl.parlamentares.models import Parlamentar + +ParlamentaresApiViewSetConstrutor = ApiViewSetConstrutor.build_class( + [ + apps.get_app_config('parlamentares') + ] +) + + +@customize(Parlamentar) +class _ParlamentarViewSet: + + class ParlamentarPermission(SaplModelPermissions): + + def has_permission(self, request, view): + if request.method == 'GET': + return True + else: + perm = super().has_permission(request, view) + return perm + + permission_classes = (ParlamentarPermission,) + + def get_serializer(self, *args, **kwargs): + if not self.request.user.has_perm('parlamentares.add_parlamentar'): + self.serializer_class = ParlamentarSerializerPublic + return super().get_serializer(*args, **kwargs) + + @action(detail=True) + def proposicoes(self, request, *args, **kwargs): + """ + Lista de proposições públicas de parlamentar específico + + :param int id: - Identificador do parlamentar que se quer recuperar as proposições + :return: uma lista de proposições + """ + # /api/parlamentares/parlamentar/{id}/proposicoes/ + # recupera proposições enviadas e incorporadas do parlamentar + # deve coincidir com + # /parlamentar/{pk}/proposicao + + return self.get_proposicoes(**kwargs) + + @wrapper_queryset_response_for_drf_action(model=Proposicao) + def get_proposicoes(self, **kwargs): + + return self.get_queryset().filter( + data_envio__isnull=False, + data_recebimento__isnull=False, + cancelado=False, + autor__object_id=kwargs['pk'], + autor__content_type=ContentType.objects.get_for_model(Parlamentar) + ) + + @action(detail=False, methods=['GET']) + def search_parlamentares(self, request, *args, **kwargs): + nome = request.query_params.get('nome_parlamentar', '') + parlamentares = Parlamentar.objects.filter( + nome_parlamentar__icontains=nome) + serializer_class = ParlamentarSerializerVerbose( + parlamentares, many=True, context={'request': request}) + return Response(serializer_class.data) + + +@customize(Legislatura) +class _LegislaturaViewSet: + + @action(detail=True) + def parlamentares(self, request, *args, **kwargs): + + def get_serializer_context(): + return { + 'request': self.request, 'legislatura': kwargs['pk'] + } + + def get_serializer_class(): + return ParlamentarSerializerVerbose + + self.get_serializer_context = get_serializer_context + self.get_serializer_class = get_serializer_class + + return self.get_parlamentares() + + @wrapper_queryset_response_for_drf_action(model=Parlamentar) + def get_parlamentares(self): + + try: + legislatura = Legislatura.objects.get(pk=self.kwargs['pk']) + except ObjectDoesNotExist: + return Response("") + + filter_params = { + 'legislatura': legislatura, + 'data_inicio_mandato__gte': legislatura.data_inicio, + 'data_fim_mandato__lte': legislatura.data_fim, + } + + mandatos = Mandato.objects.filter( + **filter_params).order_by('-data_inicio_mandato') + + parlamentares = self.get_queryset().filter( + mandato__in=mandatos).distinct() + + return parlamentares diff --git a/sapl/api/views_protocoloadm.py b/sapl/api/views_protocoloadm.py new file mode 100644 index 000000000..ac33cabab --- /dev/null +++ b/sapl/api/views_protocoloadm.py @@ -0,0 +1,102 @@ + +from django.apps.registry import apps + +from drfautoapi.drfautoapi import ApiViewSetConstrutor, \ + customize, wrapper_queryset_response_for_drf_action +from sapl.api.permissions import SaplModelPermissions +from sapl.base.models import AppConfig, DOC_ADM_OSTENSIVO +from sapl.protocoloadm.models import DocumentoAdministrativo, \ + DocumentoAcessorioAdministrativo, TramitacaoAdministrativo, Anexado + + +ProtocoloAdmApiViewSetConstrutor = ApiViewSetConstrutor.build_class( + [ + apps.get_app_config('protocoloadm') + ] +) + + +@customize(DocumentoAdministrativo) +class _DocumentoAdministrativoViewSet: + + class DocumentoAdministrativoPermission(SaplModelPermissions): + + def has_permission(self, request, view): + if request.method == 'GET': + comportamento = AppConfig.attr('documentos_administrativos') + if comportamento == DOC_ADM_OSTENSIVO: + return True + """ + Diante da lógica implementada na manutenção de documentos + administrativos: + - Se o comportamento é doc adm ostensivo, deve passar pelo + teste de permissões sem avaliá-las + - se o comportamento é doc adm restritivo, deve passar pelo + teste de permissões avaliando-as + """ + return super().has_permission(request, view) + + permission_classes = (DocumentoAdministrativoPermission,) + + def get_queryset(self): + """ + mesmo tendo passado pelo teste de permissões, deve ser filtrado, + pelo campo restrito. Sendo este igual a True, disponibilizar apenas + a um usuário conectado. Apenas isso, sem critérios outros de permissão, + conforme implementado em DocumentoAdministrativoCrud + """ + qs = super().get_queryset() + + if self.request.user.is_anonymous: + qs = qs.exclude(restrito=True) + return qs + + +@customize(DocumentoAcessorioAdministrativo) +class _DocumentoAcessorioAdministrativoViewSet: + + permission_classes = ( + _DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission,) + + def get_queryset(self): + qs = super().get_queryset() + + if self.request.user.is_anonymous: + qs = qs.exclude(documento__restrito=True) + return qs + + +@customize(TramitacaoAdministrativo) +class _TramitacaoAdministrativoViewSet: + # TODO: Implementar regras de manutenção das post, put, patch + # tramitacação de adm possui regras previstas de limitação de origem + # destino + + http_method_names = ['get', 'head', 'options', 'trace'] + + permission_classes = ( + _DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission,) + + def get_queryset(self): + qs = super().get_queryset() + + if self.request.user.is_anonymous: + qs = qs.exclude(documento__restrito=True) + return qs + + +@customize(Anexado) +class _AnexadoViewSet: + # TODO: Implementar regras de manutenção post, put, patch + # anexado deve possuir controle que impeça anexação cíclica + http_method_names = ['get', 'head', 'options', 'trace'] + + permission_classes = ( + _DocumentoAdministrativoViewSet.DocumentoAdministrativoPermission,) + + def get_queryset(self): + qs = super().get_queryset() + + if self.request.user.is_anonymous: + qs = qs.exclude(documento__restrito=True) + return qs diff --git a/sapl/api/views_sessao.py b/sapl/api/views_sessao.py new file mode 100644 index 000000000..fc6f20a5b --- /dev/null +++ b/sapl/api/views_sessao.py @@ -0,0 +1,36 @@ + +from django.apps.registry import apps +from rest_framework.decorators import action +from rest_framework.response import Response + +from drfautoapi.drfautoapi import ApiViewSetConstrutor, \ + customize, wrapper_queryset_response_for_drf_action +from sapl.api.serializers import ChoiceSerializer +from sapl.sessao.models import SessaoPlenaria, ExpedienteSessao +from sapl.utils import choice_anos_com_sessaoplenaria + + +SessaoApiViewSetConstrutor = ApiViewSetConstrutor.build_class( + [ + apps.get_app_config('sessao') + ] +) + + +@customize(SessaoPlenaria) +class _SessaoPlenariaViewSet: + + @action(detail=False) + def years(self, request, *args, **kwargs): + years = choice_anos_com_sessaoplenaria() + + serializer = ChoiceSerializer(years, many=True) + return Response(serializer.data) + + @action(detail=True) + def expedientes(self, request, *args, **kwargs): + return self.get_expedientes() + + @wrapper_queryset_response_for_drf_action(model=ExpedienteSessao) + def get_expedientes(self): + return self.get_queryset().filter(sessao_plenaria_id=self.kwargs['pk'])