Browse Source

Add documentação e faz modificações na api/autor

pull/739/head
LeandroRoberto 8 years ago
parent
commit
877175a51f
  1. 26
      sapl/api/forms.py
  2. 38
      sapl/api/serializers.py
  3. 101
      sapl/api/views.py
  4. 28
      sapl/base/forms.py
  5. 10
      sapl/compilacao/views.py
  6. 6
      sapl/crud/base.py
  7. 39
      sapl/settings.py
  8. 2
      sapl/templates/base/autor_form.html
  9. 55
      sapl/utils.py

26
sapl/api/forms.py

@ -3,35 +3,11 @@ from django.db.models import Q
from django_filters.filters import MethodFilter, ModelChoiceFilter
from rest_framework.filters import FilterSet
from sapl.base.forms import autores_models_generic_relations
from sapl.base.models import Autor, TipoAutor
from sapl.utils import SaplGenericRelation
def autores_models_generic_relations():
models_of_generic_relations = list(map(
lambda x: x.related_model,
filter(
lambda obj: obj.is_relation and
hasattr(obj, 'field') and
isinstance(obj, GenericRel),
Autor._meta.get_fields(include_hidden=True))
))
models = list(map(
lambda x: (x,
list(filter(
lambda field: (
isinstance(
field, SaplGenericRelation) and
field.related_model == Autor),
x._meta.get_fields(include_hidden=True)))),
models_of_generic_relations
))
return models
class AutorChoiceFilterSet(FilterSet):
q = MethodFilter()
tipo = ModelChoiceFilter(queryset=TipoAutor.objects.all())

38
sapl/api/serializers.py

@ -26,52 +26,16 @@ class AutorChoiceSerializer(ChoiceSerializer):
class Meta:
model = Autor
fields = ['id', 'tipo', 'nome', 'object_id', 'autor_related', 'user']
# Models que apontaram uma GenericRelation com Autor
def autores_models_generic_relations():
models_of_generic_relations = list(map(
lambda x: x.related_model,
filter(
lambda obj: obj.is_relation and
hasattr(obj, 'field') and
isinstance(obj, GenericRel),
Autor._meta.get_fields(include_hidden=True))
))
models = list(map(
lambda x: (x,
list(filter(
lambda field: (
isinstance(
field, SaplGenericRelation) and
field.related_model == Autor),
x._meta.get_fields(include_hidden=True)))),
models_of_generic_relations
))
return models
fields = ['id', 'nome']
class AutorObjectRelatedField(serializers.RelatedField):
def to_representation(self, value):
return str(value)
for gr in autores_models_generic_relations():
if isinstance(value, gr[0]):
verbose_name = gr[0]._meta.verbose_name
fields_search = gr[1][0].fields_search
raise Exception(_('Erro na seleção de autor'))
class AutorSerializer(serializers.ModelSerializer):
autor_related = AutorObjectRelatedField(read_only=True)
class Meta:

101
sapl/api/views.py

@ -1,15 +1,16 @@
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
from rest_framework.permissions import IsAuthenticated, AllowAny
from sapl.api.forms import AutorChoiceFilterSet
from sapl.api.serializers import ChoiceSerializer, AutorSerializer,\
AutorChoiceSerializer
from sapl.base.models import Autor, TipoAutor
from sapl.utils import SaplGenericRelation
from sapl.utils import SaplGenericRelation, sapl_logger
class AutorListView(ListAPIView):
@ -17,12 +18,28 @@ class AutorListView(ListAPIView):
Listagem de Autores com filtro para autores cadastrados
e/ou possíveis autores.
- tr - tipo do resultado
Prepera Lista de Autores para 3 cenários distintos
-default = 0
= 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.
= 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
- tipo - chave primária do Tipo de Autor a ser filtrado
- provaveis - variável sem relevância de valor, porém, sua presença
faz com que a AutorListView
mostre a lista de provaveis Autores armazenados segundo o
ContentType associado ao Tipo de Autor via relacionamento
genérico.
- q - busca textual no nome do Autor ou em fields_search
declarados no field SaplGenericRelation das GenericFks
@ -30,22 +47,54 @@ class AutorListView(ListAPIView):
estiver presente a variável `provaveis`. Em caso
contrário, o django-filter é desativado e a busca é feita
no model do ContentType associado ao tipo.
Outros campos
"""
TR_AUTOR_CHOICE_SERIALIZER = 1
TR_CHOICE_SERIALIZER = 2
TR_AUTOR_SERIALIZER = 3
# FIXME aplicar permissão correta de usuário
permission_classes = (IsAuthenticated,)
permission_classes = (AllowAny,)
serializer_class = AutorSerializer
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_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):
"""
desativa o django-filter se a busca for por provaveis autores
desativa o django-filter se a busca for por possiveis autores
parametro tr = TR_CHOICE_SERIALIZER
"""
provaveis = 'provaveis' in request.GET
self.filter_class = None if provaveis else AutorChoiceFilterSet
self.filter_backends = [] if provaveis else [DjangoFilterBackend]
self.serializer_class = ChoiceSerializer\
if provaveis else AutorChoiceSerializer
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:
self.serializer_class = AutorSerializer
self.permission_classes = (IsAuthenticated,)
return ListAPIView.get(self, request, *args, **kwargs)
@ -81,11 +130,19 @@ class AutorListView(ListAPIView):
field.related_model == Autor,
model_class._meta.get_fields(include_hidden=True)))
# retirar assert
assert len(fields) == 1
"""
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().order_by(
fields[0].fields_search[0][0])
qs = model_class.objects.all()
q_filter = Q()
if q:
@ -102,8 +159,10 @@ class AutorListView(ListAPIView):
qs = qs.filter(q_filter).distinct(
fields[0].fields_search[0][0])
qs = qs.values_list('id', fields[0].fields_search[0][0])
qs = qs.order_by(fields[0].fields_search[0][0]).values_list(
'id', fields[0].fields_search[0][0])
r += list(qs)
r.sort(key=lambda x: x[1])
if tipos.count() > 1:
r.sort(key=lambda x: x[1].upper())
return r

