Browse Source

Merge branch 'master' into 738-refatorar-proposicoes

pull/752/head
LeandroRoberto 8 years ago
parent
commit
3d0d52e36b
  1. 4
      sapl/api/forms.py
  2. 2
      sapl/api/pagination.py
  3. 3
      sapl/api/permissions.py
  4. 2
      sapl/api/serializers.py
  5. 9
      sapl/api/urls.py
  6. 5
      sapl/api/views.py
  7. 72
      sapl/base/forms.py
  8. 15
      sapl/base/views.py
  9. 4
      sapl/crud/base.py
  10. 10
      sapl/materia/forms.py
  11. 7
      sapl/materia/views.py
  12. 10
      sapl/static/js/app.js
  13. 34
      sapl/static/styles/app.scss
  14. 6
      sapl/templates/base.html
  15. 6
      sapl/templates/base/autor_form.html
  16. 13
      sapl/templates/materia/materialegislativa_filter.html
  17. 1
      sapl/utils.py

4
sapl/api/forms.py

@ -1,7 +1,9 @@
from django.db.models import Q
from django_filters.filters import MethodFilter, ModelChoiceFilter
from rest_framework.filters import FilterSet
from sapl.base.models import Autor, TipoAutor
from sapl.utils import generic_relations_for_model
@ -10,6 +12,7 @@ class SaplGenericRelationSearchFilterSet(FilterSet):
q = MethodFilter()
def filter_q(self, queryset, value):
query = value.split(' ')
if query:
q = Q()
@ -25,6 +28,7 @@ class SaplGenericRelationSearchFilterSet(FilterSet):
sgr = gr[1]
for item in sgr:
if item.related_model != self._meta.model:
continue
flag_order_by = True
for field in item.fields_search:

2
sapl/api/pagination.py

@ -1,5 +1,4 @@
from django.core.paginator import EmptyPage
from django.utils.encoding import force_text
from rest_framework import pagination
from rest_framework.response import Response
@ -31,4 +30,5 @@ class StandardPagination(pagination.PageNumberPagination):
'page': self.page.number,
},
'results': data,
})

3
sapl/api/permissions.py

@ -1,4 +1,4 @@
from rest_framework.permissions import DjangoModelPermissions, BasePermission
from rest_framework.permissions import DjangoModelPermissions
class DjangoModelPermissions(DjangoModelPermissions):
@ -14,4 +14,5 @@ class DjangoModelPermissions(DjangoModelPermissions):
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}

2
sapl/api/serializers.py

@ -1,6 +1,4 @@
from django.contrib.contenttypes.fields import GenericRel
from rest_framework import serializers
from sapl.base.models import Autor
from sapl.materia.models import MateriaLegislativa

9
sapl/api/urls.py

@ -1,13 +1,13 @@
from django.conf import settings
from django.conf.urls import url, include
from rest_framework.routers import DefaultRouter
from sapl.api.views import AutorListView, ModelChoiceView,\
MateriaLegislativaViewSet
from sapl.api.views import MateriaLegislativaViewSet, AutorListView,\
ModelChoiceView
from .apps import AppConfig
app_name = AppConfig.name
@ -17,11 +17,12 @@ urlpatterns_router = router.urls
urlpatterns_api = [
# url(r'^$', api_root),
url(r'^autor', AutorListView.as_view(), name='autor_list'),
url(r'^model/(?P<content_type>\d+)/(?P<pk>\d*)$',
ModelChoiceView.as_view(), name='model_list'),
]
if settings.DEBUG:

5
sapl/api/views.py

@ -1,10 +1,13 @@
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 _
from rest_framework.filters import DjangoFilterBackend
from rest_framework.generics import ListAPIView
from rest_framework.mixins import ListModelMixin, RetrieveModelMixin
from rest_framework.permissions import IsAuthenticated, AllowAny
from rest_framework.viewsets import GenericViewSet
@ -58,6 +61,7 @@ class AutorListView(ListAPIView):
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
@ -123,7 +127,6 @@ class AutorListView(ListAPIView):
return ListAPIView.get(self, request, *args, **kwargs)
def get_queryset(self):
queryset = ListAPIView.get_queryset(self)

72
sapl/base/forms.py

