diff --git a/sapl/api/__init__.py b/sapl/api/__init__.py new file mode 100644 index 000000000..9580155c5 --- /dev/null +++ b/sapl/api/__init__.py @@ -0,0 +1 @@ +default_app_config = 'sapl.api.apps.AppConfig' diff --git a/sapl/api/admin.py b/sapl/api/admin.py new file mode 100644 index 000000000..e69de29bb diff --git a/sapl/api/apps.py b/sapl/api/apps.py new file mode 100644 index 000000000..ced7e511b --- /dev/null +++ b/sapl/api/apps.py @@ -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') diff --git a/sapl/api/forms.py b/sapl/api/forms.py new file mode 100644 index 000000000..e69de29bb diff --git a/sapl/api/migrations/__init__.py b/sapl/api/migrations/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/sapl/api/serializers.py b/sapl/api/serializers.py new file mode 100644 index 000000000..d61643d21 --- /dev/null +++ b/sapl/api/serializers.py @@ -0,0 +1,25 @@ +from rest_framework import serializers + +from sapl.comissoes.models import Comissao +from sapl.parlamentares.models import Parlamentar + + +class ChoiceSerializer(serializers.Serializer): + pk = serializers.IntegerField() + display = serializers.SerializerMethodField() + + def get_display(self, obj): + return str(obj) +""" + +class ModelChoiceParlamentarSerializer(ModelChoiceSerializer): + + class Meta: + model = Parlamentar + + +class ModelChoiceComissaoSerializer(ModelChoiceSerializer): + + class Meta: + model = Comissao +""" \ No newline at end of file diff --git a/sapl/api/urls.py b/sapl/api/urls.py new file mode 100644 index 000000000..a3a130da9 --- /dev/null +++ b/sapl/api/urls.py @@ -0,0 +1,20 @@ +from django.conf.urls import include, url +from rest_framework.routers import DefaultRouter + +from sapl.api.views import TipoAutorContentOfModelContentTypeView + +from .apps import AppConfig + + +app_name = AppConfig.name + + +# router = DefaultRouter() + +urlpatterns = [ + url(r'^autor/possiveis/(?P[0-9]*)$', + TipoAutorContentOfModelContentTypeView.as_view(), + name='autores_possiveis_pelo_tipo'), +] + +# urlpatterns += router.urls diff --git a/sapl/api/views.py b/sapl/api/views.py new file mode 100644 index 000000000..08181d8a0 --- /dev/null +++ b/sapl/api/views.py @@ -0,0 +1,36 @@ +from django.http import Http404 +from rest_framework import mixins, viewsets +from rest_framework.generics import ListAPIView, GenericAPIView,\ + get_object_or_404 +from rest_framework.permissions import IsAuthenticated, AllowAny +from rest_framework.views import APIView +from rest_framework.viewsets import ModelViewSet + +from sapl.api.serializers import ChoiceSerializer +from sapl.base.models import Autor, TipoAutor + + +class TipoAutorContentOfModelContentTypeView(ListAPIView): + serializer_class = ChoiceSerializer + permission_classes = (AllowAny,) + queryset = TipoAutor.objects.all() + model = TipoAutor + pagination_class = None + + def get_queryset(self): + queryset = ModelViewSet.get_queryset(self) + if not self.kwargs['pk']: + return queryset + + obj = get_object_or_404(queryset, pk=self.kwargs['pk']) + + if not obj.content_type: + raise Http404 + + q = self.request.GET.get('q', None) + + if not q: + return [] + else: + return obj.content_type.model_class().objects.filter( + nome__icontains=q)[:10] diff --git a/sapl/base/forms.py b/sapl/base/forms.py index 952db3b35..93ae87290 100644 --- a/sapl/base/forms.py +++ b/sapl/base/forms.py @@ -1,14 +1,23 @@ -import django_filters +from crispy_forms.bootstrap import FieldWithButtons, StrictButton from crispy_forms.helper import FormHelper -from crispy_forms.layout import HTML, Button, Fieldset, Layout +from crispy_forms.layout import HTML, Button, Fieldset, Layout, Field, Div, Row +from crispy_forms.templatetags.crispy_forms_field import css_class from django import forms +from django.contrib.auth import get_user_model from django.contrib.auth.forms import AuthenticationForm -from django.core.exceptions import ValidationError -from django.db import models -from django.forms import ModelForm +from django.contrib.auth.models import Group +from django.contrib.auth.password_validation import validate_password +from django.contrib.contenttypes.fields import GenericRel +from django.contrib.contenttypes.models import ContentType +from django.core.exceptions import ValidationError, ObjectDoesNotExist +from django.db import models, transaction +from django.forms import ModelForm, widgets from django.utils.translation import ugettext_lazy as _ +import django_filters -from sapl.crispy_layout_mixin import form_actions, to_row +from sapl.base.models import Autor, TipoAutor +from sapl.crispy_layout_mixin import form_actions, to_row, SaplFormLayout,\ + to_column from sapl.materia.models import MateriaLegislativa from sapl.sessao.models import SessaoPlenaria from sapl.settings import MAX_IMAGE_UPLOAD_SIZE @@ -18,6 +27,199 @@ from sapl.utils import (RANGE_ANOS, ImageThumbnailFileInput, from .models import AppConfig, CasaLegislativa +class TipoAutorForm(ModelForm): + + content_type = forms.ModelChoiceField( + queryset=ContentType.objects.all(), + label=TipoAutor._meta.get_field('content_type').verbose_name, + required=False) + + class Meta: + model = TipoAutor + fields = ['descricao', + 'content_type', ] + + def __init__(self, *args, **kwargs): + + 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) + + self.fields['content_type'].choices = [ + ('', _('Outros (Especifique)'))] + [ + (ct.pk, ct) for key, ct in content_types.items()] + + +class AutorForm(ModelForm): + """senha = forms.CharField( + max_length=20, + label=_('Senha'), + required=True, + widget=forms.PasswordInput()) + + senha_confirma = forms.CharField( + max_length=20, + label=_('Confirmar Senha'), + required=True, + widget=forms.PasswordInput()) + + confirma_email = forms.EmailField( + required=True, + label=_('Confirmar Email')) + + username = forms.CharField( + required=True, + max_length=50 + )""" + + q = forms.CharField( + max_length=50, required=False, + label='Pesquise o nome do Autor com o tipo Selecionado') + content_object = forms.ChoiceField(label='', + required=False, + widget=forms.RadioSelect()) + + class Meta: + model = Autor + fields = ['tipo', + 'nome', + 'content_object', + 'q'] + + def __init__(self, *args, **kwargs): + + content_object = Div( + + FieldWithButtons( + Field('q', + placeholder=_('Pesquisar por possíveis autores para ' + 'o Tipo de Autor selecionado.')), + StrictButton( + _('Filtrar'), css_class='btn-default btn-filtrar-autor', + type='button')), + + + + + Field('content_object'), + css_class='hidden', + data_action='create', + data_application='AutorSearch', + data_field='content_object') + + row1 = to_row([ + ('tipo', 4), + ('nome', 8), + (content_object, 8), + + ]) + + self.helper = FormHelper() + self.helper.layout = SaplFormLayout(row1) + + super(AutorForm, self).__init__(*args, **kwargs) + + self.fields['content_object'].choices = [('1', 'teste')] + if self.instance and self.instance.content_object: + self.fields['content_object'].choices = [ + (self.instance.content_object.pk, + self.instance.content_object)] + + def valida_igualdade(self, texto1, texto2, msg): + if texto1 != texto2: + raise ValidationError(msg) + return True + + def valida_email_existente(self): + return get_user_model().objects.filter( + email=self.cleaned_data['email']).exists() + + def clea(self): + + if 'username' not in self.cleaned_data: + raise ValidationError(_('Favor informar o username')) + + if ('senha' not in self.cleaned_data or + 'senha_confirma' not in self.cleaned_data): + raise ValidationError(_('Favor informar as senhas')) + + msg = _('As senhas não conferem.') + self.valida_igualdade( + self.cleaned_data['senha'], + self.cleaned_data['senha_confirma'], + msg) + + if ('email' not in self.cleaned_data or + 'confirma_email' not in self.cleaned_data): + raise ValidationError(_('Favor informar endereços de email')) + + msg = _('Os emails não conferem.') + self.valida_igualdade( + self.cleaned_data['email'], + self.cleaned_data['confirma_email'], + msg) + + email_existente = self.valida_email_existente() + + if (Autor.objects.filter( + username=self.cleaned_data['username']).exists()): + raise ValidationError(_('Já existe um autor para este usuário')) + + if email_existente: + msg = _('Este email já foi cadastrado.') + raise ValidationError(msg) + + try: + validate_password(self.cleaned_data['senha']) + except ValidationError as error: + raise ValidationError(error) + + try: + get_user_model().objects.get( + username=self.cleaned_data['username'], + email=self.cleaned_data['email']) + except ObjectDoesNotExist: + msg = _('Este nome de usuario não está cadastrado. ' + + 'Por favor, cadastre-o no Administrador do ' + + 'Sistema antes de adicioná-lo como Autor') + raise ValidationError(msg) + + return self.cleaned_data + + @transaction.atomic + def sav(self, commit=False): + + autor = super(AutorForm, self).save(commit) + + u = get_user_model().objects.get( + username=autor.username, + email=autor.email) + + u.set_password(self.cleaned_data['senha']) + u.is_active = False + u.save() + + autor.user = u + + autor.save() + + grupo = Group.objects.filter(name='Autor')[0] + u.groups.add(grupo) + + return autor + + class RelatorioAtasFilterSet(django_filters.FilterSet): filter_overrides = {models.DateField: { diff --git a/sapl/base/migrations/0022_auto_20161009_1222.py b/sapl/base/migrations/0022_auto_20161009_1222.py new file mode 100644 index 000000000..37794c428 --- /dev/null +++ b/sapl/base/migrations/0022_auto_20161009_1222.py @@ -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), + ), + ] diff --git a/sapl/base/migrations/0023_auto_20161009_1852.py b/sapl/base/migrations/0023_auto_20161009_1852.py new file mode 100644 index 000000000..79baef066 --- /dev/null +++ b/sapl/base/migrations/0023_auto_20161009_1852.py @@ -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')]), + ), + ] diff --git a/sapl/base/models.py b/sapl/base/models.py index 7872f2c21..67896fef9 100644 --- a/sapl/base/models.py +++ b/sapl/base/models.py @@ -7,10 +7,11 @@ from django.contrib.contenttypes.models import ContentType from django.core import exceptions from django.db import models, router from django.db.utils import DEFAULT_DB_ALIAS -from django.utils.translation import ugettext_lazy as _ from django.utils.translation import string_concat +from django.utils.translation import ugettext_lazy as _ + +from sapl.utils import UF, YES_NO_CHOICES, get_settings_auth_user_model -from sapl.utils import UF, YES_NO_CHOICES TIPO_DOCUMENTO_ADMINISTRATIVO = (('O', _('Ostensivo')), ('R', _('Restritivo'))) @@ -132,6 +133,65 @@ class AppConfig(models.Model): 'id': self.id} +class TipoAutor(models.Model): + descricao = models.CharField(max_length=50, verbose_name=_('Descrição')) + + content_type = models.OneToOneField( + ContentType, + null=True, default=None, + verbose_name=_('Modelagem no SAPL')) + + class Meta: + verbose_name = _('Tipo de Autor') + verbose_name_plural = _('Tipos de Autor') + + def __str__(self): + return self.descricao + + +class Autor(models.Model): + + user = models.OneToOneField(get_settings_auth_user_model()) + + tipo = models.ForeignKey(TipoAutor, verbose_name=_('Tipo do Autor')) + + content_type = models.ForeignKey( + ContentType, + blank=True, null=True, default=None) + object_id = models.PositiveIntegerField( + blank=True, null=True, default=None) + content_object = GenericForeignKey('content_type', 'object_id') + + nome = models.CharField( + max_length=50, blank=True, verbose_name=_('Nome do Autor')) + + cargo = models.CharField(max_length=50, blank=True) + + class Meta: + verbose_name = _('Autor') + verbose_name_plural = _('Autores') + unique_together = (('content_type', 'object_id'), ) + + def __str__(self): + + if self.content_object: + return str(self.content_object) + else: + if str(self.cargo): + return _('%(nome)s - %(cargo)s') % { + 'nome': self.nome, 'cargo': self.cargo} + else: + return str(self.nome) + """if str(self.tipo) == 'Parlamentar' and self.parlamentar: + return self.parlamentar.nome_parlamentar + elif str(self.tipo) == 'Comissao' and self.comissao: + return str(self.comissao) + elif str(self.tipo) == 'Partido' and self.partido: + return str(self.partido) + else: + """ + + def create_proxy_permissions( app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, **kwargs): diff --git a/sapl/base/urls.py b/sapl/base/urls.py index cf97c33b4..1e822dcb0 100644 --- a/sapl/base/urls.py +++ b/sapl/base/urls.py @@ -3,6 +3,8 @@ from django.contrib.auth import views from django.contrib.auth.decorators import permission_required from django.views.generic.base import TemplateView +from sapl.base.views import AutorCrud, TipoAutorCrud + from .apps import AppConfig from .forms import LoginForm from .views import (AppConfigCrud, CasaLegislativaCrud, HelpView, @@ -12,23 +14,26 @@ from .views import (AppConfigCrud, CasaLegislativaCrud, HelpView, RelatorioMateriasTramitacaoView, RelatorioPresencaSessaoView) + app_name = AppConfig.name urlpatterns = [ +<<<<<<< 6123d2617726dd220c02c2bb3b3c27ed4b136df1 +======= + url(r'^sistema/autor/tipo/', include(TipoAutorCrud.get_urls())), + url(r'^sistema/autor/', include(AutorCrud.get_urls())), + +>>>>>>> Ref Autor, TipoAutor, cria app api DRF url(r'^sistema/ajuda/', TemplateView.as_view(template_name='ajuda.html')), - url(r'^sistema/ajuda/(?P\w+)$', HelpView.as_view(), name='help_topic'), + url(r'^sistema/ajuda/(?P\w+)$', + HelpView.as_view(), name='help_topic'), url(r'^sistema/ajuda/', TemplateView.as_view(template_name='ajuda/index.html'), name='help_base'), url(r'^sistema/casa-legislativa/', include(CasaLegislativaCrud.get_urls()), name="casa_legislativa"), url(r'^sistema/app-config/', include(AppConfigCrud.get_urls())), - url(r'^login/$', views.login, { - 'template_name': 'base/login.html', 'authentication_form': LoginForm}, - name='login'), - url(r'^logout/$', views.logout, {'next_page': '/login'}, name='logout'), - # TODO mover estas telas para a app 'relatorios' url(r'^sistema/relatorios/$', TemplateView.as_view( template_name='base/relatorios_list.html')), @@ -50,8 +55,21 @@ urlpatterns = [ RelatorioAtasView.as_view(), name='atas'), +<<<<<<< 6123d2617726dd220c02c2bb3b3c27ed4b136df1 +======= + + # todos os sublink s de sistema devem vir acima deste +>>>>>>> Ref Autor, TipoAutor, cria app api DRF url(r'^sistema/', permission_required('base.view_tabelas_auxiliares') (TemplateView.as_view(template_name='sistema.html'))), +<<<<<<< 6123d2617726dd220c02c2bb3b3c27ed4b136df1 +======= + url(r'^login/$', views.login, { + 'template_name': 'base/login.html', 'authentication_form': LoginForm}, + name='login'), + url(r'^logout/$', views.logout, {'next_page': '/login'}, name='logout'), + +>>>>>>> Ref Autor, TipoAutor, cria app api DRF ] diff --git a/sapl/base/views.py b/sapl/base/views.py index b7d6378b8..ac3e3ef56 100644 --- a/sapl/base/views.py +++ b/sapl/base/views.py @@ -1,16 +1,28 @@ +from crispy_forms.helper import FormHelper +from crispy_forms.layout import HTML, Button +from django.conf import settings +from django.contrib.auth import get_user_model from django.contrib.auth.mixins import PermissionRequiredMixin +from django.contrib.auth.tokens import default_token_generator +from django.core.mail import send_mail from django.core.urlresolvers import reverse from django.db.models import Count, Q from django.http import HttpResponseRedirect +from django.utils.encoding import force_bytes +from django.utils.http import urlsafe_base64_encode 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.models import TipoAutor, Autor +from sapl.crispy_layout_mixin import to_row, SaplFormLayout, form_actions from sapl.crud.base import CrudAux from sapl.materia.models import MateriaLegislativa, TipoMateriaLegislativa from sapl.parlamentares.models import Parlamentar from sapl.sessao.models import OrdemDia, SessaoPlenaria +from sapl.utils import autor_label, autor_modal from .forms import (CasaLegislativaForm, ConfiguracoesAppForm, RelatorioAtasFilterSet, @@ -26,6 +38,114 @@ def get_casalegislativa(): return CasaLegislativa.objects.first() +def montar_row_autor(name): + autor_row = to_row( + [(name, 0), + (Button('pesquisar', + 'Pesquisar Autor', + css_class='btn btn-primary btn-sm'), 2), + (Button('limpar', + 'Limpar Autor', + css_class='btn btn-primary btn-sm'), 10)]) + + return autor_row + + +def montar_helper_autor(self): + autor_row = montar_row_autor('nome') + self.helper = FormHelper() + self.helper.layout = SaplFormLayout(*self.get_layout()) + + # Adiciona o novo campo 'autor' e mecanismo de busca + self.helper.layout[0][0].append(HTML(autor_label)) + self.helper.layout[0][0].append(HTML(autor_modal)) + self.helper.layout[0][1] = autor_row + + # Adiciona espaço entre o novo campo e os botões + # self.helper.layout[0][4][1].append(HTML('

')) + + # Remove botões que estão fora do form + self.helper.layout[1].pop() + + # Adiciona novos botões dentro do form + self.helper.layout[0][4][0].insert(2, form_actions(more=[ + HTML('Cancelar')])) + + +class TipoAutorCrud(CrudAux): + model = TipoAutor + help_path = 'tipo-autor' + + class BaseMixin(CrudAux.BaseMixin): + list_field_names = ['descricao', 'content_type'] + form_class = TipoAutorForm + + +class AutorCrud(CrudAux): + model = Autor + help_path = 'autor' + + class BaseMixin(CrudAux.BaseMixin): + list_field_names = ['tipo', 'nome', 'user__username'] + + class UpdateView(CrudAux.UpdateView): + layout_key = None + form_class = AutorForm + + def __init__(self, *args, **kwargs): + # montar_helper_autor(self) + super(CrudAux.UpdateView, self).__init__(*args, **kwargs) + + def get_context_data(self, **kwargs): + context = super( + CrudAux.UpdateView, self).get_context_data(**kwargs) + #context['helper'] = self.helper + return context + + class CreateView(CrudAux.CreateView): + form_class = AutorForm + layout_key = None + + """def __init__(self, *args, **kwargs): + montar_helper_autor(self) + super(CrudAux.CreateView, self).__init__(*args, **kwargs)""" + + """def get_context_data(self, **kwargs): + context = super( + CrudAux.CreateView, self).get_context_data(**kwargs) + context['helper'] = self.helper + return context""" + + """def get_success_url(self): + pk_autor = Autor.objects.get( + email=self.request.POST.get('email')).id + kwargs = {} + user = get_user_model().objects.get( + email=self.request.POST.get('email')) + kwargs['token'] = default_token_generator.make_token(user) + kwargs['uidb64'] = urlsafe_base64_encode(force_bytes(user.pk)) + assunto = "SAPL - Confirmação de Conta" + full_url = self.request.get_raw_uri() + url_base = full_url[:full_url.find('sistema') - 1] + + mensagem = ("Este e-mail foi utilizado para fazer cadastro no " + + "SAPL com o perfil de Autor. Agora você pode " + + "criar/editar/enviar Proposições.\n" + + "Seu nome de usuário é: " + + self.request.POST['username'] + "\n" + "Caso você não tenha feito este cadastro, por favor " + + "ignore esta mensagem. Caso tenha, clique " + + "no link abaixo\n" + url_base + + reverse('sapl.materia:confirmar_email', kwargs=kwargs)) + remetente = settings.EMAIL_SEND_USER + destinatario = [self.request.POST.get('email')] + send_mail(assunto, mensagem, remetente, destinatario, + fail_silently=False) + return reverse('sapl.base:autor_detail', + kwargs={'pk': pk_autor})""" + + class RelatorioAtasView(FilterView): model = SessaoPlenaria filterset_class = RelatorioAtasFilterSet diff --git a/sapl/comissoes/models.py b/sapl/comissoes/models.py index 2036c76c0..9919857c1 100644 --- a/sapl/comissoes/models.py +++ b/sapl/comissoes/models.py @@ -1,8 +1,10 @@ +from django.contrib.contenttypes.fields import GenericRelation from django.db import models from django.utils.translation import ugettext_lazy as _ from model_utils import Choices +from sapl.base.models import Autor from sapl.parlamentares.models import Parlamentar from sapl.utils import YES_NO_CHOICES @@ -79,6 +81,8 @@ class Comissao(models.Model): choices=YES_NO_CHOICES, verbose_name=_('Comissão Ativa?')) + autor = GenericRelation(Autor) + class Meta: verbose_name = _('Comissão') verbose_name_plural = _('Comissões') diff --git a/sapl/crispy_layout_mixin.py b/sapl/crispy_layout_mixin.py index e0184e0f0..33e6d2608 100644 --- a/sapl/crispy_layout_mixin.py +++ b/sapl/crispy_layout_mixin.py @@ -1,12 +1,12 @@ from math import ceil -import rtyaml from crispy_forms.bootstrap import FormActions from crispy_forms.helper import FormHelper from crispy_forms.layout import HTML, Div, Fieldset, Layout, Submit from django import template from django.utils import formats from django.utils.translation import ugettext as _ +import rtyaml def heads_and_tails(list_of_lists): @@ -138,8 +138,9 @@ class CrispyLayoutFormMixin: # simply return None if there is no get_form on super pass else: - form.helper = FormHelper() - form.helper.layout = SaplFormLayout(*self.get_layout()) + if self.layout_key: + form.helper = FormHelper() + form.helper.layout = SaplFormLayout(*self.get_layout()) return form @property diff --git a/sapl/materia/forms.py b/sapl/materia/forms.py index 7af65be63..5e434007d 100644 --- a/sapl/materia/forms.py +++ b/sapl/materia/forms.py @@ -1,6 +1,5 @@ from datetime import datetime -import django_filters from crispy_forms.helper import FormHelper from crispy_forms.layout import HTML, Button, Column, Fieldset, Layout from django import forms @@ -12,6 +11,7 @@ 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 _ +import django_filters from sapl.comissoes.models import Comissao from sapl.crispy_layout_mixin import form_actions, to_row @@ -508,7 +508,7 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet): 'data_apresentacao', 'data_publicacao', 'autoria__autor__tipo', - 'autoria__autor__partido', + #'autoria__autor__partido', 'relatoria__parlamentar_id', 'local_origem_externa', 'tramitacao__unidade_tramitacao_destino', @@ -544,7 +544,7 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet): self.filters['tipo'].label = 'Tipo de Matéria' self.filters['autoria__autor__tipo'].label = 'Tipo de Autor' - self.filters['autoria__autor__partido'].label = 'Partido do Autor' + # self.filters['autoria__autor__partido'].label = 'Partido do Autor' self.filters['relatoria__parlamentar_id'].label = 'Relatoria' row1 = to_row( @@ -566,7 +566,8 @@ class MateriaLegislativaFilterSet(django_filters.FilterSet): css_class='btn btn-primary btn-sm'), 10)]) row5 = to_row( [('autoria__autor__tipo', 6), - ('autoria__autor__partido', 6)]) + # ('autoria__autor__partido', 6) + ]) row6 = to_row( [('relatoria__parlamentar_id', 6), ('local_origem_externa', 6)]) @@ -666,122 +667,6 @@ class AutoriaForm(ModelForm): return self.cleaned_data -class AutorForm(ModelForm): - senha = forms.CharField( - max_length=20, - label=_('Senha'), - required=True, - widget=forms.PasswordInput()) - - senha_confirma = forms.CharField( - max_length=20, - label=_('Confirmar Senha'), - required=True, - widget=forms.PasswordInput()) - - confirma_email = forms.EmailField( - required=True, - label=_('Confirmar Email')) - - username = forms.CharField( - required=True, - max_length=50 - ) - - class Meta: - model = Autor - fields = ['username', - 'senha', - 'email', - 'nome', - 'tipo', - 'cargo'] - widgets = {'nome': forms.HiddenInput()} - - def valida_igualdade(self, texto1, texto2, msg): - if texto1 != texto2: - raise ValidationError(msg) - return True - - def valida_email_existente(self): - return get_user_model().objects.filter( - email=self.cleaned_data['email']).exists() - - def clean(self): - - if 'username' not in self.cleaned_data: - raise ValidationError(_('Favor informar o username')) - - if ('senha' not in self.cleaned_data or - 'senha_confirma' not in self.cleaned_data): - raise ValidationError(_('Favor informar as senhas')) - - msg = _('As senhas não conferem.') - self.valida_igualdade( - self.cleaned_data['senha'], - self.cleaned_data['senha_confirma'], - msg) - - if ('email' not in self.cleaned_data or - 'confirma_email' not in self.cleaned_data): - raise ValidationError(_('Favor informar endereços de email')) - - msg = _('Os emails não conferem.') - self.valida_igualdade( - self.cleaned_data['email'], - self.cleaned_data['confirma_email'], - msg) - - email_existente = self.valida_email_existente() - - if (Autor.objects.filter( - username=self.cleaned_data['username']).exists()): - raise ValidationError(_('Já existe um autor para este usuário')) - - if email_existente: - msg = _('Este email já foi cadastrado.') - raise ValidationError(msg) - - try: - validate_password(self.cleaned_data['senha']) - except ValidationError as error: - raise ValidationError(error) - - try: - User.objects.get( - username=self.cleaned_data['username'], - email=self.cleaned_data['email']) - except ObjectDoesNotExist: - msg = _('Este nome de usuario não está cadastrado. ' + - 'Por favor, cadastre-o no Administrador do ' + - 'Sistema antes de adicioná-lo como Autor') - raise ValidationError(msg) - - return self.cleaned_data - - @transaction.atomic - def save(self, commit=False): - - autor = super(AutorForm, self).save(commit) - - u = User.objects.get( - username=autor.username, - email=autor.email) - - u.set_password(self.cleaned_data['senha']) - u.is_active = False - u.save() - - autor.user = u - - autor.save() - - grupo = Group.objects.filter(name='Autor')[0] - u.groups.add(grupo) - - return autor - - class AcessorioEmLoteFilterSet(django_filters.FilterSet): filter_overrides = {models.DateField: { diff --git a/sapl/materia/migrations/0054_auto_20161009_1222.py b/sapl/materia/migrations/0054_auto_20161009_1222.py new file mode 100644 index 000000000..286ce96c8 --- /dev/null +++ b/sapl/materia/migrations/0054_auto_20161009_1222.py @@ -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', + ), + ] diff --git a/sapl/materia/migrations/0055_auto_20161009_1418.py b/sapl/materia/migrations/0055_auto_20161009_1418.py new file mode 100644 index 000000000..8168ab4e4 --- /dev/null +++ b/sapl/materia/migrations/0055_auto_20161009_1418.py @@ -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'), + ), + ] diff --git a/sapl/materia/models.py b/sapl/materia/models.py index baedf7248..5a14e7ced 100644 --- a/sapl/materia/models.py +++ b/sapl/materia/models.py @@ -3,6 +3,7 @@ from django.db import models from django.utils.translation import ugettext_lazy as _ from model_utils import Choices +from sapl.base.models import Autor from sapl.comissoes.models import Comissao from sapl.parlamentares.models import Parlamentar, Partido from sapl.utils import (RANGE_ANOS, YES_NO_CHOICES, @@ -143,6 +144,12 @@ class MateriaLegislativa(models.Model): verbose_name=_('Texto Original (PDF)'), validators=[restringe_tipos_de_arquivo_txt]) + autores = models.ManyToManyField( + Autor, + through='Autoria', + through_fields=('materia', 'autor'), + symmetrical=False,) + class Meta: verbose_name = _('Matéria Legislativa') verbose_name_plural = _('Matérias Legislativas') @@ -153,6 +160,22 @@ class MateriaLegislativa(models.Model): 'tipo': self.tipo, 'numero': self.numero, 'ano': self.ano} +class Autoria(models.Model): + autor = models.ForeignKey(Autor, verbose_name=_('Autor')) + materia = models.ForeignKey( + MateriaLegislativa, verbose_name=_('Matéria Legislativa')) + primeiro_autor = models.BooleanField(verbose_name=_('Primeiro Autor'), + choices=YES_NO_CHOICES) + + class Meta: + verbose_name = _('Autoria') + verbose_name_plural = _('Autorias') + + def __str__(self): + return _('%(autor)s - %(materia)s') % { + 'autor': self.autor, 'materia': self.materia} + + class AcompanhamentoMateria(models.Model): usuario = models.CharField(max_length=50) materia = models.ForeignKey(MateriaLegislativa) @@ -204,68 +227,6 @@ class AssuntoMateria(models.Model): return self.assunto -class TipoAutor(models.Model): - descricao = models.CharField(max_length=50, verbose_name=_('Descrição')) - - class Meta: - verbose_name = _('Tipo de Autor') - verbose_name_plural = _('Tipos de Autor') - - def __str__(self): - return self.descricao - - -class Autor(models.Model): - user = models.ForeignKey( - get_settings_auth_user_model(), blank=True, null=True) - partido = models.ForeignKey(Partido, blank=True, null=True) - comissao = models.ForeignKey(Comissao, blank=True, null=True) - parlamentar = models.ForeignKey(Parlamentar, blank=True, null=True) - tipo = models.ForeignKey(TipoAutor, verbose_name=_('Tipo')) - nome = models.CharField( - max_length=50, blank=True, verbose_name=_('Autor')) - cargo = models.CharField(max_length=50, blank=True) - username = models.CharField( - max_length=50, - blank=True, - verbose_name=_('Nome de Usuário')) - email = models.EmailField( - verbose_name=_('Email')) - - class Meta: - verbose_name = _('Autor') - verbose_name_plural = _('Autores') - - def __str__(self): - if str(self.tipo) == 'Parlamentar' and self.parlamentar: - return self.parlamentar.nome_parlamentar - elif str(self.tipo) == 'Comissao' and self.comissao: - return str(self.comissao) - elif str(self.tipo) == 'Partido' and self.partido: - return str(self.partido) - else: - if str(self.cargo): - return _('%(nome)s - %(cargo)s') % { - 'nome': self.nome, 'cargo': self.cargo} - else: - return str(self.nome) - - -class Autoria(models.Model): - autor = models.ForeignKey(Autor, verbose_name=_('Autor')) - materia = models.ForeignKey(MateriaLegislativa) - primeiro_autor = models.BooleanField(verbose_name=_('Primeiro Autor'), - choices=YES_NO_CHOICES) - - class Meta: - verbose_name = _('Autoria') - verbose_name_plural = _('Autorias') - - def __str__(self): - return _('%(autor)s - %(materia)s') % { - 'autor': self.autor, 'materia': self.materia} - - class DespachoInicial(models.Model): # TODO M2M? materia = models.ForeignKey(MateriaLegislativa) diff --git a/sapl/materia/urls.py b/sapl/materia/urls.py index 5f943be9d..dc18e47ef 100644 --- a/sapl/materia/urls.py +++ b/sapl/materia/urls.py @@ -3,7 +3,7 @@ from django.conf.urls import include, url from sapl.materia.views import (AcompanhamentoConfirmarView, AcompanhamentoExcluirView, AcompanhamentoMateriaView, AnexadaCrud, - AutorCrud, AutoriaCrud, ConfirmarEmailView, + AutoriaCrud, ConfirmarEmailView, ConfirmarProposicao, DespachoInicialCrud, DocumentoAcessorioCrud, DocumentoAcessorioEmLoteView, @@ -15,7 +15,7 @@ from sapl.materia.views import (AcompanhamentoConfirmarView, ProposicaoRecebida, ProposicaoTaView, ReceberProposicao, ReciboProposicaoView, RegimeTramitacaoCrud, RelatoriaCrud, - StatusTramitacaoCrud, TipoAutorCrud, + StatusTramitacaoCrud, TipoDocumentoCrud, TipoFimRelatoriaCrud, TipoMateriaCrud, TipoProposicaoCrud, TramitacaoCrud, TramitacaoEmLoteView, @@ -78,6 +78,8 @@ urlpatterns_proposicao = [ name='proposicao-devolvida'), url(r'^proposicao/confirmar/(?P\d+)', ConfirmarProposicao.as_view(), name='proposicao-confirmar'), + url(r'^sistema/proposicao/tipo/', + include(TipoProposicaoCrud.get_urls())), url(r'^proposicao/(?P[0-9]+)/ta$', ProposicaoTaView.as_view(), name='proposicao_ta'), @@ -89,7 +91,6 @@ urlpatterns_sistema = [ url(r'^sistema/materia/tipo/', include(TipoMateriaCrud.get_urls())), url(r'^sistema/materia/regime-tramitacao/', include(RegimeTramitacaoCrud.get_urls())), - url(r'^sistema/materia/tipo-autor/', include(TipoAutorCrud.get_urls())), url(r'^sistema/materia/tipo-documento/', include(TipoDocumentoCrud.get_urls())), url(r'^sistema/materia/tipo-fim-relatoria/', @@ -97,7 +98,6 @@ urlpatterns_sistema = [ url(r'^sistema/materia/unidade-tramitacao/', include(UnidadeTramitacaoCrud.get_urls())), url(r'^sistema/materia/origem/', include(OrigemCrud.get_urls())), - url(r'^sistema/materia/autor/', include(AutorCrud.get_urls())), url(r'^sistema/materia/status-tramitacao/', include(StatusTramitacaoCrud.get_urls())), url(r'^sistema/materia/orgao/', include(OrgaoCrud.get_urls())), diff --git a/sapl/materia/views.py b/sapl/materia/views.py index 083b3ede1..71b166c12 100644 --- a/sapl/materia/views.py +++ b/sapl/materia/views.py @@ -24,7 +24,8 @@ from django.views.generic import CreateView, ListView, TemplateView, UpdateView from django.views.generic.base import RedirectView from django_filters.views import FilterView -from sapl.base.models import AppConfig, CasaLegislativa +from sapl.base.models import AppConfig, CasaLegislativa, Autor, TipoAutor +from sapl.base.views import montar_row_autor from sapl.compilacao.views import IntegracaoTaView from sapl.crispy_layout_mixin import SaplFormLayout, form_actions, to_row from sapl.crud.base import (ACTION_CREATE, ACTION_DELETE, ACTION_DETAIL, @@ -40,17 +41,17 @@ from sapl.utils import (TURNO_TRAMITACAO_CHOICES, YES_NO_CHOICES, autor_label, permissoes_protocoloadm, permission_required_for_app) from .forms import (AcessorioEmLoteFilterSet, AcompanhamentoMateriaForm, - AutorForm, ConfirmarProposicaoForm, DocumentoAcessorioForm, + ConfirmarProposicaoForm, DocumentoAcessorioForm, MateriaLegislativaFilterSet, PrimeiraTramitacaoEmLoteFilterSet, ProposicaoForm, ReceberProposicaoForm, TramitacaoEmLoteFilterSet, filtra_tramitacao_destino, filtra_tramitacao_destino_and_status, filtra_tramitacao_status) -from .models import (AcompanhamentoMateria, Anexada, Autor, Autoria, +from .models import (AcompanhamentoMateria, Anexada, Autoria, DespachoInicial, DocumentoAcessorio, MateriaLegislativa, Numeracao, Orgao, Origem, Proposicao, RegimeTramitacao, - Relatoria, StatusTramitacao, TipoAutor, TipoDocumento, + Relatoria, StatusTramitacao, TipoDocumento, TipoFimRelatoria, TipoMateriaLegislativa, TipoProposicao, Tramitacao, UnidadeTramitacao) @@ -69,20 +70,17 @@ TipoDocumentoCrud = CrudAux.build( TipoFimRelatoriaCrud = CrudAux.build( TipoFimRelatoria, 'fim_relatoria') -TipoAutorCrud = CrudAux.build( - TipoAutor, 'regime_tramitacao') - class MateriaTaView(IntegracaoTaView): model = MateriaLegislativa model_type_foreignkey = TipoMateriaLegislativa - """ - Para manter a app compilacao isolada das outras aplicações, - este get foi implementado para tratar uma prerrogativa externa - de usuário. - """ def get(self, request, *args, **kwargs): + """ + Para manter a app compilacao isolada das outras aplicações, + este get foi implementado para tratar uma prerrogativa externa + de usuário. + """ if AppConfig.attr('texto_articulado_materia'): return IntegracaoTaView.get(self, request, *args, **kwargs) else: @@ -118,89 +116,6 @@ def recuperar_materia(request): return response -def montar_helper_autor(self): - autor_row = montar_row_autor('nome') - self.helper = FormHelper() - self.helper.layout = SaplFormLayout(*self.get_layout()) - - # Adiciona o novo campo 'autor' e mecanismo de busca - self.helper.layout[0][0].append(HTML(autor_label)) - self.helper.layout[0][0].append(HTML(autor_modal)) - self.helper.layout[0][1] = autor_row - - # Adiciona espaço entre o novo campo e os botões - # self.helper.layout[0][4][1].append(HTML('

