mirror of https://github.com/interlegis/sapl.git
Browse Source
* Ref Autor, TipoAutor, cria app api DRF - Autor e TipoAutor migrados para app base. - Foram refatorados para GR - Generic Relations - Em TipoAutor: passou se a apontar também para um ContentType que é usado para contextualização de dados da GR em Autor. - A captura da combo de ContentTypes é feita através do apontamento reverso nos models que se queira disponibilizar conceitualmente como Autor - Em Autor: neste commit, o form de create está em desenvolvimento, com o buscador de possiveis autores baseados na seleção do usuário de TipoAutor que, se não possui ContentType, abre o campo nome para insersão, se possui ContentType, abre caixa de busca com atualização jquery de radiobox's para o usuário selecionar um possível autor. - api rest: para a busca funcionar e como objetivo de futuras implementações em DRF, a app api foi criada, anotada nas configurações gerais de sapl.urls com o prefixo /api. - na api foi criada a uma ListAPIView para pesquisa de possiveis autores baseados no tipo autor enviado, url /api/autor/possiveis/?P<pk>[0-9]*)$ que sem pk devolve a lista de TipoAutor e, com pk, devolve a lista dos registros ligados ao ContentType, filtrados pelo parametro q * Ajusta front para busca por possiveis autores Ajusta front e implementa SaplGenericRelation, uma customização que adiciona o atributo fields_search que possibilita passar para qualquer implementação de busca quais são os campos de busca padrão do do GenericRelation * Conc refatoração no Cada de Autor e Tipos de Autor * Alt backend de perm e pag de drf e ref layout topo * Add procedimento na alteração de username de Autor Na edição de Autores foi adicionado o tratamento por opção do usuário do que deve ser feito com o usuário que está sendo desvinculado no caso de uma alteração do username de um Autor. Foram dadas três opções: 1) Apenas retirar Perfil de Autor do Usuário que está sendo desvinculado 2) Retirar Perfil de Autor e desativar Usuário que está sendo desvinculado 3) Excluir Usuário * Add field cargo em AutorForm para tipos sem CT Cadastro de Autores de Tipos sem ContentType podem adicionar nome, cargo e usuário. * Add Bloco, Bancada, Frente possíveis Tip de Autores * Corrige frase de mensagem no cadastro de Autor * ref buscador modal de Autores c pesq param reversa * Add documentação e faz modificações na api/autor * Ref Crud para Listar GenericRelations * Ref List da aba Proposições para parlamentares * Altera imports de teste na app materia * Corrige comentários da classe AutorListView * Customiza layout do drf docs. * Altera criação do grp Autor para inc list e detail * Remove customização do bootstrap Após fork e ajustes feitos no tema drunken-parrot-ui-flat, alterações feitas nos arquivos deste commit não são mais necessárias, passando a ser renderizado os arquivos que são padrão da biblioteca django-crispy-forms. * Adequa inserção dinamica de radio ao crispy-formspull/752/head
Leandro Roberto da Silva
8 years ago
committed by
Edward
59 changed files with 2042 additions and 443 deletions
@ -0,0 +1 @@ |
|||
default_app_config = 'sapl.api.apps.AppConfig' |
@ -0,0 +1,8 @@ |
|||
from django import apps |
|||
from django.utils.translation import ugettext_lazy as _ |
|||
|
|||
|
|||
class AppConfig(apps.AppConfig): |
|||
name = 'sapl.api' |
|||
label = 'api' |
|||
verbose_name = _('API Rest') |
@ -0,0 +1,57 @@ |
|||
from django.contrib.contenttypes.fields import GenericRel |
|||
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 |
|||
|
|||
|
|||
class AutorChoiceFilterSet(FilterSet): |
|||
q = MethodFilter() |
|||
tipo = ModelChoiceFilter(queryset=TipoAutor.objects.all()) |
|||
|
|||
class Meta: |
|||
model = Autor |
|||
fields = ['q', |
|||
'tipo', |
|||
'nome', ] |
|||
|
|||
def filter_q(self, queryset, value): |
|||
|
|||
query = value.split(' ') |
|||
if query: |
|||
q = Q() |
|||
for qtext in query: |
|||
if not qtext: |
|||
continue |
|||
q_fs = Q(nome__icontains=qtext) |
|||
|
|||
order_by = [] |
|||
|
|||
for gr in autores_models_generic_relations(): |
|||
model = gr[0] |
|||
sgr = gr[1] |
|||
for item in sgr: |
|||
if item.related_model != Autor: |
|||
continue |
|||
flag_order_by = True |
|||
for field in item.fields_search: |
|||
if flag_order_by: |
|||
flag_order_by = False |
|||
order_by.append('%s__%s' % ( |
|||
item.related_query_name(), |
|||
field[0]) |
|||
) |
|||
q_fs = q_fs | Q(**{'%s__%s%s' % ( |
|||
item.related_query_name(), |
|||
field[0], |
|||
field[1]): qtext}) |
|||
|
|||
q = q & q_fs |
|||
|
|||
if q: |
|||
queryset = queryset.filter(q).order_by(*order_by) |
|||
|
|||
return queryset |
@ -0,0 +1,34 @@ |
|||
from django.core.paginator import EmptyPage |
|||
from django.utils.encoding import force_text |
|||
from rest_framework import pagination |
|||
from rest_framework.response import Response |
|||
|
|||
|
|||
class StandardPagination(pagination.PageNumberPagination): |
|||
page_size = 10 |
|||
page_size_query_param = 'page_size' |
|||
max_page_size = 50 |
|||
|
|||
def get_paginated_response(self, data): |
|||
try: |
|||
previous_page_number = self.page.previous_page_number() |
|||
except EmptyPage: |
|||
previous_page_number = None |
|||
|
|||
try: |
|||
next_page_number = self.page.next_page_number() |
|||
except EmptyPage: |
|||
next_page_number = None |
|||
|
|||
return Response({ |
|||
'pagination': { |
|||
'previous_page': previous_page_number, |
|||
'next_page': next_page_number, |
|||
'start_index': self.page.start_index(), |
|||
'end_index': self.page.end_index(), |
|||
'total_entries': self.page.paginator.count, |
|||
'total_pages': self.page.paginator.num_pages, |
|||
'page': self.page.number, |
|||
}, |
|||
'models': data, |
|||
}) |
@ -0,0 +1,17 @@ |
|||
from rest_framework.permissions import DjangoModelPermissions |
|||
|
|||
|
|||
class DjangoModelPermissions(DjangoModelPermissions): |
|||
|
|||
perms_map = { |
|||
'GET': ['%(app_label)s.list_%(model_name)s', |
|||
'%(app_label)s.detail_%(model_name)s'], |
|||
'OPTIONS': ['%(app_label)s.list_%(model_name)s', |
|||
'%(app_label)s.detail_%(model_name)s'], |
|||
'HEAD': ['%(app_label)s.list_%(model_name)s', |
|||
'%(app_label)s.detail_%(model_name)s'], |
|||
'POST': ['%(app_label)s.list_%(model_name)s'], |
|||
'PUT': ['%(app_label)s.change_%(model_name)s'], |
|||
'PATCH': ['%(app_label)s.change_%(model_name)s'], |
|||
'DELETE': ['%(app_label)s.delete_%(model_name)s'], |
|||
} |
@ -0,0 +1,43 @@ |
|||
from django.contrib.contenttypes.fields import GenericRel |
|||
from rest_framework import serializers |
|||
|
|||
from sapl.base.models import Autor |
|||
from sapl.utils import SaplGenericRelation |
|||
|
|||
|
|||
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 AutorChoiceSerializer(ChoiceSerializer): |
|||
|
|||
def get_text(self, obj): |
|||
return obj.nome |
|||
|
|||
def get_value(self, obj): |
|||
return obj.id |
|||
|
|||
class Meta: |
|||
model = Autor |
|||
fields = ['id', 'nome'] |
|||
|
|||
|
|||
class AutorObjectRelatedField(serializers.RelatedField): |
|||
|
|||
def to_representation(self, value): |
|||
return str(value) |
|||
|
|||
|
|||
class AutorSerializer(serializers.ModelSerializer): |
|||
autor_related = AutorObjectRelatedField(read_only=True) |
|||
|
|||
class Meta: |
|||
model = Autor |
|||
fields = ['id', 'tipo', 'nome', 'object_id', 'autor_related', 'user'] |
@ -0,0 +1,30 @@ |
|||
from django.conf import settings |
|||
from django.conf.urls import url, include |
|||
|
|||
from sapl.api.views import AutorListView |
|||
|
|||
from .apps import AppConfig |
|||
|
|||
|
|||
app_name = AppConfig.name |
|||
|
|||
|
|||
# router = DefaultRouter() |
|||
|
|||
# urlpatterns += router.urls |
|||
|
|||
|
|||
urlpatterns_api = [ |
|||
# url(r'^$', api_root), |
|||
url(r'^autor', |
|||
AutorListView.as_view(), |
|||
name='autor_list'), |
|||
] |
|||
|
|||
if settings.DEBUG: |
|||
urlpatterns_api += [ |
|||
url(r'^docs', include('rest_framework_docs.urls')), ] |
|||
|
|||
urlpatterns = [ |
|||
url(r'^api/', include(urlpatterns_api)) |
|||
] |
@ -0,0 +1,169 @@ |
|||
|
|||
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, 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, 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. |
|||
|
|||
= 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 |
|||
|
|||
- 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. |
|||
|
|||
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 = (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 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: |
|||
self.serializer_class = AutorSerializer |
|||
self.permission_classes = (IsAuthenticated,) |
|||
|
|||
return ListAPIView.get(self, request, *args, **kwargs) |
|||
|
|||
def get_queryset(self): |
|||
queryset = ListAPIView.get_queryset(self) |
|||
|
|||
if self.filter_backends: |
|||
return queryset |
|||
|
|||
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]) |
|||
|
|||
qs = qs.order_by(fields[0].fields_search[0][0]).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 |
@ -0,0 +1,55 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9.7 on 2016-10-09 15:22 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.conf import settings |
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
migrations.swappable_dependency(settings.AUTH_USER_MODEL), |
|||
('contenttypes', '0002_remove_content_type_name'), |
|||
('base', '0021_auto_20161006_1019'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.CreateModel( |
|||
name='Autor', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('object_id', models.PositiveIntegerField(blank=True, default=None, null=True)), |
|||
('nome', models.CharField(blank=True, max_length=50, verbose_name='Autor')), |
|||
('cargo', models.CharField(blank=True, max_length=50)), |
|||
('content_type', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType')), |
|||
], |
|||
options={ |
|||
'verbose_name': 'Autor', |
|||
'verbose_name_plural': 'Autores', |
|||
}, |
|||
), |
|||
migrations.CreateModel( |
|||
name='TipoAutor', |
|||
fields=[ |
|||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), |
|||
('descricao', models.CharField(max_length=50, verbose_name='Descrição')), |
|||
('content_type', models.OneToOneField(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType', verbose_name='Modelo do Tipo de Autor')), |
|||
], |
|||
options={ |
|||
'verbose_name': 'Tipo de Autor', |
|||
'verbose_name_plural': 'Tipos de Autor', |
|||
}, |
|||
), |
|||
migrations.AddField( |
|||
model_name='autor', |
|||
name='tipo', |
|||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='base.TipoAutor', verbose_name='Tipo'), |
|||
), |
|||
migrations.AddField( |
|||
model_name='autor', |
|||
name='user', |
|||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL), |
|||
), |
|||
] |
@ -0,0 +1,25 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9.7 on 2016-10-09 21:52 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('base', '0022_auto_20161009_1222'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterField( |
|||
model_name='tipoautor', |
|||
name='content_type', |
|||
field=models.OneToOneField(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType', verbose_name='Modelagem no SAPL'), |
|||
), |
|||
migrations.AlterUniqueTogether( |
|||
name='autor', |
|||
unique_together=set([('content_type', 'object_id')]), |
|||
), |
|||
] |
@ -0,0 +1,26 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9.7 on 2016-10-10 13:02 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('base', '0023_auto_20161009_1852'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterField( |
|||
model_name='autor', |
|||
name='nome', |
|||
field=models.CharField(blank=True, max_length=50, verbose_name='Nome do Autor'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='autor', |
|||
name='tipo', |
|||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='base.TipoAutor', verbose_name='Tipo do Autor'), |
|||
), |
|||
] |
@ -0,0 +1,20 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9.7 on 2016-10-11 14:38 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('base', '0024_auto_20161010_1002'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AddField( |
|||
model_name='tipoautor', |
|||
name='cria_usuario', |
|||
field=models.BooleanField(choices=[(True, 'Sim'), (False, 'Não')], default=False, help_text='Criação de Usuários víncula e libera o acesso de Autores ao sistema. Vincular um Autor a um tipo que esta opção está marcada como "Não", o Autor não terá username associado.', verbose_name='Criação de Usuários'), |
|||
), |
|||
] |
@ -0,0 +1,19 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9.7 on 2016-10-11 18:08 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('base', '0025_tipoautor_cria_usuario'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.RemoveField( |
|||
model_name='tipoautor', |
|||
name='cria_usuario', |
|||
), |
|||
] |
@ -0,0 +1,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9.7 on 2016-10-11 19:24 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.conf import settings |
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('base', '0026_remove_tipoautor_cria_usuario'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterField( |
|||
model_name='autor', |
|||
name='user', |
|||
field=models.OneToOneField(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL), |
|||
), |
|||
] |
@ -0,0 +1,53 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9.7 on 2016-10-09 15:22 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('protocoloadm', '0003_auto_20161009_1222'), |
|||
('materia', '0053_auto_20161004_1854'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.RemoveField( |
|||
model_name='autor', |
|||
name='comissao', |
|||
), |
|||
migrations.RemoveField( |
|||
model_name='autor', |
|||
name='parlamentar', |
|||
), |
|||
migrations.RemoveField( |
|||
model_name='autor', |
|||
name='partido', |
|||
), |
|||
migrations.RemoveField( |
|||
model_name='autor', |
|||
name='tipo', |
|||
), |
|||
migrations.RemoveField( |
|||
model_name='autor', |
|||
name='user', |
|||
), |
|||
migrations.AlterField( |
|||
model_name='autoria', |
|||
name='autor', |
|||
field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to='base.Autor', verbose_name='Autor'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='proposicao', |
|||
name='autor', |
|||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='base.Autor'), |
|||
), |
|||
migrations.DeleteModel( |
|||
name='Autor', |
|||
), |
|||
migrations.DeleteModel( |
|||
name='TipoAutor', |
|||
), |
|||
] |
@ -0,0 +1,32 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9.7 on 2016-10-09 17:18 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('base', '0022_auto_20161009_1222'), |
|||
('materia', '0054_auto_20161009_1222'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AddField( |
|||
model_name='materialegislativa', |
|||
name='autores', |
|||
field=models.ManyToManyField(through='materia.Autoria', to='base.Autor'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='autoria', |
|||
name='autor', |
|||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='base.Autor', verbose_name='Autor'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='autoria', |
|||
name='materia', |
|||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='materia.MateriaLegislativa', verbose_name='Matéria Legislativa'), |
|||
), |
|||
] |
@ -0,0 +1,16 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9.7 on 2016-10-11 19:45 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('materia', '0055_auto_20161009_1418'), |
|||
('materia', '0054_auto_20161011_0904'), |
|||
] |
|||
|
|||
operations = [ |
|||
] |
@ -0,0 +1,26 @@ |
|||
# -*- coding: utf-8 -*- |
|||
# Generated by Django 1.9.7 on 2016-10-09 15:22 |
|||
from __future__ import unicode_literals |
|||
|
|||
from django.db import migrations, models |
|||
import django.db.models.deletion |
|||
|
|||
|
|||
class Migration(migrations.Migration): |
|||
|
|||
dependencies = [ |
|||
('protocoloadm', '0002_delete_tipoinstituicao'), |
|||
] |
|||
|
|||
operations = [ |
|||
migrations.AlterField( |
|||
model_name='documentoadministrativo', |
|||
name='autor', |
|||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='base.Autor'), |
|||
), |
|||
migrations.AlterField( |
|||
model_name='protocolo', |
|||
name='autor', |
|||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='base.Autor'), |
|||
), |
|||
] |
@ -0,0 +1,146 @@ |
|||
{% extends "crud/form.html" %} |
|||
{% load i18n %} |
|||
{% block extra_js %} |
|||
|
|||
<script type="text/javascript"> |
|||
|
|||
$(document).ready(function(){ |
|||
var flag_create = false; |
|||
if (location.href.indexOf('create') > 0) { |
|||
$('.radiogroup-status').remove(); |
|||
flag_create = true |
|||
} |
|||
var active = function(str, atualizar=true) { |
|||
if (str == 'nome') { |
|||
if (atualizar) |
|||
$('#id_nome, #id_q, #id_cargo').val(''); |
|||
$('.div_nome_cargo').removeClass('hidden'); |
|||
$("#div_id_autor_related .controls").html(''); |
|||
$("[data-application='AutorSearch'], .radiogroup-autor-related").addClass('hidden'); |
|||
} |
|||
else { |
|||
$('#id_nome, #id_cargo').val(''); |
|||
$('.div_nome_cargo').addClass('hidden'); |
|||
$("#div_id_autor_related .alert").remove(); |
|||
$("[data-application='AutorSearch'], .radiogroup-autor-related").removeClass('hidden'); |
|||
} |
|||
} |
|||
var update_search = function(pk, atualizar=true) { |
|||
var q = $('#id_q').val(); |
|||
|
|||
var url = '{% url 'sapl.api:autor_list'%}' |
|||
|
|||
var formData = { |
|||
'q' : q, |
|||
'tipo' : pk, |
|||
'tr' : '2' // tipo_resultado = 2 - api fornecerá possíveis Autores |
|||
} |
|||
$.get(url, formData).done(function(data) { |
|||
active('pesquisa'); |
|||
if (atualizar) { |
|||
var radios = $("#div_id_autor_related .controls").html(''); |
|||
data.models.forEach(function (val, index) { |
|||
var html_radio = '<div class="radio"><label><span class="icons"><span class="first-icon"></span><span class="second-icon"></span></span><input type="radio" name="autor_related" id="id_autor_related_'+index+'" value="'+val.value+'" style="display:none;">'+val.text+'</label></div>'; |
|||
radios.append(html_radio); |
|||
}); |
|||
|
|||
if (data.models.length > 1) { |
|||
$('input[name=autor_related]').change(function(event){ |
|||
if (this.checked) |
|||
$('#id_q').val(event.target.parentElement.textContent); |
|||
//$('input[name=autor_related]:not(:checked)').closest('.radio').remove(); |
|||
}); |
|||
} |
|||
else { |
|||
$('input[name=autor_related]').prop('checked', 'checked'); |
|||
$('input[name=autor_related]').closest('.radio').addClass('checked'); |
|||
} |
|||
|
|||
if (data.pagination.total_entries > 10) |
|||
radios.before('<div class="alert alert-info" role="alert"><strong>{% trans "Foram encontrados" %} '+data.pagination.total_entries+' {% trans "registros"%}</strong>'+' {% trans "mas será mostrado apenas os 10 primeiros resultados. Coloque mais informações no campo pesquisa para refinar sua busca."%}</div>'); |
|||
else if (data.pagination.total_entries == 0) |
|||
radios.before('<div class="alert alert-info" role="alert"><strong>{% trans "Não foram encontrados registros com os termos de pesquisa informados." %}</div>'); |
|||
} |
|||
else{ |
|||
$('#id_nome, #id_q').val(''); |
|||
} |
|||
}).fail(function(data) { |
|||
active('nome', atualizar); |
|||
}); |
|||
} |
|||
|
|||
$('#id_tipo').change(function(event) { |
|||
if (event.target.selectedIndex == 0) { |
|||
$('#id_nome, #id_q').val(''); |
|||
active('nome'); |
|||
} |
|||
else { |
|||
var pk = this[event.target.selectedIndex].value; |
|||
$('input[name=autor_related]').closest('.radio').remove(); |
|||
update_search(pk, false) |
|||
} |
|||
}); |
|||
|
|||
$('.btn-filtrar-autor').click(function(event) { |
|||
var pk = $('#id_tipo').val(); |
|||
update_search(pk); |
|||
}); |
|||
|
|||
$('input[name=action_user]').change(function(event) { |
|||
if (!this.checked) |
|||
return; |
|||
|
|||
$('#div_id_username input').prop('readonly', ''); |
|||
|
|||
if (event.target.value == 'C') { |
|||
$('.new_user_fields, #div_id_username').removeClass('hidden'); |
|||
$('input[name=username]').val(''); |
|||
} |
|||
else if (event.target.value == 'N') { |
|||
$('.new_user_fields').addClass('hidden'); |
|||
if ($('input[name=username]').attr('data') != '') |
|||
$('.radiogroup-status').removeClass('hidden'); |
|||
|
|||
if (flag_create) { |
|||
$('#div_id_username').addClass('hidden'); |
|||
} |
|||
else { |
|||
$('#div_id_username input').prop('readonly', 'readonly'); |
|||
} |
|||
} |
|||
else { |
|||
$('.radiogroup-status').addClass('hidden'); |
|||
$('#div_id_username').removeClass('hidden'); |
|||
$('.new_user_fields').addClass('hidden'); |
|||
} |
|||
if (!flag_create) { |
|||
var username = $('input[name=username]'); |
|||
if (username.length == 1) { |
|||
if ((event.target.value == 'A' && username.attr('data') != '' && username[0].value != username.attr('data')) |
|||
|| (event.target.value == 'C' && username.attr('data') != '')) |
|||
$('.radiogroup-status').removeClass('hidden'); |
|||
} |
|||
} |
|||
}); |
|||
|
|||
$('input[name=username]').keyup(function(event) { |
|||
if (!flag_create) |
|||
if (this.getAttribute('data') != '' && this.value != this.getAttribute('data')) |
|||
$('.radiogroup-status').removeClass('hidden'); |
|||
else |
|||
$('.radiogroup-status').addClass('hidden'); |
|||
}); |
|||
|
|||
$('input[name=action_user]:checked').trigger('change'); |
|||
$('input[name=autor_related]').closest('.radio').remove(); |
|||
|
|||
var pk = $('#id_tipo').val(); |
|||
if (pk) |
|||
update_search(pk, $('#id_q').val().length > 0) |
|||
|
|||
}); |
|||
|
|||
|
|||
</script> |
|||
|
|||
{% endblock %} |
@ -0,0 +1,20 @@ |
|||
{% extends "crud/form.html" %} |
|||
{% load i18n %} |
|||
{% block extra_js %} |
|||
|
|||
<script type="text/javascript"> |
|||
|
|||
$(document).ready(function(){ |
|||
|
|||
$('#id_content_type').change(function(event) { |
|||
if (event.target.selectedIndex == 0) |
|||
$('#id_descricao').val(''); |
|||
else |
|||
$('#id_descricao').val(this[event.target.selectedIndex].text); |
|||
}); |
|||
}); |
|||
|
|||
|
|||
</script> |
|||
|
|||
{% endblock %} |
@ -0,0 +1,81 @@ |
|||
{% load static from staticfiles %} |
|||
|
|||
<!DOCTYPE html> |
|||
<html> |
|||
<head> |
|||
<meta charset="utf-8"> |
|||
<meta http-equiv="X-UA-Compatible" content="IE=edge"> |
|||
<meta name="viewport" content="width=device-width, initial-scale=1"> |
|||
|
|||
<title>{% block title %}DRF Docs{% endblock %}</title> |
|||
|
|||
{% block style %} |
|||
<link rel="stylesheet" href="{% static "rest_framework_docs/css/style.css" %}"> |
|||
{% endblock %} |
|||
</head> |
|||
|
|||
<body> |
|||
{% block github_badge %} |
|||
<a href="https://github.com/ekonstantinidis/drf-docs/" class="github-corner" target="_blank"> |
|||
<svg width="80" height="80" viewBox="0 0 250 250" style="fill:#18bc9c; color:#fff; position: absolute; top: 0; border: 0; right: 0;"> |
|||
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path> |
|||
<path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path> |
|||
<path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path> |
|||
</svg> |
|||
</a> |
|||
{% endblock %} |
|||
|
|||
<div class="container"> |
|||
|
|||
<nav class="navbar navbar-default"> |
|||
<div class="container-fluid"> |
|||
<!-- Brand and toggle get grouped for better mobile display --> |
|||
<div class="navbar-header"> |
|||
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#drfdoc-navbar" aria-expanded="false"> |
|||
<span class="sr-only">Toggle navigation</span> |
|||
<span class="icon-bar"></span> |
|||
<span class="icon-bar"></span> |
|||
<span class="icon-bar"></span> |
|||
</button> |
|||
{% block logo %}<a class="navbar-brand" href="http://www.drfdocs.com/">DRF Docs</a>{% endblock %} |
|||
</div> |
|||
|
|||
<!-- Collect the nav links, forms, and other content for toggling --> |
|||
<div class="collapse navbar-collapse" id="drfdoc-navbar"> |
|||
<form method="get" action="." class="navbar-form navbar-right" role="search"> |
|||
<div class="form-group"> |
|||
<input type="text" class="form-control" name="search" value="{{ query }}" placeholder="Search"> |
|||
</div> |
|||
</form> |
|||
<ul class="nav navbar-nav navbar-right"> |
|||
{% block apps_menu %}{% endblock %} |
|||
</ul> |
|||
</div><!-- /.navbar-collapse --> |
|||
</div><!-- /.container-fluid --> |
|||
</nav> |
|||
|
|||
{% block jumbotron %} |
|||
<div class="jumbotron"> |
|||
<h1>DRF Docs</h1> |
|||
<h3>Document Web APIs made with <a href="http://www.django-rest-framework.org/" target="_blank">Django REST Framework</a>.</h3> |
|||
</div> |
|||
{% endblock %} |
|||
|
|||
{% block content %}{% endblock %} |
|||
|
|||
{% block footer %} |
|||
<div class="footer"> |
|||
<div class="links"> |
|||
<a href="http://www.iamemmanouil.com"><i class="fa fa-link"></i></a> |
|||
<a href="http://www.github.com/ekonstantinidis"><i class="fa fa-github"></i></a> |
|||
<a href="http://www.twitter.com/iamemmanouil"><i class="fa fa-twitter"></i></a> |
|||
</div> |
|||
Copyright © 2016 Emmanouil Konstantinidis. |
|||
</div> |
|||
{% endblock %} |
|||
</div> |
|||
|
|||
<!-- Dist.js - Inlcuded Live API, jQuery, Bootstrap --> |
|||
<script type="text/javascript" src="{% static "rest_framework_docs/js/dist.min.js" %}"></script> |
|||
</body> |
|||
</html> |
@ -0,0 +1,123 @@ |
|||
{% extends "rest_framework_docs/docs.html" %} |
|||
|
|||
{% block style %}{{block.super}} |
|||
<style media="screen"> |
|||
.lead { |
|||
font-size: 14px; |
|||
} |
|||
</style> |
|||
{% endblock %} |
|||
{% block logo %} |
|||
<a class="navbar-brand" href="http://sapl31demo.interlegis.leg.br">DRF Docs - SAPL - Sistema de Apoio ao Processo Legislativo</a> |
|||
{% endblock %} |
|||
{% block title %}SAPL - Sistema de Apoio ao Processo Legislativo{% endblock %} |
|||
{% block jumbotron %} |
|||
<div class="jumbotron"> |
|||
<h1>SAPL </h1> |
|||
<h3>Sistema de Apoio ao Processo Legislativo.</h3> |
|||
</div> |
|||
{% endblock %} |
|||
|
|||
|
|||
{% block apps_menu %} |
|||
{% regroup endpoints by name_parent as endpoints_grouped %} |
|||
<li class="dropdown"> |
|||
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">Jump To <span class="caret"></span></a> |
|||
<ul class="dropdown-menu"> |
|||
{% for group in endpoints_grouped %} |
|||
<li><a href="#{{ group.grouper|lower }}-group">{{ group.grouper }}</a></li> |
|||
{% endfor %} |
|||
</ul> |
|||
</li> |
|||
{% endblock %} |
|||
|
|||
|
|||
{% block content %} |
|||
|
|||
{% regroup endpoints by name_parent as endpoints_grouped %} |
|||
|
|||
{% if endpoints_grouped %} |
|||
{% for group in endpoints_grouped %} |
|||
|
|||
<h1 id="{{ group.grouper|lower }}-group">{{group.grouper}}</h1> |
|||
|
|||
<div class="panel-group" role="tablist"> |
|||
|
|||
{% for endpoint in group.list %} |
|||
|
|||
<div class="panel panel-default endpoint"> |
|||
|
|||
<div class="panel-heading" role="tab" data-toggle="collapse" data-target="#{{ endpoint.path|slugify }}"> |
|||
<div class="row"> |
|||
<div class="col-md-7"> |
|||
<h4 class="panel-title title"> |
|||
<i class="fa fa-link"></i> {{ endpoint.path }} |
|||
</h4> |
|||
</div> |
|||
|
|||
<div class="col-md-5"> |
|||
<ul class="list-inline methods"> |
|||
{% for method in endpoint.allowed_methods %} |
|||
<li class="method {{ method|lower }}">{{ method }}</li> |
|||
{% endfor %} |
|||
<li class="method plug" |
|||
data-toggle="modal" |
|||
data-path="{{ endpoint.path }}" |
|||
data-methods="{{ endpoint.allowed_methods }}" |
|||
data-permissions="{{ endpoint.permissions }}" |
|||
data-fields="{{ endpoint.fields_json }}"> |
|||
<i class="fa fa-plug"></i></li> |
|||
</ul> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
<div id="{{ endpoint.path|slugify }}" class="panel-collapse collapse" role="tabpanel"> |
|||
<div class="panel-body"> |
|||
{% if endpoint.docstring %} |
|||
<pre class="lead">{{ endpoint.docstring}}</pre> |
|||
{% endif %} |
|||
|
|||
{% if endpoint.errors %} |
|||
<div class="alert alert-danger" role="alert">Oops! There was something wrong with {{ endpoint.errors }}. Please check your code.</div> |
|||
{% endif %} |
|||
|
|||
{% if endpoint.fields %} |
|||
<p class="fields-desc">Fields:</p> |
|||
<ul class="list fields"> |
|||
{% for field in endpoint.fields %} |
|||
<li class="field">{{ field.name }}: {{ field.type }} {% if field.required %}<span class="label label-primary label-required" title="Required">R</span>{% endif %}</li> |
|||
{% endfor %} |
|||
</ul> |
|||
{% elif not endpoint.errors %} |
|||
<p>No fields.</p> |
|||
{% endif %} |
|||
</div> |
|||
</div> |
|||
</div> |
|||
{% endfor %} |
|||
|
|||
</div> |
|||
|
|||
{% endfor %} |
|||
{% elif not query %} |
|||
<h2 class="text-center">There are currently no api endpoints to document.</h2> |
|||
{% else %} |
|||
<h2 class="text-center">No endpoints found for {{ query }}.</h2> |
|||
{% endif %} |
|||
|
|||
<!-- Modal --> |
|||
<div class="modal fade api-modal" id="liveAPIModal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel"> |
|||
<div class="modal-dialog modal-lg" role="document"> |
|||
<div class="modal-content"> |
|||
<div class="modal-header"> |
|||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button> |
|||
<h4 class="modal-title">Live API Endpoints <span class="label label-info">Beta</span></h4> |
|||
</div> |
|||
|
|||
<div id="liveAPIEndpoints"></div> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
|
|||
{% endblock %} |
Loading…
Reference in new issue