28
sapl/base/forms.py

@ -23,7 +23,8 @@ from sapl.materia.models import MateriaLegislativa
from sapl.sessao.models import SessaoPlenaria
from sapl.settings import MAX_IMAGE_UPLOAD_SIZE
from sapl.utils import (RANGE_ANOS, ImageThumbnailFileInput,
RangeWidgetOverride, autor_label, autor_modal)
RangeWidgetOverride, autor_label, autor_modal,
SaplGenericRelation)
from .models import AppConfig, CasaLegislativa
@ -44,6 +45,31 @@ STATUS_USER_CHOICE = [
]
def autores_models_generic_relations():
models_of_generic_relations = list(map(
lambda x: x.related_model,
filter(
lambda obj: obj.is_relation and
hasattr(obj, 'field') and
isinstance(obj, GenericRel),
Autor._meta.get_fields(include_hidden=True))
))
models = list(map(
lambda x: (x,
list(filter(
lambda field: (
isinstance(
field, SaplGenericRelation) and
field.related_model == Autor),
x._meta.get_fields(include_hidden=True)))),
models_of_generic_relations
))
return models
class TipoAutorForm(ModelForm):
content_type = forms.ModelChoiceField(

10
sapl/compilacao/views.py

@ -1,7 +1,7 @@
import logging
import sys
from collections import OrderedDict
from datetime import datetime, timedelta
import logging
import sys
from braces.views import FormMessagesMixin
from django import forms
@ -20,8 +20,8 @@ from django.shortcuts import get_object_or_404, redirect
from django.utils.dateparse import parse_date
from django.utils.decorators import method_decorator
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.base import TemplateView
from django.views.generic.detail import DetailView
from django.views.generic.edit import (CreateView, DeleteView, FormView,
@ -47,6 +47,8 @@ from sapl.compilacao.models import (Dispositivo, Nota,
from sapl.compilacao.utils import (DISPOSITIVO_SELECT_RELATED,
DISPOSITIVO_SELECT_RELATED_EDIT)
from sapl.crud.base import Crud, CrudListView, make_pagination
from sapl.settings import BASE_DIR
TipoNotaCrud = Crud.build(TipoNota, 'tipo_nota')
TipoVideCrud = Crud.build(TipoVide, 'tipo_vide')
@ -55,7 +57,7 @@ VeiculoPublicacaoCrud = Crud.build(VeiculoPublicacao, 'veiculo_publicacao')
TipoDispositivoCrud = Crud.build(
TipoDispositivo, 'tipo_dispositivo')
logger = logging.getLogger(__name__)
logger = logging.getLogger(BASE_DIR.name)
def get_integrations_view_names():

6
sapl/crud/base.py

@ -13,17 +13,19 @@ from django.db import models
from django.http.response import Http404
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
from django.views.generic.list import MultipleObjectMixin
from sapl.crispy_layout_mixin import CrispyLayoutFormMixin, get_field_display
from sapl.settings import BASE_DIR
from sapl.utils import normalize
logger = logging.getLogger(__name__)
logger = logging.getLogger(BASE_DIR.name)
ACTION_LIST, ACTION_CREATE, ACTION_DETAIL, ACTION_UPDATE, ACTION_DELETE = \
'list', 'create', 'detail', 'update', 'delete'

39
sapl/settings.py

@ -13,6 +13,9 @@ Quick-start development settings - unsuitable for production
See https://docs.djangoproject.com/en/1.8/howto/deployment/checklist/
"""
import logging
import sys
from decouple import config
from dj_database_url import parse as db_url
from unipath import Path
@ -20,6 +23,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)
@ -222,10 +226,41 @@ SASS_PROCESSOR_INCLUDE_DIRS = (BOWER_COMPONENTS_ROOT.child(
'bower_components', 'bootstrap-sass', 'assets', 'stylesheets'),
)
# suprime texto de ajuda default do django-filter
FILTERS_HELP_TEXT_FILTER = False
# FIXME update cripy-forms and remove this
# hack to suppress many annoying warnings from crispy_forms
# see sapl.temp_suppress_crispy_form_warnings
LOGGING = SUPRESS_CRISPY_FORM_WARNINGS_LOGGING
# suprime texto de ajuda default do django-filter
FILTERS_HELP_TEXT_FILTER = False
LOGGING_CONSOLE = config('LOGGING_CONSOLE', default=False, cast=bool)
if DEBUG and LOGGING_CONSOLE:
# Descomentar linha abaixo fará com que logs aparecam, inclusive SQL
# LOGGING['handlers']['console']['level'] = 'DEBUG'
LOGGING['loggers']['django']['level'] = 'DEBUG'
LOGGING.update({
'formatters': {
'verbose': {
'format': '%(levelname)s %(asctime)s %(pathname)s '
'%(funcName)s %(message)s'
},
'simple': {
'format': '%(levelname)s %(message)s'
},
},
})
LOGGING['handlers']['console']['formatter'] = 'verbose'
LOGGING['loggers'][BASE_DIR.name] = {
'handlers': ['console'],
'level': 'DEBUG',
}
def excepthook(*args):
logging.getLogger(BASE_DIR.name).error(
'Uncaught exception:', exc_info=args)
sys.excepthook = excepthook

2
sapl/templates/base/autor_form.html

@ -32,7 +32,7 @@ $(document).ready(function(){
var formData = {
'q' : q,
'tipo' : pk,
'provaveis' : ''
'tr' : '2' // tipo_resultado = 2 - api fornecerá possíveis Autores
}
$.get(url, formData).done(function(data) {
active('pesquisa');

55
sapl/utils.py

@ -4,7 +4,6 @@ from unicodedata import normalize as unicodedata_normalize
import hashlib
import logging
import magic
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Button
from django import forms
@ -18,13 +17,14 @@ from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import PermissionDenied, ValidationError
from django.utils.translation import ugettext_lazy as _
from floppyforms import ClearableFileInput
from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row
<<<<<<< 2783b194c573b2b8182c47369b2d251ae6cf8880
import magic
from sapl.settings import BASE_DIR
sapl_logger = logging.getLogger(BASE_DIR.name)
from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row
def normalize(txt):
return unicodedata_normalize(
@ -96,10 +96,57 @@ def montar_helper_autor(self):
class SaplGenericRelation(GenericRelation):
"""
Extenção da class GenericRelation para implmentar o atributo fields_search
fields_search é uma tupla de tuplas de dois strings no padrão de construção
de campos porém com [Field Lookups][ref_1]
exemplo:
[No Model Parlamentar em][ref_2] existe a implementação dessa
classe no atributo autor. Parlamentar possui três informações
relevantes para buscas realacionadas a Autor:
- nome_completo;
- nome_parlamentar; e
- filiacao__partido__sigla
que devem ser pesquisados, coincidentemente
pelo FieldLookup __icontains
portanto a estrutura de fields_search seria:
fields_search=(
('nome_completo', '__icontains'),
('nome_parlamentar', '__icontains'),
('filiacao__partido__sigla', '__icontains'),
)
[ref_1]: https://docs.djangoproject.com/el/1.10/topics/db/queries/#field-lookups
[ref_2]: https://github.com/interlegis/sapl/blob/master/sapl/parlamentares/models.py
"""
def __init__(self, to, fields_search=(), **kwargs):
assert fields_search
assert 'related_query_name' in kwargs, _(
'SaplGenericRelation não pode ser instanciada sem '
'related_query_name.')
assert fields_search, _(
'SaplGenericRelation não pode ser instanciada sem fields_search.')
for field in fields_search:
# descomente para ver todas os campos que são elementos de busca
#print(kwargs['related_query_name'], field)
assert isinstance(field, (tuple, list)), _(
'fields_search deve ser um array de tuplas ou listas.')
assert len(field) == 2, _(
'cada tupla de fields_search deve possuir duas strins')
# TODO implementar assert para validar campos do Model e lookups
self.fields_search = fields_search
super().__init__(to, **kwargs)

Loading…
Cancel
Save