')) - - # Remove botões que estão fora do form - self.helper.layout[1].pop() - - # Adiciona novos botões dentro do form - self.helper.layout[0][4][0].insert(2, form_actions(more=[ - HTML('Cancelar')])) - - -class AutorCrud(CrudAux): - model = Autor - help_path = 'autor' - - class BaseMixin(CrudAux.BaseMixin): - list_field_names = ['tipo', 'nome'] - - class UpdateView(CrudAux.UpdateView): - layout_key = 'AutorCreate' - - def __init__(self, *args, **kwargs): - montar_helper_autor(self) - super(UpdateView, self).__init__(*args, **kwargs) - - def get_context_data(self, **kwargs): - context = super(UpdateView, self).get_context_data(**kwargs) - context['helper'] = self.helper - return context - - class CreateView(CrudAux.CreateView): - form_class = AutorForm - layout_key = 'AutorCreate' - - def __init__(self, *args, **kwargs): - montar_helper_autor(self) - super(CreateView, self).__init__(*args, **kwargs) - - def get_context_data(self, **kwargs): - context = super(CreateView, self).get_context_data(**kwargs) - context['helper'] = self.helper - return context - - def get_success_url(self): - pk_autor = Autor.objects.get( - email=self.request.POST.get('email')).id - kwargs = {} - user = get_user_model().objects.get( - email=self.request.POST.get('email')) - kwargs['token'] = default_token_generator.make_token(user) - kwargs['uidb64'] = urlsafe_base64_encode(force_bytes(user.pk)) - assunto = "SAPL - Confirmação de Conta" - full_url = self.request.get_raw_uri() - url_base = full_url[:full_url.find('sistema') - 1] - - mensagem = ("Este e-mail foi utilizado para fazer cadastro no " + - "SAPL com o perfil de Autor. Agora você pode " + - "criar/editar/enviar Proposições.\n" + - "Seu nome de usuário é: " + - self.request.POST['username'] + "\n" - "Caso você não tenha feito este cadastro, por favor " + - "ignore esta mensagem. Caso tenha, clique " + - "no link abaixo\n" + url_base + - reverse('sapl.materia:confirmar_email', kwargs=kwargs)) - remetente = settings.EMAIL_SEND_USER - destinatario = [self.request.POST.get('email')] - send_mail(assunto, mensagem, remetente, destinatario, - fail_silently=False) - return reverse('sapl.materia:autor_detail', - kwargs={'pk': pk_autor}) - - class ConfirmarEmailView(TemplateView): template_name = "confirma_email.html" @@ -661,19 +576,6 @@ class TramitacaoCrud(MasterDetailCrud): return HttpResponseRedirect(url) -def montar_row_autor(name): - autor_row = to_row( - [(name, 0), - (Button('pesquisar', - 'Pesquisar Autor', - css_class='btn btn-primary btn-sm'), 2), - (Button('limpar', - 'Limpar Autor', - css_class='btn btn-primary btn-sm'), 10)]) - - return autor_row - - def montar_helper_documento_acessorio(self): autor_row = montar_row_autor('autor') self.helper = FormHelper() diff --git a/sapl/parlamentares/models.py b/sapl/parlamentares/models.py index cb6e8b42d..2629eac6c 100644 --- a/sapl/parlamentares/models.py +++ b/sapl/parlamentares/models.py @@ -1,9 +1,11 @@ from datetime import datetime +from django.contrib.contenttypes.fields import GenericRelation from django.db import models from django.utils.translation import ugettext_lazy as _ from model_utils import Choices +from sapl.base.models import Autor from sapl.utils import (INDICADOR_AFASTAMENTO, UF, YES_NO_CHOICES, intervalos_tem_intersecao, restringe_tipos_de_arquivo_img) @@ -262,6 +264,8 @@ class Parlamentar(models.Model): verbose_name=_('Fotografia'), validators=[restringe_tipos_de_arquivo_img]) + autor = GenericRelation(Autor) + class Meta: verbose_name = _('Parlamentar') verbose_name_plural = _('Parlamentares') diff --git a/sapl/protocoloadm/migrations/0003_auto_20161009_1222.py b/sapl/protocoloadm/migrations/0003_auto_20161009_1222.py new file mode 100644 index 000000000..a083fdffa --- /dev/null +++ b/sapl/protocoloadm/migrations/0003_auto_20161009_1222.py @@ -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'), + ), + ] diff --git a/sapl/rest_pagination.py b/sapl/rest_pagination.py new file mode 100644 index 000000000..bb7096a55 --- /dev/null +++ b/sapl/rest_pagination.py @@ -0,0 +1,33 @@ +from django.core.paginator import EmptyPage +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, + }) diff --git a/sapl/sessao/forms.py b/sapl/sessao/forms.py index fa5694777..d629c608c 100644 --- a/sapl/sessao/forms.py +++ b/sapl/sessao/forms.py @@ -1,12 +1,12 @@ from datetime import datetime -import django_filters from crispy_forms.helper import FormHelper from crispy_forms.layout import HTML, Button, Fieldset, Layout from django import forms from django.core.exceptions import ObjectDoesNotExist, ValidationError from django.forms import ModelForm from django.utils.translation import ugettext_lazy as _ +import django_filters from sapl.crispy_layout_mixin import form_actions, to_row from sapl.materia.forms import MateriaLegislativaFilterSet @@ -212,7 +212,7 @@ class AdicionarVariasMateriasFilterSet(MateriaLegislativaFilterSet): 'data_apresentacao', 'data_publicacao', 'autoria__autor__tipo', - 'autoria__autor__partido', + #'autoria__autor__partido', 'relatoria__parlamentar_id', 'local_origem_externa', 'em_tramitacao', @@ -231,7 +231,7 @@ class AdicionarVariasMateriasFilterSet(MateriaLegislativaFilterSet): self.filters['tipo'].label = 'Tipo de Matéria' self.filters['autoria__autor__tipo'].label = 'Tipo de Autor' - self.filters['autoria__autor__partido'].label = 'Partido do Autor' + #self.filters['autoria__autor__partido'].label = 'Partido do Autor' self.filters['relatoria__parlamentar_id'].label = 'Relatoria' row1 = to_row( @@ -253,7 +253,8 @@ class AdicionarVariasMateriasFilterSet(MateriaLegislativaFilterSet): css_class='btn btn-primary btn-sm'), 10)]) row5 = to_row( [('autoria__autor__tipo', 6), - ('autoria__autor__partido', 6)]) + #('autoria__autor__partido', 6) + ]) row6 = to_row( [('relatoria__parlamentar_id', 6), ('local_origem_externa', 6)]) diff --git a/sapl/sessao/urls.py b/sapl/sessao/urls.py index f24c31ef0..672a95f0c 100644 --- a/sapl/sessao/urls.py +++ b/sapl/sessao/urls.py @@ -30,6 +30,7 @@ from .apps import AppConfig app_name = AppConfig.name + urlpatterns = [ url(r'^sessao/', include(SessaoCrud.get_urls() + OradorCrud.get_urls() + OradorExpedienteCrud.get_urls() + diff --git a/sapl/settings.py b/sapl/settings.py index 1cb071eb5..b9f28d6e3 100644 --- a/sapl/settings.py +++ b/sapl/settings.py @@ -48,6 +48,7 @@ SAPL_APPS = ( 'sapl.painel', 'sapl.protocoloadm', 'sapl.compilacao', + 'sapl.api' ) INSTALLED_APPS = ( @@ -86,6 +87,29 @@ MIDDLEWARE_CLASSES = ( 'django.middleware.security.SecurityMiddleware', ) + +REST_FRAMEWORK = { + "DEFAULT_RENDERER_CLASSES": ( + "rest_framework.renderers.JSONRenderer", + # "rest_framework.renderers.BrowsableAPIRenderer", + ), + "DEFAULT_PARSER_CLASSES": ( + "rest_framework.parsers.JSONParser", + ), + "DEFAULT_PERMISSION_CLASSES": ( + "rest_framework.permissions.IsAuthenticated", + ), + "DEFAULT_AUTHENTICATION_CLASSES": ( + "rest_framework.authentication.SessionAuthentication", + ), + "DEFAULT_PAGINATION_CLASS": "sapl.rest_pagination.StandardPagination", + "DEFAULT_FILTER_BACKENDS": ( + "rest_framework.filters.SearchFilter", + "rest_framework.filters.DjangoFilterBackend", + ), +} + + ROOT_URLCONF = 'sapl.urls' TEMPLATES = [ diff --git a/sapl/static/styles/app.scss b/sapl/static/styles/app.scss index fabccf055..0e569e197 100644 --- a/sapl/static/styles/app.scss +++ b/sapl/static/styles/app.scss @@ -88,6 +88,7 @@ h6, .h6 { } } + // #### CRUD DETAIL ######################################## p.control-label { font-weight: bold; diff --git a/sapl/templates/base/autor_form.html b/sapl/templates/base/autor_form.html new file mode 100644 index 000000000..dec8b20a3 --- /dev/null +++ b/sapl/templates/base/autor_form.html @@ -0,0 +1,61 @@ +{% extends "crud/form.html" %} +{% load i18n %} +{% block extra_js %} + + + +{% endblock %} diff --git a/sapl/templates/base/layouts.yaml b/sapl/templates/base/layouts.yaml index b657d4b79..c63135ee6 100644 --- a/sapl/templates/base/layouts.yaml +++ b/sapl/templates/base/layouts.yaml @@ -15,3 +15,16 @@ AppConfig: - documentos_administrativos sequencia_numeracao painel_aberto {% trans 'Textos Articulados' %}: - texto_articulado_proposicao texto_articulado_materia texto_articulado_norma + +TipoAutor: + {% trans 'Tipo Autor' %}: + - content_type descricao:7 + +Autor: + {% trans 'Autor' %}: + - tipo:3 nome + - username:6 cargo + +AutorCreate: + {% trans 'Cadastro de Usuários Autores' %}: + - tipo:3 search_autor diff --git a/sapl/templates/base/tipoautor_form.html b/sapl/templates/base/tipoautor_form.html new file mode 100644 index 000000000..01f771246 --- /dev/null +++ b/sapl/templates/base/tipoautor_form.html @@ -0,0 +1,20 @@ +{% extends "crud/form.html" %} +{% load i18n %} +{% block extra_js %} + + + +{% endblock %} diff --git a/sapl/templates/materia/layouts.yaml b/sapl/templates/materia/layouts.yaml index a5d20cfbf..47cb7f212 100644 --- a/sapl/templates/materia/layouts.yaml +++ b/sapl/templates/materia/layouts.yaml @@ -47,23 +47,6 @@ AnexadaDetail: - materia_anexada - data_anexacao data_desanexacao -TipoAutor: - {% trans 'Tipo Autor' %}: - - descricao - -Autor: - {% trans 'Autor' %}: - - tipo:3 nome - - username:6 cargo - -AutorCreate: - Autor: - - tipo - - nome - - username:4 senha:4 senha_confirma:4 - - email:6 confirma_email:6 - - cargo - Autoria: {% trans 'Autoria' %}: - autor primeiro_autor diff --git a/sapl/templates/sistema.html b/sapl/templates/sistema.html index 0b80f85ad..9db02b417 100644 --- a/sapl/templates/sistema.html +++ b/sapl/templates/sistema.html @@ -3,10 +3,12 @@ {% block base_content %} -

Configuração Inicial

+

Configuração Gerais


@@ -49,7 +51,6 @@

Módulo Proposições


@@ -57,12 +58,10 @@ diff --git a/sapl/urls.py b/sapl/urls.py index 750cce94d..84f92363d 100644 --- a/sapl/urls.py +++ b/sapl/urls.py @@ -31,6 +31,7 @@ import sapl.parlamentares.urls import sapl.protocoloadm.urls import sapl.relatorios.urls import sapl.sessao.urls +import sapl.api.urls urlpatterns = [ url(r'^$', TemplateView.as_view(template_name='index.html')), @@ -50,6 +51,8 @@ urlpatterns = [ # must come at the end # so that base /sistema/ url doesn't capture its children url(r'', include(sapl.base.urls)), + + url(r'^api/', include(sapl.api.urls)), ]