Browse Source

Ref model, backend e frontend de TipoProposicao

pull/752/head
LeandroRoberto 8 years ago
parent
commit
b5505b4974
  1. 29
      sapl/api/forms.py
  2. 10
      sapl/api/serializers.py
  3. 11
      sapl/api/urls.py
  4. 21
      sapl/api/views.py
  5. 48
      sapl/base/forms.py
  6. 91
      sapl/materia/forms.py
  7. 38
      sapl/materia/migrations/0057_auto_20161016_0156.py
  8. 26
      sapl/materia/migrations/0058_auto_20161016_0329.py
  9. 78
      sapl/materia/models.py
  10. 20
      sapl/materia/views.py
  11. 6
      sapl/templates/materia/autor_form.html
  12. 7
      sapl/templates/materia/layouts.yaml
  13. 36
      sapl/templates/materia/tipoproposicao_form.html
  14. 62
      sapl/utils.py

29
sapl/api/forms.py

@ -1,25 +1,15 @@
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
from sapl.utils import generic_relations_for_model
class AutorChoiceFilterSet(FilterSet):
class SaplGenericRelationSearchFilterSet(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()
@ -30,11 +20,11 @@ class AutorChoiceFilterSet(FilterSet):
order_by = []
for gr in autores_models_generic_relations():
for gr in generic_relations_for_model(self._meta.model):
model = gr[0]
sgr = gr[1]
for item in sgr:
if item.related_model != Autor:
if item.related_model != self._meta.model:
continue
flag_order_by = True
for field in item.fields_search:
@ -55,3 +45,14 @@ class AutorChoiceFilterSet(FilterSet):
queryset = queryset.filter(q).order_by(*order_by)
return queryset
class AutorChoiceFilterSet(SaplGenericRelationSearchFilterSet):
q = MethodFilter()
tipo = ModelChoiceFilter(queryset=TipoAutor.objects.all())
class Meta:
model = Autor
fields = ['q',
'tipo',
'nome', ]

10
sapl/api/serializers.py

@ -16,10 +16,16 @@ class ChoiceSerializer(serializers.Serializer):
return obj[0]
class AutorChoiceSerializer(ChoiceSerializer):
class ModelChoiceSerializer(ChoiceSerializer):
def get_text(self, obj):
return obj.nome
return str(obj)
def get_value(self, obj):
return obj.id
class AutorChoiceSerializer(ModelChoiceSerializer):
def get_value(self, obj):
return obj.id

11
sapl/api/urls.py

@ -2,7 +2,7 @@
from django.conf import settings
from django.conf.urls import url, include
from sapl.api.views import AutorListView
from sapl.api.views import AutorListView, ModelChoiceView
from .apps import AppConfig
@ -16,9 +16,12 @@ app_name = AppConfig.name
urlpatterns_api = [
# url(r'^$', api_root),
url(r'^autor',
AutorListView.as_view(),
name='autor_list'),
url(r'^autor', AutorListView.as_view(), name='autor_list'),
url(r'^model/(?P<content_type>\d+)$',
ModelChoiceView.as_view(), name='model_list'),
]
if settings.DEBUG:

21
sapl/api/views.py

@ -1,4 +1,5 @@
from django.contrib.contenttypes.models import ContentType
from django.db.models import Q
from django.http import Http404
from django.utils.translation import ugettext_lazy as _
@ -10,11 +11,28 @@ from rest_framework.viewsets import ModelViewSet
from sapl.api.forms import AutorChoiceFilterSet
from sapl.api.serializers import ChoiceSerializer, AutorSerializer,\
AutorChoiceSerializer
AutorChoiceSerializer, ModelChoiceSerializer
from sapl.base.models import Autor, TipoAutor
from sapl.utils import SaplGenericRelation, sapl_logger
class ModelChoiceView(ListAPIView):
# FIXME aplicar permissão correta de usuário
permission_classes = (AllowAny,)
serializer_class = ModelChoiceSerializer
def get_queryset(self):
try:
ct = ContentType.objects.get_for_id(self.kwargs['content_type'])
except:
raise Http404
return ct.model_class().objects.all()
class AutorListView(ListAPIView):
"""
Listagem de Autores com filtro para autores cadastrados
@ -60,7 +78,6 @@ class AutorListView(ListAPIView):
# FIXME aplicar permissão correta de usuário
permission_classes = (AllowAny,)
serializer_class = AutorSerializer
queryset = Autor.objects.all()
model = Autor

48
sapl/base/forms.py

@ -27,7 +27,8 @@ 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,
SaplGenericRelation)
SaplGenericRelation, models_with_gr_for_model,
ChoiceWithoutValidationField)
from .models import AppConfig, CasaLegislativa
@ -55,31 +56,6 @@ 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(
@ -96,32 +72,14 @@ class TipoAutorForm(ModelForm):
super(TipoAutorForm, self).__init__(*args, **kwargs)
# Models que apontaram uma GenericRelation com Autor
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))
))
content_types = ContentType.objects.get_for_models(
*models_of_generic_relations)
*models_with_gr_for_model(Autor))
self.fields['content_type'].choices = [
('', _('Outros (Especifique)'))] + [
(ct.pk, ct) for key, ct in content_types.items()]
class ChoiceWithoutValidationField(forms.ChoiceField):
def validate(self, value):
if self.required and not value:
raise ValidationError(
self.error_messages['required'], code='required')
class AutorForm(ModelForm):
senha = forms.CharField(
max_length=20,

91
sapl/materia/forms.py

@ -2,23 +2,29 @@ from datetime import datetime
import django_filters
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout
from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout, Row,\
Div, Field
from django import forms
from django.contrib.contenttypes.fields import GenericRel
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from django.db import models
from django.db import models, transaction
from django.db.models import Max
from django.forms import ModelForm
from django.utils.translation import ugettext_lazy as _
from sapl.base.models import Autor
from sapl.comissoes.models import Comissao
from sapl.crispy_layout_mixin import form_actions, to_row
from sapl.crispy_layout_mixin import form_actions, to_row, to_column,\
SaplFormLayout
from sapl.materia.models import TipoProposicao
from sapl.norma.models import (LegislacaoCitada, NormaJuridica,
TipoNormaJuridica)
from sapl.parlamentares.models import Parlamentar
from sapl.settings import MAX_DOC_UPLOAD_SIZE
from sapl.utils import (RANGE_ANOS, RangeWidgetOverride, autor_label,
autor_modal)
autor_modal, models_with_gr_for_model,
ChoiceWithoutValidationField)
from .models import (AcompanhamentoMateria, Anexada, Autoria,
DespachoInicial, DocumentoAcessorio, MateriaLegislativa,
@ -767,3 +773,80 @@ class TramitacaoEmLoteFilterSet(django_filters.FilterSet):
self.form.helper.layout = Layout(
Fieldset(_('Tramitação em Lote'),
row1, row2, form_actions(save_label='Pesquisar')))
class TipoProposicaoForm(ModelForm):
conteudo = forms.ModelChoiceField(
queryset=ContentType.objects.all(),
label=TipoProposicao._meta.get_field('conteudo').verbose_name,
required=True)
tipo_conteudo_related_radio = ChoiceWithoutValidationField(
label="Seleção de Tipo",
required=True,
widget=forms.RadioSelect())
tipo_conteudo_related = forms.IntegerField(
widget=forms.HiddenInput())
class Meta:
model = TipoProposicao
fields = ['descricao',
'conteudo',
'tipo_conteudo_related_radio',
'tipo_conteudo_related']
widgets = {'tipo_conteudo_related': forms.HiddenInput()}
def __init__(self, *args, **kwargs):
tipo_select = Row(to_column(('descricao', 5)),
to_column(('conteudo', 7)),
to_column(('tipo_conteudo_related_radio', 12)))
self.helper = FormHelper()
self.helper.layout = SaplFormLayout(tipo_select)
super(TipoProposicaoForm, self).__init__(*args, **kwargs)
content_types = ContentType.objects.get_for_models(
*models_with_gr_for_model(TipoProposicao))
self.fields['conteudo'].choices = [
(ct.pk, ct) for k, ct in content_types.items()]
self.fields['conteudo'].choices.sort(key=lambda x: str(x[1]))
if self.instance.pk:
self.fields[
'tipo_conteudo_related'].initial = self.instance.object_id
def clean(self):
cd = self.cleaned_data
conteudo = cd['conteudo']
if 'tipo_conteudo_related' not in cd or not cd['tipo_conteudo_related']:
raise ValidationError(
_('Seleção de Tipo não definida'))
if not conteudo.model_class().objects.filter(
pk=cd['tipo_conteudo_related']).exists():
raise ValidationError(
_('O Registro definido (%s) não está na base de %s.'
) % (cd['tipo_conteudo_related'], cd['q'], conteudo))
return self.cleaned_data
@transaction.atomic
def save(self, commit=False):
tipo_proposicao = super(TipoProposicaoForm, self).save(commit)
assert tipo_proposicao.conteudo
tipo_proposicao.tipo_conteudo_related = \
tipo_proposicao.conteudo.model_class(
).objects.get(pk=self.cleaned_data['tipo_conteudo_related'])
tipo_proposicao.save()
return tipo_proposicao

38
sapl/materia/migrations/0057_auto_20161016_0156.py

@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-16 03:56
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('contenttypes', '0002_remove_content_type_name'),
('materia', '0056_merge'),
]
operations = [
migrations.RemoveField(
model_name='tipoproposicao',
name='materia_ou_documento',
),
migrations.RemoveField(
model_name='tipoproposicao',
name='modelo',
),
migrations.RemoveField(
model_name='tipoproposicao',
name='tipo_documento',
),
migrations.RemoveField(
model_name='tipoproposicao',
name='tipo_materia',
),
migrations.AddField(
model_name='tipoproposicao',
name='conteudo',
field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType', verbose_name='Conteúdo'),
),
]

26
sapl/materia/migrations/0058_auto_20161016_0329.py

@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.9.7 on 2016-10-16 05:29
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('materia', '0057_auto_20161016_0156'),
]
operations = [
migrations.AddField(
model_name='tipoproposicao',
name='object_id',
field=models.PositiveIntegerField(blank=True, default=None, null=True),
),
migrations.AlterField(
model_name='tipoproposicao',
name='conteudo',
field=models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType', verbose_name='Definição de Tipo'),
),
]

78
sapl/materia/models.py

@ -1,4 +1,6 @@
from django.contrib.auth.models import Group
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.utils.translation import ugettext_lazy as _
from model_utils import Choices
@ -8,7 +10,8 @@ from sapl.comissoes.models import Comissao
from sapl.parlamentares.models import Parlamentar
from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES,
get_settings_auth_user_model,
restringe_tipos_de_arquivo_txt)
restringe_tipos_de_arquivo_txt, SaplGenericRelation,
SaplGenericForeignKey)
EM_TRAMITACAO = [(1, 'Sim'),
@ -23,6 +26,38 @@ def grupo_autor():
return grupo.id
class TipoProposicao(models.Model):
descricao = models.CharField(max_length=50, verbose_name=_('Descrição'))
conteudo = models.ForeignKey(ContentType, default=None,
verbose_name=_('Definição de Tipo'))
object_id = models.PositiveIntegerField(
blank=True, null=True, default=None)
tipo_conteudo_related = SaplGenericForeignKey(
'conteudo', 'object_id', verbose_name=_('Seleção de Tipo'))
"""materia_ou_documento = models.CharField(
max_length=1, verbose_name=_('Gera'), choices=MAT_OU_DOC_CHOICES)
modelo = models.CharField(max_length=50, verbose_name=_('Modelo XML'))
# mutually exclusive (depend on materia_ou_documento)
tipo_materia = models.ForeignKey(
TipoMateriaLegislativa,
blank=True,
null=True,
verbose_name=_('Tipo de Matéria'))
tipo_documento = models.ForeignKey(
TipoDocumento, blank=True, null=True,
verbose_name=_('Tipo de Documento'))"""
class Meta:
verbose_name = _('Tipo de Proposição')
verbose_name_plural = _('Tipos de Proposições')
def __str__(self):
return self.descricao
class TipoMateriaLegislativa(models.Model):
sigla = models.CharField(max_length=5, verbose_name=_('Sigla'))
descricao = models.CharField(max_length=50, verbose_name=_('Descrição '))
@ -31,6 +66,14 @@ class TipoMateriaLegislativa(models.Model):
# XXX o que é isso ?
quorum_minimo_votacao = models.PositiveIntegerField(blank=True, null=True)
tipo_proposicao = SaplGenericRelation(
TipoProposicao,
related_query_name='tipomaterialegislativa_set',
fields_search=(
('descricao', '__icontains'),
('sigla', '__icontains')
))
class Meta:
verbose_name = _('Tipo de Matéria Legislativa')
verbose_name_plural = _('Tipos de Matérias Legislativas')
@ -246,6 +289,13 @@ class TipoDocumento(models.Model):
descricao = models.CharField(
max_length=50, verbose_name=_('Tipo Documento'))
tipo_proposicao = SaplGenericRelation(
TipoProposicao,
related_query_name='tipodocumento_set',
fields_search=(
('descricao', '__icontains'),
))
class Meta:
verbose_name = _('Tipo de Documento')
verbose_name_plural = _('Tipos de Documento')
@ -398,32 +448,6 @@ class Parecer(models.Model):
}
class TipoProposicao(models.Model):
MAT_OU_DOC_CHOICES = Choices(('M', 'materia', _('Matéria')),
('D', 'documento', _('Documento')))
descricao = models.CharField(max_length=50, verbose_name=_('Descrição'))
materia_ou_documento = models.CharField(
max_length=1, verbose_name=_('Gera'), choices=MAT_OU_DOC_CHOICES)
modelo = models.CharField(max_length=50, verbose_name=_('Modelo XML'))
# mutually exclusive (depend on materia_ou_documento)
tipo_materia = models.ForeignKey(
TipoMateriaLegislativa,
blank=True,
null=True,
verbose_name=_('Tipo Matéria'))
tipo_documento = models.ForeignKey(
TipoDocumento, blank=True, null=True, verbose_name=_('Tipo Documento'))
class Meta:
verbose_name = _('Tipo de Proposição')
verbose_name_plural = _('Tipos de Proposições')
def __str__(self):
return self.descricao
class Proposicao(models.Model):
autor = models.ForeignKey(Autor, null=True, blank=True)
tipo = models.ForeignKey(TipoProposicao, verbose_name=_('Tipo'))

20
sapl/materia/views.py

@ -29,7 +29,8 @@ from sapl.crud.base import (ACTION_CREATE, ACTION_DELETE, ACTION_DETAIL,
Crud, CrudAux, CrudDetailView, MasterDetailCrud,
make_pagination)
from sapl.materia import apps
from sapl.materia.forms import AnexadaForm, LegislacaoCitadaForm
from sapl.materia.forms import AnexadaForm, LegislacaoCitadaForm,\
TipoProposicaoForm
from sapl.norma.models import LegislacaoCitada
from sapl.utils import (TURNO_TRAMITACAO_CHOICES, YES_NO_CHOICES, autor_label,
autor_modal, gerar_hash_arquivo, get_base_url,
@ -125,11 +126,26 @@ class ConfirmarEmailView(TemplateView):
OrgaoCrud = CrudAux.build(Orgao, 'orgao')
TipoProposicaoCrud = CrudAux.build(TipoProposicao, 'tipo_proposicao')
StatusTramitacaoCrud = CrudAux.build(StatusTramitacao, 'status_tramitacao')
UnidadeTramitacaoCrud = CrudAux.build(UnidadeTramitacao, 'unidade_tramitacao')
class TipoProposicaoCrud(CrudAux):
model = TipoProposicao
help_text = 'tipo_proposicao'
class BaseMixin(CrudAux.BaseMixin):
list_field_names = ["descricao", "conteudo", 'tipo_conteudo_related']
class CreateView(CrudAux.CreateView):
form_class = TipoProposicaoForm
layout_key = None
class UpdateView(CrudAux.UpdateView):
form_class = TipoProposicaoForm
layout_key = None
def criar_materia_proposicao(proposicao):
tipo_materia = TipoMateriaLegislativa.objects.get(
descricao=proposicao.tipo.descricao)

6
sapl/templates/materia/autor_form.html

@ -1,6 +0,0 @@
{% extends "base.html" %}
{% load i18n crispy_forms_tags %}
{% block base_content %}
{% crispy form helper %}
{% endblock %}

7
sapl/templates/materia/layouts.yaml

@ -74,9 +74,10 @@ Relatoria:
TipoProposicao:
{% trans 'Tipo Proposição' %}:
- descricao
- materia_ou_documento tipo_documento
- modelo
- descricao conteudo
- tipo_conteudo_related
ProposicaoCreate:
{% trans 'Proposição' %}:

36
sapl/templates/materia/tipoproposicao_form.html

@ -0,0 +1,36 @@
{% extends "crud/form.html" %}
{% load i18n %}
aaa
{% block extra_js %}
<script type="text/javascript">
$(document).ready(function(){
var initial_select = $("input[name=tipo_conteudo_related]").val();
$("input[name=tipo_conteudo_related]").remove();
$('#id_conteudo').change(function(event) {
var pk = this[event.target.selectedIndex].value;
var url = '{% url 'sapl.api:model_list' 0 %}'
url = url.replace('0', pk)
$.get(url).done(function(data) {
var radios = $("#div_id_tipo_conteudo_related_radio .controls").html('');
data.models.forEach(function (val, index) {
var html_radio = '<label class="radio'+(initial_select==val.value?' checked':'')+'"><span class="icons"><span class="first-icon"></span><span class="second-icon"></span></span><input type="radio" name="tipo_conteudo_related" id="id_tipo_conteudo_related_'+index+'" value="'+val.value+'"'+(initial_select?' checked="checked"':'')+'>'+val.text+'</label>';
radios.append(html_radio);
});
initial_select='';
});
});
$('#id_conteudo').trigger('change');
});
</script>
{% endblock %}

62
sapl/utils.py

@ -13,13 +13,15 @@ from django.conf import settings
from django.contrib import admin
from django.contrib.auth.decorators import user_passes_test
from django.contrib.auth.models import Permission
from django.contrib.contenttypes.fields import GenericRelation
from django.contrib.contenttypes.fields import GenericRelation, GenericRel,\
GenericForeignKey
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
import magic
from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row
from sapl.settings import BASE_DIR
@ -95,6 +97,13 @@ def montar_helper_autor(self):
' class="btn btn-inverse">Cancelar</a>')]))
class SaplGenericForeignKey(GenericForeignKey):
def __init__(self, ct_field='content_type', fk_field='object_id', for_concrete_model=True, verbose_name=''):
super().__init__(ct_field, fk_field, for_concrete_model)
self.verbose_name = verbose_name
class SaplGenericRelation(GenericRelation):
"""
Extenção da class GenericRelation para implmentar o atributo fields_search
@ -462,3 +471,52 @@ def gerar_hash_arquivo(arquivo, pk, block_size=2**20):
break
md5.update(data)
return 'P' + md5.hexdigest() + '/' + pk
class ChoiceWithoutValidationField(forms.ChoiceField):
def validate(self, value):
if self.required and not value:
raise ValidationError(
self.error_messages['required'], code='required')
def models_with_gr_for_model(model):
return list(map(
lambda x: x.related_model,
filter(
lambda obj: obj.is_relation and
hasattr(obj, 'field') and
isinstance(obj, GenericRel),
model._meta.get_fields(include_hidden=True))
))
def generic_relations_for_model(model):
"""
Esta função retorna uma lista de tuplas de dois elementos, onde o primeiro
elemento é um model qualquer que implementa SaplGenericRelation (SGR), o
segundo elemento é uma lista de todas as SGR's que pode haver dentro do
model retornado na primeira posição da tupla.
Exemplo: No Sapl, o model Parlamentar tem apenas uma SGR para Autor.
Se no Sapl existisse apenas essa SGR, o resultado dessa função
seria:
[ #Uma Lista de tuplas
( # cada tupla com dois elementos
sapl.parlamentares.models.Parlamentar,
[<sapl.utils.SaplGenericRelation: autor>]
),
]
"""
return list(map(
lambda x: (x,
list(filter(
lambda field: (
isinstance(
field, SaplGenericRelation) and
field.related_model == model),
x._meta.get_fields(include_hidden=True)))),
models_with_gr_for_model(model)
))

Loading…
Cancel
Save