@ -27,9 +27,11 @@ 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, models_with_gr_for_model,
ChoiceWithoutValidationField)
from .models import AppConfig, CasaLegislativa
@ -120,16 +122,6 @@ class AutorForm(ModelForm):
choices=ACTION_CREATE_USERS_AUTOR_CHOICE,
widget=forms.RadioSelect())
status_user = forms.ChoiceField(
label=_('Bloqueio do Usuário Existente'),
choices=STATUS_USER_CHOICE,
widget=forms.RadioSelect(),
required=False,
help_text=_('Se vc está trocando ou removendo o usuário deste Autor, '
'como o Sistema deve proceder com o usuário que está sendo'
' desvinculado?'))
class Meta:
model = Autor
fields = ['tipo',
@ -150,7 +142,6 @@ class AutorForm(ModelForm):
StrictButton(
_('Filtrar'), css_class='btn-default btn-filtrar-autor',
type='button')),
css_class='hidden',
data_action='create',
data_application='AutorSearch',
@ -160,7 +151,6 @@ class AutorForm(ModelForm):
Div(to_column(('nome', 5)),
to_column(('cargo', 4)), css_class="div_nome_cargo"),
to_column((autor_related, 9)),
to_column((Div(
Field('autor_related'),
css_class='radiogroup-autor-related hidden'),
@ -175,13 +165,17 @@ class AutorForm(ModelForm):
to_column(('confirma_email', 3)),
css_class='new_user_fields hidden')
row4 = Row(to_column((Div(InlineRadios('status_user'),
css_class='radiogroup-status hidden'), 12)))
row4 = Row(to_column((
Div(InlineRadios('status_user'),
css_class='radiogroup-status hidden'),
12))) if 'status_user' in self.Meta.fields else None
controle_acesso = Fieldset(
_('Controle de Acesso do Autor'),
row2, row3, row4
)
controle_acesso = [row2, row3]
if row4:
controle_acesso.append(row4)
controle_acesso = Fieldset(_('Controle de Acesso do Autor'),
*controle_acesso)
self.helper = FormHelper()
self.helper.layout = SaplFormLayout(autor_select, controle_acesso)
@ -195,23 +189,30 @@ class AutorForm(ModelForm):
self.fields['autor_related'].choices = [
(self.instance.autor_related.pk,
self.instance.autor_related)]
self.fields['autor_related'].initial = self.instance.object_id
self.fields['q'].initial = ''
self.fields['autor_related'].initial = self.instance.autor_related
if self.instance.user:
self.fields['username'].initial = self.instance.user.username
self.fields['action_user'].initial = 'A'
self.fields['status_user'].initial = 'R'
self.fields['username'].label = string_concat(
self.fields['username'].label,
' (', self.instance.user.username, ')')
if 'status_user' in self.Meta.fields:
self.fields['status_user'].initial = 'R'
self.fields['status_user'].label = string_concat(
self.fields['status_user'].label,
' (', self.instance.user.username, ')')
self.fields['username'].widget.attrs.update({
'data': self.instance.user.username
if self.instance.user else ''})
if 'status_user' in self.Meta.fields:
self.fields['status_user'].widget.attrs.update({
'data': self.instance.user.username
if self.instance.user else ''})
@ -229,13 +230,14 @@ class AutorForm(ModelForm):
raise ValidationError(_('Informe se o Autor terá usuário '
'vinculado para acesso ao Sistema.'))
if 'status_user' in self.Meta.fields:
if self.instance.pk and self.instance.user_id:
if self.instance.user.username != cd['username']:
if 'status_user' not in cd or not cd['status_user']:
raise ValidationError(
_('Foi trocado ou removido o usuário deste Autor, '
'mas não foi informado como se deve proceder com o '
'usuário que está sendo desvinculado?'))
'mas não foi informado como se deve proceder '
'com o usuário que está sendo desvinculado?'))
qs_user = User.objects.all()
qs_autor = Autor.objects.all()
@ -374,6 +376,7 @@ class AutorForm(ModelForm):
user_old.groups.remove(grupo)
else:
if 'status_user' in self.Meta.fields:
if 'status_user' in self.cleaned_data and user_old:
if self.cleaned_data['status_user'] == 'X':
user_old.delete()
@ -387,10 +390,35 @@ class AutorForm(ModelForm):
user_old.groups.remove(grupo)
elif user_old:
user_old.groups.remove(grupo)
else:
user_old.groups.remove(grupo)
return autor
class AutorFormForAdmin(AutorForm):
status_user = forms.ChoiceField(
label=_('Bloqueio do Usuário Existente'),
choices=STATUS_USER_CHOICE,
widget=forms.RadioSelect(),
required=False,
help_text=_('Se vc está trocando ou removendo o usuário deste Autor, '
'como o Sistema deve proceder com o usuário que está sendo'
' desvinculado?'))
class Meta:
model = Autor
fields = ['tipo',
'nome',
'cargo',
'autor_related',
'q',
'action_user',
'username',
'status_user']
class RelatorioAtasFilterSet(django_filters.FilterSet):
filter_overrides = {models.DateField: {

15
sapl/base/views.py

@ -13,7 +13,9 @@ from django.utils.translation import ugettext_lazy as _
from django.views.generic.base import TemplateView
from django_filters.views import FilterView
from sapl.base.forms import AutorForm, TipoAutorForm
from sapl.base.forms import AutorForm, TipoAutorForm, AutorFormForAdmin
from sapl.base.models import Autor, TipoAutor
from sapl.crud.base import CrudAux
from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa
@ -49,7 +51,6 @@ class AutorCrud(CrudAux):
class BaseMixin(CrudAux.BaseMixin):
list_field_names = ['tipo', 'nome', 'user__username']
ordering = ('tipo__descricao', )
class DeleteView(CrudAux.DeleteView):
@ -66,6 +67,11 @@ class AutorCrud(CrudAux):
layout_key = None
form_class = AutorForm
def get(self, request, *args, **kwargs):
if request.user.is_superuser:
self.form_class = AutorFormForAdmin
return CrudAux.UpdateView.get(self, request, *args, **kwargs)
def get_success_url(self):
# FIXME try except - testar envio de emails
@ -108,6 +114,11 @@ class AutorCrud(CrudAux):
form_class = AutorForm
layout_key = None
def get(self, request, *args, **kwargs):
if request.user.is_superuser:
self.form_class = AutorFormForAdmin
return CrudAux.CreateView.get(self, request, *args, **kwargs)
def get_success_url(self):
pk_autor = self.object.id
try:

4
sapl/crud/base.py

@ -9,7 +9,6 @@ from django import forms
from django.conf.urls import url
from django.contrib import messages
from django.contrib.auth.mixins import PermissionRequiredMixin
from django.core.exceptions import PermissionDenied
from django.core.urlresolvers import reverse
from django.db import models
from django.db.models.fields.related import ForeignKey
@ -17,8 +16,8 @@ from django.http.response import Http404
from django.shortcuts import redirect
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
@ -28,6 +27,7 @@ from sapl.crispy_layout_mixin import CrispyLayoutFormMixin, get_field_display
from sapl.settings import BASE_DIR
from sapl.utils import normalize
logger = logging.getLogger(BASE_DIR.name)
ACTION_LIST, ACTION_CREATE, ACTION_DETAIL, ACTION_UPDATE, ACTION_DELETE = \

10
sapl/materia/forms.py

@ -1,20 +1,18 @@
from cProfile import label
from datetime import datetime
import django_filters
from crispy_forms.bootstrap import InlineRadios, InlineField, Alert,\
InlineCheckboxes
from crispy_forms.bootstrap import Alert, InlineCheckboxes
from crispy_forms.helper import FormHelper
from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout, Row,\
Div, Field
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, transaction
from django.db.models import Max
from django.forms import ModelForm, widgets
from django.utils.translation import ugettext_lazy as _
import django_filters
from sapl import base
from sapl.base.models import Autor

7
sapl/materia/views.py

@ -38,6 +38,7 @@ from sapl.utils import (TURNO_TRAMITACAO_CHOICES, YES_NO_CHOICES, autor_label,
permissoes_protocoloadm, permission_required_for_app,
montar_row_autor)
from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm,
ConfirmarProposicaoForm, DocumentoAcessorioForm,
MateriaLegislativaFilterSet,
@ -325,6 +326,12 @@ class ConfirmarProposicao(PermissionRequiredMixin, CreateView):
class ProposicaoCrud(Crud):
"""
TODO: Entre outros comportamento gerais, mesmo que um usuário tenha
Perfil de Autor o Crud de proposição não deverá permitir acesso a
proposições. O acesso deve ser permitido se existe um Autor registrado
e vinculado ao usuário. Essa tarefa deve ser realizada nas Tabelas Aux.
"""
model = Proposicao
help_path = ''
container_field = 'autor__user'

10
sapl/static/js/app.js

@ -150,9 +150,19 @@ function autorModal() {
get_nome_autor("#id_autoria__autor");*/
}
var customsFront = function() {
$('[type=radio], [type=checkbox]').each(function() {
var $controls = $(this).closest('.controls')
$controls && !$controls.hasClass('controls-radio-checkbox') && $controls.addClass('controls-radio-checkbox')
});
}
$(document).ready(function(){
refreshDatePicker();
refreshMask();
autorModal();
initTinymce("texto-rico");
customsFront();
});

34
sapl/static/styles/app.scss

@ -113,6 +113,40 @@ h6, .h6 {
}
}
.controls-radio-checkbox {
padding: 0px;
border: 1px solid #d6e1e5;
border-radius: 4px;
min-height: 20px;
.help-block {
margin: $grid-gutter-width / 2;
padding: $grid-gutter-width / 2;
border: 2px dashed #d6e1e5;
}
label {
padding: 5px;
.icons {
top: 5px;
left: 8px;
}
&.checkbox-inline, &.radio-inline {
padding: 8px;
padding-left: 36px;
.icons {
top: 8px;
left: 8px;
}
}
}
.checkbox, .radio, .checkbox-inline, .radio-inline {
margin: 0;
&:hover {
background-color: #d6e1e5;;
}
}
}
// #### CRUD DETAIL ########################################
p.control-label {

6
sapl/templates/base.html

@ -284,12 +284,12 @@
<script type="text/javascript" src="{% static 'js/jquery.runner.js' %}"></script>
<script type="text/javascript" src="{% static 'jquery-mask-plugin/dist/jquery.mask.js' %}"></script>
<script type="text/javascript" src="{% static 'drunken-parrot-flat-ui/js/checkbox.js' %}"></script>
<script type="text/javascript" src="{% static 'drunken-parrot-flat-ui/js/radio.js' %}"></script>
<script src="{% static 'tinymce/tinymce.min.js' %}"></script>
<script type="text/javascript" src="{% static 'jsdiff/diff.min.js' %}"></script>
<script type="text/javascript" src="{% static 'drunken-parrot-flat-ui/js/checkbox.js' %}"></script>
<script type="text/javascript" src="{% static 'drunken-parrot-flat-ui/js/radio.js' %}"></script>
<script type="text/javascript" src="{% static 'js/app.js' %}"></script>
{% block extra_js %}{% endblock %}

6
sapl/templates/base/autor_form.html

@ -40,7 +40,6 @@ $(document).ready(function(){
if (atualizar) {
var radios = $("#div_id_autor_related .controls").html('');
data.results.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);
});
@ -64,6 +63,11 @@ $(document).ready(function(){
}
else{
$('#id_nome, #id_q').val('');
if ($('input[name=autor_related]').length == 1 ) {
$('input[name=autor_related]').prop('checked', 'checked');
$('input[name=autor_related]').closest('.radio').addClass('checked');
}
}
}).fail(function(data) {
active('nome', atualizar);

13
sapl/templates/materia/materialegislativa_filter.html

@ -47,6 +47,19 @@
</br>
<strong>Localização Atual:</strong> &nbsp;{{m.tramitacao_set.last.unidade_tramitacao_destino|default_if_none:"Não Informada"}}</br>
<strong>Status:</strong> &nbsp;{{m.tramitacao_set.last.status|default_if_none:"Não Informada"}}</br>
{% if m.registrovotacao_set.exists %}
<strong>Data Votação:</strong>
{% if m.registrovotacao_set.last.ordem %}
<a href="{% url 'sapl.sessao:sessaoplenaria_detail' m.registrovotacao_set.last.ordem.sessao_plenaria_id %}">
{{ m.registrovotacao_set.last.ordem.data_ordem }}
</a>
{% elif m.registrovotacao_set.last.expediente %}
<a href="{% url 'sapl.sessao:sessaoplenaria_detail' m.registrovotacao_set.last.expediente.sessao_plenaria_id %}">
{{ m.registrovotacao_set.last.expediente.data_ordem }}
</a>
{% endif %}
</br>
{% endif %}
<strong>Data da última Tramitação:</strong> &nbsp;{{m.tramitacao_set.last.data_tramitacao|default_if_none:"Não Informada"}}</br>
<strong>Ementa:</strong>&nbsp;{{ m.ementa|safe }}</br>
<p></p>

1
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

Loading…
Cancel